]> granicus.if.org Git - imagemagick/blob - coders/svg.c
...
[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           (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1394             svg_info->bounds.x,svg_info->bounds.y);
1395           svg_info->center.x=svg_info->bounds.x;
1396           svg_info->center.y=svg_info->bounds.y;
1397           svg_info->bounds.x=0.0;
1398           svg_info->bounds.y=0.0;
1399           svg_info->bounds.width=0.0;
1400           svg_info->bounds.height=0.0;
1401           break;
1402         }
1403       if (LocaleCompare((const char *) name,"tspan") == 0)
1404         {
1405           if (*svg_info->text != '\0')
1406             {
1407               DrawInfo
1408                 *draw_info;
1409
1410               TypeMetric
1411                 metrics;
1412
1413               char
1414                 *text;
1415
1416               text=EscapeString(svg_info->text,'\'');
1417               (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
1418                 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1419                 svg_info->center.y,text);
1420               text=DestroyString(text);
1421               draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
1422               draw_info->pointsize=svg_info->pointsize;
1423               draw_info->text=AcquireString(svg_info->text);
1424               (void) ConcatenateString(&draw_info->text," ");
1425               (void) GetTypeMetrics(svg_info->image,draw_info,
1426                 &metrics,svg_info->exception);
1427               svg_info->bounds.x+=metrics.width;
1428               draw_info=DestroyDrawInfo(draw_info);
1429               *svg_info->text='\0';
1430             }
1431           PushGraphicContext(id);
1432           break;
1433         }
1434       break;
1435     }
1436     case 'U':
1437     case 'u':
1438     {
1439       if (LocaleCompare((char *) name,"use") == 0)
1440         {
1441           PushGraphicContext(id);
1442           break;
1443         }
1444       break;
1445     }
1446     default:
1447       break;
1448   }
1449   if (attributes != (const xmlChar **) NULL)
1450     for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1451     {
1452       keyword=(const char *) attributes[i];
1453       value=(const char *) attributes[i+1];
1454       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1455         "    %s = %s",keyword,value);
1456       switch (*keyword)
1457       {
1458         case 'A':
1459         case 'a':
1460         {
1461           if (LocaleCompare(keyword,"angle") == 0)
1462             {
1463               (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1464                 GetUserSpaceCoordinateValue(svg_info,0,value));
1465               break;
1466             }
1467           break;
1468         }
1469         case 'C':
1470         case 'c':
1471         {
1472           if (LocaleCompare(keyword,"class") == 0)
1473             {
1474               const char
1475                 *p;
1476
1477               for (p=value; ; )
1478               {
1479                 GetNextToken(p,&p,MagickPathExtent,token);
1480                 if (*token == ',')
1481                   GetNextToken(p,&p,MagickPathExtent,token);
1482                 if (*token != '\0')
1483                   {
1484                     (void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",
1485                       value);
1486                     break;
1487                   }
1488               }
1489               break;
1490             }
1491           if (LocaleCompare(keyword,"clip-path") == 0)
1492             {
1493               (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",
1494                 value);
1495               break;
1496             }
1497           if (LocaleCompare(keyword,"clip-rule") == 0)
1498             {
1499               (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",
1500                 value);
1501               break;
1502             }
1503           if (LocaleCompare(keyword,"clipPathUnits") == 0)
1504             {
1505               (void) CloneString(&units,value);
1506               (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
1507                 value);
1508               break;
1509             }
1510           if (LocaleCompare(keyword,"color") == 0)
1511             {
1512               (void) CloneString(&color,value);
1513               break;
1514             }
1515           if (LocaleCompare(keyword,"cx") == 0)
1516             {
1517               svg_info->element.cx=
1518                 GetUserSpaceCoordinateValue(svg_info,1,value);
1519               break;
1520             }
1521           if (LocaleCompare(keyword,"cy") == 0)
1522             {
1523               svg_info->element.cy=
1524                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1525               break;
1526             }
1527           break;
1528         }
1529         case 'D':
1530         case 'd':
1531         {
1532           if (LocaleCompare(keyword,"d") == 0)
1533             {
1534               (void) CloneString(&svg_info->vertices,value);
1535               break;
1536             }
1537           if (LocaleCompare(keyword,"dx") == 0)
1538             {
1539               svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
1540               break;
1541             }
1542           if (LocaleCompare(keyword,"dy") == 0)
1543             {
1544               svg_info->bounds.y+=
1545                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1546               break;
1547             }
1548           break;
1549         }
1550         case 'F':
1551         case 'f':
1552         {
1553           if (LocaleCompare(keyword,"fill") == 0)
1554             {
1555               if (LocaleCompare(value,"currentColor") == 0)
1556                 {
1557                   (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
1558                   break;
1559                 }
1560               (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1561               break;
1562             }
1563           if (LocaleCompare(keyword,"fillcolor") == 0)
1564             {
1565               (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
1566               break;
1567             }
1568           if (LocaleCompare(keyword,"fill-rule") == 0)
1569             {
1570               (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",
1571                 value);
1572               break;
1573             }
1574           if (LocaleCompare(keyword,"fill-opacity") == 0)
1575             {
1576               (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
1577                 value);
1578               break;
1579             }
1580           if (LocaleCompare(keyword,"font-family") == 0)
1581             {
1582               (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
1583                 value);
1584               break;
1585             }
1586           if (LocaleCompare(keyword,"font-stretch") == 0)
1587             {
1588               (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
1589                 value);
1590               break;
1591             }
1592           if (LocaleCompare(keyword,"font-style") == 0)
1593             {
1594               (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
1595                 value);
1596               break;
1597             }
1598           if (LocaleCompare(keyword,"font-size") == 0)
1599             {
1600               if (LocaleCompare(value,"xx-small") == 0)
1601                 svg_info->pointsize=6.144;
1602               else if (LocaleCompare(value,"x-small") == 0)
1603                 svg_info->pointsize=7.68;
1604               else if (LocaleCompare(value,"small") == 0)
1605                 svg_info->pointsize=9.6;
1606               else if (LocaleCompare(value,"medium") == 0)
1607                 svg_info->pointsize=12.0;
1608               else if (LocaleCompare(value,"large") == 0)
1609                 svg_info->pointsize=14.4;
1610               else if (LocaleCompare(value,"x-large") == 0)
1611                 svg_info->pointsize=17.28;
1612               else if (LocaleCompare(value,"xx-large") == 0)
1613                 svg_info->pointsize=20.736;
1614               else
1615                 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,
1616                   value);
1617               (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1618                 svg_info->pointsize);
1619               break;
1620             }
1621           if (LocaleCompare(keyword,"font-weight") == 0)
1622             {
1623               (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
1624                 value);
1625               break;
1626             }
1627           break;
1628         }
1629         case 'G':
1630         case 'g':
1631         {
1632           if (LocaleCompare(keyword,"gradientTransform") == 0)
1633             {
1634               AffineMatrix
1635                 affine,
1636                 current,
1637                 transform;
1638
1639               GetAffineMatrix(&transform);
1640               (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
1641               tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
1642               if (tokens == (char **) NULL)
1643                 break;
1644               for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
1645               {
1646                 keyword=(char *) tokens[j];
1647                 if (keyword == (char *) NULL)
1648                   continue;
1649                 value=(char *) tokens[j+1];
1650                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1651                   "    %s: %s",keyword,value);
1652                 current=transform;
1653                 GetAffineMatrix(&affine);
1654                 switch (*keyword)
1655                 {
1656                   case 'M':
1657                   case 'm':
1658                   {
1659                     if (LocaleCompare(keyword,"matrix") == 0)
1660                       {
1661                         p=(const char *) value;
1662                         GetNextToken(p,&p,MagickPathExtent,token);
1663                         affine.sx=StringToDouble(value,(char **) NULL);
1664                         GetNextToken(p,&p,MagickPathExtent,token);
1665                         if (*token == ',')
1666                           GetNextToken(p,&p,MagickPathExtent,token);
1667                         affine.rx=StringToDouble(token,&next_token);
1668                         GetNextToken(p,&p,MagickPathExtent,token);
1669                         if (*token == ',')
1670                           GetNextToken(p,&p,MagickPathExtent,token);
1671                         affine.ry=StringToDouble(token,&next_token);
1672                         GetNextToken(p,&p,MagickPathExtent,token);
1673                         if (*token == ',')
1674                           GetNextToken(p,&p,MagickPathExtent,token);
1675                         affine.sy=StringToDouble(token,&next_token);
1676                         GetNextToken(p,&p,MagickPathExtent,token);
1677                         if (*token == ',')
1678                           GetNextToken(p,&p,MagickPathExtent,token);
1679                         affine.tx=StringToDouble(token,&next_token);
1680                         GetNextToken(p,&p,MagickPathExtent,token);
1681                         if (*token == ',')
1682                           GetNextToken(p,&p,MagickPathExtent,token);
1683                         affine.ty=StringToDouble(token,&next_token);
1684                         break;
1685                       }
1686                     break;
1687                   }
1688                   case 'R':
1689                   case 'r':
1690                   {
1691                     if (LocaleCompare(keyword,"rotate") == 0)
1692                       {
1693                         double
1694                           angle;
1695
1696                         angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1697                         affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1698                         affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1699                         affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1700                         affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1701                         break;
1702                       }
1703                     break;
1704                   }
1705                   case 'S':
1706                   case 's':
1707                   {
1708                     if (LocaleCompare(keyword,"scale") == 0)
1709                       {
1710                         for (p=(const char *) value; *p != '\0'; p++)
1711                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
1712                               (*p == ','))
1713                             break;
1714                         affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1715                         affine.sy=affine.sx;
1716                         if (*p != '\0')
1717                           affine.sy=
1718                             GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1719                         svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1720                         break;
1721                       }
1722                     if (LocaleCompare(keyword,"skewX") == 0)
1723                       {
1724                         affine.sx=svg_info->affine.sx;
1725                         affine.ry=tan(DegreesToRadians(fmod(
1726                           GetUserSpaceCoordinateValue(svg_info,1,value),
1727                           360.0)));
1728                         affine.sy=svg_info->affine.sy;
1729                         break;
1730                       }
1731                     if (LocaleCompare(keyword,"skewY") == 0)
1732                       {
1733                         affine.sx=svg_info->affine.sx;
1734                         affine.rx=tan(DegreesToRadians(fmod(
1735                           GetUserSpaceCoordinateValue(svg_info,-1,value),
1736                           360.0)));
1737                         affine.sy=svg_info->affine.sy;
1738                         break;
1739                       }
1740                     break;
1741                   }
1742                   case 'T':
1743                   case 't':
1744                   {
1745                     if (LocaleCompare(keyword,"translate") == 0)
1746                       {
1747                         for (p=(const char *) value; *p != '\0'; p++)
1748                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
1749                               (*p == ','))
1750                             break;
1751                         affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1752                         affine.ty=affine.tx;
1753                         if (*p != '\0')
1754                           affine.ty=
1755                             GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1756                         break;
1757                       }
1758                     break;
1759                   }
1760                   default:
1761                     break;
1762                 }
1763                 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1764                 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1765                 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1766                 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1767                 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1768                   current.tx;
1769                 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1770                   current.ty;
1771               }
1772               (void) FormatLocaleFile(svg_info->file,
1773                 "affine %g %g %g %g %g %g\n",transform.sx,
1774                 transform.rx,transform.ry,transform.sy,transform.tx,
1775                 transform.ty);
1776               for (j=0; tokens[j] != (char *) NULL; j++)
1777                 tokens[j]=DestroyString(tokens[j]);
1778               tokens=(char **) RelinquishMagickMemory(tokens);
1779               break;
1780             }
1781           if (LocaleCompare(keyword,"gradientUnits") == 0)
1782             {
1783               (void) CloneString(&units,value);
1784               (void) FormatLocaleFile(svg_info->file,"gradient-units \"%s\"\n",
1785                 value);
1786               break;
1787             }
1788           break;
1789         }
1790         case 'H':
1791         case 'h':
1792         {
1793           if (LocaleCompare(keyword,"height") == 0)
1794             {
1795               svg_info->bounds.height=
1796                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1797               break;
1798             }
1799           if (LocaleCompare(keyword,"href") == 0)
1800             {
1801               (void) CloneString(&svg_info->url,value);
1802               break;
1803             }
1804           break;
1805         }
1806         case 'M':
1807         case 'm':
1808         {
1809           if (LocaleCompare(keyword,"major") == 0)
1810             {
1811               svg_info->element.major=
1812                 GetUserSpaceCoordinateValue(svg_info,1,value);
1813               break;
1814             }
1815           if (LocaleCompare(keyword,"mask") == 0)
1816             {
1817               (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
1818               break;
1819             }
1820           if (LocaleCompare(keyword,"minor") == 0)
1821             {
1822               svg_info->element.minor=
1823                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1824               break;
1825             }
1826           break;
1827         }
1828         case 'O':
1829         case 'o':
1830         {
1831           if (LocaleCompare(keyword,"offset") == 0)
1832             {
1833               (void) CloneString(&svg_info->offset,value);
1834               break;
1835             }
1836           if (LocaleCompare(keyword,"opacity") == 0)
1837             {
1838               (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
1839               break;
1840             }
1841           break;
1842         }
1843         case 'P':
1844         case 'p':
1845         {
1846           if (LocaleCompare(keyword,"path") == 0)
1847             {
1848               (void) CloneString(&svg_info->url,value);
1849               break;
1850             }
1851           if (LocaleCompare(keyword,"points") == 0)
1852             {
1853               (void) CloneString(&svg_info->vertices,value);
1854               break;
1855             }
1856           break;
1857         }
1858         case 'R':
1859         case 'r':
1860         {
1861           if (LocaleCompare(keyword,"r") == 0)
1862             {
1863               svg_info->element.major=
1864                 GetUserSpaceCoordinateValue(svg_info,1,value);
1865               svg_info->element.minor=
1866                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1867               break;
1868             }
1869           if (LocaleCompare(keyword,"rotate") == 0)
1870             {
1871               double
1872                 angle;
1873
1874               angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1875               (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1876                 svg_info->bounds.x,svg_info->bounds.y);
1877               svg_info->bounds.x=0;
1878               svg_info->bounds.y=0;
1879               (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
1880               break;
1881             }
1882           if (LocaleCompare(keyword,"rx") == 0)
1883             {
1884               if (LocaleCompare((const char *) name,"ellipse") == 0)
1885                 svg_info->element.major=
1886                   GetUserSpaceCoordinateValue(svg_info,1,value);
1887               else
1888                 svg_info->radius.x=
1889                   GetUserSpaceCoordinateValue(svg_info,1,value);
1890               break;
1891             }
1892           if (LocaleCompare(keyword,"ry") == 0)
1893             {
1894               if (LocaleCompare((const char *) name,"ellipse") == 0)
1895                 svg_info->element.minor=
1896                   GetUserSpaceCoordinateValue(svg_info,-1,value);
1897               else
1898                 svg_info->radius.y=
1899                   GetUserSpaceCoordinateValue(svg_info,-1,value);
1900               break;
1901             }
1902           break;
1903         }
1904         case 'S':
1905         case 's':
1906         {
1907           if (LocaleCompare(keyword,"stop-color") == 0)
1908             {
1909               (void) CloneString(&svg_info->stop_color,value);
1910               break;
1911             }
1912           if (LocaleCompare(keyword,"stroke") == 0)
1913             {
1914               if (LocaleCompare(value,"currentColor") == 0)
1915                 {
1916                   (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",
1917                     color);
1918                   break;
1919                 }
1920               (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",value);
1921               break;
1922             }
1923           if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1924             {
1925               (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
1926                 LocaleCompare(value,"true") == 0);
1927               break;
1928             }
1929           if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1930             {
1931               (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1932                 value);
1933               break;
1934             }
1935           if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1936             {
1937               (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
1938                 GetUserSpaceCoordinateValue(svg_info,1,value));
1939               break;
1940             }
1941           if (LocaleCompare(keyword,"stroke-linecap") == 0)
1942             {
1943               (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
1944                 value);
1945               break;
1946             }
1947           if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1948             {
1949               (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
1950                 value);
1951               break;
1952             }
1953           if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1954             {
1955               (void) FormatLocaleFile(svg_info->file,
1956                 "stroke-miterlimit \"%s\"\n",value);
1957               break;
1958             }
1959           if (LocaleCompare(keyword,"stroke-opacity") == 0)
1960             {
1961               (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
1962                 value);
1963               break;
1964             }
1965           if (LocaleCompare(keyword,"stroke-width") == 0)
1966             {
1967               (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
1968                 GetUserSpaceCoordinateValue(svg_info,1,value));
1969               break;
1970             }
1971           if (LocaleCompare(keyword,"style") == 0)
1972             {
1973               SVGProcessStyleElement(context,name,value);
1974               break;
1975             }
1976           break;
1977         }
1978         case 'T':
1979         case 't':
1980         {
1981           if (LocaleCompare(keyword,"text-align") == 0)
1982             {
1983               (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",
1984                 value);
1985               break;
1986             }
1987           if (LocaleCompare(keyword,"text-anchor") == 0)
1988             {
1989               (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
1990                 value);
1991               break;
1992             }
1993           if (LocaleCompare(keyword,"text-decoration") == 0)
1994             {
1995               if (LocaleCompare(value,"underline") == 0)
1996                 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
1997               if (LocaleCompare(value,"line-through") == 0)
1998                 (void) FormatLocaleFile(svg_info->file,
1999                   "decorate line-through\n");
2000               if (LocaleCompare(value,"overline") == 0)
2001                 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
2002               break;
2003             }
2004           if (LocaleCompare(keyword,"text-antialiasing") == 0)
2005             {
2006               (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
2007                 LocaleCompare(value,"true") == 0);
2008               break;
2009             }
2010           if (LocaleCompare(keyword,"transform") == 0)
2011             {
2012               AffineMatrix
2013                 affine,
2014                 current,
2015                 transform;
2016
2017               GetAffineMatrix(&transform);
2018               (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
2019               tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
2020               if (tokens == (char **) NULL)
2021                 break;
2022               for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2023               {
2024                 keyword=(char *) tokens[j];
2025                 value=(char *) tokens[j+1];
2026                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2027                   "    %s: %s",keyword,value);
2028                 current=transform;
2029                 GetAffineMatrix(&affine);
2030                 switch (*keyword)
2031                 {
2032                   case 'M':
2033                   case 'm':
2034                   {
2035                     if (LocaleCompare(keyword,"matrix") == 0)
2036                       {
2037                         p=(const char *) value;
2038                         GetNextToken(p,&p,MagickPathExtent,token);
2039                         affine.sx=StringToDouble(value,(char **) NULL);
2040                         GetNextToken(p,&p,MagickPathExtent,token);
2041                         if (*token == ',')
2042                           GetNextToken(p,&p,MagickPathExtent,token);
2043                         affine.rx=StringToDouble(token,&next_token);
2044                         GetNextToken(p,&p,MagickPathExtent,token);
2045                         if (*token == ',')
2046                           GetNextToken(p,&p,MagickPathExtent,token);
2047                         affine.ry=StringToDouble(token,&next_token);
2048                         GetNextToken(p,&p,MagickPathExtent,token);
2049                         if (*token == ',')
2050                           GetNextToken(p,&p,MagickPathExtent,token);
2051                         affine.sy=StringToDouble(token,&next_token);
2052                         GetNextToken(p,&p,MagickPathExtent,token);
2053                         if (*token == ',')
2054                           GetNextToken(p,&p,MagickPathExtent,token);
2055                         affine.tx=StringToDouble(token,&next_token);
2056                         GetNextToken(p,&p,MagickPathExtent,token);
2057                         if (*token == ',')
2058                           GetNextToken(p,&p,MagickPathExtent,token);
2059                         affine.ty=StringToDouble(token,&next_token);
2060                         break;
2061                       }
2062                     break;
2063                   }
2064                   case 'R':
2065                   case 'r':
2066                   {
2067                     if (LocaleCompare(keyword,"rotate") == 0)
2068                       {
2069                         double
2070                           angle,
2071                           x,
2072                           y;
2073
2074                         p=(const char *) value;
2075                         GetNextToken(p,&p,MagickPathExtent,token);
2076                         angle=StringToDouble(value,(char **) NULL);
2077                         GetNextToken(p,&p,MagickPathExtent,token);
2078                         if (*token == ',')
2079                           GetNextToken(p,&p,MagickPathExtent,token);
2080                         x=StringToDouble(token,&next_token);
2081                         GetNextToken(p,&p,MagickPathExtent,token);
2082                         if (*token == ',')
2083                           GetNextToken(p,&p,MagickPathExtent,token);
2084                         y=StringToDouble(token,&next_token);
2085                         affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
2086                         affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
2087                         affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
2088                         affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
2089                         affine.tx=0.0;
2090                         affine.ty=0.0;
2091                         svg_info->center.x=x;
2092                         svg_info->center.y=y;
2093                         break;
2094                       }
2095                     break;
2096                   }
2097                   case 'S':
2098                   case 's':
2099                   {
2100                     if (LocaleCompare(keyword,"scale") == 0)
2101                       {
2102                         for (p=(const char *) value; *p != '\0'; p++)
2103                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
2104                               (*p == ','))
2105                             break;
2106                         affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
2107                         affine.sy=affine.sx;
2108                         if (*p != '\0')
2109                           affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
2110                             p+1);
2111                         svg_info->scale[svg_info->n]=ExpandAffine(&affine);
2112                         break;
2113                       }
2114                     if (LocaleCompare(keyword,"skewX") == 0)
2115                       {
2116                         affine.sx=svg_info->affine.sx;
2117                         affine.ry=tan(DegreesToRadians(fmod(
2118                           GetUserSpaceCoordinateValue(svg_info,1,value),
2119                           360.0)));
2120                         affine.sy=svg_info->affine.sy;
2121                         break;
2122                       }
2123                     if (LocaleCompare(keyword,"skewY") == 0)
2124                       {
2125                         affine.sx=svg_info->affine.sx;
2126                         affine.rx=tan(DegreesToRadians(fmod(
2127                           GetUserSpaceCoordinateValue(svg_info,-1,value),
2128                           360.0)));
2129                         affine.sy=svg_info->affine.sy;
2130                         break;
2131                       }
2132                     break;
2133                   }
2134                   case 'T':
2135                   case 't':
2136                   {
2137                     if (LocaleCompare(keyword,"translate") == 0)
2138                       {
2139                         for (p=(const char *) value; *p != '\0'; p++)
2140                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
2141                               (*p == ','))
2142                             break;
2143                         affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2144                         affine.ty=affine.tx;
2145                         if (*p != '\0')
2146                           affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2147                             p+1);
2148                         break;
2149                       }
2150                     break;
2151                   }
2152                   default:
2153                     break;
2154                 }
2155                 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2156                 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2157                 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2158                 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2159                 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2160                   current.tx;
2161                 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2162                   current.ty;
2163               }
2164               (void) FormatLocaleFile(svg_info->file,
2165                 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2166                 transform.ry,transform.sy,transform.tx,transform.ty);
2167               for (j=0; tokens[j] != (char *) NULL; j++)
2168                 tokens[j]=DestroyString(tokens[j]);
2169               tokens=(char **) RelinquishMagickMemory(tokens);
2170               break;
2171             }
2172           break;
2173         }
2174         case 'V':
2175         case 'v':
2176         {
2177           if (LocaleCompare(keyword,"verts") == 0)
2178             {
2179               (void) CloneString(&svg_info->vertices,value);
2180               break;
2181             }
2182           if (LocaleCompare(keyword,"viewBox") == 0)
2183             {
2184               p=(const char *) value;
2185               GetNextToken(p,&p,MagickPathExtent,token);
2186               svg_info->view_box.x=StringToDouble(token,&next_token);
2187               GetNextToken(p,&p,MagickPathExtent,token);
2188               if (*token == ',')
2189                 GetNextToken(p,&p,MagickPathExtent,token);
2190               svg_info->view_box.y=StringToDouble(token,&next_token);
2191               GetNextToken(p,&p,MagickPathExtent,token);
2192               if (*token == ',')
2193                 GetNextToken(p,&p,MagickPathExtent,token);
2194               svg_info->view_box.width=StringToDouble(token,
2195                 (char **) NULL);
2196               if (svg_info->bounds.width == 0)
2197                 svg_info->bounds.width=svg_info->view_box.width;
2198               GetNextToken(p,&p,MagickPathExtent,token);
2199               if (*token == ',')
2200                 GetNextToken(p,&p,MagickPathExtent,token);
2201               svg_info->view_box.height=StringToDouble(token,
2202                 (char **) NULL);
2203               if (svg_info->bounds.height == 0)
2204                 svg_info->bounds.height=svg_info->view_box.height;
2205               break;
2206             }
2207           break;
2208         }
2209         case 'W':
2210         case 'w':
2211         {
2212           if (LocaleCompare(keyword,"width") == 0)
2213             {
2214               svg_info->bounds.width=
2215                 GetUserSpaceCoordinateValue(svg_info,1,value);
2216               break;
2217             }
2218           break;
2219         }
2220         case 'X':
2221         case 'x':
2222         {
2223           if (LocaleCompare(keyword,"x") == 0)
2224             {
2225               svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2226               break;
2227             }
2228           if (LocaleCompare(keyword,"xlink:href") == 0)
2229             {
2230               (void) CloneString(&svg_info->url,value);
2231               break;
2232             }
2233           if (LocaleCompare(keyword,"x1") == 0)
2234             {
2235               svg_info->segment.x1=
2236                 GetUserSpaceCoordinateValue(svg_info,1,value);
2237               break;
2238             }
2239           if (LocaleCompare(keyword,"x2") == 0)
2240             {
2241               svg_info->segment.x2=
2242                 GetUserSpaceCoordinateValue(svg_info,1,value);
2243               break;
2244             }
2245           break;
2246         }
2247         case 'Y':
2248         case 'y':
2249         {
2250           if (LocaleCompare(keyword,"y") == 0)
2251             {
2252               svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2253               break;
2254             }
2255           if (LocaleCompare(keyword,"y1") == 0)
2256             {
2257               svg_info->segment.y1=
2258                 GetUserSpaceCoordinateValue(svg_info,-1,value);
2259               break;
2260             }
2261           if (LocaleCompare(keyword,"y2") == 0)
2262             {
2263               svg_info->segment.y2=
2264                 GetUserSpaceCoordinateValue(svg_info,-1,value);
2265               break;
2266             }
2267           break;
2268         }
2269         default:
2270           break;
2271       }
2272     }
2273   if (LocaleCompare((const char *) name,"svg") == 0)
2274     {
2275       if (svg_info->document->encoding != (const xmlChar *) NULL)
2276         (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2277           (const char *) svg_info->document->encoding);
2278       if (attributes != (const xmlChar **) NULL)
2279         {
2280           double
2281             sx,
2282             sy,
2283             tx,
2284             ty;
2285
2286           if ((svg_info->view_box.width == 0.0) ||
2287               (svg_info->view_box.height == 0.0))
2288             svg_info->view_box=svg_info->bounds;
2289           svg_info->width=0;
2290           if (svg_info->bounds.width > 0.0)
2291             svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2292           svg_info->height=0;
2293           if (svg_info->bounds.height > 0.0)
2294             svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2295           (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2296             (double) svg_info->width,(double) svg_info->height);
2297           sx=(double) svg_info->width/svg_info->view_box.width;
2298           sy=(double) svg_info->height/svg_info->view_box.height;
2299           tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2300             0.0;
2301           ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2302             0.0;
2303           (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2304             sx,sy,tx,ty);
2305           if ((svg_info->svgDepth == 1) && (*background != '\0'))
2306             {
2307               PushGraphicContext(id);
2308               (void) FormatLocaleFile(svg_info->file,"fill %s\n",background);
2309               (void) FormatLocaleFile(svg_info->file,
2310                 "rectangle 0,0 %g,%g\n",svg_info->view_box.width,
2311                 svg_info->view_box.height);
2312               (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2313             }
2314         }
2315     }
2316   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  )");
2317   units=DestroyString(units);
2318   if (color != (char *) NULL)
2319     color=DestroyString(color);
2320 }
2321
2322 static void SVGEndElement(void *context,const xmlChar *name)
2323 {
2324   SVGInfo
2325     *svg_info;
2326
2327   /*
2328     Called when the end of an element has been detected.
2329   */
2330   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2331     "  SAX.endElement(%s)",name);
2332   svg_info=(SVGInfo *) context;
2333   if (strchr((char *) name,':') != (char *) NULL)
2334     {
2335       /*
2336         Skip over namespace.
2337       */
2338       for ( ; *name != ':'; name++) ;
2339       name++;
2340     }
2341   switch (*name)
2342   {
2343     case 'C':
2344     case 'c':
2345     {
2346       if (LocaleCompare((const char *) name,"circle") == 0)
2347         {
2348           (void) FormatLocaleFile(svg_info->file,"class \"circle\"\n");
2349           (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2350             svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2351             svg_info->element.cy+svg_info->element.minor);
2352           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2353           break;
2354         }
2355       if (LocaleCompare((const char *) name,"clipPath") == 0)
2356         {
2357           (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2358           break;
2359         }
2360       break;
2361     }
2362     case 'D':
2363     case 'd':
2364     {
2365       if (LocaleCompare((const char *) name,"defs") == 0)
2366         {
2367           (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2368           break;
2369         }
2370       if (LocaleCompare((const char *) name,"desc") == 0)
2371         {
2372           register char
2373             *p;
2374
2375           if (*svg_info->text == '\0')
2376             break;
2377           (void) fputc('#',svg_info->file);
2378           for (p=svg_info->text; *p != '\0'; p++)
2379           {
2380             (void) fputc(*p,svg_info->file);
2381             if (*p == '\n')
2382               (void) fputc('#',svg_info->file);
2383           }
2384           (void) fputc('\n',svg_info->file);
2385           *svg_info->text='\0';
2386           break;
2387         }
2388       break;
2389     }
2390     case 'E':
2391     case 'e':
2392     {
2393       if (LocaleCompare((const char *) name,"ellipse") == 0)
2394         {
2395           double
2396             angle;
2397
2398           (void) FormatLocaleFile(svg_info->file,"class \"ellipse\"\n");
2399           angle=svg_info->element.angle;
2400           (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2401             svg_info->element.cx,svg_info->element.cy,
2402             angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2403             angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2404           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2405           break;
2406         }
2407       break;
2408     }
2409     case 'F':
2410     case 'f':
2411     {
2412       if (LocaleCompare((const char *) name,"foreignObject") == 0)
2413         {
2414           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2415           break;
2416         }
2417       break;
2418     }
2419     case 'G':
2420     case 'g':
2421     {
2422       if (LocaleCompare((const char *) name,"g") == 0)
2423         {
2424           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2425           break;
2426         }
2427       break;
2428     }
2429     case 'I':
2430     case 'i':
2431     {
2432       if (LocaleCompare((const char *) name,"image") == 0)
2433         {
2434           (void) FormatLocaleFile(svg_info->file,
2435             "image Over %g,%g %g,%g \"%s\"\n",svg_info->bounds.x,
2436             svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2437             svg_info->url);
2438           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2439           break;
2440         }
2441       break;
2442     }
2443     case 'L':
2444     case 'l':
2445     {
2446       if (LocaleCompare((const char *) name,"line") == 0)
2447         {
2448           (void) FormatLocaleFile(svg_info->file,"class \"line\"\n");
2449           (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2450             svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2451             svg_info->segment.y2);
2452           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2453           break;
2454         }
2455       if (LocaleCompare((const char *) name,"linearGradient") == 0)
2456         {
2457           (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2458           break;
2459         }
2460       break;
2461     }
2462     case 'M':
2463     case 'm':
2464     {
2465       if (LocaleCompare((const char *) name,"mask") == 0)
2466         {
2467           (void) FormatLocaleFile(svg_info->file,"pop mask\n");
2468           break;
2469         }
2470       break;
2471     }
2472     case 'P':
2473     case 'p':
2474     {
2475       if (LocaleCompare((const char *) name,"pattern") == 0)
2476         {
2477           (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2478           break;
2479         }
2480       if (LocaleCompare((const char *) name,"path") == 0)
2481         {
2482           (void) FormatLocaleFile(svg_info->file,"class \"path\"\n");
2483           (void) FormatLocaleFile(svg_info->file,"path \"%s\"\n",
2484             svg_info->vertices);
2485           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2486           break;
2487         }
2488       if (LocaleCompare((const char *) name,"polygon") == 0)
2489         {
2490           (void) FormatLocaleFile(svg_info->file,"class \"polygon\"\n");
2491           (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2492             svg_info->vertices);
2493           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2494           break;
2495         }
2496       if (LocaleCompare((const char *) name,"polyline") == 0)
2497         {
2498           (void) FormatLocaleFile(svg_info->file,"class \"polyline\"\n");
2499           (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2500             svg_info->vertices);
2501           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2502           break;
2503         }
2504       break;
2505     }
2506     case 'R':
2507     case 'r':
2508     {
2509       if (LocaleCompare((const char *) name,"radialGradient") == 0)
2510         {
2511           (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2512           break;
2513         }
2514       if (LocaleCompare((const char *) name,"rect") == 0)
2515         {
2516           if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2517             {
2518               (void) FormatLocaleFile(svg_info->file,"class \"rect\"\n");
2519               (void) FormatLocaleFile(svg_info->file,"rectangle %g,%g %g,%g\n",
2520                 svg_info->bounds.x,svg_info->bounds.y,
2521                 svg_info->bounds.x+svg_info->bounds.width,
2522                 svg_info->bounds.y+svg_info->bounds.height);
2523               (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2524               break;
2525             }
2526           if (svg_info->radius.x == 0.0)
2527             svg_info->radius.x=svg_info->radius.y;
2528           if (svg_info->radius.y == 0.0)
2529             svg_info->radius.y=svg_info->radius.x;
2530           (void) FormatLocaleFile(svg_info->file,
2531             "roundRectangle %g,%g %g,%g %g,%g\n",
2532             svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2533             svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2534             svg_info->radius.x,svg_info->radius.y);
2535           svg_info->radius.x=0.0;
2536           svg_info->radius.y=0.0;
2537           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2538           break;
2539         }
2540       break;
2541     }
2542     case 'S':
2543     case 's':
2544     {
2545       if (LocaleCompare((const char *) name,"stop") == 0)
2546         {
2547           (void) FormatLocaleFile(svg_info->file,"stop-color \"%s\" %s\n",
2548             svg_info->stop_color,svg_info->offset);
2549           break;
2550         }
2551       if (LocaleCompare((char *) name,"style") == 0)
2552         {
2553           char
2554             *keyword,
2555             **tokens,
2556             *value;
2557
2558           register ssize_t
2559             j;
2560
2561           size_t
2562             number_tokens;
2563
2564           /*
2565             Find style definitions in svg_info->text.
2566           */
2567           tokens=SVGKeyValuePairs(context,'{','}',svg_info->text,
2568             &number_tokens);
2569           if (tokens == (char **) NULL)
2570             break;
2571           for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
2572           {
2573             keyword=(char *) tokens[j];
2574             value=(char *) tokens[j+1];
2575             (void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
2576               keyword);
2577             SVGProcessStyleElement(context,name,value);
2578             (void) FormatLocaleFile(svg_info->file,"pop class\n");
2579           }
2580           break;
2581         }
2582       if (LocaleCompare((const char *) name,"svg") == 0)
2583         {
2584           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2585           svg_info->svgDepth--;
2586           break;
2587         }
2588       if (LocaleCompare((const char *) name,"symbol") == 0)
2589         {
2590           (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
2591           break;
2592         }
2593       break;
2594     }
2595     case 'T':
2596     case 't':
2597     {
2598       if (LocaleCompare((const char *) name,"text") == 0)
2599         {
2600           if (*svg_info->text != '\0')
2601             {
2602               char
2603                 *text;
2604
2605               (void) FormatLocaleFile(svg_info->file,"class \"text\"\n");
2606               text=EscapeString(svg_info->text,'\'');
2607               (void) FormatLocaleFile(svg_info->file,"text 0,0 \"%s\"\n",text);
2608               text=DestroyString(text);
2609               *svg_info->text='\0';
2610             }
2611           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2612           break;
2613         }
2614       if (LocaleCompare((const char *) name,"tspan") == 0)
2615         {
2616           if (*svg_info->text != '\0')
2617             {
2618               DrawInfo
2619                 *draw_info;
2620
2621               TypeMetric
2622                 metrics;
2623
2624               char
2625                 *text;
2626
2627               text=EscapeString(svg_info->text,'\'');
2628               (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
2629                 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
2630                 svg_info->center.y,text);
2631               text=DestroyString(text);
2632               draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
2633               draw_info->pointsize=svg_info->pointsize;
2634               draw_info->text=AcquireString(svg_info->text);
2635               (void) ConcatenateString(&draw_info->text," ");
2636               (void) GetTypeMetrics(svg_info->image,draw_info,&metrics,
2637                 svg_info->exception);
2638               svg_info->bounds.x+=metrics.width;
2639               draw_info=DestroyDrawInfo(draw_info);
2640               *svg_info->text='\0';
2641             }
2642           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2643           break;
2644         }
2645       if (LocaleCompare((const char *) name,"title") == 0)
2646         {
2647           if (*svg_info->text == '\0')
2648             break;
2649           (void) CloneString(&svg_info->title,svg_info->text);
2650           *svg_info->text='\0';
2651           break;
2652         }
2653       break;
2654     }
2655     case 'U':
2656     case 'u':
2657     {
2658       if (LocaleCompare((char *) name,"use") == 0)
2659         {
2660           if ((svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0))
2661             (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
2662               svg_info->bounds.x,svg_info->bounds.y);
2663           (void) FormatLocaleFile(svg_info->file,"use \"url(%s)\"\n",
2664             svg_info->url);
2665           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2666           break;
2667         }
2668       break;
2669     }
2670     default:
2671       break;
2672   }
2673   *svg_info->text='\0';
2674   (void) memset(&svg_info->element,0,sizeof(svg_info->element));
2675   (void) memset(&svg_info->segment,0,sizeof(svg_info->segment));
2676   svg_info->n--;
2677 }
2678
2679 static void SVGCharacters(void *context,const xmlChar *c,int length)
2680 {
2681   char
2682     *text;
2683
2684   register char
2685     *p;
2686
2687   register ssize_t
2688     i;
2689
2690   SVGInfo
2691     *svg_info;
2692
2693   /*
2694     Receiving some characters from the parser.
2695   */
2696   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2697     "  SAX.characters(%s,%.20g)",c,(double) length);
2698   svg_info=(SVGInfo *) context;
2699   text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2700   if (text == (char *) NULL)
2701     return;
2702   p=text;
2703   for (i=0; i < (ssize_t) length; i++)
2704     *p++=c[i];
2705   *p='\0';
2706   StripString(text);
2707   if (svg_info->text == (char *) NULL)
2708     svg_info->text=text;
2709   else
2710     {
2711       (void) ConcatenateString(&svg_info->text,text);
2712       text=DestroyString(text);
2713     }
2714 }
2715
2716 static void SVGReference(void *context,const xmlChar *name)
2717 {
2718   SVGInfo
2719     *svg_info;
2720
2721   xmlParserCtxtPtr
2722     parser;
2723
2724   /*
2725     Called when an entity reference is detected.
2726   */
2727   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.reference(%s)",
2728     name);
2729   svg_info=(SVGInfo *) context;
2730   parser=svg_info->parser;
2731   if (parser == (xmlParserCtxtPtr) NULL)
2732     return;
2733   if (parser->node == (xmlNodePtr) NULL)
2734     return;
2735   if (*name == '#')
2736     (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2737   else
2738     (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2739 }
2740
2741 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2742 {
2743   SVGInfo
2744     *svg_info;
2745
2746   /*
2747     Receiving some ignorable whitespaces from the parser.
2748   */
2749   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2750     "  SAX.ignorableWhitespace(%.30s, %d)",c,length);
2751   svg_info=(SVGInfo *) context;
2752   (void) svg_info;
2753 }
2754
2755 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2756   const xmlChar *data)
2757 {
2758   SVGInfo
2759     *svg_info;
2760
2761   /*
2762     A processing instruction has been parsed.
2763   */
2764   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2765     "  SAX.processingInstruction(%s, %s)",target,data);
2766   svg_info=(SVGInfo *) context;
2767   (void) svg_info;
2768 }
2769
2770 static void SVGComment(void *context,const xmlChar *value)
2771 {
2772   SVGInfo
2773     *svg_info;
2774
2775   /*
2776     A comment has been parsed.
2777   */
2778   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.comment(%s)",
2779     value);
2780   svg_info=(SVGInfo *) context;
2781   if (svg_info->comment != (char *) NULL)
2782     (void) ConcatenateString(&svg_info->comment,"\n");
2783   (void) ConcatenateString(&svg_info->comment,(const char *) value);
2784 }
2785
2786 static void SVGWarning(void *context,const char *format,...)
2787 {
2788   char
2789     *message,
2790     reason[MagickPathExtent];
2791
2792   SVGInfo
2793     *svg_info;
2794
2795   va_list
2796     operands;
2797
2798   /**
2799     Display and format a warning messages, gives file, line, position and
2800     extra parameters.
2801   */
2802   va_start(operands,format);
2803   svg_info=(SVGInfo *) context;
2804   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.warning: ");
2805   (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2806 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2807   (void) vsprintf(reason,format,operands);
2808 #else
2809   (void) vsnprintf(reason,MagickPathExtent,format,operands);
2810 #endif
2811   message=GetExceptionMessage(errno);
2812   (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2813     DelegateWarning,reason,"`%s`",message);
2814   message=DestroyString(message);
2815   va_end(operands);
2816 }
2817
2818 static void SVGError(void *context,const char *format,...)
2819 {
2820   char
2821     *message,
2822     reason[MagickPathExtent];
2823
2824   SVGInfo
2825     *svg_info;
2826
2827   va_list
2828     operands;
2829
2830   /*
2831     Display and format a error formats, gives file, line, position and
2832     extra parameters.
2833   */
2834   va_start(operands,format);
2835   svg_info=(SVGInfo *) context;
2836   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.error: ");
2837   (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2838 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2839   (void) vsprintf(reason,format,operands);
2840 #else
2841   (void) vsnprintf(reason,MagickPathExtent,format,operands);
2842 #endif
2843   message=GetExceptionMessage(errno);
2844   (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2845     reason,"`%s`",message);
2846   message=DestroyString(message);
2847   va_end(operands);
2848 }
2849
2850 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2851 {
2852   SVGInfo
2853     *svg_info;
2854
2855    xmlNodePtr
2856      child;
2857
2858   xmlParserCtxtPtr
2859     parser;
2860
2861   /*
2862     Called when a pcdata block has been parsed.
2863   */
2864   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.pcdata(%s, %d)",
2865     value,length);
2866   svg_info=(SVGInfo *) context;
2867   parser=svg_info->parser;
2868   child=xmlGetLastChild(parser->node);
2869   if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2870     {
2871       xmlTextConcat(child,value,length);
2872       return;
2873     }
2874   (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2875 }
2876
2877 static void SVGExternalSubset(void *context,const xmlChar *name,
2878   const xmlChar *external_id,const xmlChar *system_id)
2879 {
2880   SVGInfo
2881     *svg_info;
2882
2883   xmlParserCtxt
2884     parser_context;
2885
2886   xmlParserCtxtPtr
2887     parser;
2888
2889   xmlParserInputPtr
2890     input;
2891
2892   /*
2893     Does this document has an external subset?
2894   */
2895   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2896     "  SAX.externalSubset(%s, %s, %s)",name,
2897     (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2898     (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2899   svg_info=(SVGInfo *) context;
2900   parser=svg_info->parser;
2901   if (((external_id == NULL) && (system_id == NULL)) ||
2902       ((parser->validate == 0) || (parser->wellFormed == 0) ||
2903       (svg_info->document == 0)))
2904     return;
2905   input=SVGResolveEntity(context,external_id,system_id);
2906   if (input == NULL)
2907     return;
2908   (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2909   parser_context=(*parser);
2910   parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2911   if (parser->inputTab == (xmlParserInputPtr *) NULL)
2912     {
2913       parser->errNo=XML_ERR_NO_MEMORY;
2914       parser->input=parser_context.input;
2915       parser->inputNr=parser_context.inputNr;
2916       parser->inputMax=parser_context.inputMax;
2917       parser->inputTab=parser_context.inputTab;
2918       return;
2919   }
2920   parser->inputNr=0;
2921   parser->inputMax=5;
2922   parser->input=NULL;
2923   xmlPushInput(parser,input);
2924   (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2925   if (input->filename == (char *) NULL)
2926     input->filename=(char *) xmlStrdup(system_id);
2927   input->line=1;
2928   input->col=1;
2929   input->base=parser->input->cur;
2930   input->cur=parser->input->cur;
2931   input->free=NULL;
2932   xmlParseExternalSubset(parser,external_id,system_id);
2933   while (parser->inputNr > 1)
2934     (void) xmlPopInput(parser);
2935   xmlFreeInputStream(parser->input);
2936   xmlFree(parser->inputTab);
2937   parser->input=parser_context.input;
2938   parser->inputNr=parser_context.inputNr;
2939   parser->inputMax=parser_context.inputMax;
2940   parser->inputTab=parser_context.inputTab;
2941 }
2942
2943 #if defined(__cplusplus) || defined(c_plusplus)
2944 }
2945 #endif
2946
2947 /*
2948   Static declarations.
2949 */
2950 static char
2951   SVGDensityGeometry[] = "96.0x96.0";
2952
2953
2954 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2955 {
2956   char
2957     filename[MagickPathExtent];
2958
2959   FILE
2960     *file;
2961
2962   Image
2963     *image,
2964     *next;
2965
2966   int
2967     status,
2968     unique_file;
2969
2970   ssize_t
2971     n;
2972
2973   SVGInfo
2974     *svg_info;
2975
2976   unsigned char
2977     message[MagickPathExtent];
2978
2979   xmlSAXHandler
2980     sax_modules;
2981
2982   xmlSAXHandlerPtr
2983     sax_handler;
2984
2985   /*
2986     Open image file.
2987   */
2988   assert(image_info != (const ImageInfo *) NULL);
2989   assert(image_info->signature == MagickCoreSignature);
2990   assert(exception != (ExceptionInfo *) NULL);
2991   if (image_info->debug != MagickFalse)
2992     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2993       image_info->filename);
2994   assert(exception->signature == MagickCoreSignature);
2995   image=AcquireImage(image_info,exception);
2996   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2997   if (status == MagickFalse)
2998     {
2999       image=DestroyImageList(image);
3000       return((Image *) NULL);
3001     }
3002   if ((fabs(image->resolution.x) < MagickEpsilon) ||
3003       (fabs(image->resolution.y) < MagickEpsilon))
3004     {
3005       GeometryInfo
3006         geometry_info;
3007
3008       int
3009         flags;
3010
3011       flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
3012       image->resolution.x=geometry_info.rho;
3013       image->resolution.y=geometry_info.sigma;
3014       if ((flags & SigmaValue) == 0)
3015         image->resolution.y=image->resolution.x;
3016     }
3017   if (LocaleCompare(image_info->magick,"MSVG") != 0)
3018     {
3019       const DelegateInfo
3020         *delegate_info;
3021
3022       delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception);
3023       if (delegate_info != (const DelegateInfo *) NULL)
3024         {
3025           char
3026             background[MagickPathExtent],
3027             command[MagickPathExtent],
3028             *density,
3029             input_filename[MagickPathExtent],
3030             opacity[MagickPathExtent],
3031             output_filename[MagickPathExtent],
3032             unique[MagickPathExtent];
3033
3034           int
3035             status;
3036
3037           struct stat
3038             attributes;
3039
3040           /*
3041             Our best hope for compliance with the SVG standard.
3042           */
3043           status=AcquireUniqueSymbolicLink(image->filename,input_filename);
3044           (void) AcquireUniqueFilename(output_filename);
3045           (void) AcquireUniqueFilename(unique);
3046           density=AcquireString("");
3047           (void) FormatLocaleString(density,MagickPathExtent,"%.20g,%.20g",
3048             image->resolution.x,image->resolution.y);
3049           (void) FormatLocaleString(background,MagickPathExtent,
3050             "rgb(%.20g%%,%.20g%%,%.20g%%)",
3051             100.0*QuantumScale*image->background_color.red,
3052             100.0*QuantumScale*image->background_color.green,
3053             100.0*QuantumScale*image->background_color.blue);
3054           (void) FormatLocaleString(opacity,MagickPathExtent,"%.20g",
3055             QuantumScale*image->background_color.alpha);
3056           (void) FormatLocaleString(command,MagickPathExtent,
3057             GetDelegateCommands(delegate_info),input_filename,output_filename,
3058             density,background,opacity,unique);
3059           density=DestroyString(density);
3060           status=ExternalDelegateCommand(MagickFalse,image_info->verbose,
3061             command,(char *) NULL,exception);
3062           (void) RelinquishUniqueFileResource(unique);
3063           (void) RelinquishUniqueFileResource(input_filename);
3064           if ((status == 0) && (stat(output_filename,&attributes) == 0) &&
3065               (attributes.st_size > 0))
3066             {
3067               Image
3068                 *svg_image;
3069
3070               ImageInfo
3071                 *read_info;
3072
3073               read_info=CloneImageInfo(image_info);
3074               (void) CopyMagickString(read_info->filename,output_filename,
3075                 MagickPathExtent);
3076               svg_image=ReadImage(read_info,exception);
3077               read_info=DestroyImageInfo(read_info);
3078               (void) RelinquishUniqueFileResource(output_filename);
3079               if (svg_image != (Image *) NULL)
3080                 {
3081                   for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; )
3082                   {
3083                     (void) CopyMagickString(next->filename,image->filename,
3084                       MaxTextExtent);
3085                     (void) CopyMagickString(next->magick,image->magick,
3086                       MaxTextExtent);
3087                     next=GetNextImageInList(next);
3088                   }
3089                   image=DestroyImage(image);
3090                   return(svg_image);
3091                 }
3092             }
3093           (void) RelinquishUniqueFileResource(output_filename);
3094         }
3095       {
3096 #if defined(MAGICKCORE_RSVG_DELEGATE)
3097 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3098         cairo_surface_t
3099           *cairo_surface;
3100
3101         cairo_t
3102           *cairo_image;
3103
3104         MagickBooleanType
3105           apply_density;
3106
3107         MemoryInfo
3108           *pixel_info;
3109
3110         register unsigned char
3111           *p;
3112
3113         RsvgDimensionData
3114           dimension_info;
3115
3116         unsigned char
3117           *pixels;
3118
3119 #else
3120         GdkPixbuf
3121           *pixel_buffer;
3122
3123         register const guchar
3124           *p;
3125 #endif
3126
3127         GError
3128           *error;
3129
3130         PixelInfo
3131           fill_color;
3132
3133         register ssize_t
3134           x;
3135
3136         register Quantum
3137           *q;
3138
3139         RsvgHandle
3140           *svg_handle;
3141
3142         ssize_t
3143           y;
3144
3145         svg_handle=rsvg_handle_new();
3146         if (svg_handle == (RsvgHandle *) NULL)
3147           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3148         rsvg_handle_set_base_uri(svg_handle,image_info->filename);
3149         if ((fabs(image->resolution.x) > MagickEpsilon) &&
3150             (fabs(image->resolution.y) > MagickEpsilon))
3151           rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3152             image->resolution.y);
3153         while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3154         {
3155           message[n]='\0';
3156           error=(GError *) NULL;
3157           (void) rsvg_handle_write(svg_handle,message,n,&error);
3158           if (error != (GError *) NULL)
3159             g_error_free(error);
3160         }
3161         error=(GError *) NULL;
3162         rsvg_handle_close(svg_handle,&error);
3163         if (error != (GError *) NULL)
3164           g_error_free(error);
3165 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3166         apply_density=MagickTrue;
3167         rsvg_handle_get_dimensions(svg_handle,&dimension_info);
3168         if ((image->resolution.x > 0.0) && (image->resolution.y > 0.0))
3169           {
3170             RsvgDimensionData
3171               dpi_dimension_info;
3172
3173             /*
3174               We should not apply the density when the internal 'factor' is 'i'.
3175               This can be checked by using the trick below.
3176             */
3177             rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x*256,
3178               image->resolution.y*256);
3179             rsvg_handle_get_dimensions(svg_handle,&dpi_dimension_info);
3180             if ((dpi_dimension_info.width != dimension_info.width) ||
3181                 (dpi_dimension_info.height != dimension_info.height))
3182               apply_density=MagickFalse;
3183             rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
3184               image->resolution.y);
3185           }
3186         if (image_info->size != (char *) NULL)
3187           {
3188             (void) GetGeometry(image_info->size,(ssize_t *) NULL,
3189               (ssize_t *) NULL,&image->columns,&image->rows);
3190             if ((image->columns != 0) || (image->rows != 0))
3191               {
3192                 image->resolution.x=96.0*image->columns/dimension_info.width;
3193                 image->resolution.y=96.0*image->rows/dimension_info.height;
3194                 if (fabs(image->resolution.x) < MagickEpsilon)
3195                   image->resolution.x=image->resolution.y;
3196                 else
3197                   if (fabs(image->resolution.y) < MagickEpsilon)
3198                     image->resolution.y=image->resolution.x;
3199                   else
3200                     image->resolution.x=image->resolution.y=MagickMin(
3201                       image->resolution.x,image->resolution.y);
3202                 apply_density=MagickTrue;
3203               }
3204           }
3205         if (apply_density != MagickFalse)
3206           {
3207             image->columns=image->resolution.x*dimension_info.width/96.0;
3208             image->rows=image->resolution.y*dimension_info.height/96.0;
3209           }
3210         else
3211           {
3212             image->columns=dimension_info.width;
3213             image->rows=dimension_info.height;
3214           }
3215         pixel_info=(MemoryInfo *) NULL;
3216 #else
3217         pixel_buffer=rsvg_handle_get_pixbuf(svg_handle);
3218         rsvg_handle_free(svg_handle);
3219         image->columns=gdk_pixbuf_get_width(pixel_buffer);
3220         image->rows=gdk_pixbuf_get_height(pixel_buffer);
3221 #endif
3222         image->alpha_trait=BlendPixelTrait;
3223         status=SetImageExtent(image,image->columns,image->rows,exception);
3224         if (status == MagickFalse)
3225           {
3226 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
3227             g_object_unref(G_OBJECT(pixel_buffer));
3228 #endif
3229             g_object_unref(svg_handle);
3230             ThrowReaderException(MissingDelegateError,
3231               "NoDecodeDelegateForThisImageFormat");
3232           }
3233         if (image_info->ping == MagickFalse)
3234           {
3235 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3236             size_t
3237               stride;
3238
3239             stride=4*image->columns;
3240 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
3241             stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
3242               (int) image->columns);
3243 #endif
3244             pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels));
3245             if (pixel_info == (MemoryInfo *) NULL)
3246               {
3247                 g_object_unref(svg_handle);
3248                 ThrowReaderException(ResourceLimitError,
3249                   "MemoryAllocationFailed");
3250               }
3251             pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3252 #endif
3253             (void) SetImageBackgroundColor(image,exception);
3254 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3255             cairo_surface=cairo_image_surface_create_for_data(pixels,
3256               CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int)
3257               stride);
3258             if ((cairo_surface == (cairo_surface_t *) NULL) ||
3259                 (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS))
3260               {
3261                 if (cairo_surface != (cairo_surface_t *) NULL)
3262                   cairo_surface_destroy(cairo_surface);
3263                 pixel_info=RelinquishVirtualMemory(pixel_info);
3264                 g_object_unref(svg_handle);
3265                 ThrowReaderException(ResourceLimitError,
3266                   "MemoryAllocationFailed");
3267               }
3268             cairo_image=cairo_create(cairo_surface);
3269             cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
3270             cairo_paint(cairo_image);
3271             cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
3272             if (apply_density != MagickFalse)
3273               cairo_scale(cairo_image,image->resolution.x/96.0,
3274                 image->resolution.y/96.0);
3275             rsvg_handle_render_cairo(svg_handle,cairo_image);
3276             cairo_destroy(cairo_image);
3277             cairo_surface_destroy(cairo_surface);
3278             g_object_unref(svg_handle);
3279             p=pixels;
3280 #else
3281             p=gdk_pixbuf_get_pixels(pixel_buffer);
3282 #endif
3283             GetPixelInfo(image,&fill_color);
3284             for (y=0; y < (ssize_t) image->rows; y++)
3285             {
3286               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3287               if (q == (Quantum *) NULL)
3288                 break;
3289               for (x=0; x < (ssize_t) image->columns; x++)
3290               {
3291 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3292                 fill_color.blue=ScaleCharToQuantum(*p++);
3293                 fill_color.green=ScaleCharToQuantum(*p++);
3294                 fill_color.red=ScaleCharToQuantum(*p++);
3295 #else
3296                 fill_color.red=ScaleCharToQuantum(*p++);
3297                 fill_color.green=ScaleCharToQuantum(*p++);
3298                 fill_color.blue=ScaleCharToQuantum(*p++);
3299 #endif
3300                 fill_color.alpha=ScaleCharToQuantum(*p++);
3301 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3302                 {
3303                   double
3304                     gamma;
3305
3306                   gamma=QuantumScale*fill_color.alpha;
3307                   gamma=PerceptibleReciprocal(gamma);
3308                   fill_color.blue*=gamma;
3309                   fill_color.green*=gamma;
3310                   fill_color.red*=gamma;
3311                 }
3312 #endif
3313                 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
3314                   GetPixelAlpha(image,q),q);
3315                 q+=GetPixelChannels(image);
3316               }
3317               if (SyncAuthenticPixels(image,exception) == MagickFalse)
3318                 break;
3319               if (image->previous == (Image *) NULL)
3320                 {
3321                   status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
3322                     y,image->rows);
3323                   if (status == MagickFalse)
3324                     break;
3325                 }
3326             }
3327           }
3328 #if defined(MAGICKCORE_CAIRO_DELEGATE)
3329         if (pixel_info != (MemoryInfo *) NULL)
3330           pixel_info=RelinquishVirtualMemory(pixel_info);
3331 #else
3332         g_object_unref(G_OBJECT(pixel_buffer));
3333 #endif
3334         (void) CloseBlob(image);
3335         for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3336         {
3337           (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3338           (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3339           next=GetNextImageInList(next);
3340         }
3341         return(GetFirstImageInList(image));
3342 #endif
3343       }
3344     }
3345   /*
3346     Open draw file.
3347   */
3348   file=(FILE *) NULL;
3349   unique_file=AcquireUniqueFileResource(filename);
3350   if (unique_file != -1)
3351     file=fdopen(unique_file,"w");
3352   if ((unique_file == -1) || (file == (FILE *) NULL))
3353     {
3354       (void) CopyMagickString(image->filename,filename,MagickPathExtent);
3355       ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
3356         image->filename);
3357       image=DestroyImageList(image);
3358       return((Image *) NULL);
3359     }
3360   /*
3361     Parse SVG file.
3362   */
3363   svg_info=AcquireSVGInfo();
3364   if (svg_info == (SVGInfo *) NULL)
3365     {
3366       (void) fclose(file);
3367       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3368     }
3369   svg_info->file=file;
3370   svg_info->exception=exception;
3371   svg_info->image=image;
3372   svg_info->image_info=image_info;
3373   svg_info->bounds.width=image->columns;
3374   svg_info->bounds.height=image->rows;
3375   svg_info->svgDepth=0;
3376   if (image_info->size != (char *) NULL)
3377     (void) CloneString(&svg_info->size,image_info->size);
3378   if (image->debug != MagickFalse)
3379     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3380   (void) xmlSubstituteEntitiesDefault(1);
3381   (void) memset(&sax_modules,0,sizeof(sax_modules));
3382   sax_modules.internalSubset=SVGInternalSubset;
3383   sax_modules.isStandalone=SVGIsStandalone;
3384   sax_modules.hasInternalSubset=SVGHasInternalSubset;
3385   sax_modules.hasExternalSubset=SVGHasExternalSubset;
3386   sax_modules.resolveEntity=SVGResolveEntity;
3387   sax_modules.getEntity=SVGGetEntity;
3388   sax_modules.entityDecl=SVGEntityDeclaration;
3389   sax_modules.notationDecl=SVGNotationDeclaration;
3390   sax_modules.attributeDecl=SVGAttributeDeclaration;
3391   sax_modules.elementDecl=SVGElementDeclaration;
3392   sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3393   sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3394   sax_modules.startDocument=SVGStartDocument;
3395   sax_modules.endDocument=SVGEndDocument;
3396   sax_modules.startElement=SVGStartElement;
3397   sax_modules.endElement=SVGEndElement;
3398   sax_modules.reference=SVGReference;
3399   sax_modules.characters=SVGCharacters;
3400   sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3401   sax_modules.processingInstruction=SVGProcessingInstructions;
3402   sax_modules.comment=SVGComment;
3403   sax_modules.warning=SVGWarning;
3404   sax_modules.error=SVGError;
3405   sax_modules.fatalError=SVGError;
3406   sax_modules.getParameterEntity=SVGGetParameterEntity;
3407   sax_modules.cdataBlock=SVGCDataBlock;
3408   sax_modules.externalSubset=SVGExternalSubset;
3409   sax_handler=(&sax_modules);
3410   n=ReadBlob(image,MagickPathExtent-1,message);
3411   message[n]='\0';
3412   if (n > 0)
3413     {
3414       svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3415         message,n,image->filename);
3416       while ((n=ReadBlob(image,MagickPathExtent-1,message)) != 0)
3417       {
3418         message[n]='\0';
3419         status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3420         if (status != 0)
3421           break;
3422       }
3423     }
3424   (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3425   SVGEndDocument(svg_info);
3426   xmlFreeParserCtxt(svg_info->parser);
3427   if (image->debug != MagickFalse)
3428     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3429   (void) fclose(file);
3430   (void) CloseBlob(image);
3431   image->columns=svg_info->width;
3432   image->rows=svg_info->height;
3433   if (exception->severity >= ErrorException)
3434     {
3435       svg_info=DestroySVGInfo(svg_info);
3436       (void) RelinquishUniqueFileResource(filename);
3437       image=DestroyImage(image);
3438       return((Image *) NULL);
3439     }
3440   if (image_info->ping == MagickFalse)
3441     {
3442       ImageInfo
3443         *read_info;
3444
3445       /*
3446         Draw image.
3447       */
3448       image=DestroyImage(image);
3449       image=(Image *) NULL;
3450       read_info=CloneImageInfo(image_info);
3451       SetImageInfoBlob(read_info,(void *) NULL,0);
3452       if (read_info->density != (char *) NULL)
3453         read_info->density=DestroyString(read_info->density);
3454       (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
3455         filename);
3456       image=ReadImage(read_info,exception);
3457       read_info=DestroyImageInfo(read_info);
3458       if (image != (Image *) NULL)
3459         (void) CopyMagickString(image->filename,image_info->filename,
3460           MagickPathExtent);
3461     }
3462   /*
3463     Relinquish resources.
3464   */
3465   if (image != (Image *) NULL)
3466     {
3467       if (svg_info->title != (char *) NULL)
3468         (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3469       if (svg_info->comment != (char *) NULL)
3470         (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3471           exception);
3472     }
3473   for (next=GetFirstImageInList(image); next != (Image *) NULL; )
3474   {
3475     (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
3476     (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
3477     next=GetNextImageInList(next);
3478   }
3479   svg_info=DestroySVGInfo(svg_info);
3480   (void) RelinquishUniqueFileResource(filename);
3481   return(GetFirstImageInList(image));
3482 }
3483 #endif
3484 \f
3485 /*
3486 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3487 %                                                                             %
3488 %                                                                             %
3489 %                                                                             %
3490 %   R e g i s t e r S V G I m a g e                                           %
3491 %                                                                             %
3492 %                                                                             %
3493 %                                                                             %
3494 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3495 %
3496 %  RegisterSVGImage() adds attributes for the SVG image format to
3497 %  the list of supported formats.  The attributes include the image format
3498 %  tag, a method to read and/or write the format, whether the format
3499 %  supports the saving of more than one frame to the same file or blob,
3500 %  whether the format supports native in-memory I/O, and a brief
3501 %  description of the format.
3502 %
3503 %  The format of the RegisterSVGImage method is:
3504 %
3505 %      size_t RegisterSVGImage(void)
3506 %
3507 */
3508 ModuleExport size_t RegisterSVGImage(void)
3509 {
3510   char
3511     version[MagickPathExtent];
3512
3513   MagickInfo
3514     *entry;
3515
3516   *version='\0';
3517 #if defined(LIBXML_DOTTED_VERSION)
3518   (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,
3519     MagickPathExtent);
3520 #endif
3521 #if defined(MAGICKCORE_RSVG_DELEGATE)
3522 #if !GLIB_CHECK_VERSION(2,35,0)
3523   g_type_init();
3524 #endif
3525 #if defined(MAGICKCORE_XML_DELEGATE)
3526   xmlInitParser();
3527 #endif
3528   (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d",
3529     LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3530 #endif
3531   entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics");
3532 #if defined(MAGICKCORE_XML_DELEGATE)
3533   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3534 #endif
3535   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3536   entry->flags^=CoderBlobSupportFlag;
3537 #if defined(MAGICKCORE_RSVG_DELEGATE)
3538   entry->flags^=CoderDecoderThreadSupportFlag;
3539 #endif
3540   entry->mime_type=ConstantString("image/svg+xml");
3541   if (*version != '\0')
3542     entry->version=ConstantString(version);
3543   entry->magick=(IsImageFormatHandler *) IsSVG;
3544   (void) RegisterMagickInfo(entry);
3545   entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics");
3546 #if defined(MAGICKCORE_XML_DELEGATE)
3547   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3548 #endif
3549   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3550   entry->flags^=CoderBlobSupportFlag;
3551 #if defined(MAGICKCORE_RSVG_DELEGATE)
3552   entry->flags^=CoderDecoderThreadSupportFlag;
3553 #endif
3554   entry->mime_type=ConstantString("image/svg+xml");
3555   if (*version != '\0')
3556     entry->version=ConstantString(version);
3557   entry->magick=(IsImageFormatHandler *) IsSVG;
3558   (void) RegisterMagickInfo(entry);
3559   entry=AcquireMagickInfo("SVG","MSVG",
3560     "ImageMagick's own SVG internal renderer");
3561 #if defined(MAGICKCORE_XML_DELEGATE)
3562   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3563 #endif
3564   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3565   entry->flags^=CoderBlobSupportFlag;
3566 #if defined(MAGICKCORE_RSVG_DELEGATE)
3567   entry->flags^=CoderDecoderThreadSupportFlag;
3568 #endif
3569   entry->magick=(IsImageFormatHandler *) IsSVG;
3570   (void) RegisterMagickInfo(entry);
3571   return(MagickImageCoderSignature);
3572 }
3573 \f
3574 /*
3575 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3576 %                                                                             %
3577 %                                                                             %
3578 %                                                                             %
3579 %   U n r e g i s t e r S V G I m a g e                                       %
3580 %                                                                             %
3581 %                                                                             %
3582 %                                                                             %
3583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3584 %
3585 %  UnregisterSVGImage() removes format registrations made by the
3586 %  SVG module from the list of supported formats.
3587 %
3588 %  The format of the UnregisterSVGImage method is:
3589 %
3590 %      UnregisterSVGImage(void)
3591 %
3592 */
3593 ModuleExport void UnregisterSVGImage(void)
3594 {
3595   (void) UnregisterMagickInfo("SVGZ");
3596   (void) UnregisterMagickInfo("SVG");
3597   (void) UnregisterMagickInfo("MSVG");
3598 #if defined(MAGICKCORE_XML_DELEGATE)
3599   xmlCleanupParser();
3600 #endif
3601 }
3602 \f
3603 /*
3604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3605 %                                                                             %
3606 %                                                                             %
3607 %                                                                             %
3608 %   W r i t e S V G I m a g e                                                 %
3609 %                                                                             %
3610 %                                                                             %
3611 %                                                                             %
3612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3613 %
3614 %  WriteSVGImage() writes a image in the SVG - XML based W3C standard
3615 %  format.
3616 %
3617 %  The format of the WriteSVGImage method is:
3618 %
3619 %      MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3620 %        Image *image,ExceptionInfo *exception)
3621 %
3622 %  A description of each parameter follows.
3623 %
3624 %    o image_info: the image info.
3625 %
3626 %    o image:  The image.
3627 %
3628 %    o exception: return any errors or warnings in this structure.
3629 %
3630 */
3631
3632 static void AffineToTransform(Image *image,AffineMatrix *affine)
3633 {
3634   char
3635     transform[MagickPathExtent];
3636
3637   if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3638     {
3639       if ((fabs(affine->rx) < MagickEpsilon) &&
3640           (fabs(affine->ry) < MagickEpsilon))
3641         {
3642           if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3643               (fabs(affine->sy-1.0) < MagickEpsilon))
3644             {
3645               (void) WriteBlobString(image,"\">\n");
3646               return;
3647             }
3648           (void) FormatLocaleString(transform,MagickPathExtent,
3649             "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3650           (void) WriteBlobString(image,transform);
3651           return;
3652         }
3653       else
3654         {
3655           if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3656               (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3657               (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3658                2*MagickEpsilon))
3659             {
3660               double
3661                 theta;
3662
3663               theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3664               (void) FormatLocaleString(transform,MagickPathExtent,
3665                 "\" transform=\"rotate(%g)\">\n",theta);
3666               (void) WriteBlobString(image,transform);
3667               return;
3668             }
3669         }
3670     }
3671   else
3672     {
3673       if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3674           (fabs(affine->rx) < MagickEpsilon) &&
3675           (fabs(affine->ry) < MagickEpsilon) &&
3676           (fabs(affine->sy-1.0) < MagickEpsilon))
3677         {
3678           (void) FormatLocaleString(transform,MagickPathExtent,
3679             "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3680           (void) WriteBlobString(image,transform);
3681           return;
3682         }
3683     }
3684   (void) FormatLocaleString(transform,MagickPathExtent,
3685     "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3686     affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3687   (void) WriteBlobString(image,transform);
3688 }
3689
3690 static MagickBooleanType IsPoint(const char *point)
3691 {
3692   char
3693     *p;
3694
3695   ssize_t
3696     value;
3697
3698   value=strtol(point,&p,10);
3699   (void) value;
3700   return(p != point ? MagickTrue : MagickFalse);
3701 }
3702
3703 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
3704 {
3705 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3706   {
3707     at_bitmap_type
3708       *trace;
3709
3710     at_fitting_opts_type
3711       *fitting_options;
3712
3713     at_output_opts_type
3714       *output_options;
3715
3716     at_splines_type
3717       *splines;
3718
3719     ImageType
3720       type;
3721
3722     register const Quantum
3723       *p;
3724
3725     register ssize_t
3726       i,
3727       x;
3728
3729     size_t
3730       number_planes;
3731
3732     ssize_t
3733       y;
3734
3735     /*
3736       Trace image and write as SVG.
3737     */
3738     fitting_options=at_fitting_opts_new();
3739     output_options=at_output_opts_new();
3740     (void) SetImageGray(image,exception);
3741     type=GetImageType(image);
3742     number_planes=3;
3743     if ((type == BilevelType) || (type == GrayscaleType))
3744       number_planes=1;
3745     trace=at_bitmap_new(image->columns,image->rows,number_planes);
3746     i=0;
3747     for (y=0; y < (ssize_t) image->rows; y++)
3748     {
3749       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3750       if (p == (const Quantum *) NULL)
3751         break;
3752       for (x=0; x < (ssize_t) image->columns; x++)
3753       {
3754         trace->bitmap[i++]=GetPixelRed(image,p);
3755         if (number_planes == 3)
3756           {
3757             trace->bitmap[i++]=GetPixelGreen(image,p);
3758             trace->bitmap[i++]=GetPixelBlue(image,p);
3759           }
3760         p+=GetPixelChannels(image);
3761       }
3762     }
3763     splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3764       NULL);
3765     at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3766       GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3767       NULL);
3768     /*
3769       Free resources.
3770     */
3771     at_splines_free(splines);
3772     at_bitmap_free(trace);
3773     at_output_opts_free(output_options);
3774     at_fitting_opts_free(fitting_options);
3775   }
3776 #else
3777   {
3778     char
3779       *base64,
3780       message[MagickPathExtent];
3781
3782     Image
3783       *clone_image;
3784
3785     ImageInfo
3786       *image_info;
3787
3788     register char
3789       *p;
3790
3791     size_t
3792       blob_length,
3793       encode_length;
3794
3795     ssize_t
3796       i;
3797
3798     unsigned char
3799       *blob;
3800
3801     (void) WriteBlobString(image,
3802       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
3803     (void) WriteBlobString(image,
3804       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
3805     (void) WriteBlobString(image,
3806       " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
3807     (void) FormatLocaleString(message,MagickPathExtent,
3808       "<svg version=\"1.1\" id=\"Layer_1\" "
3809       "xmlns=\"http://www.w3.org/2000/svg\" "
3810       "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" "
3811       "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" "
3812       "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">",
3813       (double) image->columns,(double) image->rows,
3814       (double) image->columns,(double) image->rows,
3815       (double) image->columns,(double) image->rows);
3816     (void) WriteBlobString(image,message);
3817     clone_image=CloneImage(image,0,0,MagickTrue,exception);
3818     if (clone_image == (Image *) NULL)
3819       return(MagickFalse);
3820     image_info=AcquireImageInfo();
3821     (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent);
3822     blob_length=2048;
3823     blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length,
3824       exception);
3825     clone_image=DestroyImage(clone_image);
3826     image_info=DestroyImageInfo(image_info);
3827     if (blob == (unsigned char *) NULL)
3828       return(MagickFalse);
3829     encode_length=0;
3830     base64=Base64Encode(blob,blob_length,&encode_length);
3831     blob=(unsigned char *) RelinquishMagickMemory(blob);
3832     (void) FormatLocaleString(message,MagickPathExtent,
3833       "  <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" "
3834       "x=\"%.20g\" y=\"%.20g\"\n    href=\"data:image/png;base64,",
3835       (double) image->scene,(double) image->columns,(double) image->rows,
3836       (double) image->page.x,(double) image->page.y);
3837     (void) WriteBlobString(image,message);
3838     p=base64;
3839     for (i=(ssize_t) encode_length; i > 0; i-=76)
3840     {
3841       (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p);
3842       (void) WriteBlobString(image,message);
3843       p+=76;
3844       if (i > 76)
3845         (void) WriteBlobString(image,"\n");
3846     }
3847     base64=DestroyString(base64);
3848     (void) WriteBlobString(image,"\" />\n");
3849     (void) WriteBlobString(image,"</svg>\n");
3850   }
3851 #endif
3852   CloseBlob(image);
3853   return(MagickTrue);
3854 }
3855
3856 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
3857   ExceptionInfo *exception)
3858 {
3859 #define BezierQuantum  200
3860
3861   AffineMatrix
3862     affine;
3863
3864   char
3865     keyword[MagickPathExtent],
3866     message[MagickPathExtent],
3867     name[MagickPathExtent],
3868     *next_token,
3869     *token,
3870     type[MagickPathExtent];
3871
3872   const char
3873     *p,
3874     *q,
3875     *value;
3876
3877   int
3878     n;
3879
3880   ssize_t
3881     j;
3882
3883   MagickBooleanType
3884     active,
3885     status;
3886
3887   PointInfo
3888     point;
3889
3890   PrimitiveInfo
3891     *primitive_info;
3892
3893   PrimitiveType
3894     primitive_type;
3895
3896   register ssize_t
3897     x;
3898
3899   register ssize_t
3900     i;
3901
3902   size_t
3903     extent,
3904     length,
3905     number_points;
3906
3907   SVGInfo
3908     svg_info;
3909
3910   /*
3911     Open output image file.
3912   */
3913   assert(image_info != (const ImageInfo *) NULL);
3914   assert(image_info->signature == MagickCoreSignature);
3915   assert(image != (Image *) NULL);
3916   assert(image->signature == MagickCoreSignature);
3917   if (image->debug != MagickFalse)
3918     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3919   assert(exception != (ExceptionInfo *) NULL);
3920   assert(exception->signature == MagickCoreSignature);
3921   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3922   if (status == MagickFalse)
3923     return(status);
3924   value=GetImageArtifact(image,"SVG");
3925   if (value != (char *) NULL)
3926     {
3927       (void) WriteBlobString(image,value);
3928       (void) CloseBlob(image);
3929       return(MagickTrue);
3930     }
3931   value=GetImageArtifact(image,"MVG");
3932   if (value == (char *) NULL)
3933     return(TraceSVGImage(image,exception));
3934   /*
3935     Write SVG header.
3936   */
3937   (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3938   (void) WriteBlobString(image,
3939     "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3940   (void) WriteBlobString(image,
3941     "  \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3942   (void) FormatLocaleString(message,MagickPathExtent,
3943     "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3944     image->rows);
3945   (void) WriteBlobString(image,message);
3946   /*
3947     Allocate primitive info memory.
3948   */
3949   number_points=2047;
3950   primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3951     sizeof(*primitive_info));
3952   if (primitive_info == (PrimitiveInfo *) NULL)
3953     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3954   GetAffineMatrix(&affine);
3955   token=AcquireString(value);
3956   extent=strlen(token)+MagickPathExtent;
3957   active=MagickFalse;
3958   n=0;
3959   status=MagickTrue;
3960   for (q=(const char *) value; *q != '\0'; )
3961   {
3962     /*
3963       Interpret graphic primitive.
3964     */
3965     GetNextToken(q,&q,MagickPathExtent,keyword);
3966     if (*keyword == '\0')
3967       break;
3968     if (*keyword == '#')
3969       {
3970         /*
3971           Comment.
3972         */
3973         if (active != MagickFalse)
3974           {
3975             AffineToTransform(image,&affine);
3976             active=MagickFalse;
3977           }
3978         (void) WriteBlobString(image,"<desc>");
3979         (void) WriteBlobString(image,keyword+1);
3980         for ( ; (*q != '\n') && (*q != '\0'); q++)
3981           switch (*q)
3982           {
3983             case '<': (void) WriteBlobString(image,"&lt;"); break;
3984             case '>': (void) WriteBlobString(image,"&gt;"); break;
3985             case '&': (void) WriteBlobString(image,"&amp;"); break;
3986             default: (void) WriteBlobByte(image,*q); break;
3987           }
3988         (void) WriteBlobString(image,"</desc>\n");
3989         continue;
3990       }
3991     primitive_type=UndefinedPrimitive;
3992     switch (*keyword)
3993     {
3994       case ';':
3995         break;
3996       case 'a':
3997       case 'A':
3998       {
3999         if (LocaleCompare("affine",keyword) == 0)
4000           {
4001             GetNextToken(q,&q,extent,token);
4002             affine.sx=StringToDouble(token,&next_token);
4003             GetNextToken(q,&q,extent,token);
4004             if (*token == ',')
4005               GetNextToken(q,&q,extent,token);
4006             affine.rx=StringToDouble(token,&next_token);
4007             GetNextToken(q,&q,extent,token);
4008             if (*token == ',')
4009               GetNextToken(q,&q,extent,token);
4010             affine.ry=StringToDouble(token,&next_token);
4011             GetNextToken(q,&q,extent,token);
4012             if (*token == ',')
4013               GetNextToken(q,&q,extent,token);
4014             affine.sy=StringToDouble(token,&next_token);
4015             GetNextToken(q,&q,extent,token);
4016             if (*token == ',')
4017               GetNextToken(q,&q,extent,token);
4018             affine.tx=StringToDouble(token,&next_token);
4019             GetNextToken(q,&q,extent,token);
4020             if (*token == ',')
4021               GetNextToken(q,&q,extent,token);
4022             affine.ty=StringToDouble(token,&next_token);
4023             break;
4024           }
4025         if (LocaleCompare("alpha",keyword) == 0)
4026           {
4027             primitive_type=AlphaPrimitive;
4028             break;
4029           }
4030         if (LocaleCompare("angle",keyword) == 0)
4031           {
4032             GetNextToken(q,&q,extent,token);
4033             affine.rx=StringToDouble(token,&next_token);
4034             affine.ry=StringToDouble(token,&next_token);
4035             break;
4036           }
4037         if (LocaleCompare("arc",keyword) == 0)
4038           {
4039             primitive_type=ArcPrimitive;
4040             break;
4041           }
4042         status=MagickFalse;
4043         break;
4044       }
4045       case 'b':
4046       case 'B':
4047       {
4048         if (LocaleCompare("bezier",keyword) == 0)
4049           {
4050             primitive_type=BezierPrimitive;
4051             break;
4052           }
4053         status=MagickFalse;
4054         break;
4055       }
4056       case 'c':
4057       case 'C':
4058       {
4059         if (LocaleCompare("clip-path",keyword) == 0)
4060           {
4061             GetNextToken(q,&q,extent,token);
4062             (void) FormatLocaleString(message,MagickPathExtent,
4063               "clip-path:url(#%s);",token);
4064             (void) WriteBlobString(image,message);
4065             break;
4066           }
4067         if (LocaleCompare("clip-rule",keyword) == 0)
4068           {
4069             GetNextToken(q,&q,extent,token);
4070             (void) FormatLocaleString(message,MagickPathExtent,"clip-rule:%s;",
4071               token);
4072             (void) WriteBlobString(image,message);
4073             break;
4074           }
4075         if (LocaleCompare("clip-units",keyword) == 0)
4076           {
4077             GetNextToken(q,&q,extent,token);
4078             (void) FormatLocaleString(message,MagickPathExtent,
4079               "clipPathUnits=%s;",token);
4080             (void) WriteBlobString(image,message);
4081             break;
4082           }
4083         if (LocaleCompare("circle",keyword) == 0)
4084           {
4085             primitive_type=CirclePrimitive;
4086             break;
4087           }
4088         if (LocaleCompare("color",keyword) == 0)
4089           {
4090             primitive_type=ColorPrimitive;
4091             break;
4092           }
4093         status=MagickFalse;
4094         break;
4095       }
4096       case 'd':
4097       case 'D':
4098       {
4099         if (LocaleCompare("decorate",keyword) == 0)
4100           {
4101             GetNextToken(q,&q,extent,token);
4102             (void) FormatLocaleString(message,MagickPathExtent,
4103               "text-decoration:%s;",token);
4104             (void) WriteBlobString(image,message);
4105             break;
4106           }
4107         status=MagickFalse;
4108         break;
4109       }
4110       case 'e':
4111       case 'E':
4112       {
4113         if (LocaleCompare("ellipse",keyword) == 0)
4114           {
4115             primitive_type=EllipsePrimitive;
4116             break;
4117           }
4118         status=MagickFalse;
4119         break;
4120       }
4121       case 'f':
4122       case 'F':
4123       {
4124         if (LocaleCompare("fill",keyword) == 0)
4125           {
4126             GetNextToken(q,&q,extent,token);
4127             (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;",
4128               token);
4129             (void) WriteBlobString(image,message);
4130             break;
4131           }
4132         if (LocaleCompare("fill-rule",keyword) == 0)
4133           {
4134             GetNextToken(q,&q,extent,token);
4135             (void) FormatLocaleString(message,MagickPathExtent,
4136               "fill-rule:%s;",token);
4137             (void) WriteBlobString(image,message);
4138             break;
4139           }
4140         if (LocaleCompare("fill-opacity",keyword) == 0)
4141           {
4142             GetNextToken(q,&q,extent,token);
4143             (void) FormatLocaleString(message,MagickPathExtent,
4144               "fill-opacity:%s;",token);
4145             (void) WriteBlobString(image,message);
4146             break;
4147           }
4148         if (LocaleCompare("font-family",keyword) == 0)
4149           {
4150             GetNextToken(q,&q,extent,token);
4151             (void) FormatLocaleString(message,MagickPathExtent,
4152               "font-family:%s;",token);
4153             (void) WriteBlobString(image,message);
4154             break;
4155           }
4156         if (LocaleCompare("font-stretch",keyword) == 0)
4157           {
4158             GetNextToken(q,&q,extent,token);
4159             (void) FormatLocaleString(message,MagickPathExtent,
4160               "font-stretch:%s;",token);
4161             (void) WriteBlobString(image,message);
4162             break;
4163           }
4164         if (LocaleCompare("font-style",keyword) == 0)
4165           {
4166             GetNextToken(q,&q,extent,token);
4167             (void) FormatLocaleString(message,MagickPathExtent,
4168               "font-style:%s;",token);
4169             (void) WriteBlobString(image,message);
4170             break;
4171           }
4172         if (LocaleCompare("font-size",keyword) == 0)
4173           {
4174             GetNextToken(q,&q,extent,token);
4175             (void) FormatLocaleString(message,MagickPathExtent,
4176               "font-size:%s;",token);
4177             (void) WriteBlobString(image,message);
4178             break;
4179           }
4180         if (LocaleCompare("font-weight",keyword) == 0)
4181           {
4182             GetNextToken(q,&q,extent,token);
4183             (void) FormatLocaleString(message,MagickPathExtent,
4184               "font-weight:%s;",token);
4185             (void) WriteBlobString(image,message);
4186             break;
4187           }
4188         status=MagickFalse;
4189         break;
4190       }
4191       case 'g':
4192       case 'G':
4193       {
4194         if (LocaleCompare("gradient-units",keyword) == 0)
4195           {
4196             GetNextToken(q,&q,extent,token);
4197             break;
4198           }
4199         if (LocaleCompare("text-align",keyword) == 0)
4200           {
4201             GetNextToken(q,&q,extent,token);
4202             (void) FormatLocaleString(message,MagickPathExtent,
4203               "text-align %s ",token);
4204             (void) WriteBlobString(image,message);
4205             break;
4206           }
4207         if (LocaleCompare("text-anchor",keyword) == 0)
4208           {
4209             GetNextToken(q,&q,extent,token);
4210             (void) FormatLocaleString(message,MagickPathExtent,
4211               "text-anchor %s ",token);
4212             (void) WriteBlobString(image,message);
4213             break;
4214           }
4215         status=MagickFalse;
4216         break;
4217       }
4218       case 'i':
4219       case 'I':
4220       {
4221         if (LocaleCompare("image",keyword) == 0)
4222           {
4223             GetNextToken(q,&q,extent,token);
4224             primitive_type=ImagePrimitive;
4225             break;
4226           }
4227         status=MagickFalse;
4228         break;
4229       }
4230       case 'l':
4231       case 'L':
4232       {
4233         if (LocaleCompare("line",keyword) == 0)
4234           {
4235             primitive_type=LinePrimitive;
4236             break;
4237           }
4238         status=MagickFalse;
4239         break;
4240       }
4241       case 'o':
4242       case 'O':
4243       {
4244         if (LocaleCompare("opacity",keyword) == 0)
4245           {
4246             GetNextToken(q,&q,extent,token);
4247             (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ",
4248               token);
4249             (void) WriteBlobString(image,message);
4250             break;
4251           }
4252         status=MagickFalse;
4253         break;
4254       }
4255       case 'p':
4256       case 'P':
4257       {
4258         if (LocaleCompare("path",keyword) == 0)
4259           {
4260             primitive_type=PathPrimitive;
4261             break;
4262           }
4263         if (LocaleCompare("point",keyword) == 0)
4264           {
4265             primitive_type=PointPrimitive;
4266             break;
4267           }
4268         if (LocaleCompare("polyline",keyword) == 0)
4269           {
4270             primitive_type=PolylinePrimitive;
4271             break;
4272           }
4273         if (LocaleCompare("polygon",keyword) == 0)
4274           {
4275             primitive_type=PolygonPrimitive;
4276             break;
4277           }
4278         if (LocaleCompare("pop",keyword) == 0)
4279           {
4280             GetNextToken(q,&q,extent,token);
4281             if (LocaleCompare("clip-path",token) == 0)
4282               {
4283                 (void) WriteBlobString(image,"</clipPath>\n");
4284                 break;
4285               }
4286             if (LocaleCompare("defs",token) == 0)
4287               {
4288                 (void) WriteBlobString(image,"</defs>\n");
4289                 break;
4290               }
4291             if (LocaleCompare("gradient",token) == 0)
4292               {
4293                 (void) FormatLocaleString(message,MagickPathExtent,
4294                   "</%sGradient>\n",type);
4295                 (void) WriteBlobString(image,message);
4296                 break;
4297               }
4298             if (LocaleCompare("graphic-context",token) == 0)
4299               {
4300                 n--;
4301                 if (n < 0)
4302                   ThrowWriterException(DrawError,
4303                     "UnbalancedGraphicContextPushPop");
4304                 (void) WriteBlobString(image,"</g>\n");
4305               }
4306             if (LocaleCompare("pattern",token) == 0)
4307               {
4308                 (void) WriteBlobString(image,"</pattern>\n");
4309                 break;
4310               }
4311             if (LocaleCompare("symbol",token) == 0)
4312               {
4313                 (void) WriteBlobString(image,"</symbol>\n");
4314                 break;
4315               }
4316             if ((LocaleCompare("defs",token) == 0) ||
4317                 (LocaleCompare("symbol",token) == 0))
4318               (void) WriteBlobString(image,"</g>\n");
4319             break;
4320           }
4321         if (LocaleCompare("push",keyword) == 0)
4322           {
4323             GetNextToken(q,&q,extent,token);
4324             if (LocaleCompare("clip-path",token) == 0)
4325               {
4326                 GetNextToken(q,&q,extent,token);
4327                 (void) FormatLocaleString(message,MagickPathExtent,
4328                   "<clipPath id=\"%s\">\n",token);
4329                 (void) WriteBlobString(image,message);
4330                 break;
4331               }
4332             if (LocaleCompare("defs",token) == 0)
4333               {
4334                 (void) WriteBlobString(image,"<defs>\n");
4335                 break;
4336               }
4337             if (LocaleCompare("gradient",token) == 0)
4338               {
4339                 GetNextToken(q,&q,extent,token);
4340                 (void) CopyMagickString(name,token,MagickPathExtent);
4341                 GetNextToken(q,&q,extent,token);
4342                 (void) CopyMagickString(type,token,MagickPathExtent);
4343                 GetNextToken(q,&q,extent,token);
4344                 svg_info.segment.x1=StringToDouble(token,&next_token);
4345                 svg_info.element.cx=StringToDouble(token,&next_token);
4346                 GetNextToken(q,&q,extent,token);
4347                 if (*token == ',')
4348                   GetNextToken(q,&q,extent,token);
4349                 svg_info.segment.y1=StringToDouble(token,&next_token);
4350                 svg_info.element.cy=StringToDouble(token,&next_token);
4351                 GetNextToken(q,&q,extent,token);
4352                 if (*token == ',')
4353                   GetNextToken(q,&q,extent,token);
4354                 svg_info.segment.x2=StringToDouble(token,&next_token);
4355                 svg_info.element.major=StringToDouble(token,
4356                   (char **) NULL);
4357                 GetNextToken(q,&q,extent,token);
4358                 if (*token == ',')
4359                   GetNextToken(q,&q,extent,token);
4360                 svg_info.segment.y2=StringToDouble(token,&next_token);
4361                 svg_info.element.minor=StringToDouble(token,
4362                   (char **) NULL);
4363                 (void) FormatLocaleString(message,MagickPathExtent,
4364                   "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
4365                   "y2=\"%g\">\n",type,name,svg_info.segment.x1,
4366                   svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
4367                 if (LocaleCompare(type,"radial") == 0)
4368                   {
4369                     GetNextToken(q,&q,extent,token);
4370                     if (*token == ',')
4371                       GetNextToken(q,&q,extent,token);
4372                     svg_info.element.angle=StringToDouble(token,
4373                       (char **) NULL);
4374                     (void) FormatLocaleString(message,MagickPathExtent,
4375                       "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
4376                       "fx=\"%g\" fy=\"%g\">\n",type,name,
4377                       svg_info.element.cx,svg_info.element.cy,
4378                       svg_info.element.angle,svg_info.element.major,
4379                       svg_info.element.minor);
4380                   }
4381                 (void) WriteBlobString(image,message);
4382                 break;
4383               }
4384             if (LocaleCompare("graphic-context",token) == 0)
4385               {
4386                 n++;
4387                 if (active)
4388                   {
4389                     AffineToTransform(image,&affine);
4390                     active=MagickFalse;
4391                   }
4392                 (void) WriteBlobString(image,"<g style=\"");
4393                 active=MagickTrue;
4394               }
4395             if (LocaleCompare("pattern",token) == 0)
4396               {
4397                 GetNextToken(q,&q,extent,token);
4398                 (void) CopyMagickString(name,token,MagickPathExtent);
4399                 GetNextToken(q,&q,extent,token);
4400                 svg_info.bounds.x=StringToDouble(token,&next_token);
4401                 GetNextToken(q,&q,extent,token);
4402                 if (*token == ',')
4403                   GetNextToken(q,&q,extent,token);
4404                 svg_info.bounds.y=StringToDouble(token,&next_token);
4405                 GetNextToken(q,&q,extent,token);
4406                 if (*token == ',')
4407                   GetNextToken(q,&q,extent,token);
4408                 svg_info.bounds.width=StringToDouble(token,
4409                   (char **) NULL);
4410                 GetNextToken(q,&q,extent,token);
4411                 if (*token == ',')
4412                   GetNextToken(q,&q,extent,token);
4413                 svg_info.bounds.height=StringToDouble(token,(char **) NULL);
4414                 (void) FormatLocaleString(message,MagickPathExtent,
4415                   "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
4416                   "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y,
4417                   svg_info.bounds.width,svg_info.bounds.height);
4418                 (void) WriteBlobString(image,message);
4419                 break;
4420               }
4421             if (LocaleCompare("symbol",token) == 0)
4422               {
4423                 (void) WriteBlobString(image,"<symbol>\n");
4424                 break;
4425               }
4426             break;
4427           }
4428         status=MagickFalse;
4429         break;
4430       }
4431       case 'r':
4432       case 'R':
4433       {
4434         if (LocaleCompare("rectangle",keyword) == 0)
4435           {
4436             primitive_type=RectanglePrimitive;
4437             break;
4438           }
4439         if (LocaleCompare("roundRectangle",keyword) == 0)
4440           {
4441             primitive_type=RoundRectanglePrimitive;
4442             break;
4443           }
4444         if (LocaleCompare("rotate",keyword) == 0)
4445           {
4446             GetNextToken(q,&q,extent,token);
4447             (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ",
4448               token);
4449             (void) WriteBlobString(image,message);
4450             break;
4451           }
4452         status=MagickFalse;
4453         break;
4454       }
4455       case 's':
4456       case 'S':
4457       {
4458         if (LocaleCompare("scale",keyword) == 0)
4459           {
4460             GetNextToken(q,&q,extent,token);
4461             affine.sx=StringToDouble(token,&next_token);
4462             GetNextToken(q,&q,extent,token);
4463             if (*token == ',')
4464               GetNextToken(q,&q,extent,token);
4465             affine.sy=StringToDouble(token,&next_token);
4466             break;
4467           }
4468         if (LocaleCompare("skewX",keyword) == 0)
4469           {
4470             GetNextToken(q,&q,extent,token);
4471             (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ",
4472               token);
4473             (void) WriteBlobString(image,message);
4474             break;
4475           }
4476         if (LocaleCompare("skewY",keyword) == 0)
4477           {
4478             GetNextToken(q,&q,extent,token);
4479             (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ",
4480               token);
4481             (void) WriteBlobString(image,message);
4482             break;
4483           }
4484         if (LocaleCompare("stop-color",keyword) == 0)
4485           {
4486             char
4487               color[MagickPathExtent];
4488
4489             GetNextToken(q,&q,extent,token);
4490             (void) CopyMagickString(color,token,MagickPathExtent);
4491             GetNextToken(q,&q,extent,token);
4492             (void) FormatLocaleString(message,MagickPathExtent,
4493               "  <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4494             (void) WriteBlobString(image,message);
4495             break;
4496           }
4497         if (LocaleCompare("stroke",keyword) == 0)
4498           {
4499             GetNextToken(q,&q,extent,token);
4500             (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;",
4501               token);
4502             (void) WriteBlobString(image,message);
4503             break;
4504           }
4505         if (LocaleCompare("stroke-antialias",keyword) == 0)
4506           {
4507             GetNextToken(q,&q,extent,token);
4508             (void) FormatLocaleString(message,MagickPathExtent,
4509               "stroke-antialias:%s;",token);
4510             (void) WriteBlobString(image,message);
4511             break;
4512           }
4513         if (LocaleCompare("stroke-dasharray",keyword) == 0)
4514           {
4515             if (IsPoint(q))
4516               {
4517                 ssize_t
4518                   k;
4519
4520                 p=q;
4521                 GetNextToken(p,&p,extent,token);
4522                 for (k=0; IsPoint(token); k++)
4523                   GetNextToken(p,&p,extent,token);
4524                 (void) WriteBlobString(image,"stroke-dasharray:");
4525                 for (j=0; j < k; j++)
4526                 {
4527                   GetNextToken(q,&q,extent,token);
4528                   (void) FormatLocaleString(message,MagickPathExtent,"%s ",
4529                     token);
4530                   (void) WriteBlobString(image,message);
4531                 }
4532                 (void) WriteBlobString(image,";");
4533                 break;
4534               }
4535             GetNextToken(q,&q,extent,token);
4536             (void) FormatLocaleString(message,MagickPathExtent,
4537               "stroke-dasharray:%s;",token);
4538             (void) WriteBlobString(image,message);
4539             break;
4540           }
4541         if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4542           {
4543             GetNextToken(q,&q,extent,token);
4544             (void) FormatLocaleString(message,MagickPathExtent,
4545               "stroke-dashoffset:%s;",token);
4546             (void) WriteBlobString(image,message);
4547             break;
4548           }
4549         if (LocaleCompare("stroke-linecap",keyword) == 0)
4550           {
4551             GetNextToken(q,&q,extent,token);
4552             (void) FormatLocaleString(message,MagickPathExtent,
4553               "stroke-linecap:%s;",token);
4554             (void) WriteBlobString(image,message);
4555             break;
4556           }
4557         if (LocaleCompare("stroke-linejoin",keyword) == 0)
4558           {
4559             GetNextToken(q,&q,extent,token);
4560             (void) FormatLocaleString(message,MagickPathExtent,
4561               "stroke-linejoin:%s;",token);
4562             (void) WriteBlobString(image,message);
4563             break;
4564           }
4565         if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4566           {
4567             GetNextToken(q,&q,extent,token);
4568             (void) FormatLocaleString(message,MagickPathExtent,
4569               "stroke-miterlimit:%s;",token);
4570             (void) WriteBlobString(image,message);
4571             break;
4572           }
4573         if (LocaleCompare("stroke-opacity",keyword) == 0)
4574           {
4575             GetNextToken(q,&q,extent,token);
4576             (void) FormatLocaleString(message,MagickPathExtent,
4577               "stroke-opacity:%s;",token);
4578             (void) WriteBlobString(image,message);
4579             break;
4580           }
4581         if (LocaleCompare("stroke-width",keyword) == 0)
4582           {
4583             GetNextToken(q,&q,extent,token);
4584             (void) FormatLocaleString(message,MagickPathExtent,
4585               "stroke-width:%s;",token);
4586             (void) WriteBlobString(image,message);
4587             continue;
4588           }
4589         status=MagickFalse;
4590         break;
4591       }
4592       case 't':
4593       case 'T':
4594       {
4595         if (LocaleCompare("text",keyword) == 0)
4596           {
4597             primitive_type=TextPrimitive;
4598             break;
4599           }
4600         if (LocaleCompare("text-antialias",keyword) == 0)
4601           {
4602             GetNextToken(q,&q,extent,token);
4603             (void) FormatLocaleString(message,MagickPathExtent,
4604               "text-antialias:%s;",token);
4605             (void) WriteBlobString(image,message);
4606             break;
4607           }
4608         if (LocaleCompare("tspan",keyword) == 0)
4609           {
4610             primitive_type=TextPrimitive;
4611             break;
4612           }
4613         if (LocaleCompare("translate",keyword) == 0)
4614           {
4615             GetNextToken(q,&q,extent,token);
4616             affine.tx=StringToDouble(token,&next_token);
4617             GetNextToken(q,&q,extent,token);
4618             if (*token == ',')
4619               GetNextToken(q,&q,extent,token);
4620             affine.ty=StringToDouble(token,&next_token);
4621             break;
4622           }
4623         status=MagickFalse;
4624         break;
4625       }
4626       case 'v':
4627       case 'V':
4628       {
4629         if (LocaleCompare("viewbox",keyword) == 0)
4630           {
4631             GetNextToken(q,&q,extent,token);
4632             if (*token == ',')
4633               GetNextToken(q,&q,extent,token);
4634             GetNextToken(q,&q,extent,token);
4635             if (*token == ',')
4636               GetNextToken(q,&q,extent,token);
4637             GetNextToken(q,&q,extent,token);
4638             if (*token == ',')
4639               GetNextToken(q,&q,extent,token);
4640             GetNextToken(q,&q,extent,token);
4641             break;
4642           }
4643         status=MagickFalse;
4644         break;
4645       }
4646       default:
4647       {
4648         status=MagickFalse;
4649         break;
4650       }
4651     }
4652     if (status == MagickFalse)
4653       break;
4654     if (primitive_type == UndefinedPrimitive)
4655       continue;
4656     /*
4657       Parse the primitive attributes.
4658     */
4659     i=0;
4660     j=0;
4661     for (x=0; *q != '\0'; x++)
4662     {
4663       /*
4664         Define points.
4665       */
4666       if (IsPoint(q) == MagickFalse)
4667         break;
4668       GetNextToken(q,&q,extent,token);
4669       point.x=StringToDouble(token,&next_token);
4670       GetNextToken(q,&q,extent,token);
4671       if (*token == ',')
4672         GetNextToken(q,&q,extent,token);
4673       point.y=StringToDouble(token,&next_token);
4674       GetNextToken(q,(const char **) NULL,extent,token);
4675       if (*token == ',')
4676         GetNextToken(q,&q,extent,token);
4677       primitive_info[i].primitive=primitive_type;
4678       primitive_info[i].point=point;
4679       primitive_info[i].coordinates=0;
4680       primitive_info[i].method=FloodfillMethod;
4681       i++;
4682       if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4683         continue;
4684       number_points+=6*BezierQuantum+360;
4685       primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4686         number_points,sizeof(*primitive_info));
4687       if (primitive_info == (PrimitiveInfo *) NULL)
4688         {
4689           (void) ThrowMagickException(exception,GetMagickModule(),
4690             ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4691           break;
4692         }
4693     }
4694     primitive_info[j].primitive=primitive_type;
4695     primitive_info[j].coordinates=x;
4696     primitive_info[j].method=FloodfillMethod;
4697     primitive_info[j].text=(char *) NULL;
4698     if (active)
4699       {
4700         AffineToTransform(image,&affine);
4701         active=MagickFalse;
4702       }
4703     active=MagickFalse;
4704     switch (primitive_type)
4705     {
4706       case PointPrimitive:
4707       default:
4708       {
4709         if (primitive_info[j].coordinates != 1)
4710           {
4711             status=MagickFalse;
4712             break;
4713           }
4714         break;
4715       }
4716       case LinePrimitive:
4717       {
4718         if (primitive_info[j].coordinates != 2)
4719           {
4720             status=MagickFalse;
4721             break;
4722           }
4723           (void) FormatLocaleString(message,MagickPathExtent,
4724           "  <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4725           primitive_info[j].point.x,primitive_info[j].point.y,
4726           primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4727         (void) WriteBlobString(image,message);
4728         break;
4729       }
4730       case RectanglePrimitive:
4731       {
4732         if (primitive_info[j].coordinates != 2)
4733           {
4734             status=MagickFalse;
4735             break;
4736           }
4737           (void) FormatLocaleString(message,MagickPathExtent,
4738           "  <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4739           primitive_info[j].point.x,primitive_info[j].point.y,
4740           primitive_info[j+1].point.x-primitive_info[j].point.x,
4741           primitive_info[j+1].point.y-primitive_info[j].point.y);
4742         (void) WriteBlobString(image,message);
4743         break;
4744       }
4745       case RoundRectanglePrimitive:
4746       {
4747         if (primitive_info[j].coordinates != 3)
4748           {
4749             status=MagickFalse;
4750             break;
4751           }
4752         (void) FormatLocaleString(message,MagickPathExtent,
4753           "  <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4754           "ry=\"%g\"/>\n",primitive_info[j].point.x,
4755           primitive_info[j].point.y,primitive_info[j+1].point.x-
4756           primitive_info[j].point.x,primitive_info[j+1].point.y-
4757           primitive_info[j].point.y,primitive_info[j+2].point.x,
4758           primitive_info[j+2].point.y);
4759         (void) WriteBlobString(image,message);
4760         break;
4761       }
4762       case ArcPrimitive:
4763       {
4764         if (primitive_info[j].coordinates != 3)
4765           {
4766             status=MagickFalse;
4767             break;
4768           }
4769         break;
4770       }
4771       case EllipsePrimitive:
4772       {
4773         if (primitive_info[j].coordinates != 3)
4774           {
4775             status=MagickFalse;
4776             break;
4777           }
4778           (void) FormatLocaleString(message,MagickPathExtent,
4779           "  <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
4780           primitive_info[j].point.x,primitive_info[j].point.y,
4781           primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4782         (void) WriteBlobString(image,message);
4783         break;
4784       }
4785       case CirclePrimitive:
4786       {
4787         double
4788           alpha,
4789           beta;
4790
4791         if (primitive_info[j].coordinates != 2)
4792           {
4793             status=MagickFalse;
4794             break;
4795           }
4796         alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4797         beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4798         (void) FormatLocaleString(message,MagickPathExtent,
4799           "  <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
4800           primitive_info[j].point.x,primitive_info[j].point.y,
4801           hypot(alpha,beta));
4802         (void) WriteBlobString(image,message);
4803         break;
4804       }
4805       case PolylinePrimitive:
4806       {
4807         if (primitive_info[j].coordinates < 2)
4808           {
4809             status=MagickFalse;
4810             break;
4811           }
4812         (void) CopyMagickString(message,"  <polyline points=\"",
4813            MagickPathExtent);
4814         (void) WriteBlobString(image,message);
4815         length=strlen(message);
4816         for ( ; j < i; j++)
4817         {
4818           (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4819             primitive_info[j].point.x,primitive_info[j].point.y);
4820           length+=strlen(message);
4821           if (length >= 80)
4822             {
4823               (void) WriteBlobString(image,"\n    ");
4824               length=strlen(message)+5;
4825             }
4826           (void) WriteBlobString(image,message);
4827         }
4828         (void) WriteBlobString(image,"\"/>\n");
4829         break;
4830       }
4831       case PolygonPrimitive:
4832       {
4833         if (primitive_info[j].coordinates < 3)
4834           {
4835             status=MagickFalse;
4836             break;
4837           }
4838         primitive_info[i]=primitive_info[j];
4839         primitive_info[i].coordinates=0;
4840         primitive_info[j].coordinates++;
4841         i++;
4842         (void) CopyMagickString(message,"  <polygon points=\"",MagickPathExtent);
4843         (void) WriteBlobString(image,message);
4844         length=strlen(message);
4845         for ( ; j < i; j++)
4846         {
4847           (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
4848             primitive_info[j].point.x,primitive_info[j].point.y);
4849           length+=strlen(message);
4850           if (length >= 80)
4851             {
4852               (void) WriteBlobString(image,"\n    ");
4853               length=strlen(message)+5;
4854             }
4855           (void) WriteBlobString(image,message);
4856         }
4857         (void) WriteBlobString(image,"\"/>\n");
4858         break;
4859       }
4860       case BezierPrimitive:
4861       {
4862         if (primitive_info[j].coordinates < 3)
4863           {
4864             status=MagickFalse;
4865             break;
4866           }
4867         break;
4868       }
4869       case PathPrimitive:
4870       {
4871         int
4872           number_attributes;
4873
4874         GetNextToken(q,&q,extent,token);
4875         number_attributes=1;
4876         for (p=token; *p != '\0'; p++)
4877           if (isalpha((int) *p))
4878             number_attributes++;
4879         if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
4880           {
4881             number_points+=6*BezierQuantum*number_attributes;
4882             primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4883               number_points,sizeof(*primitive_info));
4884             if (primitive_info == (PrimitiveInfo *) NULL)
4885               {
4886                 (void) ThrowMagickException(exception,GetMagickModule(),
4887                   ResourceLimitError,"MemoryAllocationFailed","`%s'",
4888                   image->filename);
4889                 break;
4890               }
4891           }
4892         (void) WriteBlobString(image,"  <path d=\"");
4893         (void) WriteBlobString(image,token);
4894         (void) WriteBlobString(image,"\"/>\n");
4895         break;
4896       }
4897       case AlphaPrimitive:
4898       case ColorPrimitive:
4899       {
4900         if (primitive_info[j].coordinates != 1)
4901           {
4902             status=MagickFalse;
4903             break;
4904           }
4905         GetNextToken(q,&q,extent,token);
4906         if (LocaleCompare("point",token) == 0)
4907           primitive_info[j].method=PointMethod;
4908         if (LocaleCompare("replace",token) == 0)
4909           primitive_info[j].method=ReplaceMethod;
4910         if (LocaleCompare("floodfill",token) == 0)
4911           primitive_info[j].method=FloodfillMethod;
4912         if (LocaleCompare("filltoborder",token) == 0)
4913           primitive_info[j].method=FillToBorderMethod;
4914         if (LocaleCompare("reset",token) == 0)
4915           primitive_info[j].method=ResetMethod;
4916         break;
4917       }
4918       case TextPrimitive:
4919       {
4920         register char
4921           *p;
4922
4923         if (primitive_info[j].coordinates != 1)
4924           {
4925             status=MagickFalse;
4926             break;
4927           }
4928         GetNextToken(q,&q,extent,token);
4929         (void) FormatLocaleString(message,MagickPathExtent,
4930           "  <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
4931           primitive_info[j].point.y);
4932         (void) WriteBlobString(image,message);
4933         for (p=token; *p != '\0'; p++)
4934           switch (*p)
4935           {
4936             case '<': (void) WriteBlobString(image,"&lt;"); break;
4937             case '>': (void) WriteBlobString(image,"&gt;"); break;
4938             case '&': (void) WriteBlobString(image,"&amp;"); break;
4939             default: (void) WriteBlobByte(image,*p); break;
4940           }
4941         (void) WriteBlobString(image,"</text>\n");
4942         break;
4943       }
4944       case ImagePrimitive:
4945       {
4946         if (primitive_info[j].coordinates != 2)
4947           {
4948             status=MagickFalse;
4949             break;
4950           }
4951         GetNextToken(q,&q,extent,token);
4952         (void) FormatLocaleString(message,MagickPathExtent,
4953           "  <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
4954           "href=\"%s\"/>\n",primitive_info[j].point.x,
4955           primitive_info[j].point.y,primitive_info[j+1].point.x,
4956           primitive_info[j+1].point.y,token);
4957         (void) WriteBlobString(image,message);
4958         break;
4959       }
4960     }
4961     if (primitive_info == (PrimitiveInfo *) NULL)
4962       break;
4963     primitive_info[i].primitive=UndefinedPrimitive;
4964     if (status == MagickFalse)
4965       break;
4966   }
4967   (void) WriteBlobString(image,"</svg>\n");
4968   /*
4969     Relinquish resources.
4970   */
4971   token=DestroyString(token);
4972   if (primitive_info != (PrimitiveInfo *) NULL)
4973     primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4974   (void) CloseBlob(image);
4975   return(status);
4976 }