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