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