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