blob: 81ba62e685dc05759fb0fd25809e8736f91d7401 [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% William Radcliffe %
18% March 2000 %
19% %
20% %
Cristy7ce65e72015-12-12 18:03:16 -050021% Copyright 1999-2016 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*/
cristyfa684be2015-02-08 13:43:46 +000039
cristy3ed852e2009-09-05 21:47:34 +000040/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/annotate.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/constitute.h"
51#include "MagickCore/composite-private.h"
cristy1164d5f2012-08-15 00:58:25 +000052#include "MagickCore/delegate.h"
53#include "MagickCore/delegate-private.h"
cristy4c08aed2011-07-01 19:47:50 +000054#include "MagickCore/draw.h"
55#include "MagickCore/exception.h"
56#include "MagickCore/exception-private.h"
57#include "MagickCore/gem.h"
58#include "MagickCore/image.h"
59#include "MagickCore/image-private.h"
60#include "MagickCore/list.h"
61#include "MagickCore/log.h"
62#include "MagickCore/magick.h"
63#include "MagickCore/memory_.h"
64#include "MagickCore/module.h"
65#include "MagickCore/monitor.h"
66#include "MagickCore/monitor-private.h"
67#include "MagickCore/quantum-private.h"
68#include "MagickCore/pixel-accessor.h"
69#include "MagickCore/property.h"
70#include "MagickCore/resource_.h"
71#include "MagickCore/static.h"
72#include "MagickCore/string_.h"
73#include "MagickCore/string-private.h"
74#include "MagickCore/token.h"
75#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000076#if defined(MAGICKCORE_XML_DELEGATE)
cristy0157aea2010-04-24 21:12:18 +000077# if defined(MAGICKCORE_WINDOWS_SUPPORT)
cristy14e6cc72014-10-05 19:31:31 +000078# if !defined(__MINGW32__) && !defined(__MINGW64__)
cristy3ed852e2009-09-05 21:47:34 +000079# 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"
anthonyb17d1642013-03-18 00:53:33 +000094#if !defined(LIBRSVG_CHECK_VERSION)
95#include "librsvg/rsvg-cairo.h"
96#include "librsvg/librsvg-features.h"
97#elif !LIBRSVG_CHECK_VERSION(2,36,2)
cristy3ed852e2009-09-05 21:47:34 +000098#include "librsvg/rsvg-cairo.h"
cristy3ed852e2009-09-05 21:47:34 +000099#include "librsvg/librsvg-features.h"
100#endif
cristyd1b3b292013-03-09 17:29:47 +0000101#endif
cristyfa684be2015-02-08 13:43:46 +0000102
cristy3ed852e2009-09-05 21:47:34 +0000103/*
cristy3ed852e2009-09-05 21:47:34 +0000104 Typedef declarations.
105*/
106typedef struct _BoundingBox
107{
108 double
109 x,
110 y,
111 width,
112 height;
113} BoundingBox;
114
115typedef struct _ElementInfo
116{
117 double
118 cx,
119 cy,
120 major,
121 minor,
122 angle;
123} ElementInfo;
124
125typedef struct _SVGInfo
126{
127 FILE
128 *file;
129
130 ExceptionInfo
131 *exception;
132
133 Image
134 *image;
135
136 const ImageInfo
137 *image_info;
138
139 AffineMatrix
140 affine;
141
cristybb503372010-05-27 20:51:26 +0000142 size_t
cristy3ed852e2009-09-05 21:47:34 +0000143 width,
144 height;
145
146 char
147 *size,
148 *title,
149 *comment;
150
151 int
152 n;
153
154 double
155 *scale,
156 pointsize;
157
158 ElementInfo
159 element;
160
161 SegmentInfo
162 segment;
163
164 BoundingBox
165 bounds,
166 center,
167 view_box;
168
169 PointInfo
170 radius;
171
172 char
173 *stop_color,
174 *offset,
175 *text,
176 *vertices,
177 *url;
178
179#if defined(MAGICKCORE_XML_DELEGATE)
180 xmlParserCtxtPtr
181 parser;
182
183 xmlDocPtr
184 document;
185#endif
186} SVGInfo;
cristyfa684be2015-02-08 13:43:46 +0000187
cristy3ed852e2009-09-05 21:47:34 +0000188/*
189 Forward declarations.
190*/
191static MagickBooleanType
cristy3a37efd2011-08-28 20:31:03 +0000192 WriteSVGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristyfa684be2015-02-08 13:43:46 +0000193
cristy3ed852e2009-09-05 21:47:34 +0000194/*
195%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
196% %
197% %
198% %
199% I s S V G %
200% %
201% %
202% %
203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
204%
205% IsSVG()() returns MagickTrue if the image format type, identified by the
206% magick string, is SVG.
207%
208% The format of the IsSVG method is:
209%
210% MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
211%
212% A description of each parameter follows:
213%
214% o magick: compare image format pattern against these bytes.
215%
216% o length: Specifies the length of the magick string.
217%
218*/
219static MagickBooleanType IsSVG(const unsigned char *magick,const size_t length)
220{
221 if (length < 4)
222 return(MagickFalse);
223 if (LocaleNCompare((const char *) magick,"?xml",4) == 0)
224 return(MagickTrue);
225 return(MagickFalse);
226}
dirk200dd432015-02-08 00:45:00 +0000227
cristy3ed852e2009-09-05 21:47:34 +0000228#if defined(MAGICKCORE_XML_DELEGATE)
229/*
230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
231% %
232% %
233% %
234% R e a d S V G I m a g e %
235% %
236% %
237% %
238%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
239%
240% ReadSVGImage() reads a Scalable Vector Gaphics file and returns it. It
241% allocates the memory necessary for the new Image structure and returns a
242% pointer to the new image.
243%
244% The format of the ReadSVGImage method is:
245%
246% Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
247%
248% A description of each parameter follows:
249%
250% o image_info: the image info.
251%
252% o exception: return any errors or warnings in this structure.
253%
254*/
255
256static SVGInfo *AcquireSVGInfo(void)
257{
258 SVGInfo
259 *svg_info;
260
cristy73bd4a52010-10-05 11:24:23 +0000261 svg_info=(SVGInfo *) AcquireMagickMemory(sizeof(*svg_info));
cristy3ed852e2009-09-05 21:47:34 +0000262 if (svg_info == (SVGInfo *) NULL)
263 return((SVGInfo *) NULL);
264 (void) ResetMagickMemory(svg_info,0,sizeof(*svg_info));
265 svg_info->text=AcquireString("");
cristy73bd4a52010-10-05 11:24:23 +0000266 svg_info->scale=(double *) AcquireMagickMemory(sizeof(*svg_info->scale));
cristy3ed852e2009-09-05 21:47:34 +0000267 if (svg_info->scale == (double *) NULL)
268 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
269 GetAffineMatrix(&svg_info->affine);
270 svg_info->scale[0]=ExpandAffine(&svg_info->affine);
271 return(svg_info);
272}
273
274static SVGInfo *DestroySVGInfo(SVGInfo *svg_info)
275{
276 if (svg_info->text != (char *) NULL)
277 svg_info->text=DestroyString(svg_info->text);
278 if (svg_info->scale != (double *) NULL)
dirkd5bc5a12014-04-04 17:09:07 +0000279 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
cristy3ed852e2009-09-05 21:47:34 +0000280 if (svg_info->title != (char *) NULL)
281 svg_info->title=DestroyString(svg_info->title);
282 if (svg_info->comment != (char *) NULL)
283 svg_info->comment=DestroyString(svg_info->comment);
284 return((SVGInfo *) RelinquishMagickMemory(svg_info));
285}
286
287static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
288 const char *string)
289{
290 char
cristy151b66d2015-04-15 10:50:31 +0000291 token[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000292
293 const char
294 *p;
295
296 double
297 value;
298
299 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",string);
300 assert(string != (const char *) NULL);
301 p=(const char *) string;
302 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +0000303 value=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000304 if (strchr(token,'%') != (char *) NULL)
305 {
306 double
307 alpha,
308 beta;
309
310 if (type > 0)
311 {
312 if (svg_info->view_box.width == 0.0)
cristyacff16a2012-08-16 00:21:35 +0000313 return(0.0);
cristy3ed852e2009-09-05 21:47:34 +0000314 return(svg_info->view_box.width*value/100.0);
315 }
316 if (type < 0)
317 {
318 if (svg_info->view_box.height == 0.0)
cristyacff16a2012-08-16 00:21:35 +0000319 return(0.0);
cristy3ed852e2009-09-05 21:47:34 +0000320 return(svg_info->view_box.height*value/100.0);
321 }
322 alpha=value-svg_info->view_box.width;
323 beta=value-svg_info->view_box.height;
324 return(hypot(alpha,beta)/sqrt(2.0)/100.0);
325 }
326 GetMagickToken(p,&p,token);
327 if (LocaleNCompare(token,"cm",2) == 0)
328 return(DefaultResolution*svg_info->scale[0]/2.54*value);
329 if (LocaleNCompare(token,"em",2) == 0)
330 return(svg_info->pointsize*value);
331 if (LocaleNCompare(token,"ex",2) == 0)
332 return(svg_info->pointsize*value/2.0);
333 if (LocaleNCompare(token,"in",2) == 0)
334 return(DefaultResolution*svg_info->scale[0]*value);
335 if (LocaleNCompare(token,"mm",2) == 0)
336 return(DefaultResolution*svg_info->scale[0]/25.4*value);
337 if (LocaleNCompare(token,"pc",2) == 0)
338 return(DefaultResolution*svg_info->scale[0]/6.0*value);
339 if (LocaleNCompare(token,"pt",2) == 0)
cristy2e017b02012-09-30 21:37:50 +0000340 return(1.25*svg_info->scale[0]*value);
cristy3ed852e2009-09-05 21:47:34 +0000341 if (LocaleNCompare(token,"px",2) == 0)
342 return(value);
343 return(value);
344}
345
346static void StripStyleTokens(char *message)
347{
348 register char
349 *p,
350 *q;
351
352 size_t
353 length;
354
355 assert(message != (char *) NULL);
356 if (*message == '\0')
357 return;
358 length=strlen(message);
359 p=message;
360 while (isspace((int) ((unsigned char) *p)) != 0)
361 p++;
362 q=message+length-1;
363 while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
364 q--;
365 (void) CopyMagickMemory(message,p,(size_t) (q-p+1));
366 message[q-p+1]='\0';
367 StripString(message);
368}
369
370static char **GetStyleTokens(void *context,const char *style,int *number_tokens)
371{
372 char
373 *text,
374 **tokens;
375
cristybb503372010-05-27 20:51:26 +0000376 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000377 i;
378
379 SVGInfo
380 *svg_info;
381
382 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +0000383 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +0000384 *number_tokens=0;
385 if (style == (const char *) NULL)
386 return((char **) NULL);
387 text=AcquireString(style);
388 (void) SubstituteString(&text,":","\n");
389 (void) SubstituteString(&text,";","\n");
390 tokens=StringToList(text);
391 text=DestroyString(text);
392 for (i=0; tokens[i] != (char *) NULL; i++)
393 StripStyleTokens(tokens[i]);
394 *number_tokens=i;
395 return(tokens);
396}
397
398static char **GetTransformTokens(void *context,const char *text,
399 int *number_tokens)
400{
401 char
402 **tokens;
403
404 register const char
405 *p,
406 *q;
407
cristybb503372010-05-27 20:51:26 +0000408 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000409 i;
410
411 SVGInfo
412 *svg_info;
413
414 svg_info=(SVGInfo *) context;
415 *number_tokens=0;
416 if (text == (const char *) NULL)
417 return((char **) NULL);
418 /*
419 Determine the number of arguments.
420 */
421 for (p=text; *p != '\0'; p++)
422 {
423 if (*p == '(')
424 (*number_tokens)+=2;
425 }
426 tokens=(char **) AcquireQuantumMemory(*number_tokens+2UL,sizeof(*tokens));
427 if (tokens == (char **) NULL)
428 {
429 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
430 ResourceLimitError,"MemoryAllocationFailed","`%s'",text);
431 return((char **) NULL);
432 }
433 /*
434 Convert string to an ASCII list.
435 */
436 i=0;
437 p=text;
438 for (q=p; *q != '\0'; q++)
439 {
440 if ((*q != '(') && (*q != ')') && (*q != '\0'))
441 continue;
442 tokens[i]=AcquireString(p);
443 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
444 StripString(tokens[i++]);
445 p=q+1;
446 }
447 tokens[i]=AcquireString(p);
448 (void) CopyMagickString(tokens[i],p,(size_t) (q-p+1));
449 StripString(tokens[i++]);
450 tokens[i]=(char *) NULL;
451 return(tokens);
452}
453
454#if defined(__cplusplus) || defined(c_plusplus)
455extern "C" {
456#endif
457
458static int SVGIsStandalone(void *context)
459{
460 SVGInfo
461 *svg_info;
462
463 /*
464 Is this document tagged standalone?
465 */
466 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGIsStandalone()");
467 svg_info=(SVGInfo *) context;
468 return(svg_info->document->standalone == 1);
469}
470
471static int SVGHasInternalSubset(void *context)
472{
473 SVGInfo
474 *svg_info;
475
476 /*
477 Does this document has an internal subset?
478 */
479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
480 " SAX.SVGHasInternalSubset()");
481 svg_info=(SVGInfo *) context;
482 return(svg_info->document->intSubset != NULL);
483}
484
485static int SVGHasExternalSubset(void *context)
486{
487 SVGInfo
488 *svg_info;
489
490 /*
491 Does this document has an external subset?
492 */
493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
494 " SAX.SVGHasExternalSubset()");
495 svg_info=(SVGInfo *) context;
496 return(svg_info->document->extSubset != NULL);
497}
498
499static void SVGInternalSubset(void *context,const xmlChar *name,
500 const xmlChar *external_id,const xmlChar *system_id)
501{
502 SVGInfo
503 *svg_info;
504
505 /*
506 Does this document has an internal subset?
507 */
508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
509 " SAX.internalSubset(%s, %s, %s)",(const char *) name,
510 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
511 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
512 svg_info=(SVGInfo *) context;
513 (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id);
514}
515
516static xmlParserInputPtr SVGResolveEntity(void *context,
517 const xmlChar *public_id,const xmlChar *system_id)
518{
519 SVGInfo
520 *svg_info;
521
522 xmlParserInputPtr
523 stream;
524
525 /*
526 Special entity resolver, better left to the parser, it has more
527 context than the application layer. The default behaviour is to
528 not resolve the entities, in that case the ENTITY_REF nodes are
529 built in the structure (and the parameter values).
530 */
531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
532 " SAX.resolveEntity(%s, %s)",
533 (public_id != (const xmlChar *) NULL ? (const char *) public_id : "none"),
534 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
535 svg_info=(SVGInfo *) context;
536 stream=xmlLoadExternalEntity((const char *) system_id,(const char *)
537 public_id,svg_info->parser);
538 return(stream);
539}
540
541static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
542{
543 SVGInfo
544 *svg_info;
545
546 /*
547 Get an entity by name.
548 */
549 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.SVGGetEntity(%s)",
550 name);
551 svg_info=(SVGInfo *) context;
552 return(xmlGetDocEntity(svg_info->document,name));
553}
554
555static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
556{
557 SVGInfo
558 *svg_info;
559
560 /*
561 Get a parameter entity by name.
562 */
563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
564 " SAX.getParameterEntity(%s)",name);
565 svg_info=(SVGInfo *) context;
566 return(xmlGetParameterEntity(svg_info->document,name));
567}
568
569static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
570 const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
571{
572 SVGInfo
573 *svg_info;
574
575 /*
576 An entity definition has been parsed.
577 */
578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
579 " SAX.entityDecl(%s, %d, %s, %s, %s)",name,type,
580 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
581 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",content);
582 svg_info=(SVGInfo *) context;
583 if (svg_info->parser->inSubset == 1)
584 (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id,
585 content);
586 else
587 if (svg_info->parser->inSubset == 2)
588 (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
589 content);
590}
591
592static void SVGAttributeDeclaration(void *context,const xmlChar *element,
593 const xmlChar *name,int type,int value,const xmlChar *default_value,
594 xmlEnumerationPtr tree)
595{
596 SVGInfo
597 *svg_info;
598
599 xmlChar
600 *fullname,
601 *prefix;
602
603 xmlParserCtxtPtr
604 parser;
605
606 /*
607 An attribute definition has been parsed.
608 */
609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
610 " SAX.attributeDecl(%s, %s, %d, %d, %s, ...)",element,name,type,value,
611 default_value);
612 svg_info=(SVGInfo *) context;
613 fullname=(xmlChar *) NULL;
614 prefix=(xmlChar *) NULL;
615 parser=svg_info->parser;
616 fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix);
617 if (parser->inSubset == 1)
618 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset,
619 element,fullname,prefix,(xmlAttributeType) type,
620 (xmlAttributeDefault) value,default_value,tree);
621 else
622 if (parser->inSubset == 2)
623 (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset,
624 element,fullname,prefix,(xmlAttributeType) type,
625 (xmlAttributeDefault) value,default_value,tree);
626 if (prefix != (xmlChar *) NULL)
627 xmlFree(prefix);
628 if (fullname != (xmlChar *) NULL)
629 xmlFree(fullname);
630}
631
632static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
633 xmlElementContentPtr content)
634{
635 SVGInfo
636 *svg_info;
637
638 xmlParserCtxtPtr
639 parser;
640
641 /*
642 An element definition has been parsed.
643 */
644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
645 " SAX.elementDecl(%s, %d, ...)",name,type);
646 svg_info=(SVGInfo *) context;
647 parser=svg_info->parser;
648 if (parser->inSubset == 1)
649 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset,
650 name,(xmlElementTypeVal) type,content);
651 else
652 if (parser->inSubset == 2)
653 (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
654 name,(xmlElementTypeVal) type,content);
655}
656
657static void SVGNotationDeclaration(void *context,const xmlChar *name,
658 const xmlChar *public_id,const xmlChar *system_id)
659{
660 SVGInfo
661 *svg_info;
662
663 xmlParserCtxtPtr
664 parser;
665
666 /*
667 What to do when a notation declaration has been parsed.
668 */
669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
670 " SAX.notationDecl(%s, %s, %s)",name,
671 public_id != (const xmlChar *) NULL ? (const char *) public_id : "none",
672 system_id != (const xmlChar *) NULL ? (const char *) system_id : "none");
673 svg_info=(SVGInfo *) context;
674 parser=svg_info->parser;
675 if (parser->inSubset == 1)
676 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
677 name,public_id,system_id);
678 else
679 if (parser->inSubset == 2)
680 (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
681 name,public_id,system_id);
682}
683
684static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
685 const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
686{
687 SVGInfo
688 *svg_info;
689
690 /*
691 What to do when an unparsed entity declaration is parsed.
692 */
693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
694 " SAX.unparsedEntityDecl(%s, %s, %s, %s)",name,
695 public_id != (xmlChar *) NULL ? (const char *) public_id : "none",
696 system_id != (xmlChar *) NULL ? (const char *) system_id : "none",notation);
697 svg_info=(SVGInfo *) context;
698 (void) xmlAddDocEntity(svg_info->document,name,
699 XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
700
701}
702
703static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
704{
705 SVGInfo
706 *svg_info;
707
708 /*
709 Receive the document locator at startup, actually xmlDefaultSAXLocator.
710 */
711 (void) location;
712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
713 " SAX.setDocumentLocator()");
714 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +0000715 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +0000716}
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;
cristy3ed852e2009-09-05 21:47:34 +0000731 parser=svg_info->parser;
732 svg_info->document=xmlNewDoc(parser->version);
733 if (svg_info->document == (xmlDocPtr) NULL)
734 return;
735 if (parser->encoding == NULL)
736 svg_info->document->encoding=(const xmlChar *) NULL;
737 else
738 svg_info->document->encoding=xmlStrdup(parser->encoding);
739 svg_info->document->standalone=parser->standalone;
740}
741
742static void SVGEndDocument(void *context)
743{
744 SVGInfo
745 *svg_info;
746
747 /*
748 Called when the document end has been detected.
749 */
750 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.endDocument()");
751 svg_info=(SVGInfo *) context;
752 if (svg_info->offset != (char *) NULL)
753 svg_info->offset=DestroyString(svg_info->offset);
754 if (svg_info->stop_color != (char *) NULL)
755 svg_info->stop_color=DestroyString(svg_info->stop_color);
756 if (svg_info->scale != (double *) NULL)
757 svg_info->scale=(double *) RelinquishMagickMemory(svg_info->scale);
758 if (svg_info->text != (char *) NULL)
759 svg_info->text=DestroyString(svg_info->text);
760 if (svg_info->vertices != (char *) NULL)
761 svg_info->vertices=DestroyString(svg_info->vertices);
762 if (svg_info->url != (char *) NULL)
763 svg_info->url=DestroyString(svg_info->url);
764#if defined(MAGICKCORE_XML_DELEGATE)
765 if (svg_info->document != (xmlDocPtr) NULL)
766 {
767 xmlFreeDoc(svg_info->document);
768 svg_info->document=(xmlDocPtr) NULL;
769 }
770#endif
771}
772
773static void SVGStartElement(void *context,const xmlChar *name,
774 const xmlChar **attributes)
775{
776 char
777 *color,
cristy151b66d2015-04-15 10:50:31 +0000778 id[MagickPathExtent],
779 token[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +0000780 **tokens,
781 *units;
782
783 const char
784 *keyword,
785 *p,
786 *value;
787
788 int
789 number_tokens;
790
791 SVGInfo
792 *svg_info;
793
cristybb503372010-05-27 20:51:26 +0000794 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000795 i,
796 j;
797
798 /*
799 Called when an opening tag has been processed.
800 */
801 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.startElement(%s",
802 name);
803 svg_info=(SVGInfo *) context;
804 svg_info->n++;
805 svg_info->scale=(double *) ResizeQuantumMemory(svg_info->scale,
806 svg_info->n+1UL,sizeof(*svg_info->scale));
807 if (svg_info->scale == (double *) NULL)
808 {
809 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
810 ResourceLimitError,"MemoryAllocationFailed","`%s'",name);
811 return;
812 }
813 svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
814 color=AcquireString("none");
815 units=AcquireString("userSpaceOnUse");
816 value=(const char *) NULL;
817 if (attributes != (const xmlChar **) NULL)
818 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
819 {
820 keyword=(const char *) attributes[i];
821 value=(const char *) attributes[i+1];
822 switch (*keyword)
823 {
824 case 'C':
825 case 'c':
826 {
827 if (LocaleCompare(keyword,"cx") == 0)
828 {
829 svg_info->element.cx=
830 GetUserSpaceCoordinateValue(svg_info,1,value);
831 break;
832 }
833 if (LocaleCompare(keyword,"cy") == 0)
834 {
835 svg_info->element.cy=
836 GetUserSpaceCoordinateValue(svg_info,-1,value);
837 break;
838 }
839 break;
840 }
841 case 'F':
842 case 'f':
843 {
844 if (LocaleCompare(keyword,"fx") == 0)
845 {
846 svg_info->element.major=
847 GetUserSpaceCoordinateValue(svg_info,1,value);
848 break;
849 }
850 if (LocaleCompare(keyword,"fy") == 0)
851 {
852 svg_info->element.minor=
853 GetUserSpaceCoordinateValue(svg_info,-1,value);
854 break;
855 }
856 break;
857 }
858 case 'H':
859 case 'h':
860 {
861 if (LocaleCompare(keyword,"height") == 0)
862 {
863 svg_info->bounds.height=
864 GetUserSpaceCoordinateValue(svg_info,-1,value);
865 break;
866 }
867 break;
868 }
869 case 'I':
870 case 'i':
871 {
872 if (LocaleCompare(keyword,"id") == 0)
873 {
cristy151b66d2015-04-15 10:50:31 +0000874 (void) CopyMagickString(id,value,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000875 break;
876 }
877 break;
878 }
879 case 'R':
880 case 'r':
881 {
882 if (LocaleCompare(keyword,"r") == 0)
883 {
884 svg_info->element.angle=
885 GetUserSpaceCoordinateValue(svg_info,0,value);
886 break;
887 }
888 break;
889 }
890 case 'W':
891 case 'w':
892 {
893 if (LocaleCompare(keyword,"width") == 0)
894 {
895 svg_info->bounds.width=
896 GetUserSpaceCoordinateValue(svg_info,1,value);
897 break;
898 }
899 break;
900 }
901 case 'X':
902 case 'x':
903 {
904 if (LocaleCompare(keyword,"x") == 0)
905 {
906 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value)-
907 svg_info->center.x;
908 break;
909 }
910 if (LocaleCompare(keyword,"x1") == 0)
911 {
912 svg_info->segment.x1=GetUserSpaceCoordinateValue(svg_info,1,
913 value);
914 break;
915 }
916 if (LocaleCompare(keyword,"x2") == 0)
917 {
918 svg_info->segment.x2=GetUserSpaceCoordinateValue(svg_info,1,
919 value);
920 break;
921 }
922 break;
923 }
924 case 'Y':
925 case 'y':
926 {
927 if (LocaleCompare(keyword,"y") == 0)
928 {
929 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value)-
930 svg_info->center.y;
931 break;
932 }
933 if (LocaleCompare(keyword,"y1") == 0)
934 {
935 svg_info->segment.y1=
936 GetUserSpaceCoordinateValue(svg_info,-1,value);
937 break;
938 }
939 if (LocaleCompare(keyword,"y2") == 0)
940 {
941 svg_info->segment.y2=
942 GetUserSpaceCoordinateValue(svg_info,-1,value);
943 break;
944 }
945 break;
946 }
947 default:
948 break;
949 }
950 }
cristy13d07042010-11-21 20:56:18 +0000951 if (strchr((char *) name,':') != (char *) NULL)
cristy768b9302010-11-20 00:05:47 +0000952 {
953 /*
954 Skip over namespace.
955 */
956 for ( ; *name != ':'; name++) ;
957 name++;
958 }
cristy3ed852e2009-09-05 21:47:34 +0000959 switch (*name)
960 {
961 case 'C':
962 case 'c':
963 {
964 if (LocaleCompare((const char *) name,"circle") == 0)
965 {
cristyb51dff52011-05-19 16:55:47 +0000966 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +0000967 break;
968 }
969 if (LocaleCompare((const char *) name,"clipPath") == 0)
970 {
cristyb51dff52011-05-19 16:55:47 +0000971 (void) FormatLocaleFile(svg_info->file,"push clip-path '%s'\n",id);
cristy3ed852e2009-09-05 21:47:34 +0000972 break;
973 }
974 break;
975 }
976 case 'D':
977 case 'd':
978 {
979 if (LocaleCompare((const char *) name,"defs") == 0)
980 {
cristyb51dff52011-05-19 16:55:47 +0000981 (void) FormatLocaleFile(svg_info->file,"push defs\n");
cristy3ed852e2009-09-05 21:47:34 +0000982 break;
983 }
984 break;
985 }
986 case 'E':
987 case 'e':
988 {
989 if (LocaleCompare((const char *) name,"ellipse") == 0)
990 {
cristyb51dff52011-05-19 16:55:47 +0000991 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +0000992 break;
993 }
994 break;
995 }
996 case 'G':
997 case 'g':
998 {
999 if (LocaleCompare((const char *) name,"g") == 0)
1000 {
cristyb51dff52011-05-19 16:55:47 +00001001 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001002 break;
1003 }
1004 break;
1005 }
1006 case 'I':
1007 case 'i':
1008 {
1009 if (LocaleCompare((const char *) name,"image") == 0)
1010 {
cristyb51dff52011-05-19 16:55:47 +00001011 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001012 break;
1013 }
1014 break;
1015 }
1016 case 'L':
1017 case 'l':
1018 {
1019 if (LocaleCompare((const char *) name,"line") == 0)
1020 {
cristyb51dff52011-05-19 16:55:47 +00001021 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001022 break;
1023 }
1024 if (LocaleCompare((const char *) name,"linearGradient") == 0)
1025 {
cristyb51dff52011-05-19 16:55:47 +00001026 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001027 "push gradient '%s' linear %g,%g %g,%g\n",id,
cristy3ed852e2009-09-05 21:47:34 +00001028 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
1029 svg_info->segment.y2);
1030 break;
1031 }
1032 break;
1033 }
1034 case 'P':
1035 case 'p':
1036 {
1037 if (LocaleCompare((const char *) name,"path") == 0)
1038 {
cristyb51dff52011-05-19 16:55:47 +00001039 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001040 break;
1041 }
1042 if (LocaleCompare((const char *) name,"pattern") == 0)
1043 {
cristyb51dff52011-05-19 16:55:47 +00001044 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001045 "push pattern '%s' %g,%g %g,%g\n",id,
cristy3ed852e2009-09-05 21:47:34 +00001046 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
1047 svg_info->bounds.height);
1048 break;
1049 }
1050 if (LocaleCompare((const char *) name,"polygon") == 0)
1051 {
cristyb51dff52011-05-19 16:55:47 +00001052 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001053 break;
1054 }
1055 if (LocaleCompare((const char *) name,"polyline") == 0)
1056 {
cristyb51dff52011-05-19 16:55:47 +00001057 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001058 break;
1059 }
1060 break;
1061 }
1062 case 'R':
1063 case 'r':
1064 {
1065 if (LocaleCompare((const char *) name,"radialGradient") == 0)
1066 {
cristyb51dff52011-05-19 16:55:47 +00001067 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001068 "push gradient '%s' radial %g,%g %g,%g %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001069 id,svg_info->element.cx,svg_info->element.cy,
1070 svg_info->element.major,svg_info->element.minor,
1071 svg_info->element.angle);
1072 break;
1073 }
1074 if (LocaleCompare((const char *) name,"rect") == 0)
1075 {
cristyb51dff52011-05-19 16:55:47 +00001076 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001077 break;
1078 }
1079 break;
1080 }
1081 case 'S':
1082 case 's':
1083 {
1084 if (LocaleCompare((const char *) name,"svg") == 0)
1085 {
cristyb51dff52011-05-19 16:55:47 +00001086 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001087 break;
1088 }
1089 break;
1090 }
1091 case 'T':
1092 case 't':
1093 {
1094 if (LocaleCompare((const char *) name,"text") == 0)
1095 {
cristyb51dff52011-05-19 16:55:47 +00001096 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristye4c6a422011-11-24 18:57:53 +00001097 svg_info->bounds.x=0.0;
1098 svg_info->bounds.y=0.0;
1099 svg_info->bounds.width=0.0;
1100 svg_info->bounds.height=0.0;
cristy3ed852e2009-09-05 21:47:34 +00001101 break;
1102 }
1103 if (LocaleCompare((const char *) name,"tspan") == 0)
1104 {
1105 if (*svg_info->text != '\0')
1106 {
1107 DrawInfo
1108 *draw_info;
1109
1110 TypeMetric
1111 metrics;
1112
1113 char
1114 *text;
1115
1116 text=EscapeString(svg_info->text,'\'');
cristyb51dff52011-05-19 16:55:47 +00001117 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
cristy8cd5b312010-01-07 01:10:24 +00001118 svg_info->bounds.x-svg_info->center.x,svg_info->bounds.y-
1119 svg_info->center.y,text);
cristy3ed852e2009-09-05 21:47:34 +00001120 text=DestroyString(text);
1121 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
1122 draw_info->pointsize=svg_info->pointsize;
1123 draw_info->text=AcquireString(svg_info->text);
1124 (void) ConcatenateString(&draw_info->text," ");
cristy5cbc0162011-08-29 00:36:28 +00001125 (void) GetTypeMetrics(svg_info->image,draw_info,
1126 &metrics,svg_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00001127 svg_info->bounds.x+=metrics.width;
1128 draw_info=DestroyDrawInfo(draw_info);
1129 *svg_info->text='\0';
1130 }
cristyb51dff52011-05-19 16:55:47 +00001131 (void) FormatLocaleFile(svg_info->file,"push graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00001132 break;
1133 }
1134 break;
1135 }
1136 default:
1137 break;
1138 }
1139 if (attributes != (const xmlChar **) NULL)
1140 for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
1141 {
1142 keyword=(const char *) attributes[i];
1143 value=(const char *) attributes[i+1];
1144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1145 " %s = %s",keyword,value);
1146 switch (*keyword)
1147 {
1148 case 'A':
1149 case 'a':
1150 {
1151 if (LocaleCompare(keyword,"angle") == 0)
1152 {
cristyb51dff52011-05-19 16:55:47 +00001153 (void) FormatLocaleFile(svg_info->file,"angle %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001154 GetUserSpaceCoordinateValue(svg_info,0,value));
1155 break;
1156 }
1157 break;
1158 }
1159 case 'C':
1160 case 'c':
1161 {
1162 if (LocaleCompare(keyword,"clip-path") == 0)
1163 {
cristyb51dff52011-05-19 16:55:47 +00001164 (void) FormatLocaleFile(svg_info->file,"clip-path '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001165 break;
1166 }
1167 if (LocaleCompare(keyword,"clip-rule") == 0)
1168 {
cristyb51dff52011-05-19 16:55:47 +00001169 (void) FormatLocaleFile(svg_info->file,"clip-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001170 break;
1171 }
1172 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1173 {
1174 (void) CloneString(&units,value);
cristyb51dff52011-05-19 16:55:47 +00001175 (void) FormatLocaleFile(svg_info->file,"clip-units '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001176 break;
1177 }
1178 if (LocaleCompare(keyword,"color") == 0)
1179 {
1180 (void) CloneString(&color,value);
1181 break;
1182 }
1183 if (LocaleCompare(keyword,"cx") == 0)
1184 {
1185 svg_info->element.cx=
1186 GetUserSpaceCoordinateValue(svg_info,1,value);
1187 break;
1188 }
1189 if (LocaleCompare(keyword,"cy") == 0)
1190 {
1191 svg_info->element.cy=
1192 GetUserSpaceCoordinateValue(svg_info,-1,value);
1193 break;
1194 }
1195 break;
1196 }
1197 case 'D':
1198 case 'd':
1199 {
1200 if (LocaleCompare(keyword,"d") == 0)
1201 {
1202 (void) CloneString(&svg_info->vertices,value);
1203 break;
1204 }
1205 if (LocaleCompare(keyword,"dx") == 0)
1206 {
1207 svg_info->bounds.x+=GetUserSpaceCoordinateValue(svg_info,1,value);
1208 break;
1209 }
1210 if (LocaleCompare(keyword,"dy") == 0)
1211 {
1212 svg_info->bounds.y+=
1213 GetUserSpaceCoordinateValue(svg_info,-1,value);
1214 break;
1215 }
1216 break;
1217 }
1218 case 'F':
1219 case 'f':
1220 {
1221 if (LocaleCompare(keyword,"fill") == 0)
1222 {
1223 if (LocaleCompare(value,"currentColor") == 0)
1224 {
cristyb51dff52011-05-19 16:55:47 +00001225 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001226 break;
1227 }
cristyb51dff52011-05-19 16:55:47 +00001228 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001229 break;
1230 }
1231 if (LocaleCompare(keyword,"fillcolor") == 0)
1232 {
cristyb51dff52011-05-19 16:55:47 +00001233 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001234 break;
1235 }
1236 if (LocaleCompare(keyword,"fill-rule") == 0)
1237 {
cristyb51dff52011-05-19 16:55:47 +00001238 (void) FormatLocaleFile(svg_info->file,"fill-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001239 break;
1240 }
cristy69fcc592012-04-28 18:39:59 +00001241 if (LocaleCompare(keyword,"fill-alpha") == 0)
cristy3ed852e2009-09-05 21:47:34 +00001242 {
cristy69fcc592012-04-28 18:39:59 +00001243 (void) FormatLocaleFile(svg_info->file,"fill-alpha '%s'\n",
cristyb51dff52011-05-19 16:55:47 +00001244 value);
cristy3ed852e2009-09-05 21:47:34 +00001245 break;
1246 }
1247 if (LocaleCompare(keyword,"font-family") == 0)
1248 {
cristyb51dff52011-05-19 16:55:47 +00001249 (void) FormatLocaleFile(svg_info->file,"font-family '%s'\n",
1250 value);
cristy3ed852e2009-09-05 21:47:34 +00001251 break;
1252 }
1253 if (LocaleCompare(keyword,"font-stretch") == 0)
1254 {
cristyb51dff52011-05-19 16:55:47 +00001255 (void) FormatLocaleFile(svg_info->file,"font-stretch '%s'\n",
1256 value);
cristy3ed852e2009-09-05 21:47:34 +00001257 break;
1258 }
1259 if (LocaleCompare(keyword,"font-style") == 0)
1260 {
cristyb51dff52011-05-19 16:55:47 +00001261 (void) FormatLocaleFile(svg_info->file,"font-style '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001262 break;
1263 }
1264 if (LocaleCompare(keyword,"font-size") == 0)
1265 {
1266 svg_info->pointsize=GetUserSpaceCoordinateValue(svg_info,0,value);
cristyb51dff52011-05-19 16:55:47 +00001267 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
1268 svg_info->pointsize);
cristy3ed852e2009-09-05 21:47:34 +00001269 break;
1270 }
1271 if (LocaleCompare(keyword,"font-weight") == 0)
1272 {
cristyb51dff52011-05-19 16:55:47 +00001273 (void) FormatLocaleFile(svg_info->file,"font-weight '%s'\n",
1274 value);
cristy3ed852e2009-09-05 21:47:34 +00001275 break;
1276 }
1277 break;
1278 }
1279 case 'G':
1280 case 'g':
1281 {
1282 if (LocaleCompare(keyword,"gradientTransform") == 0)
1283 {
1284 AffineMatrix
1285 affine,
1286 current,
1287 transform;
1288
1289 GetAffineMatrix(&transform);
1290 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1291 tokens=GetTransformTokens(context,value,&number_tokens);
1292 for (j=0; j < (number_tokens-1); j+=2)
1293 {
1294 keyword=(char *) tokens[j];
1295 if (keyword == (char *) NULL)
1296 continue;
1297 value=(char *) tokens[j+1];
1298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1299 " %s: %s",keyword,value);
1300 current=transform;
1301 GetAffineMatrix(&affine);
1302 switch (*keyword)
1303 {
1304 case 'M':
1305 case 'm':
1306 {
1307 if (LocaleCompare(keyword,"matrix") == 0)
1308 {
1309 p=(const char *) value;
1310 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001311 affine.sx=StringToDouble(value,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001312 GetMagickToken(p,&p,token);
1313 if (*token == ',')
1314 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001315 affine.rx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001316 GetMagickToken(p,&p,token);
1317 if (*token == ',')
1318 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001319 affine.ry=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001320 GetMagickToken(p,&p,token);
1321 if (*token == ',')
1322 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001323 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001324 GetMagickToken(p,&p,token);
1325 if (*token == ',')
1326 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001327 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001328 GetMagickToken(p,&p,token);
1329 if (*token == ',')
1330 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001331 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001332 break;
1333 }
1334 break;
1335 }
1336 case 'R':
1337 case 'r':
1338 {
1339 if (LocaleCompare(keyword,"rotate") == 0)
1340 {
1341 double
1342 angle;
1343
1344 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
1345 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1346 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1347 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1348 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1349 break;
1350 }
1351 break;
1352 }
1353 case 'S':
1354 case 's':
1355 {
1356 if (LocaleCompare(keyword,"scale") == 0)
1357 {
1358 for (p=(const char *) value; *p != '\0'; p++)
1359 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1360 (*p == ','))
1361 break;
1362 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1363 affine.sy=affine.sx;
1364 if (*p != '\0')
1365 affine.sy=
1366 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1367 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
1368 break;
1369 }
1370 if (LocaleCompare(keyword,"skewX") == 0)
1371 {
1372 affine.sx=svg_info->affine.sx;
1373 affine.ry=tan(DegreesToRadians(fmod(
1374 GetUserSpaceCoordinateValue(svg_info,1,value),
1375 360.0)));
1376 affine.sy=svg_info->affine.sy;
1377 break;
1378 }
1379 if (LocaleCompare(keyword,"skewY") == 0)
1380 {
1381 affine.sx=svg_info->affine.sx;
1382 affine.rx=tan(DegreesToRadians(fmod(
1383 GetUserSpaceCoordinateValue(svg_info,-1,value),
1384 360.0)));
1385 affine.sy=svg_info->affine.sy;
1386 break;
1387 }
1388 break;
1389 }
1390 case 'T':
1391 case 't':
1392 {
1393 if (LocaleCompare(keyword,"translate") == 0)
1394 {
1395 for (p=(const char *) value; *p != '\0'; p++)
1396 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1397 (*p == ','))
1398 break;
1399 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
1400 affine.ty=affine.tx;
1401 if (*p != '\0')
1402 affine.ty=
1403 GetUserSpaceCoordinateValue(svg_info,-1,p+1);
1404 break;
1405 }
1406 break;
1407 }
1408 default:
1409 break;
1410 }
cristyef7c8a52010-10-10 13:46:51 +00001411 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
1412 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
1413 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
1414 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
cristy2ac30482011-11-08 20:24:57 +00001415 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
1416 current.tx;
1417 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
1418 current.ty;
cristy3ed852e2009-09-05 21:47:34 +00001419 }
cristyb51dff52011-05-19 16:55:47 +00001420 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00001421 "affine %g %g %g %g %g %g\n",transform.sx,
cristy8cd5b312010-01-07 01:10:24 +00001422 transform.rx,transform.ry,transform.sy,transform.tx,
1423 transform.ty);
cristy3ed852e2009-09-05 21:47:34 +00001424 for (j=0; tokens[j] != (char *) NULL; j++)
1425 tokens[j]=DestroyString(tokens[j]);
1426 tokens=(char **) RelinquishMagickMemory(tokens);
1427 break;
1428 }
1429 if (LocaleCompare(keyword,"gradientUnits") == 0)
1430 {
1431 (void) CloneString(&units,value);
cristyb51dff52011-05-19 16:55:47 +00001432 (void) FormatLocaleFile(svg_info->file,"gradient-units '%s'\n",
1433 value);
cristy3ed852e2009-09-05 21:47:34 +00001434 break;
1435 }
1436 break;
1437 }
1438 case 'H':
1439 case 'h':
1440 {
1441 if (LocaleCompare(keyword,"height") == 0)
1442 {
1443 svg_info->bounds.height=
1444 GetUserSpaceCoordinateValue(svg_info,-1,value);
1445 break;
1446 }
1447 if (LocaleCompare(keyword,"href") == 0)
1448 {
1449 (void) CloneString(&svg_info->url,value);
1450 break;
1451 }
1452 break;
1453 }
1454 case 'M':
1455 case 'm':
1456 {
1457 if (LocaleCompare(keyword,"major") == 0)
1458 {
1459 svg_info->element.major=
1460 GetUserSpaceCoordinateValue(svg_info,1,value);
1461 break;
1462 }
1463 if (LocaleCompare(keyword,"minor") == 0)
1464 {
1465 svg_info->element.minor=
1466 GetUserSpaceCoordinateValue(svg_info,-1,value);
1467 break;
1468 }
1469 break;
1470 }
1471 case 'O':
1472 case 'o':
1473 {
1474 if (LocaleCompare(keyword,"offset") == 0)
1475 {
1476 (void) CloneString(&svg_info->offset,value);
1477 break;
1478 }
1479 if (LocaleCompare(keyword,"opacity") == 0)
1480 {
cristyb51dff52011-05-19 16:55:47 +00001481 (void) FormatLocaleFile(svg_info->file,"opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001482 break;
1483 }
1484 break;
1485 }
1486 case 'P':
1487 case 'p':
1488 {
1489 if (LocaleCompare(keyword,"path") == 0)
1490 {
1491 (void) CloneString(&svg_info->url,value);
1492 break;
1493 }
1494 if (LocaleCompare(keyword,"points") == 0)
1495 {
1496 (void) CloneString(&svg_info->vertices,value);
1497 break;
1498 }
1499 break;
1500 }
1501 case 'R':
1502 case 'r':
1503 {
1504 if (LocaleCompare(keyword,"r") == 0)
1505 {
1506 svg_info->element.major=
1507 GetUserSpaceCoordinateValue(svg_info,1,value);
1508 svg_info->element.minor=
1509 GetUserSpaceCoordinateValue(svg_info,-1,value);
1510 break;
1511 }
1512 if (LocaleCompare(keyword,"rotate") == 0)
1513 {
1514 double
1515 angle;
1516
1517 angle=GetUserSpaceCoordinateValue(svg_info,0,value);
cristyb51dff52011-05-19 16:55:47 +00001518 (void) FormatLocaleFile(svg_info->file,"translate %g,%g\n",
cristy8cd5b312010-01-07 01:10:24 +00001519 svg_info->bounds.x,svg_info->bounds.y);
cristy3ed852e2009-09-05 21:47:34 +00001520 svg_info->bounds.x=0;
1521 svg_info->bounds.y=0;
cristyb51dff52011-05-19 16:55:47 +00001522 (void) FormatLocaleFile(svg_info->file,"rotate %g\n",angle);
cristy3ed852e2009-09-05 21:47:34 +00001523 break;
1524 }
1525 if (LocaleCompare(keyword,"rx") == 0)
1526 {
1527 if (LocaleCompare((const char *) name,"ellipse") == 0)
1528 svg_info->element.major=
1529 GetUserSpaceCoordinateValue(svg_info,1,value);
1530 else
1531 svg_info->radius.x=
1532 GetUserSpaceCoordinateValue(svg_info,1,value);
1533 break;
1534 }
1535 if (LocaleCompare(keyword,"ry") == 0)
1536 {
1537 if (LocaleCompare((const char *) name,"ellipse") == 0)
1538 svg_info->element.minor=
1539 GetUserSpaceCoordinateValue(svg_info,-1,value);
1540 else
1541 svg_info->radius.y=
1542 GetUserSpaceCoordinateValue(svg_info,-1,value);
1543 break;
1544 }
1545 break;
1546 }
1547 case 'S':
1548 case 's':
1549 {
1550 if (LocaleCompare(keyword,"stop-color") == 0)
1551 {
1552 (void) CloneString(&svg_info->stop_color,value);
1553 break;
1554 }
1555 if (LocaleCompare(keyword,"stroke") == 0)
1556 {
1557 if (LocaleCompare(value,"currentColor") == 0)
1558 {
cristyb51dff52011-05-19 16:55:47 +00001559 (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001560 break;
1561 }
cristyb51dff52011-05-19 16:55:47 +00001562 (void) FormatLocaleFile(svg_info->file,"stroke '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001563 break;
1564 }
1565 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1566 {
cristyb51dff52011-05-19 16:55:47 +00001567 (void) FormatLocaleFile(svg_info->file,"stroke-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001568 LocaleCompare(value,"true") == 0);
1569 break;
1570 }
1571 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1572 {
cristyb51dff52011-05-19 16:55:47 +00001573 (void) FormatLocaleFile(svg_info->file,"stroke-dasharray %s\n",
1574 value);
cristy3ed852e2009-09-05 21:47:34 +00001575 break;
1576 }
1577 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1578 {
cristyb51dff52011-05-19 16:55:47 +00001579 (void) FormatLocaleFile(svg_info->file,"stroke-dashoffset %s\n",
1580 value);
cristy3ed852e2009-09-05 21:47:34 +00001581 break;
1582 }
1583 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1584 {
cristyb51dff52011-05-19 16:55:47 +00001585 (void) FormatLocaleFile(svg_info->file,"stroke-linecap '%s'\n",
1586 value);
cristy3ed852e2009-09-05 21:47:34 +00001587 break;
1588 }
1589 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1590 {
cristyb51dff52011-05-19 16:55:47 +00001591 (void) FormatLocaleFile(svg_info->file,"stroke-linejoin '%s'\n",
1592 value);
cristy3ed852e2009-09-05 21:47:34 +00001593 break;
1594 }
1595 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1596 {
cristyb51dff52011-05-19 16:55:47 +00001597 (void) FormatLocaleFile(svg_info->file,"stroke-miterlimit '%s'\n",
1598 value);
cristy3ed852e2009-09-05 21:47:34 +00001599 break;
1600 }
1601 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1602 {
cristyb51dff52011-05-19 16:55:47 +00001603 (void) FormatLocaleFile(svg_info->file,"stroke-opacity '%s'\n",
1604 value);
cristy3ed852e2009-09-05 21:47:34 +00001605 break;
1606 }
1607 if (LocaleCompare(keyword,"stroke-width") == 0)
1608 {
cristyb51dff52011-05-19 16:55:47 +00001609 (void) FormatLocaleFile(svg_info->file,"stroke-width %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001610 GetUserSpaceCoordinateValue(svg_info,1,value));
1611 break;
1612 }
1613 if (LocaleCompare(keyword,"style") == 0)
1614 {
1615 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1616 tokens=GetStyleTokens(context,value,&number_tokens);
1617 for (j=0; j < (number_tokens-1); j+=2)
1618 {
1619 keyword=(char *) tokens[j];
1620 value=(char *) tokens[j+1];
1621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1622 " %s: %s",keyword,value);
1623 switch (*keyword)
1624 {
1625 case 'C':
1626 case 'c':
1627 {
1628 if (LocaleCompare(keyword,"clip-path") == 0)
1629 {
cristyb51dff52011-05-19 16:55:47 +00001630 (void) FormatLocaleFile(svg_info->file,
1631 "clip-path '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001632 break;
1633 }
1634 if (LocaleCompare(keyword,"clip-rule") == 0)
1635 {
cristyb51dff52011-05-19 16:55:47 +00001636 (void) FormatLocaleFile(svg_info->file,
1637 "clip-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001638 break;
1639 }
1640 if (LocaleCompare(keyword,"clipPathUnits") == 0)
1641 {
1642 (void) CloneString(&units,value);
cristyb51dff52011-05-19 16:55:47 +00001643 (void) FormatLocaleFile(svg_info->file,
1644 "clip-units '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001645 break;
1646 }
1647 if (LocaleCompare(keyword,"color") == 0)
1648 {
1649 (void) CloneString(&color,value);
1650 break;
1651 }
1652 break;
1653 }
1654 case 'F':
1655 case 'f':
1656 {
1657 if (LocaleCompare(keyword,"fill") == 0)
1658 {
1659 if (LocaleCompare(value,"currentColor") == 0)
1660 {
cristyb51dff52011-05-19 16:55:47 +00001661 (void) FormatLocaleFile(svg_info->file,
cristyfad60c92012-01-19 18:32:39 +00001662 "fill '%s'\n",color);
cristy3ed852e2009-09-05 21:47:34 +00001663 break;
1664 }
cristyca611542013-02-19 00:54:03 +00001665 if (LocaleCompare(value,"#000000ff") == 0)
cristyb51dff52011-05-19 16:55:47 +00001666 (void) FormatLocaleFile(svg_info->file,
cristyca611542013-02-19 00:54:03 +00001667 "fill '#000000'\n");
cristy3ed852e2009-09-05 21:47:34 +00001668 else
cristyb51dff52011-05-19 16:55:47 +00001669 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
cristyca611542013-02-19 00:54:03 +00001670 value);
cristy3ed852e2009-09-05 21:47:34 +00001671 break;
1672 }
1673 if (LocaleCompare(keyword,"fillcolor") == 0)
1674 {
cristyb51dff52011-05-19 16:55:47 +00001675 (void) FormatLocaleFile(svg_info->file,"fill '%s'\n",
1676 value);
cristy3ed852e2009-09-05 21:47:34 +00001677 break;
1678 }
1679 if (LocaleCompare(keyword,"fill-rule") == 0)
1680 {
cristyb51dff52011-05-19 16:55:47 +00001681 (void) FormatLocaleFile(svg_info->file,
1682 "fill-rule '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001683 break;
1684 }
cristy69fcc592012-04-28 18:39:59 +00001685 if (LocaleCompare(keyword,"fill-alpha") == 0)
cristy3ed852e2009-09-05 21:47:34 +00001686 {
cristyb51dff52011-05-19 16:55:47 +00001687 (void) FormatLocaleFile(svg_info->file,
cristy69fcc592012-04-28 18:39:59 +00001688 "fill-alpha '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001689 break;
1690 }
1691 if (LocaleCompare(keyword,"font-family") == 0)
1692 {
cristyb51dff52011-05-19 16:55:47 +00001693 (void) FormatLocaleFile(svg_info->file,
1694 "font-family '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001695 break;
1696 }
1697 if (LocaleCompare(keyword,"font-stretch") == 0)
1698 {
cristyb51dff52011-05-19 16:55:47 +00001699 (void) FormatLocaleFile(svg_info->file,
1700 "font-stretch '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001701 break;
1702 }
1703 if (LocaleCompare(keyword,"font-style") == 0)
1704 {
cristyb51dff52011-05-19 16:55:47 +00001705 (void) FormatLocaleFile(svg_info->file,
1706 "font-style '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001707 break;
1708 }
1709 if (LocaleCompare(keyword,"font-size") == 0)
1710 {
1711 svg_info->pointsize=GetUserSpaceCoordinateValue(
1712 svg_info,0,value);
cristyb51dff52011-05-19 16:55:47 +00001713 (void) FormatLocaleFile(svg_info->file,"font-size %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001714 svg_info->pointsize);
1715 break;
1716 }
1717 if (LocaleCompare(keyword,"font-weight") == 0)
1718 {
cristyb51dff52011-05-19 16:55:47 +00001719 (void) FormatLocaleFile(svg_info->file,
1720 "font-weight '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001721 break;
1722 }
1723 break;
1724 }
1725 case 'O':
1726 case 'o':
1727 {
1728 if (LocaleCompare(keyword,"offset") == 0)
1729 {
cristyb51dff52011-05-19 16:55:47 +00001730 (void) FormatLocaleFile(svg_info->file,"offset %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001731 GetUserSpaceCoordinateValue(svg_info,1,value));
1732 break;
1733 }
1734 if (LocaleCompare(keyword,"opacity") == 0)
1735 {
cristyb51dff52011-05-19 16:55:47 +00001736 (void) FormatLocaleFile(svg_info->file,
1737 "opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001738 break;
1739 }
1740 break;
1741 }
1742 case 'S':
1743 case 's':
1744 {
1745 if (LocaleCompare(keyword,"stop-color") == 0)
1746 {
1747 (void) CloneString(&svg_info->stop_color,value);
1748 break;
1749 }
1750 if (LocaleCompare(keyword,"stroke") == 0)
1751 {
cristyfad60c92012-01-19 18:32:39 +00001752 if (LocaleCompare(value,"currentColor") == 0)
1753 {
1754 (void) FormatLocaleFile(svg_info->file,
1755 "stroke '%s'\n",color);
1756 break;
1757 }
cristyca611542013-02-19 00:54:03 +00001758 if (LocaleCompare(value,"#000000ff") == 0)
cristyb51dff52011-05-19 16:55:47 +00001759 (void) FormatLocaleFile(svg_info->file,
cristyca611542013-02-19 00:54:03 +00001760 "fill '#000000'\n");
cristy3ed852e2009-09-05 21:47:34 +00001761 else
cristyb51dff52011-05-19 16:55:47 +00001762 (void) FormatLocaleFile(svg_info->file,
cristyca611542013-02-19 00:54:03 +00001763 "stroke '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001764 break;
1765 }
1766 if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
1767 {
cristyb51dff52011-05-19 16:55:47 +00001768 (void) FormatLocaleFile(svg_info->file,
1769 "stroke-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001770 LocaleCompare(value,"true") == 0);
1771 break;
1772 }
1773 if (LocaleCompare(keyword,"stroke-dasharray") == 0)
1774 {
cristyb51dff52011-05-19 16:55:47 +00001775 (void) FormatLocaleFile(svg_info->file,
1776 "stroke-dasharray %s\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001777 break;
1778 }
1779 if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
1780 {
cristyb51dff52011-05-19 16:55:47 +00001781 (void) FormatLocaleFile(svg_info->file,
1782 "stroke-dashoffset %s\n",
cristy3ed852e2009-09-05 21:47:34 +00001783 value);
1784 break;
1785 }
1786 if (LocaleCompare(keyword,"stroke-linecap") == 0)
1787 {
cristyb51dff52011-05-19 16:55:47 +00001788 (void) FormatLocaleFile(svg_info->file,
1789 "stroke-linecap '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001790 break;
1791 }
1792 if (LocaleCompare(keyword,"stroke-linejoin") == 0)
1793 {
cristyb51dff52011-05-19 16:55:47 +00001794 (void) FormatLocaleFile(svg_info->file,
1795 "stroke-linejoin '%s'\n",
cristy3ed852e2009-09-05 21:47:34 +00001796 value);
1797 break;
1798 }
1799 if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
1800 {
cristyb51dff52011-05-19 16:55:47 +00001801 (void) FormatLocaleFile(svg_info->file,
1802 "stroke-miterlimit '%s'\n",
cristy3ed852e2009-09-05 21:47:34 +00001803 value);
1804 break;
1805 }
1806 if (LocaleCompare(keyword,"stroke-opacity") == 0)
1807 {
cristyb51dff52011-05-19 16:55:47 +00001808 (void) FormatLocaleFile(svg_info->file,
1809 "stroke-opacity '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001810 break;
1811 }
1812 if (LocaleCompare(keyword,"stroke-width") == 0)
1813 {
cristyb51dff52011-05-19 16:55:47 +00001814 (void) FormatLocaleFile(svg_info->file,
1815 "stroke-width %g\n",
cristy3ed852e2009-09-05 21:47:34 +00001816 GetUserSpaceCoordinateValue(svg_info,1,value));
1817 break;
1818 }
1819 break;
1820 }
1821 case 't':
1822 case 'T':
1823 {
1824 if (LocaleCompare(keyword,"text-align") == 0)
1825 {
cristyb51dff52011-05-19 16:55:47 +00001826 (void) FormatLocaleFile(svg_info->file,
1827 "text-align '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001828 break;
1829 }
1830 if (LocaleCompare(keyword,"text-anchor") == 0)
1831 {
cristyb51dff52011-05-19 16:55:47 +00001832 (void) FormatLocaleFile(svg_info->file,
1833 "text-anchor '%s'\n",value);
cristy3ed852e2009-09-05 21:47:34 +00001834 break;
1835 }
1836 if (LocaleCompare(keyword,"text-decoration") == 0)
1837 {
1838 if (LocaleCompare(value,"underline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001839 (void) FormatLocaleFile(svg_info->file,
1840 "decorate underline\n");
cristy3ed852e2009-09-05 21:47:34 +00001841 if (LocaleCompare(value,"line-through") == 0)
cristyb51dff52011-05-19 16:55:47 +00001842 (void) FormatLocaleFile(svg_info->file,
1843 "decorate line-through\n");
cristy3ed852e2009-09-05 21:47:34 +00001844 if (LocaleCompare(value,"overline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001845 (void) FormatLocaleFile(svg_info->file,
1846 "decorate overline\n");
cristy3ed852e2009-09-05 21:47:34 +00001847 break;
1848 }
1849 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1850 {
cristyb51dff52011-05-19 16:55:47 +00001851 (void) FormatLocaleFile(svg_info->file,
1852 "text-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001853 LocaleCompare(value,"true") == 0);
1854 break;
1855 }
1856 break;
1857 }
1858 default:
1859 break;
1860 }
1861 }
1862 for (j=0; tokens[j] != (char *) NULL; j++)
1863 tokens[j]=DestroyString(tokens[j]);
1864 tokens=(char **) RelinquishMagickMemory(tokens);
1865 break;
1866 }
1867 break;
1868 }
1869 case 'T':
1870 case 't':
1871 {
1872 if (LocaleCompare(keyword,"text-align") == 0)
1873 {
cristyb51dff52011-05-19 16:55:47 +00001874 (void) FormatLocaleFile(svg_info->file,"text-align '%s'\n",
1875 value);
cristy3ed852e2009-09-05 21:47:34 +00001876 break;
1877 }
1878 if (LocaleCompare(keyword,"text-anchor") == 0)
1879 {
cristyb51dff52011-05-19 16:55:47 +00001880 (void) FormatLocaleFile(svg_info->file,"text-anchor '%s'\n",
1881 value);
cristy3ed852e2009-09-05 21:47:34 +00001882 break;
1883 }
1884 if (LocaleCompare(keyword,"text-decoration") == 0)
1885 {
1886 if (LocaleCompare(value,"underline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001887 (void) FormatLocaleFile(svg_info->file,"decorate underline\n");
cristy3ed852e2009-09-05 21:47:34 +00001888 if (LocaleCompare(value,"line-through") == 0)
cristyb51dff52011-05-19 16:55:47 +00001889 (void) FormatLocaleFile(svg_info->file,
1890 "decorate line-through\n");
cristy3ed852e2009-09-05 21:47:34 +00001891 if (LocaleCompare(value,"overline") == 0)
cristyb51dff52011-05-19 16:55:47 +00001892 (void) FormatLocaleFile(svg_info->file,"decorate overline\n");
cristy3ed852e2009-09-05 21:47:34 +00001893 break;
1894 }
1895 if (LocaleCompare(keyword,"text-antialiasing") == 0)
1896 {
cristyb51dff52011-05-19 16:55:47 +00001897 (void) FormatLocaleFile(svg_info->file,"text-antialias %d\n",
cristy3ed852e2009-09-05 21:47:34 +00001898 LocaleCompare(value,"true") == 0);
1899 break;
1900 }
1901 if (LocaleCompare(keyword,"transform") == 0)
1902 {
1903 AffineMatrix
1904 affine,
1905 current,
1906 transform;
1907
1908 GetAffineMatrix(&transform);
1909 (void) LogMagickEvent(CoderEvent,GetMagickModule()," ");
1910 tokens=GetTransformTokens(context,value,&number_tokens);
1911 for (j=0; j < (number_tokens-1); j+=2)
1912 {
1913 keyword=(char *) tokens[j];
1914 value=(char *) tokens[j+1];
1915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1916 " %s: %s",keyword,value);
1917 current=transform;
1918 GetAffineMatrix(&affine);
1919 switch (*keyword)
1920 {
1921 case 'M':
1922 case 'm':
1923 {
1924 if (LocaleCompare(keyword,"matrix") == 0)
1925 {
1926 p=(const char *) value;
1927 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001928 affine.sx=StringToDouble(value,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001929 GetMagickToken(p,&p,token);
1930 if (*token == ',')
1931 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001932 affine.rx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001933 GetMagickToken(p,&p,token);
1934 if (*token == ',')
1935 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001936 affine.ry=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001937 GetMagickToken(p,&p,token);
1938 if (*token == ',')
1939 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001940 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001941 GetMagickToken(p,&p,token);
1942 if (*token == ',')
1943 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001944 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001945 GetMagickToken(p,&p,token);
1946 if (*token == ',')
1947 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001948 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001949 break;
1950 }
1951 break;
1952 }
1953 case 'R':
1954 case 'r':
1955 {
1956 if (LocaleCompare(keyword,"rotate") == 0)
1957 {
1958 double
1959 angle,
1960 x,
1961 y;
1962
1963 p=(const char *) value;
1964 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001965 angle=StringToDouble(value,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001966 GetMagickToken(p,&p,token);
1967 if (*token == ',')
1968 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001969 x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001970 GetMagickToken(p,&p,token);
1971 if (*token == ',')
1972 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001973 y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001974 affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
1975 affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
1976 affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
1977 affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
1978 affine.tx=x;
1979 affine.ty=y;
1980 svg_info->center.x=x;
1981 svg_info->center.y=y;
1982 break;
1983 }
1984 break;
1985 }
1986 case 'S':
1987 case 's':
1988 {
1989 if (LocaleCompare(keyword,"scale") == 0)
1990 {
1991 for (p=(const char *) value; *p != '\0'; p++)
1992 if ((isspace((int) ((unsigned char) *p)) != 0) ||
1993 (*p == ','))
1994 break;
1995 affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
1996 affine.sy=affine.sx;
1997 if (*p != '\0')
1998 affine.sy=GetUserSpaceCoordinateValue(svg_info,-1,
1999 p+1);
2000 svg_info->scale[svg_info->n]=ExpandAffine(&affine);
2001 break;
2002 }
2003 if (LocaleCompare(keyword,"skewX") == 0)
2004 {
2005 affine.sx=svg_info->affine.sx;
2006 affine.ry=tan(DegreesToRadians(fmod(
2007 GetUserSpaceCoordinateValue(svg_info,1,value),
2008 360.0)));
2009 affine.sy=svg_info->affine.sy;
2010 break;
2011 }
2012 if (LocaleCompare(keyword,"skewY") == 0)
2013 {
2014 affine.sx=svg_info->affine.sx;
2015 affine.rx=tan(DegreesToRadians(fmod(
2016 GetUserSpaceCoordinateValue(svg_info,-1,value),
2017 360.0)));
2018 affine.sy=svg_info->affine.sy;
2019 break;
2020 }
2021 break;
2022 }
2023 case 'T':
2024 case 't':
2025 {
2026 if (LocaleCompare(keyword,"translate") == 0)
2027 {
2028 for (p=(const char *) value; *p != '\0'; p++)
2029 if ((isspace((int) ((unsigned char) *p)) != 0) ||
2030 (*p == ','))
2031 break;
2032 affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
2033 affine.ty=affine.tx;
2034 if (*p != '\0')
2035 affine.ty=GetUserSpaceCoordinateValue(svg_info,-1,
2036 p+1);
2037 break;
2038 }
2039 break;
2040 }
2041 default:
2042 break;
2043 }
cristyef7c8a52010-10-10 13:46:51 +00002044 transform.sx=affine.sx*current.sx+affine.ry*current.rx;
2045 transform.rx=affine.rx*current.sx+affine.sy*current.rx;
2046 transform.ry=affine.sx*current.ry+affine.ry*current.sy;
2047 transform.sy=affine.rx*current.ry+affine.sy*current.sy;
cristy2ac30482011-11-08 20:24:57 +00002048 transform.tx=affine.tx*current.sx+affine.ty*current.ry+
2049 current.tx;
2050 transform.ty=affine.tx*current.rx+affine.ty*current.sy+
2051 current.ty;
cristy3ed852e2009-09-05 21:47:34 +00002052 }
cristyb51dff52011-05-19 16:55:47 +00002053 (void) FormatLocaleFile(svg_info->file,
cristy4096b012011-10-19 00:31:56 +00002054 "affine %g %g %g %g %g %g\n",transform.sx,transform.rx,
2055 transform.ry,transform.sy,transform.tx,transform.ty);
cristy3ed852e2009-09-05 21:47:34 +00002056 for (j=0; tokens[j] != (char *) NULL; j++)
2057 tokens[j]=DestroyString(tokens[j]);
2058 tokens=(char **) RelinquishMagickMemory(tokens);
2059 break;
2060 }
2061 break;
2062 }
2063 case 'V':
2064 case 'v':
2065 {
2066 if (LocaleCompare(keyword,"verts") == 0)
2067 {
2068 (void) CloneString(&svg_info->vertices,value);
2069 break;
2070 }
2071 if (LocaleCompare(keyword,"viewBox") == 0)
2072 {
2073 p=(const char *) value;
2074 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00002075 svg_info->view_box.x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002076 GetMagickToken(p,&p,token);
2077 if (*token == ',')
2078 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00002079 svg_info->view_box.y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002080 GetMagickToken(p,&p,token);
2081 if (*token == ',')
2082 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00002083 svg_info->view_box.width=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002084 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002085 if (svg_info->bounds.width == 0)
2086 svg_info->bounds.width=svg_info->view_box.width;
2087 GetMagickToken(p,&p,token);
2088 if (*token == ',')
2089 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00002090 svg_info->view_box.height=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00002091 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00002092 if (svg_info->bounds.height == 0)
2093 svg_info->bounds.height=svg_info->view_box.height;
2094 break;
2095 }
2096 break;
2097 }
2098 case 'W':
2099 case 'w':
2100 {
2101 if (LocaleCompare(keyword,"width") == 0)
2102 {
2103 svg_info->bounds.width=
2104 GetUserSpaceCoordinateValue(svg_info,1,value);
2105 break;
2106 }
2107 break;
2108 }
2109 case 'X':
2110 case 'x':
2111 {
2112 if (LocaleCompare(keyword,"x") == 0)
2113 {
2114 svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
2115 break;
2116 }
2117 if (LocaleCompare(keyword,"xlink:href") == 0)
2118 {
2119 (void) CloneString(&svg_info->url,value);
2120 break;
2121 }
2122 if (LocaleCompare(keyword,"x1") == 0)
2123 {
2124 svg_info->segment.x1=
2125 GetUserSpaceCoordinateValue(svg_info,1,value);
2126 break;
2127 }
2128 if (LocaleCompare(keyword,"x2") == 0)
2129 {
2130 svg_info->segment.x2=
2131 GetUserSpaceCoordinateValue(svg_info,1,value);
2132 break;
2133 }
2134 break;
2135 }
2136 case 'Y':
2137 case 'y':
2138 {
2139 if (LocaleCompare(keyword,"y") == 0)
2140 {
2141 svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
2142 break;
2143 }
2144 if (LocaleCompare(keyword,"y1") == 0)
2145 {
2146 svg_info->segment.y1=
2147 GetUserSpaceCoordinateValue(svg_info,-1,value);
2148 break;
2149 }
2150 if (LocaleCompare(keyword,"y2") == 0)
2151 {
2152 svg_info->segment.y2=
2153 GetUserSpaceCoordinateValue(svg_info,-1,value);
2154 break;
2155 }
2156 break;
2157 }
2158 default:
2159 break;
2160 }
2161 }
2162 if (LocaleCompare((const char *) name,"svg") == 0)
2163 {
2164 if (svg_info->document->encoding != (const xmlChar *) NULL)
cristyb51dff52011-05-19 16:55:47 +00002165 (void) FormatLocaleFile(svg_info->file,"encoding \"%s\"\n",
cristy3ed852e2009-09-05 21:47:34 +00002166 (const char *) svg_info->document->encoding);
2167 if (attributes != (const xmlChar **) NULL)
2168 {
2169 double
2170 sx,
cristy59ee19d2011-12-01 15:56:42 +00002171 sy,
2172 tx,
2173 ty;
cristy3ed852e2009-09-05 21:47:34 +00002174
2175 if ((svg_info->view_box.width == 0.0) ||
2176 (svg_info->view_box.height == 0.0))
2177 svg_info->view_box=svg_info->bounds;
cristybb503372010-05-27 20:51:26 +00002178 svg_info->width=(size_t) floor(svg_info->bounds.width+0.5);
2179 svg_info->height=(size_t) floor(svg_info->bounds.height+0.5);
cristyb51dff52011-05-19 16:55:47 +00002180 (void) FormatLocaleFile(svg_info->file,"viewbox 0 0 %.20g %.20g\n",
2181 (double) svg_info->width,(double) svg_info->height);
cristy3ed852e2009-09-05 21:47:34 +00002182 sx=(double) svg_info->width/svg_info->view_box.width;
2183 sy=(double) svg_info->height/svg_info->view_box.height;
cristy59ee19d2011-12-01 15:56:42 +00002184 tx=svg_info->view_box.x != 0.0 ? (double) -sx*svg_info->view_box.x :
2185 0.0;
2186 ty=svg_info->view_box.y != 0.0 ? (double) -sy*svg_info->view_box.y :
2187 0.0;
2188 (void) FormatLocaleFile(svg_info->file,"affine %g 0 0 %g %g %g\n",
2189 sx,sy,tx,ty);
cristy3ed852e2009-09-05 21:47:34 +00002190 }
2191 }
2192 (void) LogMagickEvent(CoderEvent,GetMagickModule()," )");
2193 units=DestroyString(units);
2194 if (color != (char *) NULL)
2195 color=DestroyString(color);
2196}
2197
2198static void SVGEndElement(void *context,const xmlChar *name)
2199{
2200 SVGInfo
2201 *svg_info;
2202
2203 /*
2204 Called when the end of an element has been detected.
2205 */
2206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2207 " SAX.endElement(%s)",name);
2208 svg_info=(SVGInfo *) context;
cristy13d07042010-11-21 20:56:18 +00002209 if (strchr((char *) name,':') != (char *) NULL)
cristy768b9302010-11-20 00:05:47 +00002210 {
2211 /*
2212 Skip over namespace.
2213 */
2214 for ( ; *name != ':'; name++) ;
2215 name++;
2216 }
cristy3ed852e2009-09-05 21:47:34 +00002217 switch (*name)
2218 {
2219 case 'C':
2220 case 'c':
2221 {
2222 if (LocaleCompare((const char *) name,"circle") == 0)
2223 {
cristyb51dff52011-05-19 16:55:47 +00002224 (void) FormatLocaleFile(svg_info->file,"circle %g,%g %g,%g\n",
cristy8cd5b312010-01-07 01:10:24 +00002225 svg_info->element.cx,svg_info->element.cy,svg_info->element.cx,
2226 svg_info->element.cy+svg_info->element.minor);
cristyb51dff52011-05-19 16:55:47 +00002227 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002228 break;
2229 }
2230 if (LocaleCompare((const char *) name,"clipPath") == 0)
2231 {
cristyb51dff52011-05-19 16:55:47 +00002232 (void) FormatLocaleFile(svg_info->file,"pop clip-path\n");
cristy3ed852e2009-09-05 21:47:34 +00002233 break;
2234 }
2235 break;
2236 }
2237 case 'D':
2238 case 'd':
2239 {
2240 if (LocaleCompare((const char *) name,"defs") == 0)
2241 {
cristyb51dff52011-05-19 16:55:47 +00002242 (void) FormatLocaleFile(svg_info->file,"pop defs\n");
cristy3ed852e2009-09-05 21:47:34 +00002243 break;
2244 }
2245 if (LocaleCompare((const char *) name,"desc") == 0)
2246 {
2247 register char
2248 *p;
2249
2250 if (*svg_info->text == '\0')
2251 break;
2252 (void) fputc('#',svg_info->file);
2253 for (p=svg_info->text; *p != '\0'; p++)
2254 {
2255 (void) fputc(*p,svg_info->file);
2256 if (*p == '\n')
2257 (void) fputc('#',svg_info->file);
2258 }
2259 (void) fputc('\n',svg_info->file);
2260 *svg_info->text='\0';
2261 break;
2262 }
2263 break;
2264 }
2265 case 'E':
2266 case 'e':
2267 {
2268 if (LocaleCompare((const char *) name,"ellipse") == 0)
2269 {
2270 double
2271 angle;
2272
2273 angle=svg_info->element.angle;
cristyb51dff52011-05-19 16:55:47 +00002274 (void) FormatLocaleFile(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
cristy3ed852e2009-09-05 21:47:34 +00002275 svg_info->element.cx,svg_info->element.cy,
2276 angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
2277 angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
cristyb51dff52011-05-19 16:55:47 +00002278 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002279 break;
2280 }
2281 break;
2282 }
2283 case 'G':
2284 case 'g':
2285 {
2286 if (LocaleCompare((const char *) name,"g") == 0)
2287 {
cristyb51dff52011-05-19 16:55:47 +00002288 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002289 break;
2290 }
2291 break;
2292 }
2293 case 'I':
2294 case 'i':
2295 {
2296 if (LocaleCompare((const char *) name,"image") == 0)
2297 {
cristyb51dff52011-05-19 16:55:47 +00002298 (void) FormatLocaleFile(svg_info->file,
2299 "image Over %g,%g %g,%g '%s'\n",svg_info->bounds.x,
2300 svg_info->bounds.y,svg_info->bounds.width,svg_info->bounds.height,
2301 svg_info->url);
2302 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002303 break;
2304 }
2305 break;
2306 }
2307 case 'L':
2308 case 'l':
2309 {
2310 if (LocaleCompare((const char *) name,"line") == 0)
2311 {
cristyb51dff52011-05-19 16:55:47 +00002312 (void) FormatLocaleFile(svg_info->file,"line %g,%g %g,%g\n",
cristy8cd5b312010-01-07 01:10:24 +00002313 svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
2314 svg_info->segment.y2);
cristyb51dff52011-05-19 16:55:47 +00002315 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002316 break;
2317 }
2318 if (LocaleCompare((const char *) name,"linearGradient") == 0)
2319 {
cristyb51dff52011-05-19 16:55:47 +00002320 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
cristy3ed852e2009-09-05 21:47:34 +00002321 break;
2322 }
2323 break;
2324 }
2325 case 'P':
2326 case 'p':
2327 {
2328 if (LocaleCompare((const char *) name,"pattern") == 0)
2329 {
cristyb51dff52011-05-19 16:55:47 +00002330 (void) FormatLocaleFile(svg_info->file,"pop pattern\n");
cristy3ed852e2009-09-05 21:47:34 +00002331 break;
2332 }
2333 if (LocaleCompare((const char *) name,"path") == 0)
2334 {
cristyb51dff52011-05-19 16:55:47 +00002335 (void) FormatLocaleFile(svg_info->file,"path '%s'\n",
2336 svg_info->vertices);
2337 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002338 break;
2339 }
2340 if (LocaleCompare((const char *) name,"polygon") == 0)
2341 {
cristyb51dff52011-05-19 16:55:47 +00002342 (void) FormatLocaleFile(svg_info->file,"polygon %s\n",
2343 svg_info->vertices);
2344 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002345 break;
2346 }
2347 if (LocaleCompare((const char *) name,"polyline") == 0)
2348 {
cristyb51dff52011-05-19 16:55:47 +00002349 (void) FormatLocaleFile(svg_info->file,"polyline %s\n",
2350 svg_info->vertices);
2351 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002352 break;
2353 }
2354 break;
2355 }
2356 case 'R':
2357 case 'r':
2358 {
2359 if (LocaleCompare((const char *) name,"radialGradient") == 0)
2360 {
cristyb51dff52011-05-19 16:55:47 +00002361 (void) FormatLocaleFile(svg_info->file,"pop gradient\n");
cristy3ed852e2009-09-05 21:47:34 +00002362 break;
2363 }
2364 if (LocaleCompare((const char *) name,"rect") == 0)
2365 {
2366 if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
2367 {
cristyb51dff52011-05-19 16:55:47 +00002368 (void) FormatLocaleFile(svg_info->file,"rectangle %g,%g %g,%g\n",
cristy3ed852e2009-09-05 21:47:34 +00002369 svg_info->bounds.x,svg_info->bounds.y,
2370 svg_info->bounds.x+svg_info->bounds.width,
2371 svg_info->bounds.y+svg_info->bounds.height);
cristyb51dff52011-05-19 16:55:47 +00002372 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002373 break;
2374 }
2375 if (svg_info->radius.x == 0.0)
2376 svg_info->radius.x=svg_info->radius.y;
2377 if (svg_info->radius.y == 0.0)
2378 svg_info->radius.y=svg_info->radius.x;
cristyb51dff52011-05-19 16:55:47 +00002379 (void) FormatLocaleFile(svg_info->file,
cristye7f51092010-01-17 00:39:37 +00002380 "roundRectangle %g,%g %g,%g %g,%g\n",
cristy3ed852e2009-09-05 21:47:34 +00002381 svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
2382 svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
2383 svg_info->radius.x,svg_info->radius.y);
2384 svg_info->radius.x=0.0;
2385 svg_info->radius.y=0.0;
cristyb51dff52011-05-19 16:55:47 +00002386 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002387 break;
2388 }
2389 break;
2390 }
2391 case 'S':
2392 case 's':
2393 {
2394 if (LocaleCompare((const char *) name,"stop") == 0)
2395 {
cristyb51dff52011-05-19 16:55:47 +00002396 (void) FormatLocaleFile(svg_info->file,"stop-color '%s' %s\n",
2397 svg_info->stop_color,svg_info->offset);
cristy3ed852e2009-09-05 21:47:34 +00002398 break;
2399 }
2400 if (LocaleCompare((const char *) name,"svg") == 0)
2401 {
cristyb51dff52011-05-19 16:55:47 +00002402 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002403 break;
2404 }
2405 break;
2406 }
2407 case 'T':
2408 case 't':
2409 {
2410 if (LocaleCompare((const char *) name,"text") == 0)
2411 {
2412 if (*svg_info->text != '\0')
2413 {
2414 char
2415 *text;
2416
2417 text=EscapeString(svg_info->text,'\'');
cristye8d7cc52011-11-24 18:56:11 +00002418 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
2419 svg_info->bounds.x,svg_info->bounds.y,text);
cristy3ed852e2009-09-05 21:47:34 +00002420 text=DestroyString(text);
2421 *svg_info->text='\0';
2422 }
cristyb51dff52011-05-19 16:55:47 +00002423 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002424 break;
2425 }
2426 if (LocaleCompare((const char *) name,"tspan") == 0)
2427 {
2428 if (*svg_info->text != '\0')
2429 {
2430 DrawInfo
2431 *draw_info;
2432
2433 TypeMetric
2434 metrics;
2435
2436 char
2437 *text;
2438
2439 text=EscapeString(svg_info->text,'\'');
cristyb51dff52011-05-19 16:55:47 +00002440 (void) FormatLocaleFile(svg_info->file,"text %g,%g '%s'\n",
cristy8cd5b312010-01-07 01:10:24 +00002441 svg_info->bounds.x,svg_info->bounds.y,text);
cristy3ed852e2009-09-05 21:47:34 +00002442 text=DestroyString(text);
2443 draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
2444 draw_info->pointsize=svg_info->pointsize;
2445 draw_info->text=AcquireString(svg_info->text);
2446 (void) ConcatenateString(&draw_info->text," ");
cristy5cbc0162011-08-29 00:36:28 +00002447 (void) GetTypeMetrics(svg_info->image,draw_info,&metrics,
2448 svg_info->exception);
cristy3ed852e2009-09-05 21:47:34 +00002449 svg_info->bounds.x+=metrics.width;
2450 draw_info=DestroyDrawInfo(draw_info);
2451 *svg_info->text='\0';
2452 }
cristyb51dff52011-05-19 16:55:47 +00002453 (void) FormatLocaleFile(svg_info->file,"pop graphic-context\n");
cristy3ed852e2009-09-05 21:47:34 +00002454 break;
2455 }
2456 if (LocaleCompare((const char *) name,"title") == 0)
2457 {
2458 if (*svg_info->text == '\0')
2459 break;
2460 (void) CloneString(&svg_info->title,svg_info->text);
2461 *svg_info->text='\0';
2462 break;
2463 }
2464 break;
2465 }
2466 default:
2467 break;
2468 }
2469 *svg_info->text='\0';
2470 (void) ResetMagickMemory(&svg_info->element,0,sizeof(svg_info->element));
2471 (void) ResetMagickMemory(&svg_info->segment,0,sizeof(svg_info->segment));
2472 svg_info->n--;
2473}
2474
2475static void SVGCharacters(void *context,const xmlChar *c,int length)
2476{
cristy6dc3b782011-11-08 19:24:00 +00002477 char
2478 *text;
2479
cristy3ed852e2009-09-05 21:47:34 +00002480 register char
2481 *p;
2482
cristybb503372010-05-27 20:51:26 +00002483 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002484 i;
2485
2486 SVGInfo
2487 *svg_info;
2488
2489 /*
2490 Receiving some characters from the parser.
2491 */
2492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002493 " SAX.characters(%s,%.20g)",c,(double) length);
cristy3ed852e2009-09-05 21:47:34 +00002494 svg_info=(SVGInfo *) context;
cristy6dc3b782011-11-08 19:24:00 +00002495 text=(char *) AcquireQuantumMemory(length+1,sizeof(*text));
2496 if (text == (char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002497 return;
cristy6dc3b782011-11-08 19:24:00 +00002498 p=text;
cristybb503372010-05-27 20:51:26 +00002499 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00002500 *p++=c[i];
2501 *p='\0';
cristy6dc3b782011-11-08 19:24:00 +00002502 StripString(text);
2503 if (svg_info->text == (char *) NULL)
2504 svg_info->text=text;
2505 else
2506 {
2507 (void) ConcatenateString(&svg_info->text,text);
2508 text=DestroyString(text);
2509 }
cristy3ed852e2009-09-05 21:47:34 +00002510}
2511
2512static void SVGReference(void *context,const xmlChar *name)
2513{
2514 SVGInfo
2515 *svg_info;
2516
2517 xmlParserCtxtPtr
2518 parser;
2519
2520 /*
2521 Called when an entity reference is detected.
2522 */
2523 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.reference(%s)",
2524 name);
2525 svg_info=(SVGInfo *) context;
2526 parser=svg_info->parser;
2527 if (parser == (xmlParserCtxtPtr) NULL)
2528 return;
2529 if (parser->node == (xmlNodePtr) NULL)
2530 return;
2531 if (*name == '#')
2532 (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
2533 else
2534 (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
2535}
2536
2537static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
2538{
2539 SVGInfo
2540 *svg_info;
2541
2542 /*
2543 Receiving some ignorable whitespaces from the parser.
2544 */
2545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2546 " SAX.ignorableWhitespace(%.30s, %d)",c,length);
2547 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +00002548 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +00002549}
2550
2551static void SVGProcessingInstructions(void *context,const xmlChar *target,
2552 const xmlChar *data)
2553{
2554 SVGInfo
2555 *svg_info;
2556
2557 /*
2558 A processing instruction has been parsed.
2559 */
2560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2561 " SAX.processingInstruction(%s, %s)",target,data);
2562 svg_info=(SVGInfo *) context;
cristyda16f162011-02-19 23:52:17 +00002563 (void) svg_info;
cristy3ed852e2009-09-05 21:47:34 +00002564}
2565
2566static void SVGComment(void *context,const xmlChar *value)
2567{
2568 SVGInfo
2569 *svg_info;
2570
2571 /*
2572 A comment has been parsed.
2573 */
2574 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.comment(%s)",
2575 value);
2576 svg_info=(SVGInfo *) context;
2577 if (svg_info->comment != (char *) NULL)
2578 (void) ConcatenateString(&svg_info->comment,"\n");
2579 (void) ConcatenateString(&svg_info->comment,(const char *) value);
2580}
2581
2582static void SVGWarning(void *context,const char *format,...)
2583{
2584 char
2585 *message,
cristy151b66d2015-04-15 10:50:31 +00002586 reason[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00002587
2588 SVGInfo
2589 *svg_info;
2590
2591 va_list
2592 operands;
2593
2594 /**
2595 Display and format a warning messages, gives file, line, position and
2596 extra parameters.
2597 */
2598 va_start(operands,format);
2599 svg_info=(SVGInfo *) context;
2600 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: ");
2601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2602#if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2603 (void) vsprintf(reason,format,operands);
2604#else
cristy151b66d2015-04-15 10:50:31 +00002605 (void) vsnprintf(reason,MagickPathExtent,format,operands);
cristy3ed852e2009-09-05 21:47:34 +00002606#endif
2607 message=GetExceptionMessage(errno);
2608 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),
2609 DelegateWarning,reason,"`%s`",message);
2610 message=DestroyString(message);
2611 va_end(operands);
2612}
2613
2614static void SVGError(void *context,const char *format,...)
2615{
2616 char
2617 *message,
cristy151b66d2015-04-15 10:50:31 +00002618 reason[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00002619
2620 SVGInfo
2621 *svg_info;
2622
2623 va_list
2624 operands;
2625
2626 /*
2627 Display and format a error formats, gives file, line, position and
2628 extra parameters.
2629 */
2630 va_start(operands,format);
2631 svg_info=(SVGInfo *) context;
2632 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: ");
2633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
2634#if !defined(MAGICKCORE_HAVE_VSNPRINTF)
2635 (void) vsprintf(reason,format,operands);
2636#else
cristy151b66d2015-04-15 10:50:31 +00002637 (void) vsnprintf(reason,MagickPathExtent,format,operands);
cristy3ed852e2009-09-05 21:47:34 +00002638#endif
2639 message=GetExceptionMessage(errno);
2640 (void) ThrowMagickException(svg_info->exception,GetMagickModule(),CoderError,
2641 reason,"`%s`",message);
2642 message=DestroyString(message);
2643 va_end(operands);
2644}
2645
2646static void SVGCDataBlock(void *context,const xmlChar *value,int length)
2647{
2648 SVGInfo
2649 *svg_info;
2650
2651 xmlNodePtr
2652 child;
2653
2654 xmlParserCtxtPtr
2655 parser;
2656
2657 /*
2658 Called when a pcdata block has been parsed.
2659 */
2660 (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.pcdata(%s, %d)",
2661 value,length);
2662 svg_info=(SVGInfo *) context;
2663 parser=svg_info->parser;
2664 child=xmlGetLastChild(parser->node);
2665 if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
2666 {
2667 xmlTextConcat(child,value,length);
2668 return;
2669 }
2670 (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
2671}
2672
2673static void SVGExternalSubset(void *context,const xmlChar *name,
2674 const xmlChar *external_id,const xmlChar *system_id)
2675{
2676 SVGInfo
2677 *svg_info;
2678
2679 xmlParserCtxt
2680 parser_context;
2681
2682 xmlParserCtxtPtr
2683 parser;
2684
2685 xmlParserInputPtr
2686 input;
2687
2688 /*
2689 Does this document has an external subset?
2690 */
2691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2692 " SAX.externalSubset(%s, %s, %s)",name,
2693 (external_id != (const xmlChar *) NULL ? (const char *) external_id : "none"),
2694 (system_id != (const xmlChar *) NULL ? (const char *) system_id : "none"));
2695 svg_info=(SVGInfo *) context;
2696 parser=svg_info->parser;
2697 if (((external_id == NULL) && (system_id == NULL)) ||
2698 ((parser->validate == 0) || (parser->wellFormed == 0) ||
2699 (svg_info->document == 0)))
2700 return;
2701 input=SVGResolveEntity(context,external_id,system_id);
2702 if (input == NULL)
2703 return;
2704 (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
2705 parser_context=(*parser);
2706 parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
2707 if (parser->inputTab == (xmlParserInputPtr *) NULL)
2708 {
2709 parser->errNo=XML_ERR_NO_MEMORY;
2710 parser->input=parser_context.input;
2711 parser->inputNr=parser_context.inputNr;
2712 parser->inputMax=parser_context.inputMax;
2713 parser->inputTab=parser_context.inputTab;
2714 return;
2715 }
2716 parser->inputNr=0;
2717 parser->inputMax=5;
2718 parser->input=NULL;
2719 xmlPushInput(parser,input);
2720 (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
2721 if (input->filename == (char *) NULL)
2722 input->filename=(char *) xmlStrdup(system_id);
2723 input->line=1;
2724 input->col=1;
2725 input->base=parser->input->cur;
2726 input->cur=parser->input->cur;
2727 input->free=NULL;
2728 xmlParseExternalSubset(parser,external_id,system_id);
2729 while (parser->inputNr > 1)
2730 (void) xmlPopInput(parser);
2731 xmlFreeInputStream(parser->input);
2732 xmlFree(parser->inputTab);
2733 parser->input=parser_context.input;
2734 parser->inputNr=parser_context.inputNr;
2735 parser->inputMax=parser_context.inputMax;
2736 parser->inputTab=parser_context.inputTab;
2737}
2738
cristy3ed852e2009-09-05 21:47:34 +00002739#if defined(__cplusplus) || defined(c_plusplus)
2740}
2741#endif
2742
glennrpd5d43c22012-09-25 14:48:23 +00002743/*
2744 Static declarations.
2745*/
2746static char
cristyca559d82013-03-17 13:44:24 +00002747 SVGDensityGeometry[] = "90.0x90.0";
dirk200dd432015-02-08 00:45:00 +00002748
glennrpd5d43c22012-09-25 14:48:23 +00002749
cristy3ed852e2009-09-05 21:47:34 +00002750static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2751{
cristy3ed852e2009-09-05 21:47:34 +00002752 char
cristy151b66d2015-04-15 10:50:31 +00002753 filename[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00002754
2755 FILE
2756 *file;
2757
2758 Image
2759 *image;
2760
2761 int
2762 status,
2763 unique_file;
2764
cristybb503372010-05-27 20:51:26 +00002765 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002766 n;
2767
2768 SVGInfo
2769 *svg_info;
2770
2771 unsigned char
cristy151b66d2015-04-15 10:50:31 +00002772 message[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00002773
cristy1f9e1ed2009-11-18 04:09:38 +00002774 xmlSAXHandler
cristy5f6f01c2009-11-19 19:36:42 +00002775 sax_modules;
cristy1f9e1ed2009-11-18 04:09:38 +00002776
cristy3ed852e2009-09-05 21:47:34 +00002777 xmlSAXHandlerPtr
2778 sax_handler;
2779
2780 /*
2781 Open image file.
2782 */
2783 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002784 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002785 assert(exception != (ExceptionInfo *) NULL);
2786 if (image_info->debug != MagickFalse)
2787 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2788 image_info->filename);
cristye1c94d92015-06-28 12:16:33 +00002789 assert(exception->signature == MagickCoreSignature);
cristy9950d572011-10-01 18:22:35 +00002790 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00002791 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2792 if (status == MagickFalse)
2793 {
2794 image=DestroyImageList(image);
2795 return((Image *) NULL);
2796 }
cristy7da596e2012-08-15 11:36:05 +00002797 if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
2798 {
2799 GeometryInfo
2800 geometry_info;
2801
2802 int
2803 flags;
2804
2805 flags=ParseGeometry(SVGDensityGeometry,&geometry_info);
2806 image->resolution.x=geometry_info.rho;
2807 image->resolution.y=geometry_info.sigma;
2808 if ((flags & SigmaValue) == 0)
2809 image->resolution.y=image->resolution.x;
2810 }
cristy3ed852e2009-09-05 21:47:34 +00002811 if (LocaleCompare(image_info->magick,"MSVG") != 0)
2812 {
cristy1164d5f2012-08-15 00:58:25 +00002813 const DelegateInfo
2814 *delegate_info;
2815
2816 delegate_info=GetDelegateInfo("svg:decode",(char *) NULL,exception);
2817 if (delegate_info != (const DelegateInfo *) NULL)
2818 {
2819 char
cristy151b66d2015-04-15 10:50:31 +00002820 background[MagickPathExtent],
2821 command[MagickPathExtent],
cristy51816562015-04-12 13:18:38 +00002822 *density,
cristy151b66d2015-04-15 10:50:31 +00002823 input_filename[MagickPathExtent],
2824 opacity[MagickPathExtent],
2825 output_filename[MagickPathExtent],
2826 unique[MagickPathExtent];
cristy1164d5f2012-08-15 00:58:25 +00002827
2828 int
2829 status;
2830
cristya97bc002014-10-30 20:19:53 +00002831 struct stat
2832 attributes;
2833
cristy1164d5f2012-08-15 00:58:25 +00002834 /*
cristyfa684be2015-02-08 13:43:46 +00002835 Our best hope of compliance with the SVG standard.
cristy1164d5f2012-08-15 00:58:25 +00002836 */
cristyd8e3e462014-11-04 12:19:17 +00002837 status=AcquireUniqueSymbolicLink(image->filename,input_filename);
2838 (void) AcquireUniqueFilename(output_filename);
cristy1164d5f2012-08-15 00:58:25 +00002839 (void) AcquireUniqueFilename(unique);
cristy51816562015-04-12 13:18:38 +00002840 density=AcquireString("");
cristy151b66d2015-04-15 10:50:31 +00002841 (void) FormatLocaleString(density,MagickPathExtent,"%.20g,%.20g",
cristy1164d5f2012-08-15 00:58:25 +00002842 image->resolution.x,image->resolution.y);
cristy151b66d2015-04-15 10:50:31 +00002843 (void) FormatLocaleString(background,MagickPathExtent,
cristy1164d5f2012-08-15 00:58:25 +00002844 "rgb(%.20g%%,%.20g%%,%.20g%%)",
2845 100.0*QuantumScale*image->background_color.red,
2846 100.0*QuantumScale*image->background_color.green,
2847 100.0*QuantumScale*image->background_color.blue);
cristy151b66d2015-04-15 10:50:31 +00002848 (void) FormatLocaleString(opacity,MagickPathExtent,"%.20g",
cristy1164d5f2012-08-15 00:58:25 +00002849 QuantumScale*image->background_color.alpha);
cristy151b66d2015-04-15 10:50:31 +00002850 (void) FormatLocaleString(command,MagickPathExtent,GetDelegateCommands(
cristyd8e3e462014-11-04 12:19:17 +00002851 delegate_info),input_filename,output_filename,density,background,
2852 opacity,unique);
cristy51816562015-04-12 13:18:38 +00002853 density=DestroyString(density);
cristydfc19b62014-10-17 22:52:24 +00002854 status=ExternalDelegateCommand(MagickFalse,image_info->verbose,
2855 command,(char *) NULL,exception);
cristy1164d5f2012-08-15 00:58:25 +00002856 (void) RelinquishUniqueFileResource(unique);
cristyd8e3e462014-11-04 12:19:17 +00002857 (void) RelinquishUniqueFileResource(input_filename);
cristy488eb402014-11-24 15:32:06 +00002858 if ((status == 0) && (stat(output_filename,&attributes) == 0) &&
cristyf201ba62015-07-05 13:54:28 +00002859 (attributes.st_size > 0))
cristy1164d5f2012-08-15 00:58:25 +00002860 {
cristyfa684be2015-02-08 13:43:46 +00002861 Image
2862 *svg_image;
2863
cristy1164d5f2012-08-15 00:58:25 +00002864 ImageInfo
2865 *read_info;
2866
2867 read_info=CloneImageInfo(image_info);
cristyd8e3e462014-11-04 12:19:17 +00002868 (void) CopyMagickString(read_info->filename,output_filename,
cristy151b66d2015-04-15 10:50:31 +00002869 MagickPathExtent);
cristyfa684be2015-02-08 13:43:46 +00002870 svg_image=ReadImage(read_info,exception);
cristy1164d5f2012-08-15 00:58:25 +00002871 read_info=DestroyImageInfo(read_info);
cristyd8e3e462014-11-04 12:19:17 +00002872 (void) RelinquishUniqueFileResource(output_filename);
cristyfa684be2015-02-08 13:43:46 +00002873 if (svg_image != (Image *) NULL)
2874 {
2875 image=DestroyImage(image);
2876 return(svg_image);
2877 }
cristy1164d5f2012-08-15 00:58:25 +00002878 }
cristyd8e3e462014-11-04 12:19:17 +00002879 (void) RelinquishUniqueFileResource(output_filename);
cristy1164d5f2012-08-15 00:58:25 +00002880 }
2881 {
cristy3ed852e2009-09-05 21:47:34 +00002882#if defined(MAGICKCORE_RSVG_DELEGATE)
2883#if defined(MAGICKCORE_CAIRO_DELEGATE)
cristy1164d5f2012-08-15 00:58:25 +00002884 cairo_surface_t
2885 *cairo_surface;
cristy3ed852e2009-09-05 21:47:34 +00002886
cristy1164d5f2012-08-15 00:58:25 +00002887 cairo_t
2888 *cairo_image;
cristy3ed852e2009-09-05 21:47:34 +00002889
cristy5def2032013-06-30 17:44:08 +00002890 MemoryInfo
2891 *pixel_info;
2892
cristy1164d5f2012-08-15 00:58:25 +00002893 register unsigned char
2894 *p;
cristy3ed852e2009-09-05 21:47:34 +00002895
cristy1164d5f2012-08-15 00:58:25 +00002896 RsvgDimensionData
2897 dimension_info;
cristy3ed852e2009-09-05 21:47:34 +00002898
cristy1164d5f2012-08-15 00:58:25 +00002899 unsigned char
2900 *pixels;
cristy3ed852e2009-09-05 21:47:34 +00002901
2902#else
cristy1164d5f2012-08-15 00:58:25 +00002903 GdkPixbuf
cristy5def2032013-06-30 17:44:08 +00002904 *pixel_buffer;
cristy3ed852e2009-09-05 21:47:34 +00002905
cristy1164d5f2012-08-15 00:58:25 +00002906 register const guchar
2907 *p;
cristy3ed852e2009-09-05 21:47:34 +00002908#endif
2909
cristy1164d5f2012-08-15 00:58:25 +00002910 GError
2911 *error;
cristy3ed852e2009-09-05 21:47:34 +00002912
cristy1164d5f2012-08-15 00:58:25 +00002913 ssize_t
2914 y;
cristy3ed852e2009-09-05 21:47:34 +00002915
cristy1164d5f2012-08-15 00:58:25 +00002916 PixelInfo
2917 fill_color;
cristy3ed852e2009-09-05 21:47:34 +00002918
cristy1164d5f2012-08-15 00:58:25 +00002919 register ssize_t
2920 x;
cristy3ed852e2009-09-05 21:47:34 +00002921
cristy1164d5f2012-08-15 00:58:25 +00002922 register Quantum
2923 *q;
cristy3ed852e2009-09-05 21:47:34 +00002924
cristy1164d5f2012-08-15 00:58:25 +00002925 RsvgHandle
2926 *svg_handle;
cristy3ed852e2009-09-05 21:47:34 +00002927
cristy1164d5f2012-08-15 00:58:25 +00002928 svg_handle=rsvg_handle_new();
2929 if (svg_handle == (RsvgHandle *) NULL)
2930 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2931 rsvg_handle_set_base_uri(svg_handle,image_info->filename);
dirk8355e652014-01-02 08:14:54 +00002932 if ((image->resolution.x != 90.0) && (image->resolution.y != 90.0))
2933 rsvg_handle_set_dpi_x_y(svg_handle,image->resolution.x,
2934 image->resolution.y);
cristy151b66d2015-04-15 10:50:31 +00002935 while ((n=ReadBlob(image,MagickPathExtent,message)) != 0)
cristy1164d5f2012-08-15 00:58:25 +00002936 {
2937 error=(GError *) NULL;
2938 (void) rsvg_handle_write(svg_handle,message,n,&error);
2939 if (error != (GError *) NULL)
2940 g_error_free(error);
2941 }
cristy3ed852e2009-09-05 21:47:34 +00002942 error=(GError *) NULL;
cristy1164d5f2012-08-15 00:58:25 +00002943 rsvg_handle_close(svg_handle,&error);
cristy3ed852e2009-09-05 21:47:34 +00002944 if (error != (GError *) NULL)
2945 g_error_free(error);
cristy3ed852e2009-09-05 21:47:34 +00002946#if defined(MAGICKCORE_CAIRO_DELEGATE)
cristy1164d5f2012-08-15 00:58:25 +00002947 rsvg_handle_get_dimensions(svg_handle,&dimension_info);
dirk8355e652014-01-02 08:14:54 +00002948 image->columns=image->resolution.x*dimension_info.width/90.0;
2949 image->rows=image->resolution.y*dimension_info.height/90.0;
cristy5def2032013-06-30 17:44:08 +00002950 pixel_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002951#else
cristy5def2032013-06-30 17:44:08 +00002952 pixel_buffer=rsvg_handle_get_pixbuf(svg_handle);
cristy1164d5f2012-08-15 00:58:25 +00002953 rsvg_handle_free(svg_handle);
cristy5def2032013-06-30 17:44:08 +00002954 image->columns=gdk_pixbuf_get_width(pixel_buffer);
2955 image->rows=gdk_pixbuf_get_height(pixel_buffer);
cristy3ed852e2009-09-05 21:47:34 +00002956#endif
cristyacabb842014-12-14 23:36:33 +00002957 status=SetImageExtent(image,image->columns,image->rows,exception);
2958 if (status == MagickFalse)
2959 return(DestroyImageList(image));
cristy8a46d822012-08-28 23:32:39 +00002960 image->alpha_trait=BlendPixelTrait;
cristy1164d5f2012-08-15 00:58:25 +00002961 SetImageProperty(image,"svg:base-uri",
2962 rsvg_handle_get_base_uri(svg_handle),exception);
2963 if ((image->columns == 0) || (image->rows == 0))
2964 {
cristy3ed852e2009-09-05 21:47:34 +00002965#if !defined(MAGICKCORE_CAIRO_DELEGATE)
cristy5def2032013-06-30 17:44:08 +00002966 g_object_unref(G_OBJECT(pixel_buffer));
cristy3ed852e2009-09-05 21:47:34 +00002967#endif
cristy1164d5f2012-08-15 00:58:25 +00002968 g_object_unref(svg_handle);
2969 ThrowReaderException(MissingDelegateError,
2970 "NoDecodeDelegateForThisImageFormat");
2971 }
2972 if (image_info->ping == MagickFalse)
2973 {
dirkcd2f1522014-10-11 08:11:08 +00002974#if defined(MAGICKCORE_CAIRO_DELEGATE)
cristy1164d5f2012-08-15 00:58:25 +00002975 size_t
2976 stride;
cristy20d6cfe2012-05-24 00:39:22 +00002977
cristy1164d5f2012-08-15 00:58:25 +00002978 stride=4*image->columns;
cristya0e0a2d2012-05-24 13:07:10 +00002979#if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
cristy1164d5f2012-08-15 00:58:25 +00002980 stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
cristy50631bc2014-07-25 16:16:54 +00002981 (int) image->columns);
cristya0e0a2d2012-05-24 13:07:10 +00002982#endif
cristy5def2032013-06-30 17:44:08 +00002983 pixel_info=AcquireVirtualMemory(stride,image->rows*sizeof(*pixels));
2984 if (pixel_info == (MemoryInfo *) NULL)
cristye391a852009-11-21 14:50:33 +00002985 {
cristy1164d5f2012-08-15 00:58:25 +00002986 g_object_unref(svg_handle);
2987 ThrowReaderException(ResourceLimitError,
2988 "MemoryAllocationFailed");
cristye391a852009-11-21 14:50:33 +00002989 }
cristy5def2032013-06-30 17:44:08 +00002990 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
cristye391a852009-11-21 14:50:33 +00002991#endif
cristy1164d5f2012-08-15 00:58:25 +00002992 (void) SetImageBackgroundColor(image,exception);
2993#if defined(MAGICKCORE_CAIRO_DELEGATE)
2994 cairo_surface=cairo_image_surface_create_for_data(pixels,
cristy50631bc2014-07-25 16:16:54 +00002995 CAIRO_FORMAT_ARGB32,(int) image->columns,(int) image->rows,(int)
2996 stride);
cristy1164d5f2012-08-15 00:58:25 +00002997 if (cairo_surface == (cairo_surface_t *) NULL)
cristye391a852009-11-21 14:50:33 +00002998 {
cristy5def2032013-06-30 17:44:08 +00002999 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy1164d5f2012-08-15 00:58:25 +00003000 g_object_unref(svg_handle);
3001 ThrowReaderException(ResourceLimitError,
3002 "MemoryAllocationFailed");
cristye391a852009-11-21 14:50:33 +00003003 }
cristy1164d5f2012-08-15 00:58:25 +00003004 cairo_image=cairo_create(cairo_surface);
3005 cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
3006 cairo_paint(cairo_image);
3007 cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
dirk2b629d62014-01-07 22:30:15 +00003008 cairo_scale(cairo_image,image->resolution.x/90.0,
3009 image->resolution.y/90.0);
cristy1164d5f2012-08-15 00:58:25 +00003010 rsvg_handle_render_cairo(svg_handle,cairo_image);
3011 cairo_destroy(cairo_image);
3012 cairo_surface_destroy(cairo_surface);
3013 g_object_unref(svg_handle);
3014 p=pixels;
3015#else
cristy5def2032013-06-30 17:44:08 +00003016 p=gdk_pixbuf_get_pixels(pixel_buffer);
cristy1164d5f2012-08-15 00:58:25 +00003017#endif
3018 GetPixelInfo(image,&fill_color);
3019 for (y=0; y < (ssize_t) image->rows; y++)
3020 {
3021 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3022 if (q == (Quantum *) NULL)
3023 break;
3024 for (x=0; x < (ssize_t) image->columns; x++)
3025 {
3026#if defined(MAGICKCORE_CAIRO_DELEGATE)
3027 fill_color.blue=ScaleCharToQuantum(*p++);
3028 fill_color.green=ScaleCharToQuantum(*p++);
3029 fill_color.red=ScaleCharToQuantum(*p++);
3030#else
3031 fill_color.red=ScaleCharToQuantum(*p++);
3032 fill_color.green=ScaleCharToQuantum(*p++);
3033 fill_color.blue=ScaleCharToQuantum(*p++);
3034#endif
3035 fill_color.alpha=ScaleCharToQuantum(*p++);
3036#if defined(MAGICKCORE_CAIRO_DELEGATE)
3037 {
3038 double
3039 gamma;
cristyacff16a2012-08-16 00:21:35 +00003040
cristy1164d5f2012-08-15 00:58:25 +00003041 gamma=QuantumScale*fill_color.alpha;
cristy3e3ec3a2012-11-03 23:11:06 +00003042 gamma=PerceptibleReciprocal(gamma);
cristy1164d5f2012-08-15 00:58:25 +00003043 fill_color.blue*=gamma;
3044 fill_color.green*=gamma;
3045 fill_color.red*=gamma;
3046 }
3047#endif
cristyacff16a2012-08-16 00:21:35 +00003048 CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
3049 GetPixelAlpha(image,q),q);
cristy1164d5f2012-08-15 00:58:25 +00003050 q+=GetPixelChannels(image);
3051 }
3052 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3053 break;
3054 if (image->previous == (Image *) NULL)
3055 {
3056 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType)
3057 y,image->rows);
3058 if (status == MagickFalse)
3059 break;
3060 }
3061 }
cristye391a852009-11-21 14:50:33 +00003062 }
cristye391a852009-11-21 14:50:33 +00003063#if defined(MAGICKCORE_CAIRO_DELEGATE)
cristy5def2032013-06-30 17:44:08 +00003064 if (pixel_info != (MemoryInfo *) NULL)
3065 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00003066#else
cristy5def2032013-06-30 17:44:08 +00003067 g_object_unref(G_OBJECT(pixel_buffer));
cristy3ed852e2009-09-05 21:47:34 +00003068#endif
cristy1164d5f2012-08-15 00:58:25 +00003069 (void) CloseBlob(image);
3070 return(GetFirstImageInList(image));
cristy3ed852e2009-09-05 21:47:34 +00003071#endif
cristy1164d5f2012-08-15 00:58:25 +00003072 }
cristy3ed852e2009-09-05 21:47:34 +00003073 }
3074 /*
3075 Open draw file.
3076 */
3077 file=(FILE *) NULL;
3078 unique_file=AcquireUniqueFileResource(filename);
3079 if (unique_file != -1)
3080 file=fdopen(unique_file,"w");
3081 if ((unique_file == -1) || (file == (FILE *) NULL))
3082 {
cristy151b66d2015-04-15 10:50:31 +00003083 (void) CopyMagickString(image->filename,filename,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00003084 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
3085 image->filename);
3086 image=DestroyImageList(image);
3087 return((Image *) NULL);
3088 }
3089 /*
3090 Parse SVG file.
3091 */
3092 svg_info=AcquireSVGInfo();
3093 if (svg_info == (SVGInfo *) NULL)
cristy100a0562014-04-18 01:27:37 +00003094 {
3095 (void) fclose(file);
3096 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3097 }
cristy3ed852e2009-09-05 21:47:34 +00003098 svg_info->file=file;
3099 svg_info->exception=exception;
3100 svg_info->image=image;
3101 svg_info->image_info=image_info;
3102 svg_info->bounds.width=image->columns;
3103 svg_info->bounds.height=image->rows;
3104 if (image_info->size != (char *) NULL)
3105 (void) CloneString(&svg_info->size,image_info->size);
3106 if (image->debug != MagickFalse)
3107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
cristy3ed852e2009-09-05 21:47:34 +00003108 (void) xmlSubstituteEntitiesDefault(1);
cristy5f6f01c2009-11-19 19:36:42 +00003109 (void) ResetMagickMemory(&sax_modules,0,sizeof(sax_modules));
3110 sax_modules.internalSubset=SVGInternalSubset;
3111 sax_modules.isStandalone=SVGIsStandalone;
3112 sax_modules.hasInternalSubset=SVGHasInternalSubset;
3113 sax_modules.hasExternalSubset=SVGHasExternalSubset;
3114 sax_modules.resolveEntity=SVGResolveEntity;
3115 sax_modules.getEntity=SVGGetEntity;
3116 sax_modules.entityDecl=SVGEntityDeclaration;
3117 sax_modules.notationDecl=SVGNotationDeclaration;
3118 sax_modules.attributeDecl=SVGAttributeDeclaration;
3119 sax_modules.elementDecl=SVGElementDeclaration;
3120 sax_modules.unparsedEntityDecl=SVGUnparsedEntityDeclaration;
3121 sax_modules.setDocumentLocator=SVGSetDocumentLocator;
3122 sax_modules.startDocument=SVGStartDocument;
3123 sax_modules.endDocument=SVGEndDocument;
3124 sax_modules.startElement=SVGStartElement;
3125 sax_modules.endElement=SVGEndElement;
3126 sax_modules.reference=SVGReference;
3127 sax_modules.characters=SVGCharacters;
3128 sax_modules.ignorableWhitespace=SVGIgnorableWhitespace;
3129 sax_modules.processingInstruction=SVGProcessingInstructions;
3130 sax_modules.comment=SVGComment;
3131 sax_modules.warning=SVGWarning;
3132 sax_modules.error=SVGError;
3133 sax_modules.fatalError=SVGError;
3134 sax_modules.getParameterEntity=SVGGetParameterEntity;
3135 sax_modules.cdataBlock=SVGCDataBlock;
3136 sax_modules.externalSubset=SVGExternalSubset;
3137 sax_handler=(&sax_modules);
cristy151b66d2015-04-15 10:50:31 +00003138 n=ReadBlob(image,MagickPathExtent,message);
cristy3ed852e2009-09-05 21:47:34 +00003139 if (n > 0)
3140 {
3141 svg_info->parser=xmlCreatePushParserCtxt(sax_handler,svg_info,(char *)
3142 message,n,image->filename);
cristy151b66d2015-04-15 10:50:31 +00003143 while ((n=ReadBlob(image,MagickPathExtent,message)) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003144 {
3145 status=xmlParseChunk(svg_info->parser,(char *) message,(int) n,0);
3146 if (status != 0)
3147 break;
3148 }
3149 }
3150 (void) xmlParseChunk(svg_info->parser,(char *) message,0,1);
3151 xmlFreeParserCtxt(svg_info->parser);
3152 if (image->debug != MagickFalse)
3153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
cristy3ed852e2009-09-05 21:47:34 +00003154 (void) fclose(file);
3155 (void) CloseBlob(image);
3156 image->columns=svg_info->width;
3157 image->rows=svg_info->height;
3158 if (exception->severity >= ErrorException)
3159 {
3160 image=DestroyImage(image);
3161 return((Image *) NULL);
3162 }
3163 if (image_info->ping == MagickFalse)
3164 {
3165 ImageInfo
3166 *read_info;
3167
3168 /*
3169 Draw image.
3170 */
3171 image=DestroyImage(image);
3172 image=(Image *) NULL;
3173 read_info=CloneImageInfo(image_info);
3174 SetImageInfoBlob(read_info,(void *) NULL,0);
3175 if (read_info->density != (char *) NULL)
3176 read_info->density=DestroyString(read_info->density);
cristy151b66d2015-04-15 10:50:31 +00003177 (void) FormatLocaleString(read_info->filename,MagickPathExtent,"mvg:%s",
cristy3ed852e2009-09-05 21:47:34 +00003178 filename);
3179 image=ReadImage(read_info,exception);
3180 read_info=DestroyImageInfo(read_info);
3181 if (image != (Image *) NULL)
3182 (void) CopyMagickString(image->filename,image_info->filename,
cristy151b66d2015-04-15 10:50:31 +00003183 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00003184 }
3185 /*
3186 Relinquish resources.
3187 */
3188 if (image != (Image *) NULL)
3189 {
3190 if (svg_info->title != (char *) NULL)
cristyd15e6592011-10-15 00:13:06 +00003191 (void) SetImageProperty(image,"svg:title",svg_info->title,exception);
cristy3ed852e2009-09-05 21:47:34 +00003192 if (svg_info->comment != (char *) NULL)
cristyd15e6592011-10-15 00:13:06 +00003193 (void) SetImageProperty(image,"svg:comment",svg_info->comment,
3194 exception);
cristy3ed852e2009-09-05 21:47:34 +00003195 }
3196 svg_info=DestroySVGInfo(svg_info);
3197 (void) RelinquishUniqueFileResource(filename);
3198 return(GetFirstImageInList(image));
3199}
3200#endif
cristyfa684be2015-02-08 13:43:46 +00003201
cristy3ed852e2009-09-05 21:47:34 +00003202/*
3203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3204% %
3205% %
3206% %
3207% R e g i s t e r S V G I m a g e %
3208% %
3209% %
3210% %
3211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3212%
3213% RegisterSVGImage() adds attributes for the SVG image format to
3214% the list of supported formats. The attributes include the image format
3215% tag, a method to read and/or write the format, whether the format
3216% supports the saving of more than one frame to the same file or blob,
3217% whether the format supports native in-memory I/O, and a brief
3218% description of the format.
3219%
3220% The format of the RegisterSVGImage method is:
3221%
cristybb503372010-05-27 20:51:26 +00003222% size_t RegisterSVGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00003223%
3224*/
cristybb503372010-05-27 20:51:26 +00003225ModuleExport size_t RegisterSVGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00003226{
3227 char
cristy151b66d2015-04-15 10:50:31 +00003228 version[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00003229
3230 MagickInfo
3231 *entry;
3232
3233 *version='\0';
3234#if defined(LIBXML_DOTTED_VERSION)
cristy151b66d2015-04-15 10:50:31 +00003235 (void) CopyMagickString(version,"XML " LIBXML_DOTTED_VERSION,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00003236#endif
3237#if defined(MAGICKCORE_RSVG_DELEGATE)
cristya3afd2c2013-08-31 20:26:57 +00003238#if !GLIB_CHECK_VERSION(2,35,0)
cristy7febf992013-03-25 12:01:32 +00003239 g_type_init();
cristyc3eda392013-04-03 00:22:13 +00003240#endif
cristyfbd03fe2013-08-29 13:47:42 +00003241#if defined(MAGICKCORE_XML_DELEGATE)
3242 xmlInitParser();
3243#endif
cristy151b66d2015-04-15 10:50:31 +00003244 (void) FormatLocaleString(version,MagickPathExtent,"RSVG %d.%d.%d",
cristy3ed852e2009-09-05 21:47:34 +00003245 LIBRSVG_MAJOR_VERSION,LIBRSVG_MINOR_VERSION,LIBRSVG_MICRO_VERSION);
3246#endif
dirk06b627a2015-04-06 18:59:17 +00003247 entry=AcquireMagickInfo("SVG","SVG","Scalable Vector Graphics");
cristy3ed852e2009-09-05 21:47:34 +00003248#if defined(MAGICKCORE_XML_DELEGATE)
3249 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3250#endif
3251 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
dirk08e9a112015-02-22 01:51:41 +00003252 entry->flags^=CoderBlobSupportFlag;
cristy99872d02014-02-05 12:26:14 +00003253 entry->mime_type=ConstantString("image/svg+xml");
cristy3ed852e2009-09-05 21:47:34 +00003254 if (*version != '\0')
3255 entry->version=ConstantString(version);
3256 entry->magick=(IsImageFormatHandler *) IsSVG;
cristy3ed852e2009-09-05 21:47:34 +00003257 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00003258 entry=AcquireMagickInfo("SVG","SVGZ","Compressed Scalable Vector Graphics");
cristy3ed852e2009-09-05 21:47:34 +00003259#if defined(MAGICKCORE_XML_DELEGATE)
3260 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3261#endif
3262 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
dirk08e9a112015-02-22 01:51:41 +00003263 entry->flags^=CoderBlobSupportFlag;
cristy99872d02014-02-05 12:26:14 +00003264 entry->mime_type=ConstantString("image/svg+xml");
cristy3ed852e2009-09-05 21:47:34 +00003265 if (*version != '\0')
3266 entry->version=ConstantString(version);
3267 entry->magick=(IsImageFormatHandler *) IsSVG;
cristy3ed852e2009-09-05 21:47:34 +00003268 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00003269 entry=AcquireMagickInfo("SVG","MSVG",
3270 "ImageMagick's own SVG internal renderer");
cristy3ed852e2009-09-05 21:47:34 +00003271#if defined(MAGICKCORE_XML_DELEGATE)
3272 entry->decoder=(DecodeImageHandler *) ReadSVGImage;
3273#endif
3274 entry->encoder=(EncodeImageHandler *) WriteSVGImage;
dirk08e9a112015-02-22 01:51:41 +00003275 entry->flags^=CoderBlobSupportFlag;
cristy3ed852e2009-09-05 21:47:34 +00003276 entry->magick=(IsImageFormatHandler *) IsSVG;
cristy3ed852e2009-09-05 21:47:34 +00003277 (void) RegisterMagickInfo(entry);
3278 return(MagickImageCoderSignature);
3279}
cristyfa684be2015-02-08 13:43:46 +00003280
cristy3ed852e2009-09-05 21:47:34 +00003281/*
3282%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3283% %
3284% %
3285% %
3286% U n r e g i s t e r S V G I m a g e %
3287% %
3288% %
3289% %
3290%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3291%
3292% UnregisterSVGImage() removes format registrations made by the
3293% SVG module from the list of supported formats.
3294%
3295% The format of the UnregisterSVGImage method is:
3296%
3297% UnregisterSVGImage(void)
3298%
3299*/
3300ModuleExport void UnregisterSVGImage(void)
3301{
3302 (void) UnregisterMagickInfo("SVGZ");
3303 (void) UnregisterMagickInfo("SVG");
3304 (void) UnregisterMagickInfo("MSVG");
cristyfbd03fe2013-08-29 13:47:42 +00003305#if defined(MAGICKCORE_XML_DELEGATE)
3306 xmlCleanupParser();
3307#endif
cristy3ed852e2009-09-05 21:47:34 +00003308}
cristyfa684be2015-02-08 13:43:46 +00003309
cristy3ed852e2009-09-05 21:47:34 +00003310/*
3311%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3312% %
3313% %
3314% %
3315% W r i t e S V G I m a g e %
3316% %
3317% %
3318% %
3319%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3320%
3321% WriteSVGImage() writes a image in the SVG - XML based W3C standard
3322% format.
3323%
3324% The format of the WriteSVGImage method is:
3325%
cristy3a37efd2011-08-28 20:31:03 +00003326% MagickBooleanType WriteSVGImage(const ImageInfo *image_info,
3327% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003328%
3329% A description of each parameter follows.
3330%
3331% o image_info: the image info.
3332%
3333% o image: The image.
3334%
cristy3a37efd2011-08-28 20:31:03 +00003335% o exception: return any errors or warnings in this structure.
3336%
cristy3ed852e2009-09-05 21:47:34 +00003337*/
3338
3339static void AffineToTransform(Image *image,AffineMatrix *affine)
3340{
3341 char
cristy151b66d2015-04-15 10:50:31 +00003342 transform[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00003343
3344 if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
3345 {
3346 if ((fabs(affine->rx) < MagickEpsilon) &&
3347 (fabs(affine->ry) < MagickEpsilon))
3348 {
3349 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3350 (fabs(affine->sy-1.0) < MagickEpsilon))
3351 {
3352 (void) WriteBlobString(image,"\">\n");
3353 return;
3354 }
cristy151b66d2015-04-15 10:50:31 +00003355 (void) FormatLocaleString(transform,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00003356 "\" transform=\"scale(%g,%g)\">\n",affine->sx,affine->sy);
cristy3ed852e2009-09-05 21:47:34 +00003357 (void) WriteBlobString(image,transform);
3358 return;
3359 }
3360 else
3361 {
3362 if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
3363 (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
3364 (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
3365 2*MagickEpsilon))
3366 {
3367 double
3368 theta;
3369
3370 theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
cristy151b66d2015-04-15 10:50:31 +00003371 (void) FormatLocaleString(transform,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00003372 "\" transform=\"rotate(%g)\">\n",theta);
cristy3ed852e2009-09-05 21:47:34 +00003373 (void) WriteBlobString(image,transform);
3374 return;
3375 }
3376 }
3377 }
3378 else
3379 {
3380 if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
3381 (fabs(affine->rx) < MagickEpsilon) &&
3382 (fabs(affine->ry) < MagickEpsilon) &&
3383 (fabs(affine->sy-1.0) < MagickEpsilon))
3384 {
cristy151b66d2015-04-15 10:50:31 +00003385 (void) FormatLocaleString(transform,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00003386 "\" transform=\"translate(%g,%g)\">\n",affine->tx,affine->ty);
cristy3ed852e2009-09-05 21:47:34 +00003387 (void) WriteBlobString(image,transform);
3388 return;
3389 }
3390 }
cristy151b66d2015-04-15 10:50:31 +00003391 (void) FormatLocaleString(transform,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00003392 "\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
cristy8cd5b312010-01-07 01:10:24 +00003393 affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
cristy3ed852e2009-09-05 21:47:34 +00003394 (void) WriteBlobString(image,transform);
3395}
3396
3397static MagickBooleanType IsPoint(const char *point)
3398{
3399 char
3400 *p;
3401
cristybb503372010-05-27 20:51:26 +00003402 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003403 value;
3404
3405 value=strtol(point,&p,10);
cristyda16f162011-02-19 23:52:17 +00003406 (void) value;
cristy3ed852e2009-09-05 21:47:34 +00003407 return(p != point ? MagickTrue : MagickFalse);
3408}
3409
cristyc82a27b2011-10-21 01:07:16 +00003410static MagickBooleanType TraceSVGImage(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003411{
cristy3ed852e2009-09-05 21:47:34 +00003412#if defined(MAGICKCORE_AUTOTRACE_DELEGATE)
3413 {
3414 at_bitmap_type
3415 *trace;
3416
3417 at_fitting_opts_type
3418 *fitting_options;
3419
3420 at_output_opts_type
3421 *output_options;
3422
3423 at_splines_type
3424 *splines;
3425
3426 ImageType
3427 type;
3428
cristya9b9e252014-06-10 00:18:20 +00003429 register const PixelPacket
3430 *p;
3431
cristybb503372010-05-27 20:51:26 +00003432 register ssize_t
cristya9b9e252014-06-10 00:18:20 +00003433 i,
3434 x;
cristy3ed852e2009-09-05 21:47:34 +00003435
cristybb503372010-05-27 20:51:26 +00003436 size_t
cristy3ed852e2009-09-05 21:47:34 +00003437 number_planes;
3438
cristya9b9e252014-06-10 00:18:20 +00003439 ssize_t
3440 y;
3441
cristy3ed852e2009-09-05 21:47:34 +00003442 /*
3443 Trace image and write as SVG.
3444 */
3445 fitting_options=at_fitting_opts_new();
3446 output_options=at_output_opts_new();
dirkab4f0bb2015-07-25 11:46:32 +00003447 (void) SetImageGray(image,exception);
3448 type=GetImageType(image);
cristy3ed852e2009-09-05 21:47:34 +00003449 number_planes=3;
3450 if ((type == BilevelType) || (type == GrayscaleType))
3451 number_planes=1;
3452 trace=at_bitmap_new(image->columns,image->rows,number_planes);
3453 i=0;
cristybb503372010-05-27 20:51:26 +00003454 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003455 {
cristyad3b66f2011-10-31 02:07:10 +00003456 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristya9b9e252014-06-10 00:18:20 +00003457 if (p == (const PixelPacket *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003458 break;
cristybb503372010-05-27 20:51:26 +00003459 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003460 {
cristy4c08aed2011-07-01 19:47:50 +00003461 trace->bitmap[i++]=GetPixelRed(image,p);
cristy3ed852e2009-09-05 21:47:34 +00003462 if (number_planes == 3)
3463 {
cristy4c08aed2011-07-01 19:47:50 +00003464 trace->bitmap[i++]=GetPixelGreen(image,p);
3465 trace->bitmap[i++]=GetPixelBlue(image,p);
cristy3ed852e2009-09-05 21:47:34 +00003466 }
cristyed231572011-07-14 02:18:59 +00003467 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003468 }
3469 }
3470 splines=at_splines_new_full(trace,fitting_options,NULL,NULL,NULL,NULL,NULL,
3471 NULL);
3472 at_splines_write(at_output_get_handler_by_suffix((char *) "svg"),
3473 GetBlobFileHandle(image),image->filename,output_options,splines,NULL,
3474 NULL);
3475 /*
3476 Free resources.
3477 */
3478 at_splines_free(splines);
3479 at_bitmap_free(trace);
3480 at_output_opts_free(output_options);
3481 at_fitting_opts_free(fitting_options);
3482 }
3483#else
3484 {
3485 char
cristya9b9e252014-06-10 00:18:20 +00003486 *base64,
cristy151b66d2015-04-15 10:50:31 +00003487 message[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00003488
cristya9b9e252014-06-10 00:18:20 +00003489 Image
3490 *clone_image;
cristy3ed852e2009-09-05 21:47:34 +00003491
cristya9b9e252014-06-10 00:18:20 +00003492 ImageInfo
3493 *image_info;
3494
3495 register char
3496 *p;
3497
3498 size_t
3499 blob_length,
3500 encode_length;
3501
3502 ssize_t
3503 i;
3504
3505 unsigned char
3506 *blob;
3507
3508 (void) WriteBlobString(image,
3509 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
cristy3ed852e2009-09-05 21:47:34 +00003510 (void) WriteBlobString(image,
cristy374f65e2015-02-10 16:59:21 +00003511 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"");
cristy3ed852e2009-09-05 21:47:34 +00003512 (void) WriteBlobString(image,
cristy374f65e2015-02-10 16:59:21 +00003513 " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
cristy151b66d2015-04-15 10:50:31 +00003514 (void) FormatLocaleString(message,MagickPathExtent,
cristy374f65e2015-02-10 16:59:21 +00003515 "<svg version=\"1.1\" id=\"Layer_1\" "
3516 "xmlns=\"http://www.w3.org/2000/svg\" "
3517 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" "
3518 "width=\"%.20gpx\" height=\"%.20gpx\" viewBox=\"0 0 %.20g %.20g\" "
3519 "enable-background=\"new 0 0 %.20g %.20g\" xml:space=\"preserve\">",
3520 (double) image->columns,(double) image->rows,
3521 (double) image->columns,(double) image->rows,
3522 (double) image->columns,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003523 (void) WriteBlobString(image,message);
cristya9b9e252014-06-10 00:18:20 +00003524 clone_image=CloneImage(image,0,0,MagickTrue,exception);
3525 if (clone_image == (Image *) NULL)
3526 return(MagickFalse);
3527 image_info=AcquireImageInfo();
cristy151b66d2015-04-15 10:50:31 +00003528 (void) CopyMagickString(image_info->magick,"PNG",MagickPathExtent);
cristya9b9e252014-06-10 00:18:20 +00003529 blob_length=2048;
3530 blob=(unsigned char *) ImageToBlob(image_info,clone_image,&blob_length,
3531 exception);
dirk1c5ca762015-01-24 13:10:18 +00003532 clone_image=DestroyImage(clone_image);
3533 image_info=DestroyImageInfo(image_info);
3534 if (blob == (unsigned char *) NULL)
3535 return(MagickFalse);
cristya9b9e252014-06-10 00:18:20 +00003536 encode_length=0;
3537 base64=Base64Encode(blob,blob_length,&encode_length);
3538 blob=(unsigned char *) RelinquishMagickMemory(blob);
cristy151b66d2015-04-15 10:50:31 +00003539 (void) FormatLocaleString(message,MagickPathExtent,
cristya9b9e252014-06-10 00:18:20 +00003540 " <image id=\"image%.20g\" width=\"%.20g\" height=\"%.20g\" "
3541 "x=\"%.20g\" y=\"%.20g\"\n xlink:href=\"data:image/png;base64,",
3542 (double) image->scene,(double) image->columns,(double) image->rows,
3543 (double) image->page.x,(double) image->page.y);
3544 (void) WriteBlobString(image,message);
3545 p=base64;
3546 for (i=(ssize_t) encode_length; i > 0; i-=76)
cristy3ed852e2009-09-05 21:47:34 +00003547 {
cristy151b66d2015-04-15 10:50:31 +00003548 (void) FormatLocaleString(message,MagickPathExtent,"%.76s",p);
cristya9b9e252014-06-10 00:18:20 +00003549 (void) WriteBlobString(image,message);
3550 p+=76;
3551 if (i > 76)
3552 (void) WriteBlobString(image,"\n");
cristy3ed852e2009-09-05 21:47:34 +00003553 }
dirk1c5ca762015-01-24 13:10:18 +00003554 base64=DestroyString(base64);
cristya9b9e252014-06-10 00:18:20 +00003555 (void) WriteBlobString(image,"\" />\n");
cristy3ed852e2009-09-05 21:47:34 +00003556 (void) WriteBlobString(image,"</svg>\n");
3557 }
3558#endif
3559 return(MagickTrue);
3560}
3561
cristy3a37efd2011-08-28 20:31:03 +00003562static MagickBooleanType WriteSVGImage(const ImageInfo *image_info,Image *image,
3563 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003564{
3565#define BezierQuantum 200
3566
3567 AffineMatrix
3568 affine;
3569
3570 char
cristy151b66d2015-04-15 10:50:31 +00003571 keyword[MagickPathExtent],
3572 message[MagickPathExtent],
3573 name[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +00003574 *token,
cristy151b66d2015-04-15 10:50:31 +00003575 type[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00003576
3577 const char
3578 *p,
3579 *q,
3580 *value;
3581
3582 int
3583 n;
3584
cristybb503372010-05-27 20:51:26 +00003585 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003586 j;
3587
3588 MagickBooleanType
3589 active,
3590 status;
3591
3592 PointInfo
3593 point;
3594
3595 PrimitiveInfo
3596 *primitive_info;
3597
3598 PrimitiveType
3599 primitive_type;
3600
cristybb503372010-05-27 20:51:26 +00003601 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003602 x;
3603
cristybb503372010-05-27 20:51:26 +00003604 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003605 i;
3606
3607 size_t
3608 length;
3609
3610 SVGInfo
3611 svg_info;
3612
cristybb503372010-05-27 20:51:26 +00003613 size_t
cristy3ed852e2009-09-05 21:47:34 +00003614 number_points;
3615
3616 /*
3617 Open output image file.
3618 */
3619 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003620 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00003621 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003622 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00003623 if (image->debug != MagickFalse)
3624 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00003625 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003626 assert(exception->signature == MagickCoreSignature);
cristy3a37efd2011-08-28 20:31:03 +00003627 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00003628 if (status == MagickFalse)
3629 return(status);
3630 value=GetImageArtifact(image,"SVG");
3631 if (value != (char *) NULL)
3632 {
3633 (void) WriteBlobString(image,value);
3634 (void) CloseBlob(image);
3635 return(MagickTrue);
3636 }
3637 value=GetImageArtifact(image,"MVG");
3638 if (value == (char *) NULL)
cristyc82a27b2011-10-21 01:07:16 +00003639 return(TraceSVGImage(image,exception));
cristy3ed852e2009-09-05 21:47:34 +00003640 /*
3641 Write SVG header.
3642 */
3643 (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
3644 (void) WriteBlobString(image,
3645 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
3646 (void) WriteBlobString(image,
3647 " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
cristy151b66d2015-04-15 10:50:31 +00003648 (void) FormatLocaleString(message,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00003649 "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) image->columns,(double)
3650 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003651 (void) WriteBlobString(image,message);
3652 /*
3653 Allocate primitive info memory.
3654 */
3655 number_points=2047;
3656 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory(number_points,
3657 sizeof(*primitive_info));
3658 if (primitive_info == (PrimitiveInfo *) NULL)
3659 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3660 GetAffineMatrix(&affine);
3661 token=AcquireString(value);
3662 active=MagickFalse;
3663 n=0;
3664 status=MagickTrue;
3665 for (q=(const char *) value; *q != '\0'; )
3666 {
3667 /*
3668 Interpret graphic primitive.
3669 */
3670 GetMagickToken(q,&q,keyword);
3671 if (*keyword == '\0')
3672 break;
3673 if (*keyword == '#')
3674 {
3675 /*
3676 Comment.
3677 */
3678 if (active != MagickFalse)
3679 {
3680 AffineToTransform(image,&affine);
3681 active=MagickFalse;
3682 }
3683 (void) WriteBlobString(image,"<desc>");
3684 (void) WriteBlobString(image,keyword+1);
3685 for ( ; (*q != '\n') && (*q != '\0'); q++)
3686 switch (*q)
3687 {
3688 case '<': (void) WriteBlobString(image,"&lt;"); break;
3689 case '>': (void) WriteBlobString(image,"&gt;"); break;
3690 case '&': (void) WriteBlobString(image,"&amp;"); break;
3691 default: (void) WriteBlobByte(image,*q); break;
3692 }
3693 (void) WriteBlobString(image,"</desc>\n");
3694 continue;
3695 }
3696 primitive_type=UndefinedPrimitive;
3697 switch (*keyword)
3698 {
3699 case ';':
3700 break;
3701 case 'a':
3702 case 'A':
3703 {
3704 if (LocaleCompare("affine",keyword) == 0)
3705 {
3706 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003707 affine.sx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003708 GetMagickToken(q,&q,token);
3709 if (*token == ',')
3710 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003711 affine.rx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003712 GetMagickToken(q,&q,token);
3713 if (*token == ',')
3714 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003715 affine.ry=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003716 GetMagickToken(q,&q,token);
3717 if (*token == ',')
3718 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003719 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003720 GetMagickToken(q,&q,token);
3721 if (*token == ',')
3722 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003723 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003724 GetMagickToken(q,&q,token);
3725 if (*token == ',')
3726 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003727 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003728 break;
3729 }
dirk34e1a212015-03-22 16:45:12 +00003730 if (LocaleCompare("alpha",keyword) == 0)
3731 {
3732 primitive_type=AlphaPrimitive;
3733 break;
3734 }
cristy3ed852e2009-09-05 21:47:34 +00003735 if (LocaleCompare("angle",keyword) == 0)
3736 {
3737 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00003738 affine.rx=StringToDouble(token,(char **) NULL);
3739 affine.ry=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00003740 break;
3741 }
3742 if (LocaleCompare("arc",keyword) == 0)
3743 {
3744 primitive_type=ArcPrimitive;
3745 break;
3746 }
3747 status=MagickFalse;
3748 break;
3749 }
3750 case 'b':
3751 case 'B':
3752 {
3753 if (LocaleCompare("bezier",keyword) == 0)
3754 {
3755 primitive_type=BezierPrimitive;
3756 break;
3757 }
3758 status=MagickFalse;
3759 break;
3760 }
3761 case 'c':
3762 case 'C':
3763 {
3764 if (LocaleCompare("clip-path",keyword) == 0)
3765 {
3766 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003767 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003768 "clip-path:url(#%s);",token);
3769 (void) WriteBlobString(image,message);
3770 break;
3771 }
3772 if (LocaleCompare("clip-rule",keyword) == 0)
3773 {
3774 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003775 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003776 "clip-rule:%s;",token);
3777 (void) WriteBlobString(image,message);
3778 break;
3779 }
3780 if (LocaleCompare("clip-units",keyword) == 0)
3781 {
3782 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003783 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003784 "clipPathUnits=%s;",token);
3785 (void) WriteBlobString(image,message);
3786 break;
3787 }
3788 if (LocaleCompare("circle",keyword) == 0)
3789 {
3790 primitive_type=CirclePrimitive;
3791 break;
3792 }
3793 if (LocaleCompare("color",keyword) == 0)
3794 {
3795 primitive_type=ColorPrimitive;
3796 break;
3797 }
3798 status=MagickFalse;
3799 break;
3800 }
3801 case 'd':
3802 case 'D':
3803 {
3804 if (LocaleCompare("decorate",keyword) == 0)
3805 {
3806 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003807 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003808 "text-decoration:%s;",token);
3809 (void) WriteBlobString(image,message);
3810 break;
3811 }
3812 status=MagickFalse;
3813 break;
3814 }
3815 case 'e':
3816 case 'E':
3817 {
3818 if (LocaleCompare("ellipse",keyword) == 0)
3819 {
3820 primitive_type=EllipsePrimitive;
3821 break;
3822 }
3823 status=MagickFalse;
3824 break;
3825 }
3826 case 'f':
3827 case 'F':
3828 {
3829 if (LocaleCompare("fill",keyword) == 0)
3830 {
3831 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003832 (void) FormatLocaleString(message,MagickPathExtent,"fill:%s;",
cristy3ed852e2009-09-05 21:47:34 +00003833 token);
3834 (void) WriteBlobString(image,message);
3835 break;
3836 }
3837 if (LocaleCompare("fill-rule",keyword) == 0)
3838 {
3839 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003840 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003841 "fill-rule:%s;",token);
3842 (void) WriteBlobString(image,message);
3843 break;
3844 }
cristy69fcc592012-04-28 18:39:59 +00003845 if (LocaleCompare("fill-alpha",keyword) == 0)
cristy3ed852e2009-09-05 21:47:34 +00003846 {
3847 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003848 (void) FormatLocaleString(message,MagickPathExtent,
cristy69fcc592012-04-28 18:39:59 +00003849 "fill-alpha:%s;",token);
cristy3ed852e2009-09-05 21:47:34 +00003850 (void) WriteBlobString(image,message);
3851 break;
3852 }
3853 if (LocaleCompare("font-family",keyword) == 0)
3854 {
3855 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003856 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003857 "font-family:%s;",token);
3858 (void) WriteBlobString(image,message);
3859 break;
3860 }
3861 if (LocaleCompare("font-stretch",keyword) == 0)
3862 {
3863 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003864 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003865 "font-stretch:%s;",token);
3866 (void) WriteBlobString(image,message);
3867 break;
3868 }
3869 if (LocaleCompare("font-style",keyword) == 0)
3870 {
3871 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003872 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003873 "font-style:%s;",token);
3874 (void) WriteBlobString(image,message);
3875 break;
3876 }
3877 if (LocaleCompare("font-size",keyword) == 0)
3878 {
3879 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003880 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003881 "font-size:%s;",token);
3882 (void) WriteBlobString(image,message);
3883 break;
3884 }
3885 if (LocaleCompare("font-weight",keyword) == 0)
3886 {
3887 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003888 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003889 "font-weight:%s;",token);
3890 (void) WriteBlobString(image,message);
3891 break;
3892 }
3893 status=MagickFalse;
3894 break;
3895 }
3896 case 'g':
3897 case 'G':
3898 {
3899 if (LocaleCompare("gradient-units",keyword) == 0)
3900 {
3901 GetMagickToken(q,&q,token);
3902 break;
3903 }
3904 if (LocaleCompare("text-align",keyword) == 0)
3905 {
3906 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003907 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003908 "text-align %s ",token);
3909 (void) WriteBlobString(image,message);
3910 break;
3911 }
3912 if (LocaleCompare("text-anchor",keyword) == 0)
3913 {
3914 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003915 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003916 "text-anchor %s ",token);
3917 (void) WriteBlobString(image,message);
3918 break;
3919 }
3920 status=MagickFalse;
3921 break;
3922 }
3923 case 'i':
3924 case 'I':
3925 {
3926 if (LocaleCompare("image",keyword) == 0)
3927 {
3928 GetMagickToken(q,&q,token);
3929 primitive_type=ImagePrimitive;
3930 break;
3931 }
3932 status=MagickFalse;
3933 break;
3934 }
3935 case 'l':
3936 case 'L':
3937 {
3938 if (LocaleCompare("line",keyword) == 0)
3939 {
3940 primitive_type=LinePrimitive;
3941 break;
3942 }
3943 status=MagickFalse;
3944 break;
3945 }
cristy3ed852e2009-09-05 21:47:34 +00003946 case 'o':
3947 case 'O':
3948 {
3949 if (LocaleCompare("opacity",keyword) == 0)
3950 {
3951 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00003952 (void) FormatLocaleString(message,MagickPathExtent,"opacity %s ",
cristy3ed852e2009-09-05 21:47:34 +00003953 token);
3954 (void) WriteBlobString(image,message);
3955 break;
3956 }
3957 status=MagickFalse;
3958 break;
3959 }
3960 case 'p':
3961 case 'P':
3962 {
3963 if (LocaleCompare("path",keyword) == 0)
3964 {
3965 primitive_type=PathPrimitive;
3966 break;
3967 }
3968 if (LocaleCompare("point",keyword) == 0)
3969 {
3970 primitive_type=PointPrimitive;
3971 break;
3972 }
3973 if (LocaleCompare("polyline",keyword) == 0)
3974 {
3975 primitive_type=PolylinePrimitive;
3976 break;
3977 }
3978 if (LocaleCompare("polygon",keyword) == 0)
3979 {
3980 primitive_type=PolygonPrimitive;
3981 break;
3982 }
3983 if (LocaleCompare("pop",keyword) == 0)
3984 {
3985 GetMagickToken(q,&q,token);
3986 if (LocaleCompare("clip-path",token) == 0)
3987 {
3988 (void) WriteBlobString(image,"</clipPath>\n");
3989 break;
3990 }
3991 if (LocaleCompare("defs",token) == 0)
3992 {
3993 (void) WriteBlobString(image,"</defs>\n");
3994 break;
3995 }
3996 if (LocaleCompare("gradient",token) == 0)
3997 {
cristy151b66d2015-04-15 10:50:31 +00003998 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00003999 "</%sGradient>\n",type);
4000 (void) WriteBlobString(image,message);
4001 break;
4002 }
4003 if (LocaleCompare("graphic-context",token) == 0)
4004 {
4005 n--;
4006 if (n < 0)
4007 ThrowWriterException(DrawError,
4008 "UnbalancedGraphicContextPushPop");
4009 (void) WriteBlobString(image,"</g>\n");
4010 }
4011 if (LocaleCompare("pattern",token) == 0)
4012 {
4013 (void) WriteBlobString(image,"</pattern>\n");
4014 break;
4015 }
4016 if (LocaleCompare("defs",token) == 0)
4017 (void) WriteBlobString(image,"</g>\n");
4018 break;
4019 }
4020 if (LocaleCompare("push",keyword) == 0)
4021 {
4022 GetMagickToken(q,&q,token);
4023 if (LocaleCompare("clip-path",token) == 0)
4024 {
4025 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004026 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00004027 "<clipPath id=\"%s\">\n",token);
4028 (void) WriteBlobString(image,message);
4029 break;
4030 }
4031 if (LocaleCompare("defs",token) == 0)
4032 {
4033 (void) WriteBlobString(image,"<defs>\n");
4034 break;
4035 }
4036 if (LocaleCompare("gradient",token) == 0)
4037 {
4038 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004039 (void) CopyMagickString(name,token,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00004040 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004041 (void) CopyMagickString(type,token,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00004042 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004043 svg_info.segment.x1=StringToDouble(token,(char **) NULL);
4044 svg_info.element.cx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004045 GetMagickToken(q,&q,token);
4046 if (*token == ',')
4047 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004048 svg_info.segment.y1=StringToDouble(token,(char **) NULL);
4049 svg_info.element.cy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004050 GetMagickToken(q,&q,token);
4051 if (*token == ',')
4052 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004053 svg_info.segment.x2=StringToDouble(token,(char **) NULL);
4054 svg_info.element.major=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00004055 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004056 GetMagickToken(q,&q,token);
4057 if (*token == ',')
4058 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004059 svg_info.segment.y2=StringToDouble(token,(char **) NULL);
4060 svg_info.element.minor=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00004061 (char **) NULL);
cristy151b66d2015-04-15 10:50:31 +00004062 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00004063 "<%sGradient id=\"%s\" x1=\"%g\" y1=\"%g\" x2=\"%g\" "
4064 "y2=\"%g\">\n",type,name,svg_info.segment.x1,
cristy3ed852e2009-09-05 21:47:34 +00004065 svg_info.segment.y1,svg_info.segment.x2,svg_info.segment.y2);
4066 if (LocaleCompare(type,"radial") == 0)
4067 {
4068 GetMagickToken(q,&q,token);
4069 if (*token == ',')
4070 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004071 svg_info.element.angle=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00004072 (char **) NULL);
cristy151b66d2015-04-15 10:50:31 +00004073 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00004074 "<%sGradient id=\"%s\" cx=\"%g\" cy=\"%g\" r=\"%g\" "
4075 "fx=\"%g\" fy=\"%g\">\n",type,name,
cristy8cd5b312010-01-07 01:10:24 +00004076 svg_info.element.cx,svg_info.element.cy,
4077 svg_info.element.angle,svg_info.element.major,
4078 svg_info.element.minor);
cristy3ed852e2009-09-05 21:47:34 +00004079 }
4080 (void) WriteBlobString(image,message);
4081 break;
4082 }
4083 if (LocaleCompare("graphic-context",token) == 0)
4084 {
4085 n++;
4086 if (active)
4087 {
4088 AffineToTransform(image,&affine);
4089 active=MagickFalse;
4090 }
4091 (void) WriteBlobString(image,"<g style=\"");
4092 active=MagickTrue;
4093 }
4094 if (LocaleCompare("pattern",token) == 0)
4095 {
4096 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004097 (void) CopyMagickString(name,token,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00004098 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004099 svg_info.bounds.x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004100 GetMagickToken(q,&q,token);
4101 if (*token == ',')
4102 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004103 svg_info.bounds.y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004104 GetMagickToken(q,&q,token);
4105 if (*token == ',')
4106 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004107 svg_info.bounds.width=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00004108 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004109 GetMagickToken(q,&q,token);
4110 if (*token == ',')
4111 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004112 svg_info.bounds.height=StringToDouble(token,
cristyc1acd842011-05-19 23:05:47 +00004113 (char **) NULL);
cristy151b66d2015-04-15 10:50:31 +00004114 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00004115 "<pattern id=\"%s\" x=\"%g\" y=\"%g\" width=\"%g\" "
4116 "height=\"%g\">\n",name,svg_info.bounds.x,
cristy8cd5b312010-01-07 01:10:24 +00004117 svg_info.bounds.y,svg_info.bounds.width,
4118 svg_info.bounds.height);
cristy3ed852e2009-09-05 21:47:34 +00004119 (void) WriteBlobString(image,message);
4120 break;
4121 }
4122 break;
4123 }
4124 status=MagickFalse;
4125 break;
4126 }
4127 case 'r':
4128 case 'R':
4129 {
4130 if (LocaleCompare("rectangle",keyword) == 0)
4131 {
4132 primitive_type=RectanglePrimitive;
4133 break;
4134 }
4135 if (LocaleCompare("roundRectangle",keyword) == 0)
4136 {
4137 primitive_type=RoundRectanglePrimitive;
4138 break;
4139 }
4140 if (LocaleCompare("rotate",keyword) == 0)
4141 {
4142 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004143 (void) FormatLocaleString(message,MagickPathExtent,"rotate(%s) ",
cristy3ed852e2009-09-05 21:47:34 +00004144 token);
4145 (void) WriteBlobString(image,message);
4146 break;
4147 }
4148 status=MagickFalse;
4149 break;
4150 }
4151 case 's':
4152 case 'S':
4153 {
4154 if (LocaleCompare("scale",keyword) == 0)
4155 {
4156 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004157 affine.sx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004158 GetMagickToken(q,&q,token);
4159 if (*token == ',')
4160 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004161 affine.sy=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004162 break;
4163 }
4164 if (LocaleCompare("skewX",keyword) == 0)
4165 {
4166 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004167 (void) FormatLocaleString(message,MagickPathExtent,"skewX(%s) ",
cristy3ed852e2009-09-05 21:47:34 +00004168 token);
4169 (void) WriteBlobString(image,message);
4170 break;
4171 }
4172 if (LocaleCompare("skewY",keyword) == 0)
4173 {
4174 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004175 (void) FormatLocaleString(message,MagickPathExtent,"skewY(%s) ",
cristy3ed852e2009-09-05 21:47:34 +00004176 token);
4177 (void) WriteBlobString(image,message);
4178 break;
4179 }
4180 if (LocaleCompare("stop-color",keyword) == 0)
4181 {
4182 char
cristy151b66d2015-04-15 10:50:31 +00004183 color[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00004184
4185 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004186 (void) CopyMagickString(color,token,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00004187 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004188 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00004189 " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
4190 (void) WriteBlobString(image,message);
4191 break;
4192 }
4193 if (LocaleCompare("stroke",keyword) == 0)
4194 {
4195 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004196 (void) FormatLocaleString(message,MagickPathExtent,"stroke:%s;",
cristy3ed852e2009-09-05 21:47:34 +00004197 token);
4198 (void) WriteBlobString(image,message);
4199 break;
4200 }
4201 if (LocaleCompare("stroke-antialias",keyword) == 0)
4202 {
4203 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004204 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00004205 "stroke-antialias:%s;",token);
4206 (void) WriteBlobString(image,message);
4207 break;
4208 }
4209 if (LocaleCompare("stroke-dasharray",keyword) == 0)
4210 {
4211 if (IsPoint(q))
4212 {
cristybb503372010-05-27 20:51:26 +00004213 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004214 k;
4215
4216 p=q;
4217 GetMagickToken(p,&p,token);
4218 for (k=0; IsPoint(token); k++)
4219 GetMagickToken(p,&p,token);
4220 (void) WriteBlobString(image,"stroke-dasharray:");
4221 for (j=0; j < k; j++)
4222 {
4223 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004224 (void) FormatLocaleString(message,MagickPathExtent,"%s ",
cristy3ed852e2009-09-05 21:47:34 +00004225 token);
4226 (void) WriteBlobString(image,message);
4227 }
4228 (void) WriteBlobString(image,";");
4229 break;
4230 }
4231 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004232 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00004233 "stroke-dasharray:%s;",token);
4234 (void) WriteBlobString(image,message);
4235 break;
4236 }
4237 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
4238 {
4239 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004240 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00004241 "stroke-dashoffset:%s;",token);
4242 (void) WriteBlobString(image,message);
4243 break;
4244 }
4245 if (LocaleCompare("stroke-linecap",keyword) == 0)
4246 {
4247 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004248 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00004249 "stroke-linecap:%s;",token);
4250 (void) WriteBlobString(image,message);
4251 break;
4252 }
4253 if (LocaleCompare("stroke-linejoin",keyword) == 0)
4254 {
4255 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004256 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00004257 "stroke-linejoin:%s;",token);
4258 (void) WriteBlobString(image,message);
4259 break;
4260 }
4261 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
4262 {
4263 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004264 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00004265 "stroke-miterlimit:%s;",token);
4266 (void) WriteBlobString(image,message);
4267 break;
4268 }
4269 if (LocaleCompare("stroke-opacity",keyword) == 0)
4270 {
4271 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004272 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00004273 "stroke-opacity:%s;",token);
4274 (void) WriteBlobString(image,message);
4275 break;
4276 }
4277 if (LocaleCompare("stroke-width",keyword) == 0)
4278 {
4279 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004280 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00004281 "stroke-width:%s;",token);
4282 (void) WriteBlobString(image,message);
4283 continue;
4284 }
4285 status=MagickFalse;
4286 break;
4287 }
4288 case 't':
4289 case 'T':
4290 {
4291 if (LocaleCompare("text",keyword) == 0)
4292 {
4293 primitive_type=TextPrimitive;
4294 break;
4295 }
4296 if (LocaleCompare("text-antialias",keyword) == 0)
4297 {
4298 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004299 (void) FormatLocaleString(message,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00004300 "text-antialias:%s;",token);
4301 (void) WriteBlobString(image,message);
4302 break;
4303 }
4304 if (LocaleCompare("tspan",keyword) == 0)
4305 {
4306 primitive_type=TextPrimitive;
4307 break;
4308 }
4309 if (LocaleCompare("translate",keyword) == 0)
4310 {
4311 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004312 affine.tx=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004313 GetMagickToken(q,&q,token);
4314 if (*token == ',')
4315 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004316 affine.ty=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004317 break;
4318 }
4319 status=MagickFalse;
4320 break;
4321 }
4322 case 'v':
4323 case 'V':
4324 {
4325 if (LocaleCompare("viewbox",keyword) == 0)
4326 {
4327 GetMagickToken(q,&q,token);
4328 if (*token == ',')
4329 GetMagickToken(q,&q,token);
4330 GetMagickToken(q,&q,token);
4331 if (*token == ',')
4332 GetMagickToken(q,&q,token);
4333 GetMagickToken(q,&q,token);
4334 if (*token == ',')
4335 GetMagickToken(q,&q,token);
4336 GetMagickToken(q,&q,token);
4337 break;
4338 }
4339 status=MagickFalse;
4340 break;
4341 }
4342 default:
4343 {
4344 status=MagickFalse;
4345 break;
4346 }
4347 }
4348 if (status == MagickFalse)
4349 break;
4350 if (primitive_type == UndefinedPrimitive)
4351 continue;
4352 /*
4353 Parse the primitive attributes.
4354 */
4355 i=0;
4356 j=0;
4357 for (x=0; *q != '\0'; x++)
4358 {
4359 /*
4360 Define points.
4361 */
4362 if (IsPoint(q) == MagickFalse)
4363 break;
4364 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004365 point.x=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004366 GetMagickToken(q,&q,token);
4367 if (*token == ',')
4368 GetMagickToken(q,&q,token);
cristydbdd0e32011-11-04 23:29:40 +00004369 point.y=StringToDouble(token,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +00004370 GetMagickToken(q,(const char **) NULL,token);
4371 if (*token == ',')
4372 GetMagickToken(q,&q,token);
4373 primitive_info[i].primitive=primitive_type;
4374 primitive_info[i].point=point;
4375 primitive_info[i].coordinates=0;
4376 primitive_info[i].method=FloodfillMethod;
4377 i++;
cristybb503372010-05-27 20:51:26 +00004378 if (i < (ssize_t) (number_points-6*BezierQuantum-360))
cristy3ed852e2009-09-05 21:47:34 +00004379 continue;
4380 number_points+=6*BezierQuantum+360;
4381 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4382 number_points,sizeof(*primitive_info));
4383 if (primitive_info == (PrimitiveInfo *) NULL)
4384 {
cristy3a37efd2011-08-28 20:31:03 +00004385 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004386 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
4387 break;
4388 }
4389 }
4390 primitive_info[j].primitive=primitive_type;
4391 primitive_info[j].coordinates=x;
4392 primitive_info[j].method=FloodfillMethod;
4393 primitive_info[j].text=(char *) NULL;
4394 if (active)
4395 {
4396 AffineToTransform(image,&affine);
4397 active=MagickFalse;
4398 }
4399 active=MagickFalse;
4400 switch (primitive_type)
4401 {
4402 case PointPrimitive:
4403 default:
4404 {
4405 if (primitive_info[j].coordinates != 1)
4406 {
4407 status=MagickFalse;
4408 break;
4409 }
4410 break;
4411 }
4412 case LinePrimitive:
4413 {
4414 if (primitive_info[j].coordinates != 2)
4415 {
4416 status=MagickFalse;
4417 break;
4418 }
cristy151b66d2015-04-15 10:50:31 +00004419 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00004420 " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004421 primitive_info[j].point.x,primitive_info[j].point.y,
4422 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4423 (void) WriteBlobString(image,message);
4424 break;
4425 }
4426 case RectanglePrimitive:
4427 {
4428 if (primitive_info[j].coordinates != 2)
4429 {
4430 status=MagickFalse;
4431 break;
4432 }
cristy151b66d2015-04-15 10:50:31 +00004433 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00004434 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004435 primitive_info[j].point.x,primitive_info[j].point.y,
4436 primitive_info[j+1].point.x-primitive_info[j].point.x,
4437 primitive_info[j+1].point.y-primitive_info[j].point.y);
4438 (void) WriteBlobString(image,message);
4439 break;
4440 }
4441 case RoundRectanglePrimitive:
4442 {
4443 if (primitive_info[j].coordinates != 3)
4444 {
4445 status=MagickFalse;
4446 break;
4447 }
cristy151b66d2015-04-15 10:50:31 +00004448 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00004449 " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" rx=\"%g\" "
4450 "ry=\"%g\"/>\n",primitive_info[j].point.x,
cristy8cd5b312010-01-07 01:10:24 +00004451 primitive_info[j].point.y,primitive_info[j+1].point.x-
4452 primitive_info[j].point.x,primitive_info[j+1].point.y-
4453 primitive_info[j].point.y,primitive_info[j+2].point.x,
4454 primitive_info[j+2].point.y);
cristy3ed852e2009-09-05 21:47:34 +00004455 (void) WriteBlobString(image,message);
4456 break;
4457 }
4458 case ArcPrimitive:
4459 {
4460 if (primitive_info[j].coordinates != 3)
4461 {
4462 status=MagickFalse;
4463 break;
4464 }
4465 break;
4466 }
4467 case EllipsePrimitive:
4468 {
4469 if (primitive_info[j].coordinates != 3)
4470 {
4471 status=MagickFalse;
4472 break;
4473 }
cristy151b66d2015-04-15 10:50:31 +00004474 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00004475 " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004476 primitive_info[j].point.x,primitive_info[j].point.y,
4477 primitive_info[j+1].point.x,primitive_info[j+1].point.y);
4478 (void) WriteBlobString(image,message);
4479 break;
4480 }
4481 case CirclePrimitive:
4482 {
4483 double
4484 alpha,
4485 beta;
4486
4487 if (primitive_info[j].coordinates != 2)
4488 {
4489 status=MagickFalse;
4490 break;
4491 }
4492 alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
4493 beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
cristy151b66d2015-04-15 10:50:31 +00004494 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00004495 " <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
cristy3ed852e2009-09-05 21:47:34 +00004496 primitive_info[j].point.x,primitive_info[j].point.y,
4497 hypot(alpha,beta));
4498 (void) WriteBlobString(image,message);
4499 break;
4500 }
4501 case PolylinePrimitive:
4502 {
4503 if (primitive_info[j].coordinates < 2)
4504 {
4505 status=MagickFalse;
4506 break;
4507 }
cristy151b66d2015-04-15 10:50:31 +00004508 (void) CopyMagickString(message," <polyline points=\"",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00004509 (void) WriteBlobString(image,message);
4510 length=strlen(message);
4511 for ( ; j < i; j++)
4512 {
cristy151b66d2015-04-15 10:50:31 +00004513 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
cristy3ed852e2009-09-05 21:47:34 +00004514 primitive_info[j].point.x,primitive_info[j].point.y);
4515 length+=strlen(message);
4516 if (length >= 80)
4517 {
4518 (void) WriteBlobString(image,"\n ");
4519 length=strlen(message)+5;
4520 }
4521 (void) WriteBlobString(image,message);
4522 }
4523 (void) WriteBlobString(image,"\"/>\n");
4524 break;
4525 }
4526 case PolygonPrimitive:
4527 {
4528 if (primitive_info[j].coordinates < 3)
4529 {
4530 status=MagickFalse;
4531 break;
4532 }
4533 primitive_info[i]=primitive_info[j];
4534 primitive_info[i].coordinates=0;
4535 primitive_info[j].coordinates++;
4536 i++;
cristy151b66d2015-04-15 10:50:31 +00004537 (void) CopyMagickString(message," <polygon points=\"",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00004538 (void) WriteBlobString(image,message);
4539 length=strlen(message);
4540 for ( ; j < i; j++)
4541 {
cristy151b66d2015-04-15 10:50:31 +00004542 (void) FormatLocaleString(message,MagickPathExtent,"%g,%g ",
cristy3ed852e2009-09-05 21:47:34 +00004543 primitive_info[j].point.x,primitive_info[j].point.y);
4544 length+=strlen(message);
4545 if (length >= 80)
4546 {
4547 (void) WriteBlobString(image,"\n ");
4548 length=strlen(message)+5;
4549 }
4550 (void) WriteBlobString(image,message);
4551 }
4552 (void) WriteBlobString(image,"\"/>\n");
4553 break;
4554 }
4555 case BezierPrimitive:
4556 {
4557 if (primitive_info[j].coordinates < 3)
4558 {
4559 status=MagickFalse;
4560 break;
4561 }
4562 break;
4563 }
4564 case PathPrimitive:
4565 {
4566 int
4567 number_attributes;
4568
4569 GetMagickToken(q,&q,token);
4570 number_attributes=1;
4571 for (p=token; *p != '\0'; p++)
4572 if (isalpha((int) *p))
4573 number_attributes++;
cristybb503372010-05-27 20:51:26 +00004574 if (i > (ssize_t) (number_points-6*BezierQuantum*number_attributes-1))
cristy3ed852e2009-09-05 21:47:34 +00004575 {
4576 number_points+=6*BezierQuantum*number_attributes;
4577 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
4578 number_points,sizeof(*primitive_info));
4579 if (primitive_info == (PrimitiveInfo *) NULL)
4580 {
cristy3a37efd2011-08-28 20:31:03 +00004581 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004582 ResourceLimitError,"MemoryAllocationFailed","`%s'",
4583 image->filename);
4584 break;
4585 }
4586 }
4587 (void) WriteBlobString(image," <path d=\"");
4588 (void) WriteBlobString(image,token);
4589 (void) WriteBlobString(image,"\"/>\n");
4590 break;
4591 }
dirk34e1a212015-03-22 16:45:12 +00004592 case AlphaPrimitive:
cristy3ed852e2009-09-05 21:47:34 +00004593 case ColorPrimitive:
cristy3ed852e2009-09-05 21:47:34 +00004594 {
4595 if (primitive_info[j].coordinates != 1)
4596 {
4597 status=MagickFalse;
4598 break;
4599 }
4600 GetMagickToken(q,&q,token);
4601 if (LocaleCompare("point",token) == 0)
4602 primitive_info[j].method=PointMethod;
4603 if (LocaleCompare("replace",token) == 0)
4604 primitive_info[j].method=ReplaceMethod;
4605 if (LocaleCompare("floodfill",token) == 0)
4606 primitive_info[j].method=FloodfillMethod;
4607 if (LocaleCompare("filltoborder",token) == 0)
4608 primitive_info[j].method=FillToBorderMethod;
4609 if (LocaleCompare("reset",token) == 0)
4610 primitive_info[j].method=ResetMethod;
4611 break;
4612 }
4613 case TextPrimitive:
4614 {
4615 register char
4616 *p;
4617
4618 if (primitive_info[j].coordinates != 1)
4619 {
4620 status=MagickFalse;
4621 break;
4622 }
4623 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004624 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00004625 " <text x=\"%g\" y=\"%g\">",primitive_info[j].point.x,
cristy3ed852e2009-09-05 21:47:34 +00004626 primitive_info[j].point.y);
4627 (void) WriteBlobString(image,message);
4628 for (p=token; *p != '\0'; p++)
4629 switch (*p)
4630 {
4631 case '<': (void) WriteBlobString(image,"&lt;"); break;
4632 case '>': (void) WriteBlobString(image,"&gt;"); break;
4633 case '&': (void) WriteBlobString(image,"&amp;"); break;
4634 default: (void) WriteBlobByte(image,*p); break;
4635 }
4636 (void) WriteBlobString(image,"</text>\n");
4637 break;
4638 }
4639 case ImagePrimitive:
4640 {
4641 if (primitive_info[j].coordinates != 2)
4642 {
4643 status=MagickFalse;
4644 break;
4645 }
4646 GetMagickToken(q,&q,token);
cristy151b66d2015-04-15 10:50:31 +00004647 (void) FormatLocaleString(message,MagickPathExtent,
cristye7f51092010-01-17 00:39:37 +00004648 " <image x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
cristy3ed852e2009-09-05 21:47:34 +00004649 "xlink:href=\"%s\"/>\n",primitive_info[j].point.x,
4650 primitive_info[j].point.y,primitive_info[j+1].point.x,
4651 primitive_info[j+1].point.y,token);
4652 (void) WriteBlobString(image,message);
4653 break;
4654 }
4655 }
4656 if (primitive_info == (PrimitiveInfo *) NULL)
4657 break;
4658 primitive_info[i].primitive=UndefinedPrimitive;
4659 if (status == MagickFalse)
4660 break;
4661 }
4662 (void) WriteBlobString(image,"</svg>\n");
4663 /*
4664 Relinquish resources.
4665 */
4666 token=DestroyString(token);
4667 if (primitive_info != (PrimitiveInfo *) NULL)
4668 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
4669 (void) CloseBlob(image);
4670 return(status);
4671}