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')
1422 text=EscapeString(svg_info->text,'\'');
1423 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
1424 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1425 svg_info->center.y,text);
1426 text=DestroyString(text);
1427 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
1428 draw_info->pointsize=svg_info->pointsize;
1429 draw_info->text=AcquireString(svg_info->text);
1430 (void) ConcatenateString(&draw_info->text," ");
1431 (void) GetTypeMetrics(svg_info->image,draw_info,
1432 &metrics,svg_info->exception);
1433 svg_info->bounds.x+=metrics.width;
1434 draw_info=DestroyDrawInfo(draw_info);
1435 *svg_info->text='\0';
1437 PushGraphicContext(id);
1445 if (LocaleCompare((char *) name,"use") == 0)
1447 PushGraphicContext(id);
1455 if (attributes != (const xmlChar **) NULL)
1456 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1458 keyword=(const char *) attributes[i];
1459 value=(const char *) attributes[i+1];
1460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1461 " %s = %s",keyword,value);
1467 if (LocaleCompare(keyword,"angle") == 0)
1469 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1470 GetUserSpaceCoordinateValue(svg_info,0,value));
1478 if (LocaleCompare(keyword,"class") == 0)
1485 GetNextToken(p,&p,MagickPathExtent,token);
1487 GetNextToken(p,&p,MagickPathExtent,token);
1490 (void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",
1497 if (LocaleCompare(keyword,"clip-path") == 0)
1499 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",
1503 if (LocaleCompare(keyword,"clip-rule") == 0)
1505 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",
1509 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1511 (void) CloneString(&units,value);
1512 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
1516 if (LocaleCompare(keyword,"color") == 0)
1518 (void) CloneString(&color,value);
1521 if (LocaleCompare(keyword,"cx") == 0)
1523 svg_info->element.cx=
1524 GetUserSpaceCoordinateValue(svg_info,1,value);
1527 if (LocaleCompare(keyword,"cy") == 0)
1529 svg_info->element.cy=
1530 GetUserSpaceCoordinateValue(svg_info,-1,value);
1538 if (LocaleCompare(keyword,"d") == 0)
1540 (void) CloneString(&svg_info->vertices,value);
1543 if (LocaleCompare(keyword,"dx") == 0)
1545 svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
1548 if (LocaleCompare(keyword,"dy") == 0)
1550 svg_info->bounds.y+=
1551 GetUserSpaceCoordinateValue(svg_info,-1,value);
1559 if (LocaleCompare(keyword,"fill") == 0)
1561 if (LocaleCompare(value,"currentColor") == 0)
1563 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
1566 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1569 if (LocaleCompare(keyword,"fillcolor") == 0)
1571 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1574 if (LocaleCompare(keyword,"fill-rule") == 0)
1576 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",
1580 if (LocaleCompare(keyword,"fill-opacity") == 0)
1582 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
1586 if (LocaleCompare(keyword,"font-family") == 0)
1588 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
1592 if (LocaleCompare(keyword,"font-stretch") == 0)
1594 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
1598 if (LocaleCompare(keyword,"font-style") == 0)
1600 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
1604 if (LocaleCompare(keyword,"font-size") == 0)
1606 if (LocaleCompare(value,"xx-small") == 0)
1607 svg_info->pointsize=6.144;
1608 else if (LocaleCompare(value,"x-small") == 0)
1609 svg_info->pointsize=7.68;
1610 else if (LocaleCompare(value,"small") == 0)
1611 svg_info->pointsize=9.6;
1612 else if (LocaleCompare(value,"medium") == 0)
1613 svg_info->pointsize=12.0;
1614 else if (LocaleCompare(value,"large") == 0)
1615 svg_info->pointsize=14.4;
1616 else if (LocaleCompare(value,"x-large") == 0)
1617 svg_info->pointsize=17.28;
1618 else if (LocaleCompare(value,"xx-large") == 0)
1619 svg_info->pointsize=20.736;
1621 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,
1623 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1624 svg_info->pointsize);
1627 if (LocaleCompare(keyword,"font-weight") == 0)
1629 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
1638 if (LocaleCompare(keyword,"gradientTransform") == 0)
1645 GetAffineMatrix(&transform);
1646 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1647 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
1648 if (tokens == (char **) NULL)
1650 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
1652 keyword=(char *) tokens[j];
1653 if (keyword == (char *) NULL)
1655 value=(char *) tokens[j+1];
1656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1657 " %s: %s",keyword,value);
1659 GetAffineMatrix(&affine);
1665 if (LocaleCompare(keyword,"matrix") == 0)
1667 p=(const char *) value;
1668 GetNextToken(p,&p,MagickPathExtent,token);
1669 affine.sx=StringToDouble(value,(char **) NULL);
1670 GetNextToken(p,&p,MagickPathExtent,token);
1672 GetNextToken(p,&p,MagickPathExtent,token);
1673 affine.rx=StringToDouble(token,&next_token);
1674 GetNextToken(p,&p,MagickPathExtent,token);
1676 GetNextToken(p,&p,MagickPathExtent,token);
1677 affine.ry=StringToDouble(token,&next_token);
1678 GetNextToken(p,&p,MagickPathExtent,token);
1680 GetNextToken(p,&p,MagickPathExtent,token);
1681 affine.sy=StringToDouble(token,&next_token);
1682 GetNextToken(p,&p,MagickPathExtent,token);
1684 GetNextToken(p,&p,MagickPathExtent,token);
1685 affine.tx=StringToDouble(token,&next_token);
1686 GetNextToken(p,&p,MagickPathExtent,token);
1688 GetNextToken(p,&p,MagickPathExtent,token);
1689 affine.ty=StringToDouble(token,&next_token);
1697 if (LocaleCompare(keyword,"rotate") == 0)
1702 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1703 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1704 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1705 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1706 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1714 if (LocaleCompare(keyword,"scale") == 0)
1716 for (p=(const char *) value; *p != '\0'; p++)
1717 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1720 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1721 affine.sy=affine.sx;
1724 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1725 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1728 if (LocaleCompare(keyword,"skewX") == 0)
1730 affine.sx=svg_info->affine.sx;
1731 affine.ry=tan(DegreesToRadians(fmod(
1732 GetUserSpaceCoordinateValue(svg_info,1,value),
1734 affine.sy=svg_info->affine.sy;
1737 if (LocaleCompare(keyword,"skewY") == 0)
1739 affine.sx=svg_info->affine.sx;
1740 affine.rx=tan(DegreesToRadians(fmod(
1741 GetUserSpaceCoordinateValue(svg_info,-1,value),
1743 affine.sy=svg_info->affine.sy;
1751 if (LocaleCompare(keyword,"translate") == 0)
1753 for (p=(const char *) value; *p != '\0'; p++)
1754 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1757 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1758 affine.ty=affine.tx;
1761 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1769 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1770 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1771 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1772 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1773 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1775 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1778 (void) FormatLocaleFile(svg_info->file,
1779 "affine %g %g %g %g %g %g\n",transform.sx,
1780 transform.rx,transform.ry,transform.sy,transform.tx,
1782 for (j=0; tokens[j] != (char *) NULL; j++)
1783 tokens[j]=DestroyString(tokens[j]);
1784 tokens=(char **) RelinquishMagickMemory(tokens);
1787 if (LocaleCompare(keyword,"gradientUnits") == 0)
1789 (void) CloneString(&units,value);
1790 (void) FormatLocaleFile(svg_info->file,"gradient-units \"%s\"\n",
1799 if (LocaleCompare(keyword,"height") == 0)
1801 svg_info->bounds.height=
1802 GetUserSpaceCoordinateValue(svg_info,-1,value);
1805 if (LocaleCompare(keyword,"href") == 0)
1807 (void) CloneString(&svg_info->url,value);
1815 if (LocaleCompare(keyword,"major") == 0)
1817 svg_info->element.major=
1818 GetUserSpaceCoordinateValue(svg_info,1,value);
1821 if (LocaleCompare(keyword,"mask") == 0)
1823 (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
1826 if (LocaleCompare(keyword,"minor") == 0)
1828 svg_info->element.minor=
1829 GetUserSpaceCoordinateValue(svg_info,-1,value);
1837 if (LocaleCompare(keyword,"offset") == 0)
1839 (void) CloneString(&svg_info->offset,value);
1842 if (LocaleCompare(keyword,"opacity") == 0)
1844 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
1852 if (LocaleCompare(keyword,"path") == 0)
1854 (void) CloneString(&svg_info->url,value);
1857 if (LocaleCompare(keyword,"points") == 0)
1859 (void) CloneString(&svg_info->vertices,value);
1867 if (LocaleCompare(keyword,"r") == 0)
1869 svg_info->element.major=
1870 GetUserSpaceCoordinateValue(svg_info,1,value);
1871 svg_info->element.minor=
1872 GetUserSpaceCoordinateValue(svg_info,-1,value);
1875 if (LocaleCompare(keyword,"rotate") == 0)
1880 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1881 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1882 svg_info->bounds.x,svg_info->bounds.y);
1883 svg_info->bounds.x=0;
1884 svg_info->bounds.y=0;
1885 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
1888 if (LocaleCompare(keyword,"rx") == 0)
1890 if (LocaleCompare((const char *) name,"ellipse") == 0)
1891 svg_info->element.major=
1892 GetUserSpaceCoordinateValue(svg_info,1,value);
1895 GetUserSpaceCoordinateValue(svg_info,1,value);
1898 if (LocaleCompare(keyword,"ry") == 0)
1900 if (LocaleCompare((const char *) name,"ellipse") == 0)
1901 svg_info->element.minor=
1902 GetUserSpaceCoordinateValue(svg_info,-1,value);
1905 GetUserSpaceCoordinateValue(svg_info,-1,value);
1913 if (LocaleCompare(keyword,"stop-color") == 0)
1915 (void) CloneString(&svg_info->stop_color,value);
1918 if (LocaleCompare(keyword,"stroke") == 0)
1920 if (LocaleCompare(value,"currentColor") == 0)
1922 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",
1926 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",value);
1929 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1931 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
1932 LocaleCompare(value,"true") == 0);
1935 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1937 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1941 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1943 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
1944 GetUserSpaceCoordinateValue(svg_info,1,value));
1947 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1949 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
1953 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1955 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
1959 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1961 (void) FormatLocaleFile(svg_info->file,
1962 "stroke-miterlimit \"%s\"\n",value);
1965 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1967 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
1971 if (LocaleCompare(keyword,"stroke-width") == 0)
1973 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
1974 GetUserSpaceCoordinateValue(svg_info,1,value));
1977 if (LocaleCompare(keyword,"style") == 0)
1979 SVGProcessStyleElement(context,name,value);
1987 if (LocaleCompare(keyword,"text-align") == 0)
1989 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",
1993 if (LocaleCompare(keyword,"text-anchor") == 0)
1995 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
1999 if (LocaleCompare(keyword,"text-decoration") == 0)
2001 if (LocaleCompare(value,"underline") == 0)
2002 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
2003 if (LocaleCompare(value,"line-through") == 0)
2004 (void) FormatLocaleFile(svg_info->file,
2005 "decorate line-through\n");
2006 if (LocaleCompare(value,"overline") == 0)
2007 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
2010 if (LocaleCompare(keyword,"text-antialiasing") == 0)
2012 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
2013 LocaleCompare(value,"true") == 0);
2016 if (LocaleCompare(keyword,"transform") == 0)
2023 GetAffineMatrix(&transform);
2024 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
2025 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
2026 if (tokens == (char **) NULL)
2028 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2030 keyword=(char *) tokens[j];
2031 value=(char *) tokens[j+1];
2032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2033 " %s: %s",keyword,value);
2035 GetAffineMatrix(&affine);
2041 if (LocaleCompare(keyword,"matrix") == 0)
2043 p=(const char *) value;
2044 GetNextToken(p,&p,MagickPathExtent,token);
2045 affine.sx=StringToDouble(value,(char **) NULL);
2046 GetNextToken(p,&p,MagickPathExtent,token);
2048 GetNextToken(p,&p,MagickPathExtent,token);
2049 affine.rx=StringToDouble(token,&next_token);
2050 GetNextToken(p,&p,MagickPathExtent,token);
2052 GetNextToken(p,&p,MagickPathExtent,token);
2053 affine.ry=StringToDouble(token,&next_token);
2054 GetNextToken(p,&p,MagickPathExtent,token);
2056 GetNextToken(p,&p,MagickPathExtent,token);
2057 affine.sy=StringToDouble(token,&next_token);
2058 GetNextToken(p,&p,MagickPathExtent,token);
2060 GetNextToken(p,&p,MagickPathExtent,token);
2061 affine.tx=StringToDouble(token,&next_token);
2062 GetNextToken(p,&p,MagickPathExtent,token);
2064 GetNextToken(p,&p,MagickPathExtent,token);
2065 affine.ty=StringToDouble(token,&next_token);
2073 if (LocaleCompare(keyword,"rotate") == 0)
2080 p=(const char *) value;
2081 GetNextToken(p,&p,MagickPathExtent,token);
2082 angle=StringToDouble(value,(char **) NULL);
2083 GetNextToken(p,&p,MagickPathExtent,token);
2085 GetNextToken(p,&p,MagickPathExtent,token);
2086 x=StringToDouble(token,&next_token);
2087 GetNextToken(p,&p,MagickPathExtent,token);
2089 GetNextToken(p,&p,MagickPathExtent,token);
2090 y=StringToDouble(token,&next_token);
2091 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
2092 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
2093 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
2094 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
2097 svg_info->center.x=x;
2098 svg_info->center.y=y;
2106 if (LocaleCompare(keyword,"scale") == 0)
2108 for (p=(const char *) value; *p != '\0'; p++)
2109 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2112 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
2113 affine.sy=affine.sx;
2115 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
2117 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
2120 if (LocaleCompare(keyword,"skewX") == 0)
2122 affine.sx=svg_info->affine.sx;
2123 affine.ry=tan(DegreesToRadians(fmod(
2124 GetUserSpaceCoordinateValue(svg_info,1,value),
2126 affine.sy=svg_info->affine.sy;
2129 if (LocaleCompare(keyword,"skewY") == 0)
2131 affine.sx=svg_info->affine.sx;
2132 affine.rx=tan(DegreesToRadians(fmod(
2133 GetUserSpaceCoordinateValue(svg_info,-1,value),
2135 affine.sy=svg_info->affine.sy;
2143 if (LocaleCompare(keyword,"translate") == 0)
2145 for (p=(const char *) value; *p != '\0'; p++)
2146 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2149 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2150 affine.ty=affine.tx;
2152 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2161 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2162 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2163 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2164 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2165 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2167 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2170 (void) FormatLocaleFile(svg_info->file,
2171 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2172 transform.ry,transform.sy,transform.tx,transform.ty);
2173 for (j=0; tokens[j] != (char *) NULL; j++)
2174 tokens[j]=DestroyString(tokens[j]);
2175 tokens=(char **) RelinquishMagickMemory(tokens);
2183 if (LocaleCompare(keyword,"verts") == 0)
2185 (void) CloneString(&svg_info->vertices,value);
2188 if (LocaleCompare(keyword,"viewBox") == 0)
2190 p=(const char *) value;
2191 GetNextToken(p,&p,MagickPathExtent,token);
2192 svg_info->view_box.x=StringToDouble(token,&next_token);
2193 GetNextToken(p,&p,MagickPathExtent,token);
2195 GetNextToken(p,&p,MagickPathExtent,token);
2196 svg_info->view_box.y=StringToDouble(token,&next_token);
2197 GetNextToken(p,&p,MagickPathExtent,token);
2199 GetNextToken(p,&p,MagickPathExtent,token);
2200 svg_info->view_box.width=StringToDouble(token,
2202 if (svg_info->bounds.width == 0)
2203 svg_info->bounds.width=svg_info->view_box.width;
2204 GetNextToken(p,&p,MagickPathExtent,token);
2206 GetNextToken(p,&p,MagickPathExtent,token);
2207 svg_info->view_box.height=StringToDouble(token,
2209 if (svg_info->bounds.height == 0)
2210 svg_info->bounds.height=svg_info->view_box.height;
2218 if (LocaleCompare(keyword,"width") == 0)
2220 svg_info->bounds.width=
2221 GetUserSpaceCoordinateValue(svg_info,1,value);
2229 if (LocaleCompare(keyword,"x") == 0)
2231 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2234 if (LocaleCompare(keyword,"xlink:href") == 0)
2236 (void) CloneString(&svg_info->url,value);
2239 if (LocaleCompare(keyword,"x1") == 0)
2241 svg_info->segment.x1=
2242 GetUserSpaceCoordinateValue(svg_info,1,value);
2245 if (LocaleCompare(keyword,"x2") == 0)
2247 svg_info->segment.x2=
2248 GetUserSpaceCoordinateValue(svg_info,1,value);
2256 if (LocaleCompare(keyword,"y") == 0)
2258 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2261 if (LocaleCompare(keyword,"y1") == 0)
2263 svg_info->segment.y1=
2264 GetUserSpaceCoordinateValue(svg_info,-1,value);
2267 if (LocaleCompare(keyword,"y2") == 0)
2269 svg_info->segment.y2=
2270 GetUserSpaceCoordinateValue(svg_info,-1,value);
2279 if (LocaleCompare((const char *) name,"svg") == 0)
2281 if (svg_info->document->encoding != (const xmlChar *) NULL)
2282 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2283 (const char *) svg_info->document->encoding);
2284 if (attributes != (const xmlChar **) NULL)
2292 if ((svg_info->view_box.width == 0.0) ||
2293 (svg_info->view_box.height == 0.0))
2294 svg_info->view_box=svg_info->bounds;
2296 if (svg_info->bounds.width > 0.0)
2297 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2299 if (svg_info->bounds.height > 0.0)
2300 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2301 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2302 (double) svg_info->width,(double) svg_info->height);
2303 sx=(double) svg_info->width/svg_info->view_box.width;
2304 sy=(double) svg_info->height/svg_info->view_box.height;
2305 tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2307 ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2309 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2311 if ((svg_info->svgDepth == 1) && (*background != '\0'))
2313 PushGraphicContext(id);
2314 (void) FormatLocaleFile(svg_info->file,"fill %s\n",background);
2315 (void) FormatLocaleFile(svg_info->file,
2316 "rectangle 0,0 %g,%g\n",svg_info->view_box.width,
2317 svg_info->view_box.height);
2318 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2322 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2323 if (units != (char *) NULL)
2324 units=DestroyString(units);
2325 if (color != (char *) NULL)
2326 color=DestroyString(color);
2329 static void SVGEndElement(void *context,const xmlChar *name)
2335 Called when the end of an element has been detected.
2337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2338 " SAX.endElement(%s)",name);
2339 svg_info=(SVGInfo *) context;
2340 if (strchr((char *) name,':') != (char *) NULL)
2343 Skip over namespace.
2345 for ( ; *name != ':'; name++) ;
2353 if (LocaleCompare((const char *) name,"circle") == 0)
2355 (void) FormatLocaleFile(svg_info->file,"class \"circle\"\n");
2356 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2357 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2358 svg_info->element.cy+svg_info->element.minor);
2359 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2362 if (LocaleCompare((const char *) name,"clipPath") == 0)
2364 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2372 if (LocaleCompare((const char *) name,"defs") == 0)
2374 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2377 if (LocaleCompare((const char *) name,"desc") == 0)
2382 if (*svg_info->text == '\0')
2384 (void) fputc('#',svg_info->file);
2385 for (p=svg_info->text; *p != '\0'; p++)
2387 (void) fputc(*p,svg_info->file);
2389 (void) fputc('#',svg_info->file);
2391 (void) fputc('\n',svg_info->file);
2392 *svg_info->text='\0';
2400 if (LocaleCompare((const char *) name,"ellipse") == 0)
2405 (void) FormatLocaleFile(svg_info->file,"class \"ellipse\"\n");
2406 angle=svg_info->element.angle;
2407 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2408 svg_info->element.cx,svg_info->element.cy,
2409 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2410 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2411 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2419 if (LocaleCompare((const char *) name,"foreignObject") == 0)
2421 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2429 if (LocaleCompare((const char *) name,"g") == 0)
2431 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2439 if (LocaleCompare((const char *) name,"image") == 0)
2441 (void) FormatLocaleFile(svg_info->file,
2442 "image Over %g,%g %g,%g \"%s\"\n",svg_info->bounds.x,
2443 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2445 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2453 if (LocaleCompare((const char *) name,"line") == 0)
2455 (void) FormatLocaleFile(svg_info->file,"class \"line\"\n");
2456 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2457 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2458 svg_info->segment.y2);
2459 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2462 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2464 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2472 if (LocaleCompare((const char *) name,"mask") == 0)
2474 (void) FormatLocaleFile(svg_info->file,"pop mask\n");
2482 if (LocaleCompare((const char *) name,"pattern") == 0)
2484 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2487 if (LocaleCompare((const char *) name,"path") == 0)
2489 (void) FormatLocaleFile(svg_info->file,"class \"path\"\n");
2490 (void) FormatLocaleFile(svg_info->file,"path \"%s\"\n",
2491 svg_info->vertices);
2492 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2495 if (LocaleCompare((const char *) name,"polygon") == 0)
2497 (void) FormatLocaleFile(svg_info->file,"class \"polygon\"\n");
2498 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2499 svg_info->vertices);
2500 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2503 if (LocaleCompare((const char *) name,"polyline") == 0)
2505 (void) FormatLocaleFile(svg_info->file,"class \"polyline\"\n");
2506 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2507 svg_info->vertices);
2508 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2516 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2518 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2521 if (LocaleCompare((const char *) name,"rect") == 0)
2523 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2525 (void) FormatLocaleFile(svg_info->file,"class \"rect\"\n");
2526 if ((fabs(svg_info->bounds.width-1.0) < MagickEpsilon) &&
2527 (fabs(svg_info->bounds.height-1.0) < MagickEpsilon))
2528 (void) FormatLocaleFile(svg_info->file,"point %g,%g\n",
2529 svg_info->bounds.x,svg_info->bounds.y);
2531 (void) FormatLocaleFile(svg_info->file,
2532 "rectangle %g,%g %g,%g\n",svg_info->bounds.x,
2533 svg_info->bounds.y,svg_info->bounds.x+svg_info->bounds.width,
2534 svg_info->bounds.y+svg_info->bounds.height);
2535 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2538 if (svg_info->radius.x == 0.0)
2539 svg_info->radius.x=svg_info->radius.y;
2540 if (svg_info->radius.y == 0.0)
2541 svg_info->radius.y=svg_info->radius.x;
2542 (void) FormatLocaleFile(svg_info->file,
2543 "roundRectangle %g,%g %g,%g %g,%g\n",
2544 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2545 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2546 svg_info->radius.x,svg_info->radius.y);
2547 svg_info->radius.x=0.0;
2548 svg_info->radius.y=0.0;
2549 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2557 if (LocaleCompare((const char *) name,"stop") == 0)
2559 (void) FormatLocaleFile(svg_info->file,"stop-color \"%s\" %s\n",
2560 svg_info->stop_color,svg_info->offset);
2563 if (LocaleCompare((char *) name,"style") == 0)
2577 Find style definitions in svg_info->text.
2579 tokens=SVGKeyValuePairs(context,'{','}',svg_info->text,
2581 if (tokens == (char **) NULL)
2583 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2585 keyword=(char *) tokens[j];
2586 value=(char *) tokens[j+1];
2587 (void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
2588 *keyword == '.' ? keyword+1 : keyword);
2589 SVGProcessStyleElement(context,name,value);
2590 (void) FormatLocaleFile(svg_info->file,"pop class\n");
2594 if (LocaleCompare((const char *) name,"svg") == 0)
2596 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2597 svg_info->svgDepth--;
2600 if (LocaleCompare((const char *) name,"symbol") == 0)
2602 (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
2610 if (LocaleCompare((const char *) name,"text") == 0)
2612 if (*svg_info->text != '\0')
2617 (void) FormatLocaleFile(svg_info->file,"class \"text\"\n");
2618 text=EscapeString(svg_info->text,'\'');
2619 (void) FormatLocaleFile(svg_info->file,"text 0,0 \"%s\"\n",text);
2620 text=DestroyString(text);
2621 *svg_info->text='\0';
2623 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2626 if (LocaleCompare((const char *) name,"tspan") == 0)
2628 if (*svg_info->text != '\0')
2639 text=EscapeString(svg_info->text,'\'');
2640 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2641 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
2642 svg_info->center.y,text);
2643 text=DestroyString(text);
2644 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
2645 draw_info->pointsize=svg_info->pointsize;
2646 draw_info->text=AcquireString(svg_info->text);
2647 (void) ConcatenateString(&draw_info->text," ");
2648 (void) GetTypeMetrics(svg_info->image,draw_info,&metrics,
2649 svg_info->exception);
2650 svg_info->bounds.x+=metrics.width;
2651 draw_info=DestroyDrawInfo(draw_info);
2652 *svg_info->text='\0';
2654 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2657 if (LocaleCompare((const char *) name,"title") == 0)
2659 if (*svg_info->text == '\0')
2661 (void) CloneString(&svg_info->title,svg_info->text);
2662 *svg_info->text='\0';
2670 if (LocaleCompare((char *) name,"use") == 0)
2672 if ((svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0))
2673 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2674 svg_info->bounds.x,svg_info->bounds.y);
2675 (void) FormatLocaleFile(svg_info->file,"use \"url(%s)\"\n",
2677 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2685 *svg_info->text='\0';
2686 (void) memset(&svg_info->element,0,sizeof(svg_info->element));
2687 (void) memset(&svg_info->segment,0,sizeof(svg_info->segment));
2691 static void SVGCharacters(void *context,const xmlChar *c,int length)
2706 Receiving some characters from the parser.
2708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2709 " SAX.characters(%s,%.20g)",c,(double) length);
2710 svg_info=(SVGInfo *) context;
2711 text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2712 if (text == (char *) NULL)
2715 for (i=0; i < (ssize_t) length; i++)
2719 if (svg_info->text == (char *) NULL)
2720 svg_info->text=text;
2723 (void) ConcatenateString(&svg_info->text,text);
2724 text=DestroyString(text);
2728 static void SVGReference(void *context,const xmlChar *name)
2737 Called when an entity reference is detected.
2739 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2741 svg_info=(SVGInfo *) context;
2742 parser=svg_info->parser;
2743 if (parser == (xmlParserCtxtPtr) NULL)
2745 if (parser->node == (xmlNodePtr) NULL)
2748 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2750 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2753 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2759 Receiving some ignorable whitespaces from the parser.
2761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2762 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2763 svg_info=(SVGInfo *) context;
2767 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2768 const xmlChar *data)
2774 A processing instruction has been parsed.
2776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2777 " SAX.processingInstruction(%s, %s)",target,data);
2778 svg_info=(SVGInfo *) context;
2782 static void SVGComment(void *context,const xmlChar *value)
2788 A comment has been parsed.
2790 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
2792 svg_info=(SVGInfo *) context;
2793 if (svg_info->comment != (char *) NULL)
2794 (void) ConcatenateString(&svg_info->comment,"\n");
2795 (void) ConcatenateString(&svg_info->comment,(const char *) value);
2798 static void SVGWarning(void *context,const char *format,...)
2802 reason[MagickPathExtent];
2811 Display and format a warning messages, gives file, line, position and
2814 va_start(operands,format);
2815 svg_info=(SVGInfo *) context;
2816 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
2817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2818 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2819 (void) vsprintf(reason,format,operands);
2821 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2823 message=GetExceptionMessage(errno);
2824 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2825 DelegateWarning,reason,"`%s`",message);
2826 message=DestroyString(message);
2830 static void SVGError(void *context,const char *format,...)
2834 reason[MagickPathExtent];
2843 Display and format a error formats, gives file, line, position and
2846 va_start(operands,format);
2847 svg_info=(SVGInfo *) context;
2848 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
2849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2850 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2851 (void) vsprintf(reason,format,operands);
2853 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2855 message=GetExceptionMessage(errno);
2856 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2857 reason,"`%s`",message);
2858 message=DestroyString(message);
2862 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2874 Called when a pcdata block has been parsed.
2876 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
2878 svg_info=(SVGInfo *) context;
2879 parser=svg_info->parser;
2880 child=xmlGetLastChild(parser->node);
2881 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2883 xmlTextConcat(child,value,length);
2886 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2889 static void SVGExternalSubset(void *context,const xmlChar *name,
2890 const xmlChar *external_id,const xmlChar *system_id)
2905 Does this document has an external subset?
2907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2908 " SAX.externalSubset(%s, %s, %s)",name,
2909 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2910 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2911 svg_info=(SVGInfo *) context;
2912 parser=svg_info->parser;
2913 if (((external_id == NULL) && (system_id == NULL)) ||
2914 ((parser->validate == 0) || (parser->wellFormed == 0) ||
2915 (svg_info->document == 0)))
2917 input=SVGResolveEntity(context,external_id,system_id);
2920 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2921 parser_context=(*parser);
2922 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2923 if (parser->inputTab == (xmlParserInputPtr *) NULL)
2925 parser->errNo=XML_ERR_NO_MEMORY;
2926 parser->input=parser_context.input;
2927 parser->inputNr=parser_context.inputNr;
2928 parser->inputMax=parser_context.inputMax;
2929 parser->inputTab=parser_context.inputTab;
2935 xmlPushInput(parser,input);
2936 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2937 if (input->filename == (char *) NULL)
2938 input->filename=(char *) xmlStrdup(system_id);
2941 input->base=parser->input->cur;
2942 input->cur=parser->input->cur;
2944 xmlParseExternalSubset(parser,external_id,system_id);
2945 while (parser->inputNr > 1)
2946 (void) xmlPopInput(parser);
2947 xmlFreeInputStream(parser->input);
2948 xmlFree(parser->inputTab);
2949 parser->input=parser_context.input;
2950 parser->inputNr=parser_context.inputNr;
2951 parser->inputMax=parser_context.inputMax;
2952 parser->inputTab=parser_context.inputTab;
2955 #if defined(__cplusplus) || defined(c_plusplus)
2960 Static declarations.
2963 SVGDensityGeometry[] = "96.0x96.0";
2966 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2969 filename[MagickPathExtent];
2989 message[MagickPathExtent];
3000 assert(image_info != (const ImageInfo *) NULL);
3001 assert(image_info->signature == MagickCoreSignature);
3002 assert(exception != (ExceptionInfo *) NULL);
3003 if (image_info->debug != MagickFalse)
3004 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3005 image_info->filename);
3006 assert(exception->signature == MagickCoreSignature);
3007 image=AcquireImage(image_info,exception);
3008 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3009 if (status == MagickFalse)
3011 image=DestroyImageList(image);
3012 return((Image *) NULL);
3014 if ((fabs(image->resolution.x) < MagickEpsilon) ||
3015 (fabs(image->resolution.y) < MagickEpsilon))
3023 flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3024 image->resolution.x=geometry_info.rho;
3025 image->resolution.y=geometry_info.sigma;
3026 if ((flags & SigmaValue) == 0)
3027 image->resolution.y=image->resolution.x;
3029 if (LocaleCompare(image_info->magick,"MSVG") != 0)
3034 delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception);
3035 if (delegate_info != (const DelegateInfo *) NULL)
3038 background[MagickPathExtent],
3039 command[MagickPathExtent],
3041 input_filename[MagickPathExtent],
3042 opacity[MagickPathExtent],
3043 output_filename[MagickPathExtent],
3044 unique[MagickPathExtent];
3053 Our best hope for compliance with the SVG standard.
3055 status=AcquireUniqueSymbolicLink(image->filename,input_filename);
3056 (void) AcquireUniqueFilename(output_filename);
3057 (void) AcquireUniqueFilename(unique);
3058 density=AcquireString("");
3059 (void) FormatLocaleString(density,MagickPathExtent,"%.20g,%.20g",
3060 image->resolution.x,image->resolution.y);
3061 (void) FormatLocaleString(background,MagickPathExtent,
3062 "rgb(%.20g%%,%.20g%%,%.20g%%)",
3063 100.0*QuantumScale*image->background_color.red,
3064 100.0*QuantumScale*image->background_color.green,
3065 100.0*QuantumScale*image->background_color.blue);
3066 (void) FormatLocaleString(opacity,MagickPathExtent,"%.20g",
3067 QuantumScale*image->background_color.alpha);
3068 (void) FormatLocaleString(command,MagickPathExtent,
3069 GetDelegateCommands(delegate_info),input_filename,output_filename,
3070 density,background,opacity,unique);
3071 density=DestroyString(density);
3072 status=ExternalDelegateCommand(MagickFalse,image_info->verbose,
3073 command,(char *) NULL,exception);
3074 (void) RelinquishUniqueFileResource(unique);
3075 (void) RelinquishUniqueFileResource(input_filename);
3076 if ((status == 0) && (stat(output_filename,&attributes) == 0) &&
3077 (attributes.st_size > 0))
3085 read_info=CloneImageInfo(image_info);
3086 (void) CopyMagickString(read_info->filename,output_filename,
3088 svg_image=ReadImage(read_info,exception);
3089 read_info=DestroyImageInfo(read_info);
3090 (void) RelinquishUniqueFileResource(output_filename);
3091 if (svg_image != (Image *) NULL)
3093 for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; )
3095 (void) CopyMagickString(next->filename,image->filename,
3097 (void) CopyMagickString(next->magick,image->magick,
3099 next=GetNextImageInList(next);
3101 image=DestroyImage(image);
3105 (void) RelinquishUniqueFileResource(output_filename);
3108 #if defined(MAGICKCORE_RSVG_DELEGATE)
3109 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3122 register unsigned char
3135 register const guchar
3157 svg_handle=rsvg_handle_new();
3158 if (svg_handle == (RsvgHandle *) NULL)
3159 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3160 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
3161 if ((fabs(image->resolution.x) > MagickEpsilon) &&
3162 (fabs(image->resolution.y) > MagickEpsilon))
3163 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3164 image->resolution.y);
3165 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3168 error=(GError *) NULL;
3169 (void) rsvg_handle_write(svg_handle,message,n,&error);
3170 if (error != (GError *) NULL)
3171 g_error_free(error);
3173 error=(GError *) NULL;
3174 rsvg_handle_close(svg_handle,&error);
3175 if (error != (GError *) NULL)
3176 g_error_free(error);
3177 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3178 apply_density=MagickTrue;
3179 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
3180 if ((image->resolution.x > 0.0) && (image->resolution.y > 0.0))
3186 We should not apply the density when the internal 'factor' is 'i'.
3187 This can be checked by using the trick below.
3189 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x*256,
3190 image->resolution.y*256);
3191 rsvg_handle_get_dimensions(svg_handle,&dpi_dimension_info);
3192 if ((dpi_dimension_info.width != dimension_info.width) ||
3193 (dpi_dimension_info.height != dimension_info.height))
3194 apply_density=MagickFalse;
3195 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3196 image->resolution.y);
3198 if (image_info->size != (char *) NULL)
3200 (void) GetGeometry(image_info->size,(ssize_t *) NULL,
3201 (ssize_t *) NULL,&image->columns,&image->rows);
3202 if ((image->columns != 0) || (image->rows != 0))
3204 image->resolution.x=96.0*image->columns/dimension_info.width;
3205 image->resolution.y=96.0*image->rows/dimension_info.height;
3206 if (fabs(image->resolution.x) < MagickEpsilon)
3207 image->resolution.x=image->resolution.y;
3209 if (fabs(image->resolution.y) < MagickEpsilon)
3210 image->resolution.y=image->resolution.x;
3212 image->resolution.x=image->resolution.y=MagickMin(
3213 image->resolution.x,image->resolution.y);
3214 apply_density=MagickTrue;
3217 if (apply_density != MagickFalse)
3219 image->columns=image->resolution.x*dimension_info.width/96.0;
3220 image->rows=image->resolution.y*dimension_info.height/96.0;
3224 image->columns=dimension_info.width;
3225 image->rows=dimension_info.height;
3227 pixel_info=(MemoryInfo *) NULL;
3229 pixel_buffer=rsvg_handle_get_pixbuf(svg_handle);
3230 rsvg_handle_free(svg_handle);
3231 image->columns=gdk_pixbuf_get_width(pixel_buffer);
3232 image->rows=gdk_pixbuf_get_height(pixel_buffer);
3234 image->alpha_trait=BlendPixelTrait;
3235 status=SetImageExtent(image,image->columns,image->rows,exception);
3236 if (status == MagickFalse)
3238 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
3239 g_object_unref(G_OBJECT(pixel_buffer));
3241 g_object_unref(svg_handle);
3242 ThrowReaderException(MissingDelegateError,
3243 "NoDecodeDelegateForThisImageFormat");
3245 if (image_info->ping == MagickFalse)
3247 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3251 stride=4*image->columns;
3252 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
3253 stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
3254 (int) image->columns);
3256 pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels));
3257 if (pixel_info == (MemoryInfo *) NULL)
3259 g_object_unref(svg_handle);
3260 ThrowReaderException(ResourceLimitError,
3261 "MemoryAllocationFailed");
3263 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3265 (void) SetImageBackgroundColor(image,exception);
3266 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3267 cairo_surface=cairo_image_surface_create_for_data(pixels,
3268 CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int)
3270 if ((cairo_surface == (cairo_surface_t *) NULL) ||
3271 (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS))
3273 if (cairo_surface != (cairo_surface_t *) NULL)
3274 cairo_surface_destroy(cairo_surface);
3275 pixel_info=RelinquishVirtualMemory(pixel_info);
3276 g_object_unref(svg_handle);
3277 ThrowReaderException(ResourceLimitError,
3278 "MemoryAllocationFailed");
3280 cairo_image=cairo_create(cairo_surface);
3281 cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
3282 cairo_paint(cairo_image);
3283 cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
3284 if (apply_density != MagickFalse)
3285 cairo_scale(cairo_image,image->resolution.x/96.0,
3286 image->resolution.y/96.0);
3287 rsvg_handle_render_cairo(svg_handle,cairo_image);
3288 cairo_destroy(cairo_image);
3289 cairo_surface_destroy(cairo_surface);
3290 g_object_unref(svg_handle);
3293 p=gdk_pixbuf_get_pixels(pixel_buffer);
3295 GetPixelInfo(image,&fill_color);
3296 for (y=0; y < (ssize_t) image->rows; y++)
3298 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3299 if (q == (Quantum *) NULL)
3301 for (x=0; x < (ssize_t) image->columns; x++)
3303 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3304 fill_color.blue=ScaleCharToQuantum(*p++);
3305 fill_color.green=ScaleCharToQuantum(*p++);
3306 fill_color.red=ScaleCharToQuantum(*p++);
3308 fill_color.red=ScaleCharToQuantum(*p++);
3309 fill_color.green=ScaleCharToQuantum(*p++);
3310 fill_color.blue=ScaleCharToQuantum(*p++);
3312 fill_color.alpha=ScaleCharToQuantum(*p++);
3313 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3318 gamma=QuantumScale*fill_color.alpha;
3319 gamma=PerceptibleReciprocal(gamma);
3320 fill_color.blue*=gamma;
3321 fill_color.green*=gamma;
3322 fill_color.red*=gamma;
3325 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
3326 GetPixelAlpha(image,q),q);
3327 q+=GetPixelChannels(image);
3329 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3331 if (image->previous == (Image *) NULL)
3333 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
3335 if (status == MagickFalse)
3340 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3341 if (pixel_info != (MemoryInfo *) NULL)
3342 pixel_info=RelinquishVirtualMemory(pixel_info);
3344 g_object_unref(G_OBJECT(pixel_buffer));
3346 (void) CloseBlob(image);
3347 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3349 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3350 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3351 next=GetNextImageInList(next);
3353 return(GetFirstImageInList(image));
3361 unique_file=AcquireUniqueFileResource(filename);
3362 if (unique_file != -1)
3363 file=fdopen(unique_file,"w");
3364 if ((unique_file == -1) || (file == (FILE *) NULL))
3366 (void) CopyMagickString(image->filename,filename,MagickPathExtent);
3367 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
3369 image=DestroyImageList(image);
3370 return((Image *) NULL);
3375 svg_info=AcquireSVGInfo();
3376 if (svg_info == (SVGInfo *) NULL)
3378 (void) fclose(file);
3379 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3381 svg_info->file=file;
3382 svg_info->exception=exception;
3383 svg_info->image=image;
3384 svg_info->image_info=image_info;
3385 svg_info->bounds.width=image->columns;
3386 svg_info->bounds.height=image->rows;
3387 svg_info->svgDepth=0;
3388 if (image_info->size != (char *) NULL)
3389 (void) CloneString(&svg_info->size,image_info->size);
3390 if (image->debug != MagickFalse)
3391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3392 (void) xmlSubstituteEntitiesDefault(1);
3393 (void) memset(&sax_modules,0,sizeof(sax_modules));
3394 sax_modules.internalSubset=SVGInternalSubset;
3395 sax_modules.isStandalone=SVGIsStandalone;
3396 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3397 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3398 sax_modules.resolveEntity=SVGResolveEntity;
3399 sax_modules.getEntity=SVGGetEntity;
3400 sax_modules.entityDecl=SVGEntityDeclaration;
3401 sax_modules.notationDecl=SVGNotationDeclaration;
3402 sax_modules.attributeDecl=SVGAttributeDeclaration;
3403 sax_modules.elementDecl=SVGElementDeclaration;
3404 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3405 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3406 sax_modules.startDocument=SVGStartDocument;
3407 sax_modules.endDocument=SVGEndDocument;
3408 sax_modules.startElement=SVGStartElement;
3409 sax_modules.endElement=SVGEndElement;
3410 sax_modules.reference=SVGReference;
3411 sax_modules.characters=SVGCharacters;
3412 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3413 sax_modules.processingInstruction=SVGProcessingInstructions;
3414 sax_modules.comment=SVGComment;
3415 sax_modules.warning=SVGWarning;
3416 sax_modules.error=SVGError;
3417 sax_modules.fatalError=SVGError;
3418 sax_modules.getParameterEntity=SVGGetParameterEntity;
3419 sax_modules.cdataBlock=SVGCDataBlock;
3420 sax_modules.externalSubset=SVGExternalSubset;
3421 sax_handler=(&sax_modules);
3422 n=ReadBlob(image,MagickPathExtent-1,message);
3426 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3427 message,n,image->filename);
3428 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3431 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3436 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3437 SVGEndDocument(svg_info);
3438 xmlFreeParserCtxt(svg_info->parser);
3439 if (image->debug != MagickFalse)
3440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3441 (void) fclose(file);
3442 (void) CloseBlob(image);
3443 image->columns=svg_info->width;
3444 image->rows=svg_info->height;
3445 if (exception->severity >= ErrorException)
3447 svg_info=DestroySVGInfo(svg_info);
3448 (void) RelinquishUniqueFileResource(filename);
3449 image=DestroyImage(image);
3450 return((Image *) NULL);
3452 if (image_info->ping == MagickFalse)
3460 image=DestroyImage(image);
3461 image=(Image *) NULL;
3462 read_info=CloneImageInfo(image_info);
3463 SetImageInfoBlob(read_info,(void *) NULL,0);
3464 if (read_info->density != (char *) NULL)
3465 read_info->density=DestroyString(read_info->density);
3466 (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
3468 image=ReadImage(read_info,exception);
3469 read_info=DestroyImageInfo(read_info);
3470 if (image != (Image *) NULL)
3471 (void) CopyMagickString(image->filename,image_info->filename,
3475 Relinquish resources.
3477 if (image != (Image *) NULL)
3479 if (svg_info->title != (char *) NULL)
3480 (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3481 if (svg_info->comment != (char *) NULL)
3482 (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3485 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3487 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3488 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3489 next=GetNextImageInList(next);
3491 svg_info=DestroySVGInfo(svg_info);
3492 (void) RelinquishUniqueFileResource(filename);
3493 return(GetFirstImageInList(image));
3498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3502 % R e g i s t e r S V G I m a g e %
3506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3508 % RegisterSVGImage() adds attributes for the SVG image format to
3509 % the list of supported formats. The attributes include the image format
3510 % tag, a method to read and/or write the format, whether the format
3511 % supports the saving of more than one frame to the same file or blob,
3512 % whether the format supports native in-memory I/O, and a brief
3513 % description of the format.
3515 % The format of the RegisterSVGImage method is:
3517 % size_t RegisterSVGImage(void)
3520 ModuleExport size_t RegisterSVGImage(void)
3523 version[MagickPathExtent];
3529 #if defined(LIBXML_DOTTED_VERSION)
3530 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,
3533 #if defined(MAGICKCORE_RSVG_DELEGATE)
3534 #if !GLIB_CHECK_VERSION(2,35,0)
3537 #if defined(MAGICKCORE_XML_DELEGATE)
3540 (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d",
3541 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3543 entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics");
3544 #if defined(MAGICKCORE_XML_DELEGATE)
3545 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3547 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3548 entry->flags^=CoderBlobSupportFlag;
3549 #if defined(MAGICKCORE_RSVG_DELEGATE)
3550 entry->flags^=CoderDecoderThreadSupportFlag;
3552 entry->mime_type=ConstantString("image/svg+xml");
3553 if (*version != '\0')
3554 entry->version=ConstantString(version);
3555 entry->magick=(IsImageFormatHandler *) IsSVG;
3556 (void) RegisterMagickInfo(entry);
3557 entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics");
3558 #if defined(MAGICKCORE_XML_DELEGATE)
3559 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3561 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3562 entry->flags^=CoderBlobSupportFlag;
3563 #if defined(MAGICKCORE_RSVG_DELEGATE)
3564 entry->flags^=CoderDecoderThreadSupportFlag;
3566 entry->mime_type=ConstantString("image/svg+xml");
3567 if (*version != '\0')
3568 entry->version=ConstantString(version);
3569 entry->magick=(IsImageFormatHandler *) IsSVG;
3570 (void) RegisterMagickInfo(entry);
3571 entry=AcquireMagickInfo("SVG","MSVG",
3572 "ImageMagick's own SVG internal renderer");
3573 #if defined(MAGICKCORE_XML_DELEGATE)
3574 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3576 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3577 entry->flags^=CoderBlobSupportFlag;
3578 #if defined(MAGICKCORE_RSVG_DELEGATE)
3579 entry->flags^=CoderDecoderThreadSupportFlag;
3581 entry->magick=(IsImageFormatHandler *) IsSVG;
3582 (void) RegisterMagickInfo(entry);
3583 return(MagickImageCoderSignature);
3587 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3591 % U n r e g i s t e r S V G I m a g e %
3595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3597 % UnregisterSVGImage() removes format registrations made by the
3598 % SVG module from the list of supported formats.
3600 % The format of the UnregisterSVGImage method is:
3602 % UnregisterSVGImage(void)
3605 ModuleExport void UnregisterSVGImage(void)
3607 (void) UnregisterMagickInfo("SVGZ");
3608 (void) UnregisterMagickInfo("SVG");
3609 (void) UnregisterMagickInfo("MSVG");
3610 #if defined(MAGICKCORE_XML_DELEGATE)
3616 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3620 % W r i t e S V G I m a g e %
3624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3626 % WriteSVGImage() writes a image in the SVG - XML based W3C standard
3629 % The format of the WriteSVGImage method is:
3631 % MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3632 % Image *image,ExceptionInfo *exception)
3634 % A description of each parameter follows.
3636 % o image_info: the image info.
3638 % o image: The image.
3640 % o exception: return any errors or warnings in this structure.
3644 static void AffineToTransform(Image *image,AffineMatrix *affine)
3647 transform[MagickPathExtent];
3649 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3651 if ((fabs(affine->rx) < MagickEpsilon) &&
3652 (fabs(affine->ry) < MagickEpsilon))
3654 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3655 (fabs(affine->sy-1.0) < MagickEpsilon))
3657 (void) WriteBlobString(image,"\">\n");
3660 (void) FormatLocaleString(transform,MagickPathExtent,
3661 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3662 (void) WriteBlobString(image,transform);
3667 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3668 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3669 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3675 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3676 (void) FormatLocaleString(transform,MagickPathExtent,
3677 "\" transform=\"rotate(%g)\">\n",theta);
3678 (void) WriteBlobString(image,transform);
3685 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3686 (fabs(affine->rx) < MagickEpsilon) &&
3687 (fabs(affine->ry) < MagickEpsilon) &&
3688 (fabs(affine->sy-1.0) < MagickEpsilon))
3690 (void) FormatLocaleString(transform,MagickPathExtent,
3691 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3692 (void) WriteBlobString(image,transform);
3696 (void) FormatLocaleString(transform,MagickPathExtent,
3697 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3698 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3699 (void) WriteBlobString(image,transform);
3702 static MagickBooleanType IsPoint(const char *point)
3710 value=strtol(point,&p,10);
3712 return(p != point ? MagickTrue : MagickFalse);
3715 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
3717 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3722 at_fitting_opts_type
3734 register const Quantum
3748 Trace image and write as SVG.
3750 fitting_options=at_fitting_opts_new();
3751 output_options=at_output_opts_new();
3752 (void) SetImageGray(image,exception);
3753 type=GetImageType(image);
3755 if ((type == BilevelType) || (type == GrayscaleType))
3757 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3759 for (y=0; y < (ssize_t) image->rows; y++)
3761 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3762 if (p == (const Quantum *) NULL)
3764 for (x=0; x < (ssize_t) image->columns; x++)
3766 trace->bitmap[i++]=GetPixelRed(image,p);
3767 if (number_planes == 3)
3769 trace->bitmap[i++]=GetPixelGreen(image,p);
3770 trace->bitmap[i++]=GetPixelBlue(image,p);
3772 p+=GetPixelChannels(image);
3775 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3777 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3778 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3783 at_splines_free(splines);
3784 at_bitmap_free(trace);
3785 at_output_opts_free(output_options);
3786 at_fitting_opts_free(fitting_options);
3792 message[MagickPathExtent];
3813 (void) WriteBlobString(image,
3814 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
3815 (void) WriteBlobString(image,
3816 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
3817 (void) WriteBlobString(image,
3818 " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
3819 (void) FormatLocaleString(message,MagickPathExtent,
3820 "<svg version=\"1.1\" id=\"Layer_1\" "
3821 "xmlns=\"http://www.w3.org/2000/svg\" "
3822 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" "
3823 "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" "
3824 "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">",
3825 (double) image->columns,(double) image->rows,
3826 (double) image->columns,(double) image->rows,
3827 (double) image->columns,(double) image->rows);
3828 (void) WriteBlobString(image,message);
3829 clone_image=CloneImage(image,0,0,MagickTrue,exception);
3830 if (clone_image == (Image *) NULL)
3831 return(MagickFalse);
3832 image_info=AcquireImageInfo();
3833 (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent);
3835 blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length,
3837 clone_image=DestroyImage(clone_image);
3838 image_info=DestroyImageInfo(image_info);
3839 if (blob == (unsigned char *) NULL)
3840 return(MagickFalse);
3842 base64=Base64Encode(blob,blob_length,&encode_length);
3843 blob=(unsigned char *) RelinquishMagickMemory(blob);
3844 (void) FormatLocaleString(message,MagickPathExtent,
3845 " <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" "
3846 "x=\"%.20g\" y=\"%.20g\"\n href=\"data:image/png;base64,",
3847 (double) image->scene,(double) image->columns,(double) image->rows,
3848 (double) image->page.x,(double) image->page.y);
3849 (void) WriteBlobString(image,message);
3851 for (i=(ssize_t) encode_length; i > 0; i-=76)
3853 (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p);
3854 (void) WriteBlobString(image,message);
3857 (void) WriteBlobString(image,"\n");
3859 base64=DestroyString(base64);
3860 (void) WriteBlobString(image,"\" />\n");
3861 (void) WriteBlobString(image,"</svg>\n");
3868 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
3869 ExceptionInfo *exception)
3871 #define BezierQuantum 200
3877 keyword[MagickPathExtent],
3878 message[MagickPathExtent],
3879 name[MagickPathExtent],
3882 type[MagickPathExtent];
3923 Open output image file.
3925 assert(image_info != (const ImageInfo *) NULL);
3926 assert(image_info->signature == MagickCoreSignature);
3927 assert(image != (Image *) NULL);
3928 assert(image->signature == MagickCoreSignature);
3929 if (image->debug != MagickFalse)
3930 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3931 assert(exception != (ExceptionInfo *) NULL);
3932 assert(exception->signature == MagickCoreSignature);
3933 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3934 if (status == MagickFalse)
3936 value=GetImageArtifact(image,"SVG");
3937 if (value != (char *) NULL)
3939 (void) WriteBlobString(image,value);
3940 (void) CloseBlob(image);
3943 value=GetImageArtifact(image,"MVG");
3944 if (value == (char *) NULL)
3945 return(TraceSVGImage(image,exception));
3949 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3950 (void) WriteBlobString(image,
3951 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3952 (void) WriteBlobString(image,
3953 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3954 (void) FormatLocaleString(message,MagickPathExtent,
3955 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3957 (void) WriteBlobString(image,message);
3959 Allocate primitive info memory.
3962 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3963 sizeof(*primitive_info));
3964 if (primitive_info == (PrimitiveInfo *) NULL)
3965 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3966 GetAffineMatrix(&affine);
3967 token=AcquireString(value);
3968 extent=strlen(token)+MagickPathExtent;
3972 for (q=(const char *) value; *q != '\0'; )
3975 Interpret graphic primitive.
3977 GetNextToken(q,&q,MagickPathExtent,keyword);
3978 if (*keyword == '\0')
3980 if (*keyword == '#')
3985 if (active != MagickFalse)
3987 AffineToTransform(image,&affine);
3990 (void) WriteBlobString(image,"<desc>");
3991 (void) WriteBlobString(image,keyword+1);
3992 for ( ; (*q != '\n') && (*q != '\0'); q++)
3995 case '<': (void) WriteBlobString(image,"<"); break;
3996 case '>': (void) WriteBlobString(image,">"); break;
3997 case '&': (void) WriteBlobString(image,"&"); break;
3998 default: (void) WriteBlobByte(image,*q); break;
4000 (void) WriteBlobString(image,"</desc>\n");
4003 primitive_type=UndefinedPrimitive;
4011 if (LocaleCompare("affine",keyword) == 0)
4013 GetNextToken(q,&q,extent,token);
4014 affine.sx=StringToDouble(token,&next_token);
4015 GetNextToken(q,&q,extent,token);
4017 GetNextToken(q,&q,extent,token);
4018 affine.rx=StringToDouble(token,&next_token);
4019 GetNextToken(q,&q,extent,token);
4021 GetNextToken(q,&q,extent,token);
4022 affine.ry=StringToDouble(token,&next_token);
4023 GetNextToken(q,&q,extent,token);
4025 GetNextToken(q,&q,extent,token);
4026 affine.sy=StringToDouble(token,&next_token);
4027 GetNextToken(q,&q,extent,token);
4029 GetNextToken(q,&q,extent,token);
4030 affine.tx=StringToDouble(token,&next_token);
4031 GetNextToken(q,&q,extent,token);
4033 GetNextToken(q,&q,extent,token);
4034 affine.ty=StringToDouble(token,&next_token);
4037 if (LocaleCompare("alpha",keyword) == 0)
4039 primitive_type=AlphaPrimitive;
4042 if (LocaleCompare("angle",keyword) == 0)
4044 GetNextToken(q,&q,extent,token);
4045 affine.rx=StringToDouble(token,&next_token);
4046 affine.ry=StringToDouble(token,&next_token);
4049 if (LocaleCompare("arc",keyword) == 0)
4051 primitive_type=ArcPrimitive;
4060 if (LocaleCompare("bezier",keyword) == 0)
4062 primitive_type=BezierPrimitive;
4071 if (LocaleCompare("clip-path",keyword) == 0)
4073 GetNextToken(q,&q,extent,token);
4074 (void) FormatLocaleString(message,MagickPathExtent,
4075 "clip-path:url(#%s);",token);
4076 (void) WriteBlobString(image,message);
4079 if (LocaleCompare("clip-rule",keyword) == 0)
4081 GetNextToken(q,&q,extent,token);
4082 (void) FormatLocaleString(message,MagickPathExtent,"clip-rule:%s;",
4084 (void) WriteBlobString(image,message);
4087 if (LocaleCompare("clip-units",keyword) == 0)
4089 GetNextToken(q,&q,extent,token);
4090 (void) FormatLocaleString(message,MagickPathExtent,
4091 "clipPathUnits=%s;",token);
4092 (void) WriteBlobString(image,message);
4095 if (LocaleCompare("circle",keyword) == 0)
4097 primitive_type=CirclePrimitive;
4100 if (LocaleCompare("color",keyword) == 0)
4102 primitive_type=ColorPrimitive;
4111 if (LocaleCompare("decorate",keyword) == 0)
4113 GetNextToken(q,&q,extent,token);
4114 (void) FormatLocaleString(message,MagickPathExtent,
4115 "text-decoration:%s;",token);
4116 (void) WriteBlobString(image,message);
4125 if (LocaleCompare("ellipse",keyword) == 0)
4127 primitive_type=EllipsePrimitive;
4136 if (LocaleCompare("fill",keyword) == 0)
4138 GetNextToken(q,&q,extent,token);
4139 (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;",
4141 (void) WriteBlobString(image,message);
4144 if (LocaleCompare("fill-rule",keyword) == 0)
4146 GetNextToken(q,&q,extent,token);
4147 (void) FormatLocaleString(message,MagickPathExtent,
4148 "fill-rule:%s;",token);
4149 (void) WriteBlobString(image,message);
4152 if (LocaleCompare("fill-opacity",keyword) == 0)
4154 GetNextToken(q,&q,extent,token);
4155 (void) FormatLocaleString(message,MagickPathExtent,
4156 "fill-opacity:%s;",token);
4157 (void) WriteBlobString(image,message);
4160 if (LocaleCompare("font-family",keyword) == 0)
4162 GetNextToken(q,&q,extent,token);
4163 (void) FormatLocaleString(message,MagickPathExtent,
4164 "font-family:%s;",token);
4165 (void) WriteBlobString(image,message);
4168 if (LocaleCompare("font-stretch",keyword) == 0)
4170 GetNextToken(q,&q,extent,token);
4171 (void) FormatLocaleString(message,MagickPathExtent,
4172 "font-stretch:%s;",token);
4173 (void) WriteBlobString(image,message);
4176 if (LocaleCompare("font-style",keyword) == 0)
4178 GetNextToken(q,&q,extent,token);
4179 (void) FormatLocaleString(message,MagickPathExtent,
4180 "font-style:%s;",token);
4181 (void) WriteBlobString(image,message);
4184 if (LocaleCompare("font-size",keyword) == 0)
4186 GetNextToken(q,&q,extent,token);
4187 (void) FormatLocaleString(message,MagickPathExtent,
4188 "font-size:%s;",token);
4189 (void) WriteBlobString(image,message);
4192 if (LocaleCompare("font-weight",keyword) == 0)
4194 GetNextToken(q,&q,extent,token);
4195 (void) FormatLocaleString(message,MagickPathExtent,
4196 "font-weight:%s;",token);
4197 (void) WriteBlobString(image,message);
4206 if (LocaleCompare("gradient-units",keyword) == 0)
4208 GetNextToken(q,&q,extent,token);
4211 if (LocaleCompare("text-align",keyword) == 0)
4213 GetNextToken(q,&q,extent,token);
4214 (void) FormatLocaleString(message,MagickPathExtent,
4215 "text-align %s ",token);
4216 (void) WriteBlobString(image,message);
4219 if (LocaleCompare("text-anchor",keyword) == 0)
4221 GetNextToken(q,&q,extent,token);
4222 (void) FormatLocaleString(message,MagickPathExtent,
4223 "text-anchor %s ",token);
4224 (void) WriteBlobString(image,message);
4233 if (LocaleCompare("image",keyword) == 0)
4235 GetNextToken(q,&q,extent,token);
4236 primitive_type=ImagePrimitive;
4245 if (LocaleCompare("line",keyword) == 0)
4247 primitive_type=LinePrimitive;
4256 if (LocaleCompare("opacity",keyword) == 0)
4258 GetNextToken(q,&q,extent,token);
4259 (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ",
4261 (void) WriteBlobString(image,message);
4270 if (LocaleCompare("path",keyword) == 0)
4272 primitive_type=PathPrimitive;
4275 if (LocaleCompare("point",keyword) == 0)
4277 primitive_type=PointPrimitive;
4280 if (LocaleCompare("polyline",keyword) == 0)
4282 primitive_type=PolylinePrimitive;
4285 if (LocaleCompare("polygon",keyword) == 0)
4287 primitive_type=PolygonPrimitive;
4290 if (LocaleCompare("pop",keyword) == 0)
4292 GetNextToken(q,&q,extent,token);
4293 if (LocaleCompare("clip-path",token) == 0)
4295 (void) WriteBlobString(image,"</clipPath>\n");
4298 if (LocaleCompare("defs",token) == 0)
4300 (void) WriteBlobString(image,"</defs>\n");
4303 if (LocaleCompare("gradient",token) == 0)
4305 (void) FormatLocaleString(message,MagickPathExtent,
4306 "</%sGradient>\n",type);
4307 (void) WriteBlobString(image,message);
4310 if (LocaleCompare("graphic-context",token) == 0)
4314 ThrowWriterException(DrawError,
4315 "UnbalancedGraphicContextPushPop");
4316 (void) WriteBlobString(image,"</g>\n");
4318 if (LocaleCompare("pattern",token) == 0)
4320 (void) WriteBlobString(image,"</pattern>\n");
4323 if (LocaleCompare("symbol",token) == 0)
4325 (void) WriteBlobString(image,"</symbol>\n");
4328 if ((LocaleCompare("defs",token) == 0) ||
4329 (LocaleCompare("symbol",token) == 0))
4330 (void) WriteBlobString(image,"</g>\n");
4333 if (LocaleCompare("push",keyword) == 0)
4335 GetNextToken(q,&q,extent,token);
4336 if (LocaleCompare("clip-path",token) == 0)
4338 GetNextToken(q,&q,extent,token);
4339 (void) FormatLocaleString(message,MagickPathExtent,
4340 "<clipPath id=\"%s\">\n",token);
4341 (void) WriteBlobString(image,message);
4344 if (LocaleCompare("defs",token) == 0)
4346 (void) WriteBlobString(image,"<defs>\n");
4349 if (LocaleCompare("gradient",token) == 0)
4351 GetNextToken(q,&q,extent,token);
4352 (void) CopyMagickString(name,token,MagickPathExtent);
4353 GetNextToken(q,&q,extent,token);
4354 (void) CopyMagickString(type,token,MagickPathExtent);
4355 GetNextToken(q,&q,extent,token);
4356 svg_info.segment.x1=StringToDouble(token,&next_token);
4357 svg_info.element.cx=StringToDouble(token,&next_token);
4358 GetNextToken(q,&q,extent,token);
4360 GetNextToken(q,&q,extent,token);
4361 svg_info.segment.y1=StringToDouble(token,&next_token);
4362 svg_info.element.cy=StringToDouble(token,&next_token);
4363 GetNextToken(q,&q,extent,token);
4365 GetNextToken(q,&q,extent,token);
4366 svg_info.segment.x2=StringToDouble(token,&next_token);
4367 svg_info.element.major=StringToDouble(token,
4369 GetNextToken(q,&q,extent,token);
4371 GetNextToken(q,&q,extent,token);
4372 svg_info.segment.y2=StringToDouble(token,&next_token);
4373 svg_info.element.minor=StringToDouble(token,
4375 (void) FormatLocaleString(message,MagickPathExtent,
4376 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
4377 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
4378 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
4379 if (LocaleCompare(type,"radial") == 0)
4381 GetNextToken(q,&q,extent,token);
4383 GetNextToken(q,&q,extent,token);
4384 svg_info.element.angle=StringToDouble(token,
4386 (void) FormatLocaleString(message,MagickPathExtent,
4387 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
4388 "fx=\"%g\" fy=\"%g\">\n",type,name,
4389 svg_info.element.cx,svg_info.element.cy,
4390 svg_info.element.angle,svg_info.element.major,
4391 svg_info.element.minor);
4393 (void) WriteBlobString(image,message);
4396 if (LocaleCompare("graphic-context",token) == 0)
4401 AffineToTransform(image,&affine);
4404 (void) WriteBlobString(image,"<g style=\"");
4407 if (LocaleCompare("pattern",token) == 0)
4409 GetNextToken(q,&q,extent,token);
4410 (void) CopyMagickString(name,token,MagickPathExtent);
4411 GetNextToken(q,&q,extent,token);
4412 svg_info.bounds.x=StringToDouble(token,&next_token);
4413 GetNextToken(q,&q,extent,token);
4415 GetNextToken(q,&q,extent,token);
4416 svg_info.bounds.y=StringToDouble(token,&next_token);
4417 GetNextToken(q,&q,extent,token);
4419 GetNextToken(q,&q,extent,token);
4420 svg_info.bounds.width=StringToDouble(token,
4422 GetNextToken(q,&q,extent,token);
4424 GetNextToken(q,&q,extent,token);
4425 svg_info.bounds.height=StringToDouble(token,(char **) NULL);
4426 (void) FormatLocaleString(message,MagickPathExtent,
4427 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
4428 "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y,
4429 svg_info.bounds.width,svg_info.bounds.height);
4430 (void) WriteBlobString(image,message);
4433 if (LocaleCompare("symbol",token) == 0)
4435 (void) WriteBlobString(image,"<symbol>\n");
4446 if (LocaleCompare("rectangle",keyword) == 0)
4448 primitive_type=RectanglePrimitive;
4451 if (LocaleCompare("roundRectangle",keyword) == 0)
4453 primitive_type=RoundRectanglePrimitive;
4456 if (LocaleCompare("rotate",keyword) == 0)
4458 GetNextToken(q,&q,extent,token);
4459 (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ",
4461 (void) WriteBlobString(image,message);
4470 if (LocaleCompare("scale",keyword) == 0)
4472 GetNextToken(q,&q,extent,token);
4473 affine.sx=StringToDouble(token,&next_token);
4474 GetNextToken(q,&q,extent,token);
4476 GetNextToken(q,&q,extent,token);
4477 affine.sy=StringToDouble(token,&next_token);
4480 if (LocaleCompare("skewX",keyword) == 0)
4482 GetNextToken(q,&q,extent,token);
4483 (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ",
4485 (void) WriteBlobString(image,message);
4488 if (LocaleCompare("skewY",keyword) == 0)
4490 GetNextToken(q,&q,extent,token);
4491 (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ",
4493 (void) WriteBlobString(image,message);
4496 if (LocaleCompare("stop-color",keyword) == 0)
4499 color[MagickPathExtent];
4501 GetNextToken(q,&q,extent,token);
4502 (void) CopyMagickString(color,token,MagickPathExtent);
4503 GetNextToken(q,&q,extent,token);
4504 (void) FormatLocaleString(message,MagickPathExtent,
4505 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4506 (void) WriteBlobString(image,message);
4509 if (LocaleCompare("stroke",keyword) == 0)
4511 GetNextToken(q,&q,extent,token);
4512 (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;",
4514 (void) WriteBlobString(image,message);
4517 if (LocaleCompare("stroke-antialias",keyword) == 0)
4519 GetNextToken(q,&q,extent,token);
4520 (void) FormatLocaleString(message,MagickPathExtent,
4521 "stroke-antialias:%s;",token);
4522 (void) WriteBlobString(image,message);
4525 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4533 GetNextToken(p,&p,extent,token);
4534 for (k=0; IsPoint(token); k++)
4535 GetNextToken(p,&p,extent,token);
4536 (void) WriteBlobString(image,"stroke-dasharray:");
4537 for (j=0; j < k; j++)
4539 GetNextToken(q,&q,extent,token);
4540 (void) FormatLocaleString(message,MagickPathExtent,"%s ",
4542 (void) WriteBlobString(image,message);
4544 (void) WriteBlobString(image,";");
4547 GetNextToken(q,&q,extent,token);
4548 (void) FormatLocaleString(message,MagickPathExtent,
4549 "stroke-dasharray:%s;",token);
4550 (void) WriteBlobString(image,message);
4553 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4555 GetNextToken(q,&q,extent,token);
4556 (void) FormatLocaleString(message,MagickPathExtent,
4557 "stroke-dashoffset:%s;",token);
4558 (void) WriteBlobString(image,message);
4561 if (LocaleCompare("stroke-linecap",keyword) == 0)
4563 GetNextToken(q,&q,extent,token);
4564 (void) FormatLocaleString(message,MagickPathExtent,
4565 "stroke-linecap:%s;",token);
4566 (void) WriteBlobString(image,message);
4569 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4571 GetNextToken(q,&q,extent,token);
4572 (void) FormatLocaleString(message,MagickPathExtent,
4573 "stroke-linejoin:%s;",token);
4574 (void) WriteBlobString(image,message);
4577 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4579 GetNextToken(q,&q,extent,token);
4580 (void) FormatLocaleString(message,MagickPathExtent,
4581 "stroke-miterlimit:%s;",token);
4582 (void) WriteBlobString(image,message);
4585 if (LocaleCompare("stroke-opacity",keyword) == 0)
4587 GetNextToken(q,&q,extent,token);
4588 (void) FormatLocaleString(message,MagickPathExtent,
4589 "stroke-opacity:%s;",token);
4590 (void) WriteBlobString(image,message);
4593 if (LocaleCompare("stroke-width",keyword) == 0)
4595 GetNextToken(q,&q,extent,token);
4596 (void) FormatLocaleString(message,MagickPathExtent,
4597 "stroke-width:%s;",token);
4598 (void) WriteBlobString(image,message);
4607 if (LocaleCompare("text",keyword) == 0)
4609 primitive_type=TextPrimitive;
4612 if (LocaleCompare("text-antialias",keyword) == 0)
4614 GetNextToken(q,&q,extent,token);
4615 (void) FormatLocaleString(message,MagickPathExtent,
4616 "text-antialias:%s;",token);
4617 (void) WriteBlobString(image,message);
4620 if (LocaleCompare("tspan",keyword) == 0)
4622 primitive_type=TextPrimitive;
4625 if (LocaleCompare("translate",keyword) == 0)
4627 GetNextToken(q,&q,extent,token);
4628 affine.tx=StringToDouble(token,&next_token);
4629 GetNextToken(q,&q,extent,token);
4631 GetNextToken(q,&q,extent,token);
4632 affine.ty=StringToDouble(token,&next_token);
4641 if (LocaleCompare("viewbox",keyword) == 0)
4643 GetNextToken(q,&q,extent,token);
4645 GetNextToken(q,&q,extent,token);
4646 GetNextToken(q,&q,extent,token);
4648 GetNextToken(q,&q,extent,token);
4649 GetNextToken(q,&q,extent,token);
4651 GetNextToken(q,&q,extent,token);
4652 GetNextToken(q,&q,extent,token);
4664 if (status == MagickFalse)
4666 if (primitive_type == UndefinedPrimitive)
4669 Parse the primitive attributes.
4673 for (x=0; *q != '\0'; x++)
4678 if (IsPoint(q) == MagickFalse)
4680 GetNextToken(q,&q,extent,token);
4681 point.x=StringToDouble(token,&next_token);
4682 GetNextToken(q,&q,extent,token);
4684 GetNextToken(q,&q,extent,token);
4685 point.y=StringToDouble(token,&next_token);
4686 GetNextToken(q,(const char **) NULL,extent,token);
4688 GetNextToken(q,&q,extent,token);
4689 primitive_info[i].primitive=primitive_type;
4690 primitive_info[i].point=point;
4691 primitive_info[i].coordinates=0;
4692 primitive_info[i].method=FloodfillMethod;
4694 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4696 number_points+=6*BezierQuantum+360;
4697 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4698 number_points,sizeof(*primitive_info));
4699 if (primitive_info == (PrimitiveInfo *) NULL)
4701 (void) ThrowMagickException(exception,GetMagickModule(),
4702 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4706 primitive_info[j].primitive=primitive_type;
4707 primitive_info[j].coordinates=x;
4708 primitive_info[j].method=FloodfillMethod;
4709 primitive_info[j].text=(char *) NULL;
4712 AffineToTransform(image,&affine);
4716 switch (primitive_type)
4718 case PointPrimitive:
4721 if (primitive_info[j].coordinates != 1)
4730 if (primitive_info[j].coordinates != 2)
4735 (void) FormatLocaleString(message,MagickPathExtent,
4736 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4737 primitive_info[j].point.x,primitive_info[j].point.y,
4738 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4739 (void) WriteBlobString(image,message);
4742 case RectanglePrimitive:
4744 if (primitive_info[j].coordinates != 2)
4749 (void) FormatLocaleString(message,MagickPathExtent,
4750 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4751 primitive_info[j].point.x,primitive_info[j].point.y,
4752 primitive_info[j+1].point.x-primitive_info[j].point.x,
4753 primitive_info[j+1].point.y-primitive_info[j].point.y);
4754 (void) WriteBlobString(image,message);
4757 case RoundRectanglePrimitive:
4759 if (primitive_info[j].coordinates != 3)
4764 (void) FormatLocaleString(message,MagickPathExtent,
4765 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4766 "ry=\"%g\"/>\n",primitive_info[j].point.x,
4767 primitive_info[j].point.y,primitive_info[j+1].point.x-
4768 primitive_info[j].point.x,primitive_info[j+1].point.y-
4769 primitive_info[j].point.y,primitive_info[j+2].point.x,
4770 primitive_info[j+2].point.y);
4771 (void) WriteBlobString(image,message);
4776 if (primitive_info[j].coordinates != 3)
4783 case EllipsePrimitive:
4785 if (primitive_info[j].coordinates != 3)
4790 (void) FormatLocaleString(message,MagickPathExtent,
4791 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
4792 primitive_info[j].point.x,primitive_info[j].point.y,
4793 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4794 (void) WriteBlobString(image,message);
4797 case CirclePrimitive:
4803 if (primitive_info[j].coordinates != 2)
4808 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4809 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4810 (void) FormatLocaleString(message,MagickPathExtent,
4811 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
4812 primitive_info[j].point.x,primitive_info[j].point.y,
4814 (void) WriteBlobString(image,message);
4817 case PolylinePrimitive:
4819 if (primitive_info[j].coordinates < 2)
4824 (void) CopyMagickString(message," <polyline points=\"",
4826 (void) WriteBlobString(image,message);
4827 length=strlen(message);
4830 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4831 primitive_info[j].point.x,primitive_info[j].point.y);
4832 length+=strlen(message);
4835 (void) WriteBlobString(image,"\n ");
4836 length=strlen(message)+5;
4838 (void) WriteBlobString(image,message);
4840 (void) WriteBlobString(image,"\"/>\n");
4843 case PolygonPrimitive:
4845 if (primitive_info[j].coordinates < 3)
4850 primitive_info[i]=primitive_info[j];
4851 primitive_info[i].coordinates=0;
4852 primitive_info[j].coordinates++;
4854 (void) CopyMagickString(message," <polygon points=\"",MagickPathExtent);
4855 (void) WriteBlobString(image,message);
4856 length=strlen(message);
4859 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4860 primitive_info[j].point.x,primitive_info[j].point.y);
4861 length+=strlen(message);
4864 (void) WriteBlobString(image,"\n ");
4865 length=strlen(message)+5;
4867 (void) WriteBlobString(image,message);
4869 (void) WriteBlobString(image,"\"/>\n");
4872 case BezierPrimitive:
4874 if (primitive_info[j].coordinates < 3)
4886 GetNextToken(q,&q,extent,token);
4887 number_attributes=1;
4888 for (p=token; *p != '\0'; p++)
4889 if (isalpha((int) *p))
4890 number_attributes++;
4891 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
4893 number_points+=6*BezierQuantum*number_attributes;
4894 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4895 number_points,sizeof(*primitive_info));
4896 if (primitive_info == (PrimitiveInfo *) NULL)
4898 (void) ThrowMagickException(exception,GetMagickModule(),
4899 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4904 (void) WriteBlobString(image," <path d=\"");
4905 (void) WriteBlobString(image,token);
4906 (void) WriteBlobString(image,"\"/>\n");
4909 case AlphaPrimitive:
4910 case ColorPrimitive:
4912 if (primitive_info[j].coordinates != 1)
4917 GetNextToken(q,&q,extent,token);
4918 if (LocaleCompare("point",token) == 0)
4919 primitive_info[j].method=PointMethod;
4920 if (LocaleCompare("replace",token) == 0)
4921 primitive_info[j].method=ReplaceMethod;
4922 if (LocaleCompare("floodfill",token) == 0)
4923 primitive_info[j].method=FloodfillMethod;
4924 if (LocaleCompare("filltoborder",token) == 0)
4925 primitive_info[j].method=FillToBorderMethod;
4926 if (LocaleCompare("reset",token) == 0)
4927 primitive_info[j].method=ResetMethod;
4935 if (primitive_info[j].coordinates != 1)
4940 GetNextToken(q,&q,extent,token);
4941 (void) FormatLocaleString(message,MagickPathExtent,
4942 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
4943 primitive_info[j].point.y);
4944 (void) WriteBlobString(image,message);
4945 for (p=token; *p != '\0'; p++)
4948 case '<': (void) WriteBlobString(image,"<"); break;
4949 case '>': (void) WriteBlobString(image,">"); break;
4950 case '&': (void) WriteBlobString(image,"&"); break;
4951 default: (void) WriteBlobByte(image,*p); break;
4953 (void) WriteBlobString(image,"</text>\n");
4956 case ImagePrimitive:
4958 if (primitive_info[j].coordinates != 2)
4963 GetNextToken(q,&q,extent,token);
4964 (void) FormatLocaleString(message,MagickPathExtent,
4965 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
4966 "href=\"%s\"/>\n",primitive_info[j].point.x,
4967 primitive_info[j].point.y,primitive_info[j+1].point.x,
4968 primitive_info[j+1].point.y,token);
4969 (void) WriteBlobString(image,message);
4973 if (primitive_info == (PrimitiveInfo *) NULL)
4975 primitive_info[i].primitive=UndefinedPrimitive;
4976 if (status == MagickFalse)
4979 (void) WriteBlobString(image,"</svg>\n");
4981 Relinquish resources.
4983 token=DestroyString(token);
4984 if (primitive_info != (PrimitiveInfo *) NULL)
4985 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4986 (void) CloseBlob(image);