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