]> 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-2010 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   char
2670     filename[MaxTextExtent];
2671
2672   FILE
2673     *file;
2674
2675   Image
2676     *image;
2677
2678   int
2679     status,
2680     unique_file;
2681
2682   long
2683     n;
2684
2685   SVGInfo
2686     *svg_info;
2687
2688   unsigned char
2689     message[MaxTextExtent];
2690
2691   xmlSAXHandler
2692     sax_modules;
2693
2694   xmlSAXHandlerPtr
2695     sax_handler;
2696
2697   /*
2698     Open image file.
2699   */
2700   assert(image_info != (const ImageInfo *) NULL);
2701   assert(image_info->signature == MagickSignature);
2702   assert(exception != (ExceptionInfo *) NULL);
2703   if (image_info->debug != MagickFalse)
2704     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2705       image_info->filename);
2706   assert(exception->signature == MagickSignature);
2707   image=AcquireImage(image_info);
2708   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2709   if (status == MagickFalse)
2710     {
2711       image=DestroyImageList(image);
2712       return((Image *) NULL);
2713     }
2714   if (LocaleCompare(image_info->magick,"MSVG") != 0)
2715     {
2716 #if defined(MAGICKCORE_RSVG_DELEGATE)
2717 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2718       cairo_surface_t
2719         *cairo_surface;
2720
2721       cairo_t
2722         *cairo_info;
2723
2724       register unsigned char
2725         *p;
2726
2727       RsvgDimensionData
2728         dimension_info;
2729
2730       unsigned char
2731         *pixels;
2732
2733 #else
2734       GdkPixbuf
2735         *pixel_info;
2736
2737       register const guchar
2738         *p;
2739
2740 #endif
2741
2742       GError
2743         *error;
2744
2745       long
2746         y;
2747
2748       PixelPacket
2749         fill_color;
2750
2751       register long
2752         x;
2753
2754       register PixelPacket
2755         *q;
2756
2757       RsvgHandle
2758         *svg_handle;
2759
2760       svg_handle=rsvg_handle_new();
2761       if (svg_handle == (RsvgHandle *) NULL)
2762         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2763       rsvg_handle_set_base_uri(svg_handle,image_info->filename);
2764 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
2765       rsvg_handle_set_size_callback(svg_handle,SVGSetImageSize,image,NULL);
2766 #endif
2767       if ((image->x_resolution != 72.0) && (image->y_resolution != 72.0))
2768         rsvg_handle_set_dpi_x_y(svg_handle,image->x_resolution,
2769           image->y_resolution);
2770       while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
2771       {
2772         error=(GError *) NULL;
2773         (void) rsvg_handle_write(svg_handle,message,n,&error);
2774         if (error != (GError *) NULL)
2775           g_error_free(error);
2776       }
2777       error=(GError *) NULL;
2778       rsvg_handle_close(svg_handle,&error);
2779       if (error != (GError *) NULL)
2780         g_error_free(error);
2781 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2782       rsvg_handle_get_dimensions(svg_handle,&dimension_info);
2783       image->columns=dimension_info.width;
2784       image->rows=dimension_info.height;
2785       pixels=(unsigned char *) NULL;
2786 #else
2787       pixel_info=rsvg_handle_get_pixbuf(svg_handle);
2788       rsvg_handle_free(svg_handle);
2789       image->columns=gdk_pixbuf_get_width(pixel_info);
2790       image->rows=gdk_pixbuf_get_height(pixel_info);
2791 #endif
2792       image->matte=MagickTrue;
2793       SetImageProperty(image,"svg:base-uri",
2794         rsvg_handle_get_base_uri(svg_handle));
2795       SetImageProperty(image,"svg:title",rsvg_handle_get_title(svg_handle));
2796       SetImageProperty(image,"svg:description",
2797         rsvg_handle_get_desc(svg_handle));
2798       if ((image->columns == 0) || (image->rows == 0))
2799         {
2800 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
2801           g_object_unref(G_OBJECT(pixel_info));
2802 #endif
2803           g_object_unref(svg_handle);
2804           ThrowReaderException(MissingDelegateError,
2805             "NoDecodeDelegateForThisImageFormat");
2806         }
2807       if (image_info->ping == MagickFalse)
2808         {
2809 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2810           pixels=(unsigned char *) AcquireQuantumMemory(image->columns,4*
2811             image->rows*sizeof(*pixels));
2812           if (pixels == (unsigned char *) NULL)
2813             {
2814               g_object_unref(svg_handle);
2815               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2816             }
2817 #endif
2818           (void) SetImageBackgroundColor(image);
2819 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2820           cairo_surface=cairo_image_surface_create_for_data(pixels,
2821             CAIRO_FORMAT_ARGB32,image->columns,image->rows,4*image->columns);
2822           if (cairo_surface == (cairo_surface_t *) NULL)
2823             {
2824               pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2825               g_object_unref(svg_handle);
2826               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2827             }
2828           cairo_info=cairo_create(cairo_surface);
2829           cairo_set_operator(cairo_info,CAIRO_OPERATOR_CLEAR);
2830           cairo_paint(cairo_info);
2831           cairo_set_operator(cairo_info,CAIRO_OPERATOR_OVER);
2832           rsvg_handle_render_cairo(svg_handle,cairo_info);
2833           cairo_destroy(cairo_info);
2834           cairo_surface_destroy(cairo_surface);
2835           g_object_unref(svg_handle);
2836           p=pixels;
2837 #else
2838           p=gdk_pixbuf_get_pixels(pixel_info);
2839 #endif
2840           for (y=0; y < (long) image->rows; y++)
2841           {
2842             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2843             if (q == (PixelPacket *) NULL)
2844               break;
2845             for (x=0; x < (long) image->columns; x++)
2846             {
2847 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2848               fill_color.blue=ScaleCharToQuantum(*p++);
2849               fill_color.green=ScaleCharToQuantum(*p++);
2850               fill_color.red=ScaleCharToQuantum(*p++);
2851 #else
2852               fill_color.red=ScaleCharToQuantum(*p++);
2853               fill_color.green=ScaleCharToQuantum(*p++);
2854               fill_color.blue=ScaleCharToQuantum(*p++);
2855 #endif
2856               fill_color.opacity=QuantumRange-ScaleCharToQuantum(*p++);
2857 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2858               {
2859                 double
2860                   gamma;
2861     
2862                 gamma=1.0-QuantumScale*fill_color.opacity;
2863                 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2864                 fill_color.blue*=gamma;
2865                 fill_color.green*=gamma;
2866                 fill_color.red*=gamma;
2867               }
2868 #endif
2869               MagickCompositeOver(&fill_color,fill_color.opacity,q,
2870                 (MagickRealType) q->opacity,q);
2871               q++;
2872             }
2873             if (SyncAuthenticPixels(image,exception) == MagickFalse)
2874               break;
2875             if (image->previous == (Image *) NULL)
2876               {
2877                 status=SetImageProgress(image,LoadImageTag,y,image->rows);
2878                 if (status == MagickFalse)
2879                   break;
2880               }
2881           }
2882         }
2883 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2884       if (pixels != (unsigned char *) NULL)
2885         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2886 #else
2887       g_object_unref(G_OBJECT(pixel_info));
2888 #endif
2889       (void) CloseBlob(image);
2890       return(GetFirstImageInList(image));
2891 #endif
2892     }
2893   /*
2894     Open draw file.
2895   */
2896   file=(FILE *) NULL;
2897   unique_file=AcquireUniqueFileResource(filename);
2898   if (unique_file != -1)
2899     file=fdopen(unique_file,"w");
2900   if ((unique_file == -1) || (file == (FILE *) NULL))
2901     {
2902       (void) CopyMagickString(image->filename,filename,MaxTextExtent);
2903       ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
2904         image->filename);
2905       image=DestroyImageList(image);
2906       return((Image *) NULL);
2907     }
2908   /*
2909     Parse SVG file.
2910   */
2911   svg_info=AcquireSVGInfo();
2912   if (svg_info == (SVGInfo *) NULL)
2913     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2914   svg_info->file=file;
2915   svg_info->exception=exception;
2916   svg_info->image=image;
2917   svg_info->image_info=image_info;
2918   svg_info->bounds.width=image->columns;
2919   svg_info->bounds.height=image->rows;
2920   if (image_info->size != (char *) NULL)
2921     (void) CloneString(&svg_info->size,image_info->size);
2922   if (image->debug != MagickFalse)
2923     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
2924   xmlInitParser();
2925   (void) xmlSubstituteEntitiesDefault(1);
2926   (void) ResetMagickMemory(&sax_modules,0,sizeof(sax_modules));
2927   sax_modules.internalSubset=SVGInternalSubset;
2928   sax_modules.isStandalone=SVGIsStandalone;
2929   sax_modules.hasInternalSubset=SVGHasInternalSubset;
2930   sax_modules.hasExternalSubset=SVGHasExternalSubset;
2931   sax_modules.resolveEntity=SVGResolveEntity;
2932   sax_modules.getEntity=SVGGetEntity;
2933   sax_modules.entityDecl=SVGEntityDeclaration;
2934   sax_modules.notationDecl=SVGNotationDeclaration;
2935   sax_modules.attributeDecl=SVGAttributeDeclaration;
2936   sax_modules.elementDecl=SVGElementDeclaration;
2937   sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
2938   sax_modules.setDocumentLocator=SVGSetDocumentLocator;
2939   sax_modules.startDocument=SVGStartDocument;
2940   sax_modules.endDocument=SVGEndDocument;
2941   sax_modules.startElement=SVGStartElement;
2942   sax_modules.endElement=SVGEndElement;
2943   sax_modules.reference=SVGReference;
2944   sax_modules.characters=SVGCharacters;
2945   sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
2946   sax_modules.processingInstruction=SVGProcessingInstructions;
2947   sax_modules.comment=SVGComment;
2948   sax_modules.warning=SVGWarning;
2949   sax_modules.error=SVGError;
2950   sax_modules.fatalError=SVGError;
2951   sax_modules.getParameterEntity=SVGGetParameterEntity;
2952   sax_modules.cdataBlock=SVGCDataBlock;
2953   sax_modules.externalSubset=SVGExternalSubset;
2954   sax_handler=(&sax_modules);
2955   n=ReadBlob(image,MaxTextExtent,message);
2956   if (n > 0)
2957     {
2958       svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
2959         message,n,image->filename);
2960       while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
2961       {
2962         status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
2963         if (status != 0)
2964           break;
2965       }
2966     }
2967   (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
2968   xmlFreeParserCtxt(svg_info->parser);
2969   if (image->debug != MagickFalse)
2970     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
2971   xmlCleanupParser();
2972   (void) fclose(file);
2973   (void) CloseBlob(image);
2974   image->columns=svg_info->width;
2975   image->rows=svg_info->height;
2976   if (exception->severity >= ErrorException)
2977     {
2978       image=DestroyImage(image);
2979       return((Image *) NULL);
2980     }
2981   if (image_info->ping == MagickFalse)
2982     {
2983       ImageInfo
2984         *read_info;
2985
2986       /*
2987         Draw image.
2988       */
2989       image=DestroyImage(image);
2990       image=(Image *) NULL;
2991       read_info=CloneImageInfo(image_info);
2992       SetImageInfoBlob(read_info,(void *) NULL,0);
2993       if (read_info->density != (char *) NULL)
2994         read_info->density=DestroyString(read_info->density);
2995       (void) FormatMagickString(read_info->filename,MaxTextExtent,"mvg:%s",
2996         filename);
2997       image=ReadImage(read_info,exception);
2998       read_info=DestroyImageInfo(read_info);
2999       if (image != (Image *) NULL)
3000         (void) CopyMagickString(image->filename,image_info->filename,
3001           MaxTextExtent);
3002     }
3003   /*
3004     Relinquish resources.
3005   */
3006   if (image != (Image *) NULL)
3007     {
3008       if (svg_info->title != (char *) NULL)
3009         (void) SetImageProperty(image,"svg:title",svg_info->title);
3010       if (svg_info->comment != (char *) NULL)
3011         (void) SetImageProperty(image,"svg:comment",svg_info->comment);
3012     }
3013   svg_info=DestroySVGInfo(svg_info);
3014   (void) RelinquishUniqueFileResource(filename);
3015   return(GetFirstImageInList(image));
3016 }
3017 #endif
3018 \f
3019 /*
3020 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3021 %                                                                             %
3022 %                                                                             %
3023 %                                                                             %
3024 %   R e g i s t e r S V G I m a g e                                           %
3025 %                                                                             %
3026 %                                                                             %
3027 %                                                                             %
3028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3029 %
3030 %  RegisterSVGImage() adds attributes for the SVG image format to
3031 %  the list of supported formats.  The attributes include the image format
3032 %  tag, a method to read and/or write the format, whether the format
3033 %  supports the saving of more than one frame to the same file or blob,
3034 %  whether the format supports native in-memory I/O, and a brief
3035 %  description of the format.
3036 %
3037 %  The format of the RegisterSVGImage method is:
3038 %
3039 %      unsigned long RegisterSVGImage(void)
3040 %
3041 */
3042 ModuleExport unsigned long RegisterSVGImage(void)
3043 {
3044   char
3045     version[MaxTextExtent];
3046
3047   MagickInfo
3048     *entry;
3049
3050   *version='\0';
3051 #if defined(LIBXML_DOTTED_VERSION)
3052   (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,MaxTextExtent);
3053 #endif
3054 #if defined(MAGICKCORE_RSVG_DELEGATE)
3055   rsvg_init();
3056   (void) FormatMagickString(version,MaxTextExtent,"RSVG %d.%d.%d",
3057     LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3058 #endif
3059   entry=SetMagickInfo("SVG");
3060 #if defined(MAGICKCORE_XML_DELEGATE)
3061   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3062 #endif
3063   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3064   entry->blob_support=MagickFalse;
3065   entry->seekable_stream=MagickFalse;
3066   entry->description=ConstantString("Scalable Vector Graphics");
3067   if (*version != '\0')
3068     entry->version=ConstantString(version);
3069   entry->magick=(IsImageFormatHandler *) IsSVG;
3070   entry->module=ConstantString("SVG");
3071   (void) RegisterMagickInfo(entry);
3072   entry=SetMagickInfo("SVGZ");
3073 #if defined(MAGICKCORE_XML_DELEGATE)
3074   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3075 #endif
3076   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3077   entry->blob_support=MagickFalse;
3078   entry->seekable_stream=MagickFalse;
3079   entry->description=ConstantString("Compressed Scalable Vector Graphics");
3080   if (*version != '\0')
3081     entry->version=ConstantString(version);
3082   entry->magick=(IsImageFormatHandler *) IsSVG;
3083   entry->module=ConstantString("SVG");
3084   (void) RegisterMagickInfo(entry);
3085   entry=SetMagickInfo("MSVG");
3086 #if defined(MAGICKCORE_XML_DELEGATE)
3087   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3088 #endif
3089   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3090   entry->blob_support=MagickFalse;
3091   entry->seekable_stream=MagickFalse;
3092   entry->description=ConstantString("ImageMagick's own SVG internal renderer");
3093   entry->magick=(IsImageFormatHandler *) IsSVG;
3094   entry->module=ConstantString("SVG");
3095   (void) RegisterMagickInfo(entry);
3096   return(MagickImageCoderSignature);
3097 }
3098 \f
3099 /*
3100 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3101 %                                                                             %
3102 %                                                                             %
3103 %                                                                             %
3104 %   U n r e g i s t e r S V G I m a g e                                       %
3105 %                                                                             %
3106 %                                                                             %
3107 %                                                                             %
3108 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3109 %
3110 %  UnregisterSVGImage() removes format registrations made by the
3111 %  SVG module from the list of supported formats.
3112 %
3113 %  The format of the UnregisterSVGImage method is:
3114 %
3115 %      UnregisterSVGImage(void)
3116 %
3117 */
3118 ModuleExport void UnregisterSVGImage(void)
3119 {
3120   (void) UnregisterMagickInfo("SVGZ");
3121   (void) UnregisterMagickInfo("SVG");
3122   (void) UnregisterMagickInfo("MSVG");
3123 #if defined(MAGICKCORE_RSVG_DELEGATE)
3124   rsvg_term();
3125 #endif
3126 }
3127 \f
3128 /*
3129 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3130 %                                                                             %
3131 %                                                                             %
3132 %                                                                             %
3133 %   W r i t e S V G I m a g e                                                 %
3134 %                                                                             %
3135 %                                                                             %
3136 %                                                                             %
3137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3138 %
3139 %  WriteSVGImage() writes a image in the SVG - XML based W3C standard
3140 %  format.
3141 %
3142 %  The format of the WriteSVGImage method is:
3143 %
3144 %      MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image)
3145 %
3146 %  A description of each parameter follows.
3147 %
3148 %    o image_info: the image info.
3149 %
3150 %    o image:  The image.
3151 %
3152 */
3153
3154 static void AffineToTransform(Image *image,AffineMatrix *affine)
3155 {
3156   char
3157     transform[MaxTextExtent];
3158
3159   if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3160     {
3161       if ((fabs(affine->rx) < MagickEpsilon) &&
3162           (fabs(affine->ry) < MagickEpsilon))
3163         {
3164           if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3165               (fabs(affine->sy-1.0) < MagickEpsilon))
3166             {
3167               (void) WriteBlobString(image,"\">\n");
3168               return;
3169             }
3170           (void) FormatMagickString(transform,MaxTextExtent,
3171             "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3172           (void) WriteBlobString(image,transform);
3173           return;
3174         }
3175       else
3176         {
3177           if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3178               (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3179               (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3180                2*MagickEpsilon))
3181             {
3182               double
3183                 theta;
3184
3185               theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3186               (void) FormatMagickString(transform,MaxTextExtent,
3187                 "\" transform=\"rotate(%g)\">\n",theta);
3188               (void) WriteBlobString(image,transform);
3189               return;
3190             }
3191         }
3192     }
3193   else
3194     {
3195       if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3196           (fabs(affine->rx) < MagickEpsilon) &&
3197           (fabs(affine->ry) < MagickEpsilon) &&
3198           (fabs(affine->sy-1.0) < MagickEpsilon))
3199         {
3200           (void) FormatMagickString(transform,MaxTextExtent,
3201             "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3202           (void) WriteBlobString(image,transform);
3203           return;
3204         }
3205     }
3206   (void) FormatMagickString(transform,MaxTextExtent,
3207     "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",affine->sx,affine->rx,
3208     affine->ry,affine->sy,affine->tx,affine->ty);
3209   (void) WriteBlobString(image,transform);
3210 }
3211
3212 static MagickBooleanType IsPoint(const char *point)
3213 {
3214   char
3215     *p;
3216
3217   long
3218     value;
3219
3220   value=strtol(point,&p,10);
3221   return(p != point ? MagickTrue : MagickFalse);
3222 }
3223
3224 static MagickBooleanType TraceSVGImage(Image *image)
3225 {
3226   long
3227     y;
3228
3229   register const PixelPacket
3230     *p;
3231
3232   register long
3233     x;
3234
3235 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3236   {
3237     at_bitmap_type
3238       *trace;
3239
3240     at_fitting_opts_type
3241       *fitting_options;
3242
3243     at_output_opts_type
3244       *output_options;
3245
3246     at_splines_type
3247       *splines;
3248
3249     ImageType
3250       type;
3251
3252     register long
3253       i;
3254
3255     unsigned long
3256       number_planes;
3257
3258     /*
3259       Trace image and write as SVG.
3260     */
3261     fitting_options=at_fitting_opts_new();
3262     output_options=at_output_opts_new();
3263     type=GetImageType(image,&image->exception);
3264     number_planes=3;
3265     if ((type == BilevelType) || (type == GrayscaleType))
3266       number_planes=1;
3267     trace=at_bitmap_new(image->columns,image->rows,number_planes);
3268     i=0;
3269     for (y=0; y < (long) image->rows; y++)
3270     {
3271       p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
3272       if (p == (const PixelPacket *) NULL)
3273         break;
3274       for (x=0; x < (long) image->columns; x++)
3275       {
3276         trace->bitmap[i++]=p->red;
3277         if (number_planes == 3)
3278           {
3279             trace->bitmap[i++]=p->green;
3280             trace->bitmap[i++]=p->blue;
3281           }
3282         p++;
3283       }
3284     }
3285     splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3286       NULL);
3287     at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3288       GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3289       NULL);
3290     /*
3291       Free resources.
3292     */
3293     at_splines_free(splines);
3294     at_bitmap_free(trace);
3295     at_output_opts_free(output_options);
3296     at_fitting_opts_free(fitting_options);
3297   }
3298 #else
3299   {
3300     char
3301       message[MaxTextExtent],
3302       tuple[MaxTextExtent];
3303
3304     MagickPixelPacket
3305       pixel;
3306
3307     register const IndexPacket
3308       *indexes;
3309
3310     (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3311     (void) WriteBlobString(image,
3312       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3313     (void) WriteBlobString(image,
3314       "  \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3315     (void) FormatMagickString(message,MaxTextExtent,
3316       "<svg width=\"%lu\" height=\"%lu\">\n",image->columns,image->rows);
3317     (void) WriteBlobString(image,message);
3318     GetMagickPixelPacket(image,&pixel);
3319     for (y=0; y < (long) image->rows; y++)
3320     {
3321       p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
3322       if (p == (const PixelPacket *) NULL)
3323         break;
3324       indexes=GetVirtualIndexQueue(image);
3325       for (x=0; x < (long) image->columns; x++)
3326       {
3327         SetMagickPixelPacket(image,p,indexes+x,&pixel);
3328         (void) QueryMagickColorname(image,&pixel,SVGCompliance,tuple,
3329           &image->exception);
3330         (void) FormatMagickString(message,MaxTextExtent,
3331           "  <circle cx=\"%ld\" cy=\"%ld\" r=\"1\" fill=\"%s\"/>\n",x,y,tuple);
3332         (void) WriteBlobString(image,message);
3333         p++;
3334       }
3335     }
3336     (void) WriteBlobString(image,"</svg>\n");
3337   }
3338 #endif
3339   return(MagickTrue);
3340 }
3341
3342 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image)
3343 {
3344 #define BezierQuantum  200
3345
3346   AffineMatrix
3347     affine;
3348
3349   char
3350     keyword[MaxTextExtent],
3351     message[MaxTextExtent],
3352     name[MaxTextExtent],
3353     *token,
3354     type[MaxTextExtent];
3355
3356   const char
3357     *p,
3358     *q,
3359     *value;
3360
3361   int
3362     n;
3363
3364   long
3365     j;
3366
3367   MagickBooleanType
3368     active,
3369     status;
3370
3371   PointInfo
3372     point;
3373
3374   PrimitiveInfo
3375     *primitive_info;
3376
3377   PrimitiveType
3378     primitive_type;
3379
3380   register long
3381     x;
3382
3383   register long
3384     i;
3385
3386   size_t
3387     length;
3388
3389   SVGInfo
3390     svg_info;
3391
3392   unsigned long
3393     number_points;
3394
3395   /*
3396     Open output image file.
3397   */
3398   assert(image_info != (const ImageInfo *) NULL);
3399   assert(image_info->signature == MagickSignature);
3400   assert(image != (Image *) NULL);
3401   assert(image->signature == MagickSignature);
3402   if (image->debug != MagickFalse)
3403     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3404   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
3405   if (status == MagickFalse)
3406     return(status);
3407   value=GetImageArtifact(image,"SVG");
3408   if (value != (char *) NULL)
3409     {
3410       (void) WriteBlobString(image,value);
3411       (void) CloseBlob(image);
3412       return(MagickTrue);
3413     }
3414   value=GetImageArtifact(image,"MVG");
3415   if (value == (char *) NULL)
3416     return(TraceSVGImage(image));
3417   /*
3418     Write SVG header.
3419   */
3420   (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3421   (void) WriteBlobString(image,
3422     "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3423   (void) WriteBlobString(image,
3424     "  \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3425   (void) FormatMagickString(message,MaxTextExtent,
3426     "<svg width=\"%lu\" height=\"%lu\">\n",image->columns,image->rows);
3427   (void) WriteBlobString(image,message);
3428   /*
3429     Allocate primitive info memory.
3430   */
3431   number_points=2047;
3432   primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3433     sizeof(*primitive_info));
3434   if (primitive_info == (PrimitiveInfo *) NULL)
3435     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3436   GetAffineMatrix(&affine);
3437   token=AcquireString(value);
3438   active=MagickFalse;
3439   n=0;
3440   status=MagickTrue;
3441   for (q=(const char *) value; *q != '\0'; )
3442   {
3443     /*
3444       Interpret graphic primitive.
3445     */
3446     GetMagickToken(q,&q,keyword);
3447     if (*keyword == '\0')
3448       break;
3449     if (*keyword == '#')
3450       {
3451         /*
3452           Comment.
3453         */
3454         if (active != MagickFalse)
3455           {
3456             AffineToTransform(image,&affine);
3457             active=MagickFalse;
3458           }
3459         (void) WriteBlobString(image,"<desc>");
3460         (void) WriteBlobString(image,keyword+1);
3461         for ( ; (*q != '\n') && (*q != '\0'); q++)
3462           switch (*q)
3463           {
3464             case '<': (void) WriteBlobString(image,"&lt;"); break;
3465             case '>': (void) WriteBlobString(image,"&gt;"); break;
3466             case '&': (void) WriteBlobString(image,"&amp;"); break;
3467             default: (void) WriteBlobByte(image,*q); break;
3468           }
3469         (void) WriteBlobString(image,"</desc>\n");
3470         continue;
3471       }
3472     primitive_type=UndefinedPrimitive;
3473     switch (*keyword)
3474     {
3475       case ';':
3476         break;
3477       case 'a':
3478       case 'A':
3479       {
3480         if (LocaleCompare("affine",keyword) == 0)
3481           {
3482             GetMagickToken(q,&q,token);
3483             affine.sx=atof(token);
3484             GetMagickToken(q,&q,token);
3485             if (*token == ',')
3486               GetMagickToken(q,&q,token);
3487             affine.rx=atof(token);
3488             GetMagickToken(q,&q,token);
3489             if (*token == ',')
3490               GetMagickToken(q,&q,token);
3491             affine.ry=atof(token);
3492             GetMagickToken(q,&q,token);
3493             if (*token == ',')
3494               GetMagickToken(q,&q,token);
3495             affine.sy=atof(token);
3496             GetMagickToken(q,&q,token);
3497             if (*token == ',')
3498               GetMagickToken(q,&q,token);
3499             affine.tx=atof(token);
3500             GetMagickToken(q,&q,token);
3501             if (*token == ',')
3502               GetMagickToken(q,&q,token);
3503             affine.ty=atof(token);
3504             break;
3505           }
3506         if (LocaleCompare("angle",keyword) == 0)
3507           {
3508             GetMagickToken(q,&q,token);
3509             affine.rx=atof(token);
3510             affine.ry=atof(token);
3511             break;
3512           }
3513         if (LocaleCompare("arc",keyword) == 0)
3514           {
3515             primitive_type=ArcPrimitive;
3516             break;
3517           }
3518         status=MagickFalse;
3519         break;
3520       }
3521       case 'b':
3522       case 'B':
3523       {
3524         if (LocaleCompare("bezier",keyword) == 0)
3525           {
3526             primitive_type=BezierPrimitive;
3527             break;
3528           }
3529         status=MagickFalse;
3530         break;
3531       }
3532       case 'c':
3533       case 'C':
3534       {
3535         if (LocaleCompare("clip-path",keyword) == 0)
3536           {
3537             GetMagickToken(q,&q,token);
3538             (void) FormatMagickString(message,MaxTextExtent,
3539               "clip-path:url(#%s);",token);
3540             (void) WriteBlobString(image,message);
3541             break;
3542           }
3543         if (LocaleCompare("clip-rule",keyword) == 0)
3544           {
3545             GetMagickToken(q,&q,token);
3546             (void) FormatMagickString(message,MaxTextExtent,
3547               "clip-rule:%s;",token);
3548             (void) WriteBlobString(image,message);
3549             break;
3550           }
3551         if (LocaleCompare("clip-units",keyword) == 0)
3552           {
3553             GetMagickToken(q,&q,token);
3554             (void) FormatMagickString(message,MaxTextExtent,
3555               "clipPathUnits=%s;",token);
3556             (void) WriteBlobString(image,message);
3557             break;
3558           }
3559         if (LocaleCompare("circle",keyword) == 0)
3560           {
3561             primitive_type=CirclePrimitive;
3562             break;
3563           }
3564         if (LocaleCompare("color",keyword) == 0)
3565           {
3566             primitive_type=ColorPrimitive;
3567             break;
3568           }
3569         status=MagickFalse;
3570         break;
3571       }
3572       case 'd':
3573       case 'D':
3574       {
3575         if (LocaleCompare("decorate",keyword) == 0)
3576           {
3577             GetMagickToken(q,&q,token);
3578             (void) FormatMagickString(message,MaxTextExtent,
3579               "text-decoration:%s;",token);
3580             (void) WriteBlobString(image,message);
3581             break;
3582           }
3583         status=MagickFalse;
3584         break;
3585       }
3586       case 'e':
3587       case 'E':
3588       {
3589         if (LocaleCompare("ellipse",keyword) == 0)
3590           {
3591             primitive_type=EllipsePrimitive;
3592             break;
3593           }
3594         status=MagickFalse;
3595         break;
3596       }
3597       case 'f':
3598       case 'F':
3599       {
3600         if (LocaleCompare("fill",keyword) == 0)
3601           {
3602             GetMagickToken(q,&q,token);
3603             (void) FormatMagickString(message,MaxTextExtent,"fill:%s;",
3604               token);
3605             (void) WriteBlobString(image,message);
3606             break;
3607           }
3608         if (LocaleCompare("fill-rule",keyword) == 0)
3609           {
3610             GetMagickToken(q,&q,token);
3611             (void) FormatMagickString(message,MaxTextExtent,
3612               "fill-rule:%s;",token);
3613             (void) WriteBlobString(image,message);
3614             break;
3615           }
3616         if (LocaleCompare("fill-opacity",keyword) == 0)
3617           {
3618             GetMagickToken(q,&q,token);
3619             (void) FormatMagickString(message,MaxTextExtent,
3620               "fill-opacity:%s;",token);
3621             (void) WriteBlobString(image,message);
3622             break;
3623           }
3624         if (LocaleCompare("font-family",keyword) == 0)
3625           {
3626             GetMagickToken(q,&q,token);
3627             (void) FormatMagickString(message,MaxTextExtent,
3628               "font-family:%s;",token);
3629             (void) WriteBlobString(image,message);
3630             break;
3631           }
3632         if (LocaleCompare("font-stretch",keyword) == 0)
3633           {
3634             GetMagickToken(q,&q,token);
3635             (void) FormatMagickString(message,MaxTextExtent,
3636               "font-stretch:%s;",token);
3637             (void) WriteBlobString(image,message);
3638             break;
3639           }
3640         if (LocaleCompare("font-style",keyword) == 0)
3641           {
3642             GetMagickToken(q,&q,token);
3643             (void) FormatMagickString(message,MaxTextExtent,
3644               "font-style:%s;",token);
3645             (void) WriteBlobString(image,message);
3646             break;
3647           }
3648         if (LocaleCompare("font-size",keyword) == 0)
3649           {
3650             GetMagickToken(q,&q,token);
3651             (void) FormatMagickString(message,MaxTextExtent,
3652               "font-size:%s;",token);
3653             (void) WriteBlobString(image,message);
3654             break;
3655           }
3656         if (LocaleCompare("font-weight",keyword) == 0)
3657           {
3658             GetMagickToken(q,&q,token);
3659             (void) FormatMagickString(message,MaxTextExtent,
3660               "font-weight:%s;",token);
3661             (void) WriteBlobString(image,message);
3662             break;
3663           }
3664         status=MagickFalse;
3665         break;
3666       }
3667       case 'g':
3668       case 'G':
3669       {
3670         if (LocaleCompare("gradient-units",keyword) == 0)
3671           {
3672             GetMagickToken(q,&q,token);
3673             break;
3674           }
3675         if (LocaleCompare("text-align",keyword) == 0)
3676           {
3677             GetMagickToken(q,&q,token);
3678             (void) FormatMagickString(message,MaxTextExtent,
3679               "text-align %s ",token);
3680             (void) WriteBlobString(image,message);
3681             break;
3682           }
3683         if (LocaleCompare("text-anchor",keyword) == 0)
3684           {
3685             GetMagickToken(q,&q,token);
3686             (void) FormatMagickString(message,MaxTextExtent,
3687               "text-anchor %s ",token);
3688             (void) WriteBlobString(image,message);
3689             break;
3690           }
3691         status=MagickFalse;
3692         break;
3693       }
3694       case 'i':
3695       case 'I':
3696       {
3697         if (LocaleCompare("image",keyword) == 0)
3698           {
3699             GetMagickToken(q,&q,token);
3700             primitive_type=ImagePrimitive;
3701             break;
3702           }
3703         status=MagickFalse;
3704         break;
3705       }
3706       case 'l':
3707       case 'L':
3708       {
3709         if (LocaleCompare("line",keyword) == 0)
3710           {
3711             primitive_type=LinePrimitive;
3712             break;
3713           }
3714         status=MagickFalse;
3715         break;
3716       }
3717       case 'm':
3718       case 'M':
3719       {
3720         if (LocaleCompare("matte",keyword) == 0)
3721           {
3722             primitive_type=MattePrimitive;
3723             break;
3724           }
3725         status=MagickFalse;
3726         break;
3727       }
3728       case 'o':
3729       case 'O':
3730       {
3731         if (LocaleCompare("opacity",keyword) == 0)
3732           {
3733             GetMagickToken(q,&q,token);
3734             (void) FormatMagickString(message,MaxTextExtent,"opacity %s ",
3735               token);
3736             (void) WriteBlobString(image,message);
3737             break;
3738           }
3739         status=MagickFalse;
3740         break;
3741       }
3742       case 'p':
3743       case 'P':
3744       {
3745         if (LocaleCompare("path",keyword) == 0)
3746           {
3747             primitive_type=PathPrimitive;
3748             break;
3749           }
3750         if (LocaleCompare("point",keyword) == 0)
3751           {
3752             primitive_type=PointPrimitive;
3753             break;
3754           }
3755         if (LocaleCompare("polyline",keyword) == 0)
3756           {
3757             primitive_type=PolylinePrimitive;
3758             break;
3759           }
3760         if (LocaleCompare("polygon",keyword) == 0)
3761           {
3762             primitive_type=PolygonPrimitive;
3763             break;
3764           }
3765         if (LocaleCompare("pop",keyword) == 0)
3766           {
3767             GetMagickToken(q,&q,token);
3768             if (LocaleCompare("clip-path",token) == 0)
3769               {
3770                 (void) WriteBlobString(image,"</clipPath>\n");
3771                 break;
3772               }
3773             if (LocaleCompare("defs",token) == 0)
3774               {
3775                 (void) WriteBlobString(image,"</defs>\n");
3776                 break;
3777               }
3778             if (LocaleCompare("gradient",token) == 0)
3779               {
3780                 (void) FormatMagickString(message,MaxTextExtent,
3781                   "</%sGradient>\n",type);
3782                 (void) WriteBlobString(image,message);
3783                 break;
3784               }
3785             if (LocaleCompare("graphic-context",token) == 0)
3786               {
3787                 n--;
3788                 if (n < 0)
3789                   ThrowWriterException(DrawError,
3790                     "UnbalancedGraphicContextPushPop");
3791                 (void) WriteBlobString(image,"</g>\n");
3792               }
3793             if (LocaleCompare("pattern",token) == 0)
3794               {
3795                 (void) WriteBlobString(image,"</pattern>\n");
3796                 break;
3797               }
3798             if (LocaleCompare("defs",token) == 0)
3799             (void) WriteBlobString(image,"</g>\n");
3800             break;
3801           }
3802         if (LocaleCompare("push",keyword) == 0)
3803           {
3804             GetMagickToken(q,&q,token);
3805             if (LocaleCompare("clip-path",token) == 0)
3806               {
3807                 GetMagickToken(q,&q,token);
3808                 (void) FormatMagickString(message,MaxTextExtent,
3809                   "<clipPath id=\"%s\">\n",token);
3810                 (void) WriteBlobString(image,message);
3811                 break;
3812               }
3813             if (LocaleCompare("defs",token) == 0)
3814               {
3815                 (void) WriteBlobString(image,"<defs>\n");
3816                 break;
3817               }
3818             if (LocaleCompare("gradient",token) == 0)
3819               {
3820                 GetMagickToken(q,&q,token);
3821                 (void) CopyMagickString(name,token,MaxTextExtent);
3822                 GetMagickToken(q,&q,token);
3823                 (void) CopyMagickString(type,token,MaxTextExtent);
3824                 GetMagickToken(q,&q,token);
3825                 svg_info.segment.x1=atof(token);
3826                 svg_info.element.cx=atof(token);
3827                 GetMagickToken(q,&q,token);
3828                 if (*token == ',')
3829                   GetMagickToken(q,&q,token);
3830                 svg_info.segment.y1=atof(token);
3831                 svg_info.element.cy=atof(token);
3832                 GetMagickToken(q,&q,token);
3833                 if (*token == ',')
3834                   GetMagickToken(q,&q,token);
3835                 svg_info.segment.x2=atof(token);
3836                 svg_info.element.major=atof(token);
3837                 GetMagickToken(q,&q,token);
3838                 if (*token == ',')
3839                   GetMagickToken(q,&q,token);
3840                 svg_info.segment.y2=atof(token);
3841                 svg_info.element.minor=atof(token);
3842                 (void) FormatMagickString(message,MaxTextExtent,
3843                   "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
3844                   "y2=\"%g\">\n",type,name,svg_info.segment.x1,
3845                   svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
3846                 if (LocaleCompare(type,"radial") == 0)
3847                   {
3848                     GetMagickToken(q,&q,token);
3849                     if (*token == ',')
3850                       GetMagickToken(q,&q,token);
3851                     svg_info.element.angle=atof(token);
3852                     (void) FormatMagickString(message,MaxTextExtent,
3853                       "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
3854                       "fx=\"%g\" fy=\"%g\">\n",type,name,svg_info.element.cx,
3855                       svg_info.element.cy,svg_info.element.angle,
3856                       svg_info.element.major,svg_info.element.minor);
3857                   }
3858                 (void) WriteBlobString(image,message);
3859                 break;
3860               }
3861             if (LocaleCompare("graphic-context",token) == 0)
3862               {
3863                 n++;
3864                 if (active)
3865                   {
3866                     AffineToTransform(image,&affine);
3867                     active=MagickFalse;
3868                   }
3869                 (void) WriteBlobString(image,"<g style=\"");
3870                 active=MagickTrue;
3871               }
3872             if (LocaleCompare("pattern",token) == 0)
3873               {
3874                 GetMagickToken(q,&q,token);
3875                 (void) CopyMagickString(name,token,MaxTextExtent);
3876                 GetMagickToken(q,&q,token);
3877                 svg_info.bounds.x=atof(token);
3878                 GetMagickToken(q,&q,token);
3879                 if (*token == ',')
3880                   GetMagickToken(q,&q,token);
3881                 svg_info.bounds.y=atof(token);
3882                 GetMagickToken(q,&q,token);
3883                 if (*token == ',')
3884                   GetMagickToken(q,&q,token);
3885                 svg_info.bounds.width=atof(token);
3886                 GetMagickToken(q,&q,token);
3887                 if (*token == ',')
3888                   GetMagickToken(q,&q,token);
3889                 svg_info.bounds.height=atof(token);
3890                 (void) FormatMagickString(message,MaxTextExtent,
3891                   "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
3892                   "height=\"%g\">\n",name,svg_info.bounds.x,svg_info.bounds.y,
3893                   svg_info.bounds.width,svg_info.bounds.height);
3894                 (void) WriteBlobString(image,message);
3895                 break;
3896               }
3897             break;
3898           }
3899         status=MagickFalse;
3900         break;
3901       }
3902       case 'r':
3903       case 'R':
3904       {
3905         if (LocaleCompare("rectangle",keyword) == 0)
3906           {
3907             primitive_type=RectanglePrimitive;
3908             break;
3909           }
3910         if (LocaleCompare("roundRectangle",keyword) == 0)
3911           {
3912             primitive_type=RoundRectanglePrimitive;
3913             break;
3914           }
3915         if (LocaleCompare("rotate",keyword) == 0)
3916           {
3917             GetMagickToken(q,&q,token);
3918             (void) FormatMagickString(message,MaxTextExtent,"rotate(%s) ",
3919               token);
3920             (void) WriteBlobString(image,message);
3921             break;
3922           }
3923         status=MagickFalse;
3924         break;
3925       }
3926       case 's':
3927       case 'S':
3928       {
3929         if (LocaleCompare("scale",keyword) == 0)
3930           {
3931             GetMagickToken(q,&q,token);
3932             affine.sx=atof(token);
3933             GetMagickToken(q,&q,token);
3934             if (*token == ',')
3935               GetMagickToken(q,&q,token);
3936             affine.sy=atof(token);
3937             break;
3938           }
3939         if (LocaleCompare("skewX",keyword) == 0)
3940           {
3941             GetMagickToken(q,&q,token);
3942             (void) FormatMagickString(message,MaxTextExtent,"skewX(%s) ",
3943               token);
3944             (void) WriteBlobString(image,message);
3945             break;
3946           }
3947         if (LocaleCompare("skewY",keyword) == 0)
3948           {
3949             GetMagickToken(q,&q,token);
3950             (void) FormatMagickString(message,MaxTextExtent,"skewY(%s) ",
3951               token);
3952             (void) WriteBlobString(image,message);
3953             break;
3954           }
3955         if (LocaleCompare("stop-color",keyword) == 0)
3956           {
3957             char
3958               color[MaxTextExtent];
3959
3960             GetMagickToken(q,&q,token);
3961             (void) CopyMagickString(color,token,MaxTextExtent);
3962             GetMagickToken(q,&q,token);
3963             (void) FormatMagickString(message,MaxTextExtent,
3964               "  <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
3965             (void) WriteBlobString(image,message);
3966             break;
3967           }
3968         if (LocaleCompare("stroke",keyword) == 0)
3969           {
3970             GetMagickToken(q,&q,token);
3971             (void) FormatMagickString(message,MaxTextExtent,"stroke:%s;",
3972               token);
3973             (void) WriteBlobString(image,message);
3974             break;
3975           }
3976         if (LocaleCompare("stroke-antialias",keyword) == 0)
3977           {
3978             GetMagickToken(q,&q,token);
3979             (void) FormatMagickString(message,MaxTextExtent,
3980               "stroke-antialias:%s;",token);
3981             (void) WriteBlobString(image,message);
3982             break;
3983           }
3984         if (LocaleCompare("stroke-dasharray",keyword) == 0)
3985           {
3986             if (IsPoint(q))
3987               {
3988                 long
3989                   k;
3990
3991                 p=q;
3992                 GetMagickToken(p,&p,token);
3993                 for (k=0; IsPoint(token); k++)
3994                   GetMagickToken(p,&p,token);
3995                 (void) WriteBlobString(image,"stroke-dasharray:");
3996                 for (j=0; j < k; j++)
3997                 {
3998                   GetMagickToken(q,&q,token);
3999                   (void) FormatMagickString(message,MaxTextExtent,"%s ",
4000                     token);
4001                   (void) WriteBlobString(image,message);
4002                 }
4003                 (void) WriteBlobString(image,";");
4004                 break;
4005               }
4006             GetMagickToken(q,&q,token);
4007             (void) FormatMagickString(message,MaxTextExtent,
4008               "stroke-dasharray:%s;",token);
4009             (void) WriteBlobString(image,message);
4010             break;
4011           }
4012         if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4013           {
4014             GetMagickToken(q,&q,token);
4015             (void) FormatMagickString(message,MaxTextExtent,
4016               "stroke-dashoffset:%s;",token);
4017             (void) WriteBlobString(image,message);
4018             break;
4019           }
4020         if (LocaleCompare("stroke-linecap",keyword) == 0)
4021           {
4022             GetMagickToken(q,&q,token);
4023             (void) FormatMagickString(message,MaxTextExtent,
4024               "stroke-linecap:%s;",token);
4025             (void) WriteBlobString(image,message);
4026             break;
4027           }
4028         if (LocaleCompare("stroke-linejoin",keyword) == 0)
4029           {
4030             GetMagickToken(q,&q,token);
4031             (void) FormatMagickString(message,MaxTextExtent,
4032               "stroke-linejoin:%s;",token);
4033             (void) WriteBlobString(image,message);
4034             break;
4035           }
4036         if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4037           {
4038             GetMagickToken(q,&q,token);
4039             (void) FormatMagickString(message,MaxTextExtent,
4040               "stroke-miterlimit:%s;",token);
4041             (void) WriteBlobString(image,message);
4042             break;
4043           }
4044         if (LocaleCompare("stroke-opacity",keyword) == 0)
4045           {
4046             GetMagickToken(q,&q,token);
4047             (void) FormatMagickString(message,MaxTextExtent,
4048               "stroke-opacity:%s;",token);
4049             (void) WriteBlobString(image,message);
4050             break;
4051           }
4052         if (LocaleCompare("stroke-width",keyword) == 0)
4053           {
4054             GetMagickToken(q,&q,token);
4055             (void) FormatMagickString(message,MaxTextExtent,
4056               "stroke-width:%s;",token);
4057             (void) WriteBlobString(image,message);
4058             continue;
4059           }
4060         status=MagickFalse;
4061         break;
4062       }
4063       case 't':
4064       case 'T':
4065       {
4066         if (LocaleCompare("text",keyword) == 0)
4067           {
4068             primitive_type=TextPrimitive;
4069             break;
4070           }
4071         if (LocaleCompare("text-antialias",keyword) == 0)
4072           {
4073             GetMagickToken(q,&q,token);
4074             (void) FormatMagickString(message,MaxTextExtent,
4075               "text-antialias:%s;",token);
4076             (void) WriteBlobString(image,message);
4077             break;
4078           }
4079         if (LocaleCompare("tspan",keyword) == 0)
4080           {
4081             primitive_type=TextPrimitive;
4082             break;
4083           }
4084         if (LocaleCompare("translate",keyword) == 0)
4085           {
4086             GetMagickToken(q,&q,token);
4087             affine.tx=atof(token);
4088             GetMagickToken(q,&q,token);
4089             if (*token == ',')
4090               GetMagickToken(q,&q,token);
4091             affine.ty=atof(token);
4092             break;
4093           }
4094         status=MagickFalse;
4095         break;
4096       }
4097       case 'v':
4098       case 'V':
4099       {
4100         if (LocaleCompare("viewbox",keyword) == 0)
4101           {
4102             GetMagickToken(q,&q,token);
4103             if (*token == ',')
4104               GetMagickToken(q,&q,token);
4105             GetMagickToken(q,&q,token);
4106             if (*token == ',')
4107               GetMagickToken(q,&q,token);
4108             GetMagickToken(q,&q,token);
4109             if (*token == ',')
4110               GetMagickToken(q,&q,token);
4111             GetMagickToken(q,&q,token);
4112             break;
4113           }
4114         status=MagickFalse;
4115         break;
4116       }
4117       default:
4118       {
4119         status=MagickFalse;
4120         break;
4121       }
4122     }
4123     if (status == MagickFalse)
4124       break;
4125     if (primitive_type == UndefinedPrimitive)
4126       continue;
4127     /*
4128       Parse the primitive attributes.
4129     */
4130     i=0;
4131     j=0;
4132     for (x=0; *q != '\0'; x++)
4133     {
4134       /*
4135         Define points.
4136       */
4137       if (IsPoint(q) == MagickFalse)
4138         break;
4139       GetMagickToken(q,&q,token);
4140       point.x=atof(token);
4141       GetMagickToken(q,&q,token);
4142       if (*token == ',')
4143         GetMagickToken(q,&q,token);
4144       point.y=atof(token);
4145       GetMagickToken(q,(const char **) NULL,token);
4146       if (*token == ',')
4147         GetMagickToken(q,&q,token);
4148       primitive_info[i].primitive=primitive_type;
4149       primitive_info[i].point=point;
4150       primitive_info[i].coordinates=0;
4151       primitive_info[i].method=FloodfillMethod;
4152       i++;
4153       if (i < (long) (number_points-6*BezierQuantum-360))
4154         continue;
4155       number_points+=6*BezierQuantum+360;
4156       primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4157         number_points,sizeof(*primitive_info));
4158       if (primitive_info == (PrimitiveInfo *) NULL)
4159         {
4160           (void) ThrowMagickException(&image->exception,GetMagickModule(),
4161             ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4162           break;
4163         }
4164     }
4165     primitive_info[j].primitive=primitive_type;
4166     primitive_info[j].coordinates=x;
4167     primitive_info[j].method=FloodfillMethod;
4168     primitive_info[j].text=(char *) NULL;
4169     if (active)
4170       {
4171         AffineToTransform(image,&affine);
4172         active=MagickFalse;
4173       }
4174     active=MagickFalse;
4175     switch (primitive_type)
4176     {
4177       case PointPrimitive:
4178       default:
4179       {
4180         if (primitive_info[j].coordinates != 1)
4181           {
4182             status=MagickFalse;
4183             break;
4184           }
4185         break;
4186       }
4187       case LinePrimitive:
4188       {
4189         if (primitive_info[j].coordinates != 2)
4190           {
4191             status=MagickFalse;
4192             break;
4193           }
4194           (void) FormatMagickString(message,MaxTextExtent,
4195           "  <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4196           primitive_info[j].point.x,primitive_info[j].point.y,
4197           primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4198         (void) WriteBlobString(image,message);
4199         break;
4200       }
4201       case RectanglePrimitive:
4202       {
4203         if (primitive_info[j].coordinates != 2)
4204           {
4205             status=MagickFalse;
4206             break;
4207           }
4208           (void) FormatMagickString(message,MaxTextExtent,
4209           "  <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4210           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         (void) WriteBlobString(image,message);
4214         break;
4215       }
4216       case RoundRectanglePrimitive:
4217       {
4218         if (primitive_info[j].coordinates != 3)
4219           {
4220             status=MagickFalse;
4221             break;
4222           }
4223         (void) FormatMagickString(message,MaxTextExtent,
4224           "  <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4225           "ry=\"%g\"/>\n",primitive_info[j].point.x,primitive_info[j].point.y,
4226           primitive_info[j+1].point.x-primitive_info[j].point.x,
4227           primitive_info[j+1].point.y-primitive_info[j].point.y,
4228           primitive_info[j+2].point.x,primitive_info[j+2].point.y);
4229         (void) WriteBlobString(image,message);
4230         break;
4231       }
4232       case ArcPrimitive:
4233       {
4234         if (primitive_info[j].coordinates != 3)
4235           {
4236             status=MagickFalse;
4237             break;
4238           }
4239         break;
4240       }
4241       case EllipsePrimitive:
4242       {
4243         if (primitive_info[j].coordinates != 3)
4244           {
4245             status=MagickFalse;
4246             break;
4247           }
4248           (void) FormatMagickString(message,MaxTextExtent,
4249           "  <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
4250           primitive_info[j].point.x,primitive_info[j].point.y,
4251           primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4252         (void) WriteBlobString(image,message);
4253         break;
4254       }
4255       case CirclePrimitive:
4256       {
4257         double
4258           alpha,
4259           beta;
4260
4261         if (primitive_info[j].coordinates != 2)
4262           {
4263             status=MagickFalse;
4264             break;
4265           }
4266         alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4267         beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4268         (void) FormatMagickString(message,MaxTextExtent,
4269           "  <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
4270           primitive_info[j].point.x,primitive_info[j].point.y,
4271           hypot(alpha,beta));
4272         (void) WriteBlobString(image,message);
4273         break;
4274       }
4275       case PolylinePrimitive:
4276       {
4277         if (primitive_info[j].coordinates < 2)
4278           {
4279             status=MagickFalse;
4280             break;
4281           }
4282         (void) CopyMagickString(message,"  <polyline points=\"",MaxTextExtent);
4283         (void) WriteBlobString(image,message);
4284         length=strlen(message);
4285         for ( ; j < i; j++)
4286         {
4287           (void) FormatMagickString(message,MaxTextExtent,"%g,%g ",
4288             primitive_info[j].point.x,primitive_info[j].point.y);
4289           length+=strlen(message);
4290           if (length >= 80)
4291             {
4292               (void) WriteBlobString(image,"\n    ");
4293               length=strlen(message)+5;
4294             }
4295           (void) WriteBlobString(image,message);
4296         }
4297         (void) WriteBlobString(image,"\"/>\n");
4298         break;
4299       }
4300       case PolygonPrimitive:
4301       {
4302         if (primitive_info[j].coordinates < 3)
4303           {
4304             status=MagickFalse;
4305             break;
4306           }
4307         primitive_info[i]=primitive_info[j];
4308         primitive_info[i].coordinates=0;
4309         primitive_info[j].coordinates++;
4310         i++;
4311         (void) CopyMagickString(message,"  <polygon points=\"",MaxTextExtent);
4312         (void) WriteBlobString(image,message);
4313         length=strlen(message);
4314         for ( ; j < i; j++)
4315         {
4316           (void) FormatMagickString(message,MaxTextExtent,"%g,%g ",
4317             primitive_info[j].point.x,primitive_info[j].point.y);
4318           length+=strlen(message);
4319           if (length >= 80)
4320             {
4321               (void) WriteBlobString(image,"\n    ");
4322               length=strlen(message)+5;
4323             }
4324           (void) WriteBlobString(image,message);
4325         }
4326         (void) WriteBlobString(image,"\"/>\n");
4327         break;
4328       }
4329       case BezierPrimitive:
4330       {
4331         if (primitive_info[j].coordinates < 3)
4332           {
4333             status=MagickFalse;
4334             break;
4335           }
4336         break;
4337       }
4338       case PathPrimitive:
4339       {
4340         int
4341           number_attributes;
4342
4343         GetMagickToken(q,&q,token);
4344         number_attributes=1;
4345         for (p=token; *p != '\0'; p++)
4346           if (isalpha((int) *p))
4347             number_attributes++;
4348         if (i > (long) (number_points-6*BezierQuantum*number_attributes-1))
4349           {
4350             number_points+=6*BezierQuantum*number_attributes;
4351             primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4352               number_points,sizeof(*primitive_info));
4353             if (primitive_info == (PrimitiveInfo *) NULL)
4354               {
4355                 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4356                   ResourceLimitError,"MemoryAllocationFailed","`%s'",
4357                   image->filename);
4358                 break;
4359               }
4360           }
4361         (void) WriteBlobString(image,"  <path d=\"");
4362         (void) WriteBlobString(image,token);
4363         (void) WriteBlobString(image,"\"/>\n");
4364         break;
4365       }
4366       case ColorPrimitive:
4367       case MattePrimitive:
4368       {
4369         if (primitive_info[j].coordinates != 1)
4370           {
4371             status=MagickFalse;
4372             break;
4373           }
4374         GetMagickToken(q,&q,token);
4375         if (LocaleCompare("point",token) == 0)
4376           primitive_info[j].method=PointMethod;
4377         if (LocaleCompare("replace",token) == 0)
4378           primitive_info[j].method=ReplaceMethod;
4379         if (LocaleCompare("floodfill",token) == 0)
4380           primitive_info[j].method=FloodfillMethod;
4381         if (LocaleCompare("filltoborder",token) == 0)
4382           primitive_info[j].method=FillToBorderMethod;
4383         if (LocaleCompare("reset",token) == 0)
4384           primitive_info[j].method=ResetMethod;
4385         break;
4386       }
4387       case TextPrimitive:
4388       {
4389         register char
4390           *p;
4391
4392         if (primitive_info[j].coordinates != 1)
4393           {
4394             status=MagickFalse;
4395             break;
4396           }
4397         GetMagickToken(q,&q,token);
4398         (void) FormatMagickString(message,MaxTextExtent,
4399           "  <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
4400           primitive_info[j].point.y);
4401         (void) WriteBlobString(image,message);
4402         for (p=token; *p != '\0'; p++)
4403           switch (*p)
4404           {
4405             case '<': (void) WriteBlobString(image,"&lt;"); break;
4406             case '>': (void) WriteBlobString(image,"&gt;"); break;
4407             case '&': (void) WriteBlobString(image,"&amp;"); break;
4408             default: (void) WriteBlobByte(image,*p); break;
4409           }
4410         (void) WriteBlobString(image,"</text>\n");
4411         break;
4412       }
4413       case ImagePrimitive:
4414       {
4415         if (primitive_info[j].coordinates != 2)
4416           {
4417             status=MagickFalse;
4418             break;
4419           }
4420         GetMagickToken(q,&q,token);
4421         (void) FormatMagickString(message,MaxTextExtent,
4422           "  <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
4423           "xlink:href=\"%s\"/>\n",primitive_info[j].point.x,
4424           primitive_info[j].point.y,primitive_info[j+1].point.x,
4425           primitive_info[j+1].point.y,token);
4426         (void) WriteBlobString(image,message);
4427         break;
4428       }
4429     }
4430     if (primitive_info == (PrimitiveInfo *) NULL)
4431       break;
4432     primitive_info[i].primitive=UndefinedPrimitive;
4433     if (status == MagickFalse)
4434       break;
4435   }
4436   (void) WriteBlobString(image,"</svg>\n");
4437   /*
4438     Relinquish resources.
4439   */
4440   token=DestroyString(token);
4441   if (primitive_info != (PrimitiveInfo *) NULL)
4442     primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4443   (void) CloseBlob(image);
4444   return(status);
4445 }