blob: 8a60045968f2f6e96c58f8c9c07415462b8b70ce [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% SSSSS V V GGGG %
7% SS V V G %
8% SSS V V G GG %
9% SS V V G G %
10% SSSSS V GGG %
11% %
12% %
13% Read/Write Scalable Vector Graphics Format %
14% %
15% Software Design %
16% John Cristy %
17% William Radcliffe %
18% March 2000 %
19% %
20% %
cristy7e41fe82010-12-04 23:12:08 +000021% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/annotate.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/composite-private.h"
52#include "MagickCore/draw.h"
53#include "MagickCore/exception.h"
54#include "MagickCore/exception-private.h"
55#include "MagickCore/gem.h"
56#include "MagickCore/image.h"
57#include "MagickCore/image-private.h"
58#include "MagickCore/list.h"
59#include "MagickCore/log.h"
60#include "MagickCore/magick.h"
61#include "MagickCore/memory_.h"
62#include "MagickCore/module.h"
63#include "MagickCore/monitor.h"
64#include "MagickCore/monitor-private.h"
65#include "MagickCore/quantum-private.h"
66#include "MagickCore/pixel-accessor.h"
67#include "MagickCore/property.h"
68#include "MagickCore/resource_.h"
69#include "MagickCore/static.h"
70#include "MagickCore/string_.h"
71#include "MagickCore/string-private.h"
72#include "MagickCore/token.h"
73#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000074#if defined(MAGICKCORE_XML_DELEGATE)
cristy0157aea2010-04-24 21:12:18 +000075# if defined(MAGICKCORE_WINDOWS_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +000076# if defined(__MINGW32__)
77# define _MSC_VER
78# else
79# include <win32config.h>
80# endif
81# endif
82# include <libxml/parser.h>
83# include <libxml/xmlmemory.h>
84# include <libxml/parserInternals.h>
85# include <libxml/xmlerror.h>
86#endif
87
88#if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
89#include "autotrace/autotrace.h"
90#endif
91
92#if defined(MAGICKCORE_RSVG_DELEGATE)
93#include "librsvg/rsvg.h"
94#if defined(MAGICKCORE_CAIRO_DELEGATE)
95#include "librsvg/rsvg-cairo.h"
96#endif
97#include "librsvg/librsvg-features.h"
98#endif
99
100/*
cristy3ed852e2009-09-05 21:47:34 +0000101 Typedef declarations.
102*/
103typedef struct _BoundingBox
104{
105 double
106 x,
107 y,
108 width,
109 height;
110} BoundingBox;
111
112typedef struct _ElementInfo
113{
114 double
115 cx,
116 cy,
117 major,
118 minor,
119 angle;
120} ElementInfo;
121
122typedef struct _SVGInfo
123{
124 FILE
125 *file;
126
127 ExceptionInfo
128 *exception;
129
130 Image
131 *image;
132
133 const ImageInfo
134 *image_info;
135
136 AffineMatrix
137 affine;
138
cristybb503372010-05-27 20:51:26 +0000139 size_t
cristy3ed852e2009-09-05 21:47:34 +0000140 width,
141 height;
142
143 char
144 *size,
145 *title,
146 *comment;
147
148 int
149 n;
150
151 double
152 *scale,
153 pointsize;
154
155 ElementInfo
156 element;
157
158 SegmentInfo
159 segment;
160
161 BoundingBox
162 bounds,
163 center,
164 view_box;
165
166 PointInfo
167 radius;
168
169 char
170 *stop_color,
171 *offset,
172 *text,
173 *vertices,
174 *url;
175
176#if defined(MAGICKCORE_XML_DELEGATE)
177 xmlParserCtxtPtr
178 parser;
179
180 xmlDocPtr
181 document;
182#endif
183} SVGInfo;
184
185/*
186 Forward declarations.
187*/
188static MagickBooleanType
189 WriteSVGImage(const ImageInfo *,Image *);
190
191/*
192%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
193% %
194% %
195% %
196% I s S V G %
197% %
198% %
199% %
200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
201%
202% IsSVG()() returns MagickTrue if the image format type, identified by the
203% magick string, is SVG.
204%
205% The format of the IsSVG method is:
206%
207% MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
208%
209% A description of each parameter follows:
210%
211% o magick: compare image format pattern against these bytes.
212%
213% o length: Specifies the length of the magick string.
214%
215*/
216static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
217{
218 if (length < 4)
219 return(MagickFalse);
220 if (LocaleNCompare((const char *) magick,"?xml",4) == 0)
221 return(MagickTrue);
222 return(MagickFalse);
223}
224
225#if defined(MAGICKCORE_XML_DELEGATE)
226/*
227%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
228% %
229% %
230% %
231% R e a d S V G I m a g e %
232% %
233% %
234% %
235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
236%
237% ReadSVGImage() reads a Scalable Vector Gaphics file and returns it. It
238% allocates the memory necessary for the new Image structure and returns a
239% pointer to the new image.
240%
241% The format of the ReadSVGImage method is:
242%
243% Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
244%
245% A description of each parameter follows:
246%
247% o image_info: the image info.
248%
249% o exception: return any errors or warnings in this structure.
250%
251*/
252
253static SVGInfo *AcquireSVGInfo(void)
254{
255 SVGInfo
256 *svg_info;
257
cristy73bd4a52010-10-05 11:24:23 +0000258 svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info));
cristy3ed852e2009-09-05 21:47:34 +0000259 if (svg_info == (SVGInfo *) NULL)
260 return((SVGInfo *) NULL);
261 (void) ResetMagickMemory(svg_info,0,sizeof(*svg_info));
262 svg_info->text=AcquireString("");
cristy73bd4a52010-10-05 11:24:23 +0000263 svg_info->scale=(double *) AcquireMagickMemory(sizeof(*svg_info->scale));
cristy3ed852e2009-09-05 21:47:34 +0000264 if (svg_info->scale == (double *) NULL)
265 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
266 GetAffineMatrix(&svg_info->affine);
267 svg_info->scale[0]=ExpandAffine(&svg_info->affine);
268 return(svg_info);
269}
270
271static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
272{
273 if (svg_info->text != (char *) NULL)
274 svg_info->text=DestroyString(svg_info->text);
275 if (svg_info->scale != (double *) NULL)
276 svg_info->scale=(double *) (svg_info->scale);
277 if (svg_info->title != (char *) NULL)
278 svg_info->title=DestroyString(svg_info->title);
279 if (svg_info->comment != (char *) NULL)
280 svg_info->comment=DestroyString(svg_info->comment);
281 return((SVGInfo *) RelinquishMagickMemory(svg_info));
282}
283
284static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
285 const char *string)
286{
287 char
288 token[MaxTextExtent];
289
290 const char
291 *p;
292
293 double
294 value;
295
296 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
297 assert(string != (const char *) NULL);
298 p=(const char *) string;
299 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +0000300 value=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000301 if (strchr(token,'%') != (char *) NULL)
302 {
303 double
304 alpha,
305 beta;
306
307 if (type > 0)
308 {
309 if (svg_info->view_box.width == 0.0)
310 return(1000.0);
311 return(svg_info->view_box.width*value/100.0);
312 }
313 if (type < 0)
314 {
315 if (svg_info->view_box.height == 0.0)
316 return(1000.0);
317 return(svg_info->view_box.height*value/100.0);
318 }
319 alpha=value-svg_info->view_box.width;
320 beta=value-svg_info->view_box.height;
321 return(hypot(alpha,beta)/sqrt(2.0)/100.0);
322 }
323 GetMagickToken(p,&p,token);
324 if (LocaleNCompare(token,"cm",2) == 0)
325 return(DefaultResolution*svg_info->scale[0]/2.54*value);
326 if (LocaleNCompare(token,"em",2) == 0)
327 return(svg_info->pointsize*value);
328 if (LocaleNCompare(token,"ex",2) == 0)
329 return(svg_info->pointsize*value/2.0);
330 if (LocaleNCompare(token,"in",2) == 0)
331 return(DefaultResolution*svg_info->scale[0]*value);
332 if (LocaleNCompare(token,"mm",2) == 0)
333 return(DefaultResolution*svg_info->scale[0]/25.4*value);
334 if (LocaleNCompare(token,"pc",2) == 0)
335 return(DefaultResolution*svg_info->scale[0]/6.0*value);
336 if (LocaleNCompare(token,"pt",2) == 0)
337 return(svg_info->scale[0]*value);
338 if (LocaleNCompare(token,"px",2) == 0)
339 return(value);
340 return(value);
341}
342
343static void StripStyleTokens(char *message)
344{
345 register char
346 *p,
347 *q;
348
349 size_t
350 length;
351
352 assert(message != (char *) NULL);
353 if (*message == '\0')
354 return;
355 length=strlen(message);
356 p=message;
357 while (isspace((int) ((unsigned char) *p)) != 0)
358 p++;
359 q=message+length-1;
360 while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
361 q--;
362 (void) CopyMagickMemory(message,p,(size_t) (q-p+1));
363 message[q-p+1]='\0';
364 StripString(message);
365}
366
367static char **GetStyleTokens(void *context,const char *style,int *number_tokens)
368{
369 char
370 *text,
371 **tokens;
372
cristybb503372010-05-27 20:51:26 +0000373 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000374 i;
375
376 SVGInfo
377 *svg_info;
378
379 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +0000380 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +0000381 *number_tokens=0;
382 if (style == (const char *) NULL)
383 return((char **) NULL);
384 text=AcquireString(style);
385 (void) SubstituteString(&text,":","\n");
386 (void) SubstituteString(&text,";","\n");
387 tokens=StringToList(text);
388 text=DestroyString(text);
389 for (i=0; tokens[i] != (char *) NULL; i++)
390 StripStyleTokens(tokens[i]);
391 *number_tokens=i;
392 return(tokens);
393}
394
395static char **GetTransformTokens(void *context,const char *text,
396 int *number_tokens)
397{
398 char
399 **tokens;
400
401 register const char
402 *p,
403 *q;
404
cristybb503372010-05-27 20:51:26 +0000405 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000406 i;
407
408 SVGInfo
409 *svg_info;
410
411 svg_info=(SVGInfo *) context;
412 *number_tokens=0;
413 if (text == (const char *) NULL)
414 return((char **) NULL);
415 /*
416 Determine the number of arguments.
417 */
418 for (p=text; *p != '\0'; p++)
419 {
420 if (*p == '(')
421 (*number_tokens)+=2;
422 }
423 tokens=(char **) AcquireQuantumMemory(*number_tokens+2UL,sizeof(*tokens));
424 if (tokens == (char **) NULL)
425 {
426 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
427 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
428 return((char **) NULL);
429 }
430 /*
431 Convert string to an ASCII list.
432 */
433 i=0;
434 p=text;
435 for (q=p; *q != '\0'; q++)
436 {
437 if ((*q != '(') && (*q != ')') && (*q != '\0'))
438 continue;
439 tokens[i]=AcquireString(p);
440 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
441 StripString(tokens[i++]);
442 p=q+1;
443 }
444 tokens[i]=AcquireString(p);
445 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
446 StripString(tokens[i++]);
447 tokens[i]=(char *) NULL;
448 return(tokens);
449}
450
451#if defined(__cplusplus) || defined(c_plusplus)
452extern "C" {
453#endif
454
455static int SVGIsStandalone(void *context)
456{
457 SVGInfo
458 *svg_info;
459
460 /*
461 Is this document tagged standalone?
462 */
463 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGIsStandalone()");
464 svg_info=(SVGInfo *) context;
465 return(svg_info->document->standalone == 1);
466}
467
468static int SVGHasInternalSubset(void *context)
469{
470 SVGInfo
471 *svg_info;
472
473 /*
474 Does this document has an internal subset?
475 */
476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
477 " SAX.SVGHasInternalSubset()");
478 svg_info=(SVGInfo *) context;
479 return(svg_info->document->intSubset != NULL);
480}
481
482static int SVGHasExternalSubset(void *context)
483{
484 SVGInfo
485 *svg_info;
486
487 /*
488 Does this document has an external subset?
489 */
490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
491 " SAX.SVGHasExternalSubset()");
492 svg_info=(SVGInfo *) context;
493 return(svg_info->document->extSubset != NULL);
494}
495
496static void SVGInternalSubset(void *context,const xmlChar *name,
497 const xmlChar *external_id,const xmlChar *system_id)
498{
499 SVGInfo
500 *svg_info;
501
502 /*
503 Does this document has an internal subset?
504 */
505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
506 " SAX.internalSubset(%s, %s, %s)",(const char *) name,
507 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
508 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
509 svg_info=(SVGInfo *) context;
510 (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id);
511}
512
513static xmlParserInputPtr SVGResolveEntity(void *context,
514 const xmlChar *public_id,const xmlChar *system_id)
515{
516 SVGInfo
517 *svg_info;
518
519 xmlParserInputPtr
520 stream;
521
522 /*
523 Special entity resolver, better left to the parser, it has more
524 context than the application layer. The default behaviour is to
525 not resolve the entities, in that case the ENTITY_REF nodes are
526 built in the structure (and the parameter values).
527 */
528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
529 " SAX.resolveEntity(%s, %s)",
530 (public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"),
531 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
532 svg_info=(SVGInfo *) context;
533 stream=xmlLoadExternalEntity((const char *) system_id,(const char *)
534 public_id,svg_info->parser);
535 return(stream);
536}
537
538static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
539{
540 SVGInfo
541 *svg_info;
542
543 /*
544 Get an entity by name.
545 */
546 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGGetEntity(%s)",
547 name);
548 svg_info=(SVGInfo *) context;
549 return(xmlGetDocEntity(svg_info->document,name));
550}
551
552static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
553{
554 SVGInfo
555 *svg_info;
556
557 /*
558 Get a parameter entity by name.
559 */
560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
561 " SAX.getParameterEntity(%s)",name);
562 svg_info=(SVGInfo *) context;
563 return(xmlGetParameterEntity(svg_info->document,name));
564}
565
566static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
567 const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
568{
569 SVGInfo
570 *svg_info;
571
572 /*
573 An entity definition has been parsed.
574 */
575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
576 " SAX.entityDecl(%s, %d, %s, %s, %s)",name,type,
577 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
578 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content);
579 svg_info=(SVGInfo *) context;
580 if (svg_info->parser->inSubset == 1)
581 (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id,
582 content);
583 else
584 if (svg_info->parser->inSubset == 2)
585 (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
586 content);
587}
588
589static void SVGAttributeDeclaration(void *context,const xmlChar *element,
590 const xmlChar *name,int type,int value,const xmlChar *default_value,
591 xmlEnumerationPtr tree)
592{
593 SVGInfo
594 *svg_info;
595
596 xmlChar
597 *fullname,
598 *prefix;
599
600 xmlParserCtxtPtr
601 parser;
602
603 /*
604 An attribute definition has been parsed.
605 */
606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
607 " SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value,
608 default_value);
609 svg_info=(SVGInfo *) context;
610 fullname=(xmlChar *) NULL;
611 prefix=(xmlChar *) NULL;
612 parser=svg_info->parser;
613 fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix);
614 if (parser->inSubset == 1)
615 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset,
616 element,fullname,prefix,(xmlAttributeType) type,
617 (xmlAttributeDefault) value,default_value,tree);
618 else
619 if (parser->inSubset == 2)
620 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset,
621 element,fullname,prefix,(xmlAttributeType) type,
622 (xmlAttributeDefault) value,default_value,tree);
623 if (prefix != (xmlChar *) NULL)
624 xmlFree(prefix);
625 if (fullname != (xmlChar *) NULL)
626 xmlFree(fullname);
627}
628
629static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
630 xmlElementContentPtr content)
631{
632 SVGInfo
633 *svg_info;
634
635 xmlParserCtxtPtr
636 parser;
637
638 /*
639 An element definition has been parsed.
640 */
641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
642 " SAX.elementDecl(%s, %d, ...)",name,type);
643 svg_info=(SVGInfo *) context;
644 parser=svg_info->parser;
645 if (parser->inSubset == 1)
646 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset,
647 name,(xmlElementTypeVal) type,content);
648 else
649 if (parser->inSubset == 2)
650 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
651 name,(xmlElementTypeVal) type,content);
652}
653
654static void SVGNotationDeclaration(void *context,const xmlChar *name,
655 const xmlChar *public_id,const xmlChar *system_id)
656{
657 SVGInfo
658 *svg_info;
659
660 xmlParserCtxtPtr
661 parser;
662
663 /*
664 What to do when a notation declaration has been parsed.
665 */
666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
667 " SAX.notationDecl(%s, %s, %s)",name,
668 public_id != (const xmlChar *) NULL ? (const char *) public_id : "none",
669 system_id != (const xmlChar *) NULL ? (const char *) system_id : "none");
670 svg_info=(SVGInfo *) context;
671 parser=svg_info->parser;
672 if (parser->inSubset == 1)
673 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
674 name,public_id,system_id);
675 else
676 if (parser->inSubset == 2)
677 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
678 name,public_id,system_id);
679}
680
681static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
682 const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
683{
684 SVGInfo
685 *svg_info;
686
687 /*
688 What to do when an unparsed entity declaration is parsed.
689 */
690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
691 " SAX.unparsedEntityDecl(%s, %s, %s, %s)",name,
692 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
693 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation);
694 svg_info=(SVGInfo *) context;
695 (void) xmlAddDocEntity(svg_info->document,name,
696 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
697
698}
699
700static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
701{
702 SVGInfo
703 *svg_info;
704
705 /*
706 Receive the document locator at startup, actually xmlDefaultSAXLocator.
707 */
708 (void) location;
709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
710 " SAX.setDocumentLocator()");
711 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +0000712 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +0000713}
714
715static void SVGStartDocument(void *context)
716{
717 SVGInfo
718 *svg_info;
719
720 xmlParserCtxtPtr
721 parser;
722
723 /*
724 Called when the document start being processed.
725 */
726 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startDocument()");
727 svg_info=(SVGInfo *) context;
728 GetExceptionInfo(svg_info->exception);
729 parser=svg_info->parser;
730 svg_info->document=xmlNewDoc(parser->version);
731 if (svg_info->document == (xmlDocPtr) NULL)
732 return;
733 if (parser->encoding == NULL)
734 svg_info->document->encoding=(const xmlChar *) NULL;
735 else
736 svg_info->document->encoding=xmlStrdup(parser->encoding);
737 svg_info->document->standalone=parser->standalone;
738}
739
740static void SVGEndDocument(void *context)
741{
742 SVGInfo
743 *svg_info;
744
745 /*
746 Called when the document end has been detected.
747 */
748 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()");
749 svg_info=(SVGInfo *) context;
750 if (svg_info->offset != (char *) NULL)
751 svg_info->offset=DestroyString(svg_info->offset);
752 if (svg_info->stop_color != (char *) NULL)
753 svg_info->stop_color=DestroyString(svg_info->stop_color);
754 if (svg_info->scale != (double *) NULL)
755 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
756 if (svg_info->text != (char *) NULL)
757 svg_info->text=DestroyString(svg_info->text);
758 if (svg_info->vertices != (char *) NULL)
759 svg_info->vertices=DestroyString(svg_info->vertices);
760 if (svg_info->url != (char *) NULL)
761 svg_info->url=DestroyString(svg_info->url);
762#if defined(MAGICKCORE_XML_DELEGATE)
763 if (svg_info->document != (xmlDocPtr) NULL)
764 {
765 xmlFreeDoc(svg_info->document);
766 svg_info->document=(xmlDocPtr) NULL;
767 }
768#endif
769}
770
771static void SVGStartElement(void *context,const xmlChar *name,
772 const xmlChar **attributes)
773{
774 char
775 *color,
776 id[MaxTextExtent],
777 token[MaxTextExtent],
778 **tokens,
779 *units;
780
781 const char
782 *keyword,
783 *p,
784 *value;
785
786 int
787 number_tokens;
788
789 SVGInfo
790 *svg_info;
791
cristybb503372010-05-27 20:51:26 +0000792 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000793 i,
794 j;
795
796 /*
797 Called when an opening tag has been processed.
798 */
799 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s",
800 name);
801 svg_info=(SVGInfo *) context;
802 svg_info->n++;
803 svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale,
804 svg_info->n+1UL,sizeof(*svg_info->scale));
805 if (svg_info->scale == (double *) NULL)
806 {
807 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
808 ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
809 return;
810 }
811 svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
812 color=AcquireString("none");
813 units=AcquireString("userSpaceOnUse");
814 value=(const char *) NULL;
815 if (attributes != (const xmlChar **) NULL)
816 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
817 {
818 keyword=(const char *) attributes[i];
819 value=(const char *) attributes[i+1];
820 switch (*keyword)
821 {
822 case 'C':
823 case 'c':
824 {
825 if (LocaleCompare(keyword,"cx") == 0)
826 {
827 svg_info->element.cx=
828 GetUserSpaceCoordinateValue(svg_info,1,value);
829 break;
830 }
831 if (LocaleCompare(keyword,"cy") == 0)
832 {
833 svg_info->element.cy=
834 GetUserSpaceCoordinateValue(svg_info,-1,value);
835 break;
836 }
837 break;
838 }
839 case 'F':
840 case 'f':
841 {
842 if (LocaleCompare(keyword,"fx") == 0)
843 {
844 svg_info->element.major=
845 GetUserSpaceCoordinateValue(svg_info,1,value);
846 break;
847 }
848 if (LocaleCompare(keyword,"fy") == 0)
849 {
850 svg_info->element.minor=
851 GetUserSpaceCoordinateValue(svg_info,-1,value);
852 break;
853 }
854 break;
855 }
856 case 'H':
857 case 'h':
858 {
859 if (LocaleCompare(keyword,"height") == 0)
860 {
861 svg_info->bounds.height=
862 GetUserSpaceCoordinateValue(svg_info,-1,value);
863 break;
864 }
865 break;
866 }
867 case 'I':
868 case 'i':
869 {
870 if (LocaleCompare(keyword,"id") == 0)
871 {
872 (void) CopyMagickString(id,value,MaxTextExtent);
873 break;
874 }
875 break;
876 }
877 case 'R':
878 case 'r':
879 {
880 if (LocaleCompare(keyword,"r") == 0)
881 {
882 svg_info->element.angle=
883 GetUserSpaceCoordinateValue(svg_info,0,value);
884 break;
885 }
886 break;
887 }
888 case 'W':
889 case 'w':
890 {
891 if (LocaleCompare(keyword,"width") == 0)
892 {
893 svg_info->bounds.width=
894 GetUserSpaceCoordinateValue(svg_info,1,value);
895 break;
896 }
897 break;
898 }
899 case 'X':
900 case 'x':
901 {
902 if (LocaleCompare(keyword,"x") == 0)
903 {
904 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value)-
905 svg_info->center.x;
906 break;
907 }
908 if (LocaleCompare(keyword,"x1") == 0)
909 {
910 svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
911 value);
912 break;
913 }
914 if (LocaleCompare(keyword,"x2") == 0)
915 {
916 svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
917 value);
918 break;
919 }
920 break;
921 }
922 case 'Y':
923 case 'y':
924 {
925 if (LocaleCompare(keyword,"y") == 0)
926 {
927 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value)-
928 svg_info->center.y;
929 break;
930 }
931 if (LocaleCompare(keyword,"y1") == 0)
932 {
933 svg_info->segment.y1=
934 GetUserSpaceCoordinateValue(svg_info,-1,value);
935 break;
936 }
937 if (LocaleCompare(keyword,"y2") == 0)
938 {
939 svg_info->segment.y2=
940 GetUserSpaceCoordinateValue(svg_info,-1,value);
941 break;
942 }
943 break;
944 }
945 default:
946 break;
947 }
948 }
cristy13d07042010-11-21 20:56:18 +0000949 if (strchr((char *) name,':') != (char *) NULL)
cristy768b9302010-11-20 00:05:47 +0000950 {
951 /*
952 Skip over namespace.
953 */
954 for ( ; *name != ':'; name++) ;
955 name++;
956 }
cristy3ed852e2009-09-05 21:47:34 +0000957 switch (*name)
958 {
959 case 'C':
960 case 'c':
961 {
962 if (LocaleCompare((const char *) name,"circle") == 0)
963 {
cristyb51dff52011-05-19 16:55:47 +0000964 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +0000965 break;
966 }
967 if (LocaleCompare((const char *) name,"clipPath") == 0)
968 {
cristyb51dff52011-05-19 16:55:47 +0000969 (void) FormatLocaleFile(svg_info->file,"push clip-path '%s'\n",id);
cristy3ed852e2009-09-05 21:47:34 +0000970 break;
971 }
972 break;
973 }
974 case 'D':
975 case 'd':
976 {
977 if (LocaleCompare((const char *) name,"defs") == 0)
978 {
cristyb51dff52011-05-19 16:55:47 +0000979 (void) FormatLocaleFile(svg_info->file,"push defs\n");
cristy3ed852e2009-09-05 21:47:34 +0000980 break;
981 }
982 break;
983 }
984 case 'E':
985 case 'e':
986 {
987 if (LocaleCompare((const char *) name,"ellipse") == 0)
988 {
cristyb51dff52011-05-19 16:55:47 +0000989 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +0000990 break;
991 }
992 break;
993 }
994 case 'G':
995 case 'g':
996 {
997 if (LocaleCompare((const char *) name,"g") == 0)
998 {
cristyb51dff52011-05-19 16:55:47 +0000999 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001000 break;
1001 }
1002 break;
1003 }
1004 case 'I':
1005 case 'i':
1006 {
1007 if (LocaleCompare((const char *) name,"image") == 0)
1008 {
cristyb51dff52011-05-19 16:55:47 +00001009 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001010 break;
1011 }
1012 break;
1013 }
1014 case 'L':
1015 case 'l':
1016 {
1017 if (LocaleCompare((const char *) name,"line") == 0)
1018 {
cristyb51dff52011-05-19 16:55:47 +00001019 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001020 break;
1021 }
1022 if (LocaleCompare((const char *) name,"linearGradient") == 0)
1023 {
cristyb51dff52011-05-19 16:55:47 +00001024 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001025 "push gradient '%s' linear %g,%g %g,%g\n",id,
cristy3ed852e2009-09-05 21:47:34 +00001026 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
1027 svg_info->segment.y2);
1028 break;
1029 }
1030 break;
1031 }
1032 case 'P':
1033 case 'p':
1034 {
1035 if (LocaleCompare((const char *) name,"path") == 0)
1036 {
cristyb51dff52011-05-19 16:55:47 +00001037 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001038 break;
1039 }
1040 if (LocaleCompare((const char *) name,"pattern") == 0)
1041 {
cristyb51dff52011-05-19 16:55:47 +00001042 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001043 "push pattern '%s' %g,%g %g,%g\n",id,
cristy3ed852e2009-09-05 21:47:34 +00001044 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
1045 svg_info->bounds.height);
1046 break;
1047 }
1048 if (LocaleCompare((const char *) name,"polygon") == 0)
1049 {
cristyb51dff52011-05-19 16:55:47 +00001050 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001051 break;
1052 }
1053 if (LocaleCompare((const char *) name,"polyline") == 0)
1054 {
cristyb51dff52011-05-19 16:55:47 +00001055 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001056 break;
1057 }
1058 break;
1059 }
1060 case 'R':
1061 case 'r':
1062 {
1063 if (LocaleCompare((const char *) name,"radialGradient") == 0)
1064 {
cristyb51dff52011-05-19 16:55:47 +00001065 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001066 "push gradient '%s' radial %g,%g %g,%g %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001067 id,svg_info->element.cx,svg_info->element.cy,
1068 svg_info->element.major,svg_info->element.minor,
1069 svg_info->element.angle);
1070 break;
1071 }
1072 if (LocaleCompare((const char *) name,"rect") == 0)
1073 {
cristyb51dff52011-05-19 16:55:47 +00001074 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001075 break;
1076 }
1077 break;
1078 }
1079 case 'S':
1080 case 's':
1081 {
1082 if (LocaleCompare((const char *) name,"svg") == 0)
1083 {
cristyb51dff52011-05-19 16:55:47 +00001084 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001085 break;
1086 }
1087 break;
1088 }
1089 case 'T':
1090 case 't':
1091 {
1092 if (LocaleCompare((const char *) name,"text") == 0)
1093 {
cristyb51dff52011-05-19 16:55:47 +00001094 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001095 break;
1096 }
1097 if (LocaleCompare((const char *) name,"tspan") == 0)
1098 {
1099 if (*svg_info->text != '\0')
1100 {
1101 DrawInfo
1102 *draw_info;
1103
1104 TypeMetric
1105 metrics;
1106
1107 char
1108 *text;
1109
1110 text=EscapeString(svg_info->text,'\'');
cristyb51dff52011-05-19 16:55:47 +00001111 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
cristy8cd5b312010-01-07 01:10:24 +00001112 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1113 svg_info->center.y,text);
cristy3ed852e2009-09-05 21:47:34 +00001114 text=DestroyString(text);
1115 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
1116 draw_info->pointsize=svg_info->pointsize;
1117 draw_info->text=AcquireString(svg_info->text);
1118 (void) ConcatenateString(&draw_info->text," ");
1119 GetTypeMetrics(svg_info->image,draw_info,&metrics);
1120 svg_info->bounds.x+=metrics.width;
1121 draw_info=DestroyDrawInfo(draw_info);
1122 *svg_info->text='\0';
1123 }
cristyb51dff52011-05-19 16:55:47 +00001124 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001125 break;
1126 }
1127 break;
1128 }
1129 default:
1130 break;
1131 }
1132 if (attributes != (const xmlChar **) NULL)
1133 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1134 {
1135 keyword=(const char *) attributes[i];
1136 value=(const char *) attributes[i+1];
1137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1138 " %s = %s",keyword,value);
1139 switch (*keyword)
1140 {
1141 case 'A':
1142 case 'a':
1143 {
1144 if (LocaleCompare(keyword,"angle") == 0)
1145 {
cristyb51dff52011-05-19 16:55:47 +00001146 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001147 GetUserSpaceCoordinateValue(svg_info,0,value));
1148 break;
1149 }
1150 break;
1151 }
1152 case 'C':
1153 case 'c':
1154 {
1155 if (LocaleCompare(keyword,"clip-path") == 0)
1156 {
cristyb51dff52011-05-19 16:55:47 +00001157 (void) FormatLocaleFile(svg_info->file,"clip-path '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001158 break;
1159 }
1160 if (LocaleCompare(keyword,"clip-rule") == 0)
1161 {
cristyb51dff52011-05-19 16:55:47 +00001162 (void) FormatLocaleFile(svg_info->file,"clip-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001163 break;
1164 }
1165 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1166 {
1167 (void) CloneString(&units,value);
cristyb51dff52011-05-19 16:55:47 +00001168 (void) FormatLocaleFile(svg_info->file,"clip-units '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001169 break;
1170 }
1171 if (LocaleCompare(keyword,"color") == 0)
1172 {
1173 (void) CloneString(&color,value);
1174 break;
1175 }
1176 if (LocaleCompare(keyword,"cx") == 0)
1177 {
1178 svg_info->element.cx=
1179 GetUserSpaceCoordinateValue(svg_info,1,value);
1180 break;
1181 }
1182 if (LocaleCompare(keyword,"cy") == 0)
1183 {
1184 svg_info->element.cy=
1185 GetUserSpaceCoordinateValue(svg_info,-1,value);
1186 break;
1187 }
1188 break;
1189 }
1190 case 'D':
1191 case 'd':
1192 {
1193 if (LocaleCompare(keyword,"d") == 0)
1194 {
1195 (void) CloneString(&svg_info->vertices,value);
1196 break;
1197 }
1198 if (LocaleCompare(keyword,"dx") == 0)
1199 {
1200 svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
1201 break;
1202 }
1203 if (LocaleCompare(keyword,"dy") == 0)
1204 {
1205 svg_info->bounds.y+=
1206 GetUserSpaceCoordinateValue(svg_info,-1,value);
1207 break;
1208 }
1209 break;
1210 }
1211 case 'F':
1212 case 'f':
1213 {
1214 if (LocaleCompare(keyword,"fill") == 0)
1215 {
1216 if (LocaleCompare(value,"currentColor") == 0)
1217 {
cristyb51dff52011-05-19 16:55:47 +00001218 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001219 break;
1220 }
cristyb51dff52011-05-19 16:55:47 +00001221 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001222 break;
1223 }
1224 if (LocaleCompare(keyword,"fillcolor") == 0)
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,"fill-rule") == 0)
1230 {
cristyb51dff52011-05-19 16:55:47 +00001231 (void) FormatLocaleFile(svg_info->file,"fill-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001232 break;
1233 }
1234 if (LocaleCompare(keyword,"fill-opacity") == 0)
1235 {
cristyb51dff52011-05-19 16:55:47 +00001236 (void) FormatLocaleFile(svg_info->file,"fill-opacity '%s'\n",
1237 value);
cristy3ed852e2009-09-05 21:47:34 +00001238 break;
1239 }
1240 if (LocaleCompare(keyword,"font-family") == 0)
1241 {
cristyb51dff52011-05-19 16:55:47 +00001242 (void) FormatLocaleFile(svg_info->file,"font-family '%s'\n",
1243 value);
cristy3ed852e2009-09-05 21:47:34 +00001244 break;
1245 }
1246 if (LocaleCompare(keyword,"font-stretch") == 0)
1247 {
cristyb51dff52011-05-19 16:55:47 +00001248 (void) FormatLocaleFile(svg_info->file,"font-stretch '%s'\n",
1249 value);
cristy3ed852e2009-09-05 21:47:34 +00001250 break;
1251 }
1252 if (LocaleCompare(keyword,"font-style") == 0)
1253 {
cristyb51dff52011-05-19 16:55:47 +00001254 (void) FormatLocaleFile(svg_info->file,"font-style '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001255 break;
1256 }
1257 if (LocaleCompare(keyword,"font-size") == 0)
1258 {
1259 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
cristyb51dff52011-05-19 16:55:47 +00001260 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1261 svg_info->pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001262 break;
1263 }
1264 if (LocaleCompare(keyword,"font-weight") == 0)
1265 {
cristyb51dff52011-05-19 16:55:47 +00001266 (void) FormatLocaleFile(svg_info->file,"font-weight '%s'\n",
1267 value);
cristy3ed852e2009-09-05 21:47:34 +00001268 break;
1269 }
1270 break;
1271 }
1272 case 'G':
1273 case 'g':
1274 {
1275 if (LocaleCompare(keyword,"gradientTransform") == 0)
1276 {
1277 AffineMatrix
1278 affine,
1279 current,
1280 transform;
1281
1282 GetAffineMatrix(&transform);
1283 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1284 tokens=GetTransformTokens(context,value,&number_tokens);
1285 for (j=0; j < (number_tokens-1); j+=2)
1286 {
1287 keyword=(char *) tokens[j];
1288 if (keyword == (char *) NULL)
1289 continue;
1290 value=(char *) tokens[j+1];
1291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1292 " %s: %s",keyword,value);
1293 current=transform;
1294 GetAffineMatrix(&affine);
1295 switch (*keyword)
1296 {
1297 case 'M':
1298 case 'm':
1299 {
1300 if (LocaleCompare(keyword,"matrix") == 0)
1301 {
1302 p=(const char *) value;
1303 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001304 affine.sx=InterpretLocaleValue(value,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001305 GetMagickToken(p,&p,token);
1306 if (*token == ',')
1307 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001308 affine.rx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001309 GetMagickToken(p,&p,token);
1310 if (*token == ',')
1311 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001312 affine.ry=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001313 GetMagickToken(p,&p,token);
1314 if (*token == ',')
1315 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001316 affine.sy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001317 GetMagickToken(p,&p,token);
1318 if (*token == ',')
1319 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001320 affine.tx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001321 GetMagickToken(p,&p,token);
1322 if (*token == ',')
1323 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001324 affine.ty=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001325 break;
1326 }
1327 break;
1328 }
1329 case 'R':
1330 case 'r':
1331 {
1332 if (LocaleCompare(keyword,"rotate") == 0)
1333 {
1334 double
1335 angle;
1336
1337 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1338 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1339 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1340 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1341 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1342 break;
1343 }
1344 break;
1345 }
1346 case 'S':
1347 case 's':
1348 {
1349 if (LocaleCompare(keyword,"scale") == 0)
1350 {
1351 for (p=(const char *) value; *p != '\0'; p++)
1352 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1353 (*p == ','))
1354 break;
1355 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1356 affine.sy=affine.sx;
1357 if (*p != '\0')
1358 affine.sy=
1359 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1360 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1361 break;
1362 }
1363 if (LocaleCompare(keyword,"skewX") == 0)
1364 {
1365 affine.sx=svg_info->affine.sx;
1366 affine.ry=tan(DegreesToRadians(fmod(
1367 GetUserSpaceCoordinateValue(svg_info,1,value),
1368 360.0)));
1369 affine.sy=svg_info->affine.sy;
1370 break;
1371 }
1372 if (LocaleCompare(keyword,"skewY") == 0)
1373 {
1374 affine.sx=svg_info->affine.sx;
1375 affine.rx=tan(DegreesToRadians(fmod(
1376 GetUserSpaceCoordinateValue(svg_info,-1,value),
1377 360.0)));
1378 affine.sy=svg_info->affine.sy;
1379 break;
1380 }
1381 break;
1382 }
1383 case 'T':
1384 case 't':
1385 {
1386 if (LocaleCompare(keyword,"translate") == 0)
1387 {
1388 for (p=(const char *) value; *p != '\0'; p++)
1389 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1390 (*p == ','))
1391 break;
1392 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1393 affine.ty=affine.tx;
1394 if (*p != '\0')
1395 affine.ty=
1396 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1397 break;
1398 }
1399 break;
1400 }
1401 default:
1402 break;
1403 }
cristyef7c8a52010-10-10 13:46:51 +00001404 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1405 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1406 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1407 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
1408 transform.tx=affine.sx*current.tx+affine.ry*current.ty+
1409 affine.tx;
1410 transform.ty=affine.rx*current.tx+affine.sy*current.ty+
1411 affine.ty;
cristy3ed852e2009-09-05 21:47:34 +00001412 }
cristyb51dff52011-05-19 16:55:47 +00001413 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001414 "affine %g %g %g %g %g %g\n",transform.sx,
cristy8cd5b312010-01-07 01:10:24 +00001415 transform.rx,transform.ry,transform.sy,transform.tx,
1416 transform.ty);
cristy3ed852e2009-09-05 21:47:34 +00001417 for (j=0; tokens[j] != (char *) NULL; j++)
1418 tokens[j]=DestroyString(tokens[j]);
1419 tokens=(char **) RelinquishMagickMemory(tokens);
1420 break;
1421 }
1422 if (LocaleCompare(keyword,"gradientUnits") == 0)
1423 {
1424 (void) CloneString(&units,value);
cristyb51dff52011-05-19 16:55:47 +00001425 (void) FormatLocaleFile(svg_info->file,"gradient-units '%s'\n",
1426 value);
cristy3ed852e2009-09-05 21:47:34 +00001427 break;
1428 }
1429 break;
1430 }
1431 case 'H':
1432 case 'h':
1433 {
1434 if (LocaleCompare(keyword,"height") == 0)
1435 {
1436 svg_info->bounds.height=
1437 GetUserSpaceCoordinateValue(svg_info,-1,value);
1438 break;
1439 }
1440 if (LocaleCompare(keyword,"href") == 0)
1441 {
1442 (void) CloneString(&svg_info->url,value);
1443 break;
1444 }
1445 break;
1446 }
1447 case 'M':
1448 case 'm':
1449 {
1450 if (LocaleCompare(keyword,"major") == 0)
1451 {
1452 svg_info->element.major=
1453 GetUserSpaceCoordinateValue(svg_info,1,value);
1454 break;
1455 }
1456 if (LocaleCompare(keyword,"minor") == 0)
1457 {
1458 svg_info->element.minor=
1459 GetUserSpaceCoordinateValue(svg_info,-1,value);
1460 break;
1461 }
1462 break;
1463 }
1464 case 'O':
1465 case 'o':
1466 {
1467 if (LocaleCompare(keyword,"offset") == 0)
1468 {
1469 (void) CloneString(&svg_info->offset,value);
1470 break;
1471 }
1472 if (LocaleCompare(keyword,"opacity") == 0)
1473 {
cristyb51dff52011-05-19 16:55:47 +00001474 (void) FormatLocaleFile(svg_info->file,"opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001475 break;
1476 }
1477 break;
1478 }
1479 case 'P':
1480 case 'p':
1481 {
1482 if (LocaleCompare(keyword,"path") == 0)
1483 {
1484 (void) CloneString(&svg_info->url,value);
1485 break;
1486 }
1487 if (LocaleCompare(keyword,"points") == 0)
1488 {
1489 (void) CloneString(&svg_info->vertices,value);
1490 break;
1491 }
1492 break;
1493 }
1494 case 'R':
1495 case 'r':
1496 {
1497 if (LocaleCompare(keyword,"r") == 0)
1498 {
1499 svg_info->element.major=
1500 GetUserSpaceCoordinateValue(svg_info,1,value);
1501 svg_info->element.minor=
1502 GetUserSpaceCoordinateValue(svg_info,-1,value);
1503 break;
1504 }
1505 if (LocaleCompare(keyword,"rotate") == 0)
1506 {
1507 double
1508 angle;
1509
1510 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
cristyb51dff52011-05-19 16:55:47 +00001511 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
cristy8cd5b312010-01-07 01:10:24 +00001512 svg_info->bounds.x,svg_info->bounds.y);
cristy3ed852e2009-09-05 21:47:34 +00001513 svg_info->bounds.x=0;
1514 svg_info->bounds.y=0;
cristyb51dff52011-05-19 16:55:47 +00001515 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
cristy3ed852e2009-09-05 21:47:34 +00001516 break;
1517 }
1518 if (LocaleCompare(keyword,"rx") == 0)
1519 {
1520 if (LocaleCompare((const char *) name,"ellipse") == 0)
1521 svg_info->element.major=
1522 GetUserSpaceCoordinateValue(svg_info,1,value);
1523 else
1524 svg_info->radius.x=
1525 GetUserSpaceCoordinateValue(svg_info,1,value);
1526 break;
1527 }
1528 if (LocaleCompare(keyword,"ry") == 0)
1529 {
1530 if (LocaleCompare((const char *) name,"ellipse") == 0)
1531 svg_info->element.minor=
1532 GetUserSpaceCoordinateValue(svg_info,-1,value);
1533 else
1534 svg_info->radius.y=
1535 GetUserSpaceCoordinateValue(svg_info,-1,value);
1536 break;
1537 }
1538 break;
1539 }
1540 case 'S':
1541 case 's':
1542 {
1543 if (LocaleCompare(keyword,"stop-color") == 0)
1544 {
1545 (void) CloneString(&svg_info->stop_color,value);
1546 break;
1547 }
1548 if (LocaleCompare(keyword,"stroke") == 0)
1549 {
1550 if (LocaleCompare(value,"currentColor") == 0)
1551 {
cristyb51dff52011-05-19 16:55:47 +00001552 (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001553 break;
1554 }
cristyb51dff52011-05-19 16:55:47 +00001555 (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001556 break;
1557 }
1558 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1559 {
cristyb51dff52011-05-19 16:55:47 +00001560 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001561 LocaleCompare(value,"true") == 0);
1562 break;
1563 }
1564 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1565 {
cristyb51dff52011-05-19 16:55:47 +00001566 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1567 value);
cristy3ed852e2009-09-05 21:47:34 +00001568 break;
1569 }
1570 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1571 {
cristyb51dff52011-05-19 16:55:47 +00001572 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %s\n",
1573 value);
cristy3ed852e2009-09-05 21:47:34 +00001574 break;
1575 }
1576 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1577 {
cristyb51dff52011-05-19 16:55:47 +00001578 (void) FormatLocaleFile(svg_info->file,"stroke-linecap '%s'\n",
1579 value);
cristy3ed852e2009-09-05 21:47:34 +00001580 break;
1581 }
1582 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1583 {
cristyb51dff52011-05-19 16:55:47 +00001584 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin '%s'\n",
1585 value);
cristy3ed852e2009-09-05 21:47:34 +00001586 break;
1587 }
1588 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1589 {
cristyb51dff52011-05-19 16:55:47 +00001590 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit '%s'\n",
1591 value);
cristy3ed852e2009-09-05 21:47:34 +00001592 break;
1593 }
1594 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1595 {
cristyb51dff52011-05-19 16:55:47 +00001596 (void) FormatLocaleFile(svg_info->file,"stroke-opacity '%s'\n",
1597 value);
cristy3ed852e2009-09-05 21:47:34 +00001598 break;
1599 }
1600 if (LocaleCompare(keyword,"stroke-width") == 0)
1601 {
cristyb51dff52011-05-19 16:55:47 +00001602 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001603 GetUserSpaceCoordinateValue(svg_info,1,value));
1604 break;
1605 }
1606 if (LocaleCompare(keyword,"style") == 0)
1607 {
1608 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1609 tokens=GetStyleTokens(context,value,&number_tokens);
1610 for (j=0; j < (number_tokens-1); j+=2)
1611 {
1612 keyword=(char *) tokens[j];
1613 value=(char *) tokens[j+1];
1614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1615 " %s: %s",keyword,value);
1616 switch (*keyword)
1617 {
1618 case 'C':
1619 case 'c':
1620 {
1621 if (LocaleCompare(keyword,"clip-path") == 0)
1622 {
cristyb51dff52011-05-19 16:55:47 +00001623 (void) FormatLocaleFile(svg_info->file,
1624 "clip-path '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001625 break;
1626 }
1627 if (LocaleCompare(keyword,"clip-rule") == 0)
1628 {
cristyb51dff52011-05-19 16:55:47 +00001629 (void) FormatLocaleFile(svg_info->file,
1630 "clip-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001631 break;
1632 }
1633 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1634 {
1635 (void) CloneString(&units,value);
cristyb51dff52011-05-19 16:55:47 +00001636 (void) FormatLocaleFile(svg_info->file,
1637 "clip-units '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001638 break;
1639 }
1640 if (LocaleCompare(keyword,"color") == 0)
1641 {
1642 (void) CloneString(&color,value);
1643 break;
1644 }
1645 break;
1646 }
1647 case 'F':
1648 case 'f':
1649 {
1650 if (LocaleCompare(keyword,"fill") == 0)
1651 {
1652 if (LocaleCompare(value,"currentColor") == 0)
1653 {
cristyb51dff52011-05-19 16:55:47 +00001654 (void) FormatLocaleFile(svg_info->file,
1655 "fill '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001656 break;
1657 }
1658 if (LocaleCompare(value,"#00000000") == 0)
cristyb51dff52011-05-19 16:55:47 +00001659 (void) FormatLocaleFile(svg_info->file,
1660 "fill '#000000'\n");
cristy3ed852e2009-09-05 21:47:34 +00001661 else
cristyb51dff52011-05-19 16:55:47 +00001662 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1663 value);
cristy3ed852e2009-09-05 21:47:34 +00001664 break;
1665 }
1666 if (LocaleCompare(keyword,"fillcolor") == 0)
1667 {
cristyb51dff52011-05-19 16:55:47 +00001668 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1669 value);
cristy3ed852e2009-09-05 21:47:34 +00001670 break;
1671 }
1672 if (LocaleCompare(keyword,"fill-rule") == 0)
1673 {
cristyb51dff52011-05-19 16:55:47 +00001674 (void) FormatLocaleFile(svg_info->file,
1675 "fill-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001676 break;
1677 }
1678 if (LocaleCompare(keyword,"fill-opacity") == 0)
1679 {
cristyb51dff52011-05-19 16:55:47 +00001680 (void) FormatLocaleFile(svg_info->file,
1681 "fill-opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001682 break;
1683 }
1684 if (LocaleCompare(keyword,"font-family") == 0)
1685 {
cristyb51dff52011-05-19 16:55:47 +00001686 (void) FormatLocaleFile(svg_info->file,
1687 "font-family '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001688 break;
1689 }
1690 if (LocaleCompare(keyword,"font-stretch") == 0)
1691 {
cristyb51dff52011-05-19 16:55:47 +00001692 (void) FormatLocaleFile(svg_info->file,
1693 "font-stretch '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001694 break;
1695 }
1696 if (LocaleCompare(keyword,"font-style") == 0)
1697 {
cristyb51dff52011-05-19 16:55:47 +00001698 (void) FormatLocaleFile(svg_info->file,
1699 "font-style '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001700 break;
1701 }
1702 if (LocaleCompare(keyword,"font-size") == 0)
1703 {
1704 svg_info->pointsize=GetUserSpaceCoordinateValue(
1705 svg_info,0,value);
cristyb51dff52011-05-19 16:55:47 +00001706 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001707 svg_info->pointsize);
1708 break;
1709 }
1710 if (LocaleCompare(keyword,"font-weight") == 0)
1711 {
cristyb51dff52011-05-19 16:55:47 +00001712 (void) FormatLocaleFile(svg_info->file,
1713 "font-weight '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001714 break;
1715 }
1716 break;
1717 }
1718 case 'O':
1719 case 'o':
1720 {
1721 if (LocaleCompare(keyword,"offset") == 0)
1722 {
cristyb51dff52011-05-19 16:55:47 +00001723 (void) FormatLocaleFile(svg_info->file,"offset %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001724 GetUserSpaceCoordinateValue(svg_info,1,value));
1725 break;
1726 }
1727 if (LocaleCompare(keyword,"opacity") == 0)
1728 {
cristyb51dff52011-05-19 16:55:47 +00001729 (void) FormatLocaleFile(svg_info->file,
1730 "opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001731 break;
1732 }
1733 break;
1734 }
1735 case 'S':
1736 case 's':
1737 {
1738 if (LocaleCompare(keyword,"stop-color") == 0)
1739 {
1740 (void) CloneString(&svg_info->stop_color,value);
1741 break;
1742 }
1743 if (LocaleCompare(keyword,"stroke") == 0)
1744 {
1745 if (LocaleCompare(value,"currentColor") == 0)
1746 {
cristyb51dff52011-05-19 16:55:47 +00001747 (void) FormatLocaleFile(svg_info->file,
1748 "stroke '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001749 break;
1750 }
1751 if (LocaleCompare(value,"#00000000") == 0)
cristyb51dff52011-05-19 16:55:47 +00001752 (void) FormatLocaleFile(svg_info->file,
1753 "fill '#000000'\n");
cristy3ed852e2009-09-05 21:47:34 +00001754 else
cristyb51dff52011-05-19 16:55:47 +00001755 (void) FormatLocaleFile(svg_info->file,
1756 "stroke '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001757 break;
1758 }
1759 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1760 {
cristyb51dff52011-05-19 16:55:47 +00001761 (void) FormatLocaleFile(svg_info->file,
1762 "stroke-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001763 LocaleCompare(value,"true") == 0);
1764 break;
1765 }
1766 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1767 {
cristyb51dff52011-05-19 16:55:47 +00001768 (void) FormatLocaleFile(svg_info->file,
1769 "stroke-dasharray %s\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001770 break;
1771 }
1772 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1773 {
cristyb51dff52011-05-19 16:55:47 +00001774 (void) FormatLocaleFile(svg_info->file,
1775 "stroke-dashoffset %s\n",
cristy3ed852e2009-09-05 21:47:34 +00001776 value);
1777 break;
1778 }
1779 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1780 {
cristyb51dff52011-05-19 16:55:47 +00001781 (void) FormatLocaleFile(svg_info->file,
1782 "stroke-linecap '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001783 break;
1784 }
1785 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1786 {
cristyb51dff52011-05-19 16:55:47 +00001787 (void) FormatLocaleFile(svg_info->file,
1788 "stroke-linejoin '%s'\n",
cristy3ed852e2009-09-05 21:47:34 +00001789 value);
1790 break;
1791 }
1792 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1793 {
cristyb51dff52011-05-19 16:55:47 +00001794 (void) FormatLocaleFile(svg_info->file,
1795 "stroke-miterlimit '%s'\n",
cristy3ed852e2009-09-05 21:47:34 +00001796 value);
1797 break;
1798 }
1799 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1800 {
cristyb51dff52011-05-19 16:55:47 +00001801 (void) FormatLocaleFile(svg_info->file,
1802 "stroke-opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001803 break;
1804 }
1805 if (LocaleCompare(keyword,"stroke-width") == 0)
1806 {
cristyb51dff52011-05-19 16:55:47 +00001807 (void) FormatLocaleFile(svg_info->file,
1808 "stroke-width %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001809 GetUserSpaceCoordinateValue(svg_info,1,value));
1810 break;
1811 }
1812 break;
1813 }
1814 case 't':
1815 case 'T':
1816 {
1817 if (LocaleCompare(keyword,"text-align") == 0)
1818 {
cristyb51dff52011-05-19 16:55:47 +00001819 (void) FormatLocaleFile(svg_info->file,
1820 "text-align '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001821 break;
1822 }
1823 if (LocaleCompare(keyword,"text-anchor") == 0)
1824 {
cristyb51dff52011-05-19 16:55:47 +00001825 (void) FormatLocaleFile(svg_info->file,
1826 "text-anchor '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001827 break;
1828 }
1829 if (LocaleCompare(keyword,"text-decoration") == 0)
1830 {
1831 if (LocaleCompare(value,"underline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001832 (void) FormatLocaleFile(svg_info->file,
1833 "decorate underline\n");
cristy3ed852e2009-09-05 21:47:34 +00001834 if (LocaleCompare(value,"line-through") == 0)
cristyb51dff52011-05-19 16:55:47 +00001835 (void) FormatLocaleFile(svg_info->file,
1836 "decorate line-through\n");
cristy3ed852e2009-09-05 21:47:34 +00001837 if (LocaleCompare(value,"overline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001838 (void) FormatLocaleFile(svg_info->file,
1839 "decorate overline\n");
cristy3ed852e2009-09-05 21:47:34 +00001840 break;
1841 }
1842 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1843 {
cristyb51dff52011-05-19 16:55:47 +00001844 (void) FormatLocaleFile(svg_info->file,
1845 "text-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001846 LocaleCompare(value,"true") == 0);
1847 break;
1848 }
1849 break;
1850 }
1851 default:
1852 break;
1853 }
1854 }
1855 for (j=0; tokens[j] != (char *) NULL; j++)
1856 tokens[j]=DestroyString(tokens[j]);
1857 tokens=(char **) RelinquishMagickMemory(tokens);
1858 break;
1859 }
1860 break;
1861 }
1862 case 'T':
1863 case 't':
1864 {
1865 if (LocaleCompare(keyword,"text-align") == 0)
1866 {
cristyb51dff52011-05-19 16:55:47 +00001867 (void) FormatLocaleFile(svg_info->file,"text-align '%s'\n",
1868 value);
cristy3ed852e2009-09-05 21:47:34 +00001869 break;
1870 }
1871 if (LocaleCompare(keyword,"text-anchor") == 0)
1872 {
cristyb51dff52011-05-19 16:55:47 +00001873 (void) FormatLocaleFile(svg_info->file,"text-anchor '%s'\n",
1874 value);
cristy3ed852e2009-09-05 21:47:34 +00001875 break;
1876 }
1877 if (LocaleCompare(keyword,"text-decoration") == 0)
1878 {
1879 if (LocaleCompare(value,"underline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001880 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
cristy3ed852e2009-09-05 21:47:34 +00001881 if (LocaleCompare(value,"line-through") == 0)
cristyb51dff52011-05-19 16:55:47 +00001882 (void) FormatLocaleFile(svg_info->file,
1883 "decorate line-through\n");
cristy3ed852e2009-09-05 21:47:34 +00001884 if (LocaleCompare(value,"overline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001885 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
cristy3ed852e2009-09-05 21:47:34 +00001886 break;
1887 }
1888 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1889 {
cristyb51dff52011-05-19 16:55:47 +00001890 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001891 LocaleCompare(value,"true") == 0);
1892 break;
1893 }
1894 if (LocaleCompare(keyword,"transform") == 0)
1895 {
1896 AffineMatrix
1897 affine,
1898 current,
1899 transform;
1900
1901 GetAffineMatrix(&transform);
1902 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1903 tokens=GetTransformTokens(context,value,&number_tokens);
1904 for (j=0; j < (number_tokens-1); j+=2)
1905 {
1906 keyword=(char *) tokens[j];
1907 value=(char *) tokens[j+1];
1908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1909 " %s: %s",keyword,value);
1910 current=transform;
1911 GetAffineMatrix(&affine);
1912 switch (*keyword)
1913 {
1914 case 'M':
1915 case 'm':
1916 {
1917 if (LocaleCompare(keyword,"matrix") == 0)
1918 {
1919 p=(const char *) value;
1920 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001921 affine.sx=InterpretLocaleValue(value,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001922 GetMagickToken(p,&p,token);
1923 if (*token == ',')
1924 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001925 affine.rx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001926 GetMagickToken(p,&p,token);
1927 if (*token == ',')
1928 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001929 affine.ry=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001930 GetMagickToken(p,&p,token);
1931 if (*token == ',')
1932 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001933 affine.sy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001934 GetMagickToken(p,&p,token);
1935 if (*token == ',')
1936 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001937 affine.tx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001938 GetMagickToken(p,&p,token);
1939 if (*token == ',')
1940 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001941 affine.ty=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001942 break;
1943 }
1944 break;
1945 }
1946 case 'R':
1947 case 'r':
1948 {
1949 if (LocaleCompare(keyword,"rotate") == 0)
1950 {
1951 double
1952 angle,
1953 x,
1954 y;
1955
1956 p=(const char *) value;
1957 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001958 angle=InterpretLocaleValue(value,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001959 GetMagickToken(p,&p,token);
1960 if (*token == ',')
1961 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001962 x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001963 GetMagickToken(p,&p,token);
1964 if (*token == ',')
1965 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00001966 y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001967 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1968 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1969 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1970 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1971 affine.tx=x;
1972 affine.ty=y;
1973 svg_info->center.x=x;
1974 svg_info->center.y=y;
1975 break;
1976 }
1977 break;
1978 }
1979 case 'S':
1980 case 's':
1981 {
1982 if (LocaleCompare(keyword,"scale") == 0)
1983 {
1984 for (p=(const char *) value; *p != '\0'; p++)
1985 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1986 (*p == ','))
1987 break;
1988 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1989 affine.sy=affine.sx;
1990 if (*p != '\0')
1991 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
1992 p+1);
1993 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1994 break;
1995 }
1996 if (LocaleCompare(keyword,"skewX") == 0)
1997 {
1998 affine.sx=svg_info->affine.sx;
1999 affine.ry=tan(DegreesToRadians(fmod(
2000 GetUserSpaceCoordinateValue(svg_info,1,value),
2001 360.0)));
2002 affine.sy=svg_info->affine.sy;
2003 break;
2004 }
2005 if (LocaleCompare(keyword,"skewY") == 0)
2006 {
2007 affine.sx=svg_info->affine.sx;
2008 affine.rx=tan(DegreesToRadians(fmod(
2009 GetUserSpaceCoordinateValue(svg_info,-1,value),
2010 360.0)));
2011 affine.sy=svg_info->affine.sy;
2012 break;
2013 }
2014 break;
2015 }
2016 case 'T':
2017 case 't':
2018 {
2019 if (LocaleCompare(keyword,"translate") == 0)
2020 {
2021 for (p=(const char *) value; *p != '\0'; p++)
2022 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2023 (*p == ','))
2024 break;
2025 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2026 affine.ty=affine.tx;
2027 if (*p != '\0')
2028 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2029 p+1);
2030 break;
2031 }
2032 break;
2033 }
2034 default:
2035 break;
2036 }
cristyef7c8a52010-10-10 13:46:51 +00002037 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2038 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2039 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2040 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
2041 transform.tx=affine.sx*current.tx+affine.ry*current.ty+
2042 affine.tx;
2043 transform.ty=affine.rx*current.tx+affine.sy*current.ty+
2044 affine.ty;
cristy3ed852e2009-09-05 21:47:34 +00002045 }
cristyb51dff52011-05-19 16:55:47 +00002046 (void) FormatLocaleFile(svg_info->file,
cristy88158062011-06-09 00:31:43 +00002047 "affine %g %g %g %g 0.0 0.0\n",transform.sx,transform.rx,
2048 transform.ry,transform.sy);
cristy3ed852e2009-09-05 21:47:34 +00002049 for (j=0; tokens[j] != (char *) NULL; j++)
2050 tokens[j]=DestroyString(tokens[j]);
2051 tokens=(char **) RelinquishMagickMemory(tokens);
2052 break;
2053 }
2054 break;
2055 }
2056 case 'V':
2057 case 'v':
2058 {
2059 if (LocaleCompare(keyword,"verts") == 0)
2060 {
2061 (void) CloneString(&svg_info->vertices,value);
2062 break;
2063 }
2064 if (LocaleCompare(keyword,"viewBox") == 0)
2065 {
2066 p=(const char *) value;
2067 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00002068 svg_info->view_box.x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002069 GetMagickToken(p,&p,token);
2070 if (*token == ',')
2071 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00002072 svg_info->view_box.y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002073 GetMagickToken(p,&p,token);
2074 if (*token == ',')
2075 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00002076 svg_info->view_box.width=InterpretLocaleValue(token,
2077 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002078 if (svg_info->bounds.width == 0)
2079 svg_info->bounds.width=svg_info->view_box.width;
2080 GetMagickToken(p,&p,token);
2081 if (*token == ',')
2082 GetMagickToken(p,&p,token);
cristyc1acd842011-05-19 23:05:47 +00002083 svg_info->view_box.height=InterpretLocaleValue(token,
2084 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002085 if (svg_info->bounds.height == 0)
2086 svg_info->bounds.height=svg_info->view_box.height;
2087 break;
2088 }
2089 break;
2090 }
2091 case 'W':
2092 case 'w':
2093 {
2094 if (LocaleCompare(keyword,"width") == 0)
2095 {
2096 svg_info->bounds.width=
2097 GetUserSpaceCoordinateValue(svg_info,1,value);
2098 break;
2099 }
2100 break;
2101 }
2102 case 'X':
2103 case 'x':
2104 {
2105 if (LocaleCompare(keyword,"x") == 0)
2106 {
2107 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2108 break;
2109 }
2110 if (LocaleCompare(keyword,"xlink:href") == 0)
2111 {
2112 (void) CloneString(&svg_info->url,value);
2113 break;
2114 }
2115 if (LocaleCompare(keyword,"x1") == 0)
2116 {
2117 svg_info->segment.x1=
2118 GetUserSpaceCoordinateValue(svg_info,1,value);
2119 break;
2120 }
2121 if (LocaleCompare(keyword,"x2") == 0)
2122 {
2123 svg_info->segment.x2=
2124 GetUserSpaceCoordinateValue(svg_info,1,value);
2125 break;
2126 }
2127 break;
2128 }
2129 case 'Y':
2130 case 'y':
2131 {
2132 if (LocaleCompare(keyword,"y") == 0)
2133 {
2134 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2135 break;
2136 }
2137 if (LocaleCompare(keyword,"y1") == 0)
2138 {
2139 svg_info->segment.y1=
2140 GetUserSpaceCoordinateValue(svg_info,-1,value);
2141 break;
2142 }
2143 if (LocaleCompare(keyword,"y2") == 0)
2144 {
2145 svg_info->segment.y2=
2146 GetUserSpaceCoordinateValue(svg_info,-1,value);
2147 break;
2148 }
2149 break;
2150 }
2151 default:
2152 break;
2153 }
2154 }
2155 if (LocaleCompare((const char *) name,"svg") == 0)
2156 {
2157 if (svg_info->document->encoding != (const xmlChar *) NULL)
cristyb51dff52011-05-19 16:55:47 +00002158 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
cristy3ed852e2009-09-05 21:47:34 +00002159 (const char *) svg_info->document->encoding);
2160 if (attributes != (const xmlChar **) NULL)
2161 {
2162 double
2163 sx,
2164 sy;
2165
2166 if ((svg_info->view_box.width == 0.0) ||
2167 (svg_info->view_box.height == 0.0))
2168 svg_info->view_box=svg_info->bounds;
cristybb503372010-05-27 20:51:26 +00002169 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2170 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
cristyb51dff52011-05-19 16:55:47 +00002171 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2172 (double) svg_info->width,(double) svg_info->height);
cristy3ed852e2009-09-05 21:47:34 +00002173 sx=(double) svg_info->width/svg_info->view_box.width;
2174 sy=(double) svg_info->height/svg_info->view_box.height;
cristyb51dff52011-05-19 16:55:47 +00002175 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g 0.0 0.0\n",
2176 sx,sy);
cristy3ed852e2009-09-05 21:47:34 +00002177 }
2178 }
2179 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2180 units=DestroyString(units);
2181 if (color != (char *) NULL)
2182 color=DestroyString(color);
2183}
2184
2185static void SVGEndElement(void *context,const xmlChar *name)
2186{
2187 SVGInfo
2188 *svg_info;
2189
2190 /*
2191 Called when the end of an element has been detected.
2192 */
2193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2194 " SAX.endElement(%s)",name);
2195 svg_info=(SVGInfo *) context;
cristy13d07042010-11-21 20:56:18 +00002196 if (strchr((char *) name,':') != (char *) NULL)
cristy768b9302010-11-20 00:05:47 +00002197 {
2198 /*
2199 Skip over namespace.
2200 */
2201 for ( ; *name != ':'; name++) ;
2202 name++;
2203 }
cristy3ed852e2009-09-05 21:47:34 +00002204 switch (*name)
2205 {
2206 case 'C':
2207 case 'c':
2208 {
2209 if (LocaleCompare((const char *) name,"circle") == 0)
2210 {
cristyb51dff52011-05-19 16:55:47 +00002211 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
cristy8cd5b312010-01-07 01:10:24 +00002212 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2213 svg_info->element.cy+svg_info->element.minor);
cristyb51dff52011-05-19 16:55:47 +00002214 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002215 break;
2216 }
2217 if (LocaleCompare((const char *) name,"clipPath") == 0)
2218 {
cristyb51dff52011-05-19 16:55:47 +00002219 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
cristy3ed852e2009-09-05 21:47:34 +00002220 break;
2221 }
2222 break;
2223 }
2224 case 'D':
2225 case 'd':
2226 {
2227 if (LocaleCompare((const char *) name,"defs") == 0)
2228 {
cristyb51dff52011-05-19 16:55:47 +00002229 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
cristy3ed852e2009-09-05 21:47:34 +00002230 break;
2231 }
2232 if (LocaleCompare((const char *) name,"desc") == 0)
2233 {
2234 register char
2235 *p;
2236
2237 if (*svg_info->text == '\0')
2238 break;
2239 (void) fputc('#',svg_info->file);
2240 for (p=svg_info->text; *p != '\0'; p++)
2241 {
2242 (void) fputc(*p,svg_info->file);
2243 if (*p == '\n')
2244 (void) fputc('#',svg_info->file);
2245 }
2246 (void) fputc('\n',svg_info->file);
2247 *svg_info->text='\0';
2248 break;
2249 }
2250 break;
2251 }
2252 case 'E':
2253 case 'e':
2254 {
2255 if (LocaleCompare((const char *) name,"ellipse") == 0)
2256 {
2257 double
2258 angle;
2259
2260 angle=svg_info->element.angle;
cristyb51dff52011-05-19 16:55:47 +00002261 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
cristy3ed852e2009-09-05 21:47:34 +00002262 svg_info->element.cx,svg_info->element.cy,
2263 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2264 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
cristyb51dff52011-05-19 16:55:47 +00002265 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002266 break;
2267 }
2268 break;
2269 }
2270 case 'G':
2271 case 'g':
2272 {
2273 if (LocaleCompare((const char *) name,"g") == 0)
2274 {
cristyb51dff52011-05-19 16:55:47 +00002275 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002276 break;
2277 }
2278 break;
2279 }
2280 case 'I':
2281 case 'i':
2282 {
2283 if (LocaleCompare((const char *) name,"image") == 0)
2284 {
cristyb51dff52011-05-19 16:55:47 +00002285 (void) FormatLocaleFile(svg_info->file,
2286 "image Over %g,%g %g,%g '%s'\n",svg_info->bounds.x,
2287 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2288 svg_info->url);
2289 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002290 break;
2291 }
2292 break;
2293 }
2294 case 'L':
2295 case 'l':
2296 {
2297 if (LocaleCompare((const char *) name,"line") == 0)
2298 {
cristyb51dff52011-05-19 16:55:47 +00002299 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
cristy8cd5b312010-01-07 01:10:24 +00002300 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2301 svg_info->segment.y2);
cristyb51dff52011-05-19 16:55:47 +00002302 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002303 break;
2304 }
2305 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2306 {
cristyb51dff52011-05-19 16:55:47 +00002307 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
cristy3ed852e2009-09-05 21:47:34 +00002308 break;
2309 }
2310 break;
2311 }
2312 case 'P':
2313 case 'p':
2314 {
2315 if (LocaleCompare((const char *) name,"pattern") == 0)
2316 {
cristyb51dff52011-05-19 16:55:47 +00002317 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
cristy3ed852e2009-09-05 21:47:34 +00002318 break;
2319 }
2320 if (LocaleCompare((const char *) name,"path") == 0)
2321 {
cristyb51dff52011-05-19 16:55:47 +00002322 (void) FormatLocaleFile(svg_info->file,"path '%s'\n",
2323 svg_info->vertices);
2324 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002325 break;
2326 }
2327 if (LocaleCompare((const char *) name,"polygon") == 0)
2328 {
cristyb51dff52011-05-19 16:55:47 +00002329 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2330 svg_info->vertices);
2331 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002332 break;
2333 }
2334 if (LocaleCompare((const char *) name,"polyline") == 0)
2335 {
cristyb51dff52011-05-19 16:55:47 +00002336 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2337 svg_info->vertices);
2338 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002339 break;
2340 }
2341 break;
2342 }
2343 case 'R':
2344 case 'r':
2345 {
2346 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2347 {
cristyb51dff52011-05-19 16:55:47 +00002348 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
cristy3ed852e2009-09-05 21:47:34 +00002349 break;
2350 }
2351 if (LocaleCompare((const char *) name,"rect") == 0)
2352 {
2353 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2354 {
cristyb51dff52011-05-19 16:55:47 +00002355 (void) FormatLocaleFile(svg_info->file,"rectangle %g,%g %g,%g\n",
cristy3ed852e2009-09-05 21:47:34 +00002356 svg_info->bounds.x,svg_info->bounds.y,
2357 svg_info->bounds.x+svg_info->bounds.width,
2358 svg_info->bounds.y+svg_info->bounds.height);
cristyb51dff52011-05-19 16:55:47 +00002359 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002360 break;
2361 }
2362 if (svg_info->radius.x == 0.0)
2363 svg_info->radius.x=svg_info->radius.y;
2364 if (svg_info->radius.y == 0.0)
2365 svg_info->radius.y=svg_info->radius.x;
cristyb51dff52011-05-19 16:55:47 +00002366 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00002367 "roundRectangle %g,%g %g,%g %g,%g\n",
cristy3ed852e2009-09-05 21:47:34 +00002368 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2369 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2370 svg_info->radius.x,svg_info->radius.y);
2371 svg_info->radius.x=0.0;
2372 svg_info->radius.y=0.0;
cristyb51dff52011-05-19 16:55:47 +00002373 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002374 break;
2375 }
2376 break;
2377 }
2378 case 'S':
2379 case 's':
2380 {
2381 if (LocaleCompare((const char *) name,"stop") == 0)
2382 {
cristyb51dff52011-05-19 16:55:47 +00002383 (void) FormatLocaleFile(svg_info->file,"stop-color '%s' %s\n",
2384 svg_info->stop_color,svg_info->offset);
cristy3ed852e2009-09-05 21:47:34 +00002385 break;
2386 }
2387 if (LocaleCompare((const char *) name,"svg") == 0)
2388 {
cristyb51dff52011-05-19 16:55:47 +00002389 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002390 break;
2391 }
2392 break;
2393 }
2394 case 'T':
2395 case 't':
2396 {
2397 if (LocaleCompare((const char *) name,"text") == 0)
2398 {
2399 if (*svg_info->text != '\0')
2400 {
2401 char
2402 *text;
2403
2404 text=EscapeString(svg_info->text,'\'');
cristyb51dff52011-05-19 16:55:47 +00002405 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
cristy88158062011-06-09 00:31:43 +00002406 svg_info->bounds.x,svg_info->bounds.y,text);
cristy3ed852e2009-09-05 21:47:34 +00002407 text=DestroyString(text);
2408 *svg_info->text='\0';
2409 }
cristyb51dff52011-05-19 16:55:47 +00002410 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002411 break;
2412 }
2413 if (LocaleCompare((const char *) name,"tspan") == 0)
2414 {
2415 if (*svg_info->text != '\0')
2416 {
2417 DrawInfo
2418 *draw_info;
2419
2420 TypeMetric
2421 metrics;
2422
2423 char
2424 *text;
2425
2426 text=EscapeString(svg_info->text,'\'');
cristyb51dff52011-05-19 16:55:47 +00002427 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
cristy8cd5b312010-01-07 01:10:24 +00002428 svg_info->bounds.x,svg_info->bounds.y,text);
cristy3ed852e2009-09-05 21:47:34 +00002429 text=DestroyString(text);
2430 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
2431 draw_info->pointsize=svg_info->pointsize;
2432 draw_info->text=AcquireString(svg_info->text);
2433 (void) ConcatenateString(&draw_info->text," ");
2434 GetTypeMetrics(svg_info->image,draw_info,&metrics);
2435 svg_info->bounds.x+=metrics.width;
2436 draw_info=DestroyDrawInfo(draw_info);
2437 *svg_info->text='\0';
2438 }
cristyb51dff52011-05-19 16:55:47 +00002439 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002440 break;
2441 }
2442 if (LocaleCompare((const char *) name,"title") == 0)
2443 {
2444 if (*svg_info->text == '\0')
2445 break;
2446 (void) CloneString(&svg_info->title,svg_info->text);
2447 *svg_info->text='\0';
2448 break;
2449 }
2450 break;
2451 }
2452 default:
2453 break;
2454 }
2455 *svg_info->text='\0';
2456 (void) ResetMagickMemory(&svg_info->element,0,sizeof(svg_info->element));
2457 (void) ResetMagickMemory(&svg_info->segment,0,sizeof(svg_info->segment));
2458 svg_info->n--;
2459}
2460
2461static void SVGCharacters(void *context,const xmlChar *c,int length)
2462{
2463 register char
2464 *p;
2465
cristybb503372010-05-27 20:51:26 +00002466 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002467 i;
2468
2469 SVGInfo
2470 *svg_info;
2471
2472 /*
2473 Receiving some characters from the parser.
2474 */
2475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002476 " SAX.characters(%s,%.20g)",c,(double) length);
cristy3ed852e2009-09-05 21:47:34 +00002477 svg_info=(SVGInfo *) context;
2478 if (svg_info->text != (char *) NULL)
2479 svg_info->text=(char *) ResizeQuantumMemory(svg_info->text,
2480 strlen(svg_info->text)+length+MaxTextExtent,sizeof(*svg_info->text));
2481 else
2482 {
2483 svg_info->text=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2484 sizeof(*svg_info->text));
2485 if (svg_info->text != (char *) NULL)
2486 *svg_info->text='\0';
2487 }
2488 if (svg_info->text == (char *) NULL)
2489 return;
2490 p=svg_info->text+strlen(svg_info->text);
cristybb503372010-05-27 20:51:26 +00002491 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00002492 *p++=c[i];
2493 *p='\0';
2494}
2495
2496static void SVGReference(void *context,const xmlChar *name)
2497{
2498 SVGInfo
2499 *svg_info;
2500
2501 xmlParserCtxtPtr
2502 parser;
2503
2504 /*
2505 Called when an entity reference is detected.
2506 */
2507 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2508 name);
2509 svg_info=(SVGInfo *) context;
2510 parser=svg_info->parser;
2511 if (parser == (xmlParserCtxtPtr) NULL)
2512 return;
2513 if (parser->node == (xmlNodePtr) NULL)
2514 return;
2515 if (*name == '#')
2516 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2517 else
2518 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2519}
2520
2521static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2522{
2523 SVGInfo
2524 *svg_info;
2525
2526 /*
2527 Receiving some ignorable whitespaces from the parser.
2528 */
2529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2530 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2531 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +00002532 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +00002533}
2534
2535static void SVGProcessingInstructions(void *context,const xmlChar *target,
2536 const xmlChar *data)
2537{
2538 SVGInfo
2539 *svg_info;
2540
2541 /*
2542 A processing instruction has been parsed.
2543 */
2544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2545 " SAX.processingInstruction(%s, %s)",target,data);
2546 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +00002547 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +00002548}
2549
2550static void SVGComment(void *context,const xmlChar *value)
2551{
2552 SVGInfo
2553 *svg_info;
2554
2555 /*
2556 A comment has been parsed.
2557 */
2558 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
2559 value);
2560 svg_info=(SVGInfo *) context;
2561 if (svg_info->comment != (char *) NULL)
2562 (void) ConcatenateString(&svg_info->comment,"\n");
2563 (void) ConcatenateString(&svg_info->comment,(const char *) value);
2564}
2565
2566static void SVGWarning(void *context,const char *format,...)
2567{
2568 char
2569 *message,
2570 reason[MaxTextExtent];
2571
2572 SVGInfo
2573 *svg_info;
2574
2575 va_list
2576 operands;
2577
2578 /**
2579 Display and format a warning messages, gives file, line, position and
2580 extra parameters.
2581 */
2582 va_start(operands,format);
2583 svg_info=(SVGInfo *) context;
2584 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
2585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2586#if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2587 (void) vsprintf(reason,format,operands);
2588#else
2589 (void) vsnprintf(reason,MaxTextExtent,format,operands);
2590#endif
2591 message=GetExceptionMessage(errno);
2592 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2593 DelegateWarning,reason,"`%s`",message);
2594 message=DestroyString(message);
2595 va_end(operands);
2596}
2597
2598static void SVGError(void *context,const char *format,...)
2599{
2600 char
2601 *message,
2602 reason[MaxTextExtent];
2603
2604 SVGInfo
2605 *svg_info;
2606
2607 va_list
2608 operands;
2609
2610 /*
2611 Display and format a error formats, gives file, line, position and
2612 extra parameters.
2613 */
2614 va_start(operands,format);
2615 svg_info=(SVGInfo *) context;
2616 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
2617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2618#if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2619 (void) vsprintf(reason,format,operands);
2620#else
2621 (void) vsnprintf(reason,MaxTextExtent,format,operands);
2622#endif
2623 message=GetExceptionMessage(errno);
2624 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2625 reason,"`%s`",message);
2626 message=DestroyString(message);
2627 va_end(operands);
2628}
2629
2630static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2631{
2632 SVGInfo
2633 *svg_info;
2634
2635 xmlNodePtr
2636 child;
2637
2638 xmlParserCtxtPtr
2639 parser;
2640
2641 /*
2642 Called when a pcdata block has been parsed.
2643 */
2644 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
2645 value,length);
2646 svg_info=(SVGInfo *) context;
2647 parser=svg_info->parser;
2648 child=xmlGetLastChild(parser->node);
2649 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2650 {
2651 xmlTextConcat(child,value,length);
2652 return;
2653 }
2654 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2655}
2656
2657static void SVGExternalSubset(void *context,const xmlChar *name,
2658 const xmlChar *external_id,const xmlChar *system_id)
2659{
2660 SVGInfo
2661 *svg_info;
2662
2663 xmlParserCtxt
2664 parser_context;
2665
2666 xmlParserCtxtPtr
2667 parser;
2668
2669 xmlParserInputPtr
2670 input;
2671
2672 /*
2673 Does this document has an external subset?
2674 */
2675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2676 " SAX.externalSubset(%s, %s, %s)",name,
2677 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2678 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2679 svg_info=(SVGInfo *) context;
2680 parser=svg_info->parser;
2681 if (((external_id == NULL) && (system_id == NULL)) ||
2682 ((parser->validate == 0) || (parser->wellFormed == 0) ||
2683 (svg_info->document == 0)))
2684 return;
2685 input=SVGResolveEntity(context,external_id,system_id);
2686 if (input == NULL)
2687 return;
2688 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2689 parser_context=(*parser);
2690 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2691 if (parser->inputTab == (xmlParserInputPtr *) NULL)
2692 {
2693 parser->errNo=XML_ERR_NO_MEMORY;
2694 parser->input=parser_context.input;
2695 parser->inputNr=parser_context.inputNr;
2696 parser->inputMax=parser_context.inputMax;
2697 parser->inputTab=parser_context.inputTab;
2698 return;
2699 }
2700 parser->inputNr=0;
2701 parser->inputMax=5;
2702 parser->input=NULL;
2703 xmlPushInput(parser,input);
2704 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2705 if (input->filename == (char *) NULL)
2706 input->filename=(char *) xmlStrdup(system_id);
2707 input->line=1;
2708 input->col=1;
2709 input->base=parser->input->cur;
2710 input->cur=parser->input->cur;
2711 input->free=NULL;
2712 xmlParseExternalSubset(parser,external_id,system_id);
2713 while (parser->inputNr > 1)
2714 (void) xmlPopInput(parser);
2715 xmlFreeInputStream(parser->input);
2716 xmlFree(parser->inputTab);
2717 parser->input=parser_context.input;
2718 parser->inputNr=parser_context.inputNr;
2719 parser->inputMax=parser_context.inputMax;
2720 parser->inputTab=parser_context.inputTab;
2721}
2722
2723#if defined(MAGICKCORE_RSVG_DELEGATE)
cristy3ed852e2009-09-05 21:47:34 +00002724static void SVGSetImageSize(int *width,int *height,gpointer context)
2725{
2726 Image
2727 *image;
2728
2729 image=(Image *) context;
2730 *width=(int) (*width*image->x_resolution/72.0);
2731 *height=(int) (*height*image->y_resolution/72.0);
2732}
2733#endif
cristy3ed852e2009-09-05 21:47:34 +00002734
2735#if defined(__cplusplus) || defined(c_plusplus)
2736}
2737#endif
2738
2739static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2740{
cristy3ed852e2009-09-05 21:47:34 +00002741 char
2742 filename[MaxTextExtent];
2743
2744 FILE
2745 *file;
2746
2747 Image
2748 *image;
2749
2750 int
2751 status,
2752 unique_file;
2753
cristybb503372010-05-27 20:51:26 +00002754 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002755 n;
2756
2757 SVGInfo
2758 *svg_info;
2759
2760 unsigned char
2761 message[MaxTextExtent];
2762
cristy1f9e1ed2009-11-18 04:09:38 +00002763 xmlSAXHandler
cristy5f6f01c2009-11-19 19:36:42 +00002764 sax_modules;
cristy1f9e1ed2009-11-18 04:09:38 +00002765
cristy3ed852e2009-09-05 21:47:34 +00002766 xmlSAXHandlerPtr
2767 sax_handler;
2768
2769 /*
2770 Open image file.
2771 */
2772 assert(image_info != (const ImageInfo *) NULL);
2773 assert(image_info->signature == MagickSignature);
2774 assert(exception != (ExceptionInfo *) NULL);
2775 if (image_info->debug != MagickFalse)
2776 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2777 image_info->filename);
2778 assert(exception->signature == MagickSignature);
2779 image=AcquireImage(image_info);
2780 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2781 if (status == MagickFalse)
2782 {
2783 image=DestroyImageList(image);
2784 return((Image *) NULL);
2785 }
2786 if (LocaleCompare(image_info->magick,"MSVG") != 0)
2787 {
2788#if defined(MAGICKCORE_RSVG_DELEGATE)
2789#if defined(MAGICKCORE_CAIRO_DELEGATE)
2790 cairo_surface_t
2791 *cairo_surface;
2792
2793 cairo_t
2794 *cairo_info;
2795
2796 register unsigned char
2797 *p;
2798
2799 RsvgDimensionData
2800 dimension_info;
2801
2802 unsigned char
2803 *pixels;
2804
2805#else
2806 GdkPixbuf
2807 *pixel_info;
2808
2809 register const guchar
2810 *p;
2811
2812#endif
2813
2814 GError
2815 *error;
2816
cristybb503372010-05-27 20:51:26 +00002817 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002818 y;
2819
2820 PixelPacket
2821 fill_color;
2822
cristybb503372010-05-27 20:51:26 +00002823 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002824 x;
2825
cristy4c08aed2011-07-01 19:47:50 +00002826 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002827 *q;
2828
2829 RsvgHandle
2830 *svg_handle;
2831
2832 svg_handle=rsvg_handle_new();
2833 if (svg_handle == (RsvgHandle *) NULL)
2834 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2835 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
cristy3ed852e2009-09-05 21:47:34 +00002836 rsvg_handle_set_size_callback(svg_handle,SVGSetImageSize,image,NULL);
cristy3ed852e2009-09-05 21:47:34 +00002837 if ((image->x_resolution != 72.0) && (image->y_resolution != 72.0))
2838 rsvg_handle_set_dpi_x_y(svg_handle,image->x_resolution,
2839 image->y_resolution);
2840 while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
2841 {
2842 error=(GError *) NULL;
2843 (void) rsvg_handle_write(svg_handle,message,n,&error);
2844 if (error != (GError *) NULL)
2845 g_error_free(error);
2846 }
2847 error=(GError *) NULL;
2848 rsvg_handle_close(svg_handle,&error);
2849 if (error != (GError *) NULL)
2850 g_error_free(error);
2851#if defined(MAGICKCORE_CAIRO_DELEGATE)
2852 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
cristy3de0a962009-12-06 15:34:07 +00002853 image->columns=dimension_info.width;
2854 image->rows=dimension_info.height;
cristye391a852009-11-21 14:50:33 +00002855 pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002856#else
2857 pixel_info=rsvg_handle_get_pixbuf(svg_handle);
2858 rsvg_handle_free(svg_handle);
2859 image->columns=gdk_pixbuf_get_width(pixel_info);
2860 image->rows=gdk_pixbuf_get_height(pixel_info);
2861#endif
cristye391a852009-11-21 14:50:33 +00002862 image->matte=MagickTrue;
2863 SetImageProperty(image,"svg:base-uri",
2864 rsvg_handle_get_base_uri(svg_handle));
2865 SetImageProperty(image,"svg:title",rsvg_handle_get_title(svg_handle));
2866 SetImageProperty(image,"svg:description",
2867 rsvg_handle_get_desc(svg_handle));
cristy3ed852e2009-09-05 21:47:34 +00002868 if ((image->columns == 0) || (image->rows == 0))
2869 {
2870#if !defined(MAGICKCORE_CAIRO_DELEGATE)
2871 g_object_unref(G_OBJECT(pixel_info));
2872#endif
2873 g_object_unref(svg_handle);
2874 ThrowReaderException(MissingDelegateError,
2875 "NoDecodeDelegateForThisImageFormat");
2876 }
cristye391a852009-11-21 14:50:33 +00002877 if (image_info->ping == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002878 {
cristye391a852009-11-21 14:50:33 +00002879#if defined(MAGICKCORE_CAIRO_DELEGATE)
2880 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,4*
2881 image->rows*sizeof(*pixels));
2882 if (pixels == (unsigned char *) NULL)
2883 {
2884 g_object_unref(svg_handle);
2885 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2886 }
2887#endif
2888 (void) SetImageBackgroundColor(image);
2889#if defined(MAGICKCORE_CAIRO_DELEGATE)
2890 cairo_surface=cairo_image_surface_create_for_data(pixels,
2891 CAIRO_FORMAT_ARGB32,image->columns,image->rows,4*image->columns);
2892 if (cairo_surface == (cairo_surface_t *) NULL)
2893 {
2894 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2895 g_object_unref(svg_handle);
2896 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2897 }
2898 cairo_info=cairo_create(cairo_surface);
cristye391a852009-11-21 14:50:33 +00002899 cairo_set_operator(cairo_info,CAIRO_OPERATOR_CLEAR);
2900 cairo_paint(cairo_info);
2901 cairo_set_operator(cairo_info,CAIRO_OPERATOR_OVER);
2902 rsvg_handle_render_cairo(svg_handle,cairo_info);
2903 cairo_destroy(cairo_info);
2904 cairo_surface_destroy(cairo_surface);
cristy3ed852e2009-09-05 21:47:34 +00002905 g_object_unref(svg_handle);
cristye391a852009-11-21 14:50:33 +00002906 p=pixels;
cristy3ed852e2009-09-05 21:47:34 +00002907#else
cristye391a852009-11-21 14:50:33 +00002908 p=gdk_pixbuf_get_pixels(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00002909#endif
cristybb503372010-05-27 20:51:26 +00002910 for (y=0; y < (ssize_t) image->rows; y++)
cristyd97b6ed2009-11-20 01:02:19 +00002911 {
cristye391a852009-11-21 14:50:33 +00002912 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002913 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002914 break;
cristybb503372010-05-27 20:51:26 +00002915 for (x=0; x < (ssize_t) image->columns; x++)
cristye391a852009-11-21 14:50:33 +00002916 {
cristy3ed852e2009-09-05 21:47:34 +00002917#if defined(MAGICKCORE_CAIRO_DELEGATE)
cristye391a852009-11-21 14:50:33 +00002918 fill_color.blue=ScaleCharToQuantum(*p++);
2919 fill_color.green=ScaleCharToQuantum(*p++);
2920 fill_color.red=ScaleCharToQuantum(*p++);
2921#else
2922 fill_color.red=ScaleCharToQuantum(*p++);
2923 fill_color.green=ScaleCharToQuantum(*p++);
2924 fill_color.blue=ScaleCharToQuantum(*p++);
2925#endif
cristy4c08aed2011-07-01 19:47:50 +00002926 fill_color.alpha=ScaleCharToQuantum(*p++);
cristye391a852009-11-21 14:50:33 +00002927#if defined(MAGICKCORE_CAIRO_DELEGATE)
2928 {
2929 double
2930 gamma;
2931
cristy4c08aed2011-07-01 19:47:50 +00002932 gamma=1.0-QuantumScale*fill_color.alpha;
cristye391a852009-11-21 14:50:33 +00002933 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2934 fill_color.blue*=gamma;
2935 fill_color.green*=gamma;
2936 fill_color.red*=gamma;
2937 }
2938#endif
cristy4c08aed2011-07-01 19:47:50 +00002939 CompositePixelOver(image,&fill_color,fill_color.alpha,q,
2940 (MagickRealType) GetPixelAlpha(image,q),q);
2941 q+=GetPixelChannels(image);
cristye391a852009-11-21 14:50:33 +00002942 }
2943 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2944 break;
2945 if (image->previous == (Image *) NULL)
2946 {
cristycee97112010-05-28 00:44:52 +00002947 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2948 image->rows);
cristye391a852009-11-21 14:50:33 +00002949 if (status == MagickFalse)
2950 break;
2951 }
2952 }
2953 }
2954#if defined(MAGICKCORE_CAIRO_DELEGATE)
2955 if (pixels != (unsigned char *) NULL)
2956 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +00002957#else
2958 g_object_unref(G_OBJECT(pixel_info));
2959#endif
2960 (void) CloseBlob(image);
2961 return(GetFirstImageInList(image));
2962#endif
2963 }
2964 /*
2965 Open draw file.
2966 */
2967 file=(FILE *) NULL;
2968 unique_file=AcquireUniqueFileResource(filename);
2969 if (unique_file != -1)
2970 file=fdopen(unique_file,"w");
2971 if ((unique_file == -1) || (file == (FILE *) NULL))
2972 {
2973 (void) CopyMagickString(image->filename,filename,MaxTextExtent);
2974 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
2975 image->filename);
2976 image=DestroyImageList(image);
2977 return((Image *) NULL);
2978 }
2979 /*
2980 Parse SVG file.
2981 */
2982 svg_info=AcquireSVGInfo();
2983 if (svg_info == (SVGInfo *) NULL)
2984 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2985 svg_info->file=file;
2986 svg_info->exception=exception;
2987 svg_info->image=image;
2988 svg_info->image_info=image_info;
2989 svg_info->bounds.width=image->columns;
2990 svg_info->bounds.height=image->rows;
2991 if (image_info->size != (char *) NULL)
2992 (void) CloneString(&svg_info->size,image_info->size);
2993 if (image->debug != MagickFalse)
2994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
2995 xmlInitParser();
2996 (void) xmlSubstituteEntitiesDefault(1);
cristy5f6f01c2009-11-19 19:36:42 +00002997 (void) ResetMagickMemory(&sax_modules,0,sizeof(sax_modules));
2998 sax_modules.internalSubset=SVGInternalSubset;
2999 sax_modules.isStandalone=SVGIsStandalone;
3000 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3001 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3002 sax_modules.resolveEntity=SVGResolveEntity;
3003 sax_modules.getEntity=SVGGetEntity;
3004 sax_modules.entityDecl=SVGEntityDeclaration;
3005 sax_modules.notationDecl=SVGNotationDeclaration;
3006 sax_modules.attributeDecl=SVGAttributeDeclaration;
3007 sax_modules.elementDecl=SVGElementDeclaration;
3008 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3009 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3010 sax_modules.startDocument=SVGStartDocument;
3011 sax_modules.endDocument=SVGEndDocument;
3012 sax_modules.startElement=SVGStartElement;
3013 sax_modules.endElement=SVGEndElement;
3014 sax_modules.reference=SVGReference;
3015 sax_modules.characters=SVGCharacters;
3016 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3017 sax_modules.processingInstruction=SVGProcessingInstructions;
3018 sax_modules.comment=SVGComment;
3019 sax_modules.warning=SVGWarning;
3020 sax_modules.error=SVGError;
3021 sax_modules.fatalError=SVGError;
3022 sax_modules.getParameterEntity=SVGGetParameterEntity;
3023 sax_modules.cdataBlock=SVGCDataBlock;
3024 sax_modules.externalSubset=SVGExternalSubset;
3025 sax_handler=(&sax_modules);
cristy3ed852e2009-09-05 21:47:34 +00003026 n=ReadBlob(image,MaxTextExtent,message);
3027 if (n > 0)
3028 {
3029 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3030 message,n,image->filename);
3031 while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
3032 {
3033 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3034 if (status != 0)
3035 break;
3036 }
3037 }
3038 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3039 xmlFreeParserCtxt(svg_info->parser);
3040 if (image->debug != MagickFalse)
3041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
3042 xmlCleanupParser();
3043 (void) fclose(file);
3044 (void) CloseBlob(image);
3045 image->columns=svg_info->width;
3046 image->rows=svg_info->height;
3047 if (exception->severity >= ErrorException)
3048 {
3049 image=DestroyImage(image);
3050 return((Image *) NULL);
3051 }
3052 if (image_info->ping == MagickFalse)
3053 {
3054 ImageInfo
3055 *read_info;
3056
3057 /*
3058 Draw image.
3059 */
3060 image=DestroyImage(image);
3061 image=(Image *) NULL;
3062 read_info=CloneImageInfo(image_info);
3063 SetImageInfoBlob(read_info,(void *) NULL,0);
3064 if (read_info->density != (char *) NULL)
3065 read_info->density=DestroyString(read_info->density);
cristyb51dff52011-05-19 16:55:47 +00003066 (void) FormatLocaleString(read_info->filename,MaxTextExtent,"mvg:%s",
cristy3ed852e2009-09-05 21:47:34 +00003067 filename);
3068 image=ReadImage(read_info,exception);
3069 read_info=DestroyImageInfo(read_info);
3070 if (image != (Image *) NULL)
3071 (void) CopyMagickString(image->filename,image_info->filename,
3072 MaxTextExtent);
3073 }
3074 /*
3075 Relinquish resources.
3076 */
3077 if (image != (Image *) NULL)
3078 {
3079 if (svg_info->title != (char *) NULL)
3080 (void) SetImageProperty(image,"svg:title",svg_info->title);
3081 if (svg_info->comment != (char *) NULL)
3082 (void) SetImageProperty(image,"svg:comment",svg_info->comment);
3083 }
3084 svg_info=DestroySVGInfo(svg_info);
3085 (void) RelinquishUniqueFileResource(filename);
3086 return(GetFirstImageInList(image));
3087}
3088#endif
3089
3090/*
3091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3092% %
3093% %
3094% %
3095% R e g i s t e r S V G I m a g e %
3096% %
3097% %
3098% %
3099%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3100%
3101% RegisterSVGImage() adds attributes for the SVG image format to
3102% the list of supported formats. The attributes include the image format
3103% tag, a method to read and/or write the format, whether the format
3104% supports the saving of more than one frame to the same file or blob,
3105% whether the format supports native in-memory I/O, and a brief
3106% description of the format.
3107%
3108% The format of the RegisterSVGImage method is:
3109%
cristybb503372010-05-27 20:51:26 +00003110% size_t RegisterSVGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00003111%
3112*/
cristybb503372010-05-27 20:51:26 +00003113ModuleExport size_t RegisterSVGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00003114{
3115 char
3116 version[MaxTextExtent];
3117
3118 MagickInfo
3119 *entry;
3120
3121 *version='\0';
3122#if defined(LIBXML_DOTTED_VERSION)
3123 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,MaxTextExtent);
3124#endif
3125#if defined(MAGICKCORE_RSVG_DELEGATE)
3126 rsvg_init();
cristyb51dff52011-05-19 16:55:47 +00003127 (void) FormatLocaleString(version,MaxTextExtent,"RSVG %d.%d.%d",
cristy3ed852e2009-09-05 21:47:34 +00003128 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3129#endif
3130 entry=SetMagickInfo("SVG");
3131#if defined(MAGICKCORE_XML_DELEGATE)
3132 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3133#endif
3134 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3135 entry->blob_support=MagickFalse;
3136 entry->seekable_stream=MagickFalse;
3137 entry->description=ConstantString("Scalable Vector Graphics");
3138 if (*version != '\0')
3139 entry->version=ConstantString(version);
3140 entry->magick=(IsImageFormatHandler *) IsSVG;
3141 entry->module=ConstantString("SVG");
3142 (void) RegisterMagickInfo(entry);
3143 entry=SetMagickInfo("SVGZ");
3144#if defined(MAGICKCORE_XML_DELEGATE)
3145 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3146#endif
3147 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3148 entry->blob_support=MagickFalse;
3149 entry->seekable_stream=MagickFalse;
3150 entry->description=ConstantString("Compressed Scalable Vector Graphics");
3151 if (*version != '\0')
3152 entry->version=ConstantString(version);
3153 entry->magick=(IsImageFormatHandler *) IsSVG;
3154 entry->module=ConstantString("SVG");
3155 (void) RegisterMagickInfo(entry);
3156 entry=SetMagickInfo("MSVG");
3157#if defined(MAGICKCORE_XML_DELEGATE)
3158 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3159#endif
3160 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
3161 entry->blob_support=MagickFalse;
3162 entry->seekable_stream=MagickFalse;
3163 entry->description=ConstantString("ImageMagick's own SVG internal renderer");
3164 entry->magick=(IsImageFormatHandler *) IsSVG;
3165 entry->module=ConstantString("SVG");
3166 (void) RegisterMagickInfo(entry);
3167 return(MagickImageCoderSignature);
3168}
3169
3170/*
3171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3172% %
3173% %
3174% %
3175% U n r e g i s t e r S V G I m a g e %
3176% %
3177% %
3178% %
3179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3180%
3181% UnregisterSVGImage() removes format registrations made by the
3182% SVG module from the list of supported formats.
3183%
3184% The format of the UnregisterSVGImage method is:
3185%
3186% UnregisterSVGImage(void)
3187%
3188*/
3189ModuleExport void UnregisterSVGImage(void)
3190{
3191 (void) UnregisterMagickInfo("SVGZ");
3192 (void) UnregisterMagickInfo("SVG");
3193 (void) UnregisterMagickInfo("MSVG");
3194#if defined(MAGICKCORE_RSVG_DELEGATE)
3195 rsvg_term();
3196#endif
3197}
3198
3199/*
3200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3201% %
3202% %
3203% %
3204% W r i t e S V G I m a g e %
3205% %
3206% %
3207% %
3208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3209%
3210% WriteSVGImage() writes a image in the SVG - XML based W3C standard
3211% format.
3212%
3213% The format of the WriteSVGImage method is:
3214%
3215% MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image)
3216%
3217% A description of each parameter follows.
3218%
3219% o image_info: the image info.
3220%
3221% o image: The image.
3222%
3223*/
3224
3225static void AffineToTransform(Image *image,AffineMatrix *affine)
3226{
3227 char
3228 transform[MaxTextExtent];
3229
3230 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3231 {
3232 if ((fabs(affine->rx) < MagickEpsilon) &&
3233 (fabs(affine->ry) < MagickEpsilon))
3234 {
3235 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3236 (fabs(affine->sy-1.0) < MagickEpsilon))
3237 {
3238 (void) WriteBlobString(image,"\">\n");
3239 return;
3240 }
cristyb51dff52011-05-19 16:55:47 +00003241 (void) FormatLocaleString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003242 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
cristy3ed852e2009-09-05 21:47:34 +00003243 (void) WriteBlobString(image,transform);
3244 return;
3245 }
3246 else
3247 {
3248 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3249 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3250 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3251 2*MagickEpsilon))
3252 {
3253 double
3254 theta;
3255
3256 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
cristyb51dff52011-05-19 16:55:47 +00003257 (void) FormatLocaleString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003258 "\" transform=\"rotate(%g)\">\n",theta);
cristy3ed852e2009-09-05 21:47:34 +00003259 (void) WriteBlobString(image,transform);
3260 return;
3261 }
3262 }
3263 }
3264 else
3265 {
3266 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3267 (fabs(affine->rx) < MagickEpsilon) &&
3268 (fabs(affine->ry) < MagickEpsilon) &&
3269 (fabs(affine->sy-1.0) < MagickEpsilon))
3270 {
cristyb51dff52011-05-19 16:55:47 +00003271 (void) FormatLocaleString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003272 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
cristy3ed852e2009-09-05 21:47:34 +00003273 (void) WriteBlobString(image,transform);
3274 return;
3275 }
3276 }
cristyb51dff52011-05-19 16:55:47 +00003277 (void) FormatLocaleString(transform,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003278 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
cristy8cd5b312010-01-07 01:10:24 +00003279 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
cristy3ed852e2009-09-05 21:47:34 +00003280 (void) WriteBlobString(image,transform);
3281}
3282
3283static MagickBooleanType IsPoint(const char *point)
3284{
3285 char
3286 *p;
3287
cristybb503372010-05-27 20:51:26 +00003288 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003289 value;
3290
3291 value=strtol(point,&p,10);
cristyda16f162011-02-19 23:52:17 +00003292 (void) value;
cristy3ed852e2009-09-05 21:47:34 +00003293 return(p != point ? MagickTrue : MagickFalse);
3294}
3295
3296static MagickBooleanType TraceSVGImage(Image *image)
3297{
cristybb503372010-05-27 20:51:26 +00003298 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003299 y;
3300
cristy4c08aed2011-07-01 19:47:50 +00003301 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003302 *p;
3303
cristybb503372010-05-27 20:51:26 +00003304 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003305 x;
3306
3307#if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3308 {
3309 at_bitmap_type
3310 *trace;
3311
3312 at_fitting_opts_type
3313 *fitting_options;
3314
3315 at_output_opts_type
3316 *output_options;
3317
3318 at_splines_type
3319 *splines;
3320
3321 ImageType
3322 type;
3323
cristybb503372010-05-27 20:51:26 +00003324 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003325 i;
3326
cristybb503372010-05-27 20:51:26 +00003327 size_t
cristy3ed852e2009-09-05 21:47:34 +00003328 number_planes;
3329
3330 /*
3331 Trace image and write as SVG.
3332 */
3333 fitting_options=at_fitting_opts_new();
3334 output_options=at_output_opts_new();
3335 type=GetImageType(image,&image->exception);
3336 number_planes=3;
3337 if ((type == BilevelType) || (type == GrayscaleType))
3338 number_planes=1;
3339 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3340 i=0;
cristybb503372010-05-27 20:51:26 +00003341 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003342 {
3343 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00003344 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003345 break;
cristybb503372010-05-27 20:51:26 +00003346 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003347 {
cristy4c08aed2011-07-01 19:47:50 +00003348 trace->bitmap[i++]=GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +00003349 if (number_planes == 3)
3350 {
cristy4c08aed2011-07-01 19:47:50 +00003351 trace->bitmap[i++]=GetPixelGreen(image,p);
3352 trace->bitmap[i++]=GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00003353 }
cristy4c08aed2011-07-01 19:47:50 +00003354 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003355 }
3356 }
3357 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3358 NULL);
3359 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3360 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3361 NULL);
3362 /*
3363 Free resources.
3364 */
3365 at_splines_free(splines);
3366 at_bitmap_free(trace);
3367 at_output_opts_free(output_options);
3368 at_fitting_opts_free(fitting_options);
3369 }
3370#else
3371 {
3372 char
3373 message[MaxTextExtent],
3374 tuple[MaxTextExtent];
3375
cristy4c08aed2011-07-01 19:47:50 +00003376 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00003377 pixel;
3378
cristy3ed852e2009-09-05 21:47:34 +00003379 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3380 (void) WriteBlobString(image,
3381 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3382 (void) WriteBlobString(image,
3383 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
cristyb51dff52011-05-19 16:55:47 +00003384 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00003385 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,
3386 (double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003387 (void) WriteBlobString(image,message);
cristy4c08aed2011-07-01 19:47:50 +00003388 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00003389 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003390 {
3391 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00003392 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003393 break;
cristybb503372010-05-27 20:51:26 +00003394 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003395 {
cristy4c08aed2011-07-01 19:47:50 +00003396 SetPixelInfo(image,p,&pixel);
cristy3ed852e2009-09-05 21:47:34 +00003397 (void) QueryMagickColorname(image,&pixel,SVGCompliance,tuple,
3398 &image->exception);
cristyb51dff52011-05-19 16:55:47 +00003399 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00003400 " <circle cx=\"%.20g\" cy=\"%.20g\" r=\"1\" fill=\"%s\"/>\n",
3401 (double) x,(double) y,tuple);
cristy3ed852e2009-09-05 21:47:34 +00003402 (void) WriteBlobString(image,message);
cristy4c08aed2011-07-01 19:47:50 +00003403 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003404 }
3405 }
3406 (void) WriteBlobString(image,"</svg>\n");
3407 }
3408#endif
3409 return(MagickTrue);
3410}
3411
3412static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image)
3413{
3414#define BezierQuantum 200
3415
3416 AffineMatrix
3417 affine;
3418
3419 char
3420 keyword[MaxTextExtent],
3421 message[MaxTextExtent],
3422 name[MaxTextExtent],
3423 *token,
3424 type[MaxTextExtent];
3425
3426 const char
3427 *p,
3428 *q,
3429 *value;
3430
3431 int
3432 n;
3433
cristybb503372010-05-27 20:51:26 +00003434 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003435 j;
3436
3437 MagickBooleanType
3438 active,
3439 status;
3440
3441 PointInfo
3442 point;
3443
3444 PrimitiveInfo
3445 *primitive_info;
3446
3447 PrimitiveType
3448 primitive_type;
3449
cristybb503372010-05-27 20:51:26 +00003450 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003451 x;
3452
cristybb503372010-05-27 20:51:26 +00003453 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003454 i;
3455
3456 size_t
3457 length;
3458
3459 SVGInfo
3460 svg_info;
3461
cristybb503372010-05-27 20:51:26 +00003462 size_t
cristy3ed852e2009-09-05 21:47:34 +00003463 number_points;
3464
3465 /*
3466 Open output image file.
3467 */
3468 assert(image_info != (const ImageInfo *) NULL);
3469 assert(image_info->signature == MagickSignature);
3470 assert(image != (Image *) NULL);
3471 assert(image->signature == MagickSignature);
3472 if (image->debug != MagickFalse)
3473 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3474 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
3475 if (status == MagickFalse)
3476 return(status);
3477 value=GetImageArtifact(image,"SVG");
3478 if (value != (char *) NULL)
3479 {
3480 (void) WriteBlobString(image,value);
3481 (void) CloseBlob(image);
3482 return(MagickTrue);
3483 }
3484 value=GetImageArtifact(image,"MVG");
3485 if (value == (char *) NULL)
3486 return(TraceSVGImage(image));
3487 /*
3488 Write SVG header.
3489 */
3490 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3491 (void) WriteBlobString(image,
3492 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3493 (void) WriteBlobString(image,
3494 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
cristyb51dff52011-05-19 16:55:47 +00003495 (void) FormatLocaleString(message,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00003496 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3497 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003498 (void) WriteBlobString(image,message);
3499 /*
3500 Allocate primitive info memory.
3501 */
3502 number_points=2047;
3503 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3504 sizeof(*primitive_info));
3505 if (primitive_info == (PrimitiveInfo *) NULL)
3506 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3507 GetAffineMatrix(&affine);
3508 token=AcquireString(value);
3509 active=MagickFalse;
3510 n=0;
3511 status=MagickTrue;
3512 for (q=(const char *) value; *q != '\0'; )
3513 {
3514 /*
3515 Interpret graphic primitive.
3516 */
3517 GetMagickToken(q,&q,keyword);
3518 if (*keyword == '\0')
3519 break;
3520 if (*keyword == '#')
3521 {
3522 /*
3523 Comment.
3524 */
3525 if (active != MagickFalse)
3526 {
3527 AffineToTransform(image,&affine);
3528 active=MagickFalse;
3529 }
3530 (void) WriteBlobString(image,"<desc>");
3531 (void) WriteBlobString(image,keyword+1);
3532 for ( ; (*q != '\n') && (*q != '\0'); q++)
3533 switch (*q)
3534 {
3535 case '<': (void) WriteBlobString(image,"&lt;"); break;
3536 case '>': (void) WriteBlobString(image,"&gt;"); break;
3537 case '&': (void) WriteBlobString(image,"&amp;"); break;
3538 default: (void) WriteBlobByte(image,*q); break;
3539 }
3540 (void) WriteBlobString(image,"</desc>\n");
3541 continue;
3542 }
3543 primitive_type=UndefinedPrimitive;
3544 switch (*keyword)
3545 {
3546 case ';':
3547 break;
3548 case 'a':
3549 case 'A':
3550 {
3551 if (LocaleCompare("affine",keyword) == 0)
3552 {
3553 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003554 affine.sx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003555 GetMagickToken(q,&q,token);
3556 if (*token == ',')
3557 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003558 affine.rx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003559 GetMagickToken(q,&q,token);
3560 if (*token == ',')
3561 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003562 affine.ry=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003563 GetMagickToken(q,&q,token);
3564 if (*token == ',')
3565 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003566 affine.sy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003567 GetMagickToken(q,&q,token);
3568 if (*token == ',')
3569 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003570 affine.tx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003571 GetMagickToken(q,&q,token);
3572 if (*token == ',')
3573 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003574 affine.ty=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003575 break;
3576 }
3577 if (LocaleCompare("angle",keyword) == 0)
3578 {
3579 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003580 affine.rx=InterpretLocaleValue(token,(char **) NULL);
3581 affine.ry=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003582 break;
3583 }
3584 if (LocaleCompare("arc",keyword) == 0)
3585 {
3586 primitive_type=ArcPrimitive;
3587 break;
3588 }
3589 status=MagickFalse;
3590 break;
3591 }
3592 case 'b':
3593 case 'B':
3594 {
3595 if (LocaleCompare("bezier",keyword) == 0)
3596 {
3597 primitive_type=BezierPrimitive;
3598 break;
3599 }
3600 status=MagickFalse;
3601 break;
3602 }
3603 case 'c':
3604 case 'C':
3605 {
3606 if (LocaleCompare("clip-path",keyword) == 0)
3607 {
3608 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003609 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003610 "clip-path:url(#%s);",token);
3611 (void) WriteBlobString(image,message);
3612 break;
3613 }
3614 if (LocaleCompare("clip-rule",keyword) == 0)
3615 {
3616 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003617 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003618 "clip-rule:%s;",token);
3619 (void) WriteBlobString(image,message);
3620 break;
3621 }
3622 if (LocaleCompare("clip-units",keyword) == 0)
3623 {
3624 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003625 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003626 "clipPathUnits=%s;",token);
3627 (void) WriteBlobString(image,message);
3628 break;
3629 }
3630 if (LocaleCompare("circle",keyword) == 0)
3631 {
3632 primitive_type=CirclePrimitive;
3633 break;
3634 }
3635 if (LocaleCompare("color",keyword) == 0)
3636 {
3637 primitive_type=ColorPrimitive;
3638 break;
3639 }
3640 status=MagickFalse;
3641 break;
3642 }
3643 case 'd':
3644 case 'D':
3645 {
3646 if (LocaleCompare("decorate",keyword) == 0)
3647 {
3648 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003649 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003650 "text-decoration:%s;",token);
3651 (void) WriteBlobString(image,message);
3652 break;
3653 }
3654 status=MagickFalse;
3655 break;
3656 }
3657 case 'e':
3658 case 'E':
3659 {
3660 if (LocaleCompare("ellipse",keyword) == 0)
3661 {
3662 primitive_type=EllipsePrimitive;
3663 break;
3664 }
3665 status=MagickFalse;
3666 break;
3667 }
3668 case 'f':
3669 case 'F':
3670 {
3671 if (LocaleCompare("fill",keyword) == 0)
3672 {
3673 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003674 (void) FormatLocaleString(message,MaxTextExtent,"fill:%s;",
cristy3ed852e2009-09-05 21:47:34 +00003675 token);
3676 (void) WriteBlobString(image,message);
3677 break;
3678 }
3679 if (LocaleCompare("fill-rule",keyword) == 0)
3680 {
3681 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003682 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003683 "fill-rule:%s;",token);
3684 (void) WriteBlobString(image,message);
3685 break;
3686 }
3687 if (LocaleCompare("fill-opacity",keyword) == 0)
3688 {
3689 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003690 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003691 "fill-opacity:%s;",token);
3692 (void) WriteBlobString(image,message);
3693 break;
3694 }
3695 if (LocaleCompare("font-family",keyword) == 0)
3696 {
3697 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003698 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003699 "font-family:%s;",token);
3700 (void) WriteBlobString(image,message);
3701 break;
3702 }
3703 if (LocaleCompare("font-stretch",keyword) == 0)
3704 {
3705 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003706 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003707 "font-stretch:%s;",token);
3708 (void) WriteBlobString(image,message);
3709 break;
3710 }
3711 if (LocaleCompare("font-style",keyword) == 0)
3712 {
3713 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003714 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003715 "font-style:%s;",token);
3716 (void) WriteBlobString(image,message);
3717 break;
3718 }
3719 if (LocaleCompare("font-size",keyword) == 0)
3720 {
3721 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003722 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003723 "font-size:%s;",token);
3724 (void) WriteBlobString(image,message);
3725 break;
3726 }
3727 if (LocaleCompare("font-weight",keyword) == 0)
3728 {
3729 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003730 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003731 "font-weight:%s;",token);
3732 (void) WriteBlobString(image,message);
3733 break;
3734 }
3735 status=MagickFalse;
3736 break;
3737 }
3738 case 'g':
3739 case 'G':
3740 {
3741 if (LocaleCompare("gradient-units",keyword) == 0)
3742 {
3743 GetMagickToken(q,&q,token);
3744 break;
3745 }
3746 if (LocaleCompare("text-align",keyword) == 0)
3747 {
3748 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003749 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003750 "text-align %s ",token);
3751 (void) WriteBlobString(image,message);
3752 break;
3753 }
3754 if (LocaleCompare("text-anchor",keyword) == 0)
3755 {
3756 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003757 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003758 "text-anchor %s ",token);
3759 (void) WriteBlobString(image,message);
3760 break;
3761 }
3762 status=MagickFalse;
3763 break;
3764 }
3765 case 'i':
3766 case 'I':
3767 {
3768 if (LocaleCompare("image",keyword) == 0)
3769 {
3770 GetMagickToken(q,&q,token);
3771 primitive_type=ImagePrimitive;
3772 break;
3773 }
3774 status=MagickFalse;
3775 break;
3776 }
3777 case 'l':
3778 case 'L':
3779 {
3780 if (LocaleCompare("line",keyword) == 0)
3781 {
3782 primitive_type=LinePrimitive;
3783 break;
3784 }
3785 status=MagickFalse;
3786 break;
3787 }
3788 case 'm':
3789 case 'M':
3790 {
3791 if (LocaleCompare("matte",keyword) == 0)
3792 {
3793 primitive_type=MattePrimitive;
3794 break;
3795 }
3796 status=MagickFalse;
3797 break;
3798 }
3799 case 'o':
3800 case 'O':
3801 {
3802 if (LocaleCompare("opacity",keyword) == 0)
3803 {
3804 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003805 (void) FormatLocaleString(message,MaxTextExtent,"opacity %s ",
cristy3ed852e2009-09-05 21:47:34 +00003806 token);
3807 (void) WriteBlobString(image,message);
3808 break;
3809 }
3810 status=MagickFalse;
3811 break;
3812 }
3813 case 'p':
3814 case 'P':
3815 {
3816 if (LocaleCompare("path",keyword) == 0)
3817 {
3818 primitive_type=PathPrimitive;
3819 break;
3820 }
3821 if (LocaleCompare("point",keyword) == 0)
3822 {
3823 primitive_type=PointPrimitive;
3824 break;
3825 }
3826 if (LocaleCompare("polyline",keyword) == 0)
3827 {
3828 primitive_type=PolylinePrimitive;
3829 break;
3830 }
3831 if (LocaleCompare("polygon",keyword) == 0)
3832 {
3833 primitive_type=PolygonPrimitive;
3834 break;
3835 }
3836 if (LocaleCompare("pop",keyword) == 0)
3837 {
3838 GetMagickToken(q,&q,token);
3839 if (LocaleCompare("clip-path",token) == 0)
3840 {
3841 (void) WriteBlobString(image,"</clipPath>\n");
3842 break;
3843 }
3844 if (LocaleCompare("defs",token) == 0)
3845 {
3846 (void) WriteBlobString(image,"</defs>\n");
3847 break;
3848 }
3849 if (LocaleCompare("gradient",token) == 0)
3850 {
cristyb51dff52011-05-19 16:55:47 +00003851 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003852 "</%sGradient>\n",type);
3853 (void) WriteBlobString(image,message);
3854 break;
3855 }
3856 if (LocaleCompare("graphic-context",token) == 0)
3857 {
3858 n--;
3859 if (n < 0)
3860 ThrowWriterException(DrawError,
3861 "UnbalancedGraphicContextPushPop");
3862 (void) WriteBlobString(image,"</g>\n");
3863 }
3864 if (LocaleCompare("pattern",token) == 0)
3865 {
3866 (void) WriteBlobString(image,"</pattern>\n");
3867 break;
3868 }
3869 if (LocaleCompare("defs",token) == 0)
3870 (void) WriteBlobString(image,"</g>\n");
3871 break;
3872 }
3873 if (LocaleCompare("push",keyword) == 0)
3874 {
3875 GetMagickToken(q,&q,token);
3876 if (LocaleCompare("clip-path",token) == 0)
3877 {
3878 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003879 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00003880 "<clipPath id=\"%s\">\n",token);
3881 (void) WriteBlobString(image,message);
3882 break;
3883 }
3884 if (LocaleCompare("defs",token) == 0)
3885 {
3886 (void) WriteBlobString(image,"<defs>\n");
3887 break;
3888 }
3889 if (LocaleCompare("gradient",token) == 0)
3890 {
3891 GetMagickToken(q,&q,token);
3892 (void) CopyMagickString(name,token,MaxTextExtent);
3893 GetMagickToken(q,&q,token);
3894 (void) CopyMagickString(type,token,MaxTextExtent);
3895 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003896 svg_info.segment.x1=InterpretLocaleValue(token,(char **) NULL);
3897 svg_info.element.cx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003898 GetMagickToken(q,&q,token);
3899 if (*token == ',')
3900 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003901 svg_info.segment.y1=InterpretLocaleValue(token,(char **) NULL);
3902 svg_info.element.cy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003903 GetMagickToken(q,&q,token);
3904 if (*token == ',')
3905 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003906 svg_info.segment.x2=InterpretLocaleValue(token,(char **) NULL);
3907 svg_info.element.major=InterpretLocaleValue(token,
3908 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003909 GetMagickToken(q,&q,token);
3910 if (*token == ',')
3911 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003912 svg_info.segment.y2=InterpretLocaleValue(token,(char **) NULL);
3913 svg_info.element.minor=InterpretLocaleValue(token,
3914 (char **) NULL);
cristyb51dff52011-05-19 16:55:47 +00003915 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003916 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
3917 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
cristy3ed852e2009-09-05 21:47:34 +00003918 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
3919 if (LocaleCompare(type,"radial") == 0)
3920 {
3921 GetMagickToken(q,&q,token);
3922 if (*token == ',')
3923 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003924 svg_info.element.angle=InterpretLocaleValue(token,
3925 (char **) NULL);
cristyb51dff52011-05-19 16:55:47 +00003926 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003927 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
3928 "fx=\"%g\" fy=\"%g\">\n",type,name,
cristy8cd5b312010-01-07 01:10:24 +00003929 svg_info.element.cx,svg_info.element.cy,
3930 svg_info.element.angle,svg_info.element.major,
3931 svg_info.element.minor);
cristy3ed852e2009-09-05 21:47:34 +00003932 }
3933 (void) WriteBlobString(image,message);
3934 break;
3935 }
3936 if (LocaleCompare("graphic-context",token) == 0)
3937 {
3938 n++;
3939 if (active)
3940 {
3941 AffineToTransform(image,&affine);
3942 active=MagickFalse;
3943 }
3944 (void) WriteBlobString(image,"<g style=\"");
3945 active=MagickTrue;
3946 }
3947 if (LocaleCompare("pattern",token) == 0)
3948 {
3949 GetMagickToken(q,&q,token);
3950 (void) CopyMagickString(name,token,MaxTextExtent);
3951 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003952 svg_info.bounds.x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003953 GetMagickToken(q,&q,token);
3954 if (*token == ',')
3955 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003956 svg_info.bounds.y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003957 GetMagickToken(q,&q,token);
3958 if (*token == ',')
3959 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003960 svg_info.bounds.width=InterpretLocaleValue(token,
3961 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003962 GetMagickToken(q,&q,token);
3963 if (*token == ',')
3964 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00003965 svg_info.bounds.height=InterpretLocaleValue(token,
3966 (char **) NULL);
cristyb51dff52011-05-19 16:55:47 +00003967 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00003968 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
3969 "height=\"%g\">\n",name,svg_info.bounds.x,
cristy8cd5b312010-01-07 01:10:24 +00003970 svg_info.bounds.y,svg_info.bounds.width,
3971 svg_info.bounds.height);
cristy3ed852e2009-09-05 21:47:34 +00003972 (void) WriteBlobString(image,message);
3973 break;
3974 }
3975 break;
3976 }
3977 status=MagickFalse;
3978 break;
3979 }
3980 case 'r':
3981 case 'R':
3982 {
3983 if (LocaleCompare("rectangle",keyword) == 0)
3984 {
3985 primitive_type=RectanglePrimitive;
3986 break;
3987 }
3988 if (LocaleCompare("roundRectangle",keyword) == 0)
3989 {
3990 primitive_type=RoundRectanglePrimitive;
3991 break;
3992 }
3993 if (LocaleCompare("rotate",keyword) == 0)
3994 {
3995 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00003996 (void) FormatLocaleString(message,MaxTextExtent,"rotate(%s) ",
cristy3ed852e2009-09-05 21:47:34 +00003997 token);
3998 (void) WriteBlobString(image,message);
3999 break;
4000 }
4001 status=MagickFalse;
4002 break;
4003 }
4004 case 's':
4005 case 'S':
4006 {
4007 if (LocaleCompare("scale",keyword) == 0)
4008 {
4009 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00004010 affine.sx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004011 GetMagickToken(q,&q,token);
4012 if (*token == ',')
4013 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00004014 affine.sy=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004015 break;
4016 }
4017 if (LocaleCompare("skewX",keyword) == 0)
4018 {
4019 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004020 (void) FormatLocaleString(message,MaxTextExtent,"skewX(%s) ",
cristy3ed852e2009-09-05 21:47:34 +00004021 token);
4022 (void) WriteBlobString(image,message);
4023 break;
4024 }
4025 if (LocaleCompare("skewY",keyword) == 0)
4026 {
4027 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004028 (void) FormatLocaleString(message,MaxTextExtent,"skewY(%s) ",
cristy3ed852e2009-09-05 21:47:34 +00004029 token);
4030 (void) WriteBlobString(image,message);
4031 break;
4032 }
4033 if (LocaleCompare("stop-color",keyword) == 0)
4034 {
4035 char
4036 color[MaxTextExtent];
4037
4038 GetMagickToken(q,&q,token);
4039 (void) CopyMagickString(color,token,MaxTextExtent);
4040 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004041 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004042 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4043 (void) WriteBlobString(image,message);
4044 break;
4045 }
4046 if (LocaleCompare("stroke",keyword) == 0)
4047 {
4048 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004049 (void) FormatLocaleString(message,MaxTextExtent,"stroke:%s;",
cristy3ed852e2009-09-05 21:47:34 +00004050 token);
4051 (void) WriteBlobString(image,message);
4052 break;
4053 }
4054 if (LocaleCompare("stroke-antialias",keyword) == 0)
4055 {
4056 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004057 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004058 "stroke-antialias:%s;",token);
4059 (void) WriteBlobString(image,message);
4060 break;
4061 }
4062 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4063 {
4064 if (IsPoint(q))
4065 {
cristybb503372010-05-27 20:51:26 +00004066 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004067 k;
4068
4069 p=q;
4070 GetMagickToken(p,&p,token);
4071 for (k=0; IsPoint(token); k++)
4072 GetMagickToken(p,&p,token);
4073 (void) WriteBlobString(image,"stroke-dasharray:");
4074 for (j=0; j < k; j++)
4075 {
4076 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004077 (void) FormatLocaleString(message,MaxTextExtent,"%s ",
cristy3ed852e2009-09-05 21:47:34 +00004078 token);
4079 (void) WriteBlobString(image,message);
4080 }
4081 (void) WriteBlobString(image,";");
4082 break;
4083 }
4084 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004085 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004086 "stroke-dasharray:%s;",token);
4087 (void) WriteBlobString(image,message);
4088 break;
4089 }
4090 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4091 {
4092 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004093 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004094 "stroke-dashoffset:%s;",token);
4095 (void) WriteBlobString(image,message);
4096 break;
4097 }
4098 if (LocaleCompare("stroke-linecap",keyword) == 0)
4099 {
4100 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004101 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004102 "stroke-linecap:%s;",token);
4103 (void) WriteBlobString(image,message);
4104 break;
4105 }
4106 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4107 {
4108 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004109 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004110 "stroke-linejoin:%s;",token);
4111 (void) WriteBlobString(image,message);
4112 break;
4113 }
4114 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4115 {
4116 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004117 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004118 "stroke-miterlimit:%s;",token);
4119 (void) WriteBlobString(image,message);
4120 break;
4121 }
4122 if (LocaleCompare("stroke-opacity",keyword) == 0)
4123 {
4124 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004125 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004126 "stroke-opacity:%s;",token);
4127 (void) WriteBlobString(image,message);
4128 break;
4129 }
4130 if (LocaleCompare("stroke-width",keyword) == 0)
4131 {
4132 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004133 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004134 "stroke-width:%s;",token);
4135 (void) WriteBlobString(image,message);
4136 continue;
4137 }
4138 status=MagickFalse;
4139 break;
4140 }
4141 case 't':
4142 case 'T':
4143 {
4144 if (LocaleCompare("text",keyword) == 0)
4145 {
4146 primitive_type=TextPrimitive;
4147 break;
4148 }
4149 if (LocaleCompare("text-antialias",keyword) == 0)
4150 {
4151 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004152 (void) FormatLocaleString(message,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004153 "text-antialias:%s;",token);
4154 (void) WriteBlobString(image,message);
4155 break;
4156 }
4157 if (LocaleCompare("tspan",keyword) == 0)
4158 {
4159 primitive_type=TextPrimitive;
4160 break;
4161 }
4162 if (LocaleCompare("translate",keyword) == 0)
4163 {
4164 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00004165 affine.tx=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004166 GetMagickToken(q,&q,token);
4167 if (*token == ',')
4168 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00004169 affine.ty=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004170 break;
4171 }
4172 status=MagickFalse;
4173 break;
4174 }
4175 case 'v':
4176 case 'V':
4177 {
4178 if (LocaleCompare("viewbox",keyword) == 0)
4179 {
4180 GetMagickToken(q,&q,token);
4181 if (*token == ',')
4182 GetMagickToken(q,&q,token);
4183 GetMagickToken(q,&q,token);
4184 if (*token == ',')
4185 GetMagickToken(q,&q,token);
4186 GetMagickToken(q,&q,token);
4187 if (*token == ',')
4188 GetMagickToken(q,&q,token);
4189 GetMagickToken(q,&q,token);
4190 break;
4191 }
4192 status=MagickFalse;
4193 break;
4194 }
4195 default:
4196 {
4197 status=MagickFalse;
4198 break;
4199 }
4200 }
4201 if (status == MagickFalse)
4202 break;
4203 if (primitive_type == UndefinedPrimitive)
4204 continue;
4205 /*
4206 Parse the primitive attributes.
4207 */
4208 i=0;
4209 j=0;
4210 for (x=0; *q != '\0'; x++)
4211 {
4212 /*
4213 Define points.
4214 */
4215 if (IsPoint(q) == MagickFalse)
4216 break;
4217 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00004218 point.x=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004219 GetMagickToken(q,&q,token);
4220 if (*token == ',')
4221 GetMagickToken(q,&q,token);
cristyc1acd842011-05-19 23:05:47 +00004222 point.y=InterpretLocaleValue(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004223 GetMagickToken(q,(const char **) NULL,token);
4224 if (*token == ',')
4225 GetMagickToken(q,&q,token);
4226 primitive_info[i].primitive=primitive_type;
4227 primitive_info[i].point=point;
4228 primitive_info[i].coordinates=0;
4229 primitive_info[i].method=FloodfillMethod;
4230 i++;
cristybb503372010-05-27 20:51:26 +00004231 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
cristy3ed852e2009-09-05 21:47:34 +00004232 continue;
4233 number_points+=6*BezierQuantum+360;
4234 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4235 number_points,sizeof(*primitive_info));
4236 if (primitive_info == (PrimitiveInfo *) NULL)
4237 {
4238 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4239 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4240 break;
4241 }
4242 }
4243 primitive_info[j].primitive=primitive_type;
4244 primitive_info[j].coordinates=x;
4245 primitive_info[j].method=FloodfillMethod;
4246 primitive_info[j].text=(char *) NULL;
4247 if (active)
4248 {
4249 AffineToTransform(image,&affine);
4250 active=MagickFalse;
4251 }
4252 active=MagickFalse;
4253 switch (primitive_type)
4254 {
4255 case PointPrimitive:
4256 default:
4257 {
4258 if (primitive_info[j].coordinates != 1)
4259 {
4260 status=MagickFalse;
4261 break;
4262 }
4263 break;
4264 }
4265 case LinePrimitive:
4266 {
4267 if (primitive_info[j].coordinates != 2)
4268 {
4269 status=MagickFalse;
4270 break;
4271 }
cristyb51dff52011-05-19 16:55:47 +00004272 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004273 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004274 primitive_info[j].point.x,primitive_info[j].point.y,
4275 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4276 (void) WriteBlobString(image,message);
4277 break;
4278 }
4279 case RectanglePrimitive:
4280 {
4281 if (primitive_info[j].coordinates != 2)
4282 {
4283 status=MagickFalse;
4284 break;
4285 }
cristyb51dff52011-05-19 16:55:47 +00004286 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004287 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004288 primitive_info[j].point.x,primitive_info[j].point.y,
4289 primitive_info[j+1].point.x-primitive_info[j].point.x,
4290 primitive_info[j+1].point.y-primitive_info[j].point.y);
4291 (void) WriteBlobString(image,message);
4292 break;
4293 }
4294 case RoundRectanglePrimitive:
4295 {
4296 if (primitive_info[j].coordinates != 3)
4297 {
4298 status=MagickFalse;
4299 break;
4300 }
cristyb51dff52011-05-19 16:55:47 +00004301 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004302 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4303 "ry=\"%g\"/>\n",primitive_info[j].point.x,
cristy8cd5b312010-01-07 01:10:24 +00004304 primitive_info[j].point.y,primitive_info[j+1].point.x-
4305 primitive_info[j].point.x,primitive_info[j+1].point.y-
4306 primitive_info[j].point.y,primitive_info[j+2].point.x,
4307 primitive_info[j+2].point.y);
cristy3ed852e2009-09-05 21:47:34 +00004308 (void) WriteBlobString(image,message);
4309 break;
4310 }
4311 case ArcPrimitive:
4312 {
4313 if (primitive_info[j].coordinates != 3)
4314 {
4315 status=MagickFalse;
4316 break;
4317 }
4318 break;
4319 }
4320 case EllipsePrimitive:
4321 {
4322 if (primitive_info[j].coordinates != 3)
4323 {
4324 status=MagickFalse;
4325 break;
4326 }
cristyb51dff52011-05-19 16:55:47 +00004327 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004328 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004329 primitive_info[j].point.x,primitive_info[j].point.y,
4330 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4331 (void) WriteBlobString(image,message);
4332 break;
4333 }
4334 case CirclePrimitive:
4335 {
4336 double
4337 alpha,
4338 beta;
4339
4340 if (primitive_info[j].coordinates != 2)
4341 {
4342 status=MagickFalse;
4343 break;
4344 }
4345 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4346 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
cristyb51dff52011-05-19 16:55:47 +00004347 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004348 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004349 primitive_info[j].point.x,primitive_info[j].point.y,
4350 hypot(alpha,beta));
4351 (void) WriteBlobString(image,message);
4352 break;
4353 }
4354 case PolylinePrimitive:
4355 {
4356 if (primitive_info[j].coordinates < 2)
4357 {
4358 status=MagickFalse;
4359 break;
4360 }
4361 (void) CopyMagickString(message," <polyline points=\"",MaxTextExtent);
4362 (void) WriteBlobString(image,message);
4363 length=strlen(message);
4364 for ( ; j < i; j++)
4365 {
cristyb51dff52011-05-19 16:55:47 +00004366 (void) FormatLocaleString(message,MaxTextExtent,"%g,%g ",
cristy3ed852e2009-09-05 21:47:34 +00004367 primitive_info[j].point.x,primitive_info[j].point.y);
4368 length+=strlen(message);
4369 if (length >= 80)
4370 {
4371 (void) WriteBlobString(image,"\n ");
4372 length=strlen(message)+5;
4373 }
4374 (void) WriteBlobString(image,message);
4375 }
4376 (void) WriteBlobString(image,"\"/>\n");
4377 break;
4378 }
4379 case PolygonPrimitive:
4380 {
4381 if (primitive_info[j].coordinates < 3)
4382 {
4383 status=MagickFalse;
4384 break;
4385 }
4386 primitive_info[i]=primitive_info[j];
4387 primitive_info[i].coordinates=0;
4388 primitive_info[j].coordinates++;
4389 i++;
4390 (void) CopyMagickString(message," <polygon points=\"",MaxTextExtent);
4391 (void) WriteBlobString(image,message);
4392 length=strlen(message);
4393 for ( ; j < i; j++)
4394 {
cristyb51dff52011-05-19 16:55:47 +00004395 (void) FormatLocaleString(message,MaxTextExtent,"%g,%g ",
cristy3ed852e2009-09-05 21:47:34 +00004396 primitive_info[j].point.x,primitive_info[j].point.y);
4397 length+=strlen(message);
4398 if (length >= 80)
4399 {
4400 (void) WriteBlobString(image,"\n ");
4401 length=strlen(message)+5;
4402 }
4403 (void) WriteBlobString(image,message);
4404 }
4405 (void) WriteBlobString(image,"\"/>\n");
4406 break;
4407 }
4408 case BezierPrimitive:
4409 {
4410 if (primitive_info[j].coordinates < 3)
4411 {
4412 status=MagickFalse;
4413 break;
4414 }
4415 break;
4416 }
4417 case PathPrimitive:
4418 {
4419 int
4420 number_attributes;
4421
4422 GetMagickToken(q,&q,token);
4423 number_attributes=1;
4424 for (p=token; *p != '\0'; p++)
4425 if (isalpha((int) *p))
4426 number_attributes++;
cristybb503372010-05-27 20:51:26 +00004427 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
cristy3ed852e2009-09-05 21:47:34 +00004428 {
4429 number_points+=6*BezierQuantum*number_attributes;
4430 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4431 number_points,sizeof(*primitive_info));
4432 if (primitive_info == (PrimitiveInfo *) NULL)
4433 {
4434 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4435 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4436 image->filename);
4437 break;
4438 }
4439 }
4440 (void) WriteBlobString(image," <path d=\"");
4441 (void) WriteBlobString(image,token);
4442 (void) WriteBlobString(image,"\"/>\n");
4443 break;
4444 }
4445 case ColorPrimitive:
4446 case MattePrimitive:
4447 {
4448 if (primitive_info[j].coordinates != 1)
4449 {
4450 status=MagickFalse;
4451 break;
4452 }
4453 GetMagickToken(q,&q,token);
4454 if (LocaleCompare("point",token) == 0)
4455 primitive_info[j].method=PointMethod;
4456 if (LocaleCompare("replace",token) == 0)
4457 primitive_info[j].method=ReplaceMethod;
4458 if (LocaleCompare("floodfill",token) == 0)
4459 primitive_info[j].method=FloodfillMethod;
4460 if (LocaleCompare("filltoborder",token) == 0)
4461 primitive_info[j].method=FillToBorderMethod;
4462 if (LocaleCompare("reset",token) == 0)
4463 primitive_info[j].method=ResetMethod;
4464 break;
4465 }
4466 case TextPrimitive:
4467 {
4468 register char
4469 *p;
4470
4471 if (primitive_info[j].coordinates != 1)
4472 {
4473 status=MagickFalse;
4474 break;
4475 }
4476 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004477 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004478 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
cristy3ed852e2009-09-05 21:47:34 +00004479 primitive_info[j].point.y);
4480 (void) WriteBlobString(image,message);
4481 for (p=token; *p != '\0'; p++)
4482 switch (*p)
4483 {
4484 case '<': (void) WriteBlobString(image,"&lt;"); break;
4485 case '>': (void) WriteBlobString(image,"&gt;"); break;
4486 case '&': (void) WriteBlobString(image,"&amp;"); break;
4487 default: (void) WriteBlobByte(image,*p); break;
4488 }
4489 (void) WriteBlobString(image,"</text>\n");
4490 break;
4491 }
4492 case ImagePrimitive:
4493 {
4494 if (primitive_info[j].coordinates != 2)
4495 {
4496 status=MagickFalse;
4497 break;
4498 }
4499 GetMagickToken(q,&q,token);
cristyb51dff52011-05-19 16:55:47 +00004500 (void) FormatLocaleString(message,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00004501 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
cristy3ed852e2009-09-05 21:47:34 +00004502 "xlink:href=\"%s\"/>\n",primitive_info[j].point.x,
4503 primitive_info[j].point.y,primitive_info[j+1].point.x,
4504 primitive_info[j+1].point.y,token);
4505 (void) WriteBlobString(image,message);
4506 break;
4507 }
4508 }
4509 if (primitive_info == (PrimitiveInfo *) NULL)
4510 break;
4511 primitive_info[i].primitive=UndefinedPrimitive;
4512 if (status == MagickFalse)
4513 break;
4514 }
4515 (void) WriteBlobString(image,"</svg>\n");
4516 /*
4517 Relinquish resources.
4518 */
4519 token=DestroyString(token);
4520 if (primitive_info != (PrimitiveInfo *) NULL)
4521 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4522 (void) CloseBlob(image);
4523 return(status);
4524}