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