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