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