]> granicus.if.org Git - imagemagick/blobdiff - coders/svg.c
...
[imagemagick] / coders / svg.c
index bce4758910002c1c8a185ba229729ea035126e12..60a55c32a25776fc0f5f51adc37b032253ae4633 100644 (file)
@@ -24,7 +24,7 @@
 %  You may not use this file except in compliance with the License.  You may  %
 %  obtain a copy of the License at                                            %
 %                                                                             %
-%    https://www.imagemagick.org/script/license.php                           %
+%    https://imagemagick.org/script/license.php                               %
 %                                                                             %
 %  Unless required by applicable law or agreed to in writing, software        %
 %  distributed under the License is distributed on an "AS IS" BASIS,          %
@@ -36,8 +36,7 @@
 %
 %
 */
-
-
+\f
 /*
   Include declarations.
 */
 #include "librsvg/librsvg-features.h"
 #endif
 #endif
-
-
+\f
+/*
+  Define declarations.
+*/
+#define DefaultSVGDensity  96.0
+\f
 /*
   Typedef declarations.
 */
@@ -186,9 +189,11 @@ typedef struct _SVGInfo
   xmlDocPtr
     document;
 #endif
-} SVGInfo;
-
 
+  ssize_t
+    svgDepth;
+} SVGInfo;
+\f
 /*
   Forward declarations.
 */
@@ -224,7 +229,11 @@ static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
 {
   if (length < 4)
     return(MagickFalse);
-  if (LocaleNCompare((const char *) magick,"?xml",4) == 0)
+  if (LocaleNCompare((const char *) magick+1,"svg",3) == 0)
+    return(MagickTrue);
+  if (length < 5)
+    return(MagickFalse);
+  if (LocaleNCompare((const char *) magick+1,"?xml",4) == 0)
     return(MagickTrue);
   return(MagickFalse);
 }
@@ -328,142 +337,24 @@ static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
     }
   GetNextToken(p,&p,MagickPathExtent,token);
   if (LocaleNCompare(token,"cm",2) == 0)
-    return(96.0*svg_info->scale[0]/2.54*value);
+    return(DefaultSVGDensity*svg_info->scale[0]/2.54*value);
   if (LocaleNCompare(token,"em",2) == 0)
     return(svg_info->pointsize*value);
   if (LocaleNCompare(token,"ex",2) == 0)
     return(svg_info->pointsize*value/2.0);
   if (LocaleNCompare(token,"in",2) == 0)
-    return(96.0*svg_info->scale[0]*value);
+    return(DefaultSVGDensity*svg_info->scale[0]*value);
   if (LocaleNCompare(token,"mm",2) == 0)
-    return(96.0*svg_info->scale[0]/25.4*value);
+    return(DefaultSVGDensity*svg_info->scale[0]/25.4*value);
   if (LocaleNCompare(token,"pc",2) == 0)
-    return(96.0*svg_info->scale[0]/6.0*value);
+    return(DefaultSVGDensity*svg_info->scale[0]/6.0*value);
   if (LocaleNCompare(token,"pt",2) == 0)
-    return(1.25*svg_info->scale[0]*value);
+    return(svg_info->scale[0]*value);
   if (LocaleNCompare(token,"px",2) == 0)
     return(value);
   return(value);
 }
 
-static void StripStyleTokens(char *message)
-{
-  register char
-    *p,
-    *q;
-
-  size_t
-    length;
-
-  assert(message != (char *) NULL);
-  if (*message == '\0')
-    return;
-  length=strlen(message);
-  p=message;
-  while (isspace((int) ((unsigned char) *p)) != 0)
-    p++;
-  q=message+length-1;
-  while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
-    q--;
-  (void) memcpy(message,p,(size_t) (q-p+1));
-  message[q-p+1]='\0';
-  StripString(message);
-}
-
-static char **GetStyleTokens(void *context,const char *style,
-  size_t *number_tokens)
-{
-  char
-    *text,
-    **tokens;
-
-  register ssize_t
-    i;
-
-  SVGInfo
-    *svg_info;
-
-  svg_info=(SVGInfo *) context;
-  (void) svg_info;
-  *number_tokens=0;
-  if (style == (const char *) NULL)
-    return((char **) NULL);
-  text=AcquireString(style);
-  (void) SubstituteString(&text,":","\n");
-  (void) SubstituteString(&text,";","\n");
-  tokens=StringToList(text);
-  text=DestroyString(text);
-  for (i=0; tokens[i] != (char *) NULL; i++)
-    StripStyleTokens(tokens[i]);
-  *number_tokens=(size_t) i;
-  return(tokens);
-}
-
-static char **GetTransformTokens(void *context,const char *text,
-  size_t *number_tokens)
-{
-  char
-    **tokens;
-
-  register const char
-    *p,
-    *q;
-
-  register ssize_t
-    i;
-
-  size_t
-    extent;
-
-  SVGInfo
-    *svg_info;
-
-  svg_info=(SVGInfo *) context;
-  *number_tokens=0;
-  if (text == (const char *) NULL)
-    return((char **) NULL);
-  extent=8;
-  tokens=(char **) AcquireQuantumMemory(extent+2UL,sizeof(*tokens));
-  if (tokens == (char **) NULL)
-    {
-      (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
-        ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
-      return((char **) NULL);
-    }
-  /*
-    Convert string to an ASCII list.
-  */
-  i=0;
-  p=text;
-  for (q=p; *q != '\0'; q++)
-  {
-    if ((*q != '(') && (*q != ')') && (*q != '\0'))
-      continue;
-    if (i == (ssize_t) extent)
-      {
-        extent<<=1;
-        tokens=(char **) ResizeQuantumMemory(tokens,extent+2,sizeof(*tokens));
-        if (tokens == (char **) NULL)
-          {
-            (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
-              ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
-            return((char **) NULL);
-          }
-      }
-    tokens[i]=AcquireString(p);
-    (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
-    StripString(tokens[i]);
-    i++;
-    p=q+1;
-  }
-  tokens[i]=AcquireString(p);
-  (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
-  StripString(tokens[i++]);
-  tokens[i]=(char *) NULL;
-  *number_tokens=(size_t) i;
-  return(tokens);
-}
-
 #if defined(__cplusplus) || defined(c_plusplus)
 extern "C" {
 #endif
@@ -667,6 +558,131 @@ static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
         name,(xmlElementTypeVal) type,content);
 }
 
+static void SVGStripString(const MagickBooleanType trim,char *message)
+{
+  register char
+    *p,
+    *q;
+
+  size_t
+    length;
+
+  assert(message != (char *) NULL);
+  if (*message == '\0')
+    return;
+  /*
+    Remove comment.
+  */
+  q=message;
+  for (p=message; *p != '\0'; p++)
+  {
+    if ((*p == '/') && (*(p+1) == '*'))
+      {
+        for ( ; *p != '\0'; p++)
+          if ((*p == '*') && (*(p+1) == '/'))
+            {
+              p+=2;
+              break;
+            }
+        if (*p == '\0')
+          break;
+      }
+    *q++=(*p);
+  }
+  *q='\0';
+  if (trim != MagickFalse)
+    {
+      /*
+        Remove whitespace.
+      */
+      length=strlen(message);
+      p=message;
+      while (isspace((int) ((unsigned char) *p)) != 0)
+        p++;
+      if ((*p == '\'') || (*p == '"'))
+        p++;
+      q=message+length-1;
+      while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
+        q--;
+      if (q > p)
+        if ((*q == '\'') || (*q == '"'))
+          q--;
+      (void) memmove(message,p,(size_t) (q-p+1));
+      message[q-p+1]='\0';
+    }
+  /*
+    Convert newlines to a space.
+  */
+  for (p=message; *p != '\0'; p++)
+    if (*p == '\n')
+      *p=' ';
+}
+
+static char **SVGKeyValuePairs(void *context,const int key_sentinel,
+  const int value_sentinel,const char *text,size_t *number_tokens)
+{
+  char
+    **tokens;
+
+  register const char
+    *p,
+    *q;
+
+  register ssize_t
+    i;
+
+  size_t
+    extent;
+
+  SVGInfo
+    *svg_info;
+
+  svg_info=(SVGInfo *) context;
+  *number_tokens=0;
+  if (text == (const char *) NULL)
+    return((char **) NULL);
+  extent=8;
+  tokens=(char **) AcquireQuantumMemory(extent+2UL,sizeof(*tokens));
+  if (tokens == (char **) NULL)
+    {
+      (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
+        ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
+      return((char **) NULL);
+    }
+  /*
+    Convert string to an ASCII list.
+  */
+  i=0;
+  p=text;
+  for (q=p; *q != '\0'; q++)
+  {
+    if ((*q != key_sentinel) && (*q != value_sentinel) && (*q != '\0'))
+      continue;
+    if (i == (ssize_t) extent)
+      {
+        extent<<=1;
+        tokens=(char **) ResizeQuantumMemory(tokens,extent+2,sizeof(*tokens));
+        if (tokens == (char **) NULL)
+          {
+            (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
+              ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
+            return((char **) NULL);
+          }
+      }
+    tokens[i]=AcquireString(p);
+    (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
+    SVGStripString(MagickTrue,tokens[i]);
+    i++;
+    p=q+1;
+  }
+  tokens[i]=AcquireString(p);
+  (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
+  SVGStripString(MagickTrue,tokens[i++]);
+  tokens[i]=(char *) NULL;
+  *number_tokens=(size_t) i;
+  return(tokens);
+}
+
 static void SVGNotationDeclaration(void *context,const xmlChar *name,
   const xmlChar *public_id,const xmlChar *system_id)
 {
@@ -694,6 +710,339 @@ static void SVGNotationDeclaration(void *context,const xmlChar *name,
         name,public_id,system_id);
 }
 
+static void SVGProcessStyleElement(void *context,const xmlChar *name,
+  const char *style)
+{
+  char
+    background[MagickPathExtent],
+    *color,
+    *keyword,
+    *units,
+    *value;
+
+  char
+    **tokens;
+
+  register ssize_t
+    i;
+
+  size_t
+    number_tokens;
+
+  SVGInfo
+    *svg_info;
+
+  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
+  svg_info=(SVGInfo *) context;
+  tokens=SVGKeyValuePairs(context,':',';',style,&number_tokens);
+  if (tokens == (char **) NULL)
+    return;
+  for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
+  {
+    keyword=(char *) tokens[i];
+    value=(char *) tokens[i+1];
+    if (LocaleCompare(keyword,"font-size") != 0)
+      continue;
+    svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
+    (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
+      svg_info->pointsize);
+  }
+  color=AcquireString("none");
+  units=AcquireString("userSpaceOnUse");
+  for (i=0; i < (ssize_t) (number_tokens-1); i+=2)
+  {
+    keyword=(char *) tokens[i];
+    value=(char *) tokens[i+1];
+    (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    %s: %s",keyword,
+      value);
+    switch (*keyword)
+    {
+      case 'B':
+      case 'b':
+      {
+        if (LocaleCompare((const char *) name,"background") == 0)
+          {
+            if (LocaleCompare((const char *) name,"svg") == 0)
+              (void) CopyMagickString(background,value,MagickPathExtent);
+            break;
+          }
+        break;
+      }
+      case 'C':
+      case 'c':
+      {
+         if (LocaleCompare(keyword,"clip-path") == 0)
+           {
+             (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",value);
+             break;
+           }
+        if (LocaleCompare(keyword,"clip-rule") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"clip-rule \"%s\"\n",value);
+            break;
+          }
+         if (LocaleCompare(keyword,"clipPathUnits") == 0)
+           {
+             (void) CloneString(&units,value);
+             (void) FormatLocaleFile(svg_info->file,"clip-units \"%s\"\n",
+               value);
+             break;
+           }
+        if (LocaleCompare(keyword,"color") == 0)
+          {
+            (void) CloneString(&color,value);
+            break;
+          }
+        break;
+      }
+      case 'F':
+      case 'f':
+      {
+        if (LocaleCompare(keyword,"fill") == 0)
+          {
+             if (LocaleCompare(value,"currentColor") == 0)
+               {
+                 (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",color);
+                 break;
+               }
+            if (LocaleCompare(value,"#000000ff") == 0)
+              (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
+            else
+              (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
+            break;
+          }
+        if (LocaleCompare(keyword,"fillcolor") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",value);
+            break;
+          }
+        if (LocaleCompare(keyword,"fill-rule") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"fill-rule \"%s\"\n",value);
+            break;
+          }
+        if (LocaleCompare(keyword,"fill-opacity") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"fill-opacity \"%s\"\n",
+              value);
+            break;
+          }
+        if (LocaleCompare(keyword,"font") == 0)
+          {
+            char
+              family[MagickPathExtent],
+              size[MagickPathExtent],
+              style[MagickPathExtent];
+
+            if (sscanf(value,"%2048s %2048s %2048s",style,size,family) != 3)
+              break;
+            if (GetUserSpaceCoordinateValue(svg_info,0,style) == 0)
+              (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",
+                style);
+            else
+              if (sscanf(value,"%2048s %2048s",size,family) != 2)
+                break;
+            (void) FormatLocaleFile(svg_info->file,"font-size \"%s\"\n",size);
+            (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
+              family);
+            break;
+          }
+        if (LocaleCompare(keyword,"font-family") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n",
+              value);
+            break;
+          }
+        if (LocaleCompare(keyword,"font-stretch") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"font-stretch \"%s\"\n",
+              value);
+            break;
+          }
+        if (LocaleCompare(keyword,"font-style") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n",value);
+            break;
+          }
+        if (LocaleCompare(keyword,"font-size") == 0)
+          {
+            svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
+            (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
+              svg_info->pointsize);
+            break;
+          }
+        if (LocaleCompare(keyword,"font-weight") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"font-weight \"%s\"\n",
+              value);
+            break;
+          }
+        break;
+      }
+      case 'K':
+      case 'k':
+      {
+        if (LocaleCompare(keyword,"kerning") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"kerning \"%s\"\n",value);
+            break;
+          }
+        break;
+      }
+      case 'L':
+      case 'l':
+      {
+        if (LocaleCompare(keyword,"letter-spacing") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"letter-spacing \"%s\"\n",
+              value);
+            break;
+          }
+        break;
+      }
+      case 'M':
+      case 'm':
+      {
+        if (LocaleCompare(keyword,"mask") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
+            break;
+          }
+        break;
+      }
+      case 'O':
+      case 'o':
+      {
+        if (LocaleCompare(keyword,"offset") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"offset %g\n",
+              GetUserSpaceCoordinateValue(svg_info,1,value));
+            break;
+          }
+        if (LocaleCompare(keyword,"opacity") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value);
+            break;
+          }
+        break;
+      }
+      case 'S':
+      case 's':
+      {
+        if (LocaleCompare(keyword,"stop-color") == 0)
+          {
+            (void) CloneString(&svg_info->stop_color,value);
+            break;
+          }
+        if (LocaleCompare(keyword,"stroke") == 0)
+          {
+            if (LocaleCompare(value,"currentColor") == 0)
+              {
+                (void) FormatLocaleFile(svg_info->file,"stroke \"%s\"\n",color);
+                break;
+              }
+            if (LocaleCompare(value,"#000000ff") == 0)
+              (void) FormatLocaleFile(svg_info->file,"fill '#000000'\n");
+            else
+              (void) FormatLocaleFile(svg_info->file,
+                "stroke \"%s\"\n",value);
+            break;
+          }
+        if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
+              LocaleCompare(value,"true") == 0);
+            break;
+          }
+        if (LocaleCompare(keyword,"stroke-dasharray") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
+              value);
+            break;
+          }
+        if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %g\n",
+              GetUserSpaceCoordinateValue(svg_info,1,value));
+            break;
+          }
+        if (LocaleCompare(keyword,"stroke-linecap") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"stroke-linecap \"%s\"\n",
+              value);
+            break;
+          }
+        if (LocaleCompare(keyword,"stroke-linejoin") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"stroke-linejoin \"%s\"\n",
+              value);
+            break;
+          }
+        if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit \"%s\"\n",
+              value);
+            break;
+          }
+        if (LocaleCompare(keyword,"stroke-opacity") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"stroke-opacity \"%s\"\n",
+              value);
+            break;
+          }
+        if (LocaleCompare(keyword,"stroke-width") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
+              GetUserSpaceCoordinateValue(svg_info,1,value));
+            break;
+          }
+        break;
+      }
+      case 't':
+      case 'T':
+      {
+        if (LocaleCompare(keyword,"text-align") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"text-align \"%s\"\n",value);
+            break;
+          }
+        if (LocaleCompare(keyword,"text-anchor") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"text-anchor \"%s\"\n",
+              value);
+            break;
+          }
+        if (LocaleCompare(keyword,"text-decoration") == 0)
+          {
+            if (LocaleCompare(value,"underline") == 0)
+              (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
+            if (LocaleCompare(value,"line-through") == 0)
+              (void) FormatLocaleFile(svg_info->file,"decorate line-through\n");
+            if (LocaleCompare(value,"overline") == 0)
+              (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
+            break;
+          }
+        if (LocaleCompare(keyword,"text-antialiasing") == 0)
+          {
+            (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
+              LocaleCompare(value,"true") == 0);
+            break;
+          }
+        break;
+      }
+      default:
+        break;
+    }
+  }
+  if (units != (char *) NULL)
+    units=DestroyString(units);
+  if (color != (char *) NULL)
+    color=DestroyString(color);
+  for (i=0; tokens[i] != (char *) NULL; i++)
+    tokens[i]=DestroyString(tokens[i]);
+  tokens=(char **) RelinquishMagickMemory(tokens);
+}
+
 static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
   const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
 {
@@ -940,8 +1289,8 @@ static void SVGStartElement(void *context,const xmlChar *name,
           if (LocaleCompare(keyword,"x") == 0)
             {
               if (LocaleCompare((char *) name,"tspan") != 0)
-                svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value)-
-                  svg_info->center.x;
+                svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,
+                  value)-svg_info->center.x;
               break;
             }
           if (LocaleCompare(keyword,"x1") == 0)
@@ -1079,6 +1428,16 @@ static void SVGStartElement(void *context,const xmlChar *name,
         }
       break;
     }
+    case 'M':
+    case 'm':
+    {
+      if (LocaleCompare((const char *) name,"mask") == 0)
+        {
+          (void) FormatLocaleFile(svg_info->file,"push mask \"%s\"\n",id);
+          break;
+        }
+      break;
+    }
     case 'P':
     case 'p':
     {
@@ -1129,14 +1488,13 @@ static void SVGStartElement(void *context,const xmlChar *name,
     case 'S':
     case 's':
     {
-      if (LocaleCompare((const char *) name,"symbol") == 0)
-        {
-          (void) FormatLocaleFile(svg_info->file,"push symbol\n");
-          break;
-        }
+      if (LocaleCompare((char *) name,"style") == 0)
+        break;
       if (LocaleCompare((const char *) name,"svg") == 0)
         {
+          svg_info->svgDepth++;
           PushGraphicContext(id);
+          (void) FormatLocaleFile(svg_info->file,"compliance \"SVG\"\n");
           (void) FormatLocaleFile(svg_info->file,"fill \"black\"\n");
           (void) FormatLocaleFile(svg_info->file,"fill-opacity 1\n");
           (void) FormatLocaleFile(svg_info->file,"stroke \"none\"\n");
@@ -1145,6 +1503,11 @@ static void SVGStartElement(void *context,const xmlChar *name,
           (void) FormatLocaleFile(svg_info->file,"fill-rule nonzero\n");
           break;
         }
+      if (LocaleCompare((const char *) name,"symbol") == 0)
+        {
+          (void) FormatLocaleFile(svg_info->file,"push symbol\n");
+          break;
+        }
       break;
     }
     case 'T':
@@ -1153,6 +1516,11 @@ static void SVGStartElement(void *context,const xmlChar *name,
       if (LocaleCompare((const char *) name,"text") == 0)
         {
           PushGraphicContext(id);
+          (void) FormatLocaleFile(svg_info->file,"class \"text\"\n");
+          (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
+            svg_info->bounds.x,svg_info->bounds.y);
+          svg_info->center.x=svg_info->bounds.x;
+          svg_info->center.y=svg_info->bounds.y;
           svg_info->bounds.x=0.0;
           svg_info->bounds.y=0.0;
           svg_info->bounds.width=0.0;
@@ -1163,12 +1531,6 @@ static void SVGStartElement(void *context,const xmlChar *name,
         {
           if (*svg_info->text != '\0')
             {
-              DrawInfo
-                *draw_info;
-
-              TypeMetric
-                metrics;
-
               char
                 *text;
 
@@ -1177,14 +1539,6 @@ static void SVGStartElement(void *context,const xmlChar *name,
                 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
                 svg_info->center.y,text);
               text=DestroyString(text);
-              draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
-              draw_info->pointsize=svg_info->pointsize;
-              draw_info->text=AcquireString(svg_info->text);
-              (void) ConcatenateString(&draw_info->text," ");
-              (void) GetTypeMetrics(svg_info->image,draw_info,
-                &metrics,svg_info->exception);
-              svg_info->bounds.x+=metrics.width;
-              draw_info=DestroyDrawInfo(draw_info);
               *svg_info->text='\0';
             }
           PushGraphicContext(id);
@@ -1228,6 +1582,25 @@ static void SVGStartElement(void *context,const xmlChar *name,
         case 'C':
         case 'c':
         {
+          if (LocaleCompare(keyword,"class") == 0)
+            {
+              const char
+                *p;
+
+              for (p=value; ; )
+              {
+                GetNextToken(p,&p,MagickPathExtent,token);
+                if (*token == ',')
+                  GetNextToken(p,&p,MagickPathExtent,token);
+                if (*token != '\0')
+                  {
+                    (void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",
+                      value);
+                    break;
+                  }
+              }
+              break;
+            }
           if (LocaleCompare(keyword,"clip-path") == 0)
             {
               (void) FormatLocaleFile(svg_info->file,"clip-path \"%s\"\n",
@@ -1276,13 +1649,24 @@ static void SVGStartElement(void *context,const xmlChar *name,
             }
           if (LocaleCompare(keyword,"dx") == 0)
             {
-              svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
+              double
+                dx;
+
+              dx=GetUserSpaceCoordinateValue(svg_info,1,value);
+              svg_info->bounds.x+=dx;
+              if (LocaleCompare((char *) name,"text") == 0)
+                (void) FormatLocaleFile(svg_info->file,"translate %g,0.0\n",dx);
               break;
             }
           if (LocaleCompare(keyword,"dy") == 0)
             {
-              svg_info->bounds.y+=
-                GetUserSpaceCoordinateValue(svg_info,-1,value);
+              double
+                dy;
+
+              dy=GetUserSpaceCoordinateValue(svg_info,-1,value);
+              svg_info->bounds.y+=dy;
+              if (LocaleCompare((char *) name,"text") == 0)
+                (void) FormatLocaleFile(svg_info->file,"translate 0.0,%g\n",dy);
               break;
             }
           break;
@@ -1378,7 +1762,7 @@ static void SVGStartElement(void *context,const xmlChar *name,
 
               GetAffineMatrix(&transform);
               (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
-              tokens=GetTransformTokens(context,value,&number_tokens);
+              tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
               if (tokens == (char **) NULL)
                 break;
               for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
@@ -1543,6 +1927,28 @@ static void SVGStartElement(void *context,const xmlChar *name,
             }
           break;
         }
+        case 'K':
+        case 'k':
+        {
+          if (LocaleCompare(keyword,"kerning") == 0)
+            {
+              (void) FormatLocaleFile(svg_info->file,"kerning \"%s\"\n",
+                value);
+              break;
+            }
+          break;
+        }
+        case 'L':
+        case 'l':
+        {
+          if (LocaleCompare(keyword,"letter-spacing") == 0)
+            {
+              (void) FormatLocaleFile(svg_info->file,"letter-spacing \"%s\"\n",
+                value);
+              break;
+            }
+          break;
+        }
         case 'M':
         case 'm':
         {
@@ -1552,6 +1958,11 @@ static void SVGStartElement(void *context,const xmlChar *name,
                 GetUserSpaceCoordinateValue(svg_info,1,value);
               break;
             }
+          if (LocaleCompare(keyword,"mask") == 0)
+            {
+              (void) FormatLocaleFile(svg_info->file,"mask \"%s\"\n",value);
+              break;
+            }
           if (LocaleCompare(keyword,"minor") == 0)
             {
               svg_info->element.minor=
@@ -1705,266 +2116,7 @@ static void SVGStartElement(void *context,const xmlChar *name,
             }
           if (LocaleCompare(keyword,"style") == 0)
             {
-              (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
-              tokens=GetStyleTokens(context,value,&number_tokens);
-              for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
-              {
-                keyword=(char *) tokens[j];
-                value=(char *) tokens[j+1];
-                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                  "    %s: %s",keyword,value);
-                switch (*keyword)
-                {
-                  case 'B':
-                  case 'b':
-                  {
-                    if (LocaleCompare((const char *) name,"background") == 0)
-                      {
-                        if (LocaleCompare((const char *) name,"svg") == 0)
-                          (void) CopyMagickString(background,value,
-                            MagickPathExtent);
-                        break;
-                      }
-                    break;
-                  }
-                  case 'C':
-                  case 'c':
-                  {
-                     if (LocaleCompare(keyword,"clip-path") == 0)
-                       {
-                         (void) FormatLocaleFile(svg_info->file,
-                           "clip-path \"%s\"\n",value);
-                         break;
-                       }
-                    if (LocaleCompare(keyword,"clip-rule") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "clip-rule \"%s\"\n",value);
-                        break;
-                      }
-                     if (LocaleCompare(keyword,"clipPathUnits") == 0)
-                       {
-                         (void) CloneString(&units,value);
-                         (void) FormatLocaleFile(svg_info->file,
-                          "clip-units \"%s\"\n",value);
-                         break;
-                       }
-                    if (LocaleCompare(keyword,"color") == 0)
-                      {
-                        (void) CloneString(&color,value);
-                        break;
-                      }
-                    break;
-                  }
-                  case 'F':
-                  case 'f':
-                  {
-                    if (LocaleCompare(keyword,"fill") == 0)
-                      {
-                         if (LocaleCompare(value,"currentColor") == 0)
-                           {
-                             (void) FormatLocaleFile(svg_info->file,
-                               "fill \"%s\"\n",color);
-                             break;
-                           }
-                        if (LocaleCompare(value,"#000000ff") == 0)
-                          (void) FormatLocaleFile(svg_info->file,
-                            "fill '#000000'\n");
-                        else
-                          (void) FormatLocaleFile(svg_info->file,
-                            "fill \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"fillcolor") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,"fill \"%s\"\n",
-                          value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"fill-rule") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "fill-rule \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"fill-opacity") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "fill-opacity \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"font-family") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "font-family \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"font-stretch") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "font-stretch \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"font-style") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "font-style \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"font-size") == 0)
-                      {
-                        svg_info->pointsize=GetUserSpaceCoordinateValue(
-                          svg_info,0,value);
-                        (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
-                          svg_info->pointsize);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"font-weight") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "font-weight \"%s\"\n",value);
-                        break;
-                      }
-                    break;
-                  }
-                  case 'O':
-                  case 'o':
-                  {
-                    if (LocaleCompare(keyword,"offset") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,"offset %g\n",
-                          GetUserSpaceCoordinateValue(svg_info,1,value));
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"opacity") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "opacity \"%s\"\n",value);
-                        break;
-                      }
-                    break;
-                  }
-                  case 'S':
-                  case 's':
-                  {
-                    if (LocaleCompare(keyword,"stop-color") == 0)
-                      {
-                        (void) CloneString(&svg_info->stop_color,value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"stroke") == 0)
-                      {
-                         if (LocaleCompare(value,"currentColor") == 0)
-                           {
-                             (void) FormatLocaleFile(svg_info->file,
-                               "stroke \"%s\"\n",color);
-                             break;
-                           }
-                        if (LocaleCompare(value,"#000000ff") == 0)
-                          (void) FormatLocaleFile(svg_info->file,
-                            "fill '#000000'\n");
-                        else
-                          (void) FormatLocaleFile(svg_info->file,
-                            "stroke \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "stroke-antialias %d\n",
-                          LocaleCompare(value,"true") == 0);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"stroke-dasharray") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "stroke-dasharray %s\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "stroke-dashoffset %g\n",
-                          GetUserSpaceCoordinateValue(svg_info,1,value));
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"stroke-linecap") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "stroke-linecap \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"stroke-linejoin") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "stroke-linejoin \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "stroke-miterlimit \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"stroke-opacity") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "stroke-opacity \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"stroke-width") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "stroke-width %g\n",
-                          GetUserSpaceCoordinateValue(svg_info,1,value));
-                        break;
-                      }
-                    break;
-                  }
-                  case 't':
-                  case 'T':
-                  {
-                    if (LocaleCompare(keyword,"text-align") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "text-align \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"text-anchor") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "text-anchor \"%s\"\n",value);
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"text-decoration") == 0)
-                      {
-                        if (LocaleCompare(value,"underline") == 0)
-                          (void) FormatLocaleFile(svg_info->file,
-                          "decorate underline\n");
-                        if (LocaleCompare(value,"line-through") == 0)
-                          (void) FormatLocaleFile(svg_info->file,
-                          "decorate line-through\n");
-                        if (LocaleCompare(value,"overline") == 0)
-                          (void) FormatLocaleFile(svg_info->file,
-                          "decorate overline\n");
-                        break;
-                      }
-                    if (LocaleCompare(keyword,"text-antialiasing") == 0)
-                      {
-                        (void) FormatLocaleFile(svg_info->file,
-                          "text-antialias %d\n",
-                          LocaleCompare(value,"true") == 0);
-                        break;
-                      }
-                    break;
-                  }
-                  default:
-                    break;
-                }
-              }
-              for (j=0; tokens[j] != (char *) NULL; j++)
-                tokens[j]=DestroyString(tokens[j]);
-              tokens=(char **) RelinquishMagickMemory(tokens);
+              SVGProcessStyleElement(context,name,value);
               break;
             }
           break;
@@ -2010,7 +2162,7 @@ static void SVGStartElement(void *context,const xmlChar *name,
 
               GetAffineMatrix(&transform);
               (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
-              tokens=GetTransformTokens(context,value,&number_tokens);
+              tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens);
               if (tokens == (char **) NULL)
                 break;
               for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
@@ -2068,6 +2220,10 @@ static void SVGStartElement(void *context,const xmlChar *name,
                         p=(const char *) value;
                         GetNextToken(p,&p,MagickPathExtent,token);
                         angle=StringToDouble(value,(char **) NULL);
+                        affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
+                        affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
+                        affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
+                        affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
                         GetNextToken(p,&p,MagickPathExtent,token);
                         if (*token == ',')
                           GetNextToken(p,&p,MagickPathExtent,token);
@@ -2076,14 +2232,14 @@ static void SVGStartElement(void *context,const xmlChar *name,
                         if (*token == ',')
                           GetNextToken(p,&p,MagickPathExtent,token);
                         y=StringToDouble(token,&next_token);
-                        affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
-                        affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
-                        affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
-                        affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
-                        affine.tx=x;
-                        affine.ty=y;
-                        svg_info->center.x=x;
-                        svg_info->center.y=y;
+                        affine.tx=svg_info->bounds.x+x*
+                          cos(DegreesToRadians(fmod(angle,360.0)))+y*
+                          sin(DegreesToRadians(fmod(angle,360.0)));
+                        affine.ty=svg_info->bounds.y-x*
+                          sin(DegreesToRadians(fmod(angle,360.0)))+y*
+                          cos(DegreesToRadians(fmod(angle,360.0)));
+                        affine.tx-=x/2.0;
+                        affine.ty-=y/2.0;
                         break;
                       }
                     break;
@@ -2135,7 +2291,7 @@ static void SVGStartElement(void *context,const xmlChar *name,
                               (*p == ','))
                             break;
                         affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
-                        affine.ty=affine.tx;
+                        affine.ty=0;
                         if (*p != '\0')
                           affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
                             p+1);
@@ -2296,7 +2452,7 @@ static void SVGStartElement(void *context,const xmlChar *name,
             0.0;
           (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
             sx,sy,tx,ty);
-          if (*background != '\0')
+          if ((svg_info->svgDepth == 1) && (*background != '\0'))
             {
               PushGraphicContext(id);
               (void) FormatLocaleFile(svg_info->file,"fill %s\n",background);
@@ -2308,7 +2464,8 @@ static void SVGStartElement(void *context,const xmlChar *name,
         }
     }
   (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  )");
-  units=DestroyString(units);
+  if (units != (char *) NULL)
+    units=DestroyString(units);
   if (color != (char *) NULL)
     color=DestroyString(color);
 }
@@ -2339,6 +2496,7 @@ static void SVGEndElement(void *context,const xmlChar *name)
     {
       if (LocaleCompare((const char *) name,"circle") == 0)
         {
+          (void) FormatLocaleFile(svg_info->file,"class \"circle\"\n");
           (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
             svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
             svg_info->element.cy+svg_info->element.minor);
@@ -2388,6 +2546,7 @@ static void SVGEndElement(void *context,const xmlChar *name)
           double
             angle;
 
+          (void) FormatLocaleFile(svg_info->file,"class \"ellipse\"\n");
           angle=svg_info->element.angle;
           (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
             svg_info->element.cx,svg_info->element.cy,
@@ -2437,6 +2596,7 @@ static void SVGEndElement(void *context,const xmlChar *name)
     {
       if (LocaleCompare((const char *) name,"line") == 0)
         {
+          (void) FormatLocaleFile(svg_info->file,"class \"line\"\n");
           (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
             svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
             svg_info->segment.y2);
@@ -2450,6 +2610,16 @@ static void SVGEndElement(void *context,const xmlChar *name)
         }
       break;
     }
+    case 'M':
+    case 'm':
+    {
+      if (LocaleCompare((const char *) name,"mask") == 0)
+        {
+          (void) FormatLocaleFile(svg_info->file,"pop mask\n");
+          break;
+        }
+      break;
+    }
     case 'P':
     case 'p':
     {
@@ -2460,6 +2630,7 @@ static void SVGEndElement(void *context,const xmlChar *name)
         }
       if (LocaleCompare((const char *) name,"path") == 0)
         {
+          (void) FormatLocaleFile(svg_info->file,"class \"path\"\n");
           (void) FormatLocaleFile(svg_info->file,"path \"%s\"\n",
             svg_info->vertices);
           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
@@ -2467,6 +2638,7 @@ static void SVGEndElement(void *context,const xmlChar *name)
         }
       if (LocaleCompare((const char *) name,"polygon") == 0)
         {
+          (void) FormatLocaleFile(svg_info->file,"class \"polygon\"\n");
           (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
             svg_info->vertices);
           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
@@ -2474,6 +2646,7 @@ static void SVGEndElement(void *context,const xmlChar *name)
         }
       if (LocaleCompare((const char *) name,"polyline") == 0)
         {
+          (void) FormatLocaleFile(svg_info->file,"class \"polyline\"\n");
           (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
             svg_info->vertices);
           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
@@ -2493,10 +2666,16 @@ static void SVGEndElement(void *context,const xmlChar *name)
         {
           if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
             {
-              (void) FormatLocaleFile(svg_info->file,"rectangle %g,%g %g,%g\n",
-                svg_info->bounds.x,svg_info->bounds.y,
-                svg_info->bounds.x+svg_info->bounds.width,
-                svg_info->bounds.y+svg_info->bounds.height);
+              (void) FormatLocaleFile(svg_info->file,"class \"rect\"\n");
+              if ((fabs(svg_info->bounds.width-1.0) < MagickEpsilon) &&
+                  (fabs(svg_info->bounds.height-1.0) < MagickEpsilon))
+                (void) FormatLocaleFile(svg_info->file,"point %g,%g\n",
+                  svg_info->bounds.x,svg_info->bounds.y);
+              else
+                (void) FormatLocaleFile(svg_info->file,
+                  "rectangle %g,%g %g,%g\n",svg_info->bounds.x,
+                  svg_info->bounds.y,svg_info->bounds.x+svg_info->bounds.width,
+                  svg_info->bounds.y+svg_info->bounds.height);
               (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
               break;
             }
@@ -2525,14 +2704,46 @@ static void SVGEndElement(void *context,const xmlChar *name)
             svg_info->stop_color,svg_info->offset);
           break;
         }
-      if (LocaleCompare((const char *) name,"symbol") == 0)
+      if (LocaleCompare((char *) name,"style") == 0)
         {
-          (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
+          char
+            *keyword,
+            **tokens,
+            *value;
+
+          register ssize_t
+            j;
+
+          size_t
+            number_tokens;
+
+          /*
+            Find style definitions in svg_info->text.
+          */
+          tokens=SVGKeyValuePairs(context,'{','}',svg_info->text,
+            &number_tokens);
+          if (tokens == (char **) NULL)
+            break;
+          for (j=0; j < (ssize_t) (number_tokens-1); j+=2)
+          {
+            keyword=(char *) tokens[j];
+            value=(char *) tokens[j+1];
+            (void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
+              *keyword == '.' ? keyword+1 : keyword);
+            SVGProcessStyleElement(context,name,value);
+            (void) FormatLocaleFile(svg_info->file,"pop class\n");
+          }
           break;
         }
       if (LocaleCompare((const char *) name,"svg") == 0)
         {
           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
+          svg_info->svgDepth--;
+          break;
+        }
+      if (LocaleCompare((const char *) name,"symbol") == 0)
+        {
+          (void) FormatLocaleFile(svg_info->file,"pop symbol\n");
           break;
         }
       break;
@@ -2547,11 +2758,13 @@ static void SVGEndElement(void *context,const xmlChar *name)
               char
                 *text;
 
+              SVGStripString(MagickTrue,svg_info->text);
               text=EscapeString(svg_info->text,'\'');
-              (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
-                svg_info->bounds.x,svg_info->bounds.y,text);
+              (void) FormatLocaleFile(svg_info->file,"text 0,0 \"%s\"\n",text);
               text=DestroyString(text);
               *svg_info->text='\0';
+              svg_info->center.x=0.0;
+              svg_info->center.y=0.0;
             }
           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
           break;
@@ -2560,27 +2773,15 @@ static void SVGEndElement(void *context,const xmlChar *name)
         {
           if (*svg_info->text != '\0')
             {
-              DrawInfo
-                *draw_info;
-
-              TypeMetric
-                metrics;
-
               char
                 *text;
 
+              (void) FormatLocaleFile(svg_info->file,"class \"tspan\"\n");
               text=EscapeString(svg_info->text,'\'');
               (void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n",
-                svg_info->bounds.x,svg_info->bounds.y,text);
+                svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
+                svg_info->center.y,text);
               text=DestroyString(text);
-              draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
-              draw_info->pointsize=svg_info->pointsize;
-              draw_info->text=AcquireString(svg_info->text);
-              (void) ConcatenateString(&draw_info->text," ");
-              (void) GetTypeMetrics(svg_info->image,draw_info,&metrics,
-                svg_info->exception);
-              svg_info->bounds.x+=metrics.width;
-              draw_info=DestroyDrawInfo(draw_info);
               *svg_info->text='\0';
             }
           (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
@@ -2647,7 +2848,7 @@ static void SVGCharacters(void *context,const xmlChar *c,int length)
   for (i=0; i < (ssize_t) length; i++)
     *p++=c[i];
   *p='\0';
-  StripString(text);
+  SVGStripString(MagickFalse,text);
   if (svg_info->text == (char *) NULL)
     svg_info->text=text;
   else
@@ -2727,6 +2928,9 @@ static void SVGComment(void *context,const xmlChar *value)
   (void) ConcatenateString(&svg_info->comment,(const char *) value);
 }
 
+static void SVGWarning(void *,const char *,...)
+  magick_attribute((__format__ (__printf__,2,3)));
+
 static void SVGWarning(void *context,const char *format,...)
 {
   char
@@ -2759,6 +2963,9 @@ static void SVGWarning(void *context,const char *format,...)
   va_end(operands);
 }
 
+static void SVGError(void *,const char *,...)
+  magick_attribute((__format__ (__printf__,2,3)));
+
 static void SVGError(void *context,const char *format,...)
 {
   char
@@ -2894,7 +3101,6 @@ static void SVGExternalSubset(void *context,const xmlChar *name,
 static char
   SVGDensityGeometry[] = "96.0x96.0";
 
-
 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
 {
   char
@@ -2904,7 +3110,8 @@ static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
     *file;
 
   Image
-    *image;
+    *image,
+    *next;
 
   int
     status,
@@ -2981,7 +3188,7 @@ static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
             attributes;
 
           /*
-            Our best hope of compliance with the SVG standard.
+            Our best hope for compliance with the SVG standard.
           */
           status=AcquireUniqueSymbolicLink(image->filename,input_filename);
           (void) AcquireUniqueFilename(output_filename);
@@ -3021,6 +3228,14 @@ static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
               (void) RelinquishUniqueFileResource(output_filename);
               if (svg_image != (Image *) NULL)
                 {
+                  for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; )
+                  {
+                    (void) CopyMagickString(next->filename,image->filename,
+                      MaxTextExtent);
+                    (void) CopyMagickString(next->magick,image->magick,
+                      MaxTextExtent);
+                    next=GetNextImageInList(next);
+                  }
                   image=DestroyImage(image);
                   return(svg_image);
                 }
@@ -3124,8 +3339,10 @@ static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
               (ssize_t *) NULL,&image->columns,&image->rows);
             if ((image->columns != 0) || (image->rows != 0))
               {
-                image->resolution.x=96.0*image->columns/dimension_info.width;
-                image->resolution.y=96.0*image->rows/dimension_info.height;
+                image->resolution.x=DefaultSVGDensity*image->columns/
+                  dimension_info.width;
+                image->resolution.y=DefaultSVGDensity*image->rows/
+                  dimension_info.height;
                 if (fabs(image->resolution.x) < MagickEpsilon)
                   image->resolution.x=image->resolution.y;
                 else
@@ -3139,8 +3356,10 @@ static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
           }
         if (apply_density != MagickFalse)
           {
-            image->columns=image->resolution.x*dimension_info.width/96.0;
-            image->rows=image->resolution.y*dimension_info.height/96.0;
+            image->columns=image->resolution.x*dimension_info.width/
+              DefaultSVGDensity;
+            image->rows=image->resolution.y*dimension_info.height/
+              DefaultSVGDensity;
           }
         else
           {
@@ -3155,22 +3374,24 @@ static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
         image->rows=gdk_pixbuf_get_height(pixel_buffer);
 #endif
         image->alpha_trait=BlendPixelTrait;
-        status=SetImageExtent(image,image->columns,image->rows,exception);
-        if (status == MagickFalse)
-          {
-#if !defined(MAGICKCORE_CAIRO_DELEGATE)
-            g_object_unref(G_OBJECT(pixel_buffer));
-#endif
-            g_object_unref(svg_handle);
-            ThrowReaderException(MissingDelegateError,
-              "NoDecodeDelegateForThisImageFormat");
-          }
         if (image_info->ping == MagickFalse)
           {
 #if defined(MAGICKCORE_CAIRO_DELEGATE)
             size_t
               stride;
+#endif
 
+            status=SetImageExtent(image,image->columns,image->rows,exception);
+            if (status == MagickFalse)
+              {
+#if !defined(MAGICKCORE_CAIRO_DELEGATE)
+                g_object_unref(G_OBJECT(pixel_buffer));
+#endif
+                g_object_unref(svg_handle);
+                ThrowReaderException(MissingDelegateError,
+                  "NoDecodeDelegateForThisImageFormat");
+              }
+#if defined(MAGICKCORE_CAIRO_DELEGATE)
             stride=4*image->columns;
 #if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
             stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
@@ -3205,8 +3426,8 @@ static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
             cairo_paint(cairo_image);
             cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
             if (apply_density != MagickFalse)
-              cairo_scale(cairo_image,image->resolution.x/96.0,
-                image->resolution.y/96.0);
+              cairo_scale(cairo_image,image->resolution.x/DefaultSVGDensity,
+                image->resolution.y/DefaultSVGDensity);
             rsvg_handle_render_cairo(svg_handle,cairo_image);
             cairo_destroy(cairo_image);
             cairo_surface_destroy(cairo_surface);
@@ -3267,6 +3488,12 @@ static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
         g_object_unref(G_OBJECT(pixel_buffer));
 #endif
         (void) CloseBlob(image);
+        for (next=GetFirstImageInList(image); next != (Image *) NULL; )
+        {
+          (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
+          (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
+          next=GetNextImageInList(next);
+        }
         return(GetFirstImageInList(image));
 #endif
       }
@@ -3301,6 +3528,7 @@ static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
   svg_info->image_info=image_info;
   svg_info->bounds.width=image->columns;
   svg_info->bounds.height=image->rows;
+  svg_info->svgDepth=0;
   if (image_info->size != (char *) NULL)
     (void) CloneString(&svg_info->size,image_info->size);
   if (image->debug != MagickFalse)
@@ -3377,8 +3605,6 @@ static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
       image=(Image *) NULL;
       read_info=CloneImageInfo(image_info);
       SetImageInfoBlob(read_info,(void *) NULL,0);
-      if (read_info->density != (char *) NULL)
-        read_info->density=DestroyString(read_info->density);
       (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
         filename);
       image=ReadImage(read_info,exception);
@@ -3398,6 +3624,12 @@ static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
         (void) SetImageProperty(image,"svg:comment",svg_info->comment,
           exception);
     }
+  for (next=GetFirstImageInList(image); next != (Image *) NULL; )
+  {
+    (void) CopyMagickString(next->filename,image->filename,MaxTextExtent);
+    (void) CopyMagickString(next->magick,image->magick,MaxTextExtent);
+    next=GetNextImageInList(next);
+  }
   svg_info=DestroySVGInfo(svg_info);
   (void) RelinquishUniqueFileResource(filename);
   return(GetFirstImageInList(image));
@@ -3989,8 +4221,8 @@ static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
         if (LocaleCompare("clip-rule",keyword) == 0)
           {
             GetNextToken(q,&q,extent,token);
-            (void) FormatLocaleString(message,MagickPathExtent,
-              "clip-rule:%s;",token);
+            (void) FormatLocaleString(message,MagickPathExtent,"clip-rule:%s;",
+              token);
             (void) WriteBlobString(image,message);
             break;
           }
@@ -4149,9 +4381,29 @@ static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
         status=MagickFalse;
         break;
       }
+      case 'k':
+      case 'K':
+      {
+        if (LocaleCompare("kerning",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            (void) FormatLocaleString(message,MagickPathExtent,"kerning:%s;",
+              token);
+            (void) WriteBlobString(image,message);
+          }
+        break;
+      }
       case 'l':
       case 'L':
       {
+        if (LocaleCompare("letter-spacing",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            (void) FormatLocaleString(message,MagickPathExtent,
+              "letter-spacing:%s;",token);
+            (void) WriteBlobString(image,message);
+            break;
+          }
         if (LocaleCompare("line",keyword) == 0)
           {
             primitive_type=LinePrimitive;