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