blob: 209221bdac292f0f371ee37c295edd4a79606f4c [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*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/annotate.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/composite-private.h"
52#include "MagickCore/draw.h"
53#include "MagickCore/exception.h"
54#include "MagickCore/exception-private.h"
55#include "MagickCore/gem.h"
56#include "MagickCore/image.h"
57#include "MagickCore/image-private.h"
58#include "MagickCore/list.h"
59#include "MagickCore/log.h"
60#include "MagickCore/magick.h"
61#include "MagickCore/memory_.h"
62#include "MagickCore/module.h"
63#include "MagickCore/monitor.h"
64#include "MagickCore/monitor-private.h"
65#include "MagickCore/quantum-private.h"
66#include "MagickCore/pixel-accessor.h"
67#include "MagickCore/property.h"
68#include "MagickCore/resource_.h"
69#include "MagickCore/static.h"
70#include "MagickCore/string_.h"
71#include "MagickCore/string-private.h"
72#include "MagickCore/token.h"
73#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000074#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/*
cristy3ed852e2009-09-05 21:47:34 +0000101 Typedef declarations.
102*/
103typedef struct _BoundingBox
104{
105 double
106 x,
107 y,
108 width,
109 height;
110} BoundingBox;
111
112typedef struct _ElementInfo
113{
114 double
115 cx,
116 cy,
117 major,
118 minor,
119 angle;
120} ElementInfo;
121
122typedef struct _SVGInfo
123{
124 FILE
125 *file;
126
127 ExceptionInfo
128 *exception;
129
130 Image
131 *image;
132
133 const ImageInfo
134 *image_info;
135
136 AffineMatrix
137 affine;
138
cristybb503372010-05-27 20:51:26 +0000139 size_t
cristy3ed852e2009-09-05 21:47:34 +0000140 width,
141 height;
142
143 char
144 *size,
145 *title,
146 *comment;
147
148 int
149 n;
150
151 double
152 *scale,
153 pointsize;
154
155 ElementInfo
156 element;
157
158 SegmentInfo
159 segment;
160
161 BoundingBox
162 bounds,
163 center,
164 view_box;
165
166 PointInfo
167 radius;
168
169 char
170 *stop_color,
171 *offset,
172 *text,
173 *vertices,
174 *url;
175
176#if defined(MAGICKCORE_XML_DELEGATE)
177 xmlParserCtxtPtr
178 parser;
179
180 xmlDocPtr
181 document;
182#endif
183} SVGInfo;
184
185/*
186 Forward declarations.
187*/
188static MagickBooleanType
cristy3a37efd2011-08-28 20:31:03 +0000189 WriteSVGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000190
191/*
192%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
193% %
194% %
195% %
196% I s S V G %
197% %
198% %
199% %
200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
201%
202% IsSVG()() returns MagickTrue if the image format type, identified by the
203% magick string, is SVG.
204%
205% The format of the IsSVG method is:
206%
207% MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
208%
209% A description of each parameter follows:
210%
211% o magick: compare image format pattern against these bytes.
212%
213% o length: Specifies the length of the magick string.
214%
215*/
216static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
217{
218 if (length < 4)
219 return(MagickFalse);
220 if (LocaleNCompare((const char *) magick,"?xml",4) == 0)
221 return(MagickTrue);
222 return(MagickFalse);
223}
224
225#if defined(MAGICKCORE_XML_DELEGATE)
226/*
227%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
228% %
229% %
230% %
231% R e a d S V G I m a g e %
232% %
233% %
234% %
235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
236%
237% ReadSVGImage() reads a Scalable Vector Gaphics file and returns it. It
238% allocates the memory necessary for the new Image structure and returns a
239% pointer to the new image.
240%
241% The format of the ReadSVGImage method is:
242%
243% Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
244%
245% A description of each parameter follows:
246%
247% o image_info: the image info.
248%
249% o exception: return any errors or warnings in this structure.
250%
251*/
252
253static SVGInfo *AcquireSVGInfo(void)
254{
255 SVGInfo
256 *svg_info;
257
cristy73bd4a52010-10-05 11:24:23 +0000258 svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info));
cristy3ed852e2009-09-05 21:47:34 +0000259 if (svg_info == (SVGInfo *) NULL)
260 return((SVGInfo *) NULL);
261 (void) ResetMagickMemory(svg_info,0,sizeof(*svg_info));
262 svg_info->text=AcquireString("");
cristy73bd4a52010-10-05 11:24:23 +0000263 svg_info->scale=(double *) AcquireMagickMemory(sizeof(*svg_info->scale));
cristy3ed852e2009-09-05 21:47:34 +0000264 if (svg_info->scale == (double *) NULL)
265 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
266 GetAffineMatrix(&svg_info->affine);
267 svg_info->scale[0]=ExpandAffine(&svg_info->affine);
268 return(svg_info);
269}
270
271static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
272{
273 if (svg_info->text != (char *) NULL)
274 svg_info->text=DestroyString(svg_info->text);
275 if (svg_info->scale != (double *) NULL)
276 svg_info->scale=(double *) (svg_info->scale);
277 if (svg_info->title != (char *) NULL)
278 svg_info->title=DestroyString(svg_info->title);
279 if (svg_info->comment != (char *) NULL)
280 svg_info->comment=DestroyString(svg_info->comment);
281 return((SVGInfo *) RelinquishMagickMemory(svg_info));
282}
283
284static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
285 const char *string)
286{
287 char
288 token[MaxTextExtent];
289
290 const char
291 *p;
292
293 double
294 value;
295
296 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
297 assert(string != (const char *) NULL);
298 p=(const char *) string;
299 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +0000300 value=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000301 if (strchr(token,'%') != (char *) NULL)
302 {
303 double
304 alpha,
305 beta;
306
307 if (type > 0)
308 {
309 if (svg_info->view_box.width == 0.0)
310 return(1000.0);
311 return(svg_info->view_box.width*value/100.0);
312 }
313 if (type < 0)
314 {
315 if (svg_info->view_box.height == 0.0)
316 return(1000.0);
317 return(svg_info->view_box.height*value/100.0);
318 }
319 alpha=value-svg_info->view_box.width;
320 beta=value-svg_info->view_box.height;
321 return(hypot(alpha,beta)/sqrt(2.0)/100.0);
322 }
323 GetMagickToken(p,&p,token);
324 if (LocaleNCompare(token,"cm",2) == 0)
325 return(DefaultResolution*svg_info->scale[0]/2.54*value);
326 if (LocaleNCompare(token,"em",2) == 0)
327 return(svg_info->pointsize*value);
328 if (LocaleNCompare(token,"ex",2) == 0)
329 return(svg_info->pointsize*value/2.0);
330 if (LocaleNCompare(token,"in",2) == 0)
331 return(DefaultResolution*svg_info->scale[0]*value);
332 if (LocaleNCompare(token,"mm",2) == 0)
333 return(DefaultResolution*svg_info->scale[0]/25.4*value);
334 if (LocaleNCompare(token,"pc",2) == 0)
335 return(DefaultResolution*svg_info->scale[0]/6.0*value);
336 if (LocaleNCompare(token,"pt",2) == 0)
337 return(svg_info->scale[0]*value);
338 if (LocaleNCompare(token,"px",2) == 0)
339 return(value);
340 return(value);
341}
342
343static void StripStyleTokens(char *message)
344{
345 register char
346 *p,
347 *q;
348
349 size_t
350 length;
351
352 assert(message != (char *) NULL);
353 if (*message == '\0')
354 return;
355 length=strlen(message);
356 p=message;
357 while (isspace((int) ((unsigned char) *p)) != 0)
358 p++;
359 q=message+length-1;
360 while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
361 q--;
362 (void) CopyMagickMemory(message,p,(size_t) (q-p+1));
363 message[q-p+1]='\0';
364 StripString(message);
365}
366
367static char **GetStyleTokens(void *context,const char *style,int *number_tokens)
368{
369 char
370 *text,
371 **tokens;
372
cristybb503372010-05-27 20:51:26 +0000373 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000374 i;
375
376 SVGInfo
377 *svg_info;
378
379 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +0000380 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +0000381 *number_tokens=0;
382 if (style == (const char *) NULL)
383 return((char **) NULL);
384 text=AcquireString(style);
385 (void) SubstituteString(&text,":","\n");
386 (void) SubstituteString(&text,";","\n");
387 tokens=StringToList(text);
388 text=DestroyString(text);
389 for (i=0; tokens[i] != (char *) NULL; i++)
390 StripStyleTokens(tokens[i]);
391 *number_tokens=i;
392 return(tokens);
393}
394
395static char **GetTransformTokens(void *context,const char *text,
396 int *number_tokens)
397{
398 char
399 **tokens;
400
401 register const char
402 *p,
403 *q;
404
cristybb503372010-05-27 20:51:26 +0000405 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000406 i;
407
408 SVGInfo
409 *svg_info;
410
411 svg_info=(SVGInfo *) context;
412 *number_tokens=0;
413 if (text == (const char *) NULL)
414 return((char **) NULL);
415 /*
416 Determine the number of arguments.
417 */
418 for (p=text; *p != '\0'; p++)
419 {
420 if (*p == '(')
421 (*number_tokens)+=2;
422 }
423 tokens=(char **) AcquireQuantumMemory(*number_tokens+2UL,sizeof(*tokens));
424 if (tokens == (char **) NULL)
425 {
426 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
427 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
428 return((char **) NULL);
429 }
430 /*
431 Convert string to an ASCII list.
432 */
433 i=0;
434 p=text;
435 for (q=p; *q != '\0'; q++)
436 {
437 if ((*q != '(') && (*q != ')') && (*q != '\0'))
438 continue;
439 tokens[i]=AcquireString(p);
440 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
441 StripString(tokens[i++]);
442 p=q+1;
443 }
444 tokens[i]=AcquireString(p);
445 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
446 StripString(tokens[i++]);
447 tokens[i]=(char *) NULL;
448 return(tokens);
449}
450
451#if defined(__cplusplus) || defined(c_plusplus)
452extern "C" {
453#endif
454
455static int SVGIsStandalone(void *context)
456{
457 SVGInfo
458 *svg_info;
459
460 /*
461 Is this document tagged standalone?
462 */
463 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGIsStandalone()");
464 svg_info=(SVGInfo *) context;
465 return(svg_info->document->standalone == 1);
466}
467
468static int SVGHasInternalSubset(void *context)
469{
470 SVGInfo
471 *svg_info;
472
473 /*
474 Does this document has an internal subset?
475 */
476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
477 " SAX.SVGHasInternalSubset()");
478 svg_info=(SVGInfo *) context;
479 return(svg_info->document->intSubset != NULL);
480}
481
482static int SVGHasExternalSubset(void *context)
483{
484 SVGInfo
485 *svg_info;
486
487 /*
488 Does this document has an external subset?
489 */
490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
491 " SAX.SVGHasExternalSubset()");
492 svg_info=(SVGInfo *) context;
493 return(svg_info->document->extSubset != NULL);
494}
495
496static void SVGInternalSubset(void *context,const xmlChar *name,
497 const xmlChar *external_id,const xmlChar *system_id)
498{
499 SVGInfo
500 *svg_info;
501
502 /*
503 Does this document has an internal subset?
504 */
505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
506 " SAX.internalSubset(%s, %s, %s)",(const char *) name,
507 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
508 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
509 svg_info=(SVGInfo *) context;
510 (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id);
511}
512
513static xmlParserInputPtr SVGResolveEntity(void *context,
514 const xmlChar *public_id,const xmlChar *system_id)
515{
516 SVGInfo
517 *svg_info;
518
519 xmlParserInputPtr
520 stream;
521
522 /*
523 Special entity resolver, better left to the parser, it has more
524 context than the application layer. The default behaviour is to
525 not resolve the entities, in that case the ENTITY_REF nodes are
526 built in the structure (and the parameter values).
527 */
528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
529 " SAX.resolveEntity(%s, %s)",
530 (public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"),
531 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
532 svg_info=(SVGInfo *) context;
533 stream=xmlLoadExternalEntity((const char *) system_id,(const char *)
534 public_id,svg_info->parser);
535 return(stream);
536}
537
538static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
539{
540 SVGInfo
541 *svg_info;
542
543 /*
544 Get an entity by name.
545 */
546 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGGetEntity(%s)",
547 name);
548 svg_info=(SVGInfo *) context;
549 return(xmlGetDocEntity(svg_info->document,name));
550}
551
552static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
553{
554 SVGInfo
555 *svg_info;
556
557 /*
558 Get a parameter entity by name.
559 */
560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
561 " SAX.getParameterEntity(%s)",name);
562 svg_info=(SVGInfo *) context;
563 return(xmlGetParameterEntity(svg_info->document,name));
564}
565
566static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
567 const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
568{
569 SVGInfo
570 *svg_info;
571
572 /*
573 An entity definition has been parsed.
574 */
575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
576 " SAX.entityDecl(%s, %d, %s, %s, %s)",name,type,
577 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
578 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content);
579 svg_info=(SVGInfo *) context;
580 if (svg_info->parser->inSubset == 1)
581 (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id,
582 content);
583 else
584 if (svg_info->parser->inSubset == 2)
585 (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
586 content);
587}
588
589static void SVGAttributeDeclaration(void *context,const xmlChar *element,
590 const xmlChar *name,int type,int value,const xmlChar *default_value,
591 xmlEnumerationPtr tree)
592{
593 SVGInfo
594 *svg_info;
595
596 xmlChar
597 *fullname,
598 *prefix;
599
600 xmlParserCtxtPtr
601 parser;
602
603 /*
604 An attribute definition has been parsed.
605 */
606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
607 " SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value,
608 default_value);
609 svg_info=(SVGInfo *) context;
610 fullname=(xmlChar *) NULL;
611 prefix=(xmlChar *) NULL;
612 parser=svg_info->parser;
613 fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix);
614 if (parser->inSubset == 1)
615 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset,
616 element,fullname,prefix,(xmlAttributeType) type,
617 (xmlAttributeDefault) value,default_value,tree);
618 else
619 if (parser->inSubset == 2)
620 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset,
621 element,fullname,prefix,(xmlAttributeType) type,
622 (xmlAttributeDefault) value,default_value,tree);
623 if (prefix != (xmlChar *) NULL)
624 xmlFree(prefix);
625 if (fullname != (xmlChar *) NULL)
626 xmlFree(fullname);
627}
628
629static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
630 xmlElementContentPtr content)
631{
632 SVGInfo
633 *svg_info;
634
635 xmlParserCtxtPtr
636 parser;
637
638 /*
639 An element definition has been parsed.
640 */
641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
642 " SAX.elementDecl(%s, %d, ...)",name,type);
643 svg_info=(SVGInfo *) context;
644 parser=svg_info->parser;
645 if (parser->inSubset == 1)
646 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset,
647 name,(xmlElementTypeVal) type,content);
648 else
649 if (parser->inSubset == 2)
650 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
651 name,(xmlElementTypeVal) type,content);
652}
653
654static void SVGNotationDeclaration(void *context,const xmlChar *name,
655 const xmlChar *public_id,const xmlChar *system_id)
656{
657 SVGInfo
658 *svg_info;
659
660 xmlParserCtxtPtr
661 parser;
662
663 /*
664 What to do when a notation declaration has been parsed.
665 */
666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
667 " SAX.notationDecl(%s, %s, %s)",name,
668 public_id != (const xmlChar *) NULL ? (const char *) public_id : "none",
669 system_id != (const xmlChar *) NULL ? (const char *) system_id : "none");
670 svg_info=(SVGInfo *) context;
671 parser=svg_info->parser;
672 if (parser->inSubset == 1)
673 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
674 name,public_id,system_id);
675 else
676 if (parser->inSubset == 2)
677 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
678 name,public_id,system_id);
679}
680
681static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
682 const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
683{
684 SVGInfo
685 *svg_info;
686
687 /*
688 What to do when an unparsed entity declaration is parsed.
689 */
690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
691 " SAX.unparsedEntityDecl(%s, %s, %s, %s)",name,
692 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
693 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation);
694 svg_info=(SVGInfo *) context;
695 (void) xmlAddDocEntity(svg_info->document,name,
696 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
697
698}
699
700static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
701{
702 SVGInfo
703 *svg_info;
704
705 /*
706 Receive the document locator at startup, actually xmlDefaultSAXLocator.
707 */
708 (void) location;
709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
710 " SAX.setDocumentLocator()");
711 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +0000712 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +0000713}
714
715static void SVGStartDocument(void *context)
716{
717 SVGInfo
718 *svg_info;
719
720 xmlParserCtxtPtr
721 parser;
722
723 /*
724 Called when the document start being processed.
725 */
726 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startDocument()");
727 svg_info=(SVGInfo *) context;
728 GetExceptionInfo(svg_info->exception);
729 parser=svg_info->parser;
730 svg_info->document=xmlNewDoc(parser->version);
731 if (svg_info->document == (xmlDocPtr) NULL)
732 return;
733 if (parser->encoding == NULL)
734 svg_info->document->encoding=(const xmlChar *) NULL;
735 else
736 svg_info->document->encoding=xmlStrdup(parser->encoding);
737 svg_info->document->standalone=parser->standalone;
738}
739
740static void SVGEndDocument(void *context)
741{
742 SVGInfo
743 *svg_info;
744
745 /*
746 Called when the document end has been detected.
747 */
748 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()");
749 svg_info=(SVGInfo *) context;
750 if (svg_info->offset != (char *) NULL)
751 svg_info->offset=DestroyString(svg_info->offset);
752 if (svg_info->stop_color != (char *) NULL)
753 svg_info->stop_color=DestroyString(svg_info->stop_color);
754 if (svg_info->scale != (double *) NULL)
755 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
756 if (svg_info->text != (char *) NULL)
757 svg_info->text=DestroyString(svg_info->text);
758 if (svg_info->vertices != (char *) NULL)
759 svg_info->vertices=DestroyString(svg_info->vertices);
760 if (svg_info->url != (char *) NULL)
761 svg_info->url=DestroyString(svg_info->url);
762#if defined(MAGICKCORE_XML_DELEGATE)
763 if (svg_info->document != (xmlDocPtr) NULL)
764 {
765 xmlFreeDoc(svg_info->document);
766 svg_info->document=(xmlDocPtr) NULL;
767 }
768#endif
769}
770
771static void SVGStartElement(void *context,const xmlChar *name,
772 const xmlChar **attributes)
773{
774 char
775 *color,
776 id[MaxTextExtent],
777 token[MaxTextExtent],
778 **tokens,
779 *units;
780
781 const char
782 *keyword,
783 *p,
784 *value;
785
786 int
787 number_tokens;
788
789 SVGInfo
790 *svg_info;
791
cristybb503372010-05-27 20:51:26 +0000792 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000793 i,
794 j;
795
796 /*
797 Called when an opening tag has been processed.
798 */
799 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s",
800 name);
801 svg_info=(SVGInfo *) context;
802 svg_info->n++;
803 svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale,
804 svg_info->n+1UL,sizeof(*svg_info->scale));
805 if (svg_info->scale == (double *) NULL)
806 {
807 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
808 ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
809 return;
810 }
811 svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
812 color=AcquireString("none");
813 units=AcquireString("userSpaceOnUse");
814 value=(const char *) NULL;
815 if (attributes != (const xmlChar **) NULL)
816 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
817 {
818 keyword=(const char *) attributes[i];
819 value=(const char *) attributes[i+1];
820 switch (*keyword)
821 {
822 case 'C':
823 case 'c':
824 {
825 if (LocaleCompare(keyword,"cx") == 0)
826 {
827 svg_info->element.cx=
828 GetUserSpaceCoordinateValue(svg_info,1,value);
829 break;
830 }
831 if (LocaleCompare(keyword,"cy") == 0)
832 {
833 svg_info->element.cy=
834 GetUserSpaceCoordinateValue(svg_info,-1,value);
835 break;
836 }
837 break;
838 }
839 case 'F':
840 case 'f':
841 {
842 if (LocaleCompare(keyword,"fx") == 0)
843 {
844 svg_info->element.major=
845 GetUserSpaceCoordinateValue(svg_info,1,value);
846 break;
847 }
848 if (LocaleCompare(keyword,"fy") == 0)
849 {
850 svg_info->element.minor=
851 GetUserSpaceCoordinateValue(svg_info,-1,value);
852 break;
853 }
854 break;
855 }
856 case 'H':
857 case 'h':
858 {
859 if (LocaleCompare(keyword,"height") == 0)
860 {
861 svg_info->bounds.height=
862 GetUserSpaceCoordinateValue(svg_info,-1,value);
863 break;
864 }
865 break;
866 }
867 case 'I':
868 case 'i':
869 {
870 if (LocaleCompare(keyword,"id") == 0)
871 {
872 (void) CopyMagickString(id,value,MaxTextExtent);
873 break;
874 }
875 break;
876 }
877 case 'R':
878 case 'r':
879 {
880 if (LocaleCompare(keyword,"r") == 0)
881 {
882 svg_info->element.angle=
883 GetUserSpaceCoordinateValue(svg_info,0,value);
884 break;
885 }
886 break;
887 }
888 case 'W':
889 case 'w':
890 {
891 if (LocaleCompare(keyword,"width") == 0)
892 {
893 svg_info->bounds.width=
894 GetUserSpaceCoordinateValue(svg_info,1,value);
895 break;
896 }
897 break;
898 }
899 case 'X':
900 case 'x':
901 {
902 if (LocaleCompare(keyword,"x") == 0)
903 {
904 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value)-
905 svg_info->center.x;
906 break;
907 }
908 if (LocaleCompare(keyword,"x1") == 0)
909 {
910 svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
911 value);
912 break;
913 }
914 if (LocaleCompare(keyword,"x2") == 0)
915 {
916 svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
917 value);
918 break;
919 }
920 break;
921 }
922 case 'Y':
923 case 'y':
924 {
925 if (LocaleCompare(keyword,"y") == 0)
926 {
927 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value)-
928 svg_info->center.y;
929 break;
930 }
931 if (LocaleCompare(keyword,"y1") == 0)
932 {
933 svg_info->segment.y1=
934 GetUserSpaceCoordinateValue(svg_info,-1,value);
935 break;
936 }
937 if (LocaleCompare(keyword,"y2") == 0)
938 {
939 svg_info->segment.y2=
940 GetUserSpaceCoordinateValue(svg_info,-1,value);
941 break;
942 }
943 break;
944 }
945 default:
946 break;
947 }
948 }
cristy13d07042010-11-21 20:56:18 +0000949 if (strchr((char *) name,':') != (char *) NULL)
cristy768b9302010-11-20 00:05:47 +0000950 {
951 /*
952 Skip over namespace.
953 */
954 for ( ; *name != ':'; name++) ;
955 name++;
956 }
cristy3ed852e2009-09-05 21:47:34 +0000957 switch (*name)
958 {
959 case 'C':
960 case 'c':
961 {
962 if (LocaleCompare((const char *) name,"circle") == 0)
963 {
cristyb51dff52011-05-19 16:55:47 +0000964 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +0000965 break;
966 }
967 if (LocaleCompare((const char *) name,"clipPath") == 0)
968 {
cristyb51dff52011-05-19 16:55:47 +0000969 (void) FormatLocaleFile(svg_info->file,"push clip-path '%s'\n",id);
cristy3ed852e2009-09-05 21:47:34 +0000970 break;
971 }
972 break;
973 }
974 case 'D':
975 case 'd':
976 {
977 if (LocaleCompare((const char *) name,"defs") == 0)
978 {
cristyb51dff52011-05-19 16:55:47 +0000979 (void) FormatLocaleFile(svg_info->file,"push defs\n");
cristy3ed852e2009-09-05 21:47:34 +0000980 break;
981 }
982 break;
983 }
984 case 'E':
985 case 'e':
986 {
987 if (LocaleCompare((const char *) name,"ellipse") == 0)
988 {
cristyb51dff52011-05-19 16:55:47 +0000989 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +0000990 break;
991 }
992 break;
993 }
994 case 'G':
995 case 'g':
996 {
997 if (LocaleCompare((const char *) name,"g") == 0)
998 {
cristyb51dff52011-05-19 16:55:47 +0000999 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001000 break;
1001 }
1002 break;
1003 }
1004 case 'I':
1005 case 'i':
1006 {
1007 if (LocaleCompare((const char *) name,"image") == 0)
1008 {
cristyb51dff52011-05-19 16:55:47 +00001009 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001010 break;
1011 }
1012 break;
1013 }
1014 case 'L':
1015 case 'l':
1016 {
1017 if (LocaleCompare((const char *) name,"line") == 0)
1018 {
cristyb51dff52011-05-19 16:55:47 +00001019 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001020 break;
1021 }
1022 if (LocaleCompare((const char *) name,"linearGradient") == 0)
1023 {
cristyb51dff52011-05-19 16:55:47 +00001024 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001025 "push gradient '%s' linear %g,%g %g,%g\n",id,
cristy3ed852e2009-09-05 21:47:34 +00001026 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
1027 svg_info->segment.y2);
1028 break;
1029 }
1030 break;
1031 }
1032 case 'P':
1033 case 'p':
1034 {
1035 if (LocaleCompare((const char *) name,"path") == 0)
1036 {
cristyb51dff52011-05-19 16:55:47 +00001037 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001038 break;
1039 }
1040 if (LocaleCompare((const char *) name,"pattern") == 0)
1041 {
cristyb51dff52011-05-19 16:55:47 +00001042 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001043 "push pattern '%s' %g,%g %g,%g\n",id,
cristy3ed852e2009-09-05 21:47:34 +00001044 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
1045 svg_info->bounds.height);
1046 break;
1047 }
1048 if (LocaleCompare((const char *) name,"polygon") == 0)
1049 {
cristyb51dff52011-05-19 16:55:47 +00001050 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001051 break;
1052 }
1053 if (LocaleCompare((const char *) name,"polyline") == 0)
1054 {
cristyb51dff52011-05-19 16:55:47 +00001055 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001056 break;
1057 }
1058 break;
1059 }
1060 case 'R':
1061 case 'r':
1062 {
1063 if (LocaleCompare((const char *) name,"radialGradient") == 0)
1064 {
cristyb51dff52011-05-19 16:55:47 +00001065 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001066 "push gradient '%s' radial %g,%g %g,%g %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001067 id,svg_info->element.cx,svg_info->element.cy,
1068 svg_info->element.major,svg_info->element.minor,
1069 svg_info->element.angle);
1070 break;
1071 }
1072 if (LocaleCompare((const char *) name,"rect") == 0)
1073 {
cristyb51dff52011-05-19 16:55:47 +00001074 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001075 break;
1076 }
1077 break;
1078 }
1079 case 'S':
1080 case 's':
1081 {
1082 if (LocaleCompare((const char *) name,"svg") == 0)
1083 {
cristyb51dff52011-05-19 16:55:47 +00001084 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001085 break;
1086 }
1087 break;
1088 }
1089 case 'T':
1090 case 't':
1091 {
1092 if (LocaleCompare((const char *) name,"text") == 0)
1093 {
cristyb51dff52011-05-19 16:55:47 +00001094 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001095 break;
1096 }
1097 if (LocaleCompare((const char *) name,"tspan") == 0)
1098 {
1099 if (*svg_info->text != '\0')
1100 {
1101 DrawInfo
1102 *draw_info;
1103
1104 TypeMetric
1105 metrics;
1106
1107 char
1108 *text;
1109
1110 text=EscapeString(svg_info->text,'\'');
cristyb51dff52011-05-19 16:55:47 +00001111 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
cristy8cd5b312010-01-07 01:10:24 +00001112 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1113 svg_info->center.y,text);
cristy3ed852e2009-09-05 21:47:34 +00001114 text=DestroyString(text);
1115 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
1116 draw_info->pointsize=svg_info->pointsize;
1117 draw_info->text=AcquireString(svg_info->text);
1118 (void) ConcatenateString(&draw_info->text," ");
cristy5cbc0162011-08-29 00:36:28 +00001119 (void) GetTypeMetrics(svg_info->image,draw_info,
1120 &metrics,svg_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001121 svg_info->bounds.x+=metrics.width;
1122 draw_info=DestroyDrawInfo(draw_info);
1123 *svg_info->text='\0';
1124 }
cristyb51dff52011-05-19 16:55:47 +00001125 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001126 break;
1127 }
1128 break;
1129 }
1130 default:
1131 break;
1132 }
1133 if (attributes != (const xmlChar **) NULL)
1134 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1135 {
1136 keyword=(const char *) attributes[i];
1137 value=(const char *) attributes[i+1];
1138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1139 " %s = %s",keyword,value);
1140 switch (*keyword)
1141 {
1142 case 'A':
1143 case 'a':
1144 {
1145 if (LocaleCompare(keyword,"angle") == 0)
1146 {
cristyb51dff52011-05-19 16:55:47 +00001147 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001148 GetUserSpaceCoordinateValue(svg_info,0,value));
1149 break;
1150 }
1151 break;
1152 }
1153 case 'C':
1154 case 'c':
1155 {
1156 if (LocaleCompare(keyword,"clip-path") == 0)
1157 {
cristyb51dff52011-05-19 16:55:47 +00001158 (void) FormatLocaleFile(svg_info->file,"clip-path '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001159 break;
1160 }
1161 if (LocaleCompare(keyword,"clip-rule") == 0)
1162 {
cristyb51dff52011-05-19 16:55:47 +00001163 (void) FormatLocaleFile(svg_info->file,"clip-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001164 break;
1165 }
1166 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1167 {
1168 (void) CloneString(&units,value);
cristyb51dff52011-05-19 16:55:47 +00001169 (void) FormatLocaleFile(svg_info->file,"clip-units '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001170 break;
1171 }
1172 if (LocaleCompare(keyword,"color") == 0)
1173 {
1174 (void) CloneString(&color,value);
1175 break;
1176 }
1177 if (LocaleCompare(keyword,"cx") == 0)
1178 {
1179 svg_info->element.cx=
1180 GetUserSpaceCoordinateValue(svg_info,1,value);
1181 break;
1182 }
1183 if (LocaleCompare(keyword,"cy") == 0)
1184 {
1185 svg_info->element.cy=
1186 GetUserSpaceCoordinateValue(svg_info,-1,value);
1187 break;
1188 }
1189 break;
1190 }
1191 case 'D':
1192 case 'd':
1193 {
1194 if (LocaleCompare(keyword,"d") == 0)
1195 {
1196 (void) CloneString(&svg_info->vertices,value);
1197 break;
1198 }
1199 if (LocaleCompare(keyword,"dx") == 0)
1200 {
1201 svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
1202 break;
1203 }
1204 if (LocaleCompare(keyword,"dy") == 0)
1205 {
1206 svg_info->bounds.y+=
1207 GetUserSpaceCoordinateValue(svg_info,-1,value);
1208 break;
1209 }
1210 break;
1211 }
1212 case 'F':
1213 case 'f':
1214 {
1215 if (LocaleCompare(keyword,"fill") == 0)
1216 {
1217 if (LocaleCompare(value,"currentColor") == 0)
1218 {
cristyb51dff52011-05-19 16:55:47 +00001219 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001220 break;
1221 }
cristyb51dff52011-05-19 16:55:47 +00001222 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001223 break;
1224 }
1225 if (LocaleCompare(keyword,"fillcolor") == 0)
1226 {
cristyb51dff52011-05-19 16:55:47 +00001227 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001228 break;
1229 }
1230 if (LocaleCompare(keyword,"fill-rule") == 0)
1231 {
cristyb51dff52011-05-19 16:55:47 +00001232 (void) FormatLocaleFile(svg_info->file,"fill-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001233 break;
1234 }
1235 if (LocaleCompare(keyword,"fill-opacity") == 0)
1236 {
cristyb51dff52011-05-19 16:55:47 +00001237 (void) FormatLocaleFile(svg_info->file,"fill-opacity '%s'\n",
1238 value);
cristy3ed852e2009-09-05 21:47:34 +00001239 break;
1240 }
1241 if (LocaleCompare(keyword,"font-family") == 0)
1242 {
cristyb51dff52011-05-19 16:55:47 +00001243 (void) FormatLocaleFile(svg_info->file,"font-family '%s'\n",
1244 value);
cristy3ed852e2009-09-05 21:47:34 +00001245 break;
1246 }
1247 if (LocaleCompare(keyword,"font-stretch") == 0)
1248 {
cristyb51dff52011-05-19 16:55:47 +00001249 (void) FormatLocaleFile(svg_info->file,"font-stretch '%s'\n",
1250 value);
cristy3ed852e2009-09-05 21:47:34 +00001251 break;
1252 }
1253 if (LocaleCompare(keyword,"font-style") == 0)
1254 {
cristyb51dff52011-05-19 16:55:47 +00001255 (void) FormatLocaleFile(svg_info->file,"font-style '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001256 break;
1257 }
1258 if (LocaleCompare(keyword,"font-size") == 0)
1259 {
1260 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
cristyb51dff52011-05-19 16:55:47 +00001261 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1262 svg_info->pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001263 break;
1264 }
1265 if (LocaleCompare(keyword,"font-weight") == 0)
1266 {
cristyb51dff52011-05-19 16:55:47 +00001267 (void) FormatLocaleFile(svg_info->file,"font-weight '%s'\n",
1268 value);
cristy3ed852e2009-09-05 21:47:34 +00001269 break;
1270 }
1271 break;
1272 }
1273 case 'G':
1274 case 'g':
1275 {
1276 if (LocaleCompare(keyword,"gradientTransform") == 0)
1277 {
1278 AffineMatrix
1279 affine,
1280 current,
1281 transform;
1282
1283 GetAffineMatrix(&transform);
1284 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1285 tokens=GetTransformTokens(context,value,&number_tokens);
1286 for (j=0; j < (number_tokens-1); j+=2)
1287 {
1288 keyword=(char *) tokens[j];
1289 if (keyword == (char *) NULL)
1290 continue;
1291 value=(char *) tokens[j+1];
1292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1293 " %s: %s",keyword,value);
1294 current=transform;
1295 GetAffineMatrix(&affine);
1296 switch (*keyword)
1297 {
1298 case 'M':
1299 case 'm':
1300 {
1301 if (LocaleCompare(keyword,"matrix") == 0)
1302 {
1303 p=(const char *) value;
1304 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001305 affine.sx=InterpretLocaleValue(value,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001306 GetMagickToken(p,&p,token);
1307 if (*token == ',')
1308 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001309 affine.rx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001310 GetMagickToken(p,&p,token);
1311 if (*token == ',')
1312 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001313 affine.ry=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001314 GetMagickToken(p,&p,token);
1315 if (*token == ',')
1316 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001317 affine.sy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001318 GetMagickToken(p,&p,token);
1319 if (*token == ',')
1320 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001321 affine.tx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001322 GetMagickToken(p,&p,token);
1323 if (*token == ',')
1324 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001325 affine.ty=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001326 break;
1327 }
1328 break;
1329 }
1330 case 'R':
1331 case 'r':
1332 {
1333 if (LocaleCompare(keyword,"rotate") == 0)
1334 {
1335 double
1336 angle;
1337
1338 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1339 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1340 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1341 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1342 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1343 break;
1344 }
1345 break;
1346 }
1347 case 'S':
1348 case 's':
1349 {
1350 if (LocaleCompare(keyword,"scale") == 0)
1351 {
1352 for (p=(const char *) value; *p != '\0'; p++)
1353 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1354 (*p == ','))
1355 break;
1356 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1357 affine.sy=affine.sx;
1358 if (*p != '\0')
1359 affine.sy=
1360 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1361 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1362 break;
1363 }
1364 if (LocaleCompare(keyword,"skewX") == 0)
1365 {
1366 affine.sx=svg_info->affine.sx;
1367 affine.ry=tan(DegreesToRadians(fmod(
1368 GetUserSpaceCoordinateValue(svg_info,1,value),
1369 360.0)));
1370 affine.sy=svg_info->affine.sy;
1371 break;
1372 }
1373 if (LocaleCompare(keyword,"skewY") == 0)
1374 {
1375 affine.sx=svg_info->affine.sx;
1376 affine.rx=tan(DegreesToRadians(fmod(
1377 GetUserSpaceCoordinateValue(svg_info,-1,value),
1378 360.0)));
1379 affine.sy=svg_info->affine.sy;
1380 break;
1381 }
1382 break;
1383 }
1384 case 'T':
1385 case 't':
1386 {
1387 if (LocaleCompare(keyword,"translate") == 0)
1388 {
1389 for (p=(const char *) value; *p != '\0'; p++)
1390 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1391 (*p == ','))
1392 break;
1393 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1394 affine.ty=affine.tx;
1395 if (*p != '\0')
1396 affine.ty=
1397 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1398 break;
1399 }
1400 break;
1401 }
1402 default:
1403 break;
1404 }
cristyef7c8a52010-10-10 13:46:51 +00001405 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1406 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1407 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1408 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1409 transform.tx=affine.sx*current.tx+affine.ry*current.ty+
1410 affine.tx;
1411 transform.ty=affine.rx*current.tx+affine.sy*current.ty+
1412 affine.ty;
cristy3ed852e2009-09-05 21:47:34 +00001413 }
cristyb51dff52011-05-19 16:55:47 +00001414 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001415 "affine %g %g %g %g %g %g\n",transform.sx,
cristy8cd5b312010-01-07 01:10:24 +00001416 transform.rx,transform.ry,transform.sy,transform.tx,
1417 transform.ty);
cristy3ed852e2009-09-05 21:47:34 +00001418 for (j=0; tokens[j] != (char *) NULL; j++)
1419 tokens[j]=DestroyString(tokens[j]);
1420 tokens=(char **) RelinquishMagickMemory(tokens);
1421 break;
1422 }
1423 if (LocaleCompare(keyword,"gradientUnits") == 0)
1424 {
1425 (void) CloneString(&units,value);
cristyb51dff52011-05-19 16:55:47 +00001426 (void) FormatLocaleFile(svg_info->file,"gradient-units '%s'\n",
1427 value);
cristy3ed852e2009-09-05 21:47:34 +00001428 break;
1429 }
1430 break;
1431 }
1432 case 'H':
1433 case 'h':
1434 {
1435 if (LocaleCompare(keyword,"height") == 0)
1436 {
1437 svg_info->bounds.height=
1438 GetUserSpaceCoordinateValue(svg_info,-1,value);
1439 break;
1440 }
1441 if (LocaleCompare(keyword,"href") == 0)
1442 {
1443 (void) CloneString(&svg_info->url,value);
1444 break;
1445 }
1446 break;
1447 }
1448 case 'M':
1449 case 'm':
1450 {
1451 if (LocaleCompare(keyword,"major") == 0)
1452 {
1453 svg_info->element.major=
1454 GetUserSpaceCoordinateValue(svg_info,1,value);
1455 break;
1456 }
1457 if (LocaleCompare(keyword,"minor") == 0)
1458 {
1459 svg_info->element.minor=
1460 GetUserSpaceCoordinateValue(svg_info,-1,value);
1461 break;
1462 }
1463 break;
1464 }
1465 case 'O':
1466 case 'o':
1467 {
1468 if (LocaleCompare(keyword,"offset") == 0)
1469 {
1470 (void) CloneString(&svg_info->offset,value);
1471 break;
1472 }
1473 if (LocaleCompare(keyword,"opacity") == 0)
1474 {
cristyb51dff52011-05-19 16:55:47 +00001475 (void) FormatLocaleFile(svg_info->file,"opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001476 break;
1477 }
1478 break;
1479 }
1480 case 'P':
1481 case 'p':
1482 {
1483 if (LocaleCompare(keyword,"path") == 0)
1484 {
1485 (void) CloneString(&svg_info->url,value);
1486 break;
1487 }
1488 if (LocaleCompare(keyword,"points") == 0)
1489 {
1490 (void) CloneString(&svg_info->vertices,value);
1491 break;
1492 }
1493 break;
1494 }
1495 case 'R':
1496 case 'r':
1497 {
1498 if (LocaleCompare(keyword,"r") == 0)
1499 {
1500 svg_info->element.major=
1501 GetUserSpaceCoordinateValue(svg_info,1,value);
1502 svg_info->element.minor=
1503 GetUserSpaceCoordinateValue(svg_info,-1,value);
1504 break;
1505 }
1506 if (LocaleCompare(keyword,"rotate") == 0)
1507 {
1508 double
1509 angle;
1510
1511 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
cristyb51dff52011-05-19 16:55:47 +00001512 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
cristy8cd5b312010-01-07 01:10:24 +00001513 svg_info->bounds.x,svg_info->bounds.y);
cristy3ed852e2009-09-05 21:47:34 +00001514 svg_info->bounds.x=0;
1515 svg_info->bounds.y=0;
cristyb51dff52011-05-19 16:55:47 +00001516 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
cristy3ed852e2009-09-05 21:47:34 +00001517 break;
1518 }
1519 if (LocaleCompare(keyword,"rx") == 0)
1520 {
1521 if (LocaleCompare((const char *) name,"ellipse") == 0)
1522 svg_info->element.major=
1523 GetUserSpaceCoordinateValue(svg_info,1,value);
1524 else
1525 svg_info->radius.x=
1526 GetUserSpaceCoordinateValue(svg_info,1,value);
1527 break;
1528 }
1529 if (LocaleCompare(keyword,"ry") == 0)
1530 {
1531 if (LocaleCompare((const char *) name,"ellipse") == 0)
1532 svg_info->element.minor=
1533 GetUserSpaceCoordinateValue(svg_info,-1,value);
1534 else
1535 svg_info->radius.y=
1536 GetUserSpaceCoordinateValue(svg_info,-1,value);
1537 break;
1538 }
1539 break;
1540 }
1541 case 'S':
1542 case 's':
1543 {
1544 if (LocaleCompare(keyword,"stop-color") == 0)
1545 {
1546 (void) CloneString(&svg_info->stop_color,value);
1547 break;
1548 }
1549 if (LocaleCompare(keyword,"stroke") == 0)
1550 {
1551 if (LocaleCompare(value,"currentColor") == 0)
1552 {
cristyb51dff52011-05-19 16:55:47 +00001553 (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001554 break;
1555 }
cristyb51dff52011-05-19 16:55:47 +00001556 (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001557 break;
1558 }
1559 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1560 {
cristyb51dff52011-05-19 16:55:47 +00001561 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001562 LocaleCompare(value,"true") == 0);
1563 break;
1564 }
1565 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1566 {
cristyb51dff52011-05-19 16:55:47 +00001567 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1568 value);
cristy3ed852e2009-09-05 21:47:34 +00001569 break;
1570 }
1571 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1572 {
cristyb51dff52011-05-19 16:55:47 +00001573 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %s\n",
1574 value);
cristy3ed852e2009-09-05 21:47:34 +00001575 break;
1576 }
1577 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1578 {
cristyb51dff52011-05-19 16:55:47 +00001579 (void) FormatLocaleFile(svg_info->file,"stroke-linecap '%s'\n",
1580 value);
cristy3ed852e2009-09-05 21:47:34 +00001581 break;
1582 }
1583 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1584 {
cristyb51dff52011-05-19 16:55:47 +00001585 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin '%s'\n",
1586 value);
cristy3ed852e2009-09-05 21:47:34 +00001587 break;
1588 }
1589 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1590 {
cristyb51dff52011-05-19 16:55:47 +00001591 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit '%s'\n",
1592 value);
cristy3ed852e2009-09-05 21:47:34 +00001593 break;
1594 }
1595 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1596 {
cristyb51dff52011-05-19 16:55:47 +00001597 (void) FormatLocaleFile(svg_info->file,"stroke-opacity '%s'\n",
1598 value);
cristy3ed852e2009-09-05 21:47:34 +00001599 break;
1600 }
1601 if (LocaleCompare(keyword,"stroke-width") == 0)
1602 {
cristyb51dff52011-05-19 16:55:47 +00001603 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001604 GetUserSpaceCoordinateValue(svg_info,1,value));
1605 break;
1606 }
1607 if (LocaleCompare(keyword,"style") == 0)
1608 {
1609 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1610 tokens=GetStyleTokens(context,value,&number_tokens);
1611 for (j=0; j < (number_tokens-1); j+=2)
1612 {
1613 keyword=(char *) tokens[j];
1614 value=(char *) tokens[j+1];
1615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1616 " %s: %s",keyword,value);
1617 switch (*keyword)
1618 {
1619 case 'C':
1620 case 'c':
1621 {
1622 if (LocaleCompare(keyword,"clip-path") == 0)
1623 {
cristyb51dff52011-05-19 16:55:47 +00001624 (void) FormatLocaleFile(svg_info->file,
1625 "clip-path '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001626 break;
1627 }
1628 if (LocaleCompare(keyword,"clip-rule") == 0)
1629 {
cristyb51dff52011-05-19 16:55:47 +00001630 (void) FormatLocaleFile(svg_info->file,
1631 "clip-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001632 break;
1633 }
1634 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1635 {
1636 (void) CloneString(&units,value);
cristyb51dff52011-05-19 16:55:47 +00001637 (void) FormatLocaleFile(svg_info->file,
1638 "clip-units '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001639 break;
1640 }
1641 if (LocaleCompare(keyword,"color") == 0)
1642 {
1643 (void) CloneString(&color,value);
1644 break;
1645 }
1646 break;
1647 }
1648 case 'F':
1649 case 'f':
1650 {
1651 if (LocaleCompare(keyword,"fill") == 0)
1652 {
1653 if (LocaleCompare(value,"currentColor") == 0)
1654 {
cristyb51dff52011-05-19 16:55:47 +00001655 (void) FormatLocaleFile(svg_info->file,
1656 "fill '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001657 break;
1658 }
1659 if (LocaleCompare(value,"#00000000") == 0)
cristyb51dff52011-05-19 16:55:47 +00001660 (void) FormatLocaleFile(svg_info->file,
1661 "fill '#000000'\n");
cristy3ed852e2009-09-05 21:47:34 +00001662 else
cristyb51dff52011-05-19 16:55:47 +00001663 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1664 value);
cristy3ed852e2009-09-05 21:47:34 +00001665 break;
1666 }
1667 if (LocaleCompare(keyword,"fillcolor") == 0)
1668 {
cristyb51dff52011-05-19 16:55:47 +00001669 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1670 value);
cristy3ed852e2009-09-05 21:47:34 +00001671 break;
1672 }
1673 if (LocaleCompare(keyword,"fill-rule") == 0)
1674 {
cristyb51dff52011-05-19 16:55:47 +00001675 (void) FormatLocaleFile(svg_info->file,
1676 "fill-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001677 break;
1678 }
1679 if (LocaleCompare(keyword,"fill-opacity") == 0)
1680 {
cristyb51dff52011-05-19 16:55:47 +00001681 (void) FormatLocaleFile(svg_info->file,
1682 "fill-opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001683 break;
1684 }
1685 if (LocaleCompare(keyword,"font-family") == 0)
1686 {
cristyb51dff52011-05-19 16:55:47 +00001687 (void) FormatLocaleFile(svg_info->file,
1688 "font-family '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001689 break;
1690 }
1691 if (LocaleCompare(keyword,"font-stretch") == 0)
1692 {
cristyb51dff52011-05-19 16:55:47 +00001693 (void) FormatLocaleFile(svg_info->file,
1694 "font-stretch '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001695 break;
1696 }
1697 if (LocaleCompare(keyword,"font-style") == 0)
1698 {
cristyb51dff52011-05-19 16:55:47 +00001699 (void) FormatLocaleFile(svg_info->file,
1700 "font-style '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001701 break;
1702 }
1703 if (LocaleCompare(keyword,"font-size") == 0)
1704 {
1705 svg_info->pointsize=GetUserSpaceCoordinateValue(
1706 svg_info,0,value);
cristyb51dff52011-05-19 16:55:47 +00001707 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001708 svg_info->pointsize);
1709 break;
1710 }
1711 if (LocaleCompare(keyword,"font-weight") == 0)
1712 {
cristyb51dff52011-05-19 16:55:47 +00001713 (void) FormatLocaleFile(svg_info->file,
1714 "font-weight '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001715 break;
1716 }
1717 break;
1718 }
1719 case 'O':
1720 case 'o':
1721 {
1722 if (LocaleCompare(keyword,"offset") == 0)
1723 {
cristyb51dff52011-05-19 16:55:47 +00001724 (void) FormatLocaleFile(svg_info->file,"offset %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001725 GetUserSpaceCoordinateValue(svg_info,1,value));
1726 break;
1727 }
1728 if (LocaleCompare(keyword,"opacity") == 0)
1729 {
cristyb51dff52011-05-19 16:55:47 +00001730 (void) FormatLocaleFile(svg_info->file,
1731 "opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001732 break;
1733 }
1734 break;
1735 }
1736 case 'S':
1737 case 's':
1738 {
1739 if (LocaleCompare(keyword,"stop-color") == 0)
1740 {
1741 (void) CloneString(&svg_info->stop_color,value);
1742 break;
1743 }
1744 if (LocaleCompare(keyword,"stroke") == 0)
1745 {
1746 if (LocaleCompare(value,"currentColor") == 0)
1747 {
cristyb51dff52011-05-19 16:55:47 +00001748 (void) FormatLocaleFile(svg_info->file,
1749 "stroke '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001750 break;
1751 }
1752 if (LocaleCompare(value,"#00000000") == 0)
cristyb51dff52011-05-19 16:55:47 +00001753 (void) FormatLocaleFile(svg_info->file,
1754 "fill '#000000'\n");
cristy3ed852e2009-09-05 21:47:34 +00001755 else
cristyb51dff52011-05-19 16:55:47 +00001756 (void) FormatLocaleFile(svg_info->file,
1757 "stroke '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001758 break;
1759 }
1760 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1761 {
cristyb51dff52011-05-19 16:55:47 +00001762 (void) FormatLocaleFile(svg_info->file,
1763 "stroke-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001764 LocaleCompare(value,"true") == 0);
1765 break;
1766 }
1767 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1768 {
cristyb51dff52011-05-19 16:55:47 +00001769 (void) FormatLocaleFile(svg_info->file,
1770 "stroke-dasharray %s\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001771 break;
1772 }
1773 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1774 {
cristyb51dff52011-05-19 16:55:47 +00001775 (void) FormatLocaleFile(svg_info->file,
1776 "stroke-dashoffset %s\n",
cristy3ed852e2009-09-05 21:47:34 +00001777 value);
1778 break;
1779 }
1780 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1781 {
cristyb51dff52011-05-19 16:55:47 +00001782 (void) FormatLocaleFile(svg_info->file,
1783 "stroke-linecap '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001784 break;
1785 }
1786 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1787 {
cristyb51dff52011-05-19 16:55:47 +00001788 (void) FormatLocaleFile(svg_info->file,
1789 "stroke-linejoin '%s'\n",
cristy3ed852e2009-09-05 21:47:34 +00001790 value);
1791 break;
1792 }
1793 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1794 {
cristyb51dff52011-05-19 16:55:47 +00001795 (void) FormatLocaleFile(svg_info->file,
1796 "stroke-miterlimit '%s'\n",
cristy3ed852e2009-09-05 21:47:34 +00001797 value);
1798 break;
1799 }
1800 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1801 {
cristyb51dff52011-05-19 16:55:47 +00001802 (void) FormatLocaleFile(svg_info->file,
1803 "stroke-opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001804 break;
1805 }
1806 if (LocaleCompare(keyword,"stroke-width") == 0)
1807 {
cristyb51dff52011-05-19 16:55:47 +00001808 (void) FormatLocaleFile(svg_info->file,
1809 "stroke-width %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001810 GetUserSpaceCoordinateValue(svg_info,1,value));
1811 break;
1812 }
1813 break;
1814 }
1815 case 't':
1816 case 'T':
1817 {
1818 if (LocaleCompare(keyword,"text-align") == 0)
1819 {
cristyb51dff52011-05-19 16:55:47 +00001820 (void) FormatLocaleFile(svg_info->file,
1821 "text-align '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001822 break;
1823 }
1824 if (LocaleCompare(keyword,"text-anchor") == 0)
1825 {
cristyb51dff52011-05-19 16:55:47 +00001826 (void) FormatLocaleFile(svg_info->file,
1827 "text-anchor '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001828 break;
1829 }
1830 if (LocaleCompare(keyword,"text-decoration") == 0)
1831 {
1832 if (LocaleCompare(value,"underline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001833 (void) FormatLocaleFile(svg_info->file,
1834 "decorate underline\n");
cristy3ed852e2009-09-05 21:47:34 +00001835 if (LocaleCompare(value,"line-through") == 0)
cristyb51dff52011-05-19 16:55:47 +00001836 (void) FormatLocaleFile(svg_info->file,
1837 "decorate line-through\n");
cristy3ed852e2009-09-05 21:47:34 +00001838 if (LocaleCompare(value,"overline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001839 (void) FormatLocaleFile(svg_info->file,
1840 "decorate overline\n");
cristy3ed852e2009-09-05 21:47:34 +00001841 break;
1842 }
1843 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1844 {
cristyb51dff52011-05-19 16:55:47 +00001845 (void) FormatLocaleFile(svg_info->file,
1846 "text-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001847 LocaleCompare(value,"true") == 0);
1848 break;
1849 }
1850 break;
1851 }
1852 default:
1853 break;
1854 }
1855 }
1856 for (j=0; tokens[j] != (char *) NULL; j++)
1857 tokens[j]=DestroyString(tokens[j]);
1858 tokens=(char **) RelinquishMagickMemory(tokens);
1859 break;
1860 }
1861 break;
1862 }
1863 case 'T':
1864 case 't':
1865 {
1866 if (LocaleCompare(keyword,"text-align") == 0)
1867 {
cristyb51dff52011-05-19 16:55:47 +00001868 (void) FormatLocaleFile(svg_info->file,"text-align '%s'\n",
1869 value);
cristy3ed852e2009-09-05 21:47:34 +00001870 break;
1871 }
1872 if (LocaleCompare(keyword,"text-anchor") == 0)
1873 {
cristyb51dff52011-05-19 16:55:47 +00001874 (void) FormatLocaleFile(svg_info->file,"text-anchor '%s'\n",
1875 value);
cristy3ed852e2009-09-05 21:47:34 +00001876 break;
1877 }
1878 if (LocaleCompare(keyword,"text-decoration") == 0)
1879 {
1880 if (LocaleCompare(value,"underline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001881 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
cristy3ed852e2009-09-05 21:47:34 +00001882 if (LocaleCompare(value,"line-through") == 0)
cristyb51dff52011-05-19 16:55:47 +00001883 (void) FormatLocaleFile(svg_info->file,
1884 "decorate line-through\n");
cristy3ed852e2009-09-05 21:47:34 +00001885 if (LocaleCompare(value,"overline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001886 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
cristy3ed852e2009-09-05 21:47:34 +00001887 break;
1888 }
1889 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1890 {
cristyb51dff52011-05-19 16:55:47 +00001891 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001892 LocaleCompare(value,"true") == 0);
1893 break;
1894 }
1895 if (LocaleCompare(keyword,"transform") == 0)
1896 {
1897 AffineMatrix
1898 affine,
1899 current,
1900 transform;
1901
1902 GetAffineMatrix(&transform);
1903 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1904 tokens=GetTransformTokens(context,value,&number_tokens);
1905 for (j=0; j < (number_tokens-1); j+=2)
1906 {
1907 keyword=(char *) tokens[j];
1908 value=(char *) tokens[j+1];
1909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1910 " %s: %s",keyword,value);
1911 current=transform;
1912 GetAffineMatrix(&affine);
1913 switch (*keyword)
1914 {
1915 case 'M':
1916 case 'm':
1917 {
1918 if (LocaleCompare(keyword,"matrix") == 0)
1919 {
1920 p=(const char *) value;
1921 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001922 affine.sx=InterpretLocaleValue(value,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001923 GetMagickToken(p,&p,token);
1924 if (*token == ',')
1925 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001926 affine.rx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001927 GetMagickToken(p,&p,token);
1928 if (*token == ',')
1929 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001930 affine.ry=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001931 GetMagickToken(p,&p,token);
1932 if (*token == ',')
1933 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001934 affine.sy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001935 GetMagickToken(p,&p,token);
1936 if (*token == ',')
1937 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001938 affine.tx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001939 GetMagickToken(p,&p,token);
1940 if (*token == ',')
1941 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001942 affine.ty=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001943 break;
1944 }
1945 break;
1946 }
1947 case 'R':
1948 case 'r':
1949 {
1950 if (LocaleCompare(keyword,"rotate") == 0)
1951 {
1952 double
1953 angle,
1954 x,
1955 y;
1956
1957 p=(const char *) value;
1958 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001959 angle=InterpretLocaleValue(value,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001960 GetMagickToken(p,&p,token);
1961 if (*token == ',')
1962 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001963 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001964 GetMagickToken(p,&p,token);
1965 if (*token == ',')
1966 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001967 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001968 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1969 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1970 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1971 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1972 affine.tx=x;
1973 affine.ty=y;
1974 svg_info->center.x=x;
1975 svg_info->center.y=y;
1976 break;
1977 }
1978 break;
1979 }
1980 case 'S':
1981 case 's':
1982 {
1983 if (LocaleCompare(keyword,"scale") == 0)
1984 {
1985 for (p=(const char *) value; *p != '\0'; p++)
1986 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1987 (*p == ','))
1988 break;
1989 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1990 affine.sy=affine.sx;
1991 if (*p != '\0')
1992 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
1993 p+1);
1994 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1995 break;
1996 }
1997 if (LocaleCompare(keyword,"skewX") == 0)
1998 {
1999 affine.sx=svg_info->affine.sx;
2000 affine.ry=tan(DegreesToRadians(fmod(
2001 GetUserSpaceCoordinateValue(svg_info,1,value),
2002 360.0)));
2003 affine.sy=svg_info->affine.sy;
2004 break;
2005 }
2006 if (LocaleCompare(keyword,"skewY") == 0)
2007 {
2008 affine.sx=svg_info->affine.sx;
2009 affine.rx=tan(DegreesToRadians(fmod(
2010 GetUserSpaceCoordinateValue(svg_info,-1,value),
2011 360.0)));
2012 affine.sy=svg_info->affine.sy;
2013 break;
2014 }
2015 break;
2016 }
2017 case 'T':
2018 case 't':
2019 {
2020 if (LocaleCompare(keyword,"translate") == 0)
2021 {
2022 for (p=(const char *) value; *p != '\0'; p++)
2023 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2024 (*p == ','))
2025 break;
2026 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2027 affine.ty=affine.tx;
2028 if (*p != '\0')
2029 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2030 p+1);
2031 break;
2032 }
2033 break;
2034 }
2035 default:
2036 break;
2037 }
cristyef7c8a52010-10-10 13:46:51 +00002038 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2039 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2040 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2041 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2042 transform.tx=affine.sx*current.tx+affine.ry*current.ty+
2043 affine.tx;
2044 transform.ty=affine.rx*current.tx+affine.sy*current.ty+
2045 affine.ty;
cristy3ed852e2009-09-05 21:47:34 +00002046 }
cristyb51dff52011-05-19 16:55:47 +00002047 (void) FormatLocaleFile(svg_info->file,
cristy88158062011-06-09 00:31:43 +00002048 "affine %g %g %g %g 0.0 0.0\n",transform.sx,transform.rx,
2049 transform.ry,transform.sy);
cristy3ed852e2009-09-05 21:47:34 +00002050 for (j=0; tokens[j] != (char *) NULL; j++)
2051 tokens[j]=DestroyString(tokens[j]);
2052 tokens=(char **) RelinquishMagickMemory(tokens);
2053 break;
2054 }
2055 break;
2056 }
2057 case 'V':
2058 case 'v':
2059 {
2060 if (LocaleCompare(keyword,"verts") == 0)
2061 {
2062 (void) CloneString(&svg_info->vertices,value);
2063 break;
2064 }
2065 if (LocaleCompare(keyword,"viewBox") == 0)
2066 {
2067 p=(const char *) value;
2068 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00002069 svg_info->view_box.x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002070 GetMagickToken(p,&p,token);
2071 if (*token == ',')
2072 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00002073 svg_info->view_box.y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002074 GetMagickToken(p,&p,token);
2075 if (*token == ',')
2076 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00002077 svg_info->view_box.width=InterpretLocaleValue(token,
2078 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002079 if (svg_info->bounds.width == 0)
2080 svg_info->bounds.width=svg_info->view_box.width;
2081 GetMagickToken(p,&p,token);
2082 if (*token == ',')
2083 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00002084 svg_info->view_box.height=InterpretLocaleValue(token,
2085 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002086 if (svg_info->bounds.height == 0)
2087 svg_info->bounds.height=svg_info->view_box.height;
2088 break;
2089 }
2090 break;
2091 }
2092 case 'W':
2093 case 'w':
2094 {
2095 if (LocaleCompare(keyword,"width") == 0)
2096 {
2097 svg_info->bounds.width=
2098 GetUserSpaceCoordinateValue(svg_info,1,value);
2099 break;
2100 }
2101 break;
2102 }
2103 case 'X':
2104 case 'x':
2105 {
2106 if (LocaleCompare(keyword,"x") == 0)
2107 {
2108 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2109 break;
2110 }
2111 if (LocaleCompare(keyword,"xlink:href") == 0)
2112 {
2113 (void) CloneString(&svg_info->url,value);
2114 break;
2115 }
2116 if (LocaleCompare(keyword,"x1") == 0)
2117 {
2118 svg_info->segment.x1=
2119 GetUserSpaceCoordinateValue(svg_info,1,value);
2120 break;
2121 }
2122 if (LocaleCompare(keyword,"x2") == 0)
2123 {
2124 svg_info->segment.x2=
2125 GetUserSpaceCoordinateValue(svg_info,1,value);
2126 break;
2127 }
2128 break;
2129 }
2130 case 'Y':
2131 case 'y':
2132 {
2133 if (LocaleCompare(keyword,"y") == 0)
2134 {
2135 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2136 break;
2137 }
2138 if (LocaleCompare(keyword,"y1") == 0)
2139 {
2140 svg_info->segment.y1=
2141 GetUserSpaceCoordinateValue(svg_info,-1,value);
2142 break;
2143 }
2144 if (LocaleCompare(keyword,"y2") == 0)
2145 {
2146 svg_info->segment.y2=
2147 GetUserSpaceCoordinateValue(svg_info,-1,value);
2148 break;
2149 }
2150 break;
2151 }
2152 default:
2153 break;
2154 }
2155 }
2156 if (LocaleCompare((const char *) name,"svg") == 0)
2157 {
2158 if (svg_info->document->encoding != (const xmlChar *) NULL)
cristyb51dff52011-05-19 16:55:47 +00002159 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
cristy3ed852e2009-09-05 21:47:34 +00002160 (const char *) svg_info->document->encoding);
2161 if (attributes != (const xmlChar **) NULL)
2162 {
2163 double
2164 sx,
2165 sy;
2166
2167 if ((svg_info->view_box.width == 0.0) ||
2168 (svg_info->view_box.height == 0.0))
2169 svg_info->view_box=svg_info->bounds;
cristybb503372010-05-27 20:51:26 +00002170 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2171 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
cristyb51dff52011-05-19 16:55:47 +00002172 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2173 (double) svg_info->width,(double) svg_info->height);
cristy3ed852e2009-09-05 21:47:34 +00002174 sx=(double) svg_info->width/svg_info->view_box.width;
2175 sy=(double) svg_info->height/svg_info->view_box.height;
cristyb51dff52011-05-19 16:55:47 +00002176 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g 0.0 0.0\n",
2177 sx,sy);
cristy3ed852e2009-09-05 21:47:34 +00002178 }
2179 }
2180 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2181 units=DestroyString(units);
2182 if (color != (char *) NULL)
2183 color=DestroyString(color);
2184}
2185
2186static void SVGEndElement(void *context,const xmlChar *name)
2187{
2188 SVGInfo
2189 *svg_info;
2190
2191 /*
2192 Called when the end of an element has been detected.
2193 */
2194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2195 " SAX.endElement(%s)",name);
2196 svg_info=(SVGInfo *) context;
cristy13d07042010-11-21 20:56:18 +00002197 if (strchr((char *) name,':') != (char *) NULL)
cristy768b9302010-11-20 00:05:47 +00002198 {
2199 /*
2200 Skip over namespace.
2201 */
2202 for ( ; *name != ':'; name++) ;
2203 name++;
2204 }
cristy3ed852e2009-09-05 21:47:34 +00002205 switch (*name)
2206 {
2207 case 'C':
2208 case 'c':
2209 {
2210 if (LocaleCompare((const char *) name,"circle") == 0)
2211 {
cristyb51dff52011-05-19 16:55:47 +00002212 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
cristy8cd5b312010-01-07 01:10:24 +00002213 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2214 svg_info->element.cy+svg_info->element.minor);
cristyb51dff52011-05-19 16:55:47 +00002215 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002216 break;
2217 }
2218 if (LocaleCompare((const char *) name,"clipPath") == 0)
2219 {
cristyb51dff52011-05-19 16:55:47 +00002220 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
cristy3ed852e2009-09-05 21:47:34 +00002221 break;
2222 }
2223 break;
2224 }
2225 case 'D':
2226 case 'd':
2227 {
2228 if (LocaleCompare((const char *) name,"defs") == 0)
2229 {
cristyb51dff52011-05-19 16:55:47 +00002230 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
cristy3ed852e2009-09-05 21:47:34 +00002231 break;
2232 }
2233 if (LocaleCompare((const char *) name,"desc") == 0)
2234 {
2235 register char
2236 *p;
2237
2238 if (*svg_info->text == '\0')
2239 break;
2240 (void) fputc('#',svg_info->file);
2241 for (p=svg_info->text; *p != '\0'; p++)
2242 {
2243 (void) fputc(*p,svg_info->file);
2244 if (*p == '\n')
2245 (void) fputc('#',svg_info->file);
2246 }
2247 (void) fputc('\n',svg_info->file);
2248 *svg_info->text='\0';
2249 break;
2250 }
2251 break;
2252 }
2253 case 'E':
2254 case 'e':
2255 {
2256 if (LocaleCompare((const char *) name,"ellipse") == 0)
2257 {
2258 double
2259 angle;
2260
2261 angle=svg_info->element.angle;
cristyb51dff52011-05-19 16:55:47 +00002262 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
cristy3ed852e2009-09-05 21:47:34 +00002263 svg_info->element.cx,svg_info->element.cy,
2264 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2265 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
cristyb51dff52011-05-19 16:55:47 +00002266 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002267 break;
2268 }
2269 break;
2270 }
2271 case 'G':
2272 case 'g':
2273 {
2274 if (LocaleCompare((const char *) name,"g") == 0)
2275 {
cristyb51dff52011-05-19 16:55:47 +00002276 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002277 break;
2278 }
2279 break;
2280 }
2281 case 'I':
2282 case 'i':
2283 {
2284 if (LocaleCompare((const char *) name,"image") == 0)
2285 {
cristyb51dff52011-05-19 16:55:47 +00002286 (void) FormatLocaleFile(svg_info->file,
2287 "image Over %g,%g %g,%g '%s'\n",svg_info->bounds.x,
2288 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2289 svg_info->url);
2290 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002291 break;
2292 }
2293 break;
2294 }
2295 case 'L':
2296 case 'l':
2297 {
2298 if (LocaleCompare((const char *) name,"line") == 0)
2299 {
cristyb51dff52011-05-19 16:55:47 +00002300 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
cristy8cd5b312010-01-07 01:10:24 +00002301 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2302 svg_info->segment.y2);
cristyb51dff52011-05-19 16:55:47 +00002303 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002304 break;
2305 }
2306 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2307 {
cristyb51dff52011-05-19 16:55:47 +00002308 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
cristy3ed852e2009-09-05 21:47:34 +00002309 break;
2310 }
2311 break;
2312 }
2313 case 'P':
2314 case 'p':
2315 {
2316 if (LocaleCompare((const char *) name,"pattern") == 0)
2317 {
cristyb51dff52011-05-19 16:55:47 +00002318 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
cristy3ed852e2009-09-05 21:47:34 +00002319 break;
2320 }
2321 if (LocaleCompare((const char *) name,"path") == 0)
2322 {
cristyb51dff52011-05-19 16:55:47 +00002323 (void) FormatLocaleFile(svg_info->file,"path '%s'\n",
2324 svg_info->vertices);
2325 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002326 break;
2327 }
2328 if (LocaleCompare((const char *) name,"polygon") == 0)
2329 {
cristyb51dff52011-05-19 16:55:47 +00002330 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2331 svg_info->vertices);
2332 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002333 break;
2334 }
2335 if (LocaleCompare((const char *) name,"polyline") == 0)
2336 {
cristyb51dff52011-05-19 16:55:47 +00002337 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2338 svg_info->vertices);
2339 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002340 break;
2341 }
2342 break;
2343 }
2344 case 'R':
2345 case 'r':
2346 {
2347 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2348 {
cristyb51dff52011-05-19 16:55:47 +00002349 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
cristy3ed852e2009-09-05 21:47:34 +00002350 break;
2351 }
2352 if (LocaleCompare((const char *) name,"rect") == 0)
2353 {
2354 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2355 {
cristyb51dff52011-05-19 16:55:47 +00002356 (void) FormatLocaleFile(svg_info->file,"rectangle %g,%g %g,%g\n",
cristy3ed852e2009-09-05 21:47:34 +00002357 svg_info->bounds.x,svg_info->bounds.y,
2358 svg_info->bounds.x+svg_info->bounds.width,
2359 svg_info->bounds.y+svg_info->bounds.height);
cristyb51dff52011-05-19 16:55:47 +00002360 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002361 break;
2362 }
2363 if (svg_info->radius.x == 0.0)
2364 svg_info->radius.x=svg_info->radius.y;
2365 if (svg_info->radius.y == 0.0)
2366 svg_info->radius.y=svg_info->radius.x;
cristyb51dff52011-05-19 16:55:47 +00002367 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00002368 "roundRectangle %g,%g %g,%g %g,%g\n",
cristy3ed852e2009-09-05 21:47:34 +00002369 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2370 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2371 svg_info->radius.x,svg_info->radius.y);
2372 svg_info->radius.x=0.0;
2373 svg_info->radius.y=0.0;
cristyb51dff52011-05-19 16:55:47 +00002374 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002375 break;
2376 }
2377 break;
2378 }
2379 case 'S':
2380 case 's':
2381 {
2382 if (LocaleCompare((const char *) name,"stop") == 0)
2383 {
cristyb51dff52011-05-19 16:55:47 +00002384 (void) FormatLocaleFile(svg_info->file,"stop-color '%s' %s\n",
2385 svg_info->stop_color,svg_info->offset);
cristy3ed852e2009-09-05 21:47:34 +00002386 break;
2387 }
2388 if (LocaleCompare((const char *) name,"svg") == 0)
2389 {
cristyb51dff52011-05-19 16:55:47 +00002390 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002391 break;
2392 }
2393 break;
2394 }
2395 case 'T':
2396 case 't':
2397 {
2398 if (LocaleCompare((const char *) name,"text") == 0)
2399 {
2400 if (*svg_info->text != '\0')
2401 {
2402 char
2403 *text;
2404
2405 text=EscapeString(svg_info->text,'\'');
cristyb51dff52011-05-19 16:55:47 +00002406 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
cristy88158062011-06-09 00:31:43 +00002407 svg_info->bounds.x,svg_info->bounds.y,text);
cristy3ed852e2009-09-05 21:47:34 +00002408 text=DestroyString(text);
2409 *svg_info->text='\0';
2410 }
cristyb51dff52011-05-19 16:55:47 +00002411 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002412 break;
2413 }
2414 if (LocaleCompare((const char *) name,"tspan") == 0)
2415 {
2416 if (*svg_info->text != '\0')
2417 {
2418 DrawInfo
2419 *draw_info;
2420
2421 TypeMetric
2422 metrics;
2423
2424 char
2425 *text;
2426
2427 text=EscapeString(svg_info->text,'\'');
cristyb51dff52011-05-19 16:55:47 +00002428 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
cristy8cd5b312010-01-07 01:10:24 +00002429 svg_info->bounds.x,svg_info->bounds.y,text);
cristy3ed852e2009-09-05 21:47:34 +00002430 text=DestroyString(text);
2431 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
2432 draw_info->pointsize=svg_info->pointsize;
2433 draw_info->text=AcquireString(svg_info->text);
2434 (void) ConcatenateString(&draw_info->text," ");
cristy5cbc0162011-08-29 00:36:28 +00002435 (void) GetTypeMetrics(svg_info->image,draw_info,&metrics,
2436 svg_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00002437 svg_info->bounds.x+=metrics.width;
2438 draw_info=DestroyDrawInfo(draw_info);
2439 *svg_info->text='\0';
2440 }
cristyb51dff52011-05-19 16:55:47 +00002441 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002442 break;
2443 }
2444 if (LocaleCompare((const char *) name,"title") == 0)
2445 {
2446 if (*svg_info->text == '\0')
2447 break;
2448 (void) CloneString(&svg_info->title,svg_info->text);
2449 *svg_info->text='\0';
2450 break;
2451 }
2452 break;
2453 }
2454 default:
2455 break;
2456 }
2457 *svg_info->text='\0';
2458 (void) ResetMagickMemory(&svg_info->element,0,sizeof(svg_info->element));
2459 (void) ResetMagickMemory(&svg_info->segment,0,sizeof(svg_info->segment));
2460 svg_info->n--;
2461}
2462
2463static void SVGCharacters(void *context,const xmlChar *c,int length)
2464{
2465 register char
2466 *p;
2467
cristybb503372010-05-27 20:51:26 +00002468 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002469 i;
2470
2471 SVGInfo
2472 *svg_info;
2473
2474 /*
2475 Receiving some characters from the parser.
2476 */
2477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002478 " SAX.characters(%s,%.20g)",c,(double) length);
cristy3ed852e2009-09-05 21:47:34 +00002479 svg_info=(SVGInfo *) context;
2480 if (svg_info->text != (char *) NULL)
2481 svg_info->text=(char *) ResizeQuantumMemory(svg_info->text,
2482 strlen(svg_info->text)+length+MaxTextExtent,sizeof(*svg_info->text));
2483 else
2484 {
2485 svg_info->text=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2486 sizeof(*svg_info->text));
2487 if (svg_info->text != (char *) NULL)
2488 *svg_info->text='\0';
2489 }
2490 if (svg_info->text == (char *) NULL)
2491 return;
2492 p=svg_info->text+strlen(svg_info->text);
cristybb503372010-05-27 20:51:26 +00002493 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00002494 *p++=c[i];
2495 *p='\0';
2496}
2497
2498static void SVGReference(void *context,const xmlChar *name)
2499{
2500 SVGInfo
2501 *svg_info;
2502
2503 xmlParserCtxtPtr
2504 parser;
2505
2506 /*
2507 Called when an entity reference is detected.
2508 */
2509 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2510 name);
2511 svg_info=(SVGInfo *) context;
2512 parser=svg_info->parser;
2513 if (parser == (xmlParserCtxtPtr) NULL)
2514 return;
2515 if (parser->node == (xmlNodePtr) NULL)
2516 return;
2517 if (*name == '#')
2518 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2519 else
2520 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2521}
2522
2523static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2524{
2525 SVGInfo
2526 *svg_info;
2527
2528 /*
2529 Receiving some ignorable whitespaces from the parser.
2530 */
2531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2532 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2533 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +00002534 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +00002535}
2536
2537static void SVGProcessingInstructions(void *context,const xmlChar *target,
2538 const xmlChar *data)
2539{
2540 SVGInfo
2541 *svg_info;
2542
2543 /*
2544 A processing instruction has been parsed.
2545 */
2546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2547 " SAX.processingInstruction(%s, %s)",target,data);
2548 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +00002549 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +00002550}
2551
2552static void SVGComment(void *context,const xmlChar *value)
2553{
2554 SVGInfo
2555 *svg_info;
2556
2557 /*
2558 A comment has been parsed.
2559 */
2560 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
2561 value);
2562 svg_info=(SVGInfo *) context;
2563 if (svg_info->comment != (char *) NULL)
2564 (void) ConcatenateString(&svg_info->comment,"\n");
2565 (void) ConcatenateString(&svg_info->comment,(const char *) value);
2566}
2567
2568static void SVGWarning(void *context,const char *format,...)
2569{
2570 char
2571 *message,
2572 reason[MaxTextExtent];
2573
2574 SVGInfo
2575 *svg_info;
2576
2577 va_list
2578 operands;
2579
2580 /**
2581 Display and format a warning messages, gives file, line, position and
2582 extra parameters.
2583 */
2584 va_start(operands,format);
2585 svg_info=(SVGInfo *) context;
2586 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
2587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2588#if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2589 (void) vsprintf(reason,format,operands);
2590#else
2591 (void) vsnprintf(reason,MaxTextExtent,format,operands);
2592#endif
2593 message=GetExceptionMessage(errno);
2594 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2595 DelegateWarning,reason,"`%s`",message);
2596 message=DestroyString(message);
2597 va_end(operands);
2598}
2599
2600static void SVGError(void *context,const char *format,...)
2601{
2602 char
2603 *message,
2604 reason[MaxTextExtent];
2605
2606 SVGInfo
2607 *svg_info;
2608
2609 va_list
2610 operands;
2611
2612 /*
2613 Display and format a error formats, gives file, line, position and
2614 extra parameters.
2615 */
2616 va_start(operands,format);
2617 svg_info=(SVGInfo *) context;
2618 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
2619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2620#if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2621 (void) vsprintf(reason,format,operands);
2622#else
2623 (void) vsnprintf(reason,MaxTextExtent,format,operands);
2624#endif
2625 message=GetExceptionMessage(errno);
2626 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2627 reason,"`%s`",message);
2628 message=DestroyString(message);
2629 va_end(operands);
2630}
2631
2632static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2633{
2634 SVGInfo
2635 *svg_info;
2636
2637 xmlNodePtr
2638 child;
2639
2640 xmlParserCtxtPtr
2641 parser;
2642
2643 /*
2644 Called when a pcdata block has been parsed.
2645 */
2646 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
2647 value,length);
2648 svg_info=(SVGInfo *) context;
2649 parser=svg_info->parser;
2650 child=xmlGetLastChild(parser->node);
2651 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2652 {
2653 xmlTextConcat(child,value,length);
2654 return;
2655 }
2656 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2657}
2658
2659static void SVGExternalSubset(void *context,const xmlChar *name,
2660 const xmlChar *external_id,const xmlChar *system_id)
2661{
2662 SVGInfo
2663 *svg_info;
2664
2665 xmlParserCtxt
2666 parser_context;
2667
2668 xmlParserCtxtPtr
2669 parser;
2670
2671 xmlParserInputPtr
2672 input;
2673
2674 /*
2675 Does this document has an external subset?
2676 */
2677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2678 " SAX.externalSubset(%s, %s, %s)",name,
2679 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2680 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2681 svg_info=(SVGInfo *) context;
2682 parser=svg_info->parser;
2683 if (((external_id == NULL) && (system_id == NULL)) ||
2684 ((parser->validate == 0) || (parser->wellFormed == 0) ||
2685 (svg_info->document == 0)))
2686 return;
2687 input=SVGResolveEntity(context,external_id,system_id);
2688 if (input == NULL)
2689 return;
2690 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2691 parser_context=(*parser);
2692 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2693 if (parser->inputTab == (xmlParserInputPtr *) NULL)
2694 {
2695 parser->errNo=XML_ERR_NO_MEMORY;
2696 parser->input=parser_context.input;
2697 parser->inputNr=parser_context.inputNr;
2698 parser->inputMax=parser_context.inputMax;
2699 parser->inputTab=parser_context.inputTab;
2700 return;
2701 }
2702 parser->inputNr=0;
2703 parser->inputMax=5;
2704 parser->input=NULL;
2705 xmlPushInput(parser,input);
2706 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2707 if (input->filename == (char *) NULL)
2708 input->filename=(char *) xmlStrdup(system_id);
2709 input->line=1;
2710 input->col=1;
2711 input->base=parser->input->cur;
2712 input->cur=parser->input->cur;
2713 input->free=NULL;
2714 xmlParseExternalSubset(parser,external_id,system_id);
2715 while (parser->inputNr > 1)
2716 (void) xmlPopInput(parser);
2717 xmlFreeInputStream(parser->input);
2718 xmlFree(parser->inputTab);
2719 parser->input=parser_context.input;
2720 parser->inputNr=parser_context.inputNr;
2721 parser->inputMax=parser_context.inputMax;
2722 parser->inputTab=parser_context.inputTab;
2723}
2724
2725#if defined(MAGICKCORE_RSVG_DELEGATE)
cristy3ed852e2009-09-05 21:47:34 +00002726static void SVGSetImageSize(int *width,int *height,gpointer context)
2727{
2728 Image
2729 *image;
2730
2731 image=(Image *) context;
2732 *width=(int) (*width*image->x_resolution/72.0);
2733 *height=(int) (*height*image->y_resolution/72.0);
2734}
2735#endif
cristy3ed852e2009-09-05 21:47:34 +00002736
2737#if defined(__cplusplus) || defined(c_plusplus)
2738}
2739#endif
2740
2741static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2742{
cristy3ed852e2009-09-05 21:47:34 +00002743 char
2744 filename[MaxTextExtent];
2745
2746 FILE
2747 *file;
2748
2749 Image
2750 *image;
2751
2752 int
2753 status,
2754 unique_file;
2755
cristybb503372010-05-27 20:51:26 +00002756 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002757 n;
2758
2759 SVGInfo
2760 *svg_info;
2761
2762 unsigned char
2763 message[MaxTextExtent];
2764
cristy1f9e1ed2009-11-18 04:09:38 +00002765 xmlSAXHandler
cristy5f6f01c2009-11-19 19:36:42 +00002766 sax_modules;
cristy1f9e1ed2009-11-18 04:09:38 +00002767
cristy3ed852e2009-09-05 21:47:34 +00002768 xmlSAXHandlerPtr
2769 sax_handler;
2770
2771 /*
2772 Open image file.
2773 */
2774 assert(image_info != (const ImageInfo *) NULL);
2775 assert(image_info->signature == MagickSignature);
2776 assert(exception != (ExceptionInfo *) NULL);
2777 if (image_info->debug != MagickFalse)
2778 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2779 image_info->filename);
2780 assert(exception->signature == MagickSignature);
2781 image=AcquireImage(image_info);
2782 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2783 if (status == MagickFalse)
2784 {
2785 image=DestroyImageList(image);
2786 return((Image *) NULL);
2787 }
2788 if (LocaleCompare(image_info->magick,"MSVG") != 0)
2789 {
2790#if defined(MAGICKCORE_RSVG_DELEGATE)
2791#if defined(MAGICKCORE_CAIRO_DELEGATE)
2792 cairo_surface_t
2793 *cairo_surface;
2794
2795 cairo_t
2796 *cairo_info;
2797
2798 register unsigned char
2799 *p;
2800
2801 RsvgDimensionData
2802 dimension_info;
2803
2804 unsigned char
2805 *pixels;
2806
2807#else
2808 GdkPixbuf
2809 *pixel_info;
2810
2811 register const guchar
2812 *p;
2813
2814#endif
2815
2816 GError
2817 *error;
2818
cristybb503372010-05-27 20:51:26 +00002819 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002820 y;
2821
2822 PixelPacket
2823 fill_color;
2824
cristybb503372010-05-27 20:51:26 +00002825 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002826 x;
2827
cristy4c08aed2011-07-01 19:47:50 +00002828 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002829 *q;
2830
2831 RsvgHandle
2832 *svg_handle;
2833
2834 svg_handle=rsvg_handle_new();
2835 if (svg_handle == (RsvgHandle *) NULL)
2836 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2837 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
cristy3ed852e2009-09-05 21:47:34 +00002838 rsvg_handle_set_size_callback(svg_handle,SVGSetImageSize,image,NULL);
cristy3ed852e2009-09-05 21:47:34 +00002839 if ((image->x_resolution != 72.0) && (image->y_resolution != 72.0))
2840 rsvg_handle_set_dpi_x_y(svg_handle,image->x_resolution,
2841 image->y_resolution);
2842 while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
2843 {
2844 error=(GError *) NULL;
2845 (void) rsvg_handle_write(svg_handle,message,n,&error);
2846 if (error != (GError *) NULL)
2847 g_error_free(error);
2848 }
2849 error=(GError *) NULL;
2850 rsvg_handle_close(svg_handle,&error);
2851 if (error != (GError *) NULL)
2852 g_error_free(error);
2853#if defined(MAGICKCORE_CAIRO_DELEGATE)
2854 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
cristy3de0a962009-12-06 15:34:07 +00002855 image->columns=dimension_info.width;
2856 image->rows=dimension_info.height;
cristye391a852009-11-21 14:50:33 +00002857 pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002858#else
2859 pixel_info=rsvg_handle_get_pixbuf(svg_handle);
2860 rsvg_handle_free(svg_handle);
2861 image->columns=gdk_pixbuf_get_width(pixel_info);
2862 image->rows=gdk_pixbuf_get_height(pixel_info);
2863#endif
cristye391a852009-11-21 14:50:33 +00002864 image->matte=MagickTrue;
2865 SetImageProperty(image,"svg:base-uri",
2866 rsvg_handle_get_base_uri(svg_handle));
2867 SetImageProperty(image,"svg:title",rsvg_handle_get_title(svg_handle));
2868 SetImageProperty(image,"svg:description",
2869 rsvg_handle_get_desc(svg_handle));
cristy3ed852e2009-09-05 21:47:34 +00002870 if ((image->columns == 0) || (image->rows == 0))
2871 {
2872#if !defined(MAGICKCORE_CAIRO_DELEGATE)
2873 g_object_unref(G_OBJECT(pixel_info));
2874#endif
2875 g_object_unref(svg_handle);
2876 ThrowReaderException(MissingDelegateError,
2877 "NoDecodeDelegateForThisImageFormat");
2878 }
cristye391a852009-11-21 14:50:33 +00002879 if (image_info->ping == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002880 {
cristye391a852009-11-21 14:50:33 +00002881#if defined(MAGICKCORE_CAIRO_DELEGATE)
2882 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,4*
2883 image->rows*sizeof(*pixels));
2884 if (pixels == (unsigned char *) NULL)
2885 {
2886 g_object_unref(svg_handle);
2887 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2888 }
2889#endif
2890 (void) SetImageBackgroundColor(image);
2891#if defined(MAGICKCORE_CAIRO_DELEGATE)
2892 cairo_surface=cairo_image_surface_create_for_data(pixels,
2893 CAIRO_FORMAT_ARGB32,image->columns,image->rows,4*image->columns);
2894 if (cairo_surface == (cairo_surface_t *) NULL)
2895 {
2896 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2897 g_object_unref(svg_handle);
2898 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2899 }
2900 cairo_info=cairo_create(cairo_surface);
cristye391a852009-11-21 14:50:33 +00002901 cairo_set_operator(cairo_info,CAIRO_OPERATOR_CLEAR);
2902 cairo_paint(cairo_info);
2903 cairo_set_operator(cairo_info,CAIRO_OPERATOR_OVER);
2904 rsvg_handle_render_cairo(svg_handle,cairo_info);
2905 cairo_destroy(cairo_info);
2906 cairo_surface_destroy(cairo_surface);
cristy3ed852e2009-09-05 21:47:34 +00002907 g_object_unref(svg_handle);
cristye391a852009-11-21 14:50:33 +00002908 p=pixels;
cristy3ed852e2009-09-05 21:47:34 +00002909#else
cristye391a852009-11-21 14:50:33 +00002910 p=gdk_pixbuf_get_pixels(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00002911#endif
cristybb503372010-05-27 20:51:26 +00002912 for (y=0; y < (ssize_t) image->rows; y++)
cristyd97b6ed2009-11-20 01:02:19 +00002913 {
cristye391a852009-11-21 14:50:33 +00002914 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00002915 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002916 break;
cristybb503372010-05-27 20:51:26 +00002917 for (x=0; x < (ssize_t) image->columns; x++)
cristye391a852009-11-21 14:50:33 +00002918 {
cristy3ed852e2009-09-05 21:47:34 +00002919#if defined(MAGICKCORE_CAIRO_DELEGATE)
cristye391a852009-11-21 14:50:33 +00002920 fill_color.blue=ScaleCharToQuantum(*p++);
2921 fill_color.green=ScaleCharToQuantum(*p++);
2922 fill_color.red=ScaleCharToQuantum(*p++);
2923#else
2924 fill_color.red=ScaleCharToQuantum(*p++);
2925 fill_color.green=ScaleCharToQuantum(*p++);
2926 fill_color.blue=ScaleCharToQuantum(*p++);
2927#endif
cristy4c08aed2011-07-01 19:47:50 +00002928 fill_color.alpha=ScaleCharToQuantum(*p++);
cristye391a852009-11-21 14:50:33 +00002929#if defined(MAGICKCORE_CAIRO_DELEGATE)
2930 {
2931 double
2932 gamma;
2933
cristy4c08aed2011-07-01 19:47:50 +00002934 gamma=1.0-QuantumScale*fill_color.alpha;
cristye391a852009-11-21 14:50:33 +00002935 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2936 fill_color.blue*=gamma;
2937 fill_color.green*=gamma;
2938 fill_color.red*=gamma;
2939 }
2940#endif
cristy4c08aed2011-07-01 19:47:50 +00002941 CompositePixelOver(image,&fill_color,fill_color.alpha,q,
2942 (MagickRealType) GetPixelAlpha(image,q),q);
cristyed231572011-07-14 02:18:59 +00002943 q+=GetPixelChannels(image);
cristye391a852009-11-21 14:50:33 +00002944 }
2945 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2946 break;
2947 if (image->previous == (Image *) NULL)
2948 {
cristycee97112010-05-28 00:44:52 +00002949 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2950 image->rows);
cristye391a852009-11-21 14:50:33 +00002951 if (status == MagickFalse)
2952 break;
2953 }
2954 }
2955 }
2956#if defined(MAGICKCORE_CAIRO_DELEGATE)
2957 if (pixels != (unsigned char *) NULL)
2958 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00002959#else
2960 g_object_unref(G_OBJECT(pixel_info));
2961#endif
2962 (void) CloseBlob(image);
2963 return(GetFirstImageInList(image));
2964#endif
2965 }
2966 /*
2967 Open draw file.
2968 */
2969 file=(FILE *) NULL;
2970 unique_file=AcquireUniqueFileResource(filename);
2971 if (unique_file != -1)
2972 file=fdopen(unique_file,"w");
2973 if ((unique_file == -1) || (file == (FILE *) NULL))
2974 {
2975 (void) CopyMagickString(image->filename,filename,MaxTextExtent);
2976 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
2977 image->filename);
2978 image=DestroyImageList(image);
2979 return((Image *) NULL);
2980 }
2981 /*
2982 Parse SVG file.
2983 */
2984 svg_info=AcquireSVGInfo();
2985 if (svg_info == (SVGInfo *) NULL)
2986 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2987 svg_info->file=file;
2988 svg_info->exception=exception;
2989 svg_info->image=image;
2990 svg_info->image_info=image_info;
2991 svg_info->bounds.width=image->columns;
2992 svg_info->bounds.height=image->rows;
2993 if (image_info->size != (char *) NULL)
2994 (void) CloneString(&svg_info->size,image_info->size);
2995 if (image->debug != MagickFalse)
2996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
2997 xmlInitParser();
2998 (void) xmlSubstituteEntitiesDefault(1);
cristy5f6f01c2009-11-19 19:36:42 +00002999 (void) ResetMagickMemory(&sax_modules,0,sizeof(sax_modules));
3000 sax_modules.internalSubset=SVGInternalSubset;
3001 sax_modules.isStandalone=SVGIsStandalone;
3002 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3003 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3004 sax_modules.resolveEntity=SVGResolveEntity;
3005 sax_modules.getEntity=SVGGetEntity;
3006 sax_modules.entityDecl=SVGEntityDeclaration;
3007 sax_modules.notationDecl=SVGNotationDeclaration;
3008 sax_modules.attributeDecl=SVGAttributeDeclaration;
3009 sax_modules.elementDecl=SVGElementDeclaration;
3010 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3011 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3012 sax_modules.startDocument=SVGStartDocument;
3013 sax_modules.endDocument=SVGEndDocument;
3014 sax_modules.startElement=SVGStartElement;
3015 sax_modules.endElement=SVGEndElement;
3016 sax_modules.reference=SVGReference;
3017 sax_modules.characters=SVGCharacters;
3018 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3019 sax_modules.processingInstruction=SVGProcessingInstructions;
3020 sax_modules.comment=SVGComment;
3021 sax_modules.warning=SVGWarning;
3022 sax_modules.error=SVGError;
3023 sax_modules.fatalError=SVGError;
3024 sax_modules.getParameterEntity=SVGGetParameterEntity;
3025 sax_modules.cdataBlock=SVGCDataBlock;
3026 sax_modules.externalSubset=SVGExternalSubset;
3027 sax_handler=(&sax_modules);
cristy3ed852e2009-09-05 21:47:34 +00003028 n=ReadBlob(image,MaxTextExtent,message);
3029 if (n > 0)
3030 {
3031 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3032 message,n,image->filename);
3033 while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
3034 {
3035 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3036 if (status != 0)
3037 break;
3038 }
3039 }
3040 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3041 xmlFreeParserCtxt(svg_info->parser);
3042 if (image->debug != MagickFalse)
3043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3044 xmlCleanupParser();
3045 (void) fclose(file);
3046 (void) CloseBlob(image);
3047 image->columns=svg_info->width;
3048 image->rows=svg_info->height;
3049 if (exception->severity >= ErrorException)
3050 {
3051 image=DestroyImage(image);
3052 return((Image *) NULL);
3053 }
3054 if (image_info->ping == MagickFalse)
3055 {
3056 ImageInfo
3057 *read_info;
3058
3059 /*
3060 Draw image.
3061 */
3062 image=DestroyImage(image);
3063 image=(Image *) NULL;
3064 read_info=CloneImageInfo(image_info);
3065 SetImageInfoBlob(read_info,(void *) NULL,0);
3066 if (read_info->density != (char *) NULL)
3067 read_info->density=DestroyString(read_info->density);
cristyb51dff52011-05-19 16:55:47 +00003068 (void) FormatLocaleString(read_info->filename,MaxTextExtent,"mvg:%s",
cristy3ed852e2009-09-05 21:47:34 +00003069 filename);
3070 image=ReadImage(read_info,exception);
3071 read_info=DestroyImageInfo(read_info);
3072 if (image != (Image *) NULL)
3073 (void) CopyMagickString(image->filename,image_info->filename,
3074 MaxTextExtent);
3075 }
3076 /*
3077 Relinquish resources.
3078 */
3079 if (image != (Image *) NULL)
3080 {
3081 if (svg_info->title != (char *) NULL)
3082 (void) SetImageProperty(image,"svg:title",svg_info->title);
3083 if (svg_info->comment != (char *) NULL)
3084 (void) SetImageProperty(image,"svg:comment",svg_info->comment);
3085 }
3086 svg_info=DestroySVGInfo(svg_info);
3087 (void) RelinquishUniqueFileResource(filename);
3088 return(GetFirstImageInList(image));
3089}
3090#endif
3091
3092/*
3093%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3094% %
3095% %
3096% %
3097% R e g i s t e r S V G I m a g e %
3098% %
3099% %
3100% %
3101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3102%
3103% RegisterSVGImage() adds attributes for the SVG image format to
3104% the list of supported formats. The attributes include the image format
3105% tag, a method to read and/or write the format, whether the format
3106% supports the saving of more than one frame to the same file or blob,
3107% whether the format supports native in-memory I/O, and a brief
3108% description of the format.
3109%
3110% The format of the RegisterSVGImage method is:
3111%
cristybb503372010-05-27 20:51:26 +00003112% size_t RegisterSVGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00003113%
3114*/
cristybb503372010-05-27 20:51:26 +00003115ModuleExport size_t RegisterSVGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00003116{
3117 char
3118 version[MaxTextExtent];
3119
3120 MagickInfo
3121 *entry;
3122
3123 *version='\0';
3124#if defined(LIBXML_DOTTED_VERSION)
3125 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,MaxTextExtent);
3126#endif
3127#if defined(MAGICKCORE_RSVG_DELEGATE)
3128 rsvg_init();
cristyb51dff52011-05-19 16:55:47 +00003129 (void) FormatLocaleString(version,MaxTextExtent,"RSVG %d.%d.%d",
cristy3ed852e2009-09-05 21:47:34 +00003130 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3131#endif
3132 entry=SetMagickInfo("SVG");
3133#if defined(MAGICKCORE_XML_DELEGATE)
3134 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3135#endif
3136 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3137 entry->blob_support=MagickFalse;
3138 entry->seekable_stream=MagickFalse;
3139 entry->description=ConstantString("Scalable Vector Graphics");
3140 if (*version != '\0')
3141 entry->version=ConstantString(version);
3142 entry->magick=(IsImageFormatHandler *) IsSVG;
3143 entry->module=ConstantString("SVG");
3144 (void) RegisterMagickInfo(entry);
3145 entry=SetMagickInfo("SVGZ");
3146#if defined(MAGICKCORE_XML_DELEGATE)
3147 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3148#endif
3149 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3150 entry->blob_support=MagickFalse;
3151 entry->seekable_stream=MagickFalse;
3152 entry->description=ConstantString("Compressed Scalable Vector Graphics");
3153 if (*version != '\0')
3154 entry->version=ConstantString(version);
3155 entry->magick=(IsImageFormatHandler *) IsSVG;
3156 entry->module=ConstantString("SVG");
3157 (void) RegisterMagickInfo(entry);
3158 entry=SetMagickInfo("MSVG");
3159#if defined(MAGICKCORE_XML_DELEGATE)
3160 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3161#endif
3162 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3163 entry->blob_support=MagickFalse;
3164 entry->seekable_stream=MagickFalse;
3165 entry->description=ConstantString("ImageMagick's own SVG internal renderer");
3166 entry->magick=(IsImageFormatHandler *) IsSVG;
3167 entry->module=ConstantString("SVG");
3168 (void) RegisterMagickInfo(entry);
3169 return(MagickImageCoderSignature);
3170}
3171
3172/*
3173%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3174% %
3175% %
3176% %
3177% U n r e g i s t e r S V G I m a g e %
3178% %
3179% %
3180% %
3181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3182%
3183% UnregisterSVGImage() removes format registrations made by the
3184% SVG module from the list of supported formats.
3185%
3186% The format of the UnregisterSVGImage method is:
3187%
3188% UnregisterSVGImage(void)
3189%
3190*/
3191ModuleExport void UnregisterSVGImage(void)
3192{
3193 (void) UnregisterMagickInfo("SVGZ");
3194 (void) UnregisterMagickInfo("SVG");
3195 (void) UnregisterMagickInfo("MSVG");
3196#if defined(MAGICKCORE_RSVG_DELEGATE)
3197 rsvg_term();
3198#endif
3199}
3200
3201/*
3202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3203% %
3204% %
3205% %
3206% W r i t e S V G I m a g e %
3207% %
3208% %
3209% %
3210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3211%
3212% WriteSVGImage() writes a image in the SVG - XML based W3C standard
3213% format.
3214%
3215% The format of the WriteSVGImage method is:
3216%
cristy3a37efd2011-08-28 20:31:03 +00003217% MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3218% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003219%
3220% A description of each parameter follows.
3221%
3222% o image_info: the image info.
3223%
3224% o image: The image.
3225%
cristy3a37efd2011-08-28 20:31:03 +00003226% o exception: return any errors or warnings in this structure.
3227%
cristy3ed852e2009-09-05 21:47:34 +00003228*/
3229
3230static void AffineToTransform(Image *image,AffineMatrix *affine)
3231{
3232 char
3233 transform[MaxTextExtent];
3234
3235 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3236 {
3237 if ((fabs(affine->rx) < MagickEpsilon) &&
3238 (fabs(affine->ry) < MagickEpsilon))
3239 {
3240 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3241 (fabs(affine->sy-1.0) < MagickEpsilon))
3242 {
3243 (void) WriteBlobString(image,"\">\n");
3244 return;
3245 }
cristyb51dff52011-05-19 16:55:47 +00003246 (void) FormatLocaleString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003247 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
cristy3ed852e2009-09-05 21:47:34 +00003248 (void) WriteBlobString(image,transform);
3249 return;
3250 }
3251 else
3252 {
3253 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3254 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3255 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3256 2*MagickEpsilon))
3257 {
3258 double
3259 theta;
3260
3261 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
cristyb51dff52011-05-19 16:55:47 +00003262 (void) FormatLocaleString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003263 "\" transform=\"rotate(%g)\">\n",theta);
cristy3ed852e2009-09-05 21:47:34 +00003264 (void) WriteBlobString(image,transform);
3265 return;
3266 }
3267 }
3268 }
3269 else
3270 {
3271 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3272 (fabs(affine->rx) < MagickEpsilon) &&
3273 (fabs(affine->ry) < MagickEpsilon) &&
3274 (fabs(affine->sy-1.0) < MagickEpsilon))
3275 {
cristyb51dff52011-05-19 16:55:47 +00003276 (void) FormatLocaleString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003277 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
cristy3ed852e2009-09-05 21:47:34 +00003278 (void) WriteBlobString(image,transform);
3279 return;
3280 }
3281 }
cristyb51dff52011-05-19 16:55:47 +00003282 (void) FormatLocaleString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003283 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
cristy8cd5b312010-01-07 01:10:24 +00003284 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
cristy3ed852e2009-09-05 21:47:34 +00003285 (void) WriteBlobString(image,transform);
3286}
3287
3288static MagickBooleanType IsPoint(const char *point)
3289{
3290 char
3291 *p;
3292
cristybb503372010-05-27 20:51:26 +00003293 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003294 value;
3295
3296 value=strtol(point,&p,10);
cristyda16f162011-02-19 23:52:17 +00003297 (void) value;
cristy3ed852e2009-09-05 21:47:34 +00003298 return(p != point ? MagickTrue : MagickFalse);
3299}
3300
3301static MagickBooleanType TraceSVGImage(Image *image)
3302{
cristybb503372010-05-27 20:51:26 +00003303 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003304 y;
3305
cristy4c08aed2011-07-01 19:47:50 +00003306 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003307 *p;
3308
cristybb503372010-05-27 20:51:26 +00003309 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003310 x;
3311
3312#if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3313 {
3314 at_bitmap_type
3315 *trace;
3316
3317 at_fitting_opts_type
3318 *fitting_options;
3319
3320 at_output_opts_type
3321 *output_options;
3322
3323 at_splines_type
3324 *splines;
3325
3326 ImageType
3327 type;
3328
cristybb503372010-05-27 20:51:26 +00003329 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003330 i;
3331
cristybb503372010-05-27 20:51:26 +00003332 size_t
cristy3ed852e2009-09-05 21:47:34 +00003333 number_planes;
3334
3335 /*
3336 Trace image and write as SVG.
3337 */
3338 fitting_options=at_fitting_opts_new();
3339 output_options=at_output_opts_new();
3340 type=GetImageType(image,&image->exception);
3341 number_planes=3;
3342 if ((type == BilevelType) || (type == GrayscaleType))
3343 number_planes=1;
3344 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3345 i=0;
cristybb503372010-05-27 20:51:26 +00003346 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003347 {
3348 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00003349 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003350 break;
cristybb503372010-05-27 20:51:26 +00003351 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003352 {
cristy4c08aed2011-07-01 19:47:50 +00003353 trace->bitmap[i++]=GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +00003354 if (number_planes == 3)
3355 {
cristy4c08aed2011-07-01 19:47:50 +00003356 trace->bitmap[i++]=GetPixelGreen(image,p);
3357 trace->bitmap[i++]=GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00003358 }
cristyed231572011-07-14 02:18:59 +00003359 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003360 }
3361 }
3362 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3363 NULL);
3364 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3365 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3366 NULL);
3367 /*
3368 Free resources.
3369 */
3370 at_splines_free(splines);
3371 at_bitmap_free(trace);
3372 at_output_opts_free(output_options);
3373 at_fitting_opts_free(fitting_options);
3374 }
3375#else
3376 {
3377 char
3378 message[MaxTextExtent],
3379 tuple[MaxTextExtent];
3380
cristy4c08aed2011-07-01 19:47:50 +00003381 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003382 pixel;
3383
cristy3ed852e2009-09-05 21:47:34 +00003384 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3385 (void) WriteBlobString(image,
3386 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3387 (void) WriteBlobString(image,
3388 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
cristyb51dff52011-05-19 16:55:47 +00003389 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00003390 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,
3391 (double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003392 (void) WriteBlobString(image,message);
cristy4c08aed2011-07-01 19:47:50 +00003393 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00003394 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003395 {
3396 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00003397 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003398 break;
cristybb503372010-05-27 20:51:26 +00003399 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003400 {
cristy4c08aed2011-07-01 19:47:50 +00003401 SetPixelInfo(image,p,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00003402 (void) QueryMagickColorname(image,&pixel,SVGCompliance,tuple,
3403 &image->exception);
cristyb51dff52011-05-19 16:55:47 +00003404 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00003405 " <circle cx=\"%.20g\" cy=\"%.20g\" r=\"1\" fill=\"%s\"/>\n",
3406 (double) x,(double) y,tuple);
cristy3ed852e2009-09-05 21:47:34 +00003407 (void) WriteBlobString(image,message);
cristyed231572011-07-14 02:18:59 +00003408 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003409 }
3410 }
3411 (void) WriteBlobString(image,"</svg>\n");
3412 }
3413#endif
3414 return(MagickTrue);
3415}
3416
cristy3a37efd2011-08-28 20:31:03 +00003417static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
3418 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003419{
3420#define BezierQuantum 200
3421
3422 AffineMatrix
3423 affine;
3424
3425 char
3426 keyword[MaxTextExtent],
3427 message[MaxTextExtent],
3428 name[MaxTextExtent],
3429 *token,
3430 type[MaxTextExtent];
3431
3432 const char
3433 *p,
3434 *q,
3435 *value;
3436
3437 int
3438 n;
3439
cristybb503372010-05-27 20:51:26 +00003440 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003441 j;
3442
3443 MagickBooleanType
3444 active,
3445 status;
3446
3447 PointInfo
3448 point;
3449
3450 PrimitiveInfo
3451 *primitive_info;
3452
3453 PrimitiveType
3454 primitive_type;
3455
cristybb503372010-05-27 20:51:26 +00003456 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003457 x;
3458
cristybb503372010-05-27 20:51:26 +00003459 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003460 i;
3461
3462 size_t
3463 length;
3464
3465 SVGInfo
3466 svg_info;
3467
cristybb503372010-05-27 20:51:26 +00003468 size_t
cristy3ed852e2009-09-05 21:47:34 +00003469 number_points;
3470
3471 /*
3472 Open output image file.
3473 */
3474 assert(image_info != (const ImageInfo *) NULL);
3475 assert(image_info->signature == MagickSignature);
3476 assert(image != (Image *) NULL);
3477 assert(image->signature == MagickSignature);
3478 if (image->debug != MagickFalse)
3479 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00003480 assert(exception != (ExceptionInfo *) NULL);
3481 assert(exception->signature == MagickSignature);
3482 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00003483 if (status == MagickFalse)
3484 return(status);
3485 value=GetImageArtifact(image,"SVG");
3486 if (value != (char *) NULL)
3487 {
3488 (void) WriteBlobString(image,value);
3489 (void) CloseBlob(image);
3490 return(MagickTrue);
3491 }
3492 value=GetImageArtifact(image,"MVG");
3493 if (value == (char *) NULL)
3494 return(TraceSVGImage(image));
3495 /*
3496 Write SVG header.
3497 */
3498 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3499 (void) WriteBlobString(image,
3500 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3501 (void) WriteBlobString(image,
3502 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
cristyb51dff52011-05-19 16:55:47 +00003503 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00003504 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3505 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003506 (void) WriteBlobString(image,message);
3507 /*
3508 Allocate primitive info memory.
3509 */
3510 number_points=2047;
3511 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3512 sizeof(*primitive_info));
3513 if (primitive_info == (PrimitiveInfo *) NULL)
3514 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3515 GetAffineMatrix(&affine);
3516 token=AcquireString(value);
3517 active=MagickFalse;
3518 n=0;
3519 status=MagickTrue;
3520 for (q=(const char *) value; *q != '\0'; )
3521 {
3522 /*
3523 Interpret graphic primitive.
3524 */
3525 GetMagickToken(q,&q,keyword);
3526 if (*keyword == '\0')
3527 break;
3528 if (*keyword == '#')
3529 {
3530 /*
3531 Comment.
3532 */
3533 if (active != MagickFalse)
3534 {
3535 AffineToTransform(image,&affine);
3536 active=MagickFalse;
3537 }
3538 (void) WriteBlobString(image,"<desc>");
3539 (void) WriteBlobString(image,keyword+1);
3540 for ( ; (*q != '\n') && (*q != '\0'); q++)
3541 switch (*q)
3542 {
3543 case '<': (void) WriteBlobString(image,"&lt;"); break;
3544 case '>': (void) WriteBlobString(image,"&gt;"); break;
3545 case '&': (void) WriteBlobString(image,"&amp;"); break;
3546 default: (void) WriteBlobByte(image,*q); break;
3547 }
3548 (void) WriteBlobString(image,"</desc>\n");
3549 continue;
3550 }
3551 primitive_type=UndefinedPrimitive;
3552 switch (*keyword)
3553 {
3554 case ';':
3555 break;
3556 case 'a':
3557 case 'A':
3558 {
3559 if (LocaleCompare("affine",keyword) == 0)
3560 {
3561 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003562 affine.sx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003563 GetMagickToken(q,&q,token);
3564 if (*token == ',')
3565 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003566 affine.rx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003567 GetMagickToken(q,&q,token);
3568 if (*token == ',')
3569 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003570 affine.ry=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003571 GetMagickToken(q,&q,token);
3572 if (*token == ',')
3573 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003574 affine.sy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003575 GetMagickToken(q,&q,token);
3576 if (*token == ',')
3577 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003578 affine.tx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003579 GetMagickToken(q,&q,token);
3580 if (*token == ',')
3581 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003582 affine.ty=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003583 break;
3584 }
3585 if (LocaleCompare("angle",keyword) == 0)
3586 {
3587 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003588 affine.rx=InterpretLocaleValue(token,(char **) NULL);
3589 affine.ry=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003590 break;
3591 }
3592 if (LocaleCompare("arc",keyword) == 0)
3593 {
3594 primitive_type=ArcPrimitive;
3595 break;
3596 }
3597 status=MagickFalse;
3598 break;
3599 }
3600 case 'b':
3601 case 'B':
3602 {
3603 if (LocaleCompare("bezier",keyword) == 0)
3604 {
3605 primitive_type=BezierPrimitive;
3606 break;
3607 }
3608 status=MagickFalse;
3609 break;
3610 }
3611 case 'c':
3612 case 'C':
3613 {
3614 if (LocaleCompare("clip-path",keyword) == 0)
3615 {
3616 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003617 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003618 "clip-path:url(#%s);",token);
3619 (void) WriteBlobString(image,message);
3620 break;
3621 }
3622 if (LocaleCompare("clip-rule",keyword) == 0)
3623 {
3624 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003625 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003626 "clip-rule:%s;",token);
3627 (void) WriteBlobString(image,message);
3628 break;
3629 }
3630 if (LocaleCompare("clip-units",keyword) == 0)
3631 {
3632 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003633 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003634 "clipPathUnits=%s;",token);
3635 (void) WriteBlobString(image,message);
3636 break;
3637 }
3638 if (LocaleCompare("circle",keyword) == 0)
3639 {
3640 primitive_type=CirclePrimitive;
3641 break;
3642 }
3643 if (LocaleCompare("color",keyword) == 0)
3644 {
3645 primitive_type=ColorPrimitive;
3646 break;
3647 }
3648 status=MagickFalse;
3649 break;
3650 }
3651 case 'd':
3652 case 'D':
3653 {
3654 if (LocaleCompare("decorate",keyword) == 0)
3655 {
3656 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003657 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003658 "text-decoration:%s;",token);
3659 (void) WriteBlobString(image,message);
3660 break;
3661 }
3662 status=MagickFalse;
3663 break;
3664 }
3665 case 'e':
3666 case 'E':
3667 {
3668 if (LocaleCompare("ellipse",keyword) == 0)
3669 {
3670 primitive_type=EllipsePrimitive;
3671 break;
3672 }
3673 status=MagickFalse;
3674 break;
3675 }
3676 case 'f':
3677 case 'F':
3678 {
3679 if (LocaleCompare("fill",keyword) == 0)
3680 {
3681 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003682 (void) FormatLocaleString(message,MaxTextExtent,"fill:%s;",
cristy3ed852e2009-09-05 21:47:34 +00003683 token);
3684 (void) WriteBlobString(image,message);
3685 break;
3686 }
3687 if (LocaleCompare("fill-rule",keyword) == 0)
3688 {
3689 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003690 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003691 "fill-rule:%s;",token);
3692 (void) WriteBlobString(image,message);
3693 break;
3694 }
3695 if (LocaleCompare("fill-opacity",keyword) == 0)
3696 {
3697 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003698 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003699 "fill-opacity:%s;",token);
3700 (void) WriteBlobString(image,message);
3701 break;
3702 }
3703 if (LocaleCompare("font-family",keyword) == 0)
3704 {
3705 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003706 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003707 "font-family:%s;",token);
3708 (void) WriteBlobString(image,message);
3709 break;
3710 }
3711 if (LocaleCompare("font-stretch",keyword) == 0)
3712 {
3713 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003714 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003715 "font-stretch:%s;",token);
3716 (void) WriteBlobString(image,message);
3717 break;
3718 }
3719 if (LocaleCompare("font-style",keyword) == 0)
3720 {
3721 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003722 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003723 "font-style:%s;",token);
3724 (void) WriteBlobString(image,message);
3725 break;
3726 }
3727 if (LocaleCompare("font-size",keyword) == 0)
3728 {
3729 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003730 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003731 "font-size:%s;",token);
3732 (void) WriteBlobString(image,message);
3733 break;
3734 }
3735 if (LocaleCompare("font-weight",keyword) == 0)
3736 {
3737 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003738 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003739 "font-weight:%s;",token);
3740 (void) WriteBlobString(image,message);
3741 break;
3742 }
3743 status=MagickFalse;
3744 break;
3745 }
3746 case 'g':
3747 case 'G':
3748 {
3749 if (LocaleCompare("gradient-units",keyword) == 0)
3750 {
3751 GetMagickToken(q,&q,token);
3752 break;
3753 }
3754 if (LocaleCompare("text-align",keyword) == 0)
3755 {
3756 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003757 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003758 "text-align %s ",token);
3759 (void) WriteBlobString(image,message);
3760 break;
3761 }
3762 if (LocaleCompare("text-anchor",keyword) == 0)
3763 {
3764 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003765 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003766 "text-anchor %s ",token);
3767 (void) WriteBlobString(image,message);
3768 break;
3769 }
3770 status=MagickFalse;
3771 break;
3772 }
3773 case 'i':
3774 case 'I':
3775 {
3776 if (LocaleCompare("image",keyword) == 0)
3777 {
3778 GetMagickToken(q,&q,token);
3779 primitive_type=ImagePrimitive;
3780 break;
3781 }
3782 status=MagickFalse;
3783 break;
3784 }
3785 case 'l':
3786 case 'L':
3787 {
3788 if (LocaleCompare("line",keyword) == 0)
3789 {
3790 primitive_type=LinePrimitive;
3791 break;
3792 }
3793 status=MagickFalse;
3794 break;
3795 }
3796 case 'm':
3797 case 'M':
3798 {
3799 if (LocaleCompare("matte",keyword) == 0)
3800 {
3801 primitive_type=MattePrimitive;
3802 break;
3803 }
3804 status=MagickFalse;
3805 break;
3806 }
3807 case 'o':
3808 case 'O':
3809 {
3810 if (LocaleCompare("opacity",keyword) == 0)
3811 {
3812 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003813 (void) FormatLocaleString(message,MaxTextExtent,"opacity %s ",
cristy3ed852e2009-09-05 21:47:34 +00003814 token);
3815 (void) WriteBlobString(image,message);
3816 break;
3817 }
3818 status=MagickFalse;
3819 break;
3820 }
3821 case 'p':
3822 case 'P':
3823 {
3824 if (LocaleCompare("path",keyword) == 0)
3825 {
3826 primitive_type=PathPrimitive;
3827 break;
3828 }
3829 if (LocaleCompare("point",keyword) == 0)
3830 {
3831 primitive_type=PointPrimitive;
3832 break;
3833 }
3834 if (LocaleCompare("polyline",keyword) == 0)
3835 {
3836 primitive_type=PolylinePrimitive;
3837 break;
3838 }
3839 if (LocaleCompare("polygon",keyword) == 0)
3840 {
3841 primitive_type=PolygonPrimitive;
3842 break;
3843 }
3844 if (LocaleCompare("pop",keyword) == 0)
3845 {
3846 GetMagickToken(q,&q,token);
3847 if (LocaleCompare("clip-path",token) == 0)
3848 {
3849 (void) WriteBlobString(image,"</clipPath>\n");
3850 break;
3851 }
3852 if (LocaleCompare("defs",token) == 0)
3853 {
3854 (void) WriteBlobString(image,"</defs>\n");
3855 break;
3856 }
3857 if (LocaleCompare("gradient",token) == 0)
3858 {
cristyb51dff52011-05-19 16:55:47 +00003859 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003860 "</%sGradient>\n",type);
3861 (void) WriteBlobString(image,message);
3862 break;
3863 }
3864 if (LocaleCompare("graphic-context",token) == 0)
3865 {
3866 n--;
3867 if (n < 0)
3868 ThrowWriterException(DrawError,
3869 "UnbalancedGraphicContextPushPop");
3870 (void) WriteBlobString(image,"</g>\n");
3871 }
3872 if (LocaleCompare("pattern",token) == 0)
3873 {
3874 (void) WriteBlobString(image,"</pattern>\n");
3875 break;
3876 }
3877 if (LocaleCompare("defs",token) == 0)
3878 (void) WriteBlobString(image,"</g>\n");
3879 break;
3880 }
3881 if (LocaleCompare("push",keyword) == 0)
3882 {
3883 GetMagickToken(q,&q,token);
3884 if (LocaleCompare("clip-path",token) == 0)
3885 {
3886 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003887 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003888 "<clipPath id=\"%s\">\n",token);
3889 (void) WriteBlobString(image,message);
3890 break;
3891 }
3892 if (LocaleCompare("defs",token) == 0)
3893 {
3894 (void) WriteBlobString(image,"<defs>\n");
3895 break;
3896 }
3897 if (LocaleCompare("gradient",token) == 0)
3898 {
3899 GetMagickToken(q,&q,token);
3900 (void) CopyMagickString(name,token,MaxTextExtent);
3901 GetMagickToken(q,&q,token);
3902 (void) CopyMagickString(type,token,MaxTextExtent);
3903 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003904 svg_info.segment.x1=InterpretLocaleValue(token,(char **) NULL);
3905 svg_info.element.cx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003906 GetMagickToken(q,&q,token);
3907 if (*token == ',')
3908 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003909 svg_info.segment.y1=InterpretLocaleValue(token,(char **) NULL);
3910 svg_info.element.cy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003911 GetMagickToken(q,&q,token);
3912 if (*token == ',')
3913 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003914 svg_info.segment.x2=InterpretLocaleValue(token,(char **) NULL);
3915 svg_info.element.major=InterpretLocaleValue(token,
3916 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003917 GetMagickToken(q,&q,token);
3918 if (*token == ',')
3919 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003920 svg_info.segment.y2=InterpretLocaleValue(token,(char **) NULL);
3921 svg_info.element.minor=InterpretLocaleValue(token,
3922 (char **) NULL);
cristyb51dff52011-05-19 16:55:47 +00003923 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003924 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
3925 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
cristy3ed852e2009-09-05 21:47:34 +00003926 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
3927 if (LocaleCompare(type,"radial") == 0)
3928 {
3929 GetMagickToken(q,&q,token);
3930 if (*token == ',')
3931 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003932 svg_info.element.angle=InterpretLocaleValue(token,
3933 (char **) NULL);
cristyb51dff52011-05-19 16:55:47 +00003934 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003935 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
3936 "fx=\"%g\" fy=\"%g\">\n",type,name,
cristy8cd5b312010-01-07 01:10:24 +00003937 svg_info.element.cx,svg_info.element.cy,
3938 svg_info.element.angle,svg_info.element.major,
3939 svg_info.element.minor);
cristy3ed852e2009-09-05 21:47:34 +00003940 }
3941 (void) WriteBlobString(image,message);
3942 break;
3943 }
3944 if (LocaleCompare("graphic-context",token) == 0)
3945 {
3946 n++;
3947 if (active)
3948 {
3949 AffineToTransform(image,&affine);
3950 active=MagickFalse;
3951 }
3952 (void) WriteBlobString(image,"<g style=\"");
3953 active=MagickTrue;
3954 }
3955 if (LocaleCompare("pattern",token) == 0)
3956 {
3957 GetMagickToken(q,&q,token);
3958 (void) CopyMagickString(name,token,MaxTextExtent);
3959 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003960 svg_info.bounds.x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003961 GetMagickToken(q,&q,token);
3962 if (*token == ',')
3963 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003964 svg_info.bounds.y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003965 GetMagickToken(q,&q,token);
3966 if (*token == ',')
3967 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003968 svg_info.bounds.width=InterpretLocaleValue(token,
3969 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003970 GetMagickToken(q,&q,token);
3971 if (*token == ',')
3972 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003973 svg_info.bounds.height=InterpretLocaleValue(token,
3974 (char **) NULL);
cristyb51dff52011-05-19 16:55:47 +00003975 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003976 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
3977 "height=\"%g\">\n",name,svg_info.bounds.x,
cristy8cd5b312010-01-07 01:10:24 +00003978 svg_info.bounds.y,svg_info.bounds.width,
3979 svg_info.bounds.height);
cristy3ed852e2009-09-05 21:47:34 +00003980 (void) WriteBlobString(image,message);
3981 break;
3982 }
3983 break;
3984 }
3985 status=MagickFalse;
3986 break;
3987 }
3988 case 'r':
3989 case 'R':
3990 {
3991 if (LocaleCompare("rectangle",keyword) == 0)
3992 {
3993 primitive_type=RectanglePrimitive;
3994 break;
3995 }
3996 if (LocaleCompare("roundRectangle",keyword) == 0)
3997 {
3998 primitive_type=RoundRectanglePrimitive;
3999 break;
4000 }
4001 if (LocaleCompare("rotate",keyword) == 0)
4002 {
4003 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004004 (void) FormatLocaleString(message,MaxTextExtent,"rotate(%s) ",
cristy3ed852e2009-09-05 21:47:34 +00004005 token);
4006 (void) WriteBlobString(image,message);
4007 break;
4008 }
4009 status=MagickFalse;
4010 break;
4011 }
4012 case 's':
4013 case 'S':
4014 {
4015 if (LocaleCompare("scale",keyword) == 0)
4016 {
4017 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00004018 affine.sx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004019 GetMagickToken(q,&q,token);
4020 if (*token == ',')
4021 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00004022 affine.sy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004023 break;
4024 }
4025 if (LocaleCompare("skewX",keyword) == 0)
4026 {
4027 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004028 (void) FormatLocaleString(message,MaxTextExtent,"skewX(%s) ",
cristy3ed852e2009-09-05 21:47:34 +00004029 token);
4030 (void) WriteBlobString(image,message);
4031 break;
4032 }
4033 if (LocaleCompare("skewY",keyword) == 0)
4034 {
4035 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004036 (void) FormatLocaleString(message,MaxTextExtent,"skewY(%s) ",
cristy3ed852e2009-09-05 21:47:34 +00004037 token);
4038 (void) WriteBlobString(image,message);
4039 break;
4040 }
4041 if (LocaleCompare("stop-color",keyword) == 0)
4042 {
4043 char
4044 color[MaxTextExtent];
4045
4046 GetMagickToken(q,&q,token);
4047 (void) CopyMagickString(color,token,MaxTextExtent);
4048 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004049 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004050 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4051 (void) WriteBlobString(image,message);
4052 break;
4053 }
4054 if (LocaleCompare("stroke",keyword) == 0)
4055 {
4056 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004057 (void) FormatLocaleString(message,MaxTextExtent,"stroke:%s;",
cristy3ed852e2009-09-05 21:47:34 +00004058 token);
4059 (void) WriteBlobString(image,message);
4060 break;
4061 }
4062 if (LocaleCompare("stroke-antialias",keyword) == 0)
4063 {
4064 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004065 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004066 "stroke-antialias:%s;",token);
4067 (void) WriteBlobString(image,message);
4068 break;
4069 }
4070 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4071 {
4072 if (IsPoint(q))
4073 {
cristybb503372010-05-27 20:51:26 +00004074 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004075 k;
4076
4077 p=q;
4078 GetMagickToken(p,&p,token);
4079 for (k=0; IsPoint(token); k++)
4080 GetMagickToken(p,&p,token);
4081 (void) WriteBlobString(image,"stroke-dasharray:");
4082 for (j=0; j < k; j++)
4083 {
4084 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004085 (void) FormatLocaleString(message,MaxTextExtent,"%s ",
cristy3ed852e2009-09-05 21:47:34 +00004086 token);
4087 (void) WriteBlobString(image,message);
4088 }
4089 (void) WriteBlobString(image,";");
4090 break;
4091 }
4092 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004093 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004094 "stroke-dasharray:%s;",token);
4095 (void) WriteBlobString(image,message);
4096 break;
4097 }
4098 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4099 {
4100 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004101 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004102 "stroke-dashoffset:%s;",token);
4103 (void) WriteBlobString(image,message);
4104 break;
4105 }
4106 if (LocaleCompare("stroke-linecap",keyword) == 0)
4107 {
4108 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004109 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004110 "stroke-linecap:%s;",token);
4111 (void) WriteBlobString(image,message);
4112 break;
4113 }
4114 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4115 {
4116 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004117 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004118 "stroke-linejoin:%s;",token);
4119 (void) WriteBlobString(image,message);
4120 break;
4121 }
4122 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4123 {
4124 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004125 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004126 "stroke-miterlimit:%s;",token);
4127 (void) WriteBlobString(image,message);
4128 break;
4129 }
4130 if (LocaleCompare("stroke-opacity",keyword) == 0)
4131 {
4132 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004133 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004134 "stroke-opacity:%s;",token);
4135 (void) WriteBlobString(image,message);
4136 break;
4137 }
4138 if (LocaleCompare("stroke-width",keyword) == 0)
4139 {
4140 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004141 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004142 "stroke-width:%s;",token);
4143 (void) WriteBlobString(image,message);
4144 continue;
4145 }
4146 status=MagickFalse;
4147 break;
4148 }
4149 case 't':
4150 case 'T':
4151 {
4152 if (LocaleCompare("text",keyword) == 0)
4153 {
4154 primitive_type=TextPrimitive;
4155 break;
4156 }
4157 if (LocaleCompare("text-antialias",keyword) == 0)
4158 {
4159 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004160 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004161 "text-antialias:%s;",token);
4162 (void) WriteBlobString(image,message);
4163 break;
4164 }
4165 if (LocaleCompare("tspan",keyword) == 0)
4166 {
4167 primitive_type=TextPrimitive;
4168 break;
4169 }
4170 if (LocaleCompare("translate",keyword) == 0)
4171 {
4172 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00004173 affine.tx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004174 GetMagickToken(q,&q,token);
4175 if (*token == ',')
4176 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00004177 affine.ty=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004178 break;
4179 }
4180 status=MagickFalse;
4181 break;
4182 }
4183 case 'v':
4184 case 'V':
4185 {
4186 if (LocaleCompare("viewbox",keyword) == 0)
4187 {
4188 GetMagickToken(q,&q,token);
4189 if (*token == ',')
4190 GetMagickToken(q,&q,token);
4191 GetMagickToken(q,&q,token);
4192 if (*token == ',')
4193 GetMagickToken(q,&q,token);
4194 GetMagickToken(q,&q,token);
4195 if (*token == ',')
4196 GetMagickToken(q,&q,token);
4197 GetMagickToken(q,&q,token);
4198 break;
4199 }
4200 status=MagickFalse;
4201 break;
4202 }
4203 default:
4204 {
4205 status=MagickFalse;
4206 break;
4207 }
4208 }
4209 if (status == MagickFalse)
4210 break;
4211 if (primitive_type == UndefinedPrimitive)
4212 continue;
4213 /*
4214 Parse the primitive attributes.
4215 */
4216 i=0;
4217 j=0;
4218 for (x=0; *q != '\0'; x++)
4219 {
4220 /*
4221 Define points.
4222 */
4223 if (IsPoint(q) == MagickFalse)
4224 break;
4225 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00004226 point.x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004227 GetMagickToken(q,&q,token);
4228 if (*token == ',')
4229 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00004230 point.y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004231 GetMagickToken(q,(const char **) NULL,token);
4232 if (*token == ',')
4233 GetMagickToken(q,&q,token);
4234 primitive_info[i].primitive=primitive_type;
4235 primitive_info[i].point=point;
4236 primitive_info[i].coordinates=0;
4237 primitive_info[i].method=FloodfillMethod;
4238 i++;
cristybb503372010-05-27 20:51:26 +00004239 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
cristy3ed852e2009-09-05 21:47:34 +00004240 continue;
4241 number_points+=6*BezierQuantum+360;
4242 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4243 number_points,sizeof(*primitive_info));
4244 if (primitive_info == (PrimitiveInfo *) NULL)
4245 {
cristy3a37efd2011-08-28 20:31:03 +00004246 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004247 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4248 break;
4249 }
4250 }
4251 primitive_info[j].primitive=primitive_type;
4252 primitive_info[j].coordinates=x;
4253 primitive_info[j].method=FloodfillMethod;
4254 primitive_info[j].text=(char *) NULL;
4255 if (active)
4256 {
4257 AffineToTransform(image,&affine);
4258 active=MagickFalse;
4259 }
4260 active=MagickFalse;
4261 switch (primitive_type)
4262 {
4263 case PointPrimitive:
4264 default:
4265 {
4266 if (primitive_info[j].coordinates != 1)
4267 {
4268 status=MagickFalse;
4269 break;
4270 }
4271 break;
4272 }
4273 case LinePrimitive:
4274 {
4275 if (primitive_info[j].coordinates != 2)
4276 {
4277 status=MagickFalse;
4278 break;
4279 }
cristyb51dff52011-05-19 16:55:47 +00004280 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004281 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004282 primitive_info[j].point.x,primitive_info[j].point.y,
4283 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4284 (void) WriteBlobString(image,message);
4285 break;
4286 }
4287 case RectanglePrimitive:
4288 {
4289 if (primitive_info[j].coordinates != 2)
4290 {
4291 status=MagickFalse;
4292 break;
4293 }
cristyb51dff52011-05-19 16:55:47 +00004294 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004295 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004296 primitive_info[j].point.x,primitive_info[j].point.y,
4297 primitive_info[j+1].point.x-primitive_info[j].point.x,
4298 primitive_info[j+1].point.y-primitive_info[j].point.y);
4299 (void) WriteBlobString(image,message);
4300 break;
4301 }
4302 case RoundRectanglePrimitive:
4303 {
4304 if (primitive_info[j].coordinates != 3)
4305 {
4306 status=MagickFalse;
4307 break;
4308 }
cristyb51dff52011-05-19 16:55:47 +00004309 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004310 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4311 "ry=\"%g\"/>\n",primitive_info[j].point.x,
cristy8cd5b312010-01-07 01:10:24 +00004312 primitive_info[j].point.y,primitive_info[j+1].point.x-
4313 primitive_info[j].point.x,primitive_info[j+1].point.y-
4314 primitive_info[j].point.y,primitive_info[j+2].point.x,
4315 primitive_info[j+2].point.y);
cristy3ed852e2009-09-05 21:47:34 +00004316 (void) WriteBlobString(image,message);
4317 break;
4318 }
4319 case ArcPrimitive:
4320 {
4321 if (primitive_info[j].coordinates != 3)
4322 {
4323 status=MagickFalse;
4324 break;
4325 }
4326 break;
4327 }
4328 case EllipsePrimitive:
4329 {
4330 if (primitive_info[j].coordinates != 3)
4331 {
4332 status=MagickFalse;
4333 break;
4334 }
cristyb51dff52011-05-19 16:55:47 +00004335 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004336 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004337 primitive_info[j].point.x,primitive_info[j].point.y,
4338 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4339 (void) WriteBlobString(image,message);
4340 break;
4341 }
4342 case CirclePrimitive:
4343 {
4344 double
4345 alpha,
4346 beta;
4347
4348 if (primitive_info[j].coordinates != 2)
4349 {
4350 status=MagickFalse;
4351 break;
4352 }
4353 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4354 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
cristyb51dff52011-05-19 16:55:47 +00004355 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004356 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004357 primitive_info[j].point.x,primitive_info[j].point.y,
4358 hypot(alpha,beta));
4359 (void) WriteBlobString(image,message);
4360 break;
4361 }
4362 case PolylinePrimitive:
4363 {
4364 if (primitive_info[j].coordinates < 2)
4365 {
4366 status=MagickFalse;
4367 break;
4368 }
4369 (void) CopyMagickString(message," <polyline points=\"",MaxTextExtent);
4370 (void) WriteBlobString(image,message);
4371 length=strlen(message);
4372 for ( ; j < i; j++)
4373 {
cristyb51dff52011-05-19 16:55:47 +00004374 (void) FormatLocaleString(message,MaxTextExtent,"%g,%g ",
cristy3ed852e2009-09-05 21:47:34 +00004375 primitive_info[j].point.x,primitive_info[j].point.y);
4376 length+=strlen(message);
4377 if (length >= 80)
4378 {
4379 (void) WriteBlobString(image,"\n ");
4380 length=strlen(message)+5;
4381 }
4382 (void) WriteBlobString(image,message);
4383 }
4384 (void) WriteBlobString(image,"\"/>\n");
4385 break;
4386 }
4387 case PolygonPrimitive:
4388 {
4389 if (primitive_info[j].coordinates < 3)
4390 {
4391 status=MagickFalse;
4392 break;
4393 }
4394 primitive_info[i]=primitive_info[j];
4395 primitive_info[i].coordinates=0;
4396 primitive_info[j].coordinates++;
4397 i++;
4398 (void) CopyMagickString(message," <polygon points=\"",MaxTextExtent);
4399 (void) WriteBlobString(image,message);
4400 length=strlen(message);
4401 for ( ; j < i; j++)
4402 {
cristyb51dff52011-05-19 16:55:47 +00004403 (void) FormatLocaleString(message,MaxTextExtent,"%g,%g ",
cristy3ed852e2009-09-05 21:47:34 +00004404 primitive_info[j].point.x,primitive_info[j].point.y);
4405 length+=strlen(message);
4406 if (length >= 80)
4407 {
4408 (void) WriteBlobString(image,"\n ");
4409 length=strlen(message)+5;
4410 }
4411 (void) WriteBlobString(image,message);
4412 }
4413 (void) WriteBlobString(image,"\"/>\n");
4414 break;
4415 }
4416 case BezierPrimitive:
4417 {
4418 if (primitive_info[j].coordinates < 3)
4419 {
4420 status=MagickFalse;
4421 break;
4422 }
4423 break;
4424 }
4425 case PathPrimitive:
4426 {
4427 int
4428 number_attributes;
4429
4430 GetMagickToken(q,&q,token);
4431 number_attributes=1;
4432 for (p=token; *p != '\0'; p++)
4433 if (isalpha((int) *p))
4434 number_attributes++;
cristybb503372010-05-27 20:51:26 +00004435 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
cristy3ed852e2009-09-05 21:47:34 +00004436 {
4437 number_points+=6*BezierQuantum*number_attributes;
4438 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4439 number_points,sizeof(*primitive_info));
4440 if (primitive_info == (PrimitiveInfo *) NULL)
4441 {
cristy3a37efd2011-08-28 20:31:03 +00004442 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004443 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4444 image->filename);
4445 break;
4446 }
4447 }
4448 (void) WriteBlobString(image," <path d=\"");
4449 (void) WriteBlobString(image,token);
4450 (void) WriteBlobString(image,"\"/>\n");
4451 break;
4452 }
4453 case ColorPrimitive:
4454 case MattePrimitive:
4455 {
4456 if (primitive_info[j].coordinates != 1)
4457 {
4458 status=MagickFalse;
4459 break;
4460 }
4461 GetMagickToken(q,&q,token);
4462 if (LocaleCompare("point",token) == 0)
4463 primitive_info[j].method=PointMethod;
4464 if (LocaleCompare("replace",token) == 0)
4465 primitive_info[j].method=ReplaceMethod;
4466 if (LocaleCompare("floodfill",token) == 0)
4467 primitive_info[j].method=FloodfillMethod;
4468 if (LocaleCompare("filltoborder",token) == 0)
4469 primitive_info[j].method=FillToBorderMethod;
4470 if (LocaleCompare("reset",token) == 0)
4471 primitive_info[j].method=ResetMethod;
4472 break;
4473 }
4474 case TextPrimitive:
4475 {
4476 register char
4477 *p;
4478
4479 if (primitive_info[j].coordinates != 1)
4480 {
4481 status=MagickFalse;
4482 break;
4483 }
4484 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004485 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004486 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
cristy3ed852e2009-09-05 21:47:34 +00004487 primitive_info[j].point.y);
4488 (void) WriteBlobString(image,message);
4489 for (p=token; *p != '\0'; p++)
4490 switch (*p)
4491 {
4492 case '<': (void) WriteBlobString(image,"&lt;"); break;
4493 case '>': (void) WriteBlobString(image,"&gt;"); break;
4494 case '&': (void) WriteBlobString(image,"&amp;"); break;
4495 default: (void) WriteBlobByte(image,*p); break;
4496 }
4497 (void) WriteBlobString(image,"</text>\n");
4498 break;
4499 }
4500 case ImagePrimitive:
4501 {
4502 if (primitive_info[j].coordinates != 2)
4503 {
4504 status=MagickFalse;
4505 break;
4506 }
4507 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004508 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004509 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
cristy3ed852e2009-09-05 21:47:34 +00004510 "xlink:href=\"%s\"/>\n",primitive_info[j].point.x,
4511 primitive_info[j].point.y,primitive_info[j+1].point.x,
4512 primitive_info[j+1].point.y,token);
4513 (void) WriteBlobString(image,message);
4514 break;
4515 }
4516 }
4517 if (primitive_info == (PrimitiveInfo *) NULL)
4518 break;
4519 primitive_info[i].primitive=UndefinedPrimitive;
4520 if (status == MagickFalse)
4521 break;
4522 }
4523 (void) WriteBlobString(image,"</svg>\n");
4524 /*
4525 Relinquish resources.
4526 */
4527 token=DestroyString(token);
4528 if (primitive_info != (PrimitiveInfo *) NULL)
4529 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4530 (void) CloseBlob(image);
4531 return(status);
4532}