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