2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Scalable Vector Graphics Format %
21 % Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % https://imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/annotate.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/composite-private.h"
52 #include "MagickCore/delegate.h"
53 #include "MagickCore/delegate-private.h"
54 #include "MagickCore/draw.h"
55 #include "MagickCore/exception.h"
56 #include "MagickCore/exception-private.h"
57 #include "MagickCore/gem.h"
58 #include "MagickCore/image.h"
59 #include "MagickCore/image-private.h"
60 #include "MagickCore/list.h"
61 #include "MagickCore/log.h"
62 #include "MagickCore/magick.h"
63 #include "MagickCore/memory_.h"
64 #include "MagickCore/memory-private.h"
65 #include "MagickCore/module.h"
66 #include "MagickCore/monitor.h"
67 #include "MagickCore/monitor-private.h"
68 #include "MagickCore/quantum-private.h"
69 #include "MagickCore/pixel-accessor.h"
70 #include "MagickCore/property.h"
71 #include "MagickCore/resource_.h"
72 #include "MagickCore/static.h"
73 #include "MagickCore/string_.h"
74 #include "MagickCore/string-private.h"
75 #include "MagickCore/token.h"
76 #include "MagickCore/utility.h"
77 #include "MagickCore/version.h"
78 #if defined(MAGICKCORE_XML_DELEGATE)
79 # if defined(MAGICKCORE_WINDOWS_SUPPORT)
80 # if !defined(__MINGW32__)
81 # include <win32config.h>
84 # include <libxml/parser.h>
85 # include <libxml/xmlmemory.h>
86 # include <libxml/parserInternals.h>
87 # include <libxml/xmlerror.h>
90 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
91 #include "autotrace/autotrace.h"
94 #if defined(MAGICKCORE_RSVG_DELEGATE)
95 #include "librsvg/rsvg.h"
96 #if !defined(LIBRSVG_CHECK_VERSION)
97 #include "librsvg/rsvg-cairo.h"
98 #include "librsvg/librsvg-features.h"
99 #elif !LIBRSVG_CHECK_VERSION(2,36,2)
100 #include "librsvg/rsvg-cairo.h"
101 #include "librsvg/librsvg-features.h"
108 #define SVGDensity 90.0
111 Typedef declarations.
113 typedef struct _BoundingBox
122 typedef struct _ElementInfo
132 typedef struct _SVGInfo
186 #if defined(MAGICKCORE_XML_DELEGATE)
199 Forward declarations.
201 static MagickBooleanType
202 WriteSVGImage(const ImageInfo *,Image *,ExceptionInfo *);
205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
215 % IsSVG()() returns MagickTrue if the image format type, identified by the
216 % magick string, is SVG.
218 % The format of the IsSVG method is:
220 % MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
222 % A description of each parameter follows:
224 % o magick: compare image format pattern against these bytes.
226 % o length: Specifies the length of the magick string.
229 static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
233 if (LocaleNCompare((const char *) magick+1,"svg",3) == 0)
237 if (LocaleNCompare((const char *) magick+1,"?xml",4) == 0)
242 #if defined(MAGICKCORE_XML_DELEGATE)
244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
248 % R e a d S V G I m a g e %
252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
254 % ReadSVGImage() reads a Scalable Vector Gaphics file and returns it. It
255 % allocates the memory necessary for the new Image structure and returns a
256 % pointer to the new image.
258 % The format of the ReadSVGImage method is:
260 % Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
262 % A description of each parameter follows:
264 % o image_info: the image info.
266 % o exception: return any errors or warnings in this structure.
270 static SVGInfo *AcquireSVGInfo(void)
275 svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info));
276 if (svg_info == (SVGInfo *) NULL)
277 return((SVGInfo *) NULL);
278 (void) memset(svg_info,0,sizeof(*svg_info));
279 svg_info->text=AcquireString("");
280 svg_info->scale=(double *) AcquireCriticalMemory(sizeof(*svg_info->scale));
281 GetAffineMatrix(&svg_info->affine);
282 svg_info->scale[0]=ExpandAffine(&svg_info->affine);
286 static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
288 if (svg_info->text != (char *) NULL)
289 svg_info->text=DestroyString(svg_info->text);
290 if (svg_info->scale != (double *) NULL)
291 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
292 if (svg_info->title != (char *) NULL)
293 svg_info->title=DestroyString(svg_info->title);
294 if (svg_info->comment != (char *) NULL)
295 svg_info->comment=DestroyString(svg_info->comment);
296 return((SVGInfo *) RelinquishMagickMemory(svg_info));
299 static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
304 token[MagickPathExtent];
312 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
313 assert(string != (const char *) NULL);
314 p=(const char *) string;
315 GetNextToken(p,&p,MagickPathExtent,token);
316 value=StringToDouble(token,&next_token);
317 if (strchr(token,'%') != (char *) NULL)
325 if (svg_info->view_box.width == 0.0)
327 return(svg_info->view_box.width*value/100.0);
331 if (svg_info->view_box.height == 0.0)
333 return(svg_info->view_box.height*value/100.0);
335 alpha=value-svg_info->view_box.width;
336 beta=value-svg_info->view_box.height;
337 return(hypot(alpha,beta)/sqrt(2.0)/100.0);
339 GetNextToken(p,&p,MagickPathExtent,token);
340 if (LocaleNCompare(token,"cm",2) == 0)
341 return(SVGDensity*svg_info->scale[0]/2.54*value);
342 if (LocaleNCompare(token,"em",2) == 0)
343 return(svg_info->pointsize*value);
344 if (LocaleNCompare(token,"ex",2) == 0)
345 return(svg_info->pointsize*value/2.0);
346 if (LocaleNCompare(token,"in",2) == 0)
347 return(SVGDensity*svg_info->scale[0]*value);
348 if (LocaleNCompare(token,"mm",2) == 0)
349 return(SVGDensity*svg_info->scale[0]/25.4*value);
350 if (LocaleNCompare(token,"pc",2) == 0)
351 return(SVGDensity*svg_info->scale[0]/6.0*value);
352 if (LocaleNCompare(token,"pt",2) == 0)
353 return(svg_info->scale[0]*value);
354 if (LocaleNCompare(token,"px",2) == 0)
359 #if defined(__cplusplus) || defined(c_plusplus)
363 static int SVGIsStandalone(void *context)
369 Is this document tagged standalone?
371 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGIsStandalone()");
372 svg_info=(SVGInfo *) context;
373 return(svg_info->document->standalone == 1);
376 static int SVGHasInternalSubset(void *context)
382 Does this document has an internal subset?
384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
385 " SAX.SVGHasInternalSubset()");
386 svg_info=(SVGInfo *) context;
387 return(svg_info->document->intSubset != NULL);
390 static int SVGHasExternalSubset(void *context)
396 Does this document has an external subset?
398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
399 " SAX.SVGHasExternalSubset()");
400 svg_info=(SVGInfo *) context;
401 return(svg_info->document->extSubset != NULL);
404 static void SVGInternalSubset(void *context,const xmlChar *name,
405 const xmlChar *external_id,const xmlChar *system_id)
411 Does this document has an internal subset?
413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
414 " SAX.internalSubset(%s, %s, %s)",(const char *) name,
415 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
416 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
417 svg_info=(SVGInfo *) context;
418 (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id);
421 static xmlParserInputPtr SVGResolveEntity(void *context,
422 const xmlChar *public_id,const xmlChar *system_id)
431 Special entity resolver, better left to the parser, it has more
432 context than the application layer. The default behaviour is to
433 not resolve the entities, in that case the ENTITY_REF nodes are
434 built in the structure (and the parameter values).
436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
437 " SAX.resolveEntity(%s, %s)",
438 (public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"),
439 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
440 svg_info=(SVGInfo *) context;
441 stream=xmlLoadExternalEntity((const char *) system_id,(const char *)
442 public_id,svg_info->parser);
446 static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
452 Get an entity by name.
454 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGGetEntity(%s)",
456 svg_info=(SVGInfo *) context;
457 return(xmlGetDocEntity(svg_info->document,name));
460 static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
466 Get a parameter entity by name.
468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
469 " SAX.getParameterEntity(%s)",name);
470 svg_info=(SVGInfo *) context;
471 return(xmlGetParameterEntity(svg_info->document,name));
474 static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
475 const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
481 An entity definition has been parsed.
483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
484 " SAX.entityDecl(%s, %d, %s, %s, %s)",name,type,
485 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
486 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content);
487 svg_info=(SVGInfo *) context;
488 if (svg_info->parser->inSubset == 1)
489 (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id,
492 if (svg_info->parser->inSubset == 2)
493 (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
497 static void SVGAttributeDeclaration(void *context,const xmlChar *element,
498 const xmlChar *name,int type,int value,const xmlChar *default_value,
499 xmlEnumerationPtr tree)
512 An attribute definition has been parsed.
514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
515 " SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value,
517 svg_info=(SVGInfo *) context;
518 fullname=(xmlChar *) NULL;
519 prefix=(xmlChar *) NULL;
520 parser=svg_info->parser;
521 fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix);
522 if (parser->inSubset == 1)
523 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset,
524 element,fullname,prefix,(xmlAttributeType) type,
525 (xmlAttributeDefault) value,default_value,tree);
527 if (parser->inSubset == 2)
528 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset,
529 element,fullname,prefix,(xmlAttributeType) type,
530 (xmlAttributeDefault) value,default_value,tree);
531 if (prefix != (xmlChar *) NULL)
533 if (fullname != (xmlChar *) NULL)
537 static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
538 xmlElementContentPtr content)
547 An element definition has been parsed.
549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
550 " SAX.elementDecl(%s, %d, ...)",name,type);
551 svg_info=(SVGInfo *) context;
552 parser=svg_info->parser;
553 if (parser->inSubset == 1)
554 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset,
555 name,(xmlElementTypeVal) type,content);
557 if (parser->inSubset == 2)
558 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
559 name,(xmlElementTypeVal) type,content);
562 static void SVGStripString(const MagickBooleanType trim,char *message)
571 assert(message != (char *) NULL);
572 if (*message == '\0')
578 for (p=message; *p != '\0'; p++)
580 if ((*p == '/') && (*(p+1) == '*'))
582 for ( ; *p != '\0'; p++)
583 if ((*p == '*') && (*(p+1) == '/'))
594 if (trim != MagickFalse)
599 length=strlen(message);
601 while (isspace((int) ((unsigned char) *p)) != 0)
603 if ((*p == '\'') || (*p == '"'))
606 while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
609 if ((*q == '\'') || (*q == '"'))
611 (void) memmove(message,p,(size_t) (q-p+1));
615 Convert newlines to a space.
617 for (p=message; *p != '\0'; p++)
622 static char **SVGKeyValuePairs(void *context,const int key_sentinel,
623 const int value_sentinel,const char *text,size_t *number_tokens)
641 svg_info=(SVGInfo *) context;
643 if (text == (const char *) NULL)
644 return((char **) NULL);
646 tokens=(char **) AcquireQuantumMemory(extent+2UL,sizeof(*tokens));
647 if (tokens == (char **) NULL)
649 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
650 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
651 return((char **) NULL);
654 Convert string to an ASCII list.
658 for (q=p; *q != '\0'; q++)
660 if ((*q != key_sentinel) && (*q != value_sentinel) && (*q != '\0'))
662 if (i == (ssize_t) extent)
665 tokens=(char **) ResizeQuantumMemory(tokens,extent+2,sizeof(*tokens));
666 if (tokens == (char **) NULL)
668 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
669 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
670 return((char **) NULL);
673 tokens[i]=AcquireString(p);
674 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
675 SVGStripString(MagickTrue,tokens[i]);
679 tokens[i]=AcquireString(p);
680 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
681 SVGStripString(MagickTrue,tokens[i++]);
682 tokens[i]=(char *) NULL;
683 *number_tokens=(size_t) i;
687 static void SVGNotationDeclaration(void *context,const xmlChar *name,
688 const xmlChar *public_id,const xmlChar *system_id)
697 What to do when a notation declaration has been parsed.
699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
700 " SAX.notationDecl(%s, %s, %s)",name,
701 public_id != (const xmlChar *) NULL ? (const char *) public_id : "none",
702 system_id != (const xmlChar *) NULL ? (const char *) system_id : "none");
703 svg_info=(SVGInfo *) context;
704 parser=svg_info->parser;
705 if (parser->inSubset == 1)
706 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
707 name,public_id,system_id);
709 if (parser->inSubset == 2)
710 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
711 name,public_id,system_id);
714 static void SVGProcessStyleElement(void *context,const xmlChar *name,
718 background[MagickPathExtent],
736 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
737 svg_info=(SVGInfo *) context;
738 tokens=SVGKeyValuePairs(context,':',';',style,&number_tokens);
739 if (tokens == (char **) NULL)
741 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
743 keyword=(char *) tokens[i];
744 value=(char *) tokens[i+1];
745 if (LocaleCompare(keyword,"font-size") != 0)
747 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
748 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
749 svg_info->pointsize);
751 color=AcquireString("none");
752 units=AcquireString("userSpaceOnUse");
753 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
755 keyword=(char *) tokens[i];
756 value=(char *) tokens[i+1];
757 (void) LogMagickEvent(CoderEvent,GetMagickModule()," %s: %s",keyword,
764 if (LocaleCompare((const char *) name,"background") == 0)
766 if (LocaleCompare((const char *) name,"svg") == 0)
767 (void) CopyMagickString(background,value,MagickPathExtent);
775 if (LocaleCompare(keyword,"clip-path") == 0)
777 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",value);
780 if (LocaleCompare(keyword,"clip-rule") == 0)
782 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",value);
785 if (LocaleCompare(keyword,"clipPathUnits") == 0)
787 (void) CloneString(&units,value);
788 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
792 if (LocaleCompare(keyword,"color") == 0)
794 (void) CloneString(&color,value);
802 if (LocaleCompare(keyword,"fill") == 0)
804 if (LocaleCompare(value,"currentColor") == 0)
806 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
809 if (LocaleCompare(value,"#000000ff") == 0)
810 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
812 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
815 if (LocaleCompare(keyword,"fillcolor") == 0)
817 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
820 if (LocaleCompare(keyword,"fill-rule") == 0)
822 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",value);
825 if (LocaleCompare(keyword,"fill-opacity") == 0)
827 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
831 if (LocaleCompare(keyword,"font") == 0)
834 family[MagickPathExtent],
835 size[MagickPathExtent],
836 style[MagickPathExtent];
838 if (sscanf(value,"%2048s %2048s %2048s",style,size,family) != 3)
840 if (GetUserSpaceCoordinateValue(svg_info,0,style) == 0)
841 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
844 if (sscanf(value,"%2048s %2048s",size,family) != 2)
846 (void) FormatLocaleFile(svg_info->file,"font-size \"%s\"\n",size);
847 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
851 if (LocaleCompare(keyword,"font-family") == 0)
853 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
857 if (LocaleCompare(keyword,"font-stretch") == 0)
859 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
863 if (LocaleCompare(keyword,"font-style") == 0)
865 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",value);
868 if (LocaleCompare(keyword,"font-size") == 0)
870 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
871 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
872 svg_info->pointsize);
875 if (LocaleCompare(keyword,"font-weight") == 0)
877 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
886 if (LocaleCompare(keyword,"kerning") == 0)
888 (void) FormatLocaleFile(svg_info->file,"kerning \"%s\"\n",value);
896 if (LocaleCompare(keyword,"letter-spacing") == 0)
898 (void) FormatLocaleFile(svg_info->file,"letter-spacing \"%s\"\n",
907 if (LocaleCompare(keyword,"mask") == 0)
909 (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
917 if (LocaleCompare(keyword,"offset") == 0)
919 (void) FormatLocaleFile(svg_info->file,"offset %g\n",
920 GetUserSpaceCoordinateValue(svg_info,1,value));
923 if (LocaleCompare(keyword,"opacity") == 0)
925 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
933 if (LocaleCompare(keyword,"stop-color") == 0)
935 (void) CloneString(&svg_info->stop_color,value);
938 if (LocaleCompare(keyword,"stroke") == 0)
940 if (LocaleCompare(value,"currentColor") == 0)
942 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",color);
945 if (LocaleCompare(value,"#000000ff") == 0)
946 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
948 (void) FormatLocaleFile(svg_info->file,
949 "stroke \"%s\"\n",value);
952 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
954 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
955 LocaleCompare(value,"true") == 0);
958 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
960 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
964 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
966 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
967 GetUserSpaceCoordinateValue(svg_info,1,value));
970 if (LocaleCompare(keyword,"stroke-linecap") == 0)
972 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
976 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
978 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
982 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
984 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit \"%s\"\n",
988 if (LocaleCompare(keyword,"stroke-opacity") == 0)
990 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
994 if (LocaleCompare(keyword,"stroke-width") == 0)
996 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
997 GetUserSpaceCoordinateValue(svg_info,1,value));
1005 if (LocaleCompare(keyword,"text-align") == 0)
1007 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",value);
1010 if (LocaleCompare(keyword,"text-anchor") == 0)
1012 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
1016 if (LocaleCompare(keyword,"text-decoration") == 0)
1018 if (LocaleCompare(value,"underline") == 0)
1019 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
1020 if (LocaleCompare(value,"line-through") == 0)
1021 (void) FormatLocaleFile(svg_info->file,"decorate line-through\n");
1022 if (LocaleCompare(value,"overline") == 0)
1023 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
1026 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1028 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
1029 LocaleCompare(value,"true") == 0);
1038 if (units != (char *) NULL)
1039 units=DestroyString(units);
1040 if (color != (char *) NULL)
1041 color=DestroyString(color);
1042 for (i=0; tokens[i] != (char *) NULL; i++)
1043 tokens[i]=DestroyString(tokens[i]);
1044 tokens=(char **) RelinquishMagickMemory(tokens);
1047 static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
1048 const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
1054 What to do when an unparsed entity declaration is parsed.
1056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1057 " SAX.unparsedEntityDecl(%s, %s, %s, %s)",name,
1058 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
1059 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation);
1060 svg_info=(SVGInfo *) context;
1061 (void) xmlAddDocEntity(svg_info->document,name,
1062 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
1066 static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
1072 Receive the document locator at startup, actually xmlDefaultSAXLocator.
1075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1076 " SAX.setDocumentLocator()");
1077 svg_info=(SVGInfo *) context;
1081 static void SVGStartDocument(void *context)
1090 Called when the document start being processed.
1092 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startDocument()");
1093 svg_info=(SVGInfo *) context;
1094 parser=svg_info->parser;
1095 svg_info->document=xmlNewDoc(parser->version);
1096 if (svg_info->document == (xmlDocPtr) NULL)
1098 if (parser->encoding == NULL)
1099 svg_info->document->encoding=(const xmlChar *) NULL;
1101 svg_info->document->encoding=xmlStrdup(parser->encoding);
1102 svg_info->document->standalone=parser->standalone;
1105 static void SVGEndDocument(void *context)
1111 Called when the document end has been detected.
1113 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()");
1114 svg_info=(SVGInfo *) context;
1115 if (svg_info->offset != (char *) NULL)
1116 svg_info->offset=DestroyString(svg_info->offset);
1117 if (svg_info->stop_color != (char *) NULL)
1118 svg_info->stop_color=DestroyString(svg_info->stop_color);
1119 if (svg_info->scale != (double *) NULL)
1120 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
1121 if (svg_info->text != (char *) NULL)
1122 svg_info->text=DestroyString(svg_info->text);
1123 if (svg_info->vertices != (char *) NULL)
1124 svg_info->vertices=DestroyString(svg_info->vertices);
1125 if (svg_info->url != (char *) NULL)
1126 svg_info->url=DestroyString(svg_info->url);
1127 #if defined(MAGICKCORE_XML_DELEGATE)
1128 if (svg_info->document != (xmlDocPtr) NULL)
1130 xmlFreeDoc(svg_info->document);
1131 svg_info->document=(xmlDocPtr) NULL;
1136 static void SVGStartElement(void *context,const xmlChar *name,
1137 const xmlChar **attributes)
1139 #define PushGraphicContext(id) \
1142 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); \
1144 (void) FormatLocaleFile(svg_info->file,"push graphic-context \"%s\"\n", \
1150 background[MagickPathExtent],
1151 id[MagickPathExtent],
1153 token[MagickPathExtent],
1173 Called when an opening tag has been processed.
1175 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s",
1177 svg_info=(SVGInfo *) context;
1179 svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale,
1180 svg_info->n+1UL,sizeof(*svg_info->scale));
1181 if (svg_info->scale == (double *) NULL)
1183 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
1184 ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
1187 svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
1188 color=AcquireString("none");
1189 units=AcquireString("userSpaceOnUse");
1193 value=(const char *) NULL;
1194 if ((LocaleCompare((char *) name,"image") == 0) ||
1195 (LocaleCompare((char *) name,"pattern") == 0) ||
1196 (LocaleCompare((char *) name,"rect") == 0) ||
1197 (LocaleCompare((char *) name,"text") == 0) ||
1198 (LocaleCompare((char *) name,"use") == 0))
1200 svg_info->bounds.x=0.0;
1201 svg_info->bounds.y=0.0;
1203 if (attributes != (const xmlChar **) NULL)
1204 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1206 keyword=(const char *) attributes[i];
1207 value=(const char *) attributes[i+1];
1213 if (LocaleCompare(keyword,"cx") == 0)
1215 svg_info->element.cx=
1216 GetUserSpaceCoordinateValue(svg_info,1,value);
1219 if (LocaleCompare(keyword,"cy") == 0)
1221 svg_info->element.cy=
1222 GetUserSpaceCoordinateValue(svg_info,-1,value);
1230 if (LocaleCompare(keyword,"fx") == 0)
1232 svg_info->element.major=
1233 GetUserSpaceCoordinateValue(svg_info,1,value);
1236 if (LocaleCompare(keyword,"fy") == 0)
1238 svg_info->element.minor=
1239 GetUserSpaceCoordinateValue(svg_info,-1,value);
1247 if (LocaleCompare(keyword,"height") == 0)
1249 svg_info->bounds.height=
1250 GetUserSpaceCoordinateValue(svg_info,-1,value);
1258 if (LocaleCompare(keyword,"id") == 0)
1260 (void) CopyMagickString(id,value,MagickPathExtent);
1268 if (LocaleCompare(keyword,"r") == 0)
1270 svg_info->element.angle=
1271 GetUserSpaceCoordinateValue(svg_info,0,value);
1279 if (LocaleCompare(keyword,"width") == 0)
1281 svg_info->bounds.width=
1282 GetUserSpaceCoordinateValue(svg_info,1,value);
1290 if (LocaleCompare(keyword,"x") == 0)
1292 if (LocaleCompare((char *) name,"tspan") != 0)
1293 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,
1294 value)-svg_info->center.x;
1297 if (LocaleCompare(keyword,"x1") == 0)
1299 svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
1303 if (LocaleCompare(keyword,"x2") == 0)
1305 svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
1314 if (LocaleCompare(keyword,"y") == 0)
1316 if (LocaleCompare((char *) name,"tspan") != 0)
1317 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,
1318 value)-svg_info->center.y;
1321 if (LocaleCompare(keyword,"y1") == 0)
1323 svg_info->segment.y1=GetUserSpaceCoordinateValue(svg_info,-1,
1327 if (LocaleCompare(keyword,"y2") == 0)
1329 svg_info->segment.y2=GetUserSpaceCoordinateValue(svg_info,-1,
1339 if (strchr((char *) name,':') != (char *) NULL)
1342 Skip over namespace.
1344 for ( ; *name != ':'; name++) ;
1352 if (LocaleCompare((const char *) name,"circle") == 0)
1354 PushGraphicContext(id);
1357 if (LocaleCompare((const char *) name,"clipPath") == 0)
1359 (void) FormatLocaleFile(svg_info->file,"push clip-path \"%s\"\n",id);
1367 if (LocaleCompare((const char *) name,"defs") == 0)
1369 (void) FormatLocaleFile(svg_info->file,"push defs\n");
1377 if (LocaleCompare((const char *) name,"ellipse") == 0)
1379 PushGraphicContext(id);
1387 if (LocaleCompare((const char *) name,"foreignObject") == 0)
1389 PushGraphicContext(id);
1397 if (LocaleCompare((const char *) name,"g") == 0)
1399 PushGraphicContext(id);
1407 if (LocaleCompare((const char *) name,"image") == 0)
1409 PushGraphicContext(id);
1417 if (LocaleCompare((const char *) name,"line") == 0)
1419 PushGraphicContext(id);
1422 if (LocaleCompare((const char *) name,"linearGradient") == 0)
1424 (void) FormatLocaleFile(svg_info->file,
1425 "push gradient \"%s\" linear %g,%g %g,%g\n",id,
1426 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
1427 svg_info->segment.y2);
1435 if (LocaleCompare((const char *) name,"mask") == 0)
1437 (void) FormatLocaleFile(svg_info->file,"push mask \"%s\"\n",id);
1445 if (LocaleCompare((const char *) name,"path") == 0)
1447 PushGraphicContext(id);
1450 if (LocaleCompare((const char *) name,"pattern") == 0)
1452 (void) FormatLocaleFile(svg_info->file,
1453 "push pattern \"%s\" %g,%g %g,%g\n",id,
1454 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
1455 svg_info->bounds.height);
1458 if (LocaleCompare((const char *) name,"polygon") == 0)
1460 PushGraphicContext(id);
1463 if (LocaleCompare((const char *) name,"polyline") == 0)
1465 PushGraphicContext(id);
1473 if (LocaleCompare((const char *) name,"radialGradient") == 0)
1475 (void) FormatLocaleFile(svg_info->file,
1476 "push gradient \"%s\" radial %g,%g %g,%g %g\n",
1477 id,svg_info->element.cx,svg_info->element.cy,
1478 svg_info->element.major,svg_info->element.minor,
1479 svg_info->element.angle);
1482 if (LocaleCompare((const char *) name,"rect") == 0)
1484 PushGraphicContext(id);
1492 if (LocaleCompare((char *) name,"style") == 0)
1494 if (LocaleCompare((const char *) name,"svg") == 0)
1496 svg_info->svgDepth++;
1497 PushGraphicContext(id);
1498 (void) FormatLocaleFile(svg_info->file,"compliance \"SVG\"\n");
1499 (void) FormatLocaleFile(svg_info->file,"fill \"black\"\n");
1500 (void) FormatLocaleFile(svg_info->file,"fill-opacity 1\n");
1501 (void) FormatLocaleFile(svg_info->file,"stroke \"none\"\n");
1502 (void) FormatLocaleFile(svg_info->file,"stroke-width 1\n");
1503 (void) FormatLocaleFile(svg_info->file,"stroke-opacity 1\n");
1504 (void) FormatLocaleFile(svg_info->file,"fill-rule nonzero\n");
1507 if (LocaleCompare((const char *) name,"symbol") == 0)
1509 (void) FormatLocaleFile(svg_info->file,"push symbol\n");
1517 if (LocaleCompare((const char *) name,"text") == 0)
1519 PushGraphicContext(id);
1520 (void) FormatLocaleFile(svg_info->file,"class \"text\"\n");
1521 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1522 svg_info->bounds.x,svg_info->bounds.y);
1523 svg_info->center.x=svg_info->bounds.x;
1524 svg_info->center.y=svg_info->bounds.y;
1525 svg_info->bounds.x=0.0;
1526 svg_info->bounds.y=0.0;
1527 svg_info->bounds.width=0.0;
1528 svg_info->bounds.height=0.0;
1531 if (LocaleCompare((const char *) name,"tspan") == 0)
1533 if (*svg_info->text != '\0')
1538 text=EscapeString(svg_info->text,'\'');
1539 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
1540 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1541 svg_info->center.y,text);
1542 text=DestroyString(text);
1543 *svg_info->text='\0';
1545 PushGraphicContext(id);
1553 if (LocaleCompare((char *) name,"use") == 0)
1555 PushGraphicContext(id);
1563 if (attributes != (const xmlChar **) NULL)
1564 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1566 keyword=(const char *) attributes[i];
1567 value=(const char *) attributes[i+1];
1568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1569 " %s = %s",keyword,value);
1575 if (LocaleCompare(keyword,"angle") == 0)
1577 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1578 GetUserSpaceCoordinateValue(svg_info,0,value));
1586 if (LocaleCompare(keyword,"class") == 0)
1593 GetNextToken(p,&p,MagickPathExtent,token);
1595 GetNextToken(p,&p,MagickPathExtent,token);
1598 (void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",
1605 if (LocaleCompare(keyword,"clip-path") == 0)
1607 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",
1611 if (LocaleCompare(keyword,"clip-rule") == 0)
1613 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",
1617 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1619 (void) CloneString(&units,value);
1620 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
1624 if (LocaleCompare(keyword,"color") == 0)
1626 (void) CloneString(&color,value);
1629 if (LocaleCompare(keyword,"cx") == 0)
1631 svg_info->element.cx=
1632 GetUserSpaceCoordinateValue(svg_info,1,value);
1635 if (LocaleCompare(keyword,"cy") == 0)
1637 svg_info->element.cy=
1638 GetUserSpaceCoordinateValue(svg_info,-1,value);
1646 if (LocaleCompare(keyword,"d") == 0)
1648 (void) CloneString(&svg_info->vertices,value);
1651 if (LocaleCompare(keyword,"dx") == 0)
1656 dx=GetUserSpaceCoordinateValue(svg_info,1,value);
1657 svg_info->bounds.x+=dx;
1658 if (LocaleCompare((char *) name,"text") == 0)
1659 (void) FormatLocaleFile(svg_info->file,"translate %g,0.0\n",dx);
1662 if (LocaleCompare(keyword,"dy") == 0)
1667 dy=GetUserSpaceCoordinateValue(svg_info,-1,value);
1668 svg_info->bounds.y+=dy;
1669 if (LocaleCompare((char *) name,"text") == 0)
1670 (void) FormatLocaleFile(svg_info->file,"translate 0.0,%g\n",dy);
1678 if (LocaleCompare(keyword,"fill") == 0)
1680 if (LocaleCompare(value,"currentColor") == 0)
1682 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
1685 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1688 if (LocaleCompare(keyword,"fillcolor") == 0)
1690 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1693 if (LocaleCompare(keyword,"fill-rule") == 0)
1695 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",
1699 if (LocaleCompare(keyword,"fill-opacity") == 0)
1701 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
1705 if (LocaleCompare(keyword,"font-family") == 0)
1707 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
1711 if (LocaleCompare(keyword,"font-stretch") == 0)
1713 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
1717 if (LocaleCompare(keyword,"font-style") == 0)
1719 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
1723 if (LocaleCompare(keyword,"font-size") == 0)
1725 if (LocaleCompare(value,"xx-small") == 0)
1726 svg_info->pointsize=6.144;
1727 else if (LocaleCompare(value,"x-small") == 0)
1728 svg_info->pointsize=7.68;
1729 else if (LocaleCompare(value,"small") == 0)
1730 svg_info->pointsize=9.6;
1731 else if (LocaleCompare(value,"medium") == 0)
1732 svg_info->pointsize=12.0;
1733 else if (LocaleCompare(value,"large") == 0)
1734 svg_info->pointsize=14.4;
1735 else if (LocaleCompare(value,"x-large") == 0)
1736 svg_info->pointsize=17.28;
1737 else if (LocaleCompare(value,"xx-large") == 0)
1738 svg_info->pointsize=20.736;
1740 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,
1742 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1743 svg_info->pointsize);
1746 if (LocaleCompare(keyword,"font-weight") == 0)
1748 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
1757 if (LocaleCompare(keyword,"gradientTransform") == 0)
1764 GetAffineMatrix(&transform);
1765 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1766 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
1767 if (tokens == (char **) NULL)
1769 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
1771 keyword=(char *) tokens[j];
1772 if (keyword == (char *) NULL)
1774 value=(char *) tokens[j+1];
1775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1776 " %s: %s",keyword,value);
1778 GetAffineMatrix(&affine);
1784 if (LocaleCompare(keyword,"matrix") == 0)
1786 p=(const char *) value;
1787 GetNextToken(p,&p,MagickPathExtent,token);
1788 affine.sx=StringToDouble(value,(char **) NULL);
1789 GetNextToken(p,&p,MagickPathExtent,token);
1791 GetNextToken(p,&p,MagickPathExtent,token);
1792 affine.rx=StringToDouble(token,&next_token);
1793 GetNextToken(p,&p,MagickPathExtent,token);
1795 GetNextToken(p,&p,MagickPathExtent,token);
1796 affine.ry=StringToDouble(token,&next_token);
1797 GetNextToken(p,&p,MagickPathExtent,token);
1799 GetNextToken(p,&p,MagickPathExtent,token);
1800 affine.sy=StringToDouble(token,&next_token);
1801 GetNextToken(p,&p,MagickPathExtent,token);
1803 GetNextToken(p,&p,MagickPathExtent,token);
1804 affine.tx=StringToDouble(token,&next_token);
1805 GetNextToken(p,&p,MagickPathExtent,token);
1807 GetNextToken(p,&p,MagickPathExtent,token);
1808 affine.ty=StringToDouble(token,&next_token);
1816 if (LocaleCompare(keyword,"rotate") == 0)
1821 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1822 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1823 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1824 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1825 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1833 if (LocaleCompare(keyword,"scale") == 0)
1835 for (p=(const char *) value; *p != '\0'; p++)
1836 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1839 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1840 affine.sy=affine.sx;
1843 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1844 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1847 if (LocaleCompare(keyword,"skewX") == 0)
1849 affine.sx=svg_info->affine.sx;
1850 affine.ry=tan(DegreesToRadians(fmod(
1851 GetUserSpaceCoordinateValue(svg_info,1,value),
1853 affine.sy=svg_info->affine.sy;
1856 if (LocaleCompare(keyword,"skewY") == 0)
1858 affine.sx=svg_info->affine.sx;
1859 affine.rx=tan(DegreesToRadians(fmod(
1860 GetUserSpaceCoordinateValue(svg_info,-1,value),
1862 affine.sy=svg_info->affine.sy;
1870 if (LocaleCompare(keyword,"translate") == 0)
1872 for (p=(const char *) value; *p != '\0'; p++)
1873 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1876 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1877 affine.ty=affine.tx;
1880 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1888 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1889 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1890 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1891 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1892 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1894 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1897 (void) FormatLocaleFile(svg_info->file,
1898 "affine %g %g %g %g %g %g\n",transform.sx,
1899 transform.rx,transform.ry,transform.sy,transform.tx,
1901 for (j=0; tokens[j] != (char *) NULL; j++)
1902 tokens[j]=DestroyString(tokens[j]);
1903 tokens=(char **) RelinquishMagickMemory(tokens);
1906 if (LocaleCompare(keyword,"gradientUnits") == 0)
1908 (void) CloneString(&units,value);
1909 (void) FormatLocaleFile(svg_info->file,"gradient-units \"%s\"\n",
1918 if (LocaleCompare(keyword,"height") == 0)
1920 svg_info->bounds.height=
1921 GetUserSpaceCoordinateValue(svg_info,-1,value);
1924 if (LocaleCompare(keyword,"href") == 0)
1926 (void) CloneString(&svg_info->url,value);
1934 if (LocaleCompare(keyword,"kerning") == 0)
1936 (void) FormatLocaleFile(svg_info->file,"kerning \"%s\"\n",
1945 if (LocaleCompare(keyword,"letter-spacing") == 0)
1947 (void) FormatLocaleFile(svg_info->file,"letter-spacing \"%s\"\n",
1956 if (LocaleCompare(keyword,"major") == 0)
1958 svg_info->element.major=
1959 GetUserSpaceCoordinateValue(svg_info,1,value);
1962 if (LocaleCompare(keyword,"mask") == 0)
1964 (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
1967 if (LocaleCompare(keyword,"minor") == 0)
1969 svg_info->element.minor=
1970 GetUserSpaceCoordinateValue(svg_info,-1,value);
1978 if (LocaleCompare(keyword,"offset") == 0)
1980 (void) CloneString(&svg_info->offset,value);
1983 if (LocaleCompare(keyword,"opacity") == 0)
1985 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
1993 if (LocaleCompare(keyword,"path") == 0)
1995 (void) CloneString(&svg_info->url,value);
1998 if (LocaleCompare(keyword,"points") == 0)
2000 (void) CloneString(&svg_info->vertices,value);
2008 if (LocaleCompare(keyword,"r") == 0)
2010 svg_info->element.major=
2011 GetUserSpaceCoordinateValue(svg_info,1,value);
2012 svg_info->element.minor=
2013 GetUserSpaceCoordinateValue(svg_info,-1,value);
2016 if (LocaleCompare(keyword,"rotate") == 0)
2021 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
2022 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2023 svg_info->bounds.x,svg_info->bounds.y);
2024 svg_info->bounds.x=0;
2025 svg_info->bounds.y=0;
2026 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
2029 if (LocaleCompare(keyword,"rx") == 0)
2031 if (LocaleCompare((const char *) name,"ellipse") == 0)
2032 svg_info->element.major=
2033 GetUserSpaceCoordinateValue(svg_info,1,value);
2036 GetUserSpaceCoordinateValue(svg_info,1,value);
2039 if (LocaleCompare(keyword,"ry") == 0)
2041 if (LocaleCompare((const char *) name,"ellipse") == 0)
2042 svg_info->element.minor=
2043 GetUserSpaceCoordinateValue(svg_info,-1,value);
2046 GetUserSpaceCoordinateValue(svg_info,-1,value);
2054 if (LocaleCompare(keyword,"stop-color") == 0)
2056 (void) CloneString(&svg_info->stop_color,value);
2059 if (LocaleCompare(keyword,"stroke") == 0)
2061 if (LocaleCompare(value,"currentColor") == 0)
2063 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",
2067 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",value);
2070 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
2072 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
2073 LocaleCompare(value,"true") == 0);
2076 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
2078 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
2082 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
2084 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
2085 GetUserSpaceCoordinateValue(svg_info,1,value));
2088 if (LocaleCompare(keyword,"stroke-linecap") == 0)
2090 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
2094 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
2096 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
2100 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
2102 (void) FormatLocaleFile(svg_info->file,
2103 "stroke-miterlimit \"%s\"\n",value);
2106 if (LocaleCompare(keyword,"stroke-opacity") == 0)
2108 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
2112 if (LocaleCompare(keyword,"stroke-width") == 0)
2114 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
2115 GetUserSpaceCoordinateValue(svg_info,1,value));
2118 if (LocaleCompare(keyword,"style") == 0)
2120 SVGProcessStyleElement(context,name,value);
2128 if (LocaleCompare(keyword,"text-align") == 0)
2130 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",
2134 if (LocaleCompare(keyword,"text-anchor") == 0)
2136 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
2140 if (LocaleCompare(keyword,"text-decoration") == 0)
2142 if (LocaleCompare(value,"underline") == 0)
2143 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
2144 if (LocaleCompare(value,"line-through") == 0)
2145 (void) FormatLocaleFile(svg_info->file,
2146 "decorate line-through\n");
2147 if (LocaleCompare(value,"overline") == 0)
2148 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
2151 if (LocaleCompare(keyword,"text-antialiasing") == 0)
2153 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
2154 LocaleCompare(value,"true") == 0);
2157 if (LocaleCompare(keyword,"transform") == 0)
2164 GetAffineMatrix(&transform);
2165 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
2166 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
2167 if (tokens == (char **) NULL)
2169 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2171 keyword=(char *) tokens[j];
2172 value=(char *) tokens[j+1];
2173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2174 " %s: %s",keyword,value);
2176 GetAffineMatrix(&affine);
2182 if (LocaleCompare(keyword,"matrix") == 0)
2184 p=(const char *) value;
2185 GetNextToken(p,&p,MagickPathExtent,token);
2186 affine.sx=StringToDouble(value,(char **) NULL);
2187 GetNextToken(p,&p,MagickPathExtent,token);
2189 GetNextToken(p,&p,MagickPathExtent,token);
2190 affine.rx=StringToDouble(token,&next_token);
2191 GetNextToken(p,&p,MagickPathExtent,token);
2193 GetNextToken(p,&p,MagickPathExtent,token);
2194 affine.ry=StringToDouble(token,&next_token);
2195 GetNextToken(p,&p,MagickPathExtent,token);
2197 GetNextToken(p,&p,MagickPathExtent,token);
2198 affine.sy=StringToDouble(token,&next_token);
2199 GetNextToken(p,&p,MagickPathExtent,token);
2201 GetNextToken(p,&p,MagickPathExtent,token);
2202 affine.tx=StringToDouble(token,&next_token);
2203 GetNextToken(p,&p,MagickPathExtent,token);
2205 GetNextToken(p,&p,MagickPathExtent,token);
2206 affine.ty=StringToDouble(token,&next_token);
2214 if (LocaleCompare(keyword,"rotate") == 0)
2221 p=(const char *) value;
2222 GetNextToken(p,&p,MagickPathExtent,token);
2223 angle=StringToDouble(value,(char **) NULL);
2224 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
2225 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
2226 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
2227 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
2228 GetNextToken(p,&p,MagickPathExtent,token);
2230 GetNextToken(p,&p,MagickPathExtent,token);
2231 x=StringToDouble(token,&next_token);
2232 GetNextToken(p,&p,MagickPathExtent,token);
2234 GetNextToken(p,&p,MagickPathExtent,token);
2235 y=StringToDouble(token,&next_token);
2236 affine.tx=svg_info->bounds.x+x*
2237 cos(DegreesToRadians(fmod(angle,360.0)))+y*
2238 sin(DegreesToRadians(fmod(angle,360.0)));
2239 affine.ty=svg_info->bounds.y-x*
2240 sin(DegreesToRadians(fmod(angle,360.0)))+y*
2241 cos(DegreesToRadians(fmod(angle,360.0)));
2251 if (LocaleCompare(keyword,"scale") == 0)
2253 for (p=(const char *) value; *p != '\0'; p++)
2254 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2257 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
2258 affine.sy=affine.sx;
2260 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
2262 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
2265 if (LocaleCompare(keyword,"skewX") == 0)
2267 affine.sx=svg_info->affine.sx;
2268 affine.ry=tan(DegreesToRadians(fmod(
2269 GetUserSpaceCoordinateValue(svg_info,1,value),
2271 affine.sy=svg_info->affine.sy;
2274 if (LocaleCompare(keyword,"skewY") == 0)
2276 affine.sx=svg_info->affine.sx;
2277 affine.rx=tan(DegreesToRadians(fmod(
2278 GetUserSpaceCoordinateValue(svg_info,-1,value),
2280 affine.sy=svg_info->affine.sy;
2288 if (LocaleCompare(keyword,"translate") == 0)
2290 for (p=(const char *) value; *p != '\0'; p++)
2291 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2294 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2297 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2306 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2307 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2308 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2309 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2310 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2312 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2315 (void) FormatLocaleFile(svg_info->file,
2316 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2317 transform.ry,transform.sy,transform.tx,transform.ty);
2318 for (j=0; tokens[j] != (char *) NULL; j++)
2319 tokens[j]=DestroyString(tokens[j]);
2320 tokens=(char **) RelinquishMagickMemory(tokens);
2328 if (LocaleCompare(keyword,"verts") == 0)
2330 (void) CloneString(&svg_info->vertices,value);
2333 if (LocaleCompare(keyword,"viewBox") == 0)
2335 p=(const char *) value;
2336 GetNextToken(p,&p,MagickPathExtent,token);
2337 svg_info->view_box.x=StringToDouble(token,&next_token);
2338 GetNextToken(p,&p,MagickPathExtent,token);
2340 GetNextToken(p,&p,MagickPathExtent,token);
2341 svg_info->view_box.y=StringToDouble(token,&next_token);
2342 GetNextToken(p,&p,MagickPathExtent,token);
2344 GetNextToken(p,&p,MagickPathExtent,token);
2345 svg_info->view_box.width=StringToDouble(token,
2347 if (svg_info->bounds.width == 0)
2348 svg_info->bounds.width=svg_info->view_box.width;
2349 GetNextToken(p,&p,MagickPathExtent,token);
2351 GetNextToken(p,&p,MagickPathExtent,token);
2352 svg_info->view_box.height=StringToDouble(token,
2354 if (svg_info->bounds.height == 0)
2355 svg_info->bounds.height=svg_info->view_box.height;
2363 if (LocaleCompare(keyword,"width") == 0)
2365 svg_info->bounds.width=
2366 GetUserSpaceCoordinateValue(svg_info,1,value);
2374 if (LocaleCompare(keyword,"x") == 0)
2376 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2379 if (LocaleCompare(keyword,"xlink:href") == 0)
2381 (void) CloneString(&svg_info->url,value);
2384 if (LocaleCompare(keyword,"x1") == 0)
2386 svg_info->segment.x1=
2387 GetUserSpaceCoordinateValue(svg_info,1,value);
2390 if (LocaleCompare(keyword,"x2") == 0)
2392 svg_info->segment.x2=
2393 GetUserSpaceCoordinateValue(svg_info,1,value);
2401 if (LocaleCompare(keyword,"y") == 0)
2403 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2406 if (LocaleCompare(keyword,"y1") == 0)
2408 svg_info->segment.y1=
2409 GetUserSpaceCoordinateValue(svg_info,-1,value);
2412 if (LocaleCompare(keyword,"y2") == 0)
2414 svg_info->segment.y2=
2415 GetUserSpaceCoordinateValue(svg_info,-1,value);
2424 if (LocaleCompare((const char *) name,"svg") == 0)
2426 if (svg_info->document->encoding != (const xmlChar *) NULL)
2427 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2428 (const char *) svg_info->document->encoding);
2429 if (attributes != (const xmlChar **) NULL)
2437 if ((svg_info->view_box.width == 0.0) ||
2438 (svg_info->view_box.height == 0.0))
2439 svg_info->view_box=svg_info->bounds;
2441 if (svg_info->bounds.width > 0.0)
2442 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2444 if (svg_info->bounds.height > 0.0)
2445 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2446 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2447 (double) svg_info->width,(double) svg_info->height);
2448 sx=(double) svg_info->width/svg_info->view_box.width;
2449 sy=(double) svg_info->height/svg_info->view_box.height;
2450 tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2452 ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2454 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2456 if ((svg_info->svgDepth == 1) && (*background != '\0'))
2458 PushGraphicContext(id);
2459 (void) FormatLocaleFile(svg_info->file,"fill %s\n",background);
2460 (void) FormatLocaleFile(svg_info->file,
2461 "rectangle 0,0 %g,%g\n",svg_info->view_box.width,
2462 svg_info->view_box.height);
2463 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2467 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2468 if (units != (char *) NULL)
2469 units=DestroyString(units);
2470 if (color != (char *) NULL)
2471 color=DestroyString(color);
2474 static void SVGEndElement(void *context,const xmlChar *name)
2480 Called when the end of an element has been detected.
2482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2483 " SAX.endElement(%s)",name);
2484 svg_info=(SVGInfo *) context;
2485 if (strchr((char *) name,':') != (char *) NULL)
2488 Skip over namespace.
2490 for ( ; *name != ':'; name++) ;
2498 if (LocaleCompare((const char *) name,"circle") == 0)
2500 (void) FormatLocaleFile(svg_info->file,"class \"circle\"\n");
2501 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2502 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2503 svg_info->element.cy+svg_info->element.minor);
2504 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2507 if (LocaleCompare((const char *) name,"clipPath") == 0)
2509 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2517 if (LocaleCompare((const char *) name,"defs") == 0)
2519 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2522 if (LocaleCompare((const char *) name,"desc") == 0)
2527 if (*svg_info->text == '\0')
2529 (void) fputc('#',svg_info->file);
2530 for (p=svg_info->text; *p != '\0'; p++)
2532 (void) fputc(*p,svg_info->file);
2534 (void) fputc('#',svg_info->file);
2536 (void) fputc('\n',svg_info->file);
2537 *svg_info->text='\0';
2545 if (LocaleCompare((const char *) name,"ellipse") == 0)
2550 (void) FormatLocaleFile(svg_info->file,"class \"ellipse\"\n");
2551 angle=svg_info->element.angle;
2552 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2553 svg_info->element.cx,svg_info->element.cy,
2554 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2555 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2556 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2564 if (LocaleCompare((const char *) name,"foreignObject") == 0)
2566 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2574 if (LocaleCompare((const char *) name,"g") == 0)
2576 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2584 if (LocaleCompare((const char *) name,"image") == 0)
2586 (void) FormatLocaleFile(svg_info->file,
2587 "image Over %g,%g %g,%g \"%s\"\n",svg_info->bounds.x,
2588 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2590 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2598 if (LocaleCompare((const char *) name,"line") == 0)
2600 (void) FormatLocaleFile(svg_info->file,"class \"line\"\n");
2601 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2602 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2603 svg_info->segment.y2);
2604 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2607 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2609 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2617 if (LocaleCompare((const char *) name,"mask") == 0)
2619 (void) FormatLocaleFile(svg_info->file,"pop mask\n");
2627 if (LocaleCompare((const char *) name,"pattern") == 0)
2629 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2632 if (LocaleCompare((const char *) name,"path") == 0)
2634 (void) FormatLocaleFile(svg_info->file,"class \"path\"\n");
2635 (void) FormatLocaleFile(svg_info->file,"path \"%s\"\n",
2636 svg_info->vertices);
2637 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2640 if (LocaleCompare((const char *) name,"polygon") == 0)
2642 (void) FormatLocaleFile(svg_info->file,"class \"polygon\"\n");
2643 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2644 svg_info->vertices);
2645 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2648 if (LocaleCompare((const char *) name,"polyline") == 0)
2650 (void) FormatLocaleFile(svg_info->file,"class \"polyline\"\n");
2651 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2652 svg_info->vertices);
2653 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2661 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2663 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2666 if (LocaleCompare((const char *) name,"rect") == 0)
2668 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2670 (void) FormatLocaleFile(svg_info->file,"class \"rect\"\n");
2671 if ((fabs(svg_info->bounds.width-1.0) < MagickEpsilon) &&
2672 (fabs(svg_info->bounds.height-1.0) < MagickEpsilon))
2673 (void) FormatLocaleFile(svg_info->file,"point %g,%g\n",
2674 svg_info->bounds.x,svg_info->bounds.y);
2676 (void) FormatLocaleFile(svg_info->file,
2677 "rectangle %g,%g %g,%g\n",svg_info->bounds.x,
2678 svg_info->bounds.y,svg_info->bounds.x+svg_info->bounds.width,
2679 svg_info->bounds.y+svg_info->bounds.height);
2680 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2683 if (svg_info->radius.x == 0.0)
2684 svg_info->radius.x=svg_info->radius.y;
2685 if (svg_info->radius.y == 0.0)
2686 svg_info->radius.y=svg_info->radius.x;
2687 (void) FormatLocaleFile(svg_info->file,
2688 "roundRectangle %g,%g %g,%g %g,%g\n",
2689 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2690 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2691 svg_info->radius.x,svg_info->radius.y);
2692 svg_info->radius.x=0.0;
2693 svg_info->radius.y=0.0;
2694 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2702 if (LocaleCompare((const char *) name,"stop") == 0)
2704 (void) FormatLocaleFile(svg_info->file,"stop-color \"%s\" %s\n",
2705 svg_info->stop_color,svg_info->offset);
2708 if (LocaleCompare((char *) name,"style") == 0)
2722 Find style definitions in svg_info->text.
2724 tokens=SVGKeyValuePairs(context,'{','}',svg_info->text,
2726 if (tokens == (char **) NULL)
2728 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2730 keyword=(char *) tokens[j];
2731 value=(char *) tokens[j+1];
2732 (void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
2733 *keyword == '.' ? keyword+1 : keyword);
2734 SVGProcessStyleElement(context,name,value);
2735 (void) FormatLocaleFile(svg_info->file,"pop class\n");
2739 if (LocaleCompare((const char *) name,"svg") == 0)
2741 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2742 svg_info->svgDepth--;
2745 if (LocaleCompare((const char *) name,"symbol") == 0)
2747 (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
2755 if (LocaleCompare((const char *) name,"text") == 0)
2757 if (*svg_info->text != '\0')
2762 SVGStripString(MagickTrue,svg_info->text);
2763 text=EscapeString(svg_info->text,'\'');
2764 (void) FormatLocaleFile(svg_info->file,"text 0,0 \"%s\"\n",text);
2765 text=DestroyString(text);
2766 *svg_info->text='\0';
2767 svg_info->center.x=0.0;
2768 svg_info->center.y=0.0;
2770 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2773 if (LocaleCompare((const char *) name,"tspan") == 0)
2775 if (*svg_info->text != '\0')
2780 (void) FormatLocaleFile(svg_info->file,"class \"tspan\"\n");
2781 text=EscapeString(svg_info->text,'\'');
2782 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2783 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
2784 svg_info->center.y,text);
2785 text=DestroyString(text);
2786 *svg_info->text='\0';
2788 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2791 if (LocaleCompare((const char *) name,"title") == 0)
2793 if (*svg_info->text == '\0')
2795 (void) CloneString(&svg_info->title,svg_info->text);
2796 *svg_info->text='\0';
2804 if (LocaleCompare((char *) name,"use") == 0)
2806 if ((svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0))
2807 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2808 svg_info->bounds.x,svg_info->bounds.y);
2809 (void) FormatLocaleFile(svg_info->file,"use \"url(%s)\"\n",
2811 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2819 *svg_info->text='\0';
2820 (void) memset(&svg_info->element,0,sizeof(svg_info->element));
2821 (void) memset(&svg_info->segment,0,sizeof(svg_info->segment));
2825 static void SVGCharacters(void *context,const xmlChar *c,int length)
2840 Receiving some characters from the parser.
2842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2843 " SAX.characters(%s,%.20g)",c,(double) length);
2844 svg_info=(SVGInfo *) context;
2845 text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2846 if (text == (char *) NULL)
2849 for (i=0; i < (ssize_t) length; i++)
2852 SVGStripString(MagickFalse,text);
2853 if (svg_info->text == (char *) NULL)
2854 svg_info->text=text;
2857 (void) ConcatenateString(&svg_info->text,text);
2858 text=DestroyString(text);
2862 static void SVGReference(void *context,const xmlChar *name)
2871 Called when an entity reference is detected.
2873 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2875 svg_info=(SVGInfo *) context;
2876 parser=svg_info->parser;
2877 if (parser == (xmlParserCtxtPtr) NULL)
2879 if (parser->node == (xmlNodePtr) NULL)
2882 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2884 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2887 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2893 Receiving some ignorable whitespaces from the parser.
2895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2896 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2897 svg_info=(SVGInfo *) context;
2901 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2902 const xmlChar *data)
2908 A processing instruction has been parsed.
2910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2911 " SAX.processingInstruction(%s, %s)",target,data);
2912 svg_info=(SVGInfo *) context;
2916 static void SVGComment(void *context,const xmlChar *value)
2922 A comment has been parsed.
2924 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
2926 svg_info=(SVGInfo *) context;
2927 if (svg_info->comment != (char *) NULL)
2928 (void) ConcatenateString(&svg_info->comment,"\n");
2929 (void) ConcatenateString(&svg_info->comment,(const char *) value);
2932 static void SVGWarning(void *context,const char *format,...)
2936 reason[MagickPathExtent];
2945 Display and format a warning messages, gives file, line, position and
2948 va_start(operands,format);
2949 svg_info=(SVGInfo *) context;
2950 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
2951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2952 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2953 (void) vsprintf(reason,format,operands);
2955 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2957 message=GetExceptionMessage(errno);
2958 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2959 DelegateWarning,reason,"`%s`",message);
2960 message=DestroyString(message);
2964 static void SVGError(void *context,const char *format,...)
2968 reason[MagickPathExtent];
2977 Display and format a error formats, gives file, line, position and
2980 va_start(operands,format);
2981 svg_info=(SVGInfo *) context;
2982 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
2983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2984 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2985 (void) vsprintf(reason,format,operands);
2987 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2989 message=GetExceptionMessage(errno);
2990 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2991 reason,"`%s`",message);
2992 message=DestroyString(message);
2996 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
3008 Called when a pcdata block has been parsed.
3010 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
3012 svg_info=(SVGInfo *) context;
3013 parser=svg_info->parser;
3014 child=xmlGetLastChild(parser->node);
3015 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
3017 xmlTextConcat(child,value,length);
3020 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
3023 static void SVGExternalSubset(void *context,const xmlChar *name,
3024 const xmlChar *external_id,const xmlChar *system_id)
3039 Does this document has an external subset?
3041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3042 " SAX.externalSubset(%s, %s, %s)",name,
3043 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
3044 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
3045 svg_info=(SVGInfo *) context;
3046 parser=svg_info->parser;
3047 if (((external_id == NULL) && (system_id == NULL)) ||
3048 ((parser->validate == 0) || (parser->wellFormed == 0) ||
3049 (svg_info->document == 0)))
3051 input=SVGResolveEntity(context,external_id,system_id);
3054 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
3055 parser_context=(*parser);
3056 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
3057 if (parser->inputTab == (xmlParserInputPtr *) NULL)
3059 parser->errNo=XML_ERR_NO_MEMORY;
3060 parser->input=parser_context.input;
3061 parser->inputNr=parser_context.inputNr;
3062 parser->inputMax=parser_context.inputMax;
3063 parser->inputTab=parser_context.inputTab;
3069 xmlPushInput(parser,input);
3070 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
3071 if (input->filename == (char *) NULL)
3072 input->filename=(char *) xmlStrdup(system_id);
3075 input->base=parser->input->cur;
3076 input->cur=parser->input->cur;
3078 xmlParseExternalSubset(parser,external_id,system_id);
3079 while (parser->inputNr > 1)
3080 (void) xmlPopInput(parser);
3081 xmlFreeInputStream(parser->input);
3082 xmlFree(parser->inputTab);
3083 parser->input=parser_context.input;
3084 parser->inputNr=parser_context.inputNr;
3085 parser->inputMax=parser_context.inputMax;
3086 parser->inputTab=parser_context.inputTab;
3089 #if defined(__cplusplus) || defined(c_plusplus)
3094 Static declarations.
3097 SVGDensityGeometry[] = MagickStringify(SVGDensity) "x" MagickStringify(SVGDensity);
3099 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3102 filename[MagickPathExtent];
3122 message[MagickPathExtent];
3133 assert(image_info != (const ImageInfo *) NULL);
3134 assert(image_info->signature == MagickCoreSignature);
3135 assert(exception != (ExceptionInfo *) NULL);
3136 if (image_info->debug != MagickFalse)
3137 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3138 image_info->filename);
3139 assert(exception->signature == MagickCoreSignature);
3140 image=AcquireImage(image_info,exception);
3141 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3142 if (status == MagickFalse)
3144 image=DestroyImageList(image);
3145 return((Image *) NULL);
3147 if ((fabs(image->resolution.x) < MagickEpsilon) ||
3148 (fabs(image->resolution.y) < MagickEpsilon))
3156 flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3157 image->resolution.x=geometry_info.rho;
3158 image->resolution.y=geometry_info.sigma;
3159 if ((flags & SigmaValue) == 0)
3160 image->resolution.y=image->resolution.x;
3162 if (LocaleCompare(image_info->magick,"MSVG") != 0)
3167 delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception);
3168 if (delegate_info != (const DelegateInfo *) NULL)
3171 background[MagickPathExtent],
3172 command[MagickPathExtent],
3174 input_filename[MagickPathExtent],
3175 opacity[MagickPathExtent],
3176 output_filename[MagickPathExtent],
3177 unique[MagickPathExtent];
3186 Our best hope for compliance with the SVG standard.
3188 status=AcquireUniqueSymbolicLink(image->filename,input_filename);
3189 (void) AcquireUniqueFilename(output_filename);
3190 (void) AcquireUniqueFilename(unique);
3191 density=AcquireString("");
3192 (void) FormatLocaleString(density,MagickPathExtent,"%.20g,%.20g",
3193 image->resolution.x,image->resolution.y);
3194 (void) FormatLocaleString(background,MagickPathExtent,
3195 "rgb(%.20g%%,%.20g%%,%.20g%%)",
3196 100.0*QuantumScale*image->background_color.red,
3197 100.0*QuantumScale*image->background_color.green,
3198 100.0*QuantumScale*image->background_color.blue);
3199 (void) FormatLocaleString(opacity,MagickPathExtent,"%.20g",
3200 QuantumScale*image->background_color.alpha);
3201 (void) FormatLocaleString(command,MagickPathExtent,
3202 GetDelegateCommands(delegate_info),input_filename,output_filename,
3203 density,background,opacity,unique);
3204 density=DestroyString(density);
3205 status=ExternalDelegateCommand(MagickFalse,image_info->verbose,
3206 command,(char *) NULL,exception);
3207 (void) RelinquishUniqueFileResource(unique);
3208 (void) RelinquishUniqueFileResource(input_filename);
3209 if ((status == 0) && (stat(output_filename,&attributes) == 0) &&
3210 (attributes.st_size > 0))
3218 read_info=CloneImageInfo(image_info);
3219 (void) CopyMagickString(read_info->filename,output_filename,
3221 svg_image=ReadImage(read_info,exception);
3222 read_info=DestroyImageInfo(read_info);
3223 (void) RelinquishUniqueFileResource(output_filename);
3224 if (svg_image != (Image *) NULL)
3226 for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; )
3228 (void) CopyMagickString(next->filename,image->filename,
3230 (void) CopyMagickString(next->magick,image->magick,
3232 next=GetNextImageInList(next);
3234 image=DestroyImage(image);
3238 (void) RelinquishUniqueFileResource(output_filename);
3241 #if defined(MAGICKCORE_RSVG_DELEGATE)
3242 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3255 register unsigned char
3268 register const guchar
3290 svg_handle=rsvg_handle_new();
3291 if (svg_handle == (RsvgHandle *) NULL)
3292 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3293 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
3294 if ((fabs(image->resolution.x) > MagickEpsilon) &&
3295 (fabs(image->resolution.y) > MagickEpsilon))
3296 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3297 image->resolution.y);
3298 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3301 error=(GError *) NULL;
3302 (void) rsvg_handle_write(svg_handle,message,n,&error);
3303 if (error != (GError *) NULL)
3304 g_error_free(error);
3306 error=(GError *) NULL;
3307 rsvg_handle_close(svg_handle,&error);
3308 if (error != (GError *) NULL)
3309 g_error_free(error);
3310 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3311 apply_density=MagickTrue;
3312 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
3313 if ((image->resolution.x > 0.0) && (image->resolution.y > 0.0))
3319 We should not apply the density when the internal 'factor' is 'i'.
3320 This can be checked by using the trick below.
3322 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x*256,
3323 image->resolution.y*256);
3324 rsvg_handle_get_dimensions(svg_handle,&dpi_dimension_info);
3325 if ((dpi_dimension_info.width != dimension_info.width) ||
3326 (dpi_dimension_info.height != dimension_info.height))
3327 apply_density=MagickFalse;
3328 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3329 image->resolution.y);
3331 if (image_info->size != (char *) NULL)
3333 (void) GetGeometry(image_info->size,(ssize_t *) NULL,
3334 (ssize_t *) NULL,&image->columns,&image->rows);
3335 if ((image->columns != 0) || (image->rows != 0))
3337 image->resolution.x=SVGDensity*image->columns/
3338 dimension_info.width;
3339 image->resolution.y=SVGDensity*image->rows/
3340 dimension_info.height;
3341 if (fabs(image->resolution.x) < MagickEpsilon)
3342 image->resolution.x=image->resolution.y;
3344 if (fabs(image->resolution.y) < MagickEpsilon)
3345 image->resolution.y=image->resolution.x;
3347 image->resolution.x=image->resolution.y=MagickMin(
3348 image->resolution.x,image->resolution.y);
3349 apply_density=MagickTrue;
3352 if (apply_density != MagickFalse)
3354 image->columns=image->resolution.x*dimension_info.width/SVGDensity;
3355 image->rows=image->resolution.y*dimension_info.height/SVGDensity;
3359 image->columns=dimension_info.width;
3360 image->rows=dimension_info.height;
3362 pixel_info=(MemoryInfo *) NULL;
3364 pixel_buffer=rsvg_handle_get_pixbuf(svg_handle);
3365 rsvg_handle_free(svg_handle);
3366 image->columns=gdk_pixbuf_get_width(pixel_buffer);
3367 image->rows=gdk_pixbuf_get_height(pixel_buffer);
3369 image->alpha_trait=BlendPixelTrait;
3370 if (image_info->ping == MagickFalse)
3372 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3377 status=SetImageExtent(image,image->columns,image->rows,exception);
3378 if (status == MagickFalse)
3380 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
3381 g_object_unref(G_OBJECT(pixel_buffer));
3383 g_object_unref(svg_handle);
3384 ThrowReaderException(MissingDelegateError,
3385 "NoDecodeDelegateForThisImageFormat");
3387 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3388 stride=4*image->columns;
3389 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
3390 stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
3391 (int) image->columns);
3393 pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels));
3394 if (pixel_info == (MemoryInfo *) NULL)
3396 g_object_unref(svg_handle);
3397 ThrowReaderException(ResourceLimitError,
3398 "MemoryAllocationFailed");
3400 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3402 (void) SetImageBackgroundColor(image,exception);
3403 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3404 cairo_surface=cairo_image_surface_create_for_data(pixels,
3405 CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int)
3407 if ((cairo_surface == (cairo_surface_t *) NULL) ||
3408 (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS))
3410 if (cairo_surface != (cairo_surface_t *) NULL)
3411 cairo_surface_destroy(cairo_surface);
3412 pixel_info=RelinquishVirtualMemory(pixel_info);
3413 g_object_unref(svg_handle);
3414 ThrowReaderException(ResourceLimitError,
3415 "MemoryAllocationFailed");
3417 cairo_image=cairo_create(cairo_surface);
3418 cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
3419 cairo_paint(cairo_image);
3420 cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
3421 if (apply_density != MagickFalse)
3422 cairo_scale(cairo_image,image->resolution.x/SVGDensity,
3423 image->resolution.y/SVGDensity);
3424 rsvg_handle_render_cairo(svg_handle,cairo_image);
3425 cairo_destroy(cairo_image);
3426 cairo_surface_destroy(cairo_surface);
3427 g_object_unref(svg_handle);
3430 p=gdk_pixbuf_get_pixels(pixel_buffer);
3432 GetPixelInfo(image,&fill_color);
3433 for (y=0; y < (ssize_t) image->rows; y++)
3435 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3436 if (q == (Quantum *) NULL)
3438 for (x=0; x < (ssize_t) image->columns; x++)
3440 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3441 fill_color.blue=ScaleCharToQuantum(*p++);
3442 fill_color.green=ScaleCharToQuantum(*p++);
3443 fill_color.red=ScaleCharToQuantum(*p++);
3445 fill_color.red=ScaleCharToQuantum(*p++);
3446 fill_color.green=ScaleCharToQuantum(*p++);
3447 fill_color.blue=ScaleCharToQuantum(*p++);
3449 fill_color.alpha=ScaleCharToQuantum(*p++);
3450 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3455 gamma=QuantumScale*fill_color.alpha;
3456 gamma=PerceptibleReciprocal(gamma);
3457 fill_color.blue*=gamma;
3458 fill_color.green*=gamma;
3459 fill_color.red*=gamma;
3462 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
3463 GetPixelAlpha(image,q),q);
3464 q+=GetPixelChannels(image);
3466 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3468 if (image->previous == (Image *) NULL)
3470 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
3472 if (status == MagickFalse)
3477 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3478 if (pixel_info != (MemoryInfo *) NULL)
3479 pixel_info=RelinquishVirtualMemory(pixel_info);
3481 g_object_unref(G_OBJECT(pixel_buffer));
3483 (void) CloseBlob(image);
3484 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3486 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3487 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3488 next=GetNextImageInList(next);
3490 return(GetFirstImageInList(image));
3498 unique_file=AcquireUniqueFileResource(filename);
3499 if (unique_file != -1)
3500 file=fdopen(unique_file,"w");
3501 if ((unique_file == -1) || (file == (FILE *) NULL))
3503 (void) CopyMagickString(image->filename,filename,MagickPathExtent);
3504 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
3506 image=DestroyImageList(image);
3507 return((Image *) NULL);
3512 svg_info=AcquireSVGInfo();
3513 if (svg_info == (SVGInfo *) NULL)
3515 (void) fclose(file);
3516 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3518 svg_info->file=file;
3519 svg_info->exception=exception;
3520 svg_info->image=image;
3521 svg_info->image_info=image_info;
3522 svg_info->bounds.width=image->columns;
3523 svg_info->bounds.height=image->rows;
3524 svg_info->svgDepth=0;
3525 if (image_info->size != (char *) NULL)
3526 (void) CloneString(&svg_info->size,image_info->size);
3527 if (image->debug != MagickFalse)
3528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3529 (void) xmlSubstituteEntitiesDefault(1);
3530 (void) memset(&sax_modules,0,sizeof(sax_modules));
3531 sax_modules.internalSubset=SVGInternalSubset;
3532 sax_modules.isStandalone=SVGIsStandalone;
3533 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3534 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3535 sax_modules.resolveEntity=SVGResolveEntity;
3536 sax_modules.getEntity=SVGGetEntity;
3537 sax_modules.entityDecl=SVGEntityDeclaration;
3538 sax_modules.notationDecl=SVGNotationDeclaration;
3539 sax_modules.attributeDecl=SVGAttributeDeclaration;
3540 sax_modules.elementDecl=SVGElementDeclaration;
3541 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3542 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3543 sax_modules.startDocument=SVGStartDocument;
3544 sax_modules.endDocument=SVGEndDocument;
3545 sax_modules.startElement=SVGStartElement;
3546 sax_modules.endElement=SVGEndElement;
3547 sax_modules.reference=SVGReference;
3548 sax_modules.characters=SVGCharacters;
3549 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3550 sax_modules.processingInstruction=SVGProcessingInstructions;
3551 sax_modules.comment=SVGComment;
3552 sax_modules.warning=SVGWarning;
3553 sax_modules.error=SVGError;
3554 sax_modules.fatalError=SVGError;
3555 sax_modules.getParameterEntity=SVGGetParameterEntity;
3556 sax_modules.cdataBlock=SVGCDataBlock;
3557 sax_modules.externalSubset=SVGExternalSubset;
3558 sax_handler=(&sax_modules);
3559 n=ReadBlob(image,MagickPathExtent-1,message);
3563 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3564 message,n,image->filename);
3565 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3568 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3573 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3574 SVGEndDocument(svg_info);
3575 xmlFreeParserCtxt(svg_info->parser);
3576 if (image->debug != MagickFalse)
3577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3578 (void) fclose(file);
3579 (void) CloseBlob(image);
3580 image->columns=svg_info->width;
3581 image->rows=svg_info->height;
3582 if (exception->severity >= ErrorException)
3584 svg_info=DestroySVGInfo(svg_info);
3585 (void) RelinquishUniqueFileResource(filename);
3586 image=DestroyImage(image);
3587 return((Image *) NULL);
3589 if (image_info->ping == MagickFalse)
3597 image=DestroyImage(image);
3598 image=(Image *) NULL;
3599 read_info=CloneImageInfo(image_info);
3600 SetImageInfoBlob(read_info,(void *) NULL,0);
3601 (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
3603 image=ReadImage(read_info,exception);
3604 read_info=DestroyImageInfo(read_info);
3605 if (image != (Image *) NULL)
3606 (void) CopyMagickString(image->filename,image_info->filename,
3610 Relinquish resources.
3612 if (image != (Image *) NULL)
3614 if (svg_info->title != (char *) NULL)
3615 (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3616 if (svg_info->comment != (char *) NULL)
3617 (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3620 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3622 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3623 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3624 next=GetNextImageInList(next);
3626 svg_info=DestroySVGInfo(svg_info);
3627 (void) RelinquishUniqueFileResource(filename);
3628 return(GetFirstImageInList(image));
3633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3637 % R e g i s t e r S V G I m a g e %
3641 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3643 % RegisterSVGImage() adds attributes for the SVG image format to
3644 % the list of supported formats. The attributes include the image format
3645 % tag, a method to read and/or write the format, whether the format
3646 % supports the saving of more than one frame to the same file or blob,
3647 % whether the format supports native in-memory I/O, and a brief
3648 % description of the format.
3650 % The format of the RegisterSVGImage method is:
3652 % size_t RegisterSVGImage(void)
3655 ModuleExport size_t RegisterSVGImage(void)
3658 version[MagickPathExtent];
3664 #if defined(LIBXML_DOTTED_VERSION)
3665 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,
3668 #if defined(MAGICKCORE_RSVG_DELEGATE)
3669 #if !GLIB_CHECK_VERSION(2,35,0)
3672 #if defined(MAGICKCORE_XML_DELEGATE)
3675 (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d",
3676 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3678 entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics");
3679 #if defined(MAGICKCORE_XML_DELEGATE)
3680 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3682 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3683 entry->flags^=CoderBlobSupportFlag;
3684 #if defined(MAGICKCORE_RSVG_DELEGATE)
3685 entry->flags^=CoderDecoderThreadSupportFlag;
3687 entry->mime_type=ConstantString("image/svg+xml");
3688 if (*version != '\0')
3689 entry->version=ConstantString(version);
3690 entry->magick=(IsImageFormatHandler *) IsSVG;
3691 (void) RegisterMagickInfo(entry);
3692 entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics");
3693 #if defined(MAGICKCORE_XML_DELEGATE)
3694 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3696 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3697 entry->flags^=CoderBlobSupportFlag;
3698 #if defined(MAGICKCORE_RSVG_DELEGATE)
3699 entry->flags^=CoderDecoderThreadSupportFlag;
3701 entry->mime_type=ConstantString("image/svg+xml");
3702 if (*version != '\0')
3703 entry->version=ConstantString(version);
3704 entry->magick=(IsImageFormatHandler *) IsSVG;
3705 (void) RegisterMagickInfo(entry);
3706 entry=AcquireMagickInfo("SVG","MSVG",
3707 "ImageMagick's own SVG internal renderer");
3708 #if defined(MAGICKCORE_XML_DELEGATE)
3709 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3711 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3712 entry->flags^=CoderBlobSupportFlag;
3713 #if defined(MAGICKCORE_RSVG_DELEGATE)
3714 entry->flags^=CoderDecoderThreadSupportFlag;
3716 entry->magick=(IsImageFormatHandler *) IsSVG;
3717 (void) RegisterMagickInfo(entry);
3718 return(MagickImageCoderSignature);
3722 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3726 % U n r e g i s t e r S V G I m a g e %
3730 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3732 % UnregisterSVGImage() removes format registrations made by the
3733 % SVG module from the list of supported formats.
3735 % The format of the UnregisterSVGImage method is:
3737 % UnregisterSVGImage(void)
3740 ModuleExport void UnregisterSVGImage(void)
3742 (void) UnregisterMagickInfo("SVGZ");
3743 (void) UnregisterMagickInfo("SVG");
3744 (void) UnregisterMagickInfo("MSVG");
3745 #if defined(MAGICKCORE_XML_DELEGATE)
3751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3755 % W r i t e S V G I m a g e %
3759 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3761 % WriteSVGImage() writes a image in the SVG - XML based W3C standard
3764 % The format of the WriteSVGImage method is:
3766 % MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3767 % Image *image,ExceptionInfo *exception)
3769 % A description of each parameter follows.
3771 % o image_info: the image info.
3773 % o image: The image.
3775 % o exception: return any errors or warnings in this structure.
3779 static void AffineToTransform(Image *image,AffineMatrix *affine)
3782 transform[MagickPathExtent];
3784 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3786 if ((fabs(affine->rx) < MagickEpsilon) &&
3787 (fabs(affine->ry) < MagickEpsilon))
3789 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3790 (fabs(affine->sy-1.0) < MagickEpsilon))
3792 (void) WriteBlobString(image,"\">\n");
3795 (void) FormatLocaleString(transform,MagickPathExtent,
3796 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3797 (void) WriteBlobString(image,transform);
3802 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3803 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3804 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3810 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3811 (void) FormatLocaleString(transform,MagickPathExtent,
3812 "\" transform=\"rotate(%g)\">\n",theta);
3813 (void) WriteBlobString(image,transform);
3820 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3821 (fabs(affine->rx) < MagickEpsilon) &&
3822 (fabs(affine->ry) < MagickEpsilon) &&
3823 (fabs(affine->sy-1.0) < MagickEpsilon))
3825 (void) FormatLocaleString(transform,MagickPathExtent,
3826 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3827 (void) WriteBlobString(image,transform);
3831 (void) FormatLocaleString(transform,MagickPathExtent,
3832 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3833 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3834 (void) WriteBlobString(image,transform);
3837 static MagickBooleanType IsPoint(const char *point)
3845 value=strtol(point,&p,10);
3847 return(p != point ? MagickTrue : MagickFalse);
3850 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
3852 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3857 at_fitting_opts_type
3869 register const Quantum
3883 Trace image and write as SVG.
3885 fitting_options=at_fitting_opts_new();
3886 output_options=at_output_opts_new();
3887 (void) SetImageGray(image,exception);
3888 type=GetImageType(image);
3890 if ((type == BilevelType) || (type == GrayscaleType))
3892 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3894 for (y=0; y < (ssize_t) image->rows; y++)
3896 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3897 if (p == (const Quantum *) NULL)
3899 for (x=0; x < (ssize_t) image->columns; x++)
3901 trace->bitmap[i++]=GetPixelRed(image,p);
3902 if (number_planes == 3)
3904 trace->bitmap[i++]=GetPixelGreen(image,p);
3905 trace->bitmap[i++]=GetPixelBlue(image,p);
3907 p+=GetPixelChannels(image);
3910 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3912 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3913 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3918 at_splines_free(splines);
3919 at_bitmap_free(trace);
3920 at_output_opts_free(output_options);
3921 at_fitting_opts_free(fitting_options);
3927 message[MagickPathExtent];
3948 (void) WriteBlobString(image,
3949 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
3950 (void) WriteBlobString(image,
3951 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
3952 (void) WriteBlobString(image,
3953 " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
3954 (void) FormatLocaleString(message,MagickPathExtent,
3955 "<svg version=\"1.1\" id=\"Layer_1\" "
3956 "xmlns=\"http://www.w3.org/2000/svg\" "
3957 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" "
3958 "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" "
3959 "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">",
3960 (double) image->columns,(double) image->rows,
3961 (double) image->columns,(double) image->rows,
3962 (double) image->columns,(double) image->rows);
3963 (void) WriteBlobString(image,message);
3964 clone_image=CloneImage(image,0,0,MagickTrue,exception);
3965 if (clone_image == (Image *) NULL)
3966 return(MagickFalse);
3967 image_info=AcquireImageInfo();
3968 (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent);
3970 blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length,
3972 clone_image=DestroyImage(clone_image);
3973 image_info=DestroyImageInfo(image_info);
3974 if (blob == (unsigned char *) NULL)
3975 return(MagickFalse);
3977 base64=Base64Encode(blob,blob_length,&encode_length);
3978 blob=(unsigned char *) RelinquishMagickMemory(blob);
3979 (void) FormatLocaleString(message,MagickPathExtent,
3980 " <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" "
3981 "x=\"%.20g\" y=\"%.20g\"\n href=\"data:image/png;base64,",
3982 (double) image->scene,(double) image->columns,(double) image->rows,
3983 (double) image->page.x,(double) image->page.y);
3984 (void) WriteBlobString(image,message);
3986 for (i=(ssize_t) encode_length; i > 0; i-=76)
3988 (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p);
3989 (void) WriteBlobString(image,message);
3992 (void) WriteBlobString(image,"\n");
3994 base64=DestroyString(base64);
3995 (void) WriteBlobString(image,"\" />\n");
3996 (void) WriteBlobString(image,"</svg>\n");
4003 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
4004 ExceptionInfo *exception)
4006 #define BezierQuantum 200
4012 keyword[MagickPathExtent],
4013 message[MagickPathExtent],
4014 name[MagickPathExtent],
4017 type[MagickPathExtent];
4058 Open output image file.
4060 assert(image_info != (const ImageInfo *) NULL);
4061 assert(image_info->signature == MagickCoreSignature);
4062 assert(image != (Image *) NULL);
4063 assert(image->signature == MagickCoreSignature);
4064 if (image->debug != MagickFalse)
4065 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4066 assert(exception != (ExceptionInfo *) NULL);
4067 assert(exception->signature == MagickCoreSignature);
4068 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
4069 if (status == MagickFalse)
4071 value=GetImageArtifact(image,"SVG");
4072 if (value != (char *) NULL)
4074 (void) WriteBlobString(image,value);
4075 (void) CloseBlob(image);
4078 value=GetImageArtifact(image,"MVG");
4079 if (value == (char *) NULL)
4080 return(TraceSVGImage(image,exception));
4084 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
4085 (void) WriteBlobString(image,
4086 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
4087 (void) WriteBlobString(image,
4088 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
4089 (void) FormatLocaleString(message,MagickPathExtent,
4090 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
4092 (void) WriteBlobString(image,message);
4094 Allocate primitive info memory.
4097 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
4098 sizeof(*primitive_info));
4099 if (primitive_info == (PrimitiveInfo *) NULL)
4100 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
4101 GetAffineMatrix(&affine);
4102 token=AcquireString(value);
4103 extent=strlen(token)+MagickPathExtent;
4107 for (q=(const char *) value; *q != '\0'; )
4110 Interpret graphic primitive.
4112 GetNextToken(q,&q,MagickPathExtent,keyword);
4113 if (*keyword == '\0')
4115 if (*keyword == '#')
4120 if (active != MagickFalse)
4122 AffineToTransform(image,&affine);
4125 (void) WriteBlobString(image,"<desc>");
4126 (void) WriteBlobString(image,keyword+1);
4127 for ( ; (*q != '\n') && (*q != '\0'); q++)
4130 case '<': (void) WriteBlobString(image,"<"); break;
4131 case '>': (void) WriteBlobString(image,">"); break;
4132 case '&': (void) WriteBlobString(image,"&"); break;
4133 default: (void) WriteBlobByte(image,*q); break;
4135 (void) WriteBlobString(image,"</desc>\n");
4138 primitive_type=UndefinedPrimitive;
4146 if (LocaleCompare("affine",keyword) == 0)
4148 GetNextToken(q,&q,extent,token);
4149 affine.sx=StringToDouble(token,&next_token);
4150 GetNextToken(q,&q,extent,token);
4152 GetNextToken(q,&q,extent,token);
4153 affine.rx=StringToDouble(token,&next_token);
4154 GetNextToken(q,&q,extent,token);
4156 GetNextToken(q,&q,extent,token);
4157 affine.ry=StringToDouble(token,&next_token);
4158 GetNextToken(q,&q,extent,token);
4160 GetNextToken(q,&q,extent,token);
4161 affine.sy=StringToDouble(token,&next_token);
4162 GetNextToken(q,&q,extent,token);
4164 GetNextToken(q,&q,extent,token);
4165 affine.tx=StringToDouble(token,&next_token);
4166 GetNextToken(q,&q,extent,token);
4168 GetNextToken(q,&q,extent,token);
4169 affine.ty=StringToDouble(token,&next_token);
4172 if (LocaleCompare("alpha",keyword) == 0)
4174 primitive_type=AlphaPrimitive;
4177 if (LocaleCompare("angle",keyword) == 0)
4179 GetNextToken(q,&q,extent,token);
4180 affine.rx=StringToDouble(token,&next_token);
4181 affine.ry=StringToDouble(token,&next_token);
4184 if (LocaleCompare("arc",keyword) == 0)
4186 primitive_type=ArcPrimitive;
4195 if (LocaleCompare("bezier",keyword) == 0)
4197 primitive_type=BezierPrimitive;
4206 if (LocaleCompare("clip-path",keyword) == 0)
4208 GetNextToken(q,&q,extent,token);
4209 (void) FormatLocaleString(message,MagickPathExtent,
4210 "clip-path:url(#%s);",token);
4211 (void) WriteBlobString(image,message);
4214 if (LocaleCompare("clip-rule",keyword) == 0)
4216 GetNextToken(q,&q,extent,token);
4217 (void) FormatLocaleString(message,MagickPathExtent,"clip-rule:%s;",
4219 (void) WriteBlobString(image,message);
4222 if (LocaleCompare("clip-units",keyword) == 0)
4224 GetNextToken(q,&q,extent,token);
4225 (void) FormatLocaleString(message,MagickPathExtent,
4226 "clipPathUnits=%s;",token);
4227 (void) WriteBlobString(image,message);
4230 if (LocaleCompare("circle",keyword) == 0)
4232 primitive_type=CirclePrimitive;
4235 if (LocaleCompare("color",keyword) == 0)
4237 primitive_type=ColorPrimitive;
4246 if (LocaleCompare("decorate",keyword) == 0)
4248 GetNextToken(q,&q,extent,token);
4249 (void) FormatLocaleString(message,MagickPathExtent,
4250 "text-decoration:%s;",token);
4251 (void) WriteBlobString(image,message);
4260 if (LocaleCompare("ellipse",keyword) == 0)
4262 primitive_type=EllipsePrimitive;
4271 if (LocaleCompare("fill",keyword) == 0)
4273 GetNextToken(q,&q,extent,token);
4274 (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;",
4276 (void) WriteBlobString(image,message);
4279 if (LocaleCompare("fill-rule",keyword) == 0)
4281 GetNextToken(q,&q,extent,token);
4282 (void) FormatLocaleString(message,MagickPathExtent,
4283 "fill-rule:%s;",token);
4284 (void) WriteBlobString(image,message);
4287 if (LocaleCompare("fill-opacity",keyword) == 0)
4289 GetNextToken(q,&q,extent,token);
4290 (void) FormatLocaleString(message,MagickPathExtent,
4291 "fill-opacity:%s;",token);
4292 (void) WriteBlobString(image,message);
4295 if (LocaleCompare("font-family",keyword) == 0)
4297 GetNextToken(q,&q,extent,token);
4298 (void) FormatLocaleString(message,MagickPathExtent,
4299 "font-family:%s;",token);
4300 (void) WriteBlobString(image,message);
4303 if (LocaleCompare("font-stretch",keyword) == 0)
4305 GetNextToken(q,&q,extent,token);
4306 (void) FormatLocaleString(message,MagickPathExtent,
4307 "font-stretch:%s;",token);
4308 (void) WriteBlobString(image,message);
4311 if (LocaleCompare("font-style",keyword) == 0)
4313 GetNextToken(q,&q,extent,token);
4314 (void) FormatLocaleString(message,MagickPathExtent,
4315 "font-style:%s;",token);
4316 (void) WriteBlobString(image,message);
4319 if (LocaleCompare("font-size",keyword) == 0)
4321 GetNextToken(q,&q,extent,token);
4322 (void) FormatLocaleString(message,MagickPathExtent,
4323 "font-size:%s;",token);
4324 (void) WriteBlobString(image,message);
4327 if (LocaleCompare("font-weight",keyword) == 0)
4329 GetNextToken(q,&q,extent,token);
4330 (void) FormatLocaleString(message,MagickPathExtent,
4331 "font-weight:%s;",token);
4332 (void) WriteBlobString(image,message);
4341 if (LocaleCompare("gradient-units",keyword) == 0)
4343 GetNextToken(q,&q,extent,token);
4346 if (LocaleCompare("text-align",keyword) == 0)
4348 GetNextToken(q,&q,extent,token);
4349 (void) FormatLocaleString(message,MagickPathExtent,
4350 "text-align %s ",token);
4351 (void) WriteBlobString(image,message);
4354 if (LocaleCompare("text-anchor",keyword) == 0)
4356 GetNextToken(q,&q,extent,token);
4357 (void) FormatLocaleString(message,MagickPathExtent,
4358 "text-anchor %s ",token);
4359 (void) WriteBlobString(image,message);
4368 if (LocaleCompare("image",keyword) == 0)
4370 GetNextToken(q,&q,extent,token);
4371 primitive_type=ImagePrimitive;
4380 if (LocaleCompare("kerning",keyword) == 0)
4382 GetNextToken(q,&q,extent,token);
4383 (void) FormatLocaleString(message,MagickPathExtent,"kerning:%s;",
4385 (void) WriteBlobString(image,message);
4392 if (LocaleCompare("letter-spacing",keyword) == 0)
4394 GetNextToken(q,&q,extent,token);
4395 (void) FormatLocaleString(message,MagickPathExtent,
4396 "letter-spacing:%s;",token);
4397 (void) WriteBlobString(image,message);
4400 if (LocaleCompare("line",keyword) == 0)
4402 primitive_type=LinePrimitive;
4411 if (LocaleCompare("opacity",keyword) == 0)
4413 GetNextToken(q,&q,extent,token);
4414 (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ",
4416 (void) WriteBlobString(image,message);
4425 if (LocaleCompare("path",keyword) == 0)
4427 primitive_type=PathPrimitive;
4430 if (LocaleCompare("point",keyword) == 0)
4432 primitive_type=PointPrimitive;
4435 if (LocaleCompare("polyline",keyword) == 0)
4437 primitive_type=PolylinePrimitive;
4440 if (LocaleCompare("polygon",keyword) == 0)
4442 primitive_type=PolygonPrimitive;
4445 if (LocaleCompare("pop",keyword) == 0)
4447 GetNextToken(q,&q,extent,token);
4448 if (LocaleCompare("clip-path",token) == 0)
4450 (void) WriteBlobString(image,"</clipPath>\n");
4453 if (LocaleCompare("defs",token) == 0)
4455 (void) WriteBlobString(image,"</defs>\n");
4458 if (LocaleCompare("gradient",token) == 0)
4460 (void) FormatLocaleString(message,MagickPathExtent,
4461 "</%sGradient>\n",type);
4462 (void) WriteBlobString(image,message);
4465 if (LocaleCompare("graphic-context",token) == 0)
4469 ThrowWriterException(DrawError,
4470 "UnbalancedGraphicContextPushPop");
4471 (void) WriteBlobString(image,"</g>\n");
4473 if (LocaleCompare("pattern",token) == 0)
4475 (void) WriteBlobString(image,"</pattern>\n");
4478 if (LocaleCompare("symbol",token) == 0)
4480 (void) WriteBlobString(image,"</symbol>\n");
4483 if ((LocaleCompare("defs",token) == 0) ||
4484 (LocaleCompare("symbol",token) == 0))
4485 (void) WriteBlobString(image,"</g>\n");
4488 if (LocaleCompare("push",keyword) == 0)
4490 GetNextToken(q,&q,extent,token);
4491 if (LocaleCompare("clip-path",token) == 0)
4493 GetNextToken(q,&q,extent,token);
4494 (void) FormatLocaleString(message,MagickPathExtent,
4495 "<clipPath id=\"%s\">\n",token);
4496 (void) WriteBlobString(image,message);
4499 if (LocaleCompare("defs",token) == 0)
4501 (void) WriteBlobString(image,"<defs>\n");
4504 if (LocaleCompare("gradient",token) == 0)
4506 GetNextToken(q,&q,extent,token);
4507 (void) CopyMagickString(name,token,MagickPathExtent);
4508 GetNextToken(q,&q,extent,token);
4509 (void) CopyMagickString(type,token,MagickPathExtent);
4510 GetNextToken(q,&q,extent,token);
4511 svg_info.segment.x1=StringToDouble(token,&next_token);
4512 svg_info.element.cx=StringToDouble(token,&next_token);
4513 GetNextToken(q,&q,extent,token);
4515 GetNextToken(q,&q,extent,token);
4516 svg_info.segment.y1=StringToDouble(token,&next_token);
4517 svg_info.element.cy=StringToDouble(token,&next_token);
4518 GetNextToken(q,&q,extent,token);
4520 GetNextToken(q,&q,extent,token);
4521 svg_info.segment.x2=StringToDouble(token,&next_token);
4522 svg_info.element.major=StringToDouble(token,
4524 GetNextToken(q,&q,extent,token);
4526 GetNextToken(q,&q,extent,token);
4527 svg_info.segment.y2=StringToDouble(token,&next_token);
4528 svg_info.element.minor=StringToDouble(token,
4530 (void) FormatLocaleString(message,MagickPathExtent,
4531 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
4532 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
4533 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
4534 if (LocaleCompare(type,"radial") == 0)
4536 GetNextToken(q,&q,extent,token);
4538 GetNextToken(q,&q,extent,token);
4539 svg_info.element.angle=StringToDouble(token,
4541 (void) FormatLocaleString(message,MagickPathExtent,
4542 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
4543 "fx=\"%g\" fy=\"%g\">\n",type,name,
4544 svg_info.element.cx,svg_info.element.cy,
4545 svg_info.element.angle,svg_info.element.major,
4546 svg_info.element.minor);
4548 (void) WriteBlobString(image,message);
4551 if (LocaleCompare("graphic-context",token) == 0)
4556 AffineToTransform(image,&affine);
4559 (void) WriteBlobString(image,"<g style=\"");
4562 if (LocaleCompare("pattern",token) == 0)
4564 GetNextToken(q,&q,extent,token);
4565 (void) CopyMagickString(name,token,MagickPathExtent);
4566 GetNextToken(q,&q,extent,token);
4567 svg_info.bounds.x=StringToDouble(token,&next_token);
4568 GetNextToken(q,&q,extent,token);
4570 GetNextToken(q,&q,extent,token);
4571 svg_info.bounds.y=StringToDouble(token,&next_token);
4572 GetNextToken(q,&q,extent,token);
4574 GetNextToken(q,&q,extent,token);
4575 svg_info.bounds.width=StringToDouble(token,
4577 GetNextToken(q,&q,extent,token);
4579 GetNextToken(q,&q,extent,token);
4580 svg_info.bounds.height=StringToDouble(token,(char **) NULL);
4581 (void) FormatLocaleString(message,MagickPathExtent,
4582 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
4583 "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y,
4584 svg_info.bounds.width,svg_info.bounds.height);
4585 (void) WriteBlobString(image,message);
4588 if (LocaleCompare("symbol",token) == 0)
4590 (void) WriteBlobString(image,"<symbol>\n");
4601 if (LocaleCompare("rectangle",keyword) == 0)
4603 primitive_type=RectanglePrimitive;
4606 if (LocaleCompare("roundRectangle",keyword) == 0)
4608 primitive_type=RoundRectanglePrimitive;
4611 if (LocaleCompare("rotate",keyword) == 0)
4613 GetNextToken(q,&q,extent,token);
4614 (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ",
4616 (void) WriteBlobString(image,message);
4625 if (LocaleCompare("scale",keyword) == 0)
4627 GetNextToken(q,&q,extent,token);
4628 affine.sx=StringToDouble(token,&next_token);
4629 GetNextToken(q,&q,extent,token);
4631 GetNextToken(q,&q,extent,token);
4632 affine.sy=StringToDouble(token,&next_token);
4635 if (LocaleCompare("skewX",keyword) == 0)
4637 GetNextToken(q,&q,extent,token);
4638 (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ",
4640 (void) WriteBlobString(image,message);
4643 if (LocaleCompare("skewY",keyword) == 0)
4645 GetNextToken(q,&q,extent,token);
4646 (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ",
4648 (void) WriteBlobString(image,message);
4651 if (LocaleCompare("stop-color",keyword) == 0)
4654 color[MagickPathExtent];
4656 GetNextToken(q,&q,extent,token);
4657 (void) CopyMagickString(color,token,MagickPathExtent);
4658 GetNextToken(q,&q,extent,token);
4659 (void) FormatLocaleString(message,MagickPathExtent,
4660 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4661 (void) WriteBlobString(image,message);
4664 if (LocaleCompare("stroke",keyword) == 0)
4666 GetNextToken(q,&q,extent,token);
4667 (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;",
4669 (void) WriteBlobString(image,message);
4672 if (LocaleCompare("stroke-antialias",keyword) == 0)
4674 GetNextToken(q,&q,extent,token);
4675 (void) FormatLocaleString(message,MagickPathExtent,
4676 "stroke-antialias:%s;",token);
4677 (void) WriteBlobString(image,message);
4680 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4688 GetNextToken(p,&p,extent,token);
4689 for (k=0; IsPoint(token); k++)
4690 GetNextToken(p,&p,extent,token);
4691 (void) WriteBlobString(image,"stroke-dasharray:");
4692 for (j=0; j < k; j++)
4694 GetNextToken(q,&q,extent,token);
4695 (void) FormatLocaleString(message,MagickPathExtent,"%s ",
4697 (void) WriteBlobString(image,message);
4699 (void) WriteBlobString(image,";");
4702 GetNextToken(q,&q,extent,token);
4703 (void) FormatLocaleString(message,MagickPathExtent,
4704 "stroke-dasharray:%s;",token);
4705 (void) WriteBlobString(image,message);
4708 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4710 GetNextToken(q,&q,extent,token);
4711 (void) FormatLocaleString(message,MagickPathExtent,
4712 "stroke-dashoffset:%s;",token);
4713 (void) WriteBlobString(image,message);
4716 if (LocaleCompare("stroke-linecap",keyword) == 0)
4718 GetNextToken(q,&q,extent,token);
4719 (void) FormatLocaleString(message,MagickPathExtent,
4720 "stroke-linecap:%s;",token);
4721 (void) WriteBlobString(image,message);
4724 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4726 GetNextToken(q,&q,extent,token);
4727 (void) FormatLocaleString(message,MagickPathExtent,
4728 "stroke-linejoin:%s;",token);
4729 (void) WriteBlobString(image,message);
4732 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4734 GetNextToken(q,&q,extent,token);
4735 (void) FormatLocaleString(message,MagickPathExtent,
4736 "stroke-miterlimit:%s;",token);
4737 (void) WriteBlobString(image,message);
4740 if (LocaleCompare("stroke-opacity",keyword) == 0)
4742 GetNextToken(q,&q,extent,token);
4743 (void) FormatLocaleString(message,MagickPathExtent,
4744 "stroke-opacity:%s;",token);
4745 (void) WriteBlobString(image,message);
4748 if (LocaleCompare("stroke-width",keyword) == 0)
4750 GetNextToken(q,&q,extent,token);
4751 (void) FormatLocaleString(message,MagickPathExtent,
4752 "stroke-width:%s;",token);
4753 (void) WriteBlobString(image,message);
4762 if (LocaleCompare("text",keyword) == 0)
4764 primitive_type=TextPrimitive;
4767 if (LocaleCompare("text-antialias",keyword) == 0)
4769 GetNextToken(q,&q,extent,token);
4770 (void) FormatLocaleString(message,MagickPathExtent,
4771 "text-antialias:%s;",token);
4772 (void) WriteBlobString(image,message);
4775 if (LocaleCompare("tspan",keyword) == 0)
4777 primitive_type=TextPrimitive;
4780 if (LocaleCompare("translate",keyword) == 0)
4782 GetNextToken(q,&q,extent,token);
4783 affine.tx=StringToDouble(token,&next_token);
4784 GetNextToken(q,&q,extent,token);
4786 GetNextToken(q,&q,extent,token);
4787 affine.ty=StringToDouble(token,&next_token);
4796 if (LocaleCompare("viewbox",keyword) == 0)
4798 GetNextToken(q,&q,extent,token);
4800 GetNextToken(q,&q,extent,token);
4801 GetNextToken(q,&q,extent,token);
4803 GetNextToken(q,&q,extent,token);
4804 GetNextToken(q,&q,extent,token);
4806 GetNextToken(q,&q,extent,token);
4807 GetNextToken(q,&q,extent,token);
4819 if (status == MagickFalse)
4821 if (primitive_type == UndefinedPrimitive)
4824 Parse the primitive attributes.
4828 for (x=0; *q != '\0'; x++)
4833 if (IsPoint(q) == MagickFalse)
4835 GetNextToken(q,&q,extent,token);
4836 point.x=StringToDouble(token,&next_token);
4837 GetNextToken(q,&q,extent,token);
4839 GetNextToken(q,&q,extent,token);
4840 point.y=StringToDouble(token,&next_token);
4841 GetNextToken(q,(const char **) NULL,extent,token);
4843 GetNextToken(q,&q,extent,token);
4844 primitive_info[i].primitive=primitive_type;
4845 primitive_info[i].point=point;
4846 primitive_info[i].coordinates=0;
4847 primitive_info[i].method=FloodfillMethod;
4849 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4851 number_points+=6*BezierQuantum+360;
4852 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4853 number_points,sizeof(*primitive_info));
4854 if (primitive_info == (PrimitiveInfo *) NULL)
4856 (void) ThrowMagickException(exception,GetMagickModule(),
4857 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4861 primitive_info[j].primitive=primitive_type;
4862 primitive_info[j].coordinates=x;
4863 primitive_info[j].method=FloodfillMethod;
4864 primitive_info[j].text=(char *) NULL;
4867 AffineToTransform(image,&affine);
4871 switch (primitive_type)
4873 case PointPrimitive:
4876 if (primitive_info[j].coordinates != 1)
4885 if (primitive_info[j].coordinates != 2)
4890 (void) FormatLocaleString(message,MagickPathExtent,
4891 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4892 primitive_info[j].point.x,primitive_info[j].point.y,
4893 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4894 (void) WriteBlobString(image,message);
4897 case RectanglePrimitive:
4899 if (primitive_info[j].coordinates != 2)
4904 (void) FormatLocaleString(message,MagickPathExtent,
4905 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4906 primitive_info[j].point.x,primitive_info[j].point.y,
4907 primitive_info[j+1].point.x-primitive_info[j].point.x,
4908 primitive_info[j+1].point.y-primitive_info[j].point.y);
4909 (void) WriteBlobString(image,message);
4912 case RoundRectanglePrimitive:
4914 if (primitive_info[j].coordinates != 3)
4919 (void) FormatLocaleString(message,MagickPathExtent,
4920 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4921 "ry=\"%g\"/>\n",primitive_info[j].point.x,
4922 primitive_info[j].point.y,primitive_info[j+1].point.x-
4923 primitive_info[j].point.x,primitive_info[j+1].point.y-
4924 primitive_info[j].point.y,primitive_info[j+2].point.x,
4925 primitive_info[j+2].point.y);
4926 (void) WriteBlobString(image,message);
4931 if (primitive_info[j].coordinates != 3)
4938 case EllipsePrimitive:
4940 if (primitive_info[j].coordinates != 3)
4945 (void) FormatLocaleString(message,MagickPathExtent,
4946 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
4947 primitive_info[j].point.x,primitive_info[j].point.y,
4948 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4949 (void) WriteBlobString(image,message);
4952 case CirclePrimitive:
4958 if (primitive_info[j].coordinates != 2)
4963 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4964 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4965 (void) FormatLocaleString(message,MagickPathExtent,
4966 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
4967 primitive_info[j].point.x,primitive_info[j].point.y,
4969 (void) WriteBlobString(image,message);
4972 case PolylinePrimitive:
4974 if (primitive_info[j].coordinates < 2)
4979 (void) CopyMagickString(message," <polyline points=\"",
4981 (void) WriteBlobString(image,message);
4982 length=strlen(message);
4985 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4986 primitive_info[j].point.x,primitive_info[j].point.y);
4987 length+=strlen(message);
4990 (void) WriteBlobString(image,"\n ");
4991 length=strlen(message)+5;
4993 (void) WriteBlobString(image,message);
4995 (void) WriteBlobString(image,"\"/>\n");
4998 case PolygonPrimitive:
5000 if (primitive_info[j].coordinates < 3)
5005 primitive_info[i]=primitive_info[j];
5006 primitive_info[i].coordinates=0;
5007 primitive_info[j].coordinates++;
5009 (void) CopyMagickString(message," <polygon points=\"",MagickPathExtent);
5010 (void) WriteBlobString(image,message);
5011 length=strlen(message);
5014 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
5015 primitive_info[j].point.x,primitive_info[j].point.y);
5016 length+=strlen(message);
5019 (void) WriteBlobString(image,"\n ");
5020 length=strlen(message)+5;
5022 (void) WriteBlobString(image,message);
5024 (void) WriteBlobString(image,"\"/>\n");
5027 case BezierPrimitive:
5029 if (primitive_info[j].coordinates < 3)
5041 GetNextToken(q,&q,extent,token);
5042 number_attributes=1;
5043 for (p=token; *p != '\0'; p++)
5044 if (isalpha((int) *p))
5045 number_attributes++;
5046 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
5048 number_points+=6*BezierQuantum*number_attributes;
5049 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
5050 number_points,sizeof(*primitive_info));
5051 if (primitive_info == (PrimitiveInfo *) NULL)
5053 (void) ThrowMagickException(exception,GetMagickModule(),
5054 ResourceLimitError,"MemoryAllocationFailed","`%s'",
5059 (void) WriteBlobString(image," <path d=\"");
5060 (void) WriteBlobString(image,token);
5061 (void) WriteBlobString(image,"\"/>\n");
5064 case AlphaPrimitive:
5065 case ColorPrimitive:
5067 if (primitive_info[j].coordinates != 1)
5072 GetNextToken(q,&q,extent,token);
5073 if (LocaleCompare("point",token) == 0)
5074 primitive_info[j].method=PointMethod;
5075 if (LocaleCompare("replace",token) == 0)
5076 primitive_info[j].method=ReplaceMethod;
5077 if (LocaleCompare("floodfill",token) == 0)
5078 primitive_info[j].method=FloodfillMethod;
5079 if (LocaleCompare("filltoborder",token) == 0)
5080 primitive_info[j].method=FillToBorderMethod;
5081 if (LocaleCompare("reset",token) == 0)
5082 primitive_info[j].method=ResetMethod;
5090 if (primitive_info[j].coordinates != 1)
5095 GetNextToken(q,&q,extent,token);
5096 (void) FormatLocaleString(message,MagickPathExtent,
5097 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
5098 primitive_info[j].point.y);
5099 (void) WriteBlobString(image,message);
5100 for (p=token; *p != '\0'; p++)
5103 case '<': (void) WriteBlobString(image,"<"); break;
5104 case '>': (void) WriteBlobString(image,">"); break;
5105 case '&': (void) WriteBlobString(image,"&"); break;
5106 default: (void) WriteBlobByte(image,*p); break;
5108 (void) WriteBlobString(image,"</text>\n");
5111 case ImagePrimitive:
5113 if (primitive_info[j].coordinates != 2)
5118 GetNextToken(q,&q,extent,token);
5119 (void) FormatLocaleString(message,MagickPathExtent,
5120 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
5121 "href=\"%s\"/>\n",primitive_info[j].point.x,
5122 primitive_info[j].point.y,primitive_info[j+1].point.x,
5123 primitive_info[j+1].point.y,token);
5124 (void) WriteBlobString(image,message);
5128 if (primitive_info == (PrimitiveInfo *) NULL)
5130 primitive_info[i].primitive=UndefinedPrimitive;
5131 if (status == MagickFalse)
5134 (void) WriteBlobString(image,"</svg>\n");
5136 Relinquish resources.
5138 token=DestroyString(token);
5139 if (primitive_info != (PrimitiveInfo *) NULL)
5140 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
5141 (void) CloseBlob(image);