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/option.h"
69 #include "MagickCore/pixel-accessor.h"
70 #include "MagickCore/property.h"
71 #include "MagickCore/quantum-private.h"
72 #include "MagickCore/resource_.h"
73 #include "MagickCore/static.h"
74 #include "MagickCore/string_.h"
75 #include "MagickCore/string-private.h"
76 #include "MagickCore/token.h"
77 #include "MagickCore/utility.h"
79 #if defined(MAGICKCORE_XML_DELEGATE)
80 # if defined(MAGICKCORE_WINDOWS_SUPPORT)
81 # if !defined(__MINGW32__)
82 # include <win32config.h>
85 # include <libxml/xmlmemory.h>
86 # include <libxml/parserInternals.h>
87 # include <libxml/xmlerror.h>
90 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
91 #include "autotrace/autotrace.h"
94 #if defined(MAGICKCORE_RSVG_DELEGATE)
95 #include "librsvg/rsvg.h"
96 #if !defined(LIBRSVG_CHECK_VERSION)
97 #include "librsvg/rsvg-cairo.h"
98 #include "librsvg/librsvg-features.h"
99 #elif !LIBRSVG_CHECK_VERSION(2,36,2)
100 #include "librsvg/rsvg-cairo.h"
101 #include "librsvg/librsvg-features.h"
108 #define DefaultSVGDensity 96.0
111 Typedef declarations.
113 typedef struct _BoundingBox
122 typedef struct _ElementInfo
132 typedef struct _SVGInfo
186 #if defined(MAGICKCORE_XML_DELEGATE)
202 SVGDensityGeometry[] = "96.0x96.0";
205 Forward declarations.
207 static MagickBooleanType
208 WriteSVGImage(const ImageInfo *,Image *,ExceptionInfo *);
211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
221 % IsSVG()() returns MagickTrue if the image format type, identified by the
222 % magick string, is SVG.
224 % The format of the IsSVG method is:
226 % MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
228 % A description of each parameter follows:
230 % o magick: compare image format pattern against these bytes.
232 % o length: Specifies the length of the magick string.
235 static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
239 if (LocaleNCompare((const char *) magick+1,"svg",3) == 0)
243 if (LocaleNCompare((const char *) magick+1,"?xml",4) == 0)
249 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
253 % R e a d S V G I m a g e %
257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
259 % ReadSVGImage() reads a Scalable Vector Gaphics file and returns it. It
260 % allocates the memory necessary for the new Image structure and returns a
261 % pointer to the new image.
263 % The format of the ReadSVGImage method is:
265 % Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
267 % A description of each parameter follows:
269 % o image_info: the image info.
271 % o exception: return any errors or warnings in this structure.
275 static Image *RenderSVGImage(const ImageInfo *image_info,Image *image,
276 ExceptionInfo *exception)
279 background[MagickPathExtent],
280 command[MagickPathExtent],
282 input_filename[MagickPathExtent],
283 opacity[MagickPathExtent],
284 output_filename[MagickPathExtent],
285 unique[MagickPathExtent];
300 Our best hope for compliance with the SVG standard.
302 delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception);
303 if (delegate_info == (const DelegateInfo *) NULL)
304 return((Image *) NULL);
305 status=AcquireUniqueSymbolicLink(image->filename,input_filename);
306 (void) AcquireUniqueFilename(output_filename);
307 (void) AcquireUniqueFilename(unique);
308 density=AcquireString("");
309 (void) FormatLocaleString(density,MagickPathExtent,"%.20g,%.20g",
310 image->resolution.x,image->resolution.y);
311 (void) FormatLocaleString(background,MagickPathExtent,
312 "rgb(%.20g%%,%.20g%%,%.20g%%)",
313 100.0*QuantumScale*image->background_color.red,
314 100.0*QuantumScale*image->background_color.green,
315 100.0*QuantumScale*image->background_color.blue);
316 (void) FormatLocaleString(opacity,MagickPathExtent,"%.20g",QuantumScale*
317 image->background_color.alpha);
318 (void) FormatLocaleString(command,MagickPathExtent,
319 GetDelegateCommands(delegate_info),input_filename,output_filename,density,
320 background,opacity,unique);
321 density=DestroyString(density);
322 status=ExternalDelegateCommand(MagickFalse,image_info->verbose,command,
323 (char *) NULL,exception);
324 (void) RelinquishUniqueFileResource(unique);
325 (void) RelinquishUniqueFileResource(input_filename);
326 if ((status == 0) && (stat(output_filename,&attributes) == 0) &&
327 (attributes.st_size > 0))
335 read_info=CloneImageInfo(image_info);
336 (void) CopyMagickString(read_info->filename,output_filename,
338 svg_image=ReadImage(read_info,exception);
339 read_info=DestroyImageInfo(read_info);
340 if (svg_image != (Image *) NULL)
342 (void) RelinquishUniqueFileResource(output_filename);
343 for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; )
345 (void) CopyMagickString(next->filename,image->filename,
347 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
348 next=GetNextImageInList(next);
353 (void) RelinquishUniqueFileResource(output_filename);
354 return((Image *) NULL);
357 #if defined(MAGICKCORE_XML_DELEGATE)
358 static SVGInfo *AcquireSVGInfo(void)
363 svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info));
364 if (svg_info == (SVGInfo *) NULL)
365 return((SVGInfo *) NULL);
366 (void) memset(svg_info,0,sizeof(*svg_info));
367 svg_info->text=AcquireString("");
368 svg_info->scale=(double *) AcquireCriticalMemory(sizeof(*svg_info->scale));
369 GetAffineMatrix(&svg_info->affine);
370 svg_info->scale[0]=ExpandAffine(&svg_info->affine);
374 static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
376 if (svg_info->text != (char *) NULL)
377 svg_info->text=DestroyString(svg_info->text);
378 if (svg_info->scale != (double *) NULL)
379 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
380 if (svg_info->title != (char *) NULL)
381 svg_info->title=DestroyString(svg_info->title);
382 if (svg_info->comment != (char *) NULL)
383 svg_info->comment=DestroyString(svg_info->comment);
384 return((SVGInfo *) RelinquishMagickMemory(svg_info));
387 static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
392 token[MagickPathExtent];
400 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
401 assert(string != (const char *) NULL);
402 p=(const char *) string;
403 (void) GetNextToken(p,&p,MagickPathExtent,token);
404 value=StringToDouble(token,&next_token);
405 if (strchr(token,'%') != (char *) NULL)
413 if (svg_info->view_box.width == 0.0)
415 return(svg_info->view_box.width*value/100.0);
419 if (svg_info->view_box.height == 0.0)
421 return(svg_info->view_box.height*value/100.0);
423 alpha=value-svg_info->view_box.width;
424 beta=value-svg_info->view_box.height;
425 return(hypot(alpha,beta)/sqrt(2.0)/100.0);
427 (void) GetNextToken(p,&p,MagickPathExtent,token);
428 if (LocaleNCompare(token,"cm",2) == 0)
429 return(DefaultSVGDensity*svg_info->scale[0]/2.54*value);
430 if (LocaleNCompare(token,"em",2) == 0)
431 return(svg_info->pointsize*value);
432 if (LocaleNCompare(token,"ex",2) == 0)
433 return(svg_info->pointsize*value/2.0);
434 if (LocaleNCompare(token,"in",2) == 0)
435 return(DefaultSVGDensity*svg_info->scale[0]*value);
436 if (LocaleNCompare(token,"mm",2) == 0)
437 return(DefaultSVGDensity*svg_info->scale[0]/25.4*value);
438 if (LocaleNCompare(token,"pc",2) == 0)
439 return(DefaultSVGDensity*svg_info->scale[0]/6.0*value);
440 if (LocaleNCompare(token,"pt",2) == 0)
441 return(svg_info->scale[0]*value);
442 if (LocaleNCompare(token,"px",2) == 0)
447 #if defined(__cplusplus) || defined(c_plusplus)
451 static int SVGIsStandalone(void *context)
457 Is this document tagged standalone?
459 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGIsStandalone()");
460 svg_info=(SVGInfo *) context;
461 return(svg_info->document->standalone == 1);
464 static int SVGHasInternalSubset(void *context)
470 Does this document has an internal subset?
472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
473 " SAX.SVGHasInternalSubset()");
474 svg_info=(SVGInfo *) context;
475 return(svg_info->document->intSubset != NULL);
478 static int SVGHasExternalSubset(void *context)
484 Does this document has an external subset?
486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
487 " SAX.SVGHasExternalSubset()");
488 svg_info=(SVGInfo *) context;
489 return(svg_info->document->extSubset != NULL);
492 static void SVGInternalSubset(void *context,const xmlChar *name,
493 const xmlChar *external_id,const xmlChar *system_id)
499 Does this document has an internal subset?
501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
502 " SAX.internalSubset(%s, %s, %s)",(const char *) name,
503 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
504 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
505 svg_info=(SVGInfo *) context;
506 (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id);
509 static xmlParserInputPtr SVGResolveEntity(void *context,
510 const xmlChar *public_id,const xmlChar *system_id)
519 Special entity resolver, better left to the parser, it has more
520 context than the application layer. The default behaviour is to
521 not resolve the entities, in that case the ENTITY_REF nodes are
522 built in the structure (and the parameter values).
524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
525 " SAX.resolveEntity(%s, %s)",
526 (public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"),
527 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
528 svg_info=(SVGInfo *) context;
529 stream=xmlLoadExternalEntity((const char *) system_id,(const char *)
530 public_id,svg_info->parser);
534 static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
540 Get an entity by name.
542 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGGetEntity(%s)",
544 svg_info=(SVGInfo *) context;
545 return(xmlGetDocEntity(svg_info->document,name));
548 static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
554 Get a parameter entity by name.
556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
557 " SAX.getParameterEntity(%s)",name);
558 svg_info=(SVGInfo *) context;
559 return(xmlGetParameterEntity(svg_info->document,name));
562 static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
563 const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
569 An entity definition has been parsed.
571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
572 " SAX.entityDecl(%s, %d, %s, %s, %s)",name,type,
573 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
574 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content);
575 svg_info=(SVGInfo *) context;
576 if (svg_info->parser->inSubset == 1)
577 (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id,
580 if (svg_info->parser->inSubset == 2)
581 (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
585 static void SVGAttributeDeclaration(void *context,const xmlChar *element,
586 const xmlChar *name,int type,int value,const xmlChar *default_value,
587 xmlEnumerationPtr tree)
600 An attribute definition has been parsed.
602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
603 " SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value,
605 svg_info=(SVGInfo *) context;
606 fullname=(xmlChar *) NULL;
607 prefix=(xmlChar *) NULL;
608 parser=svg_info->parser;
609 fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix);
610 if (parser->inSubset == 1)
611 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset,
612 element,fullname,prefix,(xmlAttributeType) type,
613 (xmlAttributeDefault) value,default_value,tree);
615 if (parser->inSubset == 2)
616 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset,
617 element,fullname,prefix,(xmlAttributeType) type,
618 (xmlAttributeDefault) value,default_value,tree);
619 if (prefix != (xmlChar *) NULL)
621 if (fullname != (xmlChar *) NULL)
625 static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
626 xmlElementContentPtr content)
635 An element definition has been parsed.
637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
638 " SAX.elementDecl(%s, %d, ...)",name,type);
639 svg_info=(SVGInfo *) context;
640 parser=svg_info->parser;
641 if (parser->inSubset == 1)
642 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset,
643 name,(xmlElementTypeVal) type,content);
645 if (parser->inSubset == 2)
646 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
647 name,(xmlElementTypeVal) type,content);
650 static void SVGStripString(const MagickBooleanType trim,char *message)
659 assert(message != (char *) NULL);
660 if (*message == '\0')
666 for (p=message; *p != '\0'; p++)
668 if ((*p == '/') && (*(p+1) == '*'))
670 for ( ; *p != '\0'; p++)
671 if ((*p == '*') && (*(p+1) == '/'))
682 length=strlen(message);
683 if ((trim != MagickFalse) && (length != 0))
689 while (isspace((int) ((unsigned char) *p)) != 0)
691 if ((*p == '\'') || (*p == '"'))
694 while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
697 if ((*q == '\'') || (*q == '"'))
699 (void) memmove(message,p,(size_t) (q-p+1));
703 Convert newlines to a space.
705 for (p=message; *p != '\0'; p++)
710 static char **SVGKeyValuePairs(void *context,const int key_sentinel,
711 const int value_sentinel,const char *text,size_t *number_tokens)
729 svg_info=(SVGInfo *) context;
731 if (text == (const char *) NULL)
732 return((char **) NULL);
734 tokens=(char **) AcquireQuantumMemory(extent+2UL,sizeof(*tokens));
735 if (tokens == (char **) NULL)
737 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
738 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
739 return((char **) NULL);
742 Convert string to an ASCII list.
746 for (q=p; *q != '\0'; q++)
748 if ((*q != key_sentinel) && (*q != value_sentinel) && (*q != '\0'))
750 if (i == (ssize_t) extent)
753 tokens=(char **) ResizeQuantumMemory(tokens,extent+2,sizeof(*tokens));
754 if (tokens == (char **) NULL)
756 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
757 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
758 return((char **) NULL);
761 tokens[i]=AcquireString(p);
762 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
763 SVGStripString(MagickTrue,tokens[i]);
767 tokens[i]=AcquireString(p);
768 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
769 SVGStripString(MagickTrue,tokens[i++]);
770 tokens[i]=(char *) NULL;
771 *number_tokens=(size_t) i;
775 static void SVGNotationDeclaration(void *context,const xmlChar *name,
776 const xmlChar *public_id,const xmlChar *system_id)
785 What to do when a notation declaration has been parsed.
787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
788 " SAX.notationDecl(%s, %s, %s)",name,
789 public_id != (const xmlChar *) NULL ? (const char *) public_id : "none",
790 system_id != (const xmlChar *) NULL ? (const char *) system_id : "none");
791 svg_info=(SVGInfo *) context;
792 parser=svg_info->parser;
793 if (parser->inSubset == 1)
794 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
795 name,public_id,system_id);
797 if (parser->inSubset == 2)
798 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
799 name,public_id,system_id);
802 static void SVGProcessStyleElement(void *context,const xmlChar *name,
806 background[MagickPathExtent],
824 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
825 svg_info=(SVGInfo *) context;
826 tokens=SVGKeyValuePairs(context,':',';',style,&number_tokens);
827 if (tokens == (char **) NULL)
829 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
831 keyword=(char *) tokens[i];
832 value=(char *) tokens[i+1];
833 if (LocaleCompare(keyword,"font-size") != 0)
835 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
836 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
837 svg_info->pointsize);
839 color=AcquireString("none");
840 units=AcquireString("userSpaceOnUse");
841 for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
843 keyword=(char *) tokens[i];
844 value=(char *) tokens[i+1];
845 (void) LogMagickEvent(CoderEvent,GetMagickModule()," %s: %s",keyword,
852 if (LocaleCompare((const char *) name,"background") == 0)
854 if (LocaleCompare((const char *) name,"svg") == 0)
855 (void) CopyMagickString(background,value,MagickPathExtent);
863 if (LocaleCompare(keyword,"clip-path") == 0)
865 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",value);
868 if (LocaleCompare(keyword,"clip-rule") == 0)
870 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",value);
873 if (LocaleCompare(keyword,"clipPathUnits") == 0)
875 (void) CloneString(&units,value);
876 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
880 if (LocaleCompare(keyword,"color") == 0)
882 (void) CloneString(&color,value);
890 if (LocaleCompare(keyword,"fill") == 0)
892 if (LocaleCompare(value,"currentColor") == 0)
894 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
897 if (LocaleCompare(value,"#000000ff") == 0)
898 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
900 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
903 if (LocaleCompare(keyword,"fillcolor") == 0)
905 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
908 if (LocaleCompare(keyword,"fill-rule") == 0)
910 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",value);
913 if (LocaleCompare(keyword,"fill-opacity") == 0)
915 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
919 if (LocaleCompare(keyword,"font") == 0)
922 family[MagickPathExtent],
923 size[MagickPathExtent],
924 style[MagickPathExtent];
926 if (sscanf(value,"%2048s %2048s %2048s",style,size,family) != 3)
928 if (GetUserSpaceCoordinateValue(svg_info,0,style) == 0)
929 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
932 if (sscanf(value,"%2048s %2048s",size,family) != 2)
934 (void) FormatLocaleFile(svg_info->file,"font-size \"%s\"\n",size);
935 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
939 if (LocaleCompare(keyword,"font-family") == 0)
941 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
945 if (LocaleCompare(keyword,"font-stretch") == 0)
947 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
951 if (LocaleCompare(keyword,"font-style") == 0)
953 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",value);
956 if (LocaleCompare(keyword,"font-size") == 0)
958 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
959 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
960 svg_info->pointsize);
963 if (LocaleCompare(keyword,"font-weight") == 0)
965 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
974 if (LocaleCompare(keyword,"kerning") == 0)
976 (void) FormatLocaleFile(svg_info->file,"kerning \"%s\"\n",value);
984 if (LocaleCompare(keyword,"letter-spacing") == 0)
986 (void) FormatLocaleFile(svg_info->file,"letter-spacing \"%s\"\n",
995 if (LocaleCompare(keyword,"mask") == 0)
997 (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
1005 if (LocaleCompare(keyword,"offset") == 0)
1007 (void) FormatLocaleFile(svg_info->file,"offset %g\n",
1008 GetUserSpaceCoordinateValue(svg_info,1,value));
1011 if (LocaleCompare(keyword,"opacity") == 0)
1013 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
1021 if (LocaleCompare(keyword,"stop-color") == 0)
1023 (void) CloneString(&svg_info->stop_color,value);
1026 if (LocaleCompare(keyword,"stroke") == 0)
1028 if (LocaleCompare(value,"currentColor") == 0)
1030 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",color);
1033 if (LocaleCompare(value,"#000000ff") == 0)
1034 (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
1036 (void) FormatLocaleFile(svg_info->file,
1037 "stroke \"%s\"\n",value);
1040 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1042 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
1043 LocaleCompare(value,"true") == 0);
1046 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1048 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1052 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1054 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
1055 GetUserSpaceCoordinateValue(svg_info,1,value));
1058 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1060 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
1064 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1066 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
1070 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1072 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit \"%s\"\n",
1076 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1078 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
1082 if (LocaleCompare(keyword,"stroke-width") == 0)
1084 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
1085 GetUserSpaceCoordinateValue(svg_info,1,value));
1093 if (LocaleCompare(keyword,"text-align") == 0)
1095 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",value);
1098 if (LocaleCompare(keyword,"text-anchor") == 0)
1100 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
1104 if (LocaleCompare(keyword,"text-decoration") == 0)
1106 if (LocaleCompare(value,"underline") == 0)
1107 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
1108 if (LocaleCompare(value,"line-through") == 0)
1109 (void) FormatLocaleFile(svg_info->file,"decorate line-through\n");
1110 if (LocaleCompare(value,"overline") == 0)
1111 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
1114 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1116 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
1117 LocaleCompare(value,"true") == 0);
1126 if (units != (char *) NULL)
1127 units=DestroyString(units);
1128 if (color != (char *) NULL)
1129 color=DestroyString(color);
1130 for (i=0; tokens[i] != (char *) NULL; i++)
1131 tokens[i]=DestroyString(tokens[i]);
1132 tokens=(char **) RelinquishMagickMemory(tokens);
1135 static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
1136 const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
1142 What to do when an unparsed entity declaration is parsed.
1144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1145 " SAX.unparsedEntityDecl(%s, %s, %s, %s)",name,
1146 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
1147 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation);
1148 svg_info=(SVGInfo *) context;
1149 (void) xmlAddDocEntity(svg_info->document,name,
1150 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
1154 static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
1160 Receive the document locator at startup, actually xmlDefaultSAXLocator.
1163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1164 " SAX.setDocumentLocator()");
1165 svg_info=(SVGInfo *) context;
1169 static void SVGStartDocument(void *context)
1178 Called when the document start being processed.
1180 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startDocument()");
1181 svg_info=(SVGInfo *) context;
1182 parser=svg_info->parser;
1183 svg_info->document=xmlNewDoc(parser->version);
1184 if (svg_info->document == (xmlDocPtr) NULL)
1186 if (parser->encoding == NULL)
1187 svg_info->document->encoding=(const xmlChar *) NULL;
1189 svg_info->document->encoding=xmlStrdup(parser->encoding);
1190 svg_info->document->standalone=parser->standalone;
1193 static void SVGEndDocument(void *context)
1199 Called when the document end has been detected.
1201 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()");
1202 svg_info=(SVGInfo *) context;
1203 if (svg_info->offset != (char *) NULL)
1204 svg_info->offset=DestroyString(svg_info->offset);
1205 if (svg_info->stop_color != (char *) NULL)
1206 svg_info->stop_color=DestroyString(svg_info->stop_color);
1207 if (svg_info->scale != (double *) NULL)
1208 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
1209 if (svg_info->text != (char *) NULL)
1210 svg_info->text=DestroyString(svg_info->text);
1211 if (svg_info->vertices != (char *) NULL)
1212 svg_info->vertices=DestroyString(svg_info->vertices);
1213 if (svg_info->url != (char *) NULL)
1214 svg_info->url=DestroyString(svg_info->url);
1215 #if defined(MAGICKCORE_XML_DELEGATE)
1216 if (svg_info->document != (xmlDocPtr) NULL)
1218 xmlFreeDoc(svg_info->document);
1219 svg_info->document=(xmlDocPtr) NULL;
1224 static void SVGStartElement(void *context,const xmlChar *name,
1225 const xmlChar **attributes)
1227 #define PushGraphicContext(id) \
1230 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); \
1232 (void) FormatLocaleFile(svg_info->file,"push graphic-context \"%s\"\n", \
1238 background[MagickPathExtent],
1239 id[MagickPathExtent],
1241 token[MagickPathExtent],
1261 Called when an opening tag has been processed.
1263 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s",
1265 svg_info=(SVGInfo *) context;
1267 svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale,
1268 svg_info->n+1UL,sizeof(*svg_info->scale));
1269 if (svg_info->scale == (double *) NULL)
1271 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
1272 ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
1275 svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
1276 color=AcquireString("none");
1277 units=AcquireString("userSpaceOnUse");
1281 value=(const char *) NULL;
1282 if ((LocaleCompare((char *) name,"image") == 0) ||
1283 (LocaleCompare((char *) name,"pattern") == 0) ||
1284 (LocaleCompare((char *) name,"rect") == 0) ||
1285 (LocaleCompare((char *) name,"text") == 0) ||
1286 (LocaleCompare((char *) name,"use") == 0))
1288 svg_info->bounds.x=0.0;
1289 svg_info->bounds.y=0.0;
1291 if (attributes != (const xmlChar **) NULL)
1292 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1294 keyword=(const char *) attributes[i];
1295 value=(const char *) attributes[i+1];
1301 if (LocaleCompare(keyword,"cx") == 0)
1303 svg_info->element.cx=
1304 GetUserSpaceCoordinateValue(svg_info,1,value);
1307 if (LocaleCompare(keyword,"cy") == 0)
1309 svg_info->element.cy=
1310 GetUserSpaceCoordinateValue(svg_info,-1,value);
1318 if (LocaleCompare(keyword,"fx") == 0)
1320 svg_info->element.major=
1321 GetUserSpaceCoordinateValue(svg_info,1,value);
1324 if (LocaleCompare(keyword,"fy") == 0)
1326 svg_info->element.minor=
1327 GetUserSpaceCoordinateValue(svg_info,-1,value);
1335 if (LocaleCompare(keyword,"height") == 0)
1337 svg_info->bounds.height=
1338 GetUserSpaceCoordinateValue(svg_info,-1,value);
1346 if (LocaleCompare(keyword,"id") == 0)
1348 (void) CopyMagickString(id,value,MagickPathExtent);
1356 if (LocaleCompare(keyword,"r") == 0)
1358 svg_info->element.angle=
1359 GetUserSpaceCoordinateValue(svg_info,0,value);
1367 if (LocaleCompare(keyword,"width") == 0)
1369 svg_info->bounds.width=
1370 GetUserSpaceCoordinateValue(svg_info,1,value);
1378 if (LocaleCompare(keyword,"x") == 0)
1380 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
1383 if (LocaleCompare(keyword,"x1") == 0)
1385 svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
1389 if (LocaleCompare(keyword,"x2") == 0)
1391 svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
1400 if (LocaleCompare(keyword,"y") == 0)
1402 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
1405 if (LocaleCompare(keyword,"y1") == 0)
1407 svg_info->segment.y1=GetUserSpaceCoordinateValue(svg_info,-1,
1411 if (LocaleCompare(keyword,"y2") == 0)
1413 svg_info->segment.y2=GetUserSpaceCoordinateValue(svg_info,-1,
1423 if (strchr((char *) name,':') != (char *) NULL)
1426 Skip over namespace.
1428 for ( ; *name != ':'; name++) ;
1436 if (LocaleCompare((const char *) name,"circle") == 0)
1438 PushGraphicContext(id);
1441 if (LocaleCompare((const char *) name,"clipPath") == 0)
1443 (void) FormatLocaleFile(svg_info->file,"push clip-path \"%s\"\n",id);
1451 if (LocaleCompare((const char *) name,"defs") == 0)
1453 (void) FormatLocaleFile(svg_info->file,"push defs\n");
1461 if (LocaleCompare((const char *) name,"ellipse") == 0)
1463 PushGraphicContext(id);
1471 if (LocaleCompare((const char *) name,"foreignObject") == 0)
1473 PushGraphicContext(id);
1481 if (LocaleCompare((const char *) name,"g") == 0)
1483 PushGraphicContext(id);
1491 if (LocaleCompare((const char *) name,"image") == 0)
1493 PushGraphicContext(id);
1501 if (LocaleCompare((const char *) name,"line") == 0)
1503 PushGraphicContext(id);
1506 if (LocaleCompare((const char *) name,"linearGradient") == 0)
1508 (void) FormatLocaleFile(svg_info->file,
1509 "push gradient \"%s\" linear %g,%g %g,%g\n",id,
1510 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
1511 svg_info->segment.y2);
1519 if (LocaleCompare((const char *) name,"mask") == 0)
1521 (void) FormatLocaleFile(svg_info->file,"push mask \"%s\"\n",id);
1529 if (LocaleCompare((const char *) name,"path") == 0)
1531 PushGraphicContext(id);
1534 if (LocaleCompare((const char *) name,"pattern") == 0)
1536 (void) FormatLocaleFile(svg_info->file,
1537 "push pattern \"%s\" %g,%g %g,%g\n",id,
1538 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
1539 svg_info->bounds.height);
1542 if (LocaleCompare((const char *) name,"polygon") == 0)
1544 PushGraphicContext(id);
1547 if (LocaleCompare((const char *) name,"polyline") == 0)
1549 PushGraphicContext(id);
1557 if (LocaleCompare((const char *) name,"radialGradient") == 0)
1559 (void) FormatLocaleFile(svg_info->file,
1560 "push gradient \"%s\" radial %g,%g %g,%g %g\n",
1561 id,svg_info->element.cx,svg_info->element.cy,
1562 svg_info->element.major,svg_info->element.minor,
1563 svg_info->element.angle);
1566 if (LocaleCompare((const char *) name,"rect") == 0)
1568 PushGraphicContext(id);
1576 if (LocaleCompare((char *) name,"style") == 0)
1578 if (LocaleCompare((const char *) name,"svg") == 0)
1580 svg_info->svgDepth++;
1581 PushGraphicContext(id);
1582 (void) FormatLocaleFile(svg_info->file,"compliance \"SVG\"\n");
1583 (void) FormatLocaleFile(svg_info->file,"fill \"black\"\n");
1584 (void) FormatLocaleFile(svg_info->file,"fill-opacity 1\n");
1585 (void) FormatLocaleFile(svg_info->file,"stroke \"none\"\n");
1586 (void) FormatLocaleFile(svg_info->file,"stroke-width 1\n");
1587 (void) FormatLocaleFile(svg_info->file,"stroke-opacity 1\n");
1588 (void) FormatLocaleFile(svg_info->file,"fill-rule nonzero\n");
1591 if (LocaleCompare((const char *) name,"symbol") == 0)
1593 (void) FormatLocaleFile(svg_info->file,"push symbol\n");
1601 if (LocaleCompare((const char *) name,"text") == 0)
1603 PushGraphicContext(id);
1604 (void) FormatLocaleFile(svg_info->file,"class \"text\"\n");
1605 svg_info->text_offset.x=svg_info->bounds.x;
1606 svg_info->text_offset.y=svg_info->bounds.y;
1607 svg_info->bounds.x=0.0;
1608 svg_info->bounds.y=0.0;
1609 svg_info->bounds.width=0.0;
1610 svg_info->bounds.height=0.0;
1613 if (LocaleCompare((const char *) name,"tspan") == 0)
1615 if (*svg_info->text != '\0')
1620 text=EscapeString(svg_info->text,'\"');
1621 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
1622 svg_info->text_offset.x,svg_info->text_offset.y,text);
1623 text=DestroyString(text);
1624 *svg_info->text='\0';
1626 PushGraphicContext(id);
1634 if (LocaleCompare((char *) name,"use") == 0)
1636 PushGraphicContext(id);
1644 if (attributes != (const xmlChar **) NULL)
1645 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1647 keyword=(const char *) attributes[i];
1648 value=(const char *) attributes[i+1];
1649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1650 " %s = %s",keyword,value);
1656 if (LocaleCompare(keyword,"angle") == 0)
1658 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1659 GetUserSpaceCoordinateValue(svg_info,0,value));
1667 if (LocaleCompare(keyword,"class") == 0)
1674 (void) GetNextToken(p,&p,MagickPathExtent,token);
1676 (void) GetNextToken(p,&p,MagickPathExtent,token);
1679 (void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",
1686 if (LocaleCompare(keyword,"clip-path") == 0)
1688 (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",
1692 if (LocaleCompare(keyword,"clip-rule") == 0)
1694 (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",
1698 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1700 (void) CloneString(&units,value);
1701 (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
1705 if (LocaleCompare(keyword,"color") == 0)
1707 (void) CloneString(&color,value);
1710 if (LocaleCompare(keyword,"cx") == 0)
1712 svg_info->element.cx=
1713 GetUserSpaceCoordinateValue(svg_info,1,value);
1716 if (LocaleCompare(keyword,"cy") == 0)
1718 svg_info->element.cy=
1719 GetUserSpaceCoordinateValue(svg_info,-1,value);
1727 if (LocaleCompare(keyword,"d") == 0)
1729 (void) CloneString(&svg_info->vertices,value);
1732 if (LocaleCompare(keyword,"dx") == 0)
1737 dx=GetUserSpaceCoordinateValue(svg_info,1,value);
1738 svg_info->bounds.x+=dx;
1739 svg_info->text_offset.x+=dx;
1740 if (LocaleCompare((char *) name,"text") == 0)
1741 (void) FormatLocaleFile(svg_info->file,"translate %g,0.0\n",dx);
1744 if (LocaleCompare(keyword,"dy") == 0)
1749 dy=GetUserSpaceCoordinateValue(svg_info,-1,value);
1750 svg_info->bounds.y+=dy;
1751 svg_info->text_offset.y+=dy;
1752 if (LocaleCompare((char *) name,"text") == 0)
1753 (void) FormatLocaleFile(svg_info->file,"translate 0.0,%g\n",dy);
1761 if (LocaleCompare(keyword,"fill") == 0)
1763 if (LocaleCompare(value,"currentColor") == 0)
1765 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
1768 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1771 if (LocaleCompare(keyword,"fillcolor") == 0)
1773 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1776 if (LocaleCompare(keyword,"fill-rule") == 0)
1778 (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",
1782 if (LocaleCompare(keyword,"fill-opacity") == 0)
1784 (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
1788 if (LocaleCompare(keyword,"font-family") == 0)
1790 (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
1794 if (LocaleCompare(keyword,"font-stretch") == 0)
1796 (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
1800 if (LocaleCompare(keyword,"font-style") == 0)
1802 (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
1806 if (LocaleCompare(keyword,"font-size") == 0)
1808 if (LocaleCompare(value,"xx-small") == 0)
1809 svg_info->pointsize=6.144;
1810 else if (LocaleCompare(value,"x-small") == 0)
1811 svg_info->pointsize=7.68;
1812 else if (LocaleCompare(value,"small") == 0)
1813 svg_info->pointsize=9.6;
1814 else if (LocaleCompare(value,"medium") == 0)
1815 svg_info->pointsize=12.0;
1816 else if (LocaleCompare(value,"large") == 0)
1817 svg_info->pointsize=14.4;
1818 else if (LocaleCompare(value,"x-large") == 0)
1819 svg_info->pointsize=17.28;
1820 else if (LocaleCompare(value,"xx-large") == 0)
1821 svg_info->pointsize=20.736;
1823 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,
1825 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1826 svg_info->pointsize);
1829 if (LocaleCompare(keyword,"font-weight") == 0)
1831 (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
1840 if (LocaleCompare(keyword,"gradientTransform") == 0)
1847 GetAffineMatrix(&transform);
1848 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1849 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
1850 if (tokens == (char **) NULL)
1852 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
1854 keyword=(char *) tokens[j];
1855 if (keyword == (char *) NULL)
1857 value=(char *) tokens[j+1];
1858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1859 " %s: %s",keyword,value);
1861 GetAffineMatrix(&affine);
1867 if (LocaleCompare(keyword,"matrix") == 0)
1869 p=(const char *) value;
1870 (void) GetNextToken(p,&p,MagickPathExtent,token);
1871 affine.sx=StringToDouble(value,(char **) NULL);
1872 (void) GetNextToken(p,&p,MagickPathExtent,token);
1874 (void) GetNextToken(p,&p,MagickPathExtent,token);
1875 affine.rx=StringToDouble(token,&next_token);
1876 (void) GetNextToken(p,&p,MagickPathExtent,token);
1878 (void) GetNextToken(p,&p,MagickPathExtent,token);
1879 affine.ry=StringToDouble(token,&next_token);
1880 (void) GetNextToken(p,&p,MagickPathExtent,token);
1882 (void) GetNextToken(p,&p,MagickPathExtent,token);
1883 affine.sy=StringToDouble(token,&next_token);
1884 (void) GetNextToken(p,&p,MagickPathExtent,token);
1886 (void) GetNextToken(p,&p,MagickPathExtent,token);
1887 affine.tx=StringToDouble(token,&next_token);
1888 (void) GetNextToken(p,&p,MagickPathExtent,token);
1890 (void) GetNextToken(p,&p,MagickPathExtent,token);
1891 affine.ty=StringToDouble(token,&next_token);
1899 if (LocaleCompare(keyword,"rotate") == 0)
1904 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1905 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1906 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1907 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1908 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1916 if (LocaleCompare(keyword,"scale") == 0)
1918 for (p=(const char *) value; *p != '\0'; p++)
1919 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1922 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1923 affine.sy=affine.sx;
1926 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1927 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1930 if (LocaleCompare(keyword,"skewX") == 0)
1932 affine.sx=svg_info->affine.sx;
1933 affine.ry=tan(DegreesToRadians(fmod(
1934 GetUserSpaceCoordinateValue(svg_info,1,value),
1936 affine.sy=svg_info->affine.sy;
1939 if (LocaleCompare(keyword,"skewY") == 0)
1941 affine.sx=svg_info->affine.sx;
1942 affine.rx=tan(DegreesToRadians(fmod(
1943 GetUserSpaceCoordinateValue(svg_info,-1,value),
1945 affine.sy=svg_info->affine.sy;
1953 if (LocaleCompare(keyword,"translate") == 0)
1955 for (p=(const char *) value; *p != '\0'; p++)
1956 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1959 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1960 affine.ty=affine.tx;
1963 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1971 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1972 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1973 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1974 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1975 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1977 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1980 (void) FormatLocaleFile(svg_info->file,
1981 "affine %g %g %g %g %g %g\n",transform.sx,
1982 transform.rx,transform.ry,transform.sy,transform.tx,
1984 for (j=0; tokens[j] != (char *) NULL; j++)
1985 tokens[j]=DestroyString(tokens[j]);
1986 tokens=(char **) RelinquishMagickMemory(tokens);
1989 if (LocaleCompare(keyword,"gradientUnits") == 0)
1991 (void) CloneString(&units,value);
1992 (void) FormatLocaleFile(svg_info->file,"gradient-units \"%s\"\n",
2001 if (LocaleCompare(keyword,"height") == 0)
2003 svg_info->bounds.height=
2004 GetUserSpaceCoordinateValue(svg_info,-1,value);
2007 if (LocaleCompare(keyword,"href") == 0)
2009 (void) CloneString(&svg_info->url,value);
2017 if (LocaleCompare(keyword,"kerning") == 0)
2019 (void) FormatLocaleFile(svg_info->file,"kerning \"%s\"\n",
2028 if (LocaleCompare(keyword,"letter-spacing") == 0)
2030 (void) FormatLocaleFile(svg_info->file,"letter-spacing \"%s\"\n",
2039 if (LocaleCompare(keyword,"major") == 0)
2041 svg_info->element.major=
2042 GetUserSpaceCoordinateValue(svg_info,1,value);
2045 if (LocaleCompare(keyword,"mask") == 0)
2047 (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
2050 if (LocaleCompare(keyword,"minor") == 0)
2052 svg_info->element.minor=
2053 GetUserSpaceCoordinateValue(svg_info,-1,value);
2061 if (LocaleCompare(keyword,"offset") == 0)
2063 (void) CloneString(&svg_info->offset,value);
2066 if (LocaleCompare(keyword,"opacity") == 0)
2068 (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
2076 if (LocaleCompare(keyword,"path") == 0)
2078 (void) CloneString(&svg_info->url,value);
2081 if (LocaleCompare(keyword,"points") == 0)
2083 (void) CloneString(&svg_info->vertices,value);
2091 if (LocaleCompare(keyword,"r") == 0)
2093 svg_info->element.major=
2094 GetUserSpaceCoordinateValue(svg_info,1,value);
2095 svg_info->element.minor=
2096 GetUserSpaceCoordinateValue(svg_info,-1,value);
2099 if (LocaleCompare(keyword,"rotate") == 0)
2104 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
2105 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2106 svg_info->bounds.x,svg_info->bounds.y);
2107 svg_info->bounds.x=0;
2108 svg_info->bounds.y=0;
2109 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
2112 if (LocaleCompare(keyword,"rx") == 0)
2114 if (LocaleCompare((const char *) name,"ellipse") == 0)
2115 svg_info->element.major=
2116 GetUserSpaceCoordinateValue(svg_info,1,value);
2119 GetUserSpaceCoordinateValue(svg_info,1,value);
2122 if (LocaleCompare(keyword,"ry") == 0)
2124 if (LocaleCompare((const char *) name,"ellipse") == 0)
2125 svg_info->element.minor=
2126 GetUserSpaceCoordinateValue(svg_info,-1,value);
2129 GetUserSpaceCoordinateValue(svg_info,-1,value);
2137 if (LocaleCompare(keyword,"stop-color") == 0)
2139 (void) CloneString(&svg_info->stop_color,value);
2142 if (LocaleCompare(keyword,"stroke") == 0)
2144 if (LocaleCompare(value,"currentColor") == 0)
2146 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",
2150 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",value);
2153 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
2155 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
2156 LocaleCompare(value,"true") == 0);
2159 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
2161 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
2165 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
2167 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
2168 GetUserSpaceCoordinateValue(svg_info,1,value));
2171 if (LocaleCompare(keyword,"stroke-linecap") == 0)
2173 (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
2177 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
2179 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
2183 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
2185 (void) FormatLocaleFile(svg_info->file,
2186 "stroke-miterlimit \"%s\"\n",value);
2189 if (LocaleCompare(keyword,"stroke-opacity") == 0)
2191 (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
2195 if (LocaleCompare(keyword,"stroke-width") == 0)
2197 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
2198 GetUserSpaceCoordinateValue(svg_info,1,value));
2201 if (LocaleCompare(keyword,"style") == 0)
2203 SVGProcessStyleElement(context,name,value);
2211 if (LocaleCompare(keyword,"text-align") == 0)
2213 (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",
2217 if (LocaleCompare(keyword,"text-anchor") == 0)
2219 (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
2223 if (LocaleCompare(keyword,"text-decoration") == 0)
2225 if (LocaleCompare(value,"underline") == 0)
2226 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
2227 if (LocaleCompare(value,"line-through") == 0)
2228 (void) FormatLocaleFile(svg_info->file,
2229 "decorate line-through\n");
2230 if (LocaleCompare(value,"overline") == 0)
2231 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
2234 if (LocaleCompare(keyword,"text-antialiasing") == 0)
2236 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
2237 LocaleCompare(value,"true") == 0);
2240 if (LocaleCompare(keyword,"transform") == 0)
2247 GetAffineMatrix(&transform);
2248 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
2249 tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
2250 if (tokens == (char **) NULL)
2252 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2254 keyword=(char *) tokens[j];
2255 value=(char *) tokens[j+1];
2256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2257 " %s: %s",keyword,value);
2259 GetAffineMatrix(&affine);
2265 if (LocaleCompare(keyword,"matrix") == 0)
2267 p=(const char *) value;
2268 (void) GetNextToken(p,&p,MagickPathExtent,token);
2269 affine.sx=StringToDouble(value,(char **) NULL);
2270 (void) GetNextToken(p,&p,MagickPathExtent,token);
2272 (void) GetNextToken(p,&p,MagickPathExtent,token);
2273 affine.rx=StringToDouble(token,&next_token);
2274 (void) GetNextToken(p,&p,MagickPathExtent,token);
2276 (void) GetNextToken(p,&p,MagickPathExtent,token);
2277 affine.ry=StringToDouble(token,&next_token);
2278 (void) GetNextToken(p,&p,MagickPathExtent,token);
2280 (void) GetNextToken(p,&p,MagickPathExtent,token);
2281 affine.sy=StringToDouble(token,&next_token);
2282 (void) GetNextToken(p,&p,MagickPathExtent,token);
2284 (void) GetNextToken(p,&p,MagickPathExtent,token);
2285 affine.tx=StringToDouble(token,&next_token);
2286 (void) GetNextToken(p,&p,MagickPathExtent,token);
2288 (void) GetNextToken(p,&p,MagickPathExtent,token);
2289 affine.ty=StringToDouble(token,&next_token);
2297 if (LocaleCompare(keyword,"rotate") == 0)
2304 p=(const char *) value;
2305 (void) GetNextToken(p,&p,MagickPathExtent,token);
2306 angle=StringToDouble(value,(char **) NULL);
2307 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
2308 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
2309 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
2310 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
2311 (void) GetNextToken(p,&p,MagickPathExtent,token);
2313 (void) GetNextToken(p,&p,MagickPathExtent,token);
2314 x=StringToDouble(token,&next_token);
2315 (void) GetNextToken(p,&p,MagickPathExtent,token);
2317 (void) GetNextToken(p,&p,MagickPathExtent,token);
2318 y=StringToDouble(token,&next_token);
2319 affine.tx=svg_info->bounds.x+x*
2320 cos(DegreesToRadians(fmod(angle,360.0)))+y*
2321 sin(DegreesToRadians(fmod(angle,360.0)));
2322 affine.ty=svg_info->bounds.y-x*
2323 sin(DegreesToRadians(fmod(angle,360.0)))+y*
2324 cos(DegreesToRadians(fmod(angle,360.0)));
2334 if (LocaleCompare(keyword,"scale") == 0)
2336 for (p=(const char *) value; *p != '\0'; p++)
2337 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2340 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
2341 affine.sy=affine.sx;
2343 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
2345 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
2348 if (LocaleCompare(keyword,"skewX") == 0)
2350 affine.sx=svg_info->affine.sx;
2351 affine.ry=tan(DegreesToRadians(fmod(
2352 GetUserSpaceCoordinateValue(svg_info,1,value),
2354 affine.sy=svg_info->affine.sy;
2357 if (LocaleCompare(keyword,"skewY") == 0)
2359 affine.sx=svg_info->affine.sx;
2360 affine.rx=tan(DegreesToRadians(fmod(
2361 GetUserSpaceCoordinateValue(svg_info,-1,value),
2363 affine.sy=svg_info->affine.sy;
2371 if (LocaleCompare(keyword,"translate") == 0)
2373 for (p=(const char *) value; *p != '\0'; p++)
2374 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2377 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2380 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2389 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2390 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2391 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2392 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2393 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2395 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2398 (void) FormatLocaleFile(svg_info->file,
2399 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2400 transform.ry,transform.sy,transform.tx,transform.ty);
2401 for (j=0; tokens[j] != (char *) NULL; j++)
2402 tokens[j]=DestroyString(tokens[j]);
2403 tokens=(char **) RelinquishMagickMemory(tokens);
2411 if (LocaleCompare(keyword,"verts") == 0)
2413 (void) CloneString(&svg_info->vertices,value);
2416 if (LocaleCompare(keyword,"viewBox") == 0)
2418 p=(const char *) value;
2419 (void) GetNextToken(p,&p,MagickPathExtent,token);
2420 svg_info->view_box.x=StringToDouble(token,&next_token);
2421 (void) GetNextToken(p,&p,MagickPathExtent,token);
2423 (void) GetNextToken(p,&p,MagickPathExtent,token);
2424 svg_info->view_box.y=StringToDouble(token,&next_token);
2425 (void) GetNextToken(p,&p,MagickPathExtent,token);
2427 (void) GetNextToken(p,&p,MagickPathExtent,token);
2428 svg_info->view_box.width=StringToDouble(token,
2430 if (svg_info->bounds.width == 0)
2431 svg_info->bounds.width=svg_info->view_box.width;
2432 (void) GetNextToken(p,&p,MagickPathExtent,token);
2434 (void) GetNextToken(p,&p,MagickPathExtent,token);
2435 svg_info->view_box.height=StringToDouble(token,
2437 if (svg_info->bounds.height == 0)
2438 svg_info->bounds.height=svg_info->view_box.height;
2446 if (LocaleCompare(keyword,"width") == 0)
2448 svg_info->bounds.width=
2449 GetUserSpaceCoordinateValue(svg_info,1,value);
2457 if (LocaleCompare(keyword,"x") == 0)
2459 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2462 if (LocaleCompare(keyword,"xlink:href") == 0)
2464 (void) CloneString(&svg_info->url,value);
2467 if (LocaleCompare(keyword,"x1") == 0)
2469 svg_info->segment.x1=
2470 GetUserSpaceCoordinateValue(svg_info,1,value);
2473 if (LocaleCompare(keyword,"x2") == 0)
2475 svg_info->segment.x2=
2476 GetUserSpaceCoordinateValue(svg_info,1,value);
2484 if (LocaleCompare(keyword,"y") == 0)
2486 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2489 if (LocaleCompare(keyword,"y1") == 0)
2491 svg_info->segment.y1=
2492 GetUserSpaceCoordinateValue(svg_info,-1,value);
2495 if (LocaleCompare(keyword,"y2") == 0)
2497 svg_info->segment.y2=
2498 GetUserSpaceCoordinateValue(svg_info,-1,value);
2507 if (LocaleCompare((const char *) name,"svg") == 0)
2509 if (svg_info->document->encoding != (const xmlChar *) NULL)
2510 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2511 (const char *) svg_info->document->encoding);
2512 if (attributes != (const xmlChar **) NULL)
2520 if ((svg_info->view_box.width == 0.0) ||
2521 (svg_info->view_box.height == 0.0))
2522 svg_info->view_box=svg_info->bounds;
2524 if (svg_info->bounds.width > 0.0)
2525 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2527 if (svg_info->bounds.height > 0.0)
2528 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2529 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2530 (double) svg_info->width,(double) svg_info->height);
2531 sx=PerceptibleReciprocal(svg_info->view_box.width)*svg_info->width;
2532 sy=PerceptibleReciprocal(svg_info->view_box.height)*svg_info->height;
2533 tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2535 ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2537 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2539 if ((svg_info->svgDepth == 1) && (*background != '\0'))
2541 PushGraphicContext(id);
2542 (void) FormatLocaleFile(svg_info->file,"fill %s\n",background);
2543 (void) FormatLocaleFile(svg_info->file,
2544 "rectangle 0,0 %g,%g\n",svg_info->view_box.width,
2545 svg_info->view_box.height);
2546 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2550 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2551 if (units != (char *) NULL)
2552 units=DestroyString(units);
2553 if (color != (char *) NULL)
2554 color=DestroyString(color);
2557 static void SVGEndElement(void *context,const xmlChar *name)
2563 Called when the end of an element has been detected.
2565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2566 " SAX.endElement(%s)",name);
2567 svg_info=(SVGInfo *) context;
2568 if (strchr((char *) name,':') != (char *) NULL)
2571 Skip over namespace.
2573 for ( ; *name != ':'; name++) ;
2581 if (LocaleCompare((const char *) name,"circle") == 0)
2583 (void) FormatLocaleFile(svg_info->file,"class \"circle\"\n");
2584 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2585 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2586 svg_info->element.cy+svg_info->element.minor);
2587 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2590 if (LocaleCompare((const char *) name,"clipPath") == 0)
2592 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2600 if (LocaleCompare((const char *) name,"defs") == 0)
2602 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2605 if (LocaleCompare((const char *) name,"desc") == 0)
2610 if (*svg_info->text == '\0')
2612 (void) fputc('#',svg_info->file);
2613 for (p=svg_info->text; *p != '\0'; p++)
2615 (void) fputc(*p,svg_info->file);
2617 (void) fputc('#',svg_info->file);
2619 (void) fputc('\n',svg_info->file);
2620 *svg_info->text='\0';
2628 if (LocaleCompare((const char *) name,"ellipse") == 0)
2633 (void) FormatLocaleFile(svg_info->file,"class \"ellipse\"\n");
2634 angle=svg_info->element.angle;
2635 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2636 svg_info->element.cx,svg_info->element.cy,
2637 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2638 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2639 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2647 if (LocaleCompare((const char *) name,"foreignObject") == 0)
2649 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2657 if (LocaleCompare((const char *) name,"g") == 0)
2659 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2667 if (LocaleCompare((const char *) name,"image") == 0)
2669 (void) FormatLocaleFile(svg_info->file,
2670 "image Over %g,%g %g,%g \"%s\"\n",svg_info->bounds.x,
2671 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2673 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2681 if (LocaleCompare((const char *) name,"line") == 0)
2683 (void) FormatLocaleFile(svg_info->file,"class \"line\"\n");
2684 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2685 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2686 svg_info->segment.y2);
2687 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2690 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2692 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2700 if (LocaleCompare((const char *) name,"mask") == 0)
2702 (void) FormatLocaleFile(svg_info->file,"pop mask\n");
2710 if (LocaleCompare((const char *) name,"pattern") == 0)
2712 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2715 if (LocaleCompare((const char *) name,"path") == 0)
2717 (void) FormatLocaleFile(svg_info->file,"class \"path\"\n");
2718 (void) FormatLocaleFile(svg_info->file,"path \"%s\"\n",
2719 svg_info->vertices);
2720 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2723 if (LocaleCompare((const char *) name,"polygon") == 0)
2725 (void) FormatLocaleFile(svg_info->file,"class \"polygon\"\n");
2726 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2727 svg_info->vertices);
2728 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2731 if (LocaleCompare((const char *) name,"polyline") == 0)
2733 (void) FormatLocaleFile(svg_info->file,"class \"polyline\"\n");
2734 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2735 svg_info->vertices);
2736 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2744 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2746 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2749 if (LocaleCompare((const char *) name,"rect") == 0)
2751 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2753 (void) FormatLocaleFile(svg_info->file,"class \"rect\"\n");
2754 if ((fabs(svg_info->bounds.width-1.0) < MagickEpsilon) &&
2755 (fabs(svg_info->bounds.height-1.0) < MagickEpsilon))
2756 (void) FormatLocaleFile(svg_info->file,"point %g,%g\n",
2757 svg_info->bounds.x,svg_info->bounds.y);
2759 (void) FormatLocaleFile(svg_info->file,
2760 "rectangle %g,%g %g,%g\n",svg_info->bounds.x,
2761 svg_info->bounds.y,svg_info->bounds.x+svg_info->bounds.width,
2762 svg_info->bounds.y+svg_info->bounds.height);
2763 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2766 if (svg_info->radius.x == 0.0)
2767 svg_info->radius.x=svg_info->radius.y;
2768 if (svg_info->radius.y == 0.0)
2769 svg_info->radius.y=svg_info->radius.x;
2770 (void) FormatLocaleFile(svg_info->file,
2771 "roundRectangle %g,%g %g,%g %g,%g\n",
2772 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2773 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2774 svg_info->radius.x,svg_info->radius.y);
2775 svg_info->radius.x=0.0;
2776 svg_info->radius.y=0.0;
2777 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2785 if (LocaleCompare((const char *) name,"stop") == 0)
2787 (void) FormatLocaleFile(svg_info->file,"stop-color \"%s\" %s\n",
2788 svg_info->stop_color,svg_info->offset);
2791 if (LocaleCompare((char *) name,"style") == 0)
2805 Find style definitions in svg_info->text.
2807 tokens=SVGKeyValuePairs(context,'{','}',svg_info->text,
2809 if (tokens == (char **) NULL)
2811 for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2813 keyword=(char *) tokens[j];
2814 value=(char *) tokens[j+1];
2815 (void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
2816 *keyword == '.' ? keyword+1 : keyword);
2817 SVGProcessStyleElement(context,name,value);
2818 (void) FormatLocaleFile(svg_info->file,"pop class\n");
2820 for (j=0; tokens[j] != (char *) NULL; j++)
2821 tokens[j]=DestroyString(tokens[j]);
2822 tokens=(char **) RelinquishMagickMemory(tokens);
2825 if (LocaleCompare((const char *) name,"svg") == 0)
2827 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2828 svg_info->svgDepth--;
2831 if (LocaleCompare((const char *) name,"symbol") == 0)
2833 (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
2841 if (LocaleCompare((const char *) name,"text") == 0)
2843 if (*svg_info->text != '\0')
2848 SVGStripString(MagickTrue,svg_info->text);
2849 text=EscapeString(svg_info->text,'\"');
2850 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2851 svg_info->text_offset.x,svg_info->text_offset.y,text);
2852 text=DestroyString(text);
2853 *svg_info->text='\0';
2855 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2858 if (LocaleCompare((const char *) name,"tspan") == 0)
2860 if (*svg_info->text != '\0')
2865 (void) FormatLocaleFile(svg_info->file,"class \"tspan\"\n");
2866 text=EscapeString(svg_info->text,'\"');
2867 (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2868 svg_info->bounds.x,svg_info->bounds.y,text);
2869 text=DestroyString(text);
2870 *svg_info->text='\0';
2872 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2875 if (LocaleCompare((const char *) name,"title") == 0)
2877 if (*svg_info->text == '\0')
2879 (void) CloneString(&svg_info->title,svg_info->text);
2880 *svg_info->text='\0';
2888 if (LocaleCompare((char *) name,"use") == 0)
2890 if ((svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0))
2891 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2892 svg_info->bounds.x,svg_info->bounds.y);
2893 (void) FormatLocaleFile(svg_info->file,"use \"url(%s)\"\n",
2895 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2903 *svg_info->text='\0';
2904 (void) memset(&svg_info->element,0,sizeof(svg_info->element));
2905 (void) memset(&svg_info->segment,0,sizeof(svg_info->segment));
2909 static void SVGCharacters(void *context,const xmlChar *c,int length)
2924 Receiving some characters from the parser.
2926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2927 " SAX.characters(%s,%.20g)",c,(double) length);
2928 svg_info=(SVGInfo *) context;
2929 text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2930 if (text == (char *) NULL)
2933 for (i=0; i < (ssize_t) length; i++)
2936 SVGStripString(MagickFalse,text);
2937 if (svg_info->text == (char *) NULL)
2938 svg_info->text=text;
2941 (void) ConcatenateString(&svg_info->text,text);
2942 text=DestroyString(text);
2946 static void SVGReference(void *context,const xmlChar *name)
2955 Called when an entity reference is detected.
2957 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2959 svg_info=(SVGInfo *) context;
2960 parser=svg_info->parser;
2961 if (parser == (xmlParserCtxtPtr) NULL)
2963 if (parser->node == (xmlNodePtr) NULL)
2966 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2968 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2971 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2977 Receiving some ignorable whitespaces from the parser.
2979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2980 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2981 svg_info=(SVGInfo *) context;
2985 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2986 const xmlChar *data)
2992 A processing instruction has been parsed.
2994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2995 " SAX.processingInstruction(%s, %s)",target,data);
2996 svg_info=(SVGInfo *) context;
3000 static void SVGComment(void *context,const xmlChar *value)
3006 A comment has been parsed.
3008 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
3010 svg_info=(SVGInfo *) context;
3011 if (svg_info->comment != (char *) NULL)
3012 (void) ConcatenateString(&svg_info->comment,"\n");
3013 (void) ConcatenateString(&svg_info->comment,(const char *) value);
3016 static void SVGWarning(void *,const char *,...)
3017 magick_attribute((__format__ (__printf__,2,3)));
3019 static void SVGWarning(void *context,const char *format,...)
3023 reason[MagickPathExtent];
3032 Display and format a warning messages, gives file, line, position and
3035 va_start(operands,format);
3036 svg_info=(SVGInfo *) context;
3037 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
3038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
3039 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
3040 (void) vsprintf(reason,format,operands);
3042 (void) vsnprintf(reason,MagickPathExtent,format,operands);
3044 message=GetExceptionMessage(errno);
3045 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
3046 DelegateWarning,reason,"`%s`",message);
3047 message=DestroyString(message);
3051 static void SVGError(void *,const char *,...)
3052 magick_attribute((__format__ (__printf__,2,3)));
3054 static void SVGError(void *context,const char *format,...)
3058 reason[MagickPathExtent];
3067 Display and format a error formats, gives file, line, position and
3070 va_start(operands,format);
3071 svg_info=(SVGInfo *) context;
3072 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
3073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
3074 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
3075 (void) vsprintf(reason,format,operands);
3077 (void) vsnprintf(reason,MagickPathExtent,format,operands);
3079 message=GetExceptionMessage(errno);
3080 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
3081 reason,"`%s`",message);
3082 message=DestroyString(message);
3086 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
3098 Called when a pcdata block has been parsed.
3100 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
3102 svg_info=(SVGInfo *) context;
3103 parser=svg_info->parser;
3104 child=xmlGetLastChild(parser->node);
3105 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
3107 xmlTextConcat(child,value,length);
3110 child=xmlNewCDataBlock(parser->myDoc,value,length);
3111 if (xmlAddChild(parser->node,child) == (xmlNodePtr) NULL)
3115 static void SVGExternalSubset(void *context,const xmlChar *name,
3116 const xmlChar *external_id,const xmlChar *system_id)
3131 Does this document has an external subset?
3133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3134 " SAX.externalSubset(%s, %s, %s)",name,
3135 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
3136 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
3137 svg_info=(SVGInfo *) context;
3138 parser=svg_info->parser;
3139 if (((external_id == NULL) && (system_id == NULL)) ||
3140 ((parser->validate == 0) || (parser->wellFormed == 0) ||
3141 (svg_info->document == 0)))
3143 input=SVGResolveEntity(context,external_id,system_id);
3146 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
3147 parser_context=(*parser);
3148 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
3149 if (parser->inputTab == (xmlParserInputPtr *) NULL)
3151 parser->errNo=XML_ERR_NO_MEMORY;
3152 parser->input=parser_context.input;
3153 parser->inputNr=parser_context.inputNr;
3154 parser->inputMax=parser_context.inputMax;
3155 parser->inputTab=parser_context.inputTab;
3161 xmlPushInput(parser,input);
3162 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
3163 if (input->filename == (char *) NULL)
3164 input->filename=(char *) xmlStrdup(system_id);
3167 input->base=parser->input->cur;
3168 input->cur=parser->input->cur;
3170 xmlParseExternalSubset(parser,external_id,system_id);
3171 while (parser->inputNr > 1)
3172 (void) xmlPopInput(parser);
3173 xmlFreeInputStream(parser->input);
3174 xmlFree(parser->inputTab);
3175 parser->input=parser_context.input;
3176 parser->inputNr=parser_context.inputNr;
3177 parser->inputMax=parser_context.inputMax;
3178 parser->inputTab=parser_context.inputTab;
3181 #if defined(__cplusplus) || defined(c_plusplus)
3185 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3188 filename[MagickPathExtent];
3208 message[MagickPathExtent];
3219 assert(image_info != (const ImageInfo *) NULL);
3220 assert(image_info->signature == MagickCoreSignature);
3221 assert(exception != (ExceptionInfo *) NULL);
3222 if (image_info->debug != MagickFalse)
3223 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3224 image_info->filename);
3225 assert(exception->signature == MagickCoreSignature);
3226 image=AcquireImage(image_info,exception);
3227 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3228 if (status == MagickFalse)
3230 image=DestroyImageList(image);
3231 return((Image *) NULL);
3233 if ((fabs(image->resolution.x) < MagickEpsilon) ||
3234 (fabs(image->resolution.y) < MagickEpsilon))
3242 flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3243 image->resolution.x=geometry_info.rho;
3244 image->resolution.y=geometry_info.sigma;
3245 if ((flags & SigmaValue) == 0)
3246 image->resolution.y=image->resolution.x;
3248 if (LocaleCompare(image_info->magick,"MSVG") != 0)
3253 svg_image=RenderSVGImage(image_info,image,exception);
3254 if (svg_image != (Image *) NULL)
3256 image=DestroyImageList(image);
3260 #if defined(MAGICKCORE_RSVG_DELEGATE)
3261 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3274 register unsigned char
3287 register const guchar
3309 svg_handle=rsvg_handle_new();
3310 if (svg_handle == (RsvgHandle *) NULL)
3311 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3312 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
3313 if ((fabs(image->resolution.x) > MagickEpsilon) &&
3314 (fabs(image->resolution.y) > MagickEpsilon))
3315 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3316 image->resolution.y);
3317 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3320 error=(GError *) NULL;
3321 (void) rsvg_handle_write(svg_handle,message,n,&error);
3322 if (error != (GError *) NULL)
3323 g_error_free(error);
3325 error=(GError *) NULL;
3326 rsvg_handle_close(svg_handle,&error);
3327 if (error != (GError *) NULL)
3328 g_error_free(error);
3329 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3330 apply_density=MagickTrue;
3331 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
3332 if ((image->resolution.x > 0.0) && (image->resolution.y > 0.0))
3338 We should not apply the density when the internal 'factor' is 'i'.
3339 This can be checked by using the trick below.
3341 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x*256,
3342 image->resolution.y*256);
3343 rsvg_handle_get_dimensions(svg_handle,&dpi_dimension_info);
3344 if ((dpi_dimension_info.width != dimension_info.width) ||
3345 (dpi_dimension_info.height != dimension_info.height))
3346 apply_density=MagickFalse;
3347 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3348 image->resolution.y);
3350 if (image_info->size != (char *) NULL)
3352 (void) GetGeometry(image_info->size,(ssize_t *) NULL,
3353 (ssize_t *) NULL,&image->columns,&image->rows);
3354 if ((image->columns != 0) || (image->rows != 0))
3356 image->resolution.x=DefaultSVGDensity*image->columns/
3357 dimension_info.width;
3358 image->resolution.y=DefaultSVGDensity*image->rows/
3359 dimension_info.height;
3360 if (fabs(image->resolution.x) < MagickEpsilon)
3361 image->resolution.x=image->resolution.y;
3363 if (fabs(image->resolution.y) < MagickEpsilon)
3364 image->resolution.y=image->resolution.x;
3366 image->resolution.x=image->resolution.y=MagickMin(
3367 image->resolution.x,image->resolution.y);
3368 apply_density=MagickTrue;
3371 if (apply_density != MagickFalse)
3373 image->columns=image->resolution.x*dimension_info.width/
3375 image->rows=image->resolution.y*dimension_info.height/
3380 image->columns=dimension_info.width;
3381 image->rows=dimension_info.height;
3383 pixel_info=(MemoryInfo *) NULL;
3385 pixel_buffer=rsvg_handle_get_pixbuf(svg_handle);
3386 rsvg_handle_free(svg_handle);
3387 image->columns=gdk_pixbuf_get_width(pixel_buffer);
3388 image->rows=gdk_pixbuf_get_height(pixel_buffer);
3390 image->alpha_trait=BlendPixelTrait;
3391 if (image_info->ping == MagickFalse)
3393 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3398 status=SetImageExtent(image,image->columns,image->rows,exception);
3399 if (status == MagickFalse)
3401 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
3402 g_object_unref(G_OBJECT(pixel_buffer));
3404 g_object_unref(svg_handle);
3405 ThrowReaderException(MissingDelegateError,
3406 "NoDecodeDelegateForThisImageFormat");
3408 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3409 stride=4*image->columns;
3410 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
3411 stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
3412 (int) image->columns);
3414 pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels));
3415 if (pixel_info == (MemoryInfo *) NULL)
3417 g_object_unref(svg_handle);
3418 ThrowReaderException(ResourceLimitError,
3419 "MemoryAllocationFailed");
3421 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3423 (void) SetImageBackgroundColor(image,exception);
3424 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3425 cairo_surface=cairo_image_surface_create_for_data(pixels,
3426 CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int)
3428 if ((cairo_surface == (cairo_surface_t *) NULL) ||
3429 (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS))
3431 if (cairo_surface != (cairo_surface_t *) NULL)
3432 cairo_surface_destroy(cairo_surface);
3433 pixel_info=RelinquishVirtualMemory(pixel_info);
3434 g_object_unref(svg_handle);
3435 ThrowReaderException(ResourceLimitError,
3436 "MemoryAllocationFailed");
3438 cairo_image=cairo_create(cairo_surface);
3439 cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
3440 cairo_paint(cairo_image);
3441 cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
3442 if (apply_density != MagickFalse)
3443 cairo_scale(cairo_image,image->resolution.x/DefaultSVGDensity,
3444 image->resolution.y/DefaultSVGDensity);
3445 rsvg_handle_render_cairo(svg_handle,cairo_image);
3446 cairo_destroy(cairo_image);
3447 cairo_surface_destroy(cairo_surface);
3448 g_object_unref(svg_handle);
3451 p=gdk_pixbuf_get_pixels(pixel_buffer);
3453 GetPixelInfo(image,&fill_color);
3454 for (y=0; y < (ssize_t) image->rows; y++)
3456 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3457 if (q == (Quantum *) NULL)
3459 for (x=0; x < (ssize_t) image->columns; x++)
3461 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3462 fill_color.blue=ScaleCharToQuantum(*p++);
3463 fill_color.green=ScaleCharToQuantum(*p++);
3464 fill_color.red=ScaleCharToQuantum(*p++);
3466 fill_color.red=ScaleCharToQuantum(*p++);
3467 fill_color.green=ScaleCharToQuantum(*p++);
3468 fill_color.blue=ScaleCharToQuantum(*p++);
3470 fill_color.alpha=ScaleCharToQuantum(*p++);
3471 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3476 gamma=QuantumScale*fill_color.alpha;
3477 gamma=PerceptibleReciprocal(gamma);
3478 fill_color.blue*=gamma;
3479 fill_color.green*=gamma;
3480 fill_color.red*=gamma;
3483 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
3484 GetPixelAlpha(image,q),q);
3485 q+=GetPixelChannels(image);
3487 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3489 if (image->previous == (Image *) NULL)
3491 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
3493 if (status == MagickFalse)
3498 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3499 if (pixel_info != (MemoryInfo *) NULL)
3500 pixel_info=RelinquishVirtualMemory(pixel_info);
3502 g_object_unref(G_OBJECT(pixel_buffer));
3504 (void) CloseBlob(image);
3505 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3507 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3508 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3509 next=GetNextImageInList(next);
3511 return(GetFirstImageInList(image));
3519 unique_file=AcquireUniqueFileResource(filename);
3520 if (unique_file != -1)
3521 file=fdopen(unique_file,"w");
3522 if ((unique_file == -1) || (file == (FILE *) NULL))
3524 (void) CopyMagickString(image->filename,filename,MagickPathExtent);
3525 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
3527 image=DestroyImageList(image);
3528 return((Image *) NULL);
3533 svg_info=AcquireSVGInfo();
3534 if (svg_info == (SVGInfo *) NULL)
3536 (void) fclose(file);
3537 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3539 svg_info->file=file;
3540 svg_info->exception=exception;
3541 svg_info->image=image;
3542 svg_info->image_info=image_info;
3543 svg_info->bounds.width=image->columns;
3544 svg_info->bounds.height=image->rows;
3545 svg_info->svgDepth=0;
3546 if (image_info->size != (char *) NULL)
3547 (void) CloneString(&svg_info->size,image_info->size);
3548 if (image->debug != MagickFalse)
3549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3551 (void) xmlSubstituteEntitiesDefault(1);
3552 (void) memset(&sax_modules,0,sizeof(sax_modules));
3553 sax_modules.internalSubset=SVGInternalSubset;
3554 sax_modules.isStandalone=SVGIsStandalone;
3555 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3556 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3557 sax_modules.resolveEntity=SVGResolveEntity;
3558 sax_modules.getEntity=SVGGetEntity;
3559 sax_modules.entityDecl=SVGEntityDeclaration;
3560 sax_modules.notationDecl=SVGNotationDeclaration;
3561 sax_modules.attributeDecl=SVGAttributeDeclaration;
3562 sax_modules.elementDecl=SVGElementDeclaration;
3563 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3564 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3565 sax_modules.startDocument=SVGStartDocument;
3566 sax_modules.endDocument=SVGEndDocument;
3567 sax_modules.startElement=SVGStartElement;
3568 sax_modules.endElement=SVGEndElement;
3569 sax_modules.reference=SVGReference;
3570 sax_modules.characters=SVGCharacters;
3571 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3572 sax_modules.processingInstruction=SVGProcessingInstructions;
3573 sax_modules.comment=SVGComment;
3574 sax_modules.warning=SVGWarning;
3575 sax_modules.error=SVGError;
3576 sax_modules.fatalError=SVGError;
3577 sax_modules.getParameterEntity=SVGGetParameterEntity;
3578 sax_modules.cdataBlock=SVGCDataBlock;
3579 sax_modules.externalSubset=SVGExternalSubset;
3580 sax_handler=(&sax_modules);
3581 n=ReadBlob(image,MagickPathExtent-1,message);
3588 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3589 message,n,image->filename);
3590 value=GetImageOption(image_info,"svg:xml-parse-huge");
3591 if ((value != (char *) NULL) && (IsStringTrue(value) != MagickFalse))
3592 (void) xmlCtxtUseOptions(svg_info->parser,XML_PARSE_HUGE);
3593 while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3596 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3601 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3602 SVGEndDocument(svg_info);
3603 if (svg_info->parser->myDoc != (xmlDocPtr) NULL)
3604 xmlFreeDoc(svg_info->parser->myDoc);
3605 xmlFreeParserCtxt(svg_info->parser);
3606 if (image->debug != MagickFalse)
3607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3608 (void) fclose(file);
3609 (void) CloseBlob(image);
3610 image->columns=svg_info->width;
3611 image->rows=svg_info->height;
3612 if (exception->severity >= ErrorException)
3614 svg_info=DestroySVGInfo(svg_info);
3615 (void) RelinquishUniqueFileResource(filename);
3616 image=DestroyImage(image);
3617 return((Image *) NULL);
3619 if (image_info->ping == MagickFalse)
3627 image=DestroyImage(image);
3628 image=(Image *) NULL;
3629 read_info=CloneImageInfo(image_info);
3630 SetImageInfoBlob(read_info,(void *) NULL,0);
3631 (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
3633 image=ReadImage(read_info,exception);
3634 read_info=DestroyImageInfo(read_info);
3635 if (image != (Image *) NULL)
3636 (void) CopyMagickString(image->filename,image_info->filename,
3640 Relinquish resources.
3642 if (image != (Image *) NULL)
3644 if (svg_info->title != (char *) NULL)
3645 (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3646 if (svg_info->comment != (char *) NULL)
3647 (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3650 for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3652 (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3653 (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3654 next=GetNextImageInList(next);
3656 svg_info=DestroySVGInfo(svg_info);
3657 (void) RelinquishUniqueFileResource(filename);
3658 return(GetFirstImageInList(image));
3661 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3670 assert(image_info != (const ImageInfo *) NULL);
3671 assert(image_info->signature == MagickCoreSignature);
3672 assert(exception != (ExceptionInfo *) NULL);
3673 if (image_info->debug != MagickFalse)
3674 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3675 image_info->filename);
3676 assert(exception->signature == MagickCoreSignature);
3677 image=AcquireImage(image_info,exception);
3678 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3679 if (status == MagickFalse)
3681 image=DestroyImageList(image);
3682 return((Image *) NULL);
3684 if ((fabs(image->resolution.x) < MagickEpsilon) ||
3685 (fabs(image->resolution.y) < MagickEpsilon))
3693 flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3694 image->resolution.x=geometry_info.rho;
3695 image->resolution.y=geometry_info.sigma;
3696 if ((flags & SigmaValue) == 0)
3697 image->resolution.y=image->resolution.x;
3699 svg_image=RenderSVGImage(image_info,image,exception);
3700 image=DestroyImage(image);
3706 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3710 % R e g i s t e r S V G I m a g e %
3714 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3716 % RegisterSVGImage() adds attributes for the SVG image format to
3717 % the list of supported formats. The attributes include the image format
3718 % tag, a method to read and/or write the format, whether the format
3719 % supports the saving of more than one frame to the same file or blob,
3720 % whether the format supports native in-memory I/O, and a brief
3721 % description of the format.
3723 % The format of the RegisterSVGImage method is:
3725 % size_t RegisterSVGImage(void)
3728 ModuleExport size_t RegisterSVGImage(void)
3731 version[MagickPathExtent];
3737 #if defined(LIBXML_DOTTED_VERSION)
3738 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,
3741 #if defined(MAGICKCORE_RSVG_DELEGATE)
3742 #if !GLIB_CHECK_VERSION(2,35,0)
3745 (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d",
3746 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3748 entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics");
3749 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3750 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3751 entry->flags^=CoderBlobSupportFlag;
3752 #if defined(MAGICKCORE_RSVG_DELEGATE)
3753 entry->flags^=CoderDecoderThreadSupportFlag;
3755 entry->mime_type=ConstantString("image/svg+xml");
3756 if (*version != '\0')
3757 entry->version=ConstantString(version);
3758 entry->magick=(IsImageFormatHandler *) IsSVG;
3759 (void) RegisterMagickInfo(entry);
3760 entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics");
3761 #if defined(MAGICKCORE_XML_DELEGATE)
3762 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3764 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3765 entry->flags^=CoderBlobSupportFlag;
3766 #if defined(MAGICKCORE_RSVG_DELEGATE)
3767 entry->flags^=CoderDecoderThreadSupportFlag;
3769 entry->mime_type=ConstantString("image/svg+xml");
3770 if (*version != '\0')
3771 entry->version=ConstantString(version);
3772 entry->magick=(IsImageFormatHandler *) IsSVG;
3773 (void) RegisterMagickInfo(entry);
3774 entry=AcquireMagickInfo("SVG","MSVG",
3775 "ImageMagick's own SVG internal renderer");
3776 #if defined(MAGICKCORE_XML_DELEGATE)
3777 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3779 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3780 entry->flags^=CoderBlobSupportFlag;
3781 #if defined(MAGICKCORE_RSVG_DELEGATE)
3782 entry->flags^=CoderDecoderThreadSupportFlag;
3784 entry->magick=(IsImageFormatHandler *) IsSVG;
3785 (void) RegisterMagickInfo(entry);
3786 return(MagickImageCoderSignature);
3790 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3794 % U n r e g i s t e r S V G I m a g e %
3798 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3800 % UnregisterSVGImage() removes format registrations made by the
3801 % SVG module from the list of supported formats.
3803 % The format of the UnregisterSVGImage method is:
3805 % UnregisterSVGImage(void)
3808 ModuleExport void UnregisterSVGImage(void)
3810 (void) UnregisterMagickInfo("SVGZ");
3811 (void) UnregisterMagickInfo("SVG");
3812 (void) UnregisterMagickInfo("MSVG");
3816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3820 % W r i t e S V G I m a g e %
3824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3826 % WriteSVGImage() writes a image in the SVG - XML based W3C standard
3829 % The format of the WriteSVGImage method is:
3831 % MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3832 % Image *image,ExceptionInfo *exception)
3834 % A description of each parameter follows.
3836 % o image_info: the image info.
3838 % o image: The image.
3840 % o exception: return any errors or warnings in this structure.
3844 static void AffineToTransform(Image *image,AffineMatrix *affine)
3847 transform[MagickPathExtent];
3849 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3851 if ((fabs(affine->rx) < MagickEpsilon) &&
3852 (fabs(affine->ry) < MagickEpsilon))
3854 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3855 (fabs(affine->sy-1.0) < MagickEpsilon))
3857 (void) WriteBlobString(image,"\">\n");
3860 (void) FormatLocaleString(transform,MagickPathExtent,
3861 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3862 (void) WriteBlobString(image,transform);
3867 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3868 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3869 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3875 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3876 (void) FormatLocaleString(transform,MagickPathExtent,
3877 "\" transform=\"rotate(%g)\">\n",theta);
3878 (void) WriteBlobString(image,transform);
3885 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3886 (fabs(affine->rx) < MagickEpsilon) &&
3887 (fabs(affine->ry) < MagickEpsilon) &&
3888 (fabs(affine->sy-1.0) < MagickEpsilon))
3890 (void) FormatLocaleString(transform,MagickPathExtent,
3891 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3892 (void) WriteBlobString(image,transform);
3896 (void) FormatLocaleString(transform,MagickPathExtent,
3897 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3898 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3899 (void) WriteBlobString(image,transform);
3902 static MagickBooleanType IsPoint(const char *point)
3910 value=(ssize_t) strtol(point,&p,10);
3912 return(p != point ? MagickTrue : MagickFalse);
3915 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
3917 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3922 at_fitting_opts_type
3934 register const Quantum
3948 Trace image and write as SVG.
3950 fitting_options=at_fitting_opts_new();
3951 output_options=at_output_opts_new();
3952 (void) SetImageGray(image,exception);
3953 type=GetImageType(image);
3955 if ((type == BilevelType) || (type == GrayscaleType))
3957 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3959 for (y=0; y < (ssize_t) image->rows; y++)
3961 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3962 if (p == (const Quantum *) NULL)
3964 for (x=0; x < (ssize_t) image->columns; x++)
3966 trace->bitmap[i++]=GetPixelRed(image,p);
3967 if (number_planes == 3)
3969 trace->bitmap[i++]=GetPixelGreen(image,p);
3970 trace->bitmap[i++]=GetPixelBlue(image,p);
3972 p+=GetPixelChannels(image);
3975 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3977 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3978 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3983 at_splines_free(splines);
3984 at_bitmap_free(trace);
3985 at_output_opts_free(output_options);
3986 at_fitting_opts_free(fitting_options);
3992 filename[MagickPathExtent],
3993 message[MagickPathExtent];
4020 delegate_info=GetDelegateInfo((char *) NULL,"TRACE",exception);
4021 if (delegate_info != (DelegateInfo *) NULL)
4024 Trace SVG with tracing delegate.
4026 image_info=AcquireImageInfo();
4027 (void) CopyMagickString(image_info->magick,"TRACE",MagickPathExtent);
4028 (void) FormatLocaleString(filename,MagickPathExtent,"trace:%s",
4029 image_info->filename);
4030 (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
4031 status=WriteImage(image_info,image,exception);
4032 image_info=DestroyImageInfo(image_info);
4035 (void) WriteBlobString(image,
4036 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
4037 (void) WriteBlobString(image,
4038 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
4039 (void) WriteBlobString(image,
4040 " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
4041 (void) FormatLocaleString(message,MagickPathExtent,
4042 "<svg version=\"1.1\" id=\"Layer_1\" "
4043 "xmlns=\"http://www.w3.org/2000/svg\" "
4044 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" "
4045 "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" "
4046 "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">",
4047 (double) image->columns,(double) image->rows,
4048 (double) image->columns,(double) image->rows,
4049 (double) image->columns,(double) image->rows);
4050 (void) WriteBlobString(image,message);
4051 clone_image=CloneImage(image,0,0,MagickTrue,exception);
4052 if (clone_image == (Image *) NULL)
4053 return(MagickFalse);
4054 image_info=AcquireImageInfo();
4055 (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent);
4057 blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length,
4059 clone_image=DestroyImage(clone_image);
4060 image_info=DestroyImageInfo(image_info);
4061 if (blob == (unsigned char *) NULL)
4062 return(MagickFalse);
4064 base64=Base64Encode(blob,blob_length,&encode_length);
4065 blob=(unsigned char *) RelinquishMagickMemory(blob);
4066 (void) FormatLocaleString(message,MagickPathExtent,
4067 " <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" "
4068 "x=\"%.20g\" y=\"%.20g\"\n href=\"data:image/png;base64,",
4069 (double) image->scene,(double) image->columns,(double) image->rows,
4070 (double) image->page.x,(double) image->page.y);
4071 (void) WriteBlobString(image,message);
4073 for (i=(ssize_t) encode_length; i > 0; i-=76)
4075 (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p);
4076 (void) WriteBlobString(image,message);
4079 (void) WriteBlobString(image,"\n");
4081 base64=DestroyString(base64);
4082 (void) WriteBlobString(image,"\" />\n");
4083 (void) WriteBlobString(image,"</svg>\n");
4086 (void) CloseBlob(image);
4090 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
4091 ExceptionInfo *exception)
4093 #define BezierQuantum 200
4099 keyword[MagickPathExtent],
4100 message[MagickPathExtent],
4101 name[MagickPathExtent],
4104 type[MagickPathExtent];
4145 Open output image file.
4147 assert(image_info != (const ImageInfo *) NULL);
4148 assert(image_info->signature == MagickCoreSignature);
4149 assert(image != (Image *) NULL);
4150 assert(image->signature == MagickCoreSignature);
4151 if (image->debug != MagickFalse)
4152 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4153 assert(exception != (ExceptionInfo *) NULL);
4154 assert(exception->signature == MagickCoreSignature);
4155 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
4156 if (status == MagickFalse)
4158 value=GetImageArtifact(image,"SVG");
4159 if (value != (char *) NULL)
4161 (void) WriteBlobString(image,value);
4162 (void) CloseBlob(image);
4165 value=GetImageArtifact(image,"mvg:vector-graphics");
4166 if (value == (char *) NULL)
4167 return(TraceSVGImage(image,exception));
4171 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
4172 (void) WriteBlobString(image,
4173 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
4174 (void) WriteBlobString(image,
4175 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
4176 (void) FormatLocaleString(message,MagickPathExtent,
4177 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
4179 (void) WriteBlobString(image,message);
4181 Allocate primitive info memory.
4184 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
4185 sizeof(*primitive_info));
4186 if (primitive_info == (PrimitiveInfo *) NULL)
4187 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
4188 GetAffineMatrix(&affine);
4189 token=AcquireString(value);
4190 extent=strlen(token)+MagickPathExtent;
4194 for (q=(const char *) value; *q != '\0'; )
4197 Interpret graphic primitive.
4199 (void) GetNextToken(q,&q,MagickPathExtent,keyword);
4200 if (*keyword == '\0')
4202 if (*keyword == '#')
4207 if (active != MagickFalse)
4209 AffineToTransform(image,&affine);
4212 (void) WriteBlobString(image,"<desc>");
4213 (void) WriteBlobString(image,keyword+1);
4214 for ( ; (*q != '\n') && (*q != '\0'); q++)
4217 case '<': (void) WriteBlobString(image,"<"); break;
4218 case '>': (void) WriteBlobString(image,">"); break;
4219 case '&': (void) WriteBlobString(image,"&"); break;
4220 default: (void) WriteBlobByte(image,(unsigned char) *q); break;
4222 (void) WriteBlobString(image,"</desc>\n");
4225 primitive_type=UndefinedPrimitive;
4233 if (LocaleCompare("affine",keyword) == 0)
4235 (void) GetNextToken(q,&q,extent,token);
4236 affine.sx=StringToDouble(token,&next_token);
4237 (void) GetNextToken(q,&q,extent,token);
4239 (void) GetNextToken(q,&q,extent,token);
4240 affine.rx=StringToDouble(token,&next_token);
4241 (void) GetNextToken(q,&q,extent,token);
4243 (void) GetNextToken(q,&q,extent,token);
4244 affine.ry=StringToDouble(token,&next_token);
4245 (void) GetNextToken(q,&q,extent,token);
4247 (void) GetNextToken(q,&q,extent,token);
4248 affine.sy=StringToDouble(token,&next_token);
4249 (void) GetNextToken(q,&q,extent,token);
4251 (void) GetNextToken(q,&q,extent,token);
4252 affine.tx=StringToDouble(token,&next_token);
4253 (void) GetNextToken(q,&q,extent,token);
4255 (void) GetNextToken(q,&q,extent,token);
4256 affine.ty=StringToDouble(token,&next_token);
4259 if (LocaleCompare("alpha",keyword) == 0)
4261 primitive_type=AlphaPrimitive;
4264 if (LocaleCompare("angle",keyword) == 0)
4266 (void) GetNextToken(q,&q,extent,token);
4267 affine.rx=StringToDouble(token,&next_token);
4268 affine.ry=StringToDouble(token,&next_token);
4271 if (LocaleCompare("arc",keyword) == 0)
4273 primitive_type=ArcPrimitive;
4282 if (LocaleCompare("bezier",keyword) == 0)
4284 primitive_type=BezierPrimitive;
4293 if (LocaleCompare("clip-path",keyword) == 0)
4295 (void) GetNextToken(q,&q,extent,token);
4296 (void) FormatLocaleString(message,MagickPathExtent,
4297 "clip-path:url(#%s);",token);
4298 (void) WriteBlobString(image,message);
4301 if (LocaleCompare("clip-rule",keyword) == 0)
4303 (void) GetNextToken(q,&q,extent,token);
4304 (void) FormatLocaleString(message,MagickPathExtent,"clip-rule:%s;",
4306 (void) WriteBlobString(image,message);
4309 if (LocaleCompare("clip-units",keyword) == 0)
4311 (void) GetNextToken(q,&q,extent,token);
4312 (void) FormatLocaleString(message,MagickPathExtent,
4313 "clipPathUnits=%s;",token);
4314 (void) WriteBlobString(image,message);
4317 if (LocaleCompare("circle",keyword) == 0)
4319 primitive_type=CirclePrimitive;
4322 if (LocaleCompare("color",keyword) == 0)
4324 primitive_type=ColorPrimitive;
4333 if (LocaleCompare("decorate",keyword) == 0)
4335 (void) GetNextToken(q,&q,extent,token);
4336 (void) FormatLocaleString(message,MagickPathExtent,
4337 "text-decoration:%s;",token);
4338 (void) WriteBlobString(image,message);
4347 if (LocaleCompare("ellipse",keyword) == 0)
4349 primitive_type=EllipsePrimitive;
4358 if (LocaleCompare("fill",keyword) == 0)
4360 (void) GetNextToken(q,&q,extent,token);
4361 (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;",
4363 (void) WriteBlobString(image,message);
4366 if (LocaleCompare("fill-rule",keyword) == 0)
4368 (void) GetNextToken(q,&q,extent,token);
4369 (void) FormatLocaleString(message,MagickPathExtent,
4370 "fill-rule:%s;",token);
4371 (void) WriteBlobString(image,message);
4374 if (LocaleCompare("fill-opacity",keyword) == 0)
4376 (void) GetNextToken(q,&q,extent,token);
4377 (void) FormatLocaleString(message,MagickPathExtent,
4378 "fill-opacity:%s;",token);
4379 (void) WriteBlobString(image,message);
4382 if (LocaleCompare("font-family",keyword) == 0)
4384 (void) GetNextToken(q,&q,extent,token);
4385 (void) FormatLocaleString(message,MagickPathExtent,
4386 "font-family:%s;",token);
4387 (void) WriteBlobString(image,message);
4390 if (LocaleCompare("font-stretch",keyword) == 0)
4392 (void) GetNextToken(q,&q,extent,token);
4393 (void) FormatLocaleString(message,MagickPathExtent,
4394 "font-stretch:%s;",token);
4395 (void) WriteBlobString(image,message);
4398 if (LocaleCompare("font-style",keyword) == 0)
4400 (void) GetNextToken(q,&q,extent,token);
4401 (void) FormatLocaleString(message,MagickPathExtent,
4402 "font-style:%s;",token);
4403 (void) WriteBlobString(image,message);
4406 if (LocaleCompare("font-size",keyword) == 0)
4408 (void) GetNextToken(q,&q,extent,token);
4409 (void) FormatLocaleString(message,MagickPathExtent,
4410 "font-size:%s;",token);
4411 (void) WriteBlobString(image,message);
4414 if (LocaleCompare("font-weight",keyword) == 0)
4416 (void) GetNextToken(q,&q,extent,token);
4417 (void) FormatLocaleString(message,MagickPathExtent,
4418 "font-weight:%s;",token);
4419 (void) WriteBlobString(image,message);
4428 if (LocaleCompare("gradient-units",keyword) == 0)
4430 (void) GetNextToken(q,&q,extent,token);
4433 if (LocaleCompare("text-align",keyword) == 0)
4435 (void) GetNextToken(q,&q,extent,token);
4436 (void) FormatLocaleString(message,MagickPathExtent,
4437 "text-align %s ",token);
4438 (void) WriteBlobString(image,message);
4441 if (LocaleCompare("text-anchor",keyword) == 0)
4443 (void) GetNextToken(q,&q,extent,token);
4444 (void) FormatLocaleString(message,MagickPathExtent,
4445 "text-anchor %s ",token);
4446 (void) WriteBlobString(image,message);
4455 if (LocaleCompare("image",keyword) == 0)
4457 (void) GetNextToken(q,&q,extent,token);
4458 primitive_type=ImagePrimitive;
4467 if (LocaleCompare("kerning",keyword) == 0)
4469 (void) GetNextToken(q,&q,extent,token);
4470 (void) FormatLocaleString(message,MagickPathExtent,"kerning:%s;",
4472 (void) WriteBlobString(image,message);
4479 if (LocaleCompare("letter-spacing",keyword) == 0)
4481 (void) GetNextToken(q,&q,extent,token);
4482 (void) FormatLocaleString(message,MagickPathExtent,
4483 "letter-spacing:%s;",token);
4484 (void) WriteBlobString(image,message);
4487 if (LocaleCompare("line",keyword) == 0)
4489 primitive_type=LinePrimitive;
4498 if (LocaleCompare("opacity",keyword) == 0)
4500 (void) GetNextToken(q,&q,extent,token);
4501 (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ",
4503 (void) WriteBlobString(image,message);
4512 if (LocaleCompare("path",keyword) == 0)
4514 primitive_type=PathPrimitive;
4517 if (LocaleCompare("point",keyword) == 0)
4519 primitive_type=PointPrimitive;
4522 if (LocaleCompare("polyline",keyword) == 0)
4524 primitive_type=PolylinePrimitive;
4527 if (LocaleCompare("polygon",keyword) == 0)
4529 primitive_type=PolygonPrimitive;
4532 if (LocaleCompare("pop",keyword) == 0)
4534 (void) GetNextToken(q,&q,extent,token);
4535 if (LocaleCompare("clip-path",token) == 0)
4537 (void) WriteBlobString(image,"</clipPath>\n");
4540 if (LocaleCompare("defs",token) == 0)
4542 (void) WriteBlobString(image,"</defs>\n");
4545 if (LocaleCompare("gradient",token) == 0)
4547 (void) FormatLocaleString(message,MagickPathExtent,
4548 "</%sGradient>\n",type);
4549 (void) WriteBlobString(image,message);
4552 if (LocaleCompare("graphic-context",token) == 0)
4556 ThrowWriterException(DrawError,
4557 "UnbalancedGraphicContextPushPop");
4558 (void) WriteBlobString(image,"</g>\n");
4560 if (LocaleCompare("pattern",token) == 0)
4562 (void) WriteBlobString(image,"</pattern>\n");
4565 if (LocaleCompare("symbol",token) == 0)
4567 (void) WriteBlobString(image,"</symbol>\n");
4570 if ((LocaleCompare("defs",token) == 0) ||
4571 (LocaleCompare("symbol",token) == 0))
4572 (void) WriteBlobString(image,"</g>\n");
4575 if (LocaleCompare("push",keyword) == 0)
4577 (void) GetNextToken(q,&q,extent,token);
4578 if (LocaleCompare("clip-path",token) == 0)
4580 (void) GetNextToken(q,&q,extent,token);
4581 (void) FormatLocaleString(message,MagickPathExtent,
4582 "<clipPath id=\"%s\">\n",token);
4583 (void) WriteBlobString(image,message);
4586 if (LocaleCompare("defs",token) == 0)
4588 (void) WriteBlobString(image,"<defs>\n");
4591 if (LocaleCompare("gradient",token) == 0)
4593 (void) GetNextToken(q,&q,extent,token);
4594 (void) CopyMagickString(name,token,MagickPathExtent);
4595 (void) GetNextToken(q,&q,extent,token);
4596 (void) CopyMagickString(type,token,MagickPathExtent);
4597 (void) GetNextToken(q,&q,extent,token);
4598 svg_info.segment.x1=StringToDouble(token,&next_token);
4599 svg_info.element.cx=StringToDouble(token,&next_token);
4600 (void) GetNextToken(q,&q,extent,token);
4602 (void) GetNextToken(q,&q,extent,token);
4603 svg_info.segment.y1=StringToDouble(token,&next_token);
4604 svg_info.element.cy=StringToDouble(token,&next_token);
4605 (void) GetNextToken(q,&q,extent,token);
4607 (void) GetNextToken(q,&q,extent,token);
4608 svg_info.segment.x2=StringToDouble(token,&next_token);
4609 svg_info.element.major=StringToDouble(token,
4611 (void) GetNextToken(q,&q,extent,token);
4613 (void) GetNextToken(q,&q,extent,token);
4614 svg_info.segment.y2=StringToDouble(token,&next_token);
4615 svg_info.element.minor=StringToDouble(token,
4617 (void) FormatLocaleString(message,MagickPathExtent,
4618 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
4619 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
4620 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
4621 if (LocaleCompare(type,"radial") == 0)
4623 (void) GetNextToken(q,&q,extent,token);
4625 (void) GetNextToken(q,&q,extent,token);
4626 svg_info.element.angle=StringToDouble(token,
4628 (void) FormatLocaleString(message,MagickPathExtent,
4629 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
4630 "fx=\"%g\" fy=\"%g\">\n",type,name,
4631 svg_info.element.cx,svg_info.element.cy,
4632 svg_info.element.angle,svg_info.element.major,
4633 svg_info.element.minor);
4635 (void) WriteBlobString(image,message);
4638 if (LocaleCompare("graphic-context",token) == 0)
4643 AffineToTransform(image,&affine);
4646 (void) WriteBlobString(image,"<g style=\"");
4649 if (LocaleCompare("pattern",token) == 0)
4651 (void) GetNextToken(q,&q,extent,token);
4652 (void) CopyMagickString(name,token,MagickPathExtent);
4653 (void) GetNextToken(q,&q,extent,token);
4654 svg_info.bounds.x=StringToDouble(token,&next_token);
4655 (void) GetNextToken(q,&q,extent,token);
4657 (void) GetNextToken(q,&q,extent,token);
4658 svg_info.bounds.y=StringToDouble(token,&next_token);
4659 (void) GetNextToken(q,&q,extent,token);
4661 (void) GetNextToken(q,&q,extent,token);
4662 svg_info.bounds.width=StringToDouble(token,
4664 (void) GetNextToken(q,&q,extent,token);
4666 (void) GetNextToken(q,&q,extent,token);
4667 svg_info.bounds.height=StringToDouble(token,(char **) NULL);
4668 (void) FormatLocaleString(message,MagickPathExtent,
4669 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
4670 "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y,
4671 svg_info.bounds.width,svg_info.bounds.height);
4672 (void) WriteBlobString(image,message);
4675 if (LocaleCompare("symbol",token) == 0)
4677 (void) WriteBlobString(image,"<symbol>\n");
4688 if (LocaleCompare("rectangle",keyword) == 0)
4690 primitive_type=RectanglePrimitive;
4693 if (LocaleCompare("roundRectangle",keyword) == 0)
4695 primitive_type=RoundRectanglePrimitive;
4698 if (LocaleCompare("rotate",keyword) == 0)
4700 (void) GetNextToken(q,&q,extent,token);
4701 (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ",
4703 (void) WriteBlobString(image,message);
4712 if (LocaleCompare("scale",keyword) == 0)
4714 (void) GetNextToken(q,&q,extent,token);
4715 affine.sx=StringToDouble(token,&next_token);
4716 (void) GetNextToken(q,&q,extent,token);
4718 (void) GetNextToken(q,&q,extent,token);
4719 affine.sy=StringToDouble(token,&next_token);
4722 if (LocaleCompare("skewX",keyword) == 0)
4724 (void) GetNextToken(q,&q,extent,token);
4725 (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ",
4727 (void) WriteBlobString(image,message);
4730 if (LocaleCompare("skewY",keyword) == 0)
4732 (void) GetNextToken(q,&q,extent,token);
4733 (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ",
4735 (void) WriteBlobString(image,message);
4738 if (LocaleCompare("stop-color",keyword) == 0)
4741 color[MagickPathExtent];
4743 (void) GetNextToken(q,&q,extent,token);
4744 (void) CopyMagickString(color,token,MagickPathExtent);
4745 (void) GetNextToken(q,&q,extent,token);
4746 (void) FormatLocaleString(message,MagickPathExtent,
4747 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4748 (void) WriteBlobString(image,message);
4751 if (LocaleCompare("stroke",keyword) == 0)
4753 (void) GetNextToken(q,&q,extent,token);
4754 (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;",
4756 (void) WriteBlobString(image,message);
4759 if (LocaleCompare("stroke-antialias",keyword) == 0)
4761 (void) GetNextToken(q,&q,extent,token);
4762 (void) FormatLocaleString(message,MagickPathExtent,
4763 "stroke-antialias:%s;",token);
4764 (void) WriteBlobString(image,message);
4767 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4775 (void) GetNextToken(p,&p,extent,token);
4776 for (k=0; IsPoint(token); k++)
4777 (void) GetNextToken(p,&p,extent,token);
4778 (void) WriteBlobString(image,"stroke-dasharray:");
4779 for (j=0; j < k; j++)
4781 (void) GetNextToken(q,&q,extent,token);
4782 (void) FormatLocaleString(message,MagickPathExtent,"%s ",
4784 (void) WriteBlobString(image,message);
4786 (void) WriteBlobString(image,";");
4789 (void) GetNextToken(q,&q,extent,token);
4790 (void) FormatLocaleString(message,MagickPathExtent,
4791 "stroke-dasharray:%s;",token);
4792 (void) WriteBlobString(image,message);
4795 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4797 (void) GetNextToken(q,&q,extent,token);
4798 (void) FormatLocaleString(message,MagickPathExtent,
4799 "stroke-dashoffset:%s;",token);
4800 (void) WriteBlobString(image,message);
4803 if (LocaleCompare("stroke-linecap",keyword) == 0)
4805 (void) GetNextToken(q,&q,extent,token);
4806 (void) FormatLocaleString(message,MagickPathExtent,
4807 "stroke-linecap:%s;",token);
4808 (void) WriteBlobString(image,message);
4811 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4813 (void) GetNextToken(q,&q,extent,token);
4814 (void) FormatLocaleString(message,MagickPathExtent,
4815 "stroke-linejoin:%s;",token);
4816 (void) WriteBlobString(image,message);
4819 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4821 (void) GetNextToken(q,&q,extent,token);
4822 (void) FormatLocaleString(message,MagickPathExtent,
4823 "stroke-miterlimit:%s;",token);
4824 (void) WriteBlobString(image,message);
4827 if (LocaleCompare("stroke-opacity",keyword) == 0)
4829 (void) GetNextToken(q,&q,extent,token);
4830 (void) FormatLocaleString(message,MagickPathExtent,
4831 "stroke-opacity:%s;",token);
4832 (void) WriteBlobString(image,message);
4835 if (LocaleCompare("stroke-width",keyword) == 0)
4837 (void) GetNextToken(q,&q,extent,token);
4838 (void) FormatLocaleString(message,MagickPathExtent,
4839 "stroke-width:%s;",token);
4840 (void) WriteBlobString(image,message);
4849 if (LocaleCompare("text",keyword) == 0)
4851 primitive_type=TextPrimitive;
4854 if (LocaleCompare("text-antialias",keyword) == 0)
4856 (void) GetNextToken(q,&q,extent,token);
4857 (void) FormatLocaleString(message,MagickPathExtent,
4858 "text-antialias:%s;",token);
4859 (void) WriteBlobString(image,message);
4862 if (LocaleCompare("tspan",keyword) == 0)
4864 primitive_type=TextPrimitive;
4867 if (LocaleCompare("translate",keyword) == 0)
4869 (void) GetNextToken(q,&q,extent,token);
4870 affine.tx=StringToDouble(token,&next_token);
4871 (void) GetNextToken(q,&q,extent,token);
4873 (void) GetNextToken(q,&q,extent,token);
4874 affine.ty=StringToDouble(token,&next_token);
4883 if (LocaleCompare("viewbox",keyword) == 0)
4885 (void) GetNextToken(q,&q,extent,token);
4887 (void) GetNextToken(q,&q,extent,token);
4888 (void) GetNextToken(q,&q,extent,token);
4890 (void) GetNextToken(q,&q,extent,token);
4891 (void) GetNextToken(q,&q,extent,token);
4893 (void) GetNextToken(q,&q,extent,token);
4894 (void) GetNextToken(q,&q,extent,token);
4906 if (status == MagickFalse)
4908 if (primitive_type == UndefinedPrimitive)
4911 Parse the primitive attributes.
4915 for (x=0; *q != '\0'; x++)
4920 if (IsPoint(q) == MagickFalse)
4922 (void) GetNextToken(q,&q,extent,token);
4923 point.x=StringToDouble(token,&next_token);
4924 (void) GetNextToken(q,&q,extent,token);
4926 (void) GetNextToken(q,&q,extent,token);
4927 point.y=StringToDouble(token,&next_token);
4928 (void) GetNextToken(q,(const char **) NULL,extent,token);
4930 (void) GetNextToken(q,&q,extent,token);
4931 primitive_info[i].primitive=primitive_type;
4932 primitive_info[i].point=point;
4933 primitive_info[i].coordinates=0;
4934 primitive_info[i].method=FloodfillMethod;
4936 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4938 number_points+=6*BezierQuantum+360;
4939 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4940 number_points,sizeof(*primitive_info));
4941 if (primitive_info == (PrimitiveInfo *) NULL)
4943 (void) ThrowMagickException(exception,GetMagickModule(),
4944 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4948 primitive_info[j].primitive=primitive_type;
4949 primitive_info[j].coordinates=(size_t) x;
4950 primitive_info[j].method=FloodfillMethod;
4951 primitive_info[j].text=(char *) NULL;
4954 AffineToTransform(image,&affine);
4958 switch (primitive_type)
4960 case PointPrimitive:
4963 if (primitive_info[j].coordinates != 1)
4972 if (primitive_info[j].coordinates != 2)
4977 (void) FormatLocaleString(message,MagickPathExtent,
4978 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4979 primitive_info[j].point.x,primitive_info[j].point.y,
4980 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4981 (void) WriteBlobString(image,message);
4984 case RectanglePrimitive:
4986 if (primitive_info[j].coordinates != 2)
4991 (void) FormatLocaleString(message,MagickPathExtent,
4992 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4993 primitive_info[j].point.x,primitive_info[j].point.y,
4994 primitive_info[j+1].point.x-primitive_info[j].point.x,
4995 primitive_info[j+1].point.y-primitive_info[j].point.y);
4996 (void) WriteBlobString(image,message);
4999 case RoundRectanglePrimitive:
5001 if (primitive_info[j].coordinates != 3)
5006 (void) FormatLocaleString(message,MagickPathExtent,
5007 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
5008 "ry=\"%g\"/>\n",primitive_info[j].point.x,
5009 primitive_info[j].point.y,primitive_info[j+1].point.x-
5010 primitive_info[j].point.x,primitive_info[j+1].point.y-
5011 primitive_info[j].point.y,primitive_info[j+2].point.x,
5012 primitive_info[j+2].point.y);
5013 (void) WriteBlobString(image,message);
5018 if (primitive_info[j].coordinates != 3)
5025 case EllipsePrimitive:
5027 if (primitive_info[j].coordinates != 3)
5032 (void) FormatLocaleString(message,MagickPathExtent,
5033 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
5034 primitive_info[j].point.x,primitive_info[j].point.y,
5035 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
5036 (void) WriteBlobString(image,message);
5039 case CirclePrimitive:
5045 if (primitive_info[j].coordinates != 2)
5050 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
5051 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
5052 (void) FormatLocaleString(message,MagickPathExtent,
5053 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
5054 primitive_info[j].point.x,primitive_info[j].point.y,
5056 (void) WriteBlobString(image,message);
5059 case PolylinePrimitive:
5061 if (primitive_info[j].coordinates < 2)
5066 (void) CopyMagickString(message," <polyline points=\"",
5068 (void) WriteBlobString(image,message);
5069 length=strlen(message);
5072 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
5073 primitive_info[j].point.x,primitive_info[j].point.y);
5074 length+=strlen(message);
5077 (void) WriteBlobString(image,"\n ");
5078 length=strlen(message)+5;
5080 (void) WriteBlobString(image,message);
5082 (void) WriteBlobString(image,"\"/>\n");
5085 case PolygonPrimitive:
5087 if (primitive_info[j].coordinates < 3)
5092 primitive_info[i]=primitive_info[j];
5093 primitive_info[i].coordinates=0;
5094 primitive_info[j].coordinates++;
5096 (void) CopyMagickString(message," <polygon points=\"",
5098 (void) WriteBlobString(image,message);
5099 length=strlen(message);
5102 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
5103 primitive_info[j].point.x,primitive_info[j].point.y);
5104 length+=strlen(message);
5107 (void) WriteBlobString(image,"\n ");
5108 length=strlen(message)+5;
5110 (void) WriteBlobString(image,message);
5112 (void) WriteBlobString(image,"\"/>\n");
5115 case BezierPrimitive:
5117 if (primitive_info[j].coordinates < 3)
5129 (void) GetNextToken(q,&q,extent,token);
5130 number_attributes=1;
5131 for (p=token; *p != '\0'; p++)
5132 if (isalpha((int) ((unsigned char) *p)) != 0)
5133 number_attributes++;
5134 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
5136 number_points+=6*BezierQuantum*number_attributes;
5137 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
5138 number_points,sizeof(*primitive_info));
5139 if (primitive_info == (PrimitiveInfo *) NULL)
5141 (void) ThrowMagickException(exception,GetMagickModule(),
5142 ResourceLimitError,"MemoryAllocationFailed","`%s'",
5147 (void) WriteBlobString(image," <path d=\"");
5148 (void) WriteBlobString(image,token);
5149 (void) WriteBlobString(image,"\"/>\n");
5152 case AlphaPrimitive:
5153 case ColorPrimitive:
5155 if (primitive_info[j].coordinates != 1)
5160 (void) GetNextToken(q,&q,extent,token);
5161 if (LocaleCompare("point",token) == 0)
5162 primitive_info[j].method=PointMethod;
5163 if (LocaleCompare("replace",token) == 0)
5164 primitive_info[j].method=ReplaceMethod;
5165 if (LocaleCompare("floodfill",token) == 0)
5166 primitive_info[j].method=FloodfillMethod;
5167 if (LocaleCompare("filltoborder",token) == 0)
5168 primitive_info[j].method=FillToBorderMethod;
5169 if (LocaleCompare("reset",token) == 0)
5170 primitive_info[j].method=ResetMethod;
5178 if (primitive_info[j].coordinates != 1)
5183 (void) GetNextToken(q,&q,extent,token);
5184 (void) FormatLocaleString(message,MagickPathExtent,
5185 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
5186 primitive_info[j].point.y);
5187 (void) WriteBlobString(image,message);
5188 for (p=token; *p != '\0'; p++)
5191 case '<': (void) WriteBlobString(image,"<"); break;
5192 case '>': (void) WriteBlobString(image,">"); break;
5193 case '&': (void) WriteBlobString(image,"&"); break;
5194 default: (void) WriteBlobByte(image,(unsigned char) *p); break;
5196 (void) WriteBlobString(image,"</text>\n");
5199 case ImagePrimitive:
5201 if (primitive_info[j].coordinates != 2)
5206 (void) GetNextToken(q,&q,extent,token);
5207 (void) FormatLocaleString(message,MagickPathExtent,
5208 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
5209 "href=\"%s\"/>\n",primitive_info[j].point.x,
5210 primitive_info[j].point.y,primitive_info[j+1].point.x,
5211 primitive_info[j+1].point.y,token);
5212 (void) WriteBlobString(image,message);
5216 if (primitive_info == (PrimitiveInfo *) NULL)
5218 primitive_info[i].primitive=UndefinedPrimitive;
5219 if (status == MagickFalse)
5222 (void) WriteBlobString(image,"</svg>\n");
5224 Relinquish resources.
5226 token=DestroyString(token);
5227 if (primitive_info != (PrimitiveInfo *) NULL)
5228 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
5229 (void) CloseBlob(image);