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