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