]> 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   size_t
145     width,
146     height;
147
148   char
149     *size,
150     *title,
151     *comment;
152
153   int
154     n;
155
156   double
157     *scale,
158     pointsize;
159
160   ElementInfo
161     element;
162
163   SegmentInfo
164     segment;
165
166   BoundingBox
167     bounds,
168     center,
169     view_box;
170
171   PointInfo
172     radius;
173
174   char
175     *stop_color,
176     *offset,
177     *text,
178     *vertices,
179     *url;
180
181 #if defined(MAGICKCORE_XML_DELEGATE)
182   xmlParserCtxtPtr
183     parser;
184
185   xmlDocPtr
186     document;
187 #endif
188 } SVGInfo;
189 \f
190 /*
191   Forward declarations.
192 */
193 static MagickBooleanType
194   WriteSVGImage(const ImageInfo *,Image *);
195 \f
196 /*
197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
198 %                                                                             %
199 %                                                                             %
200 %                                                                             %
201 %   I s S V G                                                                 %
202 %                                                                             %
203 %                                                                             %
204 %                                                                             %
205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
206 %
207 %  IsSVG()() returns MagickTrue if the image format type, identified by the
208 %  magick string, is SVG.
209 %
210 %  The format of the IsSVG method is:
211 %
212 %      MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
213 %
214 %  A description of each parameter follows:
215 %
216 %    o magick: compare image format pattern against these bytes.
217 %
218 %    o length: Specifies the length of the magick string.
219 %
220 */
221 static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
222 {
223   if (length < 4)
224     return(MagickFalse);
225   if (LocaleNCompare((const char *) magick,"?xml",4) == 0)
226     return(MagickTrue);
227   return(MagickFalse);
228 }
229 \f
230 #if defined(MAGICKCORE_XML_DELEGATE)
231 /*
232 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
233 %                                                                             %
234 %                                                                             %
235 %                                                                             %
236 %   R e a d S V G I m a g e                                                   %
237 %                                                                             %
238 %                                                                             %
239 %                                                                             %
240 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241 %
242 %  ReadSVGImage() reads a Scalable Vector Gaphics file and returns it.  It
243 %  allocates the memory necessary for the new Image structure and returns a
244 %  pointer to the new image.
245 %
246 %  The format of the ReadSVGImage method is:
247 %
248 %      Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
249 %
250 %  A description of each parameter follows:
251 %
252 %    o image_info: the image info.
253 %
254 %    o exception: return any errors or warnings in this structure.
255 %
256 */
257
258 static SVGInfo *AcquireSVGInfo(void)
259 {
260   SVGInfo
261     *svg_info;
262
263   svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info));
264   if (svg_info == (SVGInfo *) NULL)
265     return((SVGInfo *) NULL);
266   (void) ResetMagickMemory(svg_info,0,sizeof(*svg_info));
267   svg_info->text=AcquireString("");
268   svg_info->scale=(double *) AcquireMagickMemory(sizeof(*svg_info->scale));
269   if (svg_info->scale == (double *) NULL)
270     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
271   GetAffineMatrix(&svg_info->affine);
272   svg_info->scale[0]=ExpandAffine(&svg_info->affine);
273   return(svg_info);
274 }
275
276 static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
277 {
278   if (svg_info->text != (char *) NULL)
279     svg_info->text=DestroyString(svg_info->text);
280   if (svg_info->scale != (double *) NULL)
281     svg_info->scale=(double *) (svg_info->scale);
282   if (svg_info->title != (char *) NULL)
283     svg_info->title=DestroyString(svg_info->title);
284   if (svg_info->comment != (char *) NULL)
285     svg_info->comment=DestroyString(svg_info->comment);
286   return((SVGInfo *) RelinquishMagickMemory(svg_info));
287 }
288
289 static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
290   const char *string)
291 {
292   char
293     token[MaxTextExtent];
294
295   const char
296     *p;
297
298   double
299     value;
300
301   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
302   assert(string != (const char *) NULL);
303   p=(const char *) string;
304   GetMagickToken(p,&p,token);
305   value=StringToDouble(token);
306   if (strchr(token,'%') != (char *) NULL)
307     {
308       double
309         alpha,
310         beta;
311
312       if (type > 0)
313         {
314           if (svg_info->view_box.width == 0.0)
315             return(1000.0);
316           return(svg_info->view_box.width*value/100.0);
317         }
318       if (type < 0)
319         {
320           if (svg_info->view_box.height == 0.0)
321             return(1000.0);
322           return(svg_info->view_box.height*value/100.0);
323         }
324       alpha=value-svg_info->view_box.width;
325       beta=value-svg_info->view_box.height;
326       return(hypot(alpha,beta)/sqrt(2.0)/100.0);
327     }
328   GetMagickToken(p,&p,token);
329   if (LocaleNCompare(token,"cm",2) == 0)
330     return(DefaultResolution*svg_info->scale[0]/2.54*value);
331   if (LocaleNCompare(token,"em",2) == 0)
332     return(svg_info->pointsize*value);
333   if (LocaleNCompare(token,"ex",2) == 0)
334     return(svg_info->pointsize*value/2.0);
335   if (LocaleNCompare(token,"in",2) == 0)
336     return(DefaultResolution*svg_info->scale[0]*value);
337   if (LocaleNCompare(token,"mm",2) == 0)
338     return(DefaultResolution*svg_info->scale[0]/25.4*value);
339   if (LocaleNCompare(token,"pc",2) == 0)
340     return(DefaultResolution*svg_info->scale[0]/6.0*value);
341   if (LocaleNCompare(token,"pt",2) == 0)
342     return(svg_info->scale[0]*value);
343   if (LocaleNCompare(token,"px",2) == 0)
344     return(value);
345   return(value);
346 }
347
348 static void StripStyleTokens(char *message)
349 {
350   register char
351     *p,
352     *q;
353
354   size_t
355     length;
356
357   assert(message != (char *) NULL);
358   if (*message == '\0')
359     return;
360   length=strlen(message);
361   p=message;
362   while (isspace((int) ((unsigned char) *p)) != 0)
363     p++;
364   q=message+length-1;
365   while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
366     q--;
367   (void) CopyMagickMemory(message,p,(size_t) (q-p+1));
368   message[q-p+1]='\0';
369   StripString(message);
370 }
371
372 static char **GetStyleTokens(void *context,const char *style,int *number_tokens)
373 {
374   char
375     *text,
376     **tokens;
377
378   register ssize_t
379     i;
380
381   SVGInfo
382     *svg_info;
383
384   svg_info=(SVGInfo *) context;
385   *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 ssize_t
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 ssize_t
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=affine.sx*current.sx+affine.ry*current.rx;
1395                 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1396                 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1397                 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1398                 transform.tx=affine.sx*current.tx+affine.ry*current.ty+
1399                   affine.tx;
1400                 transform.ty=affine.rx*current.tx+affine.sy*current.ty+
1401                   affine.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=affine.sx*current.sx+affine.ry*current.rx;
1987                 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1988                 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1989                 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1990                 transform.tx=affine.sx*current.tx+affine.ry*current.ty+
1991                   affine.tx;
1992                 transform.ty=affine.rx*current.tx+affine.sy*current.ty+
1993                   affine.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=(size_t) floor(svg_info->bounds.width+0.5);
2118           svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2119           MVGPrintf(svg_info->file,"viewbox 0 0 %.20g %.20g\n",(double)
2120             svg_info->width,(double) 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 ssize_t
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,%.20g)",c,(double) 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 < (ssize_t) 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   ssize_t
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       ssize_t
2754         y;
2755
2756       PixelPacket
2757         fill_color;
2758
2759       register ssize_t
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 < (ssize_t) 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 < (ssize_t) 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,(MagickOffsetType) y,
2884                 image->rows);
2885                 if (status == MagickFalse)
2886                   break;
2887               }
2888           }
2889         }
2890 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2891       if (pixels != (unsigned char *) NULL)
2892         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2893 #else
2894       g_object_unref(G_OBJECT(pixel_info));
2895 #endif
2896       (void) CloseBlob(image);
2897       return(GetFirstImageInList(image));
2898 #endif
2899     }
2900   /*
2901     Open draw file.
2902   */
2903   file=(FILE *) NULL;
2904   unique_file=AcquireUniqueFileResource(filename);
2905   if (unique_file != -1)
2906     file=fdopen(unique_file,"w");
2907   if ((unique_file == -1) || (file == (FILE *) NULL))
2908     {
2909       (void) CopyMagickString(image->filename,filename,MaxTextExtent);
2910       ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
2911         image->filename);
2912       image=DestroyImageList(image);
2913       return((Image *) NULL);
2914     }
2915   /*
2916     Parse SVG file.
2917   */
2918   svg_info=AcquireSVGInfo();
2919   if (svg_info == (SVGInfo *) NULL)
2920     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2921   svg_info->file=file;
2922   svg_info->exception=exception;
2923   svg_info->image=image;
2924   svg_info->image_info=image_info;
2925   svg_info->bounds.width=image->columns;
2926   svg_info->bounds.height=image->rows;
2927   if (image_info->size != (char *) NULL)
2928     (void) CloneString(&svg_info->size,image_info->size);
2929   if (image->debug != MagickFalse)
2930     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
2931   xmlInitParser();
2932   (void) xmlSubstituteEntitiesDefault(1);
2933   (void) ResetMagickMemory(&sax_modules,0,sizeof(sax_modules));
2934   sax_modules.internalSubset=SVGInternalSubset;
2935   sax_modules.isStandalone=SVGIsStandalone;
2936   sax_modules.hasInternalSubset=SVGHasInternalSubset;
2937   sax_modules.hasExternalSubset=SVGHasExternalSubset;
2938   sax_modules.resolveEntity=SVGResolveEntity;
2939   sax_modules.getEntity=SVGGetEntity;
2940   sax_modules.entityDecl=SVGEntityDeclaration;
2941   sax_modules.notationDecl=SVGNotationDeclaration;
2942   sax_modules.attributeDecl=SVGAttributeDeclaration;
2943   sax_modules.elementDecl=SVGElementDeclaration;
2944   sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
2945   sax_modules.setDocumentLocator=SVGSetDocumentLocator;
2946   sax_modules.startDocument=SVGStartDocument;
2947   sax_modules.endDocument=SVGEndDocument;
2948   sax_modules.startElement=SVGStartElement;
2949   sax_modules.endElement=SVGEndElement;
2950   sax_modules.reference=SVGReference;
2951   sax_modules.characters=SVGCharacters;
2952   sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
2953   sax_modules.processingInstruction=SVGProcessingInstructions;
2954   sax_modules.comment=SVGComment;
2955   sax_modules.warning=SVGWarning;
2956   sax_modules.error=SVGError;
2957   sax_modules.fatalError=SVGError;
2958   sax_modules.getParameterEntity=SVGGetParameterEntity;
2959   sax_modules.cdataBlock=SVGCDataBlock;
2960   sax_modules.externalSubset=SVGExternalSubset;
2961   sax_handler=(&sax_modules);
2962   n=ReadBlob(image,MaxTextExtent,message);
2963   if (n > 0)
2964     {
2965       svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
2966         message,n,image->filename);
2967       while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
2968       {
2969         status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
2970         if (status != 0)
2971           break;
2972       }
2973     }
2974   (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
2975   xmlFreeParserCtxt(svg_info->parser);
2976   if (image->debug != MagickFalse)
2977     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
2978   xmlCleanupParser();
2979   (void) fclose(file);
2980   (void) CloseBlob(image);
2981   image->columns=svg_info->width;
2982   image->rows=svg_info->height;
2983   if (exception->severity >= ErrorException)
2984     {
2985       image=DestroyImage(image);
2986       return((Image *) NULL);
2987     }
2988   if (image_info->ping == MagickFalse)
2989     {
2990       ImageInfo
2991         *read_info;
2992
2993       /*
2994         Draw image.
2995       */
2996       image=DestroyImage(image);
2997       image=(Image *) NULL;
2998       read_info=CloneImageInfo(image_info);
2999       SetImageInfoBlob(read_info,(void *) NULL,0);
3000       if (read_info->density != (char *) NULL)
3001         read_info->density=DestroyString(read_info->density);
3002       (void) FormatMagickString(read_info->filename,MaxTextExtent,"mvg:%s",
3003         filename);
3004       image=ReadImage(read_info,exception);
3005       read_info=DestroyImageInfo(read_info);
3006       if (image != (Image *) NULL)
3007         (void) CopyMagickString(image->filename,image_info->filename,
3008           MaxTextExtent);
3009     }
3010   /*
3011     Relinquish resources.
3012   */
3013   if (image != (Image *) NULL)
3014     {
3015       if (svg_info->title != (char *) NULL)
3016         (void) SetImageProperty(image,"svg:title",svg_info->title);
3017       if (svg_info->comment != (char *) NULL)
3018         (void) SetImageProperty(image,"svg:comment",svg_info->comment);
3019     }
3020   svg_info=DestroySVGInfo(svg_info);
3021   (void) RelinquishUniqueFileResource(filename);
3022   return(GetFirstImageInList(image));
3023 }
3024 #endif
3025 \f
3026 /*
3027 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3028 %                                                                             %
3029 %                                                                             %
3030 %                                                                             %
3031 %   R e g i s t e r S V G I m a g e                                           %
3032 %                                                                             %
3033 %                                                                             %
3034 %                                                                             %
3035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3036 %
3037 %  RegisterSVGImage() adds attributes for the SVG image format to
3038 %  the list of supported formats.  The attributes include the image format
3039 %  tag, a method to read and/or write the format, whether the format
3040 %  supports the saving of more than one frame to the same file or blob,
3041 %  whether the format supports native in-memory I/O, and a brief
3042 %  description of the format.
3043 %
3044 %  The format of the RegisterSVGImage method is:
3045 %
3046 %      size_t RegisterSVGImage(void)
3047 %
3048 */
3049 ModuleExport size_t RegisterSVGImage(void)
3050 {
3051   char
3052     version[MaxTextExtent];
3053
3054   MagickInfo
3055     *entry;
3056
3057   *version='\0';
3058 #if defined(LIBXML_DOTTED_VERSION)
3059   (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,MaxTextExtent);
3060 #endif
3061 #if defined(MAGICKCORE_RSVG_DELEGATE)
3062   rsvg_init();
3063   (void) FormatMagickString(version,MaxTextExtent,"RSVG %d.%d.%d",
3064     LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3065 #endif
3066   entry=SetMagickInfo("SVG");
3067 #if defined(MAGICKCORE_XML_DELEGATE)
3068   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3069 #endif
3070   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3071   entry->blob_support=MagickFalse;
3072   entry->seekable_stream=MagickFalse;
3073   entry->description=ConstantString("Scalable Vector Graphics");
3074   if (*version != '\0')
3075     entry->version=ConstantString(version);
3076   entry->magick=(IsImageFormatHandler *) IsSVG;
3077   entry->module=ConstantString("SVG");
3078   (void) RegisterMagickInfo(entry);
3079   entry=SetMagickInfo("SVGZ");
3080 #if defined(MAGICKCORE_XML_DELEGATE)
3081   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3082 #endif
3083   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3084   entry->blob_support=MagickFalse;
3085   entry->seekable_stream=MagickFalse;
3086   entry->description=ConstantString("Compressed Scalable Vector Graphics");
3087   if (*version != '\0')
3088     entry->version=ConstantString(version);
3089   entry->magick=(IsImageFormatHandler *) IsSVG;
3090   entry->module=ConstantString("SVG");
3091   (void) RegisterMagickInfo(entry);
3092   entry=SetMagickInfo("MSVG");
3093 #if defined(MAGICKCORE_XML_DELEGATE)
3094   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3095 #endif
3096   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3097   entry->blob_support=MagickFalse;
3098   entry->seekable_stream=MagickFalse;
3099   entry->description=ConstantString("ImageMagick's own SVG internal renderer");
3100   entry->magick=(IsImageFormatHandler *) IsSVG;
3101   entry->module=ConstantString("SVG");
3102   (void) RegisterMagickInfo(entry);
3103   return(MagickImageCoderSignature);
3104 }
3105 \f
3106 /*
3107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3108 %                                                                             %
3109 %                                                                             %
3110 %                                                                             %
3111 %   U n r e g i s t e r S V G I m a g e                                       %
3112 %                                                                             %
3113 %                                                                             %
3114 %                                                                             %
3115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3116 %
3117 %  UnregisterSVGImage() removes format registrations made by the
3118 %  SVG module from the list of supported formats.
3119 %
3120 %  The format of the UnregisterSVGImage method is:
3121 %
3122 %      UnregisterSVGImage(void)
3123 %
3124 */
3125 ModuleExport void UnregisterSVGImage(void)
3126 {
3127   (void) UnregisterMagickInfo("SVGZ");
3128   (void) UnregisterMagickInfo("SVG");
3129   (void) UnregisterMagickInfo("MSVG");
3130 #if defined(MAGICKCORE_RSVG_DELEGATE)
3131   rsvg_term();
3132 #endif
3133 }
3134 \f
3135 /*
3136 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3137 %                                                                             %
3138 %                                                                             %
3139 %                                                                             %
3140 %   W r i t e S V G I m a g e                                                 %
3141 %                                                                             %
3142 %                                                                             %
3143 %                                                                             %
3144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3145 %
3146 %  WriteSVGImage() writes a image in the SVG - XML based W3C standard
3147 %  format.
3148 %
3149 %  The format of the WriteSVGImage method is:
3150 %
3151 %      MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image)
3152 %
3153 %  A description of each parameter follows.
3154 %
3155 %    o image_info: the image info.
3156 %
3157 %    o image:  The image.
3158 %
3159 */
3160
3161 static void AffineToTransform(Image *image,AffineMatrix *affine)
3162 {
3163   char
3164     transform[MaxTextExtent];
3165
3166   if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3167     {
3168       if ((fabs(affine->rx) < MagickEpsilon) &&
3169           (fabs(affine->ry) < MagickEpsilon))
3170         {
3171           if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3172               (fabs(affine->sy-1.0) < MagickEpsilon))
3173             {
3174               (void) WriteBlobString(image,"\">\n");
3175               return;
3176             }
3177           (void) FormatMagickString(transform,MaxTextExtent,
3178             "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3179           (void) WriteBlobString(image,transform);
3180           return;
3181         }
3182       else
3183         {
3184           if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3185               (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3186               (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3187                2*MagickEpsilon))
3188             {
3189               double
3190                 theta;
3191
3192               theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3193               (void) FormatMagickString(transform,MaxTextExtent,
3194                 "\" transform=\"rotate(%g)\">\n",theta);
3195               (void) WriteBlobString(image,transform);
3196               return;
3197             }
3198         }
3199     }
3200   else
3201     {
3202       if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3203           (fabs(affine->rx) < MagickEpsilon) &&
3204           (fabs(affine->ry) < MagickEpsilon) &&
3205           (fabs(affine->sy-1.0) < MagickEpsilon))
3206         {
3207           (void) FormatMagickString(transform,MaxTextExtent,
3208             "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3209           (void) WriteBlobString(image,transform);
3210           return;
3211         }
3212     }
3213   (void) FormatMagickString(transform,MaxTextExtent,
3214     "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3215     affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3216   (void) WriteBlobString(image,transform);
3217 }
3218
3219 static MagickBooleanType IsPoint(const char *point)
3220 {
3221   char
3222     *p;
3223
3224   ssize_t
3225     value;
3226
3227   value=strtol(point,&p,10);
3228   return(p != point ? MagickTrue : MagickFalse);
3229 }
3230
3231 static MagickBooleanType TraceSVGImage(Image *image)
3232 {
3233   ssize_t
3234     y;
3235
3236   register const PixelPacket
3237     *p;
3238
3239   register ssize_t
3240     x;
3241
3242 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3243   {
3244     at_bitmap_type
3245       *trace;
3246
3247     at_fitting_opts_type
3248       *fitting_options;
3249
3250     at_output_opts_type
3251       *output_options;
3252
3253     at_splines_type
3254       *splines;
3255
3256     ImageType
3257       type;
3258
3259     register ssize_t
3260       i;
3261
3262     size_t
3263       number_planes;
3264
3265     /*
3266       Trace image and write as SVG.
3267     */
3268     fitting_options=at_fitting_opts_new();
3269     output_options=at_output_opts_new();
3270     type=GetImageType(image,&image->exception);
3271     number_planes=3;
3272     if ((type == BilevelType) || (type == GrayscaleType))
3273       number_planes=1;
3274     trace=at_bitmap_new(image->columns,image->rows,number_planes);
3275     i=0;
3276     for (y=0; y < (ssize_t) image->rows; y++)
3277     {
3278       p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
3279       if (p == (const PixelPacket *) NULL)
3280         break;
3281       for (x=0; x < (ssize_t) image->columns; x++)
3282       {
3283         trace->bitmap[i++]=GetRedPixelComponent(p);
3284         if (number_planes == 3)
3285           {
3286             trace->bitmap[i++]=GetGreenPixelComponent(p);
3287             trace->bitmap[i++]=GetBluePixelComponent(p);
3288           }
3289         p++;
3290       }
3291     }
3292     splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3293       NULL);
3294     at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3295       GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3296       NULL);
3297     /*
3298       Free resources.
3299     */
3300     at_splines_free(splines);
3301     at_bitmap_free(trace);
3302     at_output_opts_free(output_options);
3303     at_fitting_opts_free(fitting_options);
3304   }
3305 #else
3306   {
3307     char
3308       message[MaxTextExtent],
3309       tuple[MaxTextExtent];
3310
3311     MagickPixelPacket
3312       pixel;
3313
3314     register const IndexPacket
3315       *indexes;
3316
3317     (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3318     (void) WriteBlobString(image,
3319       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3320     (void) WriteBlobString(image,
3321       "  \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3322     (void) FormatMagickString(message,MaxTextExtent,
3323       "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,
3324       (double) image->rows);
3325     (void) WriteBlobString(image,message);
3326     GetMagickPixelPacket(image,&pixel);
3327     for (y=0; y < (ssize_t) image->rows; y++)
3328     {
3329       p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
3330       if (p == (const PixelPacket *) NULL)
3331         break;
3332       indexes=GetVirtualIndexQueue(image);
3333       for (x=0; x < (ssize_t) image->columns; x++)
3334       {
3335         SetMagickPixelPacket(image,p,indexes+x,&pixel);
3336         (void) QueryMagickColorname(image,&pixel,SVGCompliance,tuple,
3337           &image->exception);
3338         (void) FormatMagickString(message,MaxTextExtent,
3339           "  <circle cx=\"%.20g\" cy=\"%.20g\" r=\"1\" fill=\"%s\"/>\n",
3340           (double) x,(double) y,tuple);
3341         (void) WriteBlobString(image,message);
3342         p++;
3343       }
3344     }
3345     (void) WriteBlobString(image,"</svg>\n");
3346   }
3347 #endif
3348   return(MagickTrue);
3349 }
3350
3351 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image)
3352 {
3353 #define BezierQuantum  200
3354
3355   AffineMatrix
3356     affine;
3357
3358   char
3359     keyword[MaxTextExtent],
3360     message[MaxTextExtent],
3361     name[MaxTextExtent],
3362     *token,
3363     type[MaxTextExtent];
3364
3365   const char
3366     *p,
3367     *q,
3368     *value;
3369
3370   int
3371     n;
3372
3373   ssize_t
3374     j;
3375
3376   MagickBooleanType
3377     active,
3378     status;
3379
3380   PointInfo
3381     point;
3382
3383   PrimitiveInfo
3384     *primitive_info;
3385
3386   PrimitiveType
3387     primitive_type;
3388
3389   register ssize_t
3390     x;
3391
3392   register ssize_t
3393     i;
3394
3395   size_t
3396     length;
3397
3398   SVGInfo
3399     svg_info;
3400
3401   size_t
3402     number_points;
3403
3404   /*
3405     Open output image file.
3406   */
3407   assert(image_info != (const ImageInfo *) NULL);
3408   assert(image_info->signature == MagickSignature);
3409   assert(image != (Image *) NULL);
3410   assert(image->signature == MagickSignature);
3411   if (image->debug != MagickFalse)
3412     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3413   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
3414   if (status == MagickFalse)
3415     return(status);
3416   value=GetImageArtifact(image,"SVG");
3417   if (value != (char *) NULL)
3418     {
3419       (void) WriteBlobString(image,value);
3420       (void) CloseBlob(image);
3421       return(MagickTrue);
3422     }
3423   value=GetImageArtifact(image,"MVG");
3424   if (value == (char *) NULL)
3425     return(TraceSVGImage(image));
3426   /*
3427     Write SVG header.
3428   */
3429   (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3430   (void) WriteBlobString(image,
3431     "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3432   (void) WriteBlobString(image,
3433     "  \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3434   (void) FormatMagickString(message,MaxTextExtent,
3435     "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3436     image->rows);
3437   (void) WriteBlobString(image,message);
3438   /*
3439     Allocate primitive info memory.
3440   */
3441   number_points=2047;
3442   primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3443     sizeof(*primitive_info));
3444   if (primitive_info == (PrimitiveInfo *) NULL)
3445     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3446   GetAffineMatrix(&affine);
3447   token=AcquireString(value);
3448   active=MagickFalse;
3449   n=0;
3450   status=MagickTrue;
3451   for (q=(const char *) value; *q != '\0'; )
3452   {
3453     /*
3454       Interpret graphic primitive.
3455     */
3456     GetMagickToken(q,&q,keyword);
3457     if (*keyword == '\0')
3458       break;
3459     if (*keyword == '#')
3460       {
3461         /*
3462           Comment.
3463         */
3464         if (active != MagickFalse)
3465           {
3466             AffineToTransform(image,&affine);
3467             active=MagickFalse;
3468           }
3469         (void) WriteBlobString(image,"<desc>");
3470         (void) WriteBlobString(image,keyword+1);
3471         for ( ; (*q != '\n') && (*q != '\0'); q++)
3472           switch (*q)
3473           {
3474             case '<': (void) WriteBlobString(image,"&lt;"); break;
3475             case '>': (void) WriteBlobString(image,"&gt;"); break;
3476             case '&': (void) WriteBlobString(image,"&amp;"); break;
3477             default: (void) WriteBlobByte(image,*q); break;
3478           }
3479         (void) WriteBlobString(image,"</desc>\n");
3480         continue;
3481       }
3482     primitive_type=UndefinedPrimitive;
3483     switch (*keyword)
3484     {
3485       case ';':
3486         break;
3487       case 'a':
3488       case 'A':
3489       {
3490         if (LocaleCompare("affine",keyword) == 0)
3491           {
3492             GetMagickToken(q,&q,token);
3493             affine.sx=StringToDouble(token);
3494             GetMagickToken(q,&q,token);
3495             if (*token == ',')
3496               GetMagickToken(q,&q,token);
3497             affine.rx=StringToDouble(token);
3498             GetMagickToken(q,&q,token);
3499             if (*token == ',')
3500               GetMagickToken(q,&q,token);
3501             affine.ry=StringToDouble(token);
3502             GetMagickToken(q,&q,token);
3503             if (*token == ',')
3504               GetMagickToken(q,&q,token);
3505             affine.sy=StringToDouble(token);
3506             GetMagickToken(q,&q,token);
3507             if (*token == ',')
3508               GetMagickToken(q,&q,token);
3509             affine.tx=StringToDouble(token);
3510             GetMagickToken(q,&q,token);
3511             if (*token == ',')
3512               GetMagickToken(q,&q,token);
3513             affine.ty=StringToDouble(token);
3514             break;
3515           }
3516         if (LocaleCompare("angle",keyword) == 0)
3517           {
3518             GetMagickToken(q,&q,token);
3519             affine.rx=StringToDouble(token);
3520             affine.ry=StringToDouble(token);
3521             break;
3522           }
3523         if (LocaleCompare("arc",keyword) == 0)
3524           {
3525             primitive_type=ArcPrimitive;
3526             break;
3527           }
3528         status=MagickFalse;
3529         break;
3530       }
3531       case 'b':
3532       case 'B':
3533       {
3534         if (LocaleCompare("bezier",keyword) == 0)
3535           {
3536             primitive_type=BezierPrimitive;
3537             break;
3538           }
3539         status=MagickFalse;
3540         break;
3541       }
3542       case 'c':
3543       case 'C':
3544       {
3545         if (LocaleCompare("clip-path",keyword) == 0)
3546           {
3547             GetMagickToken(q,&q,token);
3548             (void) FormatMagickString(message,MaxTextExtent,
3549               "clip-path:url(#%s);",token);
3550             (void) WriteBlobString(image,message);
3551             break;
3552           }
3553         if (LocaleCompare("clip-rule",keyword) == 0)
3554           {
3555             GetMagickToken(q,&q,token);
3556             (void) FormatMagickString(message,MaxTextExtent,
3557               "clip-rule:%s;",token);
3558             (void) WriteBlobString(image,message);
3559             break;
3560           }
3561         if (LocaleCompare("clip-units",keyword) == 0)
3562           {
3563             GetMagickToken(q,&q,token);
3564             (void) FormatMagickString(message,MaxTextExtent,
3565               "clipPathUnits=%s;",token);
3566             (void) WriteBlobString(image,message);
3567             break;
3568           }
3569         if (LocaleCompare("circle",keyword) == 0)
3570           {
3571             primitive_type=CirclePrimitive;
3572             break;
3573           }
3574         if (LocaleCompare("color",keyword) == 0)
3575           {
3576             primitive_type=ColorPrimitive;
3577             break;
3578           }
3579         status=MagickFalse;
3580         break;
3581       }
3582       case 'd':
3583       case 'D':
3584       {
3585         if (LocaleCompare("decorate",keyword) == 0)
3586           {
3587             GetMagickToken(q,&q,token);
3588             (void) FormatMagickString(message,MaxTextExtent,
3589               "text-decoration:%s;",token);
3590             (void) WriteBlobString(image,message);
3591             break;
3592           }
3593         status=MagickFalse;
3594         break;
3595       }
3596       case 'e':
3597       case 'E':
3598       {
3599         if (LocaleCompare("ellipse",keyword) == 0)
3600           {
3601             primitive_type=EllipsePrimitive;
3602             break;
3603           }
3604         status=MagickFalse;
3605         break;
3606       }
3607       case 'f':
3608       case 'F':
3609       {
3610         if (LocaleCompare("fill",keyword) == 0)
3611           {
3612             GetMagickToken(q,&q,token);
3613             (void) FormatMagickString(message,MaxTextExtent,"fill:%s;",
3614               token);
3615             (void) WriteBlobString(image,message);
3616             break;
3617           }
3618         if (LocaleCompare("fill-rule",keyword) == 0)
3619           {
3620             GetMagickToken(q,&q,token);
3621             (void) FormatMagickString(message,MaxTextExtent,
3622               "fill-rule:%s;",token);
3623             (void) WriteBlobString(image,message);
3624             break;
3625           }
3626         if (LocaleCompare("fill-opacity",keyword) == 0)
3627           {
3628             GetMagickToken(q,&q,token);
3629             (void) FormatMagickString(message,MaxTextExtent,
3630               "fill-opacity:%s;",token);
3631             (void) WriteBlobString(image,message);
3632             break;
3633           }
3634         if (LocaleCompare("font-family",keyword) == 0)
3635           {
3636             GetMagickToken(q,&q,token);
3637             (void) FormatMagickString(message,MaxTextExtent,
3638               "font-family:%s;",token);
3639             (void) WriteBlobString(image,message);
3640             break;
3641           }
3642         if (LocaleCompare("font-stretch",keyword) == 0)
3643           {
3644             GetMagickToken(q,&q,token);
3645             (void) FormatMagickString(message,MaxTextExtent,
3646               "font-stretch:%s;",token);
3647             (void) WriteBlobString(image,message);
3648             break;
3649           }
3650         if (LocaleCompare("font-style",keyword) == 0)
3651           {
3652             GetMagickToken(q,&q,token);
3653             (void) FormatMagickString(message,MaxTextExtent,
3654               "font-style:%s;",token);
3655             (void) WriteBlobString(image,message);
3656             break;
3657           }
3658         if (LocaleCompare("font-size",keyword) == 0)
3659           {
3660             GetMagickToken(q,&q,token);
3661             (void) FormatMagickString(message,MaxTextExtent,
3662               "font-size:%s;",token);
3663             (void) WriteBlobString(image,message);
3664             break;
3665           }
3666         if (LocaleCompare("font-weight",keyword) == 0)
3667           {
3668             GetMagickToken(q,&q,token);
3669             (void) FormatMagickString(message,MaxTextExtent,
3670               "font-weight:%s;",token);
3671             (void) WriteBlobString(image,message);
3672             break;
3673           }
3674         status=MagickFalse;
3675         break;
3676       }
3677       case 'g':
3678       case 'G':
3679       {
3680         if (LocaleCompare("gradient-units",keyword) == 0)
3681           {
3682             GetMagickToken(q,&q,token);
3683             break;
3684           }
3685         if (LocaleCompare("text-align",keyword) == 0)
3686           {
3687             GetMagickToken(q,&q,token);
3688             (void) FormatMagickString(message,MaxTextExtent,
3689               "text-align %s ",token);
3690             (void) WriteBlobString(image,message);
3691             break;
3692           }
3693         if (LocaleCompare("text-anchor",keyword) == 0)
3694           {
3695             GetMagickToken(q,&q,token);
3696             (void) FormatMagickString(message,MaxTextExtent,
3697               "text-anchor %s ",token);
3698             (void) WriteBlobString(image,message);
3699             break;
3700           }
3701         status=MagickFalse;
3702         break;
3703       }
3704       case 'i':
3705       case 'I':
3706       {
3707         if (LocaleCompare("image",keyword) == 0)
3708           {
3709             GetMagickToken(q,&q,token);
3710             primitive_type=ImagePrimitive;
3711             break;
3712           }
3713         status=MagickFalse;
3714         break;
3715       }
3716       case 'l':
3717       case 'L':
3718       {
3719         if (LocaleCompare("line",keyword) == 0)
3720           {
3721             primitive_type=LinePrimitive;
3722             break;
3723           }
3724         status=MagickFalse;
3725         break;
3726       }
3727       case 'm':
3728       case 'M':
3729       {
3730         if (LocaleCompare("matte",keyword) == 0)
3731           {
3732             primitive_type=MattePrimitive;
3733             break;
3734           }
3735         status=MagickFalse;
3736         break;
3737       }
3738       case 'o':
3739       case 'O':
3740       {
3741         if (LocaleCompare("opacity",keyword) == 0)
3742           {
3743             GetMagickToken(q,&q,token);
3744             (void) FormatMagickString(message,MaxTextExtent,"opacity %s ",
3745               token);
3746             (void) WriteBlobString(image,message);
3747             break;
3748           }
3749         status=MagickFalse;
3750         break;
3751       }
3752       case 'p':
3753       case 'P':
3754       {
3755         if (LocaleCompare("path",keyword) == 0)
3756           {
3757             primitive_type=PathPrimitive;
3758             break;
3759           }
3760         if (LocaleCompare("point",keyword) == 0)
3761           {
3762             primitive_type=PointPrimitive;
3763             break;
3764           }
3765         if (LocaleCompare("polyline",keyword) == 0)
3766           {
3767             primitive_type=PolylinePrimitive;
3768             break;
3769           }
3770         if (LocaleCompare("polygon",keyword) == 0)
3771           {
3772             primitive_type=PolygonPrimitive;
3773             break;
3774           }
3775         if (LocaleCompare("pop",keyword) == 0)
3776           {
3777             GetMagickToken(q,&q,token);
3778             if (LocaleCompare("clip-path",token) == 0)
3779               {
3780                 (void) WriteBlobString(image,"</clipPath>\n");
3781                 break;
3782               }
3783             if (LocaleCompare("defs",token) == 0)
3784               {
3785                 (void) WriteBlobString(image,"</defs>\n");
3786                 break;
3787               }
3788             if (LocaleCompare("gradient",token) == 0)
3789               {
3790                 (void) FormatMagickString(message,MaxTextExtent,
3791                   "</%sGradient>\n",type);
3792                 (void) WriteBlobString(image,message);
3793                 break;
3794               }
3795             if (LocaleCompare("graphic-context",token) == 0)
3796               {
3797                 n--;
3798                 if (n < 0)
3799                   ThrowWriterException(DrawError,
3800                     "UnbalancedGraphicContextPushPop");
3801                 (void) WriteBlobString(image,"</g>\n");
3802               }
3803             if (LocaleCompare("pattern",token) == 0)
3804               {
3805                 (void) WriteBlobString(image,"</pattern>\n");
3806                 break;
3807               }
3808             if (LocaleCompare("defs",token) == 0)
3809             (void) WriteBlobString(image,"</g>\n");
3810             break;
3811           }
3812         if (LocaleCompare("push",keyword) == 0)
3813           {
3814             GetMagickToken(q,&q,token);
3815             if (LocaleCompare("clip-path",token) == 0)
3816               {
3817                 GetMagickToken(q,&q,token);
3818                 (void) FormatMagickString(message,MaxTextExtent,
3819                   "<clipPath id=\"%s\">\n",token);
3820                 (void) WriteBlobString(image,message);
3821                 break;
3822               }
3823             if (LocaleCompare("defs",token) == 0)
3824               {
3825                 (void) WriteBlobString(image,"<defs>\n");
3826                 break;
3827               }
3828             if (LocaleCompare("gradient",token) == 0)
3829               {
3830                 GetMagickToken(q,&q,token);
3831                 (void) CopyMagickString(name,token,MaxTextExtent);
3832                 GetMagickToken(q,&q,token);
3833                 (void) CopyMagickString(type,token,MaxTextExtent);
3834                 GetMagickToken(q,&q,token);
3835                 svg_info.segment.x1=StringToDouble(token);
3836                 svg_info.element.cx=StringToDouble(token);
3837                 GetMagickToken(q,&q,token);
3838                 if (*token == ',')
3839                   GetMagickToken(q,&q,token);
3840                 svg_info.segment.y1=StringToDouble(token);
3841                 svg_info.element.cy=StringToDouble(token);
3842                 GetMagickToken(q,&q,token);
3843                 if (*token == ',')
3844                   GetMagickToken(q,&q,token);
3845                 svg_info.segment.x2=StringToDouble(token);
3846                 svg_info.element.major=StringToDouble(token);
3847                 GetMagickToken(q,&q,token);
3848                 if (*token == ',')
3849                   GetMagickToken(q,&q,token);
3850                 svg_info.segment.y2=StringToDouble(token);
3851                 svg_info.element.minor=StringToDouble(token);
3852                 (void) FormatMagickString(message,MaxTextExtent,
3853                   "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
3854                   "y2=\"%g\">\n",type,name,svg_info.segment.x1,
3855                   svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
3856                 if (LocaleCompare(type,"radial") == 0)
3857                   {
3858                     GetMagickToken(q,&q,token);
3859                     if (*token == ',')
3860                       GetMagickToken(q,&q,token);
3861                     svg_info.element.angle=StringToDouble(token);
3862                     (void) FormatMagickString(message,MaxTextExtent,
3863                       "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
3864                       "fx=\"%g\" fy=\"%g\">\n",type,name,
3865                       svg_info.element.cx,svg_info.element.cy,
3866                       svg_info.element.angle,svg_info.element.major,
3867                       svg_info.element.minor);
3868                   }
3869                 (void) WriteBlobString(image,message);
3870                 break;
3871               }
3872             if (LocaleCompare("graphic-context",token) == 0)
3873               {
3874                 n++;
3875                 if (active)
3876                   {
3877                     AffineToTransform(image,&affine);
3878                     active=MagickFalse;
3879                   }
3880                 (void) WriteBlobString(image,"<g style=\"");
3881                 active=MagickTrue;
3882               }
3883             if (LocaleCompare("pattern",token) == 0)
3884               {
3885                 GetMagickToken(q,&q,token);
3886                 (void) CopyMagickString(name,token,MaxTextExtent);
3887                 GetMagickToken(q,&q,token);
3888                 svg_info.bounds.x=StringToDouble(token);
3889                 GetMagickToken(q,&q,token);
3890                 if (*token == ',')
3891                   GetMagickToken(q,&q,token);
3892                 svg_info.bounds.y=StringToDouble(token);
3893                 GetMagickToken(q,&q,token);
3894                 if (*token == ',')
3895                   GetMagickToken(q,&q,token);
3896                 svg_info.bounds.width=StringToDouble(token);
3897                 GetMagickToken(q,&q,token);
3898                 if (*token == ',')
3899                   GetMagickToken(q,&q,token);
3900                 svg_info.bounds.height=StringToDouble(token);
3901                 (void) FormatMagickString(message,MaxTextExtent,
3902                   "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
3903                   "height=\"%g\">\n",name,svg_info.bounds.x,
3904                   svg_info.bounds.y,svg_info.bounds.width,
3905                   svg_info.bounds.height);
3906                 (void) WriteBlobString(image,message);
3907                 break;
3908               }
3909             break;
3910           }
3911         status=MagickFalse;
3912         break;
3913       }
3914       case 'r':
3915       case 'R':
3916       {
3917         if (LocaleCompare("rectangle",keyword) == 0)
3918           {
3919             primitive_type=RectanglePrimitive;
3920             break;
3921           }
3922         if (LocaleCompare("roundRectangle",keyword) == 0)
3923           {
3924             primitive_type=RoundRectanglePrimitive;
3925             break;
3926           }
3927         if (LocaleCompare("rotate",keyword) == 0)
3928           {
3929             GetMagickToken(q,&q,token);
3930             (void) FormatMagickString(message,MaxTextExtent,"rotate(%s) ",
3931               token);
3932             (void) WriteBlobString(image,message);
3933             break;
3934           }
3935         status=MagickFalse;
3936         break;
3937       }
3938       case 's':
3939       case 'S':
3940       {
3941         if (LocaleCompare("scale",keyword) == 0)
3942           {
3943             GetMagickToken(q,&q,token);
3944             affine.sx=StringToDouble(token);
3945             GetMagickToken(q,&q,token);
3946             if (*token == ',')
3947               GetMagickToken(q,&q,token);
3948             affine.sy=StringToDouble(token);
3949             break;
3950           }
3951         if (LocaleCompare("skewX",keyword) == 0)
3952           {
3953             GetMagickToken(q,&q,token);
3954             (void) FormatMagickString(message,MaxTextExtent,"skewX(%s) ",
3955               token);
3956             (void) WriteBlobString(image,message);
3957             break;
3958           }
3959         if (LocaleCompare("skewY",keyword) == 0)
3960           {
3961             GetMagickToken(q,&q,token);
3962             (void) FormatMagickString(message,MaxTextExtent,"skewY(%s) ",
3963               token);
3964             (void) WriteBlobString(image,message);
3965             break;
3966           }
3967         if (LocaleCompare("stop-color",keyword) == 0)
3968           {
3969             char
3970               color[MaxTextExtent];
3971
3972             GetMagickToken(q,&q,token);
3973             (void) CopyMagickString(color,token,MaxTextExtent);
3974             GetMagickToken(q,&q,token);
3975             (void) FormatMagickString(message,MaxTextExtent,
3976               "  <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
3977             (void) WriteBlobString(image,message);
3978             break;
3979           }
3980         if (LocaleCompare("stroke",keyword) == 0)
3981           {
3982             GetMagickToken(q,&q,token);
3983             (void) FormatMagickString(message,MaxTextExtent,"stroke:%s;",
3984               token);
3985             (void) WriteBlobString(image,message);
3986             break;
3987           }
3988         if (LocaleCompare("stroke-antialias",keyword) == 0)
3989           {
3990             GetMagickToken(q,&q,token);
3991             (void) FormatMagickString(message,MaxTextExtent,
3992               "stroke-antialias:%s;",token);
3993             (void) WriteBlobString(image,message);
3994             break;
3995           }
3996         if (LocaleCompare("stroke-dasharray",keyword) == 0)
3997           {
3998             if (IsPoint(q))
3999               {
4000                 ssize_t
4001                   k;
4002
4003                 p=q;
4004                 GetMagickToken(p,&p,token);
4005                 for (k=0; IsPoint(token); k++)
4006                   GetMagickToken(p,&p,token);
4007                 (void) WriteBlobString(image,"stroke-dasharray:");
4008                 for (j=0; j < k; j++)
4009                 {
4010                   GetMagickToken(q,&q,token);
4011                   (void) FormatMagickString(message,MaxTextExtent,"%s ",
4012                     token);
4013                   (void) WriteBlobString(image,message);
4014                 }
4015                 (void) WriteBlobString(image,";");
4016                 break;
4017               }
4018             GetMagickToken(q,&q,token);
4019             (void) FormatMagickString(message,MaxTextExtent,
4020               "stroke-dasharray:%s;",token);
4021             (void) WriteBlobString(image,message);
4022             break;
4023           }
4024         if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4025           {
4026             GetMagickToken(q,&q,token);
4027             (void) FormatMagickString(message,MaxTextExtent,
4028               "stroke-dashoffset:%s;",token);
4029             (void) WriteBlobString(image,message);
4030             break;
4031           }
4032         if (LocaleCompare("stroke-linecap",keyword) == 0)
4033           {
4034             GetMagickToken(q,&q,token);
4035             (void) FormatMagickString(message,MaxTextExtent,
4036               "stroke-linecap:%s;",token);
4037             (void) WriteBlobString(image,message);
4038             break;
4039           }
4040         if (LocaleCompare("stroke-linejoin",keyword) == 0)
4041           {
4042             GetMagickToken(q,&q,token);
4043             (void) FormatMagickString(message,MaxTextExtent,
4044               "stroke-linejoin:%s;",token);
4045             (void) WriteBlobString(image,message);
4046             break;
4047           }
4048         if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4049           {
4050             GetMagickToken(q,&q,token);
4051             (void) FormatMagickString(message,MaxTextExtent,
4052               "stroke-miterlimit:%s;",token);
4053             (void) WriteBlobString(image,message);
4054             break;
4055           }
4056         if (LocaleCompare("stroke-opacity",keyword) == 0)
4057           {
4058             GetMagickToken(q,&q,token);
4059             (void) FormatMagickString(message,MaxTextExtent,
4060               "stroke-opacity:%s;",token);
4061             (void) WriteBlobString(image,message);
4062             break;
4063           }
4064         if (LocaleCompare("stroke-width",keyword) == 0)
4065           {
4066             GetMagickToken(q,&q,token);
4067             (void) FormatMagickString(message,MaxTextExtent,
4068               "stroke-width:%s;",token);
4069             (void) WriteBlobString(image,message);
4070             continue;
4071           }
4072         status=MagickFalse;
4073         break;
4074       }
4075       case 't':
4076       case 'T':
4077       {
4078         if (LocaleCompare("text",keyword) == 0)
4079           {
4080             primitive_type=TextPrimitive;
4081             break;
4082           }
4083         if (LocaleCompare("text-antialias",keyword) == 0)
4084           {
4085             GetMagickToken(q,&q,token);
4086             (void) FormatMagickString(message,MaxTextExtent,
4087               "text-antialias:%s;",token);
4088             (void) WriteBlobString(image,message);
4089             break;
4090           }
4091         if (LocaleCompare("tspan",keyword) == 0)
4092           {
4093             primitive_type=TextPrimitive;
4094             break;
4095           }
4096         if (LocaleCompare("translate",keyword) == 0)
4097           {
4098             GetMagickToken(q,&q,token);
4099             affine.tx=StringToDouble(token);
4100             GetMagickToken(q,&q,token);
4101             if (*token == ',')
4102               GetMagickToken(q,&q,token);
4103             affine.ty=StringToDouble(token);
4104             break;
4105           }
4106         status=MagickFalse;
4107         break;
4108       }
4109       case 'v':
4110       case 'V':
4111       {
4112         if (LocaleCompare("viewbox",keyword) == 0)
4113           {
4114             GetMagickToken(q,&q,token);
4115             if (*token == ',')
4116               GetMagickToken(q,&q,token);
4117             GetMagickToken(q,&q,token);
4118             if (*token == ',')
4119               GetMagickToken(q,&q,token);
4120             GetMagickToken(q,&q,token);
4121             if (*token == ',')
4122               GetMagickToken(q,&q,token);
4123             GetMagickToken(q,&q,token);
4124             break;
4125           }
4126         status=MagickFalse;
4127         break;
4128       }
4129       default:
4130       {
4131         status=MagickFalse;
4132         break;
4133       }
4134     }
4135     if (status == MagickFalse)
4136       break;
4137     if (primitive_type == UndefinedPrimitive)
4138       continue;
4139     /*
4140       Parse the primitive attributes.
4141     */
4142     i=0;
4143     j=0;
4144     for (x=0; *q != '\0'; x++)
4145     {
4146       /*
4147         Define points.
4148       */
4149       if (IsPoint(q) == MagickFalse)
4150         break;
4151       GetMagickToken(q,&q,token);
4152       point.x=StringToDouble(token);
4153       GetMagickToken(q,&q,token);
4154       if (*token == ',')
4155         GetMagickToken(q,&q,token);
4156       point.y=StringToDouble(token);
4157       GetMagickToken(q,(const char **) NULL,token);
4158       if (*token == ',')
4159         GetMagickToken(q,&q,token);
4160       primitive_info[i].primitive=primitive_type;
4161       primitive_info[i].point=point;
4162       primitive_info[i].coordinates=0;
4163       primitive_info[i].method=FloodfillMethod;
4164       i++;
4165       if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4166         continue;
4167       number_points+=6*BezierQuantum+360;
4168       primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4169         number_points,sizeof(*primitive_info));
4170       if (primitive_info == (PrimitiveInfo *) NULL)
4171         {
4172           (void) ThrowMagickException(&image->exception,GetMagickModule(),
4173             ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4174           break;
4175         }
4176     }
4177     primitive_info[j].primitive=primitive_type;
4178     primitive_info[j].coordinates=x;
4179     primitive_info[j].method=FloodfillMethod;
4180     primitive_info[j].text=(char *) NULL;
4181     if (active)
4182       {
4183         AffineToTransform(image,&affine);
4184         active=MagickFalse;
4185       }
4186     active=MagickFalse;
4187     switch (primitive_type)
4188     {
4189       case PointPrimitive:
4190       default:
4191       {
4192         if (primitive_info[j].coordinates != 1)
4193           {
4194             status=MagickFalse;
4195             break;
4196           }
4197         break;
4198       }
4199       case LinePrimitive:
4200       {
4201         if (primitive_info[j].coordinates != 2)
4202           {
4203             status=MagickFalse;
4204             break;
4205           }
4206           (void) FormatMagickString(message,MaxTextExtent,
4207           "  <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4208           primitive_info[j].point.x,primitive_info[j].point.y,
4209           primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4210         (void) WriteBlobString(image,message);
4211         break;
4212       }
4213       case RectanglePrimitive:
4214       {
4215         if (primitive_info[j].coordinates != 2)
4216           {
4217             status=MagickFalse;
4218             break;
4219           }
4220           (void) FormatMagickString(message,MaxTextExtent,
4221           "  <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4222           primitive_info[j].point.x,primitive_info[j].point.y,
4223           primitive_info[j+1].point.x-primitive_info[j].point.x,
4224           primitive_info[j+1].point.y-primitive_info[j].point.y);
4225         (void) WriteBlobString(image,message);
4226         break;
4227       }
4228       case RoundRectanglePrimitive:
4229       {
4230         if (primitive_info[j].coordinates != 3)
4231           {
4232             status=MagickFalse;
4233             break;
4234           }
4235         (void) FormatMagickString(message,MaxTextExtent,
4236           "  <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4237           "ry=\"%g\"/>\n",primitive_info[j].point.x,
4238           primitive_info[j].point.y,primitive_info[j+1].point.x-
4239           primitive_info[j].point.x,primitive_info[j+1].point.y-
4240           primitive_info[j].point.y,primitive_info[j+2].point.x,
4241           primitive_info[j+2].point.y);
4242         (void) WriteBlobString(image,message);
4243         break;
4244       }
4245       case ArcPrimitive:
4246       {
4247         if (primitive_info[j].coordinates != 3)
4248           {
4249             status=MagickFalse;
4250             break;
4251           }
4252         break;
4253       }
4254       case EllipsePrimitive:
4255       {
4256         if (primitive_info[j].coordinates != 3)
4257           {
4258             status=MagickFalse;
4259             break;
4260           }
4261           (void) FormatMagickString(message,MaxTextExtent,
4262           "  <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
4263           primitive_info[j].point.x,primitive_info[j].point.y,
4264           primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4265         (void) WriteBlobString(image,message);
4266         break;
4267       }
4268       case CirclePrimitive:
4269       {
4270         double
4271           alpha,
4272           beta;
4273
4274         if (primitive_info[j].coordinates != 2)
4275           {
4276             status=MagickFalse;
4277             break;
4278           }
4279         alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4280         beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4281         (void) FormatMagickString(message,MaxTextExtent,
4282           "  <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
4283           primitive_info[j].point.x,primitive_info[j].point.y,
4284           hypot(alpha,beta));
4285         (void) WriteBlobString(image,message);
4286         break;
4287       }
4288       case PolylinePrimitive:
4289       {
4290         if (primitive_info[j].coordinates < 2)
4291           {
4292             status=MagickFalse;
4293             break;
4294           }
4295         (void) CopyMagickString(message,"  <polyline points=\"",MaxTextExtent);
4296         (void) WriteBlobString(image,message);
4297         length=strlen(message);
4298         for ( ; j < i; j++)
4299         {
4300           (void) FormatMagickString(message,MaxTextExtent,"%g,%g ",
4301             primitive_info[j].point.x,primitive_info[j].point.y);
4302           length+=strlen(message);
4303           if (length >= 80)
4304             {
4305               (void) WriteBlobString(image,"\n    ");
4306               length=strlen(message)+5;
4307             }
4308           (void) WriteBlobString(image,message);
4309         }
4310         (void) WriteBlobString(image,"\"/>\n");
4311         break;
4312       }
4313       case PolygonPrimitive:
4314       {
4315         if (primitive_info[j].coordinates < 3)
4316           {
4317             status=MagickFalse;
4318             break;
4319           }
4320         primitive_info[i]=primitive_info[j];
4321         primitive_info[i].coordinates=0;
4322         primitive_info[j].coordinates++;
4323         i++;
4324         (void) CopyMagickString(message,"  <polygon points=\"",MaxTextExtent);
4325         (void) WriteBlobString(image,message);
4326         length=strlen(message);
4327         for ( ; j < i; j++)
4328         {
4329           (void) FormatMagickString(message,MaxTextExtent,"%g,%g ",
4330             primitive_info[j].point.x,primitive_info[j].point.y);
4331           length+=strlen(message);
4332           if (length >= 80)
4333             {
4334               (void) WriteBlobString(image,"\n    ");
4335               length=strlen(message)+5;
4336             }
4337           (void) WriteBlobString(image,message);
4338         }
4339         (void) WriteBlobString(image,"\"/>\n");
4340         break;
4341       }
4342       case BezierPrimitive:
4343       {
4344         if (primitive_info[j].coordinates < 3)
4345           {
4346             status=MagickFalse;
4347             break;
4348           }
4349         break;
4350       }
4351       case PathPrimitive:
4352       {
4353         int
4354           number_attributes;
4355
4356         GetMagickToken(q,&q,token);
4357         number_attributes=1;
4358         for (p=token; *p != '\0'; p++)
4359           if (isalpha((int) *p))
4360             number_attributes++;
4361         if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
4362           {
4363             number_points+=6*BezierQuantum*number_attributes;
4364             primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4365               number_points,sizeof(*primitive_info));
4366             if (primitive_info == (PrimitiveInfo *) NULL)
4367               {
4368                 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4369                   ResourceLimitError,"MemoryAllocationFailed","`%s'",
4370                   image->filename);
4371                 break;
4372               }
4373           }
4374         (void) WriteBlobString(image,"  <path d=\"");
4375         (void) WriteBlobString(image,token);
4376         (void) WriteBlobString(image,"\"/>\n");
4377         break;
4378       }
4379       case ColorPrimitive:
4380       case MattePrimitive:
4381       {
4382         if (primitive_info[j].coordinates != 1)
4383           {
4384             status=MagickFalse;
4385             break;
4386           }
4387         GetMagickToken(q,&q,token);
4388         if (LocaleCompare("point",token) == 0)
4389           primitive_info[j].method=PointMethod;
4390         if (LocaleCompare("replace",token) == 0)
4391           primitive_info[j].method=ReplaceMethod;
4392         if (LocaleCompare("floodfill",token) == 0)
4393           primitive_info[j].method=FloodfillMethod;
4394         if (LocaleCompare("filltoborder",token) == 0)
4395           primitive_info[j].method=FillToBorderMethod;
4396         if (LocaleCompare("reset",token) == 0)
4397           primitive_info[j].method=ResetMethod;
4398         break;
4399       }
4400       case TextPrimitive:
4401       {
4402         register char
4403           *p;
4404
4405         if (primitive_info[j].coordinates != 1)
4406           {
4407             status=MagickFalse;
4408             break;
4409           }
4410         GetMagickToken(q,&q,token);
4411         (void) FormatMagickString(message,MaxTextExtent,
4412           "  <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
4413           primitive_info[j].point.y);
4414         (void) WriteBlobString(image,message);
4415         for (p=token; *p != '\0'; p++)
4416           switch (*p)
4417           {
4418             case '<': (void) WriteBlobString(image,"&lt;"); break;
4419             case '>': (void) WriteBlobString(image,"&gt;"); break;
4420             case '&': (void) WriteBlobString(image,"&amp;"); break;
4421             default: (void) WriteBlobByte(image,*p); break;
4422           }
4423         (void) WriteBlobString(image,"</text>\n");
4424         break;
4425       }
4426       case ImagePrimitive:
4427       {
4428         if (primitive_info[j].coordinates != 2)
4429           {
4430             status=MagickFalse;
4431             break;
4432           }
4433         GetMagickToken(q,&q,token);
4434         (void) FormatMagickString(message,MaxTextExtent,
4435           "  <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
4436           "xlink:href=\"%s\"/>\n",primitive_info[j].point.x,
4437           primitive_info[j].point.y,primitive_info[j+1].point.x,
4438           primitive_info[j+1].point.y,token);
4439         (void) WriteBlobString(image,message);
4440         break;
4441       }
4442     }
4443     if (primitive_info == (PrimitiveInfo *) NULL)
4444       break;
4445     primitive_info[i].primitive=UndefinedPrimitive;
4446     if (status == MagickFalse)
4447       break;
4448   }
4449   (void) WriteBlobString(image,"</svg>\n");
4450   /*
4451     Relinquish resources.
4452   */
4453   token=DestroyString(token);
4454   if (primitive_info != (PrimitiveInfo *) NULL)
4455     primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4456   (void) CloseBlob(image);
4457   return(status);
4458 }