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