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