blob: 0c4f87eb6c6437d12d5862bf7891e1b571aaf0df [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% %
cristy1454be72011-12-19 01:52:48 +000021% Copyright 1999-2012 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);
cristydbdd0e32011-11-04 23:29:40 +0000300 value=StringToDouble(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");
cristye4c6a422011-11-24 18:57:53 +00001095 svg_info->bounds.x=0.0;
1096 svg_info->bounds.y=0.0;
1097 svg_info->bounds.width=0.0;
1098 svg_info->bounds.height=0.0;
cristy3ed852e2009-09-05 21:47:34 +00001099 break;
1100 }
1101 if (LocaleCompare((const char *) name,"tspan") == 0)
1102 {
1103 if (*svg_info->text != '\0')
1104 {
1105 DrawInfo
1106 *draw_info;
1107
1108 TypeMetric
1109 metrics;
1110
1111 char
1112 *text;
1113
1114 text=EscapeString(svg_info->text,'\'');
cristyb51dff52011-05-19 16:55:47 +00001115 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
cristy8cd5b312010-01-07 01:10:24 +00001116 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1117 svg_info->center.y,text);
cristy3ed852e2009-09-05 21:47:34 +00001118 text=DestroyString(text);
1119 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
1120 draw_info->pointsize=svg_info->pointsize;
1121 draw_info->text=AcquireString(svg_info->text);
1122 (void) ConcatenateString(&draw_info->text," ");
cristy5cbc0162011-08-29 00:36:28 +00001123 (void) GetTypeMetrics(svg_info->image,draw_info,
1124 &metrics,svg_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001125 svg_info->bounds.x+=metrics.width;
1126 draw_info=DestroyDrawInfo(draw_info);
1127 *svg_info->text='\0';
1128 }
cristyb51dff52011-05-19 16:55:47 +00001129 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001130 break;
1131 }
1132 break;
1133 }
1134 default:
1135 break;
1136 }
1137 if (attributes != (const xmlChar **) NULL)
1138 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1139 {
1140 keyword=(const char *) attributes[i];
1141 value=(const char *) attributes[i+1];
1142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1143 " %s = %s",keyword,value);
1144 switch (*keyword)
1145 {
1146 case 'A':
1147 case 'a':
1148 {
1149 if (LocaleCompare(keyword,"angle") == 0)
1150 {
cristyb51dff52011-05-19 16:55:47 +00001151 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001152 GetUserSpaceCoordinateValue(svg_info,0,value));
1153 break;
1154 }
1155 break;
1156 }
1157 case 'C':
1158 case 'c':
1159 {
1160 if (LocaleCompare(keyword,"clip-path") == 0)
1161 {
cristyb51dff52011-05-19 16:55:47 +00001162 (void) FormatLocaleFile(svg_info->file,"clip-path '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001163 break;
1164 }
1165 if (LocaleCompare(keyword,"clip-rule") == 0)
1166 {
cristyb51dff52011-05-19 16:55:47 +00001167 (void) FormatLocaleFile(svg_info->file,"clip-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001168 break;
1169 }
1170 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1171 {
1172 (void) CloneString(&units,value);
cristyb51dff52011-05-19 16:55:47 +00001173 (void) FormatLocaleFile(svg_info->file,"clip-units '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001174 break;
1175 }
1176 if (LocaleCompare(keyword,"color") == 0)
1177 {
1178 (void) CloneString(&color,value);
1179 break;
1180 }
1181 if (LocaleCompare(keyword,"cx") == 0)
1182 {
1183 svg_info->element.cx=
1184 GetUserSpaceCoordinateValue(svg_info,1,value);
1185 break;
1186 }
1187 if (LocaleCompare(keyword,"cy") == 0)
1188 {
1189 svg_info->element.cy=
1190 GetUserSpaceCoordinateValue(svg_info,-1,value);
1191 break;
1192 }
1193 break;
1194 }
1195 case 'D':
1196 case 'd':
1197 {
1198 if (LocaleCompare(keyword,"d") == 0)
1199 {
1200 (void) CloneString(&svg_info->vertices,value);
1201 break;
1202 }
1203 if (LocaleCompare(keyword,"dx") == 0)
1204 {
1205 svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
1206 break;
1207 }
1208 if (LocaleCompare(keyword,"dy") == 0)
1209 {
1210 svg_info->bounds.y+=
1211 GetUserSpaceCoordinateValue(svg_info,-1,value);
1212 break;
1213 }
1214 break;
1215 }
1216 case 'F':
1217 case 'f':
1218 {
1219 if (LocaleCompare(keyword,"fill") == 0)
1220 {
1221 if (LocaleCompare(value,"currentColor") == 0)
1222 {
cristyb51dff52011-05-19 16:55:47 +00001223 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001224 break;
1225 }
cristyb51dff52011-05-19 16:55:47 +00001226 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001227 break;
1228 }
1229 if (LocaleCompare(keyword,"fillcolor") == 0)
1230 {
cristyb51dff52011-05-19 16:55:47 +00001231 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001232 break;
1233 }
1234 if (LocaleCompare(keyword,"fill-rule") == 0)
1235 {
cristyb51dff52011-05-19 16:55:47 +00001236 (void) FormatLocaleFile(svg_info->file,"fill-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001237 break;
1238 }
1239 if (LocaleCompare(keyword,"fill-opacity") == 0)
1240 {
cristyb51dff52011-05-19 16:55:47 +00001241 (void) FormatLocaleFile(svg_info->file,"fill-opacity '%s'\n",
1242 value);
cristy3ed852e2009-09-05 21:47:34 +00001243 break;
1244 }
1245 if (LocaleCompare(keyword,"font-family") == 0)
1246 {
cristyb51dff52011-05-19 16:55:47 +00001247 (void) FormatLocaleFile(svg_info->file,"font-family '%s'\n",
1248 value);
cristy3ed852e2009-09-05 21:47:34 +00001249 break;
1250 }
1251 if (LocaleCompare(keyword,"font-stretch") == 0)
1252 {
cristyb51dff52011-05-19 16:55:47 +00001253 (void) FormatLocaleFile(svg_info->file,"font-stretch '%s'\n",
1254 value);
cristy3ed852e2009-09-05 21:47:34 +00001255 break;
1256 }
1257 if (LocaleCompare(keyword,"font-style") == 0)
1258 {
cristyb51dff52011-05-19 16:55:47 +00001259 (void) FormatLocaleFile(svg_info->file,"font-style '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001260 break;
1261 }
1262 if (LocaleCompare(keyword,"font-size") == 0)
1263 {
1264 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
cristyb51dff52011-05-19 16:55:47 +00001265 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1266 svg_info->pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001267 break;
1268 }
1269 if (LocaleCompare(keyword,"font-weight") == 0)
1270 {
cristyb51dff52011-05-19 16:55:47 +00001271 (void) FormatLocaleFile(svg_info->file,"font-weight '%s'\n",
1272 value);
cristy3ed852e2009-09-05 21:47:34 +00001273 break;
1274 }
1275 break;
1276 }
1277 case 'G':
1278 case 'g':
1279 {
1280 if (LocaleCompare(keyword,"gradientTransform") == 0)
1281 {
1282 AffineMatrix
1283 affine,
1284 current,
1285 transform;
1286
1287 GetAffineMatrix(&transform);
1288 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1289 tokens=GetTransformTokens(context,value,&number_tokens);
1290 for (j=0; j < (number_tokens-1); j+=2)
1291 {
1292 keyword=(char *) tokens[j];
1293 if (keyword == (char *) NULL)
1294 continue;
1295 value=(char *) tokens[j+1];
1296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1297 " %s: %s",keyword,value);
1298 current=transform;
1299 GetAffineMatrix(&affine);
1300 switch (*keyword)
1301 {
1302 case 'M':
1303 case 'm':
1304 {
1305 if (LocaleCompare(keyword,"matrix") == 0)
1306 {
1307 p=(const char *) value;
1308 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001309 affine.sx=StringToDouble(value,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001310 GetMagickToken(p,&p,token);
1311 if (*token == ',')
1312 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001313 affine.rx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001314 GetMagickToken(p,&p,token);
1315 if (*token == ',')
1316 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001317 affine.ry=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001318 GetMagickToken(p,&p,token);
1319 if (*token == ',')
1320 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001321 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001322 GetMagickToken(p,&p,token);
1323 if (*token == ',')
1324 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001325 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001326 GetMagickToken(p,&p,token);
1327 if (*token == ',')
1328 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001329 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001330 break;
1331 }
1332 break;
1333 }
1334 case 'R':
1335 case 'r':
1336 {
1337 if (LocaleCompare(keyword,"rotate") == 0)
1338 {
1339 double
1340 angle;
1341
1342 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1343 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1344 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1345 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1346 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1347 break;
1348 }
1349 break;
1350 }
1351 case 'S':
1352 case 's':
1353 {
1354 if (LocaleCompare(keyword,"scale") == 0)
1355 {
1356 for (p=(const char *) value; *p != '\0'; p++)
1357 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1358 (*p == ','))
1359 break;
1360 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1361 affine.sy=affine.sx;
1362 if (*p != '\0')
1363 affine.sy=
1364 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1365 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1366 break;
1367 }
1368 if (LocaleCompare(keyword,"skewX") == 0)
1369 {
1370 affine.sx=svg_info->affine.sx;
1371 affine.ry=tan(DegreesToRadians(fmod(
1372 GetUserSpaceCoordinateValue(svg_info,1,value),
1373 360.0)));
1374 affine.sy=svg_info->affine.sy;
1375 break;
1376 }
1377 if (LocaleCompare(keyword,"skewY") == 0)
1378 {
1379 affine.sx=svg_info->affine.sx;
1380 affine.rx=tan(DegreesToRadians(fmod(
1381 GetUserSpaceCoordinateValue(svg_info,-1,value),
1382 360.0)));
1383 affine.sy=svg_info->affine.sy;
1384 break;
1385 }
1386 break;
1387 }
1388 case 'T':
1389 case 't':
1390 {
1391 if (LocaleCompare(keyword,"translate") == 0)
1392 {
1393 for (p=(const char *) value; *p != '\0'; p++)
1394 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1395 (*p == ','))
1396 break;
1397 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1398 affine.ty=affine.tx;
1399 if (*p != '\0')
1400 affine.ty=
1401 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1402 break;
1403 }
1404 break;
1405 }
1406 default:
1407 break;
1408 }
cristyef7c8a52010-10-10 13:46:51 +00001409 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1410 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1411 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1412 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
cristy2ac30482011-11-08 20:24:57 +00001413 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1414 current.tx;
1415 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1416 current.ty;
cristy3ed852e2009-09-05 21:47:34 +00001417 }
cristyb51dff52011-05-19 16:55:47 +00001418 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001419 "affine %g %g %g %g %g %g\n",transform.sx,
cristy8cd5b312010-01-07 01:10:24 +00001420 transform.rx,transform.ry,transform.sy,transform.tx,
1421 transform.ty);
cristy3ed852e2009-09-05 21:47:34 +00001422 for (j=0; tokens[j] != (char *) NULL; j++)
1423 tokens[j]=DestroyString(tokens[j]);
1424 tokens=(char **) RelinquishMagickMemory(tokens);
1425 break;
1426 }
1427 if (LocaleCompare(keyword,"gradientUnits") == 0)
1428 {
1429 (void) CloneString(&units,value);
cristyb51dff52011-05-19 16:55:47 +00001430 (void) FormatLocaleFile(svg_info->file,"gradient-units '%s'\n",
1431 value);
cristy3ed852e2009-09-05 21:47:34 +00001432 break;
1433 }
1434 break;
1435 }
1436 case 'H':
1437 case 'h':
1438 {
1439 if (LocaleCompare(keyword,"height") == 0)
1440 {
1441 svg_info->bounds.height=
1442 GetUserSpaceCoordinateValue(svg_info,-1,value);
1443 break;
1444 }
1445 if (LocaleCompare(keyword,"href") == 0)
1446 {
1447 (void) CloneString(&svg_info->url,value);
1448 break;
1449 }
1450 break;
1451 }
1452 case 'M':
1453 case 'm':
1454 {
1455 if (LocaleCompare(keyword,"major") == 0)
1456 {
1457 svg_info->element.major=
1458 GetUserSpaceCoordinateValue(svg_info,1,value);
1459 break;
1460 }
1461 if (LocaleCompare(keyword,"minor") == 0)
1462 {
1463 svg_info->element.minor=
1464 GetUserSpaceCoordinateValue(svg_info,-1,value);
1465 break;
1466 }
1467 break;
1468 }
1469 case 'O':
1470 case 'o':
1471 {
1472 if (LocaleCompare(keyword,"offset") == 0)
1473 {
1474 (void) CloneString(&svg_info->offset,value);
1475 break;
1476 }
1477 if (LocaleCompare(keyword,"opacity") == 0)
1478 {
cristyb51dff52011-05-19 16:55:47 +00001479 (void) FormatLocaleFile(svg_info->file,"opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001480 break;
1481 }
1482 break;
1483 }
1484 case 'P':
1485 case 'p':
1486 {
1487 if (LocaleCompare(keyword,"path") == 0)
1488 {
1489 (void) CloneString(&svg_info->url,value);
1490 break;
1491 }
1492 if (LocaleCompare(keyword,"points") == 0)
1493 {
1494 (void) CloneString(&svg_info->vertices,value);
1495 break;
1496 }
1497 break;
1498 }
1499 case 'R':
1500 case 'r':
1501 {
1502 if (LocaleCompare(keyword,"r") == 0)
1503 {
1504 svg_info->element.major=
1505 GetUserSpaceCoordinateValue(svg_info,1,value);
1506 svg_info->element.minor=
1507 GetUserSpaceCoordinateValue(svg_info,-1,value);
1508 break;
1509 }
1510 if (LocaleCompare(keyword,"rotate") == 0)
1511 {
1512 double
1513 angle;
1514
1515 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
cristyb51dff52011-05-19 16:55:47 +00001516 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
cristy8cd5b312010-01-07 01:10:24 +00001517 svg_info->bounds.x,svg_info->bounds.y);
cristy3ed852e2009-09-05 21:47:34 +00001518 svg_info->bounds.x=0;
1519 svg_info->bounds.y=0;
cristyb51dff52011-05-19 16:55:47 +00001520 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
cristy3ed852e2009-09-05 21:47:34 +00001521 break;
1522 }
1523 if (LocaleCompare(keyword,"rx") == 0)
1524 {
1525 if (LocaleCompare((const char *) name,"ellipse") == 0)
1526 svg_info->element.major=
1527 GetUserSpaceCoordinateValue(svg_info,1,value);
1528 else
1529 svg_info->radius.x=
1530 GetUserSpaceCoordinateValue(svg_info,1,value);
1531 break;
1532 }
1533 if (LocaleCompare(keyword,"ry") == 0)
1534 {
1535 if (LocaleCompare((const char *) name,"ellipse") == 0)
1536 svg_info->element.minor=
1537 GetUserSpaceCoordinateValue(svg_info,-1,value);
1538 else
1539 svg_info->radius.y=
1540 GetUserSpaceCoordinateValue(svg_info,-1,value);
1541 break;
1542 }
1543 break;
1544 }
1545 case 'S':
1546 case 's':
1547 {
1548 if (LocaleCompare(keyword,"stop-color") == 0)
1549 {
1550 (void) CloneString(&svg_info->stop_color,value);
1551 break;
1552 }
1553 if (LocaleCompare(keyword,"stroke") == 0)
1554 {
1555 if (LocaleCompare(value,"currentColor") == 0)
1556 {
cristyb51dff52011-05-19 16:55:47 +00001557 (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001558 break;
1559 }
cristyb51dff52011-05-19 16:55:47 +00001560 (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001561 break;
1562 }
1563 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1564 {
cristyb51dff52011-05-19 16:55:47 +00001565 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001566 LocaleCompare(value,"true") == 0);
1567 break;
1568 }
1569 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1570 {
cristyb51dff52011-05-19 16:55:47 +00001571 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1572 value);
cristy3ed852e2009-09-05 21:47:34 +00001573 break;
1574 }
1575 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1576 {
cristyb51dff52011-05-19 16:55:47 +00001577 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %s\n",
1578 value);
cristy3ed852e2009-09-05 21:47:34 +00001579 break;
1580 }
1581 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1582 {
cristyb51dff52011-05-19 16:55:47 +00001583 (void) FormatLocaleFile(svg_info->file,"stroke-linecap '%s'\n",
1584 value);
cristy3ed852e2009-09-05 21:47:34 +00001585 break;
1586 }
1587 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1588 {
cristyb51dff52011-05-19 16:55:47 +00001589 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin '%s'\n",
1590 value);
cristy3ed852e2009-09-05 21:47:34 +00001591 break;
1592 }
1593 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1594 {
cristyb51dff52011-05-19 16:55:47 +00001595 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit '%s'\n",
1596 value);
cristy3ed852e2009-09-05 21:47:34 +00001597 break;
1598 }
1599 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1600 {
cristyb51dff52011-05-19 16:55:47 +00001601 (void) FormatLocaleFile(svg_info->file,"stroke-opacity '%s'\n",
1602 value);
cristy3ed852e2009-09-05 21:47:34 +00001603 break;
1604 }
1605 if (LocaleCompare(keyword,"stroke-width") == 0)
1606 {
cristyb51dff52011-05-19 16:55:47 +00001607 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001608 GetUserSpaceCoordinateValue(svg_info,1,value));
1609 break;
1610 }
1611 if (LocaleCompare(keyword,"style") == 0)
1612 {
1613 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1614 tokens=GetStyleTokens(context,value,&number_tokens);
1615 for (j=0; j < (number_tokens-1); j+=2)
1616 {
1617 keyword=(char *) tokens[j];
1618 value=(char *) tokens[j+1];
1619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1620 " %s: %s",keyword,value);
1621 switch (*keyword)
1622 {
1623 case 'C':
1624 case 'c':
1625 {
1626 if (LocaleCompare(keyword,"clip-path") == 0)
1627 {
cristyb51dff52011-05-19 16:55:47 +00001628 (void) FormatLocaleFile(svg_info->file,
1629 "clip-path '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001630 break;
1631 }
1632 if (LocaleCompare(keyword,"clip-rule") == 0)
1633 {
cristyb51dff52011-05-19 16:55:47 +00001634 (void) FormatLocaleFile(svg_info->file,
1635 "clip-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001636 break;
1637 }
1638 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1639 {
1640 (void) CloneString(&units,value);
cristyb51dff52011-05-19 16:55:47 +00001641 (void) FormatLocaleFile(svg_info->file,
1642 "clip-units '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001643 break;
1644 }
1645 if (LocaleCompare(keyword,"color") == 0)
1646 {
1647 (void) CloneString(&color,value);
1648 break;
1649 }
1650 break;
1651 }
1652 case 'F':
1653 case 'f':
1654 {
1655 if (LocaleCompare(keyword,"fill") == 0)
1656 {
1657 if (LocaleCompare(value,"currentColor") == 0)
1658 {
cristyb51dff52011-05-19 16:55:47 +00001659 (void) FormatLocaleFile(svg_info->file,
1660 "fill '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001661 break;
1662 }
1663 if (LocaleCompare(value,"#00000000") == 0)
cristyb51dff52011-05-19 16:55:47 +00001664 (void) FormatLocaleFile(svg_info->file,
1665 "fill '#000000'\n");
cristy3ed852e2009-09-05 21:47:34 +00001666 else
cristyb51dff52011-05-19 16:55:47 +00001667 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1668 value);
cristy3ed852e2009-09-05 21:47:34 +00001669 break;
1670 }
1671 if (LocaleCompare(keyword,"fillcolor") == 0)
1672 {
cristyb51dff52011-05-19 16:55:47 +00001673 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1674 value);
cristy3ed852e2009-09-05 21:47:34 +00001675 break;
1676 }
1677 if (LocaleCompare(keyword,"fill-rule") == 0)
1678 {
cristyb51dff52011-05-19 16:55:47 +00001679 (void) FormatLocaleFile(svg_info->file,
1680 "fill-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001681 break;
1682 }
1683 if (LocaleCompare(keyword,"fill-opacity") == 0)
1684 {
cristyb51dff52011-05-19 16:55:47 +00001685 (void) FormatLocaleFile(svg_info->file,
1686 "fill-opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001687 break;
1688 }
1689 if (LocaleCompare(keyword,"font-family") == 0)
1690 {
cristyb51dff52011-05-19 16:55:47 +00001691 (void) FormatLocaleFile(svg_info->file,
1692 "font-family '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001693 break;
1694 }
1695 if (LocaleCompare(keyword,"font-stretch") == 0)
1696 {
cristyb51dff52011-05-19 16:55:47 +00001697 (void) FormatLocaleFile(svg_info->file,
1698 "font-stretch '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001699 break;
1700 }
1701 if (LocaleCompare(keyword,"font-style") == 0)
1702 {
cristyb51dff52011-05-19 16:55:47 +00001703 (void) FormatLocaleFile(svg_info->file,
1704 "font-style '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001705 break;
1706 }
1707 if (LocaleCompare(keyword,"font-size") == 0)
1708 {
1709 svg_info->pointsize=GetUserSpaceCoordinateValue(
1710 svg_info,0,value);
cristyb51dff52011-05-19 16:55:47 +00001711 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001712 svg_info->pointsize);
1713 break;
1714 }
1715 if (LocaleCompare(keyword,"font-weight") == 0)
1716 {
cristyb51dff52011-05-19 16:55:47 +00001717 (void) FormatLocaleFile(svg_info->file,
1718 "font-weight '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001719 break;
1720 }
1721 break;
1722 }
1723 case 'O':
1724 case 'o':
1725 {
1726 if (LocaleCompare(keyword,"offset") == 0)
1727 {
cristyb51dff52011-05-19 16:55:47 +00001728 (void) FormatLocaleFile(svg_info->file,"offset %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001729 GetUserSpaceCoordinateValue(svg_info,1,value));
1730 break;
1731 }
1732 if (LocaleCompare(keyword,"opacity") == 0)
1733 {
cristyb51dff52011-05-19 16:55:47 +00001734 (void) FormatLocaleFile(svg_info->file,
1735 "opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001736 break;
1737 }
1738 break;
1739 }
1740 case 'S':
1741 case 's':
1742 {
1743 if (LocaleCompare(keyword,"stop-color") == 0)
1744 {
1745 (void) CloneString(&svg_info->stop_color,value);
1746 break;
1747 }
1748 if (LocaleCompare(keyword,"stroke") == 0)
1749 {
1750 if (LocaleCompare(value,"currentColor") == 0)
1751 {
cristyb51dff52011-05-19 16:55:47 +00001752 (void) FormatLocaleFile(svg_info->file,
1753 "stroke '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001754 break;
1755 }
1756 if (LocaleCompare(value,"#00000000") == 0)
cristyb51dff52011-05-19 16:55:47 +00001757 (void) FormatLocaleFile(svg_info->file,
1758 "fill '#000000'\n");
cristy3ed852e2009-09-05 21:47:34 +00001759 else
cristyb51dff52011-05-19 16:55:47 +00001760 (void) FormatLocaleFile(svg_info->file,
1761 "stroke '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001762 break;
1763 }
1764 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1765 {
cristyb51dff52011-05-19 16:55:47 +00001766 (void) FormatLocaleFile(svg_info->file,
1767 "stroke-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001768 LocaleCompare(value,"true") == 0);
1769 break;
1770 }
1771 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1772 {
cristyb51dff52011-05-19 16:55:47 +00001773 (void) FormatLocaleFile(svg_info->file,
1774 "stroke-dasharray %s\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001775 break;
1776 }
1777 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1778 {
cristyb51dff52011-05-19 16:55:47 +00001779 (void) FormatLocaleFile(svg_info->file,
1780 "stroke-dashoffset %s\n",
cristy3ed852e2009-09-05 21:47:34 +00001781 value);
1782 break;
1783 }
1784 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1785 {
cristyb51dff52011-05-19 16:55:47 +00001786 (void) FormatLocaleFile(svg_info->file,
1787 "stroke-linecap '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001788 break;
1789 }
1790 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1791 {
cristyb51dff52011-05-19 16:55:47 +00001792 (void) FormatLocaleFile(svg_info->file,
1793 "stroke-linejoin '%s'\n",
cristy3ed852e2009-09-05 21:47:34 +00001794 value);
1795 break;
1796 }
1797 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1798 {
cristyb51dff52011-05-19 16:55:47 +00001799 (void) FormatLocaleFile(svg_info->file,
1800 "stroke-miterlimit '%s'\n",
cristy3ed852e2009-09-05 21:47:34 +00001801 value);
1802 break;
1803 }
1804 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1805 {
cristyb51dff52011-05-19 16:55:47 +00001806 (void) FormatLocaleFile(svg_info->file,
1807 "stroke-opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001808 break;
1809 }
1810 if (LocaleCompare(keyword,"stroke-width") == 0)
1811 {
cristyb51dff52011-05-19 16:55:47 +00001812 (void) FormatLocaleFile(svg_info->file,
1813 "stroke-width %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001814 GetUserSpaceCoordinateValue(svg_info,1,value));
1815 break;
1816 }
1817 break;
1818 }
1819 case 't':
1820 case 'T':
1821 {
1822 if (LocaleCompare(keyword,"text-align") == 0)
1823 {
cristyb51dff52011-05-19 16:55:47 +00001824 (void) FormatLocaleFile(svg_info->file,
1825 "text-align '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001826 break;
1827 }
1828 if (LocaleCompare(keyword,"text-anchor") == 0)
1829 {
cristyb51dff52011-05-19 16:55:47 +00001830 (void) FormatLocaleFile(svg_info->file,
1831 "text-anchor '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001832 break;
1833 }
1834 if (LocaleCompare(keyword,"text-decoration") == 0)
1835 {
1836 if (LocaleCompare(value,"underline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001837 (void) FormatLocaleFile(svg_info->file,
1838 "decorate underline\n");
cristy3ed852e2009-09-05 21:47:34 +00001839 if (LocaleCompare(value,"line-through") == 0)
cristyb51dff52011-05-19 16:55:47 +00001840 (void) FormatLocaleFile(svg_info->file,
1841 "decorate line-through\n");
cristy3ed852e2009-09-05 21:47:34 +00001842 if (LocaleCompare(value,"overline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001843 (void) FormatLocaleFile(svg_info->file,
1844 "decorate overline\n");
cristy3ed852e2009-09-05 21:47:34 +00001845 break;
1846 }
1847 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1848 {
cristyb51dff52011-05-19 16:55:47 +00001849 (void) FormatLocaleFile(svg_info->file,
1850 "text-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001851 LocaleCompare(value,"true") == 0);
1852 break;
1853 }
1854 break;
1855 }
1856 default:
1857 break;
1858 }
1859 }
1860 for (j=0; tokens[j] != (char *) NULL; j++)
1861 tokens[j]=DestroyString(tokens[j]);
1862 tokens=(char **) RelinquishMagickMemory(tokens);
1863 break;
1864 }
1865 break;
1866 }
1867 case 'T':
1868 case 't':
1869 {
1870 if (LocaleCompare(keyword,"text-align") == 0)
1871 {
cristyb51dff52011-05-19 16:55:47 +00001872 (void) FormatLocaleFile(svg_info->file,"text-align '%s'\n",
1873 value);
cristy3ed852e2009-09-05 21:47:34 +00001874 break;
1875 }
1876 if (LocaleCompare(keyword,"text-anchor") == 0)
1877 {
cristyb51dff52011-05-19 16:55:47 +00001878 (void) FormatLocaleFile(svg_info->file,"text-anchor '%s'\n",
1879 value);
cristy3ed852e2009-09-05 21:47:34 +00001880 break;
1881 }
1882 if (LocaleCompare(keyword,"text-decoration") == 0)
1883 {
1884 if (LocaleCompare(value,"underline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001885 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
cristy3ed852e2009-09-05 21:47:34 +00001886 if (LocaleCompare(value,"line-through") == 0)
cristyb51dff52011-05-19 16:55:47 +00001887 (void) FormatLocaleFile(svg_info->file,
1888 "decorate line-through\n");
cristy3ed852e2009-09-05 21:47:34 +00001889 if (LocaleCompare(value,"overline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001890 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
cristy3ed852e2009-09-05 21:47:34 +00001891 break;
1892 }
1893 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1894 {
cristyb51dff52011-05-19 16:55:47 +00001895 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001896 LocaleCompare(value,"true") == 0);
1897 break;
1898 }
1899 if (LocaleCompare(keyword,"transform") == 0)
1900 {
1901 AffineMatrix
1902 affine,
1903 current,
1904 transform;
1905
1906 GetAffineMatrix(&transform);
1907 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1908 tokens=GetTransformTokens(context,value,&number_tokens);
1909 for (j=0; j < (number_tokens-1); j+=2)
1910 {
1911 keyword=(char *) tokens[j];
1912 value=(char *) tokens[j+1];
1913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1914 " %s: %s",keyword,value);
1915 current=transform;
1916 GetAffineMatrix(&affine);
1917 switch (*keyword)
1918 {
1919 case 'M':
1920 case 'm':
1921 {
1922 if (LocaleCompare(keyword,"matrix") == 0)
1923 {
1924 p=(const char *) value;
1925 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001926 affine.sx=StringToDouble(value,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001927 GetMagickToken(p,&p,token);
1928 if (*token == ',')
1929 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001930 affine.rx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001931 GetMagickToken(p,&p,token);
1932 if (*token == ',')
1933 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001934 affine.ry=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001935 GetMagickToken(p,&p,token);
1936 if (*token == ',')
1937 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001938 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001939 GetMagickToken(p,&p,token);
1940 if (*token == ',')
1941 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001942 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001943 GetMagickToken(p,&p,token);
1944 if (*token == ',')
1945 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001946 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001947 break;
1948 }
1949 break;
1950 }
1951 case 'R':
1952 case 'r':
1953 {
1954 if (LocaleCompare(keyword,"rotate") == 0)
1955 {
1956 double
1957 angle,
1958 x,
1959 y;
1960
1961 p=(const char *) value;
1962 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001963 angle=StringToDouble(value,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001964 GetMagickToken(p,&p,token);
1965 if (*token == ',')
1966 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001967 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001968 GetMagickToken(p,&p,token);
1969 if (*token == ',')
1970 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001971 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001972 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1973 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1974 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1975 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1976 affine.tx=x;
1977 affine.ty=y;
1978 svg_info->center.x=x;
1979 svg_info->center.y=y;
1980 break;
1981 }
1982 break;
1983 }
1984 case 'S':
1985 case 's':
1986 {
1987 if (LocaleCompare(keyword,"scale") == 0)
1988 {
1989 for (p=(const char *) value; *p != '\0'; p++)
1990 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1991 (*p == ','))
1992 break;
1993 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1994 affine.sy=affine.sx;
1995 if (*p != '\0')
1996 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
1997 p+1);
1998 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1999 break;
2000 }
2001 if (LocaleCompare(keyword,"skewX") == 0)
2002 {
2003 affine.sx=svg_info->affine.sx;
2004 affine.ry=tan(DegreesToRadians(fmod(
2005 GetUserSpaceCoordinateValue(svg_info,1,value),
2006 360.0)));
2007 affine.sy=svg_info->affine.sy;
2008 break;
2009 }
2010 if (LocaleCompare(keyword,"skewY") == 0)
2011 {
2012 affine.sx=svg_info->affine.sx;
2013 affine.rx=tan(DegreesToRadians(fmod(
2014 GetUserSpaceCoordinateValue(svg_info,-1,value),
2015 360.0)));
2016 affine.sy=svg_info->affine.sy;
2017 break;
2018 }
2019 break;
2020 }
2021 case 'T':
2022 case 't':
2023 {
2024 if (LocaleCompare(keyword,"translate") == 0)
2025 {
2026 for (p=(const char *) value; *p != '\0'; p++)
2027 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2028 (*p == ','))
2029 break;
2030 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2031 affine.ty=affine.tx;
2032 if (*p != '\0')
2033 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2034 p+1);
2035 break;
2036 }
2037 break;
2038 }
2039 default:
2040 break;
2041 }
cristyef7c8a52010-10-10 13:46:51 +00002042 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2043 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2044 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2045 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
cristy2ac30482011-11-08 20:24:57 +00002046 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2047 current.tx;
2048 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2049 current.ty;
cristy3ed852e2009-09-05 21:47:34 +00002050 }
cristyb51dff52011-05-19 16:55:47 +00002051 (void) FormatLocaleFile(svg_info->file,
cristy4096b012011-10-19 00:31:56 +00002052 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2053 transform.ry,transform.sy,transform.tx,transform.ty);
cristy3ed852e2009-09-05 21:47:34 +00002054 for (j=0; tokens[j] != (char *) NULL; j++)
2055 tokens[j]=DestroyString(tokens[j]);
2056 tokens=(char **) RelinquishMagickMemory(tokens);
2057 break;
2058 }
2059 break;
2060 }
2061 case 'V':
2062 case 'v':
2063 {
2064 if (LocaleCompare(keyword,"verts") == 0)
2065 {
2066 (void) CloneString(&svg_info->vertices,value);
2067 break;
2068 }
2069 if (LocaleCompare(keyword,"viewBox") == 0)
2070 {
2071 p=(const char *) value;
2072 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00002073 svg_info->view_box.x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002074 GetMagickToken(p,&p,token);
2075 if (*token == ',')
2076 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00002077 svg_info->view_box.y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002078 GetMagickToken(p,&p,token);
2079 if (*token == ',')
2080 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00002081 svg_info->view_box.width=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002082 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002083 if (svg_info->bounds.width == 0)
2084 svg_info->bounds.width=svg_info->view_box.width;
2085 GetMagickToken(p,&p,token);
2086 if (*token == ',')
2087 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00002088 svg_info->view_box.height=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002089 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002090 if (svg_info->bounds.height == 0)
2091 svg_info->bounds.height=svg_info->view_box.height;
2092 break;
2093 }
2094 break;
2095 }
2096 case 'W':
2097 case 'w':
2098 {
2099 if (LocaleCompare(keyword,"width") == 0)
2100 {
2101 svg_info->bounds.width=
2102 GetUserSpaceCoordinateValue(svg_info,1,value);
2103 break;
2104 }
2105 break;
2106 }
2107 case 'X':
2108 case 'x':
2109 {
2110 if (LocaleCompare(keyword,"x") == 0)
2111 {
2112 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2113 break;
2114 }
2115 if (LocaleCompare(keyword,"xlink:href") == 0)
2116 {
2117 (void) CloneString(&svg_info->url,value);
2118 break;
2119 }
2120 if (LocaleCompare(keyword,"x1") == 0)
2121 {
2122 svg_info->segment.x1=
2123 GetUserSpaceCoordinateValue(svg_info,1,value);
2124 break;
2125 }
2126 if (LocaleCompare(keyword,"x2") == 0)
2127 {
2128 svg_info->segment.x2=
2129 GetUserSpaceCoordinateValue(svg_info,1,value);
2130 break;
2131 }
2132 break;
2133 }
2134 case 'Y':
2135 case 'y':
2136 {
2137 if (LocaleCompare(keyword,"y") == 0)
2138 {
2139 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2140 break;
2141 }
2142 if (LocaleCompare(keyword,"y1") == 0)
2143 {
2144 svg_info->segment.y1=
2145 GetUserSpaceCoordinateValue(svg_info,-1,value);
2146 break;
2147 }
2148 if (LocaleCompare(keyword,"y2") == 0)
2149 {
2150 svg_info->segment.y2=
2151 GetUserSpaceCoordinateValue(svg_info,-1,value);
2152 break;
2153 }
2154 break;
2155 }
2156 default:
2157 break;
2158 }
2159 }
2160 if (LocaleCompare((const char *) name,"svg") == 0)
2161 {
2162 if (svg_info->document->encoding != (const xmlChar *) NULL)
cristyb51dff52011-05-19 16:55:47 +00002163 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
cristy3ed852e2009-09-05 21:47:34 +00002164 (const char *) svg_info->document->encoding);
2165 if (attributes != (const xmlChar **) NULL)
2166 {
2167 double
2168 sx,
cristy59ee19d2011-12-01 15:56:42 +00002169 sy,
2170 tx,
2171 ty;
cristy3ed852e2009-09-05 21:47:34 +00002172
2173 if ((svg_info->view_box.width == 0.0) ||
2174 (svg_info->view_box.height == 0.0))
2175 svg_info->view_box=svg_info->bounds;
cristybb503372010-05-27 20:51:26 +00002176 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2177 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
cristyb51dff52011-05-19 16:55:47 +00002178 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2179 (double) svg_info->width,(double) svg_info->height);
cristy3ed852e2009-09-05 21:47:34 +00002180 sx=(double) svg_info->width/svg_info->view_box.width;
2181 sy=(double) svg_info->height/svg_info->view_box.height;
cristy59ee19d2011-12-01 15:56:42 +00002182 tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2183 0.0;
2184 ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2185 0.0;
2186 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2187 sx,sy,tx,ty);
cristy3ed852e2009-09-05 21:47:34 +00002188 }
2189 }
2190 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2191 units=DestroyString(units);
2192 if (color != (char *) NULL)
2193 color=DestroyString(color);
2194}
2195
2196static void SVGEndElement(void *context,const xmlChar *name)
2197{
2198 SVGInfo
2199 *svg_info;
2200
2201 /*
2202 Called when the end of an element has been detected.
2203 */
2204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2205 " SAX.endElement(%s)",name);
2206 svg_info=(SVGInfo *) context;
cristy13d07042010-11-21 20:56:18 +00002207 if (strchr((char *) name,':') != (char *) NULL)
cristy768b9302010-11-20 00:05:47 +00002208 {
2209 /*
2210 Skip over namespace.
2211 */
2212 for ( ; *name != ':'; name++) ;
2213 name++;
2214 }
cristy3ed852e2009-09-05 21:47:34 +00002215 switch (*name)
2216 {
2217 case 'C':
2218 case 'c':
2219 {
2220 if (LocaleCompare((const char *) name,"circle") == 0)
2221 {
cristyb51dff52011-05-19 16:55:47 +00002222 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
cristy8cd5b312010-01-07 01:10:24 +00002223 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2224 svg_info->element.cy+svg_info->element.minor);
cristyb51dff52011-05-19 16:55:47 +00002225 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002226 break;
2227 }
2228 if (LocaleCompare((const char *) name,"clipPath") == 0)
2229 {
cristyb51dff52011-05-19 16:55:47 +00002230 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
cristy3ed852e2009-09-05 21:47:34 +00002231 break;
2232 }
2233 break;
2234 }
2235 case 'D':
2236 case 'd':
2237 {
2238 if (LocaleCompare((const char *) name,"defs") == 0)
2239 {
cristyb51dff52011-05-19 16:55:47 +00002240 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
cristy3ed852e2009-09-05 21:47:34 +00002241 break;
2242 }
2243 if (LocaleCompare((const char *) name,"desc") == 0)
2244 {
2245 register char
2246 *p;
2247
2248 if (*svg_info->text == '\0')
2249 break;
2250 (void) fputc('#',svg_info->file);
2251 for (p=svg_info->text; *p != '\0'; p++)
2252 {
2253 (void) fputc(*p,svg_info->file);
2254 if (*p == '\n')
2255 (void) fputc('#',svg_info->file);
2256 }
2257 (void) fputc('\n',svg_info->file);
2258 *svg_info->text='\0';
2259 break;
2260 }
2261 break;
2262 }
2263 case 'E':
2264 case 'e':
2265 {
2266 if (LocaleCompare((const char *) name,"ellipse") == 0)
2267 {
2268 double
2269 angle;
2270
2271 angle=svg_info->element.angle;
cristyb51dff52011-05-19 16:55:47 +00002272 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
cristy3ed852e2009-09-05 21:47:34 +00002273 svg_info->element.cx,svg_info->element.cy,
2274 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2275 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
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 'G':
2282 case 'g':
2283 {
2284 if (LocaleCompare((const char *) name,"g") == 0)
2285 {
cristyb51dff52011-05-19 16:55:47 +00002286 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002287 break;
2288 }
2289 break;
2290 }
2291 case 'I':
2292 case 'i':
2293 {
2294 if (LocaleCompare((const char *) name,"image") == 0)
2295 {
cristyb51dff52011-05-19 16:55:47 +00002296 (void) FormatLocaleFile(svg_info->file,
2297 "image Over %g,%g %g,%g '%s'\n",svg_info->bounds.x,
2298 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2299 svg_info->url);
2300 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002301 break;
2302 }
2303 break;
2304 }
2305 case 'L':
2306 case 'l':
2307 {
2308 if (LocaleCompare((const char *) name,"line") == 0)
2309 {
cristyb51dff52011-05-19 16:55:47 +00002310 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
cristy8cd5b312010-01-07 01:10:24 +00002311 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2312 svg_info->segment.y2);
cristyb51dff52011-05-19 16:55:47 +00002313 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002314 break;
2315 }
2316 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2317 {
cristyb51dff52011-05-19 16:55:47 +00002318 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
cristy3ed852e2009-09-05 21:47:34 +00002319 break;
2320 }
2321 break;
2322 }
2323 case 'P':
2324 case 'p':
2325 {
2326 if (LocaleCompare((const char *) name,"pattern") == 0)
2327 {
cristyb51dff52011-05-19 16:55:47 +00002328 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
cristy3ed852e2009-09-05 21:47:34 +00002329 break;
2330 }
2331 if (LocaleCompare((const char *) name,"path") == 0)
2332 {
cristyb51dff52011-05-19 16:55:47 +00002333 (void) FormatLocaleFile(svg_info->file,"path '%s'\n",
2334 svg_info->vertices);
2335 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002336 break;
2337 }
2338 if (LocaleCompare((const char *) name,"polygon") == 0)
2339 {
cristyb51dff52011-05-19 16:55:47 +00002340 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2341 svg_info->vertices);
2342 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002343 break;
2344 }
2345 if (LocaleCompare((const char *) name,"polyline") == 0)
2346 {
cristyb51dff52011-05-19 16:55:47 +00002347 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2348 svg_info->vertices);
2349 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002350 break;
2351 }
2352 break;
2353 }
2354 case 'R':
2355 case 'r':
2356 {
2357 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2358 {
cristyb51dff52011-05-19 16:55:47 +00002359 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
cristy3ed852e2009-09-05 21:47:34 +00002360 break;
2361 }
2362 if (LocaleCompare((const char *) name,"rect") == 0)
2363 {
2364 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2365 {
cristyb51dff52011-05-19 16:55:47 +00002366 (void) FormatLocaleFile(svg_info->file,"rectangle %g,%g %g,%g\n",
cristy3ed852e2009-09-05 21:47:34 +00002367 svg_info->bounds.x,svg_info->bounds.y,
2368 svg_info->bounds.x+svg_info->bounds.width,
2369 svg_info->bounds.y+svg_info->bounds.height);
cristyb51dff52011-05-19 16:55:47 +00002370 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002371 break;
2372 }
2373 if (svg_info->radius.x == 0.0)
2374 svg_info->radius.x=svg_info->radius.y;
2375 if (svg_info->radius.y == 0.0)
2376 svg_info->radius.y=svg_info->radius.x;
cristyb51dff52011-05-19 16:55:47 +00002377 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00002378 "roundRectangle %g,%g %g,%g %g,%g\n",
cristy3ed852e2009-09-05 21:47:34 +00002379 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2380 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2381 svg_info->radius.x,svg_info->radius.y);
2382 svg_info->radius.x=0.0;
2383 svg_info->radius.y=0.0;
cristyb51dff52011-05-19 16:55:47 +00002384 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002385 break;
2386 }
2387 break;
2388 }
2389 case 'S':
2390 case 's':
2391 {
2392 if (LocaleCompare((const char *) name,"stop") == 0)
2393 {
cristyb51dff52011-05-19 16:55:47 +00002394 (void) FormatLocaleFile(svg_info->file,"stop-color '%s' %s\n",
2395 svg_info->stop_color,svg_info->offset);
cristy3ed852e2009-09-05 21:47:34 +00002396 break;
2397 }
2398 if (LocaleCompare((const char *) name,"svg") == 0)
2399 {
cristyb51dff52011-05-19 16:55:47 +00002400 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002401 break;
2402 }
2403 break;
2404 }
2405 case 'T':
2406 case 't':
2407 {
2408 if (LocaleCompare((const char *) name,"text") == 0)
2409 {
2410 if (*svg_info->text != '\0')
2411 {
2412 char
2413 *text;
2414
2415 text=EscapeString(svg_info->text,'\'');
cristye8d7cc52011-11-24 18:56:11 +00002416 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
2417 svg_info->bounds.x,svg_info->bounds.y,text);
cristy3ed852e2009-09-05 21:47:34 +00002418 text=DestroyString(text);
2419 *svg_info->text='\0';
2420 }
cristyb51dff52011-05-19 16:55:47 +00002421 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002422 break;
2423 }
2424 if (LocaleCompare((const char *) name,"tspan") == 0)
2425 {
2426 if (*svg_info->text != '\0')
2427 {
2428 DrawInfo
2429 *draw_info;
2430
2431 TypeMetric
2432 metrics;
2433
2434 char
2435 *text;
2436
2437 text=EscapeString(svg_info->text,'\'');
cristyb51dff52011-05-19 16:55:47 +00002438 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
cristy8cd5b312010-01-07 01:10:24 +00002439 svg_info->bounds.x,svg_info->bounds.y,text);
cristy3ed852e2009-09-05 21:47:34 +00002440 text=DestroyString(text);
2441 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
2442 draw_info->pointsize=svg_info->pointsize;
2443 draw_info->text=AcquireString(svg_info->text);
2444 (void) ConcatenateString(&draw_info->text," ");
cristy5cbc0162011-08-29 00:36:28 +00002445 (void) GetTypeMetrics(svg_info->image,draw_info,&metrics,
2446 svg_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00002447 svg_info->bounds.x+=metrics.width;
2448 draw_info=DestroyDrawInfo(draw_info);
2449 *svg_info->text='\0';
2450 }
cristyb51dff52011-05-19 16:55:47 +00002451 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002452 break;
2453 }
2454 if (LocaleCompare((const char *) name,"title") == 0)
2455 {
2456 if (*svg_info->text == '\0')
2457 break;
2458 (void) CloneString(&svg_info->title,svg_info->text);
2459 *svg_info->text='\0';
2460 break;
2461 }
2462 break;
2463 }
2464 default:
2465 break;
2466 }
2467 *svg_info->text='\0';
2468 (void) ResetMagickMemory(&svg_info->element,0,sizeof(svg_info->element));
2469 (void) ResetMagickMemory(&svg_info->segment,0,sizeof(svg_info->segment));
2470 svg_info->n--;
2471}
2472
2473static void SVGCharacters(void *context,const xmlChar *c,int length)
2474{
cristy6dc3b782011-11-08 19:24:00 +00002475 char
2476 *text;
2477
cristy3ed852e2009-09-05 21:47:34 +00002478 register char
2479 *p;
2480
cristybb503372010-05-27 20:51:26 +00002481 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002482 i;
2483
2484 SVGInfo
2485 *svg_info;
2486
2487 /*
2488 Receiving some characters from the parser.
2489 */
2490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002491 " SAX.characters(%s,%.20g)",c,(double) length);
cristy3ed852e2009-09-05 21:47:34 +00002492 svg_info=(SVGInfo *) context;
cristy6dc3b782011-11-08 19:24:00 +00002493 text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2494 if (text == (char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002495 return;
cristy6dc3b782011-11-08 19:24:00 +00002496 p=text;
cristybb503372010-05-27 20:51:26 +00002497 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00002498 *p++=c[i];
2499 *p='\0';
cristy6dc3b782011-11-08 19:24:00 +00002500 StripString(text);
2501 if (svg_info->text == (char *) NULL)
2502 svg_info->text=text;
2503 else
2504 {
2505 (void) ConcatenateString(&svg_info->text,text);
2506 text=DestroyString(text);
2507 }
cristy3ed852e2009-09-05 21:47:34 +00002508}
2509
2510static void SVGReference(void *context,const xmlChar *name)
2511{
2512 SVGInfo
2513 *svg_info;
2514
2515 xmlParserCtxtPtr
2516 parser;
2517
2518 /*
2519 Called when an entity reference is detected.
2520 */
2521 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2522 name);
2523 svg_info=(SVGInfo *) context;
2524 parser=svg_info->parser;
2525 if (parser == (xmlParserCtxtPtr) NULL)
2526 return;
2527 if (parser->node == (xmlNodePtr) NULL)
2528 return;
2529 if (*name == '#')
2530 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2531 else
2532 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2533}
2534
2535static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2536{
2537 SVGInfo
2538 *svg_info;
2539
2540 /*
2541 Receiving some ignorable whitespaces from the parser.
2542 */
2543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2544 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2545 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +00002546 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +00002547}
2548
2549static void SVGProcessingInstructions(void *context,const xmlChar *target,
2550 const xmlChar *data)
2551{
2552 SVGInfo
2553 *svg_info;
2554
2555 /*
2556 A processing instruction has been parsed.
2557 */
2558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2559 " SAX.processingInstruction(%s, %s)",target,data);
2560 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +00002561 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +00002562}
2563
2564static void SVGComment(void *context,const xmlChar *value)
2565{
2566 SVGInfo
2567 *svg_info;
2568
2569 /*
2570 A comment has been parsed.
2571 */
2572 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
2573 value);
2574 svg_info=(SVGInfo *) context;
2575 if (svg_info->comment != (char *) NULL)
2576 (void) ConcatenateString(&svg_info->comment,"\n");
2577 (void) ConcatenateString(&svg_info->comment,(const char *) value);
2578}
2579
2580static void SVGWarning(void *context,const char *format,...)
2581{
2582 char
2583 *message,
2584 reason[MaxTextExtent];
2585
2586 SVGInfo
2587 *svg_info;
2588
2589 va_list
2590 operands;
2591
2592 /**
2593 Display and format a warning messages, gives file, line, position and
2594 extra parameters.
2595 */
2596 va_start(operands,format);
2597 svg_info=(SVGInfo *) context;
2598 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
2599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2600#if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2601 (void) vsprintf(reason,format,operands);
2602#else
2603 (void) vsnprintf(reason,MaxTextExtent,format,operands);
2604#endif
2605 message=GetExceptionMessage(errno);
2606 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2607 DelegateWarning,reason,"`%s`",message);
2608 message=DestroyString(message);
2609 va_end(operands);
2610}
2611
2612static void SVGError(void *context,const char *format,...)
2613{
2614 char
2615 *message,
2616 reason[MaxTextExtent];
2617
2618 SVGInfo
2619 *svg_info;
2620
2621 va_list
2622 operands;
2623
2624 /*
2625 Display and format a error formats, gives file, line, position and
2626 extra parameters.
2627 */
2628 va_start(operands,format);
2629 svg_info=(SVGInfo *) context;
2630 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
2631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2632#if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2633 (void) vsprintf(reason,format,operands);
2634#else
2635 (void) vsnprintf(reason,MaxTextExtent,format,operands);
2636#endif
2637 message=GetExceptionMessage(errno);
2638 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2639 reason,"`%s`",message);
2640 message=DestroyString(message);
2641 va_end(operands);
2642}
2643
2644static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2645{
2646 SVGInfo
2647 *svg_info;
2648
2649 xmlNodePtr
2650 child;
2651
2652 xmlParserCtxtPtr
2653 parser;
2654
2655 /*
2656 Called when a pcdata block has been parsed.
2657 */
2658 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
2659 value,length);
2660 svg_info=(SVGInfo *) context;
2661 parser=svg_info->parser;
2662 child=xmlGetLastChild(parser->node);
2663 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2664 {
2665 xmlTextConcat(child,value,length);
2666 return;
2667 }
2668 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2669}
2670
2671static void SVGExternalSubset(void *context,const xmlChar *name,
2672 const xmlChar *external_id,const xmlChar *system_id)
2673{
2674 SVGInfo
2675 *svg_info;
2676
2677 xmlParserCtxt
2678 parser_context;
2679
2680 xmlParserCtxtPtr
2681 parser;
2682
2683 xmlParserInputPtr
2684 input;
2685
2686 /*
2687 Does this document has an external subset?
2688 */
2689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2690 " SAX.externalSubset(%s, %s, %s)",name,
2691 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2692 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2693 svg_info=(SVGInfo *) context;
2694 parser=svg_info->parser;
2695 if (((external_id == NULL) && (system_id == NULL)) ||
2696 ((parser->validate == 0) || (parser->wellFormed == 0) ||
2697 (svg_info->document == 0)))
2698 return;
2699 input=SVGResolveEntity(context,external_id,system_id);
2700 if (input == NULL)
2701 return;
2702 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2703 parser_context=(*parser);
2704 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2705 if (parser->inputTab == (xmlParserInputPtr *) NULL)
2706 {
2707 parser->errNo=XML_ERR_NO_MEMORY;
2708 parser->input=parser_context.input;
2709 parser->inputNr=parser_context.inputNr;
2710 parser->inputMax=parser_context.inputMax;
2711 parser->inputTab=parser_context.inputTab;
2712 return;
2713 }
2714 parser->inputNr=0;
2715 parser->inputMax=5;
2716 parser->input=NULL;
2717 xmlPushInput(parser,input);
2718 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2719 if (input->filename == (char *) NULL)
2720 input->filename=(char *) xmlStrdup(system_id);
2721 input->line=1;
2722 input->col=1;
2723 input->base=parser->input->cur;
2724 input->cur=parser->input->cur;
2725 input->free=NULL;
2726 xmlParseExternalSubset(parser,external_id,system_id);
2727 while (parser->inputNr > 1)
2728 (void) xmlPopInput(parser);
2729 xmlFreeInputStream(parser->input);
2730 xmlFree(parser->inputTab);
2731 parser->input=parser_context.input;
2732 parser->inputNr=parser_context.inputNr;
2733 parser->inputMax=parser_context.inputMax;
2734 parser->inputTab=parser_context.inputTab;
2735}
2736
2737#if defined(MAGICKCORE_RSVG_DELEGATE)
cristy3ed852e2009-09-05 21:47:34 +00002738static void SVGSetImageSize(int *width,int *height,gpointer context)
2739{
2740 Image
2741 *image;
2742
2743 image=(Image *) context;
cristy2a11bef2011-10-28 18:33:11 +00002744 *width=(int) (*width*image->resolution.x/72.0);
2745 *height=(int) (*height*image->resolution.y/72.0);
cristy3ed852e2009-09-05 21:47:34 +00002746}
2747#endif
cristy3ed852e2009-09-05 21:47:34 +00002748
2749#if defined(__cplusplus) || defined(c_plusplus)
2750}
2751#endif
2752
2753static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2754{
cristy3ed852e2009-09-05 21:47:34 +00002755 char
2756 filename[MaxTextExtent];
2757
2758 FILE
2759 *file;
2760
2761 Image
2762 *image;
2763
2764 int
2765 status,
2766 unique_file;
2767
cristybb503372010-05-27 20:51:26 +00002768 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002769 n;
2770
2771 SVGInfo
2772 *svg_info;
2773
2774 unsigned char
2775 message[MaxTextExtent];
2776
cristy1f9e1ed2009-11-18 04:09:38 +00002777 xmlSAXHandler
cristy5f6f01c2009-11-19 19:36:42 +00002778 sax_modules;
cristy1f9e1ed2009-11-18 04:09:38 +00002779
cristy3ed852e2009-09-05 21:47:34 +00002780 xmlSAXHandlerPtr
2781 sax_handler;
2782
2783 /*
2784 Open image file.
2785 */
2786 assert(image_info != (const ImageInfo *) NULL);
2787 assert(image_info->signature == MagickSignature);
2788 assert(exception != (ExceptionInfo *) NULL);
2789 if (image_info->debug != MagickFalse)
2790 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2791 image_info->filename);
2792 assert(exception->signature == MagickSignature);
cristy9950d572011-10-01 18:22:35 +00002793 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00002794 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2795 if (status == MagickFalse)
2796 {
2797 image=DestroyImageList(image);
2798 return((Image *) NULL);
2799 }
2800 if (LocaleCompare(image_info->magick,"MSVG") != 0)
2801 {
2802#if defined(MAGICKCORE_RSVG_DELEGATE)
2803#if defined(MAGICKCORE_CAIRO_DELEGATE)
2804 cairo_surface_t
2805 *cairo_surface;
2806
2807 cairo_t
2808 *cairo_info;
2809
2810 register unsigned char
2811 *p;
2812
2813 RsvgDimensionData
2814 dimension_info;
2815
2816 unsigned char
2817 *pixels;
2818
2819#else
2820 GdkPixbuf
2821 *pixel_info;
2822
2823 register const guchar
2824 *p;
2825
2826#endif
2827
2828 GError
2829 *error;
2830
cristybb503372010-05-27 20:51:26 +00002831 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002832 y;
2833
cristy101ab702011-10-13 13:06:32 +00002834 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00002835 fill_color;
2836
cristybb503372010-05-27 20:51:26 +00002837 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002838 x;
2839
cristy4c08aed2011-07-01 19:47:50 +00002840 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002841 *q;
2842
2843 RsvgHandle
2844 *svg_handle;
2845
2846 svg_handle=rsvg_handle_new();
2847 if (svg_handle == (RsvgHandle *) NULL)
2848 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2849 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
cristy3ed852e2009-09-05 21:47:34 +00002850 rsvg_handle_set_size_callback(svg_handle,SVGSetImageSize,image,NULL);
cristy2a11bef2011-10-28 18:33:11 +00002851 if ((image->resolution.x != 72.0) && (image->resolution.y != 72.0))
2852 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
2853 image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00002854 while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
2855 {
2856 error=(GError *) NULL;
2857 (void) rsvg_handle_write(svg_handle,message,n,&error);
2858 if (error != (GError *) NULL)
2859 g_error_free(error);
2860 }
2861 error=(GError *) NULL;
2862 rsvg_handle_close(svg_handle,&error);
2863 if (error != (GError *) NULL)
2864 g_error_free(error);
2865#if defined(MAGICKCORE_CAIRO_DELEGATE)
2866 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
cristy3de0a962009-12-06 15:34:07 +00002867 image->columns=dimension_info.width;
2868 image->rows=dimension_info.height;
cristye391a852009-11-21 14:50:33 +00002869 pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002870#else
2871 pixel_info=rsvg_handle_get_pixbuf(svg_handle);
2872 rsvg_handle_free(svg_handle);
2873 image->columns=gdk_pixbuf_get_width(pixel_info);
2874 image->rows=gdk_pixbuf_get_height(pixel_info);
2875#endif
cristye391a852009-11-21 14:50:33 +00002876 image->matte=MagickTrue;
2877 SetImageProperty(image,"svg:base-uri",
cristyd15e6592011-10-15 00:13:06 +00002878 rsvg_handle_get_base_uri(svg_handle),exception);
2879 SetImageProperty(image,"svg:title",rsvg_handle_get_title(svg_handle),
2880 exception);
cristye391a852009-11-21 14:50:33 +00002881 SetImageProperty(image,"svg:description",
cristyd15e6592011-10-15 00:13:06 +00002882 rsvg_handle_get_desc(svg_handle),exception);
cristy3ed852e2009-09-05 21:47:34 +00002883 if ((image->columns == 0) || (image->rows == 0))
2884 {
2885#if !defined(MAGICKCORE_CAIRO_DELEGATE)
2886 g_object_unref(G_OBJECT(pixel_info));
2887#endif
2888 g_object_unref(svg_handle);
2889 ThrowReaderException(MissingDelegateError,
2890 "NoDecodeDelegateForThisImageFormat");
2891 }
cristye391a852009-11-21 14:50:33 +00002892 if (image_info->ping == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002893 {
cristye391a852009-11-21 14:50:33 +00002894#if defined(MAGICKCORE_CAIRO_DELEGATE)
2895 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,4*
2896 image->rows*sizeof(*pixels));
2897 if (pixels == (unsigned char *) NULL)
2898 {
2899 g_object_unref(svg_handle);
2900 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2901 }
2902#endif
cristyea1a8aa2011-10-20 13:24:06 +00002903 (void) SetImageBackgroundColor(image,exception);
cristye391a852009-11-21 14:50:33 +00002904#if defined(MAGICKCORE_CAIRO_DELEGATE)
2905 cairo_surface=cairo_image_surface_create_for_data(pixels,
2906 CAIRO_FORMAT_ARGB32,image->columns,image->rows,4*image->columns);
2907 if (cairo_surface == (cairo_surface_t *) NULL)
2908 {
2909 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2910 g_object_unref(svg_handle);
2911 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2912 }
2913 cairo_info=cairo_create(cairo_surface);
cristye391a852009-11-21 14:50:33 +00002914 cairo_set_operator(cairo_info,CAIRO_OPERATOR_CLEAR);
2915 cairo_paint(cairo_info);
2916 cairo_set_operator(cairo_info,CAIRO_OPERATOR_OVER);
2917 rsvg_handle_render_cairo(svg_handle,cairo_info);
2918 cairo_destroy(cairo_info);
2919 cairo_surface_destroy(cairo_surface);
cristy3ed852e2009-09-05 21:47:34 +00002920 g_object_unref(svg_handle);
cristye391a852009-11-21 14:50:33 +00002921 p=pixels;
cristy3ed852e2009-09-05 21:47:34 +00002922#else
cristye391a852009-11-21 14:50:33 +00002923 p=gdk_pixbuf_get_pixels(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00002924#endif
cristy33e9da62011-10-21 19:08:58 +00002925 GetPixelInfo(image,&fill_color);
cristybb503372010-05-27 20:51:26 +00002926 for (y=0; y < (ssize_t) image->rows; y++)
cristyd97b6ed2009-11-20 01:02:19 +00002927 {
cristye391a852009-11-21 14:50:33 +00002928 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00002929 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002930 break;
cristybb503372010-05-27 20:51:26 +00002931 for (x=0; x < (ssize_t) image->columns; x++)
cristye391a852009-11-21 14:50:33 +00002932 {
cristy3ed852e2009-09-05 21:47:34 +00002933#if defined(MAGICKCORE_CAIRO_DELEGATE)
cristye391a852009-11-21 14:50:33 +00002934 fill_color.blue=ScaleCharToQuantum(*p++);
2935 fill_color.green=ScaleCharToQuantum(*p++);
2936 fill_color.red=ScaleCharToQuantum(*p++);
2937#else
2938 fill_color.red=ScaleCharToQuantum(*p++);
2939 fill_color.green=ScaleCharToQuantum(*p++);
2940 fill_color.blue=ScaleCharToQuantum(*p++);
2941#endif
cristy4c08aed2011-07-01 19:47:50 +00002942 fill_color.alpha=ScaleCharToQuantum(*p++);
cristye391a852009-11-21 14:50:33 +00002943#if defined(MAGICKCORE_CAIRO_DELEGATE)
2944 {
2945 double
2946 gamma;
2947
cristy4c08aed2011-07-01 19:47:50 +00002948 gamma=1.0-QuantumScale*fill_color.alpha;
cristye391a852009-11-21 14:50:33 +00002949 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2950 fill_color.blue*=gamma;
2951 fill_color.green*=gamma;
2952 fill_color.red*=gamma;
2953 }
2954#endif
cristy4c08aed2011-07-01 19:47:50 +00002955 CompositePixelOver(image,&fill_color,fill_color.alpha,q,
2956 (MagickRealType) GetPixelAlpha(image,q),q);
cristyed231572011-07-14 02:18:59 +00002957 q+=GetPixelChannels(image);
cristye391a852009-11-21 14:50:33 +00002958 }
2959 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2960 break;
2961 if (image->previous == (Image *) NULL)
2962 {
cristycee97112010-05-28 00:44:52 +00002963 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2964 image->rows);
cristye391a852009-11-21 14:50:33 +00002965 if (status == MagickFalse)
2966 break;
2967 }
2968 }
2969 }
2970#if defined(MAGICKCORE_CAIRO_DELEGATE)
2971 if (pixels != (unsigned char *) NULL)
2972 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00002973#else
2974 g_object_unref(G_OBJECT(pixel_info));
2975#endif
2976 (void) CloseBlob(image);
2977 return(GetFirstImageInList(image));
2978#endif
2979 }
2980 /*
2981 Open draw file.
2982 */
2983 file=(FILE *) NULL;
2984 unique_file=AcquireUniqueFileResource(filename);
2985 if (unique_file != -1)
2986 file=fdopen(unique_file,"w");
2987 if ((unique_file == -1) || (file == (FILE *) NULL))
2988 {
2989 (void) CopyMagickString(image->filename,filename,MaxTextExtent);
2990 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
2991 image->filename);
2992 image=DestroyImageList(image);
2993 return((Image *) NULL);
2994 }
2995 /*
2996 Parse SVG file.
2997 */
2998 svg_info=AcquireSVGInfo();
2999 if (svg_info == (SVGInfo *) NULL)
3000 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3001 svg_info->file=file;
3002 svg_info->exception=exception;
3003 svg_info->image=image;
3004 svg_info->image_info=image_info;
3005 svg_info->bounds.width=image->columns;
3006 svg_info->bounds.height=image->rows;
3007 if (image_info->size != (char *) NULL)
3008 (void) CloneString(&svg_info->size,image_info->size);
3009 if (image->debug != MagickFalse)
3010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
3011 xmlInitParser();
3012 (void) xmlSubstituteEntitiesDefault(1);
cristy5f6f01c2009-11-19 19:36:42 +00003013 (void) ResetMagickMemory(&sax_modules,0,sizeof(sax_modules));
3014 sax_modules.internalSubset=SVGInternalSubset;
3015 sax_modules.isStandalone=SVGIsStandalone;
3016 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3017 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3018 sax_modules.resolveEntity=SVGResolveEntity;
3019 sax_modules.getEntity=SVGGetEntity;
3020 sax_modules.entityDecl=SVGEntityDeclaration;
3021 sax_modules.notationDecl=SVGNotationDeclaration;
3022 sax_modules.attributeDecl=SVGAttributeDeclaration;
3023 sax_modules.elementDecl=SVGElementDeclaration;
3024 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3025 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3026 sax_modules.startDocument=SVGStartDocument;
3027 sax_modules.endDocument=SVGEndDocument;
3028 sax_modules.startElement=SVGStartElement;
3029 sax_modules.endElement=SVGEndElement;
3030 sax_modules.reference=SVGReference;
3031 sax_modules.characters=SVGCharacters;
3032 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3033 sax_modules.processingInstruction=SVGProcessingInstructions;
3034 sax_modules.comment=SVGComment;
3035 sax_modules.warning=SVGWarning;
3036 sax_modules.error=SVGError;
3037 sax_modules.fatalError=SVGError;
3038 sax_modules.getParameterEntity=SVGGetParameterEntity;
3039 sax_modules.cdataBlock=SVGCDataBlock;
3040 sax_modules.externalSubset=SVGExternalSubset;
3041 sax_handler=(&sax_modules);
cristy3ed852e2009-09-05 21:47:34 +00003042 n=ReadBlob(image,MaxTextExtent,message);
3043 if (n > 0)
3044 {
3045 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3046 message,n,image->filename);
3047 while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
3048 {
3049 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3050 if (status != 0)
3051 break;
3052 }
3053 }
3054 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3055 xmlFreeParserCtxt(svg_info->parser);
3056 if (image->debug != MagickFalse)
3057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3058 xmlCleanupParser();
3059 (void) fclose(file);
3060 (void) CloseBlob(image);
3061 image->columns=svg_info->width;
3062 image->rows=svg_info->height;
3063 if (exception->severity >= ErrorException)
3064 {
3065 image=DestroyImage(image);
3066 return((Image *) NULL);
3067 }
3068 if (image_info->ping == MagickFalse)
3069 {
3070 ImageInfo
3071 *read_info;
3072
3073 /*
3074 Draw image.
3075 */
3076 image=DestroyImage(image);
3077 image=(Image *) NULL;
3078 read_info=CloneImageInfo(image_info);
3079 SetImageInfoBlob(read_info,(void *) NULL,0);
3080 if (read_info->density != (char *) NULL)
3081 read_info->density=DestroyString(read_info->density);
cristyb51dff52011-05-19 16:55:47 +00003082 (void) FormatLocaleString(read_info->filename,MaxTextExtent,"mvg:%s",
cristy3ed852e2009-09-05 21:47:34 +00003083 filename);
3084 image=ReadImage(read_info,exception);
3085 read_info=DestroyImageInfo(read_info);
3086 if (image != (Image *) NULL)
3087 (void) CopyMagickString(image->filename,image_info->filename,
3088 MaxTextExtent);
3089 }
3090 /*
3091 Relinquish resources.
3092 */
3093 if (image != (Image *) NULL)
3094 {
3095 if (svg_info->title != (char *) NULL)
cristyd15e6592011-10-15 00:13:06 +00003096 (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
cristy3ed852e2009-09-05 21:47:34 +00003097 if (svg_info->comment != (char *) NULL)
cristyd15e6592011-10-15 00:13:06 +00003098 (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3099 exception);
cristy3ed852e2009-09-05 21:47:34 +00003100 }
3101 svg_info=DestroySVGInfo(svg_info);
3102 (void) RelinquishUniqueFileResource(filename);
3103 return(GetFirstImageInList(image));
3104}
3105#endif
3106
3107/*
3108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3109% %
3110% %
3111% %
3112% R e g i s t e r S V G I m a g e %
3113% %
3114% %
3115% %
3116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3117%
3118% RegisterSVGImage() adds attributes for the SVG image format to
3119% the list of supported formats. The attributes include the image format
3120% tag, a method to read and/or write the format, whether the format
3121% supports the saving of more than one frame to the same file or blob,
3122% whether the format supports native in-memory I/O, and a brief
3123% description of the format.
3124%
3125% The format of the RegisterSVGImage method is:
3126%
cristybb503372010-05-27 20:51:26 +00003127% size_t RegisterSVGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00003128%
3129*/
cristybb503372010-05-27 20:51:26 +00003130ModuleExport size_t RegisterSVGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00003131{
3132 char
3133 version[MaxTextExtent];
3134
3135 MagickInfo
3136 *entry;
3137
3138 *version='\0';
3139#if defined(LIBXML_DOTTED_VERSION)
3140 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,MaxTextExtent);
3141#endif
3142#if defined(MAGICKCORE_RSVG_DELEGATE)
3143 rsvg_init();
cristyb51dff52011-05-19 16:55:47 +00003144 (void) FormatLocaleString(version,MaxTextExtent,"RSVG %d.%d.%d",
cristy3ed852e2009-09-05 21:47:34 +00003145 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3146#endif
3147 entry=SetMagickInfo("SVG");
3148#if defined(MAGICKCORE_XML_DELEGATE)
3149 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3150#endif
3151 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3152 entry->blob_support=MagickFalse;
3153 entry->seekable_stream=MagickFalse;
3154 entry->description=ConstantString("Scalable Vector Graphics");
3155 if (*version != '\0')
3156 entry->version=ConstantString(version);
3157 entry->magick=(IsImageFormatHandler *) IsSVG;
3158 entry->module=ConstantString("SVG");
3159 (void) RegisterMagickInfo(entry);
3160 entry=SetMagickInfo("SVGZ");
3161#if defined(MAGICKCORE_XML_DELEGATE)
3162 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3163#endif
3164 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3165 entry->blob_support=MagickFalse;
3166 entry->seekable_stream=MagickFalse;
3167 entry->description=ConstantString("Compressed Scalable Vector Graphics");
3168 if (*version != '\0')
3169 entry->version=ConstantString(version);
3170 entry->magick=(IsImageFormatHandler *) IsSVG;
3171 entry->module=ConstantString("SVG");
3172 (void) RegisterMagickInfo(entry);
3173 entry=SetMagickInfo("MSVG");
3174#if defined(MAGICKCORE_XML_DELEGATE)
3175 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3176#endif
3177 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3178 entry->blob_support=MagickFalse;
3179 entry->seekable_stream=MagickFalse;
3180 entry->description=ConstantString("ImageMagick's own SVG internal renderer");
3181 entry->magick=(IsImageFormatHandler *) IsSVG;
3182 entry->module=ConstantString("SVG");
3183 (void) RegisterMagickInfo(entry);
3184 return(MagickImageCoderSignature);
3185}
3186
3187/*
3188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3189% %
3190% %
3191% %
3192% U n r e g i s t e r S V G I m a g e %
3193% %
3194% %
3195% %
3196%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3197%
3198% UnregisterSVGImage() removes format registrations made by the
3199% SVG module from the list of supported formats.
3200%
3201% The format of the UnregisterSVGImage method is:
3202%
3203% UnregisterSVGImage(void)
3204%
3205*/
3206ModuleExport void UnregisterSVGImage(void)
3207{
3208 (void) UnregisterMagickInfo("SVGZ");
3209 (void) UnregisterMagickInfo("SVG");
3210 (void) UnregisterMagickInfo("MSVG");
3211#if defined(MAGICKCORE_RSVG_DELEGATE)
3212 rsvg_term();
3213#endif
3214}
3215
3216/*
3217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3218% %
3219% %
3220% %
3221% W r i t e S V G I m a g e %
3222% %
3223% %
3224% %
3225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3226%
3227% WriteSVGImage() writes a image in the SVG - XML based W3C standard
3228% format.
3229%
3230% The format of the WriteSVGImage method is:
3231%
cristy3a37efd2011-08-28 20:31:03 +00003232% MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3233% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003234%
3235% A description of each parameter follows.
3236%
3237% o image_info: the image info.
3238%
3239% o image: The image.
3240%
cristy3a37efd2011-08-28 20:31:03 +00003241% o exception: return any errors or warnings in this structure.
3242%
cristy3ed852e2009-09-05 21:47:34 +00003243*/
3244
3245static void AffineToTransform(Image *image,AffineMatrix *affine)
3246{
3247 char
3248 transform[MaxTextExtent];
3249
3250 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3251 {
3252 if ((fabs(affine->rx) < MagickEpsilon) &&
3253 (fabs(affine->ry) < MagickEpsilon))
3254 {
3255 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3256 (fabs(affine->sy-1.0) < MagickEpsilon))
3257 {
3258 (void) WriteBlobString(image,"\">\n");
3259 return;
3260 }
cristyb51dff52011-05-19 16:55:47 +00003261 (void) FormatLocaleString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003262 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
cristy3ed852e2009-09-05 21:47:34 +00003263 (void) WriteBlobString(image,transform);
3264 return;
3265 }
3266 else
3267 {
3268 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3269 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3270 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3271 2*MagickEpsilon))
3272 {
3273 double
3274 theta;
3275
3276 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
cristyb51dff52011-05-19 16:55:47 +00003277 (void) FormatLocaleString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003278 "\" transform=\"rotate(%g)\">\n",theta);
cristy3ed852e2009-09-05 21:47:34 +00003279 (void) WriteBlobString(image,transform);
3280 return;
3281 }
3282 }
3283 }
3284 else
3285 {
3286 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3287 (fabs(affine->rx) < MagickEpsilon) &&
3288 (fabs(affine->ry) < MagickEpsilon) &&
3289 (fabs(affine->sy-1.0) < MagickEpsilon))
3290 {
cristyb51dff52011-05-19 16:55:47 +00003291 (void) FormatLocaleString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003292 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
cristy3ed852e2009-09-05 21:47:34 +00003293 (void) WriteBlobString(image,transform);
3294 return;
3295 }
3296 }
cristyb51dff52011-05-19 16:55:47 +00003297 (void) FormatLocaleString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003298 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
cristy8cd5b312010-01-07 01:10:24 +00003299 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
cristy3ed852e2009-09-05 21:47:34 +00003300 (void) WriteBlobString(image,transform);
3301}
3302
3303static MagickBooleanType IsPoint(const char *point)
3304{
3305 char
3306 *p;
3307
cristybb503372010-05-27 20:51:26 +00003308 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003309 value;
3310
3311 value=strtol(point,&p,10);
cristyda16f162011-02-19 23:52:17 +00003312 (void) value;
cristy3ed852e2009-09-05 21:47:34 +00003313 return(p != point ? MagickTrue : MagickFalse);
3314}
3315
cristyc82a27b2011-10-21 01:07:16 +00003316static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003317{
cristybb503372010-05-27 20:51:26 +00003318 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003319 y;
3320
cristy4c08aed2011-07-01 19:47:50 +00003321 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003322 *p;
3323
cristybb503372010-05-27 20:51:26 +00003324 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003325 x;
3326
3327#if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3328 {
3329 at_bitmap_type
3330 *trace;
3331
3332 at_fitting_opts_type
3333 *fitting_options;
3334
3335 at_output_opts_type
3336 *output_options;
3337
3338 at_splines_type
3339 *splines;
3340
3341 ImageType
3342 type;
3343
cristybb503372010-05-27 20:51:26 +00003344 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003345 i;
3346
cristybb503372010-05-27 20:51:26 +00003347 size_t
cristy3ed852e2009-09-05 21:47:34 +00003348 number_planes;
3349
3350 /*
3351 Trace image and write as SVG.
3352 */
3353 fitting_options=at_fitting_opts_new();
3354 output_options=at_output_opts_new();
cristyad3b66f2011-10-31 02:07:10 +00003355 type=GetImageType(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00003356 number_planes=3;
3357 if ((type == BilevelType) || (type == GrayscaleType))
3358 number_planes=1;
3359 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3360 i=0;
cristybb503372010-05-27 20:51:26 +00003361 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003362 {
cristyad3b66f2011-10-31 02:07:10 +00003363 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003364 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003365 break;
cristybb503372010-05-27 20:51:26 +00003366 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003367 {
cristy4c08aed2011-07-01 19:47:50 +00003368 trace->bitmap[i++]=GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +00003369 if (number_planes == 3)
3370 {
cristy4c08aed2011-07-01 19:47:50 +00003371 trace->bitmap[i++]=GetPixelGreen(image,p);
3372 trace->bitmap[i++]=GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00003373 }
cristyed231572011-07-14 02:18:59 +00003374 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003375 }
3376 }
3377 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3378 NULL);
3379 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3380 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3381 NULL);
3382 /*
3383 Free resources.
3384 */
3385 at_splines_free(splines);
3386 at_bitmap_free(trace);
3387 at_output_opts_free(output_options);
3388 at_fitting_opts_free(fitting_options);
3389 }
3390#else
3391 {
3392 char
3393 message[MaxTextExtent],
3394 tuple[MaxTextExtent];
3395
cristy4c08aed2011-07-01 19:47:50 +00003396 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003397 pixel;
3398
cristy3ed852e2009-09-05 21:47:34 +00003399 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3400 (void) WriteBlobString(image,
3401 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3402 (void) WriteBlobString(image,
3403 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
cristyb51dff52011-05-19 16:55:47 +00003404 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00003405 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,
3406 (double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003407 (void) WriteBlobString(image,message);
cristy4c08aed2011-07-01 19:47:50 +00003408 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00003409 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003410 {
cristyc82a27b2011-10-21 01:07:16 +00003411 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00003412 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003413 break;
cristybb503372010-05-27 20:51:26 +00003414 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003415 {
cristy803640d2011-11-17 02:11:32 +00003416 GetPixelInfoPixel(image,p,&pixel);
cristyc82a27b2011-10-21 01:07:16 +00003417 (void) QueryColorname(image,&pixel,SVGCompliance,tuple,exception);
cristyb51dff52011-05-19 16:55:47 +00003418 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00003419 " <circle cx=\"%.20g\" cy=\"%.20g\" r=\"1\" fill=\"%s\"/>\n",
3420 (double) x,(double) y,tuple);
cristy3ed852e2009-09-05 21:47:34 +00003421 (void) WriteBlobString(image,message);
cristyed231572011-07-14 02:18:59 +00003422 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003423 }
3424 }
3425 (void) WriteBlobString(image,"</svg>\n");
3426 }
3427#endif
3428 return(MagickTrue);
3429}
3430
cristy3a37efd2011-08-28 20:31:03 +00003431static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
3432 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003433{
3434#define BezierQuantum 200
3435
3436 AffineMatrix
3437 affine;
3438
3439 char
3440 keyword[MaxTextExtent],
3441 message[MaxTextExtent],
3442 name[MaxTextExtent],
3443 *token,
3444 type[MaxTextExtent];
3445
3446 const char
3447 *p,
3448 *q,
3449 *value;
3450
3451 int
3452 n;
3453
cristybb503372010-05-27 20:51:26 +00003454 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003455 j;
3456
3457 MagickBooleanType
3458 active,
3459 status;
3460
3461 PointInfo
3462 point;
3463
3464 PrimitiveInfo
3465 *primitive_info;
3466
3467 PrimitiveType
3468 primitive_type;
3469
cristybb503372010-05-27 20:51:26 +00003470 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003471 x;
3472
cristybb503372010-05-27 20:51:26 +00003473 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003474 i;
3475
3476 size_t
3477 length;
3478
3479 SVGInfo
3480 svg_info;
3481
cristybb503372010-05-27 20:51:26 +00003482 size_t
cristy3ed852e2009-09-05 21:47:34 +00003483 number_points;
3484
3485 /*
3486 Open output image file.
3487 */
3488 assert(image_info != (const ImageInfo *) NULL);
3489 assert(image_info->signature == MagickSignature);
3490 assert(image != (Image *) NULL);
3491 assert(image->signature == MagickSignature);
3492 if (image->debug != MagickFalse)
3493 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00003494 assert(exception != (ExceptionInfo *) NULL);
3495 assert(exception->signature == MagickSignature);
3496 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00003497 if (status == MagickFalse)
3498 return(status);
3499 value=GetImageArtifact(image,"SVG");
3500 if (value != (char *) NULL)
3501 {
3502 (void) WriteBlobString(image,value);
3503 (void) CloseBlob(image);
3504 return(MagickTrue);
3505 }
3506 value=GetImageArtifact(image,"MVG");
3507 if (value == (char *) NULL)
cristyc82a27b2011-10-21 01:07:16 +00003508 return(TraceSVGImage(image,exception));
cristy3ed852e2009-09-05 21:47:34 +00003509 /*
3510 Write SVG header.
3511 */
3512 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3513 (void) WriteBlobString(image,
3514 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3515 (void) WriteBlobString(image,
3516 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
cristyb51dff52011-05-19 16:55:47 +00003517 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00003518 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3519 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003520 (void) WriteBlobString(image,message);
3521 /*
3522 Allocate primitive info memory.
3523 */
3524 number_points=2047;
3525 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3526 sizeof(*primitive_info));
3527 if (primitive_info == (PrimitiveInfo *) NULL)
3528 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3529 GetAffineMatrix(&affine);
3530 token=AcquireString(value);
3531 active=MagickFalse;
3532 n=0;
3533 status=MagickTrue;
3534 for (q=(const char *) value; *q != '\0'; )
3535 {
3536 /*
3537 Interpret graphic primitive.
3538 */
3539 GetMagickToken(q,&q,keyword);
3540 if (*keyword == '\0')
3541 break;
3542 if (*keyword == '#')
3543 {
3544 /*
3545 Comment.
3546 */
3547 if (active != MagickFalse)
3548 {
3549 AffineToTransform(image,&affine);
3550 active=MagickFalse;
3551 }
3552 (void) WriteBlobString(image,"<desc>");
3553 (void) WriteBlobString(image,keyword+1);
3554 for ( ; (*q != '\n') && (*q != '\0'); q++)
3555 switch (*q)
3556 {
3557 case '<': (void) WriteBlobString(image,"&lt;"); break;
3558 case '>': (void) WriteBlobString(image,"&gt;"); break;
3559 case '&': (void) WriteBlobString(image,"&amp;"); break;
3560 default: (void) WriteBlobByte(image,*q); break;
3561 }
3562 (void) WriteBlobString(image,"</desc>\n");
3563 continue;
3564 }
3565 primitive_type=UndefinedPrimitive;
3566 switch (*keyword)
3567 {
3568 case ';':
3569 break;
3570 case 'a':
3571 case 'A':
3572 {
3573 if (LocaleCompare("affine",keyword) == 0)
3574 {
3575 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003576 affine.sx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003577 GetMagickToken(q,&q,token);
3578 if (*token == ',')
3579 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003580 affine.rx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003581 GetMagickToken(q,&q,token);
3582 if (*token == ',')
3583 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003584 affine.ry=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003585 GetMagickToken(q,&q,token);
3586 if (*token == ',')
3587 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003588 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003589 GetMagickToken(q,&q,token);
3590 if (*token == ',')
3591 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003592 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003593 GetMagickToken(q,&q,token);
3594 if (*token == ',')
3595 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003596 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003597 break;
3598 }
3599 if (LocaleCompare("angle",keyword) == 0)
3600 {
3601 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003602 affine.rx=StringToDouble(token,(char **) NULL);
3603 affine.ry=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003604 break;
3605 }
3606 if (LocaleCompare("arc",keyword) == 0)
3607 {
3608 primitive_type=ArcPrimitive;
3609 break;
3610 }
3611 status=MagickFalse;
3612 break;
3613 }
3614 case 'b':
3615 case 'B':
3616 {
3617 if (LocaleCompare("bezier",keyword) == 0)
3618 {
3619 primitive_type=BezierPrimitive;
3620 break;
3621 }
3622 status=MagickFalse;
3623 break;
3624 }
3625 case 'c':
3626 case 'C':
3627 {
3628 if (LocaleCompare("clip-path",keyword) == 0)
3629 {
3630 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003631 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003632 "clip-path:url(#%s);",token);
3633 (void) WriteBlobString(image,message);
3634 break;
3635 }
3636 if (LocaleCompare("clip-rule",keyword) == 0)
3637 {
3638 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003639 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003640 "clip-rule:%s;",token);
3641 (void) WriteBlobString(image,message);
3642 break;
3643 }
3644 if (LocaleCompare("clip-units",keyword) == 0)
3645 {
3646 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003647 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003648 "clipPathUnits=%s;",token);
3649 (void) WriteBlobString(image,message);
3650 break;
3651 }
3652 if (LocaleCompare("circle",keyword) == 0)
3653 {
3654 primitive_type=CirclePrimitive;
3655 break;
3656 }
3657 if (LocaleCompare("color",keyword) == 0)
3658 {
3659 primitive_type=ColorPrimitive;
3660 break;
3661 }
3662 status=MagickFalse;
3663 break;
3664 }
3665 case 'd':
3666 case 'D':
3667 {
3668 if (LocaleCompare("decorate",keyword) == 0)
3669 {
3670 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003671 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003672 "text-decoration:%s;",token);
3673 (void) WriteBlobString(image,message);
3674 break;
3675 }
3676 status=MagickFalse;
3677 break;
3678 }
3679 case 'e':
3680 case 'E':
3681 {
3682 if (LocaleCompare("ellipse",keyword) == 0)
3683 {
3684 primitive_type=EllipsePrimitive;
3685 break;
3686 }
3687 status=MagickFalse;
3688 break;
3689 }
3690 case 'f':
3691 case 'F':
3692 {
3693 if (LocaleCompare("fill",keyword) == 0)
3694 {
3695 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003696 (void) FormatLocaleString(message,MaxTextExtent,"fill:%s;",
cristy3ed852e2009-09-05 21:47:34 +00003697 token);
3698 (void) WriteBlobString(image,message);
3699 break;
3700 }
3701 if (LocaleCompare("fill-rule",keyword) == 0)
3702 {
3703 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003704 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003705 "fill-rule:%s;",token);
3706 (void) WriteBlobString(image,message);
3707 break;
3708 }
3709 if (LocaleCompare("fill-opacity",keyword) == 0)
3710 {
3711 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003712 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003713 "fill-opacity:%s;",token);
3714 (void) WriteBlobString(image,message);
3715 break;
3716 }
3717 if (LocaleCompare("font-family",keyword) == 0)
3718 {
3719 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003720 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003721 "font-family:%s;",token);
3722 (void) WriteBlobString(image,message);
3723 break;
3724 }
3725 if (LocaleCompare("font-stretch",keyword) == 0)
3726 {
3727 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003728 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003729 "font-stretch:%s;",token);
3730 (void) WriteBlobString(image,message);
3731 break;
3732 }
3733 if (LocaleCompare("font-style",keyword) == 0)
3734 {
3735 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003736 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003737 "font-style:%s;",token);
3738 (void) WriteBlobString(image,message);
3739 break;
3740 }
3741 if (LocaleCompare("font-size",keyword) == 0)
3742 {
3743 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003744 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003745 "font-size:%s;",token);
3746 (void) WriteBlobString(image,message);
3747 break;
3748 }
3749 if (LocaleCompare("font-weight",keyword) == 0)
3750 {
3751 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003752 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003753 "font-weight:%s;",token);
3754 (void) WriteBlobString(image,message);
3755 break;
3756 }
3757 status=MagickFalse;
3758 break;
3759 }
3760 case 'g':
3761 case 'G':
3762 {
3763 if (LocaleCompare("gradient-units",keyword) == 0)
3764 {
3765 GetMagickToken(q,&q,token);
3766 break;
3767 }
3768 if (LocaleCompare("text-align",keyword) == 0)
3769 {
3770 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003771 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003772 "text-align %s ",token);
3773 (void) WriteBlobString(image,message);
3774 break;
3775 }
3776 if (LocaleCompare("text-anchor",keyword) == 0)
3777 {
3778 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003779 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003780 "text-anchor %s ",token);
3781 (void) WriteBlobString(image,message);
3782 break;
3783 }
3784 status=MagickFalse;
3785 break;
3786 }
3787 case 'i':
3788 case 'I':
3789 {
3790 if (LocaleCompare("image",keyword) == 0)
3791 {
3792 GetMagickToken(q,&q,token);
3793 primitive_type=ImagePrimitive;
3794 break;
3795 }
3796 status=MagickFalse;
3797 break;
3798 }
3799 case 'l':
3800 case 'L':
3801 {
3802 if (LocaleCompare("line",keyword) == 0)
3803 {
3804 primitive_type=LinePrimitive;
3805 break;
3806 }
3807 status=MagickFalse;
3808 break;
3809 }
3810 case 'm':
3811 case 'M':
3812 {
3813 if (LocaleCompare("matte",keyword) == 0)
3814 {
3815 primitive_type=MattePrimitive;
3816 break;
3817 }
3818 status=MagickFalse;
3819 break;
3820 }
3821 case 'o':
3822 case 'O':
3823 {
3824 if (LocaleCompare("opacity",keyword) == 0)
3825 {
3826 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003827 (void) FormatLocaleString(message,MaxTextExtent,"opacity %s ",
cristy3ed852e2009-09-05 21:47:34 +00003828 token);
3829 (void) WriteBlobString(image,message);
3830 break;
3831 }
3832 status=MagickFalse;
3833 break;
3834 }
3835 case 'p':
3836 case 'P':
3837 {
3838 if (LocaleCompare("path",keyword) == 0)
3839 {
3840 primitive_type=PathPrimitive;
3841 break;
3842 }
3843 if (LocaleCompare("point",keyword) == 0)
3844 {
3845 primitive_type=PointPrimitive;
3846 break;
3847 }
3848 if (LocaleCompare("polyline",keyword) == 0)
3849 {
3850 primitive_type=PolylinePrimitive;
3851 break;
3852 }
3853 if (LocaleCompare("polygon",keyword) == 0)
3854 {
3855 primitive_type=PolygonPrimitive;
3856 break;
3857 }
3858 if (LocaleCompare("pop",keyword) == 0)
3859 {
3860 GetMagickToken(q,&q,token);
3861 if (LocaleCompare("clip-path",token) == 0)
3862 {
3863 (void) WriteBlobString(image,"</clipPath>\n");
3864 break;
3865 }
3866 if (LocaleCompare("defs",token) == 0)
3867 {
3868 (void) WriteBlobString(image,"</defs>\n");
3869 break;
3870 }
3871 if (LocaleCompare("gradient",token) == 0)
3872 {
cristyb51dff52011-05-19 16:55:47 +00003873 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003874 "</%sGradient>\n",type);
3875 (void) WriteBlobString(image,message);
3876 break;
3877 }
3878 if (LocaleCompare("graphic-context",token) == 0)
3879 {
3880 n--;
3881 if (n < 0)
3882 ThrowWriterException(DrawError,
3883 "UnbalancedGraphicContextPushPop");
3884 (void) WriteBlobString(image,"</g>\n");
3885 }
3886 if (LocaleCompare("pattern",token) == 0)
3887 {
3888 (void) WriteBlobString(image,"</pattern>\n");
3889 break;
3890 }
3891 if (LocaleCompare("defs",token) == 0)
3892 (void) WriteBlobString(image,"</g>\n");
3893 break;
3894 }
3895 if (LocaleCompare("push",keyword) == 0)
3896 {
3897 GetMagickToken(q,&q,token);
3898 if (LocaleCompare("clip-path",token) == 0)
3899 {
3900 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003901 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003902 "<clipPath id=\"%s\">\n",token);
3903 (void) WriteBlobString(image,message);
3904 break;
3905 }
3906 if (LocaleCompare("defs",token) == 0)
3907 {
3908 (void) WriteBlobString(image,"<defs>\n");
3909 break;
3910 }
3911 if (LocaleCompare("gradient",token) == 0)
3912 {
3913 GetMagickToken(q,&q,token);
3914 (void) CopyMagickString(name,token,MaxTextExtent);
3915 GetMagickToken(q,&q,token);
3916 (void) CopyMagickString(type,token,MaxTextExtent);
3917 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003918 svg_info.segment.x1=StringToDouble(token,(char **) NULL);
3919 svg_info.element.cx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003920 GetMagickToken(q,&q,token);
3921 if (*token == ',')
3922 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003923 svg_info.segment.y1=StringToDouble(token,(char **) NULL);
3924 svg_info.element.cy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003925 GetMagickToken(q,&q,token);
3926 if (*token == ',')
3927 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003928 svg_info.segment.x2=StringToDouble(token,(char **) NULL);
3929 svg_info.element.major=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00003930 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003931 GetMagickToken(q,&q,token);
3932 if (*token == ',')
3933 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003934 svg_info.segment.y2=StringToDouble(token,(char **) NULL);
3935 svg_info.element.minor=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00003936 (char **) NULL);
cristyb51dff52011-05-19 16:55:47 +00003937 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003938 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
3939 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
cristy3ed852e2009-09-05 21:47:34 +00003940 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
3941 if (LocaleCompare(type,"radial") == 0)
3942 {
3943 GetMagickToken(q,&q,token);
3944 if (*token == ',')
3945 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003946 svg_info.element.angle=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00003947 (char **) NULL);
cristyb51dff52011-05-19 16:55:47 +00003948 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003949 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
3950 "fx=\"%g\" fy=\"%g\">\n",type,name,
cristy8cd5b312010-01-07 01:10:24 +00003951 svg_info.element.cx,svg_info.element.cy,
3952 svg_info.element.angle,svg_info.element.major,
3953 svg_info.element.minor);
cristy3ed852e2009-09-05 21:47:34 +00003954 }
3955 (void) WriteBlobString(image,message);
3956 break;
3957 }
3958 if (LocaleCompare("graphic-context",token) == 0)
3959 {
3960 n++;
3961 if (active)
3962 {
3963 AffineToTransform(image,&affine);
3964 active=MagickFalse;
3965 }
3966 (void) WriteBlobString(image,"<g style=\"");
3967 active=MagickTrue;
3968 }
3969 if (LocaleCompare("pattern",token) == 0)
3970 {
3971 GetMagickToken(q,&q,token);
3972 (void) CopyMagickString(name,token,MaxTextExtent);
3973 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003974 svg_info.bounds.x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003975 GetMagickToken(q,&q,token);
3976 if (*token == ',')
3977 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003978 svg_info.bounds.y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003979 GetMagickToken(q,&q,token);
3980 if (*token == ',')
3981 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003982 svg_info.bounds.width=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00003983 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003984 GetMagickToken(q,&q,token);
3985 if (*token == ',')
3986 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003987 svg_info.bounds.height=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00003988 (char **) NULL);
cristyb51dff52011-05-19 16:55:47 +00003989 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003990 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
3991 "height=\"%g\">\n",name,svg_info.bounds.x,
cristy8cd5b312010-01-07 01:10:24 +00003992 svg_info.bounds.y,svg_info.bounds.width,
3993 svg_info.bounds.height);
cristy3ed852e2009-09-05 21:47:34 +00003994 (void) WriteBlobString(image,message);
3995 break;
3996 }
3997 break;
3998 }
3999 status=MagickFalse;
4000 break;
4001 }
4002 case 'r':
4003 case 'R':
4004 {
4005 if (LocaleCompare("rectangle",keyword) == 0)
4006 {
4007 primitive_type=RectanglePrimitive;
4008 break;
4009 }
4010 if (LocaleCompare("roundRectangle",keyword) == 0)
4011 {
4012 primitive_type=RoundRectanglePrimitive;
4013 break;
4014 }
4015 if (LocaleCompare("rotate",keyword) == 0)
4016 {
4017 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004018 (void) FormatLocaleString(message,MaxTextExtent,"rotate(%s) ",
cristy3ed852e2009-09-05 21:47:34 +00004019 token);
4020 (void) WriteBlobString(image,message);
4021 break;
4022 }
4023 status=MagickFalse;
4024 break;
4025 }
4026 case 's':
4027 case 'S':
4028 {
4029 if (LocaleCompare("scale",keyword) == 0)
4030 {
4031 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004032 affine.sx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004033 GetMagickToken(q,&q,token);
4034 if (*token == ',')
4035 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004036 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004037 break;
4038 }
4039 if (LocaleCompare("skewX",keyword) == 0)
4040 {
4041 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004042 (void) FormatLocaleString(message,MaxTextExtent,"skewX(%s) ",
cristy3ed852e2009-09-05 21:47:34 +00004043 token);
4044 (void) WriteBlobString(image,message);
4045 break;
4046 }
4047 if (LocaleCompare("skewY",keyword) == 0)
4048 {
4049 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004050 (void) FormatLocaleString(message,MaxTextExtent,"skewY(%s) ",
cristy3ed852e2009-09-05 21:47:34 +00004051 token);
4052 (void) WriteBlobString(image,message);
4053 break;
4054 }
4055 if (LocaleCompare("stop-color",keyword) == 0)
4056 {
4057 char
4058 color[MaxTextExtent];
4059
4060 GetMagickToken(q,&q,token);
4061 (void) CopyMagickString(color,token,MaxTextExtent);
4062 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004063 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004064 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4065 (void) WriteBlobString(image,message);
4066 break;
4067 }
4068 if (LocaleCompare("stroke",keyword) == 0)
4069 {
4070 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004071 (void) FormatLocaleString(message,MaxTextExtent,"stroke:%s;",
cristy3ed852e2009-09-05 21:47:34 +00004072 token);
4073 (void) WriteBlobString(image,message);
4074 break;
4075 }
4076 if (LocaleCompare("stroke-antialias",keyword) == 0)
4077 {
4078 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004079 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004080 "stroke-antialias:%s;",token);
4081 (void) WriteBlobString(image,message);
4082 break;
4083 }
4084 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4085 {
4086 if (IsPoint(q))
4087 {
cristybb503372010-05-27 20:51:26 +00004088 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004089 k;
4090
4091 p=q;
4092 GetMagickToken(p,&p,token);
4093 for (k=0; IsPoint(token); k++)
4094 GetMagickToken(p,&p,token);
4095 (void) WriteBlobString(image,"stroke-dasharray:");
4096 for (j=0; j < k; j++)
4097 {
4098 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004099 (void) FormatLocaleString(message,MaxTextExtent,"%s ",
cristy3ed852e2009-09-05 21:47:34 +00004100 token);
4101 (void) WriteBlobString(image,message);
4102 }
4103 (void) WriteBlobString(image,";");
4104 break;
4105 }
4106 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004107 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004108 "stroke-dasharray:%s;",token);
4109 (void) WriteBlobString(image,message);
4110 break;
4111 }
4112 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4113 {
4114 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004115 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004116 "stroke-dashoffset:%s;",token);
4117 (void) WriteBlobString(image,message);
4118 break;
4119 }
4120 if (LocaleCompare("stroke-linecap",keyword) == 0)
4121 {
4122 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004123 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004124 "stroke-linecap:%s;",token);
4125 (void) WriteBlobString(image,message);
4126 break;
4127 }
4128 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4129 {
4130 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004131 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004132 "stroke-linejoin:%s;",token);
4133 (void) WriteBlobString(image,message);
4134 break;
4135 }
4136 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4137 {
4138 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004139 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004140 "stroke-miterlimit:%s;",token);
4141 (void) WriteBlobString(image,message);
4142 break;
4143 }
4144 if (LocaleCompare("stroke-opacity",keyword) == 0)
4145 {
4146 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004147 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004148 "stroke-opacity:%s;",token);
4149 (void) WriteBlobString(image,message);
4150 break;
4151 }
4152 if (LocaleCompare("stroke-width",keyword) == 0)
4153 {
4154 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004155 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004156 "stroke-width:%s;",token);
4157 (void) WriteBlobString(image,message);
4158 continue;
4159 }
4160 status=MagickFalse;
4161 break;
4162 }
4163 case 't':
4164 case 'T':
4165 {
4166 if (LocaleCompare("text",keyword) == 0)
4167 {
4168 primitive_type=TextPrimitive;
4169 break;
4170 }
4171 if (LocaleCompare("text-antialias",keyword) == 0)
4172 {
4173 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004174 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004175 "text-antialias:%s;",token);
4176 (void) WriteBlobString(image,message);
4177 break;
4178 }
4179 if (LocaleCompare("tspan",keyword) == 0)
4180 {
4181 primitive_type=TextPrimitive;
4182 break;
4183 }
4184 if (LocaleCompare("translate",keyword) == 0)
4185 {
4186 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004187 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004188 GetMagickToken(q,&q,token);
4189 if (*token == ',')
4190 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004191 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004192 break;
4193 }
4194 status=MagickFalse;
4195 break;
4196 }
4197 case 'v':
4198 case 'V':
4199 {
4200 if (LocaleCompare("viewbox",keyword) == 0)
4201 {
4202 GetMagickToken(q,&q,token);
4203 if (*token == ',')
4204 GetMagickToken(q,&q,token);
4205 GetMagickToken(q,&q,token);
4206 if (*token == ',')
4207 GetMagickToken(q,&q,token);
4208 GetMagickToken(q,&q,token);
4209 if (*token == ',')
4210 GetMagickToken(q,&q,token);
4211 GetMagickToken(q,&q,token);
4212 break;
4213 }
4214 status=MagickFalse;
4215 break;
4216 }
4217 default:
4218 {
4219 status=MagickFalse;
4220 break;
4221 }
4222 }
4223 if (status == MagickFalse)
4224 break;
4225 if (primitive_type == UndefinedPrimitive)
4226 continue;
4227 /*
4228 Parse the primitive attributes.
4229 */
4230 i=0;
4231 j=0;
4232 for (x=0; *q != '\0'; x++)
4233 {
4234 /*
4235 Define points.
4236 */
4237 if (IsPoint(q) == MagickFalse)
4238 break;
4239 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004240 point.x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004241 GetMagickToken(q,&q,token);
4242 if (*token == ',')
4243 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004244 point.y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004245 GetMagickToken(q,(const char **) NULL,token);
4246 if (*token == ',')
4247 GetMagickToken(q,&q,token);
4248 primitive_info[i].primitive=primitive_type;
4249 primitive_info[i].point=point;
4250 primitive_info[i].coordinates=0;
4251 primitive_info[i].method=FloodfillMethod;
4252 i++;
cristybb503372010-05-27 20:51:26 +00004253 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
cristy3ed852e2009-09-05 21:47:34 +00004254 continue;
4255 number_points+=6*BezierQuantum+360;
4256 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4257 number_points,sizeof(*primitive_info));
4258 if (primitive_info == (PrimitiveInfo *) NULL)
4259 {
cristy3a37efd2011-08-28 20:31:03 +00004260 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004261 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4262 break;
4263 }
4264 }
4265 primitive_info[j].primitive=primitive_type;
4266 primitive_info[j].coordinates=x;
4267 primitive_info[j].method=FloodfillMethod;
4268 primitive_info[j].text=(char *) NULL;
4269 if (active)
4270 {
4271 AffineToTransform(image,&affine);
4272 active=MagickFalse;
4273 }
4274 active=MagickFalse;
4275 switch (primitive_type)
4276 {
4277 case PointPrimitive:
4278 default:
4279 {
4280 if (primitive_info[j].coordinates != 1)
4281 {
4282 status=MagickFalse;
4283 break;
4284 }
4285 break;
4286 }
4287 case LinePrimitive:
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 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%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+1].point.y);
4298 (void) WriteBlobString(image,message);
4299 break;
4300 }
4301 case RectanglePrimitive:
4302 {
4303 if (primitive_info[j].coordinates != 2)
4304 {
4305 status=MagickFalse;
4306 break;
4307 }
cristyb51dff52011-05-19 16:55:47 +00004308 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004309 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004310 primitive_info[j].point.x,primitive_info[j].point.y,
4311 primitive_info[j+1].point.x-primitive_info[j].point.x,
4312 primitive_info[j+1].point.y-primitive_info[j].point.y);
4313 (void) WriteBlobString(image,message);
4314 break;
4315 }
4316 case RoundRectanglePrimitive:
4317 {
4318 if (primitive_info[j].coordinates != 3)
4319 {
4320 status=MagickFalse;
4321 break;
4322 }
cristyb51dff52011-05-19 16:55:47 +00004323 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004324 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4325 "ry=\"%g\"/>\n",primitive_info[j].point.x,
cristy8cd5b312010-01-07 01:10:24 +00004326 primitive_info[j].point.y,primitive_info[j+1].point.x-
4327 primitive_info[j].point.x,primitive_info[j+1].point.y-
4328 primitive_info[j].point.y,primitive_info[j+2].point.x,
4329 primitive_info[j+2].point.y);
cristy3ed852e2009-09-05 21:47:34 +00004330 (void) WriteBlobString(image,message);
4331 break;
4332 }
4333 case ArcPrimitive:
4334 {
4335 if (primitive_info[j].coordinates != 3)
4336 {
4337 status=MagickFalse;
4338 break;
4339 }
4340 break;
4341 }
4342 case EllipsePrimitive:
4343 {
4344 if (primitive_info[j].coordinates != 3)
4345 {
4346 status=MagickFalse;
4347 break;
4348 }
cristyb51dff52011-05-19 16:55:47 +00004349 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004350 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004351 primitive_info[j].point.x,primitive_info[j].point.y,
4352 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4353 (void) WriteBlobString(image,message);
4354 break;
4355 }
4356 case CirclePrimitive:
4357 {
4358 double
4359 alpha,
4360 beta;
4361
4362 if (primitive_info[j].coordinates != 2)
4363 {
4364 status=MagickFalse;
4365 break;
4366 }
4367 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4368 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
cristyb51dff52011-05-19 16:55:47 +00004369 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004370 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004371 primitive_info[j].point.x,primitive_info[j].point.y,
4372 hypot(alpha,beta));
4373 (void) WriteBlobString(image,message);
4374 break;
4375 }
4376 case PolylinePrimitive:
4377 {
4378 if (primitive_info[j].coordinates < 2)
4379 {
4380 status=MagickFalse;
4381 break;
4382 }
4383 (void) CopyMagickString(message," <polyline points=\"",MaxTextExtent);
4384 (void) WriteBlobString(image,message);
4385 length=strlen(message);
4386 for ( ; j < i; j++)
4387 {
cristyb51dff52011-05-19 16:55:47 +00004388 (void) FormatLocaleString(message,MaxTextExtent,"%g,%g ",
cristy3ed852e2009-09-05 21:47:34 +00004389 primitive_info[j].point.x,primitive_info[j].point.y);
4390 length+=strlen(message);
4391 if (length >= 80)
4392 {
4393 (void) WriteBlobString(image,"\n ");
4394 length=strlen(message)+5;
4395 }
4396 (void) WriteBlobString(image,message);
4397 }
4398 (void) WriteBlobString(image,"\"/>\n");
4399 break;
4400 }
4401 case PolygonPrimitive:
4402 {
4403 if (primitive_info[j].coordinates < 3)
4404 {
4405 status=MagickFalse;
4406 break;
4407 }
4408 primitive_info[i]=primitive_info[j];
4409 primitive_info[i].coordinates=0;
4410 primitive_info[j].coordinates++;
4411 i++;
4412 (void) CopyMagickString(message," <polygon points=\"",MaxTextExtent);
4413 (void) WriteBlobString(image,message);
4414 length=strlen(message);
4415 for ( ; j < i; j++)
4416 {
cristyb51dff52011-05-19 16:55:47 +00004417 (void) FormatLocaleString(message,MaxTextExtent,"%g,%g ",
cristy3ed852e2009-09-05 21:47:34 +00004418 primitive_info[j].point.x,primitive_info[j].point.y);
4419 length+=strlen(message);
4420 if (length >= 80)
4421 {
4422 (void) WriteBlobString(image,"\n ");
4423 length=strlen(message)+5;
4424 }
4425 (void) WriteBlobString(image,message);
4426 }
4427 (void) WriteBlobString(image,"\"/>\n");
4428 break;
4429 }
4430 case BezierPrimitive:
4431 {
4432 if (primitive_info[j].coordinates < 3)
4433 {
4434 status=MagickFalse;
4435 break;
4436 }
4437 break;
4438 }
4439 case PathPrimitive:
4440 {
4441 int
4442 number_attributes;
4443
4444 GetMagickToken(q,&q,token);
4445 number_attributes=1;
4446 for (p=token; *p != '\0'; p++)
4447 if (isalpha((int) *p))
4448 number_attributes++;
cristybb503372010-05-27 20:51:26 +00004449 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
cristy3ed852e2009-09-05 21:47:34 +00004450 {
4451 number_points+=6*BezierQuantum*number_attributes;
4452 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4453 number_points,sizeof(*primitive_info));
4454 if (primitive_info == (PrimitiveInfo *) NULL)
4455 {
cristy3a37efd2011-08-28 20:31:03 +00004456 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004457 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4458 image->filename);
4459 break;
4460 }
4461 }
4462 (void) WriteBlobString(image," <path d=\"");
4463 (void) WriteBlobString(image,token);
4464 (void) WriteBlobString(image,"\"/>\n");
4465 break;
4466 }
4467 case ColorPrimitive:
4468 case MattePrimitive:
4469 {
4470 if (primitive_info[j].coordinates != 1)
4471 {
4472 status=MagickFalse;
4473 break;
4474 }
4475 GetMagickToken(q,&q,token);
4476 if (LocaleCompare("point",token) == 0)
4477 primitive_info[j].method=PointMethod;
4478 if (LocaleCompare("replace",token) == 0)
4479 primitive_info[j].method=ReplaceMethod;
4480 if (LocaleCompare("floodfill",token) == 0)
4481 primitive_info[j].method=FloodfillMethod;
4482 if (LocaleCompare("filltoborder",token) == 0)
4483 primitive_info[j].method=FillToBorderMethod;
4484 if (LocaleCompare("reset",token) == 0)
4485 primitive_info[j].method=ResetMethod;
4486 break;
4487 }
4488 case TextPrimitive:
4489 {
4490 register char
4491 *p;
4492
4493 if (primitive_info[j].coordinates != 1)
4494 {
4495 status=MagickFalse;
4496 break;
4497 }
4498 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004499 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004500 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
cristy3ed852e2009-09-05 21:47:34 +00004501 primitive_info[j].point.y);
4502 (void) WriteBlobString(image,message);
4503 for (p=token; *p != '\0'; p++)
4504 switch (*p)
4505 {
4506 case '<': (void) WriteBlobString(image,"&lt;"); break;
4507 case '>': (void) WriteBlobString(image,"&gt;"); break;
4508 case '&': (void) WriteBlobString(image,"&amp;"); break;
4509 default: (void) WriteBlobByte(image,*p); break;
4510 }
4511 (void) WriteBlobString(image,"</text>\n");
4512 break;
4513 }
4514 case ImagePrimitive:
4515 {
4516 if (primitive_info[j].coordinates != 2)
4517 {
4518 status=MagickFalse;
4519 break;
4520 }
4521 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004522 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004523 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
cristy3ed852e2009-09-05 21:47:34 +00004524 "xlink:href=\"%s\"/>\n",primitive_info[j].point.x,
4525 primitive_info[j].point.y,primitive_info[j+1].point.x,
4526 primitive_info[j+1].point.y,token);
4527 (void) WriteBlobString(image,message);
4528 break;
4529 }
4530 }
4531 if (primitive_info == (PrimitiveInfo *) NULL)
4532 break;
4533 primitive_info[i].primitive=UndefinedPrimitive;
4534 if (status == MagickFalse)
4535 break;
4536 }
4537 (void) WriteBlobString(image,"</svg>\n");
4538 /*
4539 Relinquish resources.
4540 */
4541 token=DestroyString(token);
4542 if (primitive_info != (PrimitiveInfo *) NULL)
4543 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4544 (void) CloseBlob(image);
4545 return(status);
4546}