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