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