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