2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Scalable Vector Graphics Format %
21 % Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % https://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/annotate.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/composite-private.h"
52 #include "MagickCore/delegate.h"
53 #include "MagickCore/delegate-private.h"
54 #include "MagickCore/draw.h"
55 #include "MagickCore/exception.h"
56 #include "MagickCore/exception-private.h"
57 #include "MagickCore/gem.h"
58 #include "MagickCore/image.h"
59 #include "MagickCore/image-private.h"
60 #include "MagickCore/list.h"
61 #include "MagickCore/log.h"
62 #include "MagickCore/magick.h"
63 #include "MagickCore/memory_.h"
64 #include "MagickCore/memory-private.h"
65 #include "MagickCore/module.h"
66 #include "MagickCore/monitor.h"
67 #include "MagickCore/monitor-private.h"
68 #include "MagickCore/quantum-private.h"
69 #include "MagickCore/pixel-accessor.h"
70 #include "MagickCore/property.h"
71 #include "MagickCore/resource_.h"
72 #include "MagickCore/static.h"
73 #include "MagickCore/string_.h"
74 #include "MagickCore/string-private.h"
75 #include "MagickCore/token.h"
76 #include "MagickCore/utility.h"
77 #if defined(MAGICKCORE_XML_DELEGATE)
78 # if defined(MAGICKCORE_WINDOWS_SUPPORT)
79 # if !defined(__MINGW32__)
80 # include <win32config.h>
83 # include <libxml/parser.h>
84 # include <libxml/xmlmemory.h>
85 # include <libxml/parserInternals.h>
86 # include <libxml/xmlerror.h>
89 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
90 #include "autotrace/autotrace.h"
93 #if defined(MAGICKCORE_RSVG_DELEGATE)
94 #include "librsvg/rsvg.h"
95 #if !defined(LIBRSVG_CHECK_VERSION)
96 #include "librsvg/rsvg-cairo.h"
97 #include "librsvg/librsvg-features.h"
98 #elif !LIBRSVG_CHECK_VERSION(2,36,2)
99 #include "librsvg/rsvg-cairo.h"
100 #include "librsvg/librsvg-features.h"
105 Typedef declarations.
107 typedef struct _BoundingBox
116 typedef struct _ElementInfo
126 typedef struct _SVGInfo
180 #if defined(MAGICKCORE_XML_DELEGATE)
193 Forward declarations.
195 static MagickBooleanType
196 WriteSVGImage(const ImageInfo *,Image *,ExceptionInfo *);
199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
209 % IsSVG()() returns MagickTrue if the image format type, identified by the
210 % magick string, is SVG.
212 % The format of the IsSVG method is:
214 % MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
216 % A description of each parameter follows:
218 % o magick: compare image format pattern against these bytes.
220 % o length: Specifies the length of the magick string.
223 static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
227 if (LocaleNCompare((const char *) magick,"?xml",4) == 0)
232 #if defined(MAGICKCORE_XML_DELEGATE)
234 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
238 % R e a d S V G I m a g e %
242 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244 % ReadSVGImage() reads a Scalable Vector Gaphics file and returns it. It
245 % allocates the memory necessary for the new Image structure and returns a
246 % pointer to the new image.
248 % The format of the ReadSVGImage method is:
250 % Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
252 % A description of each parameter follows:
254 % o image_info: the image info.
256 % o exception: return any errors or warnings in this structure.
260 static SVGInfo *AcquireSVGInfo(void)
265 svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info));
266 if (svg_info == (SVGInfo *) NULL)
267 return((SVGInfo *) NULL);
268 (void) memset(svg_info,0,sizeof(*svg_info));
269 svg_info->text=AcquireString("");
270 svg_info->scale=(double *) AcquireCriticalMemory(sizeof(*svg_info->scale));
271 GetAffineMatrix(&svg_info->affine);
272 svg_info->scale[0]=ExpandAffine(&svg_info->affine);
276 static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
278 if (svg_info->text != (char *) NULL)
279 svg_info->text=DestroyString(svg_info->text);
280 if (svg_info->scale != (double *) NULL)
281 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
282 if (svg_info->title != (char *) NULL)
283 svg_info->title=DestroyString(svg_info->title);
284 if (svg_info->comment != (char *) NULL)
285 svg_info->comment=DestroyString(svg_info->comment);
286 return((SVGInfo *) RelinquishMagickMemory(svg_info));
289 static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
294 token[MagickPathExtent];
302 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
303 assert(string != (const char *) NULL);
304 p=(const char *) string;
305 GetNextToken(p,&p,MagickPathExtent,token);
306 value=StringToDouble(token,&next_token);
307 if (strchr(token,'%') != (char *) NULL)
315 if (svg_info->view_box.width == 0.0)
317 return(svg_info->view_box.width*value/100.0);
321 if (svg_info->view_box.height == 0.0)
323 return(svg_info->view_box.height*value/100.0);
325 alpha=value-svg_info->view_box.width;
326 beta=value-svg_info->view_box.height;
327 return(hypot(alpha,beta)/sqrt(2.0)/100.0);
329 GetNextToken(p,&p,MagickPathExtent,token);
330 if (LocaleNCompare(token,"cm",2) == 0)
331 return(96.0*svg_info->scale[0]/2.54*value);
332 if (LocaleNCompare(token,"em",2) == 0)
333 return(svg_info->pointsize*value);
334 if (LocaleNCompare(token,"ex",2) == 0)
335 return(svg_info->pointsize*value/2.0);
336 if (LocaleNCompare(token,"in",2) == 0)
337 return(96.0*svg_info->scale[0]*value);
338 if (LocaleNCompare(token,"mm",2) == 0)
339 return(96.0*svg_info->scale[0]/25.4*value);
340 if (LocaleNCompare(token,"pc",2) == 0)
341 return(96.0*svg_info->scale[0]/6.0*value);
342 if (LocaleNCompare(token,"pt",2) == 0)
343 return(1.25*svg_info->scale[0]*value);
344 if (LocaleNCompare(token,"px",2) == 0)
349 #if defined(__cplusplus) || defined(c_plusplus)
353 static int SVGIsStandalone(void *context)
359 Is this document tagged standalone?
361 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGIsStandalone()");
362 svg_info=(SVGInfo *) context;
363 return(svg_info->document->standalone == 1);
366 static int SVGHasInternalSubset(void *context)
372 Does this document has an internal subset?
374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
375 " SAX.SVGHasInternalSubset()");
376 svg_info=(SVGInfo *) context;
377 return(svg_info->document->intSubset != NULL);
380 static int SVGHasExternalSubset(void *context)
386 Does this document has an external subset?
388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
389 " SAX.SVGHasExternalSubset()");
390 svg_info=(SVGInfo *) context;
391 return(svg_info->document->extSubset != NULL);
394 static void SVGInternalSubset(void *context,const xmlChar *name,
395 const xmlChar *external_id,const xmlChar *system_id)
401 Does this document has an internal subset?
403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
404 " SAX.internalSubset(%s, %s, %s)",(const char *) name,
405 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
406 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
407 svg_info=(SVGInfo *) context;
408 (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id);
411 static xmlParserInputPtr SVGResolveEntity(void *context,
412 const xmlChar *public_id,const xmlChar *system_id)
421 Special entity resolver, better left to the parser, it has more
422 context than the application layer. The default behaviour is to
423 not resolve the entities, in that case the ENTITY_REF nodes are
424 built in the structure (and the parameter values).
426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
427 " SAX.resolveEntity(%s, %s)",
428 (public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"),
429 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
430 svg_info=(SVGInfo *) context;
431 stream=xmlLoadExternalEntity((const char *) system_id,(const char *)
432 public_id,svg_info->parser);
436 static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
442 Get an entity by name.
444 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGGetEntity(%s)",
446 svg_info=(SVGInfo *) context;
447 return(xmlGetDocEntity(svg_info->document,name));
450 static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
456 Get a parameter entity by name.
458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
459 " SAX.getParameterEntity(%s)",name);
460 svg_info=(SVGInfo *) context;
461 return(xmlGetParameterEntity(svg_info->document,name));
464 static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
465 const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
471 An entity definition has been parsed.
473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
474 " SAX.entityDecl(%s, %d, %s, %s, %s)",name,type,
475 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
476 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content);
477 svg_info=(SVGInfo *) context;
478 if (svg_info->parser->inSubset == 1)
479 (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id,
482 if (svg_info->parser->inSubset == 2)
483 (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
487 static void SVGAttributeDeclaration(void *context,const xmlChar *element,
488 const xmlChar *name,int type,int value,const xmlChar *default_value,
489 xmlEnumerationPtr tree)
502 An attribute definition has been parsed.
504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
505 " SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value,
507 svg_info=(SVGInfo *) context;
508 fullname=(xmlChar *) NULL;
509 prefix=(xmlChar *) NULL;
510 parser=svg_info->parser;
511 fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix);
512 if (parser->inSubset == 1)
513 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset,
514 element,fullname,prefix,(xmlAttributeType) type,
515 (xmlAttributeDefault) value,default_value,tree);
517 if (parser->inSubset == 2)
518 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset,
519 element,fullname,prefix,(xmlAttributeType) type,
520 (xmlAttributeDefault) value,default_value,tree);
521 if (prefix != (xmlChar *) NULL)
523 if (fullname != (xmlChar *) NULL)
527 static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
528 xmlElementContentPtr content)
537 An element definition has been parsed.
539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
540 " SAX.elementDecl(%s, %d, ...)",name,type);
541 svg_info=(SVGInfo *) context;
542 parser=svg_info->parser;
543 if (parser->inSubset == 1)
544 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset,
545 name,(xmlElementTypeVal) type,content);
547 if (parser->inSubset == 2)
548 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
549 name,(xmlElementTypeVal) type,content);
552 static char **SVGKeyValuePairs(void *context,const int key_sentinel,
553 const int value_sentinel,const char *text,size_t *number_tokens)
571 svg_info=(SVGInfo *) context;
573 if (text == (const char *) NULL)
574 return((char **) NULL);
576 tokens=(char **) AcquireQuantumMemory(extent+2UL,sizeof(*tokens));
577 if (tokens == (char **) NULL)
579 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
580 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
581 return((char **) NULL);
584 Convert string to an ASCII list.
588 for (q=p; *q != '\0'; q++)
590 if ((*q != key_sentinel) && (*q != value_sentinel) && (*q != '\0'))
592 if (i == (ssize_t) extent)
595 tokens=(char **) ResizeQuantumMemory(tokens,extent+2,sizeof(*tokens));
596 if (tokens == (char **) NULL)
598 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
599 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
600 return((char **) NULL);
603 tokens[i]=AcquireString(p);
604 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
605 StripString(tokens[i]);
609 tokens[i]=AcquireString(p);
610 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
611 StripString(tokens[i++]);
612 tokens[i]=(char *) NULL;
613 *number_tokens=(size_t) i;
617 static void SVGNotationDeclaration(void *context,const xmlChar *name,
618 const xmlChar *public_id,const xmlChar *system_id)
627 What to do when a notation declaration has been parsed.
629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
630 " SAX.notationDecl(%s, %s, %s)",name,
631 public_id != (const xmlChar *) NULL ? (const char *) public_id : "none",
632 system_id != (const xmlChar *) NULL ? (const char *) system_id : "none");
633 svg_info=(SVGInfo *) context;
634 parser=svg_info->parser;
635 if (parser->inSubset == 1)
636 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
637 name,public_id,system_id);
639 if (parser->inSubset == 2)
640 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
641 name,public_id,system_id);
644 static void SVGProcessStyleElement(void *context,const xmlChar *name,
648 background[MagickPathExtent],
666 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
667 svg_info=(SVGInfo *) context;
668 tokens=SVGKeyValuePairs(context,':',';',style,&number_tokens);
669 if (tokens == (char **) NULL)
671 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
673 keyword=(char *) tokens[i];
674 value=(char *) tokens[i+1];
675 if (LocaleCompare(keyword,"font-size") != 0)
677 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
678 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
679 svg_info->pointsize);
681 color=AcquireString("none");
682 units=AcquireString("userSpaceOnUse");
683 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
685 keyword=(char *) tokens[i];
686 value=(char *) tokens[i+1];
687 (void) LogMagickEvent(CoderEvent,GetMagickModule()," %s: %s",keyword,
694 if (LocaleCompare((const char *) name,"background") == 0)
696 if (LocaleCompare((const char *) name,"svg") == 0)
697 (void) CopyMagickString(background,value,MagickPathExtent);
705 if (LocaleCompare(keyword,"clip-path") == 0)
707 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",value);
710 if (LocaleCompare(keyword,"clip-rule") == 0)
712 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",value);
715 if (LocaleCompare(keyword,"clipPathUnits") == 0)
717 (void) CloneString(&units,value);
718 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
722 if (LocaleCompare(keyword,"color") == 0)
724 (void) CloneString(&color,value);
732 if (LocaleCompare(keyword,"fill") == 0)
734 if (LocaleCompare(value,"currentColor") == 0)
736 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
739 if (LocaleCompare(value,"#000000ff") == 0)
740 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
742 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
745 if (LocaleCompare(keyword,"fillcolor") == 0)
747 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
750 if (LocaleCompare(keyword,"fill-rule") == 0)
752 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",value);
755 if (LocaleCompare(keyword,"fill-opacity") == 0)
757 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
761 if (LocaleCompare(keyword,"font-family") == 0)
763 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
767 if (LocaleCompare(keyword,"font-stretch") == 0)
769 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
773 if (LocaleCompare(keyword,"font-style") == 0)
775 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",value);
778 if (LocaleCompare(keyword,"font-size") == 0)
780 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
781 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
782 svg_info->pointsize);
785 if (LocaleCompare(keyword,"font-weight") == 0)
787 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
796 if (LocaleCompare(keyword,"offset") == 0)
798 (void) FormatLocaleFile(svg_info->file,"offset %g\n",
799 GetUserSpaceCoordinateValue(svg_info,1,value));
802 if (LocaleCompare(keyword,"opacity") == 0)
804 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
812 if (LocaleCompare(keyword,"stop-color") == 0)
814 (void) CloneString(&svg_info->stop_color,value);
817 if (LocaleCompare(keyword,"stroke") == 0)
819 if (LocaleCompare(value,"currentColor") == 0)
821 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",color);
824 if (LocaleCompare(value,"#000000ff") == 0)
825 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
827 (void) FormatLocaleFile(svg_info->file,
828 "stroke \"%s\"\n",value);
831 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
833 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
834 LocaleCompare(value,"true") == 0);
837 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
839 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
843 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
845 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
846 GetUserSpaceCoordinateValue(svg_info,1,value));
849 if (LocaleCompare(keyword,"stroke-linecap") == 0)
851 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
855 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
857 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
861 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
863 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit \"%s\"\n",
867 if (LocaleCompare(keyword,"stroke-opacity") == 0)
869 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
873 if (LocaleCompare(keyword,"stroke-width") == 0)
875 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
876 GetUserSpaceCoordinateValue(svg_info,1,value));
884 if (LocaleCompare(keyword,"text-align") == 0)
886 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",value);
889 if (LocaleCompare(keyword,"text-anchor") == 0)
891 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
895 if (LocaleCompare(keyword,"text-decoration") == 0)
897 if (LocaleCompare(value,"underline") == 0)
898 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
899 if (LocaleCompare(value,"line-through") == 0)
900 (void) FormatLocaleFile(svg_info->file,"decorate line-through\n");
901 if (LocaleCompare(value,"overline") == 0)
902 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
905 if (LocaleCompare(keyword,"text-antialiasing") == 0)
907 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
908 LocaleCompare(value,"true") == 0);
917 if (units != (char *) NULL)
918 units=DestroyString(units);
919 if (color != (char *) NULL)
920 color=DestroyString(color);
921 for (i=0; tokens[i] != (char *) NULL; i++)
922 tokens[i]=DestroyString(tokens[i]);
923 tokens=(char **) RelinquishMagickMemory(tokens);
926 static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
927 const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
933 What to do when an unparsed entity declaration is parsed.
935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
936 " SAX.unparsedEntityDecl(%s, %s, %s, %s)",name,
937 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
938 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation);
939 svg_info=(SVGInfo *) context;
940 (void) xmlAddDocEntity(svg_info->document,name,
941 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
945 static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
951 Receive the document locator at startup, actually xmlDefaultSAXLocator.
954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
955 " SAX.setDocumentLocator()");
956 svg_info=(SVGInfo *) context;
960 static void SVGStartDocument(void *context)
969 Called when the document start being processed.
971 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startDocument()");
972 svg_info=(SVGInfo *) context;
973 parser=svg_info->parser;
974 svg_info->document=xmlNewDoc(parser->version);
975 if (svg_info->document == (xmlDocPtr) NULL)
977 if (parser->encoding == NULL)
978 svg_info->document->encoding=(const xmlChar *) NULL;
980 svg_info->document->encoding=xmlStrdup(parser->encoding);
981 svg_info->document->standalone=parser->standalone;
984 static void SVGEndDocument(void *context)
990 Called when the document end has been detected.
992 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()");
993 svg_info=(SVGInfo *) context;
994 if (svg_info->offset != (char *) NULL)
995 svg_info->offset=DestroyString(svg_info->offset);
996 if (svg_info->stop_color != (char *) NULL)
997 svg_info->stop_color=DestroyString(svg_info->stop_color);
998 if (svg_info->scale != (double *) NULL)
999 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
1000 if (svg_info->text != (char *) NULL)
1001 svg_info->text=DestroyString(svg_info->text);
1002 if (svg_info->vertices != (char *) NULL)
1003 svg_info->vertices=DestroyString(svg_info->vertices);
1004 if (svg_info->url != (char *) NULL)
1005 svg_info->url=DestroyString(svg_info->url);
1006 #if defined(MAGICKCORE_XML_DELEGATE)
1007 if (svg_info->document != (xmlDocPtr) NULL)
1009 xmlFreeDoc(svg_info->document);
1010 svg_info->document=(xmlDocPtr) NULL;
1015 static void SVGStartElement(void *context,const xmlChar *name,
1016 const xmlChar **attributes)
1018 #define PushGraphicContext(id) \
1021 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); \
1023 (void) FormatLocaleFile(svg_info->file,"push graphic-context \"%s\"\n", \
1029 background[MagickPathExtent],
1030 id[MagickPathExtent],
1032 token[MagickPathExtent],
1052 Called when an opening tag has been processed.
1054 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s",
1056 svg_info=(SVGInfo *) context;
1058 svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale,
1059 svg_info->n+1UL,sizeof(*svg_info->scale));
1060 if (svg_info->scale == (double *) NULL)
1062 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
1063 ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
1066 svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
1067 color=AcquireString("none");
1068 units=AcquireString("userSpaceOnUse");
1072 value=(const char *) NULL;
1073 if ((LocaleCompare((char *) name,"image") == 0) ||
1074 (LocaleCompare((char *) name,"pattern") == 0) ||
1075 (LocaleCompare((char *) name,"rect") == 0) ||
1076 (LocaleCompare((char *) name,"text") == 0) ||
1077 (LocaleCompare((char *) name,"use") == 0))
1079 svg_info->bounds.x=0.0;
1080 svg_info->bounds.y=0.0;
1082 if (attributes != (const xmlChar **) NULL)
1083 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1085 keyword=(const char *) attributes[i];
1086 value=(const char *) attributes[i+1];
1092 if (LocaleCompare(keyword,"cx") == 0)
1094 svg_info->element.cx=
1095 GetUserSpaceCoordinateValue(svg_info,1,value);
1098 if (LocaleCompare(keyword,"cy") == 0)
1100 svg_info->element.cy=
1101 GetUserSpaceCoordinateValue(svg_info,-1,value);
1109 if (LocaleCompare(keyword,"fx") == 0)
1111 svg_info->element.major=
1112 GetUserSpaceCoordinateValue(svg_info,1,value);
1115 if (LocaleCompare(keyword,"fy") == 0)
1117 svg_info->element.minor=
1118 GetUserSpaceCoordinateValue(svg_info,-1,value);
1126 if (LocaleCompare(keyword,"height") == 0)
1128 svg_info->bounds.height=
1129 GetUserSpaceCoordinateValue(svg_info,-1,value);
1137 if (LocaleCompare(keyword,"id") == 0)
1139 (void) CopyMagickString(id,value,MagickPathExtent);
1147 if (LocaleCompare(keyword,"r") == 0)
1149 svg_info->element.angle=
1150 GetUserSpaceCoordinateValue(svg_info,0,value);
1158 if (LocaleCompare(keyword,"width") == 0)
1160 svg_info->bounds.width=
1161 GetUserSpaceCoordinateValue(svg_info,1,value);
1169 if (LocaleCompare(keyword,"x") == 0)
1171 if (LocaleCompare((char *) name,"tspan") != 0)
1172 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,
1173 value)-svg_info->center.x;
1176 if (LocaleCompare(keyword,"x1") == 0)
1178 svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
1182 if (LocaleCompare(keyword,"x2") == 0)
1184 svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
1193 if (LocaleCompare(keyword,"y") == 0)
1195 if (LocaleCompare((char *) name,"tspan") != 0)
1196 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,
1197 value)-svg_info->center.y;
1200 if (LocaleCompare(keyword,"y1") == 0)
1202 svg_info->segment.y1=GetUserSpaceCoordinateValue(svg_info,-1,
1206 if (LocaleCompare(keyword,"y2") == 0)
1208 svg_info->segment.y2=GetUserSpaceCoordinateValue(svg_info,-1,
1218 if (strchr((char *) name,':') != (char *) NULL)
1221 Skip over namespace.
1223 for ( ; *name != ':'; name++) ;
1231 if (LocaleCompare((const char *) name,"circle") == 0)
1233 PushGraphicContext(id);
1236 if (LocaleCompare((const char *) name,"clipPath") == 0)
1238 (void) FormatLocaleFile(svg_info->file,"push clip-path \"%s\"\n",id);
1246 if (LocaleCompare((const char *) name,"defs") == 0)
1248 (void) FormatLocaleFile(svg_info->file,"push defs\n");
1256 if (LocaleCompare((const char *) name,"ellipse") == 0)
1258 PushGraphicContext(id);
1266 if (LocaleCompare((const char *) name,"foreignObject") == 0)
1268 PushGraphicContext(id);
1276 if (LocaleCompare((const char *) name,"g") == 0)
1278 PushGraphicContext(id);
1286 if (LocaleCompare((const char *) name,"image") == 0)
1288 PushGraphicContext(id);
1296 if (LocaleCompare((const char *) name,"line") == 0)
1298 PushGraphicContext(id);
1301 if (LocaleCompare((const char *) name,"linearGradient") == 0)
1303 (void) FormatLocaleFile(svg_info->file,
1304 "push gradient \"%s\" linear %g,%g %g,%g\n",id,
1305 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
1306 svg_info->segment.y2);
1314 if (LocaleCompare((const char *) name,"mask") == 0)
1316 (void) FormatLocaleFile(svg_info->file,"push mask \"%s\"\n",id);
1324 if (LocaleCompare((const char *) name,"path") == 0)
1326 PushGraphicContext(id);
1329 if (LocaleCompare((const char *) name,"pattern") == 0)
1331 (void) FormatLocaleFile(svg_info->file,
1332 "push pattern \"%s\" %g,%g %g,%g\n",id,
1333 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
1334 svg_info->bounds.height);
1337 if (LocaleCompare((const char *) name,"polygon") == 0)
1339 PushGraphicContext(id);
1342 if (LocaleCompare((const char *) name,"polyline") == 0)
1344 PushGraphicContext(id);
1352 if (LocaleCompare((const char *) name,"radialGradient") == 0)
1354 (void) FormatLocaleFile(svg_info->file,
1355 "push gradient \"%s\" radial %g,%g %g,%g %g\n",
1356 id,svg_info->element.cx,svg_info->element.cy,
1357 svg_info->element.major,svg_info->element.minor,
1358 svg_info->element.angle);
1361 if (LocaleCompare((const char *) name,"rect") == 0)
1363 PushGraphicContext(id);
1371 if (LocaleCompare((char *) name,"style") == 0)
1373 if (LocaleCompare((const char *) name,"svg") == 0)
1375 svg_info->svgDepth++;
1376 PushGraphicContext(id);
1377 (void) FormatLocaleFile(svg_info->file,"compliance \"SVG\"\n");
1378 (void) FormatLocaleFile(svg_info->file,"fill \"black\"\n");
1379 (void) FormatLocaleFile(svg_info->file,"fill-opacity 1\n");
1380 (void) FormatLocaleFile(svg_info->file,"stroke \"none\"\n");
1381 (void) FormatLocaleFile(svg_info->file,"stroke-width 1\n");
1382 (void) FormatLocaleFile(svg_info->file,"stroke-opacity 1\n");
1383 (void) FormatLocaleFile(svg_info->file,"fill-rule nonzero\n");
1386 if (LocaleCompare((const char *) name,"symbol") == 0)
1388 (void) FormatLocaleFile(svg_info->file,"push symbol\n");
1396 if (LocaleCompare((const char *) name,"text") == 0)
1398 PushGraphicContext(id);
1399 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1400 svg_info->bounds.x,svg_info->bounds.y);
1401 svg_info->center.x=svg_info->bounds.x;
1402 svg_info->center.y=svg_info->bounds.y;
1403 svg_info->bounds.x=0.0;
1404 svg_info->bounds.y=0.0;
1405 svg_info->bounds.width=0.0;
1406 svg_info->bounds.height=0.0;
1409 if (LocaleCompare((const char *) name,"tspan") == 0)
1411 if (*svg_info->text != '\0')
1416 text=EscapeString(svg_info->text,'\'');
1417 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
1418 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1419 svg_info->center.y,text);
1420 text=DestroyString(text);
1421 *svg_info->text='\0';
1423 PushGraphicContext(id);
1431 if (LocaleCompare((char *) name,"use") == 0)
1433 PushGraphicContext(id);
1441 if (attributes != (const xmlChar **) NULL)
1442 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1444 keyword=(const char *) attributes[i];
1445 value=(const char *) attributes[i+1];
1446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1447 " %s = %s",keyword,value);
1453 if (LocaleCompare(keyword,"angle") == 0)
1455 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1456 GetUserSpaceCoordinateValue(svg_info,0,value));
1464 if (LocaleCompare(keyword,"class") == 0)
1471 GetNextToken(p,&p,MagickPathExtent,token);
1473 GetNextToken(p,&p,MagickPathExtent,token);
1476 (void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",
1483 if (LocaleCompare(keyword,"clip-path") == 0)
1485 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",
1489 if (LocaleCompare(keyword,"clip-rule") == 0)
1491 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",
1495 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1497 (void) CloneString(&units,value);
1498 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
1502 if (LocaleCompare(keyword,"color") == 0)
1504 (void) CloneString(&color,value);
1507 if (LocaleCompare(keyword,"cx") == 0)
1509 svg_info->element.cx=
1510 GetUserSpaceCoordinateValue(svg_info,1,value);
1513 if (LocaleCompare(keyword,"cy") == 0)
1515 svg_info->element.cy=
1516 GetUserSpaceCoordinateValue(svg_info,-1,value);
1524 if (LocaleCompare(keyword,"d") == 0)
1526 (void) CloneString(&svg_info->vertices,value);
1529 if (LocaleCompare(keyword,"dx") == 0)
1531 svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
1534 if (LocaleCompare(keyword,"dy") == 0)
1536 svg_info->bounds.y+=
1537 GetUserSpaceCoordinateValue(svg_info,-1,value);
1545 if (LocaleCompare(keyword,"fill") == 0)
1547 if (LocaleCompare(value,"currentColor") == 0)
1549 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
1552 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1555 if (LocaleCompare(keyword,"fillcolor") == 0)
1557 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1560 if (LocaleCompare(keyword,"fill-rule") == 0)
1562 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",
1566 if (LocaleCompare(keyword,"fill-opacity") == 0)
1568 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
1572 if (LocaleCompare(keyword,"font-family") == 0)
1574 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
1578 if (LocaleCompare(keyword,"font-stretch") == 0)
1580 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
1584 if (LocaleCompare(keyword,"font-style") == 0)
1586 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
1590 if (LocaleCompare(keyword,"font-size") == 0)
1592 if (LocaleCompare(value,"xx-small") == 0)
1593 svg_info->pointsize=6.144;
1594 else if (LocaleCompare(value,"x-small") == 0)
1595 svg_info->pointsize=7.68;
1596 else if (LocaleCompare(value,"small") == 0)
1597 svg_info->pointsize=9.6;
1598 else if (LocaleCompare(value,"medium") == 0)
1599 svg_info->pointsize=12.0;
1600 else if (LocaleCompare(value,"large") == 0)
1601 svg_info->pointsize=14.4;
1602 else if (LocaleCompare(value,"x-large") == 0)
1603 svg_info->pointsize=17.28;
1604 else if (LocaleCompare(value,"xx-large") == 0)
1605 svg_info->pointsize=20.736;
1607 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,
1609 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1610 svg_info->pointsize);
1613 if (LocaleCompare(keyword,"font-weight") == 0)
1615 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
1624 if (LocaleCompare(keyword,"gradientTransform") == 0)
1631 GetAffineMatrix(&transform);
1632 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1633 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
1634 if (tokens == (char **) NULL)
1636 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
1638 keyword=(char *) tokens[j];
1639 if (keyword == (char *) NULL)
1641 value=(char *) tokens[j+1];
1642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1643 " %s: %s",keyword,value);
1645 GetAffineMatrix(&affine);
1651 if (LocaleCompare(keyword,"matrix") == 0)
1653 p=(const char *) value;
1654 GetNextToken(p,&p,MagickPathExtent,token);
1655 affine.sx=StringToDouble(value,(char **) NULL);
1656 GetNextToken(p,&p,MagickPathExtent,token);
1658 GetNextToken(p,&p,MagickPathExtent,token);
1659 affine.rx=StringToDouble(token,&next_token);
1660 GetNextToken(p,&p,MagickPathExtent,token);
1662 GetNextToken(p,&p,MagickPathExtent,token);
1663 affine.ry=StringToDouble(token,&next_token);
1664 GetNextToken(p,&p,MagickPathExtent,token);
1666 GetNextToken(p,&p,MagickPathExtent,token);
1667 affine.sy=StringToDouble(token,&next_token);
1668 GetNextToken(p,&p,MagickPathExtent,token);
1670 GetNextToken(p,&p,MagickPathExtent,token);
1671 affine.tx=StringToDouble(token,&next_token);
1672 GetNextToken(p,&p,MagickPathExtent,token);
1674 GetNextToken(p,&p,MagickPathExtent,token);
1675 affine.ty=StringToDouble(token,&next_token);
1683 if (LocaleCompare(keyword,"rotate") == 0)
1688 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1689 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1690 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1691 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1692 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1700 if (LocaleCompare(keyword,"scale") == 0)
1702 for (p=(const char *) value; *p != '\0'; p++)
1703 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1706 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1707 affine.sy=affine.sx;
1710 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1711 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1714 if (LocaleCompare(keyword,"skewX") == 0)
1716 affine.sx=svg_info->affine.sx;
1717 affine.ry=tan(DegreesToRadians(fmod(
1718 GetUserSpaceCoordinateValue(svg_info,1,value),
1720 affine.sy=svg_info->affine.sy;
1723 if (LocaleCompare(keyword,"skewY") == 0)
1725 affine.sx=svg_info->affine.sx;
1726 affine.rx=tan(DegreesToRadians(fmod(
1727 GetUserSpaceCoordinateValue(svg_info,-1,value),
1729 affine.sy=svg_info->affine.sy;
1737 if (LocaleCompare(keyword,"translate") == 0)
1739 for (p=(const char *) value; *p != '\0'; p++)
1740 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1743 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1744 affine.ty=affine.tx;
1747 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1755 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1756 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1757 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1758 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1759 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1761 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1764 (void) FormatLocaleFile(svg_info->file,
1765 "affine %g %g %g %g %g %g\n",transform.sx,
1766 transform.rx,transform.ry,transform.sy,transform.tx,
1768 for (j=0; tokens[j] != (char *) NULL; j++)
1769 tokens[j]=DestroyString(tokens[j]);
1770 tokens=(char **) RelinquishMagickMemory(tokens);
1773 if (LocaleCompare(keyword,"gradientUnits") == 0)
1775 (void) CloneString(&units,value);
1776 (void) FormatLocaleFile(svg_info->file,"gradient-units \"%s\"\n",
1785 if (LocaleCompare(keyword,"height") == 0)
1787 svg_info->bounds.height=
1788 GetUserSpaceCoordinateValue(svg_info,-1,value);
1791 if (LocaleCompare(keyword,"href") == 0)
1793 (void) CloneString(&svg_info->url,value);
1801 if (LocaleCompare(keyword,"major") == 0)
1803 svg_info->element.major=
1804 GetUserSpaceCoordinateValue(svg_info,1,value);
1807 if (LocaleCompare(keyword,"mask") == 0)
1809 (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
1812 if (LocaleCompare(keyword,"minor") == 0)
1814 svg_info->element.minor=
1815 GetUserSpaceCoordinateValue(svg_info,-1,value);
1823 if (LocaleCompare(keyword,"offset") == 0)
1825 (void) CloneString(&svg_info->offset,value);
1828 if (LocaleCompare(keyword,"opacity") == 0)
1830 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
1838 if (LocaleCompare(keyword,"path") == 0)
1840 (void) CloneString(&svg_info->url,value);
1843 if (LocaleCompare(keyword,"points") == 0)
1845 (void) CloneString(&svg_info->vertices,value);
1853 if (LocaleCompare(keyword,"r") == 0)
1855 svg_info->element.major=
1856 GetUserSpaceCoordinateValue(svg_info,1,value);
1857 svg_info->element.minor=
1858 GetUserSpaceCoordinateValue(svg_info,-1,value);
1861 if (LocaleCompare(keyword,"rotate") == 0)
1866 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1867 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1868 svg_info->bounds.x,svg_info->bounds.y);
1869 svg_info->bounds.x=0;
1870 svg_info->bounds.y=0;
1871 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
1874 if (LocaleCompare(keyword,"rx") == 0)
1876 if (LocaleCompare((const char *) name,"ellipse") == 0)
1877 svg_info->element.major=
1878 GetUserSpaceCoordinateValue(svg_info,1,value);
1881 GetUserSpaceCoordinateValue(svg_info,1,value);
1884 if (LocaleCompare(keyword,"ry") == 0)
1886 if (LocaleCompare((const char *) name,"ellipse") == 0)
1887 svg_info->element.minor=
1888 GetUserSpaceCoordinateValue(svg_info,-1,value);
1891 GetUserSpaceCoordinateValue(svg_info,-1,value);
1899 if (LocaleCompare(keyword,"stop-color") == 0)
1901 (void) CloneString(&svg_info->stop_color,value);
1904 if (LocaleCompare(keyword,"stroke") == 0)
1906 if (LocaleCompare(value,"currentColor") == 0)
1908 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",
1912 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",value);
1915 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1917 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
1918 LocaleCompare(value,"true") == 0);
1921 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1923 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1927 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1929 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
1930 GetUserSpaceCoordinateValue(svg_info,1,value));
1933 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1935 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
1939 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1941 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
1945 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1947 (void) FormatLocaleFile(svg_info->file,
1948 "stroke-miterlimit \"%s\"\n",value);
1951 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1953 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
1957 if (LocaleCompare(keyword,"stroke-width") == 0)
1959 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
1960 GetUserSpaceCoordinateValue(svg_info,1,value));
1963 if (LocaleCompare(keyword,"style") == 0)
1965 SVGProcessStyleElement(context,name,value);
1973 if (LocaleCompare(keyword,"text-align") == 0)
1975 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",
1979 if (LocaleCompare(keyword,"text-anchor") == 0)
1981 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
1985 if (LocaleCompare(keyword,"text-decoration") == 0)
1987 if (LocaleCompare(value,"underline") == 0)
1988 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
1989 if (LocaleCompare(value,"line-through") == 0)
1990 (void) FormatLocaleFile(svg_info->file,
1991 "decorate line-through\n");
1992 if (LocaleCompare(value,"overline") == 0)
1993 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
1996 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1998 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
1999 LocaleCompare(value,"true") == 0);
2002 if (LocaleCompare(keyword,"transform") == 0)
2009 GetAffineMatrix(&transform);
2010 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
2011 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
2012 if (tokens == (char **) NULL)
2014 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2016 keyword=(char *) tokens[j];
2017 value=(char *) tokens[j+1];
2018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2019 " %s: %s",keyword,value);
2021 GetAffineMatrix(&affine);
2027 if (LocaleCompare(keyword,"matrix") == 0)
2029 p=(const char *) value;
2030 GetNextToken(p,&p,MagickPathExtent,token);
2031 affine.sx=StringToDouble(value,(char **) NULL);
2032 GetNextToken(p,&p,MagickPathExtent,token);
2034 GetNextToken(p,&p,MagickPathExtent,token);
2035 affine.rx=StringToDouble(token,&next_token);
2036 GetNextToken(p,&p,MagickPathExtent,token);
2038 GetNextToken(p,&p,MagickPathExtent,token);
2039 affine.ry=StringToDouble(token,&next_token);
2040 GetNextToken(p,&p,MagickPathExtent,token);
2042 GetNextToken(p,&p,MagickPathExtent,token);
2043 affine.sy=StringToDouble(token,&next_token);
2044 GetNextToken(p,&p,MagickPathExtent,token);
2046 GetNextToken(p,&p,MagickPathExtent,token);
2047 affine.tx=StringToDouble(token,&next_token);
2048 GetNextToken(p,&p,MagickPathExtent,token);
2050 GetNextToken(p,&p,MagickPathExtent,token);
2051 affine.ty=StringToDouble(token,&next_token);
2059 if (LocaleCompare(keyword,"rotate") == 0)
2066 p=(const char *) value;
2067 GetNextToken(p,&p,MagickPathExtent,token);
2068 angle=StringToDouble(value,(char **) NULL);
2069 GetNextToken(p,&p,MagickPathExtent,token);
2071 GetNextToken(p,&p,MagickPathExtent,token);
2072 x=StringToDouble(token,&next_token);
2073 GetNextToken(p,&p,MagickPathExtent,token);
2075 GetNextToken(p,&p,MagickPathExtent,token);
2076 y=StringToDouble(token,&next_token);
2077 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
2078 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
2079 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
2080 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
2083 svg_info->center.x=x;
2084 svg_info->center.y=y;
2092 if (LocaleCompare(keyword,"scale") == 0)
2094 for (p=(const char *) value; *p != '\0'; p++)
2095 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2098 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
2099 affine.sy=affine.sx;
2101 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
2103 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
2106 if (LocaleCompare(keyword,"skewX") == 0)
2108 affine.sx=svg_info->affine.sx;
2109 affine.ry=tan(DegreesToRadians(fmod(
2110 GetUserSpaceCoordinateValue(svg_info,1,value),
2112 affine.sy=svg_info->affine.sy;
2115 if (LocaleCompare(keyword,"skewY") == 0)
2117 affine.sx=svg_info->affine.sx;
2118 affine.rx=tan(DegreesToRadians(fmod(
2119 GetUserSpaceCoordinateValue(svg_info,-1,value),
2121 affine.sy=svg_info->affine.sy;
2129 if (LocaleCompare(keyword,"translate") == 0)
2131 for (p=(const char *) value; *p != '\0'; p++)
2132 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2135 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2136 affine.ty=affine.tx;
2138 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2147 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2148 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2149 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2150 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2151 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2153 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2156 (void) FormatLocaleFile(svg_info->file,
2157 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2158 transform.ry,transform.sy,transform.tx,transform.ty);
2159 for (j=0; tokens[j] != (char *) NULL; j++)
2160 tokens[j]=DestroyString(tokens[j]);
2161 tokens=(char **) RelinquishMagickMemory(tokens);
2169 if (LocaleCompare(keyword,"verts") == 0)
2171 (void) CloneString(&svg_info->vertices,value);
2174 if (LocaleCompare(keyword,"viewBox") == 0)
2176 p=(const char *) value;
2177 GetNextToken(p,&p,MagickPathExtent,token);
2178 svg_info->view_box.x=StringToDouble(token,&next_token);
2179 GetNextToken(p,&p,MagickPathExtent,token);
2181 GetNextToken(p,&p,MagickPathExtent,token);
2182 svg_info->view_box.y=StringToDouble(token,&next_token);
2183 GetNextToken(p,&p,MagickPathExtent,token);
2185 GetNextToken(p,&p,MagickPathExtent,token);
2186 svg_info->view_box.width=StringToDouble(token,
2188 if (svg_info->bounds.width == 0)
2189 svg_info->bounds.width=svg_info->view_box.width;
2190 GetNextToken(p,&p,MagickPathExtent,token);
2192 GetNextToken(p,&p,MagickPathExtent,token);
2193 svg_info->view_box.height=StringToDouble(token,
2195 if (svg_info->bounds.height == 0)
2196 svg_info->bounds.height=svg_info->view_box.height;
2204 if (LocaleCompare(keyword,"width") == 0)
2206 svg_info->bounds.width=
2207 GetUserSpaceCoordinateValue(svg_info,1,value);
2215 if (LocaleCompare(keyword,"x") == 0)
2217 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2220 if (LocaleCompare(keyword,"xlink:href") == 0)
2222 (void) CloneString(&svg_info->url,value);
2225 if (LocaleCompare(keyword,"x1") == 0)
2227 svg_info->segment.x1=
2228 GetUserSpaceCoordinateValue(svg_info,1,value);
2231 if (LocaleCompare(keyword,"x2") == 0)
2233 svg_info->segment.x2=
2234 GetUserSpaceCoordinateValue(svg_info,1,value);
2242 if (LocaleCompare(keyword,"y") == 0)
2244 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2247 if (LocaleCompare(keyword,"y1") == 0)
2249 svg_info->segment.y1=
2250 GetUserSpaceCoordinateValue(svg_info,-1,value);
2253 if (LocaleCompare(keyword,"y2") == 0)
2255 svg_info->segment.y2=
2256 GetUserSpaceCoordinateValue(svg_info,-1,value);
2265 if (LocaleCompare((const char *) name,"svg") == 0)
2267 if (svg_info->document->encoding != (const xmlChar *) NULL)
2268 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2269 (const char *) svg_info->document->encoding);
2270 if (attributes != (const xmlChar **) NULL)
2278 if ((svg_info->view_box.width == 0.0) ||
2279 (svg_info->view_box.height == 0.0))
2280 svg_info->view_box=svg_info->bounds;
2282 if (svg_info->bounds.width > 0.0)
2283 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2285 if (svg_info->bounds.height > 0.0)
2286 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2287 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2288 (double) svg_info->width,(double) svg_info->height);
2289 sx=(double) svg_info->width/svg_info->view_box.width;
2290 sy=(double) svg_info->height/svg_info->view_box.height;
2291 tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2293 ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2295 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2297 if ((svg_info->svgDepth == 1) && (*background != '\0'))
2299 PushGraphicContext(id);
2300 (void) FormatLocaleFile(svg_info->file,"fill %s\n",background);
2301 (void) FormatLocaleFile(svg_info->file,
2302 "rectangle 0,0 %g,%g\n",svg_info->view_box.width,
2303 svg_info->view_box.height);
2304 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2308 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2309 if (units != (char *) NULL)
2310 units=DestroyString(units);
2311 if (color != (char *) NULL)
2312 color=DestroyString(color);
2315 static void SVGEndElement(void *context,const xmlChar *name)
2321 Called when the end of an element has been detected.
2323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2324 " SAX.endElement(%s)",name);
2325 svg_info=(SVGInfo *) context;
2326 if (strchr((char *) name,':') != (char *) NULL)
2329 Skip over namespace.
2331 for ( ; *name != ':'; name++) ;
2339 if (LocaleCompare((const char *) name,"circle") == 0)
2341 (void) FormatLocaleFile(svg_info->file,"class \"circle\"\n");
2342 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2343 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2344 svg_info->element.cy+svg_info->element.minor);
2345 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2348 if (LocaleCompare((const char *) name,"clipPath") == 0)
2350 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2358 if (LocaleCompare((const char *) name,"defs") == 0)
2360 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2363 if (LocaleCompare((const char *) name,"desc") == 0)
2368 if (*svg_info->text == '\0')
2370 (void) fputc('#',svg_info->file);
2371 for (p=svg_info->text; *p != '\0'; p++)
2373 (void) fputc(*p,svg_info->file);
2375 (void) fputc('#',svg_info->file);
2377 (void) fputc('\n',svg_info->file);
2378 *svg_info->text='\0';
2386 if (LocaleCompare((const char *) name,"ellipse") == 0)
2391 (void) FormatLocaleFile(svg_info->file,"class \"ellipse\"\n");
2392 angle=svg_info->element.angle;
2393 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2394 svg_info->element.cx,svg_info->element.cy,
2395 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2396 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2397 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2405 if (LocaleCompare((const char *) name,"foreignObject") == 0)
2407 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2415 if (LocaleCompare((const char *) name,"g") == 0)
2417 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2425 if (LocaleCompare((const char *) name,"image") == 0)
2427 (void) FormatLocaleFile(svg_info->file,
2428 "image Over %g,%g %g,%g \"%s\"\n",svg_info->bounds.x,
2429 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2431 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2439 if (LocaleCompare((const char *) name,"line") == 0)
2441 (void) FormatLocaleFile(svg_info->file,"class \"line\"\n");
2442 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2443 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2444 svg_info->segment.y2);
2445 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2448 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2450 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2458 if (LocaleCompare((const char *) name,"mask") == 0)
2460 (void) FormatLocaleFile(svg_info->file,"pop mask\n");
2468 if (LocaleCompare((const char *) name,"pattern") == 0)
2470 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2473 if (LocaleCompare((const char *) name,"path") == 0)
2475 (void) FormatLocaleFile(svg_info->file,"class \"path\"\n");
2476 (void) FormatLocaleFile(svg_info->file,"path \"%s\"\n",
2477 svg_info->vertices);
2478 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2481 if (LocaleCompare((const char *) name,"polygon") == 0)
2483 (void) FormatLocaleFile(svg_info->file,"class \"polygon\"\n");
2484 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2485 svg_info->vertices);
2486 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2489 if (LocaleCompare((const char *) name,"polyline") == 0)
2491 (void) FormatLocaleFile(svg_info->file,"class \"polyline\"\n");
2492 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2493 svg_info->vertices);
2494 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2502 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2504 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2507 if (LocaleCompare((const char *) name,"rect") == 0)
2509 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2511 (void) FormatLocaleFile(svg_info->file,"class \"rect\"\n");
2512 if ((fabs(svg_info->bounds.width-1.0) < MagickEpsilon) &&
2513 (fabs(svg_info->bounds.height-1.0) < MagickEpsilon))
2514 (void) FormatLocaleFile(svg_info->file,"point %g,%g\n",
2515 svg_info->bounds.x,svg_info->bounds.y);
2517 (void) FormatLocaleFile(svg_info->file,
2518 "rectangle %g,%g %g,%g\n",svg_info->bounds.x,
2519 svg_info->bounds.y,svg_info->bounds.x+svg_info->bounds.width,
2520 svg_info->bounds.y+svg_info->bounds.height);
2521 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2524 if (svg_info->radius.x == 0.0)
2525 svg_info->radius.x=svg_info->radius.y;
2526 if (svg_info->radius.y == 0.0)
2527 svg_info->radius.y=svg_info->radius.x;
2528 (void) FormatLocaleFile(svg_info->file,
2529 "roundRectangle %g,%g %g,%g %g,%g\n",
2530 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2531 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2532 svg_info->radius.x,svg_info->radius.y);
2533 svg_info->radius.x=0.0;
2534 svg_info->radius.y=0.0;
2535 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2543 if (LocaleCompare((const char *) name,"stop") == 0)
2545 (void) FormatLocaleFile(svg_info->file,"stop-color \"%s\" %s\n",
2546 svg_info->stop_color,svg_info->offset);
2549 if (LocaleCompare((char *) name,"style") == 0)
2563 Find style definitions in svg_info->text.
2565 tokens=SVGKeyValuePairs(context,'{','}',svg_info->text,
2567 if (tokens == (char **) NULL)
2569 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2571 keyword=(char *) tokens[j];
2572 value=(char *) tokens[j+1];
2573 (void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
2574 *keyword == '.' ? keyword+1 : keyword);
2575 SVGProcessStyleElement(context,name,value);
2576 (void) FormatLocaleFile(svg_info->file,"pop class\n");
2580 if (LocaleCompare((const char *) name,"svg") == 0)
2582 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2583 svg_info->svgDepth--;
2586 if (LocaleCompare((const char *) name,"symbol") == 0)
2588 (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
2596 if (LocaleCompare((const char *) name,"text") == 0)
2598 if (*svg_info->text != '\0')
2603 (void) FormatLocaleFile(svg_info->file,"class \"text\"\n");
2604 text=EscapeString(svg_info->text,'\'');
2605 (void) FormatLocaleFile(svg_info->file,"text 0,0 \"%s\"\n",text);
2606 text=DestroyString(text);
2607 *svg_info->text='\0';
2609 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2612 if (LocaleCompare((const char *) name,"tspan") == 0)
2614 if (*svg_info->text != '\0')
2619 text=EscapeString(svg_info->text,'\'');
2620 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2621 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
2622 svg_info->center.y,text);
2623 text=DestroyString(text);
2624 *svg_info->text='\0';
2626 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2629 if (LocaleCompare((const char *) name,"title") == 0)
2631 if (*svg_info->text == '\0')
2633 (void) CloneString(&svg_info->title,svg_info->text);
2634 *svg_info->text='\0';
2642 if (LocaleCompare((char *) name,"use") == 0)
2644 if ((svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0))
2645 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2646 svg_info->bounds.x,svg_info->bounds.y);
2647 (void) FormatLocaleFile(svg_info->file,"use \"url(%s)\"\n",
2649 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2657 *svg_info->text='\0';
2658 (void) memset(&svg_info->element,0,sizeof(svg_info->element));
2659 (void) memset(&svg_info->segment,0,sizeof(svg_info->segment));
2663 static void SVGCharacters(void *context,const xmlChar *c,int length)
2678 Receiving some characters from the parser.
2680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2681 " SAX.characters(%s,%.20g)",c,(double) length);
2682 svg_info=(SVGInfo *) context;
2683 text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2684 if (text == (char *) NULL)
2687 for (i=0; i < (ssize_t) length; i++)
2691 if (svg_info->text == (char *) NULL)
2692 svg_info->text=text;
2695 (void) ConcatenateString(&svg_info->text,text);
2696 text=DestroyString(text);
2700 static void SVGReference(void *context,const xmlChar *name)
2709 Called when an entity reference is detected.
2711 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2713 svg_info=(SVGInfo *) context;
2714 parser=svg_info->parser;
2715 if (parser == (xmlParserCtxtPtr) NULL)
2717 if (parser->node == (xmlNodePtr) NULL)
2720 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2722 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2725 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2731 Receiving some ignorable whitespaces from the parser.
2733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2734 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2735 svg_info=(SVGInfo *) context;
2739 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2740 const xmlChar *data)
2746 A processing instruction has been parsed.
2748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2749 " SAX.processingInstruction(%s, %s)",target,data);
2750 svg_info=(SVGInfo *) context;
2754 static void SVGComment(void *context,const xmlChar *value)
2760 A comment has been parsed.
2762 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
2764 svg_info=(SVGInfo *) context;
2765 if (svg_info->comment != (char *) NULL)
2766 (void) ConcatenateString(&svg_info->comment,"\n");
2767 (void) ConcatenateString(&svg_info->comment,(const char *) value);
2770 static void SVGWarning(void *context,const char *format,...)
2774 reason[MagickPathExtent];
2783 Display and format a warning messages, gives file, line, position and
2786 va_start(operands,format);
2787 svg_info=(SVGInfo *) context;
2788 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
2789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2790 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2791 (void) vsprintf(reason,format,operands);
2793 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2795 message=GetExceptionMessage(errno);
2796 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2797 DelegateWarning,reason,"`%s`",message);
2798 message=DestroyString(message);
2802 static void SVGError(void *context,const char *format,...)
2806 reason[MagickPathExtent];
2815 Display and format a error formats, gives file, line, position and
2818 va_start(operands,format);
2819 svg_info=(SVGInfo *) context;
2820 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
2821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2822 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2823 (void) vsprintf(reason,format,operands);
2825 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2827 message=GetExceptionMessage(errno);
2828 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2829 reason,"`%s`",message);
2830 message=DestroyString(message);
2834 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2846 Called when a pcdata block has been parsed.
2848 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
2850 svg_info=(SVGInfo *) context;
2851 parser=svg_info->parser;
2852 child=xmlGetLastChild(parser->node);
2853 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2855 xmlTextConcat(child,value,length);
2858 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2861 static void SVGExternalSubset(void *context,const xmlChar *name,
2862 const xmlChar *external_id,const xmlChar *system_id)
2877 Does this document has an external subset?
2879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2880 " SAX.externalSubset(%s, %s, %s)",name,
2881 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2882 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2883 svg_info=(SVGInfo *) context;
2884 parser=svg_info->parser;
2885 if (((external_id == NULL) && (system_id == NULL)) ||
2886 ((parser->validate == 0) || (parser->wellFormed == 0) ||
2887 (svg_info->document == 0)))
2889 input=SVGResolveEntity(context,external_id,system_id);
2892 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2893 parser_context=(*parser);
2894 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2895 if (parser->inputTab == (xmlParserInputPtr *) NULL)
2897 parser->errNo=XML_ERR_NO_MEMORY;
2898 parser->input=parser_context.input;
2899 parser->inputNr=parser_context.inputNr;
2900 parser->inputMax=parser_context.inputMax;
2901 parser->inputTab=parser_context.inputTab;
2907 xmlPushInput(parser,input);
2908 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2909 if (input->filename == (char *) NULL)
2910 input->filename=(char *) xmlStrdup(system_id);
2913 input->base=parser->input->cur;
2914 input->cur=parser->input->cur;
2916 xmlParseExternalSubset(parser,external_id,system_id);
2917 while (parser->inputNr > 1)
2918 (void) xmlPopInput(parser);
2919 xmlFreeInputStream(parser->input);
2920 xmlFree(parser->inputTab);
2921 parser->input=parser_context.input;
2922 parser->inputNr=parser_context.inputNr;
2923 parser->inputMax=parser_context.inputMax;
2924 parser->inputTab=parser_context.inputTab;
2927 #if defined(__cplusplus) || defined(c_plusplus)
2932 Static declarations.
2935 SVGDensityGeometry[] = "96.0x96.0";
2938 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2941 filename[MagickPathExtent];
2961 message[MagickPathExtent];
2972 assert(image_info != (const ImageInfo *) NULL);
2973 assert(image_info->signature == MagickCoreSignature);
2974 assert(exception != (ExceptionInfo *) NULL);
2975 if (image_info->debug != MagickFalse)
2976 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2977 image_info->filename);
2978 assert(exception->signature == MagickCoreSignature);
2979 image=AcquireImage(image_info,exception);
2980 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2981 if (status == MagickFalse)
2983 image=DestroyImageList(image);
2984 return((Image *) NULL);
2986 if ((fabs(image->resolution.x) < MagickEpsilon) ||
2987 (fabs(image->resolution.y) < MagickEpsilon))
2995 flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
2996 image->resolution.x=geometry_info.rho;
2997 image->resolution.y=geometry_info.sigma;
2998 if ((flags & SigmaValue) == 0)
2999 image->resolution.y=image->resolution.x;
3001 if (LocaleCompare(image_info->magick,"MSVG") != 0)
3006 delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception);
3007 if (delegate_info != (const DelegateInfo *) NULL)
3010 background[MagickPathExtent],
3011 command[MagickPathExtent],
3013 input_filename[MagickPathExtent],
3014 opacity[MagickPathExtent],
3015 output_filename[MagickPathExtent],
3016 unique[MagickPathExtent];
3025 Our best hope for compliance with the SVG standard.
3027 status=AcquireUniqueSymbolicLink(image->filename,input_filename);
3028 (void) AcquireUniqueFilename(output_filename);
3029 (void) AcquireUniqueFilename(unique);
3030 density=AcquireString("");
3031 (void) FormatLocaleString(density,MagickPathExtent,"%.20g,%.20g",
3032 image->resolution.x,image->resolution.y);
3033 (void) FormatLocaleString(background,MagickPathExtent,
3034 "rgb(%.20g%%,%.20g%%,%.20g%%)",
3035 100.0*QuantumScale*image->background_color.red,
3036 100.0*QuantumScale*image->background_color.green,
3037 100.0*QuantumScale*image->background_color.blue);
3038 (void) FormatLocaleString(opacity,MagickPathExtent,"%.20g",
3039 QuantumScale*image->background_color.alpha);
3040 (void) FormatLocaleString(command,MagickPathExtent,
3041 GetDelegateCommands(delegate_info),input_filename,output_filename,
3042 density,background,opacity,unique);
3043 density=DestroyString(density);
3044 status=ExternalDelegateCommand(MagickFalse,image_info->verbose,
3045 command,(char *) NULL,exception);
3046 (void) RelinquishUniqueFileResource(unique);
3047 (void) RelinquishUniqueFileResource(input_filename);
3048 if ((status == 0) && (stat(output_filename,&attributes) == 0) &&
3049 (attributes.st_size > 0))
3057 read_info=CloneImageInfo(image_info);
3058 (void) CopyMagickString(read_info->filename,output_filename,
3060 svg_image=ReadImage(read_info,exception);
3061 read_info=DestroyImageInfo(read_info);
3062 (void) RelinquishUniqueFileResource(output_filename);
3063 if (svg_image != (Image *) NULL)
3065 for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; )
3067 (void) CopyMagickString(next->filename,image->filename,
3069 (void) CopyMagickString(next->magick,image->magick,
3071 next=GetNextImageInList(next);
3073 image=DestroyImage(image);
3077 (void) RelinquishUniqueFileResource(output_filename);
3080 #if defined(MAGICKCORE_RSVG_DELEGATE)
3081 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3094 register unsigned char
3107 register const guchar
3129 svg_handle=rsvg_handle_new();
3130 if (svg_handle == (RsvgHandle *) NULL)
3131 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3132 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
3133 if ((fabs(image->resolution.x) > MagickEpsilon) &&
3134 (fabs(image->resolution.y) > MagickEpsilon))
3135 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3136 image->resolution.y);
3137 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3140 error=(GError *) NULL;
3141 (void) rsvg_handle_write(svg_handle,message,n,&error);
3142 if (error != (GError *) NULL)
3143 g_error_free(error);
3145 error=(GError *) NULL;
3146 rsvg_handle_close(svg_handle,&error);
3147 if (error != (GError *) NULL)
3148 g_error_free(error);
3149 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3150 apply_density=MagickTrue;
3151 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
3152 if ((image->resolution.x > 0.0) && (image->resolution.y > 0.0))
3158 We should not apply the density when the internal 'factor' is 'i'.
3159 This can be checked by using the trick below.
3161 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x*256,
3162 image->resolution.y*256);
3163 rsvg_handle_get_dimensions(svg_handle,&dpi_dimension_info);
3164 if ((dpi_dimension_info.width != dimension_info.width) ||
3165 (dpi_dimension_info.height != dimension_info.height))
3166 apply_density=MagickFalse;
3167 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3168 image->resolution.y);
3170 if (image_info->size != (char *) NULL)
3172 (void) GetGeometry(image_info->size,(ssize_t *) NULL,
3173 (ssize_t *) NULL,&image->columns,&image->rows);
3174 if ((image->columns != 0) || (image->rows != 0))
3176 image->resolution.x=96.0*image->columns/dimension_info.width;
3177 image->resolution.y=96.0*image->rows/dimension_info.height;
3178 if (fabs(image->resolution.x) < MagickEpsilon)
3179 image->resolution.x=image->resolution.y;
3181 if (fabs(image->resolution.y) < MagickEpsilon)
3182 image->resolution.y=image->resolution.x;
3184 image->resolution.x=image->resolution.y=MagickMin(
3185 image->resolution.x,image->resolution.y);
3186 apply_density=MagickTrue;
3189 if (apply_density != MagickFalse)
3191 image->columns=image->resolution.x*dimension_info.width/96.0;
3192 image->rows=image->resolution.y*dimension_info.height/96.0;
3196 image->columns=dimension_info.width;
3197 image->rows=dimension_info.height;
3199 pixel_info=(MemoryInfo *) NULL;
3201 pixel_buffer=rsvg_handle_get_pixbuf(svg_handle);
3202 rsvg_handle_free(svg_handle);
3203 image->columns=gdk_pixbuf_get_width(pixel_buffer);
3204 image->rows=gdk_pixbuf_get_height(pixel_buffer);
3206 image->alpha_trait=BlendPixelTrait;
3207 status=SetImageExtent(image,image->columns,image->rows,exception);
3208 if (status == MagickFalse)
3210 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
3211 g_object_unref(G_OBJECT(pixel_buffer));
3213 g_object_unref(svg_handle);
3214 ThrowReaderException(MissingDelegateError,
3215 "NoDecodeDelegateForThisImageFormat");
3217 if (image_info->ping == MagickFalse)
3219 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3223 stride=4*image->columns;
3224 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
3225 stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
3226 (int) image->columns);
3228 pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels));
3229 if (pixel_info == (MemoryInfo *) NULL)
3231 g_object_unref(svg_handle);
3232 ThrowReaderException(ResourceLimitError,
3233 "MemoryAllocationFailed");
3235 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3237 (void) SetImageBackgroundColor(image,exception);
3238 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3239 cairo_surface=cairo_image_surface_create_for_data(pixels,
3240 CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int)
3242 if ((cairo_surface == (cairo_surface_t *) NULL) ||
3243 (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS))
3245 if (cairo_surface != (cairo_surface_t *) NULL)
3246 cairo_surface_destroy(cairo_surface);
3247 pixel_info=RelinquishVirtualMemory(pixel_info);
3248 g_object_unref(svg_handle);
3249 ThrowReaderException(ResourceLimitError,
3250 "MemoryAllocationFailed");
3252 cairo_image=cairo_create(cairo_surface);
3253 cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
3254 cairo_paint(cairo_image);
3255 cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
3256 if (apply_density != MagickFalse)
3257 cairo_scale(cairo_image,image->resolution.x/96.0,
3258 image->resolution.y/96.0);
3259 rsvg_handle_render_cairo(svg_handle,cairo_image);
3260 cairo_destroy(cairo_image);
3261 cairo_surface_destroy(cairo_surface);
3262 g_object_unref(svg_handle);
3265 p=gdk_pixbuf_get_pixels(pixel_buffer);
3267 GetPixelInfo(image,&fill_color);
3268 for (y=0; y < (ssize_t) image->rows; y++)
3270 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3271 if (q == (Quantum *) NULL)
3273 for (x=0; x < (ssize_t) image->columns; x++)
3275 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3276 fill_color.blue=ScaleCharToQuantum(*p++);
3277 fill_color.green=ScaleCharToQuantum(*p++);
3278 fill_color.red=ScaleCharToQuantum(*p++);
3280 fill_color.red=ScaleCharToQuantum(*p++);
3281 fill_color.green=ScaleCharToQuantum(*p++);
3282 fill_color.blue=ScaleCharToQuantum(*p++);
3284 fill_color.alpha=ScaleCharToQuantum(*p++);
3285 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3290 gamma=QuantumScale*fill_color.alpha;
3291 gamma=PerceptibleReciprocal(gamma);
3292 fill_color.blue*=gamma;
3293 fill_color.green*=gamma;
3294 fill_color.red*=gamma;
3297 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
3298 GetPixelAlpha(image,q),q);
3299 q+=GetPixelChannels(image);
3301 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3303 if (image->previous == (Image *) NULL)
3305 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
3307 if (status == MagickFalse)
3312 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3313 if (pixel_info != (MemoryInfo *) NULL)
3314 pixel_info=RelinquishVirtualMemory(pixel_info);
3316 g_object_unref(G_OBJECT(pixel_buffer));
3318 (void) CloseBlob(image);
3319 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3321 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3322 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3323 next=GetNextImageInList(next);
3325 return(GetFirstImageInList(image));
3333 unique_file=AcquireUniqueFileResource(filename);
3334 if (unique_file != -1)
3335 file=fdopen(unique_file,"w");
3336 if ((unique_file == -1) || (file == (FILE *) NULL))
3338 (void) CopyMagickString(image->filename,filename,MagickPathExtent);
3339 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
3341 image=DestroyImageList(image);
3342 return((Image *) NULL);
3347 svg_info=AcquireSVGInfo();
3348 if (svg_info == (SVGInfo *) NULL)
3350 (void) fclose(file);
3351 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3353 svg_info->file=file;
3354 svg_info->exception=exception;
3355 svg_info->image=image;
3356 svg_info->image_info=image_info;
3357 svg_info->bounds.width=image->columns;
3358 svg_info->bounds.height=image->rows;
3359 svg_info->svgDepth=0;
3360 if (image_info->size != (char *) NULL)
3361 (void) CloneString(&svg_info->size,image_info->size);
3362 if (image->debug != MagickFalse)
3363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3364 (void) xmlSubstituteEntitiesDefault(1);
3365 (void) memset(&sax_modules,0,sizeof(sax_modules));
3366 sax_modules.internalSubset=SVGInternalSubset;
3367 sax_modules.isStandalone=SVGIsStandalone;
3368 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3369 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3370 sax_modules.resolveEntity=SVGResolveEntity;
3371 sax_modules.getEntity=SVGGetEntity;
3372 sax_modules.entityDecl=SVGEntityDeclaration;
3373 sax_modules.notationDecl=SVGNotationDeclaration;
3374 sax_modules.attributeDecl=SVGAttributeDeclaration;
3375 sax_modules.elementDecl=SVGElementDeclaration;
3376 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3377 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3378 sax_modules.startDocument=SVGStartDocument;
3379 sax_modules.endDocument=SVGEndDocument;
3380 sax_modules.startElement=SVGStartElement;
3381 sax_modules.endElement=SVGEndElement;
3382 sax_modules.reference=SVGReference;
3383 sax_modules.characters=SVGCharacters;
3384 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3385 sax_modules.processingInstruction=SVGProcessingInstructions;
3386 sax_modules.comment=SVGComment;
3387 sax_modules.warning=SVGWarning;
3388 sax_modules.error=SVGError;
3389 sax_modules.fatalError=SVGError;
3390 sax_modules.getParameterEntity=SVGGetParameterEntity;
3391 sax_modules.cdataBlock=SVGCDataBlock;
3392 sax_modules.externalSubset=SVGExternalSubset;
3393 sax_handler=(&sax_modules);
3394 n=ReadBlob(image,MagickPathExtent-1,message);
3398 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3399 message,n,image->filename);
3400 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3403 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3408 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3409 SVGEndDocument(svg_info);
3410 xmlFreeParserCtxt(svg_info->parser);
3411 if (image->debug != MagickFalse)
3412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3413 (void) fclose(file);
3414 (void) CloseBlob(image);
3415 image->columns=svg_info->width;
3416 image->rows=svg_info->height;
3417 if (exception->severity >= ErrorException)
3419 svg_info=DestroySVGInfo(svg_info);
3420 (void) RelinquishUniqueFileResource(filename);
3421 image=DestroyImage(image);
3422 return((Image *) NULL);
3424 if (image_info->ping == MagickFalse)
3432 image=DestroyImage(image);
3433 image=(Image *) NULL;
3434 read_info=CloneImageInfo(image_info);
3435 SetImageInfoBlob(read_info,(void *) NULL,0);
3436 if (read_info->density != (char *) NULL)
3437 read_info->density=DestroyString(read_info->density);
3438 (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
3440 image=ReadImage(read_info,exception);
3441 read_info=DestroyImageInfo(read_info);
3442 if (image != (Image *) NULL)
3443 (void) CopyMagickString(image->filename,image_info->filename,
3447 Relinquish resources.
3449 if (image != (Image *) NULL)
3451 if (svg_info->title != (char *) NULL)
3452 (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3453 if (svg_info->comment != (char *) NULL)
3454 (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3457 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3459 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3460 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3461 next=GetNextImageInList(next);
3463 svg_info=DestroySVGInfo(svg_info);
3464 (void) RelinquishUniqueFileResource(filename);
3465 return(GetFirstImageInList(image));
3470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3474 % R e g i s t e r S V G I m a g e %
3478 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3480 % RegisterSVGImage() adds attributes for the SVG image format to
3481 % the list of supported formats. The attributes include the image format
3482 % tag, a method to read and/or write the format, whether the format
3483 % supports the saving of more than one frame to the same file or blob,
3484 % whether the format supports native in-memory I/O, and a brief
3485 % description of the format.
3487 % The format of the RegisterSVGImage method is:
3489 % size_t RegisterSVGImage(void)
3492 ModuleExport size_t RegisterSVGImage(void)
3495 version[MagickPathExtent];
3501 #if defined(LIBXML_DOTTED_VERSION)
3502 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,
3505 #if defined(MAGICKCORE_RSVG_DELEGATE)
3506 #if !GLIB_CHECK_VERSION(2,35,0)
3509 #if defined(MAGICKCORE_XML_DELEGATE)
3512 (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d",
3513 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3515 entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics");
3516 #if defined(MAGICKCORE_XML_DELEGATE)
3517 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3519 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3520 entry->flags^=CoderBlobSupportFlag;
3521 #if defined(MAGICKCORE_RSVG_DELEGATE)
3522 entry->flags^=CoderDecoderThreadSupportFlag;
3524 entry->mime_type=ConstantString("image/svg+xml");
3525 if (*version != '\0')
3526 entry->version=ConstantString(version);
3527 entry->magick=(IsImageFormatHandler *) IsSVG;
3528 (void) RegisterMagickInfo(entry);
3529 entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics");
3530 #if defined(MAGICKCORE_XML_DELEGATE)
3531 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3533 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3534 entry->flags^=CoderBlobSupportFlag;
3535 #if defined(MAGICKCORE_RSVG_DELEGATE)
3536 entry->flags^=CoderDecoderThreadSupportFlag;
3538 entry->mime_type=ConstantString("image/svg+xml");
3539 if (*version != '\0')
3540 entry->version=ConstantString(version);
3541 entry->magick=(IsImageFormatHandler *) IsSVG;
3542 (void) RegisterMagickInfo(entry);
3543 entry=AcquireMagickInfo("SVG","MSVG",
3544 "ImageMagick's own SVG internal renderer");
3545 #if defined(MAGICKCORE_XML_DELEGATE)
3546 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3548 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3549 entry->flags^=CoderBlobSupportFlag;
3550 #if defined(MAGICKCORE_RSVG_DELEGATE)
3551 entry->flags^=CoderDecoderThreadSupportFlag;
3553 entry->magick=(IsImageFormatHandler *) IsSVG;
3554 (void) RegisterMagickInfo(entry);
3555 return(MagickImageCoderSignature);
3559 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3563 % U n r e g i s t e r S V G I m a g e %
3567 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3569 % UnregisterSVGImage() removes format registrations made by the
3570 % SVG module from the list of supported formats.
3572 % The format of the UnregisterSVGImage method is:
3574 % UnregisterSVGImage(void)
3577 ModuleExport void UnregisterSVGImage(void)
3579 (void) UnregisterMagickInfo("SVGZ");
3580 (void) UnregisterMagickInfo("SVG");
3581 (void) UnregisterMagickInfo("MSVG");
3582 #if defined(MAGICKCORE_XML_DELEGATE)
3588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3592 % W r i t e S V G I m a g e %
3596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3598 % WriteSVGImage() writes a image in the SVG - XML based W3C standard
3601 % The format of the WriteSVGImage method is:
3603 % MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3604 % Image *image,ExceptionInfo *exception)
3606 % A description of each parameter follows.
3608 % o image_info: the image info.
3610 % o image: The image.
3612 % o exception: return any errors or warnings in this structure.
3616 static void AffineToTransform(Image *image,AffineMatrix *affine)
3619 transform[MagickPathExtent];
3621 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3623 if ((fabs(affine->rx) < MagickEpsilon) &&
3624 (fabs(affine->ry) < MagickEpsilon))
3626 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3627 (fabs(affine->sy-1.0) < MagickEpsilon))
3629 (void) WriteBlobString(image,"\">\n");
3632 (void) FormatLocaleString(transform,MagickPathExtent,
3633 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3634 (void) WriteBlobString(image,transform);
3639 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3640 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3641 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3647 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3648 (void) FormatLocaleString(transform,MagickPathExtent,
3649 "\" transform=\"rotate(%g)\">\n",theta);
3650 (void) WriteBlobString(image,transform);
3657 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3658 (fabs(affine->rx) < MagickEpsilon) &&
3659 (fabs(affine->ry) < MagickEpsilon) &&
3660 (fabs(affine->sy-1.0) < MagickEpsilon))
3662 (void) FormatLocaleString(transform,MagickPathExtent,
3663 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3664 (void) WriteBlobString(image,transform);
3668 (void) FormatLocaleString(transform,MagickPathExtent,
3669 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3670 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3671 (void) WriteBlobString(image,transform);
3674 static MagickBooleanType IsPoint(const char *point)
3682 value=strtol(point,&p,10);
3684 return(p != point ? MagickTrue : MagickFalse);
3687 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
3689 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3694 at_fitting_opts_type
3706 register const Quantum
3720 Trace image and write as SVG.
3722 fitting_options=at_fitting_opts_new();
3723 output_options=at_output_opts_new();
3724 (void) SetImageGray(image,exception);
3725 type=GetImageType(image);
3727 if ((type == BilevelType) || (type == GrayscaleType))
3729 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3731 for (y=0; y < (ssize_t) image->rows; y++)
3733 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3734 if (p == (const Quantum *) NULL)
3736 for (x=0; x < (ssize_t) image->columns; x++)
3738 trace->bitmap[i++]=GetPixelRed(image,p);
3739 if (number_planes == 3)
3741 trace->bitmap[i++]=GetPixelGreen(image,p);
3742 trace->bitmap[i++]=GetPixelBlue(image,p);
3744 p+=GetPixelChannels(image);
3747 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3749 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3750 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3755 at_splines_free(splines);
3756 at_bitmap_free(trace);
3757 at_output_opts_free(output_options);
3758 at_fitting_opts_free(fitting_options);
3764 message[MagickPathExtent];
3785 (void) WriteBlobString(image,
3786 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
3787 (void) WriteBlobString(image,
3788 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
3789 (void) WriteBlobString(image,
3790 " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
3791 (void) FormatLocaleString(message,MagickPathExtent,
3792 "<svg version=\"1.1\" id=\"Layer_1\" "
3793 "xmlns=\"http://www.w3.org/2000/svg\" "
3794 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" "
3795 "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" "
3796 "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">",
3797 (double) image->columns,(double) image->rows,
3798 (double) image->columns,(double) image->rows,
3799 (double) image->columns,(double) image->rows);
3800 (void) WriteBlobString(image,message);
3801 clone_image=CloneImage(image,0,0,MagickTrue,exception);
3802 if (clone_image == (Image *) NULL)
3803 return(MagickFalse);
3804 image_info=AcquireImageInfo();
3805 (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent);
3807 blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length,
3809 clone_image=DestroyImage(clone_image);
3810 image_info=DestroyImageInfo(image_info);
3811 if (blob == (unsigned char *) NULL)
3812 return(MagickFalse);
3814 base64=Base64Encode(blob,blob_length,&encode_length);
3815 blob=(unsigned char *) RelinquishMagickMemory(blob);
3816 (void) FormatLocaleString(message,MagickPathExtent,
3817 " <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" "
3818 "x=\"%.20g\" y=\"%.20g\"\n href=\"data:image/png;base64,",
3819 (double) image->scene,(double) image->columns,(double) image->rows,
3820 (double) image->page.x,(double) image->page.y);
3821 (void) WriteBlobString(image,message);
3823 for (i=(ssize_t) encode_length; i > 0; i-=76)
3825 (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p);
3826 (void) WriteBlobString(image,message);
3829 (void) WriteBlobString(image,"\n");
3831 base64=DestroyString(base64);
3832 (void) WriteBlobString(image,"\" />\n");
3833 (void) WriteBlobString(image,"</svg>\n");
3840 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
3841 ExceptionInfo *exception)
3843 #define BezierQuantum 200
3849 keyword[MagickPathExtent],
3850 message[MagickPathExtent],
3851 name[MagickPathExtent],
3854 type[MagickPathExtent];
3895 Open output image file.
3897 assert(image_info != (const ImageInfo *) NULL);
3898 assert(image_info->signature == MagickCoreSignature);
3899 assert(image != (Image *) NULL);
3900 assert(image->signature == MagickCoreSignature);
3901 if (image->debug != MagickFalse)
3902 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3903 assert(exception != (ExceptionInfo *) NULL);
3904 assert(exception->signature == MagickCoreSignature);
3905 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3906 if (status == MagickFalse)
3908 value=GetImageArtifact(image,"SVG");
3909 if (value != (char *) NULL)
3911 (void) WriteBlobString(image,value);
3912 (void) CloseBlob(image);
3915 value=GetImageArtifact(image,"MVG");
3916 if (value == (char *) NULL)
3917 return(TraceSVGImage(image,exception));
3921 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3922 (void) WriteBlobString(image,
3923 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3924 (void) WriteBlobString(image,
3925 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3926 (void) FormatLocaleString(message,MagickPathExtent,
3927 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3929 (void) WriteBlobString(image,message);
3931 Allocate primitive info memory.
3934 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3935 sizeof(*primitive_info));
3936 if (primitive_info == (PrimitiveInfo *) NULL)
3937 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3938 GetAffineMatrix(&affine);
3939 token=AcquireString(value);
3940 extent=strlen(token)+MagickPathExtent;
3944 for (q=(const char *) value; *q != '\0'; )
3947 Interpret graphic primitive.
3949 GetNextToken(q,&q,MagickPathExtent,keyword);
3950 if (*keyword == '\0')
3952 if (*keyword == '#')
3957 if (active != MagickFalse)
3959 AffineToTransform(image,&affine);
3962 (void) WriteBlobString(image,"<desc>");
3963 (void) WriteBlobString(image,keyword+1);
3964 for ( ; (*q != '\n') && (*q != '\0'); q++)
3967 case '<': (void) WriteBlobString(image,"<"); break;
3968 case '>': (void) WriteBlobString(image,">"); break;
3969 case '&': (void) WriteBlobString(image,"&"); break;
3970 default: (void) WriteBlobByte(image,*q); break;
3972 (void) WriteBlobString(image,"</desc>\n");
3975 primitive_type=UndefinedPrimitive;
3983 if (LocaleCompare("affine",keyword) == 0)
3985 GetNextToken(q,&q,extent,token);
3986 affine.sx=StringToDouble(token,&next_token);
3987 GetNextToken(q,&q,extent,token);
3989 GetNextToken(q,&q,extent,token);
3990 affine.rx=StringToDouble(token,&next_token);
3991 GetNextToken(q,&q,extent,token);
3993 GetNextToken(q,&q,extent,token);
3994 affine.ry=StringToDouble(token,&next_token);
3995 GetNextToken(q,&q,extent,token);
3997 GetNextToken(q,&q,extent,token);
3998 affine.sy=StringToDouble(token,&next_token);
3999 GetNextToken(q,&q,extent,token);
4001 GetNextToken(q,&q,extent,token);
4002 affine.tx=StringToDouble(token,&next_token);
4003 GetNextToken(q,&q,extent,token);
4005 GetNextToken(q,&q,extent,token);
4006 affine.ty=StringToDouble(token,&next_token);
4009 if (LocaleCompare("alpha",keyword) == 0)
4011 primitive_type=AlphaPrimitive;
4014 if (LocaleCompare("angle",keyword) == 0)
4016 GetNextToken(q,&q,extent,token);
4017 affine.rx=StringToDouble(token,&next_token);
4018 affine.ry=StringToDouble(token,&next_token);
4021 if (LocaleCompare("arc",keyword) == 0)
4023 primitive_type=ArcPrimitive;
4032 if (LocaleCompare("bezier",keyword) == 0)
4034 primitive_type=BezierPrimitive;
4043 if (LocaleCompare("clip-path",keyword) == 0)
4045 GetNextToken(q,&q,extent,token);
4046 (void) FormatLocaleString(message,MagickPathExtent,
4047 "clip-path:url(#%s);",token);
4048 (void) WriteBlobString(image,message);
4051 if (LocaleCompare("clip-rule",keyword) == 0)
4053 GetNextToken(q,&q,extent,token);
4054 (void) FormatLocaleString(message,MagickPathExtent,"clip-rule:%s;",
4056 (void) WriteBlobString(image,message);
4059 if (LocaleCompare("clip-units",keyword) == 0)
4061 GetNextToken(q,&q,extent,token);
4062 (void) FormatLocaleString(message,MagickPathExtent,
4063 "clipPathUnits=%s;",token);
4064 (void) WriteBlobString(image,message);
4067 if (LocaleCompare("circle",keyword) == 0)
4069 primitive_type=CirclePrimitive;
4072 if (LocaleCompare("color",keyword) == 0)
4074 primitive_type=ColorPrimitive;
4083 if (LocaleCompare("decorate",keyword) == 0)
4085 GetNextToken(q,&q,extent,token);
4086 (void) FormatLocaleString(message,MagickPathExtent,
4087 "text-decoration:%s;",token);
4088 (void) WriteBlobString(image,message);
4097 if (LocaleCompare("ellipse",keyword) == 0)
4099 primitive_type=EllipsePrimitive;
4108 if (LocaleCompare("fill",keyword) == 0)
4110 GetNextToken(q,&q,extent,token);
4111 (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;",
4113 (void) WriteBlobString(image,message);
4116 if (LocaleCompare("fill-rule",keyword) == 0)
4118 GetNextToken(q,&q,extent,token);
4119 (void) FormatLocaleString(message,MagickPathExtent,
4120 "fill-rule:%s;",token);
4121 (void) WriteBlobString(image,message);
4124 if (LocaleCompare("fill-opacity",keyword) == 0)
4126 GetNextToken(q,&q,extent,token);
4127 (void) FormatLocaleString(message,MagickPathExtent,
4128 "fill-opacity:%s;",token);
4129 (void) WriteBlobString(image,message);
4132 if (LocaleCompare("font-family",keyword) == 0)
4134 GetNextToken(q,&q,extent,token);
4135 (void) FormatLocaleString(message,MagickPathExtent,
4136 "font-family:%s;",token);
4137 (void) WriteBlobString(image,message);
4140 if (LocaleCompare("font-stretch",keyword) == 0)
4142 GetNextToken(q,&q,extent,token);
4143 (void) FormatLocaleString(message,MagickPathExtent,
4144 "font-stretch:%s;",token);
4145 (void) WriteBlobString(image,message);
4148 if (LocaleCompare("font-style",keyword) == 0)
4150 GetNextToken(q,&q,extent,token);
4151 (void) FormatLocaleString(message,MagickPathExtent,
4152 "font-style:%s;",token);
4153 (void) WriteBlobString(image,message);
4156 if (LocaleCompare("font-size",keyword) == 0)
4158 GetNextToken(q,&q,extent,token);
4159 (void) FormatLocaleString(message,MagickPathExtent,
4160 "font-size:%s;",token);
4161 (void) WriteBlobString(image,message);
4164 if (LocaleCompare("font-weight",keyword) == 0)
4166 GetNextToken(q,&q,extent,token);
4167 (void) FormatLocaleString(message,MagickPathExtent,
4168 "font-weight:%s;",token);
4169 (void) WriteBlobString(image,message);
4178 if (LocaleCompare("gradient-units",keyword) == 0)
4180 GetNextToken(q,&q,extent,token);
4183 if (LocaleCompare("text-align",keyword) == 0)
4185 GetNextToken(q,&q,extent,token);
4186 (void) FormatLocaleString(message,MagickPathExtent,
4187 "text-align %s ",token);
4188 (void) WriteBlobString(image,message);
4191 if (LocaleCompare("text-anchor",keyword) == 0)
4193 GetNextToken(q,&q,extent,token);
4194 (void) FormatLocaleString(message,MagickPathExtent,
4195 "text-anchor %s ",token);
4196 (void) WriteBlobString(image,message);
4205 if (LocaleCompare("image",keyword) == 0)
4207 GetNextToken(q,&q,extent,token);
4208 primitive_type=ImagePrimitive;
4217 if (LocaleCompare("line",keyword) == 0)
4219 primitive_type=LinePrimitive;
4228 if (LocaleCompare("opacity",keyword) == 0)
4230 GetNextToken(q,&q,extent,token);
4231 (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ",
4233 (void) WriteBlobString(image,message);
4242 if (LocaleCompare("path",keyword) == 0)
4244 primitive_type=PathPrimitive;
4247 if (LocaleCompare("point",keyword) == 0)
4249 primitive_type=PointPrimitive;
4252 if (LocaleCompare("polyline",keyword) == 0)
4254 primitive_type=PolylinePrimitive;
4257 if (LocaleCompare("polygon",keyword) == 0)
4259 primitive_type=PolygonPrimitive;
4262 if (LocaleCompare("pop",keyword) == 0)
4264 GetNextToken(q,&q,extent,token);
4265 if (LocaleCompare("clip-path",token) == 0)
4267 (void) WriteBlobString(image,"</clipPath>\n");
4270 if (LocaleCompare("defs",token) == 0)
4272 (void) WriteBlobString(image,"</defs>\n");
4275 if (LocaleCompare("gradient",token) == 0)
4277 (void) FormatLocaleString(message,MagickPathExtent,
4278 "</%sGradient>\n",type);
4279 (void) WriteBlobString(image,message);
4282 if (LocaleCompare("graphic-context",token) == 0)
4286 ThrowWriterException(DrawError,
4287 "UnbalancedGraphicContextPushPop");
4288 (void) WriteBlobString(image,"</g>\n");
4290 if (LocaleCompare("pattern",token) == 0)
4292 (void) WriteBlobString(image,"</pattern>\n");
4295 if (LocaleCompare("symbol",token) == 0)
4297 (void) WriteBlobString(image,"</symbol>\n");
4300 if ((LocaleCompare("defs",token) == 0) ||
4301 (LocaleCompare("symbol",token) == 0))
4302 (void) WriteBlobString(image,"</g>\n");
4305 if (LocaleCompare("push",keyword) == 0)
4307 GetNextToken(q,&q,extent,token);
4308 if (LocaleCompare("clip-path",token) == 0)
4310 GetNextToken(q,&q,extent,token);
4311 (void) FormatLocaleString(message,MagickPathExtent,
4312 "<clipPath id=\"%s\">\n",token);
4313 (void) WriteBlobString(image,message);
4316 if (LocaleCompare("defs",token) == 0)
4318 (void) WriteBlobString(image,"<defs>\n");
4321 if (LocaleCompare("gradient",token) == 0)
4323 GetNextToken(q,&q,extent,token);
4324 (void) CopyMagickString(name,token,MagickPathExtent);
4325 GetNextToken(q,&q,extent,token);
4326 (void) CopyMagickString(type,token,MagickPathExtent);
4327 GetNextToken(q,&q,extent,token);
4328 svg_info.segment.x1=StringToDouble(token,&next_token);
4329 svg_info.element.cx=StringToDouble(token,&next_token);
4330 GetNextToken(q,&q,extent,token);
4332 GetNextToken(q,&q,extent,token);
4333 svg_info.segment.y1=StringToDouble(token,&next_token);
4334 svg_info.element.cy=StringToDouble(token,&next_token);
4335 GetNextToken(q,&q,extent,token);
4337 GetNextToken(q,&q,extent,token);
4338 svg_info.segment.x2=StringToDouble(token,&next_token);
4339 svg_info.element.major=StringToDouble(token,
4341 GetNextToken(q,&q,extent,token);
4343 GetNextToken(q,&q,extent,token);
4344 svg_info.segment.y2=StringToDouble(token,&next_token);
4345 svg_info.element.minor=StringToDouble(token,
4347 (void) FormatLocaleString(message,MagickPathExtent,
4348 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
4349 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
4350 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
4351 if (LocaleCompare(type,"radial") == 0)
4353 GetNextToken(q,&q,extent,token);
4355 GetNextToken(q,&q,extent,token);
4356 svg_info.element.angle=StringToDouble(token,
4358 (void) FormatLocaleString(message,MagickPathExtent,
4359 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
4360 "fx=\"%g\" fy=\"%g\">\n",type,name,
4361 svg_info.element.cx,svg_info.element.cy,
4362 svg_info.element.angle,svg_info.element.major,
4363 svg_info.element.minor);
4365 (void) WriteBlobString(image,message);
4368 if (LocaleCompare("graphic-context",token) == 0)
4373 AffineToTransform(image,&affine);
4376 (void) WriteBlobString(image,"<g style=\"");
4379 if (LocaleCompare("pattern",token) == 0)
4381 GetNextToken(q,&q,extent,token);
4382 (void) CopyMagickString(name,token,MagickPathExtent);
4383 GetNextToken(q,&q,extent,token);
4384 svg_info.bounds.x=StringToDouble(token,&next_token);
4385 GetNextToken(q,&q,extent,token);
4387 GetNextToken(q,&q,extent,token);
4388 svg_info.bounds.y=StringToDouble(token,&next_token);
4389 GetNextToken(q,&q,extent,token);
4391 GetNextToken(q,&q,extent,token);
4392 svg_info.bounds.width=StringToDouble(token,
4394 GetNextToken(q,&q,extent,token);
4396 GetNextToken(q,&q,extent,token);
4397 svg_info.bounds.height=StringToDouble(token,(char **) NULL);
4398 (void) FormatLocaleString(message,MagickPathExtent,
4399 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
4400 "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y,
4401 svg_info.bounds.width,svg_info.bounds.height);
4402 (void) WriteBlobString(image,message);
4405 if (LocaleCompare("symbol",token) == 0)
4407 (void) WriteBlobString(image,"<symbol>\n");
4418 if (LocaleCompare("rectangle",keyword) == 0)
4420 primitive_type=RectanglePrimitive;
4423 if (LocaleCompare("roundRectangle",keyword) == 0)
4425 primitive_type=RoundRectanglePrimitive;
4428 if (LocaleCompare("rotate",keyword) == 0)
4430 GetNextToken(q,&q,extent,token);
4431 (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ",
4433 (void) WriteBlobString(image,message);
4442 if (LocaleCompare("scale",keyword) == 0)
4444 GetNextToken(q,&q,extent,token);
4445 affine.sx=StringToDouble(token,&next_token);
4446 GetNextToken(q,&q,extent,token);
4448 GetNextToken(q,&q,extent,token);
4449 affine.sy=StringToDouble(token,&next_token);
4452 if (LocaleCompare("skewX",keyword) == 0)
4454 GetNextToken(q,&q,extent,token);
4455 (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ",
4457 (void) WriteBlobString(image,message);
4460 if (LocaleCompare("skewY",keyword) == 0)
4462 GetNextToken(q,&q,extent,token);
4463 (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ",
4465 (void) WriteBlobString(image,message);
4468 if (LocaleCompare("stop-color",keyword) == 0)
4471 color[MagickPathExtent];
4473 GetNextToken(q,&q,extent,token);
4474 (void) CopyMagickString(color,token,MagickPathExtent);
4475 GetNextToken(q,&q,extent,token);
4476 (void) FormatLocaleString(message,MagickPathExtent,
4477 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4478 (void) WriteBlobString(image,message);
4481 if (LocaleCompare("stroke",keyword) == 0)
4483 GetNextToken(q,&q,extent,token);
4484 (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;",
4486 (void) WriteBlobString(image,message);
4489 if (LocaleCompare("stroke-antialias",keyword) == 0)
4491 GetNextToken(q,&q,extent,token);
4492 (void) FormatLocaleString(message,MagickPathExtent,
4493 "stroke-antialias:%s;",token);
4494 (void) WriteBlobString(image,message);
4497 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4505 GetNextToken(p,&p,extent,token);
4506 for (k=0; IsPoint(token); k++)
4507 GetNextToken(p,&p,extent,token);
4508 (void) WriteBlobString(image,"stroke-dasharray:");
4509 for (j=0; j < k; j++)
4511 GetNextToken(q,&q,extent,token);
4512 (void) FormatLocaleString(message,MagickPathExtent,"%s ",
4514 (void) WriteBlobString(image,message);
4516 (void) WriteBlobString(image,";");
4519 GetNextToken(q,&q,extent,token);
4520 (void) FormatLocaleString(message,MagickPathExtent,
4521 "stroke-dasharray:%s;",token);
4522 (void) WriteBlobString(image,message);
4525 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4527 GetNextToken(q,&q,extent,token);
4528 (void) FormatLocaleString(message,MagickPathExtent,
4529 "stroke-dashoffset:%s;",token);
4530 (void) WriteBlobString(image,message);
4533 if (LocaleCompare("stroke-linecap",keyword) == 0)
4535 GetNextToken(q,&q,extent,token);
4536 (void) FormatLocaleString(message,MagickPathExtent,
4537 "stroke-linecap:%s;",token);
4538 (void) WriteBlobString(image,message);
4541 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4543 GetNextToken(q,&q,extent,token);
4544 (void) FormatLocaleString(message,MagickPathExtent,
4545 "stroke-linejoin:%s;",token);
4546 (void) WriteBlobString(image,message);
4549 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4551 GetNextToken(q,&q,extent,token);
4552 (void) FormatLocaleString(message,MagickPathExtent,
4553 "stroke-miterlimit:%s;",token);
4554 (void) WriteBlobString(image,message);
4557 if (LocaleCompare("stroke-opacity",keyword) == 0)
4559 GetNextToken(q,&q,extent,token);
4560 (void) FormatLocaleString(message,MagickPathExtent,
4561 "stroke-opacity:%s;",token);
4562 (void) WriteBlobString(image,message);
4565 if (LocaleCompare("stroke-width",keyword) == 0)
4567 GetNextToken(q,&q,extent,token);
4568 (void) FormatLocaleString(message,MagickPathExtent,
4569 "stroke-width:%s;",token);
4570 (void) WriteBlobString(image,message);
4579 if (LocaleCompare("text",keyword) == 0)
4581 primitive_type=TextPrimitive;
4584 if (LocaleCompare("text-antialias",keyword) == 0)
4586 GetNextToken(q,&q,extent,token);
4587 (void) FormatLocaleString(message,MagickPathExtent,
4588 "text-antialias:%s;",token);
4589 (void) WriteBlobString(image,message);
4592 if (LocaleCompare("tspan",keyword) == 0)
4594 primitive_type=TextPrimitive;
4597 if (LocaleCompare("translate",keyword) == 0)
4599 GetNextToken(q,&q,extent,token);
4600 affine.tx=StringToDouble(token,&next_token);
4601 GetNextToken(q,&q,extent,token);
4603 GetNextToken(q,&q,extent,token);
4604 affine.ty=StringToDouble(token,&next_token);
4613 if (LocaleCompare("viewbox",keyword) == 0)
4615 GetNextToken(q,&q,extent,token);
4617 GetNextToken(q,&q,extent,token);
4618 GetNextToken(q,&q,extent,token);
4620 GetNextToken(q,&q,extent,token);
4621 GetNextToken(q,&q,extent,token);
4623 GetNextToken(q,&q,extent,token);
4624 GetNextToken(q,&q,extent,token);
4636 if (status == MagickFalse)
4638 if (primitive_type == UndefinedPrimitive)
4641 Parse the primitive attributes.
4645 for (x=0; *q != '\0'; x++)
4650 if (IsPoint(q) == MagickFalse)
4652 GetNextToken(q,&q,extent,token);
4653 point.x=StringToDouble(token,&next_token);
4654 GetNextToken(q,&q,extent,token);
4656 GetNextToken(q,&q,extent,token);
4657 point.y=StringToDouble(token,&next_token);
4658 GetNextToken(q,(const char **) NULL,extent,token);
4660 GetNextToken(q,&q,extent,token);
4661 primitive_info[i].primitive=primitive_type;
4662 primitive_info[i].point=point;
4663 primitive_info[i].coordinates=0;
4664 primitive_info[i].method=FloodfillMethod;
4666 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4668 number_points+=6*BezierQuantum+360;
4669 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4670 number_points,sizeof(*primitive_info));
4671 if (primitive_info == (PrimitiveInfo *) NULL)
4673 (void) ThrowMagickException(exception,GetMagickModule(),
4674 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4678 primitive_info[j].primitive=primitive_type;
4679 primitive_info[j].coordinates=x;
4680 primitive_info[j].method=FloodfillMethod;
4681 primitive_info[j].text=(char *) NULL;
4684 AffineToTransform(image,&affine);
4688 switch (primitive_type)
4690 case PointPrimitive:
4693 if (primitive_info[j].coordinates != 1)
4702 if (primitive_info[j].coordinates != 2)
4707 (void) FormatLocaleString(message,MagickPathExtent,
4708 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4709 primitive_info[j].point.x,primitive_info[j].point.y,
4710 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4711 (void) WriteBlobString(image,message);
4714 case RectanglePrimitive:
4716 if (primitive_info[j].coordinates != 2)
4721 (void) FormatLocaleString(message,MagickPathExtent,
4722 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4723 primitive_info[j].point.x,primitive_info[j].point.y,
4724 primitive_info[j+1].point.x-primitive_info[j].point.x,
4725 primitive_info[j+1].point.y-primitive_info[j].point.y);
4726 (void) WriteBlobString(image,message);
4729 case RoundRectanglePrimitive:
4731 if (primitive_info[j].coordinates != 3)
4736 (void) FormatLocaleString(message,MagickPathExtent,
4737 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4738 "ry=\"%g\"/>\n",primitive_info[j].point.x,
4739 primitive_info[j].point.y,primitive_info[j+1].point.x-
4740 primitive_info[j].point.x,primitive_info[j+1].point.y-
4741 primitive_info[j].point.y,primitive_info[j+2].point.x,
4742 primitive_info[j+2].point.y);
4743 (void) WriteBlobString(image,message);
4748 if (primitive_info[j].coordinates != 3)
4755 case EllipsePrimitive:
4757 if (primitive_info[j].coordinates != 3)
4762 (void) FormatLocaleString(message,MagickPathExtent,
4763 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
4764 primitive_info[j].point.x,primitive_info[j].point.y,
4765 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4766 (void) WriteBlobString(image,message);
4769 case CirclePrimitive:
4775 if (primitive_info[j].coordinates != 2)
4780 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4781 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4782 (void) FormatLocaleString(message,MagickPathExtent,
4783 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
4784 primitive_info[j].point.x,primitive_info[j].point.y,
4786 (void) WriteBlobString(image,message);
4789 case PolylinePrimitive:
4791 if (primitive_info[j].coordinates < 2)
4796 (void) CopyMagickString(message," <polyline points=\"",
4798 (void) WriteBlobString(image,message);
4799 length=strlen(message);
4802 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4803 primitive_info[j].point.x,primitive_info[j].point.y);
4804 length+=strlen(message);
4807 (void) WriteBlobString(image,"\n ");
4808 length=strlen(message)+5;
4810 (void) WriteBlobString(image,message);
4812 (void) WriteBlobString(image,"\"/>\n");
4815 case PolygonPrimitive:
4817 if (primitive_info[j].coordinates < 3)
4822 primitive_info[i]=primitive_info[j];
4823 primitive_info[i].coordinates=0;
4824 primitive_info[j].coordinates++;
4826 (void) CopyMagickString(message," <polygon points=\"",MagickPathExtent);
4827 (void) WriteBlobString(image,message);
4828 length=strlen(message);
4831 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4832 primitive_info[j].point.x,primitive_info[j].point.y);
4833 length+=strlen(message);
4836 (void) WriteBlobString(image,"\n ");
4837 length=strlen(message)+5;
4839 (void) WriteBlobString(image,message);
4841 (void) WriteBlobString(image,"\"/>\n");
4844 case BezierPrimitive:
4846 if (primitive_info[j].coordinates < 3)
4858 GetNextToken(q,&q,extent,token);
4859 number_attributes=1;
4860 for (p=token; *p != '\0'; p++)
4861 if (isalpha((int) *p))
4862 number_attributes++;
4863 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
4865 number_points+=6*BezierQuantum*number_attributes;
4866 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4867 number_points,sizeof(*primitive_info));
4868 if (primitive_info == (PrimitiveInfo *) NULL)
4870 (void) ThrowMagickException(exception,GetMagickModule(),
4871 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4876 (void) WriteBlobString(image," <path d=\"");
4877 (void) WriteBlobString(image,token);
4878 (void) WriteBlobString(image,"\"/>\n");
4881 case AlphaPrimitive:
4882 case ColorPrimitive:
4884 if (primitive_info[j].coordinates != 1)
4889 GetNextToken(q,&q,extent,token);
4890 if (LocaleCompare("point",token) == 0)
4891 primitive_info[j].method=PointMethod;
4892 if (LocaleCompare("replace",token) == 0)
4893 primitive_info[j].method=ReplaceMethod;
4894 if (LocaleCompare("floodfill",token) == 0)
4895 primitive_info[j].method=FloodfillMethod;
4896 if (LocaleCompare("filltoborder",token) == 0)
4897 primitive_info[j].method=FillToBorderMethod;
4898 if (LocaleCompare("reset",token) == 0)
4899 primitive_info[j].method=ResetMethod;
4907 if (primitive_info[j].coordinates != 1)
4912 GetNextToken(q,&q,extent,token);
4913 (void) FormatLocaleString(message,MagickPathExtent,
4914 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
4915 primitive_info[j].point.y);
4916 (void) WriteBlobString(image,message);
4917 for (p=token; *p != '\0'; p++)
4920 case '<': (void) WriteBlobString(image,"<"); break;
4921 case '>': (void) WriteBlobString(image,">"); break;
4922 case '&': (void) WriteBlobString(image,"&"); break;
4923 default: (void) WriteBlobByte(image,*p); break;
4925 (void) WriteBlobString(image,"</text>\n");
4928 case ImagePrimitive:
4930 if (primitive_info[j].coordinates != 2)
4935 GetNextToken(q,&q,extent,token);
4936 (void) FormatLocaleString(message,MagickPathExtent,
4937 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
4938 "href=\"%s\"/>\n",primitive_info[j].point.x,
4939 primitive_info[j].point.y,primitive_info[j+1].point.x,
4940 primitive_info[j+1].point.y,token);
4941 (void) WriteBlobString(image,message);
4945 if (primitive_info == (PrimitiveInfo *) NULL)
4947 primitive_info[i].primitive=UndefinedPrimitive;
4948 if (status == MagickFalse)
4951 (void) WriteBlobString(image,"</svg>\n");
4953 Relinquish resources.
4955 token=DestroyString(token);
4956 if (primitive_info != (PrimitiveInfo *) NULL)
4957 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4958 (void) CloseBlob(image);