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