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