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