]> 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-2012 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    http://www.imagemagick.org/script/license.php                            %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39 \f
40 /*
41   Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/annotate.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/composite-private.h"
52 #include "MagickCore/draw.h"
53 #include "MagickCore/exception.h"
54 #include "MagickCore/exception-private.h"
55 #include "MagickCore/gem.h"
56 #include "MagickCore/image.h"
57 #include "MagickCore/image-private.h"
58 #include "MagickCore/list.h"
59 #include "MagickCore/log.h"
60 #include "MagickCore/magick.h"
61 #include "MagickCore/memory_.h"
62 #include "MagickCore/module.h"
63 #include "MagickCore/monitor.h"
64 #include "MagickCore/monitor-private.h"
65 #include "MagickCore/quantum-private.h"
66 #include "MagickCore/pixel-accessor.h"
67 #include "MagickCore/property.h"
68 #include "MagickCore/resource_.h"
69 #include "MagickCore/static.h"
70 #include "MagickCore/string_.h"
71 #include "MagickCore/string-private.h"
72 #include "MagickCore/token.h"
73 #include "MagickCore/utility.h"
74 #if defined(MAGICKCORE_XML_DELEGATE)
75 #  if defined(MAGICKCORE_WINDOWS_SUPPORT)
76 #    if defined(__MINGW32__)
77 #      define _MSC_VER
78 #    else
79 #      include <win32config.h>
80 #    endif
81 #  endif
82 #  include <libxml/parser.h>
83 #  include <libxml/xmlmemory.h>
84 #  include <libxml/parserInternals.h>
85 #  include <libxml/xmlerror.h>
86 #endif
87
88 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
89 #include "autotrace/autotrace.h"
90 #endif
91
92 #if defined(MAGICKCORE_RSVG_DELEGATE)
93 #include "librsvg/rsvg.h"
94 #if defined(MAGICKCORE_CAIRO_DELEGATE)
95 #include "librsvg/rsvg-cairo.h"
96 #endif
97 #include "librsvg/librsvg-features.h"
98 #endif
99 \f
100 /*
101   Typedef declarations.
102 */
103 typedef struct _BoundingBox
104 {
105   double
106     x,
107     y,
108     width,
109     height;
110 } BoundingBox;
111
112 typedef struct _ElementInfo
113 {
114   double
115     cx,
116     cy,
117     major,
118     minor,
119     angle;
120 } ElementInfo;
121
122 typedef struct _SVGInfo
123 {
124   FILE
125     *file;
126
127   ExceptionInfo
128     *exception;
129
130   Image
131     *image;
132
133   const ImageInfo
134     *image_info;
135
136   AffineMatrix
137     affine;
138
139   size_t
140     width,
141     height;
142
143   char
144     *size,
145     *title,
146     *comment;
147
148   int
149     n;
150
151   double
152     *scale,
153     pointsize;
154
155   ElementInfo
156     element;
157
158   SegmentInfo
159     segment;
160
161   BoundingBox
162     bounds,
163     center,
164     view_box;
165
166   PointInfo
167     radius;
168
169   char
170     *stop_color,
171     *offset,
172     *text,
173     *vertices,
174     *url;
175
176 #if defined(MAGICKCORE_XML_DELEGATE)
177   xmlParserCtxtPtr
178     parser;
179
180   xmlDocPtr
181     document;
182 #endif
183 } SVGInfo;
184 \f
185 /*
186   Forward declarations.
187 */
188 static MagickBooleanType
189   WriteSVGImage(const ImageInfo *,Image *,ExceptionInfo *);
190 \f
191 /*
192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
193 %                                                                             %
194 %                                                                             %
195 %                                                                             %
196 %   I s S V G                                                                 %
197 %                                                                             %
198 %                                                                             %
199 %                                                                             %
200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
201 %
202 %  IsSVG()() returns MagickTrue if the image format type, identified by the
203 %  magick string, is SVG.
204 %
205 %  The format of the IsSVG method is:
206 %
207 %      MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
208 %
209 %  A description of each parameter follows:
210 %
211 %    o magick: compare image format pattern against these bytes.
212 %
213 %    o length: Specifies the length of the magick string.
214 %
215 */
216 static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
217 {
218   if (length < 4)
219     return(MagickFalse);
220   if (LocaleNCompare((const char *) magick,"?xml",4) == 0)
221     return(MagickTrue);
222   return(MagickFalse);
223 }
224 \f
225 #if defined(MAGICKCORE_XML_DELEGATE)
226 /*
227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
228 %                                                                             %
229 %                                                                             %
230 %                                                                             %
231 %   R e a d S V G I m a g e                                                   %
232 %                                                                             %
233 %                                                                             %
234 %                                                                             %
235 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
236 %
237 %  ReadSVGImage() reads a Scalable Vector Gaphics file and returns it.  It
238 %  allocates the memory necessary for the new Image structure and returns a
239 %  pointer to the new image.
240 %
241 %  The format of the ReadSVGImage method is:
242 %
243 %      Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
244 %
245 %  A description of each parameter follows:
246 %
247 %    o image_info: the image info.
248 %
249 %    o exception: return any errors or warnings in this structure.
250 %
251 */
252
253 static SVGInfo *AcquireSVGInfo(void)
254 {
255   SVGInfo
256     *svg_info;
257
258   svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info));
259   if (svg_info == (SVGInfo *) NULL)
260     return((SVGInfo *) NULL);
261   (void) ResetMagickMemory(svg_info,0,sizeof(*svg_info));
262   svg_info->text=AcquireString("");
263   svg_info->scale=(double *) AcquireMagickMemory(sizeof(*svg_info->scale));
264   if (svg_info->scale == (double *) NULL)
265     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
266   GetAffineMatrix(&svg_info->affine);
267   svg_info->scale[0]=ExpandAffine(&svg_info->affine);
268   return(svg_info);
269 }
270
271 static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
272 {
273   if (svg_info->text != (char *) NULL)
274     svg_info->text=DestroyString(svg_info->text);
275   if (svg_info->scale != (double *) NULL)
276     svg_info->scale=(double *) (svg_info->scale);
277   if (svg_info->title != (char *) NULL)
278     svg_info->title=DestroyString(svg_info->title);
279   if (svg_info->comment != (char *) NULL)
280     svg_info->comment=DestroyString(svg_info->comment);
281   return((SVGInfo *) RelinquishMagickMemory(svg_info));
282 }
283
284 static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
285   const char *string)
286 {
287   char
288     token[MaxTextExtent];
289
290   const char
291     *p;
292
293   double
294     value;
295
296   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
297   assert(string != (const char *) NULL);
298   p=(const char *) string;
299   GetMagickToken(p,&p,token);
300   value=StringToDouble(token,(char **) NULL);
301   if (strchr(token,'%') != (char *) NULL)
302     {
303       double
304         alpha,
305         beta;
306
307       if (type > 0)
308         {
309           if (svg_info->view_box.width == 0.0)
310             return(1000.0);
311           return(svg_info->view_box.width*value/100.0);
312         }
313       if (type < 0)
314         {
315           if (svg_info->view_box.height == 0.0)
316             return(1000.0);
317           return(svg_info->view_box.height*value/100.0);
318         }
319       alpha=value-svg_info->view_box.width;
320       beta=value-svg_info->view_box.height;
321       return(hypot(alpha,beta)/sqrt(2.0)/100.0);
322     }
323   GetMagickToken(p,&p,token);
324   if (LocaleNCompare(token,"cm",2) == 0)
325     return(DefaultResolution*svg_info->scale[0]/2.54*value);
326   if (LocaleNCompare(token,"em",2) == 0)
327     return(svg_info->pointsize*value);
328   if (LocaleNCompare(token,"ex",2) == 0)
329     return(svg_info->pointsize*value/2.0);
330   if (LocaleNCompare(token,"in",2) == 0)
331     return(DefaultResolution*svg_info->scale[0]*value);
332   if (LocaleNCompare(token,"mm",2) == 0)
333     return(DefaultResolution*svg_info->scale[0]/25.4*value);
334   if (LocaleNCompare(token,"pc",2) == 0)
335     return(DefaultResolution*svg_info->scale[0]/6.0*value);
336   if (LocaleNCompare(token,"pt",2) == 0)
337     return(svg_info->scale[0]*value);
338   if (LocaleNCompare(token,"px",2) == 0)
339     return(value);
340   return(value);
341 }
342
343 static void StripStyleTokens(char *message)
344 {
345   register char
346     *p,
347     *q;
348
349   size_t
350     length;
351
352   assert(message != (char *) NULL);
353   if (*message == '\0')
354     return;
355   length=strlen(message);
356   p=message;
357   while (isspace((int) ((unsigned char) *p)) != 0)
358     p++;
359   q=message+length-1;
360   while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
361     q--;
362   (void) CopyMagickMemory(message,p,(size_t) (q-p+1));
363   message[q-p+1]='\0';
364   StripString(message);
365 }
366
367 static char **GetStyleTokens(void *context,const char *style,int *number_tokens)
368 {
369   char
370     *text,
371     **tokens;
372
373   register ssize_t
374     i;
375
376   SVGInfo
377     *svg_info;
378
379   svg_info=(SVGInfo *) context;
380   (void) svg_info;
381   *number_tokens=0;
382   if (style == (const char *) NULL)
383     return((char **) NULL);
384   text=AcquireString(style);
385   (void) SubstituteString(&text,":","\n");
386   (void) SubstituteString(&text,";","\n");
387   tokens=StringToList(text);
388   text=DestroyString(text);
389   for (i=0; tokens[i] != (char *) NULL; i++)
390     StripStyleTokens(tokens[i]);
391   *number_tokens=i;
392   return(tokens);
393 }
394
395 static char **GetTransformTokens(void *context,const char *text,
396   int *number_tokens)
397 {
398   char
399     **tokens;
400
401   register const char
402     *p,
403     *q;
404
405   register ssize_t
406     i;
407
408   SVGInfo
409     *svg_info;
410
411   svg_info=(SVGInfo *) context;
412   *number_tokens=0;
413   if (text == (const char *) NULL)
414     return((char **) NULL);
415   /*
416     Determine the number of arguments.
417   */
418   for (p=text; *p != '\0'; p++)
419   {
420     if (*p == '(')
421       (*number_tokens)+=2;
422   }
423   tokens=(char **) AcquireQuantumMemory(*number_tokens+2UL,sizeof(*tokens));
424   if (tokens == (char **) NULL)
425     {
426       (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
427         ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
428       return((char **) NULL);
429     }
430   /*
431     Convert string to an ASCII list.
432   */
433   i=0;
434   p=text;
435   for (q=p; *q != '\0'; q++)
436   {
437     if ((*q != '(') && (*q != ')') && (*q != '\0'))
438       continue;
439     tokens[i]=AcquireString(p);
440     (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
441     StripString(tokens[i++]);
442     p=q+1;
443   }
444   tokens[i]=AcquireString(p);
445   (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
446   StripString(tokens[i++]);
447   tokens[i]=(char *) NULL;
448   return(tokens);
449 }
450
451 #if defined(__cplusplus) || defined(c_plusplus)
452 extern "C" {
453 #endif
454
455 static int SVGIsStandalone(void *context)
456 {
457   SVGInfo
458     *svg_info;
459
460   /*
461     Is this document tagged standalone?
462   */
463   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.SVGIsStandalone()");
464   svg_info=(SVGInfo *) context;
465   return(svg_info->document->standalone == 1);
466 }
467
468 static int SVGHasInternalSubset(void *context)
469 {
470   SVGInfo
471     *svg_info;
472
473   /*
474     Does this document has an internal subset?
475   */
476   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
477     "  SAX.SVGHasInternalSubset()");
478   svg_info=(SVGInfo *) context;
479   return(svg_info->document->intSubset != NULL);
480 }
481
482 static int SVGHasExternalSubset(void *context)
483 {
484   SVGInfo
485     *svg_info;
486
487   /*
488     Does this document has an external subset?
489   */
490   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
491     "  SAX.SVGHasExternalSubset()");
492   svg_info=(SVGInfo *) context;
493   return(svg_info->document->extSubset != NULL);
494 }
495
496 static void SVGInternalSubset(void *context,const xmlChar *name,
497   const xmlChar *external_id,const xmlChar *system_id)
498 {
499   SVGInfo
500     *svg_info;
501
502   /*
503     Does this document has an internal subset?
504   */
505   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
506     "  SAX.internalSubset(%s, %s, %s)",(const char *) name,
507     (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
508     (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
509   svg_info=(SVGInfo *) context;
510   (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id);
511 }
512
513 static xmlParserInputPtr SVGResolveEntity(void *context,
514   const xmlChar *public_id,const xmlChar *system_id)
515 {
516   SVGInfo
517     *svg_info;
518
519   xmlParserInputPtr
520     stream;
521
522   /*
523     Special entity resolver, better left to the parser, it has more
524     context than the application layer.  The default behaviour is to
525     not resolve the entities, in that case the ENTITY_REF nodes are
526     built in the structure (and the parameter values).
527   */
528   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
529     "  SAX.resolveEntity(%s, %s)",
530     (public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"),
531     (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
532   svg_info=(SVGInfo *) context;
533   stream=xmlLoadExternalEntity((const char *) system_id,(const char *)
534     public_id,svg_info->parser);
535   return(stream);
536 }
537
538 static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
539 {
540   SVGInfo
541     *svg_info;
542
543   /*
544     Get an entity by name.
545   */
546   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.SVGGetEntity(%s)",
547     name);
548   svg_info=(SVGInfo *) context;
549   return(xmlGetDocEntity(svg_info->document,name));
550 }
551
552 static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
553 {
554   SVGInfo
555     *svg_info;
556
557   /*
558     Get a parameter entity by name.
559   */
560   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
561     "  SAX.getParameterEntity(%s)",name);
562   svg_info=(SVGInfo *) context;
563   return(xmlGetParameterEntity(svg_info->document,name));
564 }
565
566 static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
567   const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
568 {
569   SVGInfo
570     *svg_info;
571
572   /*
573     An entity definition has been parsed.
574   */
575   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
576     "  SAX.entityDecl(%s, %d, %s, %s, %s)",name,type,
577     public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
578     system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content);
579   svg_info=(SVGInfo *) context;
580   if (svg_info->parser->inSubset == 1)
581     (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id,
582       content);
583   else
584     if (svg_info->parser->inSubset == 2)
585       (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
586         content);
587 }
588
589 static void SVGAttributeDeclaration(void *context,const xmlChar *element,
590   const xmlChar *name,int type,int value,const xmlChar *default_value,
591   xmlEnumerationPtr tree)
592 {
593   SVGInfo
594     *svg_info;
595
596   xmlChar
597     *fullname,
598     *prefix;
599
600   xmlParserCtxtPtr
601     parser;
602
603   /*
604     An attribute definition has been parsed.
605   */
606   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
607     "  SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value,
608     default_value);
609   svg_info=(SVGInfo *) context;
610   fullname=(xmlChar *) NULL;
611   prefix=(xmlChar *) NULL;
612   parser=svg_info->parser;
613   fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix);
614   if (parser->inSubset == 1)
615     (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset,
616       element,fullname,prefix,(xmlAttributeType) type,
617       (xmlAttributeDefault) value,default_value,tree);
618   else
619     if (parser->inSubset == 2)
620       (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset,
621         element,fullname,prefix,(xmlAttributeType) type,
622         (xmlAttributeDefault) value,default_value,tree);
623   if (prefix != (xmlChar *) NULL)
624     xmlFree(prefix);
625   if (fullname != (xmlChar *) NULL)
626     xmlFree(fullname);
627 }
628
629 static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
630   xmlElementContentPtr content)
631 {
632   SVGInfo
633     *svg_info;
634
635   xmlParserCtxtPtr
636     parser;
637
638   /*
639     An element definition has been parsed.
640   */
641   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
642     "  SAX.elementDecl(%s, %d, ...)",name,type);
643   svg_info=(SVGInfo *) context;
644   parser=svg_info->parser;
645   if (parser->inSubset == 1)
646     (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset,
647       name,(xmlElementTypeVal) type,content);
648   else
649     if (parser->inSubset == 2)
650       (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
651         name,(xmlElementTypeVal) type,content);
652 }
653
654 static void SVGNotationDeclaration(void *context,const xmlChar *name,
655   const xmlChar *public_id,const xmlChar *system_id)
656 {
657   SVGInfo
658     *svg_info;
659
660   xmlParserCtxtPtr
661     parser;
662
663   /*
664     What to do when a notation declaration has been parsed.
665   */
666   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
667     "  SAX.notationDecl(%s, %s, %s)",name,
668     public_id != (const xmlChar *) NULL ? (const char *) public_id : "none",
669     system_id != (const xmlChar *) NULL ? (const char *) system_id : "none");
670   svg_info=(SVGInfo *) context;
671   parser=svg_info->parser;
672   if (parser->inSubset == 1)
673     (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
674       name,public_id,system_id);
675   else
676     if (parser->inSubset == 2)
677       (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
678         name,public_id,system_id);
679 }
680
681 static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
682   const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
683 {
684   SVGInfo
685     *svg_info;
686
687   /*
688     What to do when an unparsed entity declaration is parsed.
689   */
690   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
691     "  SAX.unparsedEntityDecl(%s, %s, %s, %s)",name,
692     public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
693     system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation);
694   svg_info=(SVGInfo *) context;
695   (void) xmlAddDocEntity(svg_info->document,name,
696     XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
697
698 }
699
700 static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
701 {
702   SVGInfo
703     *svg_info;
704
705   /*
706     Receive the document locator at startup, actually xmlDefaultSAXLocator.
707   */
708   (void) location;
709   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
710     "  SAX.setDocumentLocator()");
711   svg_info=(SVGInfo *) context;
712   (void) svg_info;
713 }
714
715 static void SVGStartDocument(void *context)
716 {
717   SVGInfo
718     *svg_info;
719
720   xmlParserCtxtPtr
721     parser;
722
723   /*
724     Called when the document start being processed.
725   */
726   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.startDocument()");
727   svg_info=(SVGInfo *) context;
728   GetExceptionInfo(svg_info->exception);
729   parser=svg_info->parser;
730   svg_info->document=xmlNewDoc(parser->version);
731   if (svg_info->document == (xmlDocPtr) NULL)
732     return;
733   if (parser->encoding == NULL)
734     svg_info->document->encoding=(const xmlChar *) NULL;
735   else
736     svg_info->document->encoding=xmlStrdup(parser->encoding);
737   svg_info->document->standalone=parser->standalone;
738 }
739
740 static void SVGEndDocument(void *context)
741 {
742   SVGInfo
743     *svg_info;
744
745   /*
746     Called when the document end has been detected.
747   */
748   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.endDocument()");
749   svg_info=(SVGInfo *) context;
750   if (svg_info->offset != (char *) NULL)
751     svg_info->offset=DestroyString(svg_info->offset);
752   if (svg_info->stop_color != (char *) NULL)
753     svg_info->stop_color=DestroyString(svg_info->stop_color);
754   if (svg_info->scale != (double *) NULL)
755     svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
756   if (svg_info->text != (char *) NULL)
757     svg_info->text=DestroyString(svg_info->text);
758   if (svg_info->vertices != (char *) NULL)
759     svg_info->vertices=DestroyString(svg_info->vertices);
760   if (svg_info->url != (char *) NULL)
761     svg_info->url=DestroyString(svg_info->url);
762 #if defined(MAGICKCORE_XML_DELEGATE)
763   if (svg_info->document != (xmlDocPtr) NULL)
764     {
765       xmlFreeDoc(svg_info->document);
766       svg_info->document=(xmlDocPtr) NULL;
767     }
768 #endif
769 }
770
771 static void SVGStartElement(void *context,const xmlChar *name,
772   const xmlChar **attributes)
773 {
774   char
775     *color,
776     id[MaxTextExtent],
777     token[MaxTextExtent],
778     **tokens,
779     *units;
780
781   const char
782     *keyword,
783     *p,
784     *value;
785
786   int
787     number_tokens;
788
789   SVGInfo
790     *svg_info;
791
792   register ssize_t
793     i,
794     j;
795
796   /*
797     Called when an opening tag has been processed.
798   */
799   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.startElement(%s",
800     name);
801   svg_info=(SVGInfo *) context;
802   svg_info->n++;
803   svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale,
804     svg_info->n+1UL,sizeof(*svg_info->scale));
805   if (svg_info->scale == (double *) NULL)
806     {
807       (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
808         ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
809       return;
810     }
811   svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
812   color=AcquireString("none");
813   units=AcquireString("userSpaceOnUse");
814   value=(const char *) NULL;
815   if (attributes != (const xmlChar **) NULL)
816     for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
817     {
818       keyword=(const char *) attributes[i];
819       value=(const char *) attributes[i+1];
820       switch (*keyword)
821       {
822         case 'C':
823         case 'c':
824         {
825           if (LocaleCompare(keyword,"cx") == 0)
826             {
827               svg_info->element.cx=
828                 GetUserSpaceCoordinateValue(svg_info,1,value);
829               break;
830             }
831           if (LocaleCompare(keyword,"cy") == 0)
832             {
833               svg_info->element.cy=
834                 GetUserSpaceCoordinateValue(svg_info,-1,value);
835               break;
836             }
837           break;
838         }
839         case 'F':
840         case 'f':
841         {
842           if (LocaleCompare(keyword,"fx") == 0)
843             {
844               svg_info->element.major=
845                 GetUserSpaceCoordinateValue(svg_info,1,value);
846               break;
847             }
848           if (LocaleCompare(keyword,"fy") == 0)
849             {
850               svg_info->element.minor=
851                 GetUserSpaceCoordinateValue(svg_info,-1,value);
852               break;
853             }
854           break;
855         }
856         case 'H':
857         case 'h':
858         {
859           if (LocaleCompare(keyword,"height") == 0)
860             {
861               svg_info->bounds.height=
862                 GetUserSpaceCoordinateValue(svg_info,-1,value);
863               break;
864             }
865           break;
866         }
867         case 'I':
868         case 'i':
869         {
870           if (LocaleCompare(keyword,"id") == 0)
871             {
872               (void) CopyMagickString(id,value,MaxTextExtent);
873               break;
874             }
875           break;
876         }
877         case 'R':
878         case 'r':
879         {
880           if (LocaleCompare(keyword,"r") == 0)
881             {
882               svg_info->element.angle=
883                 GetUserSpaceCoordinateValue(svg_info,0,value);
884               break;
885             }
886           break;
887         }
888         case 'W':
889         case 'w':
890         {
891           if (LocaleCompare(keyword,"width") == 0)
892             {
893               svg_info->bounds.width=
894                 GetUserSpaceCoordinateValue(svg_info,1,value);
895               break;
896             }
897           break;
898         }
899         case 'X':
900         case 'x':
901         {
902           if (LocaleCompare(keyword,"x") == 0)
903             {
904               svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value)-
905                 svg_info->center.x;
906               break;
907             }
908           if (LocaleCompare(keyword,"x1") == 0)
909             {
910               svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
911                 value);
912               break;
913             }
914           if (LocaleCompare(keyword,"x2") == 0)
915             {
916               svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
917                 value);
918               break;
919             }
920           break;
921         }
922         case 'Y':
923         case 'y':
924         {
925           if (LocaleCompare(keyword,"y") == 0)
926             {
927               svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value)-
928                 svg_info->center.y;
929               break;
930             }
931           if (LocaleCompare(keyword,"y1") == 0)
932             {
933               svg_info->segment.y1=
934                 GetUserSpaceCoordinateValue(svg_info,-1,value);
935               break;
936             }
937           if (LocaleCompare(keyword,"y2") == 0)
938             {
939               svg_info->segment.y2=
940                 GetUserSpaceCoordinateValue(svg_info,-1,value);
941               break;
942             }
943           break;
944         }
945         default:
946           break;
947       }
948     }
949   if (strchr((char *) name,':') != (char *) NULL)
950     {
951       /*
952         Skip over namespace.
953       */
954       for ( ; *name != ':'; name++) ;
955       name++;
956     }
957   switch (*name)
958   {
959     case 'C':
960     case 'c':
961     {
962       if (LocaleCompare((const char *) name,"circle") == 0)
963         {
964           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
965           break;
966         }
967       if (LocaleCompare((const char *) name,"clipPath") == 0)
968         {
969           (void) FormatLocaleFile(svg_info->file,"push clip-path '%s'\n",id);
970           break;
971         }
972       break;
973     }
974     case 'D':
975     case 'd':
976     {
977       if (LocaleCompare((const char *) name,"defs") == 0)
978         {
979           (void) FormatLocaleFile(svg_info->file,"push defs\n");
980           break;
981         }
982       break;
983     }
984     case 'E':
985     case 'e':
986     {
987       if (LocaleCompare((const char *) name,"ellipse") == 0)
988         {
989           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
990           break;
991         }
992       break;
993     }
994     case 'G':
995     case 'g':
996     {
997       if (LocaleCompare((const char *) name,"g") == 0)
998         {
999           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1000           break;
1001         }
1002       break;
1003     }
1004     case 'I':
1005     case 'i':
1006     {
1007       if (LocaleCompare((const char *) name,"image") == 0)
1008         {
1009           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1010           break;
1011         }
1012       break;
1013     }
1014     case 'L':
1015     case 'l':
1016     {
1017       if (LocaleCompare((const char *) name,"line") == 0)
1018         {
1019           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1020           break;
1021         }
1022       if (LocaleCompare((const char *) name,"linearGradient") == 0)
1023         {
1024           (void) FormatLocaleFile(svg_info->file,
1025             "push gradient '%s' linear %g,%g %g,%g\n",id,
1026             svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
1027             svg_info->segment.y2);
1028           break;
1029         }
1030       break;
1031     }
1032     case 'P':
1033     case 'p':
1034     {
1035       if (LocaleCompare((const char *) name,"path") == 0)
1036         {
1037           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1038           break;
1039         }
1040       if (LocaleCompare((const char *) name,"pattern") == 0)
1041         {
1042           (void) FormatLocaleFile(svg_info->file,
1043             "push pattern '%s' %g,%g %g,%g\n",id,
1044             svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
1045             svg_info->bounds.height);
1046           break;
1047         }
1048       if (LocaleCompare((const char *) name,"polygon") == 0)
1049         {
1050           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1051           break;
1052         }
1053       if (LocaleCompare((const char *) name,"polyline") == 0)
1054         {
1055           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1056           break;
1057         }
1058       break;
1059     }
1060     case 'R':
1061     case 'r':
1062     {
1063       if (LocaleCompare((const char *) name,"radialGradient") == 0)
1064         {
1065           (void) FormatLocaleFile(svg_info->file,
1066             "push gradient '%s' radial %g,%g %g,%g %g\n",
1067             id,svg_info->element.cx,svg_info->element.cy,
1068             svg_info->element.major,svg_info->element.minor,
1069             svg_info->element.angle);
1070           break;
1071         }
1072       if (LocaleCompare((const char *) name,"rect") == 0)
1073         {
1074           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1075           break;
1076         }
1077       break;
1078     }
1079     case 'S':
1080     case 's':
1081     {
1082       if (LocaleCompare((const char *) name,"svg") == 0)
1083         {
1084           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1085           break;
1086         }
1087       break;
1088     }
1089     case 'T':
1090     case 't':
1091     {
1092       if (LocaleCompare((const char *) name,"text") == 0)
1093         {
1094           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1095           svg_info->bounds.x=0.0;
1096           svg_info->bounds.y=0.0;
1097           svg_info->bounds.width=0.0;
1098           svg_info->bounds.height=0.0;
1099           break;
1100         }
1101       if (LocaleCompare((const char *) name,"tspan") == 0)
1102         {
1103           if (*svg_info->text != '\0')
1104             {
1105               DrawInfo
1106                 *draw_info;
1107
1108               TypeMetric
1109                 metrics;
1110
1111               char
1112                 *text;
1113
1114               text=EscapeString(svg_info->text,'\'');
1115               (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
1116                 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1117                 svg_info->center.y,text);
1118               text=DestroyString(text);
1119               draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
1120               draw_info->pointsize=svg_info->pointsize;
1121               draw_info->text=AcquireString(svg_info->text);
1122               (void) ConcatenateString(&draw_info->text," ");
1123               (void) GetTypeMetrics(svg_info->image,draw_info,
1124                 &metrics,svg_info->exception);
1125               svg_info->bounds.x+=metrics.width;
1126               draw_info=DestroyDrawInfo(draw_info);
1127               *svg_info->text='\0';
1128             }
1129           (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1130           break;
1131         }
1132       break;
1133     }
1134     default:
1135       break;
1136   }
1137   if (attributes != (const xmlChar **) NULL)
1138     for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1139     {
1140       keyword=(const char *) attributes[i];
1141       value=(const char *) attributes[i+1];
1142       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1143         "    %s = %s",keyword,value);
1144       switch (*keyword)
1145       {
1146         case 'A':
1147         case 'a':
1148         {
1149           if (LocaleCompare(keyword,"angle") == 0)
1150             {
1151               (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1152                 GetUserSpaceCoordinateValue(svg_info,0,value));
1153               break;
1154             }
1155           break;
1156         }
1157         case 'C':
1158         case 'c':
1159         {
1160           if (LocaleCompare(keyword,"clip-path") == 0)
1161             {
1162               (void) FormatLocaleFile(svg_info->file,"clip-path '%s'\n",value);
1163               break;
1164             }
1165           if (LocaleCompare(keyword,"clip-rule") == 0)
1166             {
1167               (void) FormatLocaleFile(svg_info->file,"clip-rule '%s'\n",value);
1168               break;
1169             }
1170           if (LocaleCompare(keyword,"clipPathUnits") == 0)
1171             {
1172               (void) CloneString(&units,value);
1173               (void) FormatLocaleFile(svg_info->file,"clip-units '%s'\n",value);
1174               break;
1175             }
1176           if (LocaleCompare(keyword,"color") == 0)
1177             {
1178               (void) CloneString(&color,value);
1179               break;
1180             }
1181           if (LocaleCompare(keyword,"cx") == 0)
1182             {
1183               svg_info->element.cx=
1184                 GetUserSpaceCoordinateValue(svg_info,1,value);
1185               break;
1186             }
1187           if (LocaleCompare(keyword,"cy") == 0)
1188             {
1189               svg_info->element.cy=
1190                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1191               break;
1192             }
1193           break;
1194         }
1195         case 'D':
1196         case 'd':
1197         {
1198           if (LocaleCompare(keyword,"d") == 0)
1199             {
1200               (void) CloneString(&svg_info->vertices,value);
1201               break;
1202             }
1203           if (LocaleCompare(keyword,"dx") == 0)
1204             {
1205               svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
1206               break;
1207             }
1208           if (LocaleCompare(keyword,"dy") == 0)
1209             {
1210               svg_info->bounds.y+=
1211                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1212               break;
1213             }
1214           break;
1215         }
1216         case 'F':
1217         case 'f':
1218         {
1219           if (LocaleCompare(keyword,"fill") == 0)
1220             {
1221               if (LocaleCompare(value,"currentColor") == 0)
1222                 {
1223                   (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",color);
1224                   break;
1225                 }
1226               (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
1227               break;
1228             }
1229           if (LocaleCompare(keyword,"fillcolor") == 0)
1230             {
1231               (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
1232               break;
1233             }
1234           if (LocaleCompare(keyword,"fill-rule") == 0)
1235             {
1236               (void) FormatLocaleFile(svg_info->file,"fill-rule '%s'\n",value);
1237               break;
1238             }
1239           if (LocaleCompare(keyword,"fill-alpha") == 0)
1240             {
1241               (void) FormatLocaleFile(svg_info->file,"fill-alpha '%s'\n",
1242                 value);
1243               break;
1244             }
1245           if (LocaleCompare(keyword,"font-family") == 0)
1246             {
1247               (void) FormatLocaleFile(svg_info->file,"font-family '%s'\n",
1248                 value);
1249               break;
1250             }
1251           if (LocaleCompare(keyword,"font-stretch") == 0)
1252             {
1253               (void) FormatLocaleFile(svg_info->file,"font-stretch '%s'\n",
1254                 value);
1255               break;
1256             }
1257           if (LocaleCompare(keyword,"font-style") == 0)
1258             {
1259               (void) FormatLocaleFile(svg_info->file,"font-style '%s'\n",value);
1260               break;
1261             }
1262           if (LocaleCompare(keyword,"font-size") == 0)
1263             {
1264               svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
1265               (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1266                 svg_info->pointsize);
1267               break;
1268             }
1269           if (LocaleCompare(keyword,"font-weight") == 0)
1270             {
1271               (void) FormatLocaleFile(svg_info->file,"font-weight '%s'\n",
1272                 value);
1273               break;
1274             }
1275           break;
1276         }
1277         case 'G':
1278         case 'g':
1279         {
1280           if (LocaleCompare(keyword,"gradientTransform") == 0)
1281             {
1282               AffineMatrix
1283                 affine,
1284                 current,
1285                 transform;
1286
1287               GetAffineMatrix(&transform);
1288               (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
1289               tokens=GetTransformTokens(context,value,&number_tokens);
1290               for (j=0; j < (number_tokens-1); j+=2)
1291               {
1292                 keyword=(char *) tokens[j];
1293                 if (keyword == (char *) NULL)
1294                   continue;
1295                 value=(char *) tokens[j+1];
1296                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1297                   "    %s: %s",keyword,value);
1298                 current=transform;
1299                 GetAffineMatrix(&affine);
1300                 switch (*keyword)
1301                 {
1302                   case 'M':
1303                   case 'm':
1304                   {
1305                     if (LocaleCompare(keyword,"matrix") == 0)
1306                       {
1307                         p=(const char *) value;
1308                         GetMagickToken(p,&p,token);
1309                         affine.sx=StringToDouble(value,(char **) NULL);
1310                         GetMagickToken(p,&p,token);
1311                         if (*token == ',')
1312                           GetMagickToken(p,&p,token);
1313                         affine.rx=StringToDouble(token,(char **) NULL);
1314                         GetMagickToken(p,&p,token);
1315                         if (*token == ',')
1316                           GetMagickToken(p,&p,token);
1317                         affine.ry=StringToDouble(token,(char **) NULL);
1318                         GetMagickToken(p,&p,token);
1319                         if (*token == ',')
1320                           GetMagickToken(p,&p,token);
1321                         affine.sy=StringToDouble(token,(char **) NULL);
1322                         GetMagickToken(p,&p,token);
1323                         if (*token == ',')
1324                           GetMagickToken(p,&p,token);
1325                         affine.tx=StringToDouble(token,(char **) NULL);
1326                         GetMagickToken(p,&p,token);
1327                         if (*token == ',')
1328                           GetMagickToken(p,&p,token);
1329                         affine.ty=StringToDouble(token,(char **) NULL);
1330                         break;
1331                       }
1332                     break;
1333                   }
1334                   case 'R':
1335                   case 'r':
1336                   {
1337                     if (LocaleCompare(keyword,"rotate") == 0)
1338                       {
1339                         double
1340                           angle;
1341
1342                         angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1343                         affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1344                         affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1345                         affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1346                         affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1347                         break;
1348                       }
1349                     break;
1350                   }
1351                   case 'S':
1352                   case 's':
1353                   {
1354                     if (LocaleCompare(keyword,"scale") == 0)
1355                       {
1356                         for (p=(const char *) value; *p != '\0'; p++)
1357                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
1358                               (*p == ','))
1359                             break;
1360                         affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1361                         affine.sy=affine.sx;
1362                         if (*p != '\0')
1363                           affine.sy=
1364                             GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1365                         svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1366                         break;
1367                       }
1368                     if (LocaleCompare(keyword,"skewX") == 0)
1369                       {
1370                         affine.sx=svg_info->affine.sx;
1371                         affine.ry=tan(DegreesToRadians(fmod(
1372                           GetUserSpaceCoordinateValue(svg_info,1,value),
1373                           360.0)));
1374                         affine.sy=svg_info->affine.sy;
1375                         break;
1376                       }
1377                     if (LocaleCompare(keyword,"skewY") == 0)
1378                       {
1379                         affine.sx=svg_info->affine.sx;
1380                         affine.rx=tan(DegreesToRadians(fmod(
1381                           GetUserSpaceCoordinateValue(svg_info,-1,value),
1382                           360.0)));
1383                         affine.sy=svg_info->affine.sy;
1384                         break;
1385                       }
1386                     break;
1387                   }
1388                   case 'T':
1389                   case 't':
1390                   {
1391                     if (LocaleCompare(keyword,"translate") == 0)
1392                       {
1393                         for (p=(const char *) value; *p != '\0'; p++)
1394                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
1395                               (*p == ','))
1396                             break;
1397                         affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1398                         affine.ty=affine.tx;
1399                         if (*p != '\0')
1400                           affine.ty=
1401                             GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1402                         break;
1403                       }
1404                     break;
1405                   }
1406                   default:
1407                     break;
1408                 }
1409                 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1410                 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1411                 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1412                 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1413                 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1414                   current.tx;
1415                 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1416                   current.ty;
1417               }
1418               (void) FormatLocaleFile(svg_info->file,
1419                 "affine %g %g %g %g %g %g\n",transform.sx,
1420                 transform.rx,transform.ry,transform.sy,transform.tx,
1421                 transform.ty);
1422               for (j=0; tokens[j] != (char *) NULL; j++)
1423                 tokens[j]=DestroyString(tokens[j]);
1424               tokens=(char **) RelinquishMagickMemory(tokens);
1425               break;
1426             }
1427           if (LocaleCompare(keyword,"gradientUnits") == 0)
1428             {
1429               (void) CloneString(&units,value);
1430               (void) FormatLocaleFile(svg_info->file,"gradient-units '%s'\n",
1431                 value);
1432               break;
1433             }
1434           break;
1435         }
1436         case 'H':
1437         case 'h':
1438         {
1439           if (LocaleCompare(keyword,"height") == 0)
1440             {
1441               svg_info->bounds.height=
1442                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1443               break;
1444             }
1445           if (LocaleCompare(keyword,"href") == 0)
1446             {
1447               (void) CloneString(&svg_info->url,value);
1448               break;
1449             }
1450           break;
1451         }
1452         case 'M':
1453         case 'm':
1454         {
1455           if (LocaleCompare(keyword,"major") == 0)
1456             {
1457               svg_info->element.major=
1458                 GetUserSpaceCoordinateValue(svg_info,1,value);
1459               break;
1460             }
1461           if (LocaleCompare(keyword,"minor") == 0)
1462             {
1463               svg_info->element.minor=
1464                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1465               break;
1466             }
1467           break;
1468         }
1469         case 'O':
1470         case 'o':
1471         {
1472           if (LocaleCompare(keyword,"offset") == 0)
1473             {
1474               (void) CloneString(&svg_info->offset,value);
1475               break;
1476             }
1477           if (LocaleCompare(keyword,"opacity") == 0)
1478             {
1479               (void) FormatLocaleFile(svg_info->file,"opacity '%s'\n",value);
1480               break;
1481             }
1482           break;
1483         }
1484         case 'P':
1485         case 'p':
1486         {
1487           if (LocaleCompare(keyword,"path") == 0)
1488             {
1489               (void) CloneString(&svg_info->url,value);
1490               break;
1491             }
1492           if (LocaleCompare(keyword,"points") == 0)
1493             {
1494               (void) CloneString(&svg_info->vertices,value);
1495               break;
1496             }
1497           break;
1498         }
1499         case 'R':
1500         case 'r':
1501         {
1502           if (LocaleCompare(keyword,"r") == 0)
1503             {
1504               svg_info->element.major=
1505                 GetUserSpaceCoordinateValue(svg_info,1,value);
1506               svg_info->element.minor=
1507                 GetUserSpaceCoordinateValue(svg_info,-1,value);
1508               break;
1509             }
1510           if (LocaleCompare(keyword,"rotate") == 0)
1511             {
1512               double
1513                 angle;
1514
1515               angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1516               (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1517                 svg_info->bounds.x,svg_info->bounds.y);
1518               svg_info->bounds.x=0;
1519               svg_info->bounds.y=0;
1520               (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
1521               break;
1522             }
1523           if (LocaleCompare(keyword,"rx") == 0)
1524             {
1525               if (LocaleCompare((const char *) name,"ellipse") == 0)
1526                 svg_info->element.major=
1527                   GetUserSpaceCoordinateValue(svg_info,1,value);
1528               else
1529                 svg_info->radius.x=
1530                   GetUserSpaceCoordinateValue(svg_info,1,value);
1531               break;
1532             }
1533           if (LocaleCompare(keyword,"ry") == 0)
1534             {
1535               if (LocaleCompare((const char *) name,"ellipse") == 0)
1536                 svg_info->element.minor=
1537                   GetUserSpaceCoordinateValue(svg_info,-1,value);
1538               else
1539                 svg_info->radius.y=
1540                   GetUserSpaceCoordinateValue(svg_info,-1,value);
1541               break;
1542             }
1543           break;
1544         }
1545         case 'S':
1546         case 's':
1547         {
1548           if (LocaleCompare(keyword,"stop-color") == 0)
1549             {
1550               (void) CloneString(&svg_info->stop_color,value);
1551               break;
1552             }
1553           if (LocaleCompare(keyword,"stroke") == 0)
1554             {
1555               if (LocaleCompare(value,"currentColor") == 0)
1556                 {
1557                   (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",color);
1558                   break;
1559                 }
1560               (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",value);
1561               break;
1562             }
1563           if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1564             {
1565               (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
1566                 LocaleCompare(value,"true") == 0);
1567               break;
1568             }
1569           if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1570             {
1571               (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1572                 value);
1573               break;
1574             }
1575           if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1576             {
1577               (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %s\n",
1578                 value);
1579               break;
1580             }
1581           if (LocaleCompare(keyword,"stroke-linecap") == 0)
1582             {
1583               (void) FormatLocaleFile(svg_info->file,"stroke-linecap '%s'\n",
1584                 value);
1585               break;
1586             }
1587           if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1588             {
1589               (void) FormatLocaleFile(svg_info->file,"stroke-linejoin '%s'\n",
1590                 value);
1591               break;
1592             }
1593           if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1594             {
1595               (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit '%s'\n",
1596                 value);
1597               break;
1598             }
1599           if (LocaleCompare(keyword,"stroke-opacity") == 0)
1600             {
1601               (void) FormatLocaleFile(svg_info->file,"stroke-opacity '%s'\n",
1602                 value);
1603               break;
1604             }
1605           if (LocaleCompare(keyword,"stroke-width") == 0)
1606             {
1607               (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
1608                 GetUserSpaceCoordinateValue(svg_info,1,value));
1609               break;
1610             }
1611           if (LocaleCompare(keyword,"style") == 0)
1612             {
1613               (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
1614               tokens=GetStyleTokens(context,value,&number_tokens);
1615               for (j=0; j < (number_tokens-1); j+=2)
1616               {
1617                 keyword=(char *) tokens[j];
1618                 value=(char *) tokens[j+1];
1619                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1620                   "    %s: %s",keyword,value);
1621                 switch (*keyword)
1622                 {
1623                   case 'C':
1624                   case 'c':
1625                   {
1626                      if (LocaleCompare(keyword,"clip-path") == 0)
1627                        {
1628                          (void) FormatLocaleFile(svg_info->file,
1629                            "clip-path '%s'\n",value);
1630                          break;
1631                        }
1632                     if (LocaleCompare(keyword,"clip-rule") == 0)
1633                       {
1634                         (void) FormatLocaleFile(svg_info->file,
1635                           "clip-rule '%s'\n",value);
1636                         break;
1637                       }
1638                      if (LocaleCompare(keyword,"clipPathUnits") == 0)
1639                        {
1640                          (void) CloneString(&units,value);
1641                          (void) FormatLocaleFile(svg_info->file,
1642                           "clip-units '%s'\n",value);
1643                          break;
1644                        }
1645                     if (LocaleCompare(keyword,"color") == 0)
1646                       {
1647                         (void) CloneString(&color,value);
1648                         break;
1649                       }
1650                     break;
1651                   }
1652                   case 'F':
1653                   case 'f':
1654                   {
1655                     if (LocaleCompare(keyword,"fill") == 0)
1656                       {
1657                          if (LocaleCompare(value,"currentColor") == 0)
1658                            {
1659                              (void) FormatLocaleFile(svg_info->file,
1660                           "fill '%s'\n",color);
1661                              break;
1662                            }
1663                         if (LocaleCompare(value,"#00000000") == 0)
1664                           (void) FormatLocaleFile(svg_info->file,
1665                           "fill '#000000'\n");
1666                         else
1667                           (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1668                           value);
1669                         break;
1670                       }
1671                     if (LocaleCompare(keyword,"fillcolor") == 0)
1672                       {
1673                         (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1674                           value);
1675                         break;
1676                       }
1677                     if (LocaleCompare(keyword,"fill-rule") == 0)
1678                       {
1679                         (void) FormatLocaleFile(svg_info->file,
1680                           "fill-rule '%s'\n",value);
1681                         break;
1682                       }
1683                     if (LocaleCompare(keyword,"fill-alpha") == 0)
1684                       {
1685                         (void) FormatLocaleFile(svg_info->file,
1686                           "fill-alpha '%s'\n",value);
1687                         break;
1688                       }
1689                     if (LocaleCompare(keyword,"font-family") == 0)
1690                       {
1691                         (void) FormatLocaleFile(svg_info->file,
1692                           "font-family '%s'\n",value);
1693                         break;
1694                       }
1695                     if (LocaleCompare(keyword,"font-stretch") == 0)
1696                       {
1697                         (void) FormatLocaleFile(svg_info->file,
1698                           "font-stretch '%s'\n",value);
1699                         break;
1700                       }
1701                     if (LocaleCompare(keyword,"font-style") == 0)
1702                       {
1703                         (void) FormatLocaleFile(svg_info->file,
1704                           "font-style '%s'\n",value);
1705                         break;
1706                       }
1707                     if (LocaleCompare(keyword,"font-size") == 0)
1708                       {
1709                         svg_info->pointsize=GetUserSpaceCoordinateValue(
1710                           svg_info,0,value);
1711                         (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1712                           svg_info->pointsize);
1713                         break;
1714                       }
1715                     if (LocaleCompare(keyword,"font-weight") == 0)
1716                       {
1717                         (void) FormatLocaleFile(svg_info->file,
1718                           "font-weight '%s'\n",value);
1719                         break;
1720                       }
1721                     break;
1722                   }
1723                   case 'O':
1724                   case 'o':
1725                   {
1726                     if (LocaleCompare(keyword,"offset") == 0)
1727                       {
1728                         (void) FormatLocaleFile(svg_info->file,"offset %g\n",
1729                           GetUserSpaceCoordinateValue(svg_info,1,value));
1730                         break;
1731                       }
1732                     if (LocaleCompare(keyword,"opacity") == 0)
1733                       {
1734                         (void) FormatLocaleFile(svg_info->file,
1735                           "opacity '%s'\n",value);
1736                         break;
1737                       }
1738                     break;
1739                   }
1740                   case 'S':
1741                   case 's':
1742                   {
1743                     if (LocaleCompare(keyword,"stop-color") == 0)
1744                       {
1745                         (void) CloneString(&svg_info->stop_color,value);
1746                         break;
1747                       }
1748                     if (LocaleCompare(keyword,"stroke") == 0)
1749                       {
1750                          if (LocaleCompare(value,"currentColor") == 0)
1751                            {
1752                              (void) FormatLocaleFile(svg_info->file,
1753                           "stroke '%s'\n",color);
1754                              break;
1755                            }
1756                         if (LocaleCompare(value,"#00000000") == 0)
1757                           (void) FormatLocaleFile(svg_info->file,
1758                           "fill '#000000'\n");
1759                         else
1760                           (void) FormatLocaleFile(svg_info->file,
1761                           "stroke '%s'\n",value);
1762                         break;
1763                       }
1764                     if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1765                       {
1766                         (void) FormatLocaleFile(svg_info->file,
1767                           "stroke-antialias %d\n",
1768                           LocaleCompare(value,"true") == 0);
1769                         break;
1770                       }
1771                     if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1772                       {
1773                         (void) FormatLocaleFile(svg_info->file,
1774                           "stroke-dasharray %s\n",value);
1775                         break;
1776                       }
1777                     if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1778                       {
1779                         (void) FormatLocaleFile(svg_info->file,
1780                           "stroke-dashoffset %s\n",
1781                           value);
1782                         break;
1783                       }
1784                     if (LocaleCompare(keyword,"stroke-linecap") == 0)
1785                       {
1786                         (void) FormatLocaleFile(svg_info->file,
1787                           "stroke-linecap '%s'\n",value);
1788                         break;
1789                       }
1790                     if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1791                       {
1792                         (void) FormatLocaleFile(svg_info->file,
1793                           "stroke-linejoin '%s'\n",
1794                           value);
1795                         break;
1796                       }
1797                     if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1798                       {
1799                         (void) FormatLocaleFile(svg_info->file,
1800                           "stroke-miterlimit '%s'\n",
1801                           value);
1802                         break;
1803                       }
1804                     if (LocaleCompare(keyword,"stroke-opacity") == 0)
1805                       {
1806                         (void) FormatLocaleFile(svg_info->file,
1807                           "stroke-opacity '%s'\n",value);
1808                         break;
1809                       }
1810                     if (LocaleCompare(keyword,"stroke-width") == 0)
1811                       {
1812                         (void) FormatLocaleFile(svg_info->file,
1813                           "stroke-width %g\n",
1814                           GetUserSpaceCoordinateValue(svg_info,1,value));
1815                         break;
1816                       }
1817                     break;
1818                   }
1819                   case 't':
1820                   case 'T':
1821                   {
1822                     if (LocaleCompare(keyword,"text-align") == 0)
1823                       {
1824                         (void) FormatLocaleFile(svg_info->file,
1825                           "text-align '%s'\n",value);
1826                         break;
1827                       }
1828                     if (LocaleCompare(keyword,"text-anchor") == 0)
1829                       {
1830                         (void) FormatLocaleFile(svg_info->file,
1831                           "text-anchor '%s'\n",value);
1832                         break;
1833                       }
1834                     if (LocaleCompare(keyword,"text-decoration") == 0)
1835                       {
1836                         if (LocaleCompare(value,"underline") == 0)
1837                           (void) FormatLocaleFile(svg_info->file,
1838                           "decorate underline\n");
1839                         if (LocaleCompare(value,"line-through") == 0)
1840                           (void) FormatLocaleFile(svg_info->file,
1841                           "decorate line-through\n");
1842                         if (LocaleCompare(value,"overline") == 0)
1843                           (void) FormatLocaleFile(svg_info->file,
1844                           "decorate overline\n");
1845                         break;
1846                       }
1847                     if (LocaleCompare(keyword,"text-antialiasing") == 0)
1848                       {
1849                         (void) FormatLocaleFile(svg_info->file,
1850                           "text-antialias %d\n",
1851                           LocaleCompare(value,"true") == 0);
1852                         break;
1853                       }
1854                     break;
1855                   }
1856                   default:
1857                     break;
1858                 }
1859               }
1860               for (j=0; tokens[j] != (char *) NULL; j++)
1861                 tokens[j]=DestroyString(tokens[j]);
1862               tokens=(char **) RelinquishMagickMemory(tokens);
1863               break;
1864             }
1865           break;
1866         }
1867         case 'T':
1868         case 't':
1869         {
1870           if (LocaleCompare(keyword,"text-align") == 0)
1871             {
1872               (void) FormatLocaleFile(svg_info->file,"text-align '%s'\n",
1873                 value);
1874               break;
1875             }
1876           if (LocaleCompare(keyword,"text-anchor") == 0)
1877             {
1878               (void) FormatLocaleFile(svg_info->file,"text-anchor '%s'\n",
1879                 value);
1880               break;
1881             }
1882           if (LocaleCompare(keyword,"text-decoration") == 0)
1883             {
1884               if (LocaleCompare(value,"underline") == 0)
1885                 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
1886               if (LocaleCompare(value,"line-through") == 0)
1887                 (void) FormatLocaleFile(svg_info->file,
1888                   "decorate line-through\n");
1889               if (LocaleCompare(value,"overline") == 0)
1890                 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
1891               break;
1892             }
1893           if (LocaleCompare(keyword,"text-antialiasing") == 0)
1894             {
1895               (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
1896                 LocaleCompare(value,"true") == 0);
1897               break;
1898             }
1899           if (LocaleCompare(keyword,"transform") == 0)
1900             {
1901               AffineMatrix
1902                 affine,
1903                 current,
1904                 transform;
1905
1906               GetAffineMatrix(&transform);
1907               (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
1908               tokens=GetTransformTokens(context,value,&number_tokens);
1909               for (j=0; j < (number_tokens-1); j+=2)
1910               {
1911                 keyword=(char *) tokens[j];
1912                 value=(char *) tokens[j+1];
1913                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1914                   "    %s: %s",keyword,value);
1915                 current=transform;
1916                 GetAffineMatrix(&affine);
1917                 switch (*keyword)
1918                 {
1919                   case 'M':
1920                   case 'm':
1921                   {
1922                     if (LocaleCompare(keyword,"matrix") == 0)
1923                       {
1924                         p=(const char *) value;
1925                         GetMagickToken(p,&p,token);
1926                         affine.sx=StringToDouble(value,(char **) NULL);
1927                         GetMagickToken(p,&p,token);
1928                         if (*token == ',')
1929                           GetMagickToken(p,&p,token);
1930                         affine.rx=StringToDouble(token,(char **) NULL);
1931                         GetMagickToken(p,&p,token);
1932                         if (*token == ',')
1933                           GetMagickToken(p,&p,token);
1934                         affine.ry=StringToDouble(token,(char **) NULL);
1935                         GetMagickToken(p,&p,token);
1936                         if (*token == ',')
1937                           GetMagickToken(p,&p,token);
1938                         affine.sy=StringToDouble(token,(char **) NULL);
1939                         GetMagickToken(p,&p,token);
1940                         if (*token == ',')
1941                           GetMagickToken(p,&p,token);
1942                         affine.tx=StringToDouble(token,(char **) NULL);
1943                         GetMagickToken(p,&p,token);
1944                         if (*token == ',')
1945                           GetMagickToken(p,&p,token);
1946                         affine.ty=StringToDouble(token,(char **) NULL);
1947                         break;
1948                       }
1949                     break;
1950                   }
1951                   case 'R':
1952                   case 'r':
1953                   {
1954                     if (LocaleCompare(keyword,"rotate") == 0)
1955                       {
1956                         double
1957                           angle,
1958                           x,
1959                           y;
1960
1961                         p=(const char *) value;
1962                         GetMagickToken(p,&p,token);
1963                         angle=StringToDouble(value,(char **) NULL);
1964                         GetMagickToken(p,&p,token);
1965                         if (*token == ',')
1966                           GetMagickToken(p,&p,token);
1967                         x=StringToDouble(token,(char **) NULL);
1968                         GetMagickToken(p,&p,token);
1969                         if (*token == ',')
1970                           GetMagickToken(p,&p,token);
1971                         y=StringToDouble(token,(char **) NULL);
1972                         affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1973                         affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1974                         affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1975                         affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1976                         affine.tx=x;
1977                         affine.ty=y;
1978                         svg_info->center.x=x;
1979                         svg_info->center.y=y;
1980                         break;
1981                       }
1982                     break;
1983                   }
1984                   case 'S':
1985                   case 's':
1986                   {
1987                     if (LocaleCompare(keyword,"scale") == 0)
1988                       {
1989                         for (p=(const char *) value; *p != '\0'; p++)
1990                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
1991                               (*p == ','))
1992                             break;
1993                         affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1994                         affine.sy=affine.sx;
1995                         if (*p != '\0')
1996                           affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
1997                             p+1);
1998                         svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1999                         break;
2000                       }
2001                     if (LocaleCompare(keyword,"skewX") == 0)
2002                       {
2003                         affine.sx=svg_info->affine.sx;
2004                         affine.ry=tan(DegreesToRadians(fmod(
2005                           GetUserSpaceCoordinateValue(svg_info,1,value),
2006                           360.0)));
2007                         affine.sy=svg_info->affine.sy;
2008                         break;
2009                       }
2010                     if (LocaleCompare(keyword,"skewY") == 0)
2011                       {
2012                         affine.sx=svg_info->affine.sx;
2013                         affine.rx=tan(DegreesToRadians(fmod(
2014                           GetUserSpaceCoordinateValue(svg_info,-1,value),
2015                           360.0)));
2016                         affine.sy=svg_info->affine.sy;
2017                         break;
2018                       }
2019                     break;
2020                   }
2021                   case 'T':
2022                   case 't':
2023                   {
2024                     if (LocaleCompare(keyword,"translate") == 0)
2025                       {
2026                         for (p=(const char *) value; *p != '\0'; p++)
2027                           if ((isspace((int) ((unsigned char) *p)) != 0) ||
2028                               (*p == ','))
2029                             break;
2030                         affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2031                         affine.ty=affine.tx;
2032                         if (*p != '\0')
2033                           affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2034                             p+1);
2035                         break;
2036                       }
2037                     break;
2038                   }
2039                   default:
2040                     break;
2041                 }
2042                 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2043                 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2044                 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2045                 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2046                 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2047                   current.tx;
2048                 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2049                   current.ty;
2050               }
2051               (void) FormatLocaleFile(svg_info->file,
2052                 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2053                 transform.ry,transform.sy,transform.tx,transform.ty);
2054               for (j=0; tokens[j] != (char *) NULL; j++)
2055                 tokens[j]=DestroyString(tokens[j]);
2056               tokens=(char **) RelinquishMagickMemory(tokens);
2057               break;
2058             }
2059           break;
2060         }
2061         case 'V':
2062         case 'v':
2063         {
2064           if (LocaleCompare(keyword,"verts") == 0)
2065             {
2066               (void) CloneString(&svg_info->vertices,value);
2067               break;
2068             }
2069           if (LocaleCompare(keyword,"viewBox") == 0)
2070             {
2071               p=(const char *) value;
2072               GetMagickToken(p,&p,token);
2073               svg_info->view_box.x=StringToDouble(token,(char **) NULL);
2074               GetMagickToken(p,&p,token);
2075               if (*token == ',')
2076                 GetMagickToken(p,&p,token);
2077               svg_info->view_box.y=StringToDouble(token,(char **) NULL);
2078               GetMagickToken(p,&p,token);
2079               if (*token == ',')
2080                 GetMagickToken(p,&p,token);
2081               svg_info->view_box.width=StringToDouble(token,
2082                 (char **) NULL);
2083               if (svg_info->bounds.width == 0)
2084                 svg_info->bounds.width=svg_info->view_box.width;
2085               GetMagickToken(p,&p,token);
2086               if (*token == ',')
2087                 GetMagickToken(p,&p,token);
2088               svg_info->view_box.height=StringToDouble(token,
2089                 (char **) NULL);
2090               if (svg_info->bounds.height == 0)
2091                 svg_info->bounds.height=svg_info->view_box.height;
2092               break;
2093             }
2094           break;
2095         }
2096         case 'W':
2097         case 'w':
2098         {
2099           if (LocaleCompare(keyword,"width") == 0)
2100             {
2101               svg_info->bounds.width=
2102                 GetUserSpaceCoordinateValue(svg_info,1,value);
2103               break;
2104             }
2105           break;
2106         }
2107         case 'X':
2108         case 'x':
2109         {
2110           if (LocaleCompare(keyword,"x") == 0)
2111             {
2112               svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2113               break;
2114             }
2115           if (LocaleCompare(keyword,"xlink:href") == 0)
2116             {
2117               (void) CloneString(&svg_info->url,value);
2118               break;
2119             }
2120           if (LocaleCompare(keyword,"x1") == 0)
2121             {
2122               svg_info->segment.x1=
2123                 GetUserSpaceCoordinateValue(svg_info,1,value);
2124               break;
2125             }
2126           if (LocaleCompare(keyword,"x2") == 0)
2127             {
2128               svg_info->segment.x2=
2129                 GetUserSpaceCoordinateValue(svg_info,1,value);
2130               break;
2131             }
2132           break;
2133         }
2134         case 'Y':
2135         case 'y':
2136         {
2137           if (LocaleCompare(keyword,"y") == 0)
2138             {
2139               svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2140               break;
2141             }
2142           if (LocaleCompare(keyword,"y1") == 0)
2143             {
2144               svg_info->segment.y1=
2145                 GetUserSpaceCoordinateValue(svg_info,-1,value);
2146               break;
2147             }
2148           if (LocaleCompare(keyword,"y2") == 0)
2149             {
2150               svg_info->segment.y2=
2151                 GetUserSpaceCoordinateValue(svg_info,-1,value);
2152               break;
2153             }
2154           break;
2155         }
2156         default:
2157           break;
2158       }
2159     }
2160   if (LocaleCompare((const char *) name,"svg") == 0)
2161     {
2162       if (svg_info->document->encoding != (const xmlChar *) NULL)
2163         (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2164           (const char *) svg_info->document->encoding);
2165       if (attributes != (const xmlChar **) NULL)
2166         {
2167           double
2168             sx,
2169             sy,
2170             tx,
2171             ty;
2172
2173           if ((svg_info->view_box.width == 0.0) ||
2174               (svg_info->view_box.height == 0.0))
2175             svg_info->view_box=svg_info->bounds;
2176           svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2177           svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2178           (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2179             (double) svg_info->width,(double) svg_info->height);
2180           sx=(double) svg_info->width/svg_info->view_box.width;
2181           sy=(double) svg_info->height/svg_info->view_box.height;
2182           tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2183             0.0;
2184           ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2185             0.0;
2186           (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2187             sx,sy,tx,ty);
2188         }
2189     }
2190   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  )");
2191   units=DestroyString(units);
2192   if (color != (char *) NULL)
2193     color=DestroyString(color);
2194 }
2195
2196 static void SVGEndElement(void *context,const xmlChar *name)
2197 {
2198   SVGInfo
2199     *svg_info;
2200
2201   /*
2202     Called when the end of an element has been detected.
2203   */
2204   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2205     "  SAX.endElement(%s)",name);
2206   svg_info=(SVGInfo *) context;
2207   if (strchr((char *) name,':') != (char *) NULL)
2208     {
2209       /*
2210         Skip over namespace.
2211       */
2212       for ( ; *name != ':'; name++) ;
2213       name++;
2214     }
2215   switch (*name)
2216   {
2217     case 'C':
2218     case 'c':
2219     {
2220       if (LocaleCompare((const char *) name,"circle") == 0)
2221         {
2222           (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2223             svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2224             svg_info->element.cy+svg_info->element.minor);
2225           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2226           break;
2227         }
2228       if (LocaleCompare((const char *) name,"clipPath") == 0)
2229         {
2230           (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2231           break;
2232         }
2233       break;
2234     }
2235     case 'D':
2236     case 'd':
2237     {
2238       if (LocaleCompare((const char *) name,"defs") == 0)
2239         {
2240           (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2241           break;
2242         }
2243       if (LocaleCompare((const char *) name,"desc") == 0)
2244         {
2245           register char
2246             *p;
2247
2248           if (*svg_info->text == '\0')
2249             break;
2250           (void) fputc('#',svg_info->file);
2251           for (p=svg_info->text; *p != '\0'; p++)
2252           {
2253             (void) fputc(*p,svg_info->file);
2254             if (*p == '\n')
2255               (void) fputc('#',svg_info->file);
2256           }
2257           (void) fputc('\n',svg_info->file);
2258           *svg_info->text='\0';
2259           break;
2260         }
2261       break;
2262     }
2263     case 'E':
2264     case 'e':
2265     {
2266       if (LocaleCompare((const char *) name,"ellipse") == 0)
2267         {
2268           double
2269             angle;
2270
2271           angle=svg_info->element.angle;
2272           (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2273             svg_info->element.cx,svg_info->element.cy,
2274             angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2275             angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2276           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2277           break;
2278         }
2279       break;
2280     }
2281     case 'G':
2282     case 'g':
2283     {
2284       if (LocaleCompare((const char *) name,"g") == 0)
2285         {
2286           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2287           break;
2288         }
2289       break;
2290     }
2291     case 'I':
2292     case 'i':
2293     {
2294       if (LocaleCompare((const char *) name,"image") == 0)
2295         {
2296           (void) FormatLocaleFile(svg_info->file,
2297             "image Over %g,%g %g,%g '%s'\n",svg_info->bounds.x,
2298             svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2299             svg_info->url);
2300           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2301           break;
2302         }
2303       break;
2304     }
2305     case 'L':
2306     case 'l':
2307     {
2308       if (LocaleCompare((const char *) name,"line") == 0)
2309         {
2310           (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2311             svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2312             svg_info->segment.y2);
2313           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2314           break;
2315         }
2316       if (LocaleCompare((const char *) name,"linearGradient") == 0)
2317         {
2318           (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2319           break;
2320         }
2321       break;
2322     }
2323     case 'P':
2324     case 'p':
2325     {
2326       if (LocaleCompare((const char *) name,"pattern") == 0)
2327         {
2328           (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2329           break;
2330         }
2331       if (LocaleCompare((const char *) name,"path") == 0)
2332         {
2333           (void) FormatLocaleFile(svg_info->file,"path '%s'\n",
2334             svg_info->vertices);
2335           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2336           break;
2337         }
2338       if (LocaleCompare((const char *) name,"polygon") == 0)
2339         {
2340           (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2341             svg_info->vertices);
2342           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2343           break;
2344         }
2345       if (LocaleCompare((const char *) name,"polyline") == 0)
2346         {
2347           (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2348             svg_info->vertices);
2349           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2350           break;
2351         }
2352       break;
2353     }
2354     case 'R':
2355     case 'r':
2356     {
2357       if (LocaleCompare((const char *) name,"radialGradient") == 0)
2358         {
2359           (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2360           break;
2361         }
2362       if (LocaleCompare((const char *) name,"rect") == 0)
2363         {
2364           if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2365             {
2366               (void) FormatLocaleFile(svg_info->file,"rectangle %g,%g %g,%g\n",
2367                 svg_info->bounds.x,svg_info->bounds.y,
2368                 svg_info->bounds.x+svg_info->bounds.width,
2369                 svg_info->bounds.y+svg_info->bounds.height);
2370               (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2371               break;
2372             }
2373           if (svg_info->radius.x == 0.0)
2374             svg_info->radius.x=svg_info->radius.y;
2375           if (svg_info->radius.y == 0.0)
2376             svg_info->radius.y=svg_info->radius.x;
2377           (void) FormatLocaleFile(svg_info->file,
2378             "roundRectangle %g,%g %g,%g %g,%g\n",
2379             svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2380             svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2381             svg_info->radius.x,svg_info->radius.y);
2382           svg_info->radius.x=0.0;
2383           svg_info->radius.y=0.0;
2384           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2385           break;
2386         }
2387       break;
2388     }
2389     case 'S':
2390     case 's':
2391     {
2392       if (LocaleCompare((const char *) name,"stop") == 0)
2393         {
2394           (void) FormatLocaleFile(svg_info->file,"stop-color '%s' %s\n",
2395             svg_info->stop_color,svg_info->offset);
2396           break;
2397         }
2398       if (LocaleCompare((const char *) name,"svg") == 0)
2399         {
2400           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2401           break;
2402         }
2403       break;
2404     }
2405     case 'T':
2406     case 't':
2407     {
2408       if (LocaleCompare((const char *) name,"text") == 0)
2409         {
2410           if (*svg_info->text != '\0')
2411             {
2412               char
2413                 *text;
2414
2415               text=EscapeString(svg_info->text,'\'');
2416               (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
2417                 svg_info->bounds.x,svg_info->bounds.y,text);
2418               text=DestroyString(text);
2419               *svg_info->text='\0';
2420             }
2421           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2422           break;
2423         }
2424       if (LocaleCompare((const char *) name,"tspan") == 0)
2425         {
2426           if (*svg_info->text != '\0')
2427             {
2428               DrawInfo
2429                 *draw_info;
2430
2431               TypeMetric
2432                 metrics;
2433
2434               char
2435                 *text;
2436
2437               text=EscapeString(svg_info->text,'\'');
2438               (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
2439                 svg_info->bounds.x,svg_info->bounds.y,text);
2440               text=DestroyString(text);
2441               draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
2442               draw_info->pointsize=svg_info->pointsize;
2443               draw_info->text=AcquireString(svg_info->text);
2444               (void) ConcatenateString(&draw_info->text," ");
2445               (void) GetTypeMetrics(svg_info->image,draw_info,&metrics,
2446                 svg_info->exception);
2447               svg_info->bounds.x+=metrics.width;
2448               draw_info=DestroyDrawInfo(draw_info);
2449               *svg_info->text='\0';
2450             }
2451           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2452           break;
2453         }
2454       if (LocaleCompare((const char *) name,"title") == 0)
2455         {
2456           if (*svg_info->text == '\0')
2457             break;
2458           (void) CloneString(&svg_info->title,svg_info->text);
2459           *svg_info->text='\0';
2460           break;
2461         }
2462       break;
2463     }
2464     default:
2465       break;
2466   }
2467   *svg_info->text='\0';
2468   (void) ResetMagickMemory(&svg_info->element,0,sizeof(svg_info->element));
2469   (void) ResetMagickMemory(&svg_info->segment,0,sizeof(svg_info->segment));
2470   svg_info->n--;
2471 }
2472
2473 static void SVGCharacters(void *context,const xmlChar *c,int length)
2474 {
2475   char
2476     *text;
2477
2478   register char
2479     *p;
2480
2481   register ssize_t
2482     i;
2483
2484   SVGInfo
2485     *svg_info;
2486
2487   /*
2488     Receiving some characters from the parser.
2489   */
2490   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2491     "  SAX.characters(%s,%.20g)",c,(double) length);
2492   svg_info=(SVGInfo *) context;
2493   text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2494   if (text == (char *) NULL)
2495     return;
2496   p=text;
2497   for (i=0; i < (ssize_t) length; i++)
2498     *p++=c[i];
2499   *p='\0';
2500   StripString(text);
2501   if (svg_info->text == (char *) NULL)
2502     svg_info->text=text;
2503   else
2504     {
2505       (void) ConcatenateString(&svg_info->text,text);
2506       text=DestroyString(text);
2507     }
2508 }
2509
2510 static void SVGReference(void *context,const xmlChar *name)
2511 {
2512   SVGInfo
2513     *svg_info;
2514
2515   xmlParserCtxtPtr
2516     parser;
2517
2518   /*
2519     Called when an entity reference is detected.
2520   */
2521   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.reference(%s)",
2522     name);
2523   svg_info=(SVGInfo *) context;
2524   parser=svg_info->parser;
2525   if (parser == (xmlParserCtxtPtr) NULL)
2526     return;
2527   if (parser->node == (xmlNodePtr) NULL)
2528     return;
2529   if (*name == '#')
2530     (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2531   else
2532     (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2533 }
2534
2535 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2536 {
2537   SVGInfo
2538     *svg_info;
2539
2540   /*
2541     Receiving some ignorable whitespaces from the parser.
2542   */
2543   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2544     "  SAX.ignorableWhitespace(%.30s, %d)",c,length);
2545   svg_info=(SVGInfo *) context;
2546   (void) svg_info;
2547 }
2548
2549 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2550   const xmlChar *data)
2551 {
2552   SVGInfo
2553     *svg_info;
2554
2555   /*
2556     A processing instruction has been parsed.
2557   */
2558   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2559     "  SAX.processingInstruction(%s, %s)",target,data);
2560   svg_info=(SVGInfo *) context;
2561   (void) svg_info;
2562 }
2563
2564 static void SVGComment(void *context,const xmlChar *value)
2565 {
2566   SVGInfo
2567     *svg_info;
2568
2569   /*
2570     A comment has been parsed.
2571   */
2572   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.comment(%s)",
2573     value);
2574   svg_info=(SVGInfo *) context;
2575   if (svg_info->comment != (char *) NULL)
2576     (void) ConcatenateString(&svg_info->comment,"\n");
2577   (void) ConcatenateString(&svg_info->comment,(const char *) value);
2578 }
2579
2580 static void SVGWarning(void *context,const char *format,...)
2581 {
2582   char
2583     *message,
2584     reason[MaxTextExtent];
2585
2586   SVGInfo
2587     *svg_info;
2588
2589   va_list
2590     operands;
2591
2592   /**
2593     Display and format a warning messages, gives file, line, position and
2594     extra parameters.
2595   */
2596   va_start(operands,format);
2597   svg_info=(SVGInfo *) context;
2598   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.warning: ");
2599   (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2600 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2601   (void) vsprintf(reason,format,operands);
2602 #else
2603   (void) vsnprintf(reason,MaxTextExtent,format,operands);
2604 #endif
2605   message=GetExceptionMessage(errno);
2606   (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2607     DelegateWarning,reason,"`%s`",message);
2608   message=DestroyString(message);
2609   va_end(operands);
2610 }
2611
2612 static void SVGError(void *context,const char *format,...)
2613 {
2614   char
2615     *message,
2616     reason[MaxTextExtent];
2617
2618   SVGInfo
2619     *svg_info;
2620
2621   va_list
2622     operands;
2623
2624   /*
2625     Display and format a error formats, gives file, line, position and
2626     extra parameters.
2627   */
2628   va_start(operands,format);
2629   svg_info=(SVGInfo *) context;
2630   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.error: ");
2631   (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2632 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2633   (void) vsprintf(reason,format,operands);
2634 #else
2635   (void) vsnprintf(reason,MaxTextExtent,format,operands);
2636 #endif
2637   message=GetExceptionMessage(errno);
2638   (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2639     reason,"`%s`",message);
2640   message=DestroyString(message);
2641   va_end(operands);
2642 }
2643
2644 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2645 {
2646   SVGInfo
2647     *svg_info;
2648
2649    xmlNodePtr
2650      child;
2651
2652   xmlParserCtxtPtr
2653     parser;
2654
2655   /*
2656     Called when a pcdata block has been parsed.
2657   */
2658   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.pcdata(%s, %d)",
2659     value,length);
2660   svg_info=(SVGInfo *) context;
2661   parser=svg_info->parser;
2662   child=xmlGetLastChild(parser->node);
2663   if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2664     {
2665       xmlTextConcat(child,value,length);
2666       return;
2667     }
2668   (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2669 }
2670
2671 static void SVGExternalSubset(void *context,const xmlChar *name,
2672   const xmlChar *external_id,const xmlChar *system_id)
2673 {
2674   SVGInfo
2675     *svg_info;
2676
2677   xmlParserCtxt
2678     parser_context;
2679
2680   xmlParserCtxtPtr
2681     parser;
2682
2683   xmlParserInputPtr
2684     input;
2685
2686   /*
2687     Does this document has an external subset?
2688   */
2689   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2690     "  SAX.externalSubset(%s, %s, %s)",name,
2691     (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2692     (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2693   svg_info=(SVGInfo *) context;
2694   parser=svg_info->parser;
2695   if (((external_id == NULL) && (system_id == NULL)) ||
2696       ((parser->validate == 0) || (parser->wellFormed == 0) ||
2697       (svg_info->document == 0)))
2698     return;
2699   input=SVGResolveEntity(context,external_id,system_id);
2700   if (input == NULL)
2701     return;
2702   (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2703   parser_context=(*parser);
2704   parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2705   if (parser->inputTab == (xmlParserInputPtr *) NULL)
2706     {
2707       parser->errNo=XML_ERR_NO_MEMORY;
2708       parser->input=parser_context.input;
2709       parser->inputNr=parser_context.inputNr;
2710       parser->inputMax=parser_context.inputMax;
2711       parser->inputTab=parser_context.inputTab;
2712       return;
2713   }
2714   parser->inputNr=0;
2715   parser->inputMax=5;
2716   parser->input=NULL;
2717   xmlPushInput(parser,input);
2718   (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2719   if (input->filename == (char *) NULL)
2720     input->filename=(char *) xmlStrdup(system_id);
2721   input->line=1;
2722   input->col=1;
2723   input->base=parser->input->cur;
2724   input->cur=parser->input->cur;
2725   input->free=NULL;
2726   xmlParseExternalSubset(parser,external_id,system_id);
2727   while (parser->inputNr > 1)
2728     (void) xmlPopInput(parser);
2729   xmlFreeInputStream(parser->input);
2730   xmlFree(parser->inputTab);
2731   parser->input=parser_context.input;
2732   parser->inputNr=parser_context.inputNr;
2733   parser->inputMax=parser_context.inputMax;
2734   parser->inputTab=parser_context.inputTab;
2735 }
2736
2737 #if defined(__cplusplus) || defined(c_plusplus)
2738 }
2739 #endif
2740
2741 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2742 {
2743   char
2744     filename[MaxTextExtent];
2745
2746   FILE
2747     *file;
2748
2749   Image
2750     *image;
2751
2752   int
2753     status,
2754     unique_file;
2755
2756   ssize_t
2757     n;
2758
2759   SVGInfo
2760     *svg_info;
2761
2762   unsigned char
2763     message[MaxTextExtent];
2764
2765   xmlSAXHandler
2766     sax_modules;
2767
2768   xmlSAXHandlerPtr
2769     sax_handler;
2770
2771   /*
2772     Open image file.
2773   */
2774   assert(image_info != (const ImageInfo *) NULL);
2775   assert(image_info->signature == MagickSignature);
2776   assert(exception != (ExceptionInfo *) NULL);
2777   if (image_info->debug != MagickFalse)
2778     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2779       image_info->filename);
2780   assert(exception->signature == MagickSignature);
2781   image=AcquireImage(image_info,exception);
2782   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2783   if (status == MagickFalse)
2784     {
2785       image=DestroyImageList(image);
2786       return((Image *) NULL);
2787     }
2788   if (LocaleCompare(image_info->magick,"MSVG") != 0)
2789     {
2790 #if defined(MAGICKCORE_RSVG_DELEGATE)
2791 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2792       cairo_surface_t
2793         *cairo_surface;
2794
2795       cairo_t
2796         *cairo_image;
2797
2798       register unsigned char
2799         *p;
2800
2801       RsvgDimensionData
2802         dimension_info;
2803
2804       unsigned char
2805         *pixels;
2806
2807 #else
2808       GdkPixbuf
2809         *pixel_info;
2810
2811       register const guchar
2812         *p;
2813
2814 #endif
2815
2816       GError
2817         *error;
2818
2819       ssize_t
2820         y;
2821
2822       PixelInfo
2823         fill_color;
2824
2825       register ssize_t
2826         x;
2827
2828       register Quantum
2829         *q;
2830
2831       RsvgHandle
2832         *svg_handle;
2833
2834       svg_handle=rsvg_handle_new();
2835       if (svg_handle == (RsvgHandle *) NULL)
2836         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2837       rsvg_handle_set_base_uri(svg_handle,image_info->filename);
2838       if ((image->resolution.x != 72.0) && (image->resolution.y != 72.0))
2839         rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
2840           image->resolution.y);
2841       while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
2842       {
2843         error=(GError *) NULL;
2844         (void) rsvg_handle_write(svg_handle,message,n,&error);
2845         if (error != (GError *) NULL)
2846           g_error_free(error);
2847       }
2848       error=(GError *) NULL;
2849       rsvg_handle_close(svg_handle,&error);
2850       if (error != (GError *) NULL)
2851         g_error_free(error);
2852 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2853       rsvg_handle_get_dimensions(svg_handle,&dimension_info);
2854       image->columns=image->resolution.x*dimension_info.width/72.0;
2855       image->rows=image->resolution.y*dimension_info.height/72.0;
2856       pixels=(unsigned char *) NULL;
2857 #else
2858       pixel_info=rsvg_handle_get_pixbuf(svg_handle);
2859       rsvg_handle_free(svg_handle);
2860       image->columns=gdk_pixbuf_get_width(pixel_info);
2861       image->rows=gdk_pixbuf_get_height(pixel_info);
2862 #endif
2863       image->matte=MagickTrue;
2864       SetImageProperty(image,"svg:base-uri",
2865         rsvg_handle_get_base_uri(svg_handle),exception);
2866       if ((image->columns == 0) || (image->rows == 0))
2867         {
2868 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
2869           g_object_unref(G_OBJECT(pixel_info));
2870 #endif
2871           g_object_unref(svg_handle);
2872           ThrowReaderException(MissingDelegateError,
2873             "NoDecodeDelegateForThisImageFormat");
2874         }
2875       if (image_info->ping == MagickFalse)
2876         {
2877           size_t
2878             stride;
2879
2880 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2881           stride=4*image->columns;
2882 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
2883           stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
2884             image->columns);
2885 #endif
2886           pixels=(unsigned char *) AcquireQuantumMemory(stride,image->rows*
2887             sizeof(*pixels));
2888           if (pixels == (unsigned char *) NULL)
2889             {
2890               g_object_unref(svg_handle);
2891               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2892             }
2893 #endif
2894           (void) SetImageBackgroundColor(image,exception);
2895 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2896           cairo_surface=cairo_image_surface_create_for_data(pixels,
2897             CAIRO_FORMAT_ARGB32,image->columns,image->rows,stride);
2898           if (cairo_surface == (cairo_surface_t *) NULL)
2899             {
2900               pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2901               g_object_unref(svg_handle);
2902               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2903             }
2904           cairo_image=cairo_create(cairo_surface);
2905           cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
2906           cairo_paint(cairo_image);
2907           cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
2908           cairo_scale(cairo_image,image->resolution.x/72.0,image->resolution.y/
2909             72.0);
2910           rsvg_handle_render_cairo(svg_handle,cairo_image);
2911           cairo_destroy(cairo_image);
2912           cairo_surface_destroy(cairo_surface);
2913           g_object_unref(svg_handle);
2914           p=pixels;
2915 #else
2916           p=gdk_pixbuf_get_pixels(pixel_info);
2917 #endif
2918           GetPixelInfo(image,&fill_color);
2919           for (y=0; y < (ssize_t) image->rows; y++)
2920           {
2921             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2922             if (q == (Quantum *) NULL)
2923               break;
2924             for (x=0; x < (ssize_t) image->columns; x++)
2925             {
2926 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2927               fill_color.blue=ScaleCharToQuantum(*p++);
2928               fill_color.green=ScaleCharToQuantum(*p++);
2929               fill_color.red=ScaleCharToQuantum(*p++);
2930 #else
2931               fill_color.red=ScaleCharToQuantum(*p++);
2932               fill_color.green=ScaleCharToQuantum(*p++);
2933               fill_color.blue=ScaleCharToQuantum(*p++);
2934 #endif
2935               fill_color.alpha=ScaleCharToQuantum(*p++);
2936 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2937               {
2938                 double
2939                   gamma;
2940     
2941                 gamma=1.0-QuantumScale*fill_color.alpha;
2942                 gamma=MagickEpsilonReciprocal(gamma);
2943                 fill_color.blue*=gamma;
2944                 fill_color.green*=gamma;
2945                 fill_color.red*=gamma;
2946               }
2947 #endif
2948               CompositePixelOver(image,&fill_color,fill_color.alpha,q,
2949                 (MagickRealType) GetPixelAlpha(image,q),q);
2950               q+=GetPixelChannels(image);
2951             }
2952             if (SyncAuthenticPixels(image,exception) == MagickFalse)
2953               break;
2954             if (image->previous == (Image *) NULL)
2955               {
2956                 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2957                 image->rows);
2958                 if (status == MagickFalse)
2959                   break;
2960               }
2961           }
2962         }
2963 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2964       if (pixels != (unsigned char *) NULL)
2965         pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2966 #else
2967       g_object_unref(G_OBJECT(pixel_info));
2968 #endif
2969       (void) CloseBlob(image);
2970       return(GetFirstImageInList(image));
2971 #endif
2972     }
2973   /*
2974     Open draw file.
2975   */
2976   file=(FILE *) NULL;
2977   unique_file=AcquireUniqueFileResource(filename);
2978   if (unique_file != -1)
2979     file=fdopen(unique_file,"w");
2980   if ((unique_file == -1) || (file == (FILE *) NULL))
2981     {
2982       (void) CopyMagickString(image->filename,filename,MaxTextExtent);
2983       ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
2984         image->filename);
2985       image=DestroyImageList(image);
2986       return((Image *) NULL);
2987     }
2988   /*
2989     Parse SVG file.
2990   */
2991   svg_info=AcquireSVGInfo();
2992   if (svg_info == (SVGInfo *) NULL)
2993     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2994   svg_info->file=file;
2995   svg_info->exception=exception;
2996   svg_info->image=image;
2997   svg_info->image_info=image_info;
2998   svg_info->bounds.width=image->columns;
2999   svg_info->bounds.height=image->rows;
3000   if (image_info->size != (char *) NULL)
3001     (void) CloneString(&svg_info->size,image_info->size);
3002   if (image->debug != MagickFalse)
3003     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3004   xmlInitParser();
3005   (void) xmlSubstituteEntitiesDefault(1);
3006   (void) ResetMagickMemory(&sax_modules,0,sizeof(sax_modules));
3007   sax_modules.internalSubset=SVGInternalSubset;
3008   sax_modules.isStandalone=SVGIsStandalone;
3009   sax_modules.hasInternalSubset=SVGHasInternalSubset;
3010   sax_modules.hasExternalSubset=SVGHasExternalSubset;
3011   sax_modules.resolveEntity=SVGResolveEntity;
3012   sax_modules.getEntity=SVGGetEntity;
3013   sax_modules.entityDecl=SVGEntityDeclaration;
3014   sax_modules.notationDecl=SVGNotationDeclaration;
3015   sax_modules.attributeDecl=SVGAttributeDeclaration;
3016   sax_modules.elementDecl=SVGElementDeclaration;
3017   sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3018   sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3019   sax_modules.startDocument=SVGStartDocument;
3020   sax_modules.endDocument=SVGEndDocument;
3021   sax_modules.startElement=SVGStartElement;
3022   sax_modules.endElement=SVGEndElement;
3023   sax_modules.reference=SVGReference;
3024   sax_modules.characters=SVGCharacters;
3025   sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3026   sax_modules.processingInstruction=SVGProcessingInstructions;
3027   sax_modules.comment=SVGComment;
3028   sax_modules.warning=SVGWarning;
3029   sax_modules.error=SVGError;
3030   sax_modules.fatalError=SVGError;
3031   sax_modules.getParameterEntity=SVGGetParameterEntity;
3032   sax_modules.cdataBlock=SVGCDataBlock;
3033   sax_modules.externalSubset=SVGExternalSubset;
3034   sax_handler=(&sax_modules);
3035   n=ReadBlob(image,MaxTextExtent,message);
3036   if (n > 0)
3037     {
3038       svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3039         message,n,image->filename);
3040       while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
3041       {
3042         status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3043         if (status != 0)
3044           break;
3045       }
3046     }
3047   (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3048   xmlFreeParserCtxt(svg_info->parser);
3049   if (image->debug != MagickFalse)
3050     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3051   xmlCleanupParser();
3052   (void) fclose(file);
3053   (void) CloseBlob(image);
3054   image->columns=svg_info->width;
3055   image->rows=svg_info->height;
3056   if (exception->severity >= ErrorException)
3057     {
3058       image=DestroyImage(image);
3059       return((Image *) NULL);
3060     }
3061   if (image_info->ping == MagickFalse)
3062     {
3063       ImageInfo
3064         *read_info;
3065
3066       /*
3067         Draw image.
3068       */
3069       image=DestroyImage(image);
3070       image=(Image *) NULL;
3071       read_info=CloneImageInfo(image_info);
3072       SetImageInfoBlob(read_info,(void *) NULL,0);
3073       if (read_info->density != (char *) NULL)
3074         read_info->density=DestroyString(read_info->density);
3075       (void) FormatLocaleString(read_info->filename,MaxTextExtent,"mvg:%s",
3076         filename);
3077       image=ReadImage(read_info,exception);
3078       read_info=DestroyImageInfo(read_info);
3079       if (image != (Image *) NULL)
3080         (void) CopyMagickString(image->filename,image_info->filename,
3081           MaxTextExtent);
3082     }
3083   /*
3084     Relinquish resources.
3085   */
3086   if (image != (Image *) NULL)
3087     {
3088       if (svg_info->title != (char *) NULL)
3089         (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
3090       if (svg_info->comment != (char *) NULL)
3091         (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3092           exception);
3093     }
3094   svg_info=DestroySVGInfo(svg_info);
3095   (void) RelinquishUniqueFileResource(filename);
3096   return(GetFirstImageInList(image));
3097 }
3098 #endif
3099 \f
3100 /*
3101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3102 %                                                                             %
3103 %                                                                             %
3104 %                                                                             %
3105 %   R e g i s t e r S V G I m a g e                                           %
3106 %                                                                             %
3107 %                                                                             %
3108 %                                                                             %
3109 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3110 %
3111 %  RegisterSVGImage() adds attributes for the SVG image format to
3112 %  the list of supported formats.  The attributes include the image format
3113 %  tag, a method to read and/or write the format, whether the format
3114 %  supports the saving of more than one frame to the same file or blob,
3115 %  whether the format supports native in-memory I/O, and a brief
3116 %  description of the format.
3117 %
3118 %  The format of the RegisterSVGImage method is:
3119 %
3120 %      size_t RegisterSVGImage(void)
3121 %
3122 */
3123 ModuleExport size_t RegisterSVGImage(void)
3124 {
3125   char
3126     version[MaxTextExtent];
3127
3128   MagickInfo
3129     *entry;
3130
3131   *version='\0';
3132 #if defined(LIBXML_DOTTED_VERSION)
3133   (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,MaxTextExtent);
3134 #endif
3135 #if defined(MAGICKCORE_RSVG_DELEGATE)
3136   g_type_init();
3137   (void) FormatLocaleString(version,MaxTextExtent,"RSVG %d.%d.%d",
3138     LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3139 #endif
3140   entry=SetMagickInfo("SVG");
3141 #if defined(MAGICKCORE_XML_DELEGATE)
3142   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3143 #endif
3144   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3145   entry->blob_support=MagickFalse;
3146   entry->seekable_stream=MagickFalse;
3147   entry->description=ConstantString("Scalable Vector Graphics");
3148   if (*version != '\0')
3149     entry->version=ConstantString(version);
3150   entry->magick=(IsImageFormatHandler *) IsSVG;
3151   entry->module=ConstantString("SVG");
3152   (void) RegisterMagickInfo(entry);
3153   entry=SetMagickInfo("SVGZ");
3154 #if defined(MAGICKCORE_XML_DELEGATE)
3155   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3156 #endif
3157   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3158   entry->blob_support=MagickFalse;
3159   entry->seekable_stream=MagickFalse;
3160   entry->description=ConstantString("Compressed Scalable Vector Graphics");
3161   if (*version != '\0')
3162     entry->version=ConstantString(version);
3163   entry->magick=(IsImageFormatHandler *) IsSVG;
3164   entry->module=ConstantString("SVG");
3165   (void) RegisterMagickInfo(entry);
3166   entry=SetMagickInfo("MSVG");
3167 #if defined(MAGICKCORE_XML_DELEGATE)
3168   entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3169 #endif
3170   entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3171   entry->blob_support=MagickFalse;
3172   entry->seekable_stream=MagickFalse;
3173   entry->description=ConstantString("ImageMagick's own SVG internal renderer");
3174   entry->magick=(IsImageFormatHandler *) IsSVG;
3175   entry->module=ConstantString("SVG");
3176   (void) RegisterMagickInfo(entry);
3177   return(MagickImageCoderSignature);
3178 }
3179 \f
3180 /*
3181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3182 %                                                                             %
3183 %                                                                             %
3184 %                                                                             %
3185 %   U n r e g i s t e r S V G I m a g e                                       %
3186 %                                                                             %
3187 %                                                                             %
3188 %                                                                             %
3189 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3190 %
3191 %  UnregisterSVGImage() removes format registrations made by the
3192 %  SVG module from the list of supported formats.
3193 %
3194 %  The format of the UnregisterSVGImage method is:
3195 %
3196 %      UnregisterSVGImage(void)
3197 %
3198 */
3199 ModuleExport void UnregisterSVGImage(void)
3200 {
3201   (void) UnregisterMagickInfo("SVGZ");
3202   (void) UnregisterMagickInfo("SVG");
3203   (void) UnregisterMagickInfo("MSVG");
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         GetPixelInfoPixel(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-alpha",keyword) == 0)
3700           {
3701             GetMagickToken(q,&q,token);
3702             (void) FormatLocaleString(message,MaxTextExtent,
3703               "fill-alpha:%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 }