• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            SSSSS  V   V   GGGG                              %
7 %                            SS     V   V  G                                  %
8 %                             SSS   V   V  G GG                               %
9 %                               SS   V V   G   G                              %
10 %                            SSSSS    V     GGG                               %
11 %                                                                             %
12 %                                                                             %
13 %                  Read/Write Scalable Vector Graphics Format                 %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                             William Radcliffe                               %
18 %                                March 2000                                   %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2020 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    https://imagemagick.org/script/license.php                               %
28 %                                                                             %
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.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39 
40 /*
41   Include declarations.
42 */
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"
78 
79 #if defined(MAGICKCORE_XML_DELEGATE)
80 #  if defined(MAGICKCORE_WINDOWS_SUPPORT)
81 #    if !defined(__MINGW32__)
82 #      include <win32config.h>
83 #    endif
84 #  endif
85 #  include <libxml/xmlmemory.h>
86 #  include <libxml/parserInternals.h>
87 #  include <libxml/xmlerror.h>
88 #endif
89 
90 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
91 #include "autotrace/autotrace.h"
92 #endif
93 
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"
102 #endif
103 #endif
104 
105 /*
106   Define declarations.
107 */
108 #define DefaultSVGDensity  96.0
109 
110 /*
111   Typedef declarations.
112 */
113 typedef struct _BoundingBox
114 {
115   double
116     x,
117     y,
118     width,
119     height;
120 } BoundingBox;
121 
122 typedef struct _ElementInfo
123 {
124   double
125     cx,
126     cy,
127     major,
128     minor,
129     angle;
130 } ElementInfo;
131 
132 typedef struct _SVGInfo
133 {
134   FILE
135     *file;
136 
137   ExceptionInfo
138     *exception;
139 
140   Image
141     *image;
142 
143   const ImageInfo
144     *image_info;
145 
146   AffineMatrix
147     affine;
148 
149   size_t
150     width,
151     height;
152 
153   char
154     *size,
155     *title,
156     *comment;
157 
158   int
159     n;
160 
161   double
162     *scale,
163     pointsize;
164 
165   ElementInfo
166     element;
167 
168   SegmentInfo
169     segment;
170 
171   BoundingBox
172     bounds,
173     text_offset,
174     view_box;
175 
176   PointInfo
177     radius;
178 
179   char
180     *stop_color,
181     *offset,
182     *text,
183     *vertices,
184     *url;
185 
186 #if defined(MAGICKCORE_XML_DELEGATE)
187   xmlParserCtxtPtr
188     parser;
189 
190   xmlDocPtr
191     document;
192 #endif
193 
194   ssize_t
195     svgDepth;
196 } SVGInfo;
197 
198 /*
199   Static declarations.
200 */
201 static char
202   SVGDensityGeometry[] = "96.0x96.0";
203 
204 /*
205   Forward declarations.
206 */
207 static MagickBooleanType
208   WriteSVGImage(const ImageInfo *,Image *,ExceptionInfo *);
209 
210 /*
211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212 %                                                                             %
213 %                                                                             %
214 %                                                                             %
215 %   I s S V G                                                                 %
216 %                                                                             %
217 %                                                                             %
218 %                                                                             %
219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220 %
221 %  IsSVG()() returns MagickTrue if the image format type, identified by the
222 %  magick string, is SVG.
223 %
224 %  The format of the IsSVG method is:
225 %
226 %      MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
227 %
228 %  A description of each parameter follows:
229 %
230 %    o magick: compare image format pattern against these bytes.
231 %
232 %    o length: Specifies the length of the magick string.
233 %
234 */
IsSVG(const unsigned char * magick,const size_t length)235 static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
236 {
237   if (length < 4)
238     return(MagickFalse);
239   if (LocaleNCompare((const char *) magick+1,"svg",3) == 0)
240     return(MagickTrue);
241   if (length < 5)
242     return(MagickFalse);
243   if (LocaleNCompare((const char *) magick+1,"?xml",4) == 0)
244     return(MagickTrue);
245   return(MagickFalse);
246 }
247 
248 /*
249 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
250 %                                                                             %
251 %                                                                             %
252 %                                                                             %
253 %   R e a d S V G I m a g e                                                   %
254 %                                                                             %
255 %                                                                             %
256 %                                                                             %
257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
258 %
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.
262 %
263 %  The format of the ReadSVGImage method is:
264 %
265 %      Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
266 %
267 %  A description of each parameter follows:
268 %
269 %    o image_info: the image info.
270 %
271 %    o exception: return any errors or warnings in this structure.
272 %
273 */
274 
RenderSVGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)275 static Image *RenderSVGImage(const ImageInfo *image_info,Image *image,
276   ExceptionInfo *exception)
277 {
278   char
279     background[MagickPathExtent],
280     command[MagickPathExtent],
281     *density,
282     input_filename[MagickPathExtent],
283     opacity[MagickPathExtent],
284     output_filename[MagickPathExtent],
285     unique[MagickPathExtent];
286 
287   const DelegateInfo
288     *delegate_info;
289 
290   Image
291     *next;
292 
293   int
294     status;
295 
296   struct stat
297     attributes;
298 
299   /*
300     Our best hope for compliance with the SVG standard.
301   */
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))
328     {
329       Image
330         *svg_image;
331 
332       ImageInfo
333         *read_info;
334 
335       read_info=CloneImageInfo(image_info);
336       (void) CopyMagickString(read_info->filename,output_filename,
337         MagickPathExtent);
338       svg_image=ReadImage(read_info,exception);
339       read_info=DestroyImageInfo(read_info);
340       if (svg_image != (Image *) NULL)
341         {
342           (void) RelinquishUniqueFileResource(output_filename);
343           for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; )
344           {
345             (void) CopyMagickString(next->filename,image->filename,
346               MaxTextExtent);
347             (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
348             next=GetNextImageInList(next);
349           }
350           return(svg_image);
351         }
352     }
353   (void) RelinquishUniqueFileResource(output_filename);
354   return((Image *) NULL);
355 }
356 
357 #if defined(MAGICKCORE_XML_DELEGATE)
AcquireSVGInfo(void)358 static SVGInfo *AcquireSVGInfo(void)
359 {
360   SVGInfo
361     *svg_info;
362 
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);
371   return(svg_info);
372 }
373 
DestroySVGInfo(SVGInfo * svg_info)374 static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
375 {
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));
385 }
386 
GetUserSpaceCoordinateValue(const SVGInfo * svg_info,int type,const char * string)387 static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
388   const char *string)
389 {
390   char
391     *next_token,
392     token[MagickPathExtent];
393 
394   const char
395     *p;
396 
397   double
398     value;
399 
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)
406     {
407       double
408         alpha,
409         beta;
410 
411       if (type > 0)
412         {
413           if (svg_info->view_box.width == 0.0)
414             return(0.0);
415           return(svg_info->view_box.width*value/100.0);
416         }
417       if (type < 0)
418         {
419           if (svg_info->view_box.height == 0.0)
420             return(0.0);
421           return(svg_info->view_box.height*value/100.0);
422         }
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);
426     }
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)
443     return(value);
444   return(value);
445 }
446 
447 #if defined(__cplusplus) || defined(c_plusplus)
448 extern "C" {
449 #endif
450 
SVGIsStandalone(void * context)451 static int SVGIsStandalone(void *context)
452 {
453   SVGInfo
454     *svg_info;
455 
456   /*
457     Is this document tagged standalone?
458   */
459   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.SVGIsStandalone()");
460   svg_info=(SVGInfo *) context;
461   return(svg_info->document->standalone == 1);
462 }
463 
SVGHasInternalSubset(void * context)464 static int SVGHasInternalSubset(void *context)
465 {
466   SVGInfo
467     *svg_info;
468 
469   /*
470     Does this document has an internal subset?
471   */
472   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
473     "  SAX.SVGHasInternalSubset()");
474   svg_info=(SVGInfo *) context;
475   return(svg_info->document->intSubset != NULL);
476 }
477 
SVGHasExternalSubset(void * context)478 static int SVGHasExternalSubset(void *context)
479 {
480   SVGInfo
481     *svg_info;
482 
483   /*
484     Does this document has an external subset?
485   */
486   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
487     "  SAX.SVGHasExternalSubset()");
488   svg_info=(SVGInfo *) context;
489   return(svg_info->document->extSubset != NULL);
490 }
491 
SVGInternalSubset(void * context,const xmlChar * name,const xmlChar * external_id,const xmlChar * system_id)492 static void SVGInternalSubset(void *context,const xmlChar *name,
493   const xmlChar *external_id,const xmlChar *system_id)
494 {
495   SVGInfo
496     *svg_info;
497 
498   /*
499     Does this document has an internal subset?
500   */
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);
507 }
508 
SVGResolveEntity(void * context,const xmlChar * public_id,const xmlChar * system_id)509 static xmlParserInputPtr SVGResolveEntity(void *context,
510   const xmlChar *public_id,const xmlChar *system_id)
511 {
512   SVGInfo
513     *svg_info;
514 
515   xmlParserInputPtr
516     stream;
517 
518   /*
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).
523   */
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);
531   return(stream);
532 }
533 
SVGGetEntity(void * context,const xmlChar * name)534 static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
535 {
536   SVGInfo
537     *svg_info;
538 
539   /*
540     Get an entity by name.
541   */
542   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.SVGGetEntity(%s)",
543     name);
544   svg_info=(SVGInfo *) context;
545   return(xmlGetDocEntity(svg_info->document,name));
546 }
547 
SVGGetParameterEntity(void * context,const xmlChar * name)548 static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
549 {
550   SVGInfo
551     *svg_info;
552 
553   /*
554     Get a parameter entity by name.
555   */
556   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
557     "  SAX.getParameterEntity(%s)",name);
558   svg_info=(SVGInfo *) context;
559   return(xmlGetParameterEntity(svg_info->document,name));
560 }
561 
SVGEntityDeclaration(void * context,const xmlChar * name,int type,const xmlChar * public_id,const xmlChar * system_id,xmlChar * content)562 static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
563   const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
564 {
565   SVGInfo
566     *svg_info;
567 
568   /*
569     An entity definition has been parsed.
570   */
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,
578       content);
579   else
580     if (svg_info->parser->inSubset == 2)
581       (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
582         content);
583 }
584 
SVGAttributeDeclaration(void * context,const xmlChar * element,const xmlChar * name,int type,int value,const xmlChar * default_value,xmlEnumerationPtr tree)585 static void SVGAttributeDeclaration(void *context,const xmlChar *element,
586   const xmlChar *name,int type,int value,const xmlChar *default_value,
587   xmlEnumerationPtr tree)
588 {
589   SVGInfo
590     *svg_info;
591 
592   xmlChar
593     *fullname,
594     *prefix;
595 
596   xmlParserCtxtPtr
597     parser;
598 
599   /*
600     An attribute definition has been parsed.
601   */
602   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
603     "  SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value,
604     default_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);
614   else
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)
620     xmlFree(prefix);
621   if (fullname != (xmlChar *) NULL)
622     xmlFree(fullname);
623 }
624 
SVGElementDeclaration(void * context,const xmlChar * name,int type,xmlElementContentPtr content)625 static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
626   xmlElementContentPtr content)
627 {
628   SVGInfo
629     *svg_info;
630 
631   xmlParserCtxtPtr
632     parser;
633 
634   /*
635     An element definition has been parsed.
636   */
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);
644   else
645     if (parser->inSubset == 2)
646       (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
647         name,(xmlElementTypeVal) type,content);
648 }
649 
SVGStripString(const MagickBooleanType trim,char * message)650 static void SVGStripString(const MagickBooleanType trim,char *message)
651 {
652   register char
653     *p,
654     *q;
655 
656   size_t
657     length;
658 
659   assert(message != (char *) NULL);
660   if (*message == '\0')
661     return;
662   /*
663     Remove comment.
664   */
665   q=message;
666   for (p=message; *p != '\0'; p++)
667   {
668     if ((*p == '/') && (*(p+1) == '*'))
669       {
670         for ( ; *p != '\0'; p++)
671           if ((*p == '*') && (*(p+1) == '/'))
672             {
673               p+=2;
674               break;
675             }
676         if (*p == '\0')
677           break;
678       }
679     *q++=(*p);
680   }
681   *q='\0';
682   length=strlen(message);
683   if ((trim != MagickFalse) && (length != 0))
684     {
685       /*
686         Remove whitespace.
687       */
688       p=message;
689       while (isspace((int) ((unsigned char) *p)) != 0)
690         p++;
691       if ((*p == '\'') || (*p == '"'))
692         p++;
693       q=message+length-1;
694       while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
695         q--;
696       if (q > p)
697         if ((*q == '\'') || (*q == '"'))
698           q--;
699       (void) memmove(message,p,(size_t) (q-p+1));
700       message[q-p+1]='\0';
701     }
702   /*
703     Convert newlines to a space.
704   */
705   for (p=message; *p != '\0'; p++)
706     if (*p == '\n')
707       *p=' ';
708 }
709 
SVGKeyValuePairs(void * context,const int key_sentinel,const int value_sentinel,const char * text,size_t * number_tokens)710 static char **SVGKeyValuePairs(void *context,const int key_sentinel,
711   const int value_sentinel,const char *text,size_t *number_tokens)
712 {
713   char
714     **tokens;
715 
716   register const char
717     *p,
718     *q;
719 
720   register ssize_t
721     i;
722 
723   size_t
724     extent;
725 
726   SVGInfo
727     *svg_info;
728 
729   svg_info=(SVGInfo *) context;
730   *number_tokens=0;
731   if (text == (const char *) NULL)
732     return((char **) NULL);
733   extent=8;
734   tokens=(char **) AcquireQuantumMemory(extent+2UL,sizeof(*tokens));
735   if (tokens == (char **) NULL)
736     {
737       (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
738         ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
739       return((char **) NULL);
740     }
741   /*
742     Convert string to an ASCII list.
743   */
744   i=0;
745   p=text;
746   for (q=p; *q != '\0'; q++)
747   {
748     if ((*q != key_sentinel) && (*q != value_sentinel) && (*q != '\0'))
749       continue;
750     if (i == (ssize_t) extent)
751       {
752         extent<<=1;
753         tokens=(char **) ResizeQuantumMemory(tokens,extent+2,sizeof(*tokens));
754         if (tokens == (char **) NULL)
755           {
756             (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
757               ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
758             return((char **) NULL);
759           }
760       }
761     tokens[i]=AcquireString(p);
762     (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
763     SVGStripString(MagickTrue,tokens[i]);
764     i++;
765     p=q+1;
766   }
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;
772   return(tokens);
773 }
774 
SVGNotationDeclaration(void * context,const xmlChar * name,const xmlChar * public_id,const xmlChar * system_id)775 static void SVGNotationDeclaration(void *context,const xmlChar *name,
776   const xmlChar *public_id,const xmlChar *system_id)
777 {
778   SVGInfo
779     *svg_info;
780 
781   xmlParserCtxtPtr
782     parser;
783 
784   /*
785     What to do when a notation declaration has been parsed.
786   */
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);
796   else
797     if (parser->inSubset == 2)
798       (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
799         name,public_id,system_id);
800 }
801 
SVGProcessStyleElement(void * context,const xmlChar * name,const char * style)802 static void SVGProcessStyleElement(void *context,const xmlChar *name,
803   const char *style)
804 {
805   char
806     background[MagickPathExtent],
807     *color,
808     *keyword,
809     *units,
810     *value;
811 
812   char
813     **tokens;
814 
815   register ssize_t
816     i;
817 
818   size_t
819     number_tokens;
820 
821   SVGInfo
822     *svg_info;
823 
824   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
825   svg_info=(SVGInfo *) context;
826   tokens=SVGKeyValuePairs(context,':',';',style,&number_tokens);
827   if (tokens == (char **) NULL)
828     return;
829   for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
830   {
831     keyword=(char *) tokens[i];
832     value=(char *) tokens[i+1];
833     if (LocaleCompare(keyword,"font-size") != 0)
834       continue;
835     svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
836     (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
837       svg_info->pointsize);
838   }
839   color=AcquireString("none");
840   units=AcquireString("userSpaceOnUse");
841   for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
842   {
843     keyword=(char *) tokens[i];
844     value=(char *) tokens[i+1];
845     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    %s: %s",keyword,
846       value);
847     switch (*keyword)
848     {
849       case 'B':
850       case 'b':
851       {
852         if (LocaleCompare((const char *) name,"background") == 0)
853           {
854             if (LocaleCompare((const char *) name,"svg") == 0)
855               (void) CopyMagickString(background,value,MagickPathExtent);
856             break;
857           }
858         break;
859       }
860       case 'C':
861       case 'c':
862       {
863          if (LocaleCompare(keyword,"clip-path") == 0)
864            {
865              (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",value);
866              break;
867            }
868         if (LocaleCompare(keyword,"clip-rule") == 0)
869           {
870             (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",value);
871             break;
872           }
873          if (LocaleCompare(keyword,"clipPathUnits") == 0)
874            {
875              (void) CloneString(&units,value);
876              (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
877                value);
878              break;
879            }
880         if (LocaleCompare(keyword,"color") == 0)
881           {
882             (void) CloneString(&color,value);
883             break;
884           }
885         break;
886       }
887       case 'F':
888       case 'f':
889       {
890         if (LocaleCompare(keyword,"fill") == 0)
891           {
892              if (LocaleCompare(value,"currentColor") == 0)
893                {
894                  (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
895                  break;
896                }
897             if (LocaleCompare(value,"#000000ff") == 0)
898               (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
899             else
900               (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
901             break;
902           }
903         if (LocaleCompare(keyword,"fillcolor") == 0)
904           {
905             (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
906             break;
907           }
908         if (LocaleCompare(keyword,"fill-rule") == 0)
909           {
910             (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",value);
911             break;
912           }
913         if (LocaleCompare(keyword,"fill-opacity") == 0)
914           {
915             (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
916               value);
917             break;
918           }
919         if (LocaleCompare(keyword,"font") == 0)
920           {
921             char
922               family[MagickPathExtent],
923               size[MagickPathExtent],
924               style[MagickPathExtent];
925 
926             if (sscanf(value,"%2048s %2048s %2048s",style,size,family) != 3)
927               break;
928             if (GetUserSpaceCoordinateValue(svg_info,0,style) == 0)
929               (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
930                 style);
931             else
932               if (sscanf(value,"%2048s %2048s",size,family) != 2)
933                 break;
934             (void) FormatLocaleFile(svg_info->file,"font-size \"%s\"\n",size);
935             (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
936               family);
937             break;
938           }
939         if (LocaleCompare(keyword,"font-family") == 0)
940           {
941             (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
942               value);
943             break;
944           }
945         if (LocaleCompare(keyword,"font-stretch") == 0)
946           {
947             (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
948               value);
949             break;
950           }
951         if (LocaleCompare(keyword,"font-style") == 0)
952           {
953             (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",value);
954             break;
955           }
956         if (LocaleCompare(keyword,"font-size") == 0)
957           {
958             svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
959             (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
960               svg_info->pointsize);
961             break;
962           }
963         if (LocaleCompare(keyword,"font-weight") == 0)
964           {
965             (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
966               value);
967             break;
968           }
969         break;
970       }
971       case 'K':
972       case 'k':
973       {
974         if (LocaleCompare(keyword,"kerning") == 0)
975           {
976             (void) FormatLocaleFile(svg_info->file,"kerning \"%s\"\n",value);
977             break;
978           }
979         break;
980       }
981       case 'L':
982       case 'l':
983       {
984         if (LocaleCompare(keyword,"letter-spacing") == 0)
985           {
986             (void) FormatLocaleFile(svg_info->file,"letter-spacing \"%s\"\n",
987               value);
988             break;
989           }
990         break;
991       }
992       case 'M':
993       case 'm':
994       {
995         if (LocaleCompare(keyword,"mask") == 0)
996           {
997             (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
998             break;
999           }
1000         break;
1001       }
1002       case 'O':
1003       case 'o':
1004       {
1005         if (LocaleCompare(keyword,"offset") == 0)
1006           {
1007             (void) FormatLocaleFile(svg_info->file,"offset %g\n",
1008               GetUserSpaceCoordinateValue(svg_info,1,value));
1009             break;
1010           }
1011         if (LocaleCompare(keyword,"opacity") == 0)
1012           {
1013             (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
1014             break;
1015           }
1016         break;
1017       }
1018       case 'S':
1019       case 's':
1020       {
1021         if (LocaleCompare(keyword,"stop-color") == 0)
1022           {
1023             (void) CloneString(&svg_info->stop_color,value);
1024             break;
1025           }
1026         if (LocaleCompare(keyword,"stroke") == 0)
1027           {
1028             if (LocaleCompare(value,"currentColor") == 0)
1029               {
1030                 (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",color);
1031                 break;
1032               }
1033             if (LocaleCompare(value,"#000000ff") == 0)
1034               (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
1035             else
1036               (void) FormatLocaleFile(svg_info->file,
1037                 "stroke \"%s\"\n",value);
1038             break;
1039           }
1040         if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1041           {
1042             (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
1043               LocaleCompare(value,"true") == 0);
1044             break;
1045           }
1046         if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1047           {
1048             (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1049               value);
1050             break;
1051           }
1052         if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1053           {
1054             (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
1055               GetUserSpaceCoordinateValue(svg_info,1,value));
1056             break;
1057           }
1058         if (LocaleCompare(keyword,"stroke-linecap") == 0)
1059           {
1060             (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
1061               value);
1062             break;
1063           }
1064         if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1065           {
1066             (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
1067               value);
1068             break;
1069           }
1070         if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1071           {
1072             (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit \"%s\"\n",
1073               value);
1074             break;
1075           }
1076         if (LocaleCompare(keyword,"stroke-opacity") == 0)
1077           {
1078             (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
1079               value);
1080             break;
1081           }
1082         if (LocaleCompare(keyword,"stroke-width") == 0)
1083           {
1084             (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
1085               GetUserSpaceCoordinateValue(svg_info,1,value));
1086             break;
1087           }
1088         break;
1089       }
1090       case 't':
1091       case 'T':
1092       {
1093         if (LocaleCompare(keyword,"text-align") == 0)
1094           {
1095             (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",value);
1096             break;
1097           }
1098         if (LocaleCompare(keyword,"text-anchor") == 0)
1099           {
1100             (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
1101               value);
1102             break;
1103           }
1104         if (LocaleCompare(keyword,"text-decoration") == 0)
1105           {
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");
1112             break;
1113           }
1114         if (LocaleCompare(keyword,"text-antialiasing") == 0)
1115           {
1116             (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
1117               LocaleCompare(value,"true") == 0);
1118             break;
1119           }
1120         break;
1121       }
1122       default:
1123         break;
1124     }
1125   }
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);
1133 }
1134 
SVGUnparsedEntityDeclaration(void * context,const xmlChar * name,const xmlChar * public_id,const xmlChar * system_id,const xmlChar * notation)1135 static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
1136   const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
1137 {
1138   SVGInfo
1139     *svg_info;
1140 
1141   /*
1142     What to do when an unparsed entity declaration is parsed.
1143   */
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);
1151 
1152 }
1153 
SVGSetDocumentLocator(void * context,xmlSAXLocatorPtr location)1154 static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
1155 {
1156   SVGInfo
1157     *svg_info;
1158 
1159   /*
1160     Receive the document locator at startup, actually xmlDefaultSAXLocator.
1161   */
1162   (void) location;
1163   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1164     "  SAX.setDocumentLocator()");
1165   svg_info=(SVGInfo *) context;
1166   (void) svg_info;
1167 }
1168 
SVGStartDocument(void * context)1169 static void SVGStartDocument(void *context)
1170 {
1171   SVGInfo
1172     *svg_info;
1173 
1174   xmlParserCtxtPtr
1175     parser;
1176 
1177   /*
1178     Called when the document start being processed.
1179   */
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)
1185     return;
1186   if (parser->encoding == NULL)
1187     svg_info->document->encoding=(const xmlChar *) NULL;
1188   else
1189     svg_info->document->encoding=xmlStrdup(parser->encoding);
1190   svg_info->document->standalone=parser->standalone;
1191 }
1192 
SVGEndDocument(void * context)1193 static void SVGEndDocument(void *context)
1194 {
1195   SVGInfo
1196     *svg_info;
1197 
1198   /*
1199     Called when the document end has been detected.
1200   */
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)
1217     {
1218       xmlFreeDoc(svg_info->document);
1219       svg_info->document=(xmlDocPtr) NULL;
1220     }
1221 #endif
1222 }
1223 
SVGStartElement(void * context,const xmlChar * name,const xmlChar ** attributes)1224 static void SVGStartElement(void *context,const xmlChar *name,
1225   const xmlChar **attributes)
1226 {
1227 #define PushGraphicContext(id) \
1228 { \
1229   if (*id == '\0') \
1230     (void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); \
1231   else \
1232     (void) FormatLocaleFile(svg_info->file,"push graphic-context \"%s\"\n", \
1233       id); \
1234 }
1235 
1236   char
1237     *color,
1238     background[MagickPathExtent],
1239     id[MagickPathExtent],
1240     *next_token,
1241     token[MagickPathExtent],
1242     **tokens,
1243     *units;
1244 
1245   const char
1246     *keyword,
1247     *p,
1248     *value;
1249 
1250   register ssize_t
1251     i,
1252     j;
1253 
1254   size_t
1255     number_tokens;
1256 
1257   SVGInfo
1258     *svg_info;
1259 
1260   /*
1261     Called when an opening tag has been processed.
1262   */
1263   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.startElement(%s",
1264     name);
1265   svg_info=(SVGInfo *) context;
1266   svg_info->n++;
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)
1270     {
1271       (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
1272         ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
1273       return;
1274     }
1275   svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
1276   color=AcquireString("none");
1277   units=AcquireString("userSpaceOnUse");
1278   *id='\0';
1279   *token='\0';
1280   *background='\0';
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))
1287     {
1288       svg_info->bounds.x=0.0;
1289       svg_info->bounds.y=0.0;
1290     }
1291   if (attributes != (const xmlChar **) NULL)
1292     for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1293     {
1294       keyword=(const char *) attributes[i];
1295       value=(const char *) attributes[i+1];
1296       switch (*keyword)
1297       {
1298         case 'C':
1299         case 'c':
1300         {
1301           if (LocaleCompare(keyword,"cx") == 0)
1302             {
1303               svg_info->element.cx=
1304                 GetUserSpaceCoordinateValue(svg_info,1,value);
1305               break;
1306             }
1307           if (LocaleCompare(keyword,"cy") == 0)
1308             {
1309               svg_info->element.cy=
1310                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1311               break;
1312             }
1313           break;
1314         }
1315         case 'F':
1316         case 'f':
1317         {
1318           if (LocaleCompare(keyword,"fx") == 0)
1319             {
1320               svg_info->element.major=
1321                 GetUserSpaceCoordinateValue(svg_info,1,value);
1322               break;
1323             }
1324           if (LocaleCompare(keyword,"fy") == 0)
1325             {
1326               svg_info->element.minor=
1327                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1328               break;
1329             }
1330           break;
1331         }
1332         case 'H':
1333         case 'h':
1334         {
1335           if (LocaleCompare(keyword,"height") == 0)
1336             {
1337               svg_info->bounds.height=
1338                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1339               break;
1340             }
1341           break;
1342         }
1343         case 'I':
1344         case 'i':
1345         {
1346           if (LocaleCompare(keyword,"id") == 0)
1347             {
1348               (void) CopyMagickString(id,value,MagickPathExtent);
1349               break;
1350             }
1351           break;
1352         }
1353         case 'R':
1354         case 'r':
1355         {
1356           if (LocaleCompare(keyword,"r") == 0)
1357             {
1358               svg_info->element.angle=
1359                 GetUserSpaceCoordinateValue(svg_info,0,value);
1360               break;
1361             }
1362           break;
1363         }
1364         case 'W':
1365         case 'w':
1366         {
1367           if (LocaleCompare(keyword,"width") == 0)
1368             {
1369               svg_info->bounds.width=
1370                 GetUserSpaceCoordinateValue(svg_info,1,value);
1371               break;
1372             }
1373           break;
1374         }
1375         case 'X':
1376         case 'x':
1377         {
1378           if (LocaleCompare(keyword,"x") == 0)
1379             {
1380               svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
1381               break;
1382             }
1383           if (LocaleCompare(keyword,"x1") == 0)
1384             {
1385               svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
1386                 value);
1387               break;
1388             }
1389           if (LocaleCompare(keyword,"x2") == 0)
1390             {
1391               svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
1392                 value);
1393               break;
1394             }
1395           break;
1396         }
1397         case 'Y':
1398         case 'y':
1399         {
1400           if (LocaleCompare(keyword,"y") == 0)
1401             {
1402               svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
1403               break;
1404             }
1405           if (LocaleCompare(keyword,"y1") == 0)
1406             {
1407               svg_info->segment.y1=GetUserSpaceCoordinateValue(svg_info,-1,
1408                 value);
1409               break;
1410             }
1411           if (LocaleCompare(keyword,"y2") == 0)
1412             {
1413               svg_info->segment.y2=GetUserSpaceCoordinateValue(svg_info,-1,
1414                 value);
1415               break;
1416             }
1417           break;
1418         }
1419         default:
1420           break;
1421       }
1422     }
1423   if (strchr((char *) name,':') != (char *) NULL)
1424     {
1425       /*
1426         Skip over namespace.
1427       */
1428       for ( ; *name != ':'; name++) ;
1429       name++;
1430     }
1431   switch (*name)
1432   {
1433     case 'C':
1434     case 'c':
1435     {
1436       if (LocaleCompare((const char *) name,"circle") == 0)
1437         {
1438           PushGraphicContext(id);
1439           break;
1440         }
1441       if (LocaleCompare((const char *) name,"clipPath") == 0)
1442         {
1443           (void) FormatLocaleFile(svg_info->file,"push clip-path \"%s\"\n",id);
1444           break;
1445         }
1446       break;
1447     }
1448     case 'D':
1449     case 'd':
1450     {
1451       if (LocaleCompare((const char *) name,"defs") == 0)
1452         {
1453           (void) FormatLocaleFile(svg_info->file,"push defs\n");
1454           break;
1455         }
1456       break;
1457     }
1458     case 'E':
1459     case 'e':
1460     {
1461       if (LocaleCompare((const char *) name,"ellipse") == 0)
1462         {
1463           PushGraphicContext(id);
1464           break;
1465         }
1466       break;
1467     }
1468     case 'F':
1469     case 'f':
1470     {
1471       if (LocaleCompare((const char *) name,"foreignObject") == 0)
1472         {
1473           PushGraphicContext(id);
1474           break;
1475         }
1476       break;
1477     }
1478     case 'G':
1479     case 'g':
1480     {
1481       if (LocaleCompare((const char *) name,"g") == 0)
1482         {
1483           PushGraphicContext(id);
1484           break;
1485         }
1486       break;
1487     }
1488     case 'I':
1489     case 'i':
1490     {
1491       if (LocaleCompare((const char *) name,"image") == 0)
1492         {
1493           PushGraphicContext(id);
1494           break;
1495         }
1496       break;
1497     }
1498     case 'L':
1499     case 'l':
1500     {
1501       if (LocaleCompare((const char *) name,"line") == 0)
1502         {
1503           PushGraphicContext(id);
1504           break;
1505         }
1506       if (LocaleCompare((const char *) name,"linearGradient") == 0)
1507         {
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);
1512           break;
1513         }
1514       break;
1515     }
1516     case 'M':
1517     case 'm':
1518     {
1519       if (LocaleCompare((const char *) name,"mask") == 0)
1520         {
1521           (void) FormatLocaleFile(svg_info->file,"push mask \"%s\"\n",id);
1522           break;
1523         }
1524       break;
1525     }
1526     case 'P':
1527     case 'p':
1528     {
1529       if (LocaleCompare((const char *) name,"path") == 0)
1530         {
1531           PushGraphicContext(id);
1532           break;
1533         }
1534       if (LocaleCompare((const char *) name,"pattern") == 0)
1535         {
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);
1540           break;
1541         }
1542       if (LocaleCompare((const char *) name,"polygon") == 0)
1543         {
1544           PushGraphicContext(id);
1545           break;
1546         }
1547       if (LocaleCompare((const char *) name,"polyline") == 0)
1548         {
1549           PushGraphicContext(id);
1550           break;
1551         }
1552       break;
1553     }
1554     case 'R':
1555     case 'r':
1556     {
1557       if (LocaleCompare((const char *) name,"radialGradient") == 0)
1558         {
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);
1564           break;
1565         }
1566       if (LocaleCompare((const char *) name,"rect") == 0)
1567         {
1568           PushGraphicContext(id);
1569           break;
1570         }
1571       break;
1572     }
1573     case 'S':
1574     case 's':
1575     {
1576       if (LocaleCompare((char *) name,"style") == 0)
1577         break;
1578       if (LocaleCompare((const char *) name,"svg") == 0)
1579         {
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");
1589           break;
1590         }
1591       if (LocaleCompare((const char *) name,"symbol") == 0)
1592         {
1593           (void) FormatLocaleFile(svg_info->file,"push symbol\n");
1594           break;
1595         }
1596       break;
1597     }
1598     case 'T':
1599     case 't':
1600     {
1601       if (LocaleCompare((const char *) name,"text") == 0)
1602         {
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;
1611           break;
1612         }
1613       if (LocaleCompare((const char *) name,"tspan") == 0)
1614         {
1615           if (*svg_info->text != '\0')
1616             {
1617               char
1618                 *text;
1619 
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';
1625             }
1626           PushGraphicContext(id);
1627           break;
1628         }
1629       break;
1630     }
1631     case 'U':
1632     case 'u':
1633     {
1634       if (LocaleCompare((char *) name,"use") == 0)
1635         {
1636           PushGraphicContext(id);
1637           break;
1638         }
1639       break;
1640     }
1641     default:
1642       break;
1643   }
1644   if (attributes != (const xmlChar **) NULL)
1645     for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1646     {
1647       keyword=(const char *) attributes[i];
1648       value=(const char *) attributes[i+1];
1649       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1650         "    %s = %s",keyword,value);
1651       switch (*keyword)
1652       {
1653         case 'A':
1654         case 'a':
1655         {
1656           if (LocaleCompare(keyword,"angle") == 0)
1657             {
1658               (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1659                 GetUserSpaceCoordinateValue(svg_info,0,value));
1660               break;
1661             }
1662           break;
1663         }
1664         case 'C':
1665         case 'c':
1666         {
1667           if (LocaleCompare(keyword,"class") == 0)
1668             {
1669               const char
1670                 *p;
1671 
1672               p=value;
1673               (void) GetNextToken(p,&p,MagickPathExtent,token);
1674               if (*token == ',')
1675                 (void) GetNextToken(p,&p,MagickPathExtent,token);
1676               if (*token != '\0')
1677                 (void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",value);
1678               else
1679                 (void) FormatLocaleFile(svg_info->file,"class \"none\"\n");
1680               break;
1681             }
1682           if (LocaleCompare(keyword,"clip-path") == 0)
1683             {
1684               (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",
1685                 value);
1686               break;
1687             }
1688           if (LocaleCompare(keyword,"clip-rule") == 0)
1689             {
1690               (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",
1691                 value);
1692               break;
1693             }
1694           if (LocaleCompare(keyword,"clipPathUnits") == 0)
1695             {
1696               (void) CloneString(&units,value);
1697               (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
1698                 value);
1699               break;
1700             }
1701           if (LocaleCompare(keyword,"color") == 0)
1702             {
1703               (void) CloneString(&color,value);
1704               break;
1705             }
1706           if (LocaleCompare(keyword,"cx") == 0)
1707             {
1708               svg_info->element.cx=
1709                 GetUserSpaceCoordinateValue(svg_info,1,value);
1710               break;
1711             }
1712           if (LocaleCompare(keyword,"cy") == 0)
1713             {
1714               svg_info->element.cy=
1715                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1716               break;
1717             }
1718           break;
1719         }
1720         case 'D':
1721         case 'd':
1722         {
1723           if (LocaleCompare(keyword,"d") == 0)
1724             {
1725               (void) CloneString(&svg_info->vertices,value);
1726               break;
1727             }
1728           if (LocaleCompare(keyword,"dx") == 0)
1729             {
1730               double
1731                 dx;
1732 
1733               dx=GetUserSpaceCoordinateValue(svg_info,1,value);
1734               svg_info->bounds.x+=dx;
1735               svg_info->text_offset.x+=dx;
1736               if (LocaleCompare((char *) name,"text") == 0)
1737                 (void) FormatLocaleFile(svg_info->file,"translate %g,0.0\n",dx);
1738               break;
1739             }
1740           if (LocaleCompare(keyword,"dy") == 0)
1741             {
1742               double
1743                 dy;
1744 
1745               dy=GetUserSpaceCoordinateValue(svg_info,-1,value);
1746               svg_info->bounds.y+=dy;
1747               svg_info->text_offset.y+=dy;
1748               if (LocaleCompare((char *) name,"text") == 0)
1749                 (void) FormatLocaleFile(svg_info->file,"translate 0.0,%g\n",dy);
1750               break;
1751             }
1752           break;
1753         }
1754         case 'F':
1755         case 'f':
1756         {
1757           if (LocaleCompare(keyword,"fill") == 0)
1758             {
1759               if (LocaleCompare(value,"currentColor") == 0)
1760                 {
1761                   (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
1762                   break;
1763                 }
1764               (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1765               break;
1766             }
1767           if (LocaleCompare(keyword,"fillcolor") == 0)
1768             {
1769               (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1770               break;
1771             }
1772           if (LocaleCompare(keyword,"fill-rule") == 0)
1773             {
1774               (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",
1775                 value);
1776               break;
1777             }
1778           if (LocaleCompare(keyword,"fill-opacity") == 0)
1779             {
1780               (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
1781                 value);
1782               break;
1783             }
1784           if (LocaleCompare(keyword,"font-family") == 0)
1785             {
1786               (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
1787                 value);
1788               break;
1789             }
1790           if (LocaleCompare(keyword,"font-stretch") == 0)
1791             {
1792               (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
1793                 value);
1794               break;
1795             }
1796           if (LocaleCompare(keyword,"font-style") == 0)
1797             {
1798               (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
1799                 value);
1800               break;
1801             }
1802           if (LocaleCompare(keyword,"font-size") == 0)
1803             {
1804               if (LocaleCompare(value,"xx-small") == 0)
1805                 svg_info->pointsize=6.144;
1806               else if (LocaleCompare(value,"x-small") == 0)
1807                 svg_info->pointsize=7.68;
1808               else if (LocaleCompare(value,"small") == 0)
1809                 svg_info->pointsize=9.6;
1810               else if (LocaleCompare(value,"medium") == 0)
1811                 svg_info->pointsize=12.0;
1812               else if (LocaleCompare(value,"large") == 0)
1813                 svg_info->pointsize=14.4;
1814               else if (LocaleCompare(value,"x-large") == 0)
1815                 svg_info->pointsize=17.28;
1816               else if (LocaleCompare(value,"xx-large") == 0)
1817                 svg_info->pointsize=20.736;
1818               else
1819                 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,
1820                   value);
1821               (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1822                 svg_info->pointsize);
1823               break;
1824             }
1825           if (LocaleCompare(keyword,"font-weight") == 0)
1826             {
1827               (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
1828                 value);
1829               break;
1830             }
1831           break;
1832         }
1833         case 'G':
1834         case 'g':
1835         {
1836           if (LocaleCompare(keyword,"gradientTransform") == 0)
1837             {
1838               AffineMatrix
1839                 affine,
1840                 current,
1841                 transform;
1842 
1843               GetAffineMatrix(&transform);
1844               (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
1845               tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
1846               if (tokens == (char **) NULL)
1847                 break;
1848               for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
1849               {
1850                 keyword=(char *) tokens[j];
1851                 if (keyword == (char *) NULL)
1852                   continue;
1853                 value=(char *) tokens[j+1];
1854                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1855                   "    %s: %s",keyword,value);
1856                 current=transform;
1857                 GetAffineMatrix(&affine);
1858                 switch (*keyword)
1859                 {
1860                   case 'M':
1861                   case 'm':
1862                   {
1863                     if (LocaleCompare(keyword,"matrix") == 0)
1864                       {
1865                         p=(const char *) value;
1866                         (void) GetNextToken(p,&p,MagickPathExtent,token);
1867                         affine.sx=StringToDouble(value,(char **) NULL);
1868                         (void) GetNextToken(p,&p,MagickPathExtent,token);
1869                         if (*token == ',')
1870                           (void) GetNextToken(p,&p,MagickPathExtent,token);
1871                         affine.rx=StringToDouble(token,&next_token);
1872                         (void) GetNextToken(p,&p,MagickPathExtent,token);
1873                         if (*token == ',')
1874                           (void) GetNextToken(p,&p,MagickPathExtent,token);
1875                         affine.ry=StringToDouble(token,&next_token);
1876                         (void) GetNextToken(p,&p,MagickPathExtent,token);
1877                         if (*token == ',')
1878                           (void) GetNextToken(p,&p,MagickPathExtent,token);
1879                         affine.sy=StringToDouble(token,&next_token);
1880                         (void) GetNextToken(p,&p,MagickPathExtent,token);
1881                         if (*token == ',')
1882                           (void) GetNextToken(p,&p,MagickPathExtent,token);
1883                         affine.tx=StringToDouble(token,&next_token);
1884                         (void) GetNextToken(p,&p,MagickPathExtent,token);
1885                         if (*token == ',')
1886                           (void) GetNextToken(p,&p,MagickPathExtent,token);
1887                         affine.ty=StringToDouble(token,&next_token);
1888                         break;
1889                       }
1890                     break;
1891                   }
1892                   case 'R':
1893                   case 'r':
1894                   {
1895                     if (LocaleCompare(keyword,"rotate") == 0)
1896                       {
1897                         double
1898                           angle;
1899 
1900                         angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1901                         affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1902                         affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1903                         affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1904                         affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1905                         break;
1906                       }
1907                     break;
1908                   }
1909                   case 'S':
1910                   case 's':
1911                   {
1912                     if (LocaleCompare(keyword,"scale") == 0)
1913                       {
1914                         for (p=(const char *) value; *p != '\0'; p++)
1915                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
1916                               (*p == ','))
1917                             break;
1918                         affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1919                         affine.sy=affine.sx;
1920                         if (*p != '\0')
1921                           affine.sy=
1922                             GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1923                         svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1924                         break;
1925                       }
1926                     if (LocaleCompare(keyword,"skewX") == 0)
1927                       {
1928                         affine.sx=svg_info->affine.sx;
1929                         affine.ry=tan(DegreesToRadians(fmod(
1930                           GetUserSpaceCoordinateValue(svg_info,1,value),
1931                           360.0)));
1932                         affine.sy=svg_info->affine.sy;
1933                         break;
1934                       }
1935                     if (LocaleCompare(keyword,"skewY") == 0)
1936                       {
1937                         affine.sx=svg_info->affine.sx;
1938                         affine.rx=tan(DegreesToRadians(fmod(
1939                           GetUserSpaceCoordinateValue(svg_info,-1,value),
1940                           360.0)));
1941                         affine.sy=svg_info->affine.sy;
1942                         break;
1943                       }
1944                     break;
1945                   }
1946                   case 'T':
1947                   case 't':
1948                   {
1949                     if (LocaleCompare(keyword,"translate") == 0)
1950                       {
1951                         for (p=(const char *) value; *p != '\0'; p++)
1952                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
1953                               (*p == ','))
1954                             break;
1955                         affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1956                         affine.ty=affine.tx;
1957                         if (*p != '\0')
1958                           affine.ty=
1959                             GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1960                         break;
1961                       }
1962                     break;
1963                   }
1964                   default:
1965                     break;
1966                 }
1967                 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1968                 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1969                 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1970                 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1971                 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1972                   current.tx;
1973                 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1974                   current.ty;
1975               }
1976               (void) FormatLocaleFile(svg_info->file,
1977                 "affine %g %g %g %g %g %g\n",transform.sx,
1978                 transform.rx,transform.ry,transform.sy,transform.tx,
1979                 transform.ty);
1980               for (j=0; tokens[j] != (char *) NULL; j++)
1981                 tokens[j]=DestroyString(tokens[j]);
1982               tokens=(char **) RelinquishMagickMemory(tokens);
1983               break;
1984             }
1985           if (LocaleCompare(keyword,"gradientUnits") == 0)
1986             {
1987               (void) CloneString(&units,value);
1988               (void) FormatLocaleFile(svg_info->file,"gradient-units \"%s\"\n",
1989                 value);
1990               break;
1991             }
1992           break;
1993         }
1994         case 'H':
1995         case 'h':
1996         {
1997           if (LocaleCompare(keyword,"height") == 0)
1998             {
1999               svg_info->bounds.height=
2000                 GetUserSpaceCoordinateValue(svg_info,-1,value);
2001               break;
2002             }
2003           if (LocaleCompare(keyword,"href") == 0)
2004             {
2005               (void) CloneString(&svg_info->url,value);
2006               break;
2007             }
2008           break;
2009         }
2010         case 'K':
2011         case 'k':
2012         {
2013           if (LocaleCompare(keyword,"kerning") == 0)
2014             {
2015               (void) FormatLocaleFile(svg_info->file,"kerning \"%s\"\n",
2016                 value);
2017               break;
2018             }
2019           break;
2020         }
2021         case 'L':
2022         case 'l':
2023         {
2024           if (LocaleCompare(keyword,"letter-spacing") == 0)
2025             {
2026               (void) FormatLocaleFile(svg_info->file,"letter-spacing \"%s\"\n",
2027                 value);
2028               break;
2029             }
2030           break;
2031         }
2032         case 'M':
2033         case 'm':
2034         {
2035           if (LocaleCompare(keyword,"major") == 0)
2036             {
2037               svg_info->element.major=
2038                 GetUserSpaceCoordinateValue(svg_info,1,value);
2039               break;
2040             }
2041           if (LocaleCompare(keyword,"mask") == 0)
2042             {
2043               (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
2044               break;
2045             }
2046           if (LocaleCompare(keyword,"minor") == 0)
2047             {
2048               svg_info->element.minor=
2049                 GetUserSpaceCoordinateValue(svg_info,-1,value);
2050               break;
2051             }
2052           break;
2053         }
2054         case 'O':
2055         case 'o':
2056         {
2057           if (LocaleCompare(keyword,"offset") == 0)
2058             {
2059               (void) CloneString(&svg_info->offset,value);
2060               break;
2061             }
2062           if (LocaleCompare(keyword,"opacity") == 0)
2063             {
2064               (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
2065               break;
2066             }
2067           break;
2068         }
2069         case 'P':
2070         case 'p':
2071         {
2072           if (LocaleCompare(keyword,"path") == 0)
2073             {
2074               (void) CloneString(&svg_info->url,value);
2075               break;
2076             }
2077           if (LocaleCompare(keyword,"points") == 0)
2078             {
2079               (void) CloneString(&svg_info->vertices,value);
2080               break;
2081             }
2082           break;
2083         }
2084         case 'R':
2085         case 'r':
2086         {
2087           if (LocaleCompare(keyword,"r") == 0)
2088             {
2089               svg_info->element.major=
2090                 GetUserSpaceCoordinateValue(svg_info,1,value);
2091               svg_info->element.minor=
2092                 GetUserSpaceCoordinateValue(svg_info,-1,value);
2093               break;
2094             }
2095           if (LocaleCompare(keyword,"rotate") == 0)
2096             {
2097               double
2098                 angle;
2099 
2100               angle=GetUserSpaceCoordinateValue(svg_info,0,value);
2101               (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2102                 svg_info->bounds.x,svg_info->bounds.y);
2103               svg_info->bounds.x=0;
2104               svg_info->bounds.y=0;
2105               (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
2106               break;
2107             }
2108           if (LocaleCompare(keyword,"rx") == 0)
2109             {
2110               if (LocaleCompare((const char *) name,"ellipse") == 0)
2111                 svg_info->element.major=
2112                   GetUserSpaceCoordinateValue(svg_info,1,value);
2113               else
2114                 svg_info->radius.x=
2115                   GetUserSpaceCoordinateValue(svg_info,1,value);
2116               break;
2117             }
2118           if (LocaleCompare(keyword,"ry") == 0)
2119             {
2120               if (LocaleCompare((const char *) name,"ellipse") == 0)
2121                 svg_info->element.minor=
2122                   GetUserSpaceCoordinateValue(svg_info,-1,value);
2123               else
2124                 svg_info->radius.y=
2125                   GetUserSpaceCoordinateValue(svg_info,-1,value);
2126               break;
2127             }
2128           break;
2129         }
2130         case 'S':
2131         case 's':
2132         {
2133           if (LocaleCompare(keyword,"stop-color") == 0)
2134             {
2135               (void) CloneString(&svg_info->stop_color,value);
2136               break;
2137             }
2138           if (LocaleCompare(keyword,"stroke") == 0)
2139             {
2140               if (LocaleCompare(value,"currentColor") == 0)
2141                 {
2142                   (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",
2143                     color);
2144                   break;
2145                 }
2146               (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",value);
2147               break;
2148             }
2149           if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
2150             {
2151               (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
2152                 LocaleCompare(value,"true") == 0);
2153               break;
2154             }
2155           if (LocaleCompare(keyword,"stroke-dasharray") == 0)
2156             {
2157               (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
2158                 value);
2159               break;
2160             }
2161           if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
2162             {
2163               (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
2164                 GetUserSpaceCoordinateValue(svg_info,1,value));
2165               break;
2166             }
2167           if (LocaleCompare(keyword,"stroke-linecap") == 0)
2168             {
2169               (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
2170                 value);
2171               break;
2172             }
2173           if (LocaleCompare(keyword,"stroke-linejoin") == 0)
2174             {
2175               (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
2176                 value);
2177               break;
2178             }
2179           if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
2180             {
2181               (void) FormatLocaleFile(svg_info->file,
2182                 "stroke-miterlimit \"%s\"\n",value);
2183               break;
2184             }
2185           if (LocaleCompare(keyword,"stroke-opacity") == 0)
2186             {
2187               (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
2188                 value);
2189               break;
2190             }
2191           if (LocaleCompare(keyword,"stroke-width") == 0)
2192             {
2193               (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
2194                 GetUserSpaceCoordinateValue(svg_info,1,value));
2195               break;
2196             }
2197           if (LocaleCompare(keyword,"style") == 0)
2198             {
2199               SVGProcessStyleElement(context,name,value);
2200               break;
2201             }
2202           break;
2203         }
2204         case 'T':
2205         case 't':
2206         {
2207           if (LocaleCompare(keyword,"text-align") == 0)
2208             {
2209               (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",
2210                 value);
2211               break;
2212             }
2213           if (LocaleCompare(keyword,"text-anchor") == 0)
2214             {
2215               (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
2216                 value);
2217               break;
2218             }
2219           if (LocaleCompare(keyword,"text-decoration") == 0)
2220             {
2221               if (LocaleCompare(value,"underline") == 0)
2222                 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
2223               if (LocaleCompare(value,"line-through") == 0)
2224                 (void) FormatLocaleFile(svg_info->file,
2225                   "decorate line-through\n");
2226               if (LocaleCompare(value,"overline") == 0)
2227                 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
2228               break;
2229             }
2230           if (LocaleCompare(keyword,"text-antialiasing") == 0)
2231             {
2232               (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
2233                 LocaleCompare(value,"true") == 0);
2234               break;
2235             }
2236           if (LocaleCompare(keyword,"transform") == 0)
2237             {
2238               AffineMatrix
2239                 affine,
2240                 current,
2241                 transform;
2242 
2243               GetAffineMatrix(&transform);
2244               (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
2245               tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
2246               if (tokens == (char **) NULL)
2247                 break;
2248               for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2249               {
2250                 keyword=(char *) tokens[j];
2251                 value=(char *) tokens[j+1];
2252                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2253                   "    %s: %s",keyword,value);
2254                 current=transform;
2255                 GetAffineMatrix(&affine);
2256                 switch (*keyword)
2257                 {
2258                   case 'M':
2259                   case 'm':
2260                   {
2261                     if (LocaleCompare(keyword,"matrix") == 0)
2262                       {
2263                         p=(const char *) value;
2264                         (void) GetNextToken(p,&p,MagickPathExtent,token);
2265                         affine.sx=StringToDouble(value,(char **) NULL);
2266                         (void) GetNextToken(p,&p,MagickPathExtent,token);
2267                         if (*token == ',')
2268                           (void) GetNextToken(p,&p,MagickPathExtent,token);
2269                         affine.rx=StringToDouble(token,&next_token);
2270                         (void) GetNextToken(p,&p,MagickPathExtent,token);
2271                         if (*token == ',')
2272                           (void) GetNextToken(p,&p,MagickPathExtent,token);
2273                         affine.ry=StringToDouble(token,&next_token);
2274                         (void) GetNextToken(p,&p,MagickPathExtent,token);
2275                         if (*token == ',')
2276                           (void) GetNextToken(p,&p,MagickPathExtent,token);
2277                         affine.sy=StringToDouble(token,&next_token);
2278                         (void) GetNextToken(p,&p,MagickPathExtent,token);
2279                         if (*token == ',')
2280                           (void) GetNextToken(p,&p,MagickPathExtent,token);
2281                         affine.tx=StringToDouble(token,&next_token);
2282                         (void) GetNextToken(p,&p,MagickPathExtent,token);
2283                         if (*token == ',')
2284                           (void) GetNextToken(p,&p,MagickPathExtent,token);
2285                         affine.ty=StringToDouble(token,&next_token);
2286                         break;
2287                       }
2288                     break;
2289                   }
2290                   case 'R':
2291                   case 'r':
2292                   {
2293                     if (LocaleCompare(keyword,"rotate") == 0)
2294                       {
2295                         double
2296                           angle,
2297                           x,
2298                           y;
2299 
2300                         p=(const char *) value;
2301                         (void) GetNextToken(p,&p,MagickPathExtent,token);
2302                         angle=StringToDouble(value,(char **) NULL);
2303                         affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
2304                         affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
2305                         affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
2306                         affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
2307                         (void) GetNextToken(p,&p,MagickPathExtent,token);
2308                         if (*token == ',')
2309                           (void) GetNextToken(p,&p,MagickPathExtent,token);
2310                         x=StringToDouble(token,&next_token);
2311                         (void) GetNextToken(p,&p,MagickPathExtent,token);
2312                         if (*token == ',')
2313                           (void) GetNextToken(p,&p,MagickPathExtent,token);
2314                         y=StringToDouble(token,&next_token);
2315                         affine.tx=svg_info->bounds.x+x*
2316                           cos(DegreesToRadians(fmod(angle,360.0)))+y*
2317                           sin(DegreesToRadians(fmod(angle,360.0)));
2318                         affine.ty=svg_info->bounds.y-x*
2319                           sin(DegreesToRadians(fmod(angle,360.0)))+y*
2320                           cos(DegreesToRadians(fmod(angle,360.0)));
2321                         affine.tx-=x;
2322                         affine.ty-=y;
2323                         break;
2324                       }
2325                     break;
2326                   }
2327                   case 'S':
2328                   case 's':
2329                   {
2330                     if (LocaleCompare(keyword,"scale") == 0)
2331                       {
2332                         for (p=(const char *) value; *p != '\0'; p++)
2333                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
2334                               (*p == ','))
2335                             break;
2336                         affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
2337                         affine.sy=affine.sx;
2338                         if (*p != '\0')
2339                           affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
2340                             p+1);
2341                         svg_info->scale[svg_info->n]=ExpandAffine(&affine);
2342                         break;
2343                       }
2344                     if (LocaleCompare(keyword,"skewX") == 0)
2345                       {
2346                         affine.sx=svg_info->affine.sx;
2347                         affine.ry=tan(DegreesToRadians(fmod(
2348                           GetUserSpaceCoordinateValue(svg_info,1,value),
2349                           360.0)));
2350                         affine.sy=svg_info->affine.sy;
2351                         break;
2352                       }
2353                     if (LocaleCompare(keyword,"skewY") == 0)
2354                       {
2355                         affine.sx=svg_info->affine.sx;
2356                         affine.rx=tan(DegreesToRadians(fmod(
2357                           GetUserSpaceCoordinateValue(svg_info,-1,value),
2358                           360.0)));
2359                         affine.sy=svg_info->affine.sy;
2360                         break;
2361                       }
2362                     break;
2363                   }
2364                   case 'T':
2365                   case 't':
2366                   {
2367                     if (LocaleCompare(keyword,"translate") == 0)
2368                       {
2369                         for (p=(const char *) value; *p != '\0'; p++)
2370                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
2371                               (*p == ','))
2372                             break;
2373                         affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2374                         affine.ty=0;
2375                         if (*p != '\0')
2376                           affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2377                             p+1);
2378                         break;
2379                       }
2380                     break;
2381                   }
2382                   default:
2383                     break;
2384                 }
2385                 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2386                 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2387                 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2388                 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2389                 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2390                   current.tx;
2391                 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2392                   current.ty;
2393               }
2394               (void) FormatLocaleFile(svg_info->file,
2395                 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2396                 transform.ry,transform.sy,transform.tx,transform.ty);
2397               for (j=0; tokens[j] != (char *) NULL; j++)
2398                 tokens[j]=DestroyString(tokens[j]);
2399               tokens=(char **) RelinquishMagickMemory(tokens);
2400               break;
2401             }
2402           break;
2403         }
2404         case 'V':
2405         case 'v':
2406         {
2407           if (LocaleCompare(keyword,"verts") == 0)
2408             {
2409               (void) CloneString(&svg_info->vertices,value);
2410               break;
2411             }
2412           if (LocaleCompare(keyword,"viewBox") == 0)
2413             {
2414               p=(const char *) value;
2415               (void) GetNextToken(p,&p,MagickPathExtent,token);
2416               svg_info->view_box.x=StringToDouble(token,&next_token);
2417               (void) GetNextToken(p,&p,MagickPathExtent,token);
2418               if (*token == ',')
2419                 (void) GetNextToken(p,&p,MagickPathExtent,token);
2420               svg_info->view_box.y=StringToDouble(token,&next_token);
2421               (void) GetNextToken(p,&p,MagickPathExtent,token);
2422               if (*token == ',')
2423                 (void) GetNextToken(p,&p,MagickPathExtent,token);
2424               svg_info->view_box.width=StringToDouble(token,
2425                 (char **) NULL);
2426               if (svg_info->bounds.width == 0)
2427                 svg_info->bounds.width=svg_info->view_box.width;
2428               (void) GetNextToken(p,&p,MagickPathExtent,token);
2429               if (*token == ',')
2430                 (void) GetNextToken(p,&p,MagickPathExtent,token);
2431               svg_info->view_box.height=StringToDouble(token,
2432                 (char **) NULL);
2433               if (svg_info->bounds.height == 0)
2434                 svg_info->bounds.height=svg_info->view_box.height;
2435               break;
2436             }
2437           break;
2438         }
2439         case 'W':
2440         case 'w':
2441         {
2442           if (LocaleCompare(keyword,"width") == 0)
2443             {
2444               svg_info->bounds.width=
2445                 GetUserSpaceCoordinateValue(svg_info,1,value);
2446               break;
2447             }
2448           break;
2449         }
2450         case 'X':
2451         case 'x':
2452         {
2453           if (LocaleCompare(keyword,"x") == 0)
2454             {
2455               svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2456               break;
2457             }
2458           if (LocaleCompare(keyword,"xlink:href") == 0)
2459             {
2460               (void) CloneString(&svg_info->url,value);
2461               break;
2462             }
2463           if (LocaleCompare(keyword,"x1") == 0)
2464             {
2465               svg_info->segment.x1=
2466                 GetUserSpaceCoordinateValue(svg_info,1,value);
2467               break;
2468             }
2469           if (LocaleCompare(keyword,"x2") == 0)
2470             {
2471               svg_info->segment.x2=
2472                 GetUserSpaceCoordinateValue(svg_info,1,value);
2473               break;
2474             }
2475           break;
2476         }
2477         case 'Y':
2478         case 'y':
2479         {
2480           if (LocaleCompare(keyword,"y") == 0)
2481             {
2482               svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2483               break;
2484             }
2485           if (LocaleCompare(keyword,"y1") == 0)
2486             {
2487               svg_info->segment.y1=
2488                 GetUserSpaceCoordinateValue(svg_info,-1,value);
2489               break;
2490             }
2491           if (LocaleCompare(keyword,"y2") == 0)
2492             {
2493               svg_info->segment.y2=
2494                 GetUserSpaceCoordinateValue(svg_info,-1,value);
2495               break;
2496             }
2497           break;
2498         }
2499         default:
2500           break;
2501       }
2502     }
2503   if (LocaleCompare((const char *) name,"svg") == 0)
2504     {
2505       if (svg_info->document->encoding != (const xmlChar *) NULL)
2506         (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2507           (const char *) svg_info->document->encoding);
2508       if (attributes != (const xmlChar **) NULL)
2509         {
2510           double
2511             sx,
2512             sy,
2513             tx,
2514             ty;
2515 
2516           if ((svg_info->view_box.width == 0.0) ||
2517               (svg_info->view_box.height == 0.0))
2518             svg_info->view_box=svg_info->bounds;
2519           svg_info->width=0;
2520           if (svg_info->bounds.width > 0.0)
2521             svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2522           svg_info->height=0;
2523           if (svg_info->bounds.height > 0.0)
2524             svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2525           (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2526             (double) svg_info->width,(double) svg_info->height);
2527           sx=PerceptibleReciprocal(svg_info->view_box.width)*svg_info->width;
2528           sy=PerceptibleReciprocal(svg_info->view_box.height)*svg_info->height;
2529           tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2530             0.0;
2531           ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2532             0.0;
2533           (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2534             sx,sy,tx,ty);
2535           if ((svg_info->svgDepth == 1) && (*background != '\0'))
2536             {
2537               PushGraphicContext(id);
2538               (void) FormatLocaleFile(svg_info->file,"fill %s\n",background);
2539               (void) FormatLocaleFile(svg_info->file,
2540                 "rectangle 0,0 %g,%g\n",svg_info->view_box.width,
2541                 svg_info->view_box.height);
2542               (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2543             }
2544         }
2545     }
2546   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  )");
2547   if (units != (char *) NULL)
2548     units=DestroyString(units);
2549   if (color != (char *) NULL)
2550     color=DestroyString(color);
2551 }
2552 
SVGEndElement(void * context,const xmlChar * name)2553 static void SVGEndElement(void *context,const xmlChar *name)
2554 {
2555   SVGInfo
2556     *svg_info;
2557 
2558   /*
2559     Called when the end of an element has been detected.
2560   */
2561   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2562     "  SAX.endElement(%s)",name);
2563   svg_info=(SVGInfo *) context;
2564   if (strchr((char *) name,':') != (char *) NULL)
2565     {
2566       /*
2567         Skip over namespace.
2568       */
2569       for ( ; *name != ':'; name++) ;
2570       name++;
2571     }
2572   switch (*name)
2573   {
2574     case 'C':
2575     case 'c':
2576     {
2577       if (LocaleCompare((const char *) name,"circle") == 0)
2578         {
2579           (void) FormatLocaleFile(svg_info->file,"class \"circle\"\n");
2580           (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2581             svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2582             svg_info->element.cy+svg_info->element.minor);
2583           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2584           break;
2585         }
2586       if (LocaleCompare((const char *) name,"clipPath") == 0)
2587         {
2588           (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2589           break;
2590         }
2591       break;
2592     }
2593     case 'D':
2594     case 'd':
2595     {
2596       if (LocaleCompare((const char *) name,"defs") == 0)
2597         {
2598           (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2599           break;
2600         }
2601       if (LocaleCompare((const char *) name,"desc") == 0)
2602         {
2603           register char
2604             *p;
2605 
2606           if (*svg_info->text == '\0')
2607             break;
2608           (void) fputc('#',svg_info->file);
2609           for (p=svg_info->text; *p != '\0'; p++)
2610           {
2611             (void) fputc(*p,svg_info->file);
2612             if (*p == '\n')
2613               (void) fputc('#',svg_info->file);
2614           }
2615           (void) fputc('\n',svg_info->file);
2616           *svg_info->text='\0';
2617           break;
2618         }
2619       break;
2620     }
2621     case 'E':
2622     case 'e':
2623     {
2624       if (LocaleCompare((const char *) name,"ellipse") == 0)
2625         {
2626           double
2627             angle;
2628 
2629           (void) FormatLocaleFile(svg_info->file,"class \"ellipse\"\n");
2630           angle=svg_info->element.angle;
2631           (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2632             svg_info->element.cx,svg_info->element.cy,
2633             angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2634             angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2635           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2636           break;
2637         }
2638       break;
2639     }
2640     case 'F':
2641     case 'f':
2642     {
2643       if (LocaleCompare((const char *) name,"foreignObject") == 0)
2644         {
2645           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2646           break;
2647         }
2648       break;
2649     }
2650     case 'G':
2651     case 'g':
2652     {
2653       if (LocaleCompare((const char *) name,"g") == 0)
2654         {
2655           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2656           break;
2657         }
2658       break;
2659     }
2660     case 'I':
2661     case 'i':
2662     {
2663       if (LocaleCompare((const char *) name,"image") == 0)
2664         {
2665           (void) FormatLocaleFile(svg_info->file,
2666             "image Over %g,%g %g,%g \"%s\"\n",svg_info->bounds.x,
2667             svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2668             svg_info->url);
2669           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2670           break;
2671         }
2672       break;
2673     }
2674     case 'L':
2675     case 'l':
2676     {
2677       if (LocaleCompare((const char *) name,"line") == 0)
2678         {
2679           (void) FormatLocaleFile(svg_info->file,"class \"line\"\n");
2680           (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2681             svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2682             svg_info->segment.y2);
2683           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2684           break;
2685         }
2686       if (LocaleCompare((const char *) name,"linearGradient") == 0)
2687         {
2688           (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2689           break;
2690         }
2691       break;
2692     }
2693     case 'M':
2694     case 'm':
2695     {
2696       if (LocaleCompare((const char *) name,"mask") == 0)
2697         {
2698           (void) FormatLocaleFile(svg_info->file,"pop mask\n");
2699           break;
2700         }
2701       break;
2702     }
2703     case 'P':
2704     case 'p':
2705     {
2706       if (LocaleCompare((const char *) name,"pattern") == 0)
2707         {
2708           (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2709           break;
2710         }
2711       if (LocaleCompare((const char *) name,"path") == 0)
2712         {
2713           (void) FormatLocaleFile(svg_info->file,"class \"path\"\n");
2714           (void) FormatLocaleFile(svg_info->file,"path \"%s\"\n",
2715             svg_info->vertices);
2716           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2717           break;
2718         }
2719       if (LocaleCompare((const char *) name,"polygon") == 0)
2720         {
2721           (void) FormatLocaleFile(svg_info->file,"class \"polygon\"\n");
2722           (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2723             svg_info->vertices);
2724           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2725           break;
2726         }
2727       if (LocaleCompare((const char *) name,"polyline") == 0)
2728         {
2729           (void) FormatLocaleFile(svg_info->file,"class \"polyline\"\n");
2730           (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2731             svg_info->vertices);
2732           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2733           break;
2734         }
2735       break;
2736     }
2737     case 'R':
2738     case 'r':
2739     {
2740       if (LocaleCompare((const char *) name,"radialGradient") == 0)
2741         {
2742           (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2743           break;
2744         }
2745       if (LocaleCompare((const char *) name,"rect") == 0)
2746         {
2747           if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2748             {
2749               (void) FormatLocaleFile(svg_info->file,"class \"rect\"\n");
2750               if ((fabs(svg_info->bounds.width-1.0) < MagickEpsilon) &&
2751                   (fabs(svg_info->bounds.height-1.0) < MagickEpsilon))
2752                 (void) FormatLocaleFile(svg_info->file,"point %g,%g\n",
2753                   svg_info->bounds.x,svg_info->bounds.y);
2754               else
2755                 (void) FormatLocaleFile(svg_info->file,
2756                   "rectangle %g,%g %g,%g\n",svg_info->bounds.x,
2757                   svg_info->bounds.y,svg_info->bounds.x+svg_info->bounds.width,
2758                   svg_info->bounds.y+svg_info->bounds.height);
2759               (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2760               break;
2761             }
2762           if (svg_info->radius.x == 0.0)
2763             svg_info->radius.x=svg_info->radius.y;
2764           if (svg_info->radius.y == 0.0)
2765             svg_info->radius.y=svg_info->radius.x;
2766           (void) FormatLocaleFile(svg_info->file,
2767             "roundRectangle %g,%g %g,%g %g,%g\n",
2768             svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2769             svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2770             svg_info->radius.x,svg_info->radius.y);
2771           svg_info->radius.x=0.0;
2772           svg_info->radius.y=0.0;
2773           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2774           break;
2775         }
2776       break;
2777     }
2778     case 'S':
2779     case 's':
2780     {
2781       if (LocaleCompare((const char *) name,"stop") == 0)
2782         {
2783           (void) FormatLocaleFile(svg_info->file,"stop-color \"%s\" %s\n",
2784             svg_info->stop_color,svg_info->offset);
2785           break;
2786         }
2787       if (LocaleCompare((char *) name,"style") == 0)
2788         {
2789           char
2790             *keyword,
2791             **tokens,
2792             *value;
2793 
2794           register ssize_t
2795             j;
2796 
2797           size_t
2798             number_tokens;
2799 
2800           /*
2801             Find style definitions in svg_info->text.
2802           */
2803           tokens=SVGKeyValuePairs(context,'{','}',svg_info->text,
2804             &number_tokens);
2805           if (tokens == (char **) NULL)
2806             break;
2807           for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2808           {
2809             keyword=(char *) tokens[j];
2810             value=(char *) tokens[j+1];
2811             (void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
2812               *keyword == '.' ? keyword+1 : keyword);
2813             SVGProcessStyleElement(context,name,value);
2814             (void) FormatLocaleFile(svg_info->file,"pop class\n");
2815           }
2816           for (j=0; tokens[j] != (char *) NULL; j++)
2817             tokens[j]=DestroyString(tokens[j]);
2818           tokens=(char **) RelinquishMagickMemory(tokens);
2819           break;
2820         }
2821       if (LocaleCompare((const char *) name,"svg") == 0)
2822         {
2823           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2824           svg_info->svgDepth--;
2825           break;
2826         }
2827       if (LocaleCompare((const char *) name,"symbol") == 0)
2828         {
2829           (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
2830           break;
2831         }
2832       break;
2833     }
2834     case 'T':
2835     case 't':
2836     {
2837       if (LocaleCompare((const char *) name,"text") == 0)
2838         {
2839           if (*svg_info->text != '\0')
2840             {
2841               char
2842                 *text;
2843 
2844               SVGStripString(MagickTrue,svg_info->text);
2845               text=EscapeString(svg_info->text,'\"');
2846               (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2847                 svg_info->text_offset.x,svg_info->text_offset.y,text);
2848               text=DestroyString(text);
2849               *svg_info->text='\0';
2850             }
2851           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2852           break;
2853         }
2854       if (LocaleCompare((const char *) name,"tspan") == 0)
2855         {
2856           if (*svg_info->text != '\0')
2857             {
2858               char
2859                 *text;
2860 
2861               (void) FormatLocaleFile(svg_info->file,"class \"tspan\"\n");
2862               text=EscapeString(svg_info->text,'\"');
2863               (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2864                 svg_info->bounds.x,svg_info->bounds.y,text);
2865               text=DestroyString(text);
2866               *svg_info->text='\0';
2867             }
2868           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2869           break;
2870         }
2871       if (LocaleCompare((const char *) name,"title") == 0)
2872         {
2873           if (*svg_info->text == '\0')
2874             break;
2875           (void) CloneString(&svg_info->title,svg_info->text);
2876           *svg_info->text='\0';
2877           break;
2878         }
2879       break;
2880     }
2881     case 'U':
2882     case 'u':
2883     {
2884       if (LocaleCompare((char *) name,"use") == 0)
2885         {
2886           if ((svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0))
2887             (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2888               svg_info->bounds.x,svg_info->bounds.y);
2889           (void) FormatLocaleFile(svg_info->file,"use \"url(%s)\"\n",
2890             svg_info->url);
2891           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2892           break;
2893         }
2894       break;
2895     }
2896     default:
2897       break;
2898   }
2899   *svg_info->text='\0';
2900   (void) memset(&svg_info->element,0,sizeof(svg_info->element));
2901   (void) memset(&svg_info->segment,0,sizeof(svg_info->segment));
2902   svg_info->n--;
2903 }
2904 
SVGCharacters(void * context,const xmlChar * c,int length)2905 static void SVGCharacters(void *context,const xmlChar *c,int length)
2906 {
2907   char
2908     *text;
2909 
2910   register char
2911     *p;
2912 
2913   register ssize_t
2914     i;
2915 
2916   SVGInfo
2917     *svg_info;
2918 
2919   /*
2920     Receiving some characters from the parser.
2921   */
2922   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2923     "  SAX.characters(%s,%.20g)",c,(double) length);
2924   svg_info=(SVGInfo *) context;
2925   text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2926   if (text == (char *) NULL)
2927     return;
2928   p=text;
2929   for (i=0; i < (ssize_t) length; i++)
2930     *p++=c[i];
2931   *p='\0';
2932   SVGStripString(MagickFalse,text);
2933   if (svg_info->text == (char *) NULL)
2934     svg_info->text=text;
2935   else
2936     {
2937       (void) ConcatenateString(&svg_info->text,text);
2938       text=DestroyString(text);
2939     }
2940 }
2941 
SVGReference(void * context,const xmlChar * name)2942 static void SVGReference(void *context,const xmlChar *name)
2943 {
2944   SVGInfo
2945     *svg_info;
2946 
2947   xmlParserCtxtPtr
2948     parser;
2949 
2950   /*
2951     Called when an entity reference is detected.
2952   */
2953   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.reference(%s)",
2954     name);
2955   svg_info=(SVGInfo *) context;
2956   parser=svg_info->parser;
2957   if (parser == (xmlParserCtxtPtr) NULL)
2958     return;
2959   if (parser->node == (xmlNodePtr) NULL)
2960     return;
2961   if (*name == '#')
2962     (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2963   else
2964     (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2965 }
2966 
SVGIgnorableWhitespace(void * context,const xmlChar * c,int length)2967 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2968 {
2969   SVGInfo
2970     *svg_info;
2971 
2972   /*
2973     Receiving some ignorable whitespaces from the parser.
2974   */
2975   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2976     "  SAX.ignorableWhitespace(%.30s, %d)",c,length);
2977   svg_info=(SVGInfo *) context;
2978   (void) svg_info;
2979 }
2980 
SVGProcessingInstructions(void * context,const xmlChar * target,const xmlChar * data)2981 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2982   const xmlChar *data)
2983 {
2984   SVGInfo
2985     *svg_info;
2986 
2987   /*
2988     A processing instruction has been parsed.
2989   */
2990   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2991     "  SAX.processingInstruction(%s, %s)",target,data);
2992   svg_info=(SVGInfo *) context;
2993   (void) svg_info;
2994 }
2995 
SVGComment(void * context,const xmlChar * value)2996 static void SVGComment(void *context,const xmlChar *value)
2997 {
2998   SVGInfo
2999     *svg_info;
3000 
3001   /*
3002     A comment has been parsed.
3003   */
3004   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.comment(%s)",
3005     value);
3006   svg_info=(SVGInfo *) context;
3007   if (svg_info->comment != (char *) NULL)
3008     (void) ConcatenateString(&svg_info->comment,"\n");
3009   (void) ConcatenateString(&svg_info->comment,(const char *) value);
3010 }
3011 
3012 static void SVGWarning(void *,const char *,...)
3013   magick_attribute((__format__ (__printf__,2,3)));
3014 
SVGWarning(void * context,const char * format,...)3015 static void SVGWarning(void *context,const char *format,...)
3016 {
3017   char
3018     *message,
3019     reason[MagickPathExtent];
3020 
3021   SVGInfo
3022     *svg_info;
3023 
3024   va_list
3025     operands;
3026 
3027   /**
3028     Display and format a warning messages, gives file, line, position and
3029     extra parameters.
3030   */
3031   va_start(operands,format);
3032   svg_info=(SVGInfo *) context;
3033   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.warning: ");
3034   (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
3035 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
3036   (void) vsprintf(reason,format,operands);
3037 #else
3038   (void) vsnprintf(reason,MagickPathExtent,format,operands);
3039 #endif
3040   message=GetExceptionMessage(errno);
3041   (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
3042     DelegateWarning,reason,"`%s`",message);
3043   message=DestroyString(message);
3044   va_end(operands);
3045 }
3046 
3047 static void SVGError(void *,const char *,...)
3048   magick_attribute((__format__ (__printf__,2,3)));
3049 
SVGError(void * context,const char * format,...)3050 static void SVGError(void *context,const char *format,...)
3051 {
3052   char
3053     *message,
3054     reason[MagickPathExtent];
3055 
3056   SVGInfo
3057     *svg_info;
3058 
3059   va_list
3060     operands;
3061 
3062   /*
3063     Display and format a error formats, gives file, line, position and
3064     extra parameters.
3065   */
3066   va_start(operands,format);
3067   svg_info=(SVGInfo *) context;
3068   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.error: ");
3069   (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
3070 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
3071   (void) vsprintf(reason,format,operands);
3072 #else
3073   (void) vsnprintf(reason,MagickPathExtent,format,operands);
3074 #endif
3075   message=GetExceptionMessage(errno);
3076   (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
3077     reason,"`%s`",message);
3078   message=DestroyString(message);
3079   va_end(operands);
3080 }
3081 
SVGCDataBlock(void * context,const xmlChar * value,int length)3082 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
3083 {
3084   SVGInfo
3085     *svg_info;
3086 
3087   xmlNodePtr
3088     child;
3089 
3090   xmlParserCtxtPtr
3091     parser;
3092 
3093   /*
3094     Called when a pcdata block has been parsed.
3095   */
3096   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.pcdata(%s, %d)",
3097     value,length);
3098   svg_info=(SVGInfo *) context;
3099   parser=svg_info->parser;
3100   child=xmlGetLastChild(parser->node);
3101   if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
3102     {
3103       xmlTextConcat(child,value,length);
3104       return;
3105     }
3106   child=xmlNewCDataBlock(parser->myDoc,value,length);
3107   if (xmlAddChild(parser->node,child) == (xmlNodePtr) NULL)
3108     xmlFreeNode(child);
3109 }
3110 
SVGExternalSubset(void * context,const xmlChar * name,const xmlChar * external_id,const xmlChar * system_id)3111 static void SVGExternalSubset(void *context,const xmlChar *name,
3112   const xmlChar *external_id,const xmlChar *system_id)
3113 {
3114   SVGInfo
3115     *svg_info;
3116 
3117   xmlParserCtxt
3118     parser_context;
3119 
3120   xmlParserCtxtPtr
3121     parser;
3122 
3123   xmlParserInputPtr
3124     input;
3125 
3126   /*
3127     Does this document has an external subset?
3128   */
3129   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3130     "  SAX.externalSubset(%s, %s, %s)",name,
3131     (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
3132     (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
3133   svg_info=(SVGInfo *) context;
3134   parser=svg_info->parser;
3135   if (((external_id == NULL) && (system_id == NULL)) ||
3136       ((parser->validate == 0) || (parser->wellFormed == 0) ||
3137       (svg_info->document == 0)))
3138     return;
3139   input=SVGResolveEntity(context,external_id,system_id);
3140   if (input == NULL)
3141     return;
3142   (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
3143   parser_context=(*parser);
3144   parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
3145   if (parser->inputTab == (xmlParserInputPtr *) NULL)
3146     {
3147       parser->errNo=XML_ERR_NO_MEMORY;
3148       parser->input=parser_context.input;
3149       parser->inputNr=parser_context.inputNr;
3150       parser->inputMax=parser_context.inputMax;
3151       parser->inputTab=parser_context.inputTab;
3152       return;
3153   }
3154   parser->inputNr=0;
3155   parser->inputMax=5;
3156   parser->input=NULL;
3157   xmlPushInput(parser,input);
3158   (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
3159   if (input->filename == (char *) NULL)
3160     input->filename=(char *) xmlStrdup(system_id);
3161   input->line=1;
3162   input->col=1;
3163   input->base=parser->input->cur;
3164   input->cur=parser->input->cur;
3165   input->free=NULL;
3166   xmlParseExternalSubset(parser,external_id,system_id);
3167   while (parser->inputNr > 1)
3168     (void) xmlPopInput(parser);
3169   xmlFreeInputStream(parser->input);
3170   xmlFree(parser->inputTab);
3171   parser->input=parser_context.input;
3172   parser->inputNr=parser_context.inputNr;
3173   parser->inputMax=parser_context.inputMax;
3174   parser->inputTab=parser_context.inputTab;
3175 }
3176 
3177 #if defined(__cplusplus) || defined(c_plusplus)
3178 }
3179 #endif
3180 
ReadSVGImage(const ImageInfo * image_info,ExceptionInfo * exception)3181 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3182 {
3183   char
3184     filename[MagickPathExtent];
3185 
3186   FILE
3187     *file;
3188 
3189   Image
3190     *image,
3191     *next;
3192 
3193   int
3194     status,
3195     unique_file;
3196 
3197   ssize_t
3198     n;
3199 
3200   SVGInfo
3201     *svg_info;
3202 
3203   unsigned char
3204     message[MagickPathExtent];
3205 
3206   xmlSAXHandler
3207     sax_modules;
3208 
3209   xmlSAXHandlerPtr
3210     sax_handler;
3211 
3212   /*
3213     Open image file.
3214   */
3215   assert(image_info != (const ImageInfo *) NULL);
3216   assert(image_info->signature == MagickCoreSignature);
3217   assert(exception != (ExceptionInfo *) NULL);
3218   if (image_info->debug != MagickFalse)
3219     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3220       image_info->filename);
3221   assert(exception->signature == MagickCoreSignature);
3222   image=AcquireImage(image_info,exception);
3223   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3224   if (status == MagickFalse)
3225     {
3226       image=DestroyImageList(image);
3227       return((Image *) NULL);
3228     }
3229   if ((fabs(image->resolution.x) < MagickEpsilon) ||
3230       (fabs(image->resolution.y) < MagickEpsilon))
3231     {
3232       GeometryInfo
3233         geometry_info;
3234 
3235       int
3236         flags;
3237 
3238       flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3239       image->resolution.x=geometry_info.rho;
3240       image->resolution.y=geometry_info.sigma;
3241       if ((flags & SigmaValue) == 0)
3242         image->resolution.y=image->resolution.x;
3243     }
3244   if (LocaleCompare(image_info->magick,"MSVG") != 0)
3245     {
3246       Image
3247         *svg_image;
3248 
3249       svg_image=RenderSVGImage(image_info,image,exception);
3250       if (svg_image != (Image *) NULL)
3251         {
3252           image=DestroyImageList(image);
3253           return(svg_image);
3254         }
3255       {
3256 #if defined(MAGICKCORE_RSVG_DELEGATE)
3257 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3258         cairo_surface_t
3259           *cairo_surface;
3260 
3261         cairo_t
3262           *cairo_image;
3263 
3264         MagickBooleanType
3265           apply_density;
3266 
3267         MemoryInfo
3268           *pixel_info;
3269 
3270         register unsigned char
3271           *p;
3272 
3273         RsvgDimensionData
3274           dimension_info;
3275 
3276         unsigned char
3277           *pixels;
3278 
3279 #else
3280         GdkPixbuf
3281           *pixel_buffer;
3282 
3283         register const guchar
3284           *p;
3285 #endif
3286 
3287         GError
3288           *error;
3289 
3290         PixelInfo
3291           fill_color;
3292 
3293         register ssize_t
3294           x;
3295 
3296         register Quantum
3297           *q;
3298 
3299         RsvgHandle
3300           *svg_handle;
3301 
3302         ssize_t
3303           y;
3304 
3305         svg_handle=rsvg_handle_new();
3306         if (svg_handle == (RsvgHandle *) NULL)
3307           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3308         rsvg_handle_set_base_uri(svg_handle,image_info->filename);
3309         if ((fabs(image->resolution.x) > MagickEpsilon) &&
3310             (fabs(image->resolution.y) > MagickEpsilon))
3311           rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3312             image->resolution.y);
3313         while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3314         {
3315           message[n]='\0';
3316           error=(GError *) NULL;
3317           (void) rsvg_handle_write(svg_handle,message,n,&error);
3318           if (error != (GError *) NULL)
3319             g_error_free(error);
3320         }
3321         error=(GError *) NULL;
3322         rsvg_handle_close(svg_handle,&error);
3323         if (error != (GError *) NULL)
3324           g_error_free(error);
3325 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3326         apply_density=MagickTrue;
3327         rsvg_handle_get_dimensions(svg_handle,&dimension_info);
3328         if ((image->resolution.x > 0.0) && (image->resolution.y > 0.0))
3329           {
3330             RsvgDimensionData
3331               dpi_dimension_info;
3332 
3333             /*
3334               We should not apply the density when the internal 'factor' is 'i'.
3335               This can be checked by using the trick below.
3336             */
3337             rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x*256,
3338               image->resolution.y*256);
3339             rsvg_handle_get_dimensions(svg_handle,&dpi_dimension_info);
3340             if ((dpi_dimension_info.width != dimension_info.width) ||
3341                 (dpi_dimension_info.height != dimension_info.height))
3342               apply_density=MagickFalse;
3343             rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3344               image->resolution.y);
3345           }
3346         if (image_info->size != (char *) NULL)
3347           {
3348             (void) GetGeometry(image_info->size,(ssize_t *) NULL,
3349               (ssize_t *) NULL,&image->columns,&image->rows);
3350             if ((image->columns != 0) || (image->rows != 0))
3351               {
3352                 image->resolution.x=DefaultSVGDensity*image->columns/
3353                   dimension_info.width;
3354                 image->resolution.y=DefaultSVGDensity*image->rows/
3355                   dimension_info.height;
3356                 if (fabs(image->resolution.x) < MagickEpsilon)
3357                   image->resolution.x=image->resolution.y;
3358                 else
3359                   if (fabs(image->resolution.y) < MagickEpsilon)
3360                     image->resolution.y=image->resolution.x;
3361                   else
3362                     image->resolution.x=image->resolution.y=MagickMin(
3363                       image->resolution.x,image->resolution.y);
3364                 apply_density=MagickTrue;
3365               }
3366           }
3367         if (apply_density != MagickFalse)
3368           {
3369             image->columns=image->resolution.x*dimension_info.width/
3370               DefaultSVGDensity;
3371             image->rows=image->resolution.y*dimension_info.height/
3372               DefaultSVGDensity;
3373           }
3374         else
3375           {
3376             image->columns=dimension_info.width;
3377             image->rows=dimension_info.height;
3378           }
3379         pixel_info=(MemoryInfo *) NULL;
3380 #else
3381         pixel_buffer=rsvg_handle_get_pixbuf(svg_handle);
3382         rsvg_handle_free(svg_handle);
3383         image->columns=gdk_pixbuf_get_width(pixel_buffer);
3384         image->rows=gdk_pixbuf_get_height(pixel_buffer);
3385 #endif
3386         image->alpha_trait=BlendPixelTrait;
3387         if (image_info->ping == MagickFalse)
3388           {
3389 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3390             size_t
3391               stride;
3392 #endif
3393 
3394             status=SetImageExtent(image,image->columns,image->rows,exception);
3395             if (status == MagickFalse)
3396               {
3397 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
3398                 g_object_unref(G_OBJECT(pixel_buffer));
3399 #endif
3400                 g_object_unref(svg_handle);
3401                 ThrowReaderException(MissingDelegateError,
3402                   "NoDecodeDelegateForThisImageFormat");
3403               }
3404 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3405             stride=4*image->columns;
3406 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
3407             stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
3408               (int) image->columns);
3409 #endif
3410             pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels));
3411             if (pixel_info == (MemoryInfo *) NULL)
3412               {
3413                 g_object_unref(svg_handle);
3414                 ThrowReaderException(ResourceLimitError,
3415                   "MemoryAllocationFailed");
3416               }
3417             pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3418 #endif
3419             (void) SetImageBackgroundColor(image,exception);
3420 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3421             cairo_surface=cairo_image_surface_create_for_data(pixels,
3422               CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int)
3423               stride);
3424             if ((cairo_surface == (cairo_surface_t *) NULL) ||
3425                 (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS))
3426               {
3427                 if (cairo_surface != (cairo_surface_t *) NULL)
3428                   cairo_surface_destroy(cairo_surface);
3429                 pixel_info=RelinquishVirtualMemory(pixel_info);
3430                 g_object_unref(svg_handle);
3431                 ThrowReaderException(ResourceLimitError,
3432                   "MemoryAllocationFailed");
3433               }
3434             cairo_image=cairo_create(cairo_surface);
3435             cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
3436             cairo_paint(cairo_image);
3437             cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
3438             if (apply_density != MagickFalse)
3439               cairo_scale(cairo_image,image->resolution.x/DefaultSVGDensity,
3440                 image->resolution.y/DefaultSVGDensity);
3441             rsvg_handle_render_cairo(svg_handle,cairo_image);
3442             cairo_destroy(cairo_image);
3443             cairo_surface_destroy(cairo_surface);
3444             g_object_unref(svg_handle);
3445             p=pixels;
3446 #else
3447             p=gdk_pixbuf_get_pixels(pixel_buffer);
3448 #endif
3449             GetPixelInfo(image,&fill_color);
3450             for (y=0; y < (ssize_t) image->rows; y++)
3451             {
3452               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3453               if (q == (Quantum *) NULL)
3454                 break;
3455               for (x=0; x < (ssize_t) image->columns; x++)
3456               {
3457 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3458                 fill_color.blue=ScaleCharToQuantum(*p++);
3459                 fill_color.green=ScaleCharToQuantum(*p++);
3460                 fill_color.red=ScaleCharToQuantum(*p++);
3461 #else
3462                 fill_color.red=ScaleCharToQuantum(*p++);
3463                 fill_color.green=ScaleCharToQuantum(*p++);
3464                 fill_color.blue=ScaleCharToQuantum(*p++);
3465 #endif
3466                 fill_color.alpha=ScaleCharToQuantum(*p++);
3467 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3468                 {
3469                   double
3470                     gamma;
3471 
3472                   gamma=QuantumScale*fill_color.alpha;
3473                   gamma=PerceptibleReciprocal(gamma);
3474                   fill_color.blue*=gamma;
3475                   fill_color.green*=gamma;
3476                   fill_color.red*=gamma;
3477                 }
3478 #endif
3479                 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
3480                   GetPixelAlpha(image,q),q);
3481                 q+=GetPixelChannels(image);
3482               }
3483               if (SyncAuthenticPixels(image,exception) == MagickFalse)
3484                 break;
3485               if (image->previous == (Image *) NULL)
3486                 {
3487                   status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
3488                     y,image->rows);
3489                   if (status == MagickFalse)
3490                     break;
3491                 }
3492             }
3493           }
3494 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3495         if (pixel_info != (MemoryInfo *) NULL)
3496           pixel_info=RelinquishVirtualMemory(pixel_info);
3497 #else
3498         g_object_unref(G_OBJECT(pixel_buffer));
3499 #endif
3500         (void) CloseBlob(image);
3501         for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3502         {
3503           (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3504           (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3505           next=GetNextImageInList(next);
3506         }
3507         return(GetFirstImageInList(image));
3508 #endif
3509       }
3510     }
3511   /*
3512     Open draw file.
3513   */
3514   file=(FILE *) NULL;
3515   unique_file=AcquireUniqueFileResource(filename);
3516   if (unique_file != -1)
3517     file=fdopen(unique_file,"w");
3518   if ((unique_file == -1) || (file == (FILE *) NULL))
3519     {
3520       (void) CopyMagickString(image->filename,filename,MagickPathExtent);
3521       ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
3522         image->filename);
3523       image=DestroyImageList(image);
3524       return((Image *) NULL);
3525     }
3526   /*
3527     Parse SVG file.
3528   */
3529   svg_info=AcquireSVGInfo();
3530   if (svg_info == (SVGInfo *) NULL)
3531     {
3532       (void) fclose(file);
3533       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3534     }
3535   svg_info->file=file;
3536   svg_info->exception=exception;
3537   svg_info->image=image;
3538   svg_info->image_info=image_info;
3539   svg_info->bounds.width=image->columns;
3540   svg_info->bounds.height=image->rows;
3541   svg_info->svgDepth=0;
3542   if (image_info->size != (char *) NULL)
3543     (void) CloneString(&svg_info->size,image_info->size);
3544   if (image->debug != MagickFalse)
3545     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3546   xmlInitParser();
3547   (void) xmlSubstituteEntitiesDefault(1);
3548   (void) memset(&sax_modules,0,sizeof(sax_modules));
3549   sax_modules.internalSubset=SVGInternalSubset;
3550   sax_modules.isStandalone=SVGIsStandalone;
3551   sax_modules.hasInternalSubset=SVGHasInternalSubset;
3552   sax_modules.hasExternalSubset=SVGHasExternalSubset;
3553   sax_modules.resolveEntity=SVGResolveEntity;
3554   sax_modules.getEntity=SVGGetEntity;
3555   sax_modules.entityDecl=SVGEntityDeclaration;
3556   sax_modules.notationDecl=SVGNotationDeclaration;
3557   sax_modules.attributeDecl=SVGAttributeDeclaration;
3558   sax_modules.elementDecl=SVGElementDeclaration;
3559   sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3560   sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3561   sax_modules.startDocument=SVGStartDocument;
3562   sax_modules.endDocument=SVGEndDocument;
3563   sax_modules.startElement=SVGStartElement;
3564   sax_modules.endElement=SVGEndElement;
3565   sax_modules.reference=SVGReference;
3566   sax_modules.characters=SVGCharacters;
3567   sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3568   sax_modules.processingInstruction=SVGProcessingInstructions;
3569   sax_modules.comment=SVGComment;
3570   sax_modules.warning=SVGWarning;
3571   sax_modules.error=SVGError;
3572   sax_modules.fatalError=SVGError;
3573   sax_modules.getParameterEntity=SVGGetParameterEntity;
3574   sax_modules.cdataBlock=SVGCDataBlock;
3575   sax_modules.externalSubset=SVGExternalSubset;
3576   sax_handler=(&sax_modules);
3577   n=ReadBlob(image,MagickPathExtent-1,message);
3578   message[n]='\0';
3579   if (n > 0)
3580     {
3581       const char
3582         *value;
3583 
3584       svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3585         message,n,image->filename);
3586       value=GetImageOption(image_info,"svg:xml-parse-huge");
3587       if ((value != (char *) NULL) && (IsStringTrue(value) != MagickFalse))
3588         (void) xmlCtxtUseOptions(svg_info->parser,XML_PARSE_HUGE);
3589       while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3590       {
3591         message[n]='\0';
3592         status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3593         if (status != 0)
3594           break;
3595       }
3596     }
3597   (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3598   SVGEndDocument(svg_info);
3599   if (svg_info->parser->myDoc != (xmlDocPtr) NULL)
3600     xmlFreeDoc(svg_info->parser->myDoc);
3601   xmlFreeParserCtxt(svg_info->parser);
3602   if (image->debug != MagickFalse)
3603     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3604   (void) fclose(file);
3605   (void) CloseBlob(image);
3606   image->columns=svg_info->width;
3607   image->rows=svg_info->height;
3608   if (exception->severity >= ErrorException)
3609     {
3610       svg_info=DestroySVGInfo(svg_info);
3611       (void) RelinquishUniqueFileResource(filename);
3612       image=DestroyImage(image);
3613       return((Image *) NULL);
3614     }
3615   if (image_info->ping == MagickFalse)
3616     {
3617       ImageInfo
3618         *read_info;
3619 
3620       /*
3621         Draw image.
3622       */
3623       image=DestroyImage(image);
3624       image=(Image *) NULL;
3625       read_info=CloneImageInfo(image_info);
3626       SetImageInfoBlob(read_info,(void *) NULL,0);
3627       (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
3628         filename);
3629       image=ReadImage(read_info,exception);
3630       read_info=DestroyImageInfo(read_info);
3631       if (image != (Image *) NULL)
3632         (void) CopyMagickString(image->filename,image_info->filename,
3633           MagickPathExtent);
3634     }
3635   /*
3636     Relinquish resources.
3637   */
3638   if (image != (Image *) NULL)
3639     {
3640       if (svg_info->title != (char *) NULL)
3641         (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3642       if (svg_info->comment != (char *) NULL)
3643         (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3644           exception);
3645     }
3646   for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3647   {
3648     (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3649     (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3650     next=GetNextImageInList(next);
3651   }
3652   svg_info=DestroySVGInfo(svg_info);
3653   (void) RelinquishUniqueFileResource(filename);
3654   return(GetFirstImageInList(image));
3655 }
3656 #else
ReadSVGImage(const ImageInfo * image_info,ExceptionInfo * exception)3657 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3658 {
3659   Image
3660     *image,
3661     *svg_image;
3662 
3663   MagickBooleanType
3664     status;
3665 
3666   assert(image_info != (const ImageInfo *) NULL);
3667   assert(image_info->signature == MagickCoreSignature);
3668   assert(exception != (ExceptionInfo *) NULL);
3669   if (image_info->debug != MagickFalse)
3670     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3671       image_info->filename);
3672   assert(exception->signature == MagickCoreSignature);
3673   image=AcquireImage(image_info,exception);
3674   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3675   if (status == MagickFalse)
3676     {
3677       image=DestroyImageList(image);
3678       return((Image *) NULL);
3679     }
3680   if ((fabs(image->resolution.x) < MagickEpsilon) ||
3681       (fabs(image->resolution.y) < MagickEpsilon))
3682     {
3683       GeometryInfo
3684         geometry_info;
3685 
3686       MagickStatusType
3687         flags;
3688 
3689       flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3690       image->resolution.x=geometry_info.rho;
3691       image->resolution.y=geometry_info.sigma;
3692       if ((flags & SigmaValue) == 0)
3693         image->resolution.y=image->resolution.x;
3694     }
3695   svg_image=RenderSVGImage(image_info,image,exception);
3696   image=DestroyImage(image);
3697   return(svg_image);
3698 }
3699 #endif
3700 
3701 /*
3702 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3703 %                                                                             %
3704 %                                                                             %
3705 %                                                                             %
3706 %   R e g i s t e r S V G I m a g e                                           %
3707 %                                                                             %
3708 %                                                                             %
3709 %                                                                             %
3710 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3711 %
3712 %  RegisterSVGImage() adds attributes for the SVG image format to
3713 %  the list of supported formats.  The attributes include the image format
3714 %  tag, a method to read and/or write the format, whether the format
3715 %  supports the saving of more than one frame to the same file or blob,
3716 %  whether the format supports native in-memory I/O, and a brief
3717 %  description of the format.
3718 %
3719 %  The format of the RegisterSVGImage method is:
3720 %
3721 %      size_t RegisterSVGImage(void)
3722 %
3723 */
RegisterSVGImage(void)3724 ModuleExport size_t RegisterSVGImage(void)
3725 {
3726   char
3727     version[MagickPathExtent];
3728 
3729   MagickInfo
3730     *entry;
3731 
3732   *version='\0';
3733 #if defined(LIBXML_DOTTED_VERSION)
3734   (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,
3735     MagickPathExtent);
3736 #endif
3737 #if defined(MAGICKCORE_RSVG_DELEGATE)
3738 #if !GLIB_CHECK_VERSION(2,35,0)
3739   g_type_init();
3740 #endif
3741   (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d",
3742     LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3743 #endif
3744   entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics");
3745   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3746   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3747   entry->flags^=CoderBlobSupportFlag;
3748 #if defined(MAGICKCORE_RSVG_DELEGATE)
3749   entry->flags^=CoderDecoderThreadSupportFlag;
3750 #endif
3751   entry->mime_type=ConstantString("image/svg+xml");
3752   if (*version != '\0')
3753     entry->version=ConstantString(version);
3754   entry->magick=(IsImageFormatHandler *) IsSVG;
3755   (void) RegisterMagickInfo(entry);
3756   entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics");
3757 #if defined(MAGICKCORE_XML_DELEGATE)
3758   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3759 #endif
3760   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3761   entry->flags^=CoderBlobSupportFlag;
3762 #if defined(MAGICKCORE_RSVG_DELEGATE)
3763   entry->flags^=CoderDecoderThreadSupportFlag;
3764 #endif
3765   entry->mime_type=ConstantString("image/svg+xml");
3766   if (*version != '\0')
3767     entry->version=ConstantString(version);
3768   entry->magick=(IsImageFormatHandler *) IsSVG;
3769   (void) RegisterMagickInfo(entry);
3770   entry=AcquireMagickInfo("SVG","MSVG",
3771     "ImageMagick's own SVG internal renderer");
3772 #if defined(MAGICKCORE_XML_DELEGATE)
3773   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3774 #endif
3775   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3776   entry->flags^=CoderBlobSupportFlag;
3777 #if defined(MAGICKCORE_RSVG_DELEGATE)
3778   entry->flags^=CoderDecoderThreadSupportFlag;
3779 #endif
3780   entry->magick=(IsImageFormatHandler *) IsSVG;
3781   (void) RegisterMagickInfo(entry);
3782   return(MagickImageCoderSignature);
3783 }
3784 
3785 /*
3786 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3787 %                                                                             %
3788 %                                                                             %
3789 %                                                                             %
3790 %   U n r e g i s t e r S V G I m a g e                                       %
3791 %                                                                             %
3792 %                                                                             %
3793 %                                                                             %
3794 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3795 %
3796 %  UnregisterSVGImage() removes format registrations made by the
3797 %  SVG module from the list of supported formats.
3798 %
3799 %  The format of the UnregisterSVGImage method is:
3800 %
3801 %      UnregisterSVGImage(void)
3802 %
3803 */
UnregisterSVGImage(void)3804 ModuleExport void UnregisterSVGImage(void)
3805 {
3806   (void) UnregisterMagickInfo("SVGZ");
3807   (void) UnregisterMagickInfo("SVG");
3808   (void) UnregisterMagickInfo("MSVG");
3809 }
3810 
3811 /*
3812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3813 %                                                                             %
3814 %                                                                             %
3815 %                                                                             %
3816 %   W r i t e S V G I m a g e                                                 %
3817 %                                                                             %
3818 %                                                                             %
3819 %                                                                             %
3820 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3821 %
3822 %  WriteSVGImage() writes a image in the SVG - XML based W3C standard
3823 %  format.
3824 %
3825 %  The format of the WriteSVGImage method is:
3826 %
3827 %      MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3828 %        Image *image,ExceptionInfo *exception)
3829 %
3830 %  A description of each parameter follows.
3831 %
3832 %    o image_info: the image info.
3833 %
3834 %    o image:  The image.
3835 %
3836 %    o exception: return any errors or warnings in this structure.
3837 %
3838 */
3839 
AffineToTransform(Image * image,AffineMatrix * affine)3840 static void AffineToTransform(Image *image,AffineMatrix *affine)
3841 {
3842   char
3843     transform[MagickPathExtent];
3844 
3845   if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3846     {
3847       if ((fabs(affine->rx) < MagickEpsilon) &&
3848           (fabs(affine->ry) < MagickEpsilon))
3849         {
3850           if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3851               (fabs(affine->sy-1.0) < MagickEpsilon))
3852             {
3853               (void) WriteBlobString(image,"\">\n");
3854               return;
3855             }
3856           (void) FormatLocaleString(transform,MagickPathExtent,
3857             "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3858           (void) WriteBlobString(image,transform);
3859           return;
3860         }
3861       else
3862         {
3863           if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3864               (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3865               (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3866                2*MagickEpsilon))
3867             {
3868               double
3869                 theta;
3870 
3871               theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3872               (void) FormatLocaleString(transform,MagickPathExtent,
3873                 "\" transform=\"rotate(%g)\">\n",theta);
3874               (void) WriteBlobString(image,transform);
3875               return;
3876             }
3877         }
3878     }
3879   else
3880     {
3881       if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3882           (fabs(affine->rx) < MagickEpsilon) &&
3883           (fabs(affine->ry) < MagickEpsilon) &&
3884           (fabs(affine->sy-1.0) < MagickEpsilon))
3885         {
3886           (void) FormatLocaleString(transform,MagickPathExtent,
3887             "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3888           (void) WriteBlobString(image,transform);
3889           return;
3890         }
3891     }
3892   (void) FormatLocaleString(transform,MagickPathExtent,
3893     "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3894     affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3895   (void) WriteBlobString(image,transform);
3896 }
3897 
IsPoint(const char * point)3898 static MagickBooleanType IsPoint(const char *point)
3899 {
3900   char
3901     *p;
3902 
3903   ssize_t
3904     value;
3905 
3906   value=(ssize_t) strtol(point,&p,10);
3907   (void) value;
3908   return(p != point ? MagickTrue : MagickFalse);
3909 }
3910 
TraceSVGImage(Image * image,ExceptionInfo * exception)3911 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
3912 {
3913 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3914   {
3915     at_bitmap_type
3916       *trace;
3917 
3918     at_fitting_opts_type
3919       *fitting_options;
3920 
3921     at_output_opts_type
3922       *output_options;
3923 
3924     at_splines_type
3925       *splines;
3926 
3927     ImageType
3928       type;
3929 
3930     register const Quantum
3931       *p;
3932 
3933     register ssize_t
3934       i,
3935       x;
3936 
3937     size_t
3938       number_planes;
3939 
3940     ssize_t
3941       y;
3942 
3943     /*
3944       Trace image and write as SVG.
3945     */
3946     fitting_options=at_fitting_opts_new();
3947     output_options=at_output_opts_new();
3948     (void) SetImageGray(image,exception);
3949     type=GetImageType(image);
3950     number_planes=3;
3951     if ((type == BilevelType) || (type == GrayscaleType))
3952       number_planes=1;
3953     trace=at_bitmap_new(image->columns,image->rows,number_planes);
3954     i=0;
3955     for (y=0; y < (ssize_t) image->rows; y++)
3956     {
3957       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3958       if (p == (const Quantum *) NULL)
3959         break;
3960       for (x=0; x < (ssize_t) image->columns; x++)
3961       {
3962         trace->bitmap[i++]=GetPixelRed(image,p);
3963         if (number_planes == 3)
3964           {
3965             trace->bitmap[i++]=GetPixelGreen(image,p);
3966             trace->bitmap[i++]=GetPixelBlue(image,p);
3967           }
3968         p+=GetPixelChannels(image);
3969       }
3970     }
3971     splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3972       NULL);
3973     at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3974       GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3975       NULL);
3976     /*
3977       Free resources.
3978     */
3979     at_splines_free(splines);
3980     at_bitmap_free(trace);
3981     at_output_opts_free(output_options);
3982     at_fitting_opts_free(fitting_options);
3983   }
3984 #else
3985   {
3986     char
3987       *base64,
3988       filename[MagickPathExtent],
3989       message[MagickPathExtent];
3990 
3991     const DelegateInfo
3992       *delegate_info;
3993 
3994     Image
3995       *clone_image;
3996 
3997     ImageInfo
3998       *image_info;
3999 
4000     MagickBooleanType
4001       status;
4002 
4003     register char
4004       *p;
4005 
4006     size_t
4007       blob_length,
4008       encode_length;
4009 
4010     ssize_t
4011       i;
4012 
4013     unsigned char
4014       *blob;
4015 
4016     delegate_info=GetDelegateInfo((char *) NULL,"TRACE",exception);
4017     if (delegate_info != (DelegateInfo *) NULL)
4018       {
4019         /*
4020           Trace SVG with tracing delegate.
4021         */
4022         image_info=AcquireImageInfo();
4023         (void) CopyMagickString(image_info->magick,"TRACE",MagickPathExtent);
4024         (void) FormatLocaleString(filename,MagickPathExtent,"trace:%s",
4025           image_info->filename);
4026         (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
4027         status=WriteImage(image_info,image,exception);
4028         image_info=DestroyImageInfo(image_info);
4029         return(status);
4030       }
4031     (void) WriteBlobString(image,
4032       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
4033     (void) WriteBlobString(image,
4034       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
4035     (void) WriteBlobString(image,
4036       " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
4037     (void) FormatLocaleString(message,MagickPathExtent,
4038       "<svg version=\"1.1\" id=\"Layer_1\" "
4039       "xmlns=\"http://www.w3.org/2000/svg\" "
4040       "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" "
4041       "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" "
4042       "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">",
4043       (double) image->columns,(double) image->rows,
4044       (double) image->columns,(double) image->rows,
4045       (double) image->columns,(double) image->rows);
4046     (void) WriteBlobString(image,message);
4047     clone_image=CloneImage(image,0,0,MagickTrue,exception);
4048     if (clone_image == (Image *) NULL)
4049       return(MagickFalse);
4050     image_info=AcquireImageInfo();
4051     (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent);
4052     blob_length=2048;
4053     blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length,
4054       exception);
4055     clone_image=DestroyImage(clone_image);
4056     image_info=DestroyImageInfo(image_info);
4057     if (blob == (unsigned char *) NULL)
4058       return(MagickFalse);
4059     encode_length=0;
4060     base64=Base64Encode(blob,blob_length,&encode_length);
4061     blob=(unsigned char *) RelinquishMagickMemory(blob);
4062     (void) FormatLocaleString(message,MagickPathExtent,
4063       "  <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" "
4064       "x=\"%.20g\" y=\"%.20g\"\n    href=\"data:image/png;base64,",
4065       (double) image->scene,(double) image->columns,(double) image->rows,
4066       (double) image->page.x,(double) image->page.y);
4067     (void) WriteBlobString(image,message);
4068     p=base64;
4069     for (i=(ssize_t) encode_length; i > 0; i-=76)
4070     {
4071       (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p);
4072       (void) WriteBlobString(image,message);
4073       p+=76;
4074       if (i > 76)
4075         (void) WriteBlobString(image,"\n");
4076     }
4077     base64=DestroyString(base64);
4078     (void) WriteBlobString(image,"\" />\n");
4079     (void) WriteBlobString(image,"</svg>\n");
4080   }
4081 #endif
4082   (void) CloseBlob(image);
4083   return(MagickTrue);
4084 }
4085 
WriteSVGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)4086 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
4087   ExceptionInfo *exception)
4088 {
4089 #define BezierQuantum  200
4090 
4091   AffineMatrix
4092     affine;
4093 
4094   char
4095     keyword[MagickPathExtent],
4096     message[MagickPathExtent],
4097     name[MagickPathExtent],
4098     *next_token,
4099     *token,
4100     type[MagickPathExtent];
4101 
4102   const char
4103     *p,
4104     *q,
4105     *value;
4106 
4107   int
4108     n;
4109 
4110   ssize_t
4111     j;
4112 
4113   MagickBooleanType
4114     active,
4115     status;
4116 
4117   PointInfo
4118     point;
4119 
4120   PrimitiveInfo
4121     *primitive_info;
4122 
4123   PrimitiveType
4124     primitive_type;
4125 
4126   register ssize_t
4127     x;
4128 
4129   register ssize_t
4130     i;
4131 
4132   size_t
4133     extent,
4134     length,
4135     number_points;
4136 
4137   SVGInfo
4138     svg_info;
4139 
4140   /*
4141     Open output image file.
4142   */
4143   assert(image_info != (const ImageInfo *) NULL);
4144   assert(image_info->signature == MagickCoreSignature);
4145   assert(image != (Image *) NULL);
4146   assert(image->signature == MagickCoreSignature);
4147   if (image->debug != MagickFalse)
4148     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4149   assert(exception != (ExceptionInfo *) NULL);
4150   assert(exception->signature == MagickCoreSignature);
4151   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
4152   if (status == MagickFalse)
4153     return(status);
4154   value=GetImageArtifact(image,"SVG");
4155   if (value != (char *) NULL)
4156     {
4157       (void) WriteBlobString(image,value);
4158       (void) CloseBlob(image);
4159       return(MagickTrue);
4160     }
4161   value=GetImageArtifact(image,"mvg:vector-graphics");
4162   if (value == (char *) NULL)
4163     return(TraceSVGImage(image,exception));
4164   /*
4165     Write SVG header.
4166   */
4167   (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
4168   (void) WriteBlobString(image,
4169     "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
4170   (void) WriteBlobString(image,
4171     "  \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
4172   (void) FormatLocaleString(message,MagickPathExtent,
4173     "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
4174     image->rows);
4175   (void) WriteBlobString(image,message);
4176   /*
4177     Allocate primitive info memory.
4178   */
4179   number_points=2047;
4180   primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
4181     sizeof(*primitive_info));
4182   if (primitive_info == (PrimitiveInfo *) NULL)
4183     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
4184   GetAffineMatrix(&affine);
4185   token=AcquireString(value);
4186   extent=strlen(token)+MagickPathExtent;
4187   active=MagickFalse;
4188   n=0;
4189   status=MagickTrue;
4190   for (q=(const char *) value; *q != '\0'; )
4191   {
4192     /*
4193       Interpret graphic primitive.
4194     */
4195     (void) GetNextToken(q,&q,MagickPathExtent,keyword);
4196     if (*keyword == '\0')
4197       break;
4198     if (*keyword == '#')
4199       {
4200         /*
4201           Comment.
4202         */
4203         if (active != MagickFalse)
4204           {
4205             AffineToTransform(image,&affine);
4206             active=MagickFalse;
4207           }
4208         (void) WriteBlobString(image,"<desc>");
4209         (void) WriteBlobString(image,keyword+1);
4210         for ( ; (*q != '\n') && (*q != '\0'); q++)
4211           switch (*q)
4212           {
4213             case '<': (void) WriteBlobString(image,"&lt;"); break;
4214             case '>': (void) WriteBlobString(image,"&gt;"); break;
4215             case '&': (void) WriteBlobString(image,"&amp;"); break;
4216             default: (void) WriteBlobByte(image,(unsigned char) *q); break;
4217           }
4218         (void) WriteBlobString(image,"</desc>\n");
4219         continue;
4220       }
4221     primitive_type=UndefinedPrimitive;
4222     switch (*keyword)
4223     {
4224       case ';':
4225         break;
4226       case 'a':
4227       case 'A':
4228       {
4229         if (LocaleCompare("affine",keyword) == 0)
4230           {
4231             (void) GetNextToken(q,&q,extent,token);
4232             affine.sx=StringToDouble(token,&next_token);
4233             (void) GetNextToken(q,&q,extent,token);
4234             if (*token == ',')
4235               (void) GetNextToken(q,&q,extent,token);
4236             affine.rx=StringToDouble(token,&next_token);
4237             (void) GetNextToken(q,&q,extent,token);
4238             if (*token == ',')
4239               (void) GetNextToken(q,&q,extent,token);
4240             affine.ry=StringToDouble(token,&next_token);
4241             (void) GetNextToken(q,&q,extent,token);
4242             if (*token == ',')
4243               (void) GetNextToken(q,&q,extent,token);
4244             affine.sy=StringToDouble(token,&next_token);
4245             (void) GetNextToken(q,&q,extent,token);
4246             if (*token == ',')
4247               (void) GetNextToken(q,&q,extent,token);
4248             affine.tx=StringToDouble(token,&next_token);
4249             (void) GetNextToken(q,&q,extent,token);
4250             if (*token == ',')
4251               (void) GetNextToken(q,&q,extent,token);
4252             affine.ty=StringToDouble(token,&next_token);
4253             break;
4254           }
4255         if (LocaleCompare("alpha",keyword) == 0)
4256           {
4257             primitive_type=AlphaPrimitive;
4258             break;
4259           }
4260         if (LocaleCompare("angle",keyword) == 0)
4261           {
4262             (void) GetNextToken(q,&q,extent,token);
4263             affine.rx=StringToDouble(token,&next_token);
4264             affine.ry=StringToDouble(token,&next_token);
4265             break;
4266           }
4267         if (LocaleCompare("arc",keyword) == 0)
4268           {
4269             primitive_type=ArcPrimitive;
4270             break;
4271           }
4272         status=MagickFalse;
4273         break;
4274       }
4275       case 'b':
4276       case 'B':
4277       {
4278         if (LocaleCompare("bezier",keyword) == 0)
4279           {
4280             primitive_type=BezierPrimitive;
4281             break;
4282           }
4283         status=MagickFalse;
4284         break;
4285       }
4286       case 'c':
4287       case 'C':
4288       {
4289         if (LocaleCompare("clip-path",keyword) == 0)
4290           {
4291             (void) GetNextToken(q,&q,extent,token);
4292             (void) FormatLocaleString(message,MagickPathExtent,
4293               "clip-path:url(#%s);",token);
4294             (void) WriteBlobString(image,message);
4295             break;
4296           }
4297         if (LocaleCompare("clip-rule",keyword) == 0)
4298           {
4299             (void) GetNextToken(q,&q,extent,token);
4300             (void) FormatLocaleString(message,MagickPathExtent,"clip-rule:%s;",
4301               token);
4302             (void) WriteBlobString(image,message);
4303             break;
4304           }
4305         if (LocaleCompare("clip-units",keyword) == 0)
4306           {
4307             (void) GetNextToken(q,&q,extent,token);
4308             (void) FormatLocaleString(message,MagickPathExtent,
4309               "clipPathUnits=%s;",token);
4310             (void) WriteBlobString(image,message);
4311             break;
4312           }
4313         if (LocaleCompare("circle",keyword) == 0)
4314           {
4315             primitive_type=CirclePrimitive;
4316             break;
4317           }
4318         if (LocaleCompare("color",keyword) == 0)
4319           {
4320             primitive_type=ColorPrimitive;
4321             break;
4322           }
4323         status=MagickFalse;
4324         break;
4325       }
4326       case 'd':
4327       case 'D':
4328       {
4329         if (LocaleCompare("decorate",keyword) == 0)
4330           {
4331             (void) GetNextToken(q,&q,extent,token);
4332             (void) FormatLocaleString(message,MagickPathExtent,
4333               "text-decoration:%s;",token);
4334             (void) WriteBlobString(image,message);
4335             break;
4336           }
4337         status=MagickFalse;
4338         break;
4339       }
4340       case 'e':
4341       case 'E':
4342       {
4343         if (LocaleCompare("ellipse",keyword) == 0)
4344           {
4345             primitive_type=EllipsePrimitive;
4346             break;
4347           }
4348         status=MagickFalse;
4349         break;
4350       }
4351       case 'f':
4352       case 'F':
4353       {
4354         if (LocaleCompare("fill",keyword) == 0)
4355           {
4356             (void) GetNextToken(q,&q,extent,token);
4357             (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;",
4358               token);
4359             (void) WriteBlobString(image,message);
4360             break;
4361           }
4362         if (LocaleCompare("fill-rule",keyword) == 0)
4363           {
4364             (void) GetNextToken(q,&q,extent,token);
4365             (void) FormatLocaleString(message,MagickPathExtent,
4366               "fill-rule:%s;",token);
4367             (void) WriteBlobString(image,message);
4368             break;
4369           }
4370         if (LocaleCompare("fill-opacity",keyword) == 0)
4371           {
4372             (void) GetNextToken(q,&q,extent,token);
4373             (void) FormatLocaleString(message,MagickPathExtent,
4374               "fill-opacity:%s;",token);
4375             (void) WriteBlobString(image,message);
4376             break;
4377           }
4378         if (LocaleCompare("font-family",keyword) == 0)
4379           {
4380             (void) GetNextToken(q,&q,extent,token);
4381             (void) FormatLocaleString(message,MagickPathExtent,
4382               "font-family:%s;",token);
4383             (void) WriteBlobString(image,message);
4384             break;
4385           }
4386         if (LocaleCompare("font-stretch",keyword) == 0)
4387           {
4388             (void) GetNextToken(q,&q,extent,token);
4389             (void) FormatLocaleString(message,MagickPathExtent,
4390               "font-stretch:%s;",token);
4391             (void) WriteBlobString(image,message);
4392             break;
4393           }
4394         if (LocaleCompare("font-style",keyword) == 0)
4395           {
4396             (void) GetNextToken(q,&q,extent,token);
4397             (void) FormatLocaleString(message,MagickPathExtent,
4398               "font-style:%s;",token);
4399             (void) WriteBlobString(image,message);
4400             break;
4401           }
4402         if (LocaleCompare("font-size",keyword) == 0)
4403           {
4404             (void) GetNextToken(q,&q,extent,token);
4405             (void) FormatLocaleString(message,MagickPathExtent,
4406               "font-size:%s;",token);
4407             (void) WriteBlobString(image,message);
4408             break;
4409           }
4410         if (LocaleCompare("font-weight",keyword) == 0)
4411           {
4412             (void) GetNextToken(q,&q,extent,token);
4413             (void) FormatLocaleString(message,MagickPathExtent,
4414               "font-weight:%s;",token);
4415             (void) WriteBlobString(image,message);
4416             break;
4417           }
4418         status=MagickFalse;
4419         break;
4420       }
4421       case 'g':
4422       case 'G':
4423       {
4424         if (LocaleCompare("gradient-units",keyword) == 0)
4425           {
4426             (void) GetNextToken(q,&q,extent,token);
4427             break;
4428           }
4429         if (LocaleCompare("text-align",keyword) == 0)
4430           {
4431             (void) GetNextToken(q,&q,extent,token);
4432             (void) FormatLocaleString(message,MagickPathExtent,
4433               "text-align %s ",token);
4434             (void) WriteBlobString(image,message);
4435             break;
4436           }
4437         if (LocaleCompare("text-anchor",keyword) == 0)
4438           {
4439             (void) GetNextToken(q,&q,extent,token);
4440             (void) FormatLocaleString(message,MagickPathExtent,
4441               "text-anchor %s ",token);
4442             (void) WriteBlobString(image,message);
4443             break;
4444           }
4445         status=MagickFalse;
4446         break;
4447       }
4448       case 'i':
4449       case 'I':
4450       {
4451         if (LocaleCompare("image",keyword) == 0)
4452           {
4453             (void) GetNextToken(q,&q,extent,token);
4454             primitive_type=ImagePrimitive;
4455             break;
4456           }
4457         status=MagickFalse;
4458         break;
4459       }
4460       case 'k':
4461       case 'K':
4462       {
4463         if (LocaleCompare("kerning",keyword) == 0)
4464           {
4465             (void) GetNextToken(q,&q,extent,token);
4466             (void) FormatLocaleString(message,MagickPathExtent,"kerning:%s;",
4467               token);
4468             (void) WriteBlobString(image,message);
4469           }
4470         break;
4471       }
4472       case 'l':
4473       case 'L':
4474       {
4475         if (LocaleCompare("letter-spacing",keyword) == 0)
4476           {
4477             (void) GetNextToken(q,&q,extent,token);
4478             (void) FormatLocaleString(message,MagickPathExtent,
4479               "letter-spacing:%s;",token);
4480             (void) WriteBlobString(image,message);
4481             break;
4482           }
4483         if (LocaleCompare("line",keyword) == 0)
4484           {
4485             primitive_type=LinePrimitive;
4486             break;
4487           }
4488         status=MagickFalse;
4489         break;
4490       }
4491       case 'o':
4492       case 'O':
4493       {
4494         if (LocaleCompare("opacity",keyword) == 0)
4495           {
4496             (void) GetNextToken(q,&q,extent,token);
4497             (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ",
4498               token);
4499             (void) WriteBlobString(image,message);
4500             break;
4501           }
4502         status=MagickFalse;
4503         break;
4504       }
4505       case 'p':
4506       case 'P':
4507       {
4508         if (LocaleCompare("path",keyword) == 0)
4509           {
4510             primitive_type=PathPrimitive;
4511             break;
4512           }
4513         if (LocaleCompare("point",keyword) == 0)
4514           {
4515             primitive_type=PointPrimitive;
4516             break;
4517           }
4518         if (LocaleCompare("polyline",keyword) == 0)
4519           {
4520             primitive_type=PolylinePrimitive;
4521             break;
4522           }
4523         if (LocaleCompare("polygon",keyword) == 0)
4524           {
4525             primitive_type=PolygonPrimitive;
4526             break;
4527           }
4528         if (LocaleCompare("pop",keyword) == 0)
4529           {
4530             (void) GetNextToken(q,&q,extent,token);
4531             if (LocaleCompare("clip-path",token) == 0)
4532               {
4533                 (void) WriteBlobString(image,"</clipPath>\n");
4534                 break;
4535               }
4536             if (LocaleCompare("defs",token) == 0)
4537               {
4538                 (void) WriteBlobString(image,"</defs>\n");
4539                 break;
4540               }
4541             if (LocaleCompare("gradient",token) == 0)
4542               {
4543                 (void) FormatLocaleString(message,MagickPathExtent,
4544                   "</%sGradient>\n",type);
4545                 (void) WriteBlobString(image,message);
4546                 break;
4547               }
4548             if (LocaleCompare("graphic-context",token) == 0)
4549               {
4550                 n--;
4551                 if (n < 0)
4552                   ThrowWriterException(DrawError,
4553                     "UnbalancedGraphicContextPushPop");
4554                 (void) WriteBlobString(image,"</g>\n");
4555               }
4556             if (LocaleCompare("pattern",token) == 0)
4557               {
4558                 (void) WriteBlobString(image,"</pattern>\n");
4559                 break;
4560               }
4561             if (LocaleCompare("symbol",token) == 0)
4562               {
4563                 (void) WriteBlobString(image,"</symbol>\n");
4564                 break;
4565               }
4566             if ((LocaleCompare("defs",token) == 0) ||
4567                 (LocaleCompare("symbol",token) == 0))
4568               (void) WriteBlobString(image,"</g>\n");
4569             break;
4570           }
4571         if (LocaleCompare("push",keyword) == 0)
4572           {
4573             (void) GetNextToken(q,&q,extent,token);
4574             if (LocaleCompare("clip-path",token) == 0)
4575               {
4576                 (void) GetNextToken(q,&q,extent,token);
4577                 (void) FormatLocaleString(message,MagickPathExtent,
4578                   "<clipPath id=\"%s\">\n",token);
4579                 (void) WriteBlobString(image,message);
4580                 break;
4581               }
4582             if (LocaleCompare("defs",token) == 0)
4583               {
4584                 (void) WriteBlobString(image,"<defs>\n");
4585                 break;
4586               }
4587             if (LocaleCompare("gradient",token) == 0)
4588               {
4589                 (void) GetNextToken(q,&q,extent,token);
4590                 (void) CopyMagickString(name,token,MagickPathExtent);
4591                 (void) GetNextToken(q,&q,extent,token);
4592                 (void) CopyMagickString(type,token,MagickPathExtent);
4593                 (void) GetNextToken(q,&q,extent,token);
4594                 svg_info.segment.x1=StringToDouble(token,&next_token);
4595                 svg_info.element.cx=StringToDouble(token,&next_token);
4596                 (void) GetNextToken(q,&q,extent,token);
4597                 if (*token == ',')
4598                   (void) GetNextToken(q,&q,extent,token);
4599                 svg_info.segment.y1=StringToDouble(token,&next_token);
4600                 svg_info.element.cy=StringToDouble(token,&next_token);
4601                 (void) GetNextToken(q,&q,extent,token);
4602                 if (*token == ',')
4603                   (void) GetNextToken(q,&q,extent,token);
4604                 svg_info.segment.x2=StringToDouble(token,&next_token);
4605                 svg_info.element.major=StringToDouble(token,
4606                   (char **) NULL);
4607                 (void) GetNextToken(q,&q,extent,token);
4608                 if (*token == ',')
4609                   (void) GetNextToken(q,&q,extent,token);
4610                 svg_info.segment.y2=StringToDouble(token,&next_token);
4611                 svg_info.element.minor=StringToDouble(token,
4612                   (char **) NULL);
4613                 (void) FormatLocaleString(message,MagickPathExtent,
4614                   "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
4615                   "y2=\"%g\">\n",type,name,svg_info.segment.x1,
4616                   svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
4617                 if (LocaleCompare(type,"radial") == 0)
4618                   {
4619                     (void) GetNextToken(q,&q,extent,token);
4620                     if (*token == ',')
4621                       (void) GetNextToken(q,&q,extent,token);
4622                     svg_info.element.angle=StringToDouble(token,
4623                       (char **) NULL);
4624                     (void) FormatLocaleString(message,MagickPathExtent,
4625                       "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
4626                       "fx=\"%g\" fy=\"%g\">\n",type,name,
4627                       svg_info.element.cx,svg_info.element.cy,
4628                       svg_info.element.angle,svg_info.element.major,
4629                       svg_info.element.minor);
4630                   }
4631                 (void) WriteBlobString(image,message);
4632                 break;
4633               }
4634             if (LocaleCompare("graphic-context",token) == 0)
4635               {
4636                 n++;
4637                 if (active)
4638                   {
4639                     AffineToTransform(image,&affine);
4640                     active=MagickFalse;
4641                   }
4642                 (void) WriteBlobString(image,"<g style=\"");
4643                 active=MagickTrue;
4644               }
4645             if (LocaleCompare("pattern",token) == 0)
4646               {
4647                 (void) GetNextToken(q,&q,extent,token);
4648                 (void) CopyMagickString(name,token,MagickPathExtent);
4649                 (void) GetNextToken(q,&q,extent,token);
4650                 svg_info.bounds.x=StringToDouble(token,&next_token);
4651                 (void) GetNextToken(q,&q,extent,token);
4652                 if (*token == ',')
4653                   (void) GetNextToken(q,&q,extent,token);
4654                 svg_info.bounds.y=StringToDouble(token,&next_token);
4655                 (void) GetNextToken(q,&q,extent,token);
4656                 if (*token == ',')
4657                   (void) GetNextToken(q,&q,extent,token);
4658                 svg_info.bounds.width=StringToDouble(token,
4659                   (char **) NULL);
4660                 (void) GetNextToken(q,&q,extent,token);
4661                 if (*token == ',')
4662                   (void) GetNextToken(q,&q,extent,token);
4663                 svg_info.bounds.height=StringToDouble(token,(char **) NULL);
4664                 (void) FormatLocaleString(message,MagickPathExtent,
4665                   "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
4666                   "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y,
4667                   svg_info.bounds.width,svg_info.bounds.height);
4668                 (void) WriteBlobString(image,message);
4669                 break;
4670               }
4671             if (LocaleCompare("symbol",token) == 0)
4672               {
4673                 (void) WriteBlobString(image,"<symbol>\n");
4674                 break;
4675               }
4676             break;
4677           }
4678         status=MagickFalse;
4679         break;
4680       }
4681       case 'r':
4682       case 'R':
4683       {
4684         if (LocaleCompare("rectangle",keyword) == 0)
4685           {
4686             primitive_type=RectanglePrimitive;
4687             break;
4688           }
4689         if (LocaleCompare("roundRectangle",keyword) == 0)
4690           {
4691             primitive_type=RoundRectanglePrimitive;
4692             break;
4693           }
4694         if (LocaleCompare("rotate",keyword) == 0)
4695           {
4696             (void) GetNextToken(q,&q,extent,token);
4697             (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ",
4698               token);
4699             (void) WriteBlobString(image,message);
4700             break;
4701           }
4702         status=MagickFalse;
4703         break;
4704       }
4705       case 's':
4706       case 'S':
4707       {
4708         if (LocaleCompare("scale",keyword) == 0)
4709           {
4710             (void) GetNextToken(q,&q,extent,token);
4711             affine.sx=StringToDouble(token,&next_token);
4712             (void) GetNextToken(q,&q,extent,token);
4713             if (*token == ',')
4714               (void) GetNextToken(q,&q,extent,token);
4715             affine.sy=StringToDouble(token,&next_token);
4716             break;
4717           }
4718         if (LocaleCompare("skewX",keyword) == 0)
4719           {
4720             (void) GetNextToken(q,&q,extent,token);
4721             (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ",
4722               token);
4723             (void) WriteBlobString(image,message);
4724             break;
4725           }
4726         if (LocaleCompare("skewY",keyword) == 0)
4727           {
4728             (void) GetNextToken(q,&q,extent,token);
4729             (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ",
4730               token);
4731             (void) WriteBlobString(image,message);
4732             break;
4733           }
4734         if (LocaleCompare("stop-color",keyword) == 0)
4735           {
4736             char
4737               color[MagickPathExtent];
4738 
4739             (void) GetNextToken(q,&q,extent,token);
4740             (void) CopyMagickString(color,token,MagickPathExtent);
4741             (void) GetNextToken(q,&q,extent,token);
4742             (void) FormatLocaleString(message,MagickPathExtent,
4743               "  <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4744             (void) WriteBlobString(image,message);
4745             break;
4746           }
4747         if (LocaleCompare("stroke",keyword) == 0)
4748           {
4749             (void) GetNextToken(q,&q,extent,token);
4750             (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;",
4751               token);
4752             (void) WriteBlobString(image,message);
4753             break;
4754           }
4755         if (LocaleCompare("stroke-antialias",keyword) == 0)
4756           {
4757             (void) GetNextToken(q,&q,extent,token);
4758             (void) FormatLocaleString(message,MagickPathExtent,
4759               "stroke-antialias:%s;",token);
4760             (void) WriteBlobString(image,message);
4761             break;
4762           }
4763         if (LocaleCompare("stroke-dasharray",keyword) == 0)
4764           {
4765             if (IsPoint(q))
4766               {
4767                 ssize_t
4768                   k;
4769 
4770                 p=q;
4771                 (void) GetNextToken(p,&p,extent,token);
4772                 for (k=0; IsPoint(token); k++)
4773                   (void) GetNextToken(p,&p,extent,token);
4774                 (void) WriteBlobString(image,"stroke-dasharray:");
4775                 for (j=0; j < k; j++)
4776                 {
4777                   (void) GetNextToken(q,&q,extent,token);
4778                   (void) FormatLocaleString(message,MagickPathExtent,"%s ",
4779                     token);
4780                   (void) WriteBlobString(image,message);
4781                 }
4782                 (void) WriteBlobString(image,";");
4783                 break;
4784               }
4785             (void) GetNextToken(q,&q,extent,token);
4786             (void) FormatLocaleString(message,MagickPathExtent,
4787               "stroke-dasharray:%s;",token);
4788             (void) WriteBlobString(image,message);
4789             break;
4790           }
4791         if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4792           {
4793             (void) GetNextToken(q,&q,extent,token);
4794             (void) FormatLocaleString(message,MagickPathExtent,
4795               "stroke-dashoffset:%s;",token);
4796             (void) WriteBlobString(image,message);
4797             break;
4798           }
4799         if (LocaleCompare("stroke-linecap",keyword) == 0)
4800           {
4801             (void) GetNextToken(q,&q,extent,token);
4802             (void) FormatLocaleString(message,MagickPathExtent,
4803               "stroke-linecap:%s;",token);
4804             (void) WriteBlobString(image,message);
4805             break;
4806           }
4807         if (LocaleCompare("stroke-linejoin",keyword) == 0)
4808           {
4809             (void) GetNextToken(q,&q,extent,token);
4810             (void) FormatLocaleString(message,MagickPathExtent,
4811               "stroke-linejoin:%s;",token);
4812             (void) WriteBlobString(image,message);
4813             break;
4814           }
4815         if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4816           {
4817             (void) GetNextToken(q,&q,extent,token);
4818             (void) FormatLocaleString(message,MagickPathExtent,
4819               "stroke-miterlimit:%s;",token);
4820             (void) WriteBlobString(image,message);
4821             break;
4822           }
4823         if (LocaleCompare("stroke-opacity",keyword) == 0)
4824           {
4825             (void) GetNextToken(q,&q,extent,token);
4826             (void) FormatLocaleString(message,MagickPathExtent,
4827               "stroke-opacity:%s;",token);
4828             (void) WriteBlobString(image,message);
4829             break;
4830           }
4831         if (LocaleCompare("stroke-width",keyword) == 0)
4832           {
4833             (void) GetNextToken(q,&q,extent,token);
4834             (void) FormatLocaleString(message,MagickPathExtent,
4835               "stroke-width:%s;",token);
4836             (void) WriteBlobString(image,message);
4837             continue;
4838           }
4839         status=MagickFalse;
4840         break;
4841       }
4842       case 't':
4843       case 'T':
4844       {
4845         if (LocaleCompare("text",keyword) == 0)
4846           {
4847             primitive_type=TextPrimitive;
4848             break;
4849           }
4850         if (LocaleCompare("text-antialias",keyword) == 0)
4851           {
4852             (void) GetNextToken(q,&q,extent,token);
4853             (void) FormatLocaleString(message,MagickPathExtent,
4854               "text-antialias:%s;",token);
4855             (void) WriteBlobString(image,message);
4856             break;
4857           }
4858         if (LocaleCompare("tspan",keyword) == 0)
4859           {
4860             primitive_type=TextPrimitive;
4861             break;
4862           }
4863         if (LocaleCompare("translate",keyword) == 0)
4864           {
4865             (void) GetNextToken(q,&q,extent,token);
4866             affine.tx=StringToDouble(token,&next_token);
4867             (void) GetNextToken(q,&q,extent,token);
4868             if (*token == ',')
4869               (void) GetNextToken(q,&q,extent,token);
4870             affine.ty=StringToDouble(token,&next_token);
4871             break;
4872           }
4873         status=MagickFalse;
4874         break;
4875       }
4876       case 'v':
4877       case 'V':
4878       {
4879         if (LocaleCompare("viewbox",keyword) == 0)
4880           {
4881             (void) GetNextToken(q,&q,extent,token);
4882             if (*token == ',')
4883               (void) GetNextToken(q,&q,extent,token);
4884             (void) GetNextToken(q,&q,extent,token);
4885             if (*token == ',')
4886               (void) GetNextToken(q,&q,extent,token);
4887             (void) GetNextToken(q,&q,extent,token);
4888             if (*token == ',')
4889               (void) GetNextToken(q,&q,extent,token);
4890             (void) GetNextToken(q,&q,extent,token);
4891             break;
4892           }
4893         status=MagickFalse;
4894         break;
4895       }
4896       default:
4897       {
4898         status=MagickFalse;
4899         break;
4900       }
4901     }
4902     if (status == MagickFalse)
4903       break;
4904     if (primitive_type == UndefinedPrimitive)
4905       continue;
4906     /*
4907       Parse the primitive attributes.
4908     */
4909     i=0;
4910     j=0;
4911     for (x=0; *q != '\0'; x++)
4912     {
4913       /*
4914         Define points.
4915       */
4916       if (IsPoint(q) == MagickFalse)
4917         break;
4918       (void) GetNextToken(q,&q,extent,token);
4919       point.x=StringToDouble(token,&next_token);
4920       (void) GetNextToken(q,&q,extent,token);
4921       if (*token == ',')
4922         (void) GetNextToken(q,&q,extent,token);
4923       point.y=StringToDouble(token,&next_token);
4924       (void) GetNextToken(q,(const char **) NULL,extent,token);
4925       if (*token == ',')
4926         (void) GetNextToken(q,&q,extent,token);
4927       primitive_info[i].primitive=primitive_type;
4928       primitive_info[i].point=point;
4929       primitive_info[i].coordinates=0;
4930       primitive_info[i].method=FloodfillMethod;
4931       i++;
4932       if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4933         continue;
4934       number_points+=6*BezierQuantum+360;
4935       primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4936         number_points,sizeof(*primitive_info));
4937       if (primitive_info == (PrimitiveInfo *) NULL)
4938         {
4939           (void) ThrowMagickException(exception,GetMagickModule(),
4940             ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4941           break;
4942         }
4943     }
4944     primitive_info[j].primitive=primitive_type;
4945     primitive_info[j].coordinates=(size_t) x;
4946     primitive_info[j].method=FloodfillMethod;
4947     primitive_info[j].text=(char *) NULL;
4948     if (active)
4949       {
4950         AffineToTransform(image,&affine);
4951         active=MagickFalse;
4952       }
4953     active=MagickFalse;
4954     switch (primitive_type)
4955     {
4956       case PointPrimitive:
4957       default:
4958       {
4959         if (primitive_info[j].coordinates != 1)
4960           {
4961             status=MagickFalse;
4962             break;
4963           }
4964         break;
4965       }
4966       case LinePrimitive:
4967       {
4968         if (primitive_info[j].coordinates != 2)
4969           {
4970             status=MagickFalse;
4971             break;
4972           }
4973           (void) FormatLocaleString(message,MagickPathExtent,
4974           "  <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4975           primitive_info[j].point.x,primitive_info[j].point.y,
4976           primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4977         (void) WriteBlobString(image,message);
4978         break;
4979       }
4980       case RectanglePrimitive:
4981       {
4982         if (primitive_info[j].coordinates != 2)
4983           {
4984             status=MagickFalse;
4985             break;
4986           }
4987           (void) FormatLocaleString(message,MagickPathExtent,
4988           "  <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4989           primitive_info[j].point.x,primitive_info[j].point.y,
4990           primitive_info[j+1].point.x-primitive_info[j].point.x,
4991           primitive_info[j+1].point.y-primitive_info[j].point.y);
4992         (void) WriteBlobString(image,message);
4993         break;
4994       }
4995       case RoundRectanglePrimitive:
4996       {
4997         if (primitive_info[j].coordinates != 3)
4998           {
4999             status=MagickFalse;
5000             break;
5001           }
5002         (void) FormatLocaleString(message,MagickPathExtent,
5003           "  <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
5004           "ry=\"%g\"/>\n",primitive_info[j].point.x,
5005           primitive_info[j].point.y,primitive_info[j+1].point.x-
5006           primitive_info[j].point.x,primitive_info[j+1].point.y-
5007           primitive_info[j].point.y,primitive_info[j+2].point.x,
5008           primitive_info[j+2].point.y);
5009         (void) WriteBlobString(image,message);
5010         break;
5011       }
5012       case ArcPrimitive:
5013       {
5014         if (primitive_info[j].coordinates != 3)
5015           {
5016             status=MagickFalse;
5017             break;
5018           }
5019         break;
5020       }
5021       case EllipsePrimitive:
5022       {
5023         if (primitive_info[j].coordinates != 3)
5024           {
5025             status=MagickFalse;
5026             break;
5027           }
5028           (void) FormatLocaleString(message,MagickPathExtent,
5029           "  <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
5030           primitive_info[j].point.x,primitive_info[j].point.y,
5031           primitive_info[j+1].point.x,primitive_info[j+1].point.y);
5032         (void) WriteBlobString(image,message);
5033         break;
5034       }
5035       case CirclePrimitive:
5036       {
5037         double
5038           alpha,
5039           beta;
5040 
5041         if (primitive_info[j].coordinates != 2)
5042           {
5043             status=MagickFalse;
5044             break;
5045           }
5046         alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
5047         beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
5048         (void) FormatLocaleString(message,MagickPathExtent,
5049           "  <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
5050           primitive_info[j].point.x,primitive_info[j].point.y,
5051           hypot(alpha,beta));
5052         (void) WriteBlobString(image,message);
5053         break;
5054       }
5055       case PolylinePrimitive:
5056       {
5057         if (primitive_info[j].coordinates < 2)
5058           {
5059             status=MagickFalse;
5060             break;
5061           }
5062         (void) CopyMagickString(message,"  <polyline points=\"",
5063            MagickPathExtent);
5064         (void) WriteBlobString(image,message);
5065         length=strlen(message);
5066         for ( ; j < i; j++)
5067         {
5068           (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
5069             primitive_info[j].point.x,primitive_info[j].point.y);
5070           length+=strlen(message);
5071           if (length >= 80)
5072             {
5073               (void) WriteBlobString(image,"\n    ");
5074               length=strlen(message)+5;
5075             }
5076           (void) WriteBlobString(image,message);
5077         }
5078         (void) WriteBlobString(image,"\"/>\n");
5079         break;
5080       }
5081       case PolygonPrimitive:
5082       {
5083         if (primitive_info[j].coordinates < 3)
5084           {
5085             status=MagickFalse;
5086             break;
5087           }
5088         primitive_info[i]=primitive_info[j];
5089         primitive_info[i].coordinates=0;
5090         primitive_info[j].coordinates++;
5091         i++;
5092         (void) CopyMagickString(message,"  <polygon points=\"",
5093           MagickPathExtent);
5094         (void) WriteBlobString(image,message);
5095         length=strlen(message);
5096         for ( ; j < i; j++)
5097         {
5098           (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
5099             primitive_info[j].point.x,primitive_info[j].point.y);
5100           length+=strlen(message);
5101           if (length >= 80)
5102             {
5103               (void) WriteBlobString(image,"\n    ");
5104               length=strlen(message)+5;
5105             }
5106           (void) WriteBlobString(image,message);
5107         }
5108         (void) WriteBlobString(image,"\"/>\n");
5109         break;
5110       }
5111       case BezierPrimitive:
5112       {
5113         if (primitive_info[j].coordinates < 3)
5114           {
5115             status=MagickFalse;
5116             break;
5117           }
5118         break;
5119       }
5120       case PathPrimitive:
5121       {
5122         int
5123           number_attributes;
5124 
5125         (void) GetNextToken(q,&q,extent,token);
5126         number_attributes=1;
5127         for (p=token; *p != '\0'; p++)
5128           if (isalpha((int) ((unsigned char) *p)) != 0)
5129             number_attributes++;
5130         if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
5131           {
5132             number_points+=6*BezierQuantum*number_attributes;
5133             primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
5134               number_points,sizeof(*primitive_info));
5135             if (primitive_info == (PrimitiveInfo *) NULL)
5136               {
5137                 (void) ThrowMagickException(exception,GetMagickModule(),
5138                   ResourceLimitError,"MemoryAllocationFailed","`%s'",
5139                   image->filename);
5140                 break;
5141               }
5142           }
5143         (void) WriteBlobString(image,"  <path d=\"");
5144         (void) WriteBlobString(image,token);
5145         (void) WriteBlobString(image,"\"/>\n");
5146         break;
5147       }
5148       case AlphaPrimitive:
5149       case ColorPrimitive:
5150       {
5151         if (primitive_info[j].coordinates != 1)
5152           {
5153             status=MagickFalse;
5154             break;
5155           }
5156         (void) GetNextToken(q,&q,extent,token);
5157         if (LocaleCompare("point",token) == 0)
5158           primitive_info[j].method=PointMethod;
5159         if (LocaleCompare("replace",token) == 0)
5160           primitive_info[j].method=ReplaceMethod;
5161         if (LocaleCompare("floodfill",token) == 0)
5162           primitive_info[j].method=FloodfillMethod;
5163         if (LocaleCompare("filltoborder",token) == 0)
5164           primitive_info[j].method=FillToBorderMethod;
5165         if (LocaleCompare("reset",token) == 0)
5166           primitive_info[j].method=ResetMethod;
5167         break;
5168       }
5169       case TextPrimitive:
5170       {
5171         register char
5172           *p;
5173 
5174         if (primitive_info[j].coordinates != 1)
5175           {
5176             status=MagickFalse;
5177             break;
5178           }
5179         (void) GetNextToken(q,&q,extent,token);
5180         (void) FormatLocaleString(message,MagickPathExtent,
5181           "  <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
5182           primitive_info[j].point.y);
5183         (void) WriteBlobString(image,message);
5184         for (p=token; *p != '\0'; p++)
5185           switch (*p)
5186           {
5187             case '<': (void) WriteBlobString(image,"&lt;"); break;
5188             case '>': (void) WriteBlobString(image,"&gt;"); break;
5189             case '&': (void) WriteBlobString(image,"&amp;"); break;
5190             default: (void) WriteBlobByte(image,(unsigned char) *p); break;
5191           }
5192         (void) WriteBlobString(image,"</text>\n");
5193         break;
5194       }
5195       case ImagePrimitive:
5196       {
5197         if (primitive_info[j].coordinates != 2)
5198           {
5199             status=MagickFalse;
5200             break;
5201           }
5202         (void) GetNextToken(q,&q,extent,token);
5203         (void) FormatLocaleString(message,MagickPathExtent,
5204           "  <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
5205           "href=\"%s\"/>\n",primitive_info[j].point.x,
5206           primitive_info[j].point.y,primitive_info[j+1].point.x,
5207           primitive_info[j+1].point.y,token);
5208         (void) WriteBlobString(image,message);
5209         break;
5210       }
5211     }
5212     if (primitive_info == (PrimitiveInfo *) NULL)
5213       break;
5214     primitive_info[i].primitive=UndefinedPrimitive;
5215     if (status == MagickFalse)
5216       break;
5217   }
5218   (void) WriteBlobString(image,"</svg>\n");
5219   /*
5220     Relinquish resources.
5221   */
5222   token=DestroyString(token);
5223   if (primitive_info != (PrimitiveInfo *) NULL)
5224     primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
5225   (void) CloseBlob(image);
5226   return(status);
5227 }
5228