2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Scalable Vector Graphics Format %
21 % Copyright 1999-2019 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://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"
78 #if defined(MAGICKCORE_XML_DELEGATE)
79 # if defined(MAGICKCORE_WINDOWS_SUPPORT)
80 # if !defined(__MINGW32__)
81 # include <win32config.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"
107 #define DefaultSVGDensity 96.0
110 Typedef declarations.
112 typedef struct _BoundingBox
121 typedef struct _ElementInfo
131 typedef struct _SVGInfo
185 #if defined(MAGICKCORE_XML_DELEGATE)
201 SVGDensityGeometry[] = "96.0x96.0";
204 Forward declarations.
206 static MagickBooleanType
207 WriteSVGImage(const ImageInfo *,Image *,ExceptionInfo *);
210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220 % IsSVG()() returns MagickTrue if the image format type, identified by the
221 % magick string, is SVG.
223 % The format of the IsSVG method is:
225 % MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
227 % A description of each parameter follows:
229 % o magick: compare image format pattern against these bytes.
231 % o length: Specifies the length of the magick string.
234 static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
238 if (LocaleNCompare((const char *) magick+1,"svg",3) == 0)
242 if (LocaleNCompare((const char *) magick+1,"?xml",4) == 0)
248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
252 % R e a d S V G I m a g e %
256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
258 % ReadSVGImage() reads a Scalable Vector Gaphics file and returns it. It
259 % allocates the memory necessary for the new Image structure and returns a
260 % pointer to the new image.
262 % The format of the ReadSVGImage method is:
264 % Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
266 % A description of each parameter follows:
268 % o image_info: the image info.
270 % o exception: return any errors or warnings in this structure.
274 static Image *RenderSVGImage(const ImageInfo *image_info,Image *image,
275 ExceptionInfo *exception)
278 background[MagickPathExtent],
279 command[MagickPathExtent],
281 input_filename[MagickPathExtent],
282 opacity[MagickPathExtent],
283 output_filename[MagickPathExtent],
284 unique[MagickPathExtent];
299 Our best hope for compliance with the SVG standard.
301 delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception);
302 if (delegate_info == (const DelegateInfo *) NULL)
303 return((Image *) NULL);
304 status=AcquireUniqueSymbolicLink(image->filename,input_filename);
305 (void) AcquireUniqueFilename(output_filename);
306 (void) AcquireUniqueFilename(unique);
307 density=AcquireString("");
308 (void) FormatLocaleString(density,MagickPathExtent,"%.20g,%.20g",
309 image->resolution.x,image->resolution.y);
310 (void) FormatLocaleString(background,MagickPathExtent,
311 "rgb(%.20g%%,%.20g%%,%.20g%%)",
312 100.0*QuantumScale*image->background_color.red,
313 100.0*QuantumScale*image->background_color.green,
314 100.0*QuantumScale*image->background_color.blue);
315 (void) FormatLocaleString(opacity,MagickPathExtent,"%.20g",QuantumScale*
316 image->background_color.alpha);
317 (void) FormatLocaleString(command,MagickPathExtent,
318 GetDelegateCommands(delegate_info),input_filename,output_filename,density,
319 background,opacity,unique);
320 density=DestroyString(density);
321 status=ExternalDelegateCommand(MagickFalse,image_info->verbose,command,
322 (char *) NULL,exception);
323 (void) RelinquishUniqueFileResource(unique);
324 (void) RelinquishUniqueFileResource(input_filename);
325 if ((status == 0) && (stat(output_filename,&attributes) == 0) &&
326 (attributes.st_size > 0))
334 read_info=CloneImageInfo(image_info);
335 (void) CopyMagickString(read_info->filename,output_filename,
337 svg_image=ReadImage(read_info,exception);
338 read_info=DestroyImageInfo(read_info);
339 if (svg_image != (Image *) NULL)
341 (void) RelinquishUniqueFileResource(output_filename);
342 for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; )
344 (void) CopyMagickString(next->filename,image->filename,
346 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
347 next=GetNextImageInList(next);
352 (void) RelinquishUniqueFileResource(output_filename);
353 return((Image *) NULL);
356 #if defined(MAGICKCORE_XML_DELEGATE)
357 static SVGInfo *AcquireSVGInfo(void)
362 svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info));
363 if (svg_info == (SVGInfo *) NULL)
364 return((SVGInfo *) NULL);
365 (void) memset(svg_info,0,sizeof(*svg_info));
366 svg_info->text=AcquireString("");
367 svg_info->scale=(double *) AcquireCriticalMemory(sizeof(*svg_info->scale));
368 GetAffineMatrix(&svg_info->affine);
369 svg_info->scale[0]=ExpandAffine(&svg_info->affine);
373 static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
375 if (svg_info->text != (char *) NULL)
376 svg_info->text=DestroyString(svg_info->text);
377 if (svg_info->scale != (double *) NULL)
378 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
379 if (svg_info->title != (char *) NULL)
380 svg_info->title=DestroyString(svg_info->title);
381 if (svg_info->comment != (char *) NULL)
382 svg_info->comment=DestroyString(svg_info->comment);
383 return((SVGInfo *) RelinquishMagickMemory(svg_info));
386 static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
391 token[MagickPathExtent];
399 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
400 assert(string != (const char *) NULL);
401 p=(const char *) string;
402 (void) GetNextToken(p,&p,MagickPathExtent,token);
403 value=StringToDouble(token,&next_token);
404 if (strchr(token,'%') != (char *) NULL)
412 if (svg_info->view_box.width == 0.0)
414 return(svg_info->view_box.width*value/100.0);
418 if (svg_info->view_box.height == 0.0)
420 return(svg_info->view_box.height*value/100.0);
422 alpha=value-svg_info->view_box.width;
423 beta=value-svg_info->view_box.height;
424 return(hypot(alpha,beta)/sqrt(2.0)/100.0);
426 (void) GetNextToken(p,&p,MagickPathExtent,token);
427 if (LocaleNCompare(token,"cm",2) == 0)
428 return(DefaultSVGDensity*svg_info->scale[0]/2.54*value);
429 if (LocaleNCompare(token,"em",2) == 0)
430 return(svg_info->pointsize*value);
431 if (LocaleNCompare(token,"ex",2) == 0)
432 return(svg_info->pointsize*value/2.0);
433 if (LocaleNCompare(token,"in",2) == 0)
434 return(DefaultSVGDensity*svg_info->scale[0]*value);
435 if (LocaleNCompare(token,"mm",2) == 0)
436 return(DefaultSVGDensity*svg_info->scale[0]/25.4*value);
437 if (LocaleNCompare(token,"pc",2) == 0)
438 return(DefaultSVGDensity*svg_info->scale[0]/6.0*value);
439 if (LocaleNCompare(token,"pt",2) == 0)
440 return(svg_info->scale[0]*value);
441 if (LocaleNCompare(token,"px",2) == 0)
446 #if defined(__cplusplus) || defined(c_plusplus)
450 static int SVGIsStandalone(void *context)
456 Is this document tagged standalone?
458 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGIsStandalone()");
459 svg_info=(SVGInfo *) context;
460 return(svg_info->document->standalone == 1);
463 static int SVGHasInternalSubset(void *context)
469 Does this document has an internal subset?
471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
472 " SAX.SVGHasInternalSubset()");
473 svg_info=(SVGInfo *) context;
474 return(svg_info->document->intSubset != NULL);
477 static int SVGHasExternalSubset(void *context)
483 Does this document has an external subset?
485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
486 " SAX.SVGHasExternalSubset()");
487 svg_info=(SVGInfo *) context;
488 return(svg_info->document->extSubset != NULL);
491 static void SVGInternalSubset(void *context,const xmlChar *name,
492 const xmlChar *external_id,const xmlChar *system_id)
498 Does this document has an internal subset?
500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
501 " SAX.internalSubset(%s, %s, %s)",(const char *) name,
502 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
503 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
504 svg_info=(SVGInfo *) context;
505 (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id);
508 static xmlParserInputPtr SVGResolveEntity(void *context,
509 const xmlChar *public_id,const xmlChar *system_id)
518 Special entity resolver, better left to the parser, it has more
519 context than the application layer. The default behaviour is to
520 not resolve the entities, in that case the ENTITY_REF nodes are
521 built in the structure (and the parameter values).
523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
524 " SAX.resolveEntity(%s, %s)",
525 (public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"),
526 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
527 svg_info=(SVGInfo *) context;
528 stream=xmlLoadExternalEntity((const char *) system_id,(const char *)
529 public_id,svg_info->parser);
533 static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
539 Get an entity by name.
541 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGGetEntity(%s)",
543 svg_info=(SVGInfo *) context;
544 return(xmlGetDocEntity(svg_info->document,name));
547 static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
553 Get a parameter entity by name.
555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
556 " SAX.getParameterEntity(%s)",name);
557 svg_info=(SVGInfo *) context;
558 return(xmlGetParameterEntity(svg_info->document,name));
561 static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
562 const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
568 An entity definition has been parsed.
570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
571 " SAX.entityDecl(%s, %d, %s, %s, %s)",name,type,
572 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
573 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content);
574 svg_info=(SVGInfo *) context;
575 if (svg_info->parser->inSubset == 1)
576 (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id,
579 if (svg_info->parser->inSubset == 2)
580 (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
584 static void SVGAttributeDeclaration(void *context,const xmlChar *element,
585 const xmlChar *name,int type,int value,const xmlChar *default_value,
586 xmlEnumerationPtr tree)
599 An attribute definition has been parsed.
601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
602 " SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value,
604 svg_info=(SVGInfo *) context;
605 fullname=(xmlChar *) NULL;
606 prefix=(xmlChar *) NULL;
607 parser=svg_info->parser;
608 fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix);
609 if (parser->inSubset == 1)
610 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset,
611 element,fullname,prefix,(xmlAttributeType) type,
612 (xmlAttributeDefault) value,default_value,tree);
614 if (parser->inSubset == 2)
615 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset,
616 element,fullname,prefix,(xmlAttributeType) type,
617 (xmlAttributeDefault) value,default_value,tree);
618 if (prefix != (xmlChar *) NULL)
620 if (fullname != (xmlChar *) NULL)
624 static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
625 xmlElementContentPtr content)
634 An element definition has been parsed.
636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
637 " SAX.elementDecl(%s, %d, ...)",name,type);
638 svg_info=(SVGInfo *) context;
639 parser=svg_info->parser;
640 if (parser->inSubset == 1)
641 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset,
642 name,(xmlElementTypeVal) type,content);
644 if (parser->inSubset == 2)
645 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
646 name,(xmlElementTypeVal) type,content);
649 static void SVGStripString(const MagickBooleanType trim,char *message)
658 assert(message != (char *) NULL);
659 if (*message == '\0')
665 for (p=message; *p != '\0'; p++)
667 if ((*p == '/') && (*(p+1) == '*'))
669 for ( ; *p != '\0'; p++)
670 if ((*p == '*') && (*(p+1) == '/'))
681 length=strlen(message);
682 if ((trim != MagickFalse) && (length != 0))
688 while (isspace((int) ((unsigned char) *p)) != 0)
690 if ((*p == '\'') || (*p == '"'))
693 while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
696 if ((*q == '\'') || (*q == '"'))
698 (void) memmove(message,p,(size_t) (q-p+1));
702 Convert newlines to a space.
704 for (p=message; *p != '\0'; p++)
709 static char **SVGKeyValuePairs(void *context,const int key_sentinel,
710 const int value_sentinel,const char *text,size_t *number_tokens)
728 svg_info=(SVGInfo *) context;
730 if (text == (const char *) NULL)
731 return((char **) NULL);
733 tokens=(char **) AcquireQuantumMemory(extent+2UL,sizeof(*tokens));
734 if (tokens == (char **) NULL)
736 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
737 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
738 return((char **) NULL);
741 Convert string to an ASCII list.
745 for (q=p; *q != '\0'; q++)
747 if ((*q != key_sentinel) && (*q != value_sentinel) && (*q != '\0'))
749 if (i == (ssize_t) extent)
752 tokens=(char **) ResizeQuantumMemory(tokens,extent+2,sizeof(*tokens));
753 if (tokens == (char **) NULL)
755 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
756 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
757 return((char **) NULL);
760 tokens[i]=AcquireString(p);
761 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
762 SVGStripString(MagickTrue,tokens[i]);
766 tokens[i]=AcquireString(p);
767 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
768 SVGStripString(MagickTrue,tokens[i++]);
769 tokens[i]=(char *) NULL;
770 *number_tokens=(size_t) i;
774 static void SVGNotationDeclaration(void *context,const xmlChar *name,
775 const xmlChar *public_id,const xmlChar *system_id)
784 What to do when a notation declaration has been parsed.
786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
787 " SAX.notationDecl(%s, %s, %s)",name,
788 public_id != (const xmlChar *) NULL ? (const char *) public_id : "none",
789 system_id != (const xmlChar *) NULL ? (const char *) system_id : "none");
790 svg_info=(SVGInfo *) context;
791 parser=svg_info->parser;
792 if (parser->inSubset == 1)
793 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
794 name,public_id,system_id);
796 if (parser->inSubset == 2)
797 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
798 name,public_id,system_id);
801 static void SVGProcessStyleElement(void *context,const xmlChar *name,
805 background[MagickPathExtent],
823 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
824 svg_info=(SVGInfo *) context;
825 tokens=SVGKeyValuePairs(context,':',';',style,&number_tokens);
826 if (tokens == (char **) NULL)
828 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
830 keyword=(char *) tokens[i];
831 value=(char *) tokens[i+1];
832 if (LocaleCompare(keyword,"font-size") != 0)
834 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
835 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
836 svg_info->pointsize);
838 color=AcquireString("none");
839 units=AcquireString("userSpaceOnUse");
840 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
842 keyword=(char *) tokens[i];
843 value=(char *) tokens[i+1];
844 (void) LogMagickEvent(CoderEvent,GetMagickModule()," %s: %s",keyword,
851 if (LocaleCompare((const char *) name,"background") == 0)
853 if (LocaleCompare((const char *) name,"svg") == 0)
854 (void) CopyMagickString(background,value,MagickPathExtent);
862 if (LocaleCompare(keyword,"clip-path") == 0)
864 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",value);
867 if (LocaleCompare(keyword,"clip-rule") == 0)
869 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",value);
872 if (LocaleCompare(keyword,"clipPathUnits") == 0)
874 (void) CloneString(&units,value);
875 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
879 if (LocaleCompare(keyword,"color") == 0)
881 (void) CloneString(&color,value);
889 if (LocaleCompare(keyword,"fill") == 0)
891 if (LocaleCompare(value,"currentColor") == 0)
893 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
896 if (LocaleCompare(value,"#000000ff") == 0)
897 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
899 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
902 if (LocaleCompare(keyword,"fillcolor") == 0)
904 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
907 if (LocaleCompare(keyword,"fill-rule") == 0)
909 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",value);
912 if (LocaleCompare(keyword,"fill-opacity") == 0)
914 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
918 if (LocaleCompare(keyword,"font") == 0)
921 family[MagickPathExtent],
922 size[MagickPathExtent],
923 style[MagickPathExtent];
925 if (sscanf(value,"%2048s %2048s %2048s",style,size,family) != 3)
927 if (GetUserSpaceCoordinateValue(svg_info,0,style) == 0)
928 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
931 if (sscanf(value,"%2048s %2048s",size,family) != 2)
933 (void) FormatLocaleFile(svg_info->file,"font-size \"%s\"\n",size);
934 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
938 if (LocaleCompare(keyword,"font-family") == 0)
940 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
944 if (LocaleCompare(keyword,"font-stretch") == 0)
946 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
950 if (LocaleCompare(keyword,"font-style") == 0)
952 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",value);
955 if (LocaleCompare(keyword,"font-size") == 0)
957 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
958 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
959 svg_info->pointsize);
962 if (LocaleCompare(keyword,"font-weight") == 0)
964 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
973 if (LocaleCompare(keyword,"kerning") == 0)
975 (void) FormatLocaleFile(svg_info->file,"kerning \"%s\"\n",value);
983 if (LocaleCompare(keyword,"letter-spacing") == 0)
985 (void) FormatLocaleFile(svg_info->file,"letter-spacing \"%s\"\n",
994 if (LocaleCompare(keyword,"mask") == 0)
996 (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
1004 if (LocaleCompare(keyword,"offset") == 0)
1006 (void) FormatLocaleFile(svg_info->file,"offset %g\n",
1007 GetUserSpaceCoordinateValue(svg_info,1,value));
1010 if (LocaleCompare(keyword,"opacity") == 0)
1012 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
1020 if (LocaleCompare(keyword,"stop-color") == 0)
1022 (void) CloneString(&svg_info->stop_color,value);
1025 if (LocaleCompare(keyword,"stroke") == 0)
1027 if (LocaleCompare(value,"currentColor") == 0)
1029 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",color);
1032 if (LocaleCompare(value,"#000000ff") == 0)
1033 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
1035 (void) FormatLocaleFile(svg_info->file,
1036 "stroke \"%s\"\n",value);
1039 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1041 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
1042 LocaleCompare(value,"true") == 0);
1045 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1047 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1051 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1053 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
1054 GetUserSpaceCoordinateValue(svg_info,1,value));
1057 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1059 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
1063 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1065 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
1069 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1071 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit \"%s\"\n",
1075 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1077 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
1081 if (LocaleCompare(keyword,"stroke-width") == 0)
1083 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
1084 GetUserSpaceCoordinateValue(svg_info,1,value));
1092 if (LocaleCompare(keyword,"text-align") == 0)
1094 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",value);
1097 if (LocaleCompare(keyword,"text-anchor") == 0)
1099 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
1103 if (LocaleCompare(keyword,"text-decoration") == 0)
1105 if (LocaleCompare(value,"underline") == 0)
1106 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
1107 if (LocaleCompare(value,"line-through") == 0)
1108 (void) FormatLocaleFile(svg_info->file,"decorate line-through\n");
1109 if (LocaleCompare(value,"overline") == 0)
1110 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
1113 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1115 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
1116 LocaleCompare(value,"true") == 0);
1125 if (units != (char *) NULL)
1126 units=DestroyString(units);
1127 if (color != (char *) NULL)
1128 color=DestroyString(color);
1129 for (i=0; tokens[i] != (char *) NULL; i++)
1130 tokens[i]=DestroyString(tokens[i]);
1131 tokens=(char **) RelinquishMagickMemory(tokens);
1134 static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
1135 const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
1141 What to do when an unparsed entity declaration is parsed.
1143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1144 " SAX.unparsedEntityDecl(%s, %s, %s, %s)",name,
1145 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
1146 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation);
1147 svg_info=(SVGInfo *) context;
1148 (void) xmlAddDocEntity(svg_info->document,name,
1149 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
1153 static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
1159 Receive the document locator at startup, actually xmlDefaultSAXLocator.
1162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1163 " SAX.setDocumentLocator()");
1164 svg_info=(SVGInfo *) context;
1168 static void SVGStartDocument(void *context)
1177 Called when the document start being processed.
1179 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startDocument()");
1180 svg_info=(SVGInfo *) context;
1181 parser=svg_info->parser;
1182 svg_info->document=xmlNewDoc(parser->version);
1183 if (svg_info->document == (xmlDocPtr) NULL)
1185 if (parser->encoding == NULL)
1186 svg_info->document->encoding=(const xmlChar *) NULL;
1188 svg_info->document->encoding=xmlStrdup(parser->encoding);
1189 svg_info->document->standalone=parser->standalone;
1192 static void SVGEndDocument(void *context)
1198 Called when the document end has been detected.
1200 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()");
1201 svg_info=(SVGInfo *) context;
1202 if (svg_info->offset != (char *) NULL)
1203 svg_info->offset=DestroyString(svg_info->offset);
1204 if (svg_info->stop_color != (char *) NULL)
1205 svg_info->stop_color=DestroyString(svg_info->stop_color);
1206 if (svg_info->scale != (double *) NULL)
1207 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
1208 if (svg_info->text != (char *) NULL)
1209 svg_info->text=DestroyString(svg_info->text);
1210 if (svg_info->vertices != (char *) NULL)
1211 svg_info->vertices=DestroyString(svg_info->vertices);
1212 if (svg_info->url != (char *) NULL)
1213 svg_info->url=DestroyString(svg_info->url);
1214 #if defined(MAGICKCORE_XML_DELEGATE)
1215 if (svg_info->document != (xmlDocPtr) NULL)
1217 xmlFreeDoc(svg_info->document);
1218 svg_info->document=(xmlDocPtr) NULL;
1223 static void SVGStartElement(void *context,const xmlChar *name,
1224 const xmlChar **attributes)
1226 #define PushGraphicContext(id) \
1229 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); \
1231 (void) FormatLocaleFile(svg_info->file,"push graphic-context \"%s\"\n", \
1237 background[MagickPathExtent],
1238 id[MagickPathExtent],
1240 token[MagickPathExtent],
1260 Called when an opening tag has been processed.
1262 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s",
1264 svg_info=(SVGInfo *) context;
1266 svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale,
1267 svg_info->n+1UL,sizeof(*svg_info->scale));
1268 if (svg_info->scale == (double *) NULL)
1270 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
1271 ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
1274 svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
1275 color=AcquireString("none");
1276 units=AcquireString("userSpaceOnUse");
1280 value=(const char *) NULL;
1281 if ((LocaleCompare((char *) name,"image") == 0) ||
1282 (LocaleCompare((char *) name,"pattern") == 0) ||
1283 (LocaleCompare((char *) name,"rect") == 0) ||
1284 (LocaleCompare((char *) name,"text") == 0) ||
1285 (LocaleCompare((char *) name,"use") == 0))
1287 svg_info->bounds.x=0.0;
1288 svg_info->bounds.y=0.0;
1290 if (attributes != (const xmlChar **) NULL)
1291 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1293 keyword=(const char *) attributes[i];
1294 value=(const char *) attributes[i+1];
1300 if (LocaleCompare(keyword,"cx") == 0)
1302 svg_info->element.cx=
1303 GetUserSpaceCoordinateValue(svg_info,1,value);
1306 if (LocaleCompare(keyword,"cy") == 0)
1308 svg_info->element.cy=
1309 GetUserSpaceCoordinateValue(svg_info,-1,value);
1317 if (LocaleCompare(keyword,"fx") == 0)
1319 svg_info->element.major=
1320 GetUserSpaceCoordinateValue(svg_info,1,value);
1323 if (LocaleCompare(keyword,"fy") == 0)
1325 svg_info->element.minor=
1326 GetUserSpaceCoordinateValue(svg_info,-1,value);
1334 if (LocaleCompare(keyword,"height") == 0)
1336 svg_info->bounds.height=
1337 GetUserSpaceCoordinateValue(svg_info,-1,value);
1345 if (LocaleCompare(keyword,"id") == 0)
1347 (void) CopyMagickString(id,value,MagickPathExtent);
1355 if (LocaleCompare(keyword,"r") == 0)
1357 svg_info->element.angle=
1358 GetUserSpaceCoordinateValue(svg_info,0,value);
1366 if (LocaleCompare(keyword,"width") == 0)
1368 svg_info->bounds.width=
1369 GetUserSpaceCoordinateValue(svg_info,1,value);
1377 if (LocaleCompare(keyword,"x") == 0)
1379 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
1382 if (LocaleCompare(keyword,"x1") == 0)
1384 svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
1388 if (LocaleCompare(keyword,"x2") == 0)
1390 svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
1399 if (LocaleCompare(keyword,"y") == 0)
1401 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
1404 if (LocaleCompare(keyword,"y1") == 0)
1406 svg_info->segment.y1=GetUserSpaceCoordinateValue(svg_info,-1,
1410 if (LocaleCompare(keyword,"y2") == 0)
1412 svg_info->segment.y2=GetUserSpaceCoordinateValue(svg_info,-1,
1422 if (strchr((char *) name,':') != (char *) NULL)
1425 Skip over namespace.
1427 for ( ; *name != ':'; name++) ;
1435 if (LocaleCompare((const char *) name,"circle") == 0)
1437 PushGraphicContext(id);
1440 if (LocaleCompare((const char *) name,"clipPath") == 0)
1442 (void) FormatLocaleFile(svg_info->file,"push clip-path \"%s\"\n",id);
1450 if (LocaleCompare((const char *) name,"defs") == 0)
1452 (void) FormatLocaleFile(svg_info->file,"push defs\n");
1460 if (LocaleCompare((const char *) name,"ellipse") == 0)
1462 PushGraphicContext(id);
1470 if (LocaleCompare((const char *) name,"foreignObject") == 0)
1472 PushGraphicContext(id);
1480 if (LocaleCompare((const char *) name,"g") == 0)
1482 PushGraphicContext(id);
1490 if (LocaleCompare((const char *) name,"image") == 0)
1492 PushGraphicContext(id);
1500 if (LocaleCompare((const char *) name,"line") == 0)
1502 PushGraphicContext(id);
1505 if (LocaleCompare((const char *) name,"linearGradient") == 0)
1507 (void) FormatLocaleFile(svg_info->file,
1508 "push gradient \"%s\" linear %g,%g %g,%g\n",id,
1509 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
1510 svg_info->segment.y2);
1518 if (LocaleCompare((const char *) name,"mask") == 0)
1520 (void) FormatLocaleFile(svg_info->file,"push mask \"%s\"\n",id);
1528 if (LocaleCompare((const char *) name,"path") == 0)
1530 PushGraphicContext(id);
1533 if (LocaleCompare((const char *) name,"pattern") == 0)
1535 (void) FormatLocaleFile(svg_info->file,
1536 "push pattern \"%s\" %g,%g %g,%g\n",id,
1537 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
1538 svg_info->bounds.height);
1541 if (LocaleCompare((const char *) name,"polygon") == 0)
1543 PushGraphicContext(id);
1546 if (LocaleCompare((const char *) name,"polyline") == 0)
1548 PushGraphicContext(id);
1556 if (LocaleCompare((const char *) name,"radialGradient") == 0)
1558 (void) FormatLocaleFile(svg_info->file,
1559 "push gradient \"%s\" radial %g,%g %g,%g %g\n",
1560 id,svg_info->element.cx,svg_info->element.cy,
1561 svg_info->element.major,svg_info->element.minor,
1562 svg_info->element.angle);
1565 if (LocaleCompare((const char *) name,"rect") == 0)
1567 PushGraphicContext(id);
1575 if (LocaleCompare((char *) name,"style") == 0)
1577 if (LocaleCompare((const char *) name,"svg") == 0)
1579 svg_info->svgDepth++;
1580 PushGraphicContext(id);
1581 (void) FormatLocaleFile(svg_info->file,"compliance \"SVG\"\n");
1582 (void) FormatLocaleFile(svg_info->file,"fill \"black\"\n");
1583 (void) FormatLocaleFile(svg_info->file,"fill-opacity 1\n");
1584 (void) FormatLocaleFile(svg_info->file,"stroke \"none\"\n");
1585 (void) FormatLocaleFile(svg_info->file,"stroke-width 1\n");
1586 (void) FormatLocaleFile(svg_info->file,"stroke-opacity 1\n");
1587 (void) FormatLocaleFile(svg_info->file,"fill-rule nonzero\n");
1590 if (LocaleCompare((const char *) name,"symbol") == 0)
1592 (void) FormatLocaleFile(svg_info->file,"push symbol\n");
1600 if (LocaleCompare((const char *) name,"text") == 0)
1602 PushGraphicContext(id);
1603 (void) FormatLocaleFile(svg_info->file,"class \"text\"\n");
1604 svg_info->text_offset.x=svg_info->bounds.x;
1605 svg_info->text_offset.y=svg_info->bounds.y;
1606 svg_info->bounds.x=0.0;
1607 svg_info->bounds.y=0.0;
1608 svg_info->bounds.width=0.0;
1609 svg_info->bounds.height=0.0;
1612 if (LocaleCompare((const char *) name,"tspan") == 0)
1614 if (*svg_info->text != '\0')
1619 text=EscapeString(svg_info->text,'\"');
1620 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
1621 svg_info->text_offset.x,svg_info->text_offset.y,text);
1622 text=DestroyString(text);
1623 *svg_info->text='\0';
1625 PushGraphicContext(id);
1633 if (LocaleCompare((char *) name,"use") == 0)
1635 PushGraphicContext(id);
1643 if (attributes != (const xmlChar **) NULL)
1644 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1646 keyword=(const char *) attributes[i];
1647 value=(const char *) attributes[i+1];
1648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1649 " %s = %s",keyword,value);
1655 if (LocaleCompare(keyword,"angle") == 0)
1657 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1658 GetUserSpaceCoordinateValue(svg_info,0,value));
1666 if (LocaleCompare(keyword,"class") == 0)
1673 (void) GetNextToken(p,&p,MagickPathExtent,token);
1675 (void) GetNextToken(p,&p,MagickPathExtent,token);
1678 (void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",
1685 if (LocaleCompare(keyword,"clip-path") == 0)
1687 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",
1691 if (LocaleCompare(keyword,"clip-rule") == 0)
1693 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",
1697 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1699 (void) CloneString(&units,value);
1700 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
1704 if (LocaleCompare(keyword,"color") == 0)
1706 (void) CloneString(&color,value);
1709 if (LocaleCompare(keyword,"cx") == 0)
1711 svg_info->element.cx=
1712 GetUserSpaceCoordinateValue(svg_info,1,value);
1715 if (LocaleCompare(keyword,"cy") == 0)
1717 svg_info->element.cy=
1718 GetUserSpaceCoordinateValue(svg_info,-1,value);
1726 if (LocaleCompare(keyword,"d") == 0)
1728 (void) CloneString(&svg_info->vertices,value);
1731 if (LocaleCompare(keyword,"dx") == 0)
1736 dx=GetUserSpaceCoordinateValue(svg_info,1,value);
1737 svg_info->bounds.x+=dx;
1738 svg_info->text_offset.x+=dx;
1739 if (LocaleCompare((char *) name,"text") == 0)
1740 (void) FormatLocaleFile(svg_info->file,"translate %g,0.0\n",dx);
1743 if (LocaleCompare(keyword,"dy") == 0)
1748 dy=GetUserSpaceCoordinateValue(svg_info,-1,value);
1749 svg_info->bounds.y+=dy;
1750 svg_info->text_offset.y+=dy;
1751 if (LocaleCompare((char *) name,"text") == 0)
1752 (void) FormatLocaleFile(svg_info->file,"translate 0.0,%g\n",dy);
1760 if (LocaleCompare(keyword,"fill") == 0)
1762 if (LocaleCompare(value,"currentColor") == 0)
1764 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
1767 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1770 if (LocaleCompare(keyword,"fillcolor") == 0)
1772 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1775 if (LocaleCompare(keyword,"fill-rule") == 0)
1777 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",
1781 if (LocaleCompare(keyword,"fill-opacity") == 0)
1783 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
1787 if (LocaleCompare(keyword,"font-family") == 0)
1789 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
1793 if (LocaleCompare(keyword,"font-stretch") == 0)
1795 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
1799 if (LocaleCompare(keyword,"font-style") == 0)
1801 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
1805 if (LocaleCompare(keyword,"font-size") == 0)
1807 if (LocaleCompare(value,"xx-small") == 0)
1808 svg_info->pointsize=6.144;
1809 else if (LocaleCompare(value,"x-small") == 0)
1810 svg_info->pointsize=7.68;
1811 else if (LocaleCompare(value,"small") == 0)
1812 svg_info->pointsize=9.6;
1813 else if (LocaleCompare(value,"medium") == 0)
1814 svg_info->pointsize=12.0;
1815 else if (LocaleCompare(value,"large") == 0)
1816 svg_info->pointsize=14.4;
1817 else if (LocaleCompare(value,"x-large") == 0)
1818 svg_info->pointsize=17.28;
1819 else if (LocaleCompare(value,"xx-large") == 0)
1820 svg_info->pointsize=20.736;
1822 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,
1824 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1825 svg_info->pointsize);
1828 if (LocaleCompare(keyword,"font-weight") == 0)
1830 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
1839 if (LocaleCompare(keyword,"gradientTransform") == 0)
1846 GetAffineMatrix(&transform);
1847 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1848 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
1849 if (tokens == (char **) NULL)
1851 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
1853 keyword=(char *) tokens[j];
1854 if (keyword == (char *) NULL)
1856 value=(char *) tokens[j+1];
1857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1858 " %s: %s",keyword,value);
1860 GetAffineMatrix(&affine);
1866 if (LocaleCompare(keyword,"matrix") == 0)
1868 p=(const char *) value;
1869 (void) GetNextToken(p,&p,MagickPathExtent,token);
1870 affine.sx=StringToDouble(value,(char **) NULL);
1871 (void) GetNextToken(p,&p,MagickPathExtent,token);
1873 (void) GetNextToken(p,&p,MagickPathExtent,token);
1874 affine.rx=StringToDouble(token,&next_token);
1875 (void) GetNextToken(p,&p,MagickPathExtent,token);
1877 (void) GetNextToken(p,&p,MagickPathExtent,token);
1878 affine.ry=StringToDouble(token,&next_token);
1879 (void) GetNextToken(p,&p,MagickPathExtent,token);
1881 (void) GetNextToken(p,&p,MagickPathExtent,token);
1882 affine.sy=StringToDouble(token,&next_token);
1883 (void) GetNextToken(p,&p,MagickPathExtent,token);
1885 (void) GetNextToken(p,&p,MagickPathExtent,token);
1886 affine.tx=StringToDouble(token,&next_token);
1887 (void) GetNextToken(p,&p,MagickPathExtent,token);
1889 (void) GetNextToken(p,&p,MagickPathExtent,token);
1890 affine.ty=StringToDouble(token,&next_token);
1898 if (LocaleCompare(keyword,"rotate") == 0)
1903 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1904 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1905 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1906 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1907 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1915 if (LocaleCompare(keyword,"scale") == 0)
1917 for (p=(const char *) value; *p != '\0'; p++)
1918 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1921 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1922 affine.sy=affine.sx;
1925 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1926 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1929 if (LocaleCompare(keyword,"skewX") == 0)
1931 affine.sx=svg_info->affine.sx;
1932 affine.ry=tan(DegreesToRadians(fmod(
1933 GetUserSpaceCoordinateValue(svg_info,1,value),
1935 affine.sy=svg_info->affine.sy;
1938 if (LocaleCompare(keyword,"skewY") == 0)
1940 affine.sx=svg_info->affine.sx;
1941 affine.rx=tan(DegreesToRadians(fmod(
1942 GetUserSpaceCoordinateValue(svg_info,-1,value),
1944 affine.sy=svg_info->affine.sy;
1952 if (LocaleCompare(keyword,"translate") == 0)
1954 for (p=(const char *) value; *p != '\0'; p++)
1955 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1958 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1959 affine.ty=affine.tx;
1962 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1970 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1971 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1972 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1973 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1974 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1976 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1979 (void) FormatLocaleFile(svg_info->file,
1980 "affine %g %g %g %g %g %g\n",transform.sx,
1981 transform.rx,transform.ry,transform.sy,transform.tx,
1983 for (j=0; tokens[j] != (char *) NULL; j++)
1984 tokens[j]=DestroyString(tokens[j]);
1985 tokens=(char **) RelinquishMagickMemory(tokens);
1988 if (LocaleCompare(keyword,"gradientUnits") == 0)
1990 (void) CloneString(&units,value);
1991 (void) FormatLocaleFile(svg_info->file,"gradient-units \"%s\"\n",
2000 if (LocaleCompare(keyword,"height") == 0)
2002 svg_info->bounds.height=
2003 GetUserSpaceCoordinateValue(svg_info,-1,value);
2006 if (LocaleCompare(keyword,"href") == 0)
2008 (void) CloneString(&svg_info->url,value);
2016 if (LocaleCompare(keyword,"kerning") == 0)
2018 (void) FormatLocaleFile(svg_info->file,"kerning \"%s\"\n",
2027 if (LocaleCompare(keyword,"letter-spacing") == 0)
2029 (void) FormatLocaleFile(svg_info->file,"letter-spacing \"%s\"\n",
2038 if (LocaleCompare(keyword,"major") == 0)
2040 svg_info->element.major=
2041 GetUserSpaceCoordinateValue(svg_info,1,value);
2044 if (LocaleCompare(keyword,"mask") == 0)
2046 (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
2049 if (LocaleCompare(keyword,"minor") == 0)
2051 svg_info->element.minor=
2052 GetUserSpaceCoordinateValue(svg_info,-1,value);
2060 if (LocaleCompare(keyword,"offset") == 0)
2062 (void) CloneString(&svg_info->offset,value);
2065 if (LocaleCompare(keyword,"opacity") == 0)
2067 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
2075 if (LocaleCompare(keyword,"path") == 0)
2077 (void) CloneString(&svg_info->url,value);
2080 if (LocaleCompare(keyword,"points") == 0)
2082 (void) CloneString(&svg_info->vertices,value);
2090 if (LocaleCompare(keyword,"r") == 0)
2092 svg_info->element.major=
2093 GetUserSpaceCoordinateValue(svg_info,1,value);
2094 svg_info->element.minor=
2095 GetUserSpaceCoordinateValue(svg_info,-1,value);
2098 if (LocaleCompare(keyword,"rotate") == 0)
2103 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
2104 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2105 svg_info->bounds.x,svg_info->bounds.y);
2106 svg_info->bounds.x=0;
2107 svg_info->bounds.y=0;
2108 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
2111 if (LocaleCompare(keyword,"rx") == 0)
2113 if (LocaleCompare((const char *) name,"ellipse") == 0)
2114 svg_info->element.major=
2115 GetUserSpaceCoordinateValue(svg_info,1,value);
2118 GetUserSpaceCoordinateValue(svg_info,1,value);
2121 if (LocaleCompare(keyword,"ry") == 0)
2123 if (LocaleCompare((const char *) name,"ellipse") == 0)
2124 svg_info->element.minor=
2125 GetUserSpaceCoordinateValue(svg_info,-1,value);
2128 GetUserSpaceCoordinateValue(svg_info,-1,value);
2136 if (LocaleCompare(keyword,"stop-color") == 0)
2138 (void) CloneString(&svg_info->stop_color,value);
2141 if (LocaleCompare(keyword,"stroke") == 0)
2143 if (LocaleCompare(value,"currentColor") == 0)
2145 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",
2149 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",value);
2152 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
2154 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
2155 LocaleCompare(value,"true") == 0);
2158 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
2160 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
2164 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
2166 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
2167 GetUserSpaceCoordinateValue(svg_info,1,value));
2170 if (LocaleCompare(keyword,"stroke-linecap") == 0)
2172 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
2176 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
2178 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
2182 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
2184 (void) FormatLocaleFile(svg_info->file,
2185 "stroke-miterlimit \"%s\"\n",value);
2188 if (LocaleCompare(keyword,"stroke-opacity") == 0)
2190 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
2194 if (LocaleCompare(keyword,"stroke-width") == 0)
2196 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
2197 GetUserSpaceCoordinateValue(svg_info,1,value));
2200 if (LocaleCompare(keyword,"style") == 0)
2202 SVGProcessStyleElement(context,name,value);
2210 if (LocaleCompare(keyword,"text-align") == 0)
2212 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",
2216 if (LocaleCompare(keyword,"text-anchor") == 0)
2218 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
2222 if (LocaleCompare(keyword,"text-decoration") == 0)
2224 if (LocaleCompare(value,"underline") == 0)
2225 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
2226 if (LocaleCompare(value,"line-through") == 0)
2227 (void) FormatLocaleFile(svg_info->file,
2228 "decorate line-through\n");
2229 if (LocaleCompare(value,"overline") == 0)
2230 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
2233 if (LocaleCompare(keyword,"text-antialiasing") == 0)
2235 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
2236 LocaleCompare(value,"true") == 0);
2239 if (LocaleCompare(keyword,"transform") == 0)
2246 GetAffineMatrix(&transform);
2247 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
2248 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
2249 if (tokens == (char **) NULL)
2251 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2253 keyword=(char *) tokens[j];
2254 value=(char *) tokens[j+1];
2255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2256 " %s: %s",keyword,value);
2258 GetAffineMatrix(&affine);
2264 if (LocaleCompare(keyword,"matrix") == 0)
2266 p=(const char *) value;
2267 (void) GetNextToken(p,&p,MagickPathExtent,token);
2268 affine.sx=StringToDouble(value,(char **) NULL);
2269 (void) GetNextToken(p,&p,MagickPathExtent,token);
2271 (void) GetNextToken(p,&p,MagickPathExtent,token);
2272 affine.rx=StringToDouble(token,&next_token);
2273 (void) GetNextToken(p,&p,MagickPathExtent,token);
2275 (void) GetNextToken(p,&p,MagickPathExtent,token);
2276 affine.ry=StringToDouble(token,&next_token);
2277 (void) GetNextToken(p,&p,MagickPathExtent,token);
2279 (void) GetNextToken(p,&p,MagickPathExtent,token);
2280 affine.sy=StringToDouble(token,&next_token);
2281 (void) GetNextToken(p,&p,MagickPathExtent,token);
2283 (void) GetNextToken(p,&p,MagickPathExtent,token);
2284 affine.tx=StringToDouble(token,&next_token);
2285 (void) GetNextToken(p,&p,MagickPathExtent,token);
2287 (void) GetNextToken(p,&p,MagickPathExtent,token);
2288 affine.ty=StringToDouble(token,&next_token);
2296 if (LocaleCompare(keyword,"rotate") == 0)
2303 p=(const char *) value;
2304 (void) GetNextToken(p,&p,MagickPathExtent,token);
2305 angle=StringToDouble(value,(char **) NULL);
2306 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
2307 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
2308 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
2309 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
2310 (void) GetNextToken(p,&p,MagickPathExtent,token);
2312 (void) GetNextToken(p,&p,MagickPathExtent,token);
2313 x=StringToDouble(token,&next_token);
2314 (void) GetNextToken(p,&p,MagickPathExtent,token);
2316 (void) GetNextToken(p,&p,MagickPathExtent,token);
2317 y=StringToDouble(token,&next_token);
2318 affine.tx=svg_info->bounds.x+x*
2319 cos(DegreesToRadians(fmod(angle,360.0)))+y*
2320 sin(DegreesToRadians(fmod(angle,360.0)));
2321 affine.ty=svg_info->bounds.y-x*
2322 sin(DegreesToRadians(fmod(angle,360.0)))+y*
2323 cos(DegreesToRadians(fmod(angle,360.0)));
2333 if (LocaleCompare(keyword,"scale") == 0)
2335 for (p=(const char *) value; *p != '\0'; p++)
2336 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2339 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
2340 affine.sy=affine.sx;
2342 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
2344 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
2347 if (LocaleCompare(keyword,"skewX") == 0)
2349 affine.sx=svg_info->affine.sx;
2350 affine.ry=tan(DegreesToRadians(fmod(
2351 GetUserSpaceCoordinateValue(svg_info,1,value),
2353 affine.sy=svg_info->affine.sy;
2356 if (LocaleCompare(keyword,"skewY") == 0)
2358 affine.sx=svg_info->affine.sx;
2359 affine.rx=tan(DegreesToRadians(fmod(
2360 GetUserSpaceCoordinateValue(svg_info,-1,value),
2362 affine.sy=svg_info->affine.sy;
2370 if (LocaleCompare(keyword,"translate") == 0)
2372 for (p=(const char *) value; *p != '\0'; p++)
2373 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2376 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2379 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2388 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2389 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2390 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2391 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2392 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2394 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2397 (void) FormatLocaleFile(svg_info->file,
2398 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2399 transform.ry,transform.sy,transform.tx,transform.ty);
2400 for (j=0; tokens[j] != (char *) NULL; j++)
2401 tokens[j]=DestroyString(tokens[j]);
2402 tokens=(char **) RelinquishMagickMemory(tokens);
2410 if (LocaleCompare(keyword,"verts") == 0)
2412 (void) CloneString(&svg_info->vertices,value);
2415 if (LocaleCompare(keyword,"viewBox") == 0)
2417 p=(const char *) value;
2418 (void) GetNextToken(p,&p,MagickPathExtent,token);
2419 svg_info->view_box.x=StringToDouble(token,&next_token);
2420 (void) GetNextToken(p,&p,MagickPathExtent,token);
2422 (void) GetNextToken(p,&p,MagickPathExtent,token);
2423 svg_info->view_box.y=StringToDouble(token,&next_token);
2424 (void) GetNextToken(p,&p,MagickPathExtent,token);
2426 (void) GetNextToken(p,&p,MagickPathExtent,token);
2427 svg_info->view_box.width=StringToDouble(token,
2429 if (svg_info->bounds.width == 0)
2430 svg_info->bounds.width=svg_info->view_box.width;
2431 (void) GetNextToken(p,&p,MagickPathExtent,token);
2433 (void) GetNextToken(p,&p,MagickPathExtent,token);
2434 svg_info->view_box.height=StringToDouble(token,
2436 if (svg_info->bounds.height == 0)
2437 svg_info->bounds.height=svg_info->view_box.height;
2445 if (LocaleCompare(keyword,"width") == 0)
2447 svg_info->bounds.width=
2448 GetUserSpaceCoordinateValue(svg_info,1,value);
2456 if (LocaleCompare(keyword,"x") == 0)
2458 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2461 if (LocaleCompare(keyword,"xlink:href") == 0)
2463 (void) CloneString(&svg_info->url,value);
2466 if (LocaleCompare(keyword,"x1") == 0)
2468 svg_info->segment.x1=
2469 GetUserSpaceCoordinateValue(svg_info,1,value);
2472 if (LocaleCompare(keyword,"x2") == 0)
2474 svg_info->segment.x2=
2475 GetUserSpaceCoordinateValue(svg_info,1,value);
2483 if (LocaleCompare(keyword,"y") == 0)
2485 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2488 if (LocaleCompare(keyword,"y1") == 0)
2490 svg_info->segment.y1=
2491 GetUserSpaceCoordinateValue(svg_info,-1,value);
2494 if (LocaleCompare(keyword,"y2") == 0)
2496 svg_info->segment.y2=
2497 GetUserSpaceCoordinateValue(svg_info,-1,value);
2506 if (LocaleCompare((const char *) name,"svg") == 0)
2508 if (svg_info->document->encoding != (const xmlChar *) NULL)
2509 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2510 (const char *) svg_info->document->encoding);
2511 if (attributes != (const xmlChar **) NULL)
2519 if ((svg_info->view_box.width == 0.0) ||
2520 (svg_info->view_box.height == 0.0))
2521 svg_info->view_box=svg_info->bounds;
2523 if (svg_info->bounds.width > 0.0)
2524 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2526 if (svg_info->bounds.height > 0.0)
2527 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2528 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2529 (double) svg_info->width,(double) svg_info->height);
2530 sx=PerceptibleReciprocal(svg_info->view_box.width)*svg_info->width;
2531 sy=PerceptibleReciprocal(svg_info->view_box.height)*svg_info->height;
2532 tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2534 ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2536 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2538 if ((svg_info->svgDepth == 1) && (*background != '\0'))
2540 PushGraphicContext(id);
2541 (void) FormatLocaleFile(svg_info->file,"fill %s\n",background);
2542 (void) FormatLocaleFile(svg_info->file,
2543 "rectangle 0,0 %g,%g\n",svg_info->view_box.width,
2544 svg_info->view_box.height);
2545 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2549 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2550 if (units != (char *) NULL)
2551 units=DestroyString(units);
2552 if (color != (char *) NULL)
2553 color=DestroyString(color);
2556 static void SVGEndElement(void *context,const xmlChar *name)
2562 Called when the end of an element has been detected.
2564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2565 " SAX.endElement(%s)",name);
2566 svg_info=(SVGInfo *) context;
2567 if (strchr((char *) name,':') != (char *) NULL)
2570 Skip over namespace.
2572 for ( ; *name != ':'; name++) ;
2580 if (LocaleCompare((const char *) name,"circle") == 0)
2582 (void) FormatLocaleFile(svg_info->file,"class \"circle\"\n");
2583 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2584 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2585 svg_info->element.cy+svg_info->element.minor);
2586 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2589 if (LocaleCompare((const char *) name,"clipPath") == 0)
2591 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2599 if (LocaleCompare((const char *) name,"defs") == 0)
2601 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2604 if (LocaleCompare((const char *) name,"desc") == 0)
2609 if (*svg_info->text == '\0')
2611 (void) fputc('#',svg_info->file);
2612 for (p=svg_info->text; *p != '\0'; p++)
2614 (void) fputc(*p,svg_info->file);
2616 (void) fputc('#',svg_info->file);
2618 (void) fputc('\n',svg_info->file);
2619 *svg_info->text='\0';
2627 if (LocaleCompare((const char *) name,"ellipse") == 0)
2632 (void) FormatLocaleFile(svg_info->file,"class \"ellipse\"\n");
2633 angle=svg_info->element.angle;
2634 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2635 svg_info->element.cx,svg_info->element.cy,
2636 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2637 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2638 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2646 if (LocaleCompare((const char *) name,"foreignObject") == 0)
2648 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2656 if (LocaleCompare((const char *) name,"g") == 0)
2658 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2666 if (LocaleCompare((const char *) name,"image") == 0)
2668 (void) FormatLocaleFile(svg_info->file,
2669 "image Over %g,%g %g,%g \"%s\"\n",svg_info->bounds.x,
2670 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2672 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2680 if (LocaleCompare((const char *) name,"line") == 0)
2682 (void) FormatLocaleFile(svg_info->file,"class \"line\"\n");
2683 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2684 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2685 svg_info->segment.y2);
2686 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2689 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2691 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2699 if (LocaleCompare((const char *) name,"mask") == 0)
2701 (void) FormatLocaleFile(svg_info->file,"pop mask\n");
2709 if (LocaleCompare((const char *) name,"pattern") == 0)
2711 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2714 if (LocaleCompare((const char *) name,"path") == 0)
2716 (void) FormatLocaleFile(svg_info->file,"class \"path\"\n");
2717 (void) FormatLocaleFile(svg_info->file,"path \"%s\"\n",
2718 svg_info->vertices);
2719 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2722 if (LocaleCompare((const char *) name,"polygon") == 0)
2724 (void) FormatLocaleFile(svg_info->file,"class \"polygon\"\n");
2725 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2726 svg_info->vertices);
2727 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2730 if (LocaleCompare((const char *) name,"polyline") == 0)
2732 (void) FormatLocaleFile(svg_info->file,"class \"polyline\"\n");
2733 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2734 svg_info->vertices);
2735 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2743 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2745 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2748 if (LocaleCompare((const char *) name,"rect") == 0)
2750 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2752 (void) FormatLocaleFile(svg_info->file,"class \"rect\"\n");
2753 if ((fabs(svg_info->bounds.width-1.0) < MagickEpsilon) &&
2754 (fabs(svg_info->bounds.height-1.0) < MagickEpsilon))
2755 (void) FormatLocaleFile(svg_info->file,"point %g,%g\n",
2756 svg_info->bounds.x,svg_info->bounds.y);
2758 (void) FormatLocaleFile(svg_info->file,
2759 "rectangle %g,%g %g,%g\n",svg_info->bounds.x,
2760 svg_info->bounds.y,svg_info->bounds.x+svg_info->bounds.width,
2761 svg_info->bounds.y+svg_info->bounds.height);
2762 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2765 if (svg_info->radius.x == 0.0)
2766 svg_info->radius.x=svg_info->radius.y;
2767 if (svg_info->radius.y == 0.0)
2768 svg_info->radius.y=svg_info->radius.x;
2769 (void) FormatLocaleFile(svg_info->file,
2770 "roundRectangle %g,%g %g,%g %g,%g\n",
2771 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2772 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2773 svg_info->radius.x,svg_info->radius.y);
2774 svg_info->radius.x=0.0;
2775 svg_info->radius.y=0.0;
2776 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2784 if (LocaleCompare((const char *) name,"stop") == 0)
2786 (void) FormatLocaleFile(svg_info->file,"stop-color \"%s\" %s\n",
2787 svg_info->stop_color,svg_info->offset);
2790 if (LocaleCompare((char *) name,"style") == 0)
2804 Find style definitions in svg_info->text.
2806 tokens=SVGKeyValuePairs(context,'{','}',svg_info->text,
2808 if (tokens == (char **) NULL)
2810 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2812 keyword=(char *) tokens[j];
2813 value=(char *) tokens[j+1];
2814 (void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
2815 *keyword == '.' ? keyword+1 : keyword);
2816 SVGProcessStyleElement(context,name,value);
2817 (void) FormatLocaleFile(svg_info->file,"pop class\n");
2819 for (j=0; tokens[j] != (char *) NULL; j++)
2820 tokens[j]=DestroyString(tokens[j]);
2821 tokens=(char **) RelinquishMagickMemory(tokens);
2824 if (LocaleCompare((const char *) name,"svg") == 0)
2826 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2827 svg_info->svgDepth--;
2830 if (LocaleCompare((const char *) name,"symbol") == 0)
2832 (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
2840 if (LocaleCompare((const char *) name,"text") == 0)
2842 if (*svg_info->text != '\0')
2847 SVGStripString(MagickTrue,svg_info->text);
2848 text=EscapeString(svg_info->text,'\"');
2849 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2850 svg_info->text_offset.x,svg_info->text_offset.y,text);
2851 text=DestroyString(text);
2852 *svg_info->text='\0';
2854 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2857 if (LocaleCompare((const char *) name,"tspan") == 0)
2859 if (*svg_info->text != '\0')
2864 (void) FormatLocaleFile(svg_info->file,"class \"tspan\"\n");
2865 text=EscapeString(svg_info->text,'\"');
2866 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2867 svg_info->bounds.x,svg_info->bounds.y,text);
2868 text=DestroyString(text);
2869 *svg_info->text='\0';
2871 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2874 if (LocaleCompare((const char *) name,"title") == 0)
2876 if (*svg_info->text == '\0')
2878 (void) CloneString(&svg_info->title,svg_info->text);
2879 *svg_info->text='\0';
2887 if (LocaleCompare((char *) name,"use") == 0)
2889 if ((svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0))
2890 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2891 svg_info->bounds.x,svg_info->bounds.y);
2892 (void) FormatLocaleFile(svg_info->file,"use \"url(%s)\"\n",
2894 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2902 *svg_info->text='\0';
2903 (void) memset(&svg_info->element,0,sizeof(svg_info->element));
2904 (void) memset(&svg_info->segment,0,sizeof(svg_info->segment));
2908 static void SVGCharacters(void *context,const xmlChar *c,int length)
2923 Receiving some characters from the parser.
2925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2926 " SAX.characters(%s,%.20g)",c,(double) length);
2927 svg_info=(SVGInfo *) context;
2928 text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2929 if (text == (char *) NULL)
2932 for (i=0; i < (ssize_t) length; i++)
2935 SVGStripString(MagickFalse,text);
2936 if (svg_info->text == (char *) NULL)
2937 svg_info->text=text;
2940 (void) ConcatenateString(&svg_info->text,text);
2941 text=DestroyString(text);
2945 static void SVGReference(void *context,const xmlChar *name)
2954 Called when an entity reference is detected.
2956 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2958 svg_info=(SVGInfo *) context;
2959 parser=svg_info->parser;
2960 if (parser == (xmlParserCtxtPtr) NULL)
2962 if (parser->node == (xmlNodePtr) NULL)
2965 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2967 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2970 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2976 Receiving some ignorable whitespaces from the parser.
2978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2979 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2980 svg_info=(SVGInfo *) context;
2984 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2985 const xmlChar *data)
2991 A processing instruction has been parsed.
2993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2994 " SAX.processingInstruction(%s, %s)",target,data);
2995 svg_info=(SVGInfo *) context;
2999 static void SVGComment(void *context,const xmlChar *value)
3005 A comment has been parsed.
3007 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
3009 svg_info=(SVGInfo *) context;
3010 if (svg_info->comment != (char *) NULL)
3011 (void) ConcatenateString(&svg_info->comment,"\n");
3012 (void) ConcatenateString(&svg_info->comment,(const char *) value);
3015 static void SVGWarning(void *,const char *,...)
3016 magick_attribute((__format__ (__printf__,2,3)));
3018 static void SVGWarning(void *context,const char *format,...)
3022 reason[MagickPathExtent];
3031 Display and format a warning messages, gives file, line, position and
3034 va_start(operands,format);
3035 svg_info=(SVGInfo *) context;
3036 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
3037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
3038 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
3039 (void) vsprintf(reason,format,operands);
3041 (void) vsnprintf(reason,MagickPathExtent,format,operands);
3043 message=GetExceptionMessage(errno);
3044 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
3045 DelegateWarning,reason,"`%s`",message);
3046 message=DestroyString(message);
3050 static void SVGError(void *,const char *,...)
3051 magick_attribute((__format__ (__printf__,2,3)));
3053 static void SVGError(void *context,const char *format,...)
3057 reason[MagickPathExtent];
3066 Display and format a error formats, gives file, line, position and
3069 va_start(operands,format);
3070 svg_info=(SVGInfo *) context;
3071 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
3072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
3073 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
3074 (void) vsprintf(reason,format,operands);
3076 (void) vsnprintf(reason,MagickPathExtent,format,operands);
3078 message=GetExceptionMessage(errno);
3079 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
3080 reason,"`%s`",message);
3081 message=DestroyString(message);
3085 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
3097 Called when a pcdata block has been parsed.
3099 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
3101 svg_info=(SVGInfo *) context;
3102 parser=svg_info->parser;
3103 child=xmlGetLastChild(parser->node);
3104 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
3106 xmlTextConcat(child,value,length);
3109 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
3112 static void SVGExternalSubset(void *context,const xmlChar *name,
3113 const xmlChar *external_id,const xmlChar *system_id)
3128 Does this document has an external subset?
3130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3131 " SAX.externalSubset(%s, %s, %s)",name,
3132 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
3133 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
3134 svg_info=(SVGInfo *) context;
3135 parser=svg_info->parser;
3136 if (((external_id == NULL) && (system_id == NULL)) ||
3137 ((parser->validate == 0) || (parser->wellFormed == 0) ||
3138 (svg_info->document == 0)))
3140 input=SVGResolveEntity(context,external_id,system_id);
3143 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
3144 parser_context=(*parser);
3145 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
3146 if (parser->inputTab == (xmlParserInputPtr *) NULL)
3148 parser->errNo=XML_ERR_NO_MEMORY;
3149 parser->input=parser_context.input;
3150 parser->inputNr=parser_context.inputNr;
3151 parser->inputMax=parser_context.inputMax;
3152 parser->inputTab=parser_context.inputTab;
3158 xmlPushInput(parser,input);
3159 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
3160 if (input->filename == (char *) NULL)
3161 input->filename=(char *) xmlStrdup(system_id);
3164 input->base=parser->input->cur;
3165 input->cur=parser->input->cur;
3167 xmlParseExternalSubset(parser,external_id,system_id);
3168 while (parser->inputNr > 1)
3169 (void) xmlPopInput(parser);
3170 xmlFreeInputStream(parser->input);
3171 xmlFree(parser->inputTab);
3172 parser->input=parser_context.input;
3173 parser->inputNr=parser_context.inputNr;
3174 parser->inputMax=parser_context.inputMax;
3175 parser->inputTab=parser_context.inputTab;
3178 #if defined(__cplusplus) || defined(c_plusplus)
3182 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3185 filename[MagickPathExtent];
3205 message[MagickPathExtent];
3216 assert(image_info != (const ImageInfo *) NULL);
3217 assert(image_info->signature == MagickCoreSignature);
3218 assert(exception != (ExceptionInfo *) NULL);
3219 if (image_info->debug != MagickFalse)
3220 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3221 image_info->filename);
3222 assert(exception->signature == MagickCoreSignature);
3223 image=AcquireImage(image_info,exception);
3224 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3225 if (status == MagickFalse)
3227 image=DestroyImageList(image);
3228 return((Image *) NULL);
3230 if ((fabs(image->resolution.x) < MagickEpsilon) ||
3231 (fabs(image->resolution.y) < MagickEpsilon))
3239 flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3240 image->resolution.x=geometry_info.rho;
3241 image->resolution.y=geometry_info.sigma;
3242 if ((flags & SigmaValue) == 0)
3243 image->resolution.y=image->resolution.x;
3245 if (LocaleCompare(image_info->magick,"MSVG") != 0)
3250 svg_image=RenderSVGImage(image_info,image,exception);
3251 if (svg_image != (Image *) NULL)
3253 image=DestroyImageList(image);
3257 #if defined(MAGICKCORE_RSVG_DELEGATE)
3258 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3271 register unsigned char
3284 register const guchar
3306 svg_handle=rsvg_handle_new();
3307 if (svg_handle == (RsvgHandle *) NULL)
3308 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3309 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
3310 if ((fabs(image->resolution.x) > MagickEpsilon) &&
3311 (fabs(image->resolution.y) > MagickEpsilon))
3312 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3313 image->resolution.y);
3314 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3317 error=(GError *) NULL;
3318 (void) rsvg_handle_write(svg_handle,message,n,&error);
3319 if (error != (GError *) NULL)
3320 g_error_free(error);
3322 error=(GError *) NULL;
3323 rsvg_handle_close(svg_handle,&error);
3324 if (error != (GError *) NULL)
3325 g_error_free(error);
3326 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3327 apply_density=MagickTrue;
3328 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
3329 if ((image->resolution.x > 0.0) && (image->resolution.y > 0.0))
3335 We should not apply the density when the internal 'factor' is 'i'.
3336 This can be checked by using the trick below.
3338 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x*256,
3339 image->resolution.y*256);
3340 rsvg_handle_get_dimensions(svg_handle,&dpi_dimension_info);
3341 if ((dpi_dimension_info.width != dimension_info.width) ||
3342 (dpi_dimension_info.height != dimension_info.height))
3343 apply_density=MagickFalse;
3344 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3345 image->resolution.y);
3347 if (image_info->size != (char *) NULL)
3349 (void) GetGeometry(image_info->size,(ssize_t *) NULL,
3350 (ssize_t *) NULL,&image->columns,&image->rows);
3351 if ((image->columns != 0) || (image->rows != 0))
3353 image->resolution.x=DefaultSVGDensity*image->columns/
3354 dimension_info.width;
3355 image->resolution.y=DefaultSVGDensity*image->rows/
3356 dimension_info.height;
3357 if (fabs(image->resolution.x) < MagickEpsilon)
3358 image->resolution.x=image->resolution.y;
3360 if (fabs(image->resolution.y) < MagickEpsilon)
3361 image->resolution.y=image->resolution.x;
3363 image->resolution.x=image->resolution.y=MagickMin(
3364 image->resolution.x,image->resolution.y);
3365 apply_density=MagickTrue;
3368 if (apply_density != MagickFalse)
3370 image->columns=image->resolution.x*dimension_info.width/
3372 image->rows=image->resolution.y*dimension_info.height/
3377 image->columns=dimension_info.width;
3378 image->rows=dimension_info.height;
3380 pixel_info=(MemoryInfo *) NULL;
3382 pixel_buffer=rsvg_handle_get_pixbuf(svg_handle);
3383 rsvg_handle_free(svg_handle);
3384 image->columns=gdk_pixbuf_get_width(pixel_buffer);
3385 image->rows=gdk_pixbuf_get_height(pixel_buffer);
3387 image->alpha_trait=BlendPixelTrait;
3388 if (image_info->ping == MagickFalse)
3390 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3395 status=SetImageExtent(image,image->columns,image->rows,exception);
3396 if (status == MagickFalse)
3398 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
3399 g_object_unref(G_OBJECT(pixel_buffer));
3401 g_object_unref(svg_handle);
3402 ThrowReaderException(MissingDelegateError,
3403 "NoDecodeDelegateForThisImageFormat");
3405 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3406 stride=4*image->columns;
3407 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
3408 stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
3409 (int) image->columns);
3411 pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels));
3412 if (pixel_info == (MemoryInfo *) NULL)
3414 g_object_unref(svg_handle);
3415 ThrowReaderException(ResourceLimitError,
3416 "MemoryAllocationFailed");
3418 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3420 (void) SetImageBackgroundColor(image,exception);
3421 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3422 cairo_surface=cairo_image_surface_create_for_data(pixels,
3423 CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int)
3425 if ((cairo_surface == (cairo_surface_t *) NULL) ||
3426 (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS))
3428 if (cairo_surface != (cairo_surface_t *) NULL)
3429 cairo_surface_destroy(cairo_surface);
3430 pixel_info=RelinquishVirtualMemory(pixel_info);
3431 g_object_unref(svg_handle);
3432 ThrowReaderException(ResourceLimitError,
3433 "MemoryAllocationFailed");
3435 cairo_image=cairo_create(cairo_surface);
3436 cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
3437 cairo_paint(cairo_image);
3438 cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
3439 if (apply_density != MagickFalse)
3440 cairo_scale(cairo_image,image->resolution.x/DefaultSVGDensity,
3441 image->resolution.y/DefaultSVGDensity);
3442 rsvg_handle_render_cairo(svg_handle,cairo_image);
3443 cairo_destroy(cairo_image);
3444 cairo_surface_destroy(cairo_surface);
3445 g_object_unref(svg_handle);
3448 p=gdk_pixbuf_get_pixels(pixel_buffer);
3450 GetPixelInfo(image,&fill_color);
3451 for (y=0; y < (ssize_t) image->rows; y++)
3453 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3454 if (q == (Quantum *) NULL)
3456 for (x=0; x < (ssize_t) image->columns; x++)
3458 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3459 fill_color.blue=ScaleCharToQuantum(*p++);
3460 fill_color.green=ScaleCharToQuantum(*p++);
3461 fill_color.red=ScaleCharToQuantum(*p++);
3463 fill_color.red=ScaleCharToQuantum(*p++);
3464 fill_color.green=ScaleCharToQuantum(*p++);
3465 fill_color.blue=ScaleCharToQuantum(*p++);
3467 fill_color.alpha=ScaleCharToQuantum(*p++);
3468 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3473 gamma=QuantumScale*fill_color.alpha;
3474 gamma=PerceptibleReciprocal(gamma);
3475 fill_color.blue*=gamma;
3476 fill_color.green*=gamma;
3477 fill_color.red*=gamma;
3480 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
3481 GetPixelAlpha(image,q),q);
3482 q+=GetPixelChannels(image);
3484 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3486 if (image->previous == (Image *) NULL)
3488 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
3490 if (status == MagickFalse)
3495 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3496 if (pixel_info != (MemoryInfo *) NULL)
3497 pixel_info=RelinquishVirtualMemory(pixel_info);
3499 g_object_unref(G_OBJECT(pixel_buffer));
3501 (void) CloseBlob(image);
3502 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3504 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3505 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3506 next=GetNextImageInList(next);
3508 return(GetFirstImageInList(image));
3516 unique_file=AcquireUniqueFileResource(filename);
3517 if (unique_file != -1)
3518 file=fdopen(unique_file,"w");
3519 if ((unique_file == -1) || (file == (FILE *) NULL))
3521 (void) CopyMagickString(image->filename,filename,MagickPathExtent);
3522 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
3524 image=DestroyImageList(image);
3525 return((Image *) NULL);
3530 svg_info=AcquireSVGInfo();
3531 if (svg_info == (SVGInfo *) NULL)
3533 (void) fclose(file);
3534 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3536 svg_info->file=file;
3537 svg_info->exception=exception;
3538 svg_info->image=image;
3539 svg_info->image_info=image_info;
3540 svg_info->bounds.width=image->columns;
3541 svg_info->bounds.height=image->rows;
3542 svg_info->svgDepth=0;
3543 if (image_info->size != (char *) NULL)
3544 (void) CloneString(&svg_info->size,image_info->size);
3545 if (image->debug != MagickFalse)
3546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3548 (void) xmlSubstituteEntitiesDefault(1);
3549 (void) memset(&sax_modules,0,sizeof(sax_modules));
3550 sax_modules.internalSubset=SVGInternalSubset;
3551 sax_modules.isStandalone=SVGIsStandalone;
3552 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3553 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3554 sax_modules.resolveEntity=SVGResolveEntity;
3555 sax_modules.getEntity=SVGGetEntity;
3556 sax_modules.entityDecl=SVGEntityDeclaration;
3557 sax_modules.notationDecl=SVGNotationDeclaration;
3558 sax_modules.attributeDecl=SVGAttributeDeclaration;
3559 sax_modules.elementDecl=SVGElementDeclaration;
3560 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3561 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3562 sax_modules.startDocument=SVGStartDocument;
3563 sax_modules.endDocument=SVGEndDocument;
3564 sax_modules.startElement=SVGStartElement;
3565 sax_modules.endElement=SVGEndElement;
3566 sax_modules.reference=SVGReference;
3567 sax_modules.characters=SVGCharacters;
3568 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3569 sax_modules.processingInstruction=SVGProcessingInstructions;
3570 sax_modules.comment=SVGComment;
3571 sax_modules.warning=SVGWarning;
3572 sax_modules.error=SVGError;
3573 sax_modules.fatalError=SVGError;
3574 sax_modules.getParameterEntity=SVGGetParameterEntity;
3575 sax_modules.cdataBlock=SVGCDataBlock;
3576 sax_modules.externalSubset=SVGExternalSubset;
3577 sax_handler=(&sax_modules);
3578 n=ReadBlob(image,MagickPathExtent-1,message);
3582 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3583 message,n,image->filename);
3584 (void) xmlCtxtUseOptions(svg_info->parser,XML_PARSE_HUGE);
3585 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3588 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3593 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3594 SVGEndDocument(svg_info);
3595 xmlFreeParserCtxt(svg_info->parser);
3596 if (image->debug != MagickFalse)
3597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3598 (void) fclose(file);
3599 (void) CloseBlob(image);
3600 image->columns=svg_info->width;
3601 image->rows=svg_info->height;
3602 if (exception->severity >= ErrorException)
3604 svg_info=DestroySVGInfo(svg_info);
3605 (void) RelinquishUniqueFileResource(filename);
3606 image=DestroyImage(image);
3607 return((Image *) NULL);
3609 if (image_info->ping == MagickFalse)
3617 image=DestroyImage(image);
3618 image=(Image *) NULL;
3619 read_info=CloneImageInfo(image_info);
3620 SetImageInfoBlob(read_info,(void *) NULL,0);
3621 (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
3623 image=ReadImage(read_info,exception);
3624 read_info=DestroyImageInfo(read_info);
3625 if (image != (Image *) NULL)
3626 (void) CopyMagickString(image->filename,image_info->filename,
3630 Relinquish resources.
3632 if (image != (Image *) NULL)
3634 if (svg_info->title != (char *) NULL)
3635 (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3636 if (svg_info->comment != (char *) NULL)
3637 (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3640 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3642 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3643 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3644 next=GetNextImageInList(next);
3646 svg_info=DestroySVGInfo(svg_info);
3647 (void) RelinquishUniqueFileResource(filename);
3648 return(GetFirstImageInList(image));
3651 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3660 assert(image_info != (const ImageInfo *) NULL);
3661 assert(image_info->signature == MagickCoreSignature);
3662 assert(exception != (ExceptionInfo *) NULL);
3663 if (image_info->debug != MagickFalse)
3664 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3665 image_info->filename);
3666 assert(exception->signature == MagickCoreSignature);
3667 image=AcquireImage(image_info,exception);
3668 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3669 if (status == MagickFalse)
3671 image=DestroyImageList(image);
3672 return((Image *) NULL);
3674 if ((fabs(image->resolution.x) < MagickEpsilon) ||
3675 (fabs(image->resolution.y) < MagickEpsilon))
3683 flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3684 image->resolution.x=geometry_info.rho;
3685 image->resolution.y=geometry_info.sigma;
3686 if ((flags & SigmaValue) == 0)
3687 image->resolution.y=image->resolution.x;
3689 svg_image=RenderSVGImage(image_info,image,exception);
3690 image=DestroyImage(image);
3696 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3700 % R e g i s t e r S V G I m a g e %
3704 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3706 % RegisterSVGImage() adds attributes for the SVG image format to
3707 % the list of supported formats. The attributes include the image format
3708 % tag, a method to read and/or write the format, whether the format
3709 % supports the saving of more than one frame to the same file or blob,
3710 % whether the format supports native in-memory I/O, and a brief
3711 % description of the format.
3713 % The format of the RegisterSVGImage method is:
3715 % size_t RegisterSVGImage(void)
3718 ModuleExport size_t RegisterSVGImage(void)
3721 version[MagickPathExtent];
3727 #if defined(LIBXML_DOTTED_VERSION)
3728 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,
3731 #if defined(MAGICKCORE_RSVG_DELEGATE)
3732 #if !GLIB_CHECK_VERSION(2,35,0)
3735 (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d",
3736 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3738 entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics");
3739 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3740 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3741 entry->flags^=CoderBlobSupportFlag;
3742 #if defined(MAGICKCORE_RSVG_DELEGATE)
3743 entry->flags^=CoderDecoderThreadSupportFlag;
3745 entry->mime_type=ConstantString("image/svg+xml");
3746 if (*version != '\0')
3747 entry->version=ConstantString(version);
3748 entry->magick=(IsImageFormatHandler *) IsSVG;
3749 (void) RegisterMagickInfo(entry);
3750 entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics");
3751 #if defined(MAGICKCORE_XML_DELEGATE)
3752 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3754 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3755 entry->flags^=CoderBlobSupportFlag;
3756 #if defined(MAGICKCORE_RSVG_DELEGATE)
3757 entry->flags^=CoderDecoderThreadSupportFlag;
3759 entry->mime_type=ConstantString("image/svg+xml");
3760 if (*version != '\0')
3761 entry->version=ConstantString(version);
3762 entry->magick=(IsImageFormatHandler *) IsSVG;
3763 (void) RegisterMagickInfo(entry);
3764 entry=AcquireMagickInfo("SVG","MSVG",
3765 "ImageMagick's own SVG internal renderer");
3766 #if defined(MAGICKCORE_XML_DELEGATE)
3767 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3769 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3770 entry->flags^=CoderBlobSupportFlag;
3771 #if defined(MAGICKCORE_RSVG_DELEGATE)
3772 entry->flags^=CoderDecoderThreadSupportFlag;
3774 entry->magick=(IsImageFormatHandler *) IsSVG;
3775 (void) RegisterMagickInfo(entry);
3776 return(MagickImageCoderSignature);
3780 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3784 % U n r e g i s t e r S V G I m a g e %
3788 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3790 % UnregisterSVGImage() removes format registrations made by the
3791 % SVG module from the list of supported formats.
3793 % The format of the UnregisterSVGImage method is:
3795 % UnregisterSVGImage(void)
3798 ModuleExport void UnregisterSVGImage(void)
3800 (void) UnregisterMagickInfo("SVGZ");
3801 (void) UnregisterMagickInfo("SVG");
3802 (void) UnregisterMagickInfo("MSVG");
3806 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3810 % W r i t e S V G I m a g e %
3814 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3816 % WriteSVGImage() writes a image in the SVG - XML based W3C standard
3819 % The format of the WriteSVGImage method is:
3821 % MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3822 % Image *image,ExceptionInfo *exception)
3824 % A description of each parameter follows.
3826 % o image_info: the image info.
3828 % o image: The image.
3830 % o exception: return any errors or warnings in this structure.
3834 static void AffineToTransform(Image *image,AffineMatrix *affine)
3837 transform[MagickPathExtent];
3839 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3841 if ((fabs(affine->rx) < MagickEpsilon) &&
3842 (fabs(affine->ry) < MagickEpsilon))
3844 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3845 (fabs(affine->sy-1.0) < MagickEpsilon))
3847 (void) WriteBlobString(image,"\">\n");
3850 (void) FormatLocaleString(transform,MagickPathExtent,
3851 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3852 (void) WriteBlobString(image,transform);
3857 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3858 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3859 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3865 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3866 (void) FormatLocaleString(transform,MagickPathExtent,
3867 "\" transform=\"rotate(%g)\">\n",theta);
3868 (void) WriteBlobString(image,transform);
3875 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3876 (fabs(affine->rx) < MagickEpsilon) &&
3877 (fabs(affine->ry) < MagickEpsilon) &&
3878 (fabs(affine->sy-1.0) < MagickEpsilon))
3880 (void) FormatLocaleString(transform,MagickPathExtent,
3881 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3882 (void) WriteBlobString(image,transform);
3886 (void) FormatLocaleString(transform,MagickPathExtent,
3887 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3888 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3889 (void) WriteBlobString(image,transform);
3892 static MagickBooleanType IsPoint(const char *point)
3900 value=(ssize_t) strtol(point,&p,10);
3902 return(p != point ? MagickTrue : MagickFalse);
3905 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
3907 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3912 at_fitting_opts_type
3924 register const Quantum
3938 Trace image and write as SVG.
3940 fitting_options=at_fitting_opts_new();
3941 output_options=at_output_opts_new();
3942 (void) SetImageGray(image,exception);
3943 type=GetImageType(image);
3945 if ((type == BilevelType) || (type == GrayscaleType))
3947 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3949 for (y=0; y < (ssize_t) image->rows; y++)
3951 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3952 if (p == (const Quantum *) NULL)
3954 for (x=0; x < (ssize_t) image->columns; x++)
3956 trace->bitmap[i++]=GetPixelRed(image,p);
3957 if (number_planes == 3)
3959 trace->bitmap[i++]=GetPixelGreen(image,p);
3960 trace->bitmap[i++]=GetPixelBlue(image,p);
3962 p+=GetPixelChannels(image);
3965 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3967 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3968 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3973 at_splines_free(splines);
3974 at_bitmap_free(trace);
3975 at_output_opts_free(output_options);
3976 at_fitting_opts_free(fitting_options);
3982 filename[MagickPathExtent],
3983 message[MagickPathExtent];
4010 delegate_info=GetDelegateInfo((char *) NULL,"TRACE",exception);
4011 if (delegate_info != (DelegateInfo *) NULL)
4014 Trace SVG with tracing delegate.
4016 image_info=AcquireImageInfo();
4017 (void) CopyMagickString(image_info->magick,"TRACE",MagickPathExtent);
4018 (void) FormatLocaleString(filename,MagickPathExtent,"trace:%s",
4019 image_info->filename);
4020 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
4021 status=WriteImage(image_info,image,exception);
4022 image_info=DestroyImageInfo(image_info);
4025 (void) WriteBlobString(image,
4026 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
4027 (void) WriteBlobString(image,
4028 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
4029 (void) WriteBlobString(image,
4030 " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
4031 (void) FormatLocaleString(message,MagickPathExtent,
4032 "<svg version=\"1.1\" id=\"Layer_1\" "
4033 "xmlns=\"http://www.w3.org/2000/svg\" "
4034 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" "
4035 "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" "
4036 "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">",
4037 (double) image->columns,(double) image->rows,
4038 (double) image->columns,(double) image->rows,
4039 (double) image->columns,(double) image->rows);
4040 (void) WriteBlobString(image,message);
4041 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4042 if (clone_image == (Image *) NULL)
4043 return(MagickFalse);
4044 image_info=AcquireImageInfo();
4045 (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent);
4047 blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length,
4049 clone_image=DestroyImage(clone_image);
4050 image_info=DestroyImageInfo(image_info);
4051 if (blob == (unsigned char *) NULL)
4052 return(MagickFalse);
4054 base64=Base64Encode(blob,blob_length,&encode_length);
4055 blob=(unsigned char *) RelinquishMagickMemory(blob);
4056 (void) FormatLocaleString(message,MagickPathExtent,
4057 " <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" "
4058 "x=\"%.20g\" y=\"%.20g\"\n href=\"data:image/png;base64,",
4059 (double) image->scene,(double) image->columns,(double) image->rows,
4060 (double) image->page.x,(double) image->page.y);
4061 (void) WriteBlobString(image,message);
4063 for (i=(ssize_t) encode_length; i > 0; i-=76)
4065 (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p);
4066 (void) WriteBlobString(image,message);
4069 (void) WriteBlobString(image,"\n");
4071 base64=DestroyString(base64);
4072 (void) WriteBlobString(image,"\" />\n");
4073 (void) WriteBlobString(image,"</svg>\n");
4076 (void) CloseBlob(image);
4080 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
4081 ExceptionInfo *exception)
4083 #define BezierQuantum 200
4089 keyword[MagickPathExtent],
4090 message[MagickPathExtent],
4091 name[MagickPathExtent],
4094 type[MagickPathExtent];
4135 Open output image file.
4137 assert(image_info != (const ImageInfo *) NULL);
4138 assert(image_info->signature == MagickCoreSignature);
4139 assert(image != (Image *) NULL);
4140 assert(image->signature == MagickCoreSignature);
4141 if (image->debug != MagickFalse)
4142 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4143 assert(exception != (ExceptionInfo *) NULL);
4144 assert(exception->signature == MagickCoreSignature);
4145 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
4146 if (status == MagickFalse)
4148 value=GetImageArtifact(image,"SVG");
4149 if (value != (char *) NULL)
4151 (void) WriteBlobString(image,value);
4152 (void) CloseBlob(image);
4155 value=GetImageArtifact(image,"mvg:vector-graphics");
4156 if (value == (char *) NULL)
4157 return(TraceSVGImage(image,exception));
4161 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
4162 (void) WriteBlobString(image,
4163 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
4164 (void) WriteBlobString(image,
4165 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
4166 (void) FormatLocaleString(message,MagickPathExtent,
4167 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
4169 (void) WriteBlobString(image,message);
4171 Allocate primitive info memory.
4174 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
4175 sizeof(*primitive_info));
4176 if (primitive_info == (PrimitiveInfo *) NULL)
4177 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
4178 GetAffineMatrix(&affine);
4179 token=AcquireString(value);
4180 extent=strlen(token)+MagickPathExtent;
4184 for (q=(const char *) value; *q != '\0'; )
4187 Interpret graphic primitive.
4189 (void) GetNextToken(q,&q,MagickPathExtent,keyword);
4190 if (*keyword == '\0')
4192 if (*keyword == '#')
4197 if (active != MagickFalse)
4199 AffineToTransform(image,&affine);
4202 (void) WriteBlobString(image,"<desc>");
4203 (void) WriteBlobString(image,keyword+1);
4204 for ( ; (*q != '\n') && (*q != '\0'); q++)
4207 case '<': (void) WriteBlobString(image,"<"); break;
4208 case '>': (void) WriteBlobString(image,">"); break;
4209 case '&': (void) WriteBlobString(image,"&"); break;
4210 default: (void) WriteBlobByte(image,(unsigned char) *q); break;
4212 (void) WriteBlobString(image,"</desc>\n");
4215 primitive_type=UndefinedPrimitive;
4223 if (LocaleCompare("affine",keyword) == 0)
4225 (void) GetNextToken(q,&q,extent,token);
4226 affine.sx=StringToDouble(token,&next_token);
4227 (void) GetNextToken(q,&q,extent,token);
4229 (void) GetNextToken(q,&q,extent,token);
4230 affine.rx=StringToDouble(token,&next_token);
4231 (void) GetNextToken(q,&q,extent,token);
4233 (void) GetNextToken(q,&q,extent,token);
4234 affine.ry=StringToDouble(token,&next_token);
4235 (void) GetNextToken(q,&q,extent,token);
4237 (void) GetNextToken(q,&q,extent,token);
4238 affine.sy=StringToDouble(token,&next_token);
4239 (void) GetNextToken(q,&q,extent,token);
4241 (void) GetNextToken(q,&q,extent,token);
4242 affine.tx=StringToDouble(token,&next_token);
4243 (void) GetNextToken(q,&q,extent,token);
4245 (void) GetNextToken(q,&q,extent,token);
4246 affine.ty=StringToDouble(token,&next_token);
4249 if (LocaleCompare("alpha",keyword) == 0)
4251 primitive_type=AlphaPrimitive;
4254 if (LocaleCompare("angle",keyword) == 0)
4256 (void) GetNextToken(q,&q,extent,token);
4257 affine.rx=StringToDouble(token,&next_token);
4258 affine.ry=StringToDouble(token,&next_token);
4261 if (LocaleCompare("arc",keyword) == 0)
4263 primitive_type=ArcPrimitive;
4272 if (LocaleCompare("bezier",keyword) == 0)
4274 primitive_type=BezierPrimitive;
4283 if (LocaleCompare("clip-path",keyword) == 0)
4285 (void) GetNextToken(q,&q,extent,token);
4286 (void) FormatLocaleString(message,MagickPathExtent,
4287 "clip-path:url(#%s);",token);
4288 (void) WriteBlobString(image,message);
4291 if (LocaleCompare("clip-rule",keyword) == 0)
4293 (void) GetNextToken(q,&q,extent,token);
4294 (void) FormatLocaleString(message,MagickPathExtent,"clip-rule:%s;",
4296 (void) WriteBlobString(image,message);
4299 if (LocaleCompare("clip-units",keyword) == 0)
4301 (void) GetNextToken(q,&q,extent,token);
4302 (void) FormatLocaleString(message,MagickPathExtent,
4303 "clipPathUnits=%s;",token);
4304 (void) WriteBlobString(image,message);
4307 if (LocaleCompare("circle",keyword) == 0)
4309 primitive_type=CirclePrimitive;
4312 if (LocaleCompare("color",keyword) == 0)
4314 primitive_type=ColorPrimitive;
4323 if (LocaleCompare("decorate",keyword) == 0)
4325 (void) GetNextToken(q,&q,extent,token);
4326 (void) FormatLocaleString(message,MagickPathExtent,
4327 "text-decoration:%s;",token);
4328 (void) WriteBlobString(image,message);
4337 if (LocaleCompare("ellipse",keyword) == 0)
4339 primitive_type=EllipsePrimitive;
4348 if (LocaleCompare("fill",keyword) == 0)
4350 (void) GetNextToken(q,&q,extent,token);
4351 (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;",
4353 (void) WriteBlobString(image,message);
4356 if (LocaleCompare("fill-rule",keyword) == 0)
4358 (void) GetNextToken(q,&q,extent,token);
4359 (void) FormatLocaleString(message,MagickPathExtent,
4360 "fill-rule:%s;",token);
4361 (void) WriteBlobString(image,message);
4364 if (LocaleCompare("fill-opacity",keyword) == 0)
4366 (void) GetNextToken(q,&q,extent,token);
4367 (void) FormatLocaleString(message,MagickPathExtent,
4368 "fill-opacity:%s;",token);
4369 (void) WriteBlobString(image,message);
4372 if (LocaleCompare("font-family",keyword) == 0)
4374 (void) GetNextToken(q,&q,extent,token);
4375 (void) FormatLocaleString(message,MagickPathExtent,
4376 "font-family:%s;",token);
4377 (void) WriteBlobString(image,message);
4380 if (LocaleCompare("font-stretch",keyword) == 0)
4382 (void) GetNextToken(q,&q,extent,token);
4383 (void) FormatLocaleString(message,MagickPathExtent,
4384 "font-stretch:%s;",token);
4385 (void) WriteBlobString(image,message);
4388 if (LocaleCompare("font-style",keyword) == 0)
4390 (void) GetNextToken(q,&q,extent,token);
4391 (void) FormatLocaleString(message,MagickPathExtent,
4392 "font-style:%s;",token);
4393 (void) WriteBlobString(image,message);
4396 if (LocaleCompare("font-size",keyword) == 0)
4398 (void) GetNextToken(q,&q,extent,token);
4399 (void) FormatLocaleString(message,MagickPathExtent,
4400 "font-size:%s;",token);
4401 (void) WriteBlobString(image,message);
4404 if (LocaleCompare("font-weight",keyword) == 0)
4406 (void) GetNextToken(q,&q,extent,token);
4407 (void) FormatLocaleString(message,MagickPathExtent,
4408 "font-weight:%s;",token);
4409 (void) WriteBlobString(image,message);
4418 if (LocaleCompare("gradient-units",keyword) == 0)
4420 (void) GetNextToken(q,&q,extent,token);
4423 if (LocaleCompare("text-align",keyword) == 0)
4425 (void) GetNextToken(q,&q,extent,token);
4426 (void) FormatLocaleString(message,MagickPathExtent,
4427 "text-align %s ",token);
4428 (void) WriteBlobString(image,message);
4431 if (LocaleCompare("text-anchor",keyword) == 0)
4433 (void) GetNextToken(q,&q,extent,token);
4434 (void) FormatLocaleString(message,MagickPathExtent,
4435 "text-anchor %s ",token);
4436 (void) WriteBlobString(image,message);
4445 if (LocaleCompare("image",keyword) == 0)
4447 (void) GetNextToken(q,&q,extent,token);
4448 primitive_type=ImagePrimitive;
4457 if (LocaleCompare("kerning",keyword) == 0)
4459 (void) GetNextToken(q,&q,extent,token);
4460 (void) FormatLocaleString(message,MagickPathExtent,"kerning:%s;",
4462 (void) WriteBlobString(image,message);
4469 if (LocaleCompare("letter-spacing",keyword) == 0)
4471 (void) GetNextToken(q,&q,extent,token);
4472 (void) FormatLocaleString(message,MagickPathExtent,
4473 "letter-spacing:%s;",token);
4474 (void) WriteBlobString(image,message);
4477 if (LocaleCompare("line",keyword) == 0)
4479 primitive_type=LinePrimitive;
4488 if (LocaleCompare("opacity",keyword) == 0)
4490 (void) GetNextToken(q,&q,extent,token);
4491 (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ",
4493 (void) WriteBlobString(image,message);
4502 if (LocaleCompare("path",keyword) == 0)
4504 primitive_type=PathPrimitive;
4507 if (LocaleCompare("point",keyword) == 0)
4509 primitive_type=PointPrimitive;
4512 if (LocaleCompare("polyline",keyword) == 0)
4514 primitive_type=PolylinePrimitive;
4517 if (LocaleCompare("polygon",keyword) == 0)
4519 primitive_type=PolygonPrimitive;
4522 if (LocaleCompare("pop",keyword) == 0)
4524 (void) GetNextToken(q,&q,extent,token);
4525 if (LocaleCompare("clip-path",token) == 0)
4527 (void) WriteBlobString(image,"</clipPath>\n");
4530 if (LocaleCompare("defs",token) == 0)
4532 (void) WriteBlobString(image,"</defs>\n");
4535 if (LocaleCompare("gradient",token) == 0)
4537 (void) FormatLocaleString(message,MagickPathExtent,
4538 "</%sGradient>\n",type);
4539 (void) WriteBlobString(image,message);
4542 if (LocaleCompare("graphic-context",token) == 0)
4546 ThrowWriterException(DrawError,
4547 "UnbalancedGraphicContextPushPop");
4548 (void) WriteBlobString(image,"</g>\n");
4550 if (LocaleCompare("pattern",token) == 0)
4552 (void) WriteBlobString(image,"</pattern>\n");
4555 if (LocaleCompare("symbol",token) == 0)
4557 (void) WriteBlobString(image,"</symbol>\n");
4560 if ((LocaleCompare("defs",token) == 0) ||
4561 (LocaleCompare("symbol",token) == 0))
4562 (void) WriteBlobString(image,"</g>\n");
4565 if (LocaleCompare("push",keyword) == 0)
4567 (void) GetNextToken(q,&q,extent,token);
4568 if (LocaleCompare("clip-path",token) == 0)
4570 (void) GetNextToken(q,&q,extent,token);
4571 (void) FormatLocaleString(message,MagickPathExtent,
4572 "<clipPath id=\"%s\">\n",token);
4573 (void) WriteBlobString(image,message);
4576 if (LocaleCompare("defs",token) == 0)
4578 (void) WriteBlobString(image,"<defs>\n");
4581 if (LocaleCompare("gradient",token) == 0)
4583 (void) GetNextToken(q,&q,extent,token);
4584 (void) CopyMagickString(name,token,MagickPathExtent);
4585 (void) GetNextToken(q,&q,extent,token);
4586 (void) CopyMagickString(type,token,MagickPathExtent);
4587 (void) GetNextToken(q,&q,extent,token);
4588 svg_info.segment.x1=StringToDouble(token,&next_token);
4589 svg_info.element.cx=StringToDouble(token,&next_token);
4590 (void) GetNextToken(q,&q,extent,token);
4592 (void) GetNextToken(q,&q,extent,token);
4593 svg_info.segment.y1=StringToDouble(token,&next_token);
4594 svg_info.element.cy=StringToDouble(token,&next_token);
4595 (void) GetNextToken(q,&q,extent,token);
4597 (void) GetNextToken(q,&q,extent,token);
4598 svg_info.segment.x2=StringToDouble(token,&next_token);
4599 svg_info.element.major=StringToDouble(token,
4601 (void) GetNextToken(q,&q,extent,token);
4603 (void) GetNextToken(q,&q,extent,token);
4604 svg_info.segment.y2=StringToDouble(token,&next_token);
4605 svg_info.element.minor=StringToDouble(token,
4607 (void) FormatLocaleString(message,MagickPathExtent,
4608 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
4609 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
4610 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
4611 if (LocaleCompare(type,"radial") == 0)
4613 (void) GetNextToken(q,&q,extent,token);
4615 (void) GetNextToken(q,&q,extent,token);
4616 svg_info.element.angle=StringToDouble(token,
4618 (void) FormatLocaleString(message,MagickPathExtent,
4619 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
4620 "fx=\"%g\" fy=\"%g\">\n",type,name,
4621 svg_info.element.cx,svg_info.element.cy,
4622 svg_info.element.angle,svg_info.element.major,
4623 svg_info.element.minor);
4625 (void) WriteBlobString(image,message);
4628 if (LocaleCompare("graphic-context",token) == 0)
4633 AffineToTransform(image,&affine);
4636 (void) WriteBlobString(image,"<g style=\"");
4639 if (LocaleCompare("pattern",token) == 0)
4641 (void) GetNextToken(q,&q,extent,token);
4642 (void) CopyMagickString(name,token,MagickPathExtent);
4643 (void) GetNextToken(q,&q,extent,token);
4644 svg_info.bounds.x=StringToDouble(token,&next_token);
4645 (void) GetNextToken(q,&q,extent,token);
4647 (void) GetNextToken(q,&q,extent,token);
4648 svg_info.bounds.y=StringToDouble(token,&next_token);
4649 (void) GetNextToken(q,&q,extent,token);
4651 (void) GetNextToken(q,&q,extent,token);
4652 svg_info.bounds.width=StringToDouble(token,
4654 (void) GetNextToken(q,&q,extent,token);
4656 (void) GetNextToken(q,&q,extent,token);
4657 svg_info.bounds.height=StringToDouble(token,(char **) NULL);
4658 (void) FormatLocaleString(message,MagickPathExtent,
4659 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
4660 "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y,
4661 svg_info.bounds.width,svg_info.bounds.height);
4662 (void) WriteBlobString(image,message);
4665 if (LocaleCompare("symbol",token) == 0)
4667 (void) WriteBlobString(image,"<symbol>\n");
4678 if (LocaleCompare("rectangle",keyword) == 0)
4680 primitive_type=RectanglePrimitive;
4683 if (LocaleCompare("roundRectangle",keyword) == 0)
4685 primitive_type=RoundRectanglePrimitive;
4688 if (LocaleCompare("rotate",keyword) == 0)
4690 (void) GetNextToken(q,&q,extent,token);
4691 (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ",
4693 (void) WriteBlobString(image,message);
4702 if (LocaleCompare("scale",keyword) == 0)
4704 (void) GetNextToken(q,&q,extent,token);
4705 affine.sx=StringToDouble(token,&next_token);
4706 (void) GetNextToken(q,&q,extent,token);
4708 (void) GetNextToken(q,&q,extent,token);
4709 affine.sy=StringToDouble(token,&next_token);
4712 if (LocaleCompare("skewX",keyword) == 0)
4714 (void) GetNextToken(q,&q,extent,token);
4715 (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ",
4717 (void) WriteBlobString(image,message);
4720 if (LocaleCompare("skewY",keyword) == 0)
4722 (void) GetNextToken(q,&q,extent,token);
4723 (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ",
4725 (void) WriteBlobString(image,message);
4728 if (LocaleCompare("stop-color",keyword) == 0)
4731 color[MagickPathExtent];
4733 (void) GetNextToken(q,&q,extent,token);
4734 (void) CopyMagickString(color,token,MagickPathExtent);
4735 (void) GetNextToken(q,&q,extent,token);
4736 (void) FormatLocaleString(message,MagickPathExtent,
4737 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4738 (void) WriteBlobString(image,message);
4741 if (LocaleCompare("stroke",keyword) == 0)
4743 (void) GetNextToken(q,&q,extent,token);
4744 (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;",
4746 (void) WriteBlobString(image,message);
4749 if (LocaleCompare("stroke-antialias",keyword) == 0)
4751 (void) GetNextToken(q,&q,extent,token);
4752 (void) FormatLocaleString(message,MagickPathExtent,
4753 "stroke-antialias:%s;",token);
4754 (void) WriteBlobString(image,message);
4757 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4765 (void) GetNextToken(p,&p,extent,token);
4766 for (k=0; IsPoint(token); k++)
4767 (void) GetNextToken(p,&p,extent,token);
4768 (void) WriteBlobString(image,"stroke-dasharray:");
4769 for (j=0; j < k; j++)
4771 (void) GetNextToken(q,&q,extent,token);
4772 (void) FormatLocaleString(message,MagickPathExtent,"%s ",
4774 (void) WriteBlobString(image,message);
4776 (void) WriteBlobString(image,";");
4779 (void) GetNextToken(q,&q,extent,token);
4780 (void) FormatLocaleString(message,MagickPathExtent,
4781 "stroke-dasharray:%s;",token);
4782 (void) WriteBlobString(image,message);
4785 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4787 (void) GetNextToken(q,&q,extent,token);
4788 (void) FormatLocaleString(message,MagickPathExtent,
4789 "stroke-dashoffset:%s;",token);
4790 (void) WriteBlobString(image,message);
4793 if (LocaleCompare("stroke-linecap",keyword) == 0)
4795 (void) GetNextToken(q,&q,extent,token);
4796 (void) FormatLocaleString(message,MagickPathExtent,
4797 "stroke-linecap:%s;",token);
4798 (void) WriteBlobString(image,message);
4801 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4803 (void) GetNextToken(q,&q,extent,token);
4804 (void) FormatLocaleString(message,MagickPathExtent,
4805 "stroke-linejoin:%s;",token);
4806 (void) WriteBlobString(image,message);
4809 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4811 (void) GetNextToken(q,&q,extent,token);
4812 (void) FormatLocaleString(message,MagickPathExtent,
4813 "stroke-miterlimit:%s;",token);
4814 (void) WriteBlobString(image,message);
4817 if (LocaleCompare("stroke-opacity",keyword) == 0)
4819 (void) GetNextToken(q,&q,extent,token);
4820 (void) FormatLocaleString(message,MagickPathExtent,
4821 "stroke-opacity:%s;",token);
4822 (void) WriteBlobString(image,message);
4825 if (LocaleCompare("stroke-width",keyword) == 0)
4827 (void) GetNextToken(q,&q,extent,token);
4828 (void) FormatLocaleString(message,MagickPathExtent,
4829 "stroke-width:%s;",token);
4830 (void) WriteBlobString(image,message);
4839 if (LocaleCompare("text",keyword) == 0)
4841 primitive_type=TextPrimitive;
4844 if (LocaleCompare("text-antialias",keyword) == 0)
4846 (void) GetNextToken(q,&q,extent,token);
4847 (void) FormatLocaleString(message,MagickPathExtent,
4848 "text-antialias:%s;",token);
4849 (void) WriteBlobString(image,message);
4852 if (LocaleCompare("tspan",keyword) == 0)
4854 primitive_type=TextPrimitive;
4857 if (LocaleCompare("translate",keyword) == 0)
4859 (void) GetNextToken(q,&q,extent,token);
4860 affine.tx=StringToDouble(token,&next_token);
4861 (void) GetNextToken(q,&q,extent,token);
4863 (void) GetNextToken(q,&q,extent,token);
4864 affine.ty=StringToDouble(token,&next_token);
4873 if (LocaleCompare("viewbox",keyword) == 0)
4875 (void) GetNextToken(q,&q,extent,token);
4877 (void) GetNextToken(q,&q,extent,token);
4878 (void) GetNextToken(q,&q,extent,token);
4880 (void) GetNextToken(q,&q,extent,token);
4881 (void) GetNextToken(q,&q,extent,token);
4883 (void) GetNextToken(q,&q,extent,token);
4884 (void) GetNextToken(q,&q,extent,token);
4896 if (status == MagickFalse)
4898 if (primitive_type == UndefinedPrimitive)
4901 Parse the primitive attributes.
4905 for (x=0; *q != '\0'; x++)
4910 if (IsPoint(q) == MagickFalse)
4912 (void) GetNextToken(q,&q,extent,token);
4913 point.x=StringToDouble(token,&next_token);
4914 (void) GetNextToken(q,&q,extent,token);
4916 (void) GetNextToken(q,&q,extent,token);
4917 point.y=StringToDouble(token,&next_token);
4918 (void) GetNextToken(q,(const char **) NULL,extent,token);
4920 (void) GetNextToken(q,&q,extent,token);
4921 primitive_info[i].primitive=primitive_type;
4922 primitive_info[i].point=point;
4923 primitive_info[i].coordinates=0;
4924 primitive_info[i].method=FloodfillMethod;
4926 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4928 number_points+=6*BezierQuantum+360;
4929 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4930 number_points,sizeof(*primitive_info));
4931 if (primitive_info == (PrimitiveInfo *) NULL)
4933 (void) ThrowMagickException(exception,GetMagickModule(),
4934 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4938 primitive_info[j].primitive=primitive_type;
4939 primitive_info[j].coordinates=(size_t) x;
4940 primitive_info[j].method=FloodfillMethod;
4941 primitive_info[j].text=(char *) NULL;
4944 AffineToTransform(image,&affine);
4948 switch (primitive_type)
4950 case PointPrimitive:
4953 if (primitive_info[j].coordinates != 1)
4962 if (primitive_info[j].coordinates != 2)
4967 (void) FormatLocaleString(message,MagickPathExtent,
4968 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4969 primitive_info[j].point.x,primitive_info[j].point.y,
4970 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4971 (void) WriteBlobString(image,message);
4974 case RectanglePrimitive:
4976 if (primitive_info[j].coordinates != 2)
4981 (void) FormatLocaleString(message,MagickPathExtent,
4982 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4983 primitive_info[j].point.x,primitive_info[j].point.y,
4984 primitive_info[j+1].point.x-primitive_info[j].point.x,
4985 primitive_info[j+1].point.y-primitive_info[j].point.y);
4986 (void) WriteBlobString(image,message);
4989 case RoundRectanglePrimitive:
4991 if (primitive_info[j].coordinates != 3)
4996 (void) FormatLocaleString(message,MagickPathExtent,
4997 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4998 "ry=\"%g\"/>\n",primitive_info[j].point.x,
4999 primitive_info[j].point.y,primitive_info[j+1].point.x-
5000 primitive_info[j].point.x,primitive_info[j+1].point.y-
5001 primitive_info[j].point.y,primitive_info[j+2].point.x,
5002 primitive_info[j+2].point.y);
5003 (void) WriteBlobString(image,message);
5008 if (primitive_info[j].coordinates != 3)
5015 case EllipsePrimitive:
5017 if (primitive_info[j].coordinates != 3)
5022 (void) FormatLocaleString(message,MagickPathExtent,
5023 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
5024 primitive_info[j].point.x,primitive_info[j].point.y,
5025 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
5026 (void) WriteBlobString(image,message);
5029 case CirclePrimitive:
5035 if (primitive_info[j].coordinates != 2)
5040 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
5041 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
5042 (void) FormatLocaleString(message,MagickPathExtent,
5043 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
5044 primitive_info[j].point.x,primitive_info[j].point.y,
5046 (void) WriteBlobString(image,message);
5049 case PolylinePrimitive:
5051 if (primitive_info[j].coordinates < 2)
5056 (void) CopyMagickString(message," <polyline points=\"",
5058 (void) WriteBlobString(image,message);
5059 length=strlen(message);
5062 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
5063 primitive_info[j].point.x,primitive_info[j].point.y);
5064 length+=strlen(message);
5067 (void) WriteBlobString(image,"\n ");
5068 length=strlen(message)+5;
5070 (void) WriteBlobString(image,message);
5072 (void) WriteBlobString(image,"\"/>\n");
5075 case PolygonPrimitive:
5077 if (primitive_info[j].coordinates < 3)
5082 primitive_info[i]=primitive_info[j];
5083 primitive_info[i].coordinates=0;
5084 primitive_info[j].coordinates++;
5086 (void) CopyMagickString(message," <polygon points=\"",
5088 (void) WriteBlobString(image,message);
5089 length=strlen(message);
5092 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
5093 primitive_info[j].point.x,primitive_info[j].point.y);
5094 length+=strlen(message);
5097 (void) WriteBlobString(image,"\n ");
5098 length=strlen(message)+5;
5100 (void) WriteBlobString(image,message);
5102 (void) WriteBlobString(image,"\"/>\n");
5105 case BezierPrimitive:
5107 if (primitive_info[j].coordinates < 3)
5119 (void) GetNextToken(q,&q,extent,token);
5120 number_attributes=1;
5121 for (p=token; *p != '\0'; p++)
5122 if (isalpha((int) ((unsigned char) *p)) != 0)
5123 number_attributes++;
5124 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
5126 number_points+=6*BezierQuantum*number_attributes;
5127 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
5128 number_points,sizeof(*primitive_info));
5129 if (primitive_info == (PrimitiveInfo *) NULL)
5131 (void) ThrowMagickException(exception,GetMagickModule(),
5132 ResourceLimitError,"MemoryAllocationFailed","`%s'",
5137 (void) WriteBlobString(image," <path d=\"");
5138 (void) WriteBlobString(image,token);
5139 (void) WriteBlobString(image,"\"/>\n");
5142 case AlphaPrimitive:
5143 case ColorPrimitive:
5145 if (primitive_info[j].coordinates != 1)
5150 (void) GetNextToken(q,&q,extent,token);
5151 if (LocaleCompare("point",token) == 0)
5152 primitive_info[j].method=PointMethod;
5153 if (LocaleCompare("replace",token) == 0)
5154 primitive_info[j].method=ReplaceMethod;
5155 if (LocaleCompare("floodfill",token) == 0)
5156 primitive_info[j].method=FloodfillMethod;
5157 if (LocaleCompare("filltoborder",token) == 0)
5158 primitive_info[j].method=FillToBorderMethod;
5159 if (LocaleCompare("reset",token) == 0)
5160 primitive_info[j].method=ResetMethod;
5168 if (primitive_info[j].coordinates != 1)
5173 (void) GetNextToken(q,&q,extent,token);
5174 (void) FormatLocaleString(message,MagickPathExtent,
5175 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
5176 primitive_info[j].point.y);
5177 (void) WriteBlobString(image,message);
5178 for (p=token; *p != '\0'; p++)
5181 case '<': (void) WriteBlobString(image,"<"); break;
5182 case '>': (void) WriteBlobString(image,">"); break;
5183 case '&': (void) WriteBlobString(image,"&"); break;
5184 default: (void) WriteBlobByte(image,(unsigned char) *p); break;
5186 (void) WriteBlobString(image,"</text>\n");
5189 case ImagePrimitive:
5191 if (primitive_info[j].coordinates != 2)
5196 (void) GetNextToken(q,&q,extent,token);
5197 (void) FormatLocaleString(message,MagickPathExtent,
5198 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
5199 "href=\"%s\"/>\n",primitive_info[j].point.x,
5200 primitive_info[j].point.y,primitive_info[j+1].point.x,
5201 primitive_info[j+1].point.y,token);
5202 (void) WriteBlobString(image,message);
5206 if (primitive_info == (PrimitiveInfo *) NULL)
5208 primitive_info[i].primitive=UndefinedPrimitive;
5209 if (status == MagickFalse)
5212 (void) WriteBlobString(image,"</svg>\n");
5214 Relinquish resources.
5216 token=DestroyString(token);
5217 if (primitive_info != (PrimitiveInfo *) NULL)
5218 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
5219 (void) CloseBlob(image);