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