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