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