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