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://www.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 #if defined(MAGICKCORE_XML_DELEGATE)
78 # if defined(MAGICKCORE_WINDOWS_SUPPORT)
79 # if !defined(__MINGW32__)
80 # include <win32config.h>
83 # include <libxml/parser.h>
84 # include <libxml/xmlmemory.h>
85 # include <libxml/parserInternals.h>
86 # include <libxml/xmlerror.h>
89 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
90 #include "autotrace/autotrace.h"
93 #if defined(MAGICKCORE_RSVG_DELEGATE)
94 #include "librsvg/rsvg.h"
95 #if !defined(LIBRSVG_CHECK_VERSION)
96 #include "librsvg/rsvg-cairo.h"
97 #include "librsvg/librsvg-features.h"
98 #elif !LIBRSVG_CHECK_VERSION(2,36,2)
99 #include "librsvg/rsvg-cairo.h"
100 #include "librsvg/librsvg-features.h"
105 Typedef declarations.
107 typedef struct _BoundingBox
116 typedef struct _ElementInfo
126 typedef struct _SVGInfo
180 #if defined(MAGICKCORE_XML_DELEGATE)
193 Forward declarations.
195 static MagickBooleanType
196 WriteSVGImage(const ImageInfo *,Image *,ExceptionInfo *);
199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
209 % IsSVG()() returns MagickTrue if the image format type, identified by the
210 % magick string, is SVG.
212 % The format of the IsSVG method is:
214 % MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
216 % A description of each parameter follows:
218 % o magick: compare image format pattern against these bytes.
220 % o length: Specifies the length of the magick string.
223 static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
227 if (LocaleNCompare((const char *) magick,"?xml",4) == 0)
232 #if defined(MAGICKCORE_XML_DELEGATE)
234 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
238 % R e a d S V G I m a g e %
242 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244 % ReadSVGImage() reads a Scalable Vector Gaphics file and returns it. It
245 % allocates the memory necessary for the new Image structure and returns a
246 % pointer to the new image.
248 % The format of the ReadSVGImage method is:
250 % Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
252 % A description of each parameter follows:
254 % o image_info: the image info.
256 % o exception: return any errors or warnings in this structure.
260 static SVGInfo *AcquireSVGInfo(void)
265 svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info));
266 if (svg_info == (SVGInfo *) NULL)
267 return((SVGInfo *) NULL);
268 (void) memset(svg_info,0,sizeof(*svg_info));
269 svg_info->text=AcquireString("");
270 svg_info->scale=(double *) AcquireCriticalMemory(sizeof(*svg_info->scale));
271 GetAffineMatrix(&svg_info->affine);
272 svg_info->scale[0]=ExpandAffine(&svg_info->affine);
276 static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
278 if (svg_info->text != (char *) NULL)
279 svg_info->text=DestroyString(svg_info->text);
280 if (svg_info->scale != (double *) NULL)
281 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
282 if (svg_info->title != (char *) NULL)
283 svg_info->title=DestroyString(svg_info->title);
284 if (svg_info->comment != (char *) NULL)
285 svg_info->comment=DestroyString(svg_info->comment);
286 return((SVGInfo *) RelinquishMagickMemory(svg_info));
289 static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
294 token[MagickPathExtent];
302 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
303 assert(string != (const char *) NULL);
304 p=(const char *) string;
305 GetNextToken(p,&p,MagickPathExtent,token);
306 value=StringToDouble(token,&next_token);
307 if (strchr(token,'%') != (char *) NULL)
315 if (svg_info->view_box.width == 0.0)
317 return(svg_info->view_box.width*value/100.0);
321 if (svg_info->view_box.height == 0.0)
323 return(svg_info->view_box.height*value/100.0);
325 alpha=value-svg_info->view_box.width;
326 beta=value-svg_info->view_box.height;
327 return(hypot(alpha,beta)/sqrt(2.0)/100.0);
329 GetNextToken(p,&p,MagickPathExtent,token);
330 if (LocaleNCompare(token,"cm",2) == 0)
331 return(96.0*svg_info->scale[0]/2.54*value);
332 if (LocaleNCompare(token,"em",2) == 0)
333 return(svg_info->pointsize*value);
334 if (LocaleNCompare(token,"ex",2) == 0)
335 return(svg_info->pointsize*value/2.0);
336 if (LocaleNCompare(token,"in",2) == 0)
337 return(96.0*svg_info->scale[0]*value);
338 if (LocaleNCompare(token,"mm",2) == 0)
339 return(96.0*svg_info->scale[0]/25.4*value);
340 if (LocaleNCompare(token,"pc",2) == 0)
341 return(96.0*svg_info->scale[0]/6.0*value);
342 if (LocaleNCompare(token,"pt",2) == 0)
343 return(1.25*svg_info->scale[0]*value);
344 if (LocaleNCompare(token,"px",2) == 0)
349 #if defined(__cplusplus) || defined(c_plusplus)
353 static int SVGIsStandalone(void *context)
359 Is this document tagged standalone?
361 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGIsStandalone()");
362 svg_info=(SVGInfo *) context;
363 return(svg_info->document->standalone == 1);
366 static int SVGHasInternalSubset(void *context)
372 Does this document has an internal subset?
374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
375 " SAX.SVGHasInternalSubset()");
376 svg_info=(SVGInfo *) context;
377 return(svg_info->document->intSubset != NULL);
380 static int SVGHasExternalSubset(void *context)
386 Does this document has an external subset?
388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
389 " SAX.SVGHasExternalSubset()");
390 svg_info=(SVGInfo *) context;
391 return(svg_info->document->extSubset != NULL);
394 static void SVGInternalSubset(void *context,const xmlChar *name,
395 const xmlChar *external_id,const xmlChar *system_id)
401 Does this document has an internal subset?
403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
404 " SAX.internalSubset(%s, %s, %s)",(const char *) name,
405 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
406 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
407 svg_info=(SVGInfo *) context;
408 (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id);
411 static xmlParserInputPtr SVGResolveEntity(void *context,
412 const xmlChar *public_id,const xmlChar *system_id)
421 Special entity resolver, better left to the parser, it has more
422 context than the application layer. The default behaviour is to
423 not resolve the entities, in that case the ENTITY_REF nodes are
424 built in the structure (and the parameter values).
426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
427 " SAX.resolveEntity(%s, %s)",
428 (public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"),
429 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
430 svg_info=(SVGInfo *) context;
431 stream=xmlLoadExternalEntity((const char *) system_id,(const char *)
432 public_id,svg_info->parser);
436 static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
442 Get an entity by name.
444 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGGetEntity(%s)",
446 svg_info=(SVGInfo *) context;
447 return(xmlGetDocEntity(svg_info->document,name));
450 static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
456 Get a parameter entity by name.
458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
459 " SAX.getParameterEntity(%s)",name);
460 svg_info=(SVGInfo *) context;
461 return(xmlGetParameterEntity(svg_info->document,name));
464 static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
465 const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
471 An entity definition has been parsed.
473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
474 " SAX.entityDecl(%s, %d, %s, %s, %s)",name,type,
475 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
476 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content);
477 svg_info=(SVGInfo *) context;
478 if (svg_info->parser->inSubset == 1)
479 (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id,
482 if (svg_info->parser->inSubset == 2)
483 (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
487 static void SVGAttributeDeclaration(void *context,const xmlChar *element,
488 const xmlChar *name,int type,int value,const xmlChar *default_value,
489 xmlEnumerationPtr tree)
502 An attribute definition has been parsed.
504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
505 " SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value,
507 svg_info=(SVGInfo *) context;
508 fullname=(xmlChar *) NULL;
509 prefix=(xmlChar *) NULL;
510 parser=svg_info->parser;
511 fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix);
512 if (parser->inSubset == 1)
513 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset,
514 element,fullname,prefix,(xmlAttributeType) type,
515 (xmlAttributeDefault) value,default_value,tree);
517 if (parser->inSubset == 2)
518 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset,
519 element,fullname,prefix,(xmlAttributeType) type,
520 (xmlAttributeDefault) value,default_value,tree);
521 if (prefix != (xmlChar *) NULL)
523 if (fullname != (xmlChar *) NULL)
527 static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
528 xmlElementContentPtr content)
537 An element definition has been parsed.
539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
540 " SAX.elementDecl(%s, %d, ...)",name,type);
541 svg_info=(SVGInfo *) context;
542 parser=svg_info->parser;
543 if (parser->inSubset == 1)
544 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset,
545 name,(xmlElementTypeVal) type,content);
547 if (parser->inSubset == 2)
548 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
549 name,(xmlElementTypeVal) type,content);
552 static void SVGStripString(char *message)
561 assert(message != (char *) NULL);
562 if (*message == '\0')
567 for (p=message; *p != '\0'; p++)
569 if ((*p == '/') && (*(p+1) == '*'))
571 for (q=p; *q != '\0'; q++)
572 if ((*q == '*') && (*(q+1) == '/'))
574 (void) memcpy(p,q+2,strlen(message)-(q-p));
581 length=strlen(message);
583 while (isspace((int) ((unsigned char) *p)) != 0)
585 if ((*p == '\'') || (*p == '"'))
588 while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
591 if ((*q == '\'') || (*q == '"'))
593 (void) memmove(message,p,(size_t) (q-p+1));
596 Convert newlines to a space.
598 for (p=message; *p != '\0'; p++)
603 static char **SVGKeyValuePairs(void *context,const int key_sentinel,
604 const int value_sentinel,const char *text,size_t *number_tokens)
622 svg_info=(SVGInfo *) context;
624 if (text == (const char *) NULL)
625 return((char **) NULL);
627 tokens=(char **) AcquireQuantumMemory(extent+2UL,sizeof(*tokens));
628 if (tokens == (char **) NULL)
630 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
631 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
632 return((char **) NULL);
635 Convert string to an ASCII list.
639 for (q=p; *q != '\0'; q++)
641 if ((*q != key_sentinel) && (*q != value_sentinel) && (*q != '\0'))
643 if (i == (ssize_t) extent)
646 tokens=(char **) ResizeQuantumMemory(tokens,extent+2,sizeof(*tokens));
647 if (tokens == (char **) NULL)
649 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
650 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
651 return((char **) NULL);
654 tokens[i]=AcquireString(p);
655 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
656 SVGStripString(tokens[i]);
660 tokens[i]=AcquireString(p);
661 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
662 SVGStripString(tokens[i++]);
663 tokens[i]=(char *) NULL;
664 *number_tokens=(size_t) i;
668 static void SVGNotationDeclaration(void *context,const xmlChar *name,
669 const xmlChar *public_id,const xmlChar *system_id)
678 What to do when a notation declaration has been parsed.
680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
681 " SAX.notationDecl(%s, %s, %s)",name,
682 public_id != (const xmlChar *) NULL ? (const char *) public_id : "none",
683 system_id != (const xmlChar *) NULL ? (const char *) system_id : "none");
684 svg_info=(SVGInfo *) context;
685 parser=svg_info->parser;
686 if (parser->inSubset == 1)
687 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
688 name,public_id,system_id);
690 if (parser->inSubset == 2)
691 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
692 name,public_id,system_id);
695 static void SVGProcessStyleElement(void *context,const xmlChar *name,
699 background[MagickPathExtent],
717 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
718 svg_info=(SVGInfo *) context;
719 tokens=SVGKeyValuePairs(context,':',';',style,&number_tokens);
720 if (tokens == (char **) NULL)
722 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
724 keyword=(char *) tokens[i];
725 value=(char *) tokens[i+1];
726 if (LocaleCompare(keyword,"font-size") != 0)
728 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
729 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
730 svg_info->pointsize);
732 color=AcquireString("none");
733 units=AcquireString("userSpaceOnUse");
734 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
736 keyword=(char *) tokens[i];
737 value=(char *) tokens[i+1];
738 (void) LogMagickEvent(CoderEvent,GetMagickModule()," %s: %s",keyword,
745 if (LocaleCompare((const char *) name,"background") == 0)
747 if (LocaleCompare((const char *) name,"svg") == 0)
748 (void) CopyMagickString(background,value,MagickPathExtent);
756 if (LocaleCompare(keyword,"clip-path") == 0)
758 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",value);
761 if (LocaleCompare(keyword,"clip-rule") == 0)
763 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",value);
766 if (LocaleCompare(keyword,"clipPathUnits") == 0)
768 (void) CloneString(&units,value);
769 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
773 if (LocaleCompare(keyword,"color") == 0)
775 (void) CloneString(&color,value);
783 if (LocaleCompare(keyword,"fill") == 0)
785 if (LocaleCompare(value,"currentColor") == 0)
787 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
790 if (LocaleCompare(value,"#000000ff") == 0)
791 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
793 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
796 if (LocaleCompare(keyword,"fillcolor") == 0)
798 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
801 if (LocaleCompare(keyword,"fill-rule") == 0)
803 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",value);
806 if (LocaleCompare(keyword,"fill-opacity") == 0)
808 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
812 if (LocaleCompare(keyword,"font") == 0)
815 family[MagickPathExtent],
816 size[MagickPathExtent],
817 style[MagickPathExtent];
819 if (sscanf(value,"%2048s %2048s %2048s",style,size,family) != 3)
821 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",style);
822 (void) FormatLocaleFile(svg_info->file,"font-size \"%s\"\n",size);
823 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
827 if (LocaleCompare(keyword,"font-family") == 0)
829 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
833 if (LocaleCompare(keyword,"font-stretch") == 0)
835 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
839 if (LocaleCompare(keyword,"font-style") == 0)
841 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",value);
844 if (LocaleCompare(keyword,"font-size") == 0)
846 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
847 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
848 svg_info->pointsize);
851 if (LocaleCompare(keyword,"font-weight") == 0)
853 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
862 if (LocaleCompare(keyword,"offset") == 0)
864 (void) FormatLocaleFile(svg_info->file,"offset %g\n",
865 GetUserSpaceCoordinateValue(svg_info,1,value));
868 if (LocaleCompare(keyword,"opacity") == 0)
870 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
878 if (LocaleCompare(keyword,"stop-color") == 0)
880 (void) CloneString(&svg_info->stop_color,value);
883 if (LocaleCompare(keyword,"stroke") == 0)
885 if (LocaleCompare(value,"currentColor") == 0)
887 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",color);
890 if (LocaleCompare(value,"#000000ff") == 0)
891 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
893 (void) FormatLocaleFile(svg_info->file,
894 "stroke \"%s\"\n",value);
897 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
899 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
900 LocaleCompare(value,"true") == 0);
903 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
905 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
909 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
911 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
912 GetUserSpaceCoordinateValue(svg_info,1,value));
915 if (LocaleCompare(keyword,"stroke-linecap") == 0)
917 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
921 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
923 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
927 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
929 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit \"%s\"\n",
933 if (LocaleCompare(keyword,"stroke-opacity") == 0)
935 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
939 if (LocaleCompare(keyword,"stroke-width") == 0)
941 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
942 GetUserSpaceCoordinateValue(svg_info,1,value));
950 if (LocaleCompare(keyword,"text-align") == 0)
952 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",value);
955 if (LocaleCompare(keyword,"text-anchor") == 0)
957 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
961 if (LocaleCompare(keyword,"text-decoration") == 0)
963 if (LocaleCompare(value,"underline") == 0)
964 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
965 if (LocaleCompare(value,"line-through") == 0)
966 (void) FormatLocaleFile(svg_info->file,"decorate line-through\n");
967 if (LocaleCompare(value,"overline") == 0)
968 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
971 if (LocaleCompare(keyword,"text-antialiasing") == 0)
973 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
974 LocaleCompare(value,"true") == 0);
983 if (units != (char *) NULL)
984 units=DestroyString(units);
985 if (color != (char *) NULL)
986 color=DestroyString(color);
987 for (i=0; tokens[i] != (char *) NULL; i++)
988 tokens[i]=DestroyString(tokens[i]);
989 tokens=(char **) RelinquishMagickMemory(tokens);
992 static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
993 const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
999 What to do when an unparsed entity declaration is parsed.
1001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1002 " SAX.unparsedEntityDecl(%s, %s, %s, %s)",name,
1003 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
1004 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation);
1005 svg_info=(SVGInfo *) context;
1006 (void) xmlAddDocEntity(svg_info->document,name,
1007 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
1011 static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
1017 Receive the document locator at startup, actually xmlDefaultSAXLocator.
1020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1021 " SAX.setDocumentLocator()");
1022 svg_info=(SVGInfo *) context;
1026 static void SVGStartDocument(void *context)
1035 Called when the document start being processed.
1037 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startDocument()");
1038 svg_info=(SVGInfo *) context;
1039 parser=svg_info->parser;
1040 svg_info->document=xmlNewDoc(parser->version);
1041 if (svg_info->document == (xmlDocPtr) NULL)
1043 if (parser->encoding == NULL)
1044 svg_info->document->encoding=(const xmlChar *) NULL;
1046 svg_info->document->encoding=xmlStrdup(parser->encoding);
1047 svg_info->document->standalone=parser->standalone;
1050 static void SVGEndDocument(void *context)
1056 Called when the document end has been detected.
1058 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()");
1059 svg_info=(SVGInfo *) context;
1060 if (svg_info->offset != (char *) NULL)
1061 svg_info->offset=DestroyString(svg_info->offset);
1062 if (svg_info->stop_color != (char *) NULL)
1063 svg_info->stop_color=DestroyString(svg_info->stop_color);
1064 if (svg_info->scale != (double *) NULL)
1065 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
1066 if (svg_info->text != (char *) NULL)
1067 svg_info->text=DestroyString(svg_info->text);
1068 if (svg_info->vertices != (char *) NULL)
1069 svg_info->vertices=DestroyString(svg_info->vertices);
1070 if (svg_info->url != (char *) NULL)
1071 svg_info->url=DestroyString(svg_info->url);
1072 #if defined(MAGICKCORE_XML_DELEGATE)
1073 if (svg_info->document != (xmlDocPtr) NULL)
1075 xmlFreeDoc(svg_info->document);
1076 svg_info->document=(xmlDocPtr) NULL;
1081 static void SVGStartElement(void *context,const xmlChar *name,
1082 const xmlChar **attributes)
1084 #define PushGraphicContext(id) \
1087 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); \
1089 (void) FormatLocaleFile(svg_info->file,"push graphic-context \"%s\"\n", \
1095 background[MagickPathExtent],
1096 id[MagickPathExtent],
1098 token[MagickPathExtent],
1118 Called when an opening tag has been processed.
1120 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s",
1122 svg_info=(SVGInfo *) context;
1124 svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale,
1125 svg_info->n+1UL,sizeof(*svg_info->scale));
1126 if (svg_info->scale == (double *) NULL)
1128 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
1129 ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
1132 svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
1133 color=AcquireString("none");
1134 units=AcquireString("userSpaceOnUse");
1138 value=(const char *) NULL;
1139 if ((LocaleCompare((char *) name,"image") == 0) ||
1140 (LocaleCompare((char *) name,"pattern") == 0) ||
1141 (LocaleCompare((char *) name,"rect") == 0) ||
1142 (LocaleCompare((char *) name,"text") == 0) ||
1143 (LocaleCompare((char *) name,"use") == 0))
1145 svg_info->bounds.x=0.0;
1146 svg_info->bounds.y=0.0;
1148 if (attributes != (const xmlChar **) NULL)
1149 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1151 keyword=(const char *) attributes[i];
1152 value=(const char *) attributes[i+1];
1158 if (LocaleCompare(keyword,"cx") == 0)
1160 svg_info->element.cx=
1161 GetUserSpaceCoordinateValue(svg_info,1,value);
1164 if (LocaleCompare(keyword,"cy") == 0)
1166 svg_info->element.cy=
1167 GetUserSpaceCoordinateValue(svg_info,-1,value);
1175 if (LocaleCompare(keyword,"fx") == 0)
1177 svg_info->element.major=
1178 GetUserSpaceCoordinateValue(svg_info,1,value);
1181 if (LocaleCompare(keyword,"fy") == 0)
1183 svg_info->element.minor=
1184 GetUserSpaceCoordinateValue(svg_info,-1,value);
1192 if (LocaleCompare(keyword,"height") == 0)
1194 svg_info->bounds.height=
1195 GetUserSpaceCoordinateValue(svg_info,-1,value);
1203 if (LocaleCompare(keyword,"id") == 0)
1205 (void) CopyMagickString(id,value,MagickPathExtent);
1213 if (LocaleCompare(keyword,"r") == 0)
1215 svg_info->element.angle=
1216 GetUserSpaceCoordinateValue(svg_info,0,value);
1224 if (LocaleCompare(keyword,"width") == 0)
1226 svg_info->bounds.width=
1227 GetUserSpaceCoordinateValue(svg_info,1,value);
1235 if (LocaleCompare(keyword,"x") == 0)
1237 if (LocaleCompare((char *) name,"tspan") != 0)
1238 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,
1239 value)-svg_info->center.x;
1242 if (LocaleCompare(keyword,"x1") == 0)
1244 svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
1248 if (LocaleCompare(keyword,"x2") == 0)
1250 svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
1259 if (LocaleCompare(keyword,"y") == 0)
1261 if (LocaleCompare((char *) name,"tspan") != 0)
1262 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,
1263 value)-svg_info->center.y;
1266 if (LocaleCompare(keyword,"y1") == 0)
1268 svg_info->segment.y1=GetUserSpaceCoordinateValue(svg_info,-1,
1272 if (LocaleCompare(keyword,"y2") == 0)
1274 svg_info->segment.y2=GetUserSpaceCoordinateValue(svg_info,-1,
1284 if (strchr((char *) name,':') != (char *) NULL)
1287 Skip over namespace.
1289 for ( ; *name != ':'; name++) ;
1297 if (LocaleCompare((const char *) name,"circle") == 0)
1299 PushGraphicContext(id);
1302 if (LocaleCompare((const char *) name,"clipPath") == 0)
1304 (void) FormatLocaleFile(svg_info->file,"push clip-path \"%s\"\n",id);
1312 if (LocaleCompare((const char *) name,"defs") == 0)
1314 (void) FormatLocaleFile(svg_info->file,"push defs\n");
1322 if (LocaleCompare((const char *) name,"ellipse") == 0)
1324 PushGraphicContext(id);
1332 if (LocaleCompare((const char *) name,"foreignObject") == 0)
1334 PushGraphicContext(id);
1342 if (LocaleCompare((const char *) name,"g") == 0)
1344 PushGraphicContext(id);
1352 if (LocaleCompare((const char *) name,"image") == 0)
1354 PushGraphicContext(id);
1362 if (LocaleCompare((const char *) name,"line") == 0)
1364 PushGraphicContext(id);
1367 if (LocaleCompare((const char *) name,"linearGradient") == 0)
1369 (void) FormatLocaleFile(svg_info->file,
1370 "push gradient \"%s\" linear %g,%g %g,%g\n",id,
1371 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
1372 svg_info->segment.y2);
1380 if (LocaleCompare((const char *) name,"mask") == 0)
1382 (void) FormatLocaleFile(svg_info->file,"push mask \"%s\"\n",id);
1390 if (LocaleCompare((const char *) name,"path") == 0)
1392 PushGraphicContext(id);
1395 if (LocaleCompare((const char *) name,"pattern") == 0)
1397 (void) FormatLocaleFile(svg_info->file,
1398 "push pattern \"%s\" %g,%g %g,%g\n",id,
1399 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
1400 svg_info->bounds.height);
1403 if (LocaleCompare((const char *) name,"polygon") == 0)
1405 PushGraphicContext(id);
1408 if (LocaleCompare((const char *) name,"polyline") == 0)
1410 PushGraphicContext(id);
1418 if (LocaleCompare((const char *) name,"radialGradient") == 0)
1420 (void) FormatLocaleFile(svg_info->file,
1421 "push gradient \"%s\" radial %g,%g %g,%g %g\n",
1422 id,svg_info->element.cx,svg_info->element.cy,
1423 svg_info->element.major,svg_info->element.minor,
1424 svg_info->element.angle);
1427 if (LocaleCompare((const char *) name,"rect") == 0)
1429 PushGraphicContext(id);
1437 if (LocaleCompare((char *) name,"style") == 0)
1439 if (LocaleCompare((const char *) name,"svg") == 0)
1441 svg_info->svgDepth++;
1442 PushGraphicContext(id);
1443 (void) FormatLocaleFile(svg_info->file,"compliance \"SVG\"\n");
1444 (void) FormatLocaleFile(svg_info->file,"fill \"black\"\n");
1445 (void) FormatLocaleFile(svg_info->file,"fill-opacity 1\n");
1446 (void) FormatLocaleFile(svg_info->file,"stroke \"none\"\n");
1447 (void) FormatLocaleFile(svg_info->file,"stroke-width 1\n");
1448 (void) FormatLocaleFile(svg_info->file,"stroke-opacity 1\n");
1449 (void) FormatLocaleFile(svg_info->file,"fill-rule nonzero\n");
1452 if (LocaleCompare((const char *) name,"symbol") == 0)
1454 (void) FormatLocaleFile(svg_info->file,"push symbol\n");
1462 if (LocaleCompare((const char *) name,"text") == 0)
1464 PushGraphicContext(id);
1465 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1466 svg_info->bounds.x,svg_info->bounds.y);
1467 svg_info->center.x=svg_info->bounds.x;
1468 svg_info->center.y=svg_info->bounds.y;
1469 svg_info->bounds.x=0.0;
1470 svg_info->bounds.y=0.0;
1471 svg_info->bounds.width=0.0;
1472 svg_info->bounds.height=0.0;
1475 if (LocaleCompare((const char *) name,"tspan") == 0)
1477 if (*svg_info->text != '\0')
1482 text=EscapeString(svg_info->text,'\'');
1483 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
1484 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1485 svg_info->center.y,text);
1486 text=DestroyString(text);
1487 *svg_info->text='\0';
1489 PushGraphicContext(id);
1497 if (LocaleCompare((char *) name,"use") == 0)
1499 PushGraphicContext(id);
1507 if (attributes != (const xmlChar **) NULL)
1508 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1510 keyword=(const char *) attributes[i];
1511 value=(const char *) attributes[i+1];
1512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1513 " %s = %s",keyword,value);
1519 if (LocaleCompare(keyword,"angle") == 0)
1521 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1522 GetUserSpaceCoordinateValue(svg_info,0,value));
1530 if (LocaleCompare(keyword,"class") == 0)
1537 GetNextToken(p,&p,MagickPathExtent,token);
1539 GetNextToken(p,&p,MagickPathExtent,token);
1542 (void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",
1549 if (LocaleCompare(keyword,"clip-path") == 0)
1551 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",
1555 if (LocaleCompare(keyword,"clip-rule") == 0)
1557 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",
1561 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1563 (void) CloneString(&units,value);
1564 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
1568 if (LocaleCompare(keyword,"color") == 0)
1570 (void) CloneString(&color,value);
1573 if (LocaleCompare(keyword,"cx") == 0)
1575 svg_info->element.cx=
1576 GetUserSpaceCoordinateValue(svg_info,1,value);
1579 if (LocaleCompare(keyword,"cy") == 0)
1581 svg_info->element.cy=
1582 GetUserSpaceCoordinateValue(svg_info,-1,value);
1590 if (LocaleCompare(keyword,"d") == 0)
1592 (void) CloneString(&svg_info->vertices,value);
1595 if (LocaleCompare(keyword,"dx") == 0)
1600 dx=GetUserSpaceCoordinateValue(svg_info,1,value);
1601 svg_info->bounds.x+=dx;
1602 if (LocaleCompare((char *) name,"text") == 0)
1603 (void) FormatLocaleFile(svg_info->file,"translate %g,0.0\n",dx);
1606 if (LocaleCompare(keyword,"dy") == 0)
1611 dy=GetUserSpaceCoordinateValue(svg_info,-1,value);
1612 svg_info->bounds.y+=dy;
1613 if (LocaleCompare((char *) name,"text") == 0)
1614 (void) FormatLocaleFile(svg_info->file,"translate 0.0,%g\n",dy);
1622 if (LocaleCompare(keyword,"fill") == 0)
1624 if (LocaleCompare(value,"currentColor") == 0)
1626 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
1629 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1632 if (LocaleCompare(keyword,"fillcolor") == 0)
1634 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1637 if (LocaleCompare(keyword,"fill-rule") == 0)
1639 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",
1643 if (LocaleCompare(keyword,"fill-opacity") == 0)
1645 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
1649 if (LocaleCompare(keyword,"font-family") == 0)
1651 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
1655 if (LocaleCompare(keyword,"font-stretch") == 0)
1657 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
1661 if (LocaleCompare(keyword,"font-style") == 0)
1663 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
1667 if (LocaleCompare(keyword,"font-size") == 0)
1669 if (LocaleCompare(value,"xx-small") == 0)
1670 svg_info->pointsize=6.144;
1671 else if (LocaleCompare(value,"x-small") == 0)
1672 svg_info->pointsize=7.68;
1673 else if (LocaleCompare(value,"small") == 0)
1674 svg_info->pointsize=9.6;
1675 else if (LocaleCompare(value,"medium") == 0)
1676 svg_info->pointsize=12.0;
1677 else if (LocaleCompare(value,"large") == 0)
1678 svg_info->pointsize=14.4;
1679 else if (LocaleCompare(value,"x-large") == 0)
1680 svg_info->pointsize=17.28;
1681 else if (LocaleCompare(value,"xx-large") == 0)
1682 svg_info->pointsize=20.736;
1684 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,
1686 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1687 svg_info->pointsize);
1690 if (LocaleCompare(keyword,"font-weight") == 0)
1692 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
1701 if (LocaleCompare(keyword,"gradientTransform") == 0)
1708 GetAffineMatrix(&transform);
1709 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1710 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
1711 if (tokens == (char **) NULL)
1713 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
1715 keyword=(char *) tokens[j];
1716 if (keyword == (char *) NULL)
1718 value=(char *) tokens[j+1];
1719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1720 " %s: %s",keyword,value);
1722 GetAffineMatrix(&affine);
1728 if (LocaleCompare(keyword,"matrix") == 0)
1730 p=(const char *) value;
1731 GetNextToken(p,&p,MagickPathExtent,token);
1732 affine.sx=StringToDouble(value,(char **) NULL);
1733 GetNextToken(p,&p,MagickPathExtent,token);
1735 GetNextToken(p,&p,MagickPathExtent,token);
1736 affine.rx=StringToDouble(token,&next_token);
1737 GetNextToken(p,&p,MagickPathExtent,token);
1739 GetNextToken(p,&p,MagickPathExtent,token);
1740 affine.ry=StringToDouble(token,&next_token);
1741 GetNextToken(p,&p,MagickPathExtent,token);
1743 GetNextToken(p,&p,MagickPathExtent,token);
1744 affine.sy=StringToDouble(token,&next_token);
1745 GetNextToken(p,&p,MagickPathExtent,token);
1747 GetNextToken(p,&p,MagickPathExtent,token);
1748 affine.tx=StringToDouble(token,&next_token);
1749 GetNextToken(p,&p,MagickPathExtent,token);
1751 GetNextToken(p,&p,MagickPathExtent,token);
1752 affine.ty=StringToDouble(token,&next_token);
1760 if (LocaleCompare(keyword,"rotate") == 0)
1765 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1766 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1767 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1768 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1769 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1777 if (LocaleCompare(keyword,"scale") == 0)
1779 for (p=(const char *) value; *p != '\0'; p++)
1780 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1783 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1784 affine.sy=affine.sx;
1787 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1788 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1791 if (LocaleCompare(keyword,"skewX") == 0)
1793 affine.sx=svg_info->affine.sx;
1794 affine.ry=tan(DegreesToRadians(fmod(
1795 GetUserSpaceCoordinateValue(svg_info,1,value),
1797 affine.sy=svg_info->affine.sy;
1800 if (LocaleCompare(keyword,"skewY") == 0)
1802 affine.sx=svg_info->affine.sx;
1803 affine.rx=tan(DegreesToRadians(fmod(
1804 GetUserSpaceCoordinateValue(svg_info,-1,value),
1806 affine.sy=svg_info->affine.sy;
1814 if (LocaleCompare(keyword,"translate") == 0)
1816 for (p=(const char *) value; *p != '\0'; p++)
1817 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1820 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1821 affine.ty=affine.tx;
1824 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1832 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1833 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1834 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1835 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1836 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1838 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1841 (void) FormatLocaleFile(svg_info->file,
1842 "affine %g %g %g %g %g %g\n",transform.sx,
1843 transform.rx,transform.ry,transform.sy,transform.tx,
1845 for (j=0; tokens[j] != (char *) NULL; j++)
1846 tokens[j]=DestroyString(tokens[j]);
1847 tokens=(char **) RelinquishMagickMemory(tokens);
1850 if (LocaleCompare(keyword,"gradientUnits") == 0)
1852 (void) CloneString(&units,value);
1853 (void) FormatLocaleFile(svg_info->file,"gradient-units \"%s\"\n",
1862 if (LocaleCompare(keyword,"height") == 0)
1864 svg_info->bounds.height=
1865 GetUserSpaceCoordinateValue(svg_info,-1,value);
1868 if (LocaleCompare(keyword,"href") == 0)
1870 (void) CloneString(&svg_info->url,value);
1878 if (LocaleCompare(keyword,"major") == 0)
1880 svg_info->element.major=
1881 GetUserSpaceCoordinateValue(svg_info,1,value);
1884 if (LocaleCompare(keyword,"mask") == 0)
1886 (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
1889 if (LocaleCompare(keyword,"minor") == 0)
1891 svg_info->element.minor=
1892 GetUserSpaceCoordinateValue(svg_info,-1,value);
1900 if (LocaleCompare(keyword,"offset") == 0)
1902 (void) CloneString(&svg_info->offset,value);
1905 if (LocaleCompare(keyword,"opacity") == 0)
1907 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
1915 if (LocaleCompare(keyword,"path") == 0)
1917 (void) CloneString(&svg_info->url,value);
1920 if (LocaleCompare(keyword,"points") == 0)
1922 (void) CloneString(&svg_info->vertices,value);
1930 if (LocaleCompare(keyword,"r") == 0)
1932 svg_info->element.major=
1933 GetUserSpaceCoordinateValue(svg_info,1,value);
1934 svg_info->element.minor=
1935 GetUserSpaceCoordinateValue(svg_info,-1,value);
1938 if (LocaleCompare(keyword,"rotate") == 0)
1943 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1944 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1945 svg_info->bounds.x,svg_info->bounds.y);
1946 svg_info->bounds.x=0;
1947 svg_info->bounds.y=0;
1948 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
1951 if (LocaleCompare(keyword,"rx") == 0)
1953 if (LocaleCompare((const char *) name,"ellipse") == 0)
1954 svg_info->element.major=
1955 GetUserSpaceCoordinateValue(svg_info,1,value);
1958 GetUserSpaceCoordinateValue(svg_info,1,value);
1961 if (LocaleCompare(keyword,"ry") == 0)
1963 if (LocaleCompare((const char *) name,"ellipse") == 0)
1964 svg_info->element.minor=
1965 GetUserSpaceCoordinateValue(svg_info,-1,value);
1968 GetUserSpaceCoordinateValue(svg_info,-1,value);
1976 if (LocaleCompare(keyword,"stop-color") == 0)
1978 (void) CloneString(&svg_info->stop_color,value);
1981 if (LocaleCompare(keyword,"stroke") == 0)
1983 if (LocaleCompare(value,"currentColor") == 0)
1985 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",
1989 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",value);
1992 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1994 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
1995 LocaleCompare(value,"true") == 0);
1998 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
2000 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
2004 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
2006 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
2007 GetUserSpaceCoordinateValue(svg_info,1,value));
2010 if (LocaleCompare(keyword,"stroke-linecap") == 0)
2012 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
2016 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
2018 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
2022 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
2024 (void) FormatLocaleFile(svg_info->file,
2025 "stroke-miterlimit \"%s\"\n",value);
2028 if (LocaleCompare(keyword,"stroke-opacity") == 0)
2030 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
2034 if (LocaleCompare(keyword,"stroke-width") == 0)
2036 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
2037 GetUserSpaceCoordinateValue(svg_info,1,value));
2040 if (LocaleCompare(keyword,"style") == 0)
2042 SVGProcessStyleElement(context,name,value);
2050 if (LocaleCompare(keyword,"text-align") == 0)
2052 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",
2056 if (LocaleCompare(keyword,"text-anchor") == 0)
2058 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
2062 if (LocaleCompare(keyword,"text-decoration") == 0)
2064 if (LocaleCompare(value,"underline") == 0)
2065 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
2066 if (LocaleCompare(value,"line-through") == 0)
2067 (void) FormatLocaleFile(svg_info->file,
2068 "decorate line-through\n");
2069 if (LocaleCompare(value,"overline") == 0)
2070 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
2073 if (LocaleCompare(keyword,"text-antialiasing") == 0)
2075 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
2076 LocaleCompare(value,"true") == 0);
2079 if (LocaleCompare(keyword,"transform") == 0)
2086 GetAffineMatrix(&transform);
2087 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
2088 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
2089 if (tokens == (char **) NULL)
2091 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2093 keyword=(char *) tokens[j];
2094 value=(char *) tokens[j+1];
2095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2096 " %s: %s",keyword,value);
2098 GetAffineMatrix(&affine);
2104 if (LocaleCompare(keyword,"matrix") == 0)
2106 p=(const char *) value;
2107 GetNextToken(p,&p,MagickPathExtent,token);
2108 affine.sx=StringToDouble(value,(char **) NULL);
2109 GetNextToken(p,&p,MagickPathExtent,token);
2111 GetNextToken(p,&p,MagickPathExtent,token);
2112 affine.rx=StringToDouble(token,&next_token);
2113 GetNextToken(p,&p,MagickPathExtent,token);
2115 GetNextToken(p,&p,MagickPathExtent,token);
2116 affine.ry=StringToDouble(token,&next_token);
2117 GetNextToken(p,&p,MagickPathExtent,token);
2119 GetNextToken(p,&p,MagickPathExtent,token);
2120 affine.sy=StringToDouble(token,&next_token);
2121 GetNextToken(p,&p,MagickPathExtent,token);
2123 GetNextToken(p,&p,MagickPathExtent,token);
2124 affine.tx=StringToDouble(token,&next_token);
2125 GetNextToken(p,&p,MagickPathExtent,token);
2127 GetNextToken(p,&p,MagickPathExtent,token);
2128 affine.ty=StringToDouble(token,&next_token);
2136 if (LocaleCompare(keyword,"rotate") == 0)
2143 p=(const char *) value;
2144 GetNextToken(p,&p,MagickPathExtent,token);
2145 angle=StringToDouble(value,(char **) NULL);
2146 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
2147 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
2148 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
2149 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
2150 GetNextToken(p,&p,MagickPathExtent,token);
2152 GetNextToken(p,&p,MagickPathExtent,token);
2153 x=StringToDouble(token,&next_token)-svg_info->bounds.x;
2154 GetNextToken(p,&p,MagickPathExtent,token);
2156 GetNextToken(p,&p,MagickPathExtent,token);
2157 y=StringToDouble(token,&next_token)-svg_info->bounds.y;
2158 affine.tx=svg_info->bounds.x+x*cos(angle)-y*sin(angle);
2159 affine.ty=svg_info->bounds.y+x*sin(angle)+y*cos(angle);
2167 if (LocaleCompare(keyword,"scale") == 0)
2169 for (p=(const char *) value; *p != '\0'; p++)
2170 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2173 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
2174 affine.sy=affine.sx;
2176 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
2178 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
2181 if (LocaleCompare(keyword,"skewX") == 0)
2183 affine.sx=svg_info->affine.sx;
2184 affine.ry=tan(DegreesToRadians(fmod(
2185 GetUserSpaceCoordinateValue(svg_info,1,value),
2187 affine.sy=svg_info->affine.sy;
2190 if (LocaleCompare(keyword,"skewY") == 0)
2192 affine.sx=svg_info->affine.sx;
2193 affine.rx=tan(DegreesToRadians(fmod(
2194 GetUserSpaceCoordinateValue(svg_info,-1,value),
2196 affine.sy=svg_info->affine.sy;
2204 if (LocaleCompare(keyword,"translate") == 0)
2206 for (p=(const char *) value; *p != '\0'; p++)
2207 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2210 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2211 affine.ty=affine.tx;
2213 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2222 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2223 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2224 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2225 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2226 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2228 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2231 (void) FormatLocaleFile(svg_info->file,
2232 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2233 transform.ry,transform.sy,transform.tx,transform.ty);
2234 for (j=0; tokens[j] != (char *) NULL; j++)
2235 tokens[j]=DestroyString(tokens[j]);
2236 tokens=(char **) RelinquishMagickMemory(tokens);
2244 if (LocaleCompare(keyword,"verts") == 0)
2246 (void) CloneString(&svg_info->vertices,value);
2249 if (LocaleCompare(keyword,"viewBox") == 0)
2251 p=(const char *) value;
2252 GetNextToken(p,&p,MagickPathExtent,token);
2253 svg_info->view_box.x=StringToDouble(token,&next_token);
2254 GetNextToken(p,&p,MagickPathExtent,token);
2256 GetNextToken(p,&p,MagickPathExtent,token);
2257 svg_info->view_box.y=StringToDouble(token,&next_token);
2258 GetNextToken(p,&p,MagickPathExtent,token);
2260 GetNextToken(p,&p,MagickPathExtent,token);
2261 svg_info->view_box.width=StringToDouble(token,
2263 if (svg_info->bounds.width == 0)
2264 svg_info->bounds.width=svg_info->view_box.width;
2265 GetNextToken(p,&p,MagickPathExtent,token);
2267 GetNextToken(p,&p,MagickPathExtent,token);
2268 svg_info->view_box.height=StringToDouble(token,
2270 if (svg_info->bounds.height == 0)
2271 svg_info->bounds.height=svg_info->view_box.height;
2279 if (LocaleCompare(keyword,"width") == 0)
2281 svg_info->bounds.width=
2282 GetUserSpaceCoordinateValue(svg_info,1,value);
2290 if (LocaleCompare(keyword,"x") == 0)
2292 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2295 if (LocaleCompare(keyword,"xlink:href") == 0)
2297 (void) CloneString(&svg_info->url,value);
2300 if (LocaleCompare(keyword,"x1") == 0)
2302 svg_info->segment.x1=
2303 GetUserSpaceCoordinateValue(svg_info,1,value);
2306 if (LocaleCompare(keyword,"x2") == 0)
2308 svg_info->segment.x2=
2309 GetUserSpaceCoordinateValue(svg_info,1,value);
2317 if (LocaleCompare(keyword,"y") == 0)
2319 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2322 if (LocaleCompare(keyword,"y1") == 0)
2324 svg_info->segment.y1=
2325 GetUserSpaceCoordinateValue(svg_info,-1,value);
2328 if (LocaleCompare(keyword,"y2") == 0)
2330 svg_info->segment.y2=
2331 GetUserSpaceCoordinateValue(svg_info,-1,value);
2340 if (LocaleCompare((const char *) name,"svg") == 0)
2342 if (svg_info->document->encoding != (const xmlChar *) NULL)
2343 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2344 (const char *) svg_info->document->encoding);
2345 if (attributes != (const xmlChar **) NULL)
2353 if ((svg_info->view_box.width == 0.0) ||
2354 (svg_info->view_box.height == 0.0))
2355 svg_info->view_box=svg_info->bounds;
2357 if (svg_info->bounds.width > 0.0)
2358 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2360 if (svg_info->bounds.height > 0.0)
2361 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2362 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2363 (double) svg_info->width,(double) svg_info->height);
2364 sx=(double) svg_info->width/svg_info->view_box.width;
2365 sy=(double) svg_info->height/svg_info->view_box.height;
2366 tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2368 ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2370 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2372 if ((svg_info->svgDepth == 1) && (*background != '\0'))
2374 PushGraphicContext(id);
2375 (void) FormatLocaleFile(svg_info->file,"fill %s\n",background);
2376 (void) FormatLocaleFile(svg_info->file,
2377 "rectangle 0,0 %g,%g\n",svg_info->view_box.width,
2378 svg_info->view_box.height);
2379 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2383 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2384 if (units != (char *) NULL)
2385 units=DestroyString(units);
2386 if (color != (char *) NULL)
2387 color=DestroyString(color);
2390 static void SVGEndElement(void *context,const xmlChar *name)
2396 Called when the end of an element has been detected.
2398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2399 " SAX.endElement(%s)",name);
2400 svg_info=(SVGInfo *) context;
2401 if (strchr((char *) name,':') != (char *) NULL)
2404 Skip over namespace.
2406 for ( ; *name != ':'; name++) ;
2414 if (LocaleCompare((const char *) name,"circle") == 0)
2416 (void) FormatLocaleFile(svg_info->file,"class \"circle\"\n");
2417 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2418 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2419 svg_info->element.cy+svg_info->element.minor);
2420 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2423 if (LocaleCompare((const char *) name,"clipPath") == 0)
2425 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2433 if (LocaleCompare((const char *) name,"defs") == 0)
2435 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2438 if (LocaleCompare((const char *) name,"desc") == 0)
2443 if (*svg_info->text == '\0')
2445 (void) fputc('#',svg_info->file);
2446 for (p=svg_info->text; *p != '\0'; p++)
2448 (void) fputc(*p,svg_info->file);
2450 (void) fputc('#',svg_info->file);
2452 (void) fputc('\n',svg_info->file);
2453 *svg_info->text='\0';
2461 if (LocaleCompare((const char *) name,"ellipse") == 0)
2466 (void) FormatLocaleFile(svg_info->file,"class \"ellipse\"\n");
2467 angle=svg_info->element.angle;
2468 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2469 svg_info->element.cx,svg_info->element.cy,
2470 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2471 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2472 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2480 if (LocaleCompare((const char *) name,"foreignObject") == 0)
2482 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2490 if (LocaleCompare((const char *) name,"g") == 0)
2492 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2500 if (LocaleCompare((const char *) name,"image") == 0)
2502 (void) FormatLocaleFile(svg_info->file,
2503 "image Over %g,%g %g,%g \"%s\"\n",svg_info->bounds.x,
2504 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2506 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2514 if (LocaleCompare((const char *) name,"line") == 0)
2516 (void) FormatLocaleFile(svg_info->file,"class \"line\"\n");
2517 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2518 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2519 svg_info->segment.y2);
2520 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2523 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2525 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2533 if (LocaleCompare((const char *) name,"mask") == 0)
2535 (void) FormatLocaleFile(svg_info->file,"pop mask\n");
2543 if (LocaleCompare((const char *) name,"pattern") == 0)
2545 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2548 if (LocaleCompare((const char *) name,"path") == 0)
2550 (void) FormatLocaleFile(svg_info->file,"class \"path\"\n");
2551 (void) FormatLocaleFile(svg_info->file,"path \"%s\"\n",
2552 svg_info->vertices);
2553 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2556 if (LocaleCompare((const char *) name,"polygon") == 0)
2558 (void) FormatLocaleFile(svg_info->file,"class \"polygon\"\n");
2559 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2560 svg_info->vertices);
2561 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2564 if (LocaleCompare((const char *) name,"polyline") == 0)
2566 (void) FormatLocaleFile(svg_info->file,"class \"polyline\"\n");
2567 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2568 svg_info->vertices);
2569 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2577 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2579 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2582 if (LocaleCompare((const char *) name,"rect") == 0)
2584 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2586 (void) FormatLocaleFile(svg_info->file,"class \"rect\"\n");
2587 if ((fabs(svg_info->bounds.width-1.0) < MagickEpsilon) &&
2588 (fabs(svg_info->bounds.height-1.0) < MagickEpsilon))
2589 (void) FormatLocaleFile(svg_info->file,"point %g,%g\n",
2590 svg_info->bounds.x,svg_info->bounds.y);
2592 (void) FormatLocaleFile(svg_info->file,
2593 "rectangle %g,%g %g,%g\n",svg_info->bounds.x,
2594 svg_info->bounds.y,svg_info->bounds.x+svg_info->bounds.width,
2595 svg_info->bounds.y+svg_info->bounds.height);
2596 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2599 if (svg_info->radius.x == 0.0)
2600 svg_info->radius.x=svg_info->radius.y;
2601 if (svg_info->radius.y == 0.0)
2602 svg_info->radius.y=svg_info->radius.x;
2603 (void) FormatLocaleFile(svg_info->file,
2604 "roundRectangle %g,%g %g,%g %g,%g\n",
2605 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2606 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2607 svg_info->radius.x,svg_info->radius.y);
2608 svg_info->radius.x=0.0;
2609 svg_info->radius.y=0.0;
2610 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2618 if (LocaleCompare((const char *) name,"stop") == 0)
2620 (void) FormatLocaleFile(svg_info->file,"stop-color \"%s\" %s\n",
2621 svg_info->stop_color,svg_info->offset);
2624 if (LocaleCompare((char *) name,"style") == 0)
2638 Find style definitions in svg_info->text.
2640 tokens=SVGKeyValuePairs(context,'{','}',svg_info->text,
2642 if (tokens == (char **) NULL)
2644 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2646 keyword=(char *) tokens[j];
2647 value=(char *) tokens[j+1];
2648 (void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
2649 *keyword == '.' ? keyword+1 : keyword);
2650 SVGProcessStyleElement(context,name,value);
2651 (void) FormatLocaleFile(svg_info->file,"pop class\n");
2655 if (LocaleCompare((const char *) name,"svg") == 0)
2657 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2658 svg_info->svgDepth--;
2661 if (LocaleCompare((const char *) name,"symbol") == 0)
2663 (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
2671 if (LocaleCompare((const char *) name,"text") == 0)
2673 if (*svg_info->text != '\0')
2678 (void) FormatLocaleFile(svg_info->file,"class \"text\"\n");
2679 text=EscapeString(svg_info->text,'\'');
2680 (void) FormatLocaleFile(svg_info->file,"text 0,0 \"%s\"\n",text);
2681 text=DestroyString(text);
2682 *svg_info->text='\0';
2683 svg_info->center.x=0.0;
2684 svg_info->center.y=0.0;
2686 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2689 if (LocaleCompare((const char *) name,"tspan") == 0)
2691 if (*svg_info->text != '\0')
2696 text=EscapeString(svg_info->text,'\'');
2697 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2698 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
2699 svg_info->center.y,text);
2700 text=DestroyString(text);
2701 *svg_info->text='\0';
2703 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2706 if (LocaleCompare((const char *) name,"title") == 0)
2708 if (*svg_info->text == '\0')
2710 (void) CloneString(&svg_info->title,svg_info->text);
2711 *svg_info->text='\0';
2719 if (LocaleCompare((char *) name,"use") == 0)
2721 if ((svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0))
2722 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2723 svg_info->bounds.x,svg_info->bounds.y);
2724 (void) FormatLocaleFile(svg_info->file,"use \"url(%s)\"\n",
2726 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2734 *svg_info->text='\0';
2735 (void) memset(&svg_info->element,0,sizeof(svg_info->element));
2736 (void) memset(&svg_info->segment,0,sizeof(svg_info->segment));
2740 static void SVGCharacters(void *context,const xmlChar *c,int length)
2755 Receiving some characters from the parser.
2757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2758 " SAX.characters(%s,%.20g)",c,(double) length);
2759 svg_info=(SVGInfo *) context;
2760 text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2761 if (text == (char *) NULL)
2764 for (i=0; i < (ssize_t) length; i++)
2767 SVGStripString(text);
2768 if (svg_info->text == (char *) NULL)
2769 svg_info->text=text;
2772 (void) ConcatenateString(&svg_info->text,text);
2773 text=DestroyString(text);
2777 static void SVGReference(void *context,const xmlChar *name)
2786 Called when an entity reference is detected.
2788 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2790 svg_info=(SVGInfo *) context;
2791 parser=svg_info->parser;
2792 if (parser == (xmlParserCtxtPtr) NULL)
2794 if (parser->node == (xmlNodePtr) NULL)
2797 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2799 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2802 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2808 Receiving some ignorable whitespaces from the parser.
2810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2811 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2812 svg_info=(SVGInfo *) context;
2816 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2817 const xmlChar *data)
2823 A processing instruction has been parsed.
2825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2826 " SAX.processingInstruction(%s, %s)",target,data);
2827 svg_info=(SVGInfo *) context;
2831 static void SVGComment(void *context,const xmlChar *value)
2837 A comment has been parsed.
2839 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
2841 svg_info=(SVGInfo *) context;
2842 if (svg_info->comment != (char *) NULL)
2843 (void) ConcatenateString(&svg_info->comment,"\n");
2844 (void) ConcatenateString(&svg_info->comment,(const char *) value);
2847 static void SVGWarning(void *context,const char *format,...)
2851 reason[MagickPathExtent];
2860 Display and format a warning messages, gives file, line, position and
2863 va_start(operands,format);
2864 svg_info=(SVGInfo *) context;
2865 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
2866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2867 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2868 (void) vsprintf(reason,format,operands);
2870 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2872 message=GetExceptionMessage(errno);
2873 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2874 DelegateWarning,reason,"`%s`",message);
2875 message=DestroyString(message);
2879 static void SVGError(void *context,const char *format,...)
2883 reason[MagickPathExtent];
2892 Display and format a error formats, gives file, line, position and
2895 va_start(operands,format);
2896 svg_info=(SVGInfo *) context;
2897 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
2898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2899 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2900 (void) vsprintf(reason,format,operands);
2902 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2904 message=GetExceptionMessage(errno);
2905 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2906 reason,"`%s`",message);
2907 message=DestroyString(message);
2911 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2923 Called when a pcdata block has been parsed.
2925 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
2927 svg_info=(SVGInfo *) context;
2928 parser=svg_info->parser;
2929 child=xmlGetLastChild(parser->node);
2930 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2932 xmlTextConcat(child,value,length);
2935 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2938 static void SVGExternalSubset(void *context,const xmlChar *name,
2939 const xmlChar *external_id,const xmlChar *system_id)
2954 Does this document has an external subset?
2956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2957 " SAX.externalSubset(%s, %s, %s)",name,
2958 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2959 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2960 svg_info=(SVGInfo *) context;
2961 parser=svg_info->parser;
2962 if (((external_id == NULL) && (system_id == NULL)) ||
2963 ((parser->validate == 0) || (parser->wellFormed == 0) ||
2964 (svg_info->document == 0)))
2966 input=SVGResolveEntity(context,external_id,system_id);
2969 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2970 parser_context=(*parser);
2971 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2972 if (parser->inputTab == (xmlParserInputPtr *) NULL)
2974 parser->errNo=XML_ERR_NO_MEMORY;
2975 parser->input=parser_context.input;
2976 parser->inputNr=parser_context.inputNr;
2977 parser->inputMax=parser_context.inputMax;
2978 parser->inputTab=parser_context.inputTab;
2984 xmlPushInput(parser,input);
2985 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2986 if (input->filename == (char *) NULL)
2987 input->filename=(char *) xmlStrdup(system_id);
2990 input->base=parser->input->cur;
2991 input->cur=parser->input->cur;
2993 xmlParseExternalSubset(parser,external_id,system_id);
2994 while (parser->inputNr > 1)
2995 (void) xmlPopInput(parser);
2996 xmlFreeInputStream(parser->input);
2997 xmlFree(parser->inputTab);
2998 parser->input=parser_context.input;
2999 parser->inputNr=parser_context.inputNr;
3000 parser->inputMax=parser_context.inputMax;
3001 parser->inputTab=parser_context.inputTab;
3004 #if defined(__cplusplus) || defined(c_plusplus)
3009 Static declarations.
3012 SVGDensityGeometry[] = "96.0x96.0";
3015 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3018 filename[MagickPathExtent];
3038 message[MagickPathExtent];
3049 assert(image_info != (const ImageInfo *) NULL);
3050 assert(image_info->signature == MagickCoreSignature);
3051 assert(exception != (ExceptionInfo *) NULL);
3052 if (image_info->debug != MagickFalse)
3053 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3054 image_info->filename);
3055 assert(exception->signature == MagickCoreSignature);
3056 image=AcquireImage(image_info,exception);
3057 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3058 if (status == MagickFalse)
3060 image=DestroyImageList(image);
3061 return((Image *) NULL);
3063 if ((fabs(image->resolution.x) < MagickEpsilon) ||
3064 (fabs(image->resolution.y) < MagickEpsilon))
3072 flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3073 image->resolution.x=geometry_info.rho;
3074 image->resolution.y=geometry_info.sigma;
3075 if ((flags & SigmaValue) == 0)
3076 image->resolution.y=image->resolution.x;
3078 if (LocaleCompare(image_info->magick,"MSVG") != 0)
3083 delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception);
3084 if (delegate_info != (const DelegateInfo *) NULL)
3087 background[MagickPathExtent],
3088 command[MagickPathExtent],
3090 input_filename[MagickPathExtent],
3091 opacity[MagickPathExtent],
3092 output_filename[MagickPathExtent],
3093 unique[MagickPathExtent];
3102 Our best hope for compliance with the SVG standard.
3104 status=AcquireUniqueSymbolicLink(image->filename,input_filename);
3105 (void) AcquireUniqueFilename(output_filename);
3106 (void) AcquireUniqueFilename(unique);
3107 density=AcquireString("");
3108 (void) FormatLocaleString(density,MagickPathExtent,"%.20g,%.20g",
3109 image->resolution.x,image->resolution.y);
3110 (void) FormatLocaleString(background,MagickPathExtent,
3111 "rgb(%.20g%%,%.20g%%,%.20g%%)",
3112 100.0*QuantumScale*image->background_color.red,
3113 100.0*QuantumScale*image->background_color.green,
3114 100.0*QuantumScale*image->background_color.blue);
3115 (void) FormatLocaleString(opacity,MagickPathExtent,"%.20g",
3116 QuantumScale*image->background_color.alpha);
3117 (void) FormatLocaleString(command,MagickPathExtent,
3118 GetDelegateCommands(delegate_info),input_filename,output_filename,
3119 density,background,opacity,unique);
3120 density=DestroyString(density);
3121 status=ExternalDelegateCommand(MagickFalse,image_info->verbose,
3122 command,(char *) NULL,exception);
3123 (void) RelinquishUniqueFileResource(unique);
3124 (void) RelinquishUniqueFileResource(input_filename);
3125 if ((status == 0) && (stat(output_filename,&attributes) == 0) &&
3126 (attributes.st_size > 0))
3134 read_info=CloneImageInfo(image_info);
3135 (void) CopyMagickString(read_info->filename,output_filename,
3137 svg_image=ReadImage(read_info,exception);
3138 read_info=DestroyImageInfo(read_info);
3139 (void) RelinquishUniqueFileResource(output_filename);
3140 if (svg_image != (Image *) NULL)
3142 for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; )
3144 (void) CopyMagickString(next->filename,image->filename,
3146 (void) CopyMagickString(next->magick,image->magick,
3148 next=GetNextImageInList(next);
3150 image=DestroyImage(image);
3154 (void) RelinquishUniqueFileResource(output_filename);
3157 #if defined(MAGICKCORE_RSVG_DELEGATE)
3158 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3171 register unsigned char
3184 register const guchar
3206 svg_handle=rsvg_handle_new();
3207 if (svg_handle == (RsvgHandle *) NULL)
3208 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3209 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
3210 if ((fabs(image->resolution.x) > MagickEpsilon) &&
3211 (fabs(image->resolution.y) > MagickEpsilon))
3212 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3213 image->resolution.y);
3214 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3217 error=(GError *) NULL;
3218 (void) rsvg_handle_write(svg_handle,message,n,&error);
3219 if (error != (GError *) NULL)
3220 g_error_free(error);
3222 error=(GError *) NULL;
3223 rsvg_handle_close(svg_handle,&error);
3224 if (error != (GError *) NULL)
3225 g_error_free(error);
3226 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3227 apply_density=MagickTrue;
3228 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
3229 if ((image->resolution.x > 0.0) && (image->resolution.y > 0.0))
3235 We should not apply the density when the internal 'factor' is 'i'.
3236 This can be checked by using the trick below.
3238 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x*256,
3239 image->resolution.y*256);
3240 rsvg_handle_get_dimensions(svg_handle,&dpi_dimension_info);
3241 if ((dpi_dimension_info.width != dimension_info.width) ||
3242 (dpi_dimension_info.height != dimension_info.height))
3243 apply_density=MagickFalse;
3244 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3245 image->resolution.y);
3247 if (image_info->size != (char *) NULL)
3249 (void) GetGeometry(image_info->size,(ssize_t *) NULL,
3250 (ssize_t *) NULL,&image->columns,&image->rows);
3251 if ((image->columns != 0) || (image->rows != 0))
3253 image->resolution.x=96.0*image->columns/dimension_info.width;
3254 image->resolution.y=96.0*image->rows/dimension_info.height;
3255 if (fabs(image->resolution.x) < MagickEpsilon)
3256 image->resolution.x=image->resolution.y;
3258 if (fabs(image->resolution.y) < MagickEpsilon)
3259 image->resolution.y=image->resolution.x;
3261 image->resolution.x=image->resolution.y=MagickMin(
3262 image->resolution.x,image->resolution.y);
3263 apply_density=MagickTrue;
3266 if (apply_density != MagickFalse)
3268 image->columns=image->resolution.x*dimension_info.width/96.0;
3269 image->rows=image->resolution.y*dimension_info.height/96.0;
3273 image->columns=dimension_info.width;
3274 image->rows=dimension_info.height;
3276 pixel_info=(MemoryInfo *) NULL;
3278 pixel_buffer=rsvg_handle_get_pixbuf(svg_handle);
3279 rsvg_handle_free(svg_handle);
3280 image->columns=gdk_pixbuf_get_width(pixel_buffer);
3281 image->rows=gdk_pixbuf_get_height(pixel_buffer);
3283 image->alpha_trait=BlendPixelTrait;
3284 status=SetImageExtent(image,image->columns,image->rows,exception);
3285 if (status == MagickFalse)
3287 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
3288 g_object_unref(G_OBJECT(pixel_buffer));
3290 g_object_unref(svg_handle);
3291 ThrowReaderException(MissingDelegateError,
3292 "NoDecodeDelegateForThisImageFormat");
3294 if (image_info->ping == MagickFalse)
3296 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3300 stride=4*image->columns;
3301 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
3302 stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
3303 (int) image->columns);
3305 pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels));
3306 if (pixel_info == (MemoryInfo *) NULL)
3308 g_object_unref(svg_handle);
3309 ThrowReaderException(ResourceLimitError,
3310 "MemoryAllocationFailed");
3312 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3314 (void) SetImageBackgroundColor(image,exception);
3315 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3316 cairo_surface=cairo_image_surface_create_for_data(pixels,
3317 CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int)
3319 if ((cairo_surface == (cairo_surface_t *) NULL) ||
3320 (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS))
3322 if (cairo_surface != (cairo_surface_t *) NULL)
3323 cairo_surface_destroy(cairo_surface);
3324 pixel_info=RelinquishVirtualMemory(pixel_info);
3325 g_object_unref(svg_handle);
3326 ThrowReaderException(ResourceLimitError,
3327 "MemoryAllocationFailed");
3329 cairo_image=cairo_create(cairo_surface);
3330 cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
3331 cairo_paint(cairo_image);
3332 cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
3333 if (apply_density != MagickFalse)
3334 cairo_scale(cairo_image,image->resolution.x/96.0,
3335 image->resolution.y/96.0);
3336 rsvg_handle_render_cairo(svg_handle,cairo_image);
3337 cairo_destroy(cairo_image);
3338 cairo_surface_destroy(cairo_surface);
3339 g_object_unref(svg_handle);
3342 p=gdk_pixbuf_get_pixels(pixel_buffer);
3344 GetPixelInfo(image,&fill_color);
3345 for (y=0; y < (ssize_t) image->rows; y++)
3347 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3348 if (q == (Quantum *) NULL)
3350 for (x=0; x < (ssize_t) image->columns; x++)
3352 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3353 fill_color.blue=ScaleCharToQuantum(*p++);
3354 fill_color.green=ScaleCharToQuantum(*p++);
3355 fill_color.red=ScaleCharToQuantum(*p++);
3357 fill_color.red=ScaleCharToQuantum(*p++);
3358 fill_color.green=ScaleCharToQuantum(*p++);
3359 fill_color.blue=ScaleCharToQuantum(*p++);
3361 fill_color.alpha=ScaleCharToQuantum(*p++);
3362 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3367 gamma=QuantumScale*fill_color.alpha;
3368 gamma=PerceptibleReciprocal(gamma);
3369 fill_color.blue*=gamma;
3370 fill_color.green*=gamma;
3371 fill_color.red*=gamma;
3374 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
3375 GetPixelAlpha(image,q),q);
3376 q+=GetPixelChannels(image);
3378 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3380 if (image->previous == (Image *) NULL)
3382 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
3384 if (status == MagickFalse)
3389 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3390 if (pixel_info != (MemoryInfo *) NULL)
3391 pixel_info=RelinquishVirtualMemory(pixel_info);
3393 g_object_unref(G_OBJECT(pixel_buffer));
3395 (void) CloseBlob(image);
3396 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3398 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3399 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3400 next=GetNextImageInList(next);
3402 return(GetFirstImageInList(image));
3410 unique_file=AcquireUniqueFileResource(filename);
3411 if (unique_file != -1)
3412 file=fdopen(unique_file,"w");
3413 if ((unique_file == -1) || (file == (FILE *) NULL))
3415 (void) CopyMagickString(image->filename,filename,MagickPathExtent);
3416 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
3418 image=DestroyImageList(image);
3419 return((Image *) NULL);
3424 svg_info=AcquireSVGInfo();
3425 if (svg_info == (SVGInfo *) NULL)
3427 (void) fclose(file);
3428 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3430 svg_info->file=file;
3431 svg_info->exception=exception;
3432 svg_info->image=image;
3433 svg_info->image_info=image_info;
3434 svg_info->bounds.width=image->columns;
3435 svg_info->bounds.height=image->rows;
3436 svg_info->svgDepth=0;
3437 if (image_info->size != (char *) NULL)
3438 (void) CloneString(&svg_info->size,image_info->size);
3439 if (image->debug != MagickFalse)
3440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3441 (void) xmlSubstituteEntitiesDefault(1);
3442 (void) memset(&sax_modules,0,sizeof(sax_modules));
3443 sax_modules.internalSubset=SVGInternalSubset;
3444 sax_modules.isStandalone=SVGIsStandalone;
3445 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3446 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3447 sax_modules.resolveEntity=SVGResolveEntity;
3448 sax_modules.getEntity=SVGGetEntity;
3449 sax_modules.entityDecl=SVGEntityDeclaration;
3450 sax_modules.notationDecl=SVGNotationDeclaration;
3451 sax_modules.attributeDecl=SVGAttributeDeclaration;
3452 sax_modules.elementDecl=SVGElementDeclaration;
3453 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3454 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3455 sax_modules.startDocument=SVGStartDocument;
3456 sax_modules.endDocument=SVGEndDocument;
3457 sax_modules.startElement=SVGStartElement;
3458 sax_modules.endElement=SVGEndElement;
3459 sax_modules.reference=SVGReference;
3460 sax_modules.characters=SVGCharacters;
3461 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3462 sax_modules.processingInstruction=SVGProcessingInstructions;
3463 sax_modules.comment=SVGComment;
3464 sax_modules.warning=SVGWarning;
3465 sax_modules.error=SVGError;
3466 sax_modules.fatalError=SVGError;
3467 sax_modules.getParameterEntity=SVGGetParameterEntity;
3468 sax_modules.cdataBlock=SVGCDataBlock;
3469 sax_modules.externalSubset=SVGExternalSubset;
3470 sax_handler=(&sax_modules);
3471 n=ReadBlob(image,MagickPathExtent-1,message);
3475 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3476 message,n,image->filename);
3477 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3480 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3485 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3486 SVGEndDocument(svg_info);
3487 xmlFreeParserCtxt(svg_info->parser);
3488 if (image->debug != MagickFalse)
3489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3490 (void) fclose(file);
3491 (void) CloseBlob(image);
3492 image->columns=svg_info->width;
3493 image->rows=svg_info->height;
3494 if (exception->severity >= ErrorException)
3496 svg_info=DestroySVGInfo(svg_info);
3497 (void) RelinquishUniqueFileResource(filename);
3498 image=DestroyImage(image);
3499 return((Image *) NULL);
3501 if (image_info->ping == MagickFalse)
3509 image=DestroyImage(image);
3510 image=(Image *) NULL;
3511 read_info=CloneImageInfo(image_info);
3512 SetImageInfoBlob(read_info,(void *) NULL,0);
3513 if (read_info->density != (char *) NULL)
3514 read_info->density=DestroyString(read_info->density);
3515 (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
3517 image=ReadImage(read_info,exception);
3518 read_info=DestroyImageInfo(read_info);
3519 if (image != (Image *) NULL)
3520 (void) CopyMagickString(image->filename,image_info->filename,
3524 Relinquish resources.
3526 if (image != (Image *) NULL)
3528 if (svg_info->title != (char *) NULL)
3529 (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3530 if (svg_info->comment != (char *) NULL)
3531 (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3534 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3536 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3537 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3538 next=GetNextImageInList(next);
3540 svg_info=DestroySVGInfo(svg_info);
3541 (void) RelinquishUniqueFileResource(filename);
3542 return(GetFirstImageInList(image));
3547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3551 % R e g i s t e r S V G I m a g e %
3555 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3557 % RegisterSVGImage() adds attributes for the SVG image format to
3558 % the list of supported formats. The attributes include the image format
3559 % tag, a method to read and/or write the format, whether the format
3560 % supports the saving of more than one frame to the same file or blob,
3561 % whether the format supports native in-memory I/O, and a brief
3562 % description of the format.
3564 % The format of the RegisterSVGImage method is:
3566 % size_t RegisterSVGImage(void)
3569 ModuleExport size_t RegisterSVGImage(void)
3572 version[MagickPathExtent];
3578 #if defined(LIBXML_DOTTED_VERSION)
3579 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,
3582 #if defined(MAGICKCORE_RSVG_DELEGATE)
3583 #if !GLIB_CHECK_VERSION(2,35,0)
3586 #if defined(MAGICKCORE_XML_DELEGATE)
3589 (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d",
3590 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3592 entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics");
3593 #if defined(MAGICKCORE_XML_DELEGATE)
3594 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3596 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3597 entry->flags^=CoderBlobSupportFlag;
3598 #if defined(MAGICKCORE_RSVG_DELEGATE)
3599 entry->flags^=CoderDecoderThreadSupportFlag;
3601 entry->mime_type=ConstantString("image/svg+xml");
3602 if (*version != '\0')
3603 entry->version=ConstantString(version);
3604 entry->magick=(IsImageFormatHandler *) IsSVG;
3605 (void) RegisterMagickInfo(entry);
3606 entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics");
3607 #if defined(MAGICKCORE_XML_DELEGATE)
3608 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3610 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3611 entry->flags^=CoderBlobSupportFlag;
3612 #if defined(MAGICKCORE_RSVG_DELEGATE)
3613 entry->flags^=CoderDecoderThreadSupportFlag;
3615 entry->mime_type=ConstantString("image/svg+xml");
3616 if (*version != '\0')
3617 entry->version=ConstantString(version);
3618 entry->magick=(IsImageFormatHandler *) IsSVG;
3619 (void) RegisterMagickInfo(entry);
3620 entry=AcquireMagickInfo("SVG","MSVG",
3621 "ImageMagick's own SVG internal renderer");
3622 #if defined(MAGICKCORE_XML_DELEGATE)
3623 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3625 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3626 entry->flags^=CoderBlobSupportFlag;
3627 #if defined(MAGICKCORE_RSVG_DELEGATE)
3628 entry->flags^=CoderDecoderThreadSupportFlag;
3630 entry->magick=(IsImageFormatHandler *) IsSVG;
3631 (void) RegisterMagickInfo(entry);
3632 return(MagickImageCoderSignature);
3636 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3640 % U n r e g i s t e r S V G I m a g e %
3644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3646 % UnregisterSVGImage() removes format registrations made by the
3647 % SVG module from the list of supported formats.
3649 % The format of the UnregisterSVGImage method is:
3651 % UnregisterSVGImage(void)
3654 ModuleExport void UnregisterSVGImage(void)
3656 (void) UnregisterMagickInfo("SVGZ");
3657 (void) UnregisterMagickInfo("SVG");
3658 (void) UnregisterMagickInfo("MSVG");
3659 #if defined(MAGICKCORE_XML_DELEGATE)
3665 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3669 % W r i t e S V G I m a g e %
3673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3675 % WriteSVGImage() writes a image in the SVG - XML based W3C standard
3678 % The format of the WriteSVGImage method is:
3680 % MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3681 % Image *image,ExceptionInfo *exception)
3683 % A description of each parameter follows.
3685 % o image_info: the image info.
3687 % o image: The image.
3689 % o exception: return any errors or warnings in this structure.
3693 static void AffineToTransform(Image *image,AffineMatrix *affine)
3696 transform[MagickPathExtent];
3698 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3700 if ((fabs(affine->rx) < MagickEpsilon) &&
3701 (fabs(affine->ry) < MagickEpsilon))
3703 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3704 (fabs(affine->sy-1.0) < MagickEpsilon))
3706 (void) WriteBlobString(image,"\">\n");
3709 (void) FormatLocaleString(transform,MagickPathExtent,
3710 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3711 (void) WriteBlobString(image,transform);
3716 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3717 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3718 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3724 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3725 (void) FormatLocaleString(transform,MagickPathExtent,
3726 "\" transform=\"rotate(%g)\">\n",theta);
3727 (void) WriteBlobString(image,transform);
3734 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3735 (fabs(affine->rx) < MagickEpsilon) &&
3736 (fabs(affine->ry) < MagickEpsilon) &&
3737 (fabs(affine->sy-1.0) < MagickEpsilon))
3739 (void) FormatLocaleString(transform,MagickPathExtent,
3740 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3741 (void) WriteBlobString(image,transform);
3745 (void) FormatLocaleString(transform,MagickPathExtent,
3746 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3747 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3748 (void) WriteBlobString(image,transform);
3751 static MagickBooleanType IsPoint(const char *point)
3759 value=strtol(point,&p,10);
3761 return(p != point ? MagickTrue : MagickFalse);
3764 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
3766 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3771 at_fitting_opts_type
3783 register const Quantum
3797 Trace image and write as SVG.
3799 fitting_options=at_fitting_opts_new();
3800 output_options=at_output_opts_new();
3801 (void) SetImageGray(image,exception);
3802 type=GetImageType(image);
3804 if ((type == BilevelType) || (type == GrayscaleType))
3806 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3808 for (y=0; y < (ssize_t) image->rows; y++)
3810 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3811 if (p == (const Quantum *) NULL)
3813 for (x=0; x < (ssize_t) image->columns; x++)
3815 trace->bitmap[i++]=GetPixelRed(image,p);
3816 if (number_planes == 3)
3818 trace->bitmap[i++]=GetPixelGreen(image,p);
3819 trace->bitmap[i++]=GetPixelBlue(image,p);
3821 p+=GetPixelChannels(image);
3824 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3826 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3827 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3832 at_splines_free(splines);
3833 at_bitmap_free(trace);
3834 at_output_opts_free(output_options);
3835 at_fitting_opts_free(fitting_options);
3841 message[MagickPathExtent];
3862 (void) WriteBlobString(image,
3863 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
3864 (void) WriteBlobString(image,
3865 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
3866 (void) WriteBlobString(image,
3867 " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
3868 (void) FormatLocaleString(message,MagickPathExtent,
3869 "<svg version=\"1.1\" id=\"Layer_1\" "
3870 "xmlns=\"http://www.w3.org/2000/svg\" "
3871 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" "
3872 "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" "
3873 "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">",
3874 (double) image->columns,(double) image->rows,
3875 (double) image->columns,(double) image->rows,
3876 (double) image->columns,(double) image->rows);
3877 (void) WriteBlobString(image,message);
3878 clone_image=CloneImage(image,0,0,MagickTrue,exception);
3879 if (clone_image == (Image *) NULL)
3880 return(MagickFalse);
3881 image_info=AcquireImageInfo();
3882 (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent);
3884 blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length,
3886 clone_image=DestroyImage(clone_image);
3887 image_info=DestroyImageInfo(image_info);
3888 if (blob == (unsigned char *) NULL)
3889 return(MagickFalse);
3891 base64=Base64Encode(blob,blob_length,&encode_length);
3892 blob=(unsigned char *) RelinquishMagickMemory(blob);
3893 (void) FormatLocaleString(message,MagickPathExtent,
3894 " <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" "
3895 "x=\"%.20g\" y=\"%.20g\"\n href=\"data:image/png;base64,",
3896 (double) image->scene,(double) image->columns,(double) image->rows,
3897 (double) image->page.x,(double) image->page.y);
3898 (void) WriteBlobString(image,message);
3900 for (i=(ssize_t) encode_length; i > 0; i-=76)
3902 (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p);
3903 (void) WriteBlobString(image,message);
3906 (void) WriteBlobString(image,"\n");
3908 base64=DestroyString(base64);
3909 (void) WriteBlobString(image,"\" />\n");
3910 (void) WriteBlobString(image,"</svg>\n");
3917 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
3918 ExceptionInfo *exception)
3920 #define BezierQuantum 200
3926 keyword[MagickPathExtent],
3927 message[MagickPathExtent],
3928 name[MagickPathExtent],
3931 type[MagickPathExtent];
3972 Open output image file.
3974 assert(image_info != (const ImageInfo *) NULL);
3975 assert(image_info->signature == MagickCoreSignature);
3976 assert(image != (Image *) NULL);
3977 assert(image->signature == MagickCoreSignature);
3978 if (image->debug != MagickFalse)
3979 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3980 assert(exception != (ExceptionInfo *) NULL);
3981 assert(exception->signature == MagickCoreSignature);
3982 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3983 if (status == MagickFalse)
3985 value=GetImageArtifact(image,"SVG");
3986 if (value != (char *) NULL)
3988 (void) WriteBlobString(image,value);
3989 (void) CloseBlob(image);
3992 value=GetImageArtifact(image,"MVG");
3993 if (value == (char *) NULL)
3994 return(TraceSVGImage(image,exception));
3998 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3999 (void) WriteBlobString(image,
4000 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
4001 (void) WriteBlobString(image,
4002 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
4003 (void) FormatLocaleString(message,MagickPathExtent,
4004 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
4006 (void) WriteBlobString(image,message);
4008 Allocate primitive info memory.
4011 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
4012 sizeof(*primitive_info));
4013 if (primitive_info == (PrimitiveInfo *) NULL)
4014 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
4015 GetAffineMatrix(&affine);
4016 token=AcquireString(value);
4017 extent=strlen(token)+MagickPathExtent;
4021 for (q=(const char *) value; *q != '\0'; )
4024 Interpret graphic primitive.
4026 GetNextToken(q,&q,MagickPathExtent,keyword);
4027 if (*keyword == '\0')
4029 if (*keyword == '#')
4034 if (active != MagickFalse)
4036 AffineToTransform(image,&affine);
4039 (void) WriteBlobString(image,"<desc>");
4040 (void) WriteBlobString(image,keyword+1);
4041 for ( ; (*q != '\n') && (*q != '\0'); q++)
4044 case '<': (void) WriteBlobString(image,"<"); break;
4045 case '>': (void) WriteBlobString(image,">"); break;
4046 case '&': (void) WriteBlobString(image,"&"); break;
4047 default: (void) WriteBlobByte(image,*q); break;
4049 (void) WriteBlobString(image,"</desc>\n");
4052 primitive_type=UndefinedPrimitive;
4060 if (LocaleCompare("affine",keyword) == 0)
4062 GetNextToken(q,&q,extent,token);
4063 affine.sx=StringToDouble(token,&next_token);
4064 GetNextToken(q,&q,extent,token);
4066 GetNextToken(q,&q,extent,token);
4067 affine.rx=StringToDouble(token,&next_token);
4068 GetNextToken(q,&q,extent,token);
4070 GetNextToken(q,&q,extent,token);
4071 affine.ry=StringToDouble(token,&next_token);
4072 GetNextToken(q,&q,extent,token);
4074 GetNextToken(q,&q,extent,token);
4075 affine.sy=StringToDouble(token,&next_token);
4076 GetNextToken(q,&q,extent,token);
4078 GetNextToken(q,&q,extent,token);
4079 affine.tx=StringToDouble(token,&next_token);
4080 GetNextToken(q,&q,extent,token);
4082 GetNextToken(q,&q,extent,token);
4083 affine.ty=StringToDouble(token,&next_token);
4086 if (LocaleCompare("alpha",keyword) == 0)
4088 primitive_type=AlphaPrimitive;
4091 if (LocaleCompare("angle",keyword) == 0)
4093 GetNextToken(q,&q,extent,token);
4094 affine.rx=StringToDouble(token,&next_token);
4095 affine.ry=StringToDouble(token,&next_token);
4098 if (LocaleCompare("arc",keyword) == 0)
4100 primitive_type=ArcPrimitive;
4109 if (LocaleCompare("bezier",keyword) == 0)
4111 primitive_type=BezierPrimitive;
4120 if (LocaleCompare("clip-path",keyword) == 0)
4122 GetNextToken(q,&q,extent,token);
4123 (void) FormatLocaleString(message,MagickPathExtent,
4124 "clip-path:url(#%s);",token);
4125 (void) WriteBlobString(image,message);
4128 if (LocaleCompare("clip-rule",keyword) == 0)
4130 GetNextToken(q,&q,extent,token);
4131 (void) FormatLocaleString(message,MagickPathExtent,"clip-rule:%s;",
4133 (void) WriteBlobString(image,message);
4136 if (LocaleCompare("clip-units",keyword) == 0)
4138 GetNextToken(q,&q,extent,token);
4139 (void) FormatLocaleString(message,MagickPathExtent,
4140 "clipPathUnits=%s;",token);
4141 (void) WriteBlobString(image,message);
4144 if (LocaleCompare("circle",keyword) == 0)
4146 primitive_type=CirclePrimitive;
4149 if (LocaleCompare("color",keyword) == 0)
4151 primitive_type=ColorPrimitive;
4160 if (LocaleCompare("decorate",keyword) == 0)
4162 GetNextToken(q,&q,extent,token);
4163 (void) FormatLocaleString(message,MagickPathExtent,
4164 "text-decoration:%s;",token);
4165 (void) WriteBlobString(image,message);
4174 if (LocaleCompare("ellipse",keyword) == 0)
4176 primitive_type=EllipsePrimitive;
4185 if (LocaleCompare("fill",keyword) == 0)
4187 GetNextToken(q,&q,extent,token);
4188 (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;",
4190 (void) WriteBlobString(image,message);
4193 if (LocaleCompare("fill-rule",keyword) == 0)
4195 GetNextToken(q,&q,extent,token);
4196 (void) FormatLocaleString(message,MagickPathExtent,
4197 "fill-rule:%s;",token);
4198 (void) WriteBlobString(image,message);
4201 if (LocaleCompare("fill-opacity",keyword) == 0)
4203 GetNextToken(q,&q,extent,token);
4204 (void) FormatLocaleString(message,MagickPathExtent,
4205 "fill-opacity:%s;",token);
4206 (void) WriteBlobString(image,message);
4209 if (LocaleCompare("font-family",keyword) == 0)
4211 GetNextToken(q,&q,extent,token);
4212 (void) FormatLocaleString(message,MagickPathExtent,
4213 "font-family:%s;",token);
4214 (void) WriteBlobString(image,message);
4217 if (LocaleCompare("font-stretch",keyword) == 0)
4219 GetNextToken(q,&q,extent,token);
4220 (void) FormatLocaleString(message,MagickPathExtent,
4221 "font-stretch:%s;",token);
4222 (void) WriteBlobString(image,message);
4225 if (LocaleCompare("font-style",keyword) == 0)
4227 GetNextToken(q,&q,extent,token);
4228 (void) FormatLocaleString(message,MagickPathExtent,
4229 "font-style:%s;",token);
4230 (void) WriteBlobString(image,message);
4233 if (LocaleCompare("font-size",keyword) == 0)
4235 GetNextToken(q,&q,extent,token);
4236 (void) FormatLocaleString(message,MagickPathExtent,
4237 "font-size:%s;",token);
4238 (void) WriteBlobString(image,message);
4241 if (LocaleCompare("font-weight",keyword) == 0)
4243 GetNextToken(q,&q,extent,token);
4244 (void) FormatLocaleString(message,MagickPathExtent,
4245 "font-weight:%s;",token);
4246 (void) WriteBlobString(image,message);
4255 if (LocaleCompare("gradient-units",keyword) == 0)
4257 GetNextToken(q,&q,extent,token);
4260 if (LocaleCompare("text-align",keyword) == 0)
4262 GetNextToken(q,&q,extent,token);
4263 (void) FormatLocaleString(message,MagickPathExtent,
4264 "text-align %s ",token);
4265 (void) WriteBlobString(image,message);
4268 if (LocaleCompare("text-anchor",keyword) == 0)
4270 GetNextToken(q,&q,extent,token);
4271 (void) FormatLocaleString(message,MagickPathExtent,
4272 "text-anchor %s ",token);
4273 (void) WriteBlobString(image,message);
4282 if (LocaleCompare("image",keyword) == 0)
4284 GetNextToken(q,&q,extent,token);
4285 primitive_type=ImagePrimitive;
4294 if (LocaleCompare("line",keyword) == 0)
4296 primitive_type=LinePrimitive;
4305 if (LocaleCompare("opacity",keyword) == 0)
4307 GetNextToken(q,&q,extent,token);
4308 (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ",
4310 (void) WriteBlobString(image,message);
4319 if (LocaleCompare("path",keyword) == 0)
4321 primitive_type=PathPrimitive;
4324 if (LocaleCompare("point",keyword) == 0)
4326 primitive_type=PointPrimitive;
4329 if (LocaleCompare("polyline",keyword) == 0)
4331 primitive_type=PolylinePrimitive;
4334 if (LocaleCompare("polygon",keyword) == 0)
4336 primitive_type=PolygonPrimitive;
4339 if (LocaleCompare("pop",keyword) == 0)
4341 GetNextToken(q,&q,extent,token);
4342 if (LocaleCompare("clip-path",token) == 0)
4344 (void) WriteBlobString(image,"</clipPath>\n");
4347 if (LocaleCompare("defs",token) == 0)
4349 (void) WriteBlobString(image,"</defs>\n");
4352 if (LocaleCompare("gradient",token) == 0)
4354 (void) FormatLocaleString(message,MagickPathExtent,
4355 "</%sGradient>\n",type);
4356 (void) WriteBlobString(image,message);
4359 if (LocaleCompare("graphic-context",token) == 0)
4363 ThrowWriterException(DrawError,
4364 "UnbalancedGraphicContextPushPop");
4365 (void) WriteBlobString(image,"</g>\n");
4367 if (LocaleCompare("pattern",token) == 0)
4369 (void) WriteBlobString(image,"</pattern>\n");
4372 if (LocaleCompare("symbol",token) == 0)
4374 (void) WriteBlobString(image,"</symbol>\n");
4377 if ((LocaleCompare("defs",token) == 0) ||
4378 (LocaleCompare("symbol",token) == 0))
4379 (void) WriteBlobString(image,"</g>\n");
4382 if (LocaleCompare("push",keyword) == 0)
4384 GetNextToken(q,&q,extent,token);
4385 if (LocaleCompare("clip-path",token) == 0)
4387 GetNextToken(q,&q,extent,token);
4388 (void) FormatLocaleString(message,MagickPathExtent,
4389 "<clipPath id=\"%s\">\n",token);
4390 (void) WriteBlobString(image,message);
4393 if (LocaleCompare("defs",token) == 0)
4395 (void) WriteBlobString(image,"<defs>\n");
4398 if (LocaleCompare("gradient",token) == 0)
4400 GetNextToken(q,&q,extent,token);
4401 (void) CopyMagickString(name,token,MagickPathExtent);
4402 GetNextToken(q,&q,extent,token);
4403 (void) CopyMagickString(type,token,MagickPathExtent);
4404 GetNextToken(q,&q,extent,token);
4405 svg_info.segment.x1=StringToDouble(token,&next_token);
4406 svg_info.element.cx=StringToDouble(token,&next_token);
4407 GetNextToken(q,&q,extent,token);
4409 GetNextToken(q,&q,extent,token);
4410 svg_info.segment.y1=StringToDouble(token,&next_token);
4411 svg_info.element.cy=StringToDouble(token,&next_token);
4412 GetNextToken(q,&q,extent,token);
4414 GetNextToken(q,&q,extent,token);
4415 svg_info.segment.x2=StringToDouble(token,&next_token);
4416 svg_info.element.major=StringToDouble(token,
4418 GetNextToken(q,&q,extent,token);
4420 GetNextToken(q,&q,extent,token);
4421 svg_info.segment.y2=StringToDouble(token,&next_token);
4422 svg_info.element.minor=StringToDouble(token,
4424 (void) FormatLocaleString(message,MagickPathExtent,
4425 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
4426 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
4427 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
4428 if (LocaleCompare(type,"radial") == 0)
4430 GetNextToken(q,&q,extent,token);
4432 GetNextToken(q,&q,extent,token);
4433 svg_info.element.angle=StringToDouble(token,
4435 (void) FormatLocaleString(message,MagickPathExtent,
4436 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
4437 "fx=\"%g\" fy=\"%g\">\n",type,name,
4438 svg_info.element.cx,svg_info.element.cy,
4439 svg_info.element.angle,svg_info.element.major,
4440 svg_info.element.minor);
4442 (void) WriteBlobString(image,message);
4445 if (LocaleCompare("graphic-context",token) == 0)
4450 AffineToTransform(image,&affine);
4453 (void) WriteBlobString(image,"<g style=\"");
4456 if (LocaleCompare("pattern",token) == 0)
4458 GetNextToken(q,&q,extent,token);
4459 (void) CopyMagickString(name,token,MagickPathExtent);
4460 GetNextToken(q,&q,extent,token);
4461 svg_info.bounds.x=StringToDouble(token,&next_token);
4462 GetNextToken(q,&q,extent,token);
4464 GetNextToken(q,&q,extent,token);
4465 svg_info.bounds.y=StringToDouble(token,&next_token);
4466 GetNextToken(q,&q,extent,token);
4468 GetNextToken(q,&q,extent,token);
4469 svg_info.bounds.width=StringToDouble(token,
4471 GetNextToken(q,&q,extent,token);
4473 GetNextToken(q,&q,extent,token);
4474 svg_info.bounds.height=StringToDouble(token,(char **) NULL);
4475 (void) FormatLocaleString(message,MagickPathExtent,
4476 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
4477 "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y,
4478 svg_info.bounds.width,svg_info.bounds.height);
4479 (void) WriteBlobString(image,message);
4482 if (LocaleCompare("symbol",token) == 0)
4484 (void) WriteBlobString(image,"<symbol>\n");
4495 if (LocaleCompare("rectangle",keyword) == 0)
4497 primitive_type=RectanglePrimitive;
4500 if (LocaleCompare("roundRectangle",keyword) == 0)
4502 primitive_type=RoundRectanglePrimitive;
4505 if (LocaleCompare("rotate",keyword) == 0)
4507 GetNextToken(q,&q,extent,token);
4508 (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ",
4510 (void) WriteBlobString(image,message);
4519 if (LocaleCompare("scale",keyword) == 0)
4521 GetNextToken(q,&q,extent,token);
4522 affine.sx=StringToDouble(token,&next_token);
4523 GetNextToken(q,&q,extent,token);
4525 GetNextToken(q,&q,extent,token);
4526 affine.sy=StringToDouble(token,&next_token);
4529 if (LocaleCompare("skewX",keyword) == 0)
4531 GetNextToken(q,&q,extent,token);
4532 (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ",
4534 (void) WriteBlobString(image,message);
4537 if (LocaleCompare("skewY",keyword) == 0)
4539 GetNextToken(q,&q,extent,token);
4540 (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ",
4542 (void) WriteBlobString(image,message);
4545 if (LocaleCompare("stop-color",keyword) == 0)
4548 color[MagickPathExtent];
4550 GetNextToken(q,&q,extent,token);
4551 (void) CopyMagickString(color,token,MagickPathExtent);
4552 GetNextToken(q,&q,extent,token);
4553 (void) FormatLocaleString(message,MagickPathExtent,
4554 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4555 (void) WriteBlobString(image,message);
4558 if (LocaleCompare("stroke",keyword) == 0)
4560 GetNextToken(q,&q,extent,token);
4561 (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;",
4563 (void) WriteBlobString(image,message);
4566 if (LocaleCompare("stroke-antialias",keyword) == 0)
4568 GetNextToken(q,&q,extent,token);
4569 (void) FormatLocaleString(message,MagickPathExtent,
4570 "stroke-antialias:%s;",token);
4571 (void) WriteBlobString(image,message);
4574 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4582 GetNextToken(p,&p,extent,token);
4583 for (k=0; IsPoint(token); k++)
4584 GetNextToken(p,&p,extent,token);
4585 (void) WriteBlobString(image,"stroke-dasharray:");
4586 for (j=0; j < k; j++)
4588 GetNextToken(q,&q,extent,token);
4589 (void) FormatLocaleString(message,MagickPathExtent,"%s ",
4591 (void) WriteBlobString(image,message);
4593 (void) WriteBlobString(image,";");
4596 GetNextToken(q,&q,extent,token);
4597 (void) FormatLocaleString(message,MagickPathExtent,
4598 "stroke-dasharray:%s;",token);
4599 (void) WriteBlobString(image,message);
4602 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4604 GetNextToken(q,&q,extent,token);
4605 (void) FormatLocaleString(message,MagickPathExtent,
4606 "stroke-dashoffset:%s;",token);
4607 (void) WriteBlobString(image,message);
4610 if (LocaleCompare("stroke-linecap",keyword) == 0)
4612 GetNextToken(q,&q,extent,token);
4613 (void) FormatLocaleString(message,MagickPathExtent,
4614 "stroke-linecap:%s;",token);
4615 (void) WriteBlobString(image,message);
4618 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4620 GetNextToken(q,&q,extent,token);
4621 (void) FormatLocaleString(message,MagickPathExtent,
4622 "stroke-linejoin:%s;",token);
4623 (void) WriteBlobString(image,message);
4626 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4628 GetNextToken(q,&q,extent,token);
4629 (void) FormatLocaleString(message,MagickPathExtent,
4630 "stroke-miterlimit:%s;",token);
4631 (void) WriteBlobString(image,message);
4634 if (LocaleCompare("stroke-opacity",keyword) == 0)
4636 GetNextToken(q,&q,extent,token);
4637 (void) FormatLocaleString(message,MagickPathExtent,
4638 "stroke-opacity:%s;",token);
4639 (void) WriteBlobString(image,message);
4642 if (LocaleCompare("stroke-width",keyword) == 0)
4644 GetNextToken(q,&q,extent,token);
4645 (void) FormatLocaleString(message,MagickPathExtent,
4646 "stroke-width:%s;",token);
4647 (void) WriteBlobString(image,message);
4656 if (LocaleCompare("text",keyword) == 0)
4658 primitive_type=TextPrimitive;
4661 if (LocaleCompare("text-antialias",keyword) == 0)
4663 GetNextToken(q,&q,extent,token);
4664 (void) FormatLocaleString(message,MagickPathExtent,
4665 "text-antialias:%s;",token);
4666 (void) WriteBlobString(image,message);
4669 if (LocaleCompare("tspan",keyword) == 0)
4671 primitive_type=TextPrimitive;
4674 if (LocaleCompare("translate",keyword) == 0)
4676 GetNextToken(q,&q,extent,token);
4677 affine.tx=StringToDouble(token,&next_token);
4678 GetNextToken(q,&q,extent,token);
4680 GetNextToken(q,&q,extent,token);
4681 affine.ty=StringToDouble(token,&next_token);
4690 if (LocaleCompare("viewbox",keyword) == 0)
4692 GetNextToken(q,&q,extent,token);
4694 GetNextToken(q,&q,extent,token);
4695 GetNextToken(q,&q,extent,token);
4697 GetNextToken(q,&q,extent,token);
4698 GetNextToken(q,&q,extent,token);
4700 GetNextToken(q,&q,extent,token);
4701 GetNextToken(q,&q,extent,token);
4713 if (status == MagickFalse)
4715 if (primitive_type == UndefinedPrimitive)
4718 Parse the primitive attributes.
4722 for (x=0; *q != '\0'; x++)
4727 if (IsPoint(q) == MagickFalse)
4729 GetNextToken(q,&q,extent,token);
4730 point.x=StringToDouble(token,&next_token);
4731 GetNextToken(q,&q,extent,token);
4733 GetNextToken(q,&q,extent,token);
4734 point.y=StringToDouble(token,&next_token);
4735 GetNextToken(q,(const char **) NULL,extent,token);
4737 GetNextToken(q,&q,extent,token);
4738 primitive_info[i].primitive=primitive_type;
4739 primitive_info[i].point=point;
4740 primitive_info[i].coordinates=0;
4741 primitive_info[i].method=FloodfillMethod;
4743 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4745 number_points+=6*BezierQuantum+360;
4746 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4747 number_points,sizeof(*primitive_info));
4748 if (primitive_info == (PrimitiveInfo *) NULL)
4750 (void) ThrowMagickException(exception,GetMagickModule(),
4751 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4755 primitive_info[j].primitive=primitive_type;
4756 primitive_info[j].coordinates=x;
4757 primitive_info[j].method=FloodfillMethod;
4758 primitive_info[j].text=(char *) NULL;
4761 AffineToTransform(image,&affine);
4765 switch (primitive_type)
4767 case PointPrimitive:
4770 if (primitive_info[j].coordinates != 1)
4779 if (primitive_info[j].coordinates != 2)
4784 (void) FormatLocaleString(message,MagickPathExtent,
4785 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4786 primitive_info[j].point.x,primitive_info[j].point.y,
4787 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4788 (void) WriteBlobString(image,message);
4791 case RectanglePrimitive:
4793 if (primitive_info[j].coordinates != 2)
4798 (void) FormatLocaleString(message,MagickPathExtent,
4799 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4800 primitive_info[j].point.x,primitive_info[j].point.y,
4801 primitive_info[j+1].point.x-primitive_info[j].point.x,
4802 primitive_info[j+1].point.y-primitive_info[j].point.y);
4803 (void) WriteBlobString(image,message);
4806 case RoundRectanglePrimitive:
4808 if (primitive_info[j].coordinates != 3)
4813 (void) FormatLocaleString(message,MagickPathExtent,
4814 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4815 "ry=\"%g\"/>\n",primitive_info[j].point.x,
4816 primitive_info[j].point.y,primitive_info[j+1].point.x-
4817 primitive_info[j].point.x,primitive_info[j+1].point.y-
4818 primitive_info[j].point.y,primitive_info[j+2].point.x,
4819 primitive_info[j+2].point.y);
4820 (void) WriteBlobString(image,message);
4825 if (primitive_info[j].coordinates != 3)
4832 case EllipsePrimitive:
4834 if (primitive_info[j].coordinates != 3)
4839 (void) FormatLocaleString(message,MagickPathExtent,
4840 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
4841 primitive_info[j].point.x,primitive_info[j].point.y,
4842 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4843 (void) WriteBlobString(image,message);
4846 case CirclePrimitive:
4852 if (primitive_info[j].coordinates != 2)
4857 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4858 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4859 (void) FormatLocaleString(message,MagickPathExtent,
4860 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
4861 primitive_info[j].point.x,primitive_info[j].point.y,
4863 (void) WriteBlobString(image,message);
4866 case PolylinePrimitive:
4868 if (primitive_info[j].coordinates < 2)
4873 (void) CopyMagickString(message," <polyline points=\"",
4875 (void) WriteBlobString(image,message);
4876 length=strlen(message);
4879 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4880 primitive_info[j].point.x,primitive_info[j].point.y);
4881 length+=strlen(message);
4884 (void) WriteBlobString(image,"\n ");
4885 length=strlen(message)+5;
4887 (void) WriteBlobString(image,message);
4889 (void) WriteBlobString(image,"\"/>\n");
4892 case PolygonPrimitive:
4894 if (primitive_info[j].coordinates < 3)
4899 primitive_info[i]=primitive_info[j];
4900 primitive_info[i].coordinates=0;
4901 primitive_info[j].coordinates++;
4903 (void) CopyMagickString(message," <polygon points=\"",MagickPathExtent);
4904 (void) WriteBlobString(image,message);
4905 length=strlen(message);
4908 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4909 primitive_info[j].point.x,primitive_info[j].point.y);
4910 length+=strlen(message);
4913 (void) WriteBlobString(image,"\n ");
4914 length=strlen(message)+5;
4916 (void) WriteBlobString(image,message);
4918 (void) WriteBlobString(image,"\"/>\n");
4921 case BezierPrimitive:
4923 if (primitive_info[j].coordinates < 3)
4935 GetNextToken(q,&q,extent,token);
4936 number_attributes=1;
4937 for (p=token; *p != '\0'; p++)
4938 if (isalpha((int) *p))
4939 number_attributes++;
4940 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
4942 number_points+=6*BezierQuantum*number_attributes;
4943 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4944 number_points,sizeof(*primitive_info));
4945 if (primitive_info == (PrimitiveInfo *) NULL)
4947 (void) ThrowMagickException(exception,GetMagickModule(),
4948 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4953 (void) WriteBlobString(image," <path d=\"");
4954 (void) WriteBlobString(image,token);
4955 (void) WriteBlobString(image,"\"/>\n");
4958 case AlphaPrimitive:
4959 case ColorPrimitive:
4961 if (primitive_info[j].coordinates != 1)
4966 GetNextToken(q,&q,extent,token);
4967 if (LocaleCompare("point",token) == 0)
4968 primitive_info[j].method=PointMethod;
4969 if (LocaleCompare("replace",token) == 0)
4970 primitive_info[j].method=ReplaceMethod;
4971 if (LocaleCompare("floodfill",token) == 0)
4972 primitive_info[j].method=FloodfillMethod;
4973 if (LocaleCompare("filltoborder",token) == 0)
4974 primitive_info[j].method=FillToBorderMethod;
4975 if (LocaleCompare("reset",token) == 0)
4976 primitive_info[j].method=ResetMethod;
4984 if (primitive_info[j].coordinates != 1)
4989 GetNextToken(q,&q,extent,token);
4990 (void) FormatLocaleString(message,MagickPathExtent,
4991 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
4992 primitive_info[j].point.y);
4993 (void) WriteBlobString(image,message);
4994 for (p=token; *p != '\0'; p++)
4997 case '<': (void) WriteBlobString(image,"<"); break;
4998 case '>': (void) WriteBlobString(image,">"); break;
4999 case '&': (void) WriteBlobString(image,"&"); break;
5000 default: (void) WriteBlobByte(image,*p); break;
5002 (void) WriteBlobString(image,"</text>\n");
5005 case ImagePrimitive:
5007 if (primitive_info[j].coordinates != 2)
5012 GetNextToken(q,&q,extent,token);
5013 (void) FormatLocaleString(message,MagickPathExtent,
5014 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
5015 "href=\"%s\"/>\n",primitive_info[j].point.x,
5016 primitive_info[j].point.y,primitive_info[j+1].point.x,
5017 primitive_info[j+1].point.y,token);
5018 (void) WriteBlobString(image,message);
5022 if (primitive_info == (PrimitiveInfo *) NULL)
5024 primitive_info[i].primitive=UndefinedPrimitive;
5025 if (status == MagickFalse)
5028 (void) WriteBlobString(image,"</svg>\n");
5030 Relinquish resources.
5032 token=DestroyString(token);
5033 if (primitive_info != (PrimitiveInfo *) NULL)
5034 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
5035 (void) CloseBlob(image);