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