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