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