2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Scalable Vector Graphics Format %
21 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/annotate.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/composite-private.h"
52 #include "MagickCore/draw.h"
53 #include "MagickCore/exception.h"
54 #include "MagickCore/exception-private.h"
55 #include "MagickCore/gem.h"
56 #include "MagickCore/image.h"
57 #include "MagickCore/image-private.h"
58 #include "MagickCore/list.h"
59 #include "MagickCore/log.h"
60 #include "MagickCore/magick.h"
61 #include "MagickCore/memory_.h"
62 #include "MagickCore/module.h"
63 #include "MagickCore/monitor.h"
64 #include "MagickCore/monitor-private.h"
65 #include "MagickCore/quantum-private.h"
66 #include "MagickCore/pixel-accessor.h"
67 #include "MagickCore/property.h"
68 #include "MagickCore/resource_.h"
69 #include "MagickCore/static.h"
70 #include "MagickCore/string_.h"
71 #include "MagickCore/string-private.h"
72 #include "MagickCore/token.h"
73 #include "MagickCore/utility.h"
74 #if defined(MAGICKCORE_XML_DELEGATE)
75 # if defined(MAGICKCORE_WINDOWS_SUPPORT)
76 # if defined(__MINGW32__)
79 # include <win32config.h>
82 # include <libxml/parser.h>
83 # include <libxml/xmlmemory.h>
84 # include <libxml/parserInternals.h>
85 # include <libxml/xmlerror.h>
88 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
89 #include "autotrace/autotrace.h"
92 #if defined(MAGICKCORE_RSVG_DELEGATE)
93 #include "librsvg/rsvg.h"
94 #if defined(MAGICKCORE_CAIRO_DELEGATE)
95 #include "librsvg/rsvg-cairo.h"
97 #include "librsvg/librsvg-features.h"
101 Typedef declarations.
103 typedef struct _BoundingBox
112 typedef struct _ElementInfo
122 typedef struct _SVGInfo
176 #if defined(MAGICKCORE_XML_DELEGATE)
186 Forward declarations.
188 static MagickBooleanType
189 WriteSVGImage(const ImageInfo *,Image *);
192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
202 % IsSVG()() returns MagickTrue if the image format type, identified by the
203 % magick string, is SVG.
205 % The format of the IsSVG method is:
207 % MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
209 % A description of each parameter follows:
211 % o magick: compare image format pattern against these bytes.
213 % o length: Specifies the length of the magick string.
216 static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
220 if (LocaleNCompare((const char *) magick,"?xml",4) == 0)
225 #if defined(MAGICKCORE_XML_DELEGATE)
227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
231 % R e a d S V G I m a g e %
235 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
237 % ReadSVGImage() reads a Scalable Vector Gaphics file and returns it. It
238 % allocates the memory necessary for the new Image structure and returns a
239 % pointer to the new image.
241 % The format of the ReadSVGImage method is:
243 % Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
245 % A description of each parameter follows:
247 % o image_info: the image info.
249 % o exception: return any errors or warnings in this structure.
253 static SVGInfo *AcquireSVGInfo(void)
258 svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info));
259 if (svg_info == (SVGInfo *) NULL)
260 return((SVGInfo *) NULL);
261 (void) ResetMagickMemory(svg_info,0,sizeof(*svg_info));
262 svg_info->text=AcquireString("");
263 svg_info->scale=(double *) AcquireMagickMemory(sizeof(*svg_info->scale));
264 if (svg_info->scale == (double *) NULL)
265 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
266 GetAffineMatrix(&svg_info->affine);
267 svg_info->scale[0]=ExpandAffine(&svg_info->affine);
271 static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
273 if (svg_info->text != (char *) NULL)
274 svg_info->text=DestroyString(svg_info->text);
275 if (svg_info->scale != (double *) NULL)
276 svg_info->scale=(double *) (svg_info->scale);
277 if (svg_info->title != (char *) NULL)
278 svg_info->title=DestroyString(svg_info->title);
279 if (svg_info->comment != (char *) NULL)
280 svg_info->comment=DestroyString(svg_info->comment);
281 return((SVGInfo *) RelinquishMagickMemory(svg_info));
284 static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
288 token[MaxTextExtent];
296 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
297 assert(string != (const char *) NULL);
298 p=(const char *) string;
299 GetMagickToken(p,&p,token);
300 value=InterpretLocaleValue(token,(char **) NULL);
301 if (strchr(token,'%') != (char *) NULL)
309 if (svg_info->view_box.width == 0.0)
311 return(svg_info->view_box.width*value/100.0);
315 if (svg_info->view_box.height == 0.0)
317 return(svg_info->view_box.height*value/100.0);
319 alpha=value-svg_info->view_box.width;
320 beta=value-svg_info->view_box.height;
321 return(hypot(alpha,beta)/sqrt(2.0)/100.0);
323 GetMagickToken(p,&p,token);
324 if (LocaleNCompare(token,"cm",2) == 0)
325 return(DefaultResolution*svg_info->scale[0]/2.54*value);
326 if (LocaleNCompare(token,"em",2) == 0)
327 return(svg_info->pointsize*value);
328 if (LocaleNCompare(token,"ex",2) == 0)
329 return(svg_info->pointsize*value/2.0);
330 if (LocaleNCompare(token,"in",2) == 0)
331 return(DefaultResolution*svg_info->scale[0]*value);
332 if (LocaleNCompare(token,"mm",2) == 0)
333 return(DefaultResolution*svg_info->scale[0]/25.4*value);
334 if (LocaleNCompare(token,"pc",2) == 0)
335 return(DefaultResolution*svg_info->scale[0]/6.0*value);
336 if (LocaleNCompare(token,"pt",2) == 0)
337 return(svg_info->scale[0]*value);
338 if (LocaleNCompare(token,"px",2) == 0)
343 static void StripStyleTokens(char *message)
352 assert(message != (char *) NULL);
353 if (*message == '\0')
355 length=strlen(message);
357 while (isspace((int) ((unsigned char) *p)) != 0)
360 while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
362 (void) CopyMagickMemory(message,p,(size_t) (q-p+1));
364 StripString(message);
367 static char **GetStyleTokens(void *context,const char *style,int *number_tokens)
379 svg_info=(SVGInfo *) context;
382 if (style == (const char *) NULL)
383 return((char **) NULL);
384 text=AcquireString(style);
385 (void) SubstituteString(&text,":","\n");
386 (void) SubstituteString(&text,";","\n");
387 tokens=StringToList(text);
388 text=DestroyString(text);
389 for (i=0; tokens[i] != (char *) NULL; i++)
390 StripStyleTokens(tokens[i]);
395 static char **GetTransformTokens(void *context,const char *text,
411 svg_info=(SVGInfo *) context;
413 if (text == (const char *) NULL)
414 return((char **) NULL);
416 Determine the number of arguments.
418 for (p=text; *p != '\0'; p++)
423 tokens=(char **) AcquireQuantumMemory(*number_tokens+2UL,sizeof(*tokens));
424 if (tokens == (char **) NULL)
426 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
427 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
428 return((char **) NULL);
431 Convert string to an ASCII list.
435 for (q=p; *q != '\0'; q++)
437 if ((*q != '(') && (*q != ')') && (*q != '\0'))
439 tokens[i]=AcquireString(p);
440 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
441 StripString(tokens[i++]);
444 tokens[i]=AcquireString(p);
445 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
446 StripString(tokens[i++]);
447 tokens[i]=(char *) NULL;
451 #if defined(__cplusplus) || defined(c_plusplus)
455 static int SVGIsStandalone(void *context)
461 Is this document tagged standalone?
463 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGIsStandalone()");
464 svg_info=(SVGInfo *) context;
465 return(svg_info->document->standalone == 1);
468 static int SVGHasInternalSubset(void *context)
474 Does this document has an internal subset?
476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
477 " SAX.SVGHasInternalSubset()");
478 svg_info=(SVGInfo *) context;
479 return(svg_info->document->intSubset != NULL);
482 static int SVGHasExternalSubset(void *context)
488 Does this document has an external subset?
490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
491 " SAX.SVGHasExternalSubset()");
492 svg_info=(SVGInfo *) context;
493 return(svg_info->document->extSubset != NULL);
496 static void SVGInternalSubset(void *context,const xmlChar *name,
497 const xmlChar *external_id,const xmlChar *system_id)
503 Does this document has an internal subset?
505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
506 " SAX.internalSubset(%s, %s, %s)",(const char *) name,
507 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
508 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
509 svg_info=(SVGInfo *) context;
510 (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id);
513 static xmlParserInputPtr SVGResolveEntity(void *context,
514 const xmlChar *public_id,const xmlChar *system_id)
523 Special entity resolver, better left to the parser, it has more
524 context than the application layer. The default behaviour is to
525 not resolve the entities, in that case the ENTITY_REF nodes are
526 built in the structure (and the parameter values).
528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
529 " SAX.resolveEntity(%s, %s)",
530 (public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"),
531 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
532 svg_info=(SVGInfo *) context;
533 stream=xmlLoadExternalEntity((const char *) system_id,(const char *)
534 public_id,svg_info->parser);
538 static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
544 Get an entity by name.
546 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGGetEntity(%s)",
548 svg_info=(SVGInfo *) context;
549 return(xmlGetDocEntity(svg_info->document,name));
552 static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
558 Get a parameter entity by name.
560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
561 " SAX.getParameterEntity(%s)",name);
562 svg_info=(SVGInfo *) context;
563 return(xmlGetParameterEntity(svg_info->document,name));
566 static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
567 const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
573 An entity definition has been parsed.
575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
576 " SAX.entityDecl(%s, %d, %s, %s, %s)",name,type,
577 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
578 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content);
579 svg_info=(SVGInfo *) context;
580 if (svg_info->parser->inSubset == 1)
581 (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id,
584 if (svg_info->parser->inSubset == 2)
585 (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
589 static void SVGAttributeDeclaration(void *context,const xmlChar *element,
590 const xmlChar *name,int type,int value,const xmlChar *default_value,
591 xmlEnumerationPtr tree)
604 An attribute definition has been parsed.
606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
607 " SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value,
609 svg_info=(SVGInfo *) context;
610 fullname=(xmlChar *) NULL;
611 prefix=(xmlChar *) NULL;
612 parser=svg_info->parser;
613 fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix);
614 if (parser->inSubset == 1)
615 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset,
616 element,fullname,prefix,(xmlAttributeType) type,
617 (xmlAttributeDefault) value,default_value,tree);
619 if (parser->inSubset == 2)
620 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset,
621 element,fullname,prefix,(xmlAttributeType) type,
622 (xmlAttributeDefault) value,default_value,tree);
623 if (prefix != (xmlChar *) NULL)
625 if (fullname != (xmlChar *) NULL)
629 static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
630 xmlElementContentPtr content)
639 An element definition has been parsed.
641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
642 " SAX.elementDecl(%s, %d, ...)",name,type);
643 svg_info=(SVGInfo *) context;
644 parser=svg_info->parser;
645 if (parser->inSubset == 1)
646 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset,
647 name,(xmlElementTypeVal) type,content);
649 if (parser->inSubset == 2)
650 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
651 name,(xmlElementTypeVal) type,content);
654 static void SVGNotationDeclaration(void *context,const xmlChar *name,
655 const xmlChar *public_id,const xmlChar *system_id)
664 What to do when a notation declaration has been parsed.
666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
667 " SAX.notationDecl(%s, %s, %s)",name,
668 public_id != (const xmlChar *) NULL ? (const char *) public_id : "none",
669 system_id != (const xmlChar *) NULL ? (const char *) system_id : "none");
670 svg_info=(SVGInfo *) context;
671 parser=svg_info->parser;
672 if (parser->inSubset == 1)
673 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
674 name,public_id,system_id);
676 if (parser->inSubset == 2)
677 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
678 name,public_id,system_id);
681 static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
682 const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
688 What to do when an unparsed entity declaration is parsed.
690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
691 " SAX.unparsedEntityDecl(%s, %s, %s, %s)",name,
692 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
693 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation);
694 svg_info=(SVGInfo *) context;
695 (void) xmlAddDocEntity(svg_info->document,name,
696 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
700 static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
706 Receive the document locator at startup, actually xmlDefaultSAXLocator.
709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
710 " SAX.setDocumentLocator()");
711 svg_info=(SVGInfo *) context;
715 static void SVGStartDocument(void *context)
724 Called when the document start being processed.
726 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startDocument()");
727 svg_info=(SVGInfo *) context;
728 GetExceptionInfo(svg_info->exception);
729 parser=svg_info->parser;
730 svg_info->document=xmlNewDoc(parser->version);
731 if (svg_info->document == (xmlDocPtr) NULL)
733 if (parser->encoding == NULL)
734 svg_info->document->encoding=(const xmlChar *) NULL;
736 svg_info->document->encoding=xmlStrdup(parser->encoding);
737 svg_info->document->standalone=parser->standalone;
740 static void SVGEndDocument(void *context)
746 Called when the document end has been detected.
748 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()");
749 svg_info=(SVGInfo *) context;
750 if (svg_info->offset != (char *) NULL)
751 svg_info->offset=DestroyString(svg_info->offset);
752 if (svg_info->stop_color != (char *) NULL)
753 svg_info->stop_color=DestroyString(svg_info->stop_color);
754 if (svg_info->scale != (double *) NULL)
755 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
756 if (svg_info->text != (char *) NULL)
757 svg_info->text=DestroyString(svg_info->text);
758 if (svg_info->vertices != (char *) NULL)
759 svg_info->vertices=DestroyString(svg_info->vertices);
760 if (svg_info->url != (char *) NULL)
761 svg_info->url=DestroyString(svg_info->url);
762 #if defined(MAGICKCORE_XML_DELEGATE)
763 if (svg_info->document != (xmlDocPtr) NULL)
765 xmlFreeDoc(svg_info->document);
766 svg_info->document=(xmlDocPtr) NULL;
771 static void SVGStartElement(void *context,const xmlChar *name,
772 const xmlChar **attributes)
777 token[MaxTextExtent],
797 Called when an opening tag has been processed.
799 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s",
801 svg_info=(SVGInfo *) context;
803 svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale,
804 svg_info->n+1UL,sizeof(*svg_info->scale));
805 if (svg_info->scale == (double *) NULL)
807 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
808 ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
811 svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
812 color=AcquireString("none");
813 units=AcquireString("userSpaceOnUse");
814 value=(const char *) NULL;
815 if (attributes != (const xmlChar **) NULL)
816 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
818 keyword=(const char *) attributes[i];
819 value=(const char *) attributes[i+1];
825 if (LocaleCompare(keyword,"cx") == 0)
827 svg_info->element.cx=
828 GetUserSpaceCoordinateValue(svg_info,1,value);
831 if (LocaleCompare(keyword,"cy") == 0)
833 svg_info->element.cy=
834 GetUserSpaceCoordinateValue(svg_info,-1,value);
842 if (LocaleCompare(keyword,"fx") == 0)
844 svg_info->element.major=
845 GetUserSpaceCoordinateValue(svg_info,1,value);
848 if (LocaleCompare(keyword,"fy") == 0)
850 svg_info->element.minor=
851 GetUserSpaceCoordinateValue(svg_info,-1,value);
859 if (LocaleCompare(keyword,"height") == 0)
861 svg_info->bounds.height=
862 GetUserSpaceCoordinateValue(svg_info,-1,value);
870 if (LocaleCompare(keyword,"id") == 0)
872 (void) CopyMagickString(id,value,MaxTextExtent);
880 if (LocaleCompare(keyword,"r") == 0)
882 svg_info->element.angle=
883 GetUserSpaceCoordinateValue(svg_info,0,value);
891 if (LocaleCompare(keyword,"width") == 0)
893 svg_info->bounds.width=
894 GetUserSpaceCoordinateValue(svg_info,1,value);
902 if (LocaleCompare(keyword,"x") == 0)
904 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value)-
908 if (LocaleCompare(keyword,"x1") == 0)
910 svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
914 if (LocaleCompare(keyword,"x2") == 0)
916 svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
925 if (LocaleCompare(keyword,"y") == 0)
927 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value)-
931 if (LocaleCompare(keyword,"y1") == 0)
933 svg_info->segment.y1=
934 GetUserSpaceCoordinateValue(svg_info,-1,value);
937 if (LocaleCompare(keyword,"y2") == 0)
939 svg_info->segment.y2=
940 GetUserSpaceCoordinateValue(svg_info,-1,value);
949 if (strchr((char *) name,':') != (char *) NULL)
954 for ( ; *name != ':'; name++) ;
962 if (LocaleCompare((const char *) name,"circle") == 0)
964 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
967 if (LocaleCompare((const char *) name,"clipPath") == 0)
969 (void) FormatLocaleFile(svg_info->file,"push clip-path '%s'\n",id);
977 if (LocaleCompare((const char *) name,"defs") == 0)
979 (void) FormatLocaleFile(svg_info->file,"push defs\n");
987 if (LocaleCompare((const char *) name,"ellipse") == 0)
989 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
997 if (LocaleCompare((const char *) name,"g") == 0)
999 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1007 if (LocaleCompare((const char *) name,"image") == 0)
1009 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1017 if (LocaleCompare((const char *) name,"line") == 0)
1019 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1022 if (LocaleCompare((const char *) name,"linearGradient") == 0)
1024 (void) FormatLocaleFile(svg_info->file,
1025 "push gradient '%s' linear %g,%g %g,%g\n",id,
1026 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
1027 svg_info->segment.y2);
1035 if (LocaleCompare((const char *) name,"path") == 0)
1037 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1040 if (LocaleCompare((const char *) name,"pattern") == 0)
1042 (void) FormatLocaleFile(svg_info->file,
1043 "push pattern '%s' %g,%g %g,%g\n",id,
1044 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
1045 svg_info->bounds.height);
1048 if (LocaleCompare((const char *) name,"polygon") == 0)
1050 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1053 if (LocaleCompare((const char *) name,"polyline") == 0)
1055 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1063 if (LocaleCompare((const char *) name,"radialGradient") == 0)
1065 (void) FormatLocaleFile(svg_info->file,
1066 "push gradient '%s' radial %g,%g %g,%g %g\n",
1067 id,svg_info->element.cx,svg_info->element.cy,
1068 svg_info->element.major,svg_info->element.minor,
1069 svg_info->element.angle);
1072 if (LocaleCompare((const char *) name,"rect") == 0)
1074 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1082 if (LocaleCompare((const char *) name,"svg") == 0)
1084 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1092 if (LocaleCompare((const char *) name,"text") == 0)
1094 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1097 if (LocaleCompare((const char *) name,"tspan") == 0)
1099 if (*svg_info->text != '\0')
1110 text=EscapeString(svg_info->text,'\'');
1111 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
1112 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1113 svg_info->center.y,text);
1114 text=DestroyString(text);
1115 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
1116 draw_info->pointsize=svg_info->pointsize;
1117 draw_info->text=AcquireString(svg_info->text);
1118 (void) ConcatenateString(&draw_info->text," ");
1119 GetTypeMetrics(svg_info->image,draw_info,&metrics);
1120 svg_info->bounds.x+=metrics.width;
1121 draw_info=DestroyDrawInfo(draw_info);
1122 *svg_info->text='\0';
1124 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
1132 if (attributes != (const xmlChar **) NULL)
1133 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1135 keyword=(const char *) attributes[i];
1136 value=(const char *) attributes[i+1];
1137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1138 " %s = %s",keyword,value);
1144 if (LocaleCompare(keyword,"angle") == 0)
1146 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
1147 GetUserSpaceCoordinateValue(svg_info,0,value));
1155 if (LocaleCompare(keyword,"clip-path") == 0)
1157 (void) FormatLocaleFile(svg_info->file,"clip-path '%s'\n",value);
1160 if (LocaleCompare(keyword,"clip-rule") == 0)
1162 (void) FormatLocaleFile(svg_info->file,"clip-rule '%s'\n",value);
1165 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1167 (void) CloneString(&units,value);
1168 (void) FormatLocaleFile(svg_info->file,"clip-units '%s'\n",value);
1171 if (LocaleCompare(keyword,"color") == 0)
1173 (void) CloneString(&color,value);
1176 if (LocaleCompare(keyword,"cx") == 0)
1178 svg_info->element.cx=
1179 GetUserSpaceCoordinateValue(svg_info,1,value);
1182 if (LocaleCompare(keyword,"cy") == 0)
1184 svg_info->element.cy=
1185 GetUserSpaceCoordinateValue(svg_info,-1,value);
1193 if (LocaleCompare(keyword,"d") == 0)
1195 (void) CloneString(&svg_info->vertices,value);
1198 if (LocaleCompare(keyword,"dx") == 0)
1200 svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
1203 if (LocaleCompare(keyword,"dy") == 0)
1205 svg_info->bounds.y+=
1206 GetUserSpaceCoordinateValue(svg_info,-1,value);
1214 if (LocaleCompare(keyword,"fill") == 0)
1216 if (LocaleCompare(value,"currentColor") == 0)
1218 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",color);
1221 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
1224 if (LocaleCompare(keyword,"fillcolor") == 0)
1226 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
1229 if (LocaleCompare(keyword,"fill-rule") == 0)
1231 (void) FormatLocaleFile(svg_info->file,"fill-rule '%s'\n",value);
1234 if (LocaleCompare(keyword,"fill-opacity") == 0)
1236 (void) FormatLocaleFile(svg_info->file,"fill-opacity '%s'\n",
1240 if (LocaleCompare(keyword,"font-family") == 0)
1242 (void) FormatLocaleFile(svg_info->file,"font-family '%s'\n",
1246 if (LocaleCompare(keyword,"font-stretch") == 0)
1248 (void) FormatLocaleFile(svg_info->file,"font-stretch '%s'\n",
1252 if (LocaleCompare(keyword,"font-style") == 0)
1254 (void) FormatLocaleFile(svg_info->file,"font-style '%s'\n",value);
1257 if (LocaleCompare(keyword,"font-size") == 0)
1259 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
1260 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1261 svg_info->pointsize);
1264 if (LocaleCompare(keyword,"font-weight") == 0)
1266 (void) FormatLocaleFile(svg_info->file,"font-weight '%s'\n",
1275 if (LocaleCompare(keyword,"gradientTransform") == 0)
1282 GetAffineMatrix(&transform);
1283 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1284 tokens=GetTransformTokens(context,value,&number_tokens);
1285 for (j=0; j < (number_tokens-1); j+=2)
1287 keyword=(char *) tokens[j];
1288 if (keyword == (char *) NULL)
1290 value=(char *) tokens[j+1];
1291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1292 " %s: %s",keyword,value);
1294 GetAffineMatrix(&affine);
1300 if (LocaleCompare(keyword,"matrix") == 0)
1302 p=(const char *) value;
1303 GetMagickToken(p,&p,token);
1304 affine.sx=InterpretLocaleValue(value,(char **) NULL);
1305 GetMagickToken(p,&p,token);
1307 GetMagickToken(p,&p,token);
1308 affine.rx=InterpretLocaleValue(token,(char **) NULL);
1309 GetMagickToken(p,&p,token);
1311 GetMagickToken(p,&p,token);
1312 affine.ry=InterpretLocaleValue(token,(char **) NULL);
1313 GetMagickToken(p,&p,token);
1315 GetMagickToken(p,&p,token);
1316 affine.sy=InterpretLocaleValue(token,(char **) NULL);
1317 GetMagickToken(p,&p,token);
1319 GetMagickToken(p,&p,token);
1320 affine.tx=InterpretLocaleValue(token,(char **) NULL);
1321 GetMagickToken(p,&p,token);
1323 GetMagickToken(p,&p,token);
1324 affine.ty=InterpretLocaleValue(token,(char **) NULL);
1332 if (LocaleCompare(keyword,"rotate") == 0)
1337 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1338 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1339 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1340 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1341 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1349 if (LocaleCompare(keyword,"scale") == 0)
1351 for (p=(const char *) value; *p != '\0'; p++)
1352 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1355 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1356 affine.sy=affine.sx;
1359 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1360 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1363 if (LocaleCompare(keyword,"skewX") == 0)
1365 affine.sx=svg_info->affine.sx;
1366 affine.ry=tan(DegreesToRadians(fmod(
1367 GetUserSpaceCoordinateValue(svg_info,1,value),
1369 affine.sy=svg_info->affine.sy;
1372 if (LocaleCompare(keyword,"skewY") == 0)
1374 affine.sx=svg_info->affine.sx;
1375 affine.rx=tan(DegreesToRadians(fmod(
1376 GetUserSpaceCoordinateValue(svg_info,-1,value),
1378 affine.sy=svg_info->affine.sy;
1386 if (LocaleCompare(keyword,"translate") == 0)
1388 for (p=(const char *) value; *p != '\0'; p++)
1389 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1392 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1393 affine.ty=affine.tx;
1396 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1404 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1405 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1406 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1407 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1408 transform.tx=affine.sx*current.tx+affine.ry*current.ty+
1410 transform.ty=affine.rx*current.tx+affine.sy*current.ty+
1413 (void) FormatLocaleFile(svg_info->file,
1414 "affine %g %g %g %g %g %g\n",transform.sx,
1415 transform.rx,transform.ry,transform.sy,transform.tx,
1417 for (j=0; tokens[j] != (char *) NULL; j++)
1418 tokens[j]=DestroyString(tokens[j]);
1419 tokens=(char **) RelinquishMagickMemory(tokens);
1422 if (LocaleCompare(keyword,"gradientUnits") == 0)
1424 (void) CloneString(&units,value);
1425 (void) FormatLocaleFile(svg_info->file,"gradient-units '%s'\n",
1434 if (LocaleCompare(keyword,"height") == 0)
1436 svg_info->bounds.height=
1437 GetUserSpaceCoordinateValue(svg_info,-1,value);
1440 if (LocaleCompare(keyword,"href") == 0)
1442 (void) CloneString(&svg_info->url,value);
1450 if (LocaleCompare(keyword,"major") == 0)
1452 svg_info->element.major=
1453 GetUserSpaceCoordinateValue(svg_info,1,value);
1456 if (LocaleCompare(keyword,"minor") == 0)
1458 svg_info->element.minor=
1459 GetUserSpaceCoordinateValue(svg_info,-1,value);
1467 if (LocaleCompare(keyword,"offset") == 0)
1469 (void) CloneString(&svg_info->offset,value);
1472 if (LocaleCompare(keyword,"opacity") == 0)
1474 (void) FormatLocaleFile(svg_info->file,"opacity '%s'\n",value);
1482 if (LocaleCompare(keyword,"path") == 0)
1484 (void) CloneString(&svg_info->url,value);
1487 if (LocaleCompare(keyword,"points") == 0)
1489 (void) CloneString(&svg_info->vertices,value);
1497 if (LocaleCompare(keyword,"r") == 0)
1499 svg_info->element.major=
1500 GetUserSpaceCoordinateValue(svg_info,1,value);
1501 svg_info->element.minor=
1502 GetUserSpaceCoordinateValue(svg_info,-1,value);
1505 if (LocaleCompare(keyword,"rotate") == 0)
1510 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1511 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
1512 svg_info->bounds.x,svg_info->bounds.y);
1513 svg_info->bounds.x=0;
1514 svg_info->bounds.y=0;
1515 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
1518 if (LocaleCompare(keyword,"rx") == 0)
1520 if (LocaleCompare((const char *) name,"ellipse") == 0)
1521 svg_info->element.major=
1522 GetUserSpaceCoordinateValue(svg_info,1,value);
1525 GetUserSpaceCoordinateValue(svg_info,1,value);
1528 if (LocaleCompare(keyword,"ry") == 0)
1530 if (LocaleCompare((const char *) name,"ellipse") == 0)
1531 svg_info->element.minor=
1532 GetUserSpaceCoordinateValue(svg_info,-1,value);
1535 GetUserSpaceCoordinateValue(svg_info,-1,value);
1543 if (LocaleCompare(keyword,"stop-color") == 0)
1545 (void) CloneString(&svg_info->stop_color,value);
1548 if (LocaleCompare(keyword,"stroke") == 0)
1550 if (LocaleCompare(value,"currentColor") == 0)
1552 (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",color);
1555 (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",value);
1558 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1560 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
1561 LocaleCompare(value,"true") == 0);
1564 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1566 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1570 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1572 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %s\n",
1576 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1578 (void) FormatLocaleFile(svg_info->file,"stroke-linecap '%s'\n",
1582 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1584 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin '%s'\n",
1588 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1590 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit '%s'\n",
1594 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1596 (void) FormatLocaleFile(svg_info->file,"stroke-opacity '%s'\n",
1600 if (LocaleCompare(keyword,"stroke-width") == 0)
1602 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
1603 GetUserSpaceCoordinateValue(svg_info,1,value));
1606 if (LocaleCompare(keyword,"style") == 0)
1608 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1609 tokens=GetStyleTokens(context,value,&number_tokens);
1610 for (j=0; j < (number_tokens-1); j+=2)
1612 keyword=(char *) tokens[j];
1613 value=(char *) tokens[j+1];
1614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1615 " %s: %s",keyword,value);
1621 if (LocaleCompare(keyword,"clip-path") == 0)
1623 (void) FormatLocaleFile(svg_info->file,
1624 "clip-path '%s'\n",value);
1627 if (LocaleCompare(keyword,"clip-rule") == 0)
1629 (void) FormatLocaleFile(svg_info->file,
1630 "clip-rule '%s'\n",value);
1633 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1635 (void) CloneString(&units,value);
1636 (void) FormatLocaleFile(svg_info->file,
1637 "clip-units '%s'\n",value);
1640 if (LocaleCompare(keyword,"color") == 0)
1642 (void) CloneString(&color,value);
1650 if (LocaleCompare(keyword,"fill") == 0)
1652 if (LocaleCompare(value,"currentColor") == 0)
1654 (void) FormatLocaleFile(svg_info->file,
1655 "fill '%s'\n",color);
1658 if (LocaleCompare(value,"#00000000") == 0)
1659 (void) FormatLocaleFile(svg_info->file,
1660 "fill '#000000'\n");
1662 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1666 if (LocaleCompare(keyword,"fillcolor") == 0)
1668 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1672 if (LocaleCompare(keyword,"fill-rule") == 0)
1674 (void) FormatLocaleFile(svg_info->file,
1675 "fill-rule '%s'\n",value);
1678 if (LocaleCompare(keyword,"fill-opacity") == 0)
1680 (void) FormatLocaleFile(svg_info->file,
1681 "fill-opacity '%s'\n",value);
1684 if (LocaleCompare(keyword,"font-family") == 0)
1686 (void) FormatLocaleFile(svg_info->file,
1687 "font-family '%s'\n",value);
1690 if (LocaleCompare(keyword,"font-stretch") == 0)
1692 (void) FormatLocaleFile(svg_info->file,
1693 "font-stretch '%s'\n",value);
1696 if (LocaleCompare(keyword,"font-style") == 0)
1698 (void) FormatLocaleFile(svg_info->file,
1699 "font-style '%s'\n",value);
1702 if (LocaleCompare(keyword,"font-size") == 0)
1704 svg_info->pointsize=GetUserSpaceCoordinateValue(
1706 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1707 svg_info->pointsize);
1710 if (LocaleCompare(keyword,"font-weight") == 0)
1712 (void) FormatLocaleFile(svg_info->file,
1713 "font-weight '%s'\n",value);
1721 if (LocaleCompare(keyword,"offset") == 0)
1723 (void) FormatLocaleFile(svg_info->file,"offset %g\n",
1724 GetUserSpaceCoordinateValue(svg_info,1,value));
1727 if (LocaleCompare(keyword,"opacity") == 0)
1729 (void) FormatLocaleFile(svg_info->file,
1730 "opacity '%s'\n",value);
1738 if (LocaleCompare(keyword,"stop-color") == 0)
1740 (void) CloneString(&svg_info->stop_color,value);
1743 if (LocaleCompare(keyword,"stroke") == 0)
1745 if (LocaleCompare(value,"currentColor") == 0)
1747 (void) FormatLocaleFile(svg_info->file,
1748 "stroke '%s'\n",color);
1751 if (LocaleCompare(value,"#00000000") == 0)
1752 (void) FormatLocaleFile(svg_info->file,
1753 "fill '#000000'\n");
1755 (void) FormatLocaleFile(svg_info->file,
1756 "stroke '%s'\n",value);
1759 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1761 (void) FormatLocaleFile(svg_info->file,
1762 "stroke-antialias %d\n",
1763 LocaleCompare(value,"true") == 0);
1766 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1768 (void) FormatLocaleFile(svg_info->file,
1769 "stroke-dasharray %s\n",value);
1772 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1774 (void) FormatLocaleFile(svg_info->file,
1775 "stroke-dashoffset %s\n",
1779 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1781 (void) FormatLocaleFile(svg_info->file,
1782 "stroke-linecap '%s'\n",value);
1785 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1787 (void) FormatLocaleFile(svg_info->file,
1788 "stroke-linejoin '%s'\n",
1792 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1794 (void) FormatLocaleFile(svg_info->file,
1795 "stroke-miterlimit '%s'\n",
1799 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1801 (void) FormatLocaleFile(svg_info->file,
1802 "stroke-opacity '%s'\n",value);
1805 if (LocaleCompare(keyword,"stroke-width") == 0)
1807 (void) FormatLocaleFile(svg_info->file,
1808 "stroke-width %g\n",
1809 GetUserSpaceCoordinateValue(svg_info,1,value));
1817 if (LocaleCompare(keyword,"text-align") == 0)
1819 (void) FormatLocaleFile(svg_info->file,
1820 "text-align '%s'\n",value);
1823 if (LocaleCompare(keyword,"text-anchor") == 0)
1825 (void) FormatLocaleFile(svg_info->file,
1826 "text-anchor '%s'\n",value);
1829 if (LocaleCompare(keyword,"text-decoration") == 0)
1831 if (LocaleCompare(value,"underline") == 0)
1832 (void) FormatLocaleFile(svg_info->file,
1833 "decorate underline\n");
1834 if (LocaleCompare(value,"line-through") == 0)
1835 (void) FormatLocaleFile(svg_info->file,
1836 "decorate line-through\n");
1837 if (LocaleCompare(value,"overline") == 0)
1838 (void) FormatLocaleFile(svg_info->file,
1839 "decorate overline\n");
1842 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1844 (void) FormatLocaleFile(svg_info->file,
1845 "text-antialias %d\n",
1846 LocaleCompare(value,"true") == 0);
1855 for (j=0; tokens[j] != (char *) NULL; j++)
1856 tokens[j]=DestroyString(tokens[j]);
1857 tokens=(char **) RelinquishMagickMemory(tokens);
1865 if (LocaleCompare(keyword,"text-align") == 0)
1867 (void) FormatLocaleFile(svg_info->file,"text-align '%s'\n",
1871 if (LocaleCompare(keyword,"text-anchor") == 0)
1873 (void) FormatLocaleFile(svg_info->file,"text-anchor '%s'\n",
1877 if (LocaleCompare(keyword,"text-decoration") == 0)
1879 if (LocaleCompare(value,"underline") == 0)
1880 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
1881 if (LocaleCompare(value,"line-through") == 0)
1882 (void) FormatLocaleFile(svg_info->file,
1883 "decorate line-through\n");
1884 if (LocaleCompare(value,"overline") == 0)
1885 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
1888 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1890 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
1891 LocaleCompare(value,"true") == 0);
1894 if (LocaleCompare(keyword,"transform") == 0)
1901 GetAffineMatrix(&transform);
1902 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1903 tokens=GetTransformTokens(context,value,&number_tokens);
1904 for (j=0; j < (number_tokens-1); j+=2)
1906 keyword=(char *) tokens[j];
1907 value=(char *) tokens[j+1];
1908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1909 " %s: %s",keyword,value);
1911 GetAffineMatrix(&affine);
1917 if (LocaleCompare(keyword,"matrix") == 0)
1919 p=(const char *) value;
1920 GetMagickToken(p,&p,token);
1921 affine.sx=InterpretLocaleValue(value,(char **) NULL);
1922 GetMagickToken(p,&p,token);
1924 GetMagickToken(p,&p,token);
1925 affine.rx=InterpretLocaleValue(token,(char **) NULL);
1926 GetMagickToken(p,&p,token);
1928 GetMagickToken(p,&p,token);
1929 affine.ry=InterpretLocaleValue(token,(char **) NULL);
1930 GetMagickToken(p,&p,token);
1932 GetMagickToken(p,&p,token);
1933 affine.sy=InterpretLocaleValue(token,(char **) NULL);
1934 GetMagickToken(p,&p,token);
1936 GetMagickToken(p,&p,token);
1937 affine.tx=InterpretLocaleValue(token,(char **) NULL);
1938 GetMagickToken(p,&p,token);
1940 GetMagickToken(p,&p,token);
1941 affine.ty=InterpretLocaleValue(token,(char **) NULL);
1949 if (LocaleCompare(keyword,"rotate") == 0)
1956 p=(const char *) value;
1957 GetMagickToken(p,&p,token);
1958 angle=InterpretLocaleValue(value,(char **) NULL);
1959 GetMagickToken(p,&p,token);
1961 GetMagickToken(p,&p,token);
1962 x=InterpretLocaleValue(token,(char **) NULL);
1963 GetMagickToken(p,&p,token);
1965 GetMagickToken(p,&p,token);
1966 y=InterpretLocaleValue(token,(char **) NULL);
1967 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1968 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1969 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1970 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1973 svg_info->center.x=x;
1974 svg_info->center.y=y;
1982 if (LocaleCompare(keyword,"scale") == 0)
1984 for (p=(const char *) value; *p != '\0'; p++)
1985 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1988 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1989 affine.sy=affine.sx;
1991 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
1993 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1996 if (LocaleCompare(keyword,"skewX") == 0)
1998 affine.sx=svg_info->affine.sx;
1999 affine.ry=tan(DegreesToRadians(fmod(
2000 GetUserSpaceCoordinateValue(svg_info,1,value),
2002 affine.sy=svg_info->affine.sy;
2005 if (LocaleCompare(keyword,"skewY") == 0)
2007 affine.sx=svg_info->affine.sx;
2008 affine.rx=tan(DegreesToRadians(fmod(
2009 GetUserSpaceCoordinateValue(svg_info,-1,value),
2011 affine.sy=svg_info->affine.sy;
2019 if (LocaleCompare(keyword,"translate") == 0)
2021 for (p=(const char *) value; *p != '\0'; p++)
2022 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2025 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2026 affine.ty=affine.tx;
2028 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2037 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2038 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2039 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2040 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2041 transform.tx=affine.sx*current.tx+affine.ry*current.ty+
2043 transform.ty=affine.rx*current.tx+affine.sy*current.ty+
2046 (void) FormatLocaleFile(svg_info->file,
2047 "affine %g %g %g %g 0.0 0.0\n",transform.sx,transform.rx,
2048 transform.ry,transform.sy);
2049 for (j=0; tokens[j] != (char *) NULL; j++)
2050 tokens[j]=DestroyString(tokens[j]);
2051 tokens=(char **) RelinquishMagickMemory(tokens);
2059 if (LocaleCompare(keyword,"verts") == 0)
2061 (void) CloneString(&svg_info->vertices,value);
2064 if (LocaleCompare(keyword,"viewBox") == 0)
2066 p=(const char *) value;
2067 GetMagickToken(p,&p,token);
2068 svg_info->view_box.x=InterpretLocaleValue(token,(char **) NULL);
2069 GetMagickToken(p,&p,token);
2071 GetMagickToken(p,&p,token);
2072 svg_info->view_box.y=InterpretLocaleValue(token,(char **) NULL);
2073 GetMagickToken(p,&p,token);
2075 GetMagickToken(p,&p,token);
2076 svg_info->view_box.width=InterpretLocaleValue(token,
2078 if (svg_info->bounds.width == 0)
2079 svg_info->bounds.width=svg_info->view_box.width;
2080 GetMagickToken(p,&p,token);
2082 GetMagickToken(p,&p,token);
2083 svg_info->view_box.height=InterpretLocaleValue(token,
2085 if (svg_info->bounds.height == 0)
2086 svg_info->bounds.height=svg_info->view_box.height;
2094 if (LocaleCompare(keyword,"width") == 0)
2096 svg_info->bounds.width=
2097 GetUserSpaceCoordinateValue(svg_info,1,value);
2105 if (LocaleCompare(keyword,"x") == 0)
2107 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2110 if (LocaleCompare(keyword,"xlink:href") == 0)
2112 (void) CloneString(&svg_info->url,value);
2115 if (LocaleCompare(keyword,"x1") == 0)
2117 svg_info->segment.x1=
2118 GetUserSpaceCoordinateValue(svg_info,1,value);
2121 if (LocaleCompare(keyword,"x2") == 0)
2123 svg_info->segment.x2=
2124 GetUserSpaceCoordinateValue(svg_info,1,value);
2132 if (LocaleCompare(keyword,"y") == 0)
2134 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2137 if (LocaleCompare(keyword,"y1") == 0)
2139 svg_info->segment.y1=
2140 GetUserSpaceCoordinateValue(svg_info,-1,value);
2143 if (LocaleCompare(keyword,"y2") == 0)
2145 svg_info->segment.y2=
2146 GetUserSpaceCoordinateValue(svg_info,-1,value);
2155 if (LocaleCompare((const char *) name,"svg") == 0)
2157 if (svg_info->document->encoding != (const xmlChar *) NULL)
2158 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
2159 (const char *) svg_info->document->encoding);
2160 if (attributes != (const xmlChar **) NULL)
2166 if ((svg_info->view_box.width == 0.0) ||
2167 (svg_info->view_box.height == 0.0))
2168 svg_info->view_box=svg_info->bounds;
2169 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2170 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
2171 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2172 (double) svg_info->width,(double) svg_info->height);
2173 sx=(double) svg_info->width/svg_info->view_box.width;
2174 sy=(double) svg_info->height/svg_info->view_box.height;
2175 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g 0.0 0.0\n",
2179 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2180 units=DestroyString(units);
2181 if (color != (char *) NULL)
2182 color=DestroyString(color);
2185 static void SVGEndElement(void *context,const xmlChar *name)
2191 Called when the end of an element has been detected.
2193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2194 " SAX.endElement(%s)",name);
2195 svg_info=(SVGInfo *) context;
2196 if (strchr((char *) name,':') != (char *) NULL)
2199 Skip over namespace.
2201 for ( ; *name != ':'; name++) ;
2209 if (LocaleCompare((const char *) name,"circle") == 0)
2211 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
2212 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2213 svg_info->element.cy+svg_info->element.minor);
2214 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2217 if (LocaleCompare((const char *) name,"clipPath") == 0)
2219 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
2227 if (LocaleCompare((const char *) name,"defs") == 0)
2229 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
2232 if (LocaleCompare((const char *) name,"desc") == 0)
2237 if (*svg_info->text == '\0')
2239 (void) fputc('#',svg_info->file);
2240 for (p=svg_info->text; *p != '\0'; p++)
2242 (void) fputc(*p,svg_info->file);
2244 (void) fputc('#',svg_info->file);
2246 (void) fputc('\n',svg_info->file);
2247 *svg_info->text='\0';
2255 if (LocaleCompare((const char *) name,"ellipse") == 0)
2260 angle=svg_info->element.angle;
2261 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
2262 svg_info->element.cx,svg_info->element.cy,
2263 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2264 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
2265 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2273 if (LocaleCompare((const char *) name,"g") == 0)
2275 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2283 if (LocaleCompare((const char *) name,"image") == 0)
2285 (void) FormatLocaleFile(svg_info->file,
2286 "image Over %g,%g %g,%g '%s'\n",svg_info->bounds.x,
2287 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2289 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2297 if (LocaleCompare((const char *) name,"line") == 0)
2299 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
2300 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2301 svg_info->segment.y2);
2302 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2305 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2307 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2315 if (LocaleCompare((const char *) name,"pattern") == 0)
2317 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
2320 if (LocaleCompare((const char *) name,"path") == 0)
2322 (void) FormatLocaleFile(svg_info->file,"path '%s'\n",
2323 svg_info->vertices);
2324 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2327 if (LocaleCompare((const char *) name,"polygon") == 0)
2329 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2330 svg_info->vertices);
2331 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2334 if (LocaleCompare((const char *) name,"polyline") == 0)
2336 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2337 svg_info->vertices);
2338 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2346 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2348 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
2351 if (LocaleCompare((const char *) name,"rect") == 0)
2353 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2355 (void) FormatLocaleFile(svg_info->file,"rectangle %g,%g %g,%g\n",
2356 svg_info->bounds.x,svg_info->bounds.y,
2357 svg_info->bounds.x+svg_info->bounds.width,
2358 svg_info->bounds.y+svg_info->bounds.height);
2359 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2362 if (svg_info->radius.x == 0.0)
2363 svg_info->radius.x=svg_info->radius.y;
2364 if (svg_info->radius.y == 0.0)
2365 svg_info->radius.y=svg_info->radius.x;
2366 (void) FormatLocaleFile(svg_info->file,
2367 "roundRectangle %g,%g %g,%g %g,%g\n",
2368 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2369 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2370 svg_info->radius.x,svg_info->radius.y);
2371 svg_info->radius.x=0.0;
2372 svg_info->radius.y=0.0;
2373 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2381 if (LocaleCompare((const char *) name,"stop") == 0)
2383 (void) FormatLocaleFile(svg_info->file,"stop-color '%s' %s\n",
2384 svg_info->stop_color,svg_info->offset);
2387 if (LocaleCompare((const char *) name,"svg") == 0)
2389 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2397 if (LocaleCompare((const char *) name,"text") == 0)
2399 if (*svg_info->text != '\0')
2404 text=EscapeString(svg_info->text,'\'');
2405 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
2406 svg_info->bounds.x,svg_info->bounds.y,text);
2407 text=DestroyString(text);
2408 *svg_info->text='\0';
2410 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2413 if (LocaleCompare((const char *) name,"tspan") == 0)
2415 if (*svg_info->text != '\0')
2426 text=EscapeString(svg_info->text,'\'');
2427 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
2428 svg_info->bounds.x,svg_info->bounds.y,text);
2429 text=DestroyString(text);
2430 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
2431 draw_info->pointsize=svg_info->pointsize;
2432 draw_info->text=AcquireString(svg_info->text);
2433 (void) ConcatenateString(&draw_info->text," ");
2434 GetTypeMetrics(svg_info->image,draw_info,&metrics);
2435 svg_info->bounds.x+=metrics.width;
2436 draw_info=DestroyDrawInfo(draw_info);
2437 *svg_info->text='\0';
2439 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
2442 if (LocaleCompare((const char *) name,"title") == 0)
2444 if (*svg_info->text == '\0')
2446 (void) CloneString(&svg_info->title,svg_info->text);
2447 *svg_info->text='\0';
2455 *svg_info->text='\0';
2456 (void) ResetMagickMemory(&svg_info->element,0,sizeof(svg_info->element));
2457 (void) ResetMagickMemory(&svg_info->segment,0,sizeof(svg_info->segment));
2461 static void SVGCharacters(void *context,const xmlChar *c,int length)
2473 Receiving some characters from the parser.
2475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2476 " SAX.characters(%s,%.20g)",c,(double) length);
2477 svg_info=(SVGInfo *) context;
2478 if (svg_info->text != (char *) NULL)
2479 svg_info->text=(char *) ResizeQuantumMemory(svg_info->text,
2480 strlen(svg_info->text)+length+MaxTextExtent,sizeof(*svg_info->text));
2483 svg_info->text=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2484 sizeof(*svg_info->text));
2485 if (svg_info->text != (char *) NULL)
2486 *svg_info->text='\0';
2488 if (svg_info->text == (char *) NULL)
2490 p=svg_info->text+strlen(svg_info->text);
2491 for (i=0; i < (ssize_t) length; i++)
2496 static void SVGReference(void *context,const xmlChar *name)
2505 Called when an entity reference is detected.
2507 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2509 svg_info=(SVGInfo *) context;
2510 parser=svg_info->parser;
2511 if (parser == (xmlParserCtxtPtr) NULL)
2513 if (parser->node == (xmlNodePtr) NULL)
2516 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2518 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2521 static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2527 Receiving some ignorable whitespaces from the parser.
2529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2530 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2531 svg_info=(SVGInfo *) context;
2535 static void SVGProcessingInstructions(void *context,const xmlChar *target,
2536 const xmlChar *data)
2542 A processing instruction has been parsed.
2544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2545 " SAX.processingInstruction(%s, %s)",target,data);
2546 svg_info=(SVGInfo *) context;
2550 static void SVGComment(void *context,const xmlChar *value)
2556 A comment has been parsed.
2558 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
2560 svg_info=(SVGInfo *) context;
2561 if (svg_info->comment != (char *) NULL)
2562 (void) ConcatenateString(&svg_info->comment,"\n");
2563 (void) ConcatenateString(&svg_info->comment,(const char *) value);
2566 static void SVGWarning(void *context,const char *format,...)
2570 reason[MaxTextExtent];
2579 Display and format a warning messages, gives file, line, position and
2582 va_start(operands,format);
2583 svg_info=(SVGInfo *) context;
2584 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
2585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2586 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2587 (void) vsprintf(reason,format,operands);
2589 (void) vsnprintf(reason,MaxTextExtent,format,operands);
2591 message=GetExceptionMessage(errno);
2592 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2593 DelegateWarning,reason,"`%s`",message);
2594 message=DestroyString(message);
2598 static void SVGError(void *context,const char *format,...)
2602 reason[MaxTextExtent];
2611 Display and format a error formats, gives file, line, position and
2614 va_start(operands,format);
2615 svg_info=(SVGInfo *) context;
2616 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
2617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2618 #if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2619 (void) vsprintf(reason,format,operands);
2621 (void) vsnprintf(reason,MaxTextExtent,format,operands);
2623 message=GetExceptionMessage(errno);
2624 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2625 reason,"`%s`",message);
2626 message=DestroyString(message);
2630 static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2642 Called when a pcdata block has been parsed.
2644 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
2646 svg_info=(SVGInfo *) context;
2647 parser=svg_info->parser;
2648 child=xmlGetLastChild(parser->node);
2649 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2651 xmlTextConcat(child,value,length);
2654 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2657 static void SVGExternalSubset(void *context,const xmlChar *name,
2658 const xmlChar *external_id,const xmlChar *system_id)
2673 Does this document has an external subset?
2675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2676 " SAX.externalSubset(%s, %s, %s)",name,
2677 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2678 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2679 svg_info=(SVGInfo *) context;
2680 parser=svg_info->parser;
2681 if (((external_id == NULL) && (system_id == NULL)) ||
2682 ((parser->validate == 0) || (parser->wellFormed == 0) ||
2683 (svg_info->document == 0)))
2685 input=SVGResolveEntity(context,external_id,system_id);
2688 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2689 parser_context=(*parser);
2690 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2691 if (parser->inputTab == (xmlParserInputPtr *) NULL)
2693 parser->errNo=XML_ERR_NO_MEMORY;
2694 parser->input=parser_context.input;
2695 parser->inputNr=parser_context.inputNr;
2696 parser->inputMax=parser_context.inputMax;
2697 parser->inputTab=parser_context.inputTab;
2703 xmlPushInput(parser,input);
2704 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2705 if (input->filename == (char *) NULL)
2706 input->filename=(char *) xmlStrdup(system_id);
2709 input->base=parser->input->cur;
2710 input->cur=parser->input->cur;
2712 xmlParseExternalSubset(parser,external_id,system_id);
2713 while (parser->inputNr > 1)
2714 (void) xmlPopInput(parser);
2715 xmlFreeInputStream(parser->input);
2716 xmlFree(parser->inputTab);
2717 parser->input=parser_context.input;
2718 parser->inputNr=parser_context.inputNr;
2719 parser->inputMax=parser_context.inputMax;
2720 parser->inputTab=parser_context.inputTab;
2723 #if defined(MAGICKCORE_RSVG_DELEGATE)
2724 static void SVGSetImageSize(int *width,int *height,gpointer context)
2729 image=(Image *) context;
2730 *width=(int) (*width*image->x_resolution/72.0);
2731 *height=(int) (*height*image->y_resolution/72.0);
2735 #if defined(__cplusplus) || defined(c_plusplus)
2739 static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2742 filename[MaxTextExtent];
2761 message[MaxTextExtent];
2772 assert(image_info != (const ImageInfo *) NULL);
2773 assert(image_info->signature == MagickSignature);
2774 assert(exception != (ExceptionInfo *) NULL);
2775 if (image_info->debug != MagickFalse)
2776 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2777 image_info->filename);
2778 assert(exception->signature == MagickSignature);
2779 image=AcquireImage(image_info);
2780 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2781 if (status == MagickFalse)
2783 image=DestroyImageList(image);
2784 return((Image *) NULL);
2786 if (LocaleCompare(image_info->magick,"MSVG") != 0)
2788 #if defined(MAGICKCORE_RSVG_DELEGATE)
2789 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2796 register unsigned char
2809 register const guchar
2832 svg_handle=rsvg_handle_new();
2833 if (svg_handle == (RsvgHandle *) NULL)
2834 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2835 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
2836 rsvg_handle_set_size_callback(svg_handle,SVGSetImageSize,image,NULL);
2837 if ((image->x_resolution != 72.0) && (image->y_resolution != 72.0))
2838 rsvg_handle_set_dpi_x_y(svg_handle,image->x_resolution,
2839 image->y_resolution);
2840 while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
2842 error=(GError *) NULL;
2843 (void) rsvg_handle_write(svg_handle,message,n,&error);
2844 if (error != (GError *) NULL)
2845 g_error_free(error);
2847 error=(GError *) NULL;
2848 rsvg_handle_close(svg_handle,&error);
2849 if (error != (GError *) NULL)
2850 g_error_free(error);
2851 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2852 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
2853 image->columns=dimension_info.width;
2854 image->rows=dimension_info.height;
2855 pixels=(unsigned char *) NULL;
2857 pixel_info=rsvg_handle_get_pixbuf(svg_handle);
2858 rsvg_handle_free(svg_handle);
2859 image->columns=gdk_pixbuf_get_width(pixel_info);
2860 image->rows=gdk_pixbuf_get_height(pixel_info);
2862 image->matte=MagickTrue;
2863 SetImageProperty(image,"svg:base-uri",
2864 rsvg_handle_get_base_uri(svg_handle));
2865 SetImageProperty(image,"svg:title",rsvg_handle_get_title(svg_handle));
2866 SetImageProperty(image,"svg:description",
2867 rsvg_handle_get_desc(svg_handle));
2868 if ((image->columns == 0) || (image->rows == 0))
2870 #if !defined(MAGICKCORE_CAIRO_DELEGATE)
2871 g_object_unref(G_OBJECT(pixel_info));
2873 g_object_unref(svg_handle);
2874 ThrowReaderException(MissingDelegateError,
2875 "NoDecodeDelegateForThisImageFormat");
2877 if (image_info->ping == MagickFalse)
2879 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2880 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,4*
2881 image->rows*sizeof(*pixels));
2882 if (pixels == (unsigned char *) NULL)
2884 g_object_unref(svg_handle);
2885 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2888 (void) SetImageBackgroundColor(image);
2889 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2890 cairo_surface=cairo_image_surface_create_for_data(pixels,
2891 CAIRO_FORMAT_ARGB32,image->columns,image->rows,4*image->columns);
2892 if (cairo_surface == (cairo_surface_t *) NULL)
2894 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2895 g_object_unref(svg_handle);
2896 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2898 cairo_info=cairo_create(cairo_surface);
2899 cairo_set_operator(cairo_info,CAIRO_OPERATOR_CLEAR);
2900 cairo_paint(cairo_info);
2901 cairo_set_operator(cairo_info,CAIRO_OPERATOR_OVER);
2902 rsvg_handle_render_cairo(svg_handle,cairo_info);
2903 cairo_destroy(cairo_info);
2904 cairo_surface_destroy(cairo_surface);
2905 g_object_unref(svg_handle);
2908 p=gdk_pixbuf_get_pixels(pixel_info);
2910 for (y=0; y < (ssize_t) image->rows; y++)
2912 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2913 if (q == (const Quantum *) NULL)
2915 for (x=0; x < (ssize_t) image->columns; x++)
2917 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2918 fill_color.blue=ScaleCharToQuantum(*p++);
2919 fill_color.green=ScaleCharToQuantum(*p++);
2920 fill_color.red=ScaleCharToQuantum(*p++);
2922 fill_color.red=ScaleCharToQuantum(*p++);
2923 fill_color.green=ScaleCharToQuantum(*p++);
2924 fill_color.blue=ScaleCharToQuantum(*p++);
2926 fill_color.alpha=ScaleCharToQuantum(*p++);
2927 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2932 gamma=1.0-QuantumScale*fill_color.alpha;
2933 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2934 fill_color.blue*=gamma;
2935 fill_color.green*=gamma;
2936 fill_color.red*=gamma;
2939 CompositePixelOver(image,&fill_color,fill_color.alpha,q,
2940 (MagickRealType) GetPixelAlpha(image,q),q);
2941 q+=GetPixelChannels(image);
2943 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2945 if (image->previous == (Image *) NULL)
2947 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2949 if (status == MagickFalse)
2954 #if defined(MAGICKCORE_CAIRO_DELEGATE)
2955 if (pixels != (unsigned char *) NULL)
2956 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2958 g_object_unref(G_OBJECT(pixel_info));
2960 (void) CloseBlob(image);
2961 return(GetFirstImageInList(image));
2968 unique_file=AcquireUniqueFileResource(filename);
2969 if (unique_file != -1)
2970 file=fdopen(unique_file,"w");
2971 if ((unique_file == -1) || (file == (FILE *) NULL))
2973 (void) CopyMagickString(image->filename,filename,MaxTextExtent);
2974 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
2976 image=DestroyImageList(image);
2977 return((Image *) NULL);
2982 svg_info=AcquireSVGInfo();
2983 if (svg_info == (SVGInfo *) NULL)
2984 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2985 svg_info->file=file;
2986 svg_info->exception=exception;
2987 svg_info->image=image;
2988 svg_info->image_info=image_info;
2989 svg_info->bounds.width=image->columns;
2990 svg_info->bounds.height=image->rows;
2991 if (image_info->size != (char *) NULL)
2992 (void) CloneString(&svg_info->size,image_info->size);
2993 if (image->debug != MagickFalse)
2994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
2996 (void) xmlSubstituteEntitiesDefault(1);
2997 (void) ResetMagickMemory(&sax_modules,0,sizeof(sax_modules));
2998 sax_modules.internalSubset=SVGInternalSubset;
2999 sax_modules.isStandalone=SVGIsStandalone;
3000 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3001 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3002 sax_modules.resolveEntity=SVGResolveEntity;
3003 sax_modules.getEntity=SVGGetEntity;
3004 sax_modules.entityDecl=SVGEntityDeclaration;
3005 sax_modules.notationDecl=SVGNotationDeclaration;
3006 sax_modules.attributeDecl=SVGAttributeDeclaration;
3007 sax_modules.elementDecl=SVGElementDeclaration;
3008 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3009 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3010 sax_modules.startDocument=SVGStartDocument;
3011 sax_modules.endDocument=SVGEndDocument;
3012 sax_modules.startElement=SVGStartElement;
3013 sax_modules.endElement=SVGEndElement;
3014 sax_modules.reference=SVGReference;
3015 sax_modules.characters=SVGCharacters;
3016 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3017 sax_modules.processingInstruction=SVGProcessingInstructions;
3018 sax_modules.comment=SVGComment;
3019 sax_modules.warning=SVGWarning;
3020 sax_modules.error=SVGError;
3021 sax_modules.fatalError=SVGError;
3022 sax_modules.getParameterEntity=SVGGetParameterEntity;
3023 sax_modules.cdataBlock=SVGCDataBlock;
3024 sax_modules.externalSubset=SVGExternalSubset;
3025 sax_handler=(&sax_modules);
3026 n=ReadBlob(image,MaxTextExtent,message);
3029 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3030 message,n,image->filename);
3031 while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
3033 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3038 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3039 xmlFreeParserCtxt(svg_info->parser);
3040 if (image->debug != MagickFalse)
3041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3043 (void) fclose(file);
3044 (void) CloseBlob(image);
3045 image->columns=svg_info->width;
3046 image->rows=svg_info->height;
3047 if (exception->severity >= ErrorException)
3049 image=DestroyImage(image);
3050 return((Image *) NULL);
3052 if (image_info->ping == MagickFalse)
3060 image=DestroyImage(image);
3061 image=(Image *) NULL;
3062 read_info=CloneImageInfo(image_info);
3063 SetImageInfoBlob(read_info,(void *) NULL,0);
3064 if (read_info->density != (char *) NULL)
3065 read_info->density=DestroyString(read_info->density);
3066 (void) FormatLocaleString(read_info->filename,MaxTextExtent,"mvg:%s",
3068 image=ReadImage(read_info,exception);
3069 read_info=DestroyImageInfo(read_info);
3070 if (image != (Image *) NULL)
3071 (void) CopyMagickString(image->filename,image_info->filename,
3075 Relinquish resources.
3077 if (image != (Image *) NULL)
3079 if (svg_info->title != (char *) NULL)
3080 (void) SetImageProperty(image,"svg:title",svg_info->title);
3081 if (svg_info->comment != (char *) NULL)
3082 (void) SetImageProperty(image,"svg:comment",svg_info->comment);
3084 svg_info=DestroySVGInfo(svg_info);
3085 (void) RelinquishUniqueFileResource(filename);
3086 return(GetFirstImageInList(image));
3091 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3095 % R e g i s t e r S V G I m a g e %
3099 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3101 % RegisterSVGImage() adds attributes for the SVG image format to
3102 % the list of supported formats. The attributes include the image format
3103 % tag, a method to read and/or write the format, whether the format
3104 % supports the saving of more than one frame to the same file or blob,
3105 % whether the format supports native in-memory I/O, and a brief
3106 % description of the format.
3108 % The format of the RegisterSVGImage method is:
3110 % size_t RegisterSVGImage(void)
3113 ModuleExport size_t RegisterSVGImage(void)
3116 version[MaxTextExtent];
3122 #if defined(LIBXML_DOTTED_VERSION)
3123 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,MaxTextExtent);
3125 #if defined(MAGICKCORE_RSVG_DELEGATE)
3127 (void) FormatLocaleString(version,MaxTextExtent,"RSVG %d.%d.%d",
3128 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3130 entry=SetMagickInfo("SVG");
3131 #if defined(MAGICKCORE_XML_DELEGATE)
3132 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3134 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3135 entry->blob_support=MagickFalse;
3136 entry->seekable_stream=MagickFalse;
3137 entry->description=ConstantString("Scalable Vector Graphics");
3138 if (*version != '\0')
3139 entry->version=ConstantString(version);
3140 entry->magick=(IsImageFormatHandler *) IsSVG;
3141 entry->module=ConstantString("SVG");
3142 (void) RegisterMagickInfo(entry);
3143 entry=SetMagickInfo("SVGZ");
3144 #if defined(MAGICKCORE_XML_DELEGATE)
3145 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3147 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3148 entry->blob_support=MagickFalse;
3149 entry->seekable_stream=MagickFalse;
3150 entry->description=ConstantString("Compressed Scalable Vector Graphics");
3151 if (*version != '\0')
3152 entry->version=ConstantString(version);
3153 entry->magick=(IsImageFormatHandler *) IsSVG;
3154 entry->module=ConstantString("SVG");
3155 (void) RegisterMagickInfo(entry);
3156 entry=SetMagickInfo("MSVG");
3157 #if defined(MAGICKCORE_XML_DELEGATE)
3158 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3160 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3161 entry->blob_support=MagickFalse;
3162 entry->seekable_stream=MagickFalse;
3163 entry->description=ConstantString("ImageMagick's own SVG internal renderer");
3164 entry->magick=(IsImageFormatHandler *) IsSVG;
3165 entry->module=ConstantString("SVG");
3166 (void) RegisterMagickInfo(entry);
3167 return(MagickImageCoderSignature);
3171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3175 % U n r e g i s t e r S V G I m a g e %
3179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3181 % UnregisterSVGImage() removes format registrations made by the
3182 % SVG module from the list of supported formats.
3184 % The format of the UnregisterSVGImage method is:
3186 % UnregisterSVGImage(void)
3189 ModuleExport void UnregisterSVGImage(void)
3191 (void) UnregisterMagickInfo("SVGZ");
3192 (void) UnregisterMagickInfo("SVG");
3193 (void) UnregisterMagickInfo("MSVG");
3194 #if defined(MAGICKCORE_RSVG_DELEGATE)
3200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3204 % W r i t e S V G I m a g e %
3208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3210 % WriteSVGImage() writes a image in the SVG - XML based W3C standard
3213 % The format of the WriteSVGImage method is:
3215 % MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image)
3217 % A description of each parameter follows.
3219 % o image_info: the image info.
3221 % o image: The image.
3225 static void AffineToTransform(Image *image,AffineMatrix *affine)
3228 transform[MaxTextExtent];
3230 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3232 if ((fabs(affine->rx) < MagickEpsilon) &&
3233 (fabs(affine->ry) < MagickEpsilon))
3235 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3236 (fabs(affine->sy-1.0) < MagickEpsilon))
3238 (void) WriteBlobString(image,"\">\n");
3241 (void) FormatLocaleString(transform,MaxTextExtent,
3242 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
3243 (void) WriteBlobString(image,transform);
3248 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3249 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3250 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3256 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3257 (void) FormatLocaleString(transform,MaxTextExtent,
3258 "\" transform=\"rotate(%g)\">\n",theta);
3259 (void) WriteBlobString(image,transform);
3266 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3267 (fabs(affine->rx) < MagickEpsilon) &&
3268 (fabs(affine->ry) < MagickEpsilon) &&
3269 (fabs(affine->sy-1.0) < MagickEpsilon))
3271 (void) FormatLocaleString(transform,MaxTextExtent,
3272 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
3273 (void) WriteBlobString(image,transform);
3277 (void) FormatLocaleString(transform,MaxTextExtent,
3278 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
3279 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
3280 (void) WriteBlobString(image,transform);
3283 static MagickBooleanType IsPoint(const char *point)
3291 value=strtol(point,&p,10);
3293 return(p != point ? MagickTrue : MagickFalse);
3296 static MagickBooleanType TraceSVGImage(Image *image)
3301 register const Quantum
3307 #if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3312 at_fitting_opts_type
3331 Trace image and write as SVG.
3333 fitting_options=at_fitting_opts_new();
3334 output_options=at_output_opts_new();
3335 type=GetImageType(image,&image->exception);
3337 if ((type == BilevelType) || (type == GrayscaleType))
3339 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3341 for (y=0; y < (ssize_t) image->rows; y++)
3343 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
3344 if (p == (const Quantum *) NULL)
3346 for (x=0; x < (ssize_t) image->columns; x++)
3348 trace->bitmap[i++]=GetPixelRed(image,p);
3349 if (number_planes == 3)
3351 trace->bitmap[i++]=GetPixelGreen(image,p);
3352 trace->bitmap[i++]=GetPixelBlue(image,p);
3354 p+=GetPixelChannels(image);
3357 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3359 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3360 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3365 at_splines_free(splines);
3366 at_bitmap_free(trace);
3367 at_output_opts_free(output_options);
3368 at_fitting_opts_free(fitting_options);
3373 message[MaxTextExtent],
3374 tuple[MaxTextExtent];
3379 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3380 (void) WriteBlobString(image,
3381 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3382 (void) WriteBlobString(image,
3383 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3384 (void) FormatLocaleString(message,MaxTextExtent,
3385 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,
3386 (double) image->rows);
3387 (void) WriteBlobString(image,message);
3388 GetPixelInfo(image,&pixel);
3389 for (y=0; y < (ssize_t) image->rows; y++)
3391 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
3392 if (p == (const Quantum *) NULL)
3394 for (x=0; x < (ssize_t) image->columns; x++)
3396 SetPixelInfo(image,p,&pixel);
3397 (void) QueryMagickColorname(image,&pixel,SVGCompliance,tuple,
3399 (void) FormatLocaleString(message,MaxTextExtent,
3400 " <circle cx=\"%.20g\" cy=\"%.20g\" r=\"1\" fill=\"%s\"/>\n",
3401 (double) x,(double) y,tuple);
3402 (void) WriteBlobString(image,message);
3403 p+=GetPixelChannels(image);
3406 (void) WriteBlobString(image,"</svg>\n");
3412 static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image)
3414 #define BezierQuantum 200
3420 keyword[MaxTextExtent],
3421 message[MaxTextExtent],
3422 name[MaxTextExtent],
3424 type[MaxTextExtent];
3466 Open output image file.
3468 assert(image_info != (const ImageInfo *) NULL);
3469 assert(image_info->signature == MagickSignature);
3470 assert(image != (Image *) NULL);
3471 assert(image->signature == MagickSignature);
3472 if (image->debug != MagickFalse)
3473 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3474 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
3475 if (status == MagickFalse)
3477 value=GetImageArtifact(image,"SVG");
3478 if (value != (char *) NULL)
3480 (void) WriteBlobString(image,value);
3481 (void) CloseBlob(image);
3484 value=GetImageArtifact(image,"MVG");
3485 if (value == (char *) NULL)
3486 return(TraceSVGImage(image));
3490 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3491 (void) WriteBlobString(image,
3492 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3493 (void) WriteBlobString(image,
3494 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3495 (void) FormatLocaleString(message,MaxTextExtent,
3496 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3498 (void) WriteBlobString(image,message);
3500 Allocate primitive info memory.
3503 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3504 sizeof(*primitive_info));
3505 if (primitive_info == (PrimitiveInfo *) NULL)
3506 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3507 GetAffineMatrix(&affine);
3508 token=AcquireString(value);
3512 for (q=(const char *) value; *q != '\0'; )
3515 Interpret graphic primitive.
3517 GetMagickToken(q,&q,keyword);
3518 if (*keyword == '\0')
3520 if (*keyword == '#')
3525 if (active != MagickFalse)
3527 AffineToTransform(image,&affine);
3530 (void) WriteBlobString(image,"<desc>");
3531 (void) WriteBlobString(image,keyword+1);
3532 for ( ; (*q != '\n') && (*q != '\0'); q++)
3535 case '<': (void) WriteBlobString(image,"<"); break;
3536 case '>': (void) WriteBlobString(image,">"); break;
3537 case '&': (void) WriteBlobString(image,"&"); break;
3538 default: (void) WriteBlobByte(image,*q); break;
3540 (void) WriteBlobString(image,"</desc>\n");
3543 primitive_type=UndefinedPrimitive;
3551 if (LocaleCompare("affine",keyword) == 0)
3553 GetMagickToken(q,&q,token);
3554 affine.sx=InterpretLocaleValue(token,(char **) NULL);
3555 GetMagickToken(q,&q,token);
3557 GetMagickToken(q,&q,token);
3558 affine.rx=InterpretLocaleValue(token,(char **) NULL);
3559 GetMagickToken(q,&q,token);
3561 GetMagickToken(q,&q,token);
3562 affine.ry=InterpretLocaleValue(token,(char **) NULL);
3563 GetMagickToken(q,&q,token);
3565 GetMagickToken(q,&q,token);
3566 affine.sy=InterpretLocaleValue(token,(char **) NULL);
3567 GetMagickToken(q,&q,token);
3569 GetMagickToken(q,&q,token);
3570 affine.tx=InterpretLocaleValue(token,(char **) NULL);
3571 GetMagickToken(q,&q,token);
3573 GetMagickToken(q,&q,token);
3574 affine.ty=InterpretLocaleValue(token,(char **) NULL);
3577 if (LocaleCompare("angle",keyword) == 0)
3579 GetMagickToken(q,&q,token);
3580 affine.rx=InterpretLocaleValue(token,(char **) NULL);
3581 affine.ry=InterpretLocaleValue(token,(char **) NULL);
3584 if (LocaleCompare("arc",keyword) == 0)
3586 primitive_type=ArcPrimitive;
3595 if (LocaleCompare("bezier",keyword) == 0)
3597 primitive_type=BezierPrimitive;
3606 if (LocaleCompare("clip-path",keyword) == 0)
3608 GetMagickToken(q,&q,token);
3609 (void) FormatLocaleString(message,MaxTextExtent,
3610 "clip-path:url(#%s);",token);
3611 (void) WriteBlobString(image,message);
3614 if (LocaleCompare("clip-rule",keyword) == 0)
3616 GetMagickToken(q,&q,token);
3617 (void) FormatLocaleString(message,MaxTextExtent,
3618 "clip-rule:%s;",token);
3619 (void) WriteBlobString(image,message);
3622 if (LocaleCompare("clip-units",keyword) == 0)
3624 GetMagickToken(q,&q,token);
3625 (void) FormatLocaleString(message,MaxTextExtent,
3626 "clipPathUnits=%s;",token);
3627 (void) WriteBlobString(image,message);
3630 if (LocaleCompare("circle",keyword) == 0)
3632 primitive_type=CirclePrimitive;
3635 if (LocaleCompare("color",keyword) == 0)
3637 primitive_type=ColorPrimitive;
3646 if (LocaleCompare("decorate",keyword) == 0)
3648 GetMagickToken(q,&q,token);
3649 (void) FormatLocaleString(message,MaxTextExtent,
3650 "text-decoration:%s;",token);
3651 (void) WriteBlobString(image,message);
3660 if (LocaleCompare("ellipse",keyword) == 0)
3662 primitive_type=EllipsePrimitive;
3671 if (LocaleCompare("fill",keyword) == 0)
3673 GetMagickToken(q,&q,token);
3674 (void) FormatLocaleString(message,MaxTextExtent,"fill:%s;",
3676 (void) WriteBlobString(image,message);
3679 if (LocaleCompare("fill-rule",keyword) == 0)
3681 GetMagickToken(q,&q,token);
3682 (void) FormatLocaleString(message,MaxTextExtent,
3683 "fill-rule:%s;",token);
3684 (void) WriteBlobString(image,message);
3687 if (LocaleCompare("fill-opacity",keyword) == 0)
3689 GetMagickToken(q,&q,token);
3690 (void) FormatLocaleString(message,MaxTextExtent,
3691 "fill-opacity:%s;",token);
3692 (void) WriteBlobString(image,message);
3695 if (LocaleCompare("font-family",keyword) == 0)
3697 GetMagickToken(q,&q,token);
3698 (void) FormatLocaleString(message,MaxTextExtent,
3699 "font-family:%s;",token);
3700 (void) WriteBlobString(image,message);
3703 if (LocaleCompare("font-stretch",keyword) == 0)
3705 GetMagickToken(q,&q,token);
3706 (void) FormatLocaleString(message,MaxTextExtent,
3707 "font-stretch:%s;",token);
3708 (void) WriteBlobString(image,message);
3711 if (LocaleCompare("font-style",keyword) == 0)
3713 GetMagickToken(q,&q,token);
3714 (void) FormatLocaleString(message,MaxTextExtent,
3715 "font-style:%s;",token);
3716 (void) WriteBlobString(image,message);
3719 if (LocaleCompare("font-size",keyword) == 0)
3721 GetMagickToken(q,&q,token);
3722 (void) FormatLocaleString(message,MaxTextExtent,
3723 "font-size:%s;",token);
3724 (void) WriteBlobString(image,message);
3727 if (LocaleCompare("font-weight",keyword) == 0)
3729 GetMagickToken(q,&q,token);
3730 (void) FormatLocaleString(message,MaxTextExtent,
3731 "font-weight:%s;",token);
3732 (void) WriteBlobString(image,message);
3741 if (LocaleCompare("gradient-units",keyword) == 0)
3743 GetMagickToken(q,&q,token);
3746 if (LocaleCompare("text-align",keyword) == 0)
3748 GetMagickToken(q,&q,token);
3749 (void) FormatLocaleString(message,MaxTextExtent,
3750 "text-align %s ",token);
3751 (void) WriteBlobString(image,message);
3754 if (LocaleCompare("text-anchor",keyword) == 0)
3756 GetMagickToken(q,&q,token);
3757 (void) FormatLocaleString(message,MaxTextExtent,
3758 "text-anchor %s ",token);
3759 (void) WriteBlobString(image,message);
3768 if (LocaleCompare("image",keyword) == 0)
3770 GetMagickToken(q,&q,token);
3771 primitive_type=ImagePrimitive;
3780 if (LocaleCompare("line",keyword) == 0)
3782 primitive_type=LinePrimitive;
3791 if (LocaleCompare("matte",keyword) == 0)
3793 primitive_type=MattePrimitive;
3802 if (LocaleCompare("opacity",keyword) == 0)
3804 GetMagickToken(q,&q,token);
3805 (void) FormatLocaleString(message,MaxTextExtent,"opacity %s ",
3807 (void) WriteBlobString(image,message);
3816 if (LocaleCompare("path",keyword) == 0)
3818 primitive_type=PathPrimitive;
3821 if (LocaleCompare("point",keyword) == 0)
3823 primitive_type=PointPrimitive;
3826 if (LocaleCompare("polyline",keyword) == 0)
3828 primitive_type=PolylinePrimitive;
3831 if (LocaleCompare("polygon",keyword) == 0)
3833 primitive_type=PolygonPrimitive;
3836 if (LocaleCompare("pop",keyword) == 0)
3838 GetMagickToken(q,&q,token);
3839 if (LocaleCompare("clip-path",token) == 0)
3841 (void) WriteBlobString(image,"</clipPath>\n");
3844 if (LocaleCompare("defs",token) == 0)
3846 (void) WriteBlobString(image,"</defs>\n");
3849 if (LocaleCompare("gradient",token) == 0)
3851 (void) FormatLocaleString(message,MaxTextExtent,
3852 "</%sGradient>\n",type);
3853 (void) WriteBlobString(image,message);
3856 if (LocaleCompare("graphic-context",token) == 0)
3860 ThrowWriterException(DrawError,
3861 "UnbalancedGraphicContextPushPop");
3862 (void) WriteBlobString(image,"</g>\n");
3864 if (LocaleCompare("pattern",token) == 0)
3866 (void) WriteBlobString(image,"</pattern>\n");
3869 if (LocaleCompare("defs",token) == 0)
3870 (void) WriteBlobString(image,"</g>\n");
3873 if (LocaleCompare("push",keyword) == 0)
3875 GetMagickToken(q,&q,token);
3876 if (LocaleCompare("clip-path",token) == 0)
3878 GetMagickToken(q,&q,token);
3879 (void) FormatLocaleString(message,MaxTextExtent,
3880 "<clipPath id=\"%s\">\n",token);
3881 (void) WriteBlobString(image,message);
3884 if (LocaleCompare("defs",token) == 0)
3886 (void) WriteBlobString(image,"<defs>\n");
3889 if (LocaleCompare("gradient",token) == 0)
3891 GetMagickToken(q,&q,token);
3892 (void) CopyMagickString(name,token,MaxTextExtent);
3893 GetMagickToken(q,&q,token);
3894 (void) CopyMagickString(type,token,MaxTextExtent);
3895 GetMagickToken(q,&q,token);
3896 svg_info.segment.x1=InterpretLocaleValue(token,(char **) NULL);
3897 svg_info.element.cx=InterpretLocaleValue(token,(char **) NULL);
3898 GetMagickToken(q,&q,token);
3900 GetMagickToken(q,&q,token);
3901 svg_info.segment.y1=InterpretLocaleValue(token,(char **) NULL);
3902 svg_info.element.cy=InterpretLocaleValue(token,(char **) NULL);
3903 GetMagickToken(q,&q,token);
3905 GetMagickToken(q,&q,token);
3906 svg_info.segment.x2=InterpretLocaleValue(token,(char **) NULL);
3907 svg_info.element.major=InterpretLocaleValue(token,
3909 GetMagickToken(q,&q,token);
3911 GetMagickToken(q,&q,token);
3912 svg_info.segment.y2=InterpretLocaleValue(token,(char **) NULL);
3913 svg_info.element.minor=InterpretLocaleValue(token,
3915 (void) FormatLocaleString(message,MaxTextExtent,
3916 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
3917 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
3918 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
3919 if (LocaleCompare(type,"radial") == 0)
3921 GetMagickToken(q,&q,token);
3923 GetMagickToken(q,&q,token);
3924 svg_info.element.angle=InterpretLocaleValue(token,
3926 (void) FormatLocaleString(message,MaxTextExtent,
3927 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
3928 "fx=\"%g\" fy=\"%g\">\n",type,name,
3929 svg_info.element.cx,svg_info.element.cy,
3930 svg_info.element.angle,svg_info.element.major,
3931 svg_info.element.minor);
3933 (void) WriteBlobString(image,message);
3936 if (LocaleCompare("graphic-context",token) == 0)
3941 AffineToTransform(image,&affine);
3944 (void) WriteBlobString(image,"<g style=\"");
3947 if (LocaleCompare("pattern",token) == 0)
3949 GetMagickToken(q,&q,token);
3950 (void) CopyMagickString(name,token,MaxTextExtent);
3951 GetMagickToken(q,&q,token);
3952 svg_info.bounds.x=InterpretLocaleValue(token,(char **) NULL);
3953 GetMagickToken(q,&q,token);
3955 GetMagickToken(q,&q,token);
3956 svg_info.bounds.y=InterpretLocaleValue(token,(char **) NULL);
3957 GetMagickToken(q,&q,token);
3959 GetMagickToken(q,&q,token);
3960 svg_info.bounds.width=InterpretLocaleValue(token,
3962 GetMagickToken(q,&q,token);
3964 GetMagickToken(q,&q,token);
3965 svg_info.bounds.height=InterpretLocaleValue(token,
3967 (void) FormatLocaleString(message,MaxTextExtent,
3968 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
3969 "height=\"%g\">\n",name,svg_info.bounds.x,
3970 svg_info.bounds.y,svg_info.bounds.width,
3971 svg_info.bounds.height);
3972 (void) WriteBlobString(image,message);
3983 if (LocaleCompare("rectangle",keyword) == 0)
3985 primitive_type=RectanglePrimitive;
3988 if (LocaleCompare("roundRectangle",keyword) == 0)
3990 primitive_type=RoundRectanglePrimitive;
3993 if (LocaleCompare("rotate",keyword) == 0)
3995 GetMagickToken(q,&q,token);
3996 (void) FormatLocaleString(message,MaxTextExtent,"rotate(%s) ",
3998 (void) WriteBlobString(image,message);
4007 if (LocaleCompare("scale",keyword) == 0)
4009 GetMagickToken(q,&q,token);
4010 affine.sx=InterpretLocaleValue(token,(char **) NULL);
4011 GetMagickToken(q,&q,token);
4013 GetMagickToken(q,&q,token);
4014 affine.sy=InterpretLocaleValue(token,(char **) NULL);
4017 if (LocaleCompare("skewX",keyword) == 0)
4019 GetMagickToken(q,&q,token);
4020 (void) FormatLocaleString(message,MaxTextExtent,"skewX(%s) ",
4022 (void) WriteBlobString(image,message);
4025 if (LocaleCompare("skewY",keyword) == 0)
4027 GetMagickToken(q,&q,token);
4028 (void) FormatLocaleString(message,MaxTextExtent,"skewY(%s) ",
4030 (void) WriteBlobString(image,message);
4033 if (LocaleCompare("stop-color",keyword) == 0)
4036 color[MaxTextExtent];
4038 GetMagickToken(q,&q,token);
4039 (void) CopyMagickString(color,token,MaxTextExtent);
4040 GetMagickToken(q,&q,token);
4041 (void) FormatLocaleString(message,MaxTextExtent,
4042 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4043 (void) WriteBlobString(image,message);
4046 if (LocaleCompare("stroke",keyword) == 0)
4048 GetMagickToken(q,&q,token);
4049 (void) FormatLocaleString(message,MaxTextExtent,"stroke:%s;",
4051 (void) WriteBlobString(image,message);
4054 if (LocaleCompare("stroke-antialias",keyword) == 0)
4056 GetMagickToken(q,&q,token);
4057 (void) FormatLocaleString(message,MaxTextExtent,
4058 "stroke-antialias:%s;",token);
4059 (void) WriteBlobString(image,message);
4062 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4070 GetMagickToken(p,&p,token);
4071 for (k=0; IsPoint(token); k++)
4072 GetMagickToken(p,&p,token);
4073 (void) WriteBlobString(image,"stroke-dasharray:");
4074 for (j=0; j < k; j++)
4076 GetMagickToken(q,&q,token);
4077 (void) FormatLocaleString(message,MaxTextExtent,"%s ",
4079 (void) WriteBlobString(image,message);
4081 (void) WriteBlobString(image,";");
4084 GetMagickToken(q,&q,token);
4085 (void) FormatLocaleString(message,MaxTextExtent,
4086 "stroke-dasharray:%s;",token);
4087 (void) WriteBlobString(image,message);
4090 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4092 GetMagickToken(q,&q,token);
4093 (void) FormatLocaleString(message,MaxTextExtent,
4094 "stroke-dashoffset:%s;",token);
4095 (void) WriteBlobString(image,message);
4098 if (LocaleCompare("stroke-linecap",keyword) == 0)
4100 GetMagickToken(q,&q,token);
4101 (void) FormatLocaleString(message,MaxTextExtent,
4102 "stroke-linecap:%s;",token);
4103 (void) WriteBlobString(image,message);
4106 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4108 GetMagickToken(q,&q,token);
4109 (void) FormatLocaleString(message,MaxTextExtent,
4110 "stroke-linejoin:%s;",token);
4111 (void) WriteBlobString(image,message);
4114 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4116 GetMagickToken(q,&q,token);
4117 (void) FormatLocaleString(message,MaxTextExtent,
4118 "stroke-miterlimit:%s;",token);
4119 (void) WriteBlobString(image,message);
4122 if (LocaleCompare("stroke-opacity",keyword) == 0)
4124 GetMagickToken(q,&q,token);
4125 (void) FormatLocaleString(message,MaxTextExtent,
4126 "stroke-opacity:%s;",token);
4127 (void) WriteBlobString(image,message);
4130 if (LocaleCompare("stroke-width",keyword) == 0)
4132 GetMagickToken(q,&q,token);
4133 (void) FormatLocaleString(message,MaxTextExtent,
4134 "stroke-width:%s;",token);
4135 (void) WriteBlobString(image,message);
4144 if (LocaleCompare("text",keyword) == 0)
4146 primitive_type=TextPrimitive;
4149 if (LocaleCompare("text-antialias",keyword) == 0)
4151 GetMagickToken(q,&q,token);
4152 (void) FormatLocaleString(message,MaxTextExtent,
4153 "text-antialias:%s;",token);
4154 (void) WriteBlobString(image,message);
4157 if (LocaleCompare("tspan",keyword) == 0)
4159 primitive_type=TextPrimitive;
4162 if (LocaleCompare("translate",keyword) == 0)
4164 GetMagickToken(q,&q,token);
4165 affine.tx=InterpretLocaleValue(token,(char **) NULL);
4166 GetMagickToken(q,&q,token);
4168 GetMagickToken(q,&q,token);
4169 affine.ty=InterpretLocaleValue(token,(char **) NULL);
4178 if (LocaleCompare("viewbox",keyword) == 0)
4180 GetMagickToken(q,&q,token);
4182 GetMagickToken(q,&q,token);
4183 GetMagickToken(q,&q,token);
4185 GetMagickToken(q,&q,token);
4186 GetMagickToken(q,&q,token);
4188 GetMagickToken(q,&q,token);
4189 GetMagickToken(q,&q,token);
4201 if (status == MagickFalse)
4203 if (primitive_type == UndefinedPrimitive)
4206 Parse the primitive attributes.
4210 for (x=0; *q != '\0'; x++)
4215 if (IsPoint(q) == MagickFalse)
4217 GetMagickToken(q,&q,token);
4218 point.x=InterpretLocaleValue(token,(char **) NULL);
4219 GetMagickToken(q,&q,token);
4221 GetMagickToken(q,&q,token);
4222 point.y=InterpretLocaleValue(token,(char **) NULL);
4223 GetMagickToken(q,(const char **) NULL,token);
4225 GetMagickToken(q,&q,token);
4226 primitive_info[i].primitive=primitive_type;
4227 primitive_info[i].point=point;
4228 primitive_info[i].coordinates=0;
4229 primitive_info[i].method=FloodfillMethod;
4231 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
4233 number_points+=6*BezierQuantum+360;
4234 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4235 number_points,sizeof(*primitive_info));
4236 if (primitive_info == (PrimitiveInfo *) NULL)
4238 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4239 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4243 primitive_info[j].primitive=primitive_type;
4244 primitive_info[j].coordinates=x;
4245 primitive_info[j].method=FloodfillMethod;
4246 primitive_info[j].text=(char *) NULL;
4249 AffineToTransform(image,&affine);
4253 switch (primitive_type)
4255 case PointPrimitive:
4258 if (primitive_info[j].coordinates != 1)
4267 if (primitive_info[j].coordinates != 2)
4272 (void) FormatLocaleString(message,MaxTextExtent,
4273 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
4274 primitive_info[j].point.x,primitive_info[j].point.y,
4275 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4276 (void) WriteBlobString(image,message);
4279 case RectanglePrimitive:
4281 if (primitive_info[j].coordinates != 2)
4286 (void) FormatLocaleString(message,MaxTextExtent,
4287 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
4288 primitive_info[j].point.x,primitive_info[j].point.y,
4289 primitive_info[j+1].point.x-primitive_info[j].point.x,
4290 primitive_info[j+1].point.y-primitive_info[j].point.y);
4291 (void) WriteBlobString(image,message);
4294 case RoundRectanglePrimitive:
4296 if (primitive_info[j].coordinates != 3)
4301 (void) FormatLocaleString(message,MaxTextExtent,
4302 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4303 "ry=\"%g\"/>\n",primitive_info[j].point.x,
4304 primitive_info[j].point.y,primitive_info[j+1].point.x-
4305 primitive_info[j].point.x,primitive_info[j+1].point.y-
4306 primitive_info[j].point.y,primitive_info[j+2].point.x,
4307 primitive_info[j+2].point.y);
4308 (void) WriteBlobString(image,message);
4313 if (primitive_info[j].coordinates != 3)
4320 case EllipsePrimitive:
4322 if (primitive_info[j].coordinates != 3)
4327 (void) FormatLocaleString(message,MaxTextExtent,
4328 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
4329 primitive_info[j].point.x,primitive_info[j].point.y,
4330 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4331 (void) WriteBlobString(image,message);
4334 case CirclePrimitive:
4340 if (primitive_info[j].coordinates != 2)
4345 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4346 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4347 (void) FormatLocaleString(message,MaxTextExtent,
4348 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
4349 primitive_info[j].point.x,primitive_info[j].point.y,
4351 (void) WriteBlobString(image,message);
4354 case PolylinePrimitive:
4356 if (primitive_info[j].coordinates < 2)
4361 (void) CopyMagickString(message," <polyline points=\"",MaxTextExtent);
4362 (void) WriteBlobString(image,message);
4363 length=strlen(message);
4366 (void) FormatLocaleString(message,MaxTextExtent,"%g,%g ",
4367 primitive_info[j].point.x,primitive_info[j].point.y);
4368 length+=strlen(message);
4371 (void) WriteBlobString(image,"\n ");
4372 length=strlen(message)+5;
4374 (void) WriteBlobString(image,message);
4376 (void) WriteBlobString(image,"\"/>\n");
4379 case PolygonPrimitive:
4381 if (primitive_info[j].coordinates < 3)
4386 primitive_info[i]=primitive_info[j];
4387 primitive_info[i].coordinates=0;
4388 primitive_info[j].coordinates++;
4390 (void) CopyMagickString(message," <polygon points=\"",MaxTextExtent);
4391 (void) WriteBlobString(image,message);
4392 length=strlen(message);
4395 (void) FormatLocaleString(message,MaxTextExtent,"%g,%g ",
4396 primitive_info[j].point.x,primitive_info[j].point.y);
4397 length+=strlen(message);
4400 (void) WriteBlobString(image,"\n ");
4401 length=strlen(message)+5;
4403 (void) WriteBlobString(image,message);
4405 (void) WriteBlobString(image,"\"/>\n");
4408 case BezierPrimitive:
4410 if (primitive_info[j].coordinates < 3)
4422 GetMagickToken(q,&q,token);
4423 number_attributes=1;
4424 for (p=token; *p != '\0'; p++)
4425 if (isalpha((int) *p))
4426 number_attributes++;
4427 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
4429 number_points+=6*BezierQuantum*number_attributes;
4430 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4431 number_points,sizeof(*primitive_info));
4432 if (primitive_info == (PrimitiveInfo *) NULL)
4434 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4435 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4440 (void) WriteBlobString(image," <path d=\"");
4441 (void) WriteBlobString(image,token);
4442 (void) WriteBlobString(image,"\"/>\n");
4445 case ColorPrimitive:
4446 case MattePrimitive:
4448 if (primitive_info[j].coordinates != 1)
4453 GetMagickToken(q,&q,token);
4454 if (LocaleCompare("point",token) == 0)
4455 primitive_info[j].method=PointMethod;
4456 if (LocaleCompare("replace",token) == 0)
4457 primitive_info[j].method=ReplaceMethod;
4458 if (LocaleCompare("floodfill",token) == 0)
4459 primitive_info[j].method=FloodfillMethod;
4460 if (LocaleCompare("filltoborder",token) == 0)
4461 primitive_info[j].method=FillToBorderMethod;
4462 if (LocaleCompare("reset",token) == 0)
4463 primitive_info[j].method=ResetMethod;
4471 if (primitive_info[j].coordinates != 1)
4476 GetMagickToken(q,&q,token);
4477 (void) FormatLocaleString(message,MaxTextExtent,
4478 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
4479 primitive_info[j].point.y);
4480 (void) WriteBlobString(image,message);
4481 for (p=token; *p != '\0'; p++)
4484 case '<': (void) WriteBlobString(image,"<"); break;
4485 case '>': (void) WriteBlobString(image,">"); break;
4486 case '&': (void) WriteBlobString(image,"&"); break;
4487 default: (void) WriteBlobByte(image,*p); break;
4489 (void) WriteBlobString(image,"</text>\n");
4492 case ImagePrimitive:
4494 if (primitive_info[j].coordinates != 2)
4499 GetMagickToken(q,&q,token);
4500 (void) FormatLocaleString(message,MaxTextExtent,
4501 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
4502 "xlink:href=\"%s\"/>\n",primitive_info[j].point.x,
4503 primitive_info[j].point.y,primitive_info[j+1].point.x,
4504 primitive_info[j+1].point.y,token);
4505 (void) WriteBlobString(image,message);
4509 if (primitive_info == (PrimitiveInfo *) NULL)
4511 primitive_info[i].primitive=UndefinedPrimitive;
4512 if (status == MagickFalse)
4515 (void) WriteBlobString(image,"</svg>\n");
4517 Relinquish resources.
4519 token=DestroyString(token);
4520 if (primitive_info != (PrimitiveInfo *) NULL)
4521 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4522 (void) CloseBlob(image);