blob: f6b08807e51376ce65c3a250fa4a03314232d9c4 [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 {
cristy31bada52011-05-17 01:51:23 +00002983 const char
2984 *locale;
2985
cristy3ed852e2009-09-05 21:47:34 +00002986 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
2987 message,n,image->filename);
cristy31bada52011-05-17 01:51:23 +00002988 locale=setlocale(LC_NUMERIC,"C");
cristy3ed852e2009-09-05 21:47:34 +00002989 while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
2990 {
2991 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
2992 if (status != 0)
2993 break;
2994 }
cristy31bada52011-05-17 01:51:23 +00002995 (void) setlocale(LC_NUMERIC,locale);
cristy3ed852e2009-09-05 21:47:34 +00002996 }
2997 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
2998 xmlFreeParserCtxt(svg_info->parser);
2999 if (image->debug != MagickFalse)
3000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3001 xmlCleanupParser();
3002 (void) fclose(file);
3003 (void) CloseBlob(image);
3004 image->columns=svg_info->width;
3005 image->rows=svg_info->height;
3006 if (exception->severity >= ErrorException)
3007 {
3008 image=DestroyImage(image);
3009 return((Image *) NULL);
3010 }
3011 if (image_info->ping == MagickFalse)
3012 {
3013 ImageInfo
3014 *read_info;
3015
3016 /*
3017 Draw image.
3018 */
3019 image=DestroyImage(image);
3020 image=(Image *) NULL;
3021 read_info=CloneImageInfo(image_info);
3022 SetImageInfoBlob(read_info,(void *) NULL,0);
3023 if (read_info->density != (char *) NULL)
3024 read_info->density=DestroyString(read_info->density);
3025 (void) FormatMagickString(read_info->filename,MaxTextExtent,"mvg:%s",
3026 filename);
3027 image=ReadImage(read_info,exception);
3028 read_info=DestroyImageInfo(read_info);
3029 if (image != (Image *) NULL)
3030 (void) CopyMagickString(image->filename,image_info->filename,
3031 MaxTextExtent);
3032 }
3033 /*
3034 Relinquish resources.
3035 */
3036 if (image != (Image *) NULL)
3037 {
3038 if (svg_info->title != (char *) NULL)
3039 (void) SetImageProperty(image,"svg:title",svg_info->title);
3040 if (svg_info->comment != (char *) NULL)
3041 (void) SetImageProperty(image,"svg:comment",svg_info->comment);
3042 }
3043 svg_info=DestroySVGInfo(svg_info);
3044 (void) RelinquishUniqueFileResource(filename);
3045 return(GetFirstImageInList(image));
3046}
3047#endif
3048
3049/*
3050%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3051% %
3052% %
3053% %
3054% R e g i s t e r S V G I m a g e %
3055% %
3056% %
3057% %
3058%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3059%
3060% RegisterSVGImage() adds attributes for the SVG image format to
3061% the list of supported formats. The attributes include the image format
3062% tag, a method to read and/or write the format, whether the format
3063% supports the saving of more than one frame to the same file or blob,
3064% whether the format supports native in-memory I/O, and a brief
3065% description of the format.
3066%
3067% The format of the RegisterSVGImage method is:
3068%
cristybb503372010-05-27 20:51:26 +00003069% size_t RegisterSVGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00003070%
3071*/
cristybb503372010-05-27 20:51:26 +00003072ModuleExport size_t RegisterSVGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00003073{
3074 char
3075 version[MaxTextExtent];
3076
3077 MagickInfo
3078 *entry;
3079
3080 *version='\0';
3081#if defined(LIBXML_DOTTED_VERSION)
3082 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,MaxTextExtent);
3083#endif
3084#if defined(MAGICKCORE_RSVG_DELEGATE)
3085 rsvg_init();
3086 (void) FormatMagickString(version,MaxTextExtent,"RSVG %d.%d.%d",
3087 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3088#endif
3089 entry=SetMagickInfo("SVG");
3090#if defined(MAGICKCORE_XML_DELEGATE)
3091 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3092#endif
3093 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3094 entry->blob_support=MagickFalse;
3095 entry->seekable_stream=MagickFalse;
3096 entry->description=ConstantString("Scalable Vector Graphics");
3097 if (*version != '\0')
3098 entry->version=ConstantString(version);
3099 entry->magick=(IsImageFormatHandler *) IsSVG;
3100 entry->module=ConstantString("SVG");
3101 (void) RegisterMagickInfo(entry);
3102 entry=SetMagickInfo("SVGZ");
3103#if defined(MAGICKCORE_XML_DELEGATE)
3104 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3105#endif
3106 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3107 entry->blob_support=MagickFalse;
3108 entry->seekable_stream=MagickFalse;
3109 entry->description=ConstantString("Compressed Scalable Vector Graphics");
3110 if (*version != '\0')
3111 entry->version=ConstantString(version);
3112 entry->magick=(IsImageFormatHandler *) IsSVG;
3113 entry->module=ConstantString("SVG");
3114 (void) RegisterMagickInfo(entry);
3115 entry=SetMagickInfo("MSVG");
3116#if defined(MAGICKCORE_XML_DELEGATE)
3117 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3118#endif
3119 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3120 entry->blob_support=MagickFalse;
3121 entry->seekable_stream=MagickFalse;
3122 entry->description=ConstantString("ImageMagick's own SVG internal renderer");
3123 entry->magick=(IsImageFormatHandler *) IsSVG;
3124 entry->module=ConstantString("SVG");
3125 (void) RegisterMagickInfo(entry);
3126 return(MagickImageCoderSignature);
3127}
3128
3129/*
3130%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3131% %
3132% %
3133% %
3134% U n r e g i s t e r S V G I m a g e %
3135% %
3136% %
3137% %
3138%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3139%
3140% UnregisterSVGImage() removes format registrations made by the
3141% SVG module from the list of supported formats.
3142%
3143% The format of the UnregisterSVGImage method is:
3144%
3145% UnregisterSVGImage(void)
3146%
3147*/
3148ModuleExport void UnregisterSVGImage(void)
3149{
3150 (void) UnregisterMagickInfo("SVGZ");
3151 (void) UnregisterMagickInfo("SVG");
3152 (void) UnregisterMagickInfo("MSVG");
3153#if defined(MAGICKCORE_RSVG_DELEGATE)
3154 rsvg_term();
3155#endif
3156}
3157
3158/*
3159%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3160% %
3161% %
3162% %
3163% W r i t e S V G I m a g e %
3164% %
3165% %
3166% %
3167%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3168%
3169% WriteSVGImage() writes a image in the SVG - XML based W3C standard
3170% format.
3171%
3172% The format of the WriteSVGImage method is:
3173%
3174% MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image)
3175%
3176% A description of each parameter follows.
3177%
3178% o image_info: the image info.
3179%
3180% o image: The image.
3181%
3182*/
3183
3184static void AffineToTransform(Image *image,AffineMatrix *affine)
3185{
3186 char
3187 transform[MaxTextExtent];
3188
3189 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3190 {
3191 if ((fabs(affine->rx) < MagickEpsilon) &&
3192 (fabs(affine->ry) < MagickEpsilon))
3193 {
3194 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3195 (fabs(affine->sy-1.0) < MagickEpsilon))
3196 {
3197 (void) WriteBlobString(image,"\">\n");
3198 return;
3199 }
3200 (void) FormatMagickString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003201 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
cristy3ed852e2009-09-05 21:47:34 +00003202 (void) WriteBlobString(image,transform);
3203 return;
3204 }
3205 else
3206 {
3207 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3208 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3209 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3210 2*MagickEpsilon))
3211 {
3212 double
3213 theta;
3214
3215 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
3216 (void) FormatMagickString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003217 "\" transform=\"rotate(%g)\">\n",theta);
cristy3ed852e2009-09-05 21:47:34 +00003218 (void) WriteBlobString(image,transform);
3219 return;
3220 }
3221 }
3222 }
3223 else
3224 {
3225 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3226 (fabs(affine->rx) < MagickEpsilon) &&
3227 (fabs(affine->ry) < MagickEpsilon) &&
3228 (fabs(affine->sy-1.0) < MagickEpsilon))
3229 {
3230 (void) FormatMagickString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003231 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
cristy3ed852e2009-09-05 21:47:34 +00003232 (void) WriteBlobString(image,transform);
3233 return;
3234 }
3235 }
3236 (void) FormatMagickString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003237 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
cristy8cd5b312010-01-07 01:10:24 +00003238 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
cristy3ed852e2009-09-05 21:47:34 +00003239 (void) WriteBlobString(image,transform);
3240}
3241
3242static MagickBooleanType IsPoint(const char *point)
3243{
3244 char
3245 *p;
3246
cristybb503372010-05-27 20:51:26 +00003247 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003248 value;
3249
3250 value=strtol(point,&p,10);
cristyda16f162011-02-19 23:52:17 +00003251 (void) value;
cristy3ed852e2009-09-05 21:47:34 +00003252 return(p != point ? MagickTrue : MagickFalse);
3253}
3254
3255static MagickBooleanType TraceSVGImage(Image *image)
3256{
cristybb503372010-05-27 20:51:26 +00003257 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003258 y;
3259
3260 register const PixelPacket
3261 *p;
3262
cristybb503372010-05-27 20:51:26 +00003263 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003264 x;
3265
3266#if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3267 {
3268 at_bitmap_type
3269 *trace;
3270
3271 at_fitting_opts_type
3272 *fitting_options;
3273
3274 at_output_opts_type
3275 *output_options;
3276
3277 at_splines_type
3278 *splines;
3279
3280 ImageType
3281 type;
3282
cristybb503372010-05-27 20:51:26 +00003283 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003284 i;
3285
cristybb503372010-05-27 20:51:26 +00003286 size_t
cristy3ed852e2009-09-05 21:47:34 +00003287 number_planes;
3288
3289 /*
3290 Trace image and write as SVG.
3291 */
3292 fitting_options=at_fitting_opts_new();
3293 output_options=at_output_opts_new();
3294 type=GetImageType(image,&image->exception);
3295 number_planes=3;
3296 if ((type == BilevelType) || (type == GrayscaleType))
3297 number_planes=1;
3298 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3299 i=0;
cristybb503372010-05-27 20:51:26 +00003300 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003301 {
3302 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
3303 if (p == (const PixelPacket *) NULL)
3304 break;
cristybb503372010-05-27 20:51:26 +00003305 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003306 {
cristyce70c172010-01-07 17:15:30 +00003307 trace->bitmap[i++]=GetRedPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003308 if (number_planes == 3)
3309 {
cristyce70c172010-01-07 17:15:30 +00003310 trace->bitmap[i++]=GetGreenPixelComponent(p);
3311 trace->bitmap[i++]=GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003312 }
3313 p++;
3314 }
3315 }
3316 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3317 NULL);
3318 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3319 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3320 NULL);
3321 /*
3322 Free resources.
3323 */
3324 at_splines_free(splines);
3325 at_bitmap_free(trace);
3326 at_output_opts_free(output_options);
3327 at_fitting_opts_free(fitting_options);
3328 }
3329#else
3330 {
3331 char
3332 message[MaxTextExtent],
3333 tuple[MaxTextExtent];
3334
3335 MagickPixelPacket
3336 pixel;
3337
3338 register const IndexPacket
3339 *indexes;
3340
3341 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3342 (void) WriteBlobString(image,
3343 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3344 (void) WriteBlobString(image,
3345 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3346 (void) FormatMagickString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00003347 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,
3348 (double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003349 (void) WriteBlobString(image,message);
3350 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00003351 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003352 {
3353 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
3354 if (p == (const PixelPacket *) NULL)
3355 break;
3356 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00003357 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003358 {
3359 SetMagickPixelPacket(image,p,indexes+x,&pixel);
3360 (void) QueryMagickColorname(image,&pixel,SVGCompliance,tuple,
3361 &image->exception);
3362 (void) FormatMagickString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00003363 " <circle cx=\"%.20g\" cy=\"%.20g\" r=\"1\" fill=\"%s\"/>\n",
3364 (double) x,(double) y,tuple);
cristy3ed852e2009-09-05 21:47:34 +00003365 (void) WriteBlobString(image,message);
3366 p++;
3367 }
3368 }
3369 (void) WriteBlobString(image,"</svg>\n");
3370 }
3371#endif
3372 return(MagickTrue);
3373}
3374
3375static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image)
3376{
3377#define BezierQuantum 200
3378
3379 AffineMatrix
3380 affine;
3381
3382 char
3383 keyword[MaxTextExtent],
3384 message[MaxTextExtent],
3385 name[MaxTextExtent],
3386 *token,
3387 type[MaxTextExtent];
3388
3389 const char
3390 *p,
3391 *q,
3392 *value;
3393
3394 int
3395 n;
3396
cristybb503372010-05-27 20:51:26 +00003397 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003398 j;
3399
3400 MagickBooleanType
3401 active,
3402 status;
3403
3404 PointInfo
3405 point;
3406
3407 PrimitiveInfo
3408 *primitive_info;
3409
3410 PrimitiveType
3411 primitive_type;
3412
cristybb503372010-05-27 20:51:26 +00003413 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003414 x;
3415
cristybb503372010-05-27 20:51:26 +00003416 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003417 i;
3418
3419 size_t
3420 length;
3421
3422 SVGInfo
3423 svg_info;
3424
cristybb503372010-05-27 20:51:26 +00003425 size_t
cristy3ed852e2009-09-05 21:47:34 +00003426 number_points;
3427
3428 /*
3429 Open output image file.
3430 */
3431 assert(image_info != (const ImageInfo *) NULL);
3432 assert(image_info->signature == MagickSignature);
3433 assert(image != (Image *) NULL);
3434 assert(image->signature == MagickSignature);
3435 if (image->debug != MagickFalse)
3436 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3437 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
3438 if (status == MagickFalse)
3439 return(status);
3440 value=GetImageArtifact(image,"SVG");
3441 if (value != (char *) NULL)
3442 {
3443 (void) WriteBlobString(image,value);
3444 (void) CloseBlob(image);
3445 return(MagickTrue);
3446 }
3447 value=GetImageArtifact(image,"MVG");
3448 if (value == (char *) NULL)
3449 return(TraceSVGImage(image));
3450 /*
3451 Write SVG header.
3452 */
3453 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3454 (void) WriteBlobString(image,
3455 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3456 (void) WriteBlobString(image,
3457 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
3458 (void) FormatMagickString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00003459 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3460 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003461 (void) WriteBlobString(image,message);
3462 /*
3463 Allocate primitive info memory.
3464 */
3465 number_points=2047;
3466 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3467 sizeof(*primitive_info));
3468 if (primitive_info == (PrimitiveInfo *) NULL)
3469 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3470 GetAffineMatrix(&affine);
3471 token=AcquireString(value);
3472 active=MagickFalse;
3473 n=0;
3474 status=MagickTrue;
3475 for (q=(const char *) value; *q != '\0'; )
3476 {
3477 /*
3478 Interpret graphic primitive.
3479 */
3480 GetMagickToken(q,&q,keyword);
3481 if (*keyword == '\0')
3482 break;
3483 if (*keyword == '#')
3484 {
3485 /*
3486 Comment.
3487 */
3488 if (active != MagickFalse)
3489 {
3490 AffineToTransform(image,&affine);
3491 active=MagickFalse;
3492 }
3493 (void) WriteBlobString(image,"<desc>");
3494 (void) WriteBlobString(image,keyword+1);
3495 for ( ; (*q != '\n') && (*q != '\0'); q++)
3496 switch (*q)
3497 {
3498 case '<': (void) WriteBlobString(image,"&lt;"); break;
3499 case '>': (void) WriteBlobString(image,"&gt;"); break;
3500 case '&': (void) WriteBlobString(image,"&amp;"); break;
3501 default: (void) WriteBlobByte(image,*q); break;
3502 }
3503 (void) WriteBlobString(image,"</desc>\n");
3504 continue;
3505 }
3506 primitive_type=UndefinedPrimitive;
3507 switch (*keyword)
3508 {
3509 case ';':
3510 break;
3511 case 'a':
3512 case 'A':
3513 {
3514 if (LocaleCompare("affine",keyword) == 0)
3515 {
3516 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003517 affine.sx=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003518 GetMagickToken(q,&q,token);
3519 if (*token == ',')
3520 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003521 affine.rx=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003522 GetMagickToken(q,&q,token);
3523 if (*token == ',')
3524 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003525 affine.ry=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003526 GetMagickToken(q,&q,token);
3527 if (*token == ',')
3528 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003529 affine.sy=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003530 GetMagickToken(q,&q,token);
3531 if (*token == ',')
3532 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003533 affine.tx=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003534 GetMagickToken(q,&q,token);
3535 if (*token == ',')
3536 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003537 affine.ty=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003538 break;
3539 }
3540 if (LocaleCompare("angle",keyword) == 0)
3541 {
3542 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003543 affine.rx=StringToDouble(token);
3544 affine.ry=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003545 break;
3546 }
3547 if (LocaleCompare("arc",keyword) == 0)
3548 {
3549 primitive_type=ArcPrimitive;
3550 break;
3551 }
3552 status=MagickFalse;
3553 break;
3554 }
3555 case 'b':
3556 case 'B':
3557 {
3558 if (LocaleCompare("bezier",keyword) == 0)
3559 {
3560 primitive_type=BezierPrimitive;
3561 break;
3562 }
3563 status=MagickFalse;
3564 break;
3565 }
3566 case 'c':
3567 case 'C':
3568 {
3569 if (LocaleCompare("clip-path",keyword) == 0)
3570 {
3571 GetMagickToken(q,&q,token);
3572 (void) FormatMagickString(message,MaxTextExtent,
3573 "clip-path:url(#%s);",token);
3574 (void) WriteBlobString(image,message);
3575 break;
3576 }
3577 if (LocaleCompare("clip-rule",keyword) == 0)
3578 {
3579 GetMagickToken(q,&q,token);
3580 (void) FormatMagickString(message,MaxTextExtent,
3581 "clip-rule:%s;",token);
3582 (void) WriteBlobString(image,message);
3583 break;
3584 }
3585 if (LocaleCompare("clip-units",keyword) == 0)
3586 {
3587 GetMagickToken(q,&q,token);
3588 (void) FormatMagickString(message,MaxTextExtent,
3589 "clipPathUnits=%s;",token);
3590 (void) WriteBlobString(image,message);
3591 break;
3592 }
3593 if (LocaleCompare("circle",keyword) == 0)
3594 {
3595 primitive_type=CirclePrimitive;
3596 break;
3597 }
3598 if (LocaleCompare("color",keyword) == 0)
3599 {
3600 primitive_type=ColorPrimitive;
3601 break;
3602 }
3603 status=MagickFalse;
3604 break;
3605 }
3606 case 'd':
3607 case 'D':
3608 {
3609 if (LocaleCompare("decorate",keyword) == 0)
3610 {
3611 GetMagickToken(q,&q,token);
3612 (void) FormatMagickString(message,MaxTextExtent,
3613 "text-decoration:%s;",token);
3614 (void) WriteBlobString(image,message);
3615 break;
3616 }
3617 status=MagickFalse;
3618 break;
3619 }
3620 case 'e':
3621 case 'E':
3622 {
3623 if (LocaleCompare("ellipse",keyword) == 0)
3624 {
3625 primitive_type=EllipsePrimitive;
3626 break;
3627 }
3628 status=MagickFalse;
3629 break;
3630 }
3631 case 'f':
3632 case 'F':
3633 {
3634 if (LocaleCompare("fill",keyword) == 0)
3635 {
3636 GetMagickToken(q,&q,token);
3637 (void) FormatMagickString(message,MaxTextExtent,"fill:%s;",
3638 token);
3639 (void) WriteBlobString(image,message);
3640 break;
3641 }
3642 if (LocaleCompare("fill-rule",keyword) == 0)
3643 {
3644 GetMagickToken(q,&q,token);
3645 (void) FormatMagickString(message,MaxTextExtent,
3646 "fill-rule:%s;",token);
3647 (void) WriteBlobString(image,message);
3648 break;
3649 }
3650 if (LocaleCompare("fill-opacity",keyword) == 0)
3651 {
3652 GetMagickToken(q,&q,token);
3653 (void) FormatMagickString(message,MaxTextExtent,
3654 "fill-opacity:%s;",token);
3655 (void) WriteBlobString(image,message);
3656 break;
3657 }
3658 if (LocaleCompare("font-family",keyword) == 0)
3659 {
3660 GetMagickToken(q,&q,token);
3661 (void) FormatMagickString(message,MaxTextExtent,
3662 "font-family:%s;",token);
3663 (void) WriteBlobString(image,message);
3664 break;
3665 }
3666 if (LocaleCompare("font-stretch",keyword) == 0)
3667 {
3668 GetMagickToken(q,&q,token);
3669 (void) FormatMagickString(message,MaxTextExtent,
3670 "font-stretch:%s;",token);
3671 (void) WriteBlobString(image,message);
3672 break;
3673 }
3674 if (LocaleCompare("font-style",keyword) == 0)
3675 {
3676 GetMagickToken(q,&q,token);
3677 (void) FormatMagickString(message,MaxTextExtent,
3678 "font-style:%s;",token);
3679 (void) WriteBlobString(image,message);
3680 break;
3681 }
3682 if (LocaleCompare("font-size",keyword) == 0)
3683 {
3684 GetMagickToken(q,&q,token);
3685 (void) FormatMagickString(message,MaxTextExtent,
3686 "font-size:%s;",token);
3687 (void) WriteBlobString(image,message);
3688 break;
3689 }
3690 if (LocaleCompare("font-weight",keyword) == 0)
3691 {
3692 GetMagickToken(q,&q,token);
3693 (void) FormatMagickString(message,MaxTextExtent,
3694 "font-weight:%s;",token);
3695 (void) WriteBlobString(image,message);
3696 break;
3697 }
3698 status=MagickFalse;
3699 break;
3700 }
3701 case 'g':
3702 case 'G':
3703 {
3704 if (LocaleCompare("gradient-units",keyword) == 0)
3705 {
3706 GetMagickToken(q,&q,token);
3707 break;
3708 }
3709 if (LocaleCompare("text-align",keyword) == 0)
3710 {
3711 GetMagickToken(q,&q,token);
3712 (void) FormatMagickString(message,MaxTextExtent,
3713 "text-align %s ",token);
3714 (void) WriteBlobString(image,message);
3715 break;
3716 }
3717 if (LocaleCompare("text-anchor",keyword) == 0)
3718 {
3719 GetMagickToken(q,&q,token);
3720 (void) FormatMagickString(message,MaxTextExtent,
3721 "text-anchor %s ",token);
3722 (void) WriteBlobString(image,message);
3723 break;
3724 }
3725 status=MagickFalse;
3726 break;
3727 }
3728 case 'i':
3729 case 'I':
3730 {
3731 if (LocaleCompare("image",keyword) == 0)
3732 {
3733 GetMagickToken(q,&q,token);
3734 primitive_type=ImagePrimitive;
3735 break;
3736 }
3737 status=MagickFalse;
3738 break;
3739 }
3740 case 'l':
3741 case 'L':
3742 {
3743 if (LocaleCompare("line",keyword) == 0)
3744 {
3745 primitive_type=LinePrimitive;
3746 break;
3747 }
3748 status=MagickFalse;
3749 break;
3750 }
3751 case 'm':
3752 case 'M':
3753 {
3754 if (LocaleCompare("matte",keyword) == 0)
3755 {
3756 primitive_type=MattePrimitive;
3757 break;
3758 }
3759 status=MagickFalse;
3760 break;
3761 }
3762 case 'o':
3763 case 'O':
3764 {
3765 if (LocaleCompare("opacity",keyword) == 0)
3766 {
3767 GetMagickToken(q,&q,token);
3768 (void) FormatMagickString(message,MaxTextExtent,"opacity %s ",
3769 token);
3770 (void) WriteBlobString(image,message);
3771 break;
3772 }
3773 status=MagickFalse;
3774 break;
3775 }
3776 case 'p':
3777 case 'P':
3778 {
3779 if (LocaleCompare("path",keyword) == 0)
3780 {
3781 primitive_type=PathPrimitive;
3782 break;
3783 }
3784 if (LocaleCompare("point",keyword) == 0)
3785 {
3786 primitive_type=PointPrimitive;
3787 break;
3788 }
3789 if (LocaleCompare("polyline",keyword) == 0)
3790 {
3791 primitive_type=PolylinePrimitive;
3792 break;
3793 }
3794 if (LocaleCompare("polygon",keyword) == 0)
3795 {
3796 primitive_type=PolygonPrimitive;
3797 break;
3798 }
3799 if (LocaleCompare("pop",keyword) == 0)
3800 {
3801 GetMagickToken(q,&q,token);
3802 if (LocaleCompare("clip-path",token) == 0)
3803 {
3804 (void) WriteBlobString(image,"</clipPath>\n");
3805 break;
3806 }
3807 if (LocaleCompare("defs",token) == 0)
3808 {
3809 (void) WriteBlobString(image,"</defs>\n");
3810 break;
3811 }
3812 if (LocaleCompare("gradient",token) == 0)
3813 {
3814 (void) FormatMagickString(message,MaxTextExtent,
3815 "</%sGradient>\n",type);
3816 (void) WriteBlobString(image,message);
3817 break;
3818 }
3819 if (LocaleCompare("graphic-context",token) == 0)
3820 {
3821 n--;
3822 if (n < 0)
3823 ThrowWriterException(DrawError,
3824 "UnbalancedGraphicContextPushPop");
3825 (void) WriteBlobString(image,"</g>\n");
3826 }
3827 if (LocaleCompare("pattern",token) == 0)
3828 {
3829 (void) WriteBlobString(image,"</pattern>\n");
3830 break;
3831 }
3832 if (LocaleCompare("defs",token) == 0)
3833 (void) WriteBlobString(image,"</g>\n");
3834 break;
3835 }
3836 if (LocaleCompare("push",keyword) == 0)
3837 {
3838 GetMagickToken(q,&q,token);
3839 if (LocaleCompare("clip-path",token) == 0)
3840 {
3841 GetMagickToken(q,&q,token);
3842 (void) FormatMagickString(message,MaxTextExtent,
3843 "<clipPath id=\"%s\">\n",token);
3844 (void) WriteBlobString(image,message);
3845 break;
3846 }
3847 if (LocaleCompare("defs",token) == 0)
3848 {
3849 (void) WriteBlobString(image,"<defs>\n");
3850 break;
3851 }
3852 if (LocaleCompare("gradient",token) == 0)
3853 {
3854 GetMagickToken(q,&q,token);
3855 (void) CopyMagickString(name,token,MaxTextExtent);
3856 GetMagickToken(q,&q,token);
3857 (void) CopyMagickString(type,token,MaxTextExtent);
3858 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003859 svg_info.segment.x1=StringToDouble(token);
3860 svg_info.element.cx=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.y1=StringToDouble(token);
3865 svg_info.element.cy=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.x2=StringToDouble(token);
3870 svg_info.element.major=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003871 GetMagickToken(q,&q,token);
3872 if (*token == ',')
3873 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003874 svg_info.segment.y2=StringToDouble(token);
3875 svg_info.element.minor=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003876 (void) FormatMagickString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003877 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
3878 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
cristy3ed852e2009-09-05 21:47:34 +00003879 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
3880 if (LocaleCompare(type,"radial") == 0)
3881 {
3882 GetMagickToken(q,&q,token);
3883 if (*token == ',')
3884 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003885 svg_info.element.angle=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003886 (void) FormatMagickString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003887 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
3888 "fx=\"%g\" fy=\"%g\">\n",type,name,
cristy8cd5b312010-01-07 01:10:24 +00003889 svg_info.element.cx,svg_info.element.cy,
3890 svg_info.element.angle,svg_info.element.major,
3891 svg_info.element.minor);
cristy3ed852e2009-09-05 21:47:34 +00003892 }
3893 (void) WriteBlobString(image,message);
3894 break;
3895 }
3896 if (LocaleCompare("graphic-context",token) == 0)
3897 {
3898 n++;
3899 if (active)
3900 {
3901 AffineToTransform(image,&affine);
3902 active=MagickFalse;
3903 }
3904 (void) WriteBlobString(image,"<g style=\"");
3905 active=MagickTrue;
3906 }
3907 if (LocaleCompare("pattern",token) == 0)
3908 {
3909 GetMagickToken(q,&q,token);
3910 (void) CopyMagickString(name,token,MaxTextExtent);
3911 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003912 svg_info.bounds.x=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003913 GetMagickToken(q,&q,token);
3914 if (*token == ',')
3915 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003916 svg_info.bounds.y=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003917 GetMagickToken(q,&q,token);
3918 if (*token == ',')
3919 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003920 svg_info.bounds.width=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003921 GetMagickToken(q,&q,token);
3922 if (*token == ',')
3923 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003924 svg_info.bounds.height=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003925 (void) FormatMagickString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003926 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
3927 "height=\"%g\">\n",name,svg_info.bounds.x,
cristy8cd5b312010-01-07 01:10:24 +00003928 svg_info.bounds.y,svg_info.bounds.width,
3929 svg_info.bounds.height);
cristy3ed852e2009-09-05 21:47:34 +00003930 (void) WriteBlobString(image,message);
3931 break;
3932 }
3933 break;
3934 }
3935 status=MagickFalse;
3936 break;
3937 }
3938 case 'r':
3939 case 'R':
3940 {
3941 if (LocaleCompare("rectangle",keyword) == 0)
3942 {
3943 primitive_type=RectanglePrimitive;
3944 break;
3945 }
3946 if (LocaleCompare("roundRectangle",keyword) == 0)
3947 {
3948 primitive_type=RoundRectanglePrimitive;
3949 break;
3950 }
3951 if (LocaleCompare("rotate",keyword) == 0)
3952 {
3953 GetMagickToken(q,&q,token);
3954 (void) FormatMagickString(message,MaxTextExtent,"rotate(%s) ",
3955 token);
3956 (void) WriteBlobString(image,message);
3957 break;
3958 }
3959 status=MagickFalse;
3960 break;
3961 }
3962 case 's':
3963 case 'S':
3964 {
3965 if (LocaleCompare("scale",keyword) == 0)
3966 {
3967 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003968 affine.sx=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003969 GetMagickToken(q,&q,token);
3970 if (*token == ',')
3971 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00003972 affine.sy=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00003973 break;
3974 }
3975 if (LocaleCompare("skewX",keyword) == 0)
3976 {
3977 GetMagickToken(q,&q,token);
3978 (void) FormatMagickString(message,MaxTextExtent,"skewX(%s) ",
3979 token);
3980 (void) WriteBlobString(image,message);
3981 break;
3982 }
3983 if (LocaleCompare("skewY",keyword) == 0)
3984 {
3985 GetMagickToken(q,&q,token);
3986 (void) FormatMagickString(message,MaxTextExtent,"skewY(%s) ",
3987 token);
3988 (void) WriteBlobString(image,message);
3989 break;
3990 }
3991 if (LocaleCompare("stop-color",keyword) == 0)
3992 {
3993 char
3994 color[MaxTextExtent];
3995
3996 GetMagickToken(q,&q,token);
3997 (void) CopyMagickString(color,token,MaxTextExtent);
3998 GetMagickToken(q,&q,token);
3999 (void) FormatMagickString(message,MaxTextExtent,
4000 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4001 (void) WriteBlobString(image,message);
4002 break;
4003 }
4004 if (LocaleCompare("stroke",keyword) == 0)
4005 {
4006 GetMagickToken(q,&q,token);
4007 (void) FormatMagickString(message,MaxTextExtent,"stroke:%s;",
4008 token);
4009 (void) WriteBlobString(image,message);
4010 break;
4011 }
4012 if (LocaleCompare("stroke-antialias",keyword) == 0)
4013 {
4014 GetMagickToken(q,&q,token);
4015 (void) FormatMagickString(message,MaxTextExtent,
4016 "stroke-antialias:%s;",token);
4017 (void) WriteBlobString(image,message);
4018 break;
4019 }
4020 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4021 {
4022 if (IsPoint(q))
4023 {
cristybb503372010-05-27 20:51:26 +00004024 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004025 k;
4026
4027 p=q;
4028 GetMagickToken(p,&p,token);
4029 for (k=0; IsPoint(token); k++)
4030 GetMagickToken(p,&p,token);
4031 (void) WriteBlobString(image,"stroke-dasharray:");
4032 for (j=0; j < k; j++)
4033 {
4034 GetMagickToken(q,&q,token);
4035 (void) FormatMagickString(message,MaxTextExtent,"%s ",
4036 token);
4037 (void) WriteBlobString(image,message);
4038 }
4039 (void) WriteBlobString(image,";");
4040 break;
4041 }
4042 GetMagickToken(q,&q,token);
4043 (void) FormatMagickString(message,MaxTextExtent,
4044 "stroke-dasharray:%s;",token);
4045 (void) WriteBlobString(image,message);
4046 break;
4047 }
4048 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4049 {
4050 GetMagickToken(q,&q,token);
4051 (void) FormatMagickString(message,MaxTextExtent,
4052 "stroke-dashoffset:%s;",token);
4053 (void) WriteBlobString(image,message);
4054 break;
4055 }
4056 if (LocaleCompare("stroke-linecap",keyword) == 0)
4057 {
4058 GetMagickToken(q,&q,token);
4059 (void) FormatMagickString(message,MaxTextExtent,
4060 "stroke-linecap:%s;",token);
4061 (void) WriteBlobString(image,message);
4062 break;
4063 }
4064 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4065 {
4066 GetMagickToken(q,&q,token);
4067 (void) FormatMagickString(message,MaxTextExtent,
4068 "stroke-linejoin:%s;",token);
4069 (void) WriteBlobString(image,message);
4070 break;
4071 }
4072 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4073 {
4074 GetMagickToken(q,&q,token);
4075 (void) FormatMagickString(message,MaxTextExtent,
4076 "stroke-miterlimit:%s;",token);
4077 (void) WriteBlobString(image,message);
4078 break;
4079 }
4080 if (LocaleCompare("stroke-opacity",keyword) == 0)
4081 {
4082 GetMagickToken(q,&q,token);
4083 (void) FormatMagickString(message,MaxTextExtent,
4084 "stroke-opacity:%s;",token);
4085 (void) WriteBlobString(image,message);
4086 break;
4087 }
4088 if (LocaleCompare("stroke-width",keyword) == 0)
4089 {
4090 GetMagickToken(q,&q,token);
4091 (void) FormatMagickString(message,MaxTextExtent,
4092 "stroke-width:%s;",token);
4093 (void) WriteBlobString(image,message);
4094 continue;
4095 }
4096 status=MagickFalse;
4097 break;
4098 }
4099 case 't':
4100 case 'T':
4101 {
4102 if (LocaleCompare("text",keyword) == 0)
4103 {
4104 primitive_type=TextPrimitive;
4105 break;
4106 }
4107 if (LocaleCompare("text-antialias",keyword) == 0)
4108 {
4109 GetMagickToken(q,&q,token);
4110 (void) FormatMagickString(message,MaxTextExtent,
4111 "text-antialias:%s;",token);
4112 (void) WriteBlobString(image,message);
4113 break;
4114 }
4115 if (LocaleCompare("tspan",keyword) == 0)
4116 {
4117 primitive_type=TextPrimitive;
4118 break;
4119 }
4120 if (LocaleCompare("translate",keyword) == 0)
4121 {
4122 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00004123 affine.tx=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00004124 GetMagickToken(q,&q,token);
4125 if (*token == ',')
4126 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00004127 affine.ty=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00004128 break;
4129 }
4130 status=MagickFalse;
4131 break;
4132 }
4133 case 'v':
4134 case 'V':
4135 {
4136 if (LocaleCompare("viewbox",keyword) == 0)
4137 {
4138 GetMagickToken(q,&q,token);
4139 if (*token == ',')
4140 GetMagickToken(q,&q,token);
4141 GetMagickToken(q,&q,token);
4142 if (*token == ',')
4143 GetMagickToken(q,&q,token);
4144 GetMagickToken(q,&q,token);
4145 if (*token == ',')
4146 GetMagickToken(q,&q,token);
4147 GetMagickToken(q,&q,token);
4148 break;
4149 }
4150 status=MagickFalse;
4151 break;
4152 }
4153 default:
4154 {
4155 status=MagickFalse;
4156 break;
4157 }
4158 }
4159 if (status == MagickFalse)
4160 break;
4161 if (primitive_type == UndefinedPrimitive)
4162 continue;
4163 /*
4164 Parse the primitive attributes.
4165 */
4166 i=0;
4167 j=0;
4168 for (x=0; *q != '\0'; x++)
4169 {
4170 /*
4171 Define points.
4172 */
4173 if (IsPoint(q) == MagickFalse)
4174 break;
4175 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00004176 point.x=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00004177 GetMagickToken(q,&q,token);
4178 if (*token == ',')
4179 GetMagickToken(q,&q,token);
cristyf2f27272009-12-17 14:48:46 +00004180 point.y=StringToDouble(token);
cristy3ed852e2009-09-05 21:47:34 +00004181 GetMagickToken(q,(const char **) NULL,token);
4182 if (*token == ',')
4183 GetMagickToken(q,&q,token);
4184 primitive_info[i].primitive=primitive_type;
4185 primitive_info[i].point=point;
4186 primitive_info[i].coordinates=0;
4187 primitive_info[i].method=FloodfillMethod;
4188 i++;
cristybb503372010-05-27 20:51:26 +00004189 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
cristy3ed852e2009-09-05 21:47:34 +00004190 continue;
4191 number_points+=6*BezierQuantum+360;
4192 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4193 number_points,sizeof(*primitive_info));
4194 if (primitive_info == (PrimitiveInfo *) NULL)
4195 {
4196 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4197 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4198 break;
4199 }
4200 }
4201 primitive_info[j].primitive=primitive_type;
4202 primitive_info[j].coordinates=x;
4203 primitive_info[j].method=FloodfillMethod;
4204 primitive_info[j].text=(char *) NULL;
4205 if (active)
4206 {
4207 AffineToTransform(image,&affine);
4208 active=MagickFalse;
4209 }
4210 active=MagickFalse;
4211 switch (primitive_type)
4212 {
4213 case PointPrimitive:
4214 default:
4215 {
4216 if (primitive_info[j].coordinates != 1)
4217 {
4218 status=MagickFalse;
4219 break;
4220 }
4221 break;
4222 }
4223 case LinePrimitive:
4224 {
4225 if (primitive_info[j].coordinates != 2)
4226 {
4227 status=MagickFalse;
4228 break;
4229 }
4230 (void) FormatMagickString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004231 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004232 primitive_info[j].point.x,primitive_info[j].point.y,
4233 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4234 (void) WriteBlobString(image,message);
4235 break;
4236 }
4237 case RectanglePrimitive:
4238 {
4239 if (primitive_info[j].coordinates != 2)
4240 {
4241 status=MagickFalse;
4242 break;
4243 }
4244 (void) FormatMagickString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004245 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004246 primitive_info[j].point.x,primitive_info[j].point.y,
4247 primitive_info[j+1].point.x-primitive_info[j].point.x,
4248 primitive_info[j+1].point.y-primitive_info[j].point.y);
4249 (void) WriteBlobString(image,message);
4250 break;
4251 }
4252 case RoundRectanglePrimitive:
4253 {
4254 if (primitive_info[j].coordinates != 3)
4255 {
4256 status=MagickFalse;
4257 break;
4258 }
4259 (void) FormatMagickString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004260 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4261 "ry=\"%g\"/>\n",primitive_info[j].point.x,
cristy8cd5b312010-01-07 01:10:24 +00004262 primitive_info[j].point.y,primitive_info[j+1].point.x-
4263 primitive_info[j].point.x,primitive_info[j+1].point.y-
4264 primitive_info[j].point.y,primitive_info[j+2].point.x,
4265 primitive_info[j+2].point.y);
cristy3ed852e2009-09-05 21:47:34 +00004266 (void) WriteBlobString(image,message);
4267 break;
4268 }
4269 case ArcPrimitive:
4270 {
4271 if (primitive_info[j].coordinates != 3)
4272 {
4273 status=MagickFalse;
4274 break;
4275 }
4276 break;
4277 }
4278 case EllipsePrimitive:
4279 {
4280 if (primitive_info[j].coordinates != 3)
4281 {
4282 status=MagickFalse;
4283 break;
4284 }
4285 (void) FormatMagickString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004286 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004287 primitive_info[j].point.x,primitive_info[j].point.y,
4288 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4289 (void) WriteBlobString(image,message);
4290 break;
4291 }
4292 case CirclePrimitive:
4293 {
4294 double
4295 alpha,
4296 beta;
4297
4298 if (primitive_info[j].coordinates != 2)
4299 {
4300 status=MagickFalse;
4301 break;
4302 }
4303 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4304 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
4305 (void) FormatMagickString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004306 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004307 primitive_info[j].point.x,primitive_info[j].point.y,
4308 hypot(alpha,beta));
4309 (void) WriteBlobString(image,message);
4310 break;
4311 }
4312 case PolylinePrimitive:
4313 {
4314 if (primitive_info[j].coordinates < 2)
4315 {
4316 status=MagickFalse;
4317 break;
4318 }
4319 (void) CopyMagickString(message," <polyline points=\"",MaxTextExtent);
4320 (void) WriteBlobString(image,message);
4321 length=strlen(message);
4322 for ( ; j < i; j++)
4323 {
cristye7f51092010-01-17 00:39:37 +00004324 (void) FormatMagickString(message,MaxTextExtent,"%g,%g ",
cristy3ed852e2009-09-05 21:47:34 +00004325 primitive_info[j].point.x,primitive_info[j].point.y);
4326 length+=strlen(message);
4327 if (length >= 80)
4328 {
4329 (void) WriteBlobString(image,"\n ");
4330 length=strlen(message)+5;
4331 }
4332 (void) WriteBlobString(image,message);
4333 }
4334 (void) WriteBlobString(image,"\"/>\n");
4335 break;
4336 }
4337 case PolygonPrimitive:
4338 {
4339 if (primitive_info[j].coordinates < 3)
4340 {
4341 status=MagickFalse;
4342 break;
4343 }
4344 primitive_info[i]=primitive_info[j];
4345 primitive_info[i].coordinates=0;
4346 primitive_info[j].coordinates++;
4347 i++;
4348 (void) CopyMagickString(message," <polygon points=\"",MaxTextExtent);
4349 (void) WriteBlobString(image,message);
4350 length=strlen(message);
4351 for ( ; j < i; j++)
4352 {
cristye7f51092010-01-17 00:39:37 +00004353 (void) FormatMagickString(message,MaxTextExtent,"%g,%g ",
cristy3ed852e2009-09-05 21:47:34 +00004354 primitive_info[j].point.x,primitive_info[j].point.y);
4355 length+=strlen(message);
4356 if (length >= 80)
4357 {
4358 (void) WriteBlobString(image,"\n ");
4359 length=strlen(message)+5;
4360 }
4361 (void) WriteBlobString(image,message);
4362 }
4363 (void) WriteBlobString(image,"\"/>\n");
4364 break;
4365 }
4366 case BezierPrimitive:
4367 {
4368 if (primitive_info[j].coordinates < 3)
4369 {
4370 status=MagickFalse;
4371 break;
4372 }
4373 break;
4374 }
4375 case PathPrimitive:
4376 {
4377 int
4378 number_attributes;
4379
4380 GetMagickToken(q,&q,token);
4381 number_attributes=1;
4382 for (p=token; *p != '\0'; p++)
4383 if (isalpha((int) *p))
4384 number_attributes++;
cristybb503372010-05-27 20:51:26 +00004385 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
cristy3ed852e2009-09-05 21:47:34 +00004386 {
4387 number_points+=6*BezierQuantum*number_attributes;
4388 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4389 number_points,sizeof(*primitive_info));
4390 if (primitive_info == (PrimitiveInfo *) NULL)
4391 {
4392 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4393 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4394 image->filename);
4395 break;
4396 }
4397 }
4398 (void) WriteBlobString(image," <path d=\"");
4399 (void) WriteBlobString(image,token);
4400 (void) WriteBlobString(image,"\"/>\n");
4401 break;
4402 }
4403 case ColorPrimitive:
4404 case MattePrimitive:
4405 {
4406 if (primitive_info[j].coordinates != 1)
4407 {
4408 status=MagickFalse;
4409 break;
4410 }
4411 GetMagickToken(q,&q,token);
4412 if (LocaleCompare("point",token) == 0)
4413 primitive_info[j].method=PointMethod;
4414 if (LocaleCompare("replace",token) == 0)
4415 primitive_info[j].method=ReplaceMethod;
4416 if (LocaleCompare("floodfill",token) == 0)
4417 primitive_info[j].method=FloodfillMethod;
4418 if (LocaleCompare("filltoborder",token) == 0)
4419 primitive_info[j].method=FillToBorderMethod;
4420 if (LocaleCompare("reset",token) == 0)
4421 primitive_info[j].method=ResetMethod;
4422 break;
4423 }
4424 case TextPrimitive:
4425 {
4426 register char
4427 *p;
4428
4429 if (primitive_info[j].coordinates != 1)
4430 {
4431 status=MagickFalse;
4432 break;
4433 }
4434 GetMagickToken(q,&q,token);
4435 (void) FormatMagickString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004436 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
cristy3ed852e2009-09-05 21:47:34 +00004437 primitive_info[j].point.y);
4438 (void) WriteBlobString(image,message);
4439 for (p=token; *p != '\0'; p++)
4440 switch (*p)
4441 {
4442 case '<': (void) WriteBlobString(image,"&lt;"); break;
4443 case '>': (void) WriteBlobString(image,"&gt;"); break;
4444 case '&': (void) WriteBlobString(image,"&amp;"); break;
4445 default: (void) WriteBlobByte(image,*p); break;
4446 }
4447 (void) WriteBlobString(image,"</text>\n");
4448 break;
4449 }
4450 case ImagePrimitive:
4451 {
4452 if (primitive_info[j].coordinates != 2)
4453 {
4454 status=MagickFalse;
4455 break;
4456 }
4457 GetMagickToken(q,&q,token);
4458 (void) FormatMagickString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004459 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
cristy3ed852e2009-09-05 21:47:34 +00004460 "xlink:href=\"%s\"/>\n",primitive_info[j].point.x,
4461 primitive_info[j].point.y,primitive_info[j+1].point.x,
4462 primitive_info[j+1].point.y,token);
4463 (void) WriteBlobString(image,message);
4464 break;
4465 }
4466 }
4467 if (primitive_info == (PrimitiveInfo *) NULL)
4468 break;
4469 primitive_info[i].primitive=UndefinedPrimitive;
4470 if (status == MagickFalse)
4471 break;
4472 }
4473 (void) WriteBlobString(image,"</svg>\n");
4474 /*
4475 Relinquish resources.
4476 */
4477 token=DestroyString(token);
4478 if (primitive_info != (PrimitiveInfo *) NULL)
4479 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4480 (void) CloseBlob(image);
4481 return(status);
4482}