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 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
683 keyword=(char *) tokens[i];
684 value=(char *) tokens[i+1];
685 (void) LogMagickEvent(CoderEvent,GetMagickModule()," %s: %s",keyword,
692 if (LocaleCompare((const char *) name,"background") == 0)
694 if (LocaleCompare((const char *) name,"svg") == 0)
695 (void) CopyMagickString(background,value,MagickPathExtent);
703 if (LocaleCompare(keyword,"clip-path") == 0)
705 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",value);
708 if (LocaleCompare(keyword,"clip-rule") == 0)
710 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",value);
713 if (LocaleCompare(keyword,"clipPathUnits") == 0)
715 (void) CloneString(&units,value);
716 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
720 if (LocaleCompare(keyword,"color") == 0)
722 (void) CloneString(&color,value);
730 if (LocaleCompare(keyword,"fill") == 0)
732 if (LocaleCompare(value,"currentColor") == 0)
734 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
737 if (LocaleCompare(value,"#000000ff") == 0)
738 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
740 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
743 if (LocaleCompare(keyword,"fillcolor") == 0)
745 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
748 if (LocaleCompare(keyword,"fill-rule") == 0)
750 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",value);
753 if (LocaleCompare(keyword,"fill-opacity") == 0)
755 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
759 if (LocaleCompare(keyword,"font-family") == 0)
761 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
765 if (LocaleCompare(keyword,"font-stretch") == 0)
767 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
771 if (LocaleCompare(keyword,"font-style") == 0)
773 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",value);
776 if (LocaleCompare(keyword,"font-size") == 0)
778 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
779 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
780 svg_info->pointsize);
783 if (LocaleCompare(keyword,"font-weight") == 0)
785 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
794 if (LocaleCompare(keyword,"offset") == 0)
796 (void) FormatLocaleFile(svg_info->file,"offset %g\n",
797 GetUserSpaceCoordinateValue(svg_info,1,value));
800 if (LocaleCompare(keyword,"opacity") == 0)
802 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
810 if (LocaleCompare(keyword,"stop-color") == 0)
812 (void) CloneString(&svg_info->stop_color,value);
815 if (LocaleCompare(keyword,"stroke") == 0)
817 if (LocaleCompare(value,"currentColor") == 0)
819 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",color);
822 if (LocaleCompare(value,"#000000ff") == 0)
823 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
825 (void) FormatLocaleFile(svg_info->file,
826 "stroke \"%s\"\n",value);
829 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
831 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
832 LocaleCompare(value,"true") == 0);
835 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
837 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
841 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
843 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
844 GetUserSpaceCoordinateValue(svg_info,1,value));
847 if (LocaleCompare(keyword,"stroke-linecap") == 0)
849 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
853 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
855 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
859 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
861 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit \"%s\"\n",
865 if (LocaleCompare(keyword,"stroke-opacity") == 0)
867 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
871 if (LocaleCompare(keyword,"stroke-width") == 0)
873 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
874 GetUserSpaceCoordinateValue(svg_info,1,value));
882 if (LocaleCompare(keyword,"text-align") == 0)
884 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",value);
887 if (LocaleCompare(keyword,"text-anchor") == 0)
889 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
893 if (LocaleCompare(keyword,"text-decoration") == 0)
895 if (LocaleCompare(value,"underline") == 0)
896 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
897 if (LocaleCompare(value,"line-through") == 0)
898 (void) FormatLocaleFile(svg_info->file,"decorate line-through\n");
899 if (LocaleCompare(value,"overline") == 0)
900 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
903 if (LocaleCompare(keyword,"text-antialiasing") == 0)
905 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
906 LocaleCompare(value,"true") == 0);
915 for (i=0; tokens[i] != (char *) NULL; i++)
916 tokens[i]=DestroyString(tokens[i]);
917 tokens=(char **) RelinquishMagickMemory(tokens);
920 static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
921 const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
927 What to do when an unparsed entity declaration is parsed.
929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
930 " SAX.unparsedEntityDecl(%s, %s, %s, %s)",name,
931 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
932 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation);
933 svg_info=(SVGInfo *) context;
934 (void) xmlAddDocEntity(svg_info->document,name,
935 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
939 static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
945 Receive the document locator at startup, actually xmlDefaultSAXLocator.
948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
949 " SAX.setDocumentLocator()");
950 svg_info=(SVGInfo *) context;
954 static void SVGStartDocument(void *context)
963 Called when the document start being processed.
965 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startDocument()");
966 svg_info=(SVGInfo *) context;
967 parser=svg_info->parser;
968 svg_info->document=xmlNewDoc(parser->version);
969 if (svg_info->document == (xmlDocPtr) NULL)
971 if (parser->encoding == NULL)
972 svg_info->document->encoding=(const xmlChar *) NULL;
974 svg_info->document->encoding=xmlStrdup(parser->encoding);
975 svg_info->document->standalone=parser->standalone;
978 static void SVGEndDocument(void *context)
984 Called when the document end has been detected.
986 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()");
987 svg_info=(SVGInfo *) context;
988 if (svg_info->offset != (char *) NULL)
989 svg_info->offset=DestroyString(svg_info->offset);
990 if (svg_info->stop_color != (char *) NULL)
991 svg_info->stop_color=DestroyString(svg_info->stop_color);
992 if (svg_info->scale != (double *) NULL)
993 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
994 if (svg_info->text != (char *) NULL)
995 svg_info->text=DestroyString(svg_info->text);
996 if (svg_info->vertices != (char *) NULL)
997 svg_info->vertices=DestroyString(svg_info->vertices);
998 if (svg_info->url != (char *) NULL)
999 svg_info->url=DestroyString(svg_info->url);
1000 #if defined(MAGICKCORE_XML_DELEGATE)
1001 if (svg_info->document != (xmlDocPtr) NULL)
1003 xmlFreeDoc(svg_info->document);
1004 svg_info->document=(xmlDocPtr) NULL;
1009 static void SVGStartElement(void *context,const xmlChar *name,
1010 const xmlChar **attributes)
1012 #define PushGraphicContext(id) \
1015 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); \
1017 (void) FormatLocaleFile(svg_info->file,"push graphic-context \"%s\"\n", \
1023 background[MagickPathExtent],
1024 id[MagickPathExtent],
1026 token[MagickPathExtent],
1046 Called when an opening tag has been processed.
1048 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s",
1050 svg_info=(SVGInfo *) context;
1052 svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale,
1053 svg_info->n+1UL,sizeof(*svg_info->scale));
1054 if (svg_info->scale == (double *) NULL)
1056 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
1057 ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
1060 svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
1061 color=AcquireString("none");
1062 units=AcquireString("userSpaceOnUse");
1066 value=(const char *) NULL;
1067 if ((LocaleCompare((char *) name,"image") == 0) ||
1068 (LocaleCompare((char *) name,"pattern") == 0) ||
1069 (LocaleCompare((char *) name,"rect") == 0) ||
1070 (LocaleCompare((char *) name,"text") == 0) ||
1071 (LocaleCompare((char *) name,"use") == 0))
1073 svg_info->bounds.x=0.0;
1074 svg_info->bounds.y=0.0;
1076 if (attributes != (const xmlChar **) NULL)
1077 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1079 keyword=(const char *) attributes[i];
1080 value=(const char *) attributes[i+1];
1086 if (LocaleCompare(keyword,"cx") == 0)
1088 svg_info->element.cx=
1089 GetUserSpaceCoordinateValue(svg_info,1,value);
1092 if (LocaleCompare(keyword,"cy") == 0)
1094 svg_info->element.cy=
1095 GetUserSpaceCoordinateValue(svg_info,-1,value);
1103 if (LocaleCompare(keyword,"fx") == 0)
1105 svg_info->element.major=
1106 GetUserSpaceCoordinateValue(svg_info,1,value);
1109 if (LocaleCompare(keyword,"fy") == 0)
1111 svg_info->element.minor=
1112 GetUserSpaceCoordinateValue(svg_info,-1,value);
1120 if (LocaleCompare(keyword,"height") == 0)
1122 svg_info->bounds.height=
1123 GetUserSpaceCoordinateValue(svg_info,-1,value);
1131 if (LocaleCompare(keyword,"id") == 0)
1133 (void) CopyMagickString(id,value,MagickPathExtent);
1141 if (LocaleCompare(keyword,"r") == 0)
1143 svg_info->element.angle=
1144 GetUserSpaceCoordinateValue(svg_info,0,value);
1152 if (LocaleCompare(keyword,"width") == 0)
1154 svg_info->bounds.width=
1155 GetUserSpaceCoordinateValue(svg_info,1,value);
1163 if (LocaleCompare(keyword,"x") == 0)
1165 if (LocaleCompare((char *) name,"tspan") != 0)
1166 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,
1167 value)-svg_info->center.x;
1170 if (LocaleCompare(keyword,"x1") == 0)
1172 svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
1176 if (LocaleCompare(keyword,"x2") == 0)
1178 svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
1187 if (LocaleCompare(keyword,"y") == 0)
1189 if (LocaleCompare((char *) name,"tspan") != 0)
1190 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,
1191 value)-svg_info->center.y;
1194 if (LocaleCompare(keyword,"y1") == 0)
1196 svg_info->segment.y1=GetUserSpaceCoordinateValue(svg_info,-1,
1200 if (LocaleCompare(keyword,"y2") == 0)
1202 svg_info->segment.y2=GetUserSpaceCoordinateValue(svg_info,-1,
1212 if (strchr((char *) name,':') != (char *) NULL)
1215 Skip over namespace.
1217 for ( ; *name != ':'; name++) ;
1225 if (LocaleCompare((const char *) name,"circle") == 0)
1227 PushGraphicContext(id);
1230 if (LocaleCompare((const char *) name,"clipPath") == 0)
1232 (void) FormatLocaleFile(svg_info->file,"push clip-path \"%s\"\n",id);
1240 if (LocaleCompare((const char *) name,"defs") == 0)
1242 (void) FormatLocaleFile(svg_info->file,"push defs\n");
1250 if (LocaleCompare((const char *) name,"ellipse") == 0)
1252 PushGraphicContext(id);
1260 if (LocaleCompare((const char *) name,"foreignObject") == 0)
1262 PushGraphicContext(id);
1270 if (LocaleCompare((const char *) name,"g") == 0)
1272 PushGraphicContext(id);
1280 if (LocaleCompare((const char *) name,"image") == 0)
1282 PushGraphicContext(id);
1290 if (LocaleCompare((const char *) name,"line") == 0)
1292 PushGraphicContext(id);
1295 if (LocaleCompare((const char *) name,"linearGradient") == 0)
1297 (void) FormatLocaleFile(svg_info->file,
1298 "push gradient \"%s\" linear %g,%g %g,%g\n",id,
1299 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
1300 svg_info->segment.y2);
1308 if (LocaleCompare((const char *) name,"mask") == 0)
1310 (void) FormatLocaleFile(svg_info->file,"push mask \"%s\"\n",id);
1318 if (LocaleCompare((const char *) name,"path") == 0)
1320 PushGraphicContext(id);
1323 if (LocaleCompare((const char *) name,"pattern") == 0)
1325 (void) FormatLocaleFile(svg_info->file,
1326 "push pattern \"%s\" %g,%g %g,%g\n",id,
1327 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
1328 svg_info->bounds.height);
1331 if (LocaleCompare((const char *) name,"polygon") == 0)
1333 PushGraphicContext(id);
1336 if (LocaleCompare((const char *) name,"polyline") == 0)
1338 PushGraphicContext(id);
1346 if (LocaleCompare((const char *) name,"radialGradient") == 0)
1348 (void) FormatLocaleFile(svg_info->file,
1349 "push gradient \"%s\" radial %g,%g %g,%g %g\n",
1350 id,svg_info->element.cx,svg_info->element.cy,
1351 svg_info->element.major,svg_info->element.minor,
1352 svg_info->element.angle);
1355 if (LocaleCompare((const char *) name,"rect") == 0)
1357 PushGraphicContext(id);
1365 if (LocaleCompare((char *) name,"style") == 0)
1367 if (LocaleCompare((const char *) name,"svg") == 0)
1369 svg_info->svgDepth++;
1370 PushGraphicContext(id);
1371 (void) FormatLocaleFile(svg_info->file,"compliance \"SVG\"\n");
1372 (void) FormatLocaleFile(svg_info->file,"fill \"black\"\n");
1373 (void) FormatLocaleFile(svg_info->file,"fill-opacity 1\n");
1374 (void) FormatLocaleFile(svg_info->file,"stroke \"none\"\n");
1375 (void) FormatLocaleFile(svg_info->file,"stroke-width 1\n");
1376 (void) FormatLocaleFile(svg_info->file,"stroke-opacity 1\n");
1377 (void) FormatLocaleFile(svg_info->file,"fill-rule nonzero\n");
1380 if (LocaleCompare((const char *) name,"symbol") == 0)
1382 (void) FormatLocaleFile(svg_info->file,"push symbol\n");
1390 if (LocaleCompare((const char *) name,"text") == 0)
1392 PushGraphicContext(id);
1393 svg_info->bounds.x=0.0;
1394 svg_info->bounds.y=0.0;
1395 svg_info->bounds.width=0.0;
1396 svg_info->bounds.height=0.0;
1399 if (LocaleCompare((const char *) name,"tspan") == 0)
1401 if (*svg_info->text != '\0')
1412 text=EscapeString(svg_info->text,'\'');
1413 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
1414 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1415 svg_info->center.y,text);
1416 text=DestroyString(text);
1417 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
1418 draw_info->pointsize=svg_info->pointsize;
1419 draw_info->text=AcquireString(svg_info->text);
1420 (void) ConcatenateString(&draw_info->text," ");
1421 (void) GetTypeMetrics(svg_info->image,draw_info,
1422 &metrics,svg_info->exception);
1423 svg_info->bounds.x+=metrics.width;
1424 draw_info=DestroyDrawInfo(draw_info);
1425 *svg_info->text='\0';
1427 PushGraphicContext(id);
1435 if (LocaleCompare((char *) name,"use") == 0)
1437 PushGraphicContext(id);
1445 if (attributes != (const xmlChar **) NULL)
1446 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1448 keyword=(const char *) attributes[i];
1449 value=(const char *) attributes[i+1];
1450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1451 " %s = %s",keyword,value);
1457 if (LocaleCompare(keyword,"angle") == 0)
1459 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1460 GetUserSpaceCoordinateValue(svg_info,0,value));
1468 if (LocaleCompare(keyword,"class") == 0)
1475 GetNextToken(p,&p,MagickPathExtent,token);
1477 GetNextToken(p,&p,MagickPathExtent,token);
1480 (void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",
1487 if (LocaleCompare(keyword,"clip-path") == 0)
1489 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",
1493 if (LocaleCompare(keyword,"clip-rule") == 0)
1495 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",
1499 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1501 (void) CloneString(&units,value);
1502 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
1506 if (LocaleCompare(keyword,"color") == 0)
1508 (void) CloneString(&color,value);
1511 if (LocaleCompare(keyword,"cx") == 0)
1513 svg_info->element.cx=
1514 GetUserSpaceCoordinateValue(svg_info,1,value);
1517 if (LocaleCompare(keyword,"cy") == 0)
1519 svg_info->element.cy=
1520 GetUserSpaceCoordinateValue(svg_info,-1,value);
1528 if (LocaleCompare(keyword,"d") == 0)
1530 (void) CloneString(&svg_info->vertices,value);
1533 if (LocaleCompare(keyword,"dx") == 0)
1535 svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
1538 if (LocaleCompare(keyword,"dy") == 0)
1540 svg_info->bounds.y+=
1541 GetUserSpaceCoordinateValue(svg_info,-1,value);
1549 if (LocaleCompare(keyword,"fill") == 0)
1551 if (LocaleCompare(value,"currentColor") == 0)
1553 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
1556 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1559 if (LocaleCompare(keyword,"fillcolor") == 0)
1561 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1564 if (LocaleCompare(keyword,"fill-rule") == 0)
1566 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",
1570 if (LocaleCompare(keyword,"fill-opacity") == 0)
1572 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
1576 if (LocaleCompare(keyword,"font-family") == 0)
1578 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
1582 if (LocaleCompare(keyword,"font-stretch") == 0)
1584 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
1588 if (LocaleCompare(keyword,"font-style") == 0)
1590 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
1594 if (LocaleCompare(keyword,"font-size") == 0)
1596 if (LocaleCompare(value,"xx-small") == 0)
1597 svg_info->pointsize=6.144;
1598 else if (LocaleCompare(value,"x-small") == 0)
1599 svg_info->pointsize=7.68;
1600 else if (LocaleCompare(value,"small") == 0)
1601 svg_info->pointsize=9.6;
1602 else if (LocaleCompare(value,"medium") == 0)
1603 svg_info->pointsize=12.0;
1604 else if (LocaleCompare(value,"large") == 0)
1605 svg_info->pointsize=14.4;
1606 else if (LocaleCompare(value,"x-large") == 0)
1607 svg_info->pointsize=17.28;
1608 else if (LocaleCompare(value,"xx-large") == 0)
1609 svg_info->pointsize=20.736;
1611 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,
1613 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1614 svg_info->pointsize);
1617 if (LocaleCompare(keyword,"font-weight") == 0)
1619 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
1628 if (LocaleCompare(keyword,"gradientTransform") == 0)
1635 GetAffineMatrix(&transform);
1636 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1637 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
1638 if (tokens == (char **) NULL)
1640 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
1642 keyword=(char *) tokens[j];
1643 if (keyword == (char *) NULL)
1645 value=(char *) tokens[j+1];
1646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1647 " %s: %s",keyword,value);
1649 GetAffineMatrix(&affine);
1655 if (LocaleCompare(keyword,"matrix") == 0)
1657 p=(const char *) value;
1658 GetNextToken(p,&p,MagickPathExtent,token);
1659 affine.sx=StringToDouble(value,(char **) NULL);
1660 GetNextToken(p,&p,MagickPathExtent,token);
1662 GetNextToken(p,&p,MagickPathExtent,token);
1663 affine.rx=StringToDouble(token,&next_token);
1664 GetNextToken(p,&p,MagickPathExtent,token);
1666 GetNextToken(p,&p,MagickPathExtent,token);
1667 affine.ry=StringToDouble(token,&next_token);
1668 GetNextToken(p,&p,MagickPathExtent,token);
1670 GetNextToken(p,&p,MagickPathExtent,token);
1671 affine.sy=StringToDouble(token,&next_token);
1672 GetNextToken(p,&p,MagickPathExtent,token);
1674 GetNextToken(p,&p,MagickPathExtent,token);
1675 affine.tx=StringToDouble(token,&next_token);
1676 GetNextToken(p,&p,MagickPathExtent,token);
1678 GetNextToken(p,&p,MagickPathExtent,token);
1679 affine.ty=StringToDouble(token,&next_token);
1687 if (LocaleCompare(keyword,"rotate") == 0)
1692 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1693 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1694 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1695 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1696 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1704 if (LocaleCompare(keyword,"scale") == 0)
1706 for (p=(const char *) value; *p != '\0'; p++)
1707 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1710 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1711 affine.sy=affine.sx;
1714 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1715 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1718 if (LocaleCompare(keyword,"skewX") == 0)
1720 affine.sx=svg_info->affine.sx;
1721 affine.ry=tan(DegreesToRadians(fmod(
1722 GetUserSpaceCoordinateValue(svg_info,1,value),
1724 affine.sy=svg_info->affine.sy;
1727 if (LocaleCompare(keyword,"skewY") == 0)
1729 affine.sx=svg_info->affine.sx;
1730 affine.rx=tan(DegreesToRadians(fmod(
1731 GetUserSpaceCoordinateValue(svg_info,-1,value),
1733 affine.sy=svg_info->affine.sy;
1741 if (LocaleCompare(keyword,"translate") == 0)
1743 for (p=(const char *) value; *p != '\0'; p++)
1744 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1747 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1748 affine.ty=affine.tx;
1751 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1759 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1760 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1761 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1762 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1763 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1765 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1768 (void) FormatLocaleFile(svg_info->file,
1769 "affine %g %g %g %g %g %g\n",transform.sx,
1770 transform.rx,transform.ry,transform.sy,transform.tx,
1772 for (j=0; tokens[j] != (char *) NULL; j++)
1773 tokens[j]=DestroyString(tokens[j]);
1774 tokens=(char **) RelinquishMagickMemory(tokens);
1777 if (LocaleCompare(keyword,"gradientUnits") == 0)
1779 (void) CloneString(&units,value);
1780 (void) FormatLocaleFile(svg_info->file,"gradient-units \"%s\"\n",
1789 if (LocaleCompare(keyword,"height") == 0)
1791 svg_info->bounds.height=
1792 GetUserSpaceCoordinateValue(svg_info,-1,value);
1795 if (LocaleCompare(keyword,"href") == 0)
1797 (void) CloneString(&svg_info->url,value);
1805 if (LocaleCompare(keyword,"major") == 0)
1807 svg_info->element.major=
1808 GetUserSpaceCoordinateValue(svg_info,1,value);
1811 if (LocaleCompare(keyword,"mask") == 0)
1813 (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
1816 if (LocaleCompare(keyword,"minor") == 0)
1818 svg_info->element.minor=
1819 GetUserSpaceCoordinateValue(svg_info,-1,value);
1827 if (LocaleCompare(keyword,"offset") == 0)
1829 (void) CloneString(&svg_info->offset,value);
1832 if (LocaleCompare(keyword,"opacity") == 0)
1834 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
1842 if (LocaleCompare(keyword,"path") == 0)
1844 (void) CloneString(&svg_info->url,value);
1847 if (LocaleCompare(keyword,"points") == 0)
1849 (void) CloneString(&svg_info->vertices,value);
1857 if (LocaleCompare(keyword,"r") == 0)
1859 svg_info->element.major=
1860 GetUserSpaceCoordinateValue(svg_info,1,value);
1861 svg_info->element.minor=
1862 GetUserSpaceCoordinateValue(svg_info,-1,value);
1865 if (LocaleCompare(keyword,"rotate") == 0)
1870 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1871 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1872 svg_info->bounds.x,svg_info->bounds.y);
1873 svg_info->bounds.x=0;
1874 svg_info->bounds.y=0;
1875 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
1878 if (LocaleCompare(keyword,"rx") == 0)
1880 if (LocaleCompare((const char *) name,"ellipse") == 0)
1881 svg_info->element.major=
1882 GetUserSpaceCoordinateValue(svg_info,1,value);
1885 GetUserSpaceCoordinateValue(svg_info,1,value);
1888 if (LocaleCompare(keyword,"ry") == 0)
1890 if (LocaleCompare((const char *) name,"ellipse") == 0)
1891 svg_info->element.minor=
1892 GetUserSpaceCoordinateValue(svg_info,-1,value);
1895 GetUserSpaceCoordinateValue(svg_info,-1,value);
1903 if (LocaleCompare(keyword,"stop-color") == 0)
1905 (void) CloneString(&svg_info->stop_color,value);
1908 if (LocaleCompare(keyword,"stroke") == 0)
1910 if (LocaleCompare(value,"currentColor") == 0)
1912 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",
1916 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",value);
1919 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1921 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
1922 LocaleCompare(value,"true") == 0);
1925 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1927 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1931 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1933 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
1934 GetUserSpaceCoordinateValue(svg_info,1,value));
1937 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1939 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
1943 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1945 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
1949 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1951 (void) FormatLocaleFile(svg_info->file,
1952 "stroke-miterlimit \"%s\"\n",value);
1955 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1957 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
1961 if (LocaleCompare(keyword,"stroke-width") == 0)
1963 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
1964 GetUserSpaceCoordinateValue(svg_info,1,value));
1967 if (LocaleCompare(keyword,"style") == 0)
1969 SVGProcessStyleElement(context,name,value);
1977 if (LocaleCompare(keyword,"text-align") == 0)
1979 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",
1983 if (LocaleCompare(keyword,"text-anchor") == 0)
1985 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
1989 if (LocaleCompare(keyword,"text-decoration") == 0)
1991 if (LocaleCompare(value,"underline") == 0)
1992 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
1993 if (LocaleCompare(value,"line-through") == 0)
1994 (void) FormatLocaleFile(svg_info->file,
1995 "decorate line-through\n");
1996 if (LocaleCompare(value,"overline") == 0)
1997 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
2000 if (LocaleCompare(keyword,"text-antialiasing") == 0)
2002 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
2003 LocaleCompare(value,"true") == 0);
2006 if (LocaleCompare(keyword,"transform") == 0)
2013 GetAffineMatrix(&transform);
2014 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
2015 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
2016 if (tokens == (char **) NULL)
2018 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2020 keyword=(char *) tokens[j];
2021 value=(char *) tokens[j+1];
2022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2023 " %s: %s",keyword,value);
2025 GetAffineMatrix(&affine);
2031 if (LocaleCompare(keyword,"matrix") == 0)
2033 p=(const char *) value;
2034 GetNextToken(p,&p,MagickPathExtent,token);
2035 affine.sx=StringToDouble(value,(char **) NULL);
2036 GetNextToken(p,&p,MagickPathExtent,token);
2038 GetNextToken(p,&p,MagickPathExtent,token);
2039 affine.rx=StringToDouble(token,&next_token);
2040 GetNextToken(p,&p,MagickPathExtent,token);
2042 GetNextToken(p,&p,MagickPathExtent,token);
2043 affine.ry=StringToDouble(token,&next_token);
2044 GetNextToken(p,&p,MagickPathExtent,token);
2046 GetNextToken(p,&p,MagickPathExtent,token);
2047 affine.sy=StringToDouble(token,&next_token);
2048 GetNextToken(p,&p,MagickPathExtent,token);
2050 GetNextToken(p,&p,MagickPathExtent,token);
2051 affine.tx=StringToDouble(token,&next_token);
2052 GetNextToken(p,&p,MagickPathExtent,token);
2054 GetNextToken(p,&p,MagickPathExtent,token);
2055 affine.ty=StringToDouble(token,&next_token);
2063 if (LocaleCompare(keyword,"rotate") == 0)
2070 p=(const char *) value;
2071 GetNextToken(p,&p,MagickPathExtent,token);
2072 angle=StringToDouble(value,(char **) NULL);
2073 GetNextToken(p,&p,MagickPathExtent,token);
2075 GetNextToken(p,&p,MagickPathExtent,token);
2076 x=StringToDouble(token,&next_token);
2077 GetNextToken(p,&p,MagickPathExtent,token);
2079 GetNextToken(p,&p,MagickPathExtent,token);
2080 y=StringToDouble(token,&next_token);
2081 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
2082 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
2083 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
2084 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
2087 svg_info->center.x=x;
2088 svg_info->center.y=y;
2096 if (LocaleCompare(keyword,"scale") == 0)
2098 for (p=(const char *) value; *p != '\0'; p++)
2099 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2102 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
2103 affine.sy=affine.sx;
2105 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
2107 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
2110 if (LocaleCompare(keyword,"skewX") == 0)
2112 affine.sx=svg_info->affine.sx;
2113 affine.ry=tan(DegreesToRadians(fmod(
2114 GetUserSpaceCoordinateValue(svg_info,1,value),
2116 affine.sy=svg_info->affine.sy;
2119 if (LocaleCompare(keyword,"skewY") == 0)
2121 affine.sx=svg_info->affine.sx;
2122 affine.rx=tan(DegreesToRadians(fmod(
2123 GetUserSpaceCoordinateValue(svg_info,-1,value),
2125 affine.sy=svg_info->affine.sy;
2133 if (LocaleCompare(keyword,"translate") == 0)
2135 for (p=(const char *) value; *p != '\0'; p++)
2136 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2139 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2140 affine.ty=affine.tx;
2142 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2151 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2152 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2153 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2154 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2155 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2157 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2160 (void) FormatLocaleFile(svg_info->file,
2161 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2162 transform.ry,transform.sy,transform.tx,transform.ty);
2163 for (j=0; tokens[j] != (char *) NULL; j++)
2164 tokens[j]=DestroyString(tokens[j]);
2165 tokens=(char **) RelinquishMagickMemory(tokens);
2173 if (LocaleCompare(keyword,"verts") == 0)
2175 (void) CloneString(&svg_info->vertices,value);
2178 if (LocaleCompare(keyword,"viewBox") == 0)
2180 p=(const char *) value;
2181 GetNextToken(p,&p,MagickPathExtent,token);
2182 svg_info->view_box.x=StringToDouble(token,&next_token);
2183 GetNextToken(p,&p,MagickPathExtent,token);
2185 GetNextToken(p,&p,MagickPathExtent,token);
2186 svg_info->view_box.y=StringToDouble(token,&next_token);
2187 GetNextToken(p,&p,MagickPathExtent,token);
2189 GetNextToken(p,&p,MagickPathExtent,token);
2190 svg_info->view_box.width=StringToDouble(token,
2192 if (svg_info->bounds.width == 0)
2193 svg_info->bounds.width=svg_info->view_box.width;
2194 GetNextToken(p,&p,MagickPathExtent,token);
2196 GetNextToken(p,&p,MagickPathExtent,token);
2197 svg_info->view_box.height=StringToDouble(token,
2199 if (svg_info->bounds.height == 0)
2200 svg_info->bounds.height=svg_info->view_box.height;
2208 if (LocaleCompare(keyword,"width") == 0)
2210 svg_info->bounds.width=
2211 GetUserSpaceCoordinateValue(svg_info,1,value);
2219 if (LocaleCompare(keyword,"x") == 0)
2221 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2224 if (LocaleCompare(keyword,"xlink:href") == 0)
2226 (void) CloneString(&svg_info->url,value);
2229 if (LocaleCompare(keyword,"x1") == 0)
2231 svg_info->segment.x1=
2232 GetUserSpaceCoordinateValue(svg_info,1,value);
2235 if (LocaleCompare(keyword,"x2") == 0)
2237 svg_info->segment.x2=
2238 GetUserSpaceCoordinateValue(svg_info,1,value);
2246 if (LocaleCompare(keyword,"y") == 0)
2248 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2251 if (LocaleCompare(keyword,"y1") == 0)
2253 svg_info->segment.y1=
2254 GetUserSpaceCoordinateValue(svg_info,-1,value);
2257 if (LocaleCompare(keyword,"y2") == 0)
2259 svg_info->segment.y2=
2260 GetUserSpaceCoordinateValue(svg_info,-1,value);
2269 if (LocaleCompare((const char *) name,"svg") == 0)
2271 if (svg_info->document->encoding != (const xmlChar *) NULL)
2272 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2273 (const char *) svg_info->document->encoding);
2274 if (attributes != (const xmlChar **) NULL)
2282 if ((svg_info->view_box.width == 0.0) ||
2283 (svg_info->view_box.height == 0.0))
2284 svg_info->view_box=svg_info->bounds;
2286 if (svg_info->bounds.width > 0.0)
2287 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2289 if (svg_info->bounds.height > 0.0)
2290 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2291 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2292 (double) svg_info->width,(double) svg_info->height);
2293 sx=(double) svg_info->width/svg_info->view_box.width;
2294 sy=(double) svg_info->height/svg_info->view_box.height;
2295 tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2297 ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2299 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2301 if ((svg_info->svgDepth == 1) && (*background != '\0'))
2303 PushGraphicContext(id);
2304 (void) FormatLocaleFile(svg_info->file,"fill %s\n",background);
2305 (void) FormatLocaleFile(svg_info->file,
2306 "rectangle 0,0 %g,%g\n",svg_info->view_box.width,
2307 svg_info->view_box.height);
2308 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2312 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2313 units=DestroyString(units);
2314 if (color != (char *) NULL)
2315 color=DestroyString(color);
2318 static void SVGEndElement(void *context,const xmlChar *name)
2324 Called when the end of an element has been detected.
2326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2327 " SAX.endElement(%s)",name);
2328 svg_info=(SVGInfo *) context;
2329 if (strchr((char *) name,':') != (char *) NULL)
2332 Skip over namespace.
2334 for ( ; *name != ':'; name++) ;
2342 if (LocaleCompare((const char *) name,"circle") == 0)
2344 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2345 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2346 svg_info->element.cy+svg_info->element.minor);
2347 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2350 if (LocaleCompare((const char *) name,"clipPath") == 0)
2352 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2360 if (LocaleCompare((const char *) name,"defs") == 0)
2362 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2365 if (LocaleCompare((const char *) name,"desc") == 0)
2370 if (*svg_info->text == '\0')
2372 (void) fputc('#',svg_info->file);
2373 for (p=svg_info->text; *p != '\0'; p++)
2375 (void) fputc(*p,svg_info->file);
2377 (void) fputc('#',svg_info->file);
2379 (void) fputc('\n',svg_info->file);
2380 *svg_info->text='\0';
2388 if (LocaleCompare((const char *) name,"ellipse") == 0)
2393 angle=svg_info->element.angle;
2394 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2395 svg_info->element.cx,svg_info->element.cy,
2396 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2397 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2398 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2406 if (LocaleCompare((const char *) name,"foreignObject") == 0)
2408 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2416 if (LocaleCompare((const char *) name,"g") == 0)
2418 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2426 if (LocaleCompare((const char *) name,"image") == 0)
2428 (void) FormatLocaleFile(svg_info->file,
2429 "image Over %g,%g %g,%g \"%s\"\n",svg_info->bounds.x,
2430 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2432 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2440 if (LocaleCompare((const char *) name,"line") == 0)
2442 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2443 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2444 svg_info->segment.y2);
2445 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2448 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2450 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2458 if (LocaleCompare((const char *) name,"mask") == 0)
2460 (void) FormatLocaleFile(svg_info->file,"pop mask\n");
2468 if (LocaleCompare((const char *) name,"pattern") == 0)
2470 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2473 if (LocaleCompare((const char *) name,"path") == 0)
2475 (void) FormatLocaleFile(svg_info->file,"path \"%s\"\n",
2476 svg_info->vertices);
2477 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2480 if (LocaleCompare((const char *) name,"polygon") == 0)
2482 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2483 svg_info->vertices);
2484 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2487 if (LocaleCompare((const char *) name,"polyline") == 0)
2489 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2490 svg_info->vertices);
2491 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2499 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2501 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2504 if (LocaleCompare((const char *) name,"rect") == 0)
2506 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2508 (void) FormatLocaleFile(svg_info->file,"rectangle %g,%g %g,%g\n",
2509 svg_info->bounds.x,svg_info->bounds.y,
2510 svg_info->bounds.x+svg_info->bounds.width,
2511 svg_info->bounds.y+svg_info->bounds.height);
2512 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2515 if (svg_info->radius.x == 0.0)
2516 svg_info->radius.x=svg_info->radius.y;
2517 if (svg_info->radius.y == 0.0)
2518 svg_info->radius.y=svg_info->radius.x;
2519 (void) FormatLocaleFile(svg_info->file,
2520 "roundRectangle %g,%g %g,%g %g,%g\n",
2521 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2522 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2523 svg_info->radius.x,svg_info->radius.y);
2524 svg_info->radius.x=0.0;
2525 svg_info->radius.y=0.0;
2526 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2534 if (LocaleCompare((const char *) name,"stop") == 0)
2536 (void) FormatLocaleFile(svg_info->file,"stop-color \"%s\" %s\n",
2537 svg_info->stop_color,svg_info->offset);
2540 if (LocaleCompare((char *) name,"style") == 0)
2554 Find style definitions in svg_info->text.
2556 tokens=SVGKeyValuePairs(context,'{','}',svg_info->text,
2558 if (tokens == (char **) NULL)
2560 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2562 keyword=(char *) tokens[j];
2563 value=(char *) tokens[j+1];
2564 (void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
2566 SVGProcessStyleElement(context,name,value);
2567 (void) FormatLocaleFile(svg_info->file,"pop class\n");
2571 if (LocaleCompare((const char *) name,"svg") == 0)
2573 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2574 svg_info->svgDepth--;
2577 if (LocaleCompare((const char *) name,"symbol") == 0)
2579 (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
2587 if (LocaleCompare((const char *) name,"text") == 0)
2589 if (*svg_info->text != '\0')
2594 text=EscapeString(svg_info->text,'\'');
2595 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2596 svg_info->bounds.x,svg_info->bounds.y,text);
2597 text=DestroyString(text);
2598 *svg_info->text='\0';
2600 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2603 if (LocaleCompare((const char *) name,"tspan") == 0)
2605 if (*svg_info->text != '\0')
2616 text=EscapeString(svg_info->text,'\'');
2617 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2618 svg_info->bounds.x,svg_info->bounds.y,text);
2619 text=DestroyString(text);
2620 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
2621 draw_info->pointsize=svg_info->pointsize;
2622 draw_info->text=AcquireString(svg_info->text);
2623 (void) ConcatenateString(&draw_info->text," ");
2624 (void) GetTypeMetrics(svg_info->image,draw_info,&metrics,
2625 svg_info->exception);
2626 svg_info->bounds.x+=metrics.width;
2627 draw_info=DestroyDrawInfo(draw_info);
2628 *svg_info->text='\0';
2630 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2633 if (LocaleCompare((const char *) name,"title") == 0)
2635 if (*svg_info->text == '\0')
2637 (void) CloneString(&svg_info->title,svg_info->text);
2638 *svg_info->text='\0';
2646 if (LocaleCompare((char *) name,"use") == 0)
2648 if ((svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0))
2649 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2650 svg_info->bounds.x,svg_info->bounds.y);
2651 (void) FormatLocaleFile(svg_info->file,"use \"url(%s)\"\n",
2653 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2661 *svg_info->text='\0';
2662 (void) memset(&svg_info->element,0,sizeof(svg_info->element));
2663 (void) memset(&svg_info->segment,0,sizeof(svg_info->segment));
2667 static void SVGCharacters(void *context,const xmlChar *c,int length)
2682 Receiving some characters from the parser.
2684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2685 " SAX.characters(%s,%.20g)",c,(double) length);
2686 svg_info=(SVGInfo *) context;
2687 text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2688 if (text == (char *) NULL)
2691 for (i=0; i < (ssize_t) length; i++)
2695 if (svg_info->text == (char *) NULL)
2696 svg_info->text=text;
2699 (void) ConcatenateString(&svg_info->text,text);
2700 text=DestroyString(text);
2704 static void SVGReference(void *context,const xmlChar *name)
2713 Called when an entity reference is detected.
2715 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2717 svg_info=(SVGInfo *) context;
2718 parser=svg_info->parser;
2719 if (parser == (xmlParserCtxtPtr) NULL)
2721 if (parser->node == (xmlNodePtr) NULL)
2724 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2726 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2729 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2735 Receiving some ignorable whitespaces from the parser.
2737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2738 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2739 svg_info=(SVGInfo *) context;
2743 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2744 const xmlChar *data)
2750 A processing instruction has been parsed.
2752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2753 " SAX.processingInstruction(%s, %s)",target,data);
2754 svg_info=(SVGInfo *) context;
2758 static void SVGComment(void *context,const xmlChar *value)
2764 A comment has been parsed.
2766 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
2768 svg_info=(SVGInfo *) context;
2769 if (svg_info->comment != (char *) NULL)
2770 (void) ConcatenateString(&svg_info->comment,"\n");
2771 (void) ConcatenateString(&svg_info->comment,(const char *) value);
2774 static void SVGWarning(void *context,const char *format,...)
2778 reason[MagickPathExtent];
2787 Display and format a warning messages, gives file, line, position and
2790 va_start(operands,format);
2791 svg_info=(SVGInfo *) context;
2792 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
2793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2794 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2795 (void) vsprintf(reason,format,operands);
2797 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2799 message=GetExceptionMessage(errno);
2800 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2801 DelegateWarning,reason,"`%s`",message);
2802 message=DestroyString(message);
2806 static void SVGError(void *context,const char *format,...)
2810 reason[MagickPathExtent];
2819 Display and format a error formats, gives file, line, position and
2822 va_start(operands,format);
2823 svg_info=(SVGInfo *) context;
2824 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
2825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2826 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2827 (void) vsprintf(reason,format,operands);
2829 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2831 message=GetExceptionMessage(errno);
2832 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2833 reason,"`%s`",message);
2834 message=DestroyString(message);
2838 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2850 Called when a pcdata block has been parsed.
2852 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
2854 svg_info=(SVGInfo *) context;
2855 parser=svg_info->parser;
2856 child=xmlGetLastChild(parser->node);
2857 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2859 xmlTextConcat(child,value,length);
2862 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2865 static void SVGExternalSubset(void *context,const xmlChar *name,
2866 const xmlChar *external_id,const xmlChar *system_id)
2881 Does this document has an external subset?
2883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2884 " SAX.externalSubset(%s, %s, %s)",name,
2885 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2886 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2887 svg_info=(SVGInfo *) context;
2888 parser=svg_info->parser;
2889 if (((external_id == NULL) && (system_id == NULL)) ||
2890 ((parser->validate == 0) || (parser->wellFormed == 0) ||
2891 (svg_info->document == 0)))
2893 input=SVGResolveEntity(context,external_id,system_id);
2896 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2897 parser_context=(*parser);
2898 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2899 if (parser->inputTab == (xmlParserInputPtr *) NULL)
2901 parser->errNo=XML_ERR_NO_MEMORY;
2902 parser->input=parser_context.input;
2903 parser->inputNr=parser_context.inputNr;
2904 parser->inputMax=parser_context.inputMax;
2905 parser->inputTab=parser_context.inputTab;
2911 xmlPushInput(parser,input);
2912 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2913 if (input->filename == (char *) NULL)
2914 input->filename=(char *) xmlStrdup(system_id);
2917 input->base=parser->input->cur;
2918 input->cur=parser->input->cur;
2920 xmlParseExternalSubset(parser,external_id,system_id);
2921 while (parser->inputNr > 1)
2922 (void) xmlPopInput(parser);
2923 xmlFreeInputStream(parser->input);
2924 xmlFree(parser->inputTab);
2925 parser->input=parser_context.input;
2926 parser->inputNr=parser_context.inputNr;
2927 parser->inputMax=parser_context.inputMax;
2928 parser->inputTab=parser_context.inputTab;
2931 #if defined(__cplusplus) || defined(c_plusplus)
2936 Static declarations.
2939 SVGDensityGeometry[] = "96.0x96.0";
2942 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2945 filename[MagickPathExtent];
2965 message[MagickPathExtent];
2976 assert(image_info != (const ImageInfo *) NULL);
2977 assert(image_info->signature == MagickCoreSignature);
2978 assert(exception != (ExceptionInfo *) NULL);
2979 if (image_info->debug != MagickFalse)
2980 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2981 image_info->filename);
2982 assert(exception->signature == MagickCoreSignature);
2983 image=AcquireImage(image_info,exception);
2984 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2985 if (status == MagickFalse)
2987 image=DestroyImageList(image);
2988 return((Image *) NULL);
2990 if ((fabs(image->resolution.x) < MagickEpsilon) ||
2991 (fabs(image->resolution.y) < MagickEpsilon))
2999 flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3000 image->resolution.x=geometry_info.rho;
3001 image->resolution.y=geometry_info.sigma;
3002 if ((flags & SigmaValue) == 0)
3003 image->resolution.y=image->resolution.x;
3005 if (LocaleCompare(image_info->magick,"MSVG") != 0)
3010 delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception);
3011 if (delegate_info != (const DelegateInfo *) NULL)
3014 background[MagickPathExtent],
3015 command[MagickPathExtent],
3017 input_filename[MagickPathExtent],
3018 opacity[MagickPathExtent],
3019 output_filename[MagickPathExtent],
3020 unique[MagickPathExtent];
3029 Our best hope for compliance with the SVG standard.
3031 status=AcquireUniqueSymbolicLink(image->filename,input_filename);
3032 (void) AcquireUniqueFilename(output_filename);
3033 (void) AcquireUniqueFilename(unique);
3034 density=AcquireString("");
3035 (void) FormatLocaleString(density,MagickPathExtent,"%.20g,%.20g",
3036 image->resolution.x,image->resolution.y);
3037 (void) FormatLocaleString(background,MagickPathExtent,
3038 "rgb(%.20g%%,%.20g%%,%.20g%%)",
3039 100.0*QuantumScale*image->background_color.red,
3040 100.0*QuantumScale*image->background_color.green,
3041 100.0*QuantumScale*image->background_color.blue);
3042 (void) FormatLocaleString(opacity,MagickPathExtent,"%.20g",
3043 QuantumScale*image->background_color.alpha);
3044 (void) FormatLocaleString(command,MagickPathExtent,
3045 GetDelegateCommands(delegate_info),input_filename,output_filename,
3046 density,background,opacity,unique);
3047 density=DestroyString(density);
3048 status=ExternalDelegateCommand(MagickFalse,image_info->verbose,
3049 command,(char *) NULL,exception);
3050 (void) RelinquishUniqueFileResource(unique);
3051 (void) RelinquishUniqueFileResource(input_filename);
3052 if ((status == 0) && (stat(output_filename,&attributes) == 0) &&
3053 (attributes.st_size > 0))
3061 read_info=CloneImageInfo(image_info);
3062 (void) CopyMagickString(read_info->filename,output_filename,
3064 svg_image=ReadImage(read_info,exception);
3065 read_info=DestroyImageInfo(read_info);
3066 (void) RelinquishUniqueFileResource(output_filename);
3067 if (svg_image != (Image *) NULL)
3069 for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; )
3071 (void) CopyMagickString(next->filename,image->filename,
3073 (void) CopyMagickString(next->magick,image->magick,
3075 next=GetNextImageInList(next);
3077 image=DestroyImage(image);
3081 (void) RelinquishUniqueFileResource(output_filename);
3084 #if defined(MAGICKCORE_RSVG_DELEGATE)
3085 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3098 register unsigned char
3111 register const guchar
3133 svg_handle=rsvg_handle_new();
3134 if (svg_handle == (RsvgHandle *) NULL)
3135 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3136 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
3137 if ((fabs(image->resolution.x) > MagickEpsilon) &&
3138 (fabs(image->resolution.y) > MagickEpsilon))
3139 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3140 image->resolution.y);
3141 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3144 error=(GError *) NULL;
3145 (void) rsvg_handle_write(svg_handle,message,n,&error);
3146 if (error != (GError *) NULL)
3147 g_error_free(error);
3149 error=(GError *) NULL;
3150 rsvg_handle_close(svg_handle,&error);
3151 if (error != (GError *) NULL)
3152 g_error_free(error);
3153 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3154 apply_density=MagickTrue;
3155 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
3156 if ((image->resolution.x > 0.0) && (image->resolution.y > 0.0))
3162 We should not apply the density when the internal 'factor' is 'i'.
3163 This can be checked by using the trick below.
3165 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x*256,
3166 image->resolution.y*256);
3167 rsvg_handle_get_dimensions(svg_handle,&dpi_dimension_info);
3168 if ((dpi_dimension_info.width != dimension_info.width) ||
3169 (dpi_dimension_info.height != dimension_info.height))
3170 apply_density=MagickFalse;
3171 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3172 image->resolution.y);
3174 if (image_info->size != (char *) NULL)
3176 (void) GetGeometry(image_info->size,(ssize_t *) NULL,
3177 (ssize_t *) NULL,&image->columns,&image->rows);
3178 if ((image->columns != 0) || (image->rows != 0))
3180 image->resolution.x=96.0*image->columns/dimension_info.width;
3181 image->resolution.y=96.0*image->rows/dimension_info.height;
3182 if (fabs(image->resolution.x) < MagickEpsilon)
3183 image->resolution.x=image->resolution.y;
3185 if (fabs(image->resolution.y) < MagickEpsilon)
3186 image->resolution.y=image->resolution.x;
3188 image->resolution.x=image->resolution.y=MagickMin(
3189 image->resolution.x,image->resolution.y);
3190 apply_density=MagickTrue;
3193 if (apply_density != MagickFalse)
3195 image->columns=image->resolution.x*dimension_info.width/96.0;
3196 image->rows=image->resolution.y*dimension_info.height/96.0;
3200 image->columns=dimension_info.width;
3201 image->rows=dimension_info.height;
3203 pixel_info=(MemoryInfo *) NULL;
3205 pixel_buffer=rsvg_handle_get_pixbuf(svg_handle);
3206 rsvg_handle_free(svg_handle);
3207 image->columns=gdk_pixbuf_get_width(pixel_buffer);
3208 image->rows=gdk_pixbuf_get_height(pixel_buffer);
3210 image->alpha_trait=BlendPixelTrait;
3211 status=SetImageExtent(image,image->columns,image->rows,exception);
3212 if (status == MagickFalse)
3214 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
3215 g_object_unref(G_OBJECT(pixel_buffer));
3217 g_object_unref(svg_handle);
3218 ThrowReaderException(MissingDelegateError,
3219 "NoDecodeDelegateForThisImageFormat");
3221 if (image_info->ping == MagickFalse)
3223 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3227 stride=4*image->columns;
3228 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
3229 stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
3230 (int) image->columns);
3232 pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels));
3233 if (pixel_info == (MemoryInfo *) NULL)
3235 g_object_unref(svg_handle);
3236 ThrowReaderException(ResourceLimitError,
3237 "MemoryAllocationFailed");
3239 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3241 (void) SetImageBackgroundColor(image,exception);
3242 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3243 cairo_surface=cairo_image_surface_create_for_data(pixels,
3244 CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int)
3246 if ((cairo_surface == (cairo_surface_t *) NULL) ||
3247 (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS))
3249 if (cairo_surface != (cairo_surface_t *) NULL)
3250 cairo_surface_destroy(cairo_surface);
3251 pixel_info=RelinquishVirtualMemory(pixel_info);
3252 g_object_unref(svg_handle);
3253 ThrowReaderException(ResourceLimitError,
3254 "MemoryAllocationFailed");
3256 cairo_image=cairo_create(cairo_surface);
3257 cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
3258 cairo_paint(cairo_image);
3259 cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
3260 if (apply_density != MagickFalse)
3261 cairo_scale(cairo_image,image->resolution.x/96.0,
3262 image->resolution.y/96.0);
3263 rsvg_handle_render_cairo(svg_handle,cairo_image);
3264 cairo_destroy(cairo_image);
3265 cairo_surface_destroy(cairo_surface);
3266 g_object_unref(svg_handle);
3269 p=gdk_pixbuf_get_pixels(pixel_buffer);
3271 GetPixelInfo(image,&fill_color);
3272 for (y=0; y < (ssize_t) image->rows; y++)
3274 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3275 if (q == (Quantum *) NULL)
3277 for (x=0; x < (ssize_t) image->columns; x++)
3279 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3280 fill_color.blue=ScaleCharToQuantum(*p++);
3281 fill_color.green=ScaleCharToQuantum(*p++);
3282 fill_color.red=ScaleCharToQuantum(*p++);
3284 fill_color.red=ScaleCharToQuantum(*p++);
3285 fill_color.green=ScaleCharToQuantum(*p++);
3286 fill_color.blue=ScaleCharToQuantum(*p++);
3288 fill_color.alpha=ScaleCharToQuantum(*p++);
3289 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3294 gamma=QuantumScale*fill_color.alpha;
3295 gamma=PerceptibleReciprocal(gamma);
3296 fill_color.blue*=gamma;
3297 fill_color.green*=gamma;
3298 fill_color.red*=gamma;
3301 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
3302 GetPixelAlpha(image,q),q);
3303 q+=GetPixelChannels(image);
3305 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3307 if (image->previous == (Image *) NULL)
3309 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
3311 if (status == MagickFalse)
3316 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3317 if (pixel_info != (MemoryInfo *) NULL)
3318 pixel_info=RelinquishVirtualMemory(pixel_info);
3320 g_object_unref(G_OBJECT(pixel_buffer));
3322 (void) CloseBlob(image);
3323 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3325 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3326 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3327 next=GetNextImageInList(next);
3329 return(GetFirstImageInList(image));
3337 unique_file=AcquireUniqueFileResource(filename);
3338 if (unique_file != -1)
3339 file=fdopen(unique_file,"w");
3340 if ((unique_file == -1) || (file == (FILE *) NULL))
3342 (void) CopyMagickString(image->filename,filename,MagickPathExtent);
3343 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
3345 image=DestroyImageList(image);
3346 return((Image *) NULL);
3351 svg_info=AcquireSVGInfo();
3352 if (svg_info == (SVGInfo *) NULL)
3354 (void) fclose(file);
3355 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3357 svg_info->file=file;
3358 svg_info->exception=exception;
3359 svg_info->image=image;
3360 svg_info->image_info=image_info;
3361 svg_info->bounds.width=image->columns;
3362 svg_info->bounds.height=image->rows;
3363 svg_info->svgDepth=0;
3364 if (image_info->size != (char *) NULL)
3365 (void) CloneString(&svg_info->size,image_info->size);
3366 if (image->debug != MagickFalse)
3367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3368 (void) xmlSubstituteEntitiesDefault(1);
3369 (void) memset(&sax_modules,0,sizeof(sax_modules));
3370 sax_modules.internalSubset=SVGInternalSubset;
3371 sax_modules.isStandalone=SVGIsStandalone;
3372 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3373 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3374 sax_modules.resolveEntity=SVGResolveEntity;
3375 sax_modules.getEntity=SVGGetEntity;
3376 sax_modules.entityDecl=SVGEntityDeclaration;
3377 sax_modules.notationDecl=SVGNotationDeclaration;
3378 sax_modules.attributeDecl=SVGAttributeDeclaration;
3379 sax_modules.elementDecl=SVGElementDeclaration;
3380 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3381 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3382 sax_modules.startDocument=SVGStartDocument;
3383 sax_modules.endDocument=SVGEndDocument;
3384 sax_modules.startElement=SVGStartElement;
3385 sax_modules.endElement=SVGEndElement;
3386 sax_modules.reference=SVGReference;
3387 sax_modules.characters=SVGCharacters;
3388 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3389 sax_modules.processingInstruction=SVGProcessingInstructions;
3390 sax_modules.comment=SVGComment;
3391 sax_modules.warning=SVGWarning;
3392 sax_modules.error=SVGError;
3393 sax_modules.fatalError=SVGError;
3394 sax_modules.getParameterEntity=SVGGetParameterEntity;
3395 sax_modules.cdataBlock=SVGCDataBlock;
3396 sax_modules.externalSubset=SVGExternalSubset;
3397 sax_handler=(&sax_modules);
3398 n=ReadBlob(image,MagickPathExtent-1,message);
3402 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3403 message,n,image->filename);
3404 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3407 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3412 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3413 SVGEndDocument(svg_info);
3414 xmlFreeParserCtxt(svg_info->parser);
3415 if (image->debug != MagickFalse)
3416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3417 (void) fclose(file);
3418 (void) CloseBlob(image);
3419 image->columns=svg_info->width;
3420 image->rows=svg_info->height;
3421 if (exception->severity >= ErrorException)
3423 svg_info=DestroySVGInfo(svg_info);
3424 (void) RelinquishUniqueFileResource(filename);
3425 image=DestroyImage(image);
3426 return((Image *) NULL);
3428 if (image_info->ping == MagickFalse)
3436 image=DestroyImage(image);
3437 image=(Image *) NULL;
3438 read_info=CloneImageInfo(image_info);
3439 SetImageInfoBlob(read_info,(void *) NULL,0);
3440 if (read_info->density != (char *) NULL)
3441 read_info->density=DestroyString(read_info->density);
3442 (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
3444 image=ReadImage(read_info,exception);
3445 read_info=DestroyImageInfo(read_info);
3446 if (image != (Image *) NULL)
3447 (void) CopyMagickString(image->filename,image_info->filename,
3451 Relinquish resources.
3453 if (image != (Image *) NULL)
3455 if (svg_info->title != (char *) NULL)
3456 (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3457 if (svg_info->comment != (char *) NULL)
3458 (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3461 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3463 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3464 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3465 next=GetNextImageInList(next);
3467 svg_info=DestroySVGInfo(svg_info);
3468 (void) RelinquishUniqueFileResource(filename);
3469 return(GetFirstImageInList(image));
3474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3478 % R e g i s t e r S V G I m a g e %
3482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3484 % RegisterSVGImage() adds attributes for the SVG image format to
3485 % the list of supported formats. The attributes include the image format
3486 % tag, a method to read and/or write the format, whether the format
3487 % supports the saving of more than one frame to the same file or blob,
3488 % whether the format supports native in-memory I/O, and a brief
3489 % description of the format.
3491 % The format of the RegisterSVGImage method is:
3493 % size_t RegisterSVGImage(void)
3496 ModuleExport size_t RegisterSVGImage(void)
3499 version[MagickPathExtent];
3505 #if defined(LIBXML_DOTTED_VERSION)
3506 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,
3509 #if defined(MAGICKCORE_RSVG_DELEGATE)
3510 #if !GLIB_CHECK_VERSION(2,35,0)
3513 #if defined(MAGICKCORE_XML_DELEGATE)
3516 (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d",
3517 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3519 entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics");
3520 #if defined(MAGICKCORE_XML_DELEGATE)
3521 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3523 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3524 entry->flags^=CoderBlobSupportFlag;
3525 #if defined(MAGICKCORE_RSVG_DELEGATE)
3526 entry->flags^=CoderDecoderThreadSupportFlag;
3528 entry->mime_type=ConstantString("image/svg+xml");
3529 if (*version != '\0')
3530 entry->version=ConstantString(version);
3531 entry->magick=(IsImageFormatHandler *) IsSVG;
3532 (void) RegisterMagickInfo(entry);
3533 entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics");
3534 #if defined(MAGICKCORE_XML_DELEGATE)
3535 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3537 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3538 entry->flags^=CoderBlobSupportFlag;
3539 #if defined(MAGICKCORE_RSVG_DELEGATE)
3540 entry->flags^=CoderDecoderThreadSupportFlag;
3542 entry->mime_type=ConstantString("image/svg+xml");
3543 if (*version != '\0')
3544 entry->version=ConstantString(version);
3545 entry->magick=(IsImageFormatHandler *) IsSVG;
3546 (void) RegisterMagickInfo(entry);
3547 entry=AcquireMagickInfo("SVG","MSVG",
3548 "ImageMagick's own SVG internal renderer");
3549 #if defined(MAGICKCORE_XML_DELEGATE)
3550 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3552 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3553 entry->flags^=CoderBlobSupportFlag;
3554 #if defined(MAGICKCORE_RSVG_DELEGATE)
3555 entry->flags^=CoderDecoderThreadSupportFlag;
3557 entry->magick=(IsImageFormatHandler *) IsSVG;
3558 (void) RegisterMagickInfo(entry);
3559 return(MagickImageCoderSignature);
3563 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3567 % U n r e g i s t e r S V G I m a g e %
3571 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3573 % UnregisterSVGImage() removes format registrations made by the
3574 % SVG module from the list of supported formats.
3576 % The format of the UnregisterSVGImage method is:
3578 % UnregisterSVGImage(void)
3581 ModuleExport void UnregisterSVGImage(void)
3583 (void) UnregisterMagickInfo("SVGZ");
3584 (void) UnregisterMagickInfo("SVG");
3585 (void) UnregisterMagickInfo("MSVG");
3586 #if defined(MAGICKCORE_XML_DELEGATE)
3592 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3596 % W r i t e S V G I m a g e %
3600 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3602 % WriteSVGImage() writes a image in the SVG - XML based W3C standard
3605 % The format of the WriteSVGImage method is:
3607 % MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3608 % Image *image,ExceptionInfo *exception)
3610 % A description of each parameter follows.
3612 % o image_info: the image info.
3614 % o image: The image.
3616 % o exception: return any errors or warnings in this structure.
3620 static void AffineToTransform(Image *image,AffineMatrix *affine)
3623 transform[MagickPathExtent];
3625 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3627 if ((fabs(affine->rx) < MagickEpsilon) &&
3628 (fabs(affine->ry) < MagickEpsilon))
3630 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3631 (fabs(affine->sy-1.0) < MagickEpsilon))
3633 (void) WriteBlobString(image,"\">\n");
3636 (void) FormatLocaleString(transform,MagickPathExtent,
3637 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3638 (void) WriteBlobString(image,transform);
3643 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3644 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3645 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3651 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3652 (void) FormatLocaleString(transform,MagickPathExtent,
3653 "\" transform=\"rotate(%g)\">\n",theta);
3654 (void) WriteBlobString(image,transform);
3661 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3662 (fabs(affine->rx) < MagickEpsilon) &&
3663 (fabs(affine->ry) < MagickEpsilon) &&
3664 (fabs(affine->sy-1.0) < MagickEpsilon))
3666 (void) FormatLocaleString(transform,MagickPathExtent,
3667 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3668 (void) WriteBlobString(image,transform);
3672 (void) FormatLocaleString(transform,MagickPathExtent,
3673 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3674 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3675 (void) WriteBlobString(image,transform);
3678 static MagickBooleanType IsPoint(const char *point)
3686 value=strtol(point,&p,10);
3688 return(p != point ? MagickTrue : MagickFalse);
3691 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
3693 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3698 at_fitting_opts_type
3710 register const Quantum
3724 Trace image and write as SVG.
3726 fitting_options=at_fitting_opts_new();
3727 output_options=at_output_opts_new();
3728 (void) SetImageGray(image,exception);
3729 type=GetImageType(image);
3731 if ((type == BilevelType) || (type == GrayscaleType))
3733 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3735 for (y=0; y < (ssize_t) image->rows; y++)
3737 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3738 if (p == (const Quantum *) NULL)
3740 for (x=0; x < (ssize_t) image->columns; x++)
3742 trace->bitmap[i++]=GetPixelRed(image,p);
3743 if (number_planes == 3)
3745 trace->bitmap[i++]=GetPixelGreen(image,p);
3746 trace->bitmap[i++]=GetPixelBlue(image,p);
3748 p+=GetPixelChannels(image);
3751 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3753 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3754 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3759 at_splines_free(splines);
3760 at_bitmap_free(trace);
3761 at_output_opts_free(output_options);
3762 at_fitting_opts_free(fitting_options);
3768 message[MagickPathExtent];
3789 (void) WriteBlobString(image,
3790 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
3791 (void) WriteBlobString(image,
3792 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
3793 (void) WriteBlobString(image,
3794 " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
3795 (void) FormatLocaleString(message,MagickPathExtent,
3796 "<svg version=\"1.1\" id=\"Layer_1\" "
3797 "xmlns=\"http://www.w3.org/2000/svg\" "
3798 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" "
3799 "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" "
3800 "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">",
3801 (double) image->columns,(double) image->rows,
3802 (double) image->columns,(double) image->rows,
3803 (double) image->columns,(double) image->rows);
3804 (void) WriteBlobString(image,message);
3805 clone_image=CloneImage(image,0,0,MagickTrue,exception);
3806 if (clone_image == (Image *) NULL)
3807 return(MagickFalse);
3808 image_info=AcquireImageInfo();
3809 (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent);
3811 blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length,
3813 clone_image=DestroyImage(clone_image);
3814 image_info=DestroyImageInfo(image_info);
3815 if (blob == (unsigned char *) NULL)
3816 return(MagickFalse);
3818 base64=Base64Encode(blob,blob_length,&encode_length);
3819 blob=(unsigned char *) RelinquishMagickMemory(blob);
3820 (void) FormatLocaleString(message,MagickPathExtent,
3821 " <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" "
3822 "x=\"%.20g\" y=\"%.20g\"\n href=\"data:image/png;base64,",
3823 (double) image->scene,(double) image->columns,(double) image->rows,
3824 (double) image->page.x,(double) image->page.y);
3825 (void) WriteBlobString(image,message);
3827 for (i=(ssize_t) encode_length; i > 0; i-=76)
3829 (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p);
3830 (void) WriteBlobString(image,message);
3833 (void) WriteBlobString(image,"\n");
3835 base64=DestroyString(base64);
3836 (void) WriteBlobString(image,"\" />\n");
3837 (void) WriteBlobString(image,"</svg>\n");
3844 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
3845 ExceptionInfo *exception)
3847 #define BezierQuantum 200
3853 keyword[MagickPathExtent],
3854 message[MagickPathExtent],
3855 name[MagickPathExtent],
3858 type[MagickPathExtent];
3899 Open output image file.
3901 assert(image_info != (const ImageInfo *) NULL);
3902 assert(image_info->signature == MagickCoreSignature);
3903 assert(image != (Image *) NULL);
3904 assert(image->signature == MagickCoreSignature);
3905 if (image->debug != MagickFalse)
3906 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3907 assert(exception != (ExceptionInfo *) NULL);
3908 assert(exception->signature == MagickCoreSignature);
3909 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3910 if (status == MagickFalse)
3912 value=GetImageArtifact(image,"SVG");
3913 if (value != (char *) NULL)
3915 (void) WriteBlobString(image,value);
3916 (void) CloseBlob(image);
3919 value=GetImageArtifact(image,"MVG");
3920 if (value == (char *) NULL)
3921 return(TraceSVGImage(image,exception));
3925 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3926 (void) WriteBlobString(image,
3927 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3928 (void) WriteBlobString(image,
3929 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3930 (void) FormatLocaleString(message,MagickPathExtent,
3931 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3933 (void) WriteBlobString(image,message);
3935 Allocate primitive info memory.
3938 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3939 sizeof(*primitive_info));
3940 if (primitive_info == (PrimitiveInfo *) NULL)
3941 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3942 GetAffineMatrix(&affine);
3943 token=AcquireString(value);
3944 extent=strlen(token)+MagickPathExtent;
3948 for (q=(const char *) value; *q != '\0'; )
3951 Interpret graphic primitive.
3953 GetNextToken(q,&q,MagickPathExtent,keyword);
3954 if (*keyword == '\0')
3956 if (*keyword == '#')
3961 if (active != MagickFalse)
3963 AffineToTransform(image,&affine);
3966 (void) WriteBlobString(image,"<desc>");
3967 (void) WriteBlobString(image,keyword+1);
3968 for ( ; (*q != '\n') && (*q != '\0'); q++)
3971 case '<': (void) WriteBlobString(image,"<"); break;
3972 case '>': (void) WriteBlobString(image,">"); break;
3973 case '&': (void) WriteBlobString(image,"&"); break;
3974 default: (void) WriteBlobByte(image,*q); break;
3976 (void) WriteBlobString(image,"</desc>\n");
3979 primitive_type=UndefinedPrimitive;
3987 if (LocaleCompare("affine",keyword) == 0)
3989 GetNextToken(q,&q,extent,token);
3990 affine.sx=StringToDouble(token,&next_token);
3991 GetNextToken(q,&q,extent,token);
3993 GetNextToken(q,&q,extent,token);
3994 affine.rx=StringToDouble(token,&next_token);
3995 GetNextToken(q,&q,extent,token);
3997 GetNextToken(q,&q,extent,token);
3998 affine.ry=StringToDouble(token,&next_token);
3999 GetNextToken(q,&q,extent,token);
4001 GetNextToken(q,&q,extent,token);
4002 affine.sy=StringToDouble(token,&next_token);
4003 GetNextToken(q,&q,extent,token);
4005 GetNextToken(q,&q,extent,token);
4006 affine.tx=StringToDouble(token,&next_token);
4007 GetNextToken(q,&q,extent,token);
4009 GetNextToken(q,&q,extent,token);
4010 affine.ty=StringToDouble(token,&next_token);
4013 if (LocaleCompare("alpha",keyword) == 0)
4015 primitive_type=AlphaPrimitive;
4018 if (LocaleCompare("angle",keyword) == 0)
4020 GetNextToken(q,&q,extent,token);
4021 affine.rx=StringToDouble(token,&next_token);
4022 affine.ry=StringToDouble(token,&next_token);
4025 if (LocaleCompare("arc",keyword) == 0)
4027 primitive_type=ArcPrimitive;
4036 if (LocaleCompare("bezier",keyword) == 0)
4038 primitive_type=BezierPrimitive;
4047 if (LocaleCompare("clip-path",keyword) == 0)
4049 GetNextToken(q,&q,extent,token);
4050 (void) FormatLocaleString(message,MagickPathExtent,
4051 "clip-path:url(#%s);",token);
4052 (void) WriteBlobString(image,message);
4055 if (LocaleCompare("clip-rule",keyword) == 0)
4057 GetNextToken(q,&q,extent,token);
4058 (void) FormatLocaleString(message,MagickPathExtent,"clip-rule:%s;",
4060 (void) WriteBlobString(image,message);
4063 if (LocaleCompare("clip-units",keyword) == 0)
4065 GetNextToken(q,&q,extent,token);
4066 (void) FormatLocaleString(message,MagickPathExtent,
4067 "clipPathUnits=%s;",token);
4068 (void) WriteBlobString(image,message);
4071 if (LocaleCompare("circle",keyword) == 0)
4073 primitive_type=CirclePrimitive;
4076 if (LocaleCompare("color",keyword) == 0)
4078 primitive_type=ColorPrimitive;
4087 if (LocaleCompare("decorate",keyword) == 0)
4089 GetNextToken(q,&q,extent,token);
4090 (void) FormatLocaleString(message,MagickPathExtent,
4091 "text-decoration:%s;",token);
4092 (void) WriteBlobString(image,message);
4101 if (LocaleCompare("ellipse",keyword) == 0)
4103 primitive_type=EllipsePrimitive;
4112 if (LocaleCompare("fill",keyword) == 0)
4114 GetNextToken(q,&q,extent,token);
4115 (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;",
4117 (void) WriteBlobString(image,message);
4120 if (LocaleCompare("fill-rule",keyword) == 0)
4122 GetNextToken(q,&q,extent,token);
4123 (void) FormatLocaleString(message,MagickPathExtent,
4124 "fill-rule:%s;",token);
4125 (void) WriteBlobString(image,message);
4128 if (LocaleCompare("fill-opacity",keyword) == 0)
4130 GetNextToken(q,&q,extent,token);
4131 (void) FormatLocaleString(message,MagickPathExtent,
4132 "fill-opacity:%s;",token);
4133 (void) WriteBlobString(image,message);
4136 if (LocaleCompare("font-family",keyword) == 0)
4138 GetNextToken(q,&q,extent,token);
4139 (void) FormatLocaleString(message,MagickPathExtent,
4140 "font-family:%s;",token);
4141 (void) WriteBlobString(image,message);
4144 if (LocaleCompare("font-stretch",keyword) == 0)
4146 GetNextToken(q,&q,extent,token);
4147 (void) FormatLocaleString(message,MagickPathExtent,
4148 "font-stretch:%s;",token);
4149 (void) WriteBlobString(image,message);
4152 if (LocaleCompare("font-style",keyword) == 0)
4154 GetNextToken(q,&q,extent,token);
4155 (void) FormatLocaleString(message,MagickPathExtent,
4156 "font-style:%s;",token);
4157 (void) WriteBlobString(image,message);
4160 if (LocaleCompare("font-size",keyword) == 0)
4162 GetNextToken(q,&q,extent,token);
4163 (void) FormatLocaleString(message,MagickPathExtent,
4164 "font-size:%s;",token);
4165 (void) WriteBlobString(image,message);
4168 if (LocaleCompare("font-weight",keyword) == 0)
4170 GetNextToken(q,&q,extent,token);
4171 (void) FormatLocaleString(message,MagickPathExtent,
4172 "font-weight:%s;",token);
4173 (void) WriteBlobString(image,message);
4182 if (LocaleCompare("gradient-units",keyword) == 0)
4184 GetNextToken(q,&q,extent,token);
4187 if (LocaleCompare("text-align",keyword) == 0)
4189 GetNextToken(q,&q,extent,token);
4190 (void) FormatLocaleString(message,MagickPathExtent,
4191 "text-align %s ",token);
4192 (void) WriteBlobString(image,message);
4195 if (LocaleCompare("text-anchor",keyword) == 0)
4197 GetNextToken(q,&q,extent,token);
4198 (void) FormatLocaleString(message,MagickPathExtent,
4199 "text-anchor %s ",token);
4200 (void) WriteBlobString(image,message);
4209 if (LocaleCompare("image",keyword) == 0)
4211 GetNextToken(q,&q,extent,token);
4212 primitive_type=ImagePrimitive;
4221 if (LocaleCompare("line",keyword) == 0)
4223 primitive_type=LinePrimitive;
4232 if (LocaleCompare("opacity",keyword) == 0)
4234 GetNextToken(q,&q,extent,token);
4235 (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ",
4237 (void) WriteBlobString(image,message);
4246 if (LocaleCompare("path",keyword) == 0)
4248 primitive_type=PathPrimitive;
4251 if (LocaleCompare("point",keyword) == 0)
4253 primitive_type=PointPrimitive;
4256 if (LocaleCompare("polyline",keyword) == 0)
4258 primitive_type=PolylinePrimitive;
4261 if (LocaleCompare("polygon",keyword) == 0)
4263 primitive_type=PolygonPrimitive;
4266 if (LocaleCompare("pop",keyword) == 0)
4268 GetNextToken(q,&q,extent,token);
4269 if (LocaleCompare("clip-path",token) == 0)
4271 (void) WriteBlobString(image,"</clipPath>\n");
4274 if (LocaleCompare("defs",token) == 0)
4276 (void) WriteBlobString(image,"</defs>\n");
4279 if (LocaleCompare("gradient",token) == 0)
4281 (void) FormatLocaleString(message,MagickPathExtent,
4282 "</%sGradient>\n",type);
4283 (void) WriteBlobString(image,message);
4286 if (LocaleCompare("graphic-context",token) == 0)
4290 ThrowWriterException(DrawError,
4291 "UnbalancedGraphicContextPushPop");
4292 (void) WriteBlobString(image,"</g>\n");
4294 if (LocaleCompare("pattern",token) == 0)
4296 (void) WriteBlobString(image,"</pattern>\n");
4299 if (LocaleCompare("symbol",token) == 0)
4301 (void) WriteBlobString(image,"</symbol>\n");
4304 if ((LocaleCompare("defs",token) == 0) ||
4305 (LocaleCompare("symbol",token) == 0))
4306 (void) WriteBlobString(image,"</g>\n");
4309 if (LocaleCompare("push",keyword) == 0)
4311 GetNextToken(q,&q,extent,token);
4312 if (LocaleCompare("clip-path",token) == 0)
4314 GetNextToken(q,&q,extent,token);
4315 (void) FormatLocaleString(message,MagickPathExtent,
4316 "<clipPath id=\"%s\">\n",token);
4317 (void) WriteBlobString(image,message);
4320 if (LocaleCompare("defs",token) == 0)
4322 (void) WriteBlobString(image,"<defs>\n");
4325 if (LocaleCompare("gradient",token) == 0)
4327 GetNextToken(q,&q,extent,token);
4328 (void) CopyMagickString(name,token,MagickPathExtent);
4329 GetNextToken(q,&q,extent,token);
4330 (void) CopyMagickString(type,token,MagickPathExtent);
4331 GetNextToken(q,&q,extent,token);
4332 svg_info.segment.x1=StringToDouble(token,&next_token);
4333 svg_info.element.cx=StringToDouble(token,&next_token);
4334 GetNextToken(q,&q,extent,token);
4336 GetNextToken(q,&q,extent,token);
4337 svg_info.segment.y1=StringToDouble(token,&next_token);
4338 svg_info.element.cy=StringToDouble(token,&next_token);
4339 GetNextToken(q,&q,extent,token);
4341 GetNextToken(q,&q,extent,token);
4342 svg_info.segment.x2=StringToDouble(token,&next_token);
4343 svg_info.element.major=StringToDouble(token,
4345 GetNextToken(q,&q,extent,token);
4347 GetNextToken(q,&q,extent,token);
4348 svg_info.segment.y2=StringToDouble(token,&next_token);
4349 svg_info.element.minor=StringToDouble(token,
4351 (void) FormatLocaleString(message,MagickPathExtent,
4352 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
4353 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
4354 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
4355 if (LocaleCompare(type,"radial") == 0)
4357 GetNextToken(q,&q,extent,token);
4359 GetNextToken(q,&q,extent,token);
4360 svg_info.element.angle=StringToDouble(token,
4362 (void) FormatLocaleString(message,MagickPathExtent,
4363 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
4364 "fx=\"%g\" fy=\"%g\">\n",type,name,
4365 svg_info.element.cx,svg_info.element.cy,
4366 svg_info.element.angle,svg_info.element.major,
4367 svg_info.element.minor);
4369 (void) WriteBlobString(image,message);
4372 if (LocaleCompare("graphic-context",token) == 0)
4377 AffineToTransform(image,&affine);
4380 (void) WriteBlobString(image,"<g style=\"");
4383 if (LocaleCompare("pattern",token) == 0)
4385 GetNextToken(q,&q,extent,token);
4386 (void) CopyMagickString(name,token,MagickPathExtent);
4387 GetNextToken(q,&q,extent,token);
4388 svg_info.bounds.x=StringToDouble(token,&next_token);
4389 GetNextToken(q,&q,extent,token);
4391 GetNextToken(q,&q,extent,token);
4392 svg_info.bounds.y=StringToDouble(token,&next_token);
4393 GetNextToken(q,&q,extent,token);
4395 GetNextToken(q,&q,extent,token);
4396 svg_info.bounds.width=StringToDouble(token,
4398 GetNextToken(q,&q,extent,token);
4400 GetNextToken(q,&q,extent,token);
4401 svg_info.bounds.height=StringToDouble(token,(char **) NULL);
4402 (void) FormatLocaleString(message,MagickPathExtent,
4403 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
4404 "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y,
4405 svg_info.bounds.width,svg_info.bounds.height);
4406 (void) WriteBlobString(image,message);
4409 if (LocaleCompare("symbol",token) == 0)
4411 (void) WriteBlobString(image,"<symbol>\n");
4422 if (LocaleCompare("rectangle",keyword) == 0)
4424 primitive_type=RectanglePrimitive;
4427 if (LocaleCompare("roundRectangle",keyword) == 0)
4429 primitive_type=RoundRectanglePrimitive;
4432 if (LocaleCompare("rotate",keyword) == 0)
4434 GetNextToken(q,&q,extent,token);
4435 (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ",
4437 (void) WriteBlobString(image,message);
4446 if (LocaleCompare("scale",keyword) == 0)
4448 GetNextToken(q,&q,extent,token);
4449 affine.sx=StringToDouble(token,&next_token);
4450 GetNextToken(q,&q,extent,token);
4452 GetNextToken(q,&q,extent,token);
4453 affine.sy=StringToDouble(token,&next_token);
4456 if (LocaleCompare("skewX",keyword) == 0)
4458 GetNextToken(q,&q,extent,token);
4459 (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ",
4461 (void) WriteBlobString(image,message);
4464 if (LocaleCompare("skewY",keyword) == 0)
4466 GetNextToken(q,&q,extent,token);
4467 (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ",
4469 (void) WriteBlobString(image,message);
4472 if (LocaleCompare("stop-color",keyword) == 0)
4475 color[MagickPathExtent];
4477 GetNextToken(q,&q,extent,token);
4478 (void) CopyMagickString(color,token,MagickPathExtent);
4479 GetNextToken(q,&q,extent,token);
4480 (void) FormatLocaleString(message,MagickPathExtent,
4481 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4482 (void) WriteBlobString(image,message);
4485 if (LocaleCompare("stroke",keyword) == 0)
4487 GetNextToken(q,&q,extent,token);
4488 (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;",
4490 (void) WriteBlobString(image,message);
4493 if (LocaleCompare("stroke-antialias",keyword) == 0)
4495 GetNextToken(q,&q,extent,token);
4496 (void) FormatLocaleString(message,MagickPathExtent,
4497 "stroke-antialias:%s;",token);
4498 (void) WriteBlobString(image,message);
4501 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4509 GetNextToken(p,&p,extent,token);
4510 for (k=0; IsPoint(token); k++)
4511 GetNextToken(p,&p,extent,token);
4512 (void) WriteBlobString(image,"stroke-dasharray:");
4513 for (j=0; j < k; j++)
4515 GetNextToken(q,&q,extent,token);
4516 (void) FormatLocaleString(message,MagickPathExtent,"%s ",
4518 (void) WriteBlobString(image,message);
4520 (void) WriteBlobString(image,";");
4523 GetNextToken(q,&q,extent,token);
4524 (void) FormatLocaleString(message,MagickPathExtent,
4525 "stroke-dasharray:%s;",token);
4526 (void) WriteBlobString(image,message);
4529 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4531 GetNextToken(q,&q,extent,token);
4532 (void) FormatLocaleString(message,MagickPathExtent,
4533 "stroke-dashoffset:%s;",token);
4534 (void) WriteBlobString(image,message);
4537 if (LocaleCompare("stroke-linecap",keyword) == 0)
4539 GetNextToken(q,&q,extent,token);
4540 (void) FormatLocaleString(message,MagickPathExtent,
4541 "stroke-linecap:%s;",token);
4542 (void) WriteBlobString(image,message);
4545 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4547 GetNextToken(q,&q,extent,token);
4548 (void) FormatLocaleString(message,MagickPathExtent,
4549 "stroke-linejoin:%s;",token);
4550 (void) WriteBlobString(image,message);
4553 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4555 GetNextToken(q,&q,extent,token);
4556 (void) FormatLocaleString(message,MagickPathExtent,
4557 "stroke-miterlimit:%s;",token);
4558 (void) WriteBlobString(image,message);
4561 if (LocaleCompare("stroke-opacity",keyword) == 0)
4563 GetNextToken(q,&q,extent,token);
4564 (void) FormatLocaleString(message,MagickPathExtent,
4565 "stroke-opacity:%s;",token);
4566 (void) WriteBlobString(image,message);
4569 if (LocaleCompare("stroke-width",keyword) == 0)
4571 GetNextToken(q,&q,extent,token);
4572 (void) FormatLocaleString(message,MagickPathExtent,
4573 "stroke-width:%s;",token);
4574 (void) WriteBlobString(image,message);
4583 if (LocaleCompare("text",keyword) == 0)
4585 primitive_type=TextPrimitive;
4588 if (LocaleCompare("text-antialias",keyword) == 0)
4590 GetNextToken(q,&q,extent,token);
4591 (void) FormatLocaleString(message,MagickPathExtent,
4592 "text-antialias:%s;",token);
4593 (void) WriteBlobString(image,message);
4596 if (LocaleCompare("tspan",keyword) == 0)
4598 primitive_type=TextPrimitive;
4601 if (LocaleCompare("translate",keyword) == 0)
4603 GetNextToken(q,&q,extent,token);
4604 affine.tx=StringToDouble(token,&next_token);
4605 GetNextToken(q,&q,extent,token);
4607 GetNextToken(q,&q,extent,token);
4608 affine.ty=StringToDouble(token,&next_token);
4617 if (LocaleCompare("viewbox",keyword) == 0)
4619 GetNextToken(q,&q,extent,token);
4621 GetNextToken(q,&q,extent,token);
4622 GetNextToken(q,&q,extent,token);
4624 GetNextToken(q,&q,extent,token);
4625 GetNextToken(q,&q,extent,token);
4627 GetNextToken(q,&q,extent,token);
4628 GetNextToken(q,&q,extent,token);
4640 if (status == MagickFalse)
4642 if (primitive_type == UndefinedPrimitive)
4645 Parse the primitive attributes.
4649 for (x=0; *q != '\0'; x++)
4654 if (IsPoint(q) == MagickFalse)
4656 GetNextToken(q,&q,extent,token);
4657 point.x=StringToDouble(token,&next_token);
4658 GetNextToken(q,&q,extent,token);
4660 GetNextToken(q,&q,extent,token);
4661 point.y=StringToDouble(token,&next_token);
4662 GetNextToken(q,(const char **) NULL,extent,token);
4664 GetNextToken(q,&q,extent,token);
4665 primitive_info[i].primitive=primitive_type;
4666 primitive_info[i].point=point;
4667 primitive_info[i].coordinates=0;
4668 primitive_info[i].method=FloodfillMethod;
4670 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4672 number_points+=6*BezierQuantum+360;
4673 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4674 number_points,sizeof(*primitive_info));
4675 if (primitive_info == (PrimitiveInfo *) NULL)
4677 (void) ThrowMagickException(exception,GetMagickModule(),
4678 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4682 primitive_info[j].primitive=primitive_type;
4683 primitive_info[j].coordinates=x;
4684 primitive_info[j].method=FloodfillMethod;
4685 primitive_info[j].text=(char *) NULL;
4688 AffineToTransform(image,&affine);
4692 switch (primitive_type)
4694 case PointPrimitive:
4697 if (primitive_info[j].coordinates != 1)
4706 if (primitive_info[j].coordinates != 2)
4711 (void) FormatLocaleString(message,MagickPathExtent,
4712 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4713 primitive_info[j].point.x,primitive_info[j].point.y,
4714 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4715 (void) WriteBlobString(image,message);
4718 case RectanglePrimitive:
4720 if (primitive_info[j].coordinates != 2)
4725 (void) FormatLocaleString(message,MagickPathExtent,
4726 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4727 primitive_info[j].point.x,primitive_info[j].point.y,
4728 primitive_info[j+1].point.x-primitive_info[j].point.x,
4729 primitive_info[j+1].point.y-primitive_info[j].point.y);
4730 (void) WriteBlobString(image,message);
4733 case RoundRectanglePrimitive:
4735 if (primitive_info[j].coordinates != 3)
4740 (void) FormatLocaleString(message,MagickPathExtent,
4741 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4742 "ry=\"%g\"/>\n",primitive_info[j].point.x,
4743 primitive_info[j].point.y,primitive_info[j+1].point.x-
4744 primitive_info[j].point.x,primitive_info[j+1].point.y-
4745 primitive_info[j].point.y,primitive_info[j+2].point.x,
4746 primitive_info[j+2].point.y);
4747 (void) WriteBlobString(image,message);
4752 if (primitive_info[j].coordinates != 3)
4759 case EllipsePrimitive:
4761 if (primitive_info[j].coordinates != 3)
4766 (void) FormatLocaleString(message,MagickPathExtent,
4767 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
4768 primitive_info[j].point.x,primitive_info[j].point.y,
4769 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4770 (void) WriteBlobString(image,message);
4773 case CirclePrimitive:
4779 if (primitive_info[j].coordinates != 2)
4784 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4785 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4786 (void) FormatLocaleString(message,MagickPathExtent,
4787 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
4788 primitive_info[j].point.x,primitive_info[j].point.y,
4790 (void) WriteBlobString(image,message);
4793 case PolylinePrimitive:
4795 if (primitive_info[j].coordinates < 2)
4800 (void) CopyMagickString(message," <polyline points=\"",
4802 (void) WriteBlobString(image,message);
4803 length=strlen(message);
4806 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4807 primitive_info[j].point.x,primitive_info[j].point.y);
4808 length+=strlen(message);
4811 (void) WriteBlobString(image,"\n ");
4812 length=strlen(message)+5;
4814 (void) WriteBlobString(image,message);
4816 (void) WriteBlobString(image,"\"/>\n");
4819 case PolygonPrimitive:
4821 if (primitive_info[j].coordinates < 3)
4826 primitive_info[i]=primitive_info[j];
4827 primitive_info[i].coordinates=0;
4828 primitive_info[j].coordinates++;
4830 (void) CopyMagickString(message," <polygon points=\"",MagickPathExtent);
4831 (void) WriteBlobString(image,message);
4832 length=strlen(message);
4835 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4836 primitive_info[j].point.x,primitive_info[j].point.y);
4837 length+=strlen(message);
4840 (void) WriteBlobString(image,"\n ");
4841 length=strlen(message)+5;
4843 (void) WriteBlobString(image,message);
4845 (void) WriteBlobString(image,"\"/>\n");
4848 case BezierPrimitive:
4850 if (primitive_info[j].coordinates < 3)
4862 GetNextToken(q,&q,extent,token);
4863 number_attributes=1;
4864 for (p=token; *p != '\0'; p++)
4865 if (isalpha((int) *p))
4866 number_attributes++;
4867 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
4869 number_points+=6*BezierQuantum*number_attributes;
4870 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4871 number_points,sizeof(*primitive_info));
4872 if (primitive_info == (PrimitiveInfo *) NULL)
4874 (void) ThrowMagickException(exception,GetMagickModule(),
4875 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4880 (void) WriteBlobString(image," <path d=\"");
4881 (void) WriteBlobString(image,token);
4882 (void) WriteBlobString(image,"\"/>\n");
4885 case AlphaPrimitive:
4886 case ColorPrimitive:
4888 if (primitive_info[j].coordinates != 1)
4893 GetNextToken(q,&q,extent,token);
4894 if (LocaleCompare("point",token) == 0)
4895 primitive_info[j].method=PointMethod;
4896 if (LocaleCompare("replace",token) == 0)
4897 primitive_info[j].method=ReplaceMethod;
4898 if (LocaleCompare("floodfill",token) == 0)
4899 primitive_info[j].method=FloodfillMethod;
4900 if (LocaleCompare("filltoborder",token) == 0)
4901 primitive_info[j].method=FillToBorderMethod;
4902 if (LocaleCompare("reset",token) == 0)
4903 primitive_info[j].method=ResetMethod;
4911 if (primitive_info[j].coordinates != 1)
4916 GetNextToken(q,&q,extent,token);
4917 (void) FormatLocaleString(message,MagickPathExtent,
4918 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
4919 primitive_info[j].point.y);
4920 (void) WriteBlobString(image,message);
4921 for (p=token; *p != '\0'; p++)
4924 case '<': (void) WriteBlobString(image,"<"); break;
4925 case '>': (void) WriteBlobString(image,">"); break;
4926 case '&': (void) WriteBlobString(image,"&"); break;
4927 default: (void) WriteBlobByte(image,*p); break;
4929 (void) WriteBlobString(image,"</text>\n");
4932 case ImagePrimitive:
4934 if (primitive_info[j].coordinates != 2)
4939 GetNextToken(q,&q,extent,token);
4940 (void) FormatLocaleString(message,MagickPathExtent,
4941 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
4942 "href=\"%s\"/>\n",primitive_info[j].point.x,
4943 primitive_info[j].point.y,primitive_info[j+1].point.x,
4944 primitive_info[j+1].point.y,token);
4945 (void) WriteBlobString(image,message);
4949 if (primitive_info == (PrimitiveInfo *) NULL)
4951 primitive_info[i].primitive=UndefinedPrimitive;
4952 if (status == MagickFalse)
4955 (void) WriteBlobString(image,"</svg>\n");
4957 Relinquish resources.
4959 token=DestroyString(token);
4960 if (primitive_info != (PrimitiveInfo *) NULL)
4961 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4962 (void) CloseBlob(image);