% 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, %
#endif
#endif
\f
+/*
+ Define declarations.
+*/
+#define DefaultSVGDensity 96.0
+\f
/*
Typedef declarations.
*/
{
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);
}
}
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);
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)
{
}
tokens[i]=AcquireString(p);
(void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
- StripString(tokens[i]);
+ SVGStripString(MagickTrue,tokens[i]);
i++;
p=q+1;
}
tokens[i]=AcquireString(p);
(void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
- StripString(tokens[i++]);
+ SVGStripString(MagickTrue,tokens[i++]);
tokens[i]=(char *) NULL;
*number_tokens=(size_t) i;
return(tokens);
(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);
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",
}
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':
{
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);
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;
{
if (*svg_info->text != '\0')
{
- DrawInfo
- *draw_info;
-
- TypeMetric
- metrics;
-
char
*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';
}
PushGraphicContext(id);
}
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;
}
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':
{
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);
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=0.0;
- affine.ty=0.0;
- 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;
(*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);
}
}
(void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
- units=DestroyString(units);
+ if (units != (char *) NULL)
+ units=DestroyString(units);
if (color != (char *) NULL)
color=DestroyString(color);
}
{
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);
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,
{
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);
}
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");
}
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");
}
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");
{
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;
}
keyword=(char *) tokens[j];
value=(char *) tokens[j+1];
(void) FormatLocaleFile(svg_info->file,"push class \"%s\"\n",
- keyword);
+ *keyword == '.' ? keyword+1 : keyword);
SVGProcessStyleElement(context,name,value);
(void) FormatLocaleFile(svg_info->file,"pop class\n");
}
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;
{
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");
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
(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
va_end(operands);
}
+static void SVGError(void *,const char *,...)
+ magick_attribute((__format__ (__printf__,2,3)));
+
static void SVGError(void *context,const char *format,...)
{
char
static char
SVGDensityGeometry[] = "96.0x96.0";
-
static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
char
(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
}
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
{
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,
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);
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);
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;