Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
ImageMagick/coders/svg.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
5295 lines (5049 sloc)
165 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
% % | |
% % | |
% % | |
% SSSSS V V GGGG % | |
% SS V V G % | |
% SSS V V G GG % | |
% SS V V G G % | |
% SSSSS V GGG % | |
% % | |
% % | |
% Read/Write Scalable Vector Graphics Format % | |
% % | |
% Software Design % | |
% Cristy % | |
% William Radcliffe % | |
% March 2000 % | |
% % | |
% % | |
% Copyright @ 2000 ImageMagick Studio LLC, a non-profit organization % | |
% dedicated to making software imaging solutions freely available. % | |
% % | |
% You may not use this file except in compliance with the License. You may % | |
% obtain a copy of the License at % | |
% % | |
% 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, % | |
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % | |
% See the License for the specific language governing permissions and % | |
% limitations under the License. % | |
% % | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
% | |
% | |
*/ | |
/* | |
Include declarations. | |
*/ | |
#include "MagickCore/studio.h" | |
#include "MagickCore/annotate.h" | |
#include "MagickCore/artifact.h" | |
#include "MagickCore/attribute.h" | |
#include "MagickCore/blob.h" | |
#include "MagickCore/blob-private.h" | |
#include "MagickCore/cache.h" | |
#include "MagickCore/constitute.h" | |
#include "MagickCore/composite-private.h" | |
#include "MagickCore/delegate.h" | |
#include "MagickCore/delegate-private.h" | |
#include "MagickCore/draw.h" | |
#include "MagickCore/exception.h" | |
#include "MagickCore/exception-private.h" | |
#include "MagickCore/gem.h" | |
#include "MagickCore/image.h" | |
#include "MagickCore/image-private.h" | |
#include "MagickCore/list.h" | |
#include "MagickCore/log.h" | |
#include "MagickCore/magick.h" | |
#include "MagickCore/memory_.h" | |
#include "MagickCore/memory-private.h" | |
#include "MagickCore/module.h" | |
#include "MagickCore/monitor.h" | |
#include "MagickCore/monitor-private.h" | |
#include "MagickCore/option.h" | |
#include "MagickCore/pixel-accessor.h" | |
#include "MagickCore/policy.h" | |
#include "MagickCore/property.h" | |
#include "MagickCore/quantum-private.h" | |
#include "MagickCore/resource_.h" | |
#include "MagickCore/static.h" | |
#include "MagickCore/string_.h" | |
#include "MagickCore/string-private.h" | |
#include "MagickCore/token.h" | |
#include "MagickCore/utility.h" | |
#include "coders/coders-private.h" | |
#if defined(MAGICKCORE_XML_DELEGATE) | |
# if defined(MAGICKCORE_WINDOWS_SUPPORT) | |
# if !defined(__MINGW32__) | |
# include <win32config.h> | |
# endif | |
# endif | |
# include <libxml/xmlmemory.h> | |
# include <libxml/parserInternals.h> | |
# include <libxml/xmlerror.h> | |
#endif | |
#if defined(MAGICKCORE_AUTOTRACE_DELEGATE) | |
#include "autotrace/autotrace.h" | |
#endif | |
#if defined(MAGICKCORE_RSVG_DELEGATE) | |
#include "librsvg/rsvg.h" | |
#if !defined(LIBRSVG_CHECK_VERSION) | |
#include "librsvg/rsvg-cairo.h" | |
#include "librsvg/librsvg-features.h" | |
#elif !LIBRSVG_CHECK_VERSION(2,36,2) | |
#include "librsvg/rsvg-cairo.h" | |
#include "librsvg/librsvg-features.h" | |
#endif | |
#endif | |
/* | |
Define declarations. | |
*/ | |
#define DefaultSVGDensity 96.0 | |
/* | |
Typedef declarations. | |
*/ | |
typedef struct _BoundingBox | |
{ | |
double | |
x, | |
y, | |
width, | |
height; | |
} BoundingBox; | |
typedef struct _ElementInfo | |
{ | |
double | |
cx, | |
cy, | |
major, | |
minor, | |
angle; | |
} ElementInfo; | |
typedef struct _SVGInfo | |
{ | |
FILE | |
*file; | |
ExceptionInfo | |
*exception; | |
Image | |
*image; | |
const ImageInfo | |
*image_info; | |
AffineMatrix | |
affine; | |
size_t | |
width, | |
height; | |
char | |
*size, | |
*title, | |
*comment; | |
int | |
n; | |
double | |
*scale, | |
pointsize; | |
ElementInfo | |
element; | |
SegmentInfo | |
segment; | |
BoundingBox | |
bounds, | |
text_offset, | |
view_box; | |
PointInfo | |
radius; | |
char | |
*stop_color, | |
*offset, | |
*text, | |
*vertices, | |
*url; | |
#if defined(MAGICKCORE_XML_DELEGATE) | |
xmlParserCtxtPtr | |
parser; | |
xmlDocPtr | |
document; | |
#endif | |
ssize_t | |
svgDepth; | |
} SVGInfo; | |
/* | |
Static declarations. | |
*/ | |
static char | |
SVGDensityGeometry[] = "96.0x96.0"; | |
/* | |
Forward declarations. | |
*/ | |
static MagickBooleanType | |
WriteSVGImage(const ImageInfo *,Image *,ExceptionInfo *); | |
/* | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
% % | |
% % | |
% % | |
% I s S V G % | |
% % | |
% % | |
% % | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
% | |
% IsSVG()() returns MagickTrue if the image format type, identified by the | |
% magick string, is SVG. | |
% | |
% The format of the IsSVG method is: | |
% | |
% MagickBooleanType IsSVG(const unsigned char *magick,const size_t length) | |
% | |
% A description of each parameter follows: | |
% | |
% o magick: compare image format pattern against these bytes. | |
% | |
% o length: Specifies the length of the magick string. | |
% | |
*/ | |
static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length) | |
{ | |
if (length < 4) | |
return(MagickFalse); | |
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); | |
} | |
/* | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
% % | |
% % | |
% % | |
% R e a d S V G I m a g e % | |
% % | |
% % | |
% % | |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
% | |
% ReadSVGImage() reads a Scalable Vector Graphics file and returns it. It | |
% allocates the memory necessary for the new Image structure and returns a | |
% pointer to the new image. | |
% | |
% The format of the ReadSVGImage method is: | |
% | |
% Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception) | |
% | |
% A description of each parameter follows: | |
% | |
% o image_info: the image info. | |
% | |
% o exception: return any errors or warnings in this structure. | |
% | |
*/ | |
static Image *RenderSVGImage(const ImageInfo *image_info,Image *image, | |
ExceptionInfo *exception) | |
{ | |
char | |
background[MagickPathExtent], | |
command[MagickPathExtent], | |
*density, | |
input_filename[MagickPathExtent], | |
opacity[MagickPathExtent], | |
output_filename[MagickPathExtent], | |
unique[MagickPathExtent]; | |
const DelegateInfo | |
*delegate_info; | |
Image | |
*next; | |
int | |
status; | |
struct stat | |
attributes; | |
/* | |
Our best hope for compliance with the SVG standard. | |
*/ | |
delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception); | |
if (delegate_info == (const DelegateInfo *) NULL) | |
return((Image *) NULL); | |
status=AcquireUniqueSymbolicLink(image->filename,input_filename); | |
(void) AcquireUniqueFilename(unique); | |
(void) FormatLocaleString(output_filename,MagickPathExtent,"%s.png",unique); | |
(void) AcquireUniqueFilename(unique); | |
density=AcquireString(""); | |
(void) FormatLocaleString(density,MagickPathExtent,"%.20g", | |
ceil(sqrt(image->resolution.x*image->resolution.y)-0.5)); | |
(void) FormatLocaleString(background,MagickPathExtent, | |
"rgb(%.20g%%,%.20g%%,%.20g%%)", | |
100.0*QuantumScale*image->background_color.red, | |
100.0*QuantumScale*image->background_color.green, | |
100.0*QuantumScale*image->background_color.blue); | |
(void) FormatLocaleString(opacity,MagickPathExtent,"%.20g",QuantumScale* | |
image->background_color.alpha); | |
(void) FormatLocaleString(command,MagickPathExtent, | |
GetDelegateCommands(delegate_info),input_filename,output_filename,density, | |
background,opacity,unique); | |
density=DestroyString(density); | |
status=ExternalDelegateCommand(MagickFalse,image_info->verbose,command, | |
(char *) NULL,exception); | |
(void) RelinquishUniqueFileResource(unique); | |
(void) RelinquishUniqueFileResource(input_filename); | |
if ((status == 0) && (stat(output_filename,&attributes) == 0) && | |
(attributes.st_size > 0)) | |
{ | |
Image | |
*svg_image; | |
ImageInfo | |
*read_info; | |
read_info=CloneImageInfo(image_info); | |
(void) CopyMagickString(read_info->filename,output_filename, | |
MagickPathExtent); | |
svg_image=ReadImage(read_info,exception); | |
read_info=DestroyImageInfo(read_info); | |
if (svg_image != (Image *) NULL) | |
{ | |
(void) RelinquishUniqueFileResource(output_filename); | |
for (next=GetFirstImageInList(svg_image); next != (Image *) NULL; ) | |
{ | |
(void) CopyMagickString(next->filename,image->filename, | |
MagickPathExtent); | |
(void) CopyMagickString(next->magick,image->magick, | |
MagickPathExtent); | |
next=GetNextImageInList(next); | |
} | |
return(svg_image); | |
} | |
} | |
(void) RelinquishUniqueFileResource(output_filename); | |
return((Image *) NULL); | |
} | |
#if defined(MAGICKCORE_RSVG_DELEGATE) | |
static Image *RenderRSVGImage(const ImageInfo *image_info,Image *image, | |
ExceptionInfo *exception) | |
{ | |
#if defined(MAGICKCORE_CAIRO_DELEGATE) | |
cairo_surface_t | |
*cairo_surface; | |
cairo_t | |
*cairo_image; | |
MagickBooleanType | |
apply_density; | |
MemoryInfo | |
*pixel_info; | |
unsigned char | |
*p; | |
RsvgDimensionData | |
dimension_info; | |
unsigned char | |
*pixels; | |
#else | |
GdkPixbuf | |
*pixel_buffer; | |
const guchar | |
*p; | |
#endif | |
const char | |
*option; | |
GError | |
*error; | |
Image | |
*next; | |
MagickBooleanType | |
status; | |
PixelInfo | |
fill_color; | |
Quantum | |
*q; | |
RsvgHandle | |
*svg_handle; | |
ssize_t | |
n, | |
x, | |
y; | |
unsigned char | |
*buffer; | |
buffer=(unsigned char *) AcquireQuantumMemory(MagickMaxBufferExtent, | |
sizeof(*buffer)); | |
if (buffer == (unsigned char *) NULL) | |
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); | |
#if LIBRSVG_CHECK_VERSION(2,40,3) | |
option=GetImageOption(image_info,"svg:xml-parse-huge"); | |
if ((option != (char *) NULL) && (IsStringTrue(option) != MagickFalse)) | |
svg_handle=rsvg_handle_new_with_flags(RSVG_HANDLE_FLAG_UNLIMITED); | |
else | |
#endif | |
svg_handle=rsvg_handle_new(); | |
if (svg_handle == (RsvgHandle *) NULL) | |
{ | |
buffer=(unsigned char *) RelinquishMagickMemory(buffer); | |
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); | |
} | |
rsvg_handle_set_base_uri(svg_handle,image_info->filename); | |
if ((fabs(image->resolution.x) > MagickEpsilon) && | |
(fabs(image->resolution.y) > MagickEpsilon)) | |
rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x, | |
image->resolution.y); | |
while ((n=ReadBlob(image,MagickMaxBufferExtent-1,buffer)) != 0) | |
{ | |
buffer[n]='\0'; | |
error=(GError *) NULL; | |
(void) rsvg_handle_write(svg_handle,buffer,n,&error); | |
if (error != (GError *) NULL) | |
g_error_free(error); | |
} | |
buffer=(unsigned char *) RelinquishMagickMemory(buffer); | |
error=(GError *) NULL; | |
rsvg_handle_close(svg_handle,&error); | |
if (error != (GError *) NULL) | |
g_error_free(error); | |
#if defined(MAGICKCORE_CAIRO_DELEGATE) | |
apply_density=MagickTrue; | |
rsvg_handle_get_dimensions(svg_handle,&dimension_info); | |
if ((image->resolution.x > 0.0) && (image->resolution.y > 0.0)) | |
{ | |
RsvgDimensionData | |
dpi_dimension_info; | |
/* | |
We should not apply the density when the internal 'factor' is 'i'. | |
This can be checked by using the trick below. | |
*/ | |
rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x*256, | |
image->resolution.y*256); | |
rsvg_handle_get_dimensions(svg_handle,&dpi_dimension_info); | |
if ((fabs((double) dpi_dimension_info.width-dimension_info.width) >= MagickEpsilon) || | |
(fabs((double) dpi_dimension_info.height-dimension_info.height) >= MagickEpsilon)) | |
apply_density=MagickFalse; | |
rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x, | |
image->resolution.y); | |
} | |
if (image_info->size != (char *) NULL) | |
{ | |
(void) GetGeometry(image_info->size,(ssize_t *) NULL,(ssize_t *) NULL, | |
&image->columns,&image->rows); | |
if ((image->columns != 0) || (image->rows != 0)) | |
{ | |
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 (fabs(image->resolution.y) < MagickEpsilon) | |
image->resolution.y=image->resolution.x; | |
else | |
image->resolution.x=image->resolution.y=MagickMin( | |
image->resolution.x,image->resolution.y); | |
apply_density=MagickTrue; | |
} | |
} | |
if (apply_density != MagickFalse) | |
{ | |
image->columns=image->resolution.x*dimension_info.width/ | |
DefaultSVGDensity; | |
image->rows=image->resolution.y*dimension_info.height/ | |
DefaultSVGDensity; | |
} | |
else | |
{ | |
image->columns=dimension_info.width; | |
image->rows=dimension_info.height; | |
} | |
pixel_info=(MemoryInfo *) NULL; | |
#else | |
pixel_buffer=rsvg_handle_get_pixbuf(svg_handle); | |
rsvg_handle_free(svg_handle); | |
image->columns=gdk_pixbuf_get_width(pixel_buffer); | |
image->rows=gdk_pixbuf_get_height(pixel_buffer); | |
#endif | |
image->alpha_trait=BlendPixelTrait; | |
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, | |
(int) image->columns); | |
#endif | |
pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels)); | |
if (pixel_info == (MemoryInfo *) NULL) | |
{ | |
g_object_unref(svg_handle); | |
ThrowReaderException(ResourceLimitError, | |
"MemoryAllocationFailed"); | |
} | |
pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info); | |
#endif | |
(void) SetImageBackgroundColor(image,exception); | |
#if defined(MAGICKCORE_CAIRO_DELEGATE) | |
cairo_surface=cairo_image_surface_create_for_data(pixels, | |
CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int) | |
stride); | |
if ((cairo_surface == (cairo_surface_t *) NULL) || | |
(cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS)) | |
{ | |
if (cairo_surface != (cairo_surface_t *) NULL) | |
cairo_surface_destroy(cairo_surface); | |
pixel_info=RelinquishVirtualMemory(pixel_info); | |
g_object_unref(svg_handle); | |
ThrowReaderException(ResourceLimitError, | |
"MemoryAllocationFailed"); | |
} | |
cairo_image=cairo_create(cairo_surface); | |
cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR); | |
cairo_paint(cairo_image); | |
cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER); | |
if (apply_density != MagickFalse) | |
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); | |
g_object_unref(svg_handle); | |
p=pixels; | |
#else | |
p=gdk_pixbuf_get_pixels(pixel_buffer); | |
#endif | |
GetPixelInfo(image,&fill_color); | |
for (y=0; y < (ssize_t) image->rows; y++) | |
{ | |
q=GetAuthenticPixels(image,0,y,image->columns,1,exception); | |
if (q == (Quantum *) NULL) | |
break; | |
for (x=0; x < (ssize_t) image->columns; x++) | |
{ | |
#if defined(MAGICKCORE_CAIRO_DELEGATE) | |
fill_color.blue=ScaleCharToQuantum(*p++); | |
fill_color.green=ScaleCharToQuantum(*p++); | |
fill_color.red=ScaleCharToQuantum(*p++); | |
#else | |
fill_color.red=ScaleCharToQuantum(*p++); | |
fill_color.green=ScaleCharToQuantum(*p++); | |
fill_color.blue=ScaleCharToQuantum(*p++); | |
#endif | |
fill_color.alpha=ScaleCharToQuantum(*p++); | |
#if defined(MAGICKCORE_CAIRO_DELEGATE) | |
{ | |
double | |
gamma; | |
gamma=QuantumScale*fill_color.alpha; | |
gamma=PerceptibleReciprocal(gamma); | |
fill_color.blue*=gamma; | |
fill_color.green*=gamma; | |
fill_color.red*=gamma; | |
} | |
#endif | |
CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double) | |
GetPixelAlpha(image,q),q); | |
q+=GetPixelChannels(image); | |
} | |
if (SyncAuthenticPixels(image,exception) == MagickFalse) | |
break; | |
if (image->previous == (Image *) NULL) | |
{ | |
status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, | |
image->rows); | |
if (status == MagickFalse) | |
break; | |
} | |
} | |
} | |
#if defined(MAGICKCORE_CAIRO_DELEGATE) | |
else | |
g_object_unref(svg_handle); | |
if (pixel_info != (MemoryInfo *) NULL) | |
pixel_info=RelinquishVirtualMemory(pixel_info); | |
#else | |
g_object_unref(G_OBJECT(pixel_buffer)); | |
#endif | |
(void) CloseBlob(image); | |
for (next=GetFirstImageInList(image); next != (Image *) NULL; ) | |
{ | |
(void) CopyMagickString(next->filename,image->filename,MagickPathExtent); | |
(void) CopyMagickString(next->magick,image->magick,MagickPathExtent); | |
next=GetNextImageInList(next); | |
} | |
return(GetFirstImageInList(image)); | |
} | |
#endif | |
#if defined(MAGICKCORE_XML_DELEGATE) | |
static SVGInfo *AcquireSVGInfo(void) | |
{ | |
SVGInfo | |
*svg_info; | |
svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info)); | |
if (svg_info == (SVGInfo *) NULL) | |
return((SVGInfo *) NULL); | |
(void) memset(svg_info,0,sizeof(*svg_info)); | |
svg_info->text=AcquireString(""); | |
svg_info->scale=(double *) AcquireCriticalMemory(sizeof(*svg_info->scale)); | |
GetAffineMatrix(&svg_info->affine); | |
svg_info->scale[0]=ExpandAffine(&svg_info->affine); | |
return(svg_info); | |
} | |
static SVGInfo *DestroySVGInfo(SVGInfo *svg_info) | |
{ | |
if (svg_info->size != (char *) NULL) | |
svg_info->size=DestroyString(svg_info->size); | |
if (svg_info->text != (char *) NULL) | |
svg_info->text=DestroyString(svg_info->text); | |
if (svg_info->scale != (double *) NULL) | |
svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale); | |
if (svg_info->title != (char *) NULL) | |
svg_info->title=DestroyString(svg_info->title); | |
if (svg_info->comment != (char *) NULL) | |
svg_info->comment=DestroyString(svg_info->comment); | |
return((SVGInfo *) RelinquishMagickMemory(svg_info)); | |
} | |
static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type, | |
const char *string) | |
{ | |
char | |
*next_token, | |
token[MagickPathExtent]; | |
const char | |
*p; | |
double | |
value; | |
if (IsEventLogging() != MagickFalse) | |
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string); | |
assert(string != (const char *) NULL); | |
p=(const char *) string; | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
value=StringToDouble(token,&next_token); | |
if (strchr(token,'%') != (char *) NULL) | |
{ | |
double | |
alpha, | |
beta; | |
if (type > 0) | |
{ | |
if (svg_info->view_box.width < MagickEpsilon) | |
return(0.0); | |
return(svg_info->view_box.width*value/100.0); | |
} | |
if (type < 0) | |
{ | |
if (svg_info->view_box.height < MagickEpsilon) | |
return(0.0); | |
return(svg_info->view_box.height*value/100.0); | |
} | |
alpha=value-svg_info->view_box.width; | |
beta=value-svg_info->view_box.height; | |
return(hypot(alpha,beta)/sqrt(2.0)/100.0); | |
} | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (LocaleNCompare(token,"cm",2) == 0) | |
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(DefaultSVGDensity*svg_info->scale[0]*value); | |
if (LocaleNCompare(token,"mm",2) == 0) | |
return(DefaultSVGDensity*svg_info->scale[0]/25.4*value); | |
if (LocaleNCompare(token,"pc",2) == 0) | |
return(DefaultSVGDensity*svg_info->scale[0]/6.0*value); | |
if (LocaleNCompare(token,"pt",2) == 0) | |
return(svg_info->scale[0]*value); | |
if (LocaleNCompare(token,"px",2) == 0) | |
return(value); | |
return(value); | |
} | |
#if defined(__cplusplus) || defined(c_plusplus) | |
extern "C" { | |
#endif | |
static int SVGIsStandalone(void *context) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
Is this document tagged standalone? | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGIsStandalone()"); | |
svg_info=(SVGInfo *) context; | |
return(svg_info->document->standalone == 1); | |
} | |
static int SVGHasInternalSubset(void *context) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
Does this document has an internal subset? | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.SVGHasInternalSubset()"); | |
svg_info=(SVGInfo *) context; | |
return(svg_info->document->intSubset != NULL); | |
} | |
static int SVGHasExternalSubset(void *context) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
Does this document has an external subset? | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.SVGHasExternalSubset()"); | |
svg_info=(SVGInfo *) context; | |
return(svg_info->document->extSubset != NULL); | |
} | |
static void SVGInternalSubset(void *context,const xmlChar *name, | |
const xmlChar *external_id,const xmlChar *system_id) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
Does this document have an internal subset? | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.internalSubset(%s, %s, %s)",(const char *) name, | |
(external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"), | |
(system_id != (const xmlChar *) NULL ? (const char *) system_id : "none")); | |
svg_info=(SVGInfo *) context; | |
(void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id); | |
} | |
static xmlParserInputPtr SVGResolveEntity(void *context, | |
const xmlChar *public_id,const xmlChar *system_id) | |
{ | |
SVGInfo | |
*svg_info; | |
xmlParserInputPtr | |
stream; | |
/* | |
Special entity resolver, better left to the parser, it has more | |
context than the application layer. The default behaviour is to | |
not resolve the entities, in that case the ENTITY_REF nodes are | |
built in the structure (and the parameter values). | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.resolveEntity(%s, %s)", | |
(public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"), | |
(system_id != (const xmlChar *) NULL ? (const char *) system_id : "none")); | |
svg_info=(SVGInfo *) context; | |
stream=xmlLoadExternalEntity((const char *) system_id,(const char *) | |
public_id,svg_info->parser); | |
return(stream); | |
} | |
static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
Get an entity by name. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGGetEntity(%s)", | |
name); | |
svg_info=(SVGInfo *) context; | |
return(xmlGetDocEntity(svg_info->document,name)); | |
} | |
static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
Get a parameter entity by name. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.getParameterEntity(%s)",name); | |
svg_info=(SVGInfo *) context; | |
return(xmlGetParameterEntity(svg_info->document,name)); | |
} | |
static void SVGError(void *,const char *,...) | |
magick_attribute((__format__ (__printf__,2,3))); | |
static void SVGEntityDeclaration(void *context,const xmlChar *name,int type, | |
const xmlChar *public_id,const xmlChar *system_id,xmlChar *content) | |
{ | |
SVGInfo | |
*svg_info; | |
xmlEntityPtr | |
entity; | |
/* | |
An entity definition has been parsed. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.entityDecl(%s, %d, %s, %s, %s)",name,type, | |
public_id != (xmlChar *) NULL ? (const char *) public_id : "none", | |
system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content); | |
svg_info=(SVGInfo *) context; | |
if (svg_info->parser->inSubset == 1) | |
entity=xmlAddDocEntity(svg_info->document,name,type,public_id,system_id, | |
content); | |
else | |
if (svg_info->parser->inSubset == 2) | |
entity=xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id, | |
content); | |
else | |
return; | |
if (entity == (xmlEntityPtr) NULL) | |
SVGError(svg_info,"NULL entity"); | |
} | |
static void SVGAttributeDeclaration(void *context,const xmlChar *element, | |
const xmlChar *name,int type,int value,const xmlChar *default_value, | |
xmlEnumerationPtr tree) | |
{ | |
SVGInfo | |
*svg_info; | |
xmlChar | |
*fullname, | |
*prefix; | |
xmlParserCtxtPtr | |
parser; | |
/* | |
An attribute definition has been parsed. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value, | |
default_value); | |
svg_info=(SVGInfo *) context; | |
fullname=(xmlChar *) NULL; | |
prefix=(xmlChar *) NULL; | |
parser=svg_info->parser; | |
fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix); | |
if (parser->inSubset == 1) | |
(void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset, | |
element,fullname,prefix,(xmlAttributeType) type, | |
(xmlAttributeDefault) value,default_value,tree); | |
else | |
if (parser->inSubset == 2) | |
(void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset, | |
element,fullname,prefix,(xmlAttributeType) type, | |
(xmlAttributeDefault) value,default_value,tree); | |
if (prefix != (xmlChar *) NULL) | |
xmlFree(prefix); | |
if (fullname != (xmlChar *) NULL) | |
xmlFree(fullname); | |
} | |
static void SVGElementDeclaration(void *context,const xmlChar *name,int type, | |
xmlElementContentPtr content) | |
{ | |
SVGInfo | |
*svg_info; | |
xmlParserCtxtPtr | |
parser; | |
/* | |
An element definition has been parsed. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.elementDecl(%s, %d, ...)",name,type); | |
svg_info=(SVGInfo *) context; | |
parser=svg_info->parser; | |
if (parser->inSubset == 1) | |
(void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset, | |
name,(xmlElementTypeVal) type,content); | |
else | |
if (parser->inSubset == 2) | |
(void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset, | |
name,(xmlElementTypeVal) type,content); | |
} | |
static void SVGStripString(const MagickBooleanType trim,char *message) | |
{ | |
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'; | |
length=strlen(message); | |
if ((trim != MagickFalse) && (length != 0)) | |
{ | |
/* | |
Remove whitespace. | |
*/ | |
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; | |
const char | |
*p, | |
*q; | |
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]=(char *) AcquireMagickMemory((size_t) (q-p+2)); | |
if (tokens[i] == (char *) NULL) | |
{ | |
(void) ThrowMagickException(svg_info->exception,GetMagickModule(), | |
ResourceLimitError,"MemoryAllocationFailed","`%s'",text); | |
break; | |
} | |
(void) CopyMagickString(tokens[i],p,(size_t) (q-p+1)); | |
SVGStripString(MagickTrue,tokens[i]); | |
i++; | |
p=q+1; | |
} | |
tokens[i]=(char *) AcquireMagickMemory((size_t) (q-p+2)); | |
if (tokens[i] == (char *) NULL) | |
(void) ThrowMagickException(svg_info->exception,GetMagickModule(), | |
ResourceLimitError,"MemoryAllocationFailed","`%s'",text); | |
else | |
{ | |
(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) | |
{ | |
SVGInfo | |
*svg_info; | |
xmlParserCtxtPtr | |
parser; | |
/* | |
What to do when a notation declaration has been parsed. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.notationDecl(%s, %s, %s)",name, | |
public_id != (const xmlChar *) NULL ? (const char *) public_id : "none", | |
system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"); | |
svg_info=(SVGInfo *) context; | |
parser=svg_info->parser; | |
if (parser->inSubset == 1) | |
(void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset, | |
name,public_id,system_id); | |
else | |
if (parser->inSubset == 2) | |
(void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset, | |
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; | |
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); | |
(void) FormatLocaleFile(svg_info->file,"currentColor \"%s\"\n", | |
color); | |
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 | |
font_family[MagickPathExtent], | |
font_size[MagickPathExtent], | |
font_style[MagickPathExtent]; | |
if (sscanf(value,"%2048s %2048s %2048s",font_style,font_size, | |
font_family) != 3) | |
break; | |
if (GetUserSpaceCoordinateValue(svg_info,0,font_style) == 0) | |
(void) FormatLocaleFile(svg_info->file,"font-style \"%s\"\n", | |
style); | |
else | |
if (sscanf(value,"%2048s %2048s",font_size,font_family) != 2) | |
break; | |
(void) FormatLocaleFile(svg_info->file,"font-size \"%s\"\n", | |
font_size); | |
(void) FormatLocaleFile(svg_info->file,"font-family \"%s\"\n", | |
font_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) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
What to do when an unparsed entity declaration is parsed. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.unparsedEntityDecl(%s, %s, %s, %s)",name, | |
public_id != (xmlChar *) NULL ? (const char *) public_id : "none", | |
system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation); | |
svg_info=(SVGInfo *) context; | |
(void) xmlAddDocEntity(svg_info->document,name, | |
XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation); | |
} | |
static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
Receive the document locator at startup, actually xmlDefaultSAXLocator. | |
*/ | |
(void) location; | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.setDocumentLocator()"); | |
svg_info=(SVGInfo *) context; | |
(void) svg_info; | |
} | |
static void SVGStartDocument(void *context) | |
{ | |
SVGInfo | |
*svg_info; | |
xmlParserCtxtPtr | |
parser; | |
/* | |
Called when the document start being processed. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startDocument()"); | |
svg_info=(SVGInfo *) context; | |
parser=svg_info->parser; | |
svg_info->document=xmlNewDoc(parser->version); | |
if (svg_info->document == (xmlDocPtr) NULL) | |
return; | |
if (parser->encoding == NULL) | |
svg_info->document->encoding=(const xmlChar *) NULL; | |
else | |
svg_info->document->encoding=xmlStrdup(parser->encoding); | |
svg_info->document->standalone=parser->standalone; | |
} | |
static void SVGEndDocument(void *context) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
Called when the document end has been detected. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()"); | |
svg_info=(SVGInfo *) context; | |
if (svg_info->offset != (char *) NULL) | |
svg_info->offset=DestroyString(svg_info->offset); | |
if (svg_info->stop_color != (char *) NULL) | |
svg_info->stop_color=DestroyString(svg_info->stop_color); | |
if (svg_info->scale != (double *) NULL) | |
svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale); | |
if (svg_info->text != (char *) NULL) | |
svg_info->text=DestroyString(svg_info->text); | |
if (svg_info->vertices != (char *) NULL) | |
svg_info->vertices=DestroyString(svg_info->vertices); | |
if (svg_info->url != (char *) NULL) | |
svg_info->url=DestroyString(svg_info->url); | |
#if defined(MAGICKCORE_XML_DELEGATE) | |
if (svg_info->document != (xmlDocPtr) NULL) | |
{ | |
xmlFreeDoc(svg_info->document); | |
svg_info->document=(xmlDocPtr) NULL; | |
} | |
#endif | |
} | |
static void SVGStartElement(void *context,const xmlChar *name, | |
const xmlChar **attributes) | |
{ | |
#define PushGraphicContext(id) \ | |
{ \ | |
if (*id == '\0') \ | |
(void) FormatLocaleFile(svg_info->file,"push graphic-context\n"); \ | |
else \ | |
(void) FormatLocaleFile(svg_info->file,"push graphic-context \"%s\"\n", \ | |
id); \ | |
} | |
char | |
*color, | |
background[MagickPathExtent], | |
id[MagickPathExtent], | |
*next_token, | |
token[MagickPathExtent], | |
**tokens, | |
*units; | |
const char | |
*keyword, | |
*p, | |
*value; | |
ssize_t | |
i, | |
j; | |
size_t | |
number_tokens; | |
SVGInfo | |
*svg_info; | |
/* | |
Called when an opening tag has been processed. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s", | |
name); | |
svg_info=(SVGInfo *) context; | |
svg_info->n++; | |
svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale, | |
svg_info->n+1UL,sizeof(*svg_info->scale)); | |
if (svg_info->scale == (double *) NULL) | |
{ | |
(void) ThrowMagickException(svg_info->exception,GetMagickModule(), | |
ResourceLimitError,"MemoryAllocationFailed","`%s'",name); | |
return; | |
} | |
svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1]; | |
color=AcquireString("none"); | |
units=AcquireString("userSpaceOnUse"); | |
*id='\0'; | |
*token='\0'; | |
*background='\0'; | |
value=(const char *) NULL; | |
if ((LocaleCompare((char *) name,"image") == 0) || | |
(LocaleCompare((char *) name,"pattern") == 0) || | |
(LocaleCompare((char *) name,"rect") == 0) || | |
(LocaleCompare((char *) name,"text") == 0) || | |
(LocaleCompare((char *) name,"use") == 0)) | |
{ | |
svg_info->bounds.x=0.0; | |
svg_info->bounds.y=0.0; | |
} | |
if (attributes != (const xmlChar **) NULL) | |
for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2) | |
{ | |
keyword=(const char *) attributes[i]; | |
value=(const char *) attributes[i+1]; | |
switch (*keyword) | |
{ | |
case 'C': | |
case 'c': | |
{ | |
if (LocaleCompare(keyword,"cx") == 0) | |
{ | |
svg_info->element.cx= | |
GetUserSpaceCoordinateValue(svg_info,1,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"cy") == 0) | |
{ | |
svg_info->element.cy= | |
GetUserSpaceCoordinateValue(svg_info,-1,value); | |
break; | |
} | |
break; | |
} | |
case 'F': | |
case 'f': | |
{ | |
if (LocaleCompare(keyword,"fx") == 0) | |
{ | |
svg_info->element.major= | |
GetUserSpaceCoordinateValue(svg_info,1,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"fy") == 0) | |
{ | |
svg_info->element.minor= | |
GetUserSpaceCoordinateValue(svg_info,-1,value); | |
break; | |
} | |
break; | |
} | |
case 'H': | |
case 'h': | |
{ | |
if (LocaleCompare(keyword,"height") == 0) | |
{ | |
svg_info->bounds.height= | |
GetUserSpaceCoordinateValue(svg_info,-1,value); | |
break; | |
} | |
break; | |
} | |
case 'I': | |
case 'i': | |
{ | |
if (LocaleCompare(keyword,"id") == 0) | |
{ | |
(void) CopyMagickString(id,value,MagickPathExtent); | |
break; | |
} | |
break; | |
} | |
case 'R': | |
case 'r': | |
{ | |
if (LocaleCompare(keyword,"r") == 0) | |
{ | |
svg_info->element.angle=GetUserSpaceCoordinateValue(svg_info,0, | |
value); | |
break; | |
} | |
break; | |
} | |
case 'W': | |
case 'w': | |
{ | |
if (LocaleCompare(keyword,"width") == 0) | |
{ | |
svg_info->bounds.width= | |
GetUserSpaceCoordinateValue(svg_info,1,value); | |
break; | |
} | |
break; | |
} | |
case 'X': | |
case 'x': | |
{ | |
if (LocaleCompare(keyword,"x") == 0) | |
{ | |
svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"x1") == 0) | |
{ | |
svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1, | |
value); | |
break; | |
} | |
if (LocaleCompare(keyword,"x2") == 0) | |
{ | |
svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1, | |
value); | |
break; | |
} | |
break; | |
} | |
case 'Y': | |
case 'y': | |
{ | |
if (LocaleCompare(keyword,"y") == 0) | |
{ | |
svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"y1") == 0) | |
{ | |
svg_info->segment.y1=GetUserSpaceCoordinateValue(svg_info,-1, | |
value); | |
break; | |
} | |
if (LocaleCompare(keyword,"y2") == 0) | |
{ | |
svg_info->segment.y2=GetUserSpaceCoordinateValue(svg_info,-1, | |
value); | |
break; | |
} | |
break; | |
} | |
default: | |
break; | |
} | |
} | |
if (strchr((char *) name,':') != (char *) NULL) | |
{ | |
/* | |
Skip over namespace. | |
*/ | |
for ( ; *name != ':'; name++) ; | |
name++; | |
} | |
switch (*name) | |
{ | |
case 'C': | |
case 'c': | |
{ | |
if (LocaleCompare((const char *) name,"circle") == 0) | |
{ | |
PushGraphicContext(id); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"clipPath") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"push clip-path \"%s\"\n",id); | |
break; | |
} | |
break; | |
} | |
case 'D': | |
case 'd': | |
{ | |
if (LocaleCompare((const char *) name,"defs") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"push defs\n"); | |
break; | |
} | |
break; | |
} | |
case 'E': | |
case 'e': | |
{ | |
if (LocaleCompare((const char *) name,"ellipse") == 0) | |
{ | |
PushGraphicContext(id); | |
break; | |
} | |
break; | |
} | |
case 'F': | |
case 'f': | |
{ | |
if (LocaleCompare((const char *) name,"foreignObject") == 0) | |
{ | |
PushGraphicContext(id); | |
break; | |
} | |
break; | |
} | |
case 'G': | |
case 'g': | |
{ | |
if (LocaleCompare((const char *) name,"g") == 0) | |
{ | |
PushGraphicContext(id); | |
break; | |
} | |
break; | |
} | |
case 'I': | |
case 'i': | |
{ | |
if (LocaleCompare((const char *) name,"image") == 0) | |
{ | |
PushGraphicContext(id); | |
break; | |
} | |
break; | |
} | |
case 'L': | |
case 'l': | |
{ | |
if (LocaleCompare((const char *) name,"line") == 0) | |
{ | |
PushGraphicContext(id); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"linearGradient") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file, | |
"push gradient \"%s\" linear %g,%g %g,%g\n",id, | |
svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2, | |
svg_info->segment.y2); | |
break; | |
} | |
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': | |
{ | |
if (LocaleCompare((const char *) name,"path") == 0) | |
{ | |
PushGraphicContext(id); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"pattern") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file, | |
"push pattern \"%s\" %g,%g %g,%g\n",id, | |
svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width, | |
svg_info->bounds.height); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"polygon") == 0) | |
{ | |
PushGraphicContext(id); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"polyline") == 0) | |
{ | |
PushGraphicContext(id); | |
break; | |
} | |
break; | |
} | |
case 'R': | |
case 'r': | |
{ | |
if (LocaleCompare((const char *) name,"radialGradient") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file, | |
"push gradient \"%s\" radial %g,%g %g,%g %g\n", | |
id,svg_info->element.cx,svg_info->element.cy, | |
svg_info->element.major,svg_info->element.minor, | |
svg_info->element.angle); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"rect") == 0) | |
{ | |
PushGraphicContext(id); | |
break; | |
} | |
break; | |
} | |
case 'S': | |
case 's': | |
{ | |
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"); | |
(void) FormatLocaleFile(svg_info->file,"stroke-width 1\n"); | |
(void) FormatLocaleFile(svg_info->file,"stroke-opacity 1\n"); | |
(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': | |
case 't': | |
{ | |
if (LocaleCompare((const char *) name,"text") == 0) | |
{ | |
PushGraphicContext(id); | |
svg_info->text_offset.x=svg_info->bounds.x; | |
svg_info->text_offset.y=svg_info->bounds.y; | |
svg_info->bounds.x=0.0; | |
svg_info->bounds.y=0.0; | |
svg_info->bounds.width=0.0; | |
svg_info->bounds.height=0.0; | |
break; | |
} | |
if (LocaleCompare((const char *) name,"tspan") == 0) | |
{ | |
if (*svg_info->text != '\0') | |
{ | |
char | |
*text; | |
text=EscapeString(svg_info->text,'\"'); | |
(void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n", | |
svg_info->text_offset.x,svg_info->text_offset.y,text); | |
text=DestroyString(text); | |
*svg_info->text='\0'; | |
} | |
PushGraphicContext(id); | |
break; | |
} | |
break; | |
} | |
case 'U': | |
case 'u': | |
{ | |
if (LocaleCompare((char *) name,"use") == 0) | |
{ | |
PushGraphicContext(id); | |
break; | |
} | |
break; | |
} | |
default: | |
break; | |
} | |
if (attributes != (const xmlChar **) NULL) | |
for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2) | |
{ | |
keyword=(const char *) attributes[i]; | |
value=(const char *) attributes[i+1]; | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" %s = %s",keyword,value); | |
switch (*keyword) | |
{ | |
case 'A': | |
case 'a': | |
{ | |
if (LocaleCompare(keyword,"angle") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"angle %g\n", | |
GetUserSpaceCoordinateValue(svg_info,0,value)); | |
break; | |
} | |
break; | |
} | |
case 'C': | |
case 'c': | |
{ | |
if (LocaleCompare(keyword,"class") == 0) | |
{ | |
p=value; | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token != '\0') | |
(void) FormatLocaleFile(svg_info->file,"class \"%s\"\n",value); | |
else | |
(void) FormatLocaleFile(svg_info->file,"class \"none\"\n"); | |
break; | |
} | |
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; | |
} | |
if (LocaleCompare(keyword,"cx") == 0) | |
{ | |
svg_info->element.cx= | |
GetUserSpaceCoordinateValue(svg_info,1,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"cy") == 0) | |
{ | |
svg_info->element.cy= | |
GetUserSpaceCoordinateValue(svg_info,-1,value); | |
break; | |
} | |
break; | |
} | |
case 'D': | |
case 'd': | |
{ | |
if (LocaleCompare(keyword,"d") == 0) | |
{ | |
(void) CloneString(&svg_info->vertices,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"dx") == 0) | |
{ | |
double | |
dx; | |
dx=GetUserSpaceCoordinateValue(svg_info,1,value); | |
svg_info->bounds.x+=dx; | |
svg_info->text_offset.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) | |
{ | |
double | |
dy; | |
dy=GetUserSpaceCoordinateValue(svg_info,-1,value); | |
svg_info->bounds.y+=dy; | |
svg_info->text_offset.y+=dy; | |
if (LocaleCompare((char *) name,"text") == 0) | |
(void) FormatLocaleFile(svg_info->file,"translate 0.0,%g\n",dy); | |
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; | |
} | |
(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) | |
{ | |
if (LocaleCompare(value,"xx-small") == 0) | |
svg_info->pointsize=6.144; | |
else if (LocaleCompare(value,"x-small") == 0) | |
svg_info->pointsize=7.68; | |
else if (LocaleCompare(value,"small") == 0) | |
svg_info->pointsize=9.6; | |
else if (LocaleCompare(value,"medium") == 0) | |
svg_info->pointsize=12.0; | |
else if (LocaleCompare(value,"large") == 0) | |
svg_info->pointsize=14.4; | |
else if (LocaleCompare(value,"x-large") == 0) | |
svg_info->pointsize=17.28; | |
else if (LocaleCompare(value,"xx-large") == 0) | |
svg_info->pointsize=20.736; | |
else | |
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 'G': | |
case 'g': | |
{ | |
if (LocaleCompare(keyword,"gradientTransform") == 0) | |
{ | |
AffineMatrix | |
affine, | |
current, | |
transform; | |
GetAffineMatrix(&transform); | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," "); | |
tokens=SVGKeyValuePairs(context,'(',')',value,&number_tokens); | |
if (tokens == (char **) NULL) | |
break; | |
for (j=0; j < (ssize_t) (number_tokens-1); j+=2) | |
{ | |
keyword=(char *) tokens[j]; | |
if (keyword == (char *) NULL) | |
continue; | |
value=(char *) tokens[j+1]; | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" %s: %s",keyword,value); | |
current=transform; | |
GetAffineMatrix(&affine); | |
switch (*keyword) | |
{ | |
case 'M': | |
case 'm': | |
{ | |
if (LocaleCompare(keyword,"matrix") == 0) | |
{ | |
p=value; | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
affine.sx=StringToDouble(value,(char **) NULL); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
affine.rx=StringToDouble(token,&next_token); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
affine.ry=StringToDouble(token,&next_token); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
affine.sy=StringToDouble(token,&next_token); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
affine.tx=StringToDouble(token,&next_token); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
affine.ty=StringToDouble(token,&next_token); | |
break; | |
} | |
break; | |
} | |
case 'R': | |
case 'r': | |
{ | |
if (LocaleCompare(keyword,"rotate") == 0) | |
{ | |
double | |
angle; | |
angle=GetUserSpaceCoordinateValue(svg_info,0,value); | |
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))); | |
break; | |
} | |
break; | |
} | |
case 'S': | |
case 's': | |
{ | |
if (LocaleCompare(keyword,"scale") == 0) | |
{ | |
for (p=value; *p != '\0'; p++) | |
if ((isspace((int) ((unsigned char) *p)) != 0) || | |
(*p == ',')) | |
break; | |
affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value); | |
affine.sy=affine.sx; | |
if (*p != '\0') | |
affine.sy= | |
GetUserSpaceCoordinateValue(svg_info,-1,p+1); | |
svg_info->scale[svg_info->n]=ExpandAffine(&affine); | |
break; | |
} | |
if (LocaleCompare(keyword,"skewX") == 0) | |
{ | |
affine.sx=svg_info->affine.sx; | |
affine.ry=tan(DegreesToRadians(fmod( | |
GetUserSpaceCoordinateValue(svg_info,1,value), | |
360.0))); | |
affine.sy=svg_info->affine.sy; | |
break; | |
} | |
if (LocaleCompare(keyword,"skewY") == 0) | |
{ | |
affine.sx=svg_info->affine.sx; | |
affine.rx=tan(DegreesToRadians(fmod( | |
GetUserSpaceCoordinateValue(svg_info,-1,value), | |
360.0))); | |
affine.sy=svg_info->affine.sy; | |
break; | |
} | |
break; | |
} | |
case 'T': | |
case 't': | |
{ | |
if (LocaleCompare(keyword,"translate") == 0) | |
{ | |
for (p=value; *p != '\0'; p++) | |
if ((isspace((int) ((unsigned char) *p)) != 0) || | |
(*p == ',')) | |
break; | |
affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value); | |
affine.ty=affine.tx; | |
if (*p != '\0') | |
affine.ty= | |
GetUserSpaceCoordinateValue(svg_info,-1,p+1); | |
break; | |
} | |
break; | |
} | |
default: | |
break; | |
} | |
transform.sx=affine.sx*current.sx+affine.ry*current.rx; | |
transform.rx=affine.rx*current.sx+affine.sy*current.rx; | |
transform.ry=affine.sx*current.ry+affine.ry*current.sy; | |
transform.sy=affine.rx*current.ry+affine.sy*current.sy; | |
transform.tx=affine.tx*current.sx+affine.ty*current.ry+ | |
current.tx; | |
transform.ty=affine.tx*current.rx+affine.ty*current.sy+ | |
current.ty; | |
} | |
(void) FormatLocaleFile(svg_info->file, | |
"affine %g %g %g %g %g %g\n",transform.sx, | |
transform.rx,transform.ry,transform.sy,transform.tx, | |
transform.ty); | |
for (j=0; tokens[j] != (char *) NULL; j++) | |
tokens[j]=DestroyString(tokens[j]); | |
tokens=(char **) RelinquishMagickMemory(tokens); | |
break; | |
} | |
if (LocaleCompare(keyword,"gradientUnits") == 0) | |
{ | |
(void) CloneString(&units,value); | |
(void) FormatLocaleFile(svg_info->file,"gradient-units \"%s\"\n", | |
value); | |
break; | |
} | |
break; | |
} | |
case 'H': | |
case 'h': | |
{ | |
if (LocaleCompare(keyword,"height") == 0) | |
{ | |
svg_info->bounds.height= | |
GetUserSpaceCoordinateValue(svg_info,-1,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"href") == 0) | |
{ | |
(void) CloneString(&svg_info->url,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,"major") == 0) | |
{ | |
svg_info->element.major= | |
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= | |
GetUserSpaceCoordinateValue(svg_info,-1,value); | |
break; | |
} | |
break; | |
} | |
case 'O': | |
case 'o': | |
{ | |
if (LocaleCompare(keyword,"offset") == 0) | |
{ | |
(void) CloneString(&svg_info->offset,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"opacity") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"opacity \"%s\"\n",value); | |
break; | |
} | |
break; | |
} | |
case 'P': | |
case 'p': | |
{ | |
if (LocaleCompare(keyword,"path") == 0) | |
{ | |
(void) CloneString(&svg_info->url,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"points") == 0) | |
{ | |
(void) CloneString(&svg_info->vertices,value); | |
break; | |
} | |
break; | |
} | |
case 'R': | |
case 'r': | |
{ | |
if (LocaleCompare(keyword,"r") == 0) | |
{ | |
svg_info->element.major= | |
GetUserSpaceCoordinateValue(svg_info,1,value); | |
svg_info->element.minor= | |
GetUserSpaceCoordinateValue(svg_info,-1,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"rotate") == 0) | |
{ | |
double | |
angle; | |
angle=GetUserSpaceCoordinateValue(svg_info,0,value); | |
(void) FormatLocaleFile(svg_info->file,"translate %g,%g\n", | |
svg_info->bounds.x,svg_info->bounds.y); | |
svg_info->bounds.x=0; | |
svg_info->bounds.y=0; | |
(void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle); | |
break; | |
} | |
if (LocaleCompare(keyword,"rx") == 0) | |
{ | |
if (LocaleCompare((const char *) name,"ellipse") == 0) | |
svg_info->element.major= | |
GetUserSpaceCoordinateValue(svg_info,1,value); | |
else | |
svg_info->radius.x= | |
GetUserSpaceCoordinateValue(svg_info,1,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"ry") == 0) | |
{ | |
if (LocaleCompare((const char *) name,"ellipse") == 0) | |
svg_info->element.minor= | |
GetUserSpaceCoordinateValue(svg_info,-1,value); | |
else | |
svg_info->radius.y= | |
GetUserSpaceCoordinateValue(svg_info,-1,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; | |
} | |
(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; | |
} | |
if (LocaleCompare(keyword,"style") == 0) | |
{ | |
SVGProcessStyleElement(context,name,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; | |
} | |
if (LocaleCompare(keyword,"transform") == 0) | |
{ | |
AffineMatrix | |
affine, | |
current, | |
transform; | |
GetAffineMatrix(&transform); | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," "); | |
tokens=SVGKeyValuePairs(context,'(',')',value,&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) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" %s: %s",keyword,value); | |
current=transform; | |
GetAffineMatrix(&affine); | |
switch (*keyword) | |
{ | |
case 'M': | |
case 'm': | |
{ | |
if (LocaleCompare(keyword,"matrix") == 0) | |
{ | |
p=value; | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
affine.sx=StringToDouble(value,(char **) NULL); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
affine.rx=StringToDouble(token,&next_token); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
affine.ry=StringToDouble(token,&next_token); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
affine.sy=StringToDouble(token,&next_token); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
affine.tx=StringToDouble(token,&next_token); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
affine.ty=StringToDouble(token,&next_token); | |
break; | |
} | |
break; | |
} | |
case 'R': | |
case 'r': | |
{ | |
if (LocaleCompare(keyword,"rotate") == 0) | |
{ | |
double | |
angle, | |
x, | |
y; | |
p=value; | |
(void) 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))); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
x=StringToDouble(token,&next_token); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
y=StringToDouble(token,&next_token); | |
y=StringToDouble(token,&next_token); | |
affine.tx=(-1.0*(svg_info->bounds.x+x* | |
cos(DegreesToRadians(fmod(angle,360.0)))-y* | |
sin(DegreesToRadians(fmod(angle,360.0)))))+x; | |
affine.ty=(-1.0*(svg_info->bounds.y+x* | |
sin(DegreesToRadians(fmod(angle,360.0)))+y* | |
cos(DegreesToRadians(fmod(angle,360.0)))))+y; | |
break; | |
} | |
break; | |
} | |
case 'S': | |
case 's': | |
{ | |
if (LocaleCompare(keyword,"scale") == 0) | |
{ | |
for (p=value; *p != '\0'; p++) | |
if ((isspace((int) ((unsigned char) *p)) != 0) || | |
(*p == ',')) | |
break; | |
affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value); | |
affine.sy=affine.sx; | |
if (*p != '\0') | |
affine.sy=GetUserSpaceCoordinateValue(svg_info,-1, | |
p+1); | |
svg_info->scale[svg_info->n]=ExpandAffine(&affine); | |
break; | |
} | |
if (LocaleCompare(keyword,"skewX") == 0) | |
{ | |
affine.sx=svg_info->affine.sx; | |
affine.ry=tan(DegreesToRadians(fmod( | |
GetUserSpaceCoordinateValue(svg_info,1,value), | |
360.0))); | |
affine.sy=svg_info->affine.sy; | |
break; | |
} | |
if (LocaleCompare(keyword,"skewY") == 0) | |
{ | |
affine.sx=svg_info->affine.sx; | |
affine.rx=tan(DegreesToRadians(fmod( | |
GetUserSpaceCoordinateValue(svg_info,-1,value), | |
360.0))); | |
affine.sy=svg_info->affine.sy; | |
break; | |
} | |
break; | |
} | |
case 'T': | |
case 't': | |
{ | |
if (LocaleCompare(keyword,"translate") == 0) | |
{ | |
for (p=value; *p != '\0'; p++) | |
if ((isspace((int) ((unsigned char) *p)) != 0) || | |
(*p == ',')) | |
break; | |
affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value); | |
affine.ty=0; | |
if (*p != '\0') | |
affine.ty=GetUserSpaceCoordinateValue(svg_info,-1, | |
p+1); | |
break; | |
} | |
break; | |
} | |
default: | |
break; | |
} | |
transform.sx=affine.sx*current.sx+affine.ry*current.rx; | |
transform.rx=affine.rx*current.sx+affine.sy*current.rx; | |
transform.ry=affine.sx*current.ry+affine.ry*current.sy; | |
transform.sy=affine.rx*current.ry+affine.sy*current.sy; | |
transform.tx=affine.tx*current.sx+affine.ty*current.ry+ | |
current.tx; | |
transform.ty=affine.tx*current.rx+affine.ty*current.sy+ | |
current.ty; | |
} | |
(void) FormatLocaleFile(svg_info->file, | |
"affine %g %g %g %g %g %g\n",transform.sx,transform.rx, | |
transform.ry,transform.sy,transform.tx,transform.ty); | |
for (j=0; tokens[j] != (char *) NULL; j++) | |
tokens[j]=DestroyString(tokens[j]); | |
tokens=(char **) RelinquishMagickMemory(tokens); | |
break; | |
} | |
break; | |
} | |
case 'V': | |
case 'v': | |
{ | |
if (LocaleCompare(keyword,"verts") == 0) | |
{ | |
(void) CloneString(&svg_info->vertices,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"viewBox") == 0) | |
{ | |
p=value; | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
svg_info->view_box.x=StringToDouble(token,&next_token); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
svg_info->view_box.y=StringToDouble(token,&next_token); | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
svg_info->view_box.width=StringToDouble(token, | |
(char **) NULL); | |
if (svg_info->bounds.width < MagickEpsilon) | |
svg_info->bounds.width=svg_info->view_box.width; | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
if (*token == ',') | |
(void) GetNextToken(p,&p,MagickPathExtent,token); | |
svg_info->view_box.height=StringToDouble(token, | |
(char **) NULL); | |
if (svg_info->bounds.height == 0) | |
svg_info->bounds.height=svg_info->view_box.height; | |
break; | |
} | |
break; | |
} | |
case 'W': | |
case 'w': | |
{ | |
if (LocaleCompare(keyword,"width") == 0) | |
{ | |
svg_info->bounds.width= | |
GetUserSpaceCoordinateValue(svg_info,1,value); | |
break; | |
} | |
break; | |
} | |
case 'X': | |
case 'x': | |
{ | |
if (LocaleCompare(keyword,"x") == 0) | |
{ | |
svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"xlink:href") == 0) | |
{ | |
(void) CloneString(&svg_info->url,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"x1") == 0) | |
{ | |
svg_info->segment.x1= | |
GetUserSpaceCoordinateValue(svg_info,1,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"x2") == 0) | |
{ | |
svg_info->segment.x2= | |
GetUserSpaceCoordinateValue(svg_info,1,value); | |
break; | |
} | |
break; | |
} | |
case 'Y': | |
case 'y': | |
{ | |
if (LocaleCompare(keyword,"y") == 0) | |
{ | |
svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"y1") == 0) | |
{ | |
svg_info->segment.y1= | |
GetUserSpaceCoordinateValue(svg_info,-1,value); | |
break; | |
} | |
if (LocaleCompare(keyword,"y2") == 0) | |
{ | |
svg_info->segment.y2= | |
GetUserSpaceCoordinateValue(svg_info,-1,value); | |
break; | |
} | |
break; | |
} | |
default: | |
break; | |
} | |
} | |
if (LocaleCompare((const char *) name,"svg") == 0) | |
{ | |
if (svg_info->document->encoding != (const xmlChar *) NULL) | |
(void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n", | |
(const char *) svg_info->document->encoding); | |
if (attributes != (const xmlChar **) NULL) | |
{ | |
double | |
sx, | |
sy, | |
tx, | |
ty; | |
if ((svg_info->view_box.width < MagickEpsilon) || | |
(svg_info->view_box.height < MagickEpsilon)) | |
svg_info->view_box=svg_info->bounds; | |
svg_info->width=0; | |
if (svg_info->bounds.width >= MagickEpsilon) | |
svg_info->width=(size_t) floor(svg_info->bounds.width+0.5); | |
svg_info->height=0; | |
if (svg_info->bounds.height >= MagickEpsilon) | |
svg_info->height=(size_t) floor(svg_info->bounds.height+0.5); | |
(void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n", | |
(double) svg_info->width,(double) svg_info->height); | |
sx=PerceptibleReciprocal(svg_info->view_box.width)*svg_info->width; | |
sy=PerceptibleReciprocal(svg_info->view_box.height)*svg_info->height; | |
tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x : | |
0.0; | |
ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y : | |
0.0; | |
(void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n", | |
sx,sy,tx,ty); | |
if ((svg_info->svgDepth == 1) && (*background != '\0')) | |
{ | |
PushGraphicContext(id); | |
(void) FormatLocaleFile(svg_info->file,"fill %s\n",background); | |
(void) FormatLocaleFile(svg_info->file, | |
"rectangle 0,0 %g,%g\n",svg_info->view_box.width, | |
svg_info->view_box.height); | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
} | |
} | |
} | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," )"); | |
if (units != (char *) NULL) | |
units=DestroyString(units); | |
if (color != (char *) NULL) | |
color=DestroyString(color); | |
} | |
static void SVGEndElement(void *context,const xmlChar *name) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
Called when the end of an element has been detected. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.endElement(%s)",name); | |
svg_info=(SVGInfo *) context; | |
if (strchr((char *) name,':') != (char *) NULL) | |
{ | |
/* | |
Skip over namespace. | |
*/ | |
for ( ; *name != ':'; name++) ; | |
name++; | |
} | |
switch (*name) | |
{ | |
case 'C': | |
case 'c': | |
{ | |
if (LocaleCompare((const char *) name,"circle") == 0) | |
{ | |
(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); | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"clipPath") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"pop clip-path\n"); | |
break; | |
} | |
break; | |
} | |
case 'D': | |
case 'd': | |
{ | |
if (LocaleCompare((const char *) name,"defs") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"pop defs\n"); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"desc") == 0) | |
{ | |
char | |
*p; | |
if (*svg_info->text == '\0') | |
break; | |
(void) fputc('#',svg_info->file); | |
for (p=svg_info->text; *p != '\0'; p++) | |
{ | |
(void) fputc(*p,svg_info->file); | |
if (*p == '\n') | |
(void) fputc('#',svg_info->file); | |
} | |
(void) fputc('\n',svg_info->file); | |
*svg_info->text='\0'; | |
break; | |
} | |
break; | |
} | |
case 'E': | |
case 'e': | |
{ | |
if (LocaleCompare((const char *) name,"ellipse") == 0) | |
{ | |
double | |
angle; | |
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, | |
angle == 0.0 ? svg_info->element.major : svg_info->element.minor, | |
angle == 0.0 ? svg_info->element.minor : svg_info->element.major); | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
break; | |
} | |
case 'F': | |
case 'f': | |
{ | |
if (LocaleCompare((const char *) name,"foreignObject") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
break; | |
} | |
case 'G': | |
case 'g': | |
{ | |
if (LocaleCompare((const char *) name,"g") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
break; | |
} | |
case 'I': | |
case 'i': | |
{ | |
if (LocaleCompare((const char *) name,"image") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file, | |
"image Over %g,%g %g,%g \"%s\"\n",svg_info->bounds.x, | |
svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height, | |
svg_info->url); | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
break; | |
} | |
case 'L': | |
case 'l': | |
{ | |
if (LocaleCompare((const char *) name,"line") == 0) | |
{ | |
(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); | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"linearGradient") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"pop gradient\n"); | |
break; | |
} | |
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': | |
{ | |
if (LocaleCompare((const char *) name,"pattern") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"pop pattern\n"); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"path") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"path \"%s\"\n", | |
svg_info->vertices); | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"polygon") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"polygon %s\n", | |
svg_info->vertices); | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"polyline") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"polyline %s\n", | |
svg_info->vertices); | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
break; | |
} | |
case 'R': | |
case 'r': | |
{ | |
if (LocaleCompare((const char *) name,"radialGradient") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"pop gradient\n"); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"rect") == 0) | |
{ | |
if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0)) | |
{ | |
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; | |
} | |
if (svg_info->radius.x == 0.0) | |
svg_info->radius.x=svg_info->radius.y; | |
if (svg_info->radius.y == 0.0) | |
svg_info->radius.y=svg_info->radius.x; | |
(void) FormatLocaleFile(svg_info->file, | |
"roundRectangle %g,%g %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, | |
svg_info->radius.x,svg_info->radius.y); | |
svg_info->radius.x=0.0; | |
svg_info->radius.y=0.0; | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
break; | |
} | |
case 'S': | |
case 's': | |
{ | |
if (LocaleCompare((const char *) name,"stop") == 0) | |
{ | |
(void) FormatLocaleFile(svg_info->file,"stop-color \"%s\" %s\n", | |
svg_info->stop_color,svg_info->offset == (char *) NULL ? "100%" : | |
svg_info->offset); | |
break; | |
} | |
if (LocaleCompare((char *) name,"style") == 0) | |
{ | |
char | |
*keyword, | |
**tokens, | |
*value; | |
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"); | |
} | |
for (j=0; tokens[j] != (char *) NULL; j++) | |
tokens[j]=DestroyString(tokens[j]); | |
tokens=(char **) RelinquishMagickMemory(tokens); | |
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; | |
} | |
case 'T': | |
case 't': | |
{ | |
if (LocaleCompare((const char *) name,"text") == 0) | |
{ | |
if (*svg_info->text != '\0') | |
{ | |
char | |
*text; | |
SVGStripString(MagickTrue,svg_info->text); | |
text=EscapeString(svg_info->text,'\"'); | |
(void) FormatLocaleFile(svg_info->file,"text %g,%g \"%s\"\n", | |
svg_info->text_offset.x,svg_info->text_offset.y,text); | |
text=DestroyString(text); | |
*svg_info->text='\0'; | |
} | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"tspan") == 0) | |
{ | |
if (*svg_info->text != '\0') | |
{ | |
char | |
*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); | |
text=DestroyString(text); | |
*svg_info->text='\0'; | |
} | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
if (LocaleCompare((const char *) name,"title") == 0) | |
{ | |
if (*svg_info->text == '\0') | |
break; | |
(void) CloneString(&svg_info->title,svg_info->text); | |
*svg_info->text='\0'; | |
break; | |
} | |
break; | |
} | |
case 'U': | |
case 'u': | |
{ | |
if (LocaleCompare((char *) name,"use") == 0) | |
{ | |
if ((svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0)) | |
(void) FormatLocaleFile(svg_info->file,"translate %g,%g\n", | |
svg_info->bounds.x,svg_info->bounds.y); | |
(void) FormatLocaleFile(svg_info->file,"use \"url(%s)\"\n", | |
svg_info->url); | |
(void) FormatLocaleFile(svg_info->file,"pop graphic-context\n"); | |
break; | |
} | |
break; | |
} | |
default: | |
break; | |
} | |
*svg_info->text='\0'; | |
(void) memset(&svg_info->element,0,sizeof(svg_info->element)); | |
(void) memset(&svg_info->segment,0,sizeof(svg_info->segment)); | |
svg_info->n--; | |
} | |
static void SVGCharacters(void *context,const xmlChar *c,int length) | |
{ | |
char | |
*text; | |
char | |
*p; | |
ssize_t | |
i; | |
SVGInfo | |
*svg_info; | |
/* | |
Receiving some characters from the parser. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.characters(%s,%.20g)",c,(double) length); | |
svg_info=(SVGInfo *) context; | |
text=(char *) AcquireQuantumMemory(length+1,sizeof(*text)); | |
if (text == (char *) NULL) | |
return; | |
p=text; | |
for (i=0; i < (ssize_t) length; i++) | |
*p++=c[i]; | |
*p='\0'; | |
SVGStripString(MagickFalse,text); | |
if (svg_info->text == (char *) NULL) | |
svg_info->text=text; | |
else | |
{ | |
(void) ConcatenateString(&svg_info->text,text); | |
text=DestroyString(text); | |
} | |
} | |
static void SVGReference(void *context,const xmlChar *name) | |
{ | |
SVGInfo | |
*svg_info; | |
xmlParserCtxtPtr | |
parser; | |
/* | |
Called when an entity reference is detected. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)", | |
name); | |
svg_info=(SVGInfo *) context; | |
parser=svg_info->parser; | |
if (parser == (xmlParserCtxtPtr) NULL) | |
return; | |
if (parser->node == (xmlNodePtr) NULL) | |
return; | |
if (*name == '#') | |
(void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name)); | |
else | |
(void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name)); | |
} | |
static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
Receiving some ignorable whitespaces from the parser. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.ignorableWhitespace(%.30s, %d)",c,length); | |
svg_info=(SVGInfo *) context; | |
(void) svg_info; | |
} | |
static void SVGProcessingInstructions(void *context,const xmlChar *target, | |
const xmlChar *data) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
A processing instruction has been parsed. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.processingInstruction(%s, %s)",target,data); | |
svg_info=(SVGInfo *) context; | |
(void) svg_info; | |
} | |
static void SVGComment(void *context,const xmlChar *value) | |
{ | |
SVGInfo | |
*svg_info; | |
/* | |
A comment has been parsed. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)", | |
value); | |
svg_info=(SVGInfo *) context; | |
if (svg_info->comment != (char *) NULL) | |
(void) ConcatenateString(&svg_info->comment,"\n"); | |
(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 | |
*message, | |
reason[MagickPathExtent]; | |
SVGInfo | |
*svg_info; | |
va_list | |
operands; | |
/** | |
Display and format a warning messages, gives file, line, position and | |
extra parameters. | |
*/ | |
va_start(operands,format); | |
svg_info=(SVGInfo *) context; | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: "); | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands); | |
#if !defined(MAGICKCORE_HAVE_VSNPRINTF) | |
(void) vsprintf(reason,format,operands); | |
#else | |
(void) vsnprintf(reason,MagickPathExtent,format,operands); | |
#endif | |
message=GetExceptionMessage(errno); | |
(void) ThrowMagickException(svg_info->exception,GetMagickModule(), | |
DelegateWarning,reason,"`%s`",message); | |
message=DestroyString(message); | |
va_end(operands); | |
} | |
static void SVGError(void *context,const char *format,...) | |
{ | |
char | |
*message, | |
reason[MagickPathExtent]; | |
SVGInfo | |
*svg_info; | |
va_list | |
operands; | |
/* | |
Display and format a error formats, gives file, line, position and | |
extra parameters. | |
*/ | |
va_start(operands,format); | |
svg_info=(SVGInfo *) context; | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: "); | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands); | |
#if !defined(MAGICKCORE_HAVE_VSNPRINTF) | |
(void) vsprintf(reason,format,operands); | |
#else | |
(void) vsnprintf(reason,MagickPathExtent,format,operands); | |
#endif | |
message=GetExceptionMessage(errno); | |
(void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError, | |
reason,"`%s`",message); | |
message=DestroyString(message); | |
va_end(operands); | |
xmlStopParser(svg_info->parser); | |
} | |
static void SVGCDataBlock(void *context,const xmlChar *value,int length) | |
{ | |
SVGInfo | |
*svg_info; | |
xmlNodePtr | |
child; | |
xmlParserCtxtPtr | |
parser; | |
/* | |
Called when a pcdata block has been parsed. | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)", | |
value,length); | |
svg_info=(SVGInfo *) context; | |
parser=svg_info->parser; | |
child=xmlGetLastChild(parser->node); | |
if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE)) | |
{ | |
xmlTextConcat(child,value,length); | |
return; | |
} | |
child=xmlNewCDataBlock(parser->myDoc,value,length); | |
if (xmlAddChild(parser->node,child) == (xmlNodePtr) NULL) | |
xmlFreeNode(child); | |
} | |
static void SVGExternalSubset(void *context,const xmlChar *name, | |
const xmlChar *external_id,const xmlChar *system_id) | |
{ | |
SVGInfo | |
*svg_info; | |
xmlParserCtxt | |
parser_context; | |
xmlParserCtxtPtr | |
parser; | |
xmlParserInputPtr | |
input; | |
/* | |
Does this document has an external subset? | |
*/ | |
(void) LogMagickEvent(CoderEvent,GetMagickModule(), | |
" SAX.externalSubset(%s, %s, %s)",name, | |
(external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"), | |
(system_id != (const xmlChar *) NULL ? (const char *) system_id : "none")); | |
svg_info=(SVGInfo *) context; | |
parser=svg_info->parser; | |
if (((external_id == NULL) && (system_id == NULL)) || | |
((parser->validate == 0) || (parser->wellFormed == 0) || | |
(svg_info->document == 0))) | |
return; | |
input=SVGResolveEntity(context,external_id,system_id); | |
if (input == NULL) | |
return; | |
(void) xmlNewDtd(svg_info->document,name,external_id,system_id); | |
parser_context=(*parser); | |
parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab)); | |
if (parser->inputTab == (xmlParserInputPtr *) NULL) | |
{ | |
parser->errNo=XML_ERR_NO_MEMORY; | |
parser->input=parser_context.input; | |
parser->inputNr=parser_context.inputNr; | |
parser->inputMax=parser_context.inputMax; | |
parser->inputTab=parser_context.inputTab; | |
return; | |
} | |
parser->inputNr=0; | |
parser->inputMax=5; | |
parser->input=NULL; | |
xmlPushInput(parser,input); | |
(void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4)); | |
if (input->filename == (char *) NULL) | |
input->filename=(char *) xmlStrdup(system_id); | |
input->line=1; | |
input->col=1; | |
input->base=parser->input->cur; | |
input->cur=parser->input->cur; | |
input->free=NULL; | |
xmlParseExternalSubset(parser,external_id,system_id); | |
while (parser->inputNr > 1) | |
(void) xmlPopInput(parser); | |
xmlFreeInputStream(parser->input); | |
xmlFree(parser->inputTab); | |
parser->input=parser_context.input; | |
parser->inputNr=parser_context.inputNr; | |
parser->inputMax=parser_context.inputMax; | |
parser->inputTab=parser_context.inputTab; | |
} | |
#if defined(__cplusplus) || defined(c_plusplus) | |
} | |
#endif | |
static Image *RenderMSVGImage(const ImageInfo *image_info,Image *image, | |