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 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1394 svg_info->bounds.x,svg_info->bounds.y);
1395 svg_info->center.x=svg_info->bounds.x;
1396 svg_info->center.y=svg_info->bounds.y;
1397 svg_info->bounds.x=0.0;
1398 svg_info->bounds.y=0.0;
1399 svg_info->bounds.width=0.0;
1400 svg_info->bounds.height=0.0;
1403 if (LocaleCompare((const char *) name,"tspan") == 0)
1405 if (*svg_info->text != '\0')
1416 text=EscapeString(svg_info->text,'\'');
1417 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
1418 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1419 svg_info->center.y,text);
1420 text=DestroyString(text);
1421 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
1422 draw_info->pointsize=svg_info->pointsize;
1423 draw_info->text=AcquireString(svg_info->text);
1424 (void) ConcatenateString(&draw_info->text," ");
1425 (void) GetTypeMetrics(svg_info->image,draw_info,
1426 &metrics,svg_info->exception);
1427 svg_info->bounds.x+=metrics.width;
1428 draw_info=DestroyDrawInfo(draw_info);
1429 *svg_info->text='\0';
1431 PushGraphicContext(id);
1439 if (LocaleCompare((char *) name,"use") == 0)
1441 PushGraphicContext(id);
1449 if (attributes != (const xmlChar **) NULL)
1450 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1452 keyword=(const char *) attributes[i];
1453 value=(const char *) attributes[i+1];
1454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1455 " %s = %s",keyword,value);
1461 if (LocaleCompare(keyword,"angle") == 0)
1463 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1464 GetUserSpaceCoordinateValue(svg_info,0,value));
1472 if (LocaleCompare(keyword,"class") == 0)
1479 GetNextToken(p,&p,MagickPathExtent,token);
1481 GetNextToken(p,&p,MagickPathExtent,token);
1484 (void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",
1491 if (LocaleCompare(keyword,"clip-path") == 0)
1493 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",
1497 if (LocaleCompare(keyword,"clip-rule") == 0)
1499 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",
1503 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1505 (void) CloneString(&units,value);
1506 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
1510 if (LocaleCompare(keyword,"color") == 0)
1512 (void) CloneString(&color,value);
1515 if (LocaleCompare(keyword,"cx") == 0)
1517 svg_info->element.cx=
1518 GetUserSpaceCoordinateValue(svg_info,1,value);
1521 if (LocaleCompare(keyword,"cy") == 0)
1523 svg_info->element.cy=
1524 GetUserSpaceCoordinateValue(svg_info,-1,value);
1532 if (LocaleCompare(keyword,"d") == 0)
1534 (void) CloneString(&svg_info->vertices,value);
1537 if (LocaleCompare(keyword,"dx") == 0)
1539 svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
1542 if (LocaleCompare(keyword,"dy") == 0)
1544 svg_info->bounds.y+=
1545 GetUserSpaceCoordinateValue(svg_info,-1,value);
1553 if (LocaleCompare(keyword,"fill") == 0)
1555 if (LocaleCompare(value,"currentColor") == 0)
1557 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
1560 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1563 if (LocaleCompare(keyword,"fillcolor") == 0)
1565 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1568 if (LocaleCompare(keyword,"fill-rule") == 0)
1570 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",
1574 if (LocaleCompare(keyword,"fill-opacity") == 0)
1576 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
1580 if (LocaleCompare(keyword,"font-family") == 0)
1582 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
1586 if (LocaleCompare(keyword,"font-stretch") == 0)
1588 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
1592 if (LocaleCompare(keyword,"font-style") == 0)
1594 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
1598 if (LocaleCompare(keyword,"font-size") == 0)
1600 if (LocaleCompare(value,"xx-small") == 0)
1601 svg_info->pointsize=6.144;
1602 else if (LocaleCompare(value,"x-small") == 0)
1603 svg_info->pointsize=7.68;
1604 else if (LocaleCompare(value,"small") == 0)
1605 svg_info->pointsize=9.6;
1606 else if (LocaleCompare(value,"medium") == 0)
1607 svg_info->pointsize=12.0;
1608 else if (LocaleCompare(value,"large") == 0)
1609 svg_info->pointsize=14.4;
1610 else if (LocaleCompare(value,"x-large") == 0)
1611 svg_info->pointsize=17.28;
1612 else if (LocaleCompare(value,"xx-large") == 0)
1613 svg_info->pointsize=20.736;
1615 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,
1617 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1618 svg_info->pointsize);
1621 if (LocaleCompare(keyword,"font-weight") == 0)
1623 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
1632 if (LocaleCompare(keyword,"gradientTransform") == 0)
1639 GetAffineMatrix(&transform);
1640 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1641 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
1642 if (tokens == (char **) NULL)
1644 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
1646 keyword=(char *) tokens[j];
1647 if (keyword == (char *) NULL)
1649 value=(char *) tokens[j+1];
1650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1651 " %s: %s",keyword,value);
1653 GetAffineMatrix(&affine);
1659 if (LocaleCompare(keyword,"matrix") == 0)
1661 p=(const char *) value;
1662 GetNextToken(p,&p,MagickPathExtent,token);
1663 affine.sx=StringToDouble(value,(char **) NULL);
1664 GetNextToken(p,&p,MagickPathExtent,token);
1666 GetNextToken(p,&p,MagickPathExtent,token);
1667 affine.rx=StringToDouble(token,&next_token);
1668 GetNextToken(p,&p,MagickPathExtent,token);
1670 GetNextToken(p,&p,MagickPathExtent,token);
1671 affine.ry=StringToDouble(token,&next_token);
1672 GetNextToken(p,&p,MagickPathExtent,token);
1674 GetNextToken(p,&p,MagickPathExtent,token);
1675 affine.sy=StringToDouble(token,&next_token);
1676 GetNextToken(p,&p,MagickPathExtent,token);
1678 GetNextToken(p,&p,MagickPathExtent,token);
1679 affine.tx=StringToDouble(token,&next_token);
1680 GetNextToken(p,&p,MagickPathExtent,token);
1682 GetNextToken(p,&p,MagickPathExtent,token);
1683 affine.ty=StringToDouble(token,&next_token);
1691 if (LocaleCompare(keyword,"rotate") == 0)
1696 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1697 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1698 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1699 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1700 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1708 if (LocaleCompare(keyword,"scale") == 0)
1710 for (p=(const char *) value; *p != '\0'; p++)
1711 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1714 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1715 affine.sy=affine.sx;
1718 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1719 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1722 if (LocaleCompare(keyword,"skewX") == 0)
1724 affine.sx=svg_info->affine.sx;
1725 affine.ry=tan(DegreesToRadians(fmod(
1726 GetUserSpaceCoordinateValue(svg_info,1,value),
1728 affine.sy=svg_info->affine.sy;
1731 if (LocaleCompare(keyword,"skewY") == 0)
1733 affine.sx=svg_info->affine.sx;
1734 affine.rx=tan(DegreesToRadians(fmod(
1735 GetUserSpaceCoordinateValue(svg_info,-1,value),
1737 affine.sy=svg_info->affine.sy;
1745 if (LocaleCompare(keyword,"translate") == 0)
1747 for (p=(const char *) value; *p != '\0'; p++)
1748 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1751 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1752 affine.ty=affine.tx;
1755 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1763 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1764 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1765 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1766 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1767 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1769 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1772 (void) FormatLocaleFile(svg_info->file,
1773 "affine %g %g %g %g %g %g\n",transform.sx,
1774 transform.rx,transform.ry,transform.sy,transform.tx,
1776 for (j=0; tokens[j] != (char *) NULL; j++)
1777 tokens[j]=DestroyString(tokens[j]);
1778 tokens=(char **) RelinquishMagickMemory(tokens);
1781 if (LocaleCompare(keyword,"gradientUnits") == 0)
1783 (void) CloneString(&units,value);
1784 (void) FormatLocaleFile(svg_info->file,"gradient-units \"%s\"\n",
1793 if (LocaleCompare(keyword,"height") == 0)
1795 svg_info->bounds.height=
1796 GetUserSpaceCoordinateValue(svg_info,-1,value);
1799 if (LocaleCompare(keyword,"href") == 0)
1801 (void) CloneString(&svg_info->url,value);
1809 if (LocaleCompare(keyword,"major") == 0)
1811 svg_info->element.major=
1812 GetUserSpaceCoordinateValue(svg_info,1,value);
1815 if (LocaleCompare(keyword,"mask") == 0)
1817 (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
1820 if (LocaleCompare(keyword,"minor") == 0)
1822 svg_info->element.minor=
1823 GetUserSpaceCoordinateValue(svg_info,-1,value);
1831 if (LocaleCompare(keyword,"offset") == 0)
1833 (void) CloneString(&svg_info->offset,value);
1836 if (LocaleCompare(keyword,"opacity") == 0)
1838 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
1846 if (LocaleCompare(keyword,"path") == 0)
1848 (void) CloneString(&svg_info->url,value);
1851 if (LocaleCompare(keyword,"points") == 0)
1853 (void) CloneString(&svg_info->vertices,value);
1861 if (LocaleCompare(keyword,"r") == 0)
1863 svg_info->element.major=
1864 GetUserSpaceCoordinateValue(svg_info,1,value);
1865 svg_info->element.minor=
1866 GetUserSpaceCoordinateValue(svg_info,-1,value);
1869 if (LocaleCompare(keyword,"rotate") == 0)
1874 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1875 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1876 svg_info->bounds.x,svg_info->bounds.y);
1877 svg_info->bounds.x=0;
1878 svg_info->bounds.y=0;
1879 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
1882 if (LocaleCompare(keyword,"rx") == 0)
1884 if (LocaleCompare((const char *) name,"ellipse") == 0)
1885 svg_info->element.major=
1886 GetUserSpaceCoordinateValue(svg_info,1,value);
1889 GetUserSpaceCoordinateValue(svg_info,1,value);
1892 if (LocaleCompare(keyword,"ry") == 0)
1894 if (LocaleCompare((const char *) name,"ellipse") == 0)
1895 svg_info->element.minor=
1896 GetUserSpaceCoordinateValue(svg_info,-1,value);
1899 GetUserSpaceCoordinateValue(svg_info,-1,value);
1907 if (LocaleCompare(keyword,"stop-color") == 0)
1909 (void) CloneString(&svg_info->stop_color,value);
1912 if (LocaleCompare(keyword,"stroke") == 0)
1914 if (LocaleCompare(value,"currentColor") == 0)
1916 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",
1920 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",value);
1923 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1925 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
1926 LocaleCompare(value,"true") == 0);
1929 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1931 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1935 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1937 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
1938 GetUserSpaceCoordinateValue(svg_info,1,value));
1941 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1943 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
1947 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1949 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
1953 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1955 (void) FormatLocaleFile(svg_info->file,
1956 "stroke-miterlimit \"%s\"\n",value);
1959 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1961 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
1965 if (LocaleCompare(keyword,"stroke-width") == 0)
1967 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
1968 GetUserSpaceCoordinateValue(svg_info,1,value));
1971 if (LocaleCompare(keyword,"style") == 0)
1973 SVGProcessStyleElement(context,name,value);
1981 if (LocaleCompare(keyword,"text-align") == 0)
1983 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",
1987 if (LocaleCompare(keyword,"text-anchor") == 0)
1989 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
1993 if (LocaleCompare(keyword,"text-decoration") == 0)
1995 if (LocaleCompare(value,"underline") == 0)
1996 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
1997 if (LocaleCompare(value,"line-through") == 0)
1998 (void) FormatLocaleFile(svg_info->file,
1999 "decorate line-through\n");
2000 if (LocaleCompare(value,"overline") == 0)
2001 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
2004 if (LocaleCompare(keyword,"text-antialiasing") == 0)
2006 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
2007 LocaleCompare(value,"true") == 0);
2010 if (LocaleCompare(keyword,"transform") == 0)
2017 GetAffineMatrix(&transform);
2018 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
2019 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
2020 if (tokens == (char **) NULL)
2022 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2024 keyword=(char *) tokens[j];
2025 value=(char *) tokens[j+1];
2026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2027 " %s: %s",keyword,value);
2029 GetAffineMatrix(&affine);
2035 if (LocaleCompare(keyword,"matrix") == 0)
2037 p=(const char *) value;
2038 GetNextToken(p,&p,MagickPathExtent,token);
2039 affine.sx=StringToDouble(value,(char **) NULL);
2040 GetNextToken(p,&p,MagickPathExtent,token);
2042 GetNextToken(p,&p,MagickPathExtent,token);
2043 affine.rx=StringToDouble(token,&next_token);
2044 GetNextToken(p,&p,MagickPathExtent,token);
2046 GetNextToken(p,&p,MagickPathExtent,token);
2047 affine.ry=StringToDouble(token,&next_token);
2048 GetNextToken(p,&p,MagickPathExtent,token);
2050 GetNextToken(p,&p,MagickPathExtent,token);
2051 affine.sy=StringToDouble(token,&next_token);
2052 GetNextToken(p,&p,MagickPathExtent,token);
2054 GetNextToken(p,&p,MagickPathExtent,token);
2055 affine.tx=StringToDouble(token,&next_token);
2056 GetNextToken(p,&p,MagickPathExtent,token);
2058 GetNextToken(p,&p,MagickPathExtent,token);
2059 affine.ty=StringToDouble(token,&next_token);
2067 if (LocaleCompare(keyword,"rotate") == 0)
2074 p=(const char *) value;
2075 GetNextToken(p,&p,MagickPathExtent,token);
2076 angle=StringToDouble(value,(char **) NULL);
2077 GetNextToken(p,&p,MagickPathExtent,token);
2079 GetNextToken(p,&p,MagickPathExtent,token);
2080 x=StringToDouble(token,&next_token);
2081 GetNextToken(p,&p,MagickPathExtent,token);
2083 GetNextToken(p,&p,MagickPathExtent,token);
2084 y=StringToDouble(token,&next_token);
2085 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
2086 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
2087 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
2088 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
2091 svg_info->center.x=x;
2092 svg_info->center.y=y;
2100 if (LocaleCompare(keyword,"scale") == 0)
2102 for (p=(const char *) value; *p != '\0'; p++)
2103 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2106 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
2107 affine.sy=affine.sx;
2109 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
2111 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
2114 if (LocaleCompare(keyword,"skewX") == 0)
2116 affine.sx=svg_info->affine.sx;
2117 affine.ry=tan(DegreesToRadians(fmod(
2118 GetUserSpaceCoordinateValue(svg_info,1,value),
2120 affine.sy=svg_info->affine.sy;
2123 if (LocaleCompare(keyword,"skewY") == 0)
2125 affine.sx=svg_info->affine.sx;
2126 affine.rx=tan(DegreesToRadians(fmod(
2127 GetUserSpaceCoordinateValue(svg_info,-1,value),
2129 affine.sy=svg_info->affine.sy;
2137 if (LocaleCompare(keyword,"translate") == 0)
2139 for (p=(const char *) value; *p != '\0'; p++)
2140 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2143 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2144 affine.ty=affine.tx;
2146 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2155 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2156 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2157 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2158 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2159 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2161 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2164 (void) FormatLocaleFile(svg_info->file,
2165 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2166 transform.ry,transform.sy,transform.tx,transform.ty);
2167 for (j=0; tokens[j] != (char *) NULL; j++)
2168 tokens[j]=DestroyString(tokens[j]);
2169 tokens=(char **) RelinquishMagickMemory(tokens);
2177 if (LocaleCompare(keyword,"verts") == 0)
2179 (void) CloneString(&svg_info->vertices,value);
2182 if (LocaleCompare(keyword,"viewBox") == 0)
2184 p=(const char *) value;
2185 GetNextToken(p,&p,MagickPathExtent,token);
2186 svg_info->view_box.x=StringToDouble(token,&next_token);
2187 GetNextToken(p,&p,MagickPathExtent,token);
2189 GetNextToken(p,&p,MagickPathExtent,token);
2190 svg_info->view_box.y=StringToDouble(token,&next_token);
2191 GetNextToken(p,&p,MagickPathExtent,token);
2193 GetNextToken(p,&p,MagickPathExtent,token);
2194 svg_info->view_box.width=StringToDouble(token,
2196 if (svg_info->bounds.width == 0)
2197 svg_info->bounds.width=svg_info->view_box.width;
2198 GetNextToken(p,&p,MagickPathExtent,token);
2200 GetNextToken(p,&p,MagickPathExtent,token);
2201 svg_info->view_box.height=StringToDouble(token,
2203 if (svg_info->bounds.height == 0)
2204 svg_info->bounds.height=svg_info->view_box.height;
2212 if (LocaleCompare(keyword,"width") == 0)
2214 svg_info->bounds.width=
2215 GetUserSpaceCoordinateValue(svg_info,1,value);
2223 if (LocaleCompare(keyword,"x") == 0)
2225 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2228 if (LocaleCompare(keyword,"xlink:href") == 0)
2230 (void) CloneString(&svg_info->url,value);
2233 if (LocaleCompare(keyword,"x1") == 0)
2235 svg_info->segment.x1=
2236 GetUserSpaceCoordinateValue(svg_info,1,value);
2239 if (LocaleCompare(keyword,"x2") == 0)
2241 svg_info->segment.x2=
2242 GetUserSpaceCoordinateValue(svg_info,1,value);
2250 if (LocaleCompare(keyword,"y") == 0)
2252 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2255 if (LocaleCompare(keyword,"y1") == 0)
2257 svg_info->segment.y1=
2258 GetUserSpaceCoordinateValue(svg_info,-1,value);
2261 if (LocaleCompare(keyword,"y2") == 0)
2263 svg_info->segment.y2=
2264 GetUserSpaceCoordinateValue(svg_info,-1,value);
2273 if (LocaleCompare((const char *) name,"svg") == 0)
2275 if (svg_info->document->encoding != (const xmlChar *) NULL)
2276 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2277 (const char *) svg_info->document->encoding);
2278 if (attributes != (const xmlChar **) NULL)
2286 if ((svg_info->view_box.width == 0.0) ||
2287 (svg_info->view_box.height == 0.0))
2288 svg_info->view_box=svg_info->bounds;
2290 if (svg_info->bounds.width > 0.0)
2291 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2293 if (svg_info->bounds.height > 0.0)
2294 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2295 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2296 (double) svg_info->width,(double) svg_info->height);
2297 sx=(double) svg_info->width/svg_info->view_box.width;
2298 sy=(double) svg_info->height/svg_info->view_box.height;
2299 tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2301 ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2303 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2305 if ((svg_info->svgDepth == 1) && (*background != '\0'))
2307 PushGraphicContext(id);
2308 (void) FormatLocaleFile(svg_info->file,"fill %s\n",background);
2309 (void) FormatLocaleFile(svg_info->file,
2310 "rectangle 0,0 %g,%g\n",svg_info->view_box.width,
2311 svg_info->view_box.height);
2312 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2316 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2317 units=DestroyString(units);
2318 if (color != (char *) NULL)
2319 color=DestroyString(color);
2322 static void SVGEndElement(void *context,const xmlChar *name)
2328 Called when the end of an element has been detected.
2330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2331 " SAX.endElement(%s)",name);
2332 svg_info=(SVGInfo *) context;
2333 if (strchr((char *) name,':') != (char *) NULL)
2336 Skip over namespace.
2338 for ( ; *name != ':'; name++) ;
2346 if (LocaleCompare((const char *) name,"circle") == 0)
2348 (void) FormatLocaleFile(svg_info->file,"class \"circle\"\n");
2349 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2350 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2351 svg_info->element.cy+svg_info->element.minor);
2352 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2355 if (LocaleCompare((const char *) name,"clipPath") == 0)
2357 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2365 if (LocaleCompare((const char *) name,"defs") == 0)
2367 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2370 if (LocaleCompare((const char *) name,"desc") == 0)
2375 if (*svg_info->text == '\0')
2377 (void) fputc('#',svg_info->file);
2378 for (p=svg_info->text; *p != '\0'; p++)
2380 (void) fputc(*p,svg_info->file);
2382 (void) fputc('#',svg_info->file);
2384 (void) fputc('\n',svg_info->file);
2385 *svg_info->text='\0';
2393 if (LocaleCompare((const char *) name,"ellipse") == 0)
2398 (void) FormatLocaleFile(svg_info->file,"class \"ellipse\"\n");
2399 angle=svg_info->element.angle;
2400 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2401 svg_info->element.cx,svg_info->element.cy,
2402 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2403 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2404 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2412 if (LocaleCompare((const char *) name,"foreignObject") == 0)
2414 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2422 if (LocaleCompare((const char *) name,"g") == 0)
2424 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2432 if (LocaleCompare((const char *) name,"image") == 0)
2434 (void) FormatLocaleFile(svg_info->file,
2435 "image Over %g,%g %g,%g \"%s\"\n",svg_info->bounds.x,
2436 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2438 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2446 if (LocaleCompare((const char *) name,"line") == 0)
2448 (void) FormatLocaleFile(svg_info->file,"class \"line\"\n");
2449 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2450 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2451 svg_info->segment.y2);
2452 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2455 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2457 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2465 if (LocaleCompare((const char *) name,"mask") == 0)
2467 (void) FormatLocaleFile(svg_info->file,"pop mask\n");
2475 if (LocaleCompare((const char *) name,"pattern") == 0)
2477 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2480 if (LocaleCompare((const char *) name,"path") == 0)
2482 (void) FormatLocaleFile(svg_info->file,"class \"path\"\n");
2483 (void) FormatLocaleFile(svg_info->file,"path \"%s\"\n",
2484 svg_info->vertices);
2485 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2488 if (LocaleCompare((const char *) name,"polygon") == 0)
2490 (void) FormatLocaleFile(svg_info->file,"class \"polygon\"\n");
2491 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2492 svg_info->vertices);
2493 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2496 if (LocaleCompare((const char *) name,"polyline") == 0)
2498 (void) FormatLocaleFile(svg_info->file,"class \"polyline\"\n");
2499 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2500 svg_info->vertices);
2501 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2509 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2511 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2514 if (LocaleCompare((const char *) name,"rect") == 0)
2516 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2518 (void) FormatLocaleFile(svg_info->file,"class \"rect\"\n");
2519 (void) FormatLocaleFile(svg_info->file,"rectangle %g,%g %g,%g\n",
2520 svg_info->bounds.x,svg_info->bounds.y,
2521 svg_info->bounds.x+svg_info->bounds.width,
2522 svg_info->bounds.y+svg_info->bounds.height);
2523 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2526 if (svg_info->radius.x == 0.0)
2527 svg_info->radius.x=svg_info->radius.y;
2528 if (svg_info->radius.y == 0.0)
2529 svg_info->radius.y=svg_info->radius.x;
2530 (void) FormatLocaleFile(svg_info->file,
2531 "roundRectangle %g,%g %g,%g %g,%g\n",
2532 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2533 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2534 svg_info->radius.x,svg_info->radius.y);
2535 svg_info->radius.x=0.0;
2536 svg_info->radius.y=0.0;
2537 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2545 if (LocaleCompare((const char *) name,"stop") == 0)
2547 (void) FormatLocaleFile(svg_info->file,"stop-color \"%s\" %s\n",
2548 svg_info->stop_color,svg_info->offset);
2551 if (LocaleCompare((char *) name,"style") == 0)
2565 Find style definitions in svg_info->text.
2567 tokens=SVGKeyValuePairs(context,'{','}',svg_info->text,
2569 if (tokens == (char **) NULL)
2571 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2573 keyword=(char *) tokens[j];
2574 value=(char *) tokens[j+1];
2575 (void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
2577 SVGProcessStyleElement(context,name,value);
2578 (void) FormatLocaleFile(svg_info->file,"pop class\n");
2582 if (LocaleCompare((const char *) name,"svg") == 0)
2584 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2585 svg_info->svgDepth--;
2588 if (LocaleCompare((const char *) name,"symbol") == 0)
2590 (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
2598 if (LocaleCompare((const char *) name,"text") == 0)
2600 if (*svg_info->text != '\0')
2605 (void) FormatLocaleFile(svg_info->file,"class \"text\"\n");
2606 text=EscapeString(svg_info->text,'\'');
2607 (void) FormatLocaleFile(svg_info->file,"text 0,0 \"%s\"\n",text);
2608 text=DestroyString(text);
2609 *svg_info->text='\0';
2611 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2614 if (LocaleCompare((const char *) name,"tspan") == 0)
2616 if (*svg_info->text != '\0')
2627 text=EscapeString(svg_info->text,'\'');
2628 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2629 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
2630 svg_info->center.y,text);
2631 text=DestroyString(text);
2632 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
2633 draw_info->pointsize=svg_info->pointsize;
2634 draw_info->text=AcquireString(svg_info->text);
2635 (void) ConcatenateString(&draw_info->text," ");
2636 (void) GetTypeMetrics(svg_info->image,draw_info,&metrics,
2637 svg_info->exception);
2638 svg_info->bounds.x+=metrics.width;
2639 draw_info=DestroyDrawInfo(draw_info);
2640 *svg_info->text='\0';
2642 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2645 if (LocaleCompare((const char *) name,"title") == 0)
2647 if (*svg_info->text == '\0')
2649 (void) CloneString(&svg_info->title,svg_info->text);
2650 *svg_info->text='\0';
2658 if (LocaleCompare((char *) name,"use") == 0)
2660 if ((svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0))
2661 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2662 svg_info->bounds.x,svg_info->bounds.y);
2663 (void) FormatLocaleFile(svg_info->file,"use \"url(%s)\"\n",
2665 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2673 *svg_info->text='\0';
2674 (void) memset(&svg_info->element,0,sizeof(svg_info->element));
2675 (void) memset(&svg_info->segment,0,sizeof(svg_info->segment));
2679 static void SVGCharacters(void *context,const xmlChar *c,int length)
2694 Receiving some characters from the parser.
2696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2697 " SAX.characters(%s,%.20g)",c,(double) length);
2698 svg_info=(SVGInfo *) context;
2699 text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2700 if (text == (char *) NULL)
2703 for (i=0; i < (ssize_t) length; i++)
2707 if (svg_info->text == (char *) NULL)
2708 svg_info->text=text;
2711 (void) ConcatenateString(&svg_info->text,text);
2712 text=DestroyString(text);
2716 static void SVGReference(void *context,const xmlChar *name)
2725 Called when an entity reference is detected.
2727 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2729 svg_info=(SVGInfo *) context;
2730 parser=svg_info->parser;
2731 if (parser == (xmlParserCtxtPtr) NULL)
2733 if (parser->node == (xmlNodePtr) NULL)
2736 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2738 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2741 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2747 Receiving some ignorable whitespaces from the parser.
2749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2750 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2751 svg_info=(SVGInfo *) context;
2755 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2756 const xmlChar *data)
2762 A processing instruction has been parsed.
2764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2765 " SAX.processingInstruction(%s, %s)",target,data);
2766 svg_info=(SVGInfo *) context;
2770 static void SVGComment(void *context,const xmlChar *value)
2776 A comment has been parsed.
2778 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
2780 svg_info=(SVGInfo *) context;
2781 if (svg_info->comment != (char *) NULL)
2782 (void) ConcatenateString(&svg_info->comment,"\n");
2783 (void) ConcatenateString(&svg_info->comment,(const char *) value);
2786 static void SVGWarning(void *context,const char *format,...)
2790 reason[MagickPathExtent];
2799 Display and format a warning messages, gives file, line, position and
2802 va_start(operands,format);
2803 svg_info=(SVGInfo *) context;
2804 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
2805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2806 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2807 (void) vsprintf(reason,format,operands);
2809 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2811 message=GetExceptionMessage(errno);
2812 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2813 DelegateWarning,reason,"`%s`",message);
2814 message=DestroyString(message);
2818 static void SVGError(void *context,const char *format,...)
2822 reason[MagickPathExtent];
2831 Display and format a error formats, gives file, line, position and
2834 va_start(operands,format);
2835 svg_info=(SVGInfo *) context;
2836 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
2837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2838 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2839 (void) vsprintf(reason,format,operands);
2841 (void) vsnprintf(reason,MagickPathExtent,format,operands);
2843 message=GetExceptionMessage(errno);
2844 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2845 reason,"`%s`",message);
2846 message=DestroyString(message);
2850 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2862 Called when a pcdata block has been parsed.
2864 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
2866 svg_info=(SVGInfo *) context;
2867 parser=svg_info->parser;
2868 child=xmlGetLastChild(parser->node);
2869 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2871 xmlTextConcat(child,value,length);
2874 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2877 static void SVGExternalSubset(void *context,const xmlChar *name,
2878 const xmlChar *external_id,const xmlChar *system_id)
2893 Does this document has an external subset?
2895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2896 " SAX.externalSubset(%s, %s, %s)",name,
2897 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2898 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2899 svg_info=(SVGInfo *) context;
2900 parser=svg_info->parser;
2901 if (((external_id == NULL) && (system_id == NULL)) ||
2902 ((parser->validate == 0) || (parser->wellFormed == 0) ||
2903 (svg_info->document == 0)))
2905 input=SVGResolveEntity(context,external_id,system_id);
2908 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2909 parser_context=(*parser);
2910 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2911 if (parser->inputTab == (xmlParserInputPtr *) NULL)
2913 parser->errNo=XML_ERR_NO_MEMORY;
2914 parser->input=parser_context.input;
2915 parser->inputNr=parser_context.inputNr;
2916 parser->inputMax=parser_context.inputMax;
2917 parser->inputTab=parser_context.inputTab;
2923 xmlPushInput(parser,input);
2924 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2925 if (input->filename == (char *) NULL)
2926 input->filename=(char *) xmlStrdup(system_id);
2929 input->base=parser->input->cur;
2930 input->cur=parser->input->cur;
2932 xmlParseExternalSubset(parser,external_id,system_id);
2933 while (parser->inputNr > 1)
2934 (void) xmlPopInput(parser);
2935 xmlFreeInputStream(parser->input);
2936 xmlFree(parser->inputTab);
2937 parser->input=parser_context.input;
2938 parser->inputNr=parser_context.inputNr;
2939 parser->inputMax=parser_context.inputMax;
2940 parser->inputTab=parser_context.inputTab;
2943 #if defined(__cplusplus) || defined(c_plusplus)
2948 Static declarations.
2951 SVGDensityGeometry[] = "96.0x96.0";
2954 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2957 filename[MagickPathExtent];
2977 message[MagickPathExtent];
2988 assert(image_info != (const ImageInfo *) NULL);
2989 assert(image_info->signature == MagickCoreSignature);
2990 assert(exception != (ExceptionInfo *) NULL);
2991 if (image_info->debug != MagickFalse)
2992 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2993 image_info->filename);
2994 assert(exception->signature == MagickCoreSignature);
2995 image=AcquireImage(image_info,exception);
2996 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2997 if (status == MagickFalse)
2999 image=DestroyImageList(image);
3000 return((Image *) NULL);
3002 if ((fabs(image->resolution.x) < MagickEpsilon) ||
3003 (fabs(image->resolution.y) < MagickEpsilon))
3011 flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3012 image->resolution.x=geometry_info.rho;
3013 image->resolution.y=geometry_info.sigma;
3014 if ((flags & SigmaValue) == 0)
3015 image->resolution.y=image->resolution.x;
3017 if (LocaleCompare(image_info->magick,"MSVG") != 0)
3022 delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception);
3023 if (delegate_info != (const DelegateInfo *) NULL)
3026 background[MagickPathExtent],
3027 command[MagickPathExtent],
3029 input_filename[MagickPathExtent],
3030 opacity[MagickPathExtent],
3031 output_filename[MagickPathExtent],
3032 unique[MagickPathExtent];
3041 Our best hope for compliance with the SVG standard.
3043 status=AcquireUniqueSymbolicLink(image->filename,input_filename);
3044 (void) AcquireUniqueFilename(output_filename);
3045 (void) AcquireUniqueFilename(unique);
3046 density=AcquireString("");
3047 (void) FormatLocaleString(density,MagickPathExtent,"%.20g,%.20g",
3048 image->resolution.x,image->resolution.y);
3049 (void) FormatLocaleString(background,MagickPathExtent,
3050 "rgb(%.20g%%,%.20g%%,%.20g%%)",
3051 100.0*QuantumScale*image->background_color.red,
3052 100.0*QuantumScale*image->background_color.green,
3053 100.0*QuantumScale*image->background_color.blue);
3054 (void) FormatLocaleString(opacity,MagickPathExtent,"%.20g",
3055 QuantumScale*image->background_color.alpha);
3056 (void) FormatLocaleString(command,MagickPathExtent,
3057 GetDelegateCommands(delegate_info),input_filename,output_filename,
3058 density,background,opacity,unique);
3059 density=DestroyString(density);
3060 status=ExternalDelegateCommand(MagickFalse,image_info->verbose,
3061 command,(char *) NULL,exception);
3062 (void) RelinquishUniqueFileResource(unique);
3063 (void) RelinquishUniqueFileResource(input_filename);
3064 if ((status == 0) && (stat(output_filename,&attributes) == 0) &&
3065 (attributes.st_size > 0))
3073 read_info=CloneImageInfo(image_info);
3074 (void) CopyMagickString(read_info->filename,output_filename,
3076 svg_image=ReadImage(read_info,exception);
3077 read_info=DestroyImageInfo(read_info);
3078 (void) RelinquishUniqueFileResource(output_filename);
3079 if (svg_image != (Image *) NULL)
3081 for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; )
3083 (void) CopyMagickString(next->filename,image->filename,
3085 (void) CopyMagickString(next->magick,image->magick,
3087 next=GetNextImageInList(next);
3089 image=DestroyImage(image);
3093 (void) RelinquishUniqueFileResource(output_filename);
3096 #if defined(MAGICKCORE_RSVG_DELEGATE)
3097 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3110 register unsigned char
3123 register const guchar
3145 svg_handle=rsvg_handle_new();
3146 if (svg_handle == (RsvgHandle *) NULL)
3147 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3148 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
3149 if ((fabs(image->resolution.x) > MagickEpsilon) &&
3150 (fabs(image->resolution.y) > MagickEpsilon))
3151 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3152 image->resolution.y);
3153 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3156 error=(GError *) NULL;
3157 (void) rsvg_handle_write(svg_handle,message,n,&error);
3158 if (error != (GError *) NULL)
3159 g_error_free(error);
3161 error=(GError *) NULL;
3162 rsvg_handle_close(svg_handle,&error);
3163 if (error != (GError *) NULL)
3164 g_error_free(error);
3165 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3166 apply_density=MagickTrue;
3167 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
3168 if ((image->resolution.x > 0.0) && (image->resolution.y > 0.0))
3174 We should not apply the density when the internal 'factor' is 'i'.
3175 This can be checked by using the trick below.
3177 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x*256,
3178 image->resolution.y*256);
3179 rsvg_handle_get_dimensions(svg_handle,&dpi_dimension_info);
3180 if ((dpi_dimension_info.width != dimension_info.width) ||
3181 (dpi_dimension_info.height != dimension_info.height))
3182 apply_density=MagickFalse;
3183 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3184 image->resolution.y);
3186 if (image_info->size != (char *) NULL)
3188 (void) GetGeometry(image_info->size,(ssize_t *) NULL,
3189 (ssize_t *) NULL,&image->columns,&image->rows);
3190 if ((image->columns != 0) || (image->rows != 0))
3192 image->resolution.x=96.0*image->columns/dimension_info.width;
3193 image->resolution.y=96.0*image->rows/dimension_info.height;
3194 if (fabs(image->resolution.x) < MagickEpsilon)
3195 image->resolution.x=image->resolution.y;
3197 if (fabs(image->resolution.y) < MagickEpsilon)
3198 image->resolution.y=image->resolution.x;
3200 image->resolution.x=image->resolution.y=MagickMin(
3201 image->resolution.x,image->resolution.y);
3202 apply_density=MagickTrue;
3205 if (apply_density != MagickFalse)
3207 image->columns=image->resolution.x*dimension_info.width/96.0;
3208 image->rows=image->resolution.y*dimension_info.height/96.0;
3212 image->columns=dimension_info.width;
3213 image->rows=dimension_info.height;
3215 pixel_info=(MemoryInfo *) NULL;
3217 pixel_buffer=rsvg_handle_get_pixbuf(svg_handle);
3218 rsvg_handle_free(svg_handle);
3219 image->columns=gdk_pixbuf_get_width(pixel_buffer);
3220 image->rows=gdk_pixbuf_get_height(pixel_buffer);
3222 image->alpha_trait=BlendPixelTrait;
3223 status=SetImageExtent(image,image->columns,image->rows,exception);
3224 if (status == MagickFalse)
3226 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
3227 g_object_unref(G_OBJECT(pixel_buffer));
3229 g_object_unref(svg_handle);
3230 ThrowReaderException(MissingDelegateError,
3231 "NoDecodeDelegateForThisImageFormat");
3233 if (image_info->ping == MagickFalse)
3235 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3239 stride=4*image->columns;
3240 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
3241 stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
3242 (int) image->columns);
3244 pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels));
3245 if (pixel_info == (MemoryInfo *) NULL)
3247 g_object_unref(svg_handle);
3248 ThrowReaderException(ResourceLimitError,
3249 "MemoryAllocationFailed");
3251 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3253 (void) SetImageBackgroundColor(image,exception);
3254 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3255 cairo_surface=cairo_image_surface_create_for_data(pixels,
3256 CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int)
3258 if ((cairo_surface == (cairo_surface_t *) NULL) ||
3259 (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS))
3261 if (cairo_surface != (cairo_surface_t *) NULL)
3262 cairo_surface_destroy(cairo_surface);
3263 pixel_info=RelinquishVirtualMemory(pixel_info);
3264 g_object_unref(svg_handle);
3265 ThrowReaderException(ResourceLimitError,
3266 "MemoryAllocationFailed");
3268 cairo_image=cairo_create(cairo_surface);
3269 cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
3270 cairo_paint(cairo_image);
3271 cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
3272 if (apply_density != MagickFalse)
3273 cairo_scale(cairo_image,image->resolution.x/96.0,
3274 image->resolution.y/96.0);
3275 rsvg_handle_render_cairo(svg_handle,cairo_image);
3276 cairo_destroy(cairo_image);
3277 cairo_surface_destroy(cairo_surface);
3278 g_object_unref(svg_handle);
3281 p=gdk_pixbuf_get_pixels(pixel_buffer);
3283 GetPixelInfo(image,&fill_color);
3284 for (y=0; y < (ssize_t) image->rows; y++)
3286 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3287 if (q == (Quantum *) NULL)
3289 for (x=0; x < (ssize_t) image->columns; x++)
3291 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3292 fill_color.blue=ScaleCharToQuantum(*p++);
3293 fill_color.green=ScaleCharToQuantum(*p++);
3294 fill_color.red=ScaleCharToQuantum(*p++);
3296 fill_color.red=ScaleCharToQuantum(*p++);
3297 fill_color.green=ScaleCharToQuantum(*p++);
3298 fill_color.blue=ScaleCharToQuantum(*p++);
3300 fill_color.alpha=ScaleCharToQuantum(*p++);
3301 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3306 gamma=QuantumScale*fill_color.alpha;
3307 gamma=PerceptibleReciprocal(gamma);
3308 fill_color.blue*=gamma;
3309 fill_color.green*=gamma;
3310 fill_color.red*=gamma;
3313 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
3314 GetPixelAlpha(image,q),q);
3315 q+=GetPixelChannels(image);
3317 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3319 if (image->previous == (Image *) NULL)
3321 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
3323 if (status == MagickFalse)
3328 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3329 if (pixel_info != (MemoryInfo *) NULL)
3330 pixel_info=RelinquishVirtualMemory(pixel_info);
3332 g_object_unref(G_OBJECT(pixel_buffer));
3334 (void) CloseBlob(image);
3335 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3337 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3338 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3339 next=GetNextImageInList(next);
3341 return(GetFirstImageInList(image));
3349 unique_file=AcquireUniqueFileResource(filename);
3350 if (unique_file != -1)
3351 file=fdopen(unique_file,"w");
3352 if ((unique_file == -1) || (file == (FILE *) NULL))
3354 (void) CopyMagickString(image->filename,filename,MagickPathExtent);
3355 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
3357 image=DestroyImageList(image);
3358 return((Image *) NULL);
3363 svg_info=AcquireSVGInfo();
3364 if (svg_info == (SVGInfo *) NULL)
3366 (void) fclose(file);
3367 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3369 svg_info->file=file;
3370 svg_info->exception=exception;
3371 svg_info->image=image;
3372 svg_info->image_info=image_info;
3373 svg_info->bounds.width=image->columns;
3374 svg_info->bounds.height=image->rows;
3375 svg_info->svgDepth=0;
3376 if (image_info->size != (char *) NULL)
3377 (void) CloneString(&svg_info->size,image_info->size);
3378 if (image->debug != MagickFalse)
3379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3380 (void) xmlSubstituteEntitiesDefault(1);
3381 (void) memset(&sax_modules,0,sizeof(sax_modules));
3382 sax_modules.internalSubset=SVGInternalSubset;
3383 sax_modules.isStandalone=SVGIsStandalone;
3384 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3385 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3386 sax_modules.resolveEntity=SVGResolveEntity;
3387 sax_modules.getEntity=SVGGetEntity;
3388 sax_modules.entityDecl=SVGEntityDeclaration;
3389 sax_modules.notationDecl=SVGNotationDeclaration;
3390 sax_modules.attributeDecl=SVGAttributeDeclaration;
3391 sax_modules.elementDecl=SVGElementDeclaration;
3392 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3393 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3394 sax_modules.startDocument=SVGStartDocument;
3395 sax_modules.endDocument=SVGEndDocument;
3396 sax_modules.startElement=SVGStartElement;
3397 sax_modules.endElement=SVGEndElement;
3398 sax_modules.reference=SVGReference;
3399 sax_modules.characters=SVGCharacters;
3400 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3401 sax_modules.processingInstruction=SVGProcessingInstructions;
3402 sax_modules.comment=SVGComment;
3403 sax_modules.warning=SVGWarning;
3404 sax_modules.error=SVGError;
3405 sax_modules.fatalError=SVGError;
3406 sax_modules.getParameterEntity=SVGGetParameterEntity;
3407 sax_modules.cdataBlock=SVGCDataBlock;
3408 sax_modules.externalSubset=SVGExternalSubset;
3409 sax_handler=(&sax_modules);
3410 n=ReadBlob(image,MagickPathExtent-1,message);
3414 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3415 message,n,image->filename);
3416 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3419 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3424 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3425 SVGEndDocument(svg_info);
3426 xmlFreeParserCtxt(svg_info->parser);
3427 if (image->debug != MagickFalse)
3428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3429 (void) fclose(file);
3430 (void) CloseBlob(image);
3431 image->columns=svg_info->width;
3432 image->rows=svg_info->height;
3433 if (exception->severity >= ErrorException)
3435 svg_info=DestroySVGInfo(svg_info);
3436 (void) RelinquishUniqueFileResource(filename);
3437 image=DestroyImage(image);
3438 return((Image *) NULL);
3440 if (image_info->ping == MagickFalse)
3448 image=DestroyImage(image);
3449 image=(Image *) NULL;
3450 read_info=CloneImageInfo(image_info);
3451 SetImageInfoBlob(read_info,(void *) NULL,0);
3452 if (read_info->density != (char *) NULL)
3453 read_info->density=DestroyString(read_info->density);
3454 (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
3456 image=ReadImage(read_info,exception);
3457 read_info=DestroyImageInfo(read_info);
3458 if (image != (Image *) NULL)
3459 (void) CopyMagickString(image->filename,image_info->filename,
3463 Relinquish resources.
3465 if (image != (Image *) NULL)
3467 if (svg_info->title != (char *) NULL)
3468 (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3469 if (svg_info->comment != (char *) NULL)
3470 (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3473 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3475 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3476 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3477 next=GetNextImageInList(next);
3479 svg_info=DestroySVGInfo(svg_info);
3480 (void) RelinquishUniqueFileResource(filename);
3481 return(GetFirstImageInList(image));
3486 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3490 % R e g i s t e r S V G I m a g e %
3494 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3496 % RegisterSVGImage() adds attributes for the SVG image format to
3497 % the list of supported formats. The attributes include the image format
3498 % tag, a method to read and/or write the format, whether the format
3499 % supports the saving of more than one frame to the same file or blob,
3500 % whether the format supports native in-memory I/O, and a brief
3501 % description of the format.
3503 % The format of the RegisterSVGImage method is:
3505 % size_t RegisterSVGImage(void)
3508 ModuleExport size_t RegisterSVGImage(void)
3511 version[MagickPathExtent];
3517 #if defined(LIBXML_DOTTED_VERSION)
3518 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,
3521 #if defined(MAGICKCORE_RSVG_DELEGATE)
3522 #if !GLIB_CHECK_VERSION(2,35,0)
3525 #if defined(MAGICKCORE_XML_DELEGATE)
3528 (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d",
3529 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3531 entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics");
3532 #if defined(MAGICKCORE_XML_DELEGATE)
3533 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3535 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3536 entry->flags^=CoderBlobSupportFlag;
3537 #if defined(MAGICKCORE_RSVG_DELEGATE)
3538 entry->flags^=CoderDecoderThreadSupportFlag;
3540 entry->mime_type=ConstantString("image/svg+xml");
3541 if (*version != '\0')
3542 entry->version=ConstantString(version);
3543 entry->magick=(IsImageFormatHandler *) IsSVG;
3544 (void) RegisterMagickInfo(entry);
3545 entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics");
3546 #if defined(MAGICKCORE_XML_DELEGATE)
3547 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3549 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3550 entry->flags^=CoderBlobSupportFlag;
3551 #if defined(MAGICKCORE_RSVG_DELEGATE)
3552 entry->flags^=CoderDecoderThreadSupportFlag;
3554 entry->mime_type=ConstantString("image/svg+xml");
3555 if (*version != '\0')
3556 entry->version=ConstantString(version);
3557 entry->magick=(IsImageFormatHandler *) IsSVG;
3558 (void) RegisterMagickInfo(entry);
3559 entry=AcquireMagickInfo("SVG","MSVG",
3560 "ImageMagick's own SVG internal renderer");
3561 #if defined(MAGICKCORE_XML_DELEGATE)
3562 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3564 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3565 entry->flags^=CoderBlobSupportFlag;
3566 #if defined(MAGICKCORE_RSVG_DELEGATE)
3567 entry->flags^=CoderDecoderThreadSupportFlag;
3569 entry->magick=(IsImageFormatHandler *) IsSVG;
3570 (void) RegisterMagickInfo(entry);
3571 return(MagickImageCoderSignature);
3575 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3579 % U n r e g i s t e r S V G I m a g e %
3583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3585 % UnregisterSVGImage() removes format registrations made by the
3586 % SVG module from the list of supported formats.
3588 % The format of the UnregisterSVGImage method is:
3590 % UnregisterSVGImage(void)
3593 ModuleExport void UnregisterSVGImage(void)
3595 (void) UnregisterMagickInfo("SVGZ");
3596 (void) UnregisterMagickInfo("SVG");
3597 (void) UnregisterMagickInfo("MSVG");
3598 #if defined(MAGICKCORE_XML_DELEGATE)
3604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3608 % W r i t e S V G I m a g e %
3612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3614 % WriteSVGImage() writes a image in the SVG - XML based W3C standard
3617 % The format of the WriteSVGImage method is:
3619 % MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3620 % Image *image,ExceptionInfo *exception)
3622 % A description of each parameter follows.
3624 % o image_info: the image info.
3626 % o image: The image.
3628 % o exception: return any errors or warnings in this structure.
3632 static void AffineToTransform(Image *image,AffineMatrix *affine)
3635 transform[MagickPathExtent];
3637 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3639 if ((fabs(affine->rx) < MagickEpsilon) &&
3640 (fabs(affine->ry) < MagickEpsilon))
3642 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3643 (fabs(affine->sy-1.0) < MagickEpsilon))
3645 (void) WriteBlobString(image,"\">\n");
3648 (void) FormatLocaleString(transform,MagickPathExtent,
3649 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3650 (void) WriteBlobString(image,transform);
3655 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3656 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3657 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3663 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3664 (void) FormatLocaleString(transform,MagickPathExtent,
3665 "\" transform=\"rotate(%g)\">\n",theta);
3666 (void) WriteBlobString(image,transform);
3673 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3674 (fabs(affine->rx) < MagickEpsilon) &&
3675 (fabs(affine->ry) < MagickEpsilon) &&
3676 (fabs(affine->sy-1.0) < MagickEpsilon))
3678 (void) FormatLocaleString(transform,MagickPathExtent,
3679 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3680 (void) WriteBlobString(image,transform);
3684 (void) FormatLocaleString(transform,MagickPathExtent,
3685 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3686 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3687 (void) WriteBlobString(image,transform);
3690 static MagickBooleanType IsPoint(const char *point)
3698 value=strtol(point,&p,10);
3700 return(p != point ? MagickTrue : MagickFalse);
3703 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
3705 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3710 at_fitting_opts_type
3722 register const Quantum
3736 Trace image and write as SVG.
3738 fitting_options=at_fitting_opts_new();
3739 output_options=at_output_opts_new();
3740 (void) SetImageGray(image,exception);
3741 type=GetImageType(image);
3743 if ((type == BilevelType) || (type == GrayscaleType))
3745 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3747 for (y=0; y < (ssize_t) image->rows; y++)
3749 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3750 if (p == (const Quantum *) NULL)
3752 for (x=0; x < (ssize_t) image->columns; x++)
3754 trace->bitmap[i++]=GetPixelRed(image,p);
3755 if (number_planes == 3)
3757 trace->bitmap[i++]=GetPixelGreen(image,p);
3758 trace->bitmap[i++]=GetPixelBlue(image,p);
3760 p+=GetPixelChannels(image);
3763 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3765 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3766 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3771 at_splines_free(splines);
3772 at_bitmap_free(trace);
3773 at_output_opts_free(output_options);
3774 at_fitting_opts_free(fitting_options);
3780 message[MagickPathExtent];
3801 (void) WriteBlobString(image,
3802 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
3803 (void) WriteBlobString(image,
3804 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
3805 (void) WriteBlobString(image,
3806 " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
3807 (void) FormatLocaleString(message,MagickPathExtent,
3808 "<svg version=\"1.1\" id=\"Layer_1\" "
3809 "xmlns=\"http://www.w3.org/2000/svg\" "
3810 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" "
3811 "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" "
3812 "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">",
3813 (double) image->columns,(double) image->rows,
3814 (double) image->columns,(double) image->rows,
3815 (double) image->columns,(double) image->rows);
3816 (void) WriteBlobString(image,message);
3817 clone_image=CloneImage(image,0,0,MagickTrue,exception);
3818 if (clone_image == (Image *) NULL)
3819 return(MagickFalse);
3820 image_info=AcquireImageInfo();
3821 (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent);
3823 blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length,
3825 clone_image=DestroyImage(clone_image);
3826 image_info=DestroyImageInfo(image_info);
3827 if (blob == (unsigned char *) NULL)
3828 return(MagickFalse);
3830 base64=Base64Encode(blob,blob_length,&encode_length);
3831 blob=(unsigned char *) RelinquishMagickMemory(blob);
3832 (void) FormatLocaleString(message,MagickPathExtent,
3833 " <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" "
3834 "x=\"%.20g\" y=\"%.20g\"\n href=\"data:image/png;base64,",
3835 (double) image->scene,(double) image->columns,(double) image->rows,
3836 (double) image->page.x,(double) image->page.y);
3837 (void) WriteBlobString(image,message);
3839 for (i=(ssize_t) encode_length; i > 0; i-=76)
3841 (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p);
3842 (void) WriteBlobString(image,message);
3845 (void) WriteBlobString(image,"\n");
3847 base64=DestroyString(base64);
3848 (void) WriteBlobString(image,"\" />\n");
3849 (void) WriteBlobString(image,"</svg>\n");
3856 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
3857 ExceptionInfo *exception)
3859 #define BezierQuantum 200
3865 keyword[MagickPathExtent],
3866 message[MagickPathExtent],
3867 name[MagickPathExtent],
3870 type[MagickPathExtent];
3911 Open output image file.
3913 assert(image_info != (const ImageInfo *) NULL);
3914 assert(image_info->signature == MagickCoreSignature);
3915 assert(image != (Image *) NULL);
3916 assert(image->signature == MagickCoreSignature);
3917 if (image->debug != MagickFalse)
3918 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3919 assert(exception != (ExceptionInfo *) NULL);
3920 assert(exception->signature == MagickCoreSignature);
3921 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3922 if (status == MagickFalse)
3924 value=GetImageArtifact(image,"SVG");
3925 if (value != (char *) NULL)
3927 (void) WriteBlobString(image,value);
3928 (void) CloseBlob(image);
3931 value=GetImageArtifact(image,"MVG");
3932 if (value == (char *) NULL)
3933 return(TraceSVGImage(image,exception));
3937 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3938 (void) WriteBlobString(image,
3939 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3940 (void) WriteBlobString(image,
3941 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3942 (void) FormatLocaleString(message,MagickPathExtent,
3943 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3945 (void) WriteBlobString(image,message);
3947 Allocate primitive info memory.
3950 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3951 sizeof(*primitive_info));
3952 if (primitive_info == (PrimitiveInfo *) NULL)
3953 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3954 GetAffineMatrix(&affine);
3955 token=AcquireString(value);
3956 extent=strlen(token)+MagickPathExtent;
3960 for (q=(const char *) value; *q != '\0'; )
3963 Interpret graphic primitive.
3965 GetNextToken(q,&q,MagickPathExtent,keyword);
3966 if (*keyword == '\0')
3968 if (*keyword == '#')
3973 if (active != MagickFalse)
3975 AffineToTransform(image,&affine);
3978 (void) WriteBlobString(image,"<desc>");
3979 (void) WriteBlobString(image,keyword+1);
3980 for ( ; (*q != '\n') && (*q != '\0'); q++)
3983 case '<': (void) WriteBlobString(image,"<"); break;
3984 case '>': (void) WriteBlobString(image,">"); break;
3985 case '&': (void) WriteBlobString(image,"&"); break;
3986 default: (void) WriteBlobByte(image,*q); break;
3988 (void) WriteBlobString(image,"</desc>\n");
3991 primitive_type=UndefinedPrimitive;
3999 if (LocaleCompare("affine",keyword) == 0)
4001 GetNextToken(q,&q,extent,token);
4002 affine.sx=StringToDouble(token,&next_token);
4003 GetNextToken(q,&q,extent,token);
4005 GetNextToken(q,&q,extent,token);
4006 affine.rx=StringToDouble(token,&next_token);
4007 GetNextToken(q,&q,extent,token);
4009 GetNextToken(q,&q,extent,token);
4010 affine.ry=StringToDouble(token,&next_token);
4011 GetNextToken(q,&q,extent,token);
4013 GetNextToken(q,&q,extent,token);
4014 affine.sy=StringToDouble(token,&next_token);
4015 GetNextToken(q,&q,extent,token);
4017 GetNextToken(q,&q,extent,token);
4018 affine.tx=StringToDouble(token,&next_token);
4019 GetNextToken(q,&q,extent,token);
4021 GetNextToken(q,&q,extent,token);
4022 affine.ty=StringToDouble(token,&next_token);
4025 if (LocaleCompare("alpha",keyword) == 0)
4027 primitive_type=AlphaPrimitive;
4030 if (LocaleCompare("angle",keyword) == 0)
4032 GetNextToken(q,&q,extent,token);
4033 affine.rx=StringToDouble(token,&next_token);
4034 affine.ry=StringToDouble(token,&next_token);
4037 if (LocaleCompare("arc",keyword) == 0)
4039 primitive_type=ArcPrimitive;
4048 if (LocaleCompare("bezier",keyword) == 0)
4050 primitive_type=BezierPrimitive;
4059 if (LocaleCompare("clip-path",keyword) == 0)
4061 GetNextToken(q,&q,extent,token);
4062 (void) FormatLocaleString(message,MagickPathExtent,
4063 "clip-path:url(#%s);",token);
4064 (void) WriteBlobString(image,message);
4067 if (LocaleCompare("clip-rule",keyword) == 0)
4069 GetNextToken(q,&q,extent,token);
4070 (void) FormatLocaleString(message,MagickPathExtent,"clip-rule:%s;",
4072 (void) WriteBlobString(image,message);
4075 if (LocaleCompare("clip-units",keyword) == 0)
4077 GetNextToken(q,&q,extent,token);
4078 (void) FormatLocaleString(message,MagickPathExtent,
4079 "clipPathUnits=%s;",token);
4080 (void) WriteBlobString(image,message);
4083 if (LocaleCompare("circle",keyword) == 0)
4085 primitive_type=CirclePrimitive;
4088 if (LocaleCompare("color",keyword) == 0)
4090 primitive_type=ColorPrimitive;
4099 if (LocaleCompare("decorate",keyword) == 0)
4101 GetNextToken(q,&q,extent,token);
4102 (void) FormatLocaleString(message,MagickPathExtent,
4103 "text-decoration:%s;",token);
4104 (void) WriteBlobString(image,message);
4113 if (LocaleCompare("ellipse",keyword) == 0)
4115 primitive_type=EllipsePrimitive;
4124 if (LocaleCompare("fill",keyword) == 0)
4126 GetNextToken(q,&q,extent,token);
4127 (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;",
4129 (void) WriteBlobString(image,message);
4132 if (LocaleCompare("fill-rule",keyword) == 0)
4134 GetNextToken(q,&q,extent,token);
4135 (void) FormatLocaleString(message,MagickPathExtent,
4136 "fill-rule:%s;",token);
4137 (void) WriteBlobString(image,message);
4140 if (LocaleCompare("fill-opacity",keyword) == 0)
4142 GetNextToken(q,&q,extent,token);
4143 (void) FormatLocaleString(message,MagickPathExtent,
4144 "fill-opacity:%s;",token);
4145 (void) WriteBlobString(image,message);
4148 if (LocaleCompare("font-family",keyword) == 0)
4150 GetNextToken(q,&q,extent,token);
4151 (void) FormatLocaleString(message,MagickPathExtent,
4152 "font-family:%s;",token);
4153 (void) WriteBlobString(image,message);
4156 if (LocaleCompare("font-stretch",keyword) == 0)
4158 GetNextToken(q,&q,extent,token);
4159 (void) FormatLocaleString(message,MagickPathExtent,
4160 "font-stretch:%s;",token);
4161 (void) WriteBlobString(image,message);
4164 if (LocaleCompare("font-style",keyword) == 0)
4166 GetNextToken(q,&q,extent,token);
4167 (void) FormatLocaleString(message,MagickPathExtent,
4168 "font-style:%s;",token);
4169 (void) WriteBlobString(image,message);
4172 if (LocaleCompare("font-size",keyword) == 0)
4174 GetNextToken(q,&q,extent,token);
4175 (void) FormatLocaleString(message,MagickPathExtent,
4176 "font-size:%s;",token);
4177 (void) WriteBlobString(image,message);
4180 if (LocaleCompare("font-weight",keyword) == 0)
4182 GetNextToken(q,&q,extent,token);
4183 (void) FormatLocaleString(message,MagickPathExtent,
4184 "font-weight:%s;",token);
4185 (void) WriteBlobString(image,message);
4194 if (LocaleCompare("gradient-units",keyword) == 0)
4196 GetNextToken(q,&q,extent,token);
4199 if (LocaleCompare("text-align",keyword) == 0)
4201 GetNextToken(q,&q,extent,token);
4202 (void) FormatLocaleString(message,MagickPathExtent,
4203 "text-align %s ",token);
4204 (void) WriteBlobString(image,message);
4207 if (LocaleCompare("text-anchor",keyword) == 0)
4209 GetNextToken(q,&q,extent,token);
4210 (void) FormatLocaleString(message,MagickPathExtent,
4211 "text-anchor %s ",token);
4212 (void) WriteBlobString(image,message);
4221 if (LocaleCompare("image",keyword) == 0)
4223 GetNextToken(q,&q,extent,token);
4224 primitive_type=ImagePrimitive;
4233 if (LocaleCompare("line",keyword) == 0)
4235 primitive_type=LinePrimitive;
4244 if (LocaleCompare("opacity",keyword) == 0)
4246 GetNextToken(q,&q,extent,token);
4247 (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ",
4249 (void) WriteBlobString(image,message);
4258 if (LocaleCompare("path",keyword) == 0)
4260 primitive_type=PathPrimitive;
4263 if (LocaleCompare("point",keyword) == 0)
4265 primitive_type=PointPrimitive;
4268 if (LocaleCompare("polyline",keyword) == 0)
4270 primitive_type=PolylinePrimitive;
4273 if (LocaleCompare("polygon",keyword) == 0)
4275 primitive_type=PolygonPrimitive;
4278 if (LocaleCompare("pop",keyword) == 0)
4280 GetNextToken(q,&q,extent,token);
4281 if (LocaleCompare("clip-path",token) == 0)
4283 (void) WriteBlobString(image,"</clipPath>\n");
4286 if (LocaleCompare("defs",token) == 0)
4288 (void) WriteBlobString(image,"</defs>\n");
4291 if (LocaleCompare("gradient",token) == 0)
4293 (void) FormatLocaleString(message,MagickPathExtent,
4294 "</%sGradient>\n",type);
4295 (void) WriteBlobString(image,message);
4298 if (LocaleCompare("graphic-context",token) == 0)
4302 ThrowWriterException(DrawError,
4303 "UnbalancedGraphicContextPushPop");
4304 (void) WriteBlobString(image,"</g>\n");
4306 if (LocaleCompare("pattern",token) == 0)
4308 (void) WriteBlobString(image,"</pattern>\n");
4311 if (LocaleCompare("symbol",token) == 0)
4313 (void) WriteBlobString(image,"</symbol>\n");
4316 if ((LocaleCompare("defs",token) == 0) ||
4317 (LocaleCompare("symbol",token) == 0))
4318 (void) WriteBlobString(image,"</g>\n");
4321 if (LocaleCompare("push",keyword) == 0)
4323 GetNextToken(q,&q,extent,token);
4324 if (LocaleCompare("clip-path",token) == 0)
4326 GetNextToken(q,&q,extent,token);
4327 (void) FormatLocaleString(message,MagickPathExtent,
4328 "<clipPath id=\"%s\">\n",token);
4329 (void) WriteBlobString(image,message);
4332 if (LocaleCompare("defs",token) == 0)
4334 (void) WriteBlobString(image,"<defs>\n");
4337 if (LocaleCompare("gradient",token) == 0)
4339 GetNextToken(q,&q,extent,token);
4340 (void) CopyMagickString(name,token,MagickPathExtent);
4341 GetNextToken(q,&q,extent,token);
4342 (void) CopyMagickString(type,token,MagickPathExtent);
4343 GetNextToken(q,&q,extent,token);
4344 svg_info.segment.x1=StringToDouble(token,&next_token);
4345 svg_info.element.cx=StringToDouble(token,&next_token);
4346 GetNextToken(q,&q,extent,token);
4348 GetNextToken(q,&q,extent,token);
4349 svg_info.segment.y1=StringToDouble(token,&next_token);
4350 svg_info.element.cy=StringToDouble(token,&next_token);
4351 GetNextToken(q,&q,extent,token);
4353 GetNextToken(q,&q,extent,token);
4354 svg_info.segment.x2=StringToDouble(token,&next_token);
4355 svg_info.element.major=StringToDouble(token,
4357 GetNextToken(q,&q,extent,token);
4359 GetNextToken(q,&q,extent,token);
4360 svg_info.segment.y2=StringToDouble(token,&next_token);
4361 svg_info.element.minor=StringToDouble(token,
4363 (void) FormatLocaleString(message,MagickPathExtent,
4364 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
4365 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
4366 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
4367 if (LocaleCompare(type,"radial") == 0)
4369 GetNextToken(q,&q,extent,token);
4371 GetNextToken(q,&q,extent,token);
4372 svg_info.element.angle=StringToDouble(token,
4374 (void) FormatLocaleString(message,MagickPathExtent,
4375 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
4376 "fx=\"%g\" fy=\"%g\">\n",type,name,
4377 svg_info.element.cx,svg_info.element.cy,
4378 svg_info.element.angle,svg_info.element.major,
4379 svg_info.element.minor);
4381 (void) WriteBlobString(image,message);
4384 if (LocaleCompare("graphic-context",token) == 0)
4389 AffineToTransform(image,&affine);
4392 (void) WriteBlobString(image,"<g style=\"");
4395 if (LocaleCompare("pattern",token) == 0)
4397 GetNextToken(q,&q,extent,token);
4398 (void) CopyMagickString(name,token,MagickPathExtent);
4399 GetNextToken(q,&q,extent,token);
4400 svg_info.bounds.x=StringToDouble(token,&next_token);
4401 GetNextToken(q,&q,extent,token);
4403 GetNextToken(q,&q,extent,token);
4404 svg_info.bounds.y=StringToDouble(token,&next_token);
4405 GetNextToken(q,&q,extent,token);
4407 GetNextToken(q,&q,extent,token);
4408 svg_info.bounds.width=StringToDouble(token,
4410 GetNextToken(q,&q,extent,token);
4412 GetNextToken(q,&q,extent,token);
4413 svg_info.bounds.height=StringToDouble(token,(char **) NULL);
4414 (void) FormatLocaleString(message,MagickPathExtent,
4415 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
4416 "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y,
4417 svg_info.bounds.width,svg_info.bounds.height);
4418 (void) WriteBlobString(image,message);
4421 if (LocaleCompare("symbol",token) == 0)
4423 (void) WriteBlobString(image,"<symbol>\n");
4434 if (LocaleCompare("rectangle",keyword) == 0)
4436 primitive_type=RectanglePrimitive;
4439 if (LocaleCompare("roundRectangle",keyword) == 0)
4441 primitive_type=RoundRectanglePrimitive;
4444 if (LocaleCompare("rotate",keyword) == 0)
4446 GetNextToken(q,&q,extent,token);
4447 (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ",
4449 (void) WriteBlobString(image,message);
4458 if (LocaleCompare("scale",keyword) == 0)
4460 GetNextToken(q,&q,extent,token);
4461 affine.sx=StringToDouble(token,&next_token);
4462 GetNextToken(q,&q,extent,token);
4464 GetNextToken(q,&q,extent,token);
4465 affine.sy=StringToDouble(token,&next_token);
4468 if (LocaleCompare("skewX",keyword) == 0)
4470 GetNextToken(q,&q,extent,token);
4471 (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ",
4473 (void) WriteBlobString(image,message);
4476 if (LocaleCompare("skewY",keyword) == 0)
4478 GetNextToken(q,&q,extent,token);
4479 (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ",
4481 (void) WriteBlobString(image,message);
4484 if (LocaleCompare("stop-color",keyword) == 0)
4487 color[MagickPathExtent];
4489 GetNextToken(q,&q,extent,token);
4490 (void) CopyMagickString(color,token,MagickPathExtent);
4491 GetNextToken(q,&q,extent,token);
4492 (void) FormatLocaleString(message,MagickPathExtent,
4493 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4494 (void) WriteBlobString(image,message);
4497 if (LocaleCompare("stroke",keyword) == 0)
4499 GetNextToken(q,&q,extent,token);
4500 (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;",
4502 (void) WriteBlobString(image,message);
4505 if (LocaleCompare("stroke-antialias",keyword) == 0)
4507 GetNextToken(q,&q,extent,token);
4508 (void) FormatLocaleString(message,MagickPathExtent,
4509 "stroke-antialias:%s;",token);
4510 (void) WriteBlobString(image,message);
4513 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4521 GetNextToken(p,&p,extent,token);
4522 for (k=0; IsPoint(token); k++)
4523 GetNextToken(p,&p,extent,token);
4524 (void) WriteBlobString(image,"stroke-dasharray:");
4525 for (j=0; j < k; j++)
4527 GetNextToken(q,&q,extent,token);
4528 (void) FormatLocaleString(message,MagickPathExtent,"%s ",
4530 (void) WriteBlobString(image,message);
4532 (void) WriteBlobString(image,";");
4535 GetNextToken(q,&q,extent,token);
4536 (void) FormatLocaleString(message,MagickPathExtent,
4537 "stroke-dasharray:%s;",token);
4538 (void) WriteBlobString(image,message);
4541 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4543 GetNextToken(q,&q,extent,token);
4544 (void) FormatLocaleString(message,MagickPathExtent,
4545 "stroke-dashoffset:%s;",token);
4546 (void) WriteBlobString(image,message);
4549 if (LocaleCompare("stroke-linecap",keyword) == 0)
4551 GetNextToken(q,&q,extent,token);
4552 (void) FormatLocaleString(message,MagickPathExtent,
4553 "stroke-linecap:%s;",token);
4554 (void) WriteBlobString(image,message);
4557 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4559 GetNextToken(q,&q,extent,token);
4560 (void) FormatLocaleString(message,MagickPathExtent,
4561 "stroke-linejoin:%s;",token);
4562 (void) WriteBlobString(image,message);
4565 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4567 GetNextToken(q,&q,extent,token);
4568 (void) FormatLocaleString(message,MagickPathExtent,
4569 "stroke-miterlimit:%s;",token);
4570 (void) WriteBlobString(image,message);
4573 if (LocaleCompare("stroke-opacity",keyword) == 0)
4575 GetNextToken(q,&q,extent,token);
4576 (void) FormatLocaleString(message,MagickPathExtent,
4577 "stroke-opacity:%s;",token);
4578 (void) WriteBlobString(image,message);
4581 if (LocaleCompare("stroke-width",keyword) == 0)
4583 GetNextToken(q,&q,extent,token);
4584 (void) FormatLocaleString(message,MagickPathExtent,
4585 "stroke-width:%s;",token);
4586 (void) WriteBlobString(image,message);
4595 if (LocaleCompare("text",keyword) == 0)
4597 primitive_type=TextPrimitive;
4600 if (LocaleCompare("text-antialias",keyword) == 0)
4602 GetNextToken(q,&q,extent,token);
4603 (void) FormatLocaleString(message,MagickPathExtent,
4604 "text-antialias:%s;",token);
4605 (void) WriteBlobString(image,message);
4608 if (LocaleCompare("tspan",keyword) == 0)
4610 primitive_type=TextPrimitive;
4613 if (LocaleCompare("translate",keyword) == 0)
4615 GetNextToken(q,&q,extent,token);
4616 affine.tx=StringToDouble(token,&next_token);
4617 GetNextToken(q,&q,extent,token);
4619 GetNextToken(q,&q,extent,token);
4620 affine.ty=StringToDouble(token,&next_token);
4629 if (LocaleCompare("viewbox",keyword) == 0)
4631 GetNextToken(q,&q,extent,token);
4633 GetNextToken(q,&q,extent,token);
4634 GetNextToken(q,&q,extent,token);
4636 GetNextToken(q,&q,extent,token);
4637 GetNextToken(q,&q,extent,token);
4639 GetNextToken(q,&q,extent,token);
4640 GetNextToken(q,&q,extent,token);
4652 if (status == MagickFalse)
4654 if (primitive_type == UndefinedPrimitive)
4657 Parse the primitive attributes.
4661 for (x=0; *q != '\0'; x++)
4666 if (IsPoint(q) == MagickFalse)
4668 GetNextToken(q,&q,extent,token);
4669 point.x=StringToDouble(token,&next_token);
4670 GetNextToken(q,&q,extent,token);
4672 GetNextToken(q,&q,extent,token);
4673 point.y=StringToDouble(token,&next_token);
4674 GetNextToken(q,(const char **) NULL,extent,token);
4676 GetNextToken(q,&q,extent,token);
4677 primitive_info[i].primitive=primitive_type;
4678 primitive_info[i].point=point;
4679 primitive_info[i].coordinates=0;
4680 primitive_info[i].method=FloodfillMethod;
4682 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4684 number_points+=6*BezierQuantum+360;
4685 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4686 number_points,sizeof(*primitive_info));
4687 if (primitive_info == (PrimitiveInfo *) NULL)
4689 (void) ThrowMagickException(exception,GetMagickModule(),
4690 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4694 primitive_info[j].primitive=primitive_type;
4695 primitive_info[j].coordinates=x;
4696 primitive_info[j].method=FloodfillMethod;
4697 primitive_info[j].text=(char *) NULL;
4700 AffineToTransform(image,&affine);
4704 switch (primitive_type)
4706 case PointPrimitive:
4709 if (primitive_info[j].coordinates != 1)
4718 if (primitive_info[j].coordinates != 2)
4723 (void) FormatLocaleString(message,MagickPathExtent,
4724 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4725 primitive_info[j].point.x,primitive_info[j].point.y,
4726 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4727 (void) WriteBlobString(image,message);
4730 case RectanglePrimitive:
4732 if (primitive_info[j].coordinates != 2)
4737 (void) FormatLocaleString(message,MagickPathExtent,
4738 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4739 primitive_info[j].point.x,primitive_info[j].point.y,
4740 primitive_info[j+1].point.x-primitive_info[j].point.x,
4741 primitive_info[j+1].point.y-primitive_info[j].point.y);
4742 (void) WriteBlobString(image,message);
4745 case RoundRectanglePrimitive:
4747 if (primitive_info[j].coordinates != 3)
4752 (void) FormatLocaleString(message,MagickPathExtent,
4753 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4754 "ry=\"%g\"/>\n",primitive_info[j].point.x,
4755 primitive_info[j].point.y,primitive_info[j+1].point.x-
4756 primitive_info[j].point.x,primitive_info[j+1].point.y-
4757 primitive_info[j].point.y,primitive_info[j+2].point.x,
4758 primitive_info[j+2].point.y);
4759 (void) WriteBlobString(image,message);
4764 if (primitive_info[j].coordinates != 3)
4771 case EllipsePrimitive:
4773 if (primitive_info[j].coordinates != 3)
4778 (void) FormatLocaleString(message,MagickPathExtent,
4779 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
4780 primitive_info[j].point.x,primitive_info[j].point.y,
4781 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4782 (void) WriteBlobString(image,message);
4785 case CirclePrimitive:
4791 if (primitive_info[j].coordinates != 2)
4796 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4797 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4798 (void) FormatLocaleString(message,MagickPathExtent,
4799 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
4800 primitive_info[j].point.x,primitive_info[j].point.y,
4802 (void) WriteBlobString(image,message);
4805 case PolylinePrimitive:
4807 if (primitive_info[j].coordinates < 2)
4812 (void) CopyMagickString(message," <polyline points=\"",
4814 (void) WriteBlobString(image,message);
4815 length=strlen(message);
4818 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4819 primitive_info[j].point.x,primitive_info[j].point.y);
4820 length+=strlen(message);
4823 (void) WriteBlobString(image,"\n ");
4824 length=strlen(message)+5;
4826 (void) WriteBlobString(image,message);
4828 (void) WriteBlobString(image,"\"/>\n");
4831 case PolygonPrimitive:
4833 if (primitive_info[j].coordinates < 3)
4838 primitive_info[i]=primitive_info[j];
4839 primitive_info[i].coordinates=0;
4840 primitive_info[j].coordinates++;
4842 (void) CopyMagickString(message," <polygon points=\"",MagickPathExtent);
4843 (void) WriteBlobString(image,message);
4844 length=strlen(message);
4847 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4848 primitive_info[j].point.x,primitive_info[j].point.y);
4849 length+=strlen(message);
4852 (void) WriteBlobString(image,"\n ");
4853 length=strlen(message)+5;
4855 (void) WriteBlobString(image,message);
4857 (void) WriteBlobString(image,"\"/>\n");
4860 case BezierPrimitive:
4862 if (primitive_info[j].coordinates < 3)
4874 GetNextToken(q,&q,extent,token);
4875 number_attributes=1;
4876 for (p=token; *p != '\0'; p++)
4877 if (isalpha((int) *p))
4878 number_attributes++;
4879 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
4881 number_points+=6*BezierQuantum*number_attributes;
4882 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4883 number_points,sizeof(*primitive_info));
4884 if (primitive_info == (PrimitiveInfo *) NULL)
4886 (void) ThrowMagickException(exception,GetMagickModule(),
4887 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4892 (void) WriteBlobString(image," <path d=\"");
4893 (void) WriteBlobString(image,token);
4894 (void) WriteBlobString(image,"\"/>\n");
4897 case AlphaPrimitive:
4898 case ColorPrimitive:
4900 if (primitive_info[j].coordinates != 1)
4905 GetNextToken(q,&q,extent,token);
4906 if (LocaleCompare("point",token) == 0)
4907 primitive_info[j].method=PointMethod;
4908 if (LocaleCompare("replace",token) == 0)
4909 primitive_info[j].method=ReplaceMethod;
4910 if (LocaleCompare("floodfill",token) == 0)
4911 primitive_info[j].method=FloodfillMethod;
4912 if (LocaleCompare("filltoborder",token) == 0)
4913 primitive_info[j].method=FillToBorderMethod;
4914 if (LocaleCompare("reset",token) == 0)
4915 primitive_info[j].method=ResetMethod;
4923 if (primitive_info[j].coordinates != 1)
4928 GetNextToken(q,&q,extent,token);
4929 (void) FormatLocaleString(message,MagickPathExtent,
4930 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
4931 primitive_info[j].point.y);
4932 (void) WriteBlobString(image,message);
4933 for (p=token; *p != '\0'; p++)
4936 case '<': (void) WriteBlobString(image,"<"); break;
4937 case '>': (void) WriteBlobString(image,">"); break;
4938 case '&': (void) WriteBlobString(image,"&"); break;
4939 default: (void) WriteBlobByte(image,*p); break;
4941 (void) WriteBlobString(image,"</text>\n");
4944 case ImagePrimitive:
4946 if (primitive_info[j].coordinates != 2)
4951 GetNextToken(q,&q,extent,token);
4952 (void) FormatLocaleString(message,MagickPathExtent,
4953 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
4954 "href=\"%s\"/>\n",primitive_info[j].point.x,
4955 primitive_info[j].point.y,primitive_info[j+1].point.x,
4956 primitive_info[j+1].point.y,token);
4957 (void) WriteBlobString(image,message);
4961 if (primitive_info == (PrimitiveInfo *) NULL)
4963 primitive_info[i].primitive=UndefinedPrimitive;
4964 if (status == MagickFalse)
4967 (void) WriteBlobString(image,"</svg>\n");
4969 Relinquish resources.
4971 token=DestroyString(token);
4972 if (primitive_info != (PrimitiveInfo *) NULL)
4973 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4974 (void) CloseBlob(image);