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