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