]> 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 *,ExceptionInfo *);
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=StringToDouble(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           svg_info->bounds.x=0;
1096           svg_info->bounds.y=0;
1097           svg_info->bounds.width=0;
1098           svg_info->bounds.height=0;
1099           break;
1100         }
1101       if (LocaleCompare((const char *) name,"tspan") == 0)
1102         {
1103           if (*svg_info->text != '\0')
1104             {
1105               DrawInfo
1106                 *draw_info;
1107
1108               TypeMetric
1109                 metrics;
1110
1111               char
1112                 *text;
1113
1114               text=EscapeString(svg_info->text,'\'');
1115               (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
1116                 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1117                 svg_info->center.y,text);
1118               text=DestroyString(text);
1119               draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
1120               draw_info->pointsize=svg_info->pointsize;
1121               draw_info->text=AcquireString(svg_info->text);
1122               (void) ConcatenateString(&draw_info->text," ");
1123               (void) GetTypeMetrics(svg_info->image,draw_info,
1124                 &metrics,svg_info->exception);
1125               svg_info->bounds.x+=metrics.width;
1126               draw_info=DestroyDrawInfo(draw_info);
1127               *svg_info->text='\0';
1128             }
1129           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1130           break;
1131         }
1132       break;
1133     }
1134     default:
1135       break;
1136   }
1137   if (attributes != (const xmlChar **) NULL)
1138     for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1139     {
1140       keyword=(const char *) attributes[i];
1141       value=(const char *) attributes[i+1];
1142       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1143         "    %s = %s",keyword,value);
1144       switch (*keyword)
1145       {
1146         case 'A':
1147         case 'a':
1148         {
1149           if (LocaleCompare(keyword,"angle") == 0)
1150             {
1151               (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1152                 GetUserSpaceCoordinateValue(svg_info,0,value));
1153               break;
1154             }
1155           break;
1156         }
1157         case 'C':
1158         case 'c':
1159         {
1160           if (LocaleCompare(keyword,"clip-path") == 0)
1161             {
1162               (void) FormatLocaleFile(svg_info->file,"clip-path '%s'\n",value);
1163               break;
1164             }
1165           if (LocaleCompare(keyword,"clip-rule") == 0)
1166             {
1167               (void) FormatLocaleFile(svg_info->file,"clip-rule '%s'\n",value);
1168               break;
1169             }
1170           if (LocaleCompare(keyword,"clipPathUnits") == 0)
1171             {
1172               (void) CloneString(&units,value);
1173               (void) FormatLocaleFile(svg_info->file,"clip-units '%s'\n",value);
1174               break;
1175             }
1176           if (LocaleCompare(keyword,"color") == 0)
1177             {
1178               (void) CloneString(&color,value);
1179               break;
1180             }
1181           if (LocaleCompare(keyword,"cx") == 0)
1182             {
1183               svg_info->element.cx=
1184                 GetUserSpaceCoordinateValue(svg_info,1,value);
1185               break;
1186             }
1187           if (LocaleCompare(keyword,"cy") == 0)
1188             {
1189               svg_info->element.cy=
1190                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1191               break;
1192             }
1193           break;
1194         }
1195         case 'D':
1196         case 'd':
1197         {
1198           if (LocaleCompare(keyword,"d") == 0)
1199             {
1200               (void) CloneString(&svg_info->vertices,value);
1201               break;
1202             }
1203           if (LocaleCompare(keyword,"dx") == 0)
1204             {
1205               svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
1206               break;
1207             }
1208           if (LocaleCompare(keyword,"dy") == 0)
1209             {
1210               svg_info->bounds.y+=
1211                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1212               break;
1213             }
1214           break;
1215         }
1216         case 'F':
1217         case 'f':
1218         {
1219           if (LocaleCompare(keyword,"fill") == 0)
1220             {
1221               if (LocaleCompare(value,"currentColor") == 0)
1222                 {
1223                   (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",color);
1224                   break;
1225                 }
1226               (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
1227               break;
1228             }
1229           if (LocaleCompare(keyword,"fillcolor") == 0)
1230             {
1231               (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
1232               break;
1233             }
1234           if (LocaleCompare(keyword,"fill-rule") == 0)
1235             {
1236               (void) FormatLocaleFile(svg_info->file,"fill-rule '%s'\n",value);
1237               break;
1238             }
1239           if (LocaleCompare(keyword,"fill-opacity") == 0)
1240             {
1241               (void) FormatLocaleFile(svg_info->file,"fill-opacity '%s'\n",
1242                 value);
1243               break;
1244             }
1245           if (LocaleCompare(keyword,"font-family") == 0)
1246             {
1247               (void) FormatLocaleFile(svg_info->file,"font-family '%s'\n",
1248                 value);
1249               break;
1250             }
1251           if (LocaleCompare(keyword,"font-stretch") == 0)
1252             {
1253               (void) FormatLocaleFile(svg_info->file,"font-stretch '%s'\n",
1254                 value);
1255               break;
1256             }
1257           if (LocaleCompare(keyword,"font-style") == 0)
1258             {
1259               (void) FormatLocaleFile(svg_info->file,"font-style '%s'\n",value);
1260               break;
1261             }
1262           if (LocaleCompare(keyword,"font-size") == 0)
1263             {
1264               svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
1265               (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1266                 svg_info->pointsize);
1267               break;
1268             }
1269           if (LocaleCompare(keyword,"font-weight") == 0)
1270             {
1271               (void) FormatLocaleFile(svg_info->file,"font-weight '%s'\n",
1272                 value);
1273               break;
1274             }
1275           break;
1276         }
1277         case 'G':
1278         case 'g':
1279         {
1280           if (LocaleCompare(keyword,"gradientTransform") == 0)
1281             {
1282               AffineMatrix
1283                 affine,
1284                 current,
1285                 transform;
1286
1287               GetAffineMatrix(&transform);
1288               (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
1289               tokens=GetTransformTokens(context,value,&number_tokens);
1290               for (j=0; j < (number_tokens-1); j+=2)
1291               {
1292                 keyword=(char *) tokens[j];
1293                 if (keyword == (char *) NULL)
1294                   continue;
1295                 value=(char *) tokens[j+1];
1296                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1297                   "    %s: %s",keyword,value);
1298                 current=transform;
1299                 GetAffineMatrix(&affine);
1300                 switch (*keyword)
1301                 {
1302                   case 'M':
1303                   case 'm':
1304                   {
1305                     if (LocaleCompare(keyword,"matrix") == 0)
1306                       {
1307                         p=(const char *) value;
1308                         GetMagickToken(p,&p,token);
1309                         affine.sx=StringToDouble(value,(char **) NULL);
1310                         GetMagickToken(p,&p,token);
1311                         if (*token == ',')
1312                           GetMagickToken(p,&p,token);
1313                         affine.rx=StringToDouble(token,(char **) NULL);
1314                         GetMagickToken(p,&p,token);
1315                         if (*token == ',')
1316                           GetMagickToken(p,&p,token);
1317                         affine.ry=StringToDouble(token,(char **) NULL);
1318                         GetMagickToken(p,&p,token);
1319                         if (*token == ',')
1320                           GetMagickToken(p,&p,token);
1321                         affine.sy=StringToDouble(token,(char **) NULL);
1322                         GetMagickToken(p,&p,token);
1323                         if (*token == ',')
1324                           GetMagickToken(p,&p,token);
1325                         affine.tx=StringToDouble(token,(char **) NULL);
1326                         GetMagickToken(p,&p,token);
1327                         if (*token == ',')
1328                           GetMagickToken(p,&p,token);
1329                         affine.ty=StringToDouble(token,(char **) NULL);
1330                         break;
1331                       }
1332                     break;
1333                   }
1334                   case 'R':
1335                   case 'r':
1336                   {
1337                     if (LocaleCompare(keyword,"rotate") == 0)
1338                       {
1339                         double
1340                           angle;
1341
1342                         angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1343                         affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1344                         affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1345                         affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1346                         affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1347                         break;
1348                       }
1349                     break;
1350                   }
1351                   case 'S':
1352                   case 's':
1353                   {
1354                     if (LocaleCompare(keyword,"scale") == 0)
1355                       {
1356                         for (p=(const char *) value; *p != '\0'; p++)
1357                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
1358                               (*p == ','))
1359                             break;
1360                         affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1361                         affine.sy=affine.sx;
1362                         if (*p != '\0')
1363                           affine.sy=
1364                             GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1365                         svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1366                         break;
1367                       }
1368                     if (LocaleCompare(keyword,"skewX") == 0)
1369                       {
1370                         affine.sx=svg_info->affine.sx;
1371                         affine.ry=tan(DegreesToRadians(fmod(
1372                           GetUserSpaceCoordinateValue(svg_info,1,value),
1373                           360.0)));
1374                         affine.sy=svg_info->affine.sy;
1375                         break;
1376                       }
1377                     if (LocaleCompare(keyword,"skewY") == 0)
1378                       {
1379                         affine.sx=svg_info->affine.sx;
1380                         affine.rx=tan(DegreesToRadians(fmod(
1381                           GetUserSpaceCoordinateValue(svg_info,-1,value),
1382                           360.0)));
1383                         affine.sy=svg_info->affine.sy;
1384                         break;
1385                       }
1386                     break;
1387                   }
1388                   case 'T':
1389                   case 't':
1390                   {
1391                     if (LocaleCompare(keyword,"translate") == 0)
1392                       {
1393                         for (p=(const char *) value; *p != '\0'; p++)
1394                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
1395                               (*p == ','))
1396                             break;
1397                         affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1398                         affine.ty=affine.tx;
1399                         if (*p != '\0')
1400                           affine.ty=
1401                             GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1402                         break;
1403                       }
1404                     break;
1405                   }
1406                   default:
1407                     break;
1408                 }
1409                 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1410                 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1411                 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1412                 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1413                 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1414                   current.tx;
1415                 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1416                   current.ty;
1417               }
1418               (void) FormatLocaleFile(svg_info->file,
1419                 "affine %g %g %g %g %g %g\n",transform.sx,
1420                 transform.rx,transform.ry,transform.sy,transform.tx,
1421                 transform.ty);
1422               for (j=0; tokens[j] != (char *) NULL; j++)
1423                 tokens[j]=DestroyString(tokens[j]);
1424               tokens=(char **) RelinquishMagickMemory(tokens);
1425               break;
1426             }
1427           if (LocaleCompare(keyword,"gradientUnits") == 0)
1428             {
1429               (void) CloneString(&units,value);
1430               (void) FormatLocaleFile(svg_info->file,"gradient-units '%s'\n",
1431                 value);
1432               break;
1433             }
1434           break;
1435         }
1436         case 'H':
1437         case 'h':
1438         {
1439           if (LocaleCompare(keyword,"height") == 0)
1440             {
1441               svg_info->bounds.height=
1442                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1443               break;
1444             }
1445           if (LocaleCompare(keyword,"href") == 0)
1446             {
1447               (void) CloneString(&svg_info->url,value);
1448               break;
1449             }
1450           break;
1451         }
1452         case 'M':
1453         case 'm':
1454         {
1455           if (LocaleCompare(keyword,"major") == 0)
1456             {
1457               svg_info->element.major=
1458                 GetUserSpaceCoordinateValue(svg_info,1,value);
1459               break;
1460             }
1461           if (LocaleCompare(keyword,"minor") == 0)
1462             {
1463               svg_info->element.minor=
1464                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1465               break;
1466             }
1467           break;
1468         }
1469         case 'O':
1470         case 'o':
1471         {
1472           if (LocaleCompare(keyword,"offset") == 0)
1473             {
1474               (void) CloneString(&svg_info->offset,value);
1475               break;
1476             }
1477           if (LocaleCompare(keyword,"opacity") == 0)
1478             {
1479               (void) FormatLocaleFile(svg_info->file,"opacity '%s'\n",value);
1480               break;
1481             }
1482           break;
1483         }
1484         case 'P':
1485         case 'p':
1486         {
1487           if (LocaleCompare(keyword,"path") == 0)
1488             {
1489               (void) CloneString(&svg_info->url,value);
1490               break;
1491             }
1492           if (LocaleCompare(keyword,"points") == 0)
1493             {
1494               (void) CloneString(&svg_info->vertices,value);
1495               break;
1496             }
1497           break;
1498         }
1499         case 'R':
1500         case 'r':
1501         {
1502           if (LocaleCompare(keyword,"r") == 0)
1503             {
1504               svg_info->element.major=
1505                 GetUserSpaceCoordinateValue(svg_info,1,value);
1506               svg_info->element.minor=
1507                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1508               break;
1509             }
1510           if (LocaleCompare(keyword,"rotate") == 0)
1511             {
1512               double
1513                 angle;
1514
1515               angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1516               (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1517                 svg_info->bounds.x,svg_info->bounds.y);
1518               svg_info->bounds.x=0;
1519               svg_info->bounds.y=0;
1520               (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
1521               break;
1522             }
1523           if (LocaleCompare(keyword,"rx") == 0)
1524             {
1525               if (LocaleCompare((const char *) name,"ellipse") == 0)
1526                 svg_info->element.major=
1527                   GetUserSpaceCoordinateValue(svg_info,1,value);
1528               else
1529                 svg_info->radius.x=
1530                   GetUserSpaceCoordinateValue(svg_info,1,value);
1531               break;
1532             }
1533           if (LocaleCompare(keyword,"ry") == 0)
1534             {
1535               if (LocaleCompare((const char *) name,"ellipse") == 0)
1536                 svg_info->element.minor=
1537                   GetUserSpaceCoordinateValue(svg_info,-1,value);
1538               else
1539                 svg_info->radius.y=
1540                   GetUserSpaceCoordinateValue(svg_info,-1,value);
1541               break;
1542             }
1543           break;
1544         }
1545         case 'S':
1546         case 's':
1547         {
1548           if (LocaleCompare(keyword,"stop-color") == 0)
1549             {
1550               (void) CloneString(&svg_info->stop_color,value);
1551               break;
1552             }
1553           if (LocaleCompare(keyword,"stroke") == 0)
1554             {
1555               if (LocaleCompare(value,"currentColor") == 0)
1556                 {
1557                   (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",color);
1558                   break;
1559                 }
1560               (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",value);
1561               break;
1562             }
1563           if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1564             {
1565               (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
1566                 LocaleCompare(value,"true") == 0);
1567               break;
1568             }
1569           if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1570             {
1571               (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1572                 value);
1573               break;
1574             }
1575           if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1576             {
1577               (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %s\n",
1578                 value);
1579               break;
1580             }
1581           if (LocaleCompare(keyword,"stroke-linecap") == 0)
1582             {
1583               (void) FormatLocaleFile(svg_info->file,"stroke-linecap '%s'\n",
1584                 value);
1585               break;
1586             }
1587           if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1588             {
1589               (void) FormatLocaleFile(svg_info->file,"stroke-linejoin '%s'\n",
1590                 value);
1591               break;
1592             }
1593           if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1594             {
1595               (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit '%s'\n",
1596                 value);
1597               break;
1598             }
1599           if (LocaleCompare(keyword,"stroke-opacity") == 0)
1600             {
1601               (void) FormatLocaleFile(svg_info->file,"stroke-opacity '%s'\n",
1602                 value);
1603               break;
1604             }
1605           if (LocaleCompare(keyword,"stroke-width") == 0)
1606             {
1607               (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
1608                 GetUserSpaceCoordinateValue(svg_info,1,value));
1609               break;
1610             }
1611           if (LocaleCompare(keyword,"style") == 0)
1612             {
1613               (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
1614               tokens=GetStyleTokens(context,value,&number_tokens);
1615               for (j=0; j < (number_tokens-1); j+=2)
1616               {
1617                 keyword=(char *) tokens[j];
1618                 value=(char *) tokens[j+1];
1619                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1620                   "    %s: %s",keyword,value);
1621                 switch (*keyword)
1622                 {
1623                   case 'C':
1624                   case 'c':
1625                   {
1626                      if (LocaleCompare(keyword,"clip-path") == 0)
1627                        {
1628                          (void) FormatLocaleFile(svg_info->file,
1629                            "clip-path '%s'\n",value);
1630                          break;
1631                        }
1632                     if (LocaleCompare(keyword,"clip-rule") == 0)
1633                       {
1634                         (void) FormatLocaleFile(svg_info->file,
1635                           "clip-rule '%s'\n",value);
1636                         break;
1637                       }
1638                      if (LocaleCompare(keyword,"clipPathUnits") == 0)
1639                        {
1640                          (void) CloneString(&units,value);
1641                          (void) FormatLocaleFile(svg_info->file,
1642                           "clip-units '%s'\n",value);
1643                          break;
1644                        }
1645                     if (LocaleCompare(keyword,"color") == 0)
1646                       {
1647                         (void) CloneString(&color,value);
1648                         break;
1649                       }
1650                     break;
1651                   }
1652                   case 'F':
1653                   case 'f':
1654                   {
1655                     if (LocaleCompare(keyword,"fill") == 0)
1656                       {
1657                          if (LocaleCompare(value,"currentColor") == 0)
1658                            {
1659                              (void) FormatLocaleFile(svg_info->file,
1660                           "fill '%s'\n",color);
1661                              break;
1662                            }
1663                         if (LocaleCompare(value,"#00000000") == 0)
1664                           (void) FormatLocaleFile(svg_info->file,
1665                           "fill '#000000'\n");
1666                         else
1667                           (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1668                           value);
1669                         break;
1670                       }
1671                     if (LocaleCompare(keyword,"fillcolor") == 0)
1672                       {
1673                         (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1674                           value);
1675                         break;
1676                       }
1677                     if (LocaleCompare(keyword,"fill-rule") == 0)
1678                       {
1679                         (void) FormatLocaleFile(svg_info->file,
1680                           "fill-rule '%s'\n",value);
1681                         break;
1682                       }
1683                     if (LocaleCompare(keyword,"fill-opacity") == 0)
1684                       {
1685                         (void) FormatLocaleFile(svg_info->file,
1686                           "fill-opacity '%s'\n",value);
1687                         break;
1688                       }
1689                     if (LocaleCompare(keyword,"font-family") == 0)
1690                       {
1691                         (void) FormatLocaleFile(svg_info->file,
1692                           "font-family '%s'\n",value);
1693                         break;
1694                       }
1695                     if (LocaleCompare(keyword,"font-stretch") == 0)
1696                       {
1697                         (void) FormatLocaleFile(svg_info->file,
1698                           "font-stretch '%s'\n",value);
1699                         break;
1700                       }
1701                     if (LocaleCompare(keyword,"font-style") == 0)
1702                       {
1703                         (void) FormatLocaleFile(svg_info->file,
1704                           "font-style '%s'\n",value);
1705                         break;
1706                       }
1707                     if (LocaleCompare(keyword,"font-size") == 0)
1708                       {
1709                         svg_info->pointsize=GetUserSpaceCoordinateValue(
1710                           svg_info,0,value);
1711                         (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1712                           svg_info->pointsize);
1713                         break;
1714                       }
1715                     if (LocaleCompare(keyword,"font-weight") == 0)
1716                       {
1717                         (void) FormatLocaleFile(svg_info->file,
1718                           "font-weight '%s'\n",value);
1719                         break;
1720                       }
1721                     break;
1722                   }
1723                   case 'O':
1724                   case 'o':
1725                   {
1726                     if (LocaleCompare(keyword,"offset") == 0)
1727                       {
1728                         (void) FormatLocaleFile(svg_info->file,"offset %g\n",
1729                           GetUserSpaceCoordinateValue(svg_info,1,value));
1730                         break;
1731                       }
1732                     if (LocaleCompare(keyword,"opacity") == 0)
1733                       {
1734                         (void) FormatLocaleFile(svg_info->file,
1735                           "opacity '%s'\n",value);
1736                         break;
1737                       }
1738                     break;
1739                   }
1740                   case 'S':
1741                   case 's':
1742                   {
1743                     if (LocaleCompare(keyword,"stop-color") == 0)
1744                       {
1745                         (void) CloneString(&svg_info->stop_color,value);
1746                         break;
1747                       }
1748                     if (LocaleCompare(keyword,"stroke") == 0)
1749                       {
1750                          if (LocaleCompare(value,"currentColor") == 0)
1751                            {
1752                              (void) FormatLocaleFile(svg_info->file,
1753                           "stroke '%s'\n",color);
1754                              break;
1755                            }
1756                         if (LocaleCompare(value,"#00000000") == 0)
1757                           (void) FormatLocaleFile(svg_info->file,
1758                           "fill '#000000'\n");
1759                         else
1760                           (void) FormatLocaleFile(svg_info->file,
1761                           "stroke '%s'\n",value);
1762                         break;
1763                       }
1764                     if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1765                       {
1766                         (void) FormatLocaleFile(svg_info->file,
1767                           "stroke-antialias %d\n",
1768                           LocaleCompare(value,"true") == 0);
1769                         break;
1770                       }
1771                     if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1772                       {
1773                         (void) FormatLocaleFile(svg_info->file,
1774                           "stroke-dasharray %s\n",value);
1775                         break;
1776                       }
1777                     if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1778                       {
1779                         (void) FormatLocaleFile(svg_info->file,
1780                           "stroke-dashoffset %s\n",
1781                           value);
1782                         break;
1783                       }
1784                     if (LocaleCompare(keyword,"stroke-linecap") == 0)
1785                       {
1786                         (void) FormatLocaleFile(svg_info->file,
1787                           "stroke-linecap '%s'\n",value);
1788                         break;
1789                       }
1790                     if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1791                       {
1792                         (void) FormatLocaleFile(svg_info->file,
1793                           "stroke-linejoin '%s'\n",
1794                           value);
1795                         break;
1796                       }
1797                     if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1798                       {
1799                         (void) FormatLocaleFile(svg_info->file,
1800                           "stroke-miterlimit '%s'\n",
1801                           value);
1802                         break;
1803                       }
1804                     if (LocaleCompare(keyword,"stroke-opacity") == 0)
1805                       {
1806                         (void) FormatLocaleFile(svg_info->file,
1807                           "stroke-opacity '%s'\n",value);
1808                         break;
1809                       }
1810                     if (LocaleCompare(keyword,"stroke-width") == 0)
1811                       {
1812                         (void) FormatLocaleFile(svg_info->file,
1813                           "stroke-width %g\n",
1814                           GetUserSpaceCoordinateValue(svg_info,1,value));
1815                         break;
1816                       }
1817                     break;
1818                   }
1819                   case 't':
1820                   case 'T':
1821                   {
1822                     if (LocaleCompare(keyword,"text-align") == 0)
1823                       {
1824                         (void) FormatLocaleFile(svg_info->file,
1825                           "text-align '%s'\n",value);
1826                         break;
1827                       }
1828                     if (LocaleCompare(keyword,"text-anchor") == 0)
1829                       {
1830                         (void) FormatLocaleFile(svg_info->file,
1831                           "text-anchor '%s'\n",value);
1832                         break;
1833                       }
1834                     if (LocaleCompare(keyword,"text-decoration") == 0)
1835                       {
1836                         if (LocaleCompare(value,"underline") == 0)
1837                           (void) FormatLocaleFile(svg_info->file,
1838                           "decorate underline\n");
1839                         if (LocaleCompare(value,"line-through") == 0)
1840                           (void) FormatLocaleFile(svg_info->file,
1841                           "decorate line-through\n");
1842                         if (LocaleCompare(value,"overline") == 0)
1843                           (void) FormatLocaleFile(svg_info->file,
1844                           "decorate overline\n");
1845                         break;
1846                       }
1847                     if (LocaleCompare(keyword,"text-antialiasing") == 0)
1848                       {
1849                         (void) FormatLocaleFile(svg_info->file,
1850                           "text-antialias %d\n",
1851                           LocaleCompare(value,"true") == 0);
1852                         break;
1853                       }
1854                     break;
1855                   }
1856                   default:
1857                     break;
1858                 }
1859               }
1860               for (j=0; tokens[j] != (char *) NULL; j++)
1861                 tokens[j]=DestroyString(tokens[j]);
1862               tokens=(char **) RelinquishMagickMemory(tokens);
1863               break;
1864             }
1865           break;
1866         }
1867         case 'T':
1868         case 't':
1869         {
1870           if (LocaleCompare(keyword,"text-align") == 0)
1871             {
1872               (void) FormatLocaleFile(svg_info->file,"text-align '%s'\n",
1873                 value);
1874               break;
1875             }
1876           if (LocaleCompare(keyword,"text-anchor") == 0)
1877             {
1878               (void) FormatLocaleFile(svg_info->file,"text-anchor '%s'\n",
1879                 value);
1880               break;
1881             }
1882           if (LocaleCompare(keyword,"text-decoration") == 0)
1883             {
1884               if (LocaleCompare(value,"underline") == 0)
1885                 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
1886               if (LocaleCompare(value,"line-through") == 0)
1887                 (void) FormatLocaleFile(svg_info->file,
1888                   "decorate line-through\n");
1889               if (LocaleCompare(value,"overline") == 0)
1890                 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
1891               break;
1892             }
1893           if (LocaleCompare(keyword,"text-antialiasing") == 0)
1894             {
1895               (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
1896                 LocaleCompare(value,"true") == 0);
1897               break;
1898             }
1899           if (LocaleCompare(keyword,"transform") == 0)
1900             {
1901               AffineMatrix
1902                 affine,
1903                 current,
1904                 transform;
1905
1906               GetAffineMatrix(&transform);
1907               (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
1908               tokens=GetTransformTokens(context,value,&number_tokens);
1909               for (j=0; j < (number_tokens-1); j+=2)
1910               {
1911                 keyword=(char *) tokens[j];
1912                 value=(char *) tokens[j+1];
1913                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1914                   "    %s: %s",keyword,value);
1915                 current=transform;
1916                 GetAffineMatrix(&affine);
1917                 switch (*keyword)
1918                 {
1919                   case 'M':
1920                   case 'm':
1921                   {
1922                     if (LocaleCompare(keyword,"matrix") == 0)
1923                       {
1924                         p=(const char *) value;
1925                         GetMagickToken(p,&p,token);
1926                         affine.sx=StringToDouble(value,(char **) NULL);
1927                         GetMagickToken(p,&p,token);
1928                         if (*token == ',')
1929                           GetMagickToken(p,&p,token);
1930                         affine.rx=StringToDouble(token,(char **) NULL);
1931                         GetMagickToken(p,&p,token);
1932                         if (*token == ',')
1933                           GetMagickToken(p,&p,token);
1934                         affine.ry=StringToDouble(token,(char **) NULL);
1935                         GetMagickToken(p,&p,token);
1936                         if (*token == ',')
1937                           GetMagickToken(p,&p,token);
1938                         affine.sy=StringToDouble(token,(char **) NULL);
1939                         GetMagickToken(p,&p,token);
1940                         if (*token == ',')
1941                           GetMagickToken(p,&p,token);
1942                         affine.tx=StringToDouble(token,(char **) NULL);
1943                         GetMagickToken(p,&p,token);
1944                         if (*token == ',')
1945                           GetMagickToken(p,&p,token);
1946                         affine.ty=StringToDouble(token,(char **) NULL);
1947                         break;
1948                       }
1949                     break;
1950                   }
1951                   case 'R':
1952                   case 'r':
1953                   {
1954                     if (LocaleCompare(keyword,"rotate") == 0)
1955                       {
1956                         double
1957                           angle,
1958                           x,
1959                           y;
1960
1961                         p=(const char *) value;
1962                         GetMagickToken(p,&p,token);
1963                         angle=StringToDouble(value,(char **) NULL);
1964                         GetMagickToken(p,&p,token);
1965                         if (*token == ',')
1966                           GetMagickToken(p,&p,token);
1967                         x=StringToDouble(token,(char **) NULL);
1968                         GetMagickToken(p,&p,token);
1969                         if (*token == ',')
1970                           GetMagickToken(p,&p,token);
1971                         y=StringToDouble(token,(char **) NULL);
1972                         affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1973                         affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1974                         affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1975                         affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1976                         affine.tx=x;
1977                         affine.ty=y;
1978                         svg_info->center.x=x;
1979                         svg_info->center.y=y;
1980                         break;
1981                       }
1982                     break;
1983                   }
1984                   case 'S':
1985                   case 's':
1986                   {
1987                     if (LocaleCompare(keyword,"scale") == 0)
1988                       {
1989                         for (p=(const char *) value; *p != '\0'; p++)
1990                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
1991                               (*p == ','))
1992                             break;
1993                         affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1994                         affine.sy=affine.sx;
1995                         if (*p != '\0')
1996                           affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
1997                             p+1);
1998                         svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1999                         break;
2000                       }
2001                     if (LocaleCompare(keyword,"skewX") == 0)
2002                       {
2003                         affine.sx=svg_info->affine.sx;
2004                         affine.ry=tan(DegreesToRadians(fmod(
2005                           GetUserSpaceCoordinateValue(svg_info,1,value),
2006                           360.0)));
2007                         affine.sy=svg_info->affine.sy;
2008                         break;
2009                       }
2010                     if (LocaleCompare(keyword,"skewY") == 0)
2011                       {
2012                         affine.sx=svg_info->affine.sx;
2013                         affine.rx=tan(DegreesToRadians(fmod(
2014                           GetUserSpaceCoordinateValue(svg_info,-1,value),
2015                           360.0)));
2016                         affine.sy=svg_info->affine.sy;
2017                         break;
2018                       }
2019                     break;
2020                   }
2021                   case 'T':
2022                   case 't':
2023                   {
2024                     if (LocaleCompare(keyword,"translate") == 0)
2025                       {
2026                         for (p=(const char *) value; *p != '\0'; p++)
2027                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
2028                               (*p == ','))
2029                             break;
2030                         affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2031                         affine.ty=affine.tx;
2032                         if (*p != '\0')
2033                           affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2034                             p+1);
2035                         break;
2036                       }
2037                     break;
2038                   }
2039                   default:
2040                     break;
2041                 }
2042                 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2043                 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2044                 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2045                 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2046                 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2047                   current.tx;
2048                 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2049                   current.ty;
2050               }
2051               (void) FormatLocaleFile(svg_info->file,
2052                 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2053                 transform.ry,transform.sy,transform.tx,transform.ty);
2054               for (j=0; tokens[j] != (char *) NULL; j++)
2055                 tokens[j]=DestroyString(tokens[j]);
2056               tokens=(char **) RelinquishMagickMemory(tokens);
2057               break;
2058             }
2059           break;
2060         }
2061         case 'V':
2062         case 'v':
2063         {
2064           if (LocaleCompare(keyword,"verts") == 0)
2065             {
2066               (void) CloneString(&svg_info->vertices,value);
2067               break;
2068             }
2069           if (LocaleCompare(keyword,"viewBox") == 0)
2070             {
2071               p=(const char *) value;
2072               GetMagickToken(p,&p,token);
2073               svg_info->view_box.x=StringToDouble(token,(char **) NULL);
2074               GetMagickToken(p,&p,token);
2075               if (*token == ',')
2076                 GetMagickToken(p,&p,token);
2077               svg_info->view_box.y=StringToDouble(token,(char **) NULL);
2078               GetMagickToken(p,&p,token);
2079               if (*token == ',')
2080                 GetMagickToken(p,&p,token);
2081               svg_info->view_box.width=StringToDouble(token,
2082                 (char **) NULL);
2083               if (svg_info->bounds.width == 0)
2084                 svg_info->bounds.width=svg_info->view_box.width;
2085               GetMagickToken(p,&p,token);
2086               if (*token == ',')
2087                 GetMagickToken(p,&p,token);
2088               svg_info->view_box.height=StringToDouble(token,
2089                 (char **) NULL);
2090               if (svg_info->bounds.height == 0)
2091                 svg_info->bounds.height=svg_info->view_box.height;
2092               break;
2093             }
2094           break;
2095         }
2096         case 'W':
2097         case 'w':
2098         {
2099           if (LocaleCompare(keyword,"width") == 0)
2100             {
2101               svg_info->bounds.width=
2102                 GetUserSpaceCoordinateValue(svg_info,1,value);
2103               break;
2104             }
2105           break;
2106         }
2107         case 'X':
2108         case 'x':
2109         {
2110           if (LocaleCompare(keyword,"x") == 0)
2111             {
2112               svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2113               break;
2114             }
2115           if (LocaleCompare(keyword,"xlink:href") == 0)
2116             {
2117               (void) CloneString(&svg_info->url,value);
2118               break;
2119             }
2120           if (LocaleCompare(keyword,"x1") == 0)
2121             {
2122               svg_info->segment.x1=
2123                 GetUserSpaceCoordinateValue(svg_info,1,value);
2124               break;
2125             }
2126           if (LocaleCompare(keyword,"x2") == 0)
2127             {
2128               svg_info->segment.x2=
2129                 GetUserSpaceCoordinateValue(svg_info,1,value);
2130               break;
2131             }
2132           break;
2133         }
2134         case 'Y':
2135         case 'y':
2136         {
2137           if (LocaleCompare(keyword,"y") == 0)
2138             {
2139               svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2140               break;
2141             }
2142           if (LocaleCompare(keyword,"y1") == 0)
2143             {
2144               svg_info->segment.y1=
2145                 GetUserSpaceCoordinateValue(svg_info,-1,value);
2146               break;
2147             }
2148           if (LocaleCompare(keyword,"y2") == 0)
2149             {
2150               svg_info->segment.y2=
2151                 GetUserSpaceCoordinateValue(svg_info,-1,value);
2152               break;
2153             }
2154           break;
2155         }
2156         default:
2157           break;
2158       }
2159     }
2160   if (LocaleCompare((const char *) name,"svg") == 0)
2161     {
2162       if (svg_info->document->encoding != (const xmlChar *) NULL)
2163         (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2164           (const char *) svg_info->document->encoding);
2165       if (attributes != (const xmlChar **) NULL)
2166         {
2167           double
2168             sx,
2169             sy;
2170
2171           if ((svg_info->view_box.width == 0.0) ||
2172               (svg_info->view_box.height == 0.0))
2173             svg_info->view_box=svg_info->bounds;
2174           svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2175           svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2176           (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2177             (double) svg_info->width,(double) svg_info->height);
2178           sx=(double) svg_info->width/svg_info->view_box.width;
2179           sy=(double) svg_info->height/svg_info->view_box.height;
2180           (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g 0.0 0.0\n",
2181             sx,sy);
2182         }
2183     }
2184   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  )");
2185   units=DestroyString(units);
2186   if (color != (char *) NULL)
2187     color=DestroyString(color);
2188 }
2189
2190 static void SVGEndElement(void *context,const xmlChar *name)
2191 {
2192   SVGInfo
2193     *svg_info;
2194
2195   /*
2196     Called when the end of an element has been detected.
2197   */
2198   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2199     "  SAX.endElement(%s)",name);
2200   svg_info=(SVGInfo *) context;
2201   if (strchr((char *) name,':') != (char *) NULL)
2202     {
2203       /*
2204         Skip over namespace.
2205       */
2206       for ( ; *name != ':'; name++) ;
2207       name++;
2208     }
2209   switch (*name)
2210   {
2211     case 'C':
2212     case 'c':
2213     {
2214       if (LocaleCompare((const char *) name,"circle") == 0)
2215         {
2216           (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2217             svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2218             svg_info->element.cy+svg_info->element.minor);
2219           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2220           break;
2221         }
2222       if (LocaleCompare((const char *) name,"clipPath") == 0)
2223         {
2224           (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2225           break;
2226         }
2227       break;
2228     }
2229     case 'D':
2230     case 'd':
2231     {
2232       if (LocaleCompare((const char *) name,"defs") == 0)
2233         {
2234           (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2235           break;
2236         }
2237       if (LocaleCompare((const char *) name,"desc") == 0)
2238         {
2239           register char
2240             *p;
2241
2242           if (*svg_info->text == '\0')
2243             break;
2244           (void) fputc('#',svg_info->file);
2245           for (p=svg_info->text; *p != '\0'; p++)
2246           {
2247             (void) fputc(*p,svg_info->file);
2248             if (*p == '\n')
2249               (void) fputc('#',svg_info->file);
2250           }
2251           (void) fputc('\n',svg_info->file);
2252           *svg_info->text='\0';
2253           break;
2254         }
2255       break;
2256     }
2257     case 'E':
2258     case 'e':
2259     {
2260       if (LocaleCompare((const char *) name,"ellipse") == 0)
2261         {
2262           double
2263             angle;
2264
2265           angle=svg_info->element.angle;
2266           (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2267             svg_info->element.cx,svg_info->element.cy,
2268             angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2269             angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2270           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2271           break;
2272         }
2273       break;
2274     }
2275     case 'G':
2276     case 'g':
2277     {
2278       if (LocaleCompare((const char *) name,"g") == 0)
2279         {
2280           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2281           break;
2282         }
2283       break;
2284     }
2285     case 'I':
2286     case 'i':
2287     {
2288       if (LocaleCompare((const char *) name,"image") == 0)
2289         {
2290           (void) FormatLocaleFile(svg_info->file,
2291             "image Over %g,%g %g,%g '%s'\n",svg_info->bounds.x,
2292             svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2293             svg_info->url);
2294           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2295           break;
2296         }
2297       break;
2298     }
2299     case 'L':
2300     case 'l':
2301     {
2302       if (LocaleCompare((const char *) name,"line") == 0)
2303         {
2304           (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2305             svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2306             svg_info->segment.y2);
2307           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2308           break;
2309         }
2310       if (LocaleCompare((const char *) name,"linearGradient") == 0)
2311         {
2312           (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2313           break;
2314         }
2315       break;
2316     }
2317     case 'P':
2318     case 'p':
2319     {
2320       if (LocaleCompare((const char *) name,"pattern") == 0)
2321         {
2322           (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2323           break;
2324         }
2325       if (LocaleCompare((const char *) name,"path") == 0)
2326         {
2327           (void) FormatLocaleFile(svg_info->file,"path '%s'\n",
2328             svg_info->vertices);
2329           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2330           break;
2331         }
2332       if (LocaleCompare((const char *) name,"polygon") == 0)
2333         {
2334           (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2335             svg_info->vertices);
2336           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2337           break;
2338         }
2339       if (LocaleCompare((const char *) name,"polyline") == 0)
2340         {
2341           (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2342             svg_info->vertices);
2343           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2344           break;
2345         }
2346       break;
2347     }
2348     case 'R':
2349     case 'r':
2350     {
2351       if (LocaleCompare((const char *) name,"radialGradient") == 0)
2352         {
2353           (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2354           break;
2355         }
2356       if (LocaleCompare((const char *) name,"rect") == 0)
2357         {
2358           if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2359             {
2360               (void) FormatLocaleFile(svg_info->file,"rectangle %g,%g %g,%g\n",
2361                 svg_info->bounds.x,svg_info->bounds.y,
2362                 svg_info->bounds.x+svg_info->bounds.width,
2363                 svg_info->bounds.y+svg_info->bounds.height);
2364               (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2365               break;
2366             }
2367           if (svg_info->radius.x == 0.0)
2368             svg_info->radius.x=svg_info->radius.y;
2369           if (svg_info->radius.y == 0.0)
2370             svg_info->radius.y=svg_info->radius.x;
2371           (void) FormatLocaleFile(svg_info->file,
2372             "roundRectangle %g,%g %g,%g %g,%g\n",
2373             svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2374             svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2375             svg_info->radius.x,svg_info->radius.y);
2376           svg_info->radius.x=0.0;
2377           svg_info->radius.y=0.0;
2378           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2379           break;
2380         }
2381       break;
2382     }
2383     case 'S':
2384     case 's':
2385     {
2386       if (LocaleCompare((const char *) name,"stop") == 0)
2387         {
2388           (void) FormatLocaleFile(svg_info->file,"stop-color '%s' %s\n",
2389             svg_info->stop_color,svg_info->offset);
2390           break;
2391         }
2392       if (LocaleCompare((const char *) name,"svg") == 0)
2393         {
2394           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2395           break;
2396         }
2397       break;
2398     }
2399     case 'T':
2400     case 't':
2401     {
2402       if (LocaleCompare((const char *) name,"text") == 0)
2403         {
2404           if (*svg_info->text != '\0')
2405             {
2406               char
2407                 *text;
2408
2409               text=EscapeString(svg_info->text,'\'');
2410               (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
2411                 svg_info->bounds.x,svg_info->bounds.y,text);
2412               text=DestroyString(text);
2413               *svg_info->text='\0';
2414             }
2415           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2416           break;
2417         }
2418       if (LocaleCompare((const char *) name,"tspan") == 0)
2419         {
2420           if (*svg_info->text != '\0')
2421             {
2422               DrawInfo
2423                 *draw_info;
2424
2425               TypeMetric
2426                 metrics;
2427
2428               char
2429                 *text;
2430
2431               text=EscapeString(svg_info->text,'\'');
2432               (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
2433                 svg_info->bounds.x,svg_info->bounds.y,text);
2434               text=DestroyString(text);
2435               draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
2436               draw_info->pointsize=svg_info->pointsize;
2437               draw_info->text=AcquireString(svg_info->text);
2438               (void) ConcatenateString(&draw_info->text," ");
2439               (void) GetTypeMetrics(svg_info->image,draw_info,&metrics,
2440                 svg_info->exception);
2441               svg_info->bounds.x+=metrics.width;
2442               draw_info=DestroyDrawInfo(draw_info);
2443               *svg_info->text='\0';
2444             }
2445           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2446           break;
2447         }
2448       if (LocaleCompare((const char *) name,"title") == 0)
2449         {
2450           if (*svg_info->text == '\0')
2451             break;
2452           (void) CloneString(&svg_info->title,svg_info->text);
2453           *svg_info->text='\0';
2454           break;
2455         }
2456       break;
2457     }
2458     default:
2459       break;
2460   }
2461   *svg_info->text='\0';
2462   (void) ResetMagickMemory(&svg_info->element,0,sizeof(svg_info->element));
2463   (void) ResetMagickMemory(&svg_info->segment,0,sizeof(svg_info->segment));
2464   svg_info->n--;
2465 }
2466
2467 static void SVGCharacters(void *context,const xmlChar *c,int length)
2468 {
2469   char
2470     *text;
2471
2472   register char
2473     *p;
2474
2475   register ssize_t
2476     i;
2477
2478   SVGInfo
2479     *svg_info;
2480
2481   /*
2482     Receiving some characters from the parser.
2483   */
2484   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2485     "  SAX.characters(%s,%.20g)",c,(double) length);
2486   svg_info=(SVGInfo *) context;
2487   text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2488   if (text == (char *) NULL)
2489     return;
2490   p=text;
2491   for (i=0; i < (ssize_t) length; i++)
2492     *p++=c[i];
2493   *p='\0';
2494   StripString(text);
2495   if (svg_info->text == (char *) NULL)
2496     svg_info->text=text;
2497   else
2498     {
2499       (void) ConcatenateString(&svg_info->text,text);
2500       text=DestroyString(text);
2501     }
2502 }
2503
2504 static void SVGReference(void *context,const xmlChar *name)
2505 {
2506   SVGInfo
2507     *svg_info;
2508
2509   xmlParserCtxtPtr
2510     parser;
2511
2512   /*
2513     Called when an entity reference is detected.
2514   */
2515   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.reference(%s)",
2516     name);
2517   svg_info=(SVGInfo *) context;
2518   parser=svg_info->parser;
2519   if (parser == (xmlParserCtxtPtr) NULL)
2520     return;
2521   if (parser->node == (xmlNodePtr) NULL)
2522     return;
2523   if (*name == '#')
2524     (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2525   else
2526     (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2527 }
2528
2529 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2530 {
2531   SVGInfo
2532     *svg_info;
2533
2534   /*
2535     Receiving some ignorable whitespaces from the parser.
2536   */
2537   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2538     "  SAX.ignorableWhitespace(%.30s, %d)",c,length);
2539   svg_info=(SVGInfo *) context;
2540   (void) svg_info;
2541 }
2542
2543 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2544   const xmlChar *data)
2545 {
2546   SVGInfo
2547     *svg_info;
2548
2549   /*
2550     A processing instruction has been parsed.
2551   */
2552   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2553     "  SAX.processingInstruction(%s, %s)",target,data);
2554   svg_info=(SVGInfo *) context;
2555   (void) svg_info;
2556 }
2557
2558 static void SVGComment(void *context,const xmlChar *value)
2559 {
2560   SVGInfo
2561     *svg_info;
2562
2563   /*
2564     A comment has been parsed.
2565   */
2566   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.comment(%s)",
2567     value);
2568   svg_info=(SVGInfo *) context;
2569   if (svg_info->comment != (char *) NULL)
2570     (void) ConcatenateString(&svg_info->comment,"\n");
2571   (void) ConcatenateString(&svg_info->comment,(const char *) value);
2572 }
2573
2574 static void SVGWarning(void *context,const char *format,...)
2575 {
2576   char
2577     *message,
2578     reason[MaxTextExtent];
2579
2580   SVGInfo
2581     *svg_info;
2582
2583   va_list
2584     operands;
2585
2586   /**
2587     Display and format a warning messages, gives file, line, position and
2588     extra parameters.
2589   */
2590   va_start(operands,format);
2591   svg_info=(SVGInfo *) context;
2592   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.warning: ");
2593   (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2594 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2595   (void) vsprintf(reason,format,operands);
2596 #else
2597   (void) vsnprintf(reason,MaxTextExtent,format,operands);
2598 #endif
2599   message=GetExceptionMessage(errno);
2600   (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2601     DelegateWarning,reason,"`%s`",message);
2602   message=DestroyString(message);
2603   va_end(operands);
2604 }
2605
2606 static void SVGError(void *context,const char *format,...)
2607 {
2608   char
2609     *message,
2610     reason[MaxTextExtent];
2611
2612   SVGInfo
2613     *svg_info;
2614
2615   va_list
2616     operands;
2617
2618   /*
2619     Display and format a error formats, gives file, line, position and
2620     extra parameters.
2621   */
2622   va_start(operands,format);
2623   svg_info=(SVGInfo *) context;
2624   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.error: ");
2625   (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2626 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2627   (void) vsprintf(reason,format,operands);
2628 #else
2629   (void) vsnprintf(reason,MaxTextExtent,format,operands);
2630 #endif
2631   message=GetExceptionMessage(errno);
2632   (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2633     reason,"`%s`",message);
2634   message=DestroyString(message);
2635   va_end(operands);
2636 }
2637
2638 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2639 {
2640   SVGInfo
2641     *svg_info;
2642
2643    xmlNodePtr
2644      child;
2645
2646   xmlParserCtxtPtr
2647     parser;
2648
2649   /*
2650     Called when a pcdata block has been parsed.
2651   */
2652   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.pcdata(%s, %d)",
2653     value,length);
2654   svg_info=(SVGInfo *) context;
2655   parser=svg_info->parser;
2656   child=xmlGetLastChild(parser->node);
2657   if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2658     {
2659       xmlTextConcat(child,value,length);
2660       return;
2661     }
2662   (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2663 }
2664
2665 static void SVGExternalSubset(void *context,const xmlChar *name,
2666   const xmlChar *external_id,const xmlChar *system_id)
2667 {
2668   SVGInfo
2669     *svg_info;
2670
2671   xmlParserCtxt
2672     parser_context;
2673
2674   xmlParserCtxtPtr
2675     parser;
2676
2677   xmlParserInputPtr
2678     input;
2679
2680   /*
2681     Does this document has an external subset?
2682   */
2683   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2684     "  SAX.externalSubset(%s, %s, %s)",name,
2685     (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2686     (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2687   svg_info=(SVGInfo *) context;
2688   parser=svg_info->parser;
2689   if (((external_id == NULL) && (system_id == NULL)) ||
2690       ((parser->validate == 0) || (parser->wellFormed == 0) ||
2691       (svg_info->document == 0)))
2692     return;
2693   input=SVGResolveEntity(context,external_id,system_id);
2694   if (input == NULL)
2695     return;
2696   (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2697   parser_context=(*parser);
2698   parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2699   if (parser->inputTab == (xmlParserInputPtr *) NULL)
2700     {
2701       parser->errNo=XML_ERR_NO_MEMORY;
2702       parser->input=parser_context.input;
2703       parser->inputNr=parser_context.inputNr;
2704       parser->inputMax=parser_context.inputMax;
2705       parser->inputTab=parser_context.inputTab;
2706       return;
2707   }
2708   parser->inputNr=0;
2709   parser->inputMax=5;
2710   parser->input=NULL;
2711   xmlPushInput(parser,input);
2712   (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2713   if (input->filename == (char *) NULL)
2714     input->filename=(char *) xmlStrdup(system_id);
2715   input->line=1;
2716   input->col=1;
2717   input->base=parser->input->cur;
2718   input->cur=parser->input->cur;
2719   input->free=NULL;
2720   xmlParseExternalSubset(parser,external_id,system_id);
2721   while (parser->inputNr > 1)
2722     (void) xmlPopInput(parser);
2723   xmlFreeInputStream(parser->input);
2724   xmlFree(parser->inputTab);
2725   parser->input=parser_context.input;
2726   parser->inputNr=parser_context.inputNr;
2727   parser->inputMax=parser_context.inputMax;
2728   parser->inputTab=parser_context.inputTab;
2729 }
2730
2731 #if defined(MAGICKCORE_RSVG_DELEGATE)
2732 static void SVGSetImageSize(int *width,int *height,gpointer context)
2733 {
2734   Image
2735     *image;
2736
2737   image=(Image *) context;
2738   *width=(int) (*width*image->resolution.x/72.0);
2739   *height=(int) (*height*image->resolution.y/72.0);
2740 }
2741 #endif
2742
2743 #if defined(__cplusplus) || defined(c_plusplus)
2744 }
2745 #endif
2746
2747 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2748 {
2749   char
2750     filename[MaxTextExtent];
2751
2752   FILE
2753     *file;
2754
2755   Image
2756     *image;
2757
2758   int
2759     status,
2760     unique_file;
2761
2762   ssize_t
2763     n;
2764
2765   SVGInfo
2766     *svg_info;
2767
2768   unsigned char
2769     message[MaxTextExtent];
2770
2771   xmlSAXHandler
2772     sax_modules;
2773
2774   xmlSAXHandlerPtr
2775     sax_handler;
2776
2777   /*
2778     Open image file.
2779   */
2780   assert(image_info != (const ImageInfo *) NULL);
2781   assert(image_info->signature == MagickSignature);
2782   assert(exception != (ExceptionInfo *) NULL);
2783   if (image_info->debug != MagickFalse)
2784     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2785       image_info->filename);
2786   assert(exception->signature == MagickSignature);
2787   image=AcquireImage(image_info,exception);
2788   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2789   if (status == MagickFalse)
2790     {
2791       image=DestroyImageList(image);
2792       return((Image *) NULL);
2793     }
2794   if (LocaleCompare(image_info->magick,"MSVG") != 0)
2795     {
2796 #if defined(MAGICKCORE_RSVG_DELEGATE)
2797 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2798       cairo_surface_t
2799         *cairo_surface;
2800
2801       cairo_t
2802         *cairo_info;
2803
2804       register unsigned char
2805         *p;
2806
2807       RsvgDimensionData
2808         dimension_info;
2809
2810       unsigned char
2811         *pixels;
2812
2813 #else
2814       GdkPixbuf
2815         *pixel_info;
2816
2817       register const guchar
2818         *p;
2819
2820 #endif
2821
2822       GError
2823         *error;
2824
2825       ssize_t
2826         y;
2827
2828       PixelInfo
2829         fill_color;
2830
2831       register ssize_t
2832         x;
2833
2834       register Quantum
2835         *q;
2836
2837       RsvgHandle
2838         *svg_handle;
2839
2840       svg_handle=rsvg_handle_new();
2841       if (svg_handle == (RsvgHandle *) NULL)
2842         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2843       rsvg_handle_set_base_uri(svg_handle,image_info->filename);
2844       rsvg_handle_set_size_callback(svg_handle,SVGSetImageSize,image,NULL);
2845       if ((image->resolution.x != 72.0) && (image->resolution.y != 72.0))
2846         rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
2847           image->resolution.y);
2848       while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
2849       {
2850         error=(GError *) NULL;
2851         (void) rsvg_handle_write(svg_handle,message,n,&error);
2852         if (error != (GError *) NULL)
2853           g_error_free(error);
2854       }
2855       error=(GError *) NULL;
2856       rsvg_handle_close(svg_handle,&error);
2857       if (error != (GError *) NULL)
2858         g_error_free(error);
2859 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2860       rsvg_handle_get_dimensions(svg_handle,&dimension_info);
2861       image->columns=dimension_info.width;
2862       image->rows=dimension_info.height;
2863       pixels=(unsigned char *) NULL;
2864 #else
2865       pixel_info=rsvg_handle_get_pixbuf(svg_handle);
2866       rsvg_handle_free(svg_handle);
2867       image->columns=gdk_pixbuf_get_width(pixel_info);
2868       image->rows=gdk_pixbuf_get_height(pixel_info);
2869 #endif
2870       image->matte=MagickTrue;
2871       SetImageProperty(image,"svg:base-uri",
2872         rsvg_handle_get_base_uri(svg_handle),exception);
2873       SetImageProperty(image,"svg:title",rsvg_handle_get_title(svg_handle),
2874         exception);
2875       SetImageProperty(image,"svg:description",
2876         rsvg_handle_get_desc(svg_handle),exception);
2877       if ((image->columns == 0) || (image->rows == 0))
2878         {
2879 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
2880           g_object_unref(G_OBJECT(pixel_info));
2881 #endif
2882           g_object_unref(svg_handle);
2883           ThrowReaderException(MissingDelegateError,
2884             "NoDecodeDelegateForThisImageFormat");
2885         }
2886       if (image_info->ping == MagickFalse)
2887         {
2888 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2889           pixels=(unsigned char *) AcquireQuantumMemory(image->columns,4*
2890             image->rows*sizeof(*pixels));
2891           if (pixels == (unsigned char *) NULL)
2892             {
2893               g_object_unref(svg_handle);
2894               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2895             }
2896 #endif
2897           (void) SetImageBackgroundColor(image,exception);
2898 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2899           cairo_surface=cairo_image_surface_create_for_data(pixels,
2900             CAIRO_FORMAT_ARGB32,image->columns,image->rows,4*image->columns);
2901           if (cairo_surface == (cairo_surface_t *) NULL)
2902             {
2903               pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2904               g_object_unref(svg_handle);
2905               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2906             }
2907           cairo_info=cairo_create(cairo_surface);
2908           cairo_set_operator(cairo_info,CAIRO_OPERATOR_CLEAR);
2909           cairo_paint(cairo_info);
2910           cairo_set_operator(cairo_info,CAIRO_OPERATOR_OVER);
2911           rsvg_handle_render_cairo(svg_handle,cairo_info);
2912           cairo_destroy(cairo_info);
2913           cairo_surface_destroy(cairo_surface);
2914           g_object_unref(svg_handle);
2915           p=pixels;
2916 #else
2917           p=gdk_pixbuf_get_pixels(pixel_info);
2918 #endif
2919           GetPixelInfo(image,&fill_color);
2920           for (y=0; y < (ssize_t) image->rows; y++)
2921           {
2922             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2923             if (q == (Quantum *) NULL)
2924               break;
2925             for (x=0; x < (ssize_t) image->columns; x++)
2926             {
2927 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2928               fill_color.blue=ScaleCharToQuantum(*p++);
2929               fill_color.green=ScaleCharToQuantum(*p++);
2930               fill_color.red=ScaleCharToQuantum(*p++);
2931 #else
2932               fill_color.red=ScaleCharToQuantum(*p++);
2933               fill_color.green=ScaleCharToQuantum(*p++);
2934               fill_color.blue=ScaleCharToQuantum(*p++);
2935 #endif
2936               fill_color.alpha=ScaleCharToQuantum(*p++);
2937 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2938               {
2939                 double
2940                   gamma;
2941     
2942                 gamma=1.0-QuantumScale*fill_color.alpha;
2943                 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2944                 fill_color.blue*=gamma;
2945                 fill_color.green*=gamma;
2946                 fill_color.red*=gamma;
2947               }
2948 #endif
2949               CompositePixelOver(image,&fill_color,fill_color.alpha,q,
2950                 (MagickRealType) GetPixelAlpha(image,q),q);
2951               q+=GetPixelChannels(image);
2952             }
2953             if (SyncAuthenticPixels(image,exception) == MagickFalse)
2954               break;
2955             if (image->previous == (Image *) NULL)
2956               {
2957                 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2958                 image->rows);
2959                 if (status == MagickFalse)
2960                   break;
2961               }
2962           }
2963         }
2964 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2965       if (pixels != (unsigned char *) NULL)
2966         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2967 #else
2968       g_object_unref(G_OBJECT(pixel_info));
2969 #endif
2970       (void) CloseBlob(image);
2971       return(GetFirstImageInList(image));
2972 #endif
2973     }
2974   /*
2975     Open draw file.
2976   */
2977   file=(FILE *) NULL;
2978   unique_file=AcquireUniqueFileResource(filename);
2979   if (unique_file != -1)
2980     file=fdopen(unique_file,"w");
2981   if ((unique_file == -1) || (file == (FILE *) NULL))
2982     {
2983       (void) CopyMagickString(image->filename,filename,MaxTextExtent);
2984       ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
2985         image->filename);
2986       image=DestroyImageList(image);
2987       return((Image *) NULL);
2988     }
2989   /*
2990     Parse SVG file.
2991   */
2992   svg_info=AcquireSVGInfo();
2993   if (svg_info == (SVGInfo *) NULL)
2994     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2995   svg_info->file=file;
2996   svg_info->exception=exception;
2997   svg_info->image=image;
2998   svg_info->image_info=image_info;
2999   svg_info->bounds.width=image->columns;
3000   svg_info->bounds.height=image->rows;
3001   if (image_info->size != (char *) NULL)
3002     (void) CloneString(&svg_info->size,image_info->size);
3003   if (image->debug != MagickFalse)
3004     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3005   xmlInitParser();
3006   (void) xmlSubstituteEntitiesDefault(1);
3007   (void) ResetMagickMemory(&sax_modules,0,sizeof(sax_modules));
3008   sax_modules.internalSubset=SVGInternalSubset;
3009   sax_modules.isStandalone=SVGIsStandalone;
3010   sax_modules.hasInternalSubset=SVGHasInternalSubset;
3011   sax_modules.hasExternalSubset=SVGHasExternalSubset;
3012   sax_modules.resolveEntity=SVGResolveEntity;
3013   sax_modules.getEntity=SVGGetEntity;
3014   sax_modules.entityDecl=SVGEntityDeclaration;
3015   sax_modules.notationDecl=SVGNotationDeclaration;
3016   sax_modules.attributeDecl=SVGAttributeDeclaration;
3017   sax_modules.elementDecl=SVGElementDeclaration;
3018   sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3019   sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3020   sax_modules.startDocument=SVGStartDocument;
3021   sax_modules.endDocument=SVGEndDocument;
3022   sax_modules.startElement=SVGStartElement;
3023   sax_modules.endElement=SVGEndElement;
3024   sax_modules.reference=SVGReference;
3025   sax_modules.characters=SVGCharacters;
3026   sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3027   sax_modules.processingInstruction=SVGProcessingInstructions;
3028   sax_modules.comment=SVGComment;
3029   sax_modules.warning=SVGWarning;
3030   sax_modules.error=SVGError;
3031   sax_modules.fatalError=SVGError;
3032   sax_modules.getParameterEntity=SVGGetParameterEntity;
3033   sax_modules.cdataBlock=SVGCDataBlock;
3034   sax_modules.externalSubset=SVGExternalSubset;
3035   sax_handler=(&sax_modules);
3036   n=ReadBlob(image,MaxTextExtent,message);
3037   if (n > 0)
3038     {
3039       svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3040         message,n,image->filename);
3041       while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
3042       {
3043         status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3044         if (status != 0)
3045           break;
3046       }
3047     }
3048   (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3049   xmlFreeParserCtxt(svg_info->parser);
3050   if (image->debug != MagickFalse)
3051     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3052   xmlCleanupParser();
3053   (void) fclose(file);
3054   (void) CloseBlob(image);
3055   image->columns=svg_info->width;
3056   image->rows=svg_info->height;
3057   if (exception->severity >= ErrorException)
3058     {
3059       image=DestroyImage(image);
3060       return((Image *) NULL);
3061     }
3062   if (image_info->ping == MagickFalse)
3063     {
3064       ImageInfo
3065         *read_info;
3066
3067       /*
3068         Draw image.
3069       */
3070       image=DestroyImage(image);
3071       image=(Image *) NULL;
3072       read_info=CloneImageInfo(image_info);
3073       SetImageInfoBlob(read_info,(void *) NULL,0);
3074       if (read_info->density != (char *) NULL)
3075         read_info->density=DestroyString(read_info->density);
3076       (void) FormatLocaleString(read_info->filename,MaxTextExtent,"mvg:%s",
3077         filename);
3078       image=ReadImage(read_info,exception);
3079       read_info=DestroyImageInfo(read_info);
3080       if (image != (Image *) NULL)
3081         (void) CopyMagickString(image->filename,image_info->filename,
3082           MaxTextExtent);
3083     }
3084   /*
3085     Relinquish resources.
3086   */
3087   if (image != (Image *) NULL)
3088     {
3089       if (svg_info->title != (char *) NULL)
3090         (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3091       if (svg_info->comment != (char *) NULL)
3092         (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3093           exception);
3094     }
3095   svg_info=DestroySVGInfo(svg_info);
3096   (void) RelinquishUniqueFileResource(filename);
3097   return(GetFirstImageInList(image));
3098 }
3099 #endif
3100 \f
3101 /*
3102 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3103 %                                                                             %
3104 %                                                                             %
3105 %                                                                             %
3106 %   R e g i s t e r S V G I m a g e                                           %
3107 %                                                                             %
3108 %                                                                             %
3109 %                                                                             %
3110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3111 %
3112 %  RegisterSVGImage() adds attributes for the SVG image format to
3113 %  the list of supported formats.  The attributes include the image format
3114 %  tag, a method to read and/or write the format, whether the format
3115 %  supports the saving of more than one frame to the same file or blob,
3116 %  whether the format supports native in-memory I/O, and a brief
3117 %  description of the format.
3118 %
3119 %  The format of the RegisterSVGImage method is:
3120 %
3121 %      size_t RegisterSVGImage(void)
3122 %
3123 */
3124 ModuleExport size_t RegisterSVGImage(void)
3125 {
3126   char
3127     version[MaxTextExtent];
3128
3129   MagickInfo
3130     *entry;
3131
3132   *version='\0';
3133 #if defined(LIBXML_DOTTED_VERSION)
3134   (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,MaxTextExtent);
3135 #endif
3136 #if defined(MAGICKCORE_RSVG_DELEGATE)
3137   rsvg_init();
3138   (void) FormatLocaleString(version,MaxTextExtent,"RSVG %d.%d.%d",
3139     LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3140 #endif
3141   entry=SetMagickInfo("SVG");
3142 #if defined(MAGICKCORE_XML_DELEGATE)
3143   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3144 #endif
3145   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3146   entry->blob_support=MagickFalse;
3147   entry->seekable_stream=MagickFalse;
3148   entry->description=ConstantString("Scalable Vector Graphics");
3149   if (*version != '\0')
3150     entry->version=ConstantString(version);
3151   entry->magick=(IsImageFormatHandler *) IsSVG;
3152   entry->module=ConstantString("SVG");
3153   (void) RegisterMagickInfo(entry);
3154   entry=SetMagickInfo("SVGZ");
3155 #if defined(MAGICKCORE_XML_DELEGATE)
3156   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3157 #endif
3158   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3159   entry->blob_support=MagickFalse;
3160   entry->seekable_stream=MagickFalse;
3161   entry->description=ConstantString("Compressed Scalable Vector Graphics");
3162   if (*version != '\0')
3163     entry->version=ConstantString(version);
3164   entry->magick=(IsImageFormatHandler *) IsSVG;
3165   entry->module=ConstantString("SVG");
3166   (void) RegisterMagickInfo(entry);
3167   entry=SetMagickInfo("MSVG");
3168 #if defined(MAGICKCORE_XML_DELEGATE)
3169   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3170 #endif
3171   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3172   entry->blob_support=MagickFalse;
3173   entry->seekable_stream=MagickFalse;
3174   entry->description=ConstantString("ImageMagick's own SVG internal renderer");
3175   entry->magick=(IsImageFormatHandler *) IsSVG;
3176   entry->module=ConstantString("SVG");
3177   (void) RegisterMagickInfo(entry);
3178   return(MagickImageCoderSignature);
3179 }
3180 \f
3181 /*
3182 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3183 %                                                                             %
3184 %                                                                             %
3185 %                                                                             %
3186 %   U n r e g i s t e r S V G I m a g e                                       %
3187 %                                                                             %
3188 %                                                                             %
3189 %                                                                             %
3190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3191 %
3192 %  UnregisterSVGImage() removes format registrations made by the
3193 %  SVG module from the list of supported formats.
3194 %
3195 %  The format of the UnregisterSVGImage method is:
3196 %
3197 %      UnregisterSVGImage(void)
3198 %
3199 */
3200 ModuleExport void UnregisterSVGImage(void)
3201 {
3202   (void) UnregisterMagickInfo("SVGZ");
3203   (void) UnregisterMagickInfo("SVG");
3204   (void) UnregisterMagickInfo("MSVG");
3205 #if defined(MAGICKCORE_RSVG_DELEGATE)
3206   rsvg_term();
3207 #endif
3208 }
3209 \f
3210 /*
3211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3212 %                                                                             %
3213 %                                                                             %
3214 %                                                                             %
3215 %   W r i t e S V G I m a g e                                                 %
3216 %                                                                             %
3217 %                                                                             %
3218 %                                                                             %
3219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3220 %
3221 %  WriteSVGImage() writes a image in the SVG - XML based W3C standard
3222 %  format.
3223 %
3224 %  The format of the WriteSVGImage method is:
3225 %
3226 %      MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3227 %        Image *image,ExceptionInfo *exception)
3228 %
3229 %  A description of each parameter follows.
3230 %
3231 %    o image_info: the image info.
3232 %
3233 %    o image:  The image.
3234 %
3235 %    o exception: return any errors or warnings in this structure.
3236 %
3237 */
3238
3239 static void AffineToTransform(Image *image,AffineMatrix *affine)
3240 {
3241   char
3242     transform[MaxTextExtent];
3243
3244   if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3245     {
3246       if ((fabs(affine->rx) < MagickEpsilon) &&
3247           (fabs(affine->ry) < MagickEpsilon))
3248         {
3249           if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3250               (fabs(affine->sy-1.0) < MagickEpsilon))
3251             {
3252               (void) WriteBlobString(image,"\">\n");
3253               return;
3254             }
3255           (void) FormatLocaleString(transform,MaxTextExtent,
3256             "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3257           (void) WriteBlobString(image,transform);
3258           return;
3259         }
3260       else
3261         {
3262           if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3263               (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3264               (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3265                2*MagickEpsilon))
3266             {
3267               double
3268                 theta;
3269
3270               theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3271               (void) FormatLocaleString(transform,MaxTextExtent,
3272                 "\" transform=\"rotate(%g)\">\n",theta);
3273               (void) WriteBlobString(image,transform);
3274               return;
3275             }
3276         }
3277     }
3278   else
3279     {
3280       if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3281           (fabs(affine->rx) < MagickEpsilon) &&
3282           (fabs(affine->ry) < MagickEpsilon) &&
3283           (fabs(affine->sy-1.0) < MagickEpsilon))
3284         {
3285           (void) FormatLocaleString(transform,MaxTextExtent,
3286             "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3287           (void) WriteBlobString(image,transform);
3288           return;
3289         }
3290     }
3291   (void) FormatLocaleString(transform,MaxTextExtent,
3292     "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3293     affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3294   (void) WriteBlobString(image,transform);
3295 }
3296
3297 static MagickBooleanType IsPoint(const char *point)
3298 {
3299   char
3300     *p;
3301
3302   ssize_t
3303     value;
3304
3305   value=strtol(point,&p,10);
3306   (void) value;
3307   return(p != point ? MagickTrue : MagickFalse);
3308 }
3309
3310 static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
3311 {
3312   ssize_t
3313     y;
3314
3315   register const Quantum
3316     *p;
3317
3318   register ssize_t
3319     x;
3320
3321 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3322   {
3323     at_bitmap_type
3324       *trace;
3325
3326     at_fitting_opts_type
3327       *fitting_options;
3328
3329     at_output_opts_type
3330       *output_options;
3331
3332     at_splines_type
3333       *splines;
3334
3335     ImageType
3336       type;
3337
3338     register ssize_t
3339       i;
3340
3341     size_t
3342       number_planes;
3343
3344     /*
3345       Trace image and write as SVG.
3346     */
3347     fitting_options=at_fitting_opts_new();
3348     output_options=at_output_opts_new();
3349     type=GetImageType(image,exception);
3350     number_planes=3;
3351     if ((type == BilevelType) || (type == GrayscaleType))
3352       number_planes=1;
3353     trace=at_bitmap_new(image->columns,image->rows,number_planes);
3354     i=0;
3355     for (y=0; y < (ssize_t) image->rows; y++)
3356     {
3357       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3358       if (p == (const Quantum *) NULL)
3359         break;
3360       for (x=0; x < (ssize_t) image->columns; x++)
3361       {
3362         trace->bitmap[i++]=GetPixelRed(image,p);
3363         if (number_planes == 3)
3364           {
3365             trace->bitmap[i++]=GetPixelGreen(image,p);
3366             trace->bitmap[i++]=GetPixelBlue(image,p);
3367           }
3368         p+=GetPixelChannels(image);
3369       }
3370     }
3371     splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3372       NULL);
3373     at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3374       GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3375       NULL);
3376     /*
3377       Free resources.
3378     */
3379     at_splines_free(splines);
3380     at_bitmap_free(trace);
3381     at_output_opts_free(output_options);
3382     at_fitting_opts_free(fitting_options);
3383   }
3384 #else
3385   {
3386     char
3387       message[MaxTextExtent],
3388       tuple[MaxTextExtent];
3389
3390     PixelInfo
3391       pixel;
3392
3393     (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3394     (void) WriteBlobString(image,
3395       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3396     (void) WriteBlobString(image,
3397       "  \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3398     (void) FormatLocaleString(message,MaxTextExtent,
3399       "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,
3400       (double) image->rows);
3401     (void) WriteBlobString(image,message);
3402     GetPixelInfo(image,&pixel);
3403     for (y=0; y < (ssize_t) image->rows; y++)
3404     {
3405       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
3406       if (p == (const Quantum *) NULL)
3407         break;
3408       for (x=0; x < (ssize_t) image->columns; x++)
3409       {
3410         GetPixelInfoPixel(image,p,&pixel);
3411         (void) QueryColorname(image,&pixel,SVGCompliance,tuple,exception);
3412         (void) FormatLocaleString(message,MaxTextExtent,
3413           "  <circle cx=\"%.20g\" cy=\"%.20g\" r=\"1\" fill=\"%s\"/>\n",
3414           (double) x,(double) y,tuple);
3415         (void) WriteBlobString(image,message);
3416         p+=GetPixelChannels(image);
3417       }
3418     }
3419     (void) WriteBlobString(image,"</svg>\n");
3420   }
3421 #endif
3422   return(MagickTrue);
3423 }
3424
3425 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
3426   ExceptionInfo *exception)
3427 {
3428 #define BezierQuantum  200
3429
3430   AffineMatrix
3431     affine;
3432
3433   char
3434     keyword[MaxTextExtent],
3435     message[MaxTextExtent],
3436     name[MaxTextExtent],
3437     *token,
3438     type[MaxTextExtent];
3439
3440   const char
3441     *p,
3442     *q,
3443     *value;
3444
3445   int
3446     n;
3447
3448   ssize_t
3449     j;
3450
3451   MagickBooleanType
3452     active,
3453     status;
3454
3455   PointInfo
3456     point;
3457
3458   PrimitiveInfo
3459     *primitive_info;
3460
3461   PrimitiveType
3462     primitive_type;
3463
3464   register ssize_t
3465     x;
3466
3467   register ssize_t
3468     i;
3469
3470   size_t
3471     length;
3472
3473   SVGInfo
3474     svg_info;
3475
3476   size_t
3477     number_points;
3478
3479   /*
3480     Open output image file.
3481   */
3482   assert(image_info != (const ImageInfo *) NULL);
3483   assert(image_info->signature == MagickSignature);
3484   assert(image != (Image *) NULL);
3485   assert(image->signature == MagickSignature);
3486   if (image->debug != MagickFalse)
3487     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3488   assert(exception != (ExceptionInfo *) NULL);
3489   assert(exception->signature == MagickSignature);
3490   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3491   if (status == MagickFalse)
3492     return(status);
3493   value=GetImageArtifact(image,"SVG");
3494   if (value != (char *) NULL)
3495     {
3496       (void) WriteBlobString(image,value);
3497       (void) CloseBlob(image);
3498       return(MagickTrue);
3499     }
3500   value=GetImageArtifact(image,"MVG");
3501   if (value == (char *) NULL)
3502     return(TraceSVGImage(image,exception));
3503   /*
3504     Write SVG header.
3505   */
3506   (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3507   (void) WriteBlobString(image,
3508     "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3509   (void) WriteBlobString(image,
3510     "  \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3511   (void) FormatLocaleString(message,MaxTextExtent,
3512     "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3513     image->rows);
3514   (void) WriteBlobString(image,message);
3515   /*
3516     Allocate primitive info memory.
3517   */
3518   number_points=2047;
3519   primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3520     sizeof(*primitive_info));
3521   if (primitive_info == (PrimitiveInfo *) NULL)
3522     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3523   GetAffineMatrix(&affine);
3524   token=AcquireString(value);
3525   active=MagickFalse;
3526   n=0;
3527   status=MagickTrue;
3528   for (q=(const char *) value; *q != '\0'; )
3529   {
3530     /*
3531       Interpret graphic primitive.
3532     */
3533     GetMagickToken(q,&q,keyword);
3534     if (*keyword == '\0')
3535       break;
3536     if (*keyword == '#')
3537       {
3538         /*
3539           Comment.
3540         */
3541         if (active != MagickFalse)
3542           {
3543             AffineToTransform(image,&affine);
3544             active=MagickFalse;
3545           }
3546         (void) WriteBlobString(image,"<desc>");
3547         (void) WriteBlobString(image,keyword+1);
3548         for ( ; (*q != '\n') && (*q != '\0'); q++)
3549           switch (*q)
3550           {
3551             case '<': (void) WriteBlobString(image,"&lt;"); break;
3552             case '>': (void) WriteBlobString(image,"&gt;"); break;
3553             case '&': (void) WriteBlobString(image,"&amp;"); break;
3554             default: (void) WriteBlobByte(image,*q); break;
3555           }
3556         (void) WriteBlobString(image,"</desc>\n");
3557         continue;
3558       }
3559     primitive_type=UndefinedPrimitive;
3560     switch (*keyword)
3561     {
3562       case ';':
3563         break;
3564       case 'a':
3565       case 'A':
3566       {
3567         if (LocaleCompare("affine",keyword) == 0)
3568           {
3569             GetMagickToken(q,&q,token);
3570             affine.sx=StringToDouble(token,(char **) NULL);
3571             GetMagickToken(q,&q,token);
3572             if (*token == ',')
3573               GetMagickToken(q,&q,token);
3574             affine.rx=StringToDouble(token,(char **) NULL);
3575             GetMagickToken(q,&q,token);
3576             if (*token == ',')
3577               GetMagickToken(q,&q,token);
3578             affine.ry=StringToDouble(token,(char **) NULL);
3579             GetMagickToken(q,&q,token);
3580             if (*token == ',')
3581               GetMagickToken(q,&q,token);
3582             affine.sy=StringToDouble(token,(char **) NULL);
3583             GetMagickToken(q,&q,token);
3584             if (*token == ',')
3585               GetMagickToken(q,&q,token);
3586             affine.tx=StringToDouble(token,(char **) NULL);
3587             GetMagickToken(q,&q,token);
3588             if (*token == ',')
3589               GetMagickToken(q,&q,token);
3590             affine.ty=StringToDouble(token,(char **) NULL);
3591             break;
3592           }
3593         if (LocaleCompare("angle",keyword) == 0)
3594           {
3595             GetMagickToken(q,&q,token);
3596             affine.rx=StringToDouble(token,(char **) NULL);
3597             affine.ry=StringToDouble(token,(char **) NULL);
3598             break;
3599           }
3600         if (LocaleCompare("arc",keyword) == 0)
3601           {
3602             primitive_type=ArcPrimitive;
3603             break;
3604           }
3605         status=MagickFalse;
3606         break;
3607       }
3608       case 'b':
3609       case 'B':
3610       {
3611         if (LocaleCompare("bezier",keyword) == 0)
3612           {
3613             primitive_type=BezierPrimitive;
3614             break;
3615           }
3616         status=MagickFalse;
3617         break;
3618       }
3619       case 'c':
3620       case 'C':
3621       {
3622         if (LocaleCompare("clip-path",keyword) == 0)
3623           {
3624             GetMagickToken(q,&q,token);
3625             (void) FormatLocaleString(message,MaxTextExtent,
3626               "clip-path:url(#%s);",token);
3627             (void) WriteBlobString(image,message);
3628             break;
3629           }
3630         if (LocaleCompare("clip-rule",keyword) == 0)
3631           {
3632             GetMagickToken(q,&q,token);
3633             (void) FormatLocaleString(message,MaxTextExtent,
3634               "clip-rule:%s;",token);
3635             (void) WriteBlobString(image,message);
3636             break;
3637           }
3638         if (LocaleCompare("clip-units",keyword) == 0)
3639           {
3640             GetMagickToken(q,&q,token);
3641             (void) FormatLocaleString(message,MaxTextExtent,
3642               "clipPathUnits=%s;",token);
3643             (void) WriteBlobString(image,message);
3644             break;
3645           }
3646         if (LocaleCompare("circle",keyword) == 0)
3647           {
3648             primitive_type=CirclePrimitive;
3649             break;
3650           }
3651         if (LocaleCompare("color",keyword) == 0)
3652           {
3653             primitive_type=ColorPrimitive;
3654             break;
3655           }
3656         status=MagickFalse;
3657         break;
3658       }
3659       case 'd':
3660       case 'D':
3661       {
3662         if (LocaleCompare("decorate",keyword) == 0)
3663           {
3664             GetMagickToken(q,&q,token);
3665             (void) FormatLocaleString(message,MaxTextExtent,
3666               "text-decoration:%s;",token);
3667             (void) WriteBlobString(image,message);
3668             break;
3669           }
3670         status=MagickFalse;
3671         break;
3672       }
3673       case 'e':
3674       case 'E':
3675       {
3676         if (LocaleCompare("ellipse",keyword) == 0)
3677           {
3678             primitive_type=EllipsePrimitive;
3679             break;
3680           }
3681         status=MagickFalse;
3682         break;
3683       }
3684       case 'f':
3685       case 'F':
3686       {
3687         if (LocaleCompare("fill",keyword) == 0)
3688           {
3689             GetMagickToken(q,&q,token);
3690             (void) FormatLocaleString(message,MaxTextExtent,"fill:%s;",
3691               token);
3692             (void) WriteBlobString(image,message);
3693             break;
3694           }
3695         if (LocaleCompare("fill-rule",keyword) == 0)
3696           {
3697             GetMagickToken(q,&q,token);
3698             (void) FormatLocaleString(message,MaxTextExtent,
3699               "fill-rule:%s;",token);
3700             (void) WriteBlobString(image,message);
3701             break;
3702           }
3703         if (LocaleCompare("fill-opacity",keyword) == 0)
3704           {
3705             GetMagickToken(q,&q,token);
3706             (void) FormatLocaleString(message,MaxTextExtent,
3707               "fill-opacity:%s;",token);
3708             (void) WriteBlobString(image,message);
3709             break;
3710           }
3711         if (LocaleCompare("font-family",keyword) == 0)
3712           {
3713             GetMagickToken(q,&q,token);
3714             (void) FormatLocaleString(message,MaxTextExtent,
3715               "font-family:%s;",token);
3716             (void) WriteBlobString(image,message);
3717             break;
3718           }
3719         if (LocaleCompare("font-stretch",keyword) == 0)
3720           {
3721             GetMagickToken(q,&q,token);
3722             (void) FormatLocaleString(message,MaxTextExtent,
3723               "font-stretch:%s;",token);
3724             (void) WriteBlobString(image,message);
3725             break;
3726           }
3727         if (LocaleCompare("font-style",keyword) == 0)
3728           {
3729             GetMagickToken(q,&q,token);
3730             (void) FormatLocaleString(message,MaxTextExtent,
3731               "font-style:%s;",token);
3732             (void) WriteBlobString(image,message);
3733             break;
3734           }
3735         if (LocaleCompare("font-size",keyword) == 0)
3736           {
3737             GetMagickToken(q,&q,token);
3738             (void) FormatLocaleString(message,MaxTextExtent,
3739               "font-size:%s;",token);
3740             (void) WriteBlobString(image,message);
3741             break;
3742           }
3743         if (LocaleCompare("font-weight",keyword) == 0)
3744           {
3745             GetMagickToken(q,&q,token);
3746             (void) FormatLocaleString(message,MaxTextExtent,
3747               "font-weight:%s;",token);
3748             (void) WriteBlobString(image,message);
3749             break;
3750           }
3751         status=MagickFalse;
3752         break;
3753       }
3754       case 'g':
3755       case 'G':
3756       {
3757         if (LocaleCompare("gradient-units",keyword) == 0)
3758           {
3759             GetMagickToken(q,&q,token);
3760             break;
3761           }
3762         if (LocaleCompare("text-align",keyword) == 0)
3763           {
3764             GetMagickToken(q,&q,token);
3765             (void) FormatLocaleString(message,MaxTextExtent,
3766               "text-align %s ",token);
3767             (void) WriteBlobString(image,message);
3768             break;
3769           }
3770         if (LocaleCompare("text-anchor",keyword) == 0)
3771           {
3772             GetMagickToken(q,&q,token);
3773             (void) FormatLocaleString(message,MaxTextExtent,
3774               "text-anchor %s ",token);
3775             (void) WriteBlobString(image,message);
3776             break;
3777           }
3778         status=MagickFalse;
3779         break;
3780       }
3781       case 'i':
3782       case 'I':
3783       {
3784         if (LocaleCompare("image",keyword) == 0)
3785           {
3786             GetMagickToken(q,&q,token);
3787             primitive_type=ImagePrimitive;
3788             break;
3789           }
3790         status=MagickFalse;
3791         break;
3792       }
3793       case 'l':
3794       case 'L':
3795       {
3796         if (LocaleCompare("line",keyword) == 0)
3797           {
3798             primitive_type=LinePrimitive;
3799             break;
3800           }
3801         status=MagickFalse;
3802         break;
3803       }
3804       case 'm':
3805       case 'M':
3806       {
3807         if (LocaleCompare("matte",keyword) == 0)
3808           {
3809             primitive_type=MattePrimitive;
3810             break;
3811           }
3812         status=MagickFalse;
3813         break;
3814       }
3815       case 'o':
3816       case 'O':
3817       {
3818         if (LocaleCompare("opacity",keyword) == 0)
3819           {
3820             GetMagickToken(q,&q,token);
3821             (void) FormatLocaleString(message,MaxTextExtent,"opacity %s ",
3822               token);
3823             (void) WriteBlobString(image,message);
3824             break;
3825           }
3826         status=MagickFalse;
3827         break;
3828       }
3829       case 'p':
3830       case 'P':
3831       {
3832         if (LocaleCompare("path",keyword) == 0)
3833           {
3834             primitive_type=PathPrimitive;
3835             break;
3836           }
3837         if (LocaleCompare("point",keyword) == 0)
3838           {
3839             primitive_type=PointPrimitive;
3840             break;
3841           }
3842         if (LocaleCompare("polyline",keyword) == 0)
3843           {
3844             primitive_type=PolylinePrimitive;
3845             break;
3846           }
3847         if (LocaleCompare("polygon",keyword) == 0)
3848           {
3849             primitive_type=PolygonPrimitive;
3850             break;
3851           }
3852         if (LocaleCompare("pop",keyword) == 0)
3853           {
3854             GetMagickToken(q,&q,token);
3855             if (LocaleCompare("clip-path",token) == 0)
3856               {
3857                 (void) WriteBlobString(image,"</clipPath>\n");
3858                 break;
3859               }
3860             if (LocaleCompare("defs",token) == 0)
3861               {
3862                 (void) WriteBlobString(image,"</defs>\n");
3863                 break;
3864               }
3865             if (LocaleCompare("gradient",token) == 0)
3866               {
3867                 (void) FormatLocaleString(message,MaxTextExtent,
3868                   "</%sGradient>\n",type);
3869                 (void) WriteBlobString(image,message);
3870                 break;
3871               }
3872             if (LocaleCompare("graphic-context",token) == 0)
3873               {
3874                 n--;
3875                 if (n < 0)
3876                   ThrowWriterException(DrawError,
3877                     "UnbalancedGraphicContextPushPop");
3878                 (void) WriteBlobString(image,"</g>\n");
3879               }
3880             if (LocaleCompare("pattern",token) == 0)
3881               {
3882                 (void) WriteBlobString(image,"</pattern>\n");
3883                 break;
3884               }
3885             if (LocaleCompare("defs",token) == 0)
3886             (void) WriteBlobString(image,"</g>\n");
3887             break;
3888           }
3889         if (LocaleCompare("push",keyword) == 0)
3890           {
3891             GetMagickToken(q,&q,token);
3892             if (LocaleCompare("clip-path",token) == 0)
3893               {
3894                 GetMagickToken(q,&q,token);
3895                 (void) FormatLocaleString(message,MaxTextExtent,
3896                   "<clipPath id=\"%s\">\n",token);
3897                 (void) WriteBlobString(image,message);
3898                 break;
3899               }
3900             if (LocaleCompare("defs",token) == 0)
3901               {
3902                 (void) WriteBlobString(image,"<defs>\n");
3903                 break;
3904               }
3905             if (LocaleCompare("gradient",token) == 0)
3906               {
3907                 GetMagickToken(q,&q,token);
3908                 (void) CopyMagickString(name,token,MaxTextExtent);
3909                 GetMagickToken(q,&q,token);
3910                 (void) CopyMagickString(type,token,MaxTextExtent);
3911                 GetMagickToken(q,&q,token);
3912                 svg_info.segment.x1=StringToDouble(token,(char **) NULL);
3913                 svg_info.element.cx=StringToDouble(token,(char **) NULL);
3914                 GetMagickToken(q,&q,token);
3915                 if (*token == ',')
3916                   GetMagickToken(q,&q,token);
3917                 svg_info.segment.y1=StringToDouble(token,(char **) NULL);
3918                 svg_info.element.cy=StringToDouble(token,(char **) NULL);
3919                 GetMagickToken(q,&q,token);
3920                 if (*token == ',')
3921                   GetMagickToken(q,&q,token);
3922                 svg_info.segment.x2=StringToDouble(token,(char **) NULL);
3923                 svg_info.element.major=StringToDouble(token,
3924                   (char **) NULL);
3925                 GetMagickToken(q,&q,token);
3926                 if (*token == ',')
3927                   GetMagickToken(q,&q,token);
3928                 svg_info.segment.y2=StringToDouble(token,(char **) NULL);
3929                 svg_info.element.minor=StringToDouble(token,
3930                   (char **) NULL);
3931                 (void) FormatLocaleString(message,MaxTextExtent,
3932                   "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
3933                   "y2=\"%g\">\n",type,name,svg_info.segment.x1,
3934                   svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
3935                 if (LocaleCompare(type,"radial") == 0)
3936                   {
3937                     GetMagickToken(q,&q,token);
3938                     if (*token == ',')
3939                       GetMagickToken(q,&q,token);
3940                     svg_info.element.angle=StringToDouble(token,
3941                       (char **) NULL);
3942                     (void) FormatLocaleString(message,MaxTextExtent,
3943                       "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
3944                       "fx=\"%g\" fy=\"%g\">\n",type,name,
3945                       svg_info.element.cx,svg_info.element.cy,
3946                       svg_info.element.angle,svg_info.element.major,
3947                       svg_info.element.minor);
3948                   }
3949                 (void) WriteBlobString(image,message);
3950                 break;
3951               }
3952             if (LocaleCompare("graphic-context",token) == 0)
3953               {
3954                 n++;
3955                 if (active)
3956                   {
3957                     AffineToTransform(image,&affine);
3958                     active=MagickFalse;
3959                   }
3960                 (void) WriteBlobString(image,"<g style=\"");
3961                 active=MagickTrue;
3962               }
3963             if (LocaleCompare("pattern",token) == 0)
3964               {
3965                 GetMagickToken(q,&q,token);
3966                 (void) CopyMagickString(name,token,MaxTextExtent);
3967                 GetMagickToken(q,&q,token);
3968                 svg_info.bounds.x=StringToDouble(token,(char **) NULL);
3969                 GetMagickToken(q,&q,token);
3970                 if (*token == ',')
3971                   GetMagickToken(q,&q,token);
3972                 svg_info.bounds.y=StringToDouble(token,(char **) NULL);
3973                 GetMagickToken(q,&q,token);
3974                 if (*token == ',')
3975                   GetMagickToken(q,&q,token);
3976                 svg_info.bounds.width=StringToDouble(token,
3977                   (char **) NULL);
3978                 GetMagickToken(q,&q,token);
3979                 if (*token == ',')
3980                   GetMagickToken(q,&q,token);
3981                 svg_info.bounds.height=StringToDouble(token,
3982                   (char **) NULL);
3983                 (void) FormatLocaleString(message,MaxTextExtent,
3984                   "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
3985                   "height=\"%g\">\n",name,svg_info.bounds.x,
3986                   svg_info.bounds.y,svg_info.bounds.width,
3987                   svg_info.bounds.height);
3988                 (void) WriteBlobString(image,message);
3989                 break;
3990               }
3991             break;
3992           }
3993         status=MagickFalse;
3994         break;
3995       }
3996       case 'r':
3997       case 'R':
3998       {
3999         if (LocaleCompare("rectangle",keyword) == 0)
4000           {
4001             primitive_type=RectanglePrimitive;
4002             break;
4003           }
4004         if (LocaleCompare("roundRectangle",keyword) == 0)
4005           {
4006             primitive_type=RoundRectanglePrimitive;
4007             break;
4008           }
4009         if (LocaleCompare("rotate",keyword) == 0)
4010           {
4011             GetMagickToken(q,&q,token);
4012             (void) FormatLocaleString(message,MaxTextExtent,"rotate(%s) ",
4013               token);
4014             (void) WriteBlobString(image,message);
4015             break;
4016           }
4017         status=MagickFalse;
4018         break;
4019       }
4020       case 's':
4021       case 'S':
4022       {
4023         if (LocaleCompare("scale",keyword) == 0)
4024           {
4025             GetMagickToken(q,&q,token);
4026             affine.sx=StringToDouble(token,(char **) NULL);
4027             GetMagickToken(q,&q,token);
4028             if (*token == ',')
4029               GetMagickToken(q,&q,token);
4030             affine.sy=StringToDouble(token,(char **) NULL);
4031             break;
4032           }
4033         if (LocaleCompare("skewX",keyword) == 0)
4034           {
4035             GetMagickToken(q,&q,token);
4036             (void) FormatLocaleString(message,MaxTextExtent,"skewX(%s) ",
4037               token);
4038             (void) WriteBlobString(image,message);
4039             break;
4040           }
4041         if (LocaleCompare("skewY",keyword) == 0)
4042           {
4043             GetMagickToken(q,&q,token);
4044             (void) FormatLocaleString(message,MaxTextExtent,"skewY(%s) ",
4045               token);
4046             (void) WriteBlobString(image,message);
4047             break;
4048           }
4049         if (LocaleCompare("stop-color",keyword) == 0)
4050           {
4051             char
4052               color[MaxTextExtent];
4053
4054             GetMagickToken(q,&q,token);
4055             (void) CopyMagickString(color,token,MaxTextExtent);
4056             GetMagickToken(q,&q,token);
4057             (void) FormatLocaleString(message,MaxTextExtent,
4058               "  <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4059             (void) WriteBlobString(image,message);
4060             break;
4061           }
4062         if (LocaleCompare("stroke",keyword) == 0)
4063           {
4064             GetMagickToken(q,&q,token);
4065             (void) FormatLocaleString(message,MaxTextExtent,"stroke:%s;",
4066               token);
4067             (void) WriteBlobString(image,message);
4068             break;
4069           }
4070         if (LocaleCompare("stroke-antialias",keyword) == 0)
4071           {
4072             GetMagickToken(q,&q,token);
4073             (void) FormatLocaleString(message,MaxTextExtent,
4074               "stroke-antialias:%s;",token);
4075             (void) WriteBlobString(image,message);
4076             break;
4077           }
4078         if (LocaleCompare("stroke-dasharray",keyword) == 0)
4079           {
4080             if (IsPoint(q))
4081               {
4082                 ssize_t
4083                   k;
4084
4085                 p=q;
4086                 GetMagickToken(p,&p,token);
4087                 for (k=0; IsPoint(token); k++)
4088                   GetMagickToken(p,&p,token);
4089                 (void) WriteBlobString(image,"stroke-dasharray:");
4090                 for (j=0; j < k; j++)
4091                 {
4092                   GetMagickToken(q,&q,token);
4093                   (void) FormatLocaleString(message,MaxTextExtent,"%s ",
4094                     token);
4095                   (void) WriteBlobString(image,message);
4096                 }
4097                 (void) WriteBlobString(image,";");
4098                 break;
4099               }
4100             GetMagickToken(q,&q,token);
4101             (void) FormatLocaleString(message,MaxTextExtent,
4102               "stroke-dasharray:%s;",token);
4103             (void) WriteBlobString(image,message);
4104             break;
4105           }
4106         if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4107           {
4108             GetMagickToken(q,&q,token);
4109             (void) FormatLocaleString(message,MaxTextExtent,
4110               "stroke-dashoffset:%s;",token);
4111             (void) WriteBlobString(image,message);
4112             break;
4113           }
4114         if (LocaleCompare("stroke-linecap",keyword) == 0)
4115           {
4116             GetMagickToken(q,&q,token);
4117             (void) FormatLocaleString(message,MaxTextExtent,
4118               "stroke-linecap:%s;",token);
4119             (void) WriteBlobString(image,message);
4120             break;
4121           }
4122         if (LocaleCompare("stroke-linejoin",keyword) == 0)
4123           {
4124             GetMagickToken(q,&q,token);
4125             (void) FormatLocaleString(message,MaxTextExtent,
4126               "stroke-linejoin:%s;",token);
4127             (void) WriteBlobString(image,message);
4128             break;
4129           }
4130         if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4131           {
4132             GetMagickToken(q,&q,token);
4133             (void) FormatLocaleString(message,MaxTextExtent,
4134               "stroke-miterlimit:%s;",token);
4135             (void) WriteBlobString(image,message);
4136             break;
4137           }
4138         if (LocaleCompare("stroke-opacity",keyword) == 0)
4139           {
4140             GetMagickToken(q,&q,token);
4141             (void) FormatLocaleString(message,MaxTextExtent,
4142               "stroke-opacity:%s;",token);
4143             (void) WriteBlobString(image,message);
4144             break;
4145           }
4146         if (LocaleCompare("stroke-width",keyword) == 0)
4147           {
4148             GetMagickToken(q,&q,token);
4149             (void) FormatLocaleString(message,MaxTextExtent,
4150               "stroke-width:%s;",token);
4151             (void) WriteBlobString(image,message);
4152             continue;
4153           }
4154         status=MagickFalse;
4155         break;
4156       }
4157       case 't':
4158       case 'T':
4159       {
4160         if (LocaleCompare("text",keyword) == 0)
4161           {
4162             primitive_type=TextPrimitive;
4163             break;
4164           }
4165         if (LocaleCompare("text-antialias",keyword) == 0)
4166           {
4167             GetMagickToken(q,&q,token);
4168             (void) FormatLocaleString(message,MaxTextExtent,
4169               "text-antialias:%s;",token);
4170             (void) WriteBlobString(image,message);
4171             break;
4172           }
4173         if (LocaleCompare("tspan",keyword) == 0)
4174           {
4175             primitive_type=TextPrimitive;
4176             break;
4177           }
4178         if (LocaleCompare("translate",keyword) == 0)
4179           {
4180             GetMagickToken(q,&q,token);
4181             affine.tx=StringToDouble(token,(char **) NULL);
4182             GetMagickToken(q,&q,token);
4183             if (*token == ',')
4184               GetMagickToken(q,&q,token);
4185             affine.ty=StringToDouble(token,(char **) NULL);
4186             break;
4187           }
4188         status=MagickFalse;
4189         break;
4190       }
4191       case 'v':
4192       case 'V':
4193       {
4194         if (LocaleCompare("viewbox",keyword) == 0)
4195           {
4196             GetMagickToken(q,&q,token);
4197             if (*token == ',')
4198               GetMagickToken(q,&q,token);
4199             GetMagickToken(q,&q,token);
4200             if (*token == ',')
4201               GetMagickToken(q,&q,token);
4202             GetMagickToken(q,&q,token);
4203             if (*token == ',')
4204               GetMagickToken(q,&q,token);
4205             GetMagickToken(q,&q,token);
4206             break;
4207           }
4208         status=MagickFalse;
4209         break;
4210       }
4211       default:
4212       {
4213         status=MagickFalse;
4214         break;
4215       }
4216     }
4217     if (status == MagickFalse)
4218       break;
4219     if (primitive_type == UndefinedPrimitive)
4220       continue;
4221     /*
4222       Parse the primitive attributes.
4223     */
4224     i=0;
4225     j=0;
4226     for (x=0; *q != '\0'; x++)
4227     {
4228       /*
4229         Define points.
4230       */
4231       if (IsPoint(q) == MagickFalse)
4232         break;
4233       GetMagickToken(q,&q,token);
4234       point.x=StringToDouble(token,(char **) NULL);
4235       GetMagickToken(q,&q,token);
4236       if (*token == ',')
4237         GetMagickToken(q,&q,token);
4238       point.y=StringToDouble(token,(char **) NULL);
4239       GetMagickToken(q,(const char **) NULL,token);
4240       if (*token == ',')
4241         GetMagickToken(q,&q,token);
4242       primitive_info[i].primitive=primitive_type;
4243       primitive_info[i].point=point;
4244       primitive_info[i].coordinates=0;
4245       primitive_info[i].method=FloodfillMethod;
4246       i++;
4247       if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4248         continue;
4249       number_points+=6*BezierQuantum+360;
4250       primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4251         number_points,sizeof(*primitive_info));
4252       if (primitive_info == (PrimitiveInfo *) NULL)
4253         {
4254           (void) ThrowMagickException(exception,GetMagickModule(),
4255             ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4256           break;
4257         }
4258     }
4259     primitive_info[j].primitive=primitive_type;
4260     primitive_info[j].coordinates=x;
4261     primitive_info[j].method=FloodfillMethod;
4262     primitive_info[j].text=(char *) NULL;
4263     if (active)
4264       {
4265         AffineToTransform(image,&affine);
4266         active=MagickFalse;
4267       }
4268     active=MagickFalse;
4269     switch (primitive_type)
4270     {
4271       case PointPrimitive:
4272       default:
4273       {
4274         if (primitive_info[j].coordinates != 1)
4275           {
4276             status=MagickFalse;
4277             break;
4278           }
4279         break;
4280       }
4281       case LinePrimitive:
4282       {
4283         if (primitive_info[j].coordinates != 2)
4284           {
4285             status=MagickFalse;
4286             break;
4287           }
4288           (void) FormatLocaleString(message,MaxTextExtent,
4289           "  <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4290           primitive_info[j].point.x,primitive_info[j].point.y,
4291           primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4292         (void) WriteBlobString(image,message);
4293         break;
4294       }
4295       case RectanglePrimitive:
4296       {
4297         if (primitive_info[j].coordinates != 2)
4298           {
4299             status=MagickFalse;
4300             break;
4301           }
4302           (void) FormatLocaleString(message,MaxTextExtent,
4303           "  <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4304           primitive_info[j].point.x,primitive_info[j].point.y,
4305           primitive_info[j+1].point.x-primitive_info[j].point.x,
4306           primitive_info[j+1].point.y-primitive_info[j].point.y);
4307         (void) WriteBlobString(image,message);
4308         break;
4309       }
4310       case RoundRectanglePrimitive:
4311       {
4312         if (primitive_info[j].coordinates != 3)
4313           {
4314             status=MagickFalse;
4315             break;
4316           }
4317         (void) FormatLocaleString(message,MaxTextExtent,
4318           "  <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4319           "ry=\"%g\"/>\n",primitive_info[j].point.x,
4320           primitive_info[j].point.y,primitive_info[j+1].point.x-
4321           primitive_info[j].point.x,primitive_info[j+1].point.y-
4322           primitive_info[j].point.y,primitive_info[j+2].point.x,
4323           primitive_info[j+2].point.y);
4324         (void) WriteBlobString(image,message);
4325         break;
4326       }
4327       case ArcPrimitive:
4328       {
4329         if (primitive_info[j].coordinates != 3)
4330           {
4331             status=MagickFalse;
4332             break;
4333           }
4334         break;
4335       }
4336       case EllipsePrimitive:
4337       {
4338         if (primitive_info[j].coordinates != 3)
4339           {
4340             status=MagickFalse;
4341             break;
4342           }
4343           (void) FormatLocaleString(message,MaxTextExtent,
4344           "  <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
4345           primitive_info[j].point.x,primitive_info[j].point.y,
4346           primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4347         (void) WriteBlobString(image,message);
4348         break;
4349       }
4350       case CirclePrimitive:
4351       {
4352         double
4353           alpha,
4354           beta;
4355
4356         if (primitive_info[j].coordinates != 2)
4357           {
4358             status=MagickFalse;
4359             break;
4360           }
4361         alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4362         beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4363         (void) FormatLocaleString(message,MaxTextExtent,
4364           "  <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
4365           primitive_info[j].point.x,primitive_info[j].point.y,
4366           hypot(alpha,beta));
4367         (void) WriteBlobString(image,message);
4368         break;
4369       }
4370       case PolylinePrimitive:
4371       {
4372         if (primitive_info[j].coordinates < 2)
4373           {
4374             status=MagickFalse;
4375             break;
4376           }
4377         (void) CopyMagickString(message,"  <polyline points=\"",MaxTextExtent);
4378         (void) WriteBlobString(image,message);
4379         length=strlen(message);
4380         for ( ; j < i; j++)
4381         {
4382           (void) FormatLocaleString(message,MaxTextExtent,"%g,%g ",
4383             primitive_info[j].point.x,primitive_info[j].point.y);
4384           length+=strlen(message);
4385           if (length >= 80)
4386             {
4387               (void) WriteBlobString(image,"\n    ");
4388               length=strlen(message)+5;
4389             }
4390           (void) WriteBlobString(image,message);
4391         }
4392         (void) WriteBlobString(image,"\"/>\n");
4393         break;
4394       }
4395       case PolygonPrimitive:
4396       {
4397         if (primitive_info[j].coordinates < 3)
4398           {
4399             status=MagickFalse;
4400             break;
4401           }
4402         primitive_info[i]=primitive_info[j];
4403         primitive_info[i].coordinates=0;
4404         primitive_info[j].coordinates++;
4405         i++;
4406         (void) CopyMagickString(message,"  <polygon points=\"",MaxTextExtent);
4407         (void) WriteBlobString(image,message);
4408         length=strlen(message);
4409         for ( ; j < i; j++)
4410         {
4411           (void) FormatLocaleString(message,MaxTextExtent,"%g,%g ",
4412             primitive_info[j].point.x,primitive_info[j].point.y);
4413           length+=strlen(message);
4414           if (length >= 80)
4415             {
4416               (void) WriteBlobString(image,"\n    ");
4417               length=strlen(message)+5;
4418             }
4419           (void) WriteBlobString(image,message);
4420         }
4421         (void) WriteBlobString(image,"\"/>\n");
4422         break;
4423       }
4424       case BezierPrimitive:
4425       {
4426         if (primitive_info[j].coordinates < 3)
4427           {
4428             status=MagickFalse;
4429             break;
4430           }
4431         break;
4432       }
4433       case PathPrimitive:
4434       {
4435         int
4436           number_attributes;
4437
4438         GetMagickToken(q,&q,token);
4439         number_attributes=1;
4440         for (p=token; *p != '\0'; p++)
4441           if (isalpha((int) *p))
4442             number_attributes++;
4443         if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
4444           {
4445             number_points+=6*BezierQuantum*number_attributes;
4446             primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4447               number_points,sizeof(*primitive_info));
4448             if (primitive_info == (PrimitiveInfo *) NULL)
4449               {
4450                 (void) ThrowMagickException(exception,GetMagickModule(),
4451                   ResourceLimitError,"MemoryAllocationFailed","`%s'",
4452                   image->filename);
4453                 break;
4454               }
4455           }
4456         (void) WriteBlobString(image,"  <path d=\"");
4457         (void) WriteBlobString(image,token);
4458         (void) WriteBlobString(image,"\"/>\n");
4459         break;
4460       }
4461       case ColorPrimitive:
4462       case MattePrimitive:
4463       {
4464         if (primitive_info[j].coordinates != 1)
4465           {
4466             status=MagickFalse;
4467             break;
4468           }
4469         GetMagickToken(q,&q,token);
4470         if (LocaleCompare("point",token) == 0)
4471           primitive_info[j].method=PointMethod;
4472         if (LocaleCompare("replace",token) == 0)
4473           primitive_info[j].method=ReplaceMethod;
4474         if (LocaleCompare("floodfill",token) == 0)
4475           primitive_info[j].method=FloodfillMethod;
4476         if (LocaleCompare("filltoborder",token) == 0)
4477           primitive_info[j].method=FillToBorderMethod;
4478         if (LocaleCompare("reset",token) == 0)
4479           primitive_info[j].method=ResetMethod;
4480         break;
4481       }
4482       case TextPrimitive:
4483       {
4484         register char
4485           *p;
4486
4487         if (primitive_info[j].coordinates != 1)
4488           {
4489             status=MagickFalse;
4490             break;
4491           }
4492         GetMagickToken(q,&q,token);
4493         (void) FormatLocaleString(message,MaxTextExtent,
4494           "  <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
4495           primitive_info[j].point.y);
4496         (void) WriteBlobString(image,message);
4497         for (p=token; *p != '\0'; p++)
4498           switch (*p)
4499           {
4500             case '<': (void) WriteBlobString(image,"&lt;"); break;
4501             case '>': (void) WriteBlobString(image,"&gt;"); break;
4502             case '&': (void) WriteBlobString(image,"&amp;"); break;
4503             default: (void) WriteBlobByte(image,*p); break;
4504           }
4505         (void) WriteBlobString(image,"</text>\n");
4506         break;
4507       }
4508       case ImagePrimitive:
4509       {
4510         if (primitive_info[j].coordinates != 2)
4511           {
4512             status=MagickFalse;
4513             break;
4514           }
4515         GetMagickToken(q,&q,token);
4516         (void) FormatLocaleString(message,MaxTextExtent,
4517           "  <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
4518           "xlink:href=\"%s\"/>\n",primitive_info[j].point.x,
4519           primitive_info[j].point.y,primitive_info[j+1].point.x,
4520           primitive_info[j+1].point.y,token);
4521         (void) WriteBlobString(image,message);
4522         break;
4523       }
4524     }
4525     if (primitive_info == (PrimitiveInfo *) NULL)
4526       break;
4527     primitive_info[i].primitive=UndefinedPrimitive;
4528     if (status == MagickFalse)
4529       break;
4530   }
4531   (void) WriteBlobString(image,"</svg>\n");
4532   /*
4533     Relinquish resources.
4534   */
4535   token=DestroyString(token);
4536   if (primitive_info != (PrimitiveInfo *) NULL)
4537     primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4538   (void) CloseBlob(image);
4539   return(status);
4540 }