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+1,"svg",3) == 0)
231 if (LocaleNCompare((const char *) magick+1,"?xml",4) == 0)
236 #if defined(MAGICKCORE_XML_DELEGATE)
238 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
242 % R e a d S V G I m a g e %
246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
248 % ReadSVGImage() reads a Scalable Vector Gaphics file and returns it. It
249 % allocates the memory necessary for the new Image structure and returns a
250 % pointer to the new image.
252 % The format of the ReadSVGImage method is:
254 % Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
256 % A description of each parameter follows:
258 % o image_info: the image info.
260 % o exception: return any errors or warnings in this structure.
264 static SVGInfo *AcquireSVGInfo(void)
269 svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info));
270 if (svg_info == (SVGInfo *) NULL)
271 return((SVGInfo *) NULL);
272 (void) memset(svg_info,0,sizeof(*svg_info));
273 svg_info->text=AcquireString("");
274 svg_info->scale=(double *) AcquireCriticalMemory(sizeof(*svg_info->scale));
275 GetAffineMatrix(&svg_info->affine);
276 svg_info->scale[0]=ExpandAffine(&svg_info->affine);
280 static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
282 if (svg_info->text != (char *) NULL)
283 svg_info->text=DestroyString(svg_info->text);
284 if (svg_info->scale != (double *) NULL)
285 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
286 if (svg_info->title != (char *) NULL)
287 svg_info->title=DestroyString(svg_info->title);
288 if (svg_info->comment != (char *) NULL)
289 svg_info->comment=DestroyString(svg_info->comment);
290 return((SVGInfo *) RelinquishMagickMemory(svg_info));
293 static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
298 token[MagickPathExtent];
306 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
307 assert(string != (const char *) NULL);
308 p=(const char *) string;
309 GetNextToken(p,&p,MagickPathExtent,token);
310 value=StringToDouble(token,&next_token);
311 if (strchr(token,'%') != (char *) NULL)
319 if (svg_info->view_box.width == 0.0)
321 return(svg_info->view_box.width*value/100.0);
325 if (svg_info->view_box.height == 0.0)
327 return(svg_info->view_box.height*value/100.0);
329 alpha=value-svg_info->view_box.width;
330 beta=value-svg_info->view_box.height;
331 return(hypot(alpha,beta)/sqrt(2.0)/100.0);
333 GetNextToken(p,&p,MagickPathExtent,token);
334 if (LocaleNCompare(token,"cm",2) == 0)
335 return(72.0*svg_info->scale[0]/2.54*value);
336 if (LocaleNCompare(token,"em",2) == 0)
337 return(svg_info->pointsize*value);
338 if (LocaleNCompare(token,"ex",2) == 0)
339 return(svg_info->pointsize*value/2.0);
340 if (LocaleNCompare(token,"in",2) == 0)
341 return(72.0*svg_info->scale[0]*value);
342 if (LocaleNCompare(token,"mm",2) == 0)
343 return(72.0*svg_info->scale[0]/25.4*value);
344 if (LocaleNCompare(token,"pc",2) == 0)
345 return(72.0*svg_info->scale[0]/6.0*value);
346 if (LocaleNCompare(token,"pt",2) == 0)
347 return(svg_info->scale[0]*value);
348 if (LocaleNCompare(token,"px",2) == 0)
353 #if defined(__cplusplus) || defined(c_plusplus)
357 static int SVGIsStandalone(void *context)
363 Is this document tagged standalone?
365 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGIsStandalone()");
366 svg_info=(SVGInfo *) context;
367 return(svg_info->document->standalone == 1);
370 static int SVGHasInternalSubset(void *context)
376 Does this document has an internal subset?
378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
379 " SAX.SVGHasInternalSubset()");
380 svg_info=(SVGInfo *) context;
381 return(svg_info->document->intSubset != NULL);
384 static int SVGHasExternalSubset(void *context)
390 Does this document has an external subset?
392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
393 " SAX.SVGHasExternalSubset()");
394 svg_info=(SVGInfo *) context;
395 return(svg_info->document->extSubset != NULL);
398 static void SVGInternalSubset(void *context,const xmlChar *name,
399 const xmlChar *external_id,const xmlChar *system_id)
405 Does this document has an internal subset?
407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
408 " SAX.internalSubset(%s, %s, %s)",(const char *) name,
409 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
410 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
411 svg_info=(SVGInfo *) context;
412 (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id);
415 static xmlParserInputPtr SVGResolveEntity(void *context,
416 const xmlChar *public_id,const xmlChar *system_id)
425 Special entity resolver, better left to the parser, it has more
426 context than the application layer. The default behaviour is to
427 not resolve the entities, in that case the ENTITY_REF nodes are
428 built in the structure (and the parameter values).
430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
431 " SAX.resolveEntity(%s, %s)",
432 (public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"),
433 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
434 svg_info=(SVGInfo *) context;
435 stream=xmlLoadExternalEntity((const char *) system_id,(const char *)
436 public_id,svg_info->parser);
440 static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
446 Get an entity by name.
448 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGGetEntity(%s)",
450 svg_info=(SVGInfo *) context;
451 return(xmlGetDocEntity(svg_info->document,name));
454 static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
460 Get a parameter entity by name.
462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
463 " SAX.getParameterEntity(%s)",name);
464 svg_info=(SVGInfo *) context;
465 return(xmlGetParameterEntity(svg_info->document,name));
468 static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
469 const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
475 An entity definition has been parsed.
477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
478 " SAX.entityDecl(%s, %d, %s, %s, %s)",name,type,
479 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
480 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content);
481 svg_info=(SVGInfo *) context;
482 if (svg_info->parser->inSubset == 1)
483 (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id,
486 if (svg_info->parser->inSubset == 2)
487 (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
491 static void SVGAttributeDeclaration(void *context,const xmlChar *element,
492 const xmlChar *name,int type,int value,const xmlChar *default_value,
493 xmlEnumerationPtr tree)
506 An attribute definition has been parsed.
508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
509 " SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value,
511 svg_info=(SVGInfo *) context;
512 fullname=(xmlChar *) NULL;
513 prefix=(xmlChar *) NULL;
514 parser=svg_info->parser;
515 fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix);
516 if (parser->inSubset == 1)
517 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset,
518 element,fullname,prefix,(xmlAttributeType) type,
519 (xmlAttributeDefault) value,default_value,tree);
521 if (parser->inSubset == 2)
522 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset,
523 element,fullname,prefix,(xmlAttributeType) type,
524 (xmlAttributeDefault) value,default_value,tree);
525 if (prefix != (xmlChar *) NULL)
527 if (fullname != (xmlChar *) NULL)
531 static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
532 xmlElementContentPtr content)
541 An element definition has been parsed.
543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
544 " SAX.elementDecl(%s, %d, ...)",name,type);
545 svg_info=(SVGInfo *) context;
546 parser=svg_info->parser;
547 if (parser->inSubset == 1)
548 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset,
549 name,(xmlElementTypeVal) type,content);
551 if (parser->inSubset == 2)
552 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
553 name,(xmlElementTypeVal) type,content);
556 static void SVGStripString(char *message)
565 assert(message != (char *) NULL);
566 if (*message == '\0')
572 for (p=message; *p != '\0'; p++)
574 if ((*p == '/') && (*(p+1) == '*'))
576 for ( ; *p != '\0'; p++)
577 if ((*p == '*') && (*(p+1) == '/'))
589 length=strlen(message);
591 while (isspace((int) ((unsigned char) *p)) != 0)
593 if ((*p == '\'') || (*p == '"'))
596 while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
599 if ((*q == '\'') || (*q == '"'))
601 (void) memmove(message,p,(size_t) (q-p+1));
604 Convert newlines to a space.
606 for (p=message; *p != '\0'; p++)
611 static char **SVGKeyValuePairs(void *context,const int key_sentinel,
612 const int value_sentinel,const char *text,size_t *number_tokens)
630 svg_info=(SVGInfo *) context;
632 if (text == (const char *) NULL)
633 return((char **) NULL);
635 tokens=(char **) AcquireQuantumMemory(extent+2UL,sizeof(*tokens));
636 if (tokens == (char **) NULL)
638 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
639 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
640 return((char **) NULL);
643 Convert string to an ASCII list.
647 for (q=p; *q != '\0'; q++)
649 if ((*q != key_sentinel) && (*q != value_sentinel) && (*q != '\0'))
651 if (i == (ssize_t) extent)
654 tokens=(char **) ResizeQuantumMemory(tokens,extent+2,sizeof(*tokens));
655 if (tokens == (char **) NULL)
657 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
658 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
659 return((char **) NULL);
662 tokens[i]=AcquireString(p);
663 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
664 SVGStripString(tokens[i]);
668 tokens[i]=AcquireString(p);
669 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
670 SVGStripString(tokens[i++]);
671 tokens[i]=(char *) NULL;
672 *number_tokens=(size_t) i;
676 static void SVGNotationDeclaration(void *context,const xmlChar *name,
677 const xmlChar *public_id,const xmlChar *system_id)
686 What to do when a notation declaration has been parsed.
688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
689 " SAX.notationDecl(%s, %s, %s)",name,
690 public_id != (const xmlChar *) NULL ? (const char *) public_id : "none",
691 system_id != (const xmlChar *) NULL ? (const char *) system_id : "none");
692 svg_info=(SVGInfo *) context;
693 parser=svg_info->parser;
694 if (parser->inSubset == 1)
695 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
696 name,public_id,system_id);
698 if (parser->inSubset == 2)
699 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
700 name,public_id,system_id);
703 static void SVGProcessStyleElement(void *context,const xmlChar *name,
707 background[MagickPathExtent],
725 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
726 svg_info=(SVGInfo *) context;
727 tokens=SVGKeyValuePairs(context,':',';',style,&number_tokens);
728 if (tokens == (char **) NULL)
730 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
732 keyword=(char *) tokens[i];
733 value=(char *) tokens[i+1];
734 if (LocaleCompare(keyword,"font-size") != 0)
736 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
737 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
738 svg_info->pointsize);
740 color=AcquireString("none");
741 units=AcquireString("userSpaceOnUse");
742 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
744 keyword=(char *) tokens[i];
745 value=(char *) tokens[i+1];
746 (void) LogMagickEvent(CoderEvent,GetMagickModule()," %s: %s",keyword,
753 if (LocaleCompare((const char *) name,"background") == 0)
755 if (LocaleCompare((const char *) name,"svg") == 0)
756 (void) CopyMagickString(background,value,MagickPathExtent);
764 if (LocaleCompare(keyword,"clip-path") == 0)
766 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",value);
769 if (LocaleCompare(keyword,"clip-rule") == 0)
771 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",value);
774 if (LocaleCompare(keyword,"clipPathUnits") == 0)
776 (void) CloneString(&units,value);
777 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
781 if (LocaleCompare(keyword,"color") == 0)
783 (void) CloneString(&color,value);
791 if (LocaleCompare(keyword,"fill") == 0)
793 if (LocaleCompare(value,"currentColor") == 0)
795 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
798 if (LocaleCompare(value,"#000000ff") == 0)
799 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
801 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
804 if (LocaleCompare(keyword,"fillcolor") == 0)
806 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
809 if (LocaleCompare(keyword,"fill-rule") == 0)
811 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",value);
814 if (LocaleCompare(keyword,"fill-opacity") == 0)
816 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
820 if (LocaleCompare(keyword,"font") == 0)
823 family[MagickPathExtent],
824 size[MagickPathExtent],
825 style[MagickPathExtent];
827 if (sscanf(value,"%2048s %2048s %2048s",style,size,family) != 3)
829 if (GetUserSpaceCoordinateValue(svg_info,0,style) == 0)
830 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
833 if (sscanf(value,"%2048s %2048s",size,family) != 2)
835 (void) FormatLocaleFile(svg_info->file,"font-size \"%s\"\n",size);
836 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
840 if (LocaleCompare(keyword,"font-family") == 0)
842 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
846 if (LocaleCompare(keyword,"font-stretch") == 0)
848 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
852 if (LocaleCompare(keyword,"font-style") == 0)
854 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",value);
857 if (LocaleCompare(keyword,"font-size") == 0)
859 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
860 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
861 svg_info->pointsize);
864 if (LocaleCompare(keyword,"font-weight") == 0)
866 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
875 if (LocaleCompare(keyword,"offset") == 0)
877 (void) FormatLocaleFile(svg_info->file,"offset %g\n",
878 GetUserSpaceCoordinateValue(svg_info,1,value));
881 if (LocaleCompare(keyword,"opacity") == 0)
883 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
891 if (LocaleCompare(keyword,"stop-color") == 0)
893 (void) CloneString(&svg_info->stop_color,value);
896 if (LocaleCompare(keyword,"stroke") == 0)
898 if (LocaleCompare(value,"currentColor") == 0)
900 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",color);
903 if (LocaleCompare(value,"#000000ff") == 0)
904 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
906 (void) FormatLocaleFile(svg_info->file,
907 "stroke \"%s\"\n",value);
910 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
912 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
913 LocaleCompare(value,"true") == 0);
916 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
918 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
922 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
924 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
925 GetUserSpaceCoordinateValue(svg_info,1,value));
928 if (LocaleCompare(keyword,"stroke-linecap") == 0)
930 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
934 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
936 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
940 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
942 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit \"%s\"\n",
946 if (LocaleCompare(keyword,"stroke-opacity") == 0)
948 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
952 if (LocaleCompare(keyword,"stroke-width") == 0)
954 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
955 GetUserSpaceCoordinateValue(svg_info,1,value));
963 if (LocaleCompare(keyword,"text-align") == 0)
965 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",value);
968 if (LocaleCompare(keyword,"text-anchor") == 0)
970 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
974 if (LocaleCompare(keyword,"text-decoration") == 0)
976 if (LocaleCompare(value,"underline") == 0)
977 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
978 if (LocaleCompare(value,"line-through") == 0)
979 (void) FormatLocaleFile(svg_info->file,"decorate line-through\n");
980 if (LocaleCompare(value,"overline") == 0)
981 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
984 if (LocaleCompare(keyword,"text-antialiasing") == 0)
986 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
987 LocaleCompare(value,"true") == 0);
996 if (units != (char *) NULL)
997 units=DestroyString(units);
998 if (color != (char *) NULL)
999 color=DestroyString(color);
1000 for (i=0; tokens[i] != (char *) NULL; i++)
1001 tokens[i]=DestroyString(tokens[i]);
1002 tokens=(char **) RelinquishMagickMemory(tokens);
1005 static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
1006 const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
1012 What to do when an unparsed entity declaration is parsed.
1014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1015 " SAX.unparsedEntityDecl(%s, %s, %s, %s)",name,
1016 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
1017 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation);
1018 svg_info=(SVGInfo *) context;
1019 (void) xmlAddDocEntity(svg_info->document,name,
1020 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
1024 static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
1030 Receive the document locator at startup, actually xmlDefaultSAXLocator.
1033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1034 " SAX.setDocumentLocator()");
1035 svg_info=(SVGInfo *) context;
1039 static void SVGStartDocument(void *context)
1048 Called when the document start being processed.
1050 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startDocument()");
1051 svg_info=(SVGInfo *) context;
1052 parser=svg_info->parser;
1053 svg_info->document=xmlNewDoc(parser->version);
1054 if (svg_info->document == (xmlDocPtr) NULL)
1056 if (parser->encoding == NULL)
1057 svg_info->document->encoding=(const xmlChar *) NULL;
1059 svg_info->document->encoding=xmlStrdup(parser->encoding);
1060 svg_info->document->standalone=parser->standalone;
1063 static void SVGEndDocument(void *context)
1069 Called when the document end has been detected.
1071 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()");
1072 svg_info=(SVGInfo *) context;
1073 if (svg_info->offset != (char *) NULL)
1074 svg_info->offset=DestroyString(svg_info->offset);
1075 if (svg_info->stop_color != (char *) NULL)
1076 svg_info->stop_color=DestroyString(svg_info->stop_color);
1077 if (svg_info->scale != (double *) NULL)
1078 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
1079 if (svg_info->text != (char *) NULL)
1080 svg_info->text=DestroyString(svg_info->text);
1081 if (svg_info->vertices != (char *) NULL)
1082 svg_info->vertices=DestroyString(svg_info->vertices);
1083 if (svg_info->url != (char *) NULL)
1084 svg_info->url=DestroyString(svg_info->url);
1085 #if defined(MAGICKCORE_XML_DELEGATE)
1086 if (svg_info->document != (xmlDocPtr) NULL)
1088 xmlFreeDoc(svg_info->document);
1089 svg_info->document=(xmlDocPtr) NULL;
1094 static void SVGStartElement(void *context,const xmlChar *name,
1095 const xmlChar **attributes)
1097 #define PushGraphicContext(id) \
1100 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); \
1102 (void) FormatLocaleFile(svg_info->file,"push graphic-context \"%s\"\n", \
1108 background[MagickPathExtent],
1109 id[MagickPathExtent],
1111 token[MagickPathExtent],
1131 Called when an opening tag has been processed.
1133 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s",
1135 svg_info=(SVGInfo *) context;
1137 svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale,
1138 svg_info->n+1UL,sizeof(*svg_info->scale));
1139 if (svg_info->scale == (double *) NULL)
1141 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
1142 ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
1145 svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
1146 color=AcquireString("none");
1147 units=AcquireString("userSpaceOnUse");
1151 value=(const char *) NULL;
1152 if ((LocaleCompare((char *) name,"image") == 0) ||
1153 (LocaleCompare((char *) name,"pattern") == 0) ||
1154 (LocaleCompare((char *) name,"rect") == 0) ||
1155 (LocaleCompare((char *) name,"text") == 0) ||
1156 (LocaleCompare((char *) name,"use") == 0))
1158 svg_info->bounds.x=0.0;
1159 svg_info->bounds.y=0.0;
1161 if (attributes != (const xmlChar **) NULL)
1162 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1164 keyword=(const char *) attributes[i];
1165 value=(const char *) attributes[i+1];
1171 if (LocaleCompare(keyword,"cx") == 0)
1173 svg_info->element.cx=
1174 GetUserSpaceCoordinateValue(svg_info,1,value);
1177 if (LocaleCompare(keyword,"cy") == 0)
1179 svg_info->element.cy=
1180 GetUserSpaceCoordinateValue(svg_info,-1,value);
1188 if (LocaleCompare(keyword,"fx") == 0)
1190 svg_info->element.major=
1191 GetUserSpaceCoordinateValue(svg_info,1,value);
1194 if (LocaleCompare(keyword,"fy") == 0)
1196 svg_info->element.minor=
1197 GetUserSpaceCoordinateValue(svg_info,-1,value);
1205 if (LocaleCompare(keyword,"height") == 0)
1207 svg_info->bounds.height=
1208 GetUserSpaceCoordinateValue(svg_info,-1,value);
1216 if (LocaleCompare(keyword,"id") == 0)
1218 (void) CopyMagickString(id,value,MagickPathExtent);
1226 if (LocaleCompare(keyword,"r") == 0)
1228 svg_info->element.angle=
1229 GetUserSpaceCoordinateValue(svg_info,0,value);
1237 if (LocaleCompare(keyword,"width") == 0)
1239 svg_info->bounds.width=
1240 GetUserSpaceCoordinateValue(svg_info,1,value);
1248 if (LocaleCompare(keyword,"x") == 0)
1250 if (LocaleCompare((char *) name,"tspan") != 0)
1251 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,
1252 value)-svg_info->center.x;
1255 if (LocaleCompare(keyword,"x1") == 0)
1257 svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
1261 if (LocaleCompare(keyword,"x2") == 0)
1263 svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
1272 if (LocaleCompare(keyword,"y") == 0)
1274 if (LocaleCompare((char *) name,"tspan") != 0)
1275 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,
1276 value)-svg_info->center.y;
1279 if (LocaleCompare(keyword,"y1") == 0)
1281 svg_info->segment.y1=GetUserSpaceCoordinateValue(svg_info,-1,
1285 if (LocaleCompare(keyword,"y2") == 0)
1287 svg_info->segment.y2=GetUserSpaceCoordinateValue(svg_info,-1,
1297 if (strchr((char *) name,':') != (char *) NULL)
1300 Skip over namespace.
1302 for ( ; *name != ':'; name++) ;
1310 if (LocaleCompare((const char *) name,"circle") == 0)
1312 PushGraphicContext(id);
1315 if (LocaleCompare((const char *) name,"clipPath") == 0)
1317 (void) FormatLocaleFile(svg_info->file,"push clip-path \"%s\"\n",id);
1325 if (LocaleCompare((const char *) name,"defs") == 0)
1327 (void) FormatLocaleFile(svg_info->file,"push defs\n");
1335 if (LocaleCompare((const char *) name,"ellipse") == 0)
1337 PushGraphicContext(id);
1345 if (LocaleCompare((const char *) name,"foreignObject") == 0)
1347 PushGraphicContext(id);
1355 if (LocaleCompare((const char *) name,"g") == 0)
1357 PushGraphicContext(id);
1365 if (LocaleCompare((const char *) name,"image") == 0)
1367 PushGraphicContext(id);
1375 if (LocaleCompare((const char *) name,"line") == 0)
1377 PushGraphicContext(id);
1380 if (LocaleCompare((const char *) name,"linearGradient") == 0)
1382 (void) FormatLocaleFile(svg_info->file,
1383 "push gradient \"%s\" linear %g,%g %g,%g\n",id,
1384 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
1385 svg_info->segment.y2);
1393 if (LocaleCompare((const char *) name,"mask") == 0)
1395 (void) FormatLocaleFile(svg_info->file,"push mask \"%s\"\n",id);
1403 if (LocaleCompare((const char *) name,"path") == 0)
1405 PushGraphicContext(id);
1408 if (LocaleCompare((const char *) name,"pattern") == 0)
1410 (void) FormatLocaleFile(svg_info->file,
1411 "push pattern \"%s\" %g,%g %g,%g\n",id,
1412 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
1413 svg_info->bounds.height);
1416 if (LocaleCompare((const char *) name,"polygon") == 0)
1418 PushGraphicContext(id);
1421 if (LocaleCompare((const char *) name,"polyline") == 0)
1423 PushGraphicContext(id);
1431 if (LocaleCompare((const char *) name,"radialGradient") == 0)
1433 (void) FormatLocaleFile(svg_info->file,
1434 "push gradient \"%s\" radial %g,%g %g,%g %g\n",
1435 id,svg_info->element.cx,svg_info->element.cy,
1436 svg_info->element.major,svg_info->element.minor,
1437 svg_info->element.angle);
1440 if (LocaleCompare((const char *) name,"rect") == 0)
1442 PushGraphicContext(id);
1450 if (LocaleCompare((char *) name,"style") == 0)
1452 if (LocaleCompare((const char *) name,"svg") == 0)
1454 svg_info->svgDepth++;
1455 PushGraphicContext(id);
1456 (void) FormatLocaleFile(svg_info->file,"compliance \"SVG\"\n");
1457 (void) FormatLocaleFile(svg_info->file,"fill \"black\"\n");
1458 (void) FormatLocaleFile(svg_info->file,"fill-opacity 1\n");
1459 (void) FormatLocaleFile(svg_info->file,"stroke \"none\"\n");
1460 (void) FormatLocaleFile(svg_info->file,"stroke-width 1\n");
1461 (void) FormatLocaleFile(svg_info->file,"stroke-opacity 1\n");
1462 (void) FormatLocaleFile(svg_info->file,"fill-rule nonzero\n");
1465 if (LocaleCompare((const char *) name,"symbol") == 0)
1467 (void) FormatLocaleFile(svg_info->file,"push symbol\n");
1475 if (LocaleCompare((const char *) name,"text") == 0)
1477 PushGraphicContext(id);
1478 (void) FormatLocaleFile(svg_info->file,"class \"text\"\n");
1479 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1480 svg_info->bounds.x,svg_info->bounds.y);
1481 svg_info->center.x=svg_info->bounds.x;
1482 svg_info->center.y=svg_info->bounds.y;
1483 svg_info->bounds.x=0.0;
1484 svg_info->bounds.y=0.0;
1485 svg_info->bounds.width=0.0;
1486 svg_info->bounds.height=0.0;
1489 if (LocaleCompare((const char *) name,"tspan") == 0)
1491 if (*svg_info->text != '\0')
1496 text=EscapeString(svg_info->text,'\'');
1497 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
1498 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1499 svg_info->center.y,text);
1500 text=DestroyString(text);
1501 *svg_info->text='\0';
1503 PushGraphicContext(id);
1511 if (LocaleCompare((char *) name,"use") == 0)
1513 PushGraphicContext(id);
1521 if (attributes != (const xmlChar **) NULL)
1522 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1524 keyword=(const char *) attributes[i];
1525 value=(const char *) attributes[i+1];
1526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1527 " %s = %s",keyword,value);
1533 if (LocaleCompare(keyword,"angle") == 0)
1535 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1536 GetUserSpaceCoordinateValue(svg_info,0,value));
1544 if (LocaleCompare(keyword,"class") == 0)
1551 GetNextToken(p,&p,MagickPathExtent,token);
1553 GetNextToken(p,&p,MagickPathExtent,token);
1556 (void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",
1563 if (LocaleCompare(keyword,"clip-path") == 0)
1565 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",
1569 if (LocaleCompare(keyword,"clip-rule") == 0)
1571 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",
1575 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1577 (void) CloneString(&units,value);
1578 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
1582 if (LocaleCompare(keyword,"color") == 0)
1584 (void) CloneString(&color,value);
1587 if (LocaleCompare(keyword,"cx") == 0)
1589 svg_info->element.cx=
1590 GetUserSpaceCoordinateValue(svg_info,1,value);
1593 if (LocaleCompare(keyword,"cy") == 0)
1595 svg_info->element.cy=
1596 GetUserSpaceCoordinateValue(svg_info,-1,value);
1604 if (LocaleCompare(keyword,"d") == 0)
1606 (void) CloneString(&svg_info->vertices,value);
1609 if (LocaleCompare(keyword,"dx") == 0)
1614 dx=GetUserSpaceCoordinateValue(svg_info,1,value);
1615 svg_info->bounds.x+=dx;
1616 if (LocaleCompare((char *) name,"text") == 0)
1617 (void) FormatLocaleFile(svg_info->file,"translate %g,0.0\n",dx);
1620 if (LocaleCompare(keyword,"dy") == 0)
1625 dy=GetUserSpaceCoordinateValue(svg_info,-1,value);
1626 svg_info->bounds.y+=dy;
1627 if (LocaleCompare((char *) name,"text") == 0)
1628 (void) FormatLocaleFile(svg_info->file,"translate 0.0,%g\n",dy);
1636 if (LocaleCompare(keyword,"fill") == 0)
1638 if (LocaleCompare(value,"currentColor") == 0)
1640 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
1643 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1646 if (LocaleCompare(keyword,"fillcolor") == 0)
1648 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1651 if (LocaleCompare(keyword,"fill-rule") == 0)
1653 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",
1657 if (LocaleCompare(keyword,"fill-opacity") == 0)
1659 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
1663 if (LocaleCompare(keyword,"font-family") == 0)
1665 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
1669 if (LocaleCompare(keyword,"font-stretch") == 0)
1671 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
1675 if (LocaleCompare(keyword,"font-style") == 0)
1677 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
1681 if (LocaleCompare(keyword,"font-size") == 0)
1683 if (LocaleCompare(value,"xx-small") == 0)
1684 svg_info->pointsize=6.144;
1685 else if (LocaleCompare(value,"x-small") == 0)
1686 svg_info->pointsize=7.68;
1687 else if (LocaleCompare(value,"small") == 0)
1688 svg_info->pointsize=9.6;
1689 else if (LocaleCompare(value,"medium") == 0)
1690 svg_info->pointsize=12.0;
1691 else if (LocaleCompare(value,"large") == 0)
1692 svg_info->pointsize=14.4;
1693 else if (LocaleCompare(value,"x-large") == 0)
1694 svg_info->pointsize=17.28;
1695 else if (LocaleCompare(value,"xx-large") == 0)
1696 svg_info->pointsize=20.736;
1698 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,
1700 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1701 svg_info->pointsize);
1704 if (LocaleCompare(keyword,"font-weight") == 0)
1706 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
1715 if (LocaleCompare(keyword,"gradientTransform") == 0)
1722 GetAffineMatrix(&transform);
1723 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1724 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
1725 if (tokens == (char **) NULL)
1727 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
1729 keyword=(char *) tokens[j];
1730 if (keyword == (char *) NULL)
1732 value=(char *) tokens[j+1];
1733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1734 " %s: %s",keyword,value);
1736 GetAffineMatrix(&affine);
1742 if (LocaleCompare(keyword,"matrix") == 0)
1744 p=(const char *) value;
1745 GetNextToken(p,&p,MagickPathExtent,token);
1746 affine.sx=StringToDouble(value,(char **) NULL);
1747 GetNextToken(p,&p,MagickPathExtent,token);
1749 GetNextToken(p,&p,MagickPathExtent,token);
1750 affine.rx=StringToDouble(token,&next_token);
1751 GetNextToken(p,&p,MagickPathExtent,token);
1753 GetNextToken(p,&p,MagickPathExtent,token);
1754 affine.ry=StringToDouble(token,&next_token);
1755 GetNextToken(p,&p,MagickPathExtent,token);
1757 GetNextToken(p,&p,MagickPathExtent,token);
1758 affine.sy=StringToDouble(token,&next_token);
1759 GetNextToken(p,&p,MagickPathExtent,token);
1761 GetNextToken(p,&p,MagickPathExtent,token);
1762 affine.tx=StringToDouble(token,&next_token);
1763 GetNextToken(p,&p,MagickPathExtent,token);
1765 GetNextToken(p,&p,MagickPathExtent,token);
1766 affine.ty=StringToDouble(token,&next_token);
1774 if (LocaleCompare(keyword,"rotate") == 0)
1779 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1780 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1781 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1782 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1783 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1791 if (LocaleCompare(keyword,"scale") == 0)
1793 for (p=(const char *) value; *p != '\0'; p++)
1794 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1797 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1798 affine.sy=affine.sx;
1801 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1802 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1805 if (LocaleCompare(keyword,"skewX") == 0)
1807 affine.sx=svg_info->affine.sx;
1808 affine.ry=tan(DegreesToRadians(fmod(
1809 GetUserSpaceCoordinateValue(svg_info,1,value),
1811 affine.sy=svg_info->affine.sy;
1814 if (LocaleCompare(keyword,"skewY") == 0)
1816 affine.sx=svg_info->affine.sx;
1817 affine.rx=tan(DegreesToRadians(fmod(
1818 GetUserSpaceCoordinateValue(svg_info,-1,value),
1820 affine.sy=svg_info->affine.sy;
1828 if (LocaleCompare(keyword,"translate") == 0)
1830 for (p=(const char *) value; *p != '\0'; p++)
1831 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1834 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1835 affine.ty=affine.tx;
1838 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1846 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1847 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1848 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1849 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1850 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1852 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1855 (void) FormatLocaleFile(svg_info->file,
1856 "affine %g %g %g %g %g %g\n",transform.sx,
1857 transform.rx,transform.ry,transform.sy,transform.tx,
1859 for (j=0; tokens[j] != (char *) NULL; j++)
1860 tokens[j]=DestroyString(tokens[j]);
1861 tokens=(char **) RelinquishMagickMemory(tokens);
1864 if (LocaleCompare(keyword,"gradientUnits") == 0)
1866 (void) CloneString(&units,value);
1867 (void) FormatLocaleFile(svg_info->file,"gradient-units \"%s\"\n",
1876 if (LocaleCompare(keyword,"height") == 0)
1878 svg_info->bounds.height=
1879 GetUserSpaceCoordinateValue(svg_info,-1,value);
1882 if (LocaleCompare(keyword,"href") == 0)
1884 (void) CloneString(&svg_info->url,value);
1892 if (LocaleCompare(keyword,"major") == 0)
1894 svg_info->element.major=
1895 GetUserSpaceCoordinateValue(svg_info,1,value);
1898 if (LocaleCompare(keyword,"mask") == 0)
1900 (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
1903 if (LocaleCompare(keyword,"minor") == 0)
1905 svg_info->element.minor=
1906 GetUserSpaceCoordinateValue(svg_info,-1,value);
1914 if (LocaleCompare(keyword,"offset") == 0)
1916 (void) CloneString(&svg_info->offset,value);
1919 if (LocaleCompare(keyword,"opacity") == 0)
1921 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
1929 if (LocaleCompare(keyword,"path") == 0)
1931 (void) CloneString(&svg_info->url,value);
1934 if (LocaleCompare(keyword,"points") == 0)
1936 (void) CloneString(&svg_info->vertices,value);
1944 if (LocaleCompare(keyword,"r") == 0)
1946 svg_info->element.major=
1947 GetUserSpaceCoordinateValue(svg_info,1,value);
1948 svg_info->element.minor=
1949 GetUserSpaceCoordinateValue(svg_info,-1,value);
1952 if (LocaleCompare(keyword,"rotate") == 0)
1957 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1958 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1959 svg_info->bounds.x,svg_info->bounds.y);
1960 svg_info->bounds.x=0;
1961 svg_info->bounds.y=0;
1962 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
1965 if (LocaleCompare(keyword,"rx") == 0)
1967 if (LocaleCompare((const char *) name,"ellipse") == 0)
1968 svg_info->element.major=
1969 GetUserSpaceCoordinateValue(svg_info,1,value);
1972 GetUserSpaceCoordinateValue(svg_info,1,value);
1975 if (LocaleCompare(keyword,"ry") == 0)
1977 if (LocaleCompare((const char *) name,"ellipse") == 0)
1978 svg_info->element.minor=
1979 GetUserSpaceCoordinateValue(svg_info,-1,value);
1982 GetUserSpaceCoordinateValue(svg_info,-1,value);
1990 if (LocaleCompare(keyword,"stop-color") == 0)
1992 (void) CloneString(&svg_info->stop_color,value);
1995 if (LocaleCompare(keyword,"stroke") == 0)
1997 if (LocaleCompare(value,"currentColor") == 0)
1999 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",
2003 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",value);
2006 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
2008 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
2009 LocaleCompare(value,"true") == 0);
2012 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
2014 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
2018 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
2020 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
2021 GetUserSpaceCoordinateValue(svg_info,1,value));
2024 if (LocaleCompare(keyword,"stroke-linecap") == 0)
2026 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
2030 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
2032 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
2036 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
2038 (void) FormatLocaleFile(svg_info->file,
2039 "stroke-miterlimit \"%s\"\n",value);
2042 if (LocaleCompare(keyword,"stroke-opacity") == 0)
2044 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
2048 if (LocaleCompare(keyword,"stroke-width") == 0)
2050 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
2051 GetUserSpaceCoordinateValue(svg_info,1,value));
2054 if (LocaleCompare(keyword,"style") == 0)
2056 SVGProcessStyleElement(context,name,value);
2064 if (LocaleCompare(keyword,"text-align") == 0)
2066 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",
2070 if (LocaleCompare(keyword,"text-anchor") == 0)
2072 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
2076 if (LocaleCompare(keyword,"text-decoration") == 0)
2078 if (LocaleCompare(value,"underline") == 0)
2079 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
2080 if (LocaleCompare(value,"line-through") == 0)
2081 (void) FormatLocaleFile(svg_info->file,
2082 "decorate line-through\n");
2083 if (LocaleCompare(value,"overline") == 0)
2084 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
2087 if (LocaleCompare(keyword,"text-antialiasing") == 0)
2089 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
2090 LocaleCompare(value,"true") == 0);
2093 if (LocaleCompare(keyword,"transform") == 0)
2100 GetAffineMatrix(&transform);
2101 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
2102 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
2103 if (tokens == (char **) NULL)
2105 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2107 keyword=(char *) tokens[j];
2108 value=(char *) tokens[j+1];
2109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2110 " %s: %s",keyword,value);
2112 GetAffineMatrix(&affine);
2118 if (LocaleCompare(keyword,"matrix") == 0)
2120 p=(const char *) value;
2121 GetNextToken(p,&p,MagickPathExtent,token);
2122 affine.sx=StringToDouble(value,(char **) NULL);
2123 GetNextToken(p,&p,MagickPathExtent,token);
2125 GetNextToken(p,&p,MagickPathExtent,token);
2126 affine.rx=StringToDouble(token,&next_token);
2127 GetNextToken(p,&p,MagickPathExtent,token);
2129 GetNextToken(p,&p,MagickPathExtent,token);
2130 affine.ry=StringToDouble(token,&next_token);
2131 GetNextToken(p,&p,MagickPathExtent,token);
2133 GetNextToken(p,&p,MagickPathExtent,token);
2134 affine.sy=StringToDouble(token,&next_token);
2135 GetNextToken(p,&p,MagickPathExtent,token);
2137 GetNextToken(p,&p,MagickPathExtent,token);
2138 affine.tx=StringToDouble(token,&next_token);
2139 GetNextToken(p,&p,MagickPathExtent,token);
2141 GetNextToken(p,&p,MagickPathExtent,token);
2142 affine.ty=StringToDouble(token,&next_token);
2150 if (LocaleCompare(keyword,"rotate") == 0)
2157 p=(const char *) value;
2158 GetNextToken(p,&p,MagickPathExtent,token);
2159 angle=StringToDouble(value,(char **) NULL);
2160 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
2161 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
2162 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
2163 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
2164 GetNextToken(p,&p,MagickPathExtent,token);
2166 GetNextToken(p,&p,MagickPathExtent,token);
2167 x=StringToDouble(token,&next_token);
2168 GetNextToken(p,&p,MagickPathExtent,token);
2170 GetNextToken(p,&p,MagickPathExtent,token);
2171 y=StringToDouble(token,&next_token);
2172 affine.tx=svg_info->bounds.x+x*
2173 cos(DegreesToRadians(fmod(angle,360.0)))+y*
2174 sin(DegreesToRadians(fmod(angle,360.0)));
2175 affine.ty=svg_info->bounds.y+x*
2176 sin(DegreesToRadians(fmod(angle,360.0)))-y*
2177 cos(DegreesToRadians(fmod(angle,360.0)));
2185 if (LocaleCompare(keyword,"scale") == 0)
2187 for (p=(const char *) value; *p != '\0'; p++)
2188 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2191 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
2192 affine.sy=affine.sx;
2194 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
2196 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
2199 if (LocaleCompare(keyword,"skewX") == 0)
2201 affine.sx=svg_info->affine.sx;
2202 affine.ry=tan(DegreesToRadians(fmod(
2203 GetUserSpaceCoordinateValue(svg_info,1,value),
2205 affine.sy=svg_info->affine.sy;
2208 if (LocaleCompare(keyword,"skewY") == 0)
2210 affine.sx=svg_info->affine.sx;
2211 affine.rx=tan(DegreesToRadians(fmod(
2212 GetUserSpaceCoordinateValue(svg_info,-1,value),
2214 affine.sy=svg_info->affine.sy;
2222 if (LocaleCompare(keyword,"translate") == 0)
2224 for (p=(const char *) value; *p != '\0'; p++)
2225 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2228 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2231 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2240 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2241 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2242 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2243 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2244 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2246 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2249 (void) FormatLocaleFile(svg_info->file,
2250 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2251 transform.ry,transform.sy,transform.tx,transform.ty);
2252 for (j=0; tokens[j] != (char *) NULL; j++)
2253 tokens[j]=DestroyString(tokens[j]);
2254 tokens=(char **) RelinquishMagickMemory(tokens);
2262 if (LocaleCompare(keyword,"verts") == 0)
2264 (void) CloneString(&svg_info->vertices,value);
2267 if (LocaleCompare(keyword,"viewBox") == 0)
2269 p=(const char *) value;
2270 GetNextToken(p,&p,MagickPathExtent,token);
2271 svg_info->view_box.x=StringToDouble(token,&next_token);
2272 GetNextToken(p,&p,MagickPathExtent,token);
2274 GetNextToken(p,&p,MagickPathExtent,token);
2275 svg_info->view_box.y=StringToDouble(token,&next_token);
2276 GetNextToken(p,&p,MagickPathExtent,token);
2278 GetNextToken(p,&p,MagickPathExtent,token);
2279 svg_info->view_box.width=StringToDouble(token,
2281 if (svg_info->bounds.width == 0)
2282 svg_info->bounds.width=svg_info->view_box.width;
2283 GetNextToken(p,&p,MagickPathExtent,token);
2285 GetNextToken(p,&p,MagickPathExtent,token);
2286 svg_info->view_box.height=StringToDouble(token,
2288 if (svg_info->bounds.height == 0)
2289 svg_info->bounds.height=svg_info->view_box.height;
2297 if (LocaleCompare(keyword,"width") == 0)
2299 svg_info->bounds.width=
2300 GetUserSpaceCoordinateValue(svg_info,1,value);
2308 if (LocaleCompare(keyword,"x") == 0)
2310 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2313 if (LocaleCompare(keyword,"xlink:href") == 0)
2315 (void) CloneString(&svg_info->url,value);
2318 if (LocaleCompare(keyword,"x1") == 0)
2320 svg_info->segment.x1=
2321 GetUserSpaceCoordinateValue(svg_info,1,value);
2324 if (LocaleCompare(keyword,"x2") == 0)
2326 svg_info->segment.x2=
2327 GetUserSpaceCoordinateValue(svg_info,1,value);
2335 if (LocaleCompare(keyword,"y") == 0)
2337 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2340 if (LocaleCompare(keyword,"y1") == 0)
2342 svg_info->segment.y1=
2343 GetUserSpaceCoordinateValue(svg_info,-1,value);
2346 if (LocaleCompare(keyword,"y2") == 0)
2348 svg_info->segment.y2=
2349 GetUserSpaceCoordinateValue(svg_info,-1,value);
2358 if (LocaleCompare((const char *) name,"svg") == 0)
2360 if (svg_info->document->encoding != (const xmlChar *) NULL)
2361 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2362 (const char *) svg_info->document->encoding);
2363 if (attributes != (const xmlChar **) NULL)
2371 if ((svg_info->view_box.width == 0.0) ||
2372 (svg_info->view_box.height == 0.0))
2373 svg_info->view_box=svg_info->bounds;
2375 if (svg_info->bounds.width > 0.0)
2376 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2378 if (svg_info->bounds.height > 0.0)
2379 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2380 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2381 (double) svg_info->width,(double) svg_info->height);
2382 sx=(double) svg_info->width/svg_info->view_box.width;
2383 sy=(double) svg_info->height/svg_info->view_box.height;
2384 tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2386 ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2388 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2390 if ((svg_info->svgDepth == 1) && (*background != '\0'))
2392 PushGraphicContext(id);
2393 (void) FormatLocaleFile(svg_info->file,"fill %s\n",background);
2394 (void) FormatLocaleFile(svg_info->file,
2395 "rectangle 0,0 %g,%g\n",svg_info->view_box.width,
2396 svg_info->view_box.height);
2397 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2401 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2402 if (units != (char *) NULL)
2403 units=DestroyString(units);
2404 if (color != (char *) NULL)
2405 color=DestroyString(color);
2408 static void SVGEndElement(void *context,const xmlChar *name)
2414 Called when the end of an element has been detected.
2416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2417 " SAX.endElement(%s)",name);
2418 svg_info=(SVGInfo *) context;
2419 if (strchr((char *) name,':') != (char *) NULL)
2422 Skip over namespace.
2424 for ( ; *name != ':'; name++) ;
2432 if (LocaleCompare((const char *) name,"circle") == 0)
2434 (void) FormatLocaleFile(svg_info->file,"class \"circle\"\n");
2435 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2436 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2437 svg_info->element.cy+svg_info->element.minor);
2438 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2441 if (LocaleCompare((const char *) name,"clipPath") == 0)
2443 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2451 if (LocaleCompare((const char *) name,"defs") == 0)
2453 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2456 if (LocaleCompare((const char *) name,"desc") == 0)
2461 if (*svg_info->text == '\0')
2463 (void) fputc('#',svg_info->file);
2464 for (p=svg_info->text; *p != '\0'; p++)
2466 (void) fputc(*p,svg_info->file);
2468 (void) fputc('#',svg_info->file);
2470 (void) fputc('\n',svg_info->file);
2471 *svg_info->text='\0';
2479 if (LocaleCompare((const char *) name,"ellipse") == 0)
2484 (void) FormatLocaleFile(svg_info->file,"class \"ellipse\"\n");
2485 angle=svg_info->element.angle;
2486 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2487 svg_info->element.cx,svg_info->element.cy,
2488 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2489 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2490 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2498 if (LocaleCompare((const char *) name,"foreignObject") == 0)
2500 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2508 if (LocaleCompare((const char *) name,"g") == 0)
2510 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2518 if (LocaleCompare((const char *) name,"image") == 0)
2520 (void) FormatLocaleFile(svg_info->file,
2521 "image Over %g,%g %g,%g \"%s\"\n",svg_info->bounds.x,
2522 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2524 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2532 if (LocaleCompare((const char *) name,"line") == 0)
2534 (void) FormatLocaleFile(svg_info->file,"class \"line\"\n");
2535 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2536 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2537 svg_info->segment.y2);
2538 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2541 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2543 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2551 if (LocaleCompare((const char *) name,"mask") == 0)
2553 (void) FormatLocaleFile(svg_info->file,"pop mask\n");
2561 if (LocaleCompare((const char *) name,"pattern") == 0)
2563 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2566 if (LocaleCompare((const char *) name,"path") == 0)
2568 (void) FormatLocaleFile(svg_info->file,"class \"path\"\n");
2569 (void) FormatLocaleFile(svg_info->file,"path \"%s\"\n",
2570 svg_info->vertices);
2571 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2574 if (LocaleCompare((const char *) name,"polygon") == 0)
2576 (void) FormatLocaleFile(svg_info->file,"class \"polygon\"\n");
2577 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2578 svg_info->vertices);
2579 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2582 if (LocaleCompare((const char *) name,"polyline") == 0)
2584 (void) FormatLocaleFile(svg_info->file,"class \"polyline\"\n");
2585 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2586 svg_info->vertices);
2587 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2595 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2597 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2600 if (LocaleCompare((const char *) name,"rect") == 0)
2602 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2604 (void) FormatLocaleFile(svg_info->file,"class \"rect\"\n");
2605 if ((fabs(svg_info->bounds.width-1.0) < MagickEpsilon) &&
2606 (fabs(svg_info->bounds.height-1.0) < MagickEpsilon))
2607 (void) FormatLocaleFile(svg_info->file,"point %g,%g\n",
2608 svg_info->bounds.x,svg_info->bounds.y);
2610 (void) FormatLocaleFile(svg_info->file,
2611 "rectangle %g,%g %g,%g\n",svg_info->bounds.x,
2612 svg_info->bounds.y,svg_info->bounds.x+svg_info->bounds.width,
2613 svg_info->bounds.y+svg_info->bounds.height);
2614 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2617 if (svg_info->radius.x == 0.0)
2618 svg_info->radius.x=svg_info->radius.y;
2619 if (svg_info->radius.y == 0.0)
2620 svg_info->radius.y=svg_info->radius.x;
2621 (void) FormatLocaleFile(svg_info->file,
2622 "roundRectangle %g,%g %g,%g %g,%g\n",
2623 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2624 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2625 svg_info->radius.x,svg_info->radius.y);
2626 svg_info->radius.x=0.0;
2627 svg_info->radius.y=0.0;
2628 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2636 if (LocaleCompare((const char *) name,"stop") == 0)
2638 (void) FormatLocaleFile(svg_info->file,"stop-color \"%s\" %s\n",
2639 svg_info->stop_color,svg_info->offset);
2642 if (LocaleCompare((char *) name,"style") == 0)
2656 Find style definitions in svg_info->text.
2658 tokens=SVGKeyValuePairs(context,'{','}',svg_info->text,
2660 if (tokens == (char **) NULL)
2662 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2664 keyword=(char *) tokens[j];
2665 value=(char *) tokens[j+1];
2666 (void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
2667 *keyword == '.' ? keyword+1 : keyword);
2668 SVGProcessStyleElement(context,name,value);
2669 (void) FormatLocaleFile(svg_info->file,"pop class\n");
2673 if (LocaleCompare((const char *) name,"svg") == 0)
2675 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2676 svg_info->svgDepth--;
2679 if (LocaleCompare((const char *) name,"symbol") == 0)
2681 (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
2689 if (LocaleCompare((const char *) name,"text") == 0)
2691 if (*svg_info->text != '\0')
2696 text=EscapeString(svg_info->text,'\'');
2697 (void) FormatLocaleFile(svg_info->file,"text 0,0 \"%s\"\n",text);
2698 text=DestroyString(text);
2699 *svg_info->text='\0';
2700 svg_info->center.x=0.0;
2701 svg_info->center.y=0.0;
2703 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2706 if (LocaleCompare((const char *) name,"tspan") == 0)
2708 if (*svg_info->text != '\0')
2713 (void) FormatLocaleFile(svg_info->file,"class \"tspan\"\n");
2714 text=EscapeString(svg_info->text,'\'');
2715 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2716 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
2717 svg_info->center.y,text);
2718 text=DestroyString(text);
2719 *svg_info->text='\0';
2721 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2724 if (LocaleCompare((const char *) name,"title") == 0)
2726 if (*svg_info->text == '\0')
2728 (void) CloneString(&svg_info->title,svg_info->text);
2729 *svg_info->text='\0';
2737 if (LocaleCompare((char *) name,"use") == 0)
2739 if ((svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0))
2740 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2741 svg_info->bounds.x,svg_info->bounds.y);
2742 (void) FormatLocaleFile(svg_info->file,"use \"url(%s)\"\n",
2744 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2752 *svg_info->text='\0';
2753 (void) memset(&svg_info->element,0,sizeof(svg_info->element));
2754 (void) memset(&svg_info->segment,0,sizeof(svg_info->segment));
2758 static void SVGCharacters(void *context,const xmlChar *c,int length)
2773 Receiving some characters from the parser.
2775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2776 " SAX.characters(%s,%.20g)",c,(double) length);
2777 svg_info=(SVGInfo *) context;
2778 text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2779 if (text == (char *) NULL)
2782 for (i=0; i < (ssize_t) length; i++)
2785 SVGStripString(text);
2786 if (svg_info->text == (char *) NULL)
2787 svg_info->text=text;
2790 (void) ConcatenateString(&svg_info->text,text);
2791 text=DestroyString(text);
2795 static void SVGReference(void *context,const xmlChar *name)
2804 Called when an entity reference is detected.
2806 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2808 svg_info=(SVGInfo *) context;
2809 parser=svg_info->parser;
2810 if (parser == (xmlParserCtxtPtr) NULL)
2812 if (parser->node == (xmlNodePtr) NULL)
2815 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2817 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2820 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2826 Receiving some ignorable whitespaces from the parser.
2828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2829 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2830 svg_info=(SVGInfo *) context;
2834 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2835 const xmlChar *data)
2841 A processing instruction has been parsed.
2843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2844 " SAX.processingInstruction(%s, %s)",target,data);
2845 svg_info=(SVGInfo *) context;
2849 static void SVGComment(void *context,const xmlChar *value)
2855 A comment has been parsed.
2857 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
2859 svg_info=(SVGInfo *) context;
2860 if (svg_info->comment != (char *) NULL)
2861 (void) ConcatenateString(&svg_info->comment,"\n");
2862 (void) ConcatenateString(&svg_info->comment,(const char *) value);
2865 static void SVGWarning(void *context,const char *format,...)
2869 reason[MagickPathExtent];
2878 Display and format a warning messages, gives file, line, position and
2881 va_start(operands,format);
2882 svg_info=(SVGInfo *) context;
2883 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
2884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2885 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2886 (void) vsprintf(reason,format,operands);
2888 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2890 message=GetExceptionMessage(errno);
2891 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2892 DelegateWarning,reason,"`%s`",message);
2893 message=DestroyString(message);
2897 static void SVGError(void *context,const char *format,...)
2901 reason[MagickPathExtent];
2910 Display and format a error formats, gives file, line, position and
2913 va_start(operands,format);
2914 svg_info=(SVGInfo *) context;
2915 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
2916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2917 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2918 (void) vsprintf(reason,format,operands);
2920 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2922 message=GetExceptionMessage(errno);
2923 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2924 reason,"`%s`",message);
2925 message=DestroyString(message);
2929 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2941 Called when a pcdata block has been parsed.
2943 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
2945 svg_info=(SVGInfo *) context;
2946 parser=svg_info->parser;
2947 child=xmlGetLastChild(parser->node);
2948 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2950 xmlTextConcat(child,value,length);
2953 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2956 static void SVGExternalSubset(void *context,const xmlChar *name,
2957 const xmlChar *external_id,const xmlChar *system_id)
2972 Does this document has an external subset?
2974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2975 " SAX.externalSubset(%s, %s, %s)",name,
2976 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2977 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2978 svg_info=(SVGInfo *) context;
2979 parser=svg_info->parser;
2980 if (((external_id == NULL) && (system_id == NULL)) ||
2981 ((parser->validate == 0) || (parser->wellFormed == 0) ||
2982 (svg_info->document == 0)))
2984 input=SVGResolveEntity(context,external_id,system_id);
2987 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2988 parser_context=(*parser);
2989 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2990 if (parser->inputTab == (xmlParserInputPtr *) NULL)
2992 parser->errNo=XML_ERR_NO_MEMORY;
2993 parser->input=parser_context.input;
2994 parser->inputNr=parser_context.inputNr;
2995 parser->inputMax=parser_context.inputMax;
2996 parser->inputTab=parser_context.inputTab;
3002 xmlPushInput(parser,input);
3003 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
3004 if (input->filename == (char *) NULL)
3005 input->filename=(char *) xmlStrdup(system_id);
3008 input->base=parser->input->cur;
3009 input->cur=parser->input->cur;
3011 xmlParseExternalSubset(parser,external_id,system_id);
3012 while (parser->inputNr > 1)
3013 (void) xmlPopInput(parser);
3014 xmlFreeInputStream(parser->input);
3015 xmlFree(parser->inputTab);
3016 parser->input=parser_context.input;
3017 parser->inputNr=parser_context.inputNr;
3018 parser->inputMax=parser_context.inputMax;
3019 parser->inputTab=parser_context.inputTab;
3022 #if defined(__cplusplus) || defined(c_plusplus)
3027 Static declarations.
3030 SVGDensityGeometry[] = "72.0x72.0";
3032 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3035 filename[MagickPathExtent];
3055 message[MagickPathExtent];
3066 assert(image_info != (const ImageInfo *) NULL);
3067 assert(image_info->signature == MagickCoreSignature);
3068 assert(exception != (ExceptionInfo *) NULL);
3069 if (image_info->debug != MagickFalse)
3070 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3071 image_info->filename);
3072 assert(exception->signature == MagickCoreSignature);
3073 image=AcquireImage(image_info,exception);
3074 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3075 if (status == MagickFalse)
3077 image=DestroyImageList(image);
3078 return((Image *) NULL);
3080 if ((fabs(image->resolution.x) < MagickEpsilon) ||
3081 (fabs(image->resolution.y) < MagickEpsilon))
3089 flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3090 image->resolution.x=geometry_info.rho;
3091 image->resolution.y=geometry_info.sigma;
3092 if ((flags & SigmaValue) == 0)
3093 image->resolution.y=image->resolution.x;
3095 if (LocaleCompare(image_info->magick,"MSVG") != 0)
3100 delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception);
3101 if (delegate_info != (const DelegateInfo *) NULL)
3104 background[MagickPathExtent],
3105 command[MagickPathExtent],
3107 input_filename[MagickPathExtent],
3108 opacity[MagickPathExtent],
3109 output_filename[MagickPathExtent],
3110 unique[MagickPathExtent];
3119 Our best hope for compliance with the SVG standard.
3121 status=AcquireUniqueSymbolicLink(image->filename,input_filename);
3122 (void) AcquireUniqueFilename(output_filename);
3123 (void) AcquireUniqueFilename(unique);
3124 density=AcquireString("");
3125 (void) FormatLocaleString(density,MagickPathExtent,"%.20g,%.20g",
3126 image->resolution.x,image->resolution.y);
3127 (void) FormatLocaleString(background,MagickPathExtent,
3128 "rgb(%.20g%%,%.20g%%,%.20g%%)",
3129 100.0*QuantumScale*image->background_color.red,
3130 100.0*QuantumScale*image->background_color.green,
3131 100.0*QuantumScale*image->background_color.blue);
3132 (void) FormatLocaleString(opacity,MagickPathExtent,"%.20g",
3133 QuantumScale*image->background_color.alpha);
3134 (void) FormatLocaleString(command,MagickPathExtent,
3135 GetDelegateCommands(delegate_info),input_filename,output_filename,
3136 density,background,opacity,unique);
3137 density=DestroyString(density);
3138 status=ExternalDelegateCommand(MagickFalse,image_info->verbose,
3139 command,(char *) NULL,exception);
3140 (void) RelinquishUniqueFileResource(unique);
3141 (void) RelinquishUniqueFileResource(input_filename);
3142 if ((status == 0) && (stat(output_filename,&attributes) == 0) &&
3143 (attributes.st_size > 0))
3151 read_info=CloneImageInfo(image_info);
3152 (void) CopyMagickString(read_info->filename,output_filename,
3154 svg_image=ReadImage(read_info,exception);
3155 read_info=DestroyImageInfo(read_info);
3156 (void) RelinquishUniqueFileResource(output_filename);
3157 if (svg_image != (Image *) NULL)
3159 for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; )
3161 (void) CopyMagickString(next->filename,image->filename,
3163 (void) CopyMagickString(next->magick,image->magick,
3165 next=GetNextImageInList(next);
3167 image=DestroyImage(image);
3171 (void) RelinquishUniqueFileResource(output_filename);
3174 #if defined(MAGICKCORE_RSVG_DELEGATE)
3175 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3188 register unsigned char
3201 register const guchar
3223 svg_handle=rsvg_handle_new();
3224 if (svg_handle == (RsvgHandle *) NULL)
3225 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3226 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
3227 if ((fabs(image->resolution.x) > MagickEpsilon) &&
3228 (fabs(image->resolution.y) > MagickEpsilon))
3229 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3230 image->resolution.y);
3231 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3234 error=(GError *) NULL;
3235 (void) rsvg_handle_write(svg_handle,message,n,&error);
3236 if (error != (GError *) NULL)
3237 g_error_free(error);
3239 error=(GError *) NULL;
3240 rsvg_handle_close(svg_handle,&error);
3241 if (error != (GError *) NULL)
3242 g_error_free(error);
3243 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3244 apply_density=MagickTrue;
3245 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
3246 if ((image->resolution.x > 0.0) && (image->resolution.y > 0.0))
3252 We should not apply the density when the internal 'factor' is 'i'.
3253 This can be checked by using the trick below.
3255 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x*256,
3256 image->resolution.y*256);
3257 rsvg_handle_get_dimensions(svg_handle,&dpi_dimension_info);
3258 if ((dpi_dimension_info.width != dimension_info.width) ||
3259 (dpi_dimension_info.height != dimension_info.height))
3260 apply_density=MagickFalse;
3261 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3262 image->resolution.y);
3264 if (image_info->size != (char *) NULL)
3266 (void) GetGeometry(image_info->size,(ssize_t *) NULL,
3267 (ssize_t *) NULL,&image->columns,&image->rows);
3268 if ((image->columns != 0) || (image->rows != 0))
3270 image->resolution.x=72.0*image->columns/dimension_info.width;
3271 image->resolution.y=72.0*image->rows/dimension_info.height;
3272 if (fabs(image->resolution.x) < MagickEpsilon)
3273 image->resolution.x=image->resolution.y;
3275 if (fabs(image->resolution.y) < MagickEpsilon)
3276 image->resolution.y=image->resolution.x;
3278 image->resolution.x=image->resolution.y=MagickMin(
3279 image->resolution.x,image->resolution.y);
3280 apply_density=MagickTrue;
3283 if (apply_density != MagickFalse)
3285 image->columns=image->resolution.x*dimension_info.width/72.0;
3286 image->rows=image->resolution.y*dimension_info.height/72.0;
3290 image->columns=dimension_info.width;
3291 image->rows=dimension_info.height;
3293 pixel_info=(MemoryInfo *) NULL;
3295 pixel_buffer=rsvg_handle_get_pixbuf(svg_handle);
3296 rsvg_handle_free(svg_handle);
3297 image->columns=gdk_pixbuf_get_width(pixel_buffer);
3298 image->rows=gdk_pixbuf_get_height(pixel_buffer);
3300 image->alpha_trait=BlendPixelTrait;
3301 if (image_info->ping == MagickFalse)
3303 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3308 status=SetImageExtent(image,image->columns,image->rows,exception);
3309 if (status == MagickFalse)
3311 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
3312 g_object_unref(G_OBJECT(pixel_buffer));
3314 g_object_unref(svg_handle);
3315 ThrowReaderException(MissingDelegateError,
3316 "NoDecodeDelegateForThisImageFormat");
3318 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3319 stride=4*image->columns;
3320 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
3321 stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
3322 (int) image->columns);
3324 pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels));
3325 if (pixel_info == (MemoryInfo *) NULL)
3327 g_object_unref(svg_handle);
3328 ThrowReaderException(ResourceLimitError,
3329 "MemoryAllocationFailed");
3331 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3333 (void) SetImageBackgroundColor(image,exception);
3334 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3335 cairo_surface=cairo_image_surface_create_for_data(pixels,
3336 CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int)
3338 if ((cairo_surface == (cairo_surface_t *) NULL) ||
3339 (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS))
3341 if (cairo_surface != (cairo_surface_t *) NULL)
3342 cairo_surface_destroy(cairo_surface);
3343 pixel_info=RelinquishVirtualMemory(pixel_info);
3344 g_object_unref(svg_handle);
3345 ThrowReaderException(ResourceLimitError,
3346 "MemoryAllocationFailed");
3348 cairo_image=cairo_create(cairo_surface);
3349 cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
3350 cairo_paint(cairo_image);
3351 cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
3352 if (apply_density != MagickFalse)
3353 cairo_scale(cairo_image,image->resolution.x/72.0,
3354 image->resolution.y/72.0);
3355 rsvg_handle_render_cairo(svg_handle,cairo_image);
3356 cairo_destroy(cairo_image);
3357 cairo_surface_destroy(cairo_surface);
3358 g_object_unref(svg_handle);
3361 p=gdk_pixbuf_get_pixels(pixel_buffer);
3363 GetPixelInfo(image,&fill_color);
3364 for (y=0; y < (ssize_t) image->rows; y++)
3366 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3367 if (q == (Quantum *) NULL)
3369 for (x=0; x < (ssize_t) image->columns; x++)
3371 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3372 fill_color.blue=ScaleCharToQuantum(*p++);
3373 fill_color.green=ScaleCharToQuantum(*p++);
3374 fill_color.red=ScaleCharToQuantum(*p++);
3376 fill_color.red=ScaleCharToQuantum(*p++);
3377 fill_color.green=ScaleCharToQuantum(*p++);
3378 fill_color.blue=ScaleCharToQuantum(*p++);
3380 fill_color.alpha=ScaleCharToQuantum(*p++);
3381 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3386 gamma=QuantumScale*fill_color.alpha;
3387 gamma=PerceptibleReciprocal(gamma);
3388 fill_color.blue*=gamma;
3389 fill_color.green*=gamma;
3390 fill_color.red*=gamma;
3393 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
3394 GetPixelAlpha(image,q),q);
3395 q+=GetPixelChannels(image);
3397 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3399 if (image->previous == (Image *) NULL)
3401 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
3403 if (status == MagickFalse)
3408 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3409 if (pixel_info != (MemoryInfo *) NULL)
3410 pixel_info=RelinquishVirtualMemory(pixel_info);
3412 g_object_unref(G_OBJECT(pixel_buffer));
3414 (void) CloseBlob(image);
3415 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3417 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3418 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3419 next=GetNextImageInList(next);
3421 return(GetFirstImageInList(image));
3429 unique_file=AcquireUniqueFileResource(filename);
3430 if (unique_file != -1)
3431 file=fdopen(unique_file,"w");
3432 if ((unique_file == -1) || (file == (FILE *) NULL))
3434 (void) CopyMagickString(image->filename,filename,MagickPathExtent);
3435 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
3437 image=DestroyImageList(image);
3438 return((Image *) NULL);
3443 svg_info=AcquireSVGInfo();
3444 if (svg_info == (SVGInfo *) NULL)
3446 (void) fclose(file);
3447 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3449 svg_info->file=file;
3450 svg_info->exception=exception;
3451 svg_info->image=image;
3452 svg_info->image_info=image_info;
3453 svg_info->bounds.width=image->columns;
3454 svg_info->bounds.height=image->rows;
3455 svg_info->svgDepth=0;
3456 if (image_info->size != (char *) NULL)
3457 (void) CloneString(&svg_info->size,image_info->size);
3458 if (image->debug != MagickFalse)
3459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3460 (void) xmlSubstituteEntitiesDefault(1);
3461 (void) memset(&sax_modules,0,sizeof(sax_modules));
3462 sax_modules.internalSubset=SVGInternalSubset;
3463 sax_modules.isStandalone=SVGIsStandalone;
3464 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3465 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3466 sax_modules.resolveEntity=SVGResolveEntity;
3467 sax_modules.getEntity=SVGGetEntity;
3468 sax_modules.entityDecl=SVGEntityDeclaration;
3469 sax_modules.notationDecl=SVGNotationDeclaration;
3470 sax_modules.attributeDecl=SVGAttributeDeclaration;
3471 sax_modules.elementDecl=SVGElementDeclaration;
3472 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3473 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3474 sax_modules.startDocument=SVGStartDocument;
3475 sax_modules.endDocument=SVGEndDocument;
3476 sax_modules.startElement=SVGStartElement;
3477 sax_modules.endElement=SVGEndElement;
3478 sax_modules.reference=SVGReference;
3479 sax_modules.characters=SVGCharacters;
3480 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3481 sax_modules.processingInstruction=SVGProcessingInstructions;
3482 sax_modules.comment=SVGComment;
3483 sax_modules.warning=SVGWarning;
3484 sax_modules.error=SVGError;
3485 sax_modules.fatalError=SVGError;
3486 sax_modules.getParameterEntity=SVGGetParameterEntity;
3487 sax_modules.cdataBlock=SVGCDataBlock;
3488 sax_modules.externalSubset=SVGExternalSubset;
3489 sax_handler=(&sax_modules);
3490 n=ReadBlob(image,MagickPathExtent-1,message);
3494 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3495 message,n,image->filename);
3496 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3499 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3504 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3505 SVGEndDocument(svg_info);
3506 xmlFreeParserCtxt(svg_info->parser);
3507 if (image->debug != MagickFalse)
3508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3509 (void) fclose(file);
3510 (void) CloseBlob(image);
3511 image->columns=svg_info->width;
3512 image->rows=svg_info->height;
3513 if (exception->severity >= ErrorException)
3515 svg_info=DestroySVGInfo(svg_info);
3516 (void) RelinquishUniqueFileResource(filename);
3517 image=DestroyImage(image);
3518 return((Image *) NULL);
3520 if (image_info->ping == MagickFalse)
3528 image=DestroyImage(image);
3529 image=(Image *) NULL;
3530 read_info=CloneImageInfo(image_info);
3531 SetImageInfoBlob(read_info,(void *) NULL,0);
3532 (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
3534 image=ReadImage(read_info,exception);
3535 read_info=DestroyImageInfo(read_info);
3536 if (image != (Image *) NULL)
3537 (void) CopyMagickString(image->filename,image_info->filename,
3541 Relinquish resources.
3543 if (image != (Image *) NULL)
3545 if (svg_info->title != (char *) NULL)
3546 (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3547 if (svg_info->comment != (char *) NULL)
3548 (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3551 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3553 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3554 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3555 next=GetNextImageInList(next);
3557 svg_info=DestroySVGInfo(svg_info);
3558 (void) RelinquishUniqueFileResource(filename);
3559 return(GetFirstImageInList(image));
3564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3568 % R e g i s t e r S V G I m a g e %
3572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3574 % RegisterSVGImage() adds attributes for the SVG image format to
3575 % the list of supported formats. The attributes include the image format
3576 % tag, a method to read and/or write the format, whether the format
3577 % supports the saving of more than one frame to the same file or blob,
3578 % whether the format supports native in-memory I/O, and a brief
3579 % description of the format.
3581 % The format of the RegisterSVGImage method is:
3583 % size_t RegisterSVGImage(void)
3586 ModuleExport size_t RegisterSVGImage(void)
3589 version[MagickPathExtent];
3595 #if defined(LIBXML_DOTTED_VERSION)
3596 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,
3599 #if defined(MAGICKCORE_RSVG_DELEGATE)
3600 #if !GLIB_CHECK_VERSION(2,35,0)
3603 #if defined(MAGICKCORE_XML_DELEGATE)
3606 (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d",
3607 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3609 entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics");
3610 #if defined(MAGICKCORE_XML_DELEGATE)
3611 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3613 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3614 entry->flags^=CoderBlobSupportFlag;
3615 #if defined(MAGICKCORE_RSVG_DELEGATE)
3616 entry->flags^=CoderDecoderThreadSupportFlag;
3618 entry->mime_type=ConstantString("image/svg+xml");
3619 if (*version != '\0')
3620 entry->version=ConstantString(version);
3621 entry->magick=(IsImageFormatHandler *) IsSVG;
3622 (void) RegisterMagickInfo(entry);
3623 entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics");
3624 #if defined(MAGICKCORE_XML_DELEGATE)
3625 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3627 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3628 entry->flags^=CoderBlobSupportFlag;
3629 #if defined(MAGICKCORE_RSVG_DELEGATE)
3630 entry->flags^=CoderDecoderThreadSupportFlag;
3632 entry->mime_type=ConstantString("image/svg+xml");
3633 if (*version != '\0')
3634 entry->version=ConstantString(version);
3635 entry->magick=(IsImageFormatHandler *) IsSVG;
3636 (void) RegisterMagickInfo(entry);
3637 entry=AcquireMagickInfo("SVG","MSVG",
3638 "ImageMagick's own SVG internal renderer");
3639 #if defined(MAGICKCORE_XML_DELEGATE)
3640 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3642 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3643 entry->flags^=CoderBlobSupportFlag;
3644 #if defined(MAGICKCORE_RSVG_DELEGATE)
3645 entry->flags^=CoderDecoderThreadSupportFlag;
3647 entry->magick=(IsImageFormatHandler *) IsSVG;
3648 (void) RegisterMagickInfo(entry);
3649 return(MagickImageCoderSignature);
3653 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3657 % U n r e g i s t e r S V G I m a g e %
3661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3663 % UnregisterSVGImage() removes format registrations made by the
3664 % SVG module from the list of supported formats.
3666 % The format of the UnregisterSVGImage method is:
3668 % UnregisterSVGImage(void)
3671 ModuleExport void UnregisterSVGImage(void)
3673 (void) UnregisterMagickInfo("SVGZ");
3674 (void) UnregisterMagickInfo("SVG");
3675 (void) UnregisterMagickInfo("MSVG");
3676 #if defined(MAGICKCORE_XML_DELEGATE)
3682 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3686 % W r i t e S V G I m a g e %
3690 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3692 % WriteSVGImage() writes a image in the SVG - XML based W3C standard
3695 % The format of the WriteSVGImage method is:
3697 % MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3698 % Image *image,ExceptionInfo *exception)
3700 % A description of each parameter follows.
3702 % o image_info: the image info.
3704 % o image: The image.
3706 % o exception: return any errors or warnings in this structure.
3710 static void AffineToTransform(Image *image,AffineMatrix *affine)
3713 transform[MagickPathExtent];
3715 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3717 if ((fabs(affine->rx) < MagickEpsilon) &&
3718 (fabs(affine->ry) < MagickEpsilon))
3720 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3721 (fabs(affine->sy-1.0) < MagickEpsilon))
3723 (void) WriteBlobString(image,"\">\n");
3726 (void) FormatLocaleString(transform,MagickPathExtent,
3727 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3728 (void) WriteBlobString(image,transform);
3733 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3734 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3735 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3741 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3742 (void) FormatLocaleString(transform,MagickPathExtent,
3743 "\" transform=\"rotate(%g)\">\n",theta);
3744 (void) WriteBlobString(image,transform);
3751 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3752 (fabs(affine->rx) < MagickEpsilon) &&
3753 (fabs(affine->ry) < MagickEpsilon) &&
3754 (fabs(affine->sy-1.0) < MagickEpsilon))
3756 (void) FormatLocaleString(transform,MagickPathExtent,
3757 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3758 (void) WriteBlobString(image,transform);
3762 (void) FormatLocaleString(transform,MagickPathExtent,
3763 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3764 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3765 (void) WriteBlobString(image,transform);
3768 static MagickBooleanType IsPoint(const char *point)
3776 value=strtol(point,&p,10);
3778 return(p != point ? MagickTrue : MagickFalse);
3781 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
3783 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3788 at_fitting_opts_type
3800 register const Quantum
3814 Trace image and write as SVG.
3816 fitting_options=at_fitting_opts_new();
3817 output_options=at_output_opts_new();
3818 (void) SetImageGray(image,exception);
3819 type=GetImageType(image);
3821 if ((type == BilevelType) || (type == GrayscaleType))
3823 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3825 for (y=0; y < (ssize_t) image->rows; y++)
3827 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3828 if (p == (const Quantum *) NULL)
3830 for (x=0; x < (ssize_t) image->columns; x++)
3832 trace->bitmap[i++]=GetPixelRed(image,p);
3833 if (number_planes == 3)
3835 trace->bitmap[i++]=GetPixelGreen(image,p);
3836 trace->bitmap[i++]=GetPixelBlue(image,p);
3838 p+=GetPixelChannels(image);
3841 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3843 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3844 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3849 at_splines_free(splines);
3850 at_bitmap_free(trace);
3851 at_output_opts_free(output_options);
3852 at_fitting_opts_free(fitting_options);
3858 message[MagickPathExtent];
3879 (void) WriteBlobString(image,
3880 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
3881 (void) WriteBlobString(image,
3882 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
3883 (void) WriteBlobString(image,
3884 " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
3885 (void) FormatLocaleString(message,MagickPathExtent,
3886 "<svg version=\"1.1\" id=\"Layer_1\" "
3887 "xmlns=\"http://www.w3.org/2000/svg\" "
3888 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" "
3889 "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" "
3890 "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">",
3891 (double) image->columns,(double) image->rows,
3892 (double) image->columns,(double) image->rows,
3893 (double) image->columns,(double) image->rows);
3894 (void) WriteBlobString(image,message);
3895 clone_image=CloneImage(image,0,0,MagickTrue,exception);
3896 if (clone_image == (Image *) NULL)
3897 return(MagickFalse);
3898 image_info=AcquireImageInfo();
3899 (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent);
3901 blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length,
3903 clone_image=DestroyImage(clone_image);
3904 image_info=DestroyImageInfo(image_info);
3905 if (blob == (unsigned char *) NULL)
3906 return(MagickFalse);
3908 base64=Base64Encode(blob,blob_length,&encode_length);
3909 blob=(unsigned char *) RelinquishMagickMemory(blob);
3910 (void) FormatLocaleString(message,MagickPathExtent,
3911 " <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" "
3912 "x=\"%.20g\" y=\"%.20g\"\n href=\"data:image/png;base64,",
3913 (double) image->scene,(double) image->columns,(double) image->rows,
3914 (double) image->page.x,(double) image->page.y);
3915 (void) WriteBlobString(image,message);
3917 for (i=(ssize_t) encode_length; i > 0; i-=76)
3919 (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p);
3920 (void) WriteBlobString(image,message);
3923 (void) WriteBlobString(image,"\n");
3925 base64=DestroyString(base64);
3926 (void) WriteBlobString(image,"\" />\n");
3927 (void) WriteBlobString(image,"</svg>\n");
3934 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
3935 ExceptionInfo *exception)
3937 #define BezierQuantum 200
3943 keyword[MagickPathExtent],
3944 message[MagickPathExtent],
3945 name[MagickPathExtent],
3948 type[MagickPathExtent];
3989 Open output image file.
3991 assert(image_info != (const ImageInfo *) NULL);
3992 assert(image_info->signature == MagickCoreSignature);
3993 assert(image != (Image *) NULL);
3994 assert(image->signature == MagickCoreSignature);
3995 if (image->debug != MagickFalse)
3996 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3997 assert(exception != (ExceptionInfo *) NULL);
3998 assert(exception->signature == MagickCoreSignature);
3999 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
4000 if (status == MagickFalse)
4002 value=GetImageArtifact(image,"SVG");
4003 if (value != (char *) NULL)
4005 (void) WriteBlobString(image,value);
4006 (void) CloseBlob(image);
4009 value=GetImageArtifact(image,"MVG");
4010 if (value == (char *) NULL)
4011 return(TraceSVGImage(image,exception));
4015 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
4016 (void) WriteBlobString(image,
4017 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
4018 (void) WriteBlobString(image,
4019 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
4020 (void) FormatLocaleString(message,MagickPathExtent,
4021 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
4023 (void) WriteBlobString(image,message);
4025 Allocate primitive info memory.
4028 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
4029 sizeof(*primitive_info));
4030 if (primitive_info == (PrimitiveInfo *) NULL)
4031 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
4032 GetAffineMatrix(&affine);
4033 token=AcquireString(value);
4034 extent=strlen(token)+MagickPathExtent;
4038 for (q=(const char *) value; *q != '\0'; )
4041 Interpret graphic primitive.
4043 GetNextToken(q,&q,MagickPathExtent,keyword);
4044 if (*keyword == '\0')
4046 if (*keyword == '#')
4051 if (active != MagickFalse)
4053 AffineToTransform(image,&affine);
4056 (void) WriteBlobString(image,"<desc>");
4057 (void) WriteBlobString(image,keyword+1);
4058 for ( ; (*q != '\n') && (*q != '\0'); q++)
4061 case '<': (void) WriteBlobString(image,"<"); break;
4062 case '>': (void) WriteBlobString(image,">"); break;
4063 case '&': (void) WriteBlobString(image,"&"); break;
4064 default: (void) WriteBlobByte(image,*q); break;
4066 (void) WriteBlobString(image,"</desc>\n");
4069 primitive_type=UndefinedPrimitive;
4077 if (LocaleCompare("affine",keyword) == 0)
4079 GetNextToken(q,&q,extent,token);
4080 affine.sx=StringToDouble(token,&next_token);
4081 GetNextToken(q,&q,extent,token);
4083 GetNextToken(q,&q,extent,token);
4084 affine.rx=StringToDouble(token,&next_token);
4085 GetNextToken(q,&q,extent,token);
4087 GetNextToken(q,&q,extent,token);
4088 affine.ry=StringToDouble(token,&next_token);
4089 GetNextToken(q,&q,extent,token);
4091 GetNextToken(q,&q,extent,token);
4092 affine.sy=StringToDouble(token,&next_token);
4093 GetNextToken(q,&q,extent,token);
4095 GetNextToken(q,&q,extent,token);
4096 affine.tx=StringToDouble(token,&next_token);
4097 GetNextToken(q,&q,extent,token);
4099 GetNextToken(q,&q,extent,token);
4100 affine.ty=StringToDouble(token,&next_token);
4103 if (LocaleCompare("alpha",keyword) == 0)
4105 primitive_type=AlphaPrimitive;
4108 if (LocaleCompare("angle",keyword) == 0)
4110 GetNextToken(q,&q,extent,token);
4111 affine.rx=StringToDouble(token,&next_token);
4112 affine.ry=StringToDouble(token,&next_token);
4115 if (LocaleCompare("arc",keyword) == 0)
4117 primitive_type=ArcPrimitive;
4126 if (LocaleCompare("bezier",keyword) == 0)
4128 primitive_type=BezierPrimitive;
4137 if (LocaleCompare("clip-path",keyword) == 0)
4139 GetNextToken(q,&q,extent,token);
4140 (void) FormatLocaleString(message,MagickPathExtent,
4141 "clip-path:url(#%s);",token);
4142 (void) WriteBlobString(image,message);
4145 if (LocaleCompare("clip-rule",keyword) == 0)
4147 GetNextToken(q,&q,extent,token);
4148 (void) FormatLocaleString(message,MagickPathExtent,"clip-rule:%s;",
4150 (void) WriteBlobString(image,message);
4153 if (LocaleCompare("clip-units",keyword) == 0)
4155 GetNextToken(q,&q,extent,token);
4156 (void) FormatLocaleString(message,MagickPathExtent,
4157 "clipPathUnits=%s;",token);
4158 (void) WriteBlobString(image,message);
4161 if (LocaleCompare("circle",keyword) == 0)
4163 primitive_type=CirclePrimitive;
4166 if (LocaleCompare("color",keyword) == 0)
4168 primitive_type=ColorPrimitive;
4177 if (LocaleCompare("decorate",keyword) == 0)
4179 GetNextToken(q,&q,extent,token);
4180 (void) FormatLocaleString(message,MagickPathExtent,
4181 "text-decoration:%s;",token);
4182 (void) WriteBlobString(image,message);
4191 if (LocaleCompare("ellipse",keyword) == 0)
4193 primitive_type=EllipsePrimitive;
4202 if (LocaleCompare("fill",keyword) == 0)
4204 GetNextToken(q,&q,extent,token);
4205 (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;",
4207 (void) WriteBlobString(image,message);
4210 if (LocaleCompare("fill-rule",keyword) == 0)
4212 GetNextToken(q,&q,extent,token);
4213 (void) FormatLocaleString(message,MagickPathExtent,
4214 "fill-rule:%s;",token);
4215 (void) WriteBlobString(image,message);
4218 if (LocaleCompare("fill-opacity",keyword) == 0)
4220 GetNextToken(q,&q,extent,token);
4221 (void) FormatLocaleString(message,MagickPathExtent,
4222 "fill-opacity:%s;",token);
4223 (void) WriteBlobString(image,message);
4226 if (LocaleCompare("font-family",keyword) == 0)
4228 GetNextToken(q,&q,extent,token);
4229 (void) FormatLocaleString(message,MagickPathExtent,
4230 "font-family:%s;",token);
4231 (void) WriteBlobString(image,message);
4234 if (LocaleCompare("font-stretch",keyword) == 0)
4236 GetNextToken(q,&q,extent,token);
4237 (void) FormatLocaleString(message,MagickPathExtent,
4238 "font-stretch:%s;",token);
4239 (void) WriteBlobString(image,message);
4242 if (LocaleCompare("font-style",keyword) == 0)
4244 GetNextToken(q,&q,extent,token);
4245 (void) FormatLocaleString(message,MagickPathExtent,
4246 "font-style:%s;",token);
4247 (void) WriteBlobString(image,message);
4250 if (LocaleCompare("font-size",keyword) == 0)
4252 GetNextToken(q,&q,extent,token);
4253 (void) FormatLocaleString(message,MagickPathExtent,
4254 "font-size:%s;",token);
4255 (void) WriteBlobString(image,message);
4258 if (LocaleCompare("font-weight",keyword) == 0)
4260 GetNextToken(q,&q,extent,token);
4261 (void) FormatLocaleString(message,MagickPathExtent,
4262 "font-weight:%s;",token);
4263 (void) WriteBlobString(image,message);
4272 if (LocaleCompare("gradient-units",keyword) == 0)
4274 GetNextToken(q,&q,extent,token);
4277 if (LocaleCompare("text-align",keyword) == 0)
4279 GetNextToken(q,&q,extent,token);
4280 (void) FormatLocaleString(message,MagickPathExtent,
4281 "text-align %s ",token);
4282 (void) WriteBlobString(image,message);
4285 if (LocaleCompare("text-anchor",keyword) == 0)
4287 GetNextToken(q,&q,extent,token);
4288 (void) FormatLocaleString(message,MagickPathExtent,
4289 "text-anchor %s ",token);
4290 (void) WriteBlobString(image,message);
4299 if (LocaleCompare("image",keyword) == 0)
4301 GetNextToken(q,&q,extent,token);
4302 primitive_type=ImagePrimitive;
4311 if (LocaleCompare("line",keyword) == 0)
4313 primitive_type=LinePrimitive;
4322 if (LocaleCompare("opacity",keyword) == 0)
4324 GetNextToken(q,&q,extent,token);
4325 (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ",
4327 (void) WriteBlobString(image,message);
4336 if (LocaleCompare("path",keyword) == 0)
4338 primitive_type=PathPrimitive;
4341 if (LocaleCompare("point",keyword) == 0)
4343 primitive_type=PointPrimitive;
4346 if (LocaleCompare("polyline",keyword) == 0)
4348 primitive_type=PolylinePrimitive;
4351 if (LocaleCompare("polygon",keyword) == 0)
4353 primitive_type=PolygonPrimitive;
4356 if (LocaleCompare("pop",keyword) == 0)
4358 GetNextToken(q,&q,extent,token);
4359 if (LocaleCompare("clip-path",token) == 0)
4361 (void) WriteBlobString(image,"</clipPath>\n");
4364 if (LocaleCompare("defs",token) == 0)
4366 (void) WriteBlobString(image,"</defs>\n");
4369 if (LocaleCompare("gradient",token) == 0)
4371 (void) FormatLocaleString(message,MagickPathExtent,
4372 "</%sGradient>\n",type);
4373 (void) WriteBlobString(image,message);
4376 if (LocaleCompare("graphic-context",token) == 0)
4380 ThrowWriterException(DrawError,
4381 "UnbalancedGraphicContextPushPop");
4382 (void) WriteBlobString(image,"</g>\n");
4384 if (LocaleCompare("pattern",token) == 0)
4386 (void) WriteBlobString(image,"</pattern>\n");
4389 if (LocaleCompare("symbol",token) == 0)
4391 (void) WriteBlobString(image,"</symbol>\n");
4394 if ((LocaleCompare("defs",token) == 0) ||
4395 (LocaleCompare("symbol",token) == 0))
4396 (void) WriteBlobString(image,"</g>\n");
4399 if (LocaleCompare("push",keyword) == 0)
4401 GetNextToken(q,&q,extent,token);
4402 if (LocaleCompare("clip-path",token) == 0)
4404 GetNextToken(q,&q,extent,token);
4405 (void) FormatLocaleString(message,MagickPathExtent,
4406 "<clipPath id=\"%s\">\n",token);
4407 (void) WriteBlobString(image,message);
4410 if (LocaleCompare("defs",token) == 0)
4412 (void) WriteBlobString(image,"<defs>\n");
4415 if (LocaleCompare("gradient",token) == 0)
4417 GetNextToken(q,&q,extent,token);
4418 (void) CopyMagickString(name,token,MagickPathExtent);
4419 GetNextToken(q,&q,extent,token);
4420 (void) CopyMagickString(type,token,MagickPathExtent);
4421 GetNextToken(q,&q,extent,token);
4422 svg_info.segment.x1=StringToDouble(token,&next_token);
4423 svg_info.element.cx=StringToDouble(token,&next_token);
4424 GetNextToken(q,&q,extent,token);
4426 GetNextToken(q,&q,extent,token);
4427 svg_info.segment.y1=StringToDouble(token,&next_token);
4428 svg_info.element.cy=StringToDouble(token,&next_token);
4429 GetNextToken(q,&q,extent,token);
4431 GetNextToken(q,&q,extent,token);
4432 svg_info.segment.x2=StringToDouble(token,&next_token);
4433 svg_info.element.major=StringToDouble(token,
4435 GetNextToken(q,&q,extent,token);
4437 GetNextToken(q,&q,extent,token);
4438 svg_info.segment.y2=StringToDouble(token,&next_token);
4439 svg_info.element.minor=StringToDouble(token,
4441 (void) FormatLocaleString(message,MagickPathExtent,
4442 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
4443 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
4444 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
4445 if (LocaleCompare(type,"radial") == 0)
4447 GetNextToken(q,&q,extent,token);
4449 GetNextToken(q,&q,extent,token);
4450 svg_info.element.angle=StringToDouble(token,
4452 (void) FormatLocaleString(message,MagickPathExtent,
4453 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
4454 "fx=\"%g\" fy=\"%g\">\n",type,name,
4455 svg_info.element.cx,svg_info.element.cy,
4456 svg_info.element.angle,svg_info.element.major,
4457 svg_info.element.minor);
4459 (void) WriteBlobString(image,message);
4462 if (LocaleCompare("graphic-context",token) == 0)
4467 AffineToTransform(image,&affine);
4470 (void) WriteBlobString(image,"<g style=\"");
4473 if (LocaleCompare("pattern",token) == 0)
4475 GetNextToken(q,&q,extent,token);
4476 (void) CopyMagickString(name,token,MagickPathExtent);
4477 GetNextToken(q,&q,extent,token);
4478 svg_info.bounds.x=StringToDouble(token,&next_token);
4479 GetNextToken(q,&q,extent,token);
4481 GetNextToken(q,&q,extent,token);
4482 svg_info.bounds.y=StringToDouble(token,&next_token);
4483 GetNextToken(q,&q,extent,token);
4485 GetNextToken(q,&q,extent,token);
4486 svg_info.bounds.width=StringToDouble(token,
4488 GetNextToken(q,&q,extent,token);
4490 GetNextToken(q,&q,extent,token);
4491 svg_info.bounds.height=StringToDouble(token,(char **) NULL);
4492 (void) FormatLocaleString(message,MagickPathExtent,
4493 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
4494 "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y,
4495 svg_info.bounds.width,svg_info.bounds.height);
4496 (void) WriteBlobString(image,message);
4499 if (LocaleCompare("symbol",token) == 0)
4501 (void) WriteBlobString(image,"<symbol>\n");
4512 if (LocaleCompare("rectangle",keyword) == 0)
4514 primitive_type=RectanglePrimitive;
4517 if (LocaleCompare("roundRectangle",keyword) == 0)
4519 primitive_type=RoundRectanglePrimitive;
4522 if (LocaleCompare("rotate",keyword) == 0)
4524 GetNextToken(q,&q,extent,token);
4525 (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ",
4527 (void) WriteBlobString(image,message);
4536 if (LocaleCompare("scale",keyword) == 0)
4538 GetNextToken(q,&q,extent,token);
4539 affine.sx=StringToDouble(token,&next_token);
4540 GetNextToken(q,&q,extent,token);
4542 GetNextToken(q,&q,extent,token);
4543 affine.sy=StringToDouble(token,&next_token);
4546 if (LocaleCompare("skewX",keyword) == 0)
4548 GetNextToken(q,&q,extent,token);
4549 (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ",
4551 (void) WriteBlobString(image,message);
4554 if (LocaleCompare("skewY",keyword) == 0)
4556 GetNextToken(q,&q,extent,token);
4557 (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ",
4559 (void) WriteBlobString(image,message);
4562 if (LocaleCompare("stop-color",keyword) == 0)
4565 color[MagickPathExtent];
4567 GetNextToken(q,&q,extent,token);
4568 (void) CopyMagickString(color,token,MagickPathExtent);
4569 GetNextToken(q,&q,extent,token);
4570 (void) FormatLocaleString(message,MagickPathExtent,
4571 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4572 (void) WriteBlobString(image,message);
4575 if (LocaleCompare("stroke",keyword) == 0)
4577 GetNextToken(q,&q,extent,token);
4578 (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;",
4580 (void) WriteBlobString(image,message);
4583 if (LocaleCompare("stroke-antialias",keyword) == 0)
4585 GetNextToken(q,&q,extent,token);
4586 (void) FormatLocaleString(message,MagickPathExtent,
4587 "stroke-antialias:%s;",token);
4588 (void) WriteBlobString(image,message);
4591 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4599 GetNextToken(p,&p,extent,token);
4600 for (k=0; IsPoint(token); k++)
4601 GetNextToken(p,&p,extent,token);
4602 (void) WriteBlobString(image,"stroke-dasharray:");
4603 for (j=0; j < k; j++)
4605 GetNextToken(q,&q,extent,token);
4606 (void) FormatLocaleString(message,MagickPathExtent,"%s ",
4608 (void) WriteBlobString(image,message);
4610 (void) WriteBlobString(image,";");
4613 GetNextToken(q,&q,extent,token);
4614 (void) FormatLocaleString(message,MagickPathExtent,
4615 "stroke-dasharray:%s;",token);
4616 (void) WriteBlobString(image,message);
4619 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4621 GetNextToken(q,&q,extent,token);
4622 (void) FormatLocaleString(message,MagickPathExtent,
4623 "stroke-dashoffset:%s;",token);
4624 (void) WriteBlobString(image,message);
4627 if (LocaleCompare("stroke-linecap",keyword) == 0)
4629 GetNextToken(q,&q,extent,token);
4630 (void) FormatLocaleString(message,MagickPathExtent,
4631 "stroke-linecap:%s;",token);
4632 (void) WriteBlobString(image,message);
4635 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4637 GetNextToken(q,&q,extent,token);
4638 (void) FormatLocaleString(message,MagickPathExtent,
4639 "stroke-linejoin:%s;",token);
4640 (void) WriteBlobString(image,message);
4643 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4645 GetNextToken(q,&q,extent,token);
4646 (void) FormatLocaleString(message,MagickPathExtent,
4647 "stroke-miterlimit:%s;",token);
4648 (void) WriteBlobString(image,message);
4651 if (LocaleCompare("stroke-opacity",keyword) == 0)
4653 GetNextToken(q,&q,extent,token);
4654 (void) FormatLocaleString(message,MagickPathExtent,
4655 "stroke-opacity:%s;",token);
4656 (void) WriteBlobString(image,message);
4659 if (LocaleCompare("stroke-width",keyword) == 0)
4661 GetNextToken(q,&q,extent,token);
4662 (void) FormatLocaleString(message,MagickPathExtent,
4663 "stroke-width:%s;",token);
4664 (void) WriteBlobString(image,message);
4673 if (LocaleCompare("text",keyword) == 0)
4675 primitive_type=TextPrimitive;
4678 if (LocaleCompare("text-antialias",keyword) == 0)
4680 GetNextToken(q,&q,extent,token);
4681 (void) FormatLocaleString(message,MagickPathExtent,
4682 "text-antialias:%s;",token);
4683 (void) WriteBlobString(image,message);
4686 if (LocaleCompare("tspan",keyword) == 0)
4688 primitive_type=TextPrimitive;
4691 if (LocaleCompare("translate",keyword) == 0)
4693 GetNextToken(q,&q,extent,token);
4694 affine.tx=StringToDouble(token,&next_token);
4695 GetNextToken(q,&q,extent,token);
4697 GetNextToken(q,&q,extent,token);
4698 affine.ty=StringToDouble(token,&next_token);
4707 if (LocaleCompare("viewbox",keyword) == 0)
4709 GetNextToken(q,&q,extent,token);
4711 GetNextToken(q,&q,extent,token);
4712 GetNextToken(q,&q,extent,token);
4714 GetNextToken(q,&q,extent,token);
4715 GetNextToken(q,&q,extent,token);
4717 GetNextToken(q,&q,extent,token);
4718 GetNextToken(q,&q,extent,token);
4730 if (status == MagickFalse)
4732 if (primitive_type == UndefinedPrimitive)
4735 Parse the primitive attributes.
4739 for (x=0; *q != '\0'; x++)
4744 if (IsPoint(q) == MagickFalse)
4746 GetNextToken(q,&q,extent,token);
4747 point.x=StringToDouble(token,&next_token);
4748 GetNextToken(q,&q,extent,token);
4750 GetNextToken(q,&q,extent,token);
4751 point.y=StringToDouble(token,&next_token);
4752 GetNextToken(q,(const char **) NULL,extent,token);
4754 GetNextToken(q,&q,extent,token);
4755 primitive_info[i].primitive=primitive_type;
4756 primitive_info[i].point=point;
4757 primitive_info[i].coordinates=0;
4758 primitive_info[i].method=FloodfillMethod;
4760 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4762 number_points+=6*BezierQuantum+360;
4763 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4764 number_points,sizeof(*primitive_info));
4765 if (primitive_info == (PrimitiveInfo *) NULL)
4767 (void) ThrowMagickException(exception,GetMagickModule(),
4768 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4772 primitive_info[j].primitive=primitive_type;
4773 primitive_info[j].coordinates=x;
4774 primitive_info[j].method=FloodfillMethod;
4775 primitive_info[j].text=(char *) NULL;
4778 AffineToTransform(image,&affine);
4782 switch (primitive_type)
4784 case PointPrimitive:
4787 if (primitive_info[j].coordinates != 1)
4796 if (primitive_info[j].coordinates != 2)
4801 (void) FormatLocaleString(message,MagickPathExtent,
4802 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4803 primitive_info[j].point.x,primitive_info[j].point.y,
4804 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4805 (void) WriteBlobString(image,message);
4808 case RectanglePrimitive:
4810 if (primitive_info[j].coordinates != 2)
4815 (void) FormatLocaleString(message,MagickPathExtent,
4816 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4817 primitive_info[j].point.x,primitive_info[j].point.y,
4818 primitive_info[j+1].point.x-primitive_info[j].point.x,
4819 primitive_info[j+1].point.y-primitive_info[j].point.y);
4820 (void) WriteBlobString(image,message);
4823 case RoundRectanglePrimitive:
4825 if (primitive_info[j].coordinates != 3)
4830 (void) FormatLocaleString(message,MagickPathExtent,
4831 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4832 "ry=\"%g\"/>\n",primitive_info[j].point.x,
4833 primitive_info[j].point.y,primitive_info[j+1].point.x-
4834 primitive_info[j].point.x,primitive_info[j+1].point.y-
4835 primitive_info[j].point.y,primitive_info[j+2].point.x,
4836 primitive_info[j+2].point.y);
4837 (void) WriteBlobString(image,message);
4842 if (primitive_info[j].coordinates != 3)
4849 case EllipsePrimitive:
4851 if (primitive_info[j].coordinates != 3)
4856 (void) FormatLocaleString(message,MagickPathExtent,
4857 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
4858 primitive_info[j].point.x,primitive_info[j].point.y,
4859 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4860 (void) WriteBlobString(image,message);
4863 case CirclePrimitive:
4869 if (primitive_info[j].coordinates != 2)
4874 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4875 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4876 (void) FormatLocaleString(message,MagickPathExtent,
4877 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
4878 primitive_info[j].point.x,primitive_info[j].point.y,
4880 (void) WriteBlobString(image,message);
4883 case PolylinePrimitive:
4885 if (primitive_info[j].coordinates < 2)
4890 (void) CopyMagickString(message," <polyline points=\"",
4892 (void) WriteBlobString(image,message);
4893 length=strlen(message);
4896 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4897 primitive_info[j].point.x,primitive_info[j].point.y);
4898 length+=strlen(message);
4901 (void) WriteBlobString(image,"\n ");
4902 length=strlen(message)+5;
4904 (void) WriteBlobString(image,message);
4906 (void) WriteBlobString(image,"\"/>\n");
4909 case PolygonPrimitive:
4911 if (primitive_info[j].coordinates < 3)
4916 primitive_info[i]=primitive_info[j];
4917 primitive_info[i].coordinates=0;
4918 primitive_info[j].coordinates++;
4920 (void) CopyMagickString(message," <polygon points=\"",MagickPathExtent);
4921 (void) WriteBlobString(image,message);
4922 length=strlen(message);
4925 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4926 primitive_info[j].point.x,primitive_info[j].point.y);
4927 length+=strlen(message);
4930 (void) WriteBlobString(image,"\n ");
4931 length=strlen(message)+5;
4933 (void) WriteBlobString(image,message);
4935 (void) WriteBlobString(image,"\"/>\n");
4938 case BezierPrimitive:
4940 if (primitive_info[j].coordinates < 3)
4952 GetNextToken(q,&q,extent,token);
4953 number_attributes=1;
4954 for (p=token; *p != '\0'; p++)
4955 if (isalpha((int) *p))
4956 number_attributes++;
4957 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
4959 number_points+=6*BezierQuantum*number_attributes;
4960 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4961 number_points,sizeof(*primitive_info));
4962 if (primitive_info == (PrimitiveInfo *) NULL)
4964 (void) ThrowMagickException(exception,GetMagickModule(),
4965 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4970 (void) WriteBlobString(image," <path d=\"");
4971 (void) WriteBlobString(image,token);
4972 (void) WriteBlobString(image,"\"/>\n");
4975 case AlphaPrimitive:
4976 case ColorPrimitive:
4978 if (primitive_info[j].coordinates != 1)
4983 GetNextToken(q,&q,extent,token);
4984 if (LocaleCompare("point",token) == 0)
4985 primitive_info[j].method=PointMethod;
4986 if (LocaleCompare("replace",token) == 0)
4987 primitive_info[j].method=ReplaceMethod;
4988 if (LocaleCompare("floodfill",token) == 0)
4989 primitive_info[j].method=FloodfillMethod;
4990 if (LocaleCompare("filltoborder",token) == 0)
4991 primitive_info[j].method=FillToBorderMethod;
4992 if (LocaleCompare("reset",token) == 0)
4993 primitive_info[j].method=ResetMethod;
5001 if (primitive_info[j].coordinates != 1)
5006 GetNextToken(q,&q,extent,token);
5007 (void) FormatLocaleString(message,MagickPathExtent,
5008 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
5009 primitive_info[j].point.y);
5010 (void) WriteBlobString(image,message);
5011 for (p=token; *p != '\0'; p++)
5014 case '<': (void) WriteBlobString(image,"<"); break;
5015 case '>': (void) WriteBlobString(image,">"); break;
5016 case '&': (void) WriteBlobString(image,"&"); break;
5017 default: (void) WriteBlobByte(image,*p); break;
5019 (void) WriteBlobString(image,"</text>\n");
5022 case ImagePrimitive:
5024 if (primitive_info[j].coordinates != 2)
5029 GetNextToken(q,&q,extent,token);
5030 (void) FormatLocaleString(message,MagickPathExtent,
5031 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
5032 "href=\"%s\"/>\n",primitive_info[j].point.x,
5033 primitive_info[j].point.y,primitive_info[j+1].point.x,
5034 primitive_info[j+1].point.y,token);
5035 (void) WriteBlobString(image,message);
5039 if (primitive_info == (PrimitiveInfo *) NULL)
5041 primitive_info[i].primitive=UndefinedPrimitive;
5042 if (status == MagickFalse)
5045 (void) WriteBlobString(image,"</svg>\n");
5047 Relinquish resources.
5049 token=DestroyString(token);
5050 if (primitive_info != (PrimitiveInfo *) NULL)
5051 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
5052 (void) CloseBlob(image);