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