/src/graphicsmagick/coders/svg.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | % Copyright (C) 2003-2025 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % |
5 | | % This program is covered by multiple licenses, which are described in |
6 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
7 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
8 | | % |
9 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
10 | | % % |
11 | | % % |
12 | | % % |
13 | | % SSSSS V V GGGG % |
14 | | % SS V V G % |
15 | | % SSS V V G GG % |
16 | | % SS V V G G % |
17 | | % SSSSS V GGG % |
18 | | % % |
19 | | % % |
20 | | % Read/Write Scalable Vector Graphics Format. % |
21 | | % % |
22 | | % % |
23 | | % Software Design % |
24 | | % John Cristy % |
25 | | % William Radcliffe % |
26 | | % March 2000 % |
27 | | % % |
28 | | % % |
29 | | % % |
30 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
31 | | % |
32 | | % |
33 | | */ |
34 | | |
35 | | /* |
36 | | Include declarations. |
37 | | */ |
38 | | #include "magick/studio.h" |
39 | | #include "magick/attribute.h" |
40 | | #include "magick/blob.h" |
41 | | #include "magick/color.h" |
42 | | #include "magick/constitute.h" |
43 | | #include "magick/gem.h" |
44 | | #include "magick/log.h" |
45 | | #include "magick/magick.h" |
46 | | #include "magick/render.h" |
47 | | #include "magick/tempfile.h" |
48 | | #include "magick/utility.h" |
49 | | #if defined(HasXML) |
50 | | # if defined(MSWINDOWS) |
51 | | # if defined(__MINGW32__) |
52 | | # if !defined(_MSC_VER) |
53 | | # define _MSC_VER 1200 |
54 | | # endif |
55 | | # else |
56 | | # include <win32config.h> |
57 | | # endif |
58 | | # endif |
59 | | # include <libxml/parser.h> |
60 | | # include <libxml/xmlmemory.h> |
61 | | # include <libxml/parserInternals.h> |
62 | | # include <libxml/xmlerror.h> |
63 | | #endif |
64 | | |
65 | | /* Enable support for XML internal subset */ |
66 | | #define ENABLE_XML_INTERNAL_SUBSET 1 |
67 | | |
68 | | /* |
69 | | Avoid shadowing library globals and functions. |
70 | | */ |
71 | | #define attribute attribute_magick |
72 | | |
73 | | #if defined(HasAUTOTRACE) |
74 | | #include "types.h" |
75 | | #include "image-header.h" |
76 | | #include "fit.h" |
77 | | #include "output.h" |
78 | | #include "pxl-outline.h" |
79 | | #include "atquantize.h" |
80 | | #include "thin-image.h" |
81 | | |
82 | | char |
83 | | *version_string = "AutoTrace version 0.24a"; |
84 | | #endif |
85 | | |
86 | | /* |
87 | | Define declarations. |
88 | | */ |
89 | 1.15M | #define MVGPrintf (void) fprintf |
90 | | |
91 | | /* |
92 | | Typedef declarations. |
93 | | */ |
94 | | typedef struct _BoundingBox |
95 | | { |
96 | | double |
97 | | x, |
98 | | y, |
99 | | width, |
100 | | height; |
101 | | } BoundingBox; |
102 | | |
103 | | typedef struct _SVGInfo |
104 | | { |
105 | | FILE |
106 | | *file; |
107 | | |
108 | | ExceptionInfo |
109 | | *exception; |
110 | | |
111 | | Image |
112 | | *image; |
113 | | |
114 | | const ImageInfo |
115 | | *image_info; |
116 | | |
117 | | AffineMatrix |
118 | | affine; |
119 | | |
120 | | unsigned long |
121 | | width, |
122 | | height; |
123 | | |
124 | | char |
125 | | *size, |
126 | | *title, |
127 | | *comment; |
128 | | |
129 | | int |
130 | | n; |
131 | | |
132 | | double |
133 | | *scale, |
134 | | pointsize; |
135 | | |
136 | | ElementInfo |
137 | | element; |
138 | | |
139 | | SegmentInfo |
140 | | segment; |
141 | | |
142 | | BoundingBox |
143 | | bounds, |
144 | | view_box; |
145 | | |
146 | | PointInfo |
147 | | radius; |
148 | | |
149 | | char |
150 | | *stop_color, |
151 | | *offset, |
152 | | *text, |
153 | | *vertices, |
154 | | *url; |
155 | | |
156 | | size_t |
157 | | comment_len, |
158 | | text_len; |
159 | | |
160 | | /* |
161 | | Even though it's unlikely to happen, keep track of nested <defs> and |
162 | | elements tagged with an id. |
163 | | */ |
164 | | int |
165 | | defsPushCount, /* for tracking nested <defs> */ |
166 | | idLevelInsideDefs, /* when an "id" is seen, remember svg->n (SVG element level) */ |
167 | | svgPushCount; /* for tracking nested <svg> elements */ |
168 | | |
169 | | unsigned long |
170 | | signature; |
171 | | |
172 | | } SVGInfo; |
173 | | |
174 | | /* |
175 | | Forward declarations. |
176 | | */ |
177 | | #if defined(ENABLE_SVG_WRITER) && ENABLE_SVG_WRITER |
178 | | static unsigned int |
179 | | WriteSVGImage(const ImageInfo *,Image *); |
180 | | #endif /* if defined(ENABLE_SVG_WRITER) && ENABLE_SVG_WRITER */ |
181 | | |
182 | | #if defined(HasXML) |
183 | | /* |
184 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
185 | | % % |
186 | | % % |
187 | | % % |
188 | | % R e a d S V G I m a g e % |
189 | | % % |
190 | | % % |
191 | | % % |
192 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
193 | | % |
194 | | % Method ReadSVGImage reads a Scalable Vector Gaphics file and returns it. It |
195 | | % allocates the memory necessary for the new Image structure and returns a |
196 | | % pointer to the new image. |
197 | | % |
198 | | % The format of the ReadSVGImage method is: |
199 | | % |
200 | | % Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception) |
201 | | % |
202 | | % A description of each parameter follows: |
203 | | % |
204 | | % o image: Method ReadSVGImage returns a pointer to the image after |
205 | | % reading. A null image is returned if there is a memory shortage or if |
206 | | % the image cannot be read. |
207 | | % |
208 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
209 | | % |
210 | | % o exception: return any errors or warnings in this structure. |
211 | | % |
212 | | % |
213 | | */ |
214 | | |
215 | | static double |
216 | | GetUserSpaceCoordinateValue(const SVGInfo *svg_info, |
217 | | int type, |
218 | | const char *string, |
219 | | MagickBool positive) |
220 | 192k | { |
221 | 192k | char |
222 | 192k | *p, |
223 | 192k | token[MaxTextExtent]; |
224 | | |
225 | 192k | double |
226 | 192k | value; |
227 | | |
228 | 192k | assert(string != (const char *) NULL); |
229 | 192k | p=(char *) string; |
230 | 192k | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
231 | 192k | if ((MagickAtoFChk(token,&value) == MagickFail) || |
232 | 192k | (((positive) && value < 0.0))) |
233 | 9.46k | { |
234 | 9.46k | errno=0; |
235 | 9.46k | ThrowException(svg_info->exception,DrawError,InvalidPrimitiveArgument, |
236 | 9.46k | string); |
237 | 9.46k | } |
238 | 192k | if (strchr(token,'%') != (char *) NULL) |
239 | 30.0k | { |
240 | 30.0k | double |
241 | 30.0k | alpha, |
242 | 30.0k | beta; |
243 | | |
244 | 30.0k | if (type > 0) |
245 | 15.4k | return(svg_info->view_box.width*value/100.0); |
246 | 14.6k | if (type < 0) |
247 | 12.9k | return(svg_info->view_box.height*value/100.0); |
248 | 1.68k | alpha=value-svg_info->view_box.width; |
249 | 1.68k | beta=value-svg_info->view_box.height; |
250 | 1.68k | return(sqrt(alpha*alpha+beta*beta)/sqrt(2.0)/100.0); |
251 | 14.6k | } |
252 | 162k | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
253 | 162k | if (LocaleNCompare(token,"cm",2) == 0) |
254 | 118 | return(72.0*svg_info->scale[0]/2.54*value); |
255 | 162k | if (LocaleNCompare(token,"em",2) == 0) |
256 | 1.47k | return(svg_info->pointsize*value); |
257 | 161k | if (LocaleNCompare(token,"ex",2) == 0) |
258 | 883 | return(svg_info->pointsize*value/2.0); |
259 | 160k | if (LocaleNCompare(token,"in",2) == 0) |
260 | 399 | return(72.0*svg_info->scale[0]*value); |
261 | 160k | if (LocaleNCompare(token,"mm",2) == 0) |
262 | 290 | return(72.0*svg_info->scale[0]/25.4*value); |
263 | 159k | if (LocaleNCompare(token,"pc",2) == 0) |
264 | 486 | return(72.0*svg_info->scale[0]/6.0*value); |
265 | 159k | if (LocaleNCompare(token,"pt",2) == 0) |
266 | 2.88k | return(svg_info->scale[0]*value); |
267 | 156k | if (LocaleNCompare(token,"px",2) == 0) |
268 | 3.47k | return(value); |
269 | 152k | return(value); |
270 | 156k | } |
271 | | |
272 | | #define MagickCopySubstrToToken(dst, src, size) \ |
273 | 1.13M | do { \ |
274 | 1.13M | (void) memcpy(dst,src,(size)); \ |
275 | 1.13M | dst[(size)]='\0'; \ |
276 | 1.13M | } while (0); |
277 | | |
278 | | static char **GetStyleTokens(const SVGInfo *svg_info,const char *text,size_t *number_tokens) |
279 | 16.0k | { |
280 | 18.3k | #define MaxStyleTokens 256 |
281 | | |
282 | 16.0k | char |
283 | 16.0k | **tokens; |
284 | | |
285 | 16.0k | const char |
286 | 16.0k | *p, |
287 | 16.0k | *q; |
288 | | |
289 | 16.0k | size_t |
290 | 16.0k | alloc_tokens, |
291 | 16.0k | i, |
292 | 16.0k | iListFront; |
293 | | |
294 | 16.0k | MagickBool |
295 | 16.0k | IsFontSize = MagickFalse; |
296 | | |
297 | 16.0k | assert(svg_info->signature == MagickSignature); |
298 | 16.0k | *number_tokens=0; |
299 | 16.0k | alloc_tokens=0; |
300 | 16.0k | if (text == (const char *) NULL) |
301 | 0 | return((char **) NULL); |
302 | | /* |
303 | | Determine the number of arguments. |
304 | | |
305 | | style="fill: red; stroke: blue; stroke-width: 3" |
306 | | */ |
307 | 4.72M | for (p=text; *p != '\0'; p++) |
308 | 4.70M | if (*p == ':') |
309 | 1.22M | alloc_tokens+=2; |
310 | 16.0k | if (alloc_tokens == 0) |
311 | 182 | return((char **) NULL); |
312 | 15.9k | if (alloc_tokens >= MaxStyleTokens) |
313 | 2.40k | alloc_tokens=MaxStyleTokens; |
314 | 15.9k | tokens=MagickAllocateMemory(char **,(alloc_tokens+2)*sizeof(*tokens)); |
315 | 15.9k | if (tokens == (char **) NULL) |
316 | 0 | { |
317 | 0 | ThrowException3(svg_info->exception,ResourceLimitError, |
318 | 0 | MemoryAllocationFailed,UnableToConvertStringToTokens); |
319 | 0 | return((char **) NULL); |
320 | 0 | } |
321 | 15.9k | (void) memset(tokens,0,(alloc_tokens+2)*sizeof(*tokens)); |
322 | | /* |
323 | | Convert string to an ASCII list. |
324 | | */ |
325 | 15.9k | i=0; |
326 | 15.9k | p=text; |
327 | 15.9k | iListFront = 0; |
328 | 3.92M | for (q=p; *q != '\0'; q++) |
329 | 3.91M | { |
330 | | /* |
331 | | ':' terminates the style element (e.g., fill:) |
332 | | ';' terminates the style element value (e.g., red) |
333 | | */ |
334 | 3.91M | if ((*q != ':') && (*q != ';') && (*q != '\0')) |
335 | 3.16M | continue; |
336 | 752k | tokens[i]=MagickAllocateMemory(char *,(size_t) (q-p+1)); |
337 | 752k | if (tokens[i] == NULL) |
338 | 0 | { |
339 | 0 | ThrowException3(svg_info->exception,ResourceLimitError, |
340 | 0 | MemoryAllocationFailed,UnableToConvertStringToTokens); |
341 | 0 | break; |
342 | 0 | } |
343 | 752k | MagickCopySubstrToToken(tokens[i], p, q-p); |
344 | 752k | (void) MagickStripString(tokens[i]); |
345 | | /* |
346 | | Check for "font-size", which we will move to the first position in |
347 | | the list. This will ensure that any following numerical conversions |
348 | | that depend on the font size will use the new value. |
349 | | */ |
350 | 752k | if ( (i & 1) == 0 ) /*element name*/ |
351 | 381k | IsFontSize = (LocaleCompare("font-size",tokens[i]) == 0) ? MagickTrue : MagickFalse; |
352 | 371k | else if ( IsFontSize ) |
353 | 2.54k | {/*found font-size/value pair*/ |
354 | 2.54k | if ( (i-1) == iListFront ) |
355 | 75 | iListFront += 2; /* already at front of list */ |
356 | 2.47k | else |
357 | 2.47k | { |
358 | | /* move "font-size" and value to top of list */ |
359 | 2.47k | char * pToken = tokens[iListFront]; |
360 | 2.47k | tokens[iListFront] = tokens[i-1]; |
361 | 2.47k | tokens[i-1] = pToken; |
362 | 2.47k | iListFront++; |
363 | 2.47k | pToken = tokens[iListFront]; |
364 | 2.47k | tokens[iListFront] = tokens[i]; |
365 | 2.47k | tokens[i] = pToken; |
366 | 2.47k | iListFront++; |
367 | 2.47k | } |
368 | 2.54k | }/*found font-size/value pair*/ |
369 | 752k | i++; |
370 | 752k | if (i >= alloc_tokens) |
371 | 3.47k | break; |
372 | 749k | p=q+1; |
373 | 749k | } |
374 | 15.9k | if (i < alloc_tokens) |
375 | 12.4k | { |
376 | 12.4k | tokens[i]=MagickAllocateMemory(char *,(size_t) (q-p+1)); |
377 | 12.4k | if (tokens[i] == NULL) |
378 | 0 | { |
379 | 0 | ThrowException3(svg_info->exception,ResourceLimitError, |
380 | 0 | MemoryAllocationFailed,UnableToConvertStringToTokens); |
381 | 0 | } |
382 | 12.4k | else |
383 | 12.4k | { |
384 | 12.4k | MagickCopySubstrToToken(tokens[i], p, q-p); |
385 | 12.4k | (void) MagickStripString(tokens[i]); |
386 | 12.4k | i++; |
387 | 12.4k | } |
388 | 12.4k | } |
389 | 15.9k | tokens[i]=(char *) NULL; |
390 | 15.9k | *number_tokens=i; |
391 | 15.9k | return(tokens); |
392 | 15.9k | } |
393 | | |
394 | | |
395 | | #define THROW_GET_TRANSFORM_TOKENS_EXCEPTION() \ |
396 | 0 | do { \ |
397 | 0 | ThrowException3(svg_info->exception,ResourceLimitError, \ |
398 | 0 | MemoryAllocationFailed,UnableToConvertStringToTokens); \ |
399 | 0 | } while (0) |
400 | | |
401 | | static char **GetTransformTokens(const SVGInfo *svg_info,const char *text, |
402 | | size_t *number_tokens) |
403 | 22.2k | { |
404 | 369k | #define MaxTransformTokens 256 |
405 | | |
406 | 22.2k | char |
407 | 22.2k | **tokens; |
408 | | |
409 | 22.2k | register const char |
410 | 22.2k | *p, |
411 | 22.2k | *q; |
412 | | |
413 | 22.2k | register size_t |
414 | 22.2k | i; |
415 | | |
416 | 22.2k | size_t |
417 | 22.2k | alloc_tokens; |
418 | | |
419 | 22.2k | assert(svg_info->signature == MagickSignature); |
420 | 22.2k | *number_tokens=0; |
421 | 22.2k | if (text == (const char *) NULL) |
422 | 0 | return((char **) NULL); |
423 | | |
424 | 22.2k | alloc_tokens=8; |
425 | 22.2k | tokens=MagickAllocateMemory(char **,(alloc_tokens+2)*sizeof(*tokens)); |
426 | 22.2k | if (tokens == (char **) NULL) |
427 | 0 | { |
428 | 0 | ThrowException3(svg_info->exception,ResourceLimitError, |
429 | 0 | MemoryAllocationFailed,UnableToConvertStringToTokens); |
430 | 0 | return((char **) NULL); |
431 | 0 | } |
432 | | /* |
433 | | Convert string to an ASCII list. |
434 | | */ |
435 | 22.2k | i=0; |
436 | 22.2k | p=text; |
437 | 2.53M | for (q=p; *q != '\0'; q++) |
438 | 2.51M | { |
439 | 2.51M | if ((*q != '(') && (*q != ')') && (*q != '\0')) |
440 | 2.16M | continue; |
441 | 347k | if (i == alloc_tokens) |
442 | 8.99k | { |
443 | 8.99k | alloc_tokens <<= 1; |
444 | 8.99k | MagickReallocMemory(char **,tokens,(alloc_tokens+2)*sizeof(*tokens)); |
445 | 8.99k | if (tokens == (char **) NULL) |
446 | 0 | THROW_GET_TRANSFORM_TOKENS_EXCEPTION(); |
447 | 8.99k | } |
448 | | /* |
449 | | Apply an arbitrary limit on number of tokens to avoid DOS |
450 | | */ |
451 | 347k | if (i >= MaxTransformTokens) |
452 | 637 | break; |
453 | 346k | tokens[i]=MagickAllocateMemory(char *,(size_t) (q-p+1)); |
454 | 346k | if (tokens[i] == NULL) |
455 | 0 | THROW_GET_TRANSFORM_TOKENS_EXCEPTION(); |
456 | 346k | MagickCopySubstrToToken(tokens[i], p, q-p); |
457 | 346k | (void) MagickStripString(tokens[i]); |
458 | 346k | i++; |
459 | 346k | p=q+1; |
460 | 346k | } |
461 | 22.2k | if (i < MaxTransformTokens) |
462 | 21.6k | { |
463 | 21.6k | tokens[i]=MagickAllocateMemory(char *,(size_t) (q-p+1)); |
464 | 21.6k | if (tokens[i] == NULL) |
465 | 0 | THROW_GET_TRANSFORM_TOKENS_EXCEPTION(); |
466 | 21.6k | MagickCopySubstrToToken(tokens[i], p, q-p); |
467 | 21.6k | (void) MagickStripString(tokens[i]); |
468 | 21.6k | i++; |
469 | 21.6k | } |
470 | 22.2k | tokens[i]=(char *) NULL; |
471 | 22.2k | *number_tokens=i; |
472 | 22.2k | return(tokens); |
473 | 22.2k | } |
474 | | |
475 | | #if defined(__cplusplus) || defined(c_plusplus) |
476 | | extern "C" { |
477 | | #endif |
478 | | |
479 | | |
480 | | static void SVGStartElement(void *context,const xmlChar *name, |
481 | | const xmlChar **attributes); |
482 | | |
483 | | static void SVGEndElement(void *context,const xmlChar *name); |
484 | | |
485 | | static void SVGCharacters(void *context,const xmlChar *c,int length); |
486 | | |
487 | | static void SVGWarning(void *context,const char *format,...); |
488 | | |
489 | | ModuleExport void RegisterSVGImage(void); |
490 | | |
491 | | ModuleExport void UnregisterSVGImage(void); |
492 | | |
493 | | #if defined(__cplusplus) || defined(c_plusplus) |
494 | | } |
495 | | #endif |
496 | | |
497 | | |
498 | | |
499 | | |
500 | | |
501 | | /* |
502 | | Code from SVGStartElement() that processed transform="..." has been refactored |
503 | | into new function SVGProcessTransformString(). |
504 | | */ |
505 | | static void |
506 | | SVGProcessTransformString(const SVGInfo *svg_info,char const *TransformString) |
507 | 22.2k | {/*SVGProcessTransformString*/ |
508 | | |
509 | 22.2k | char |
510 | 22.2k | **tokens; |
511 | | |
512 | 22.2k | AffineMatrix |
513 | 22.2k | affine, |
514 | 22.2k | current, |
515 | 22.2k | transform; |
516 | | |
517 | 22.2k | char |
518 | 22.2k | *p = NULL, |
519 | 22.2k | token[MaxTextExtent]; |
520 | | |
521 | 22.2k | size_t |
522 | 22.2k | j, |
523 | 22.2k | number_tokens = 0; |
524 | | |
525 | 22.2k | assert(svg_info->signature == MagickSignature); |
526 | 22.2k | IdentityAffine(&transform); |
527 | 22.2k | (void) LogMagickEvent(CoderEvent,GetMagickModule()," "); |
528 | 22.2k | tokens=GetTransformTokens(svg_info,TransformString,&number_tokens); |
529 | 22.2k | if ((tokens != (char **) NULL) && (number_tokens > 0)) |
530 | 22.2k | {/*if ((tokens != (char **) NULL) && (number_tokens > 0))*/ |
531 | | |
532 | 22.2k | const char |
533 | 22.2k | *keyword = NULL, |
534 | 22.2k | *value = NULL; |
535 | | |
536 | 199k | for (j=0; j < (number_tokens-1); j+=2) |
537 | 177k | {/*j token loop*/ |
538 | | |
539 | 177k | keyword=(char *) tokens[j]; /* matrix, rotate, etc. */ |
540 | 177k | value=(char *) tokens[j+1]; /* associated numerical values */ |
541 | 177k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
542 | 177k | " %.1024s: %.1024s",keyword,value); |
543 | 177k | current=transform; |
544 | 177k | IdentityAffine(&affine); |
545 | 177k | switch (*keyword) |
546 | 177k | {/*keyword switch*/ |
547 | | |
548 | 1.59k | case 'M': |
549 | 2.84k | case 'm': |
550 | 2.84k | {/*Mm*/ |
551 | 2.84k | if (LocaleCompare(keyword,"matrix") == 0) |
552 | 0 | { |
553 | 0 | p=(char *) value; |
554 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
555 | 0 | affine.sx=MagickAtoF(token); |
556 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
557 | 0 | if (*token == ',') |
558 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
559 | 0 | affine.rx=MagickAtoF(token); |
560 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
561 | 0 | if (*token == ',') |
562 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
563 | 0 | affine.ry=MagickAtoF(token); |
564 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
565 | 0 | if (*token == ',') |
566 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
567 | 0 | affine.sy=MagickAtoF(token); |
568 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
569 | 0 | if (*token == ',') |
570 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
571 | 0 | affine.tx=MagickAtoF(token); |
572 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
573 | 0 | if (*token == ',') |
574 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
575 | 0 | affine.ty=MagickAtoF(token); |
576 | 0 | break; |
577 | 0 | } |
578 | 2.84k | break; |
579 | 2.84k | }/*Mm*/ |
580 | | |
581 | 2.84k | case 'R': |
582 | 9.34k | case 'r': |
583 | 9.34k | {/*Rr*/ |
584 | 9.34k | if (LocaleCompare(keyword,"rotate") == 0) |
585 | 2.99k | { |
586 | 2.99k | double |
587 | 2.99k | angle; |
588 | | |
589 | 2.99k | angle=GetUserSpaceCoordinateValue(svg_info,0,value,MagickFalse); |
590 | 2.99k | affine.sx=cos(DegreesToRadians(fmod(angle,360.0))); |
591 | 2.99k | affine.rx=sin(DegreesToRadians(fmod(angle,360.0))); |
592 | 2.99k | affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0)))); |
593 | 2.99k | affine.sy=cos(DegreesToRadians(fmod(angle,360.0))); |
594 | 2.99k | break; |
595 | 2.99k | } |
596 | 6.35k | break; |
597 | 9.34k | }/*Rr*/ |
598 | | |
599 | 6.35k | case 'S': |
600 | 15.8k | case 's': |
601 | 15.8k | {/*Ss*/ |
602 | 15.8k | if (LocaleCompare(keyword,"scale") == 0) |
603 | 8.36k | { |
604 | 164k | for (p=(char *) value; *p != '\0'; p++) |
605 | 157k | if (isspace((int) (*p)) || (*p == ',')) |
606 | 1.60k | break; |
607 | 8.36k | affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
608 | 8.36k | affine.sy=affine.sx; |
609 | 8.36k | if (*p != '\0') |
610 | 1.60k | affine.sy= |
611 | 1.60k | GetUserSpaceCoordinateValue(svg_info,-1,p+1,MagickFalse); |
612 | 8.36k | svg_info->scale[svg_info->n]=ExpandAffine(&affine); |
613 | 8.36k | break; |
614 | 8.36k | } |
615 | 7.48k | if (LocaleCompare(keyword,"skewX") == 0) |
616 | 26 | { |
617 | 26 | affine.sx=svg_info->affine.sx; |
618 | 26 | affine.ry=tan(DegreesToRadians(fmod( |
619 | 26 | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse), |
620 | 26 | 360.0))); |
621 | 26 | affine.sy=svg_info->affine.sy; |
622 | 26 | break; |
623 | 26 | } |
624 | 7.46k | if (LocaleCompare(keyword,"skewY") == 0) |
625 | 94 | { |
626 | 94 | affine.sx=svg_info->affine.sx; |
627 | 94 | affine.rx=tan(DegreesToRadians(fmod( |
628 | 94 | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse), |
629 | 94 | 360.0))); |
630 | 94 | affine.sy=svg_info->affine.sy; |
631 | 94 | break; |
632 | 94 | } |
633 | 7.36k | break; |
634 | 7.46k | }/*Ss*/ |
635 | | |
636 | 7.36k | case 'T': |
637 | 6.93k | case 't': |
638 | 6.93k | {/*Tt*/ |
639 | 6.93k | if (LocaleCompare(keyword,"translate") == 0) |
640 | 4.17k | { |
641 | 45.9k | for (p=(char *) value; *p != '\0'; p++) |
642 | 44.5k | if (isspace((int) (*p)) || (*p == ',')) |
643 | 2.75k | break; |
644 | 4.17k | affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
645 | 4.17k | affine.ty=affine.tx; |
646 | 4.17k | if (*p != '\0') |
647 | 2.75k | affine.ty= |
648 | 2.75k | GetUserSpaceCoordinateValue(svg_info,-1,p+1,MagickFalse); |
649 | 4.17k | break; |
650 | 4.17k | } |
651 | 2.75k | break; |
652 | 6.93k | }/*Tt*/ |
653 | | |
654 | 142k | default: |
655 | 142k | break; |
656 | | |
657 | 177k | }/*keyword switch*/ |
658 | | |
659 | 177k | transform.sx=current.sx*affine.sx+current.ry*affine.rx; |
660 | 177k | transform.rx=current.rx*affine.sx+current.sy*affine.rx; |
661 | 177k | transform.ry=current.sx*affine.ry+current.ry*affine.sy; |
662 | 177k | transform.sy=current.rx*affine.ry+current.sy*affine.sy; |
663 | 177k | transform.tx=current.sx*affine.tx+current.ry*affine.ty+ |
664 | 177k | current.tx; |
665 | 177k | transform.ty=current.rx*affine.tx+current.sy*affine.ty+ |
666 | 177k | current.ty; |
667 | | |
668 | 177k | }/*j token loop*/ |
669 | | |
670 | 22.2k | MVGPrintf(svg_info->file,"affine %g %g %g %g %g %g\n", |
671 | 22.2k | transform.sx,transform.rx,transform.ry,transform.sy, |
672 | 22.2k | transform.tx,transform.ty); |
673 | | |
674 | 22.2k | }/*if ((tokens != (char **) NULL) && (number_tokens > 0))*/ |
675 | | |
676 | | /* clean up memory used for tokens */ |
677 | 22.2k | if (tokens != (char **) NULL) |
678 | 22.2k | { |
679 | 390k | for (j=0; tokens[j] != (char *) NULL; j++) |
680 | 368k | MagickFreeMemory(tokens[j]); |
681 | 22.2k | MagickFreeMemory(tokens); |
682 | 22.2k | } |
683 | | |
684 | 22.2k | }/*SVGProcessTransformString*/ |
685 | | |
686 | | |
687 | | static void |
688 | | SVGStartElement(void *context,const xmlChar *name, |
689 | | const xmlChar **attributes) |
690 | 1.36M | { |
691 | 1.36M | char |
692 | 1.36M | *color = NULL, |
693 | 1.36M | id[MaxTextExtent], |
694 | 1.36M | *p = NULL, |
695 | 1.36M | token[MaxTextExtent], |
696 | 1.36M | *units = NULL; |
697 | | |
698 | 1.36M | const char |
699 | 1.36M | *keyword = NULL, |
700 | 1.36M | *value = NULL; |
701 | | |
702 | 1.36M | size_t |
703 | 1.36M | number_tokens = 0; |
704 | | |
705 | 1.36M | SVGInfo |
706 | 1.36M | *svg_info; |
707 | | |
708 | 1.36M | xmlParserCtxtPtr |
709 | 1.36M | parser; |
710 | | |
711 | 1.36M | size_t |
712 | 1.36M | i, |
713 | 1.36M | j; |
714 | | |
715 | 1.36M | char |
716 | 1.36M | svg_element_background_color[MaxTextExtent]; /* to support style="background:color" */ |
717 | | |
718 | 1.36M | MagickBool |
719 | 1.36M | IsTSpan = MagickFalse, |
720 | 1.36M | IsTextOrTSpan = MagickFalse; |
721 | | |
722 | | /* |
723 | | Called when an opening tag has been processed. |
724 | | */ |
725 | 1.36M | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
726 | 1.36M | " SAX.startElement(%.1024s",name); |
727 | 1.36M | id[0]='\0'; |
728 | 1.36M | token[0]='\0'; |
729 | 1.36M | parser=(xmlParserCtxtPtr) context; |
730 | 1.36M | svg_info=(SVGInfo *) parser->_private; |
731 | 1.36M | assert(svg_info->signature == MagickSignature); |
732 | 1.36M | svg_info->n++; |
733 | 1.36M | MagickReallocMemory(double *,svg_info->scale,MagickArraySize((size_t)svg_info->n+1,sizeof(double))); |
734 | 1.36M | if (svg_info->scale == (double *) NULL) |
735 | 0 | { |
736 | 0 | ThrowException(svg_info->exception,ResourceLimitError, |
737 | 0 | MemoryAllocationFailed,"unable to convert SVG image"); |
738 | 0 | return; |
739 | 0 | } |
740 | 1.36M | svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1]; |
741 | 1.36M | color=AcquireString("none"); |
742 | 1.36M | units=AcquireString("userSpaceOnUse"); |
743 | 1.36M | value=(const char *) NULL; |
744 | 1.36M | svg_element_background_color[0]='\0'; |
745 | 1.36M | IsTextOrTSpan = IsTSpan = LocaleCompare((char *) name,"tspan") == 0; /* need to know this early */ |
746 | | /* |
747 | | According to the SVG spec, for the following SVG elements, if the x or y |
748 | | attribute is not specified, the effect is as if a value of "0" were specified. |
749 | | */ |
750 | 1.36M | if ( |
751 | 1.36M | (LocaleCompare((char *) name,"image") == 0) |
752 | 1.36M | || (LocaleCompare((char *) name,"pattern") == 0) |
753 | 1.36M | || (LocaleCompare((char *) name,"rect") == 0) |
754 | 1.36M | || (LocaleCompare((char *) name,"text") == 0) |
755 | 1.36M | || (LocaleCompare((char *) name,"use") == 0) |
756 | 1.36M | ) |
757 | 79.0k | { |
758 | 79.0k | svg_info->bounds.x = svg_info->bounds.y = 0; |
759 | 79.0k | } |
760 | | /* |
761 | | NOTE: SVG spec makes similar statements for cx,cy of circle and ellipse, and |
762 | | x1,y1,x2,y2 of line, but these are zeroed out initially, AND at the end of |
763 | | SVGEndElement() after they have been used. |
764 | | */ |
765 | | |
766 | | /* |
767 | | When "font-size" is (or is contained in) one of the attributes for this SVG |
768 | | element, we want it to be processed first so that any numerical conversions |
769 | | that depend on the font size will use the new value. So we will first scan |
770 | | the attribute list and move any "font-size", "class" (which may contain a |
771 | | "font-size"), or "style" (which may contain a "font-size") attributes to the |
772 | | front of the attribute list. |
773 | | |
774 | | For now we will ignore the possibility that "font-size" may be specified |
775 | | more than once among "font-size", "class", and "style". However, the |
776 | | relative order among these three will be preserved. |
777 | | */ |
778 | 1.36M | if (attributes != (const xmlChar **) NULL) |
779 | 289k | {/*have some attributes*/ |
780 | | |
781 | 289k | size_t iListFront = 0; |
782 | 830k | for ( i = 0; (attributes[i] != (const xmlChar *) NULL); i += 2 ) |
783 | 540k | {/*attribute[i]*/ |
784 | | |
785 | 540k | keyword = (const char *) attributes[i]; |
786 | 540k | if ( (LocaleCompare(keyword,"font-size") == 0) |
787 | 540k | || (LocaleCompare(keyword,"class") == 0) |
788 | 540k | || (LocaleCompare(keyword,"style") == 0) |
789 | 540k | ) |
790 | 23.1k | {/*(possible) font-size*/ |
791 | | |
792 | 23.1k | if ( i == iListFront ) |
793 | 17.7k | iListFront += 2; /* already at front of list */ |
794 | 5.34k | else |
795 | 5.34k | { |
796 | | /* move to front of list */ |
797 | 5.34k | const xmlChar * pAttr = attributes[iListFront]; |
798 | 5.34k | attributes[iListFront] = attributes[i]; |
799 | 5.34k | attributes[i] = pAttr; |
800 | 5.34k | iListFront++; |
801 | 5.34k | pAttr = attributes[iListFront]; |
802 | 5.34k | attributes[iListFront] = attributes[i+1]; |
803 | 5.34k | attributes[i+1] = pAttr; |
804 | 5.34k | iListFront++; |
805 | 5.34k | } |
806 | | |
807 | 23.1k | }/*(possible) font-size*/ |
808 | | |
809 | 540k | }/*attribute[i]*/ |
810 | | |
811 | 289k | }/*have some attributes*/ |
812 | | |
813 | 1.36M | if (attributes != (const xmlChar **) NULL) |
814 | 810k | for (i=0; (svg_info->exception->severity < ErrorException) && |
815 | 810k | (attributes[i] != (const xmlChar *) NULL); i+=2) |
816 | 520k | { |
817 | 520k | keyword=(const char *) attributes[i]; |
818 | 520k | value=(const char *) attributes[i+1]; |
819 | 520k | switch (*keyword) |
820 | 520k | { |
821 | 2.56k | case 'C': |
822 | 27.5k | case 'c': |
823 | 27.5k | { |
824 | 27.5k | if (LocaleCompare(keyword,"cx") == 0) |
825 | 3.15k | { |
826 | 3.15k | svg_info->element.cx= |
827 | 3.15k | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
828 | 3.15k | break; |
829 | 3.15k | } |
830 | 24.3k | if (LocaleCompare(keyword,"cy") == 0) |
831 | 2.97k | { |
832 | 2.97k | svg_info->element.cy= |
833 | 2.97k | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
834 | 2.97k | break; |
835 | 2.97k | } |
836 | 21.4k | break; |
837 | 24.3k | } |
838 | 21.4k | case 'F': |
839 | 26.0k | case 'f': |
840 | 26.0k | { |
841 | 26.0k | if (LocaleCompare(keyword,"fx") == 0) |
842 | 313 | { |
843 | 313 | svg_info->element.major= |
844 | 313 | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
845 | 313 | break; |
846 | 313 | } |
847 | 25.7k | if (LocaleCompare(keyword,"fy") == 0) |
848 | 1.69k | { |
849 | 1.69k | svg_info->element.minor= |
850 | 1.69k | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
851 | 1.69k | break; |
852 | 1.69k | } |
853 | 24.0k | break; |
854 | 25.7k | } |
855 | 24.0k | case 'H': |
856 | 22.2k | case 'h': |
857 | 22.2k | { |
858 | 22.2k | if (LocaleCompare(keyword,"height") == 0) |
859 | 12.0k | { |
860 | 12.0k | svg_info->bounds.height= |
861 | 12.0k | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickTrue); |
862 | 12.0k | break; |
863 | 12.0k | } |
864 | 10.2k | break; |
865 | 22.2k | } |
866 | 10.2k | case 'I': |
867 | 31.7k | case 'i': |
868 | 31.7k | { |
869 | 31.7k | if (LocaleCompare(keyword,"id") == 0) |
870 | 29.9k | { |
871 | 29.9k | (void) strlcpy(id,value,MaxTextExtent); |
872 | | /* track elements inside <defs> that have an "id" */ |
873 | 29.9k | if ( (svg_info->defsPushCount > 0) |
874 | 29.9k | && (svg_info->idLevelInsideDefs == 0) /* do not allow nested "id" elements for now */ |
875 | 29.9k | && (LocaleCompare((const char *)name,"clipPath") != 0) /* handled separately */ |
876 | 29.9k | && (LocaleCompare((const char *)name,"mask") != 0) /* handled separately */ |
877 | 29.9k | ) |
878 | 2.26k | svg_info->idLevelInsideDefs = svg_info->n; |
879 | 29.9k | break; |
880 | 29.9k | } |
881 | 1.76k | break; |
882 | 31.7k | } |
883 | 7.15k | case 'R': |
884 | 39.7k | case 'r': |
885 | 39.7k | { |
886 | 39.7k | if (LocaleCompare(keyword,"r") == 0) |
887 | 9.63k | { |
888 | 9.63k | svg_info->element.angle= |
889 | 9.63k | GetUserSpaceCoordinateValue(svg_info,0,value,MagickFalse); |
890 | 9.63k | break; |
891 | 9.63k | } |
892 | 30.1k | break; |
893 | 39.7k | } |
894 | 30.1k | case 'W': |
895 | 17.3k | case 'w': |
896 | 17.3k | { |
897 | 17.3k | if (LocaleCompare(keyword,"width") == 0) |
898 | 14.4k | { |
899 | 14.4k | svg_info->bounds.width= |
900 | 14.4k | GetUserSpaceCoordinateValue(svg_info,1,value,MagickTrue); |
901 | 14.4k | break; |
902 | 14.4k | } |
903 | 2.91k | break; |
904 | 17.3k | } |
905 | 4.29k | case 'X': |
906 | 35.6k | case 'x': |
907 | 35.6k | { |
908 | 35.6k | if (LocaleCompare(keyword,"x") == 0) |
909 | 5.62k | { |
910 | | /* if processing a tspan, preserve the current bounds.x, which belongs to the |
911 | | previously processed text or tspan; the bounds.x for the current tspan will |
912 | | be set later */ |
913 | 5.62k | if (!IsTSpan) |
914 | 5.52k | svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
915 | 5.62k | break; |
916 | 5.62k | } |
917 | 30.0k | if (LocaleCompare(keyword,"x1") == 0) |
918 | 1.01k | { |
919 | 1.01k | svg_info->segment.x1= |
920 | 1.01k | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
921 | 1.01k | break; |
922 | 1.01k | } |
923 | 29.0k | if (LocaleCompare(keyword,"x2") == 0) |
924 | 2.67k | { |
925 | 2.67k | svg_info->segment.x2= |
926 | 2.67k | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
927 | 2.67k | break; |
928 | 2.67k | } |
929 | 26.3k | break; |
930 | 29.0k | } |
931 | 26.3k | case 'Y': |
932 | 9.16k | case 'y': |
933 | 9.16k | { |
934 | 9.16k | if (LocaleCompare(keyword,"y") == 0) |
935 | 551 | { |
936 | | /* if processing a tspan, preserve the current bounds.y, which belongs to the |
937 | | previously processed text or tspan; the bounds.y for the current tspan will |
938 | | be set later */ |
939 | 551 | if (!IsTSpan) |
940 | 472 | svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
941 | 551 | break; |
942 | 551 | } |
943 | 8.61k | if (LocaleCompare(keyword,"y1") == 0) |
944 | 912 | { |
945 | 912 | svg_info->segment.y1= |
946 | 912 | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
947 | 912 | break; |
948 | 912 | } |
949 | 7.69k | if (LocaleCompare(keyword,"y2") == 0) |
950 | 5.91k | { |
951 | 5.91k | svg_info->segment.y2= |
952 | 5.91k | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
953 | 5.91k | break; |
954 | 5.91k | } |
955 | 1.78k | break; |
956 | 7.69k | } |
957 | 310k | default: |
958 | 310k | break; |
959 | 520k | } |
960 | 520k | } |
961 | 1.36M | if (svg_info->exception->severity >= ErrorException) |
962 | 23.0k | goto svg_start_element_error; |
963 | 1.34M | if (strchr((char *) name,':') != (char *) NULL) |
964 | 46.3k | { |
965 | | /* |
966 | | Skip over namespace. |
967 | | */ |
968 | 113k | for ( ; *name != ':'; name++) ; |
969 | 46.3k | name++; |
970 | 46.3k | } |
971 | 1.34M | switch (*name) |
972 | 1.34M | { |
973 | 28.1k | case 'C': |
974 | 67.0k | case 'c': |
975 | 67.0k | { |
976 | 67.0k | if (LocaleCompare((char *) name,"circle") == 0) |
977 | 5.81k | { |
978 | 5.81k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "push id" if warranted */ |
979 | 110 | MVGPrintf(svg_info->file,"push id '%s'\n",id); |
980 | 5.81k | MVGPrintf(svg_info->file,"push graphic-context\n"); |
981 | 5.81k | break; |
982 | 5.81k | } |
983 | 61.2k | if (LocaleCompare((char *) name,"clipPath") == 0) |
984 | 85 | { |
985 | 85 | MVGPrintf(svg_info->file,"push clip-path '%s'\n",id); |
986 | 85 | break; |
987 | 85 | } |
988 | 61.1k | break; |
989 | 61.2k | } |
990 | 61.1k | case 'D': |
991 | 29.1k | case 'd': |
992 | 29.1k | { |
993 | 29.1k | if (LocaleCompare((char *) name,"defs") == 0) |
994 | 5.01k | { |
995 | 5.01k | svg_info->defsPushCount++; |
996 | 5.01k | MVGPrintf(svg_info->file,"push defs\n"); |
997 | 5.01k | break; |
998 | 5.01k | } |
999 | 24.1k | break; |
1000 | 29.1k | } |
1001 | 44.4k | case 'E': |
1002 | 54.7k | case 'e': |
1003 | 54.7k | { |
1004 | 54.7k | if (LocaleCompare((char *) name,"ellipse") == 0) |
1005 | 4.15k | { |
1006 | 4.15k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "push id" if warranted */ |
1007 | 36 | MVGPrintf(svg_info->file,"push id '%s'\n",id); |
1008 | 4.15k | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1009 | 4.15k | break; |
1010 | 4.15k | } |
1011 | 50.6k | break; |
1012 | 54.7k | } |
1013 | 50.6k | case 'F': |
1014 | 13.5k | case 'f': |
1015 | 13.5k | { |
1016 | | /* |
1017 | | For now we are ignoring "foreignObject". However, we do a push/pop |
1018 | | graphic-context so that any settings (e.g., fill) are consumed and |
1019 | | discarded. Otherwise they might persist and "leak" into the graphic |
1020 | | elements that follow. |
1021 | | */ |
1022 | 13.5k | if (LocaleCompare((char *) name,"foreignObject") == 0) |
1023 | 105 | { |
1024 | 105 | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1025 | 105 | break; |
1026 | 105 | } |
1027 | 13.4k | break; |
1028 | 13.5k | } |
1029 | 13.4k | case 'G': |
1030 | 89.3k | case 'g': |
1031 | 89.3k | { |
1032 | 89.3k | if (LocaleCompare((char *) name,"g") == 0) |
1033 | 83.9k | { |
1034 | 83.9k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "push id" if warranted */ |
1035 | 510 | MVGPrintf(svg_info->file,"push id '%s'\n",id); |
1036 | 83.9k | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1037 | 83.9k | break; |
1038 | 83.9k | } |
1039 | 5.41k | break; |
1040 | 89.3k | } |
1041 | 10.1k | case 'I': |
1042 | 21.4k | case 'i': |
1043 | 21.4k | { |
1044 | 21.4k | if (LocaleCompare((char *) name,"image") == 0) |
1045 | 1.47k | { |
1046 | 1.47k | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1047 | 1.47k | break; |
1048 | 1.47k | } |
1049 | 19.9k | break; |
1050 | 21.4k | } |
1051 | 19.9k | case 'L': |
1052 | 32.4k | case 'l': |
1053 | 32.4k | { |
1054 | 32.4k | if (LocaleCompare((char *) name,"line") == 0) |
1055 | 3.22k | { |
1056 | 3.22k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "push id" if warranted */ |
1057 | 96 | MVGPrintf(svg_info->file,"push id '%s'\n",id); |
1058 | 3.22k | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1059 | 3.22k | break; |
1060 | 3.22k | } |
1061 | 29.2k | if (LocaleCompare((char *) name,"linearGradient") == 0) |
1062 | 3.19k | { |
1063 | 3.19k | MVGPrintf(svg_info->file,"push gradient '%s' linear %g,%g %g,%g\n",id, |
1064 | 3.19k | svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2, |
1065 | 3.19k | svg_info->segment.y2); |
1066 | 3.19k | break; |
1067 | 3.19k | } |
1068 | 26.0k | break; |
1069 | 29.2k | } |
1070 | 26.0k | case 'M': |
1071 | 44.9k | case 'm': |
1072 | 44.9k | { |
1073 | 44.9k | if (LocaleCompare((char *) name,"mask") == 0) /* added mask */ |
1074 | 494 | { |
1075 | 494 | MVGPrintf(svg_info->file,"push mask '%s'\n",id); |
1076 | 494 | break; |
1077 | 494 | } |
1078 | 44.4k | break; |
1079 | 44.9k | } |
1080 | 44.4k | case 'P': |
1081 | 30.6k | case 'p': |
1082 | 30.6k | { |
1083 | 30.6k | if (LocaleCompare((char *) name,"path") == 0) |
1084 | 13.3k | { |
1085 | 13.3k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "push id" if warranted */ |
1086 | 482 | MVGPrintf(svg_info->file,"push id '%s'\n",id); |
1087 | 13.3k | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1088 | 13.3k | break; |
1089 | 13.3k | } |
1090 | 17.3k | if (LocaleCompare((char *) name,"pattern") == 0) |
1091 | 6.22k | { |
1092 | 6.22k | MVGPrintf(svg_info->file,"push pattern '%s' %g,%g %g,%g\n",id, |
1093 | 6.22k | svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width, |
1094 | 6.22k | svg_info->bounds.height); |
1095 | 6.22k | break; |
1096 | 6.22k | } |
1097 | 11.0k | if (LocaleCompare((char *) name,"polygon") == 0) |
1098 | 531 | { |
1099 | 531 | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "push id" if warranted */ |
1100 | 8 | MVGPrintf(svg_info->file,"push id '%s'\n",id); |
1101 | 531 | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1102 | 531 | break; |
1103 | 531 | } |
1104 | 10.5k | if (LocaleCompare((char *) name,"polyline") == 0) |
1105 | 421 | { |
1106 | 421 | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "push id" if warranted */ |
1107 | 18 | MVGPrintf(svg_info->file,"push id '%s'\n",id); |
1108 | 421 | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1109 | 421 | break; |
1110 | 421 | } |
1111 | 10.1k | break; |
1112 | 10.5k | } |
1113 | 10.1k | case 'R': |
1114 | 77.1k | case 'r': |
1115 | 77.1k | { |
1116 | 77.1k | if (LocaleCompare((char *) name,"radialGradient") == 0) |
1117 | 1.17k | { |
1118 | 1.17k | if (svg_info->element.angle < 0.0) |
1119 | 0 | { |
1120 | 0 | errno=0; |
1121 | 0 | ThrowException(svg_info->exception,DrawError,InvalidPrimitiveArgument, |
1122 | 0 | value); |
1123 | 0 | break; |
1124 | 0 | } |
1125 | 1.17k | MVGPrintf(svg_info->file,"push gradient '%s' radial %g,%g %g,%g %g\n", |
1126 | 1.17k | id,svg_info->element.cx,svg_info->element.cy, |
1127 | 1.17k | svg_info->element.major,svg_info->element.minor, |
1128 | 1.17k | svg_info->element.angle); |
1129 | 1.17k | break; |
1130 | 1.17k | } |
1131 | 75.9k | if (LocaleCompare((char *) name,"rect") == 0) |
1132 | 10.0k | { |
1133 | 10.0k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "push id" if warranted */ |
1134 | 850 | MVGPrintf(svg_info->file,"push id '%s'\n",id); |
1135 | 10.0k | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1136 | 10.0k | break; |
1137 | 10.0k | } |
1138 | 65.9k | break; |
1139 | 75.9k | } |
1140 | 65.9k | case 'S': |
1141 | 99.4k | case 's': |
1142 | 99.4k | { |
1143 | | /* element "style" inside <defs> */ |
1144 | 99.4k | if (LocaleCompare((char *) name,"style") == 0) |
1145 | 22.5k | { |
1146 | | /* |
1147 | | This is here more or less as a documentation aid. The real work is done when |
1148 | | we encounter </style>. |
1149 | | */ |
1150 | 22.5k | break; |
1151 | 22.5k | } |
1152 | 76.9k | if (LocaleCompare((char *) name,"svg") == 0) |
1153 | 27.2k | { |
1154 | 27.2k | svg_info->svgPushCount++; |
1155 | 27.2k | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1156 | | /* |
1157 | | Per the SVG spec, initialize the MVG coder with the following |
1158 | | SVG defaults: |
1159 | | - svg-compliant: "1" (note: internal to GM, not an SVG element) |
1160 | | - fill color: "black" |
1161 | | - fill-opacity value: "1" |
1162 | | - stroke color: "none" |
1163 | | - stroke-width value: "1" |
1164 | | - stroke-opacity value: "1" |
1165 | | - fill-rule value: "nonzero" |
1166 | | */ |
1167 | 27.2k | MVGPrintf(svg_info->file,"svg-compliant 1\n"); |
1168 | 27.2k | MVGPrintf(svg_info->file,"fill 'black'\n"); |
1169 | 27.2k | MVGPrintf(svg_info->file,"fill-opacity 1\n"); |
1170 | 27.2k | MVGPrintf(svg_info->file,"stroke 'none'\n"); |
1171 | 27.2k | MVGPrintf(svg_info->file,"stroke-width 1\n"); |
1172 | 27.2k | MVGPrintf(svg_info->file,"stroke-opacity 1\n"); |
1173 | 27.2k | MVGPrintf(svg_info->file,"fill-rule 'nonzero'\n"); |
1174 | 27.2k | break; |
1175 | 27.2k | } |
1176 | 49.6k | break; |
1177 | 76.9k | } |
1178 | 196k | case 'T': |
1179 | 352k | case 't': |
1180 | 352k | { |
1181 | 352k | if (LocaleCompare((char *) name,"text") == 0) |
1182 | 54.9k | { |
1183 | 54.9k | IsTextOrTSpan = MagickTrue; |
1184 | 54.9k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "push id" if warranted */ |
1185 | 89 | MVGPrintf(svg_info->file,"push id '%s'\n",id); |
1186 | 54.9k | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1187 | | /* update text current position */ |
1188 | 54.9k | MVGPrintf(svg_info->file,"textx %g\n",svg_info->bounds.x); |
1189 | 54.9k | MVGPrintf(svg_info->file,"texty %g\n",svg_info->bounds.y); |
1190 | 54.9k | break; |
1191 | 54.9k | } |
1192 | 297k | if (LocaleCompare((char *) name,"tspan") == 0) |
1193 | 2.66k | { |
1194 | 2.66k | IsTextOrTSpan = MagickTrue; |
1195 | 2.66k | svg_info->text_len=MagickStripString(svg_info->text); |
1196 | 2.66k | if (*svg_info->text != '\0') |
1197 | 752 | { |
1198 | 752 | char |
1199 | 752 | *text; |
1200 | | |
1201 | 752 | text=EscapeString(svg_info->text,'\''); |
1202 | 752 | MVGPrintf(svg_info->file,"textc '%s'\n",text); |
1203 | 752 | MagickFreeMemory(text); |
1204 | | /* |
1205 | | The code that used to be here to compute the next text position has been eliminated. |
1206 | | The reason is that at this point in the code we may not know the font or font size |
1207 | | (they may be hidden in a "class" definition), so we can't really do that computation. |
1208 | | This functionality is now handled by DrawImage() in render.c. |
1209 | | */ |
1210 | 752 | *svg_info->text='\0'; |
1211 | 752 | svg_info->text_len=strlen(svg_info->text); |
1212 | 752 | } |
1213 | 2.66k | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1214 | 2.66k | break; |
1215 | 2.66k | } |
1216 | 294k | break; |
1217 | 297k | } |
1218 | 294k | case 'U': |
1219 | 25.9k | case 'u': |
1220 | 25.9k | { |
1221 | 25.9k | if (LocaleCompare((char *) name,"use") == 0) |
1222 | 4.17k | { |
1223 | | /* "use" behaves like "g" */ |
1224 | 4.17k | MVGPrintf(svg_info->file,"push graphic-context\n"); |
1225 | 4.17k | break; |
1226 | 4.17k | } |
1227 | 21.8k | break; |
1228 | 25.9k | } |
1229 | 402k | default: |
1230 | 402k | break; |
1231 | 1.34M | } |
1232 | 1.34M | if (attributes != (const xmlChar **) NULL) |
1233 | 796k | for (i=0; (svg_info->exception->severity < ErrorException) && |
1234 | 796k | (attributes[i] != (const xmlChar *) NULL); i+=2) |
1235 | 520k | { |
1236 | 520k | keyword=(const char *) attributes[i]; |
1237 | 520k | value=(const char *) attributes[i+1]; |
1238 | 520k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1239 | 520k | " %.1024s = %.1024s",keyword,value); |
1240 | 520k | switch (*keyword) |
1241 | 520k | { |
1242 | 522 | case 'A': |
1243 | 7.02k | case 'a': |
1244 | 7.02k | { |
1245 | 7.02k | if (LocaleCompare(keyword,"angle") == 0) |
1246 | 0 | { |
1247 | 0 | MVGPrintf(svg_info->file,"angle %g\n", |
1248 | 0 | GetUserSpaceCoordinateValue(svg_info,0,value,MagickFalse)); |
1249 | 0 | break; |
1250 | 0 | } |
1251 | 7.02k | break; |
1252 | 7.02k | } |
1253 | 7.02k | case 'C': |
1254 | 27.5k | case 'c': |
1255 | 27.5k | { |
1256 | 27.5k | if (LocaleCompare(keyword,"class") == 0) |
1257 | 6.75k | {/*"class=classname"*/ |
1258 | 6.75k | char * pClassNames = (char * ) value; |
1259 | 6.75k | do |
1260 | 38.2k | { |
1261 | 38.2k | (void) MagickGetToken(pClassNames,&pClassNames,token,MaxTextExtent); |
1262 | 38.2k | if ( *token == ',' ) |
1263 | 1.93k | (void) MagickGetToken(pClassNames,&pClassNames,token,MaxTextExtent); |
1264 | 38.2k | if ( *token != '\0' ) |
1265 | 31.4k | MVGPrintf(svg_info->file,"class '%s'\n",token); |
1266 | 38.2k | } |
1267 | 38.2k | while ( *token != '\0' ); |
1268 | 6.75k | break; |
1269 | 6.75k | }/*"class=classname"*/ |
1270 | 20.7k | if (LocaleCompare(keyword,"clip-path") == 0) |
1271 | 0 | { |
1272 | 0 | MVGPrintf(svg_info->file,"clip-path '%s'\n",value); |
1273 | 0 | break; |
1274 | 0 | } |
1275 | 20.7k | if (LocaleCompare(keyword,"clip-rule") == 0) |
1276 | 0 | { |
1277 | 0 | MVGPrintf(svg_info->file,"clip-rule '%s'\n",value); |
1278 | 0 | break; |
1279 | 0 | } |
1280 | 20.7k | if (LocaleCompare(keyword,"clipPathUnits") == 0) |
1281 | 0 | { |
1282 | 0 | (void) CloneString(&units,value); |
1283 | 0 | MVGPrintf(svg_info->file,"clip-units '%s'\n",value); |
1284 | 0 | break; |
1285 | 0 | } |
1286 | 20.7k | if (LocaleCompare(keyword,"color") == 0) |
1287 | 81 | { |
1288 | 81 | (void) CloneString(&color,value); |
1289 | 81 | break; |
1290 | 81 | } |
1291 | 20.6k | if (LocaleCompare(keyword,"cx") == 0) |
1292 | 3.15k | { |
1293 | 3.15k | svg_info->element.cx= |
1294 | 3.15k | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
1295 | 3.15k | break; |
1296 | 3.15k | } |
1297 | 17.5k | if (LocaleCompare(keyword,"cy") == 0) |
1298 | 2.97k | { |
1299 | 2.97k | svg_info->element.cy= |
1300 | 2.97k | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
1301 | 2.97k | break; |
1302 | 2.97k | } |
1303 | 14.5k | break; |
1304 | 17.5k | } |
1305 | 24.0k | case 'D': |
1306 | 31.3k | case 'd': |
1307 | 31.3k | { |
1308 | 31.3k | if (LocaleCompare(keyword,"d") == 0) |
1309 | 4.20k | { |
1310 | 4.20k | (void) CloneString(&svg_info->vertices,value); |
1311 | 4.20k | break; |
1312 | 4.20k | } |
1313 | 27.1k | if (LocaleCompare(keyword,"dx") == 0) |
1314 | 16.0k | { |
1315 | 16.0k | double dx=GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
1316 | 16.0k | svg_info->bounds.x+=dx; /* preserve previous behavior */ |
1317 | | /* update text current position for text or tspan */ |
1318 | 16.0k | if ( IsTextOrTSpan ) |
1319 | 14.3k | { |
1320 | 14.3k | char * pUnit; |
1321 | 14.3k | (void) MagickGetToken(value,&pUnit,token,MaxTextExtent); |
1322 | 14.3k | if ( *pUnit && ((LocaleNCompare(pUnit,"em",2) == 0) || (LocaleNCompare(pUnit,"ex",2) == 0)) ) |
1323 | 454 | MVGPrintf(svg_info->file,"textdx %s\n",value); /* postpone interpretation of "em" or "ex" until we know point size */ |
1324 | 13.8k | else |
1325 | 13.8k | MVGPrintf(svg_info->file,"textdx %g\n",dx); |
1326 | 14.3k | } |
1327 | 16.0k | break; |
1328 | 16.0k | } |
1329 | 11.1k | if (LocaleCompare(keyword,"dy") == 0) |
1330 | 8.40k | { |
1331 | 8.40k | double dy=GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
1332 | 8.40k | svg_info->bounds.y+=dy; /* preserve previous behavior */ |
1333 | | /* update text current position for text or tspan */ |
1334 | 8.40k | if ( IsTextOrTSpan ) |
1335 | 2.45k | { |
1336 | 2.45k | char * pUnit; |
1337 | 2.45k | (void) MagickGetToken(value,&pUnit,token,MaxTextExtent); |
1338 | 2.45k | if ( *pUnit && ((LocaleNCompare(pUnit,"em",2) == 0) || (LocaleNCompare(pUnit,"ex",2) == 0)) ) |
1339 | 76 | MVGPrintf(svg_info->file,"textdy %s\n",value); /* postpone interpretation of "em" or "ex" until we know point size */ |
1340 | 2.38k | else |
1341 | 2.38k | MVGPrintf(svg_info->file,"textdy %g\n",dy); |
1342 | 2.45k | } |
1343 | 8.40k | break; |
1344 | 8.40k | } |
1345 | 2.73k | break; |
1346 | 11.1k | } |
1347 | 2.86k | case 'F': |
1348 | 26.0k | case 'f': |
1349 | 26.0k | { |
1350 | 26.0k | if (LocaleCompare(keyword,"fill") == 0) |
1351 | 12.4k | { |
1352 | 12.4k | if (LocaleCompare(value,"currentColor") == 0) |
1353 | 0 | { |
1354 | 0 | MVGPrintf(svg_info->file,"fill '%s'\n",color); |
1355 | 0 | break; |
1356 | 0 | } |
1357 | 12.4k | MVGPrintf(svg_info->file,"fill '%s'\n",value); |
1358 | 12.4k | break; |
1359 | 12.4k | } |
1360 | 13.6k | if (LocaleCompare(keyword,"fillcolor") == 0) |
1361 | 34 | { |
1362 | 34 | MVGPrintf(svg_info->file,"fill '%s'\n",value); |
1363 | 34 | break; |
1364 | 34 | } |
1365 | 13.5k | if (LocaleCompare(keyword,"fill-rule") == 0) |
1366 | 0 | { |
1367 | 0 | MVGPrintf(svg_info->file,"fill-rule '%s'\n",value); |
1368 | 0 | break; |
1369 | 0 | } |
1370 | 13.5k | if (LocaleCompare(keyword,"fill-opacity") == 0) |
1371 | 71 | { |
1372 | 71 | MVGPrintf(svg_info->file,"fill-opacity '%s'\n",value); |
1373 | 71 | break; |
1374 | 71 | } |
1375 | 13.4k | if (LocaleCompare(keyword,"font-family") == 0) |
1376 | 152 | { |
1377 | | /* |
1378 | | Deal with Adobe Illustrator 10.0 which double-quotes |
1379 | | font-family. Maybe we need a generalized solution for |
1380 | | this. |
1381 | | */ |
1382 | 152 | int value_length; |
1383 | 152 | if ((value[0] == '\'') && ((value_length=(int) strlen(value)) > 2) |
1384 | 152 | && (value[value_length-1] == '\'')) |
1385 | 10 | { |
1386 | 10 | MVGPrintf(svg_info->file,"font-family '%.*s'\n", |
1387 | 10 | (int)(value_length-2),value+1); |
1388 | 10 | } |
1389 | 142 | else |
1390 | 142 | { |
1391 | 142 | MVGPrintf(svg_info->file,"font-family '%s'\n",value); |
1392 | 142 | } |
1393 | 152 | break; |
1394 | 152 | } |
1395 | 13.3k | if (LocaleCompare(keyword,"font-stretch") == 0) |
1396 | 0 | { |
1397 | 0 | MVGPrintf(svg_info->file,"font-stretch '%s'\n",value); |
1398 | 0 | break; |
1399 | 0 | } |
1400 | 13.3k | if (LocaleCompare(keyword,"font-style") == 0) |
1401 | 0 | { |
1402 | 0 | MVGPrintf(svg_info->file,"font-style '%s'\n",value); |
1403 | 0 | break; |
1404 | 0 | } |
1405 | 13.3k | if (LocaleCompare(keyword,"font-size") == 0) |
1406 | 46 | { |
1407 | 46 | svg_info->pointsize= |
1408 | 46 | GetUserSpaceCoordinateValue(svg_info,0,value,MagickTrue); |
1409 | 46 | MVGPrintf(svg_info->file,"font-size %g\n",svg_info->pointsize); |
1410 | 46 | break; |
1411 | 46 | } |
1412 | 13.2k | if (LocaleCompare(keyword,"font-weight") == 0) |
1413 | 444 | { |
1414 | 444 | MVGPrintf(svg_info->file,"font-weight '%s'\n",value); |
1415 | 444 | break; |
1416 | 444 | } |
1417 | 12.8k | break; |
1418 | 13.2k | } |
1419 | 12.8k | case 'G': |
1420 | 2.02k | case 'g': |
1421 | 2.02k | { |
1422 | 2.02k | if (LocaleCompare(keyword,"gradientTransform") == 0) |
1423 | 0 | { |
1424 | 0 | char |
1425 | 0 | **tokens; |
1426 | |
|
1427 | 0 | AffineMatrix |
1428 | 0 | affine, |
1429 | 0 | current, |
1430 | 0 | transform; |
1431 | |
|
1432 | 0 | IdentityAffine(&transform); |
1433 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule()," "); |
1434 | 0 | tokens=GetTransformTokens(svg_info,value,&number_tokens); |
1435 | 0 | if ((tokens != (char **) NULL) && (number_tokens > 0)) |
1436 | 0 | { |
1437 | 0 | for (j=0; j < (number_tokens-1); j+=2) |
1438 | 0 | { |
1439 | 0 | keyword=(char *) tokens[j]; |
1440 | 0 | if (keyword == (char *) NULL) |
1441 | 0 | continue; |
1442 | 0 | value=(char *) tokens[j+1]; |
1443 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1444 | 0 | " %.1024s: %.1024s",keyword,value); |
1445 | 0 | current=transform; |
1446 | 0 | IdentityAffine(&affine); |
1447 | 0 | switch (*keyword) |
1448 | 0 | { |
1449 | 0 | case 'M': |
1450 | 0 | case 'm': |
1451 | 0 | { |
1452 | 0 | if (LocaleCompare(keyword,"matrix") == 0) |
1453 | 0 | { |
1454 | 0 | p=(char *) value; |
1455 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
1456 | 0 | affine.sx=MagickAtoF(value); |
1457 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
1458 | 0 | if (*token == ',') |
1459 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
1460 | 0 | affine.rx=MagickAtoF(token); |
1461 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
1462 | 0 | if (*token == ',') |
1463 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
1464 | 0 | affine.ry=MagickAtoF(token); |
1465 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
1466 | 0 | if (*token == ',') |
1467 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
1468 | 0 | affine.sy=MagickAtoF(token); |
1469 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
1470 | 0 | if (*token == ',') |
1471 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
1472 | 0 | affine.tx=MagickAtoF(token); |
1473 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
1474 | 0 | if (*token == ',') |
1475 | 0 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
1476 | 0 | affine.ty=MagickAtoF(token); |
1477 | 0 | break; |
1478 | 0 | } |
1479 | 0 | break; |
1480 | 0 | } |
1481 | 0 | case 'R': |
1482 | 0 | case 'r': |
1483 | 0 | { |
1484 | 0 | if (LocaleCompare(keyword,"rotate") == 0) |
1485 | 0 | { |
1486 | 0 | double |
1487 | 0 | angle; |
1488 | |
|
1489 | 0 | angle=GetUserSpaceCoordinateValue(svg_info,0,value,MagickFalse); |
1490 | 0 | affine.sx=cos(DegreesToRadians(fmod(angle,360.0))); |
1491 | 0 | affine.rx=sin(DegreesToRadians(fmod(angle,360.0))); |
1492 | 0 | affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0)))); |
1493 | 0 | affine.sy=cos(DegreesToRadians(fmod(angle,360.0))); |
1494 | 0 | break; |
1495 | 0 | } |
1496 | 0 | break; |
1497 | 0 | } |
1498 | 0 | case 'S': |
1499 | 0 | case 's': |
1500 | 0 | { |
1501 | 0 | if (LocaleCompare(keyword,"scale") == 0) |
1502 | 0 | { |
1503 | 0 | for (p=(char *) value; *p != '\0'; p++) |
1504 | 0 | if (isspace((int) (*p)) || (*p == ',')) |
1505 | 0 | break; |
1506 | 0 | affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
1507 | 0 | affine.sy=affine.sx; |
1508 | 0 | if (*p != '\0') |
1509 | 0 | affine.sy= |
1510 | 0 | GetUserSpaceCoordinateValue(svg_info,-1,p+1,MagickFalse); |
1511 | 0 | svg_info->scale[svg_info->n]=ExpandAffine(&affine); |
1512 | 0 | break; |
1513 | 0 | } |
1514 | 0 | if (LocaleCompare(keyword,"skewX") == 0) |
1515 | 0 | { |
1516 | 0 | affine.sx=svg_info->affine.sx; |
1517 | 0 | affine.ry=tan(DegreesToRadians(fmod( |
1518 | 0 | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse), |
1519 | 0 | 360.0))); |
1520 | 0 | affine.sy=svg_info->affine.sy; |
1521 | 0 | break; |
1522 | 0 | } |
1523 | 0 | if (LocaleCompare(keyword,"skewY") == 0) |
1524 | 0 | { |
1525 | 0 | affine.sx=svg_info->affine.sx; |
1526 | 0 | affine.rx=tan(DegreesToRadians(fmod( |
1527 | 0 | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse), |
1528 | 0 | 360.0))); |
1529 | 0 | affine.sy=svg_info->affine.sy; |
1530 | 0 | break; |
1531 | 0 | } |
1532 | 0 | break; |
1533 | 0 | } |
1534 | 0 | case 'T': |
1535 | 0 | case 't': |
1536 | 0 | { |
1537 | 0 | if (LocaleCompare(keyword,"translate") == 0) |
1538 | 0 | { |
1539 | 0 | for (p=(char *) value; *p != '\0'; p++) |
1540 | 0 | if (isspace((int) (*p)) || (*p == ',')) |
1541 | 0 | break; |
1542 | 0 | affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
1543 | 0 | affine.ty=affine.tx; |
1544 | 0 | if (*p != '\0') |
1545 | 0 | affine.ty= |
1546 | 0 | GetUserSpaceCoordinateValue(svg_info,-1,p+1,MagickFalse); |
1547 | 0 | break; |
1548 | 0 | } |
1549 | 0 | break; |
1550 | 0 | } |
1551 | 0 | default: |
1552 | 0 | break; |
1553 | 0 | } /* end switch */ |
1554 | 0 | transform.sx=current.sx*affine.sx+current.ry*affine.rx; |
1555 | 0 | transform.rx=current.rx*affine.sx+current.sy*affine.rx; |
1556 | 0 | transform.ry=current.sx*affine.ry+current.ry*affine.sy; |
1557 | 0 | transform.sy=current.rx*affine.ry+current.sy*affine.sy; |
1558 | 0 | transform.tx=current.sx*affine.tx+current.ry*affine.ty+ |
1559 | 0 | current.tx; |
1560 | 0 | transform.ty=current.rx*affine.tx+current.sy*affine.ty+ |
1561 | 0 | current.ty; |
1562 | 0 | } /* end for */ |
1563 | 0 | MVGPrintf(svg_info->file,"affine %g %g %g %g %g %g\n", |
1564 | 0 | transform.sx,transform.rx,transform.ry,transform.sy, |
1565 | 0 | transform.tx,transform.ty); |
1566 | 0 | } /* end if */ |
1567 | 0 | if (tokens != (char **) NULL) |
1568 | 0 | { |
1569 | 0 | for (j=0; tokens[j] != (char *) NULL; j++) |
1570 | 0 | MagickFreeMemory(tokens[j]); |
1571 | 0 | MagickFreeMemory(tokens); |
1572 | 0 | } |
1573 | 0 | break; |
1574 | 0 | } |
1575 | 2.02k | if (LocaleCompare(keyword,"gradientUnits") == 0) |
1576 | 715 | { |
1577 | 715 | (void) CloneString(&units,value); |
1578 | 715 | MVGPrintf(svg_info->file,"gradient-units '%s'\n",value); |
1579 | 715 | break; |
1580 | 715 | } |
1581 | 1.31k | break; |
1582 | 2.02k | } |
1583 | 1.31k | case 'H': |
1584 | 22.2k | case 'h': |
1585 | 22.2k | { |
1586 | 22.2k | if (LocaleCompare(keyword,"height") == 0) |
1587 | 12.0k | { |
1588 | 12.0k | svg_info->bounds.height= |
1589 | 12.0k | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickTrue); |
1590 | 12.0k | break; |
1591 | 12.0k | } |
1592 | 10.2k | if (LocaleCompare(keyword,"href") == 0) |
1593 | 4.38k | { |
1594 | | /* process "#identifier" as if it were "url(#identifier)" */ |
1595 | 4.38k | if ( value[0] == '#' ) |
1596 | 1.17k | { |
1597 | | /* reallocate the needed memory once */ |
1598 | 1.17k | size_t NewSize = strlen(value) + 6; /* 6 == url()<null> */ |
1599 | 1.17k | MagickReallocMemory(char *,svg_info->url,NewSize); |
1600 | 1.17k | strlcpy(svg_info->url,"url(",NewSize); |
1601 | 1.17k | strlcat(svg_info->url,value,NewSize); |
1602 | 1.17k | strlcat(svg_info->url,")",NewSize); |
1603 | 1.17k | } |
1604 | 3.21k | else |
1605 | 3.21k | { |
1606 | 3.21k | (void) CloneString(&svg_info->url,value); |
1607 | 3.21k | } |
1608 | 4.38k | break; |
1609 | 4.38k | } |
1610 | 5.82k | break; |
1611 | 10.2k | } |
1612 | 5.82k | case 'M': |
1613 | 1.07k | case 'm': |
1614 | 1.07k | { |
1615 | 1.07k | if (LocaleCompare(keyword,"major") == 0) |
1616 | 0 | { |
1617 | 0 | svg_info->element.major= |
1618 | 0 | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
1619 | 0 | break; |
1620 | 0 | } |
1621 | 1.07k | if (LocaleCompare(keyword,"mask") == 0) /* added mask */ |
1622 | 0 | { |
1623 | 0 | MVGPrintf(svg_info->file,"mask '%s'\n",value); |
1624 | 0 | break; |
1625 | 0 | } |
1626 | 1.07k | if (LocaleCompare(keyword,"minor") == 0) |
1627 | 0 | { |
1628 | 0 | svg_info->element.minor= |
1629 | 0 | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
1630 | 0 | break; |
1631 | 0 | } |
1632 | 1.07k | break; |
1633 | 1.07k | } |
1634 | 1.07k | case 'O': |
1635 | 16.4k | case 'o': |
1636 | 16.4k | { |
1637 | 16.4k | if (LocaleCompare(keyword,"offset") == 0) |
1638 | 5.82k | { |
1639 | 5.82k | (void) CloneString(&svg_info->offset,value); |
1640 | 5.82k | break; |
1641 | 5.82k | } |
1642 | 10.6k | if (LocaleCompare(keyword,"opacity") == 0) |
1643 | 6.96k | { |
1644 | 6.96k | MVGPrintf(svg_info->file,"opacity '%s'\n",value); |
1645 | 6.96k | break; |
1646 | 6.96k | } |
1647 | 3.65k | break; |
1648 | 10.6k | } |
1649 | 3.65k | case 'P': |
1650 | 1.48k | case 'p': |
1651 | 1.48k | { |
1652 | 1.48k | if (LocaleCompare(keyword,"path") == 0) |
1653 | 16 | { |
1654 | 16 | (void) CloneString(&svg_info->url,value); |
1655 | 16 | break; |
1656 | 16 | } |
1657 | 1.47k | if (LocaleCompare(keyword,"points") == 0) |
1658 | 0 | { |
1659 | 0 | (void) CloneString(&svg_info->vertices,value); |
1660 | 0 | break; |
1661 | 0 | } |
1662 | 1.47k | break; |
1663 | 1.47k | } |
1664 | 6.96k | case 'R': |
1665 | 39.5k | case 'r': |
1666 | 39.5k | { |
1667 | 39.5k | if (LocaleCompare(keyword,"r") == 0) |
1668 | 9.42k | { |
1669 | 9.42k | svg_info->element.major= |
1670 | 9.42k | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
1671 | 9.42k | svg_info->element.minor= |
1672 | 9.42k | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
1673 | 9.42k | break; |
1674 | 9.42k | } |
1675 | 30.1k | if (LocaleCompare(keyword,"rotate") == 0) |
1676 | 0 | { |
1677 | 0 | double |
1678 | 0 | angle; |
1679 | |
|
1680 | 0 | angle=GetUserSpaceCoordinateValue(svg_info,0,value,MagickFalse); |
1681 | | /* |
1682 | | When the current text position was managed in this code, and a "rotate" was encountered |
1683 | | (indicating that the text character was to be rotated), the code would emit to the MVG file: |
1684 | | |
1685 | | translate x y (where x, y indicate the current text position) |
1686 | | rotate angle (where angle indicates the rotation angle) |
1687 | | |
1688 | | Now that the current text position is being managed by DrawImage() in render.c, this code |
1689 | | cannot issue the "translate" because it can't know the current text position. To handle |
1690 | | this, "textr" (text rotation) has been implemented in DrawImage() to perform the appropriate |
1691 | | translation/rotation sequence. |
1692 | | */ |
1693 | 0 | if ( IsTextOrTSpan ) |
1694 | 0 | MVGPrintf(svg_info->file,"textr %g\n",angle); /* pre-translation will be handled in DrawImage() */ |
1695 | 0 | else |
1696 | 0 | { |
1697 | 0 | MVGPrintf(svg_info->file,"translate %g,%g\n",svg_info->bounds.x,svg_info->bounds.y); |
1698 | 0 | svg_info->bounds.x=0; |
1699 | 0 | svg_info->bounds.y=0; |
1700 | 0 | MVGPrintf(svg_info->file,"rotate %g\n",angle); |
1701 | 0 | } |
1702 | 0 | break; |
1703 | 0 | } |
1704 | 30.1k | if (LocaleCompare(keyword,"rx") == 0) |
1705 | 2.99k | { |
1706 | 2.99k | if (LocaleCompare((char *) name,"ellipse") == 0) |
1707 | 574 | svg_info->element.major= |
1708 | 574 | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
1709 | 2.41k | else |
1710 | 2.41k | svg_info->radius.x= |
1711 | 2.41k | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
1712 | 2.99k | break; |
1713 | 2.99k | } |
1714 | 27.1k | if (LocaleCompare(keyword,"ry") == 0) |
1715 | 3.48k | { |
1716 | 3.48k | if (LocaleCompare((char *) name,"ellipse") == 0) |
1717 | 528 | svg_info->element.minor= |
1718 | 528 | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
1719 | 2.95k | else |
1720 | 2.95k | svg_info->radius.y= |
1721 | 2.95k | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
1722 | 3.48k | break; |
1723 | 3.48k | } |
1724 | 23.6k | break; |
1725 | 27.1k | } |
1726 | 23.6k | case 'S': |
1727 | 44.3k | case 's': |
1728 | 44.3k | { |
1729 | 44.3k | if (LocaleCompare(keyword,"stop-color") == 0) |
1730 | 4.79k | { |
1731 | 4.79k | (void) CloneString(&svg_info->stop_color,value); |
1732 | 4.79k | break; |
1733 | 4.79k | } |
1734 | 39.5k | if (LocaleCompare(keyword,"stroke") == 0) |
1735 | 4.73k | { |
1736 | 4.73k | if (LocaleCompare(value,"currentColor") == 0) |
1737 | 0 | { |
1738 | 0 | MVGPrintf(svg_info->file,"stroke '%s'\n",color); |
1739 | 0 | break; |
1740 | 0 | } |
1741 | 4.73k | MVGPrintf(svg_info->file,"stroke '%s'\n",value); |
1742 | 4.73k | break; |
1743 | 4.73k | } |
1744 | 34.8k | if (LocaleCompare(keyword,"stroke-antialiasing") == 0) |
1745 | 0 | { |
1746 | 0 | MVGPrintf(svg_info->file,"stroke-antialias %d\n", |
1747 | 0 | LocaleCompare(value,"true") == 0); |
1748 | 0 | break; |
1749 | 0 | } |
1750 | 34.8k | if (LocaleCompare(keyword,"stroke-dasharray") == 0) |
1751 | 2.41k | { |
1752 | 2.41k | MVGPrintf(svg_info->file,"stroke-dasharray %s\n",value); |
1753 | 2.41k | break; |
1754 | 2.41k | } |
1755 | 32.4k | if (LocaleCompare(keyword,"stroke-dashoffset") == 0) |
1756 | 660 | { |
1757 | 660 | double dashoffset=GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
1758 | 660 | MVGPrintf(svg_info->file,"stroke-dashoffset %g\n",dashoffset); |
1759 | 660 | break; |
1760 | 660 | } |
1761 | 31.7k | if (LocaleCompare(keyword,"stroke-linecap") == 0) |
1762 | 1.55k | { |
1763 | 1.55k | MVGPrintf(svg_info->file,"stroke-linecap '%s'\n",value); |
1764 | 1.55k | break; |
1765 | 1.55k | } |
1766 | 30.2k | if (LocaleCompare(keyword,"stroke-linejoin") == 0) |
1767 | 645 | { |
1768 | 645 | MVGPrintf(svg_info->file,"stroke-linejoin '%s'\n",value); |
1769 | 645 | break; |
1770 | 645 | } |
1771 | 29.5k | if (LocaleCompare(keyword,"stroke-miterlimit") == 0) |
1772 | 728 | { |
1773 | 728 | double stroke_miterlimit; |
1774 | 728 | if ((MagickAtoFChk(value,&stroke_miterlimit) != MagickPass) || |
1775 | 728 | stroke_miterlimit < 1.0) |
1776 | 2 | { |
1777 | 2 | errno=0; |
1778 | 2 | ThrowException(svg_info->exception,DrawError,InvalidPrimitiveArgument, |
1779 | 2 | value); |
1780 | 2 | break; |
1781 | 2 | } |
1782 | 726 | MVGPrintf(svg_info->file,"stroke-miterlimit '%ld'\n", |
1783 | 726 | (long) stroke_miterlimit); |
1784 | 726 | break; |
1785 | 728 | } |
1786 | 28.8k | if (LocaleCompare(keyword,"stroke-opacity") == 0) |
1787 | 779 | { |
1788 | 779 | MVGPrintf(svg_info->file,"stroke-opacity '%s'\n",value); |
1789 | 779 | break; |
1790 | 779 | } |
1791 | 28.0k | if (LocaleCompare(keyword,"stroke-width") == 0) |
1792 | 3.10k | { |
1793 | 3.10k | MVGPrintf(svg_info->file,"stroke-width %g\n", |
1794 | 3.10k | GetUserSpaceCoordinateValue(svg_info,1,value,MagickTrue)); |
1795 | 3.10k | break; |
1796 | 3.10k | } |
1797 | 24.9k | if (LocaleCompare(keyword,"style") == 0) |
1798 | 16.0k | { |
1799 | 16.0k | char |
1800 | 16.0k | **tokens; |
1801 | | |
1802 | 16.0k | (void) LogMagickEvent(CoderEvent,GetMagickModule()," "); |
1803 | 16.0k | tokens=GetStyleTokens(svg_info,value,&number_tokens); |
1804 | 16.0k | if ((tokens != (char **) NULL) && (number_tokens > 0)) |
1805 | 15.9k | { |
1806 | 397k | for (j=0; j < (number_tokens-1); j+=2) |
1807 | 381k | { |
1808 | 381k | keyword=(char *) tokens[j]; |
1809 | 381k | value=(char *) tokens[j+1]; |
1810 | 381k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1811 | 381k | " %.1024s: %.1024s",keyword,value); |
1812 | 381k | switch (*keyword) |
1813 | 381k | { |
1814 | 2.85k | case 'B': |
1815 | 6.53k | case 'b': |
1816 | 6.53k | { |
1817 | 6.53k | if (LocaleCompare(keyword,"background") == 0) |
1818 | 130 | { |
1819 | | /* only do this if background was specified inside <svg ... */ |
1820 | 130 | if ( LocaleCompare((const char *)name,"svg") == 0 ) |
1821 | 31 | strlcpy(svg_element_background_color,value,MaxTextExtent); |
1822 | 130 | break; |
1823 | 130 | } |
1824 | 6.40k | break; |
1825 | 6.53k | } |
1826 | 7.53k | case 'C': |
1827 | 16.4k | case 'c': |
1828 | 16.4k | { |
1829 | 16.4k | if (LocaleCompare(keyword,"clip-path") == 0) |
1830 | 0 | { |
1831 | 0 | MVGPrintf(svg_info->file,"clip-path '%s'\n",value); |
1832 | 0 | break; |
1833 | 0 | } |
1834 | 16.4k | if (LocaleCompare(keyword,"clip-rule") == 0) |
1835 | 0 | { |
1836 | 0 | MVGPrintf(svg_info->file,"clip-rule '%s'\n",value); |
1837 | 0 | break; |
1838 | 0 | } |
1839 | 16.4k | if (LocaleCompare(keyword,"clipPathUnits") == 0) |
1840 | 0 | { |
1841 | 0 | (void) CloneString(&units,value); |
1842 | 0 | MVGPrintf(svg_info->file,"clip-units '%s'\n",value); |
1843 | 0 | break; |
1844 | 0 | } |
1845 | 16.4k | if (LocaleCompare(keyword,"color") == 0) |
1846 | 3.94k | { |
1847 | 3.94k | (void) CloneString(&color,value); |
1848 | 3.94k | break; |
1849 | 3.94k | } |
1850 | 12.5k | break; |
1851 | 16.4k | } |
1852 | 12.5k | case 'F': |
1853 | 21.9k | case 'f': |
1854 | 21.9k | { |
1855 | 21.9k | if (LocaleCompare(keyword,"fill") == 0) |
1856 | 4.89k | { |
1857 | 4.89k | if (LocaleCompare(value,"currentColor") == 0) |
1858 | 0 | { |
1859 | 0 | MVGPrintf(svg_info->file,"fill '%s'\n",color); |
1860 | 0 | break; |
1861 | 0 | } |
1862 | 4.89k | MVGPrintf(svg_info->file,"fill '%s'\n",value); |
1863 | 4.89k | break; |
1864 | 4.89k | } |
1865 | 17.0k | if (LocaleCompare(keyword,"fillcolor") == 0) |
1866 | 314 | { |
1867 | 314 | MVGPrintf(svg_info->file,"fill '%s'\n",value); |
1868 | 314 | break; |
1869 | 314 | } |
1870 | 16.7k | if (LocaleCompare(keyword,"fill-rule") == 0) |
1871 | 0 | { |
1872 | 0 | MVGPrintf(svg_info->file,"fill-rule '%s'\n",value); |
1873 | 0 | break; |
1874 | 0 | } |
1875 | 16.7k | if (LocaleCompare(keyword,"fill-opacity") == 0) |
1876 | 49 | { |
1877 | 49 | MVGPrintf(svg_info->file,"fill-opacity '%s'\n",value); |
1878 | 49 | break; |
1879 | 49 | } |
1880 | 16.7k | if (LocaleCompare(keyword,"font-family") == 0) |
1881 | 3.80k | { |
1882 | 3.80k | MVGPrintf(svg_info->file,"font-family '%s'\n",value); |
1883 | 3.80k | break; |
1884 | 3.80k | } |
1885 | 12.9k | if (LocaleCompare(keyword,"font-stretch") == 0) |
1886 | 0 | { |
1887 | 0 | MVGPrintf(svg_info->file,"font-stretch '%s'\n",value); |
1888 | 0 | break; |
1889 | 0 | } |
1890 | 12.9k | if (LocaleCompare(keyword,"font-style") == 0) |
1891 | 1.21k | { |
1892 | 1.21k | MVGPrintf(svg_info->file,"font-style '%s'\n",value); |
1893 | 1.21k | break; |
1894 | 1.21k | } |
1895 | 11.6k | if (LocaleCompare(keyword,"font-size") == 0) |
1896 | 2.69k | { |
1897 | 2.69k | if (LocaleCompare(value,"medium") == 0) |
1898 | 75 | svg_info->pointsize=12; |
1899 | 2.62k | else |
1900 | 2.62k | svg_info->pointsize= |
1901 | 2.62k | GetUserSpaceCoordinateValue(svg_info,0,value,MagickTrue); |
1902 | 2.69k | MVGPrintf(svg_info->file,"font-size %g\n", |
1903 | 2.69k | svg_info->pointsize); |
1904 | 2.69k | break; |
1905 | 2.69k | } |
1906 | 8.99k | if (LocaleCompare(keyword,"font-weight") == 0) |
1907 | 1.61k | { |
1908 | 1.61k | MVGPrintf(svg_info->file,"font-weight '%s'\n",value); |
1909 | 1.61k | break; |
1910 | 1.61k | } |
1911 | 7.37k | break; |
1912 | 8.99k | } |
1913 | 7.37k | case 'O': |
1914 | 8.59k | case 'o': |
1915 | 8.59k | { |
1916 | 8.59k | if (LocaleCompare(keyword,"offset") == 0) |
1917 | 19 | { |
1918 | 19 | MVGPrintf(svg_info->file,"offset %g\n", |
1919 | 19 | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse)); |
1920 | 19 | break; |
1921 | 19 | } |
1922 | 8.57k | if (LocaleCompare(keyword,"opacity") == 0) |
1923 | 1.90k | { |
1924 | 1.90k | MVGPrintf(svg_info->file,"opacity '%s'\n",value); |
1925 | 1.90k | break; |
1926 | 1.90k | } |
1927 | 6.67k | break; |
1928 | 8.57k | } |
1929 | 6.67k | case 'S': |
1930 | 12.4k | case 's': |
1931 | 12.4k | { |
1932 | 12.4k | if (LocaleCompare(keyword,"stop-color") == 0) |
1933 | 66 | { |
1934 | 66 | (void) CloneString(&svg_info->stop_color,value); |
1935 | 66 | break; |
1936 | 66 | } |
1937 | 12.3k | if (LocaleCompare(keyword,"stroke") == 0) |
1938 | 1.28k | { |
1939 | 1.28k | if (LocaleCompare(value,"currentColor") == 0) |
1940 | 0 | { |
1941 | 0 | MVGPrintf(svg_info->file,"stroke '%s'\n",color); |
1942 | 0 | break; |
1943 | 0 | } |
1944 | 1.28k | MVGPrintf(svg_info->file,"stroke '%s'\n",value); |
1945 | 1.28k | break; |
1946 | 1.28k | } |
1947 | 11.0k | if (LocaleCompare(keyword,"stroke-antialiasing") == 0) |
1948 | 0 | { |
1949 | 0 | MVGPrintf(svg_info->file,"stroke-antialias %d\n", |
1950 | 0 | LocaleCompare(value,"true") == 0); |
1951 | 0 | break; |
1952 | 0 | } |
1953 | 11.0k | if (LocaleCompare(keyword,"stroke-dasharray") == 0) |
1954 | 585 | { |
1955 | 585 | MVGPrintf(svg_info->file,"stroke-dasharray %s\n",value); |
1956 | 585 | break; |
1957 | 585 | } |
1958 | 10.4k | if (LocaleCompare(keyword,"stroke-dashoffset") == 0) |
1959 | 1 | { |
1960 | 1 | double dashoffset=GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
1961 | 1 | MVGPrintf(svg_info->file,"stroke-dashoffset %g\n",dashoffset); |
1962 | 1 | break; |
1963 | 1 | } |
1964 | 10.4k | if (LocaleCompare(keyword,"stroke-linecap") == 0) |
1965 | 11 | { |
1966 | 11 | MVGPrintf(svg_info->file,"stroke-linecap '%s'\n",value); |
1967 | 11 | break; |
1968 | 11 | } |
1969 | 10.4k | if (LocaleCompare(keyword,"stroke-linejoin") == 0) |
1970 | 964 | { |
1971 | 964 | MVGPrintf(svg_info->file,"stroke-linejoin '%s'\n", |
1972 | 964 | value); |
1973 | 964 | break; |
1974 | 964 | } |
1975 | 9.51k | if (LocaleCompare(keyword,"stroke-miterlimit") == 0) |
1976 | 1 | { |
1977 | 1 | double stroke_miterlimit; |
1978 | 1 | if ((MagickAtoFChk(value,&stroke_miterlimit) != MagickPass) || |
1979 | 1 | stroke_miterlimit < 1.0) |
1980 | 1 | { |
1981 | 1 | errno=0; |
1982 | 1 | ThrowException(svg_info->exception,DrawError,InvalidPrimitiveArgument, |
1983 | 1 | value); |
1984 | 1 | break; |
1985 | 1 | } |
1986 | 0 | MVGPrintf(svg_info->file,"stroke-miterlimit '%ld'\n", |
1987 | 0 | (long) stroke_miterlimit); |
1988 | 0 | break; |
1989 | 1 | } |
1990 | 9.51k | if (LocaleCompare(keyword,"stroke-opacity") == 0) |
1991 | 1.19k | { |
1992 | 1.19k | MVGPrintf(svg_info->file,"stroke-opacity '%s'\n",value); |
1993 | 1.19k | break; |
1994 | 1.19k | } |
1995 | 8.31k | if (LocaleCompare(keyword,"stroke-width") == 0) |
1996 | 121 | { |
1997 | 121 | MVGPrintf(svg_info->file,"stroke-width %g\n", |
1998 | 121 | GetUserSpaceCoordinateValue(svg_info,1,value,MagickTrue)); |
1999 | 121 | break; |
2000 | 121 | } |
2001 | 8.19k | break; |
2002 | 8.31k | } |
2003 | 8.19k | case 't': |
2004 | 11.7k | case 'T': |
2005 | 11.7k | { |
2006 | 11.7k | if (LocaleCompare(keyword,"text-align") == 0) |
2007 | 1.89k | { |
2008 | 1.89k | MVGPrintf(svg_info->file,"text-align '%s'\n",value); |
2009 | 1.89k | break; |
2010 | 1.89k | } |
2011 | 9.85k | if (LocaleCompare(keyword,"text-anchor") == 0) |
2012 | 0 | { |
2013 | 0 | MVGPrintf(svg_info->file,"text-anchor '%s'\n",value); |
2014 | 0 | break; |
2015 | 0 | } |
2016 | 9.85k | if (LocaleCompare(keyword,"text-decoration") == 0) |
2017 | 315 | { |
2018 | 315 | if (LocaleCompare(value,"underline") == 0) |
2019 | 47 | MVGPrintf(svg_info->file,"decorate underline\n"); |
2020 | 315 | if (LocaleCompare(value,"line-through") == 0) |
2021 | 32 | MVGPrintf(svg_info->file,"decorate line-through\n"); |
2022 | 315 | if (LocaleCompare(value,"overline") == 0) |
2023 | 0 | MVGPrintf(svg_info->file,"decorate overline\n"); |
2024 | 315 | break; |
2025 | 315 | } |
2026 | 9.53k | if (LocaleCompare(keyword,"text-antialiasing") == 0) |
2027 | 0 | { |
2028 | 0 | MVGPrintf(svg_info->file,"text-antialias %d\n", |
2029 | 0 | LocaleCompare(value,"true") == 0); |
2030 | 0 | break; |
2031 | 0 | } |
2032 | 9.53k | if (LocaleCompare(keyword,"transform") == 0) |
2033 | 251 | { |
2034 | | /* implement style="transform: translate(..." */ |
2035 | 251 | SVGProcessTransformString(svg_info,value); |
2036 | 251 | break; |
2037 | 251 | } |
2038 | 9.28k | break; |
2039 | 9.53k | } |
2040 | 303k | default: |
2041 | 303k | break; |
2042 | 381k | } |
2043 | 381k | } |
2044 | 15.9k | } |
2045 | 16.0k | if (tokens != (char **) NULL) |
2046 | 15.9k | { |
2047 | 781k | for (j=0; tokens[j] != (char *) NULL; j++) |
2048 | 765k | MagickFreeMemory(tokens[j]); |
2049 | 15.9k | MagickFreeMemory(tokens); |
2050 | 15.9k | } |
2051 | 16.0k | break; |
2052 | 16.0k | } |
2053 | 8.87k | break; |
2054 | 24.9k | } |
2055 | 8.87k | case 'T': |
2056 | 33.6k | case 't': |
2057 | 33.6k | { |
2058 | 33.6k | if (LocaleCompare(keyword,"text-align") == 0) |
2059 | 0 | { |
2060 | 0 | MVGPrintf(svg_info->file,"text-align '%s'\n",value); |
2061 | 0 | break; |
2062 | 0 | } |
2063 | 33.6k | if (LocaleCompare(keyword,"text-anchor") == 0) |
2064 | 0 | { |
2065 | 0 | MVGPrintf(svg_info->file,"text-anchor '%s'\n",value); |
2066 | 0 | break; |
2067 | 0 | } |
2068 | 33.6k | if (LocaleCompare(keyword,"text-decoration") == 0) |
2069 | 0 | { |
2070 | 0 | if (LocaleCompare(value,"underline") == 0) |
2071 | 0 | MVGPrintf(svg_info->file,"decorate underline\n"); |
2072 | 0 | if (LocaleCompare(value,"line-through") == 0) |
2073 | 0 | MVGPrintf(svg_info->file,"decorate line-through\n"); |
2074 | 0 | if (LocaleCompare(value,"overline") == 0) |
2075 | 0 | MVGPrintf(svg_info->file,"decorate overline\n"); |
2076 | 0 | break; |
2077 | 0 | } |
2078 | 33.6k | if (LocaleCompare(keyword,"text-antialiasing") == 0) |
2079 | 0 | { |
2080 | 0 | MVGPrintf(svg_info->file,"text-antialias %d\n", |
2081 | 0 | LocaleCompare(value,"true") == 0); |
2082 | 0 | break; |
2083 | 0 | } |
2084 | 33.6k | if (LocaleCompare(keyword,"transform") == 0) |
2085 | 14.3k | { |
2086 | | /* |
2087 | | The code that was here has been refactored into |
2088 | | function SVGProcessTransformString() |
2089 | | */ |
2090 | 14.3k | SVGProcessTransformString(svg_info,value); |
2091 | 14.3k | break; |
2092 | 14.3k | } |
2093 | 19.2k | break; |
2094 | 33.6k | } |
2095 | 19.2k | case 'V': |
2096 | 63.5k | case 'v': |
2097 | 63.5k | { |
2098 | 63.5k | if (LocaleCompare(keyword,"verts") == 0) |
2099 | 0 | { |
2100 | 0 | (void) CloneString(&svg_info->vertices,value); |
2101 | 0 | break; |
2102 | 0 | } |
2103 | 63.5k | if (LocaleCompare(keyword,"viewBox") == 0) |
2104 | 2.95k | { |
2105 | 2.95k | p=(char *) value; |
2106 | 2.95k | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
2107 | 2.95k | svg_info->view_box.x=MagickAtoF(token); |
2108 | 2.95k | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
2109 | 2.95k | if (*token == ',') |
2110 | 1.29k | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
2111 | 2.95k | svg_info->view_box.y=MagickAtoF(token); |
2112 | 2.95k | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
2113 | 2.95k | if (*token == ',') |
2114 | 1.52k | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
2115 | 2.95k | svg_info->view_box.width=MagickAtoF(token); |
2116 | 2.95k | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
2117 | 2.95k | if (*token == ',') |
2118 | 859 | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
2119 | 2.95k | svg_info->view_box.height=MagickAtoF(token); |
2120 | 2.95k | if (svg_info->view_box.width < 0.0 || |
2121 | 2.95k | svg_info->view_box.height < 0.0) |
2122 | 6 | { |
2123 | 6 | ThrowException(svg_info->exception,CorruptImageError, |
2124 | 6 | NegativeOrZeroImageSize, |
2125 | 6 | svg_info->image->filename); |
2126 | 6 | goto svg_start_element_error; |
2127 | 6 | } |
2128 | 2.94k | if (svg_info->bounds.width == 0) |
2129 | 631 | svg_info->bounds.width=svg_info->view_box.width; |
2130 | 2.94k | if (svg_info->bounds.height == 0) |
2131 | 160 | svg_info->bounds.height=svg_info->view_box.height; |
2132 | 2.94k | break; |
2133 | 2.95k | } |
2134 | 60.5k | break; |
2135 | 63.5k | } |
2136 | 60.5k | case 'W': |
2137 | 17.3k | case 'w': |
2138 | 17.3k | { |
2139 | 17.3k | if (LocaleCompare(keyword,"width") == 0) |
2140 | 14.4k | { |
2141 | 14.4k | svg_info->bounds.width= |
2142 | 14.4k | GetUserSpaceCoordinateValue(svg_info,1,value,MagickTrue); |
2143 | 14.4k | break; |
2144 | 14.4k | } |
2145 | 2.91k | break; |
2146 | 17.3k | } |
2147 | 4.27k | case 'X': |
2148 | 35.5k | case 'x': |
2149 | 35.5k | { |
2150 | 35.5k | if (LocaleCompare(keyword,"x") == 0) |
2151 | 5.55k | { |
2152 | 5.55k | svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
2153 | | /* update text current position for tspan (already did this if text) */ |
2154 | 5.55k | if ( IsTSpan ) |
2155 | 95 | MVGPrintf(svg_info->file,"textx %g\n",svg_info->bounds.x); |
2156 | 5.55k | break; |
2157 | 5.55k | } |
2158 | 29.9k | if (LocaleCompare(keyword,"xlink:href") == 0) |
2159 | 2.13k | { |
2160 | | /* process "#identifier" as if it were "url(#identifier)" */ |
2161 | 2.13k | if ( value[0] == '#' ) |
2162 | 1.62k | { |
2163 | | /* reallocate the needed memory once */ |
2164 | 1.62k | size_t NewSize = strlen(value) + 6; /* 6 == url()<null> */ |
2165 | 1.62k | MagickReallocMemory(char *,svg_info->url,NewSize); |
2166 | 1.62k | strlcpy(svg_info->url,"url(",NewSize); |
2167 | 1.62k | strlcat(svg_info->url,value,NewSize); |
2168 | 1.62k | strlcat(svg_info->url,")",NewSize); |
2169 | 1.62k | } |
2170 | 514 | else |
2171 | 514 | (void) CloneString(&svg_info->url,value); |
2172 | 2.13k | break; |
2173 | 2.13k | } |
2174 | 27.8k | if (LocaleCompare(keyword,"x1") == 0) |
2175 | 1.00k | { |
2176 | 1.00k | svg_info->segment.x1= |
2177 | 1.00k | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
2178 | 1.00k | break; |
2179 | 1.00k | } |
2180 | 26.8k | if (LocaleCompare(keyword,"x2") == 0) |
2181 | 2.66k | { |
2182 | 2.66k | svg_info->segment.x2= |
2183 | 2.66k | GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
2184 | 2.66k | break; |
2185 | 2.66k | } |
2186 | 24.1k | break; |
2187 | 26.8k | } |
2188 | 24.1k | case 'Y': |
2189 | 9.13k | case 'y': |
2190 | 9.13k | { |
2191 | 9.13k | if (LocaleCompare(keyword,"y") == 0) |
2192 | 529 | { |
2193 | 529 | svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
2194 | | /* update text current position for tspan (already did this if text) */ |
2195 | 529 | if ( IsTSpan ) |
2196 | 79 | MVGPrintf(svg_info->file,"texty %g\n",svg_info->bounds.y); |
2197 | 529 | break; |
2198 | 529 | } |
2199 | 8.60k | if (LocaleCompare(keyword,"y1") == 0) |
2200 | 910 | { |
2201 | 910 | svg_info->segment.y1= |
2202 | 910 | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
2203 | 910 | break; |
2204 | 910 | } |
2205 | 7.69k | if (LocaleCompare(keyword,"y2") == 0) |
2206 | 5.91k | { |
2207 | 5.91k | svg_info->segment.y2= |
2208 | 5.91k | GetUserSpaceCoordinateValue(svg_info,-1,value,MagickFalse); |
2209 | 5.91k | break; |
2210 | 5.91k | } |
2211 | 1.78k | break; |
2212 | 7.69k | } |
2213 | 141k | default: |
2214 | 141k | break; |
2215 | 520k | } |
2216 | 520k | } |
2217 | 1.34M | if (svg_info->exception->severity >= ErrorException) |
2218 | 140 | goto svg_start_element_error; |
2219 | | #ifdef BROKEN_SIZE_OPTION |
2220 | | if (LocaleCompare((char *) name,"svg") == 0) |
2221 | | { |
2222 | | if (parser->encoding != (const xmlChar *) NULL) |
2223 | | MVGPrintf(svg_info->file,"encoding %.1024s\n", |
2224 | | (const char *) parser->encoding); |
2225 | | if (attributes != (const xmlChar **) NULL) |
2226 | | { |
2227 | | double |
2228 | | sx, |
2229 | | sy; |
2230 | | |
2231 | | if ((svg_info->view_box.width == 0.0) || |
2232 | | (svg_info->view_box.height == 0.0)) |
2233 | | svg_info->view_box=svg_info->bounds; |
2234 | | svg_info->width=(unsigned long) svg_info->bounds.width; |
2235 | | svg_info->height=(unsigned long) svg_info->bounds.height; |
2236 | | MVGPrintf(svg_info->file,"viewbox 0 0 %lu %lu\n",svg_info->width, |
2237 | | svg_info->height); |
2238 | | /* |
2239 | | Set initial canvas background color to user specified value |
2240 | | */ |
2241 | | { |
2242 | | char |
2243 | | tuple[MaxTextExtent]; |
2244 | | |
2245 | | GetColorTuple(&svg_info->image_info->background_color,8,True,True, |
2246 | | tuple); |
2247 | | |
2248 | | MVGPrintf(svg_info->file,"push graphic-context\n"); |
2249 | | MVGPrintf(svg_info->file,"fill %s\n", tuple); |
2250 | | MVGPrintf(svg_info->file,"rectangle 0,0 %g,%g\n", |
2251 | | svg_info->view_box.width,svg_info->view_box.height); |
2252 | | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
2253 | | } |
2254 | | sx=(double) svg_info->width/svg_info->view_box.width; |
2255 | | sy=(double) svg_info->height/svg_info->view_box.height; |
2256 | | MVGPrintf(svg_info->file,"affine %g 0 0 %g %g %g\n",sx,sy, |
2257 | | -sx*svg_info->view_box.x,-sy*svg_info->view_box.y); |
2258 | | } |
2259 | | } |
2260 | | #else |
2261 | 1.34M | if (LocaleCompare((char *) name,"svg") == 0) |
2262 | 27.2k | { |
2263 | | /* if (svg_info->document->encoding != (const xmlChar *) NULL) */ |
2264 | | /* (void) fprintf(svg_info->file,"encoding %.1024s\n", */ |
2265 | | /* (char *) svg_info->document->encoding); */ |
2266 | 27.2k | if (attributes != (const xmlChar **) NULL) |
2267 | 7.99k | { |
2268 | 7.99k | char |
2269 | 7.99k | *geometry; |
2270 | | |
2271 | 7.99k | RectangleInfo |
2272 | 7.99k | page; |
2273 | | |
2274 | 7.99k | double |
2275 | 7.99k | sx, |
2276 | 7.99k | sy; |
2277 | | |
2278 | 7.99k | if (svg_info->bounds.width < MagickEpsilon || |
2279 | 7.99k | svg_info->bounds.height < MagickEpsilon) |
2280 | 175 | { |
2281 | 175 | ThrowException(svg_info->exception,CorruptImageError, |
2282 | 175 | NegativeOrZeroImageSize,(char *) NULL); |
2283 | 175 | goto svg_start_element_error; |
2284 | 175 | } |
2285 | 7.81k | if ((svg_info->view_box.width == 0.0) || |
2286 | 7.81k | (svg_info->view_box.height == 0.0)) |
2287 | 3.27k | svg_info->view_box=svg_info->bounds; |
2288 | 7.81k | if (svg_info->view_box.width < MagickEpsilon || |
2289 | 7.81k | svg_info->view_box.height < MagickEpsilon) |
2290 | 0 | { |
2291 | 0 | ThrowException(svg_info->exception,CorruptImageError, |
2292 | 0 | NegativeOrZeroImageSize,(char *) NULL); |
2293 | 0 | goto svg_start_element_error; |
2294 | 0 | } |
2295 | 7.81k | SetGeometry(svg_info->image,&page); |
2296 | 7.81k | page.width=(unsigned long) svg_info->bounds.width; |
2297 | 7.81k | page.height=(unsigned long) svg_info->bounds.height; |
2298 | 7.81k | geometry=(char *) NULL; |
2299 | | /* at one point we use to try to use either page geometry |
2300 | | or size to set the dimensions of the output page, but |
2301 | | now we only look for size |
2302 | | */ |
2303 | | #ifdef PARSE_PAGE_FIRST |
2304 | | if (svg_info->page != (char *) NULL) |
2305 | | geometry=GetPageGeometry(svg_info->page); |
2306 | | else |
2307 | | #endif |
2308 | 7.81k | if (svg_info->size != (char *) NULL) |
2309 | 0 | geometry=GetPageGeometry(svg_info->size); |
2310 | 7.81k | if (geometry != (char *) NULL) |
2311 | 0 | { |
2312 | | /* |
2313 | | A terminating '>' in a geometry string is interpreted to mean that |
2314 | | the dimensions of an image should only be changed if its width or |
2315 | | height exceeds the geometry specification. For an unapparent and |
2316 | | undocumented reason, a terminating '>', if present, was being nulled |
2317 | | out, making this feature unusable for SVG files (now fixed). |
2318 | | */ |
2319 | 0 | (void) GetMagickGeometry(geometry,&page.x,&page.y, |
2320 | 0 | &page.width,&page.height); |
2321 | 0 | MagickFreeMemory(geometry); |
2322 | 0 | } |
2323 | | /* NOTE: the scale factor returned by ExpandAffine() has already been applied |
2324 | | to page.width and page.height |
2325 | | */ |
2326 | 7.81k | (void) MVGPrintf(svg_info->file,"viewbox 0 0 %g %g\n", |
2327 | 7.81k | svg_info->view_box.width,svg_info->view_box.height); |
2328 | 7.81k | sx=(double) page.width/svg_info->view_box.width; |
2329 | 7.81k | sy=(double) page.height/svg_info->view_box.height; |
2330 | 7.81k | MVGPrintf(svg_info->file,"affine %g 0 0 %g %g %g\n",sx,sy, |
2331 | 7.81k | -sx*svg_info->view_box.x,-sy*svg_info->view_box.y); |
2332 | | /* only set the output width and height if this is the outermost <svg> */ |
2333 | 7.81k | if ( svg_info->svgPushCount == 1 ) |
2334 | 2.24k | {/*outermost <svg>*/ |
2335 | 2.24k | svg_info->width=page.width; |
2336 | 2.24k | svg_info->height=page.height; |
2337 | | /* check if background color was specified using <svg ... style="background:color" */ |
2338 | 2.24k | if ( svg_element_background_color[0] != '\0' ) |
2339 | 3 | { |
2340 | 3 | MVGPrintf(svg_info->file,"push graphic-context\n"); |
2341 | 3 | MVGPrintf(svg_info->file,"fill %s\n",svg_element_background_color); |
2342 | 3 | MVGPrintf(svg_info->file,"rectangle 0,0 %g,%g\n",svg_info->view_box.width,svg_info->view_box.height); |
2343 | 3 | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
2344 | 3 | } |
2345 | 2.24k | }/*outermost <svg>*/ |
2346 | 7.81k | } |
2347 | 27.2k | } |
2348 | 1.34M | #endif |
2349 | | /* Error dispatch point */ |
2350 | 1.36M | svg_start_element_error:; |
2351 | | |
2352 | 1.36M | (void) LogMagickEvent(CoderEvent,GetMagickModule()," )"); |
2353 | 1.36M | MagickFreeMemory(units); |
2354 | 1.36M | MagickFreeMemory(color); |
2355 | 1.36M | } |
2356 | | |
2357 | | /* Process the "class" definitions inside <style> ... </style> */ |
2358 | | static |
2359 | | void ProcessStyleClassDefs ( |
2360 | | SVGInfo * svg_info |
2361 | | ) |
2362 | 18.0k | {/*ProcessStyleClassDefs*/ |
2363 | | |
2364 | | /* |
2365 | | Style defs look like: |
2366 | | |
2367 | | .class1,.class2,.class3{kwd:val;kwd:val;} |
2368 | | |
2369 | | Class name (e.g., ".class1"} can show up multiple times |
2370 | | */ |
2371 | | |
2372 | | /* keyword/value pair for an element */ |
2373 | 18.0k | typedef struct ElementValue |
2374 | 18.0k | {/*ElementValue*/ |
2375 | 18.0k | struct ElementValue * pNext; /* next in list */ |
2376 | 18.0k | char * pKeyword; |
2377 | 18.0k | char * pValue; |
2378 | 18.0k | }/*ElementValue*/ |
2379 | 18.0k | ElementValue; |
2380 | | |
2381 | | /* the keyword/value pair list associated with a class */ |
2382 | 18.0k | typedef struct ClassDef |
2383 | 18.0k | {/*ClassDef*/ |
2384 | 18.0k | struct ClassDef * pNext; /* next in list */ |
2385 | 18.0k | struct ClassDef * pActiveNext; /* next in active list */ |
2386 | 18.0k | char * pName; /* class name */ |
2387 | 18.0k | ElementValue ElementValueHead; /* list head for style element/value pairs */ |
2388 | 18.0k | ElementValue * pElementValueLast; |
2389 | 18.0k | }/*ClassDef*/ |
2390 | 18.0k | ClassDef; |
2391 | | |
2392 | 18.0k | ClassDef ClassDefHead; /* list head for classes */ |
2393 | 18.0k | ClassDef * pClassDefLast = &ClassDefHead; /* tail ptr for class def list */ |
2394 | 18.0k | ClassDef ClassDefActiveHead; /* list head for "active" class defs */ |
2395 | 18.0k | ClassDef * pClassDefActiveLast = &ClassDefActiveHead; /* tail ptr for "active" class def list */ |
2396 | 18.0k | ClassDef * pClassDef; |
2397 | 18.0k | char * pCopyOfText; |
2398 | 18.0k | char * pString; |
2399 | 18.0k | char * cp,*cp2; |
2400 | 18.0k | int c; |
2401 | | |
2402 | 18.0k | memset(&ClassDefHead,0,sizeof(ClassDefHead)); |
2403 | 18.0k | memset(&ClassDefActiveHead,0,sizeof(ClassDefActiveHead)); |
2404 | | |
2405 | | /* a macro to allocate an zero out a new struct instance, |
2406 | | and add it to the end of a linked list */ |
2407 | | /* FIXME: Does not deal with allocation failure! */ |
2408 | 18.0k | #define ADD_NEW_STRUCT(pNew,pLast,TheTypeDef) \ |
2409 | 629k | pNew = MagickAllocateClearedMemory(TheTypeDef *,sizeof(TheTypeDef)); \ |
2410 | 629k | pLast = pLast->pNext = pNew |
2411 | | |
2412 | | /* we will get a modifiable value of the string, and delimit |
2413 | | substrings by storing null characters */ |
2414 | 18.0k | pCopyOfText = AcquireString(svg_info->text); |
2415 | 68.5k | for ( pString = pCopyOfText; *pString; ) |
2416 | 55.5k | {/*pString loop*/ |
2417 | 55.5k | char * pClassNameList; |
2418 | 55.5k | char * pStyleElementList; |
2419 | | |
2420 | 81.5k | while ( (c = *pString) && isspace(c) ) pString++; /* skip white space */ |
2421 | 55.5k | if ( !*pString ) break; /* found end of string; done */ |
2422 | 54.1k | pClassNameList = pString; |
2423 | | |
2424 | | /* find the end of the comma-separated list of class names */ |
2425 | 10.8M | while ( (c = *pString) && (c != '{') ) pString++; |
2426 | 54.1k | if ( !*pString ) |
2427 | 2.23k | { |
2428 | | /* malformed input: class name list not followed by '{' */ |
2429 | 2.23k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2430 | 2.23k | " Malformed input: class name list not followed by '{"); |
2431 | 2.23k | goto process_style_class_defs_abort; |
2432 | 0 | return; |
2433 | 2.23k | } |
2434 | 51.9k | *pString++ = '\0'; |
2435 | 51.9k | pStyleElementList = pString; |
2436 | | |
2437 | | /* extract the class names */ |
2438 | 51.9k | ClassDefActiveHead.pActiveNext = 0; |
2439 | 51.9k | pClassDefActiveLast = &ClassDefActiveHead; /* initially, no active class defs */ |
2440 | 383k | for ( cp = pClassNameList; *cp; ) |
2441 | 331k | {/*extract class name loop*/ |
2442 | | |
2443 | 817k | while ( (c = *cp) && (isspace(c) || (c ==',')) ) cp++; /* skip white space/commas */ |
2444 | 331k | if ( *cp == '.' ) cp++; /* .classname, skip leading period */ |
2445 | 331k | if ( *cp ) |
2446 | 327k | {/*found class name*/ |
2447 | | |
2448 | 327k | char * pClassName = cp; |
2449 | 9.60M | while ( (c = *cp) && !(isspace(c) || (c == ',')) ) cp++; /* find white space/comma/null */ |
2450 | 327k | if ( *cp ) |
2451 | 285k | *cp++ = '\0'; /* terminate identifier string and increment */ |
2452 | | #if 0 |
2453 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2454 | | " Found ClassName: \"%s\"", pClassName); |
2455 | | #endif |
2456 | | /* add uniquely to list */ |
2457 | 327k | for ( pClassDef = ClassDefHead.pNext; |
2458 | 6.70M | pClassDef && (strcmp(pClassName,pClassDef->pName) != 0); |
2459 | 6.38M | pClassDef = pClassDef->pNext ); |
2460 | 327k | if ( pClassDef == 0 ) |
2461 | 171k | { /* new class name */ |
2462 | 171k | ADD_NEW_STRUCT(pClassDef,pClassDefLast,ClassDef); |
2463 | 171k | pClassDef->pElementValueLast = &pClassDef->ElementValueHead; |
2464 | 171k | pClassDef->pName = pClassName; |
2465 | 171k | pClassDef->pActiveNext = 0; |
2466 | 171k | pClassDefActiveLast = pClassDefActiveLast->pActiveNext = pClassDef; /* add to active list */ |
2467 | 171k | } /* new class name */ |
2468 | 155k | else |
2469 | 155k | { /* found on "all" list; if already on active list, ignore */ |
2470 | 155k | ClassDef * pClassDefActive; |
2471 | 155k | for ( pClassDefActive = ClassDefActiveHead.pActiveNext; |
2472 | 1.45M | pClassDefActive && (pClassDefActive != pClassDef); |
2473 | 1.29M | pClassDefActive = pClassDefActive->pActiveNext ); |
2474 | 155k | if ( pClassDefActive == 0 ) /* did not find on active list */ |
2475 | 72.0k | { |
2476 | 72.0k | pClassDef->pActiveNext = 0; |
2477 | 72.0k | pClassDefActiveLast = pClassDefActiveLast->pActiveNext = pClassDef; /* add to active list */ |
2478 | | #if 0 |
2479 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2480 | | " Adding Active Class: \"%s\"", pClassDef->pName); |
2481 | | #endif |
2482 | 72.0k | } |
2483 | 155k | }/* found on "all" list; if already on active list, ignore */ |
2484 | | |
2485 | 327k | }/*found class name*/ |
2486 | | |
2487 | 331k | }/*extract class name loop*/ |
2488 | | |
2489 | | #if 0 |
2490 | | /* verify lists */ |
2491 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2492 | | "=== Verifying full ClassDef list..."); |
2493 | | for ( pClassDef = ClassDefHead.pNext; |
2494 | | pClassDef; |
2495 | | pClassDef = pClassDef->pNext ) |
2496 | | { |
2497 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2498 | | "%p Class %s", pClassDef, pClassDef->pName); |
2499 | | } |
2500 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2501 | | "Done Verifying full ClassDef list"); |
2502 | | #endif |
2503 | | |
2504 | | #if 0 |
2505 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2506 | | "=== Verifying active ClassDef list..."); |
2507 | | for ( pClassDef = ClassDefActiveHead.pActiveNext; |
2508 | | pClassDef; |
2509 | | pClassDef = pClassDef->pActiveNext ) /* Seems this is supposed to be pActiveNext */ |
2510 | | { |
2511 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2512 | | "%p Class %s", pClassDef, pClassDef->pName); |
2513 | | } |
2514 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2515 | | "Done Verifying active ClassDef list"); |
2516 | | #endif |
2517 | | |
2518 | | /* find the end of the style elements */ |
2519 | 2.91M | while ( (c = *pString) && (c != '}') ) pString++; |
2520 | 51.9k | if ( !*pString ) |
2521 | 1.35k | { |
2522 | | /* malformed input: style elements not terminated by '{' */ |
2523 | 1.35k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2524 | 1.35k | " Malformed input: style elements not terminated by '{'"); |
2525 | 1.35k | goto process_style_class_defs_abort; |
2526 | 1.35k | } |
2527 | 50.5k | *pString++ = '\0'; /* advance past '}' for next loop pass */ |
2528 | | |
2529 | | /* get the style elements */ |
2530 | 118k | for ( cp = pStyleElementList; *cp; ) |
2531 | 67.5k | {/*extract style elements loop*/ |
2532 | | |
2533 | | /* looking for <space><classname><space>: */ |
2534 | 109k | while ( (c = *cp) && isspace(c) ) cp++; /* skip white space */ |
2535 | 67.5k | if ( *cp ) |
2536 | 61.7k | {/*found style element*/ |
2537 | | |
2538 | 61.7k | char * pStyleElement = cp; |
2539 | 1.24M | while ( (c = *cp) && (c != ':') ) cp++; /* find colon/null */ |
2540 | 61.7k | for ( cp2 = cp-1; isspace((int) (*cp2)); *cp2-- = '\0'); /* trim white space */ |
2541 | 61.7k | if ( *cp ) |
2542 | 42.5k | *cp++ = '\0'; /* terminate style element string and increment */ |
2543 | | |
2544 | | /* looking for <space><style-value>; */ |
2545 | 78.5k | while ( (c = *cp) && isspace(c) ) cp++; /* skip white space */ |
2546 | 61.7k | if ( *cp ) |
2547 | 42.1k | {/*found style element value*/ |
2548 | | |
2549 | 42.1k | char * pStyleValue = cp; |
2550 | 1.57M | while ( (c = *cp) && (c != ';') ) cp++; /* find semi-colon/null */ |
2551 | 42.1k | for ( cp2 = cp-1; isspace((int) (*cp2)); *cp2-- = '\0'); /* trim white space */ |
2552 | 42.1k | if ( *cp ) |
2553 | 20.0k | *cp++ = '\0'; /* terminate style value string and increment */ |
2554 | | |
2555 | | /* add style element/value pair to each active class def */ |
2556 | 42.1k | for ( pClassDef = ClassDefActiveHead.pActiveNext; |
2557 | 500k | pClassDef; |
2558 | 457k | pClassDef = pClassDef->pActiveNext ) |
2559 | 457k | { |
2560 | 457k | ElementValue * pEV; |
2561 | 457k | ADD_NEW_STRUCT(pEV,pClassDef->pElementValueLast,ElementValue); |
2562 | 457k | pEV->pKeyword = pStyleElement; |
2563 | 457k | pEV->pValue = pStyleValue; |
2564 | 457k | } |
2565 | | |
2566 | 42.1k | }/*found style element value*/ |
2567 | | |
2568 | 61.7k | }/*found style element*/ |
2569 | | |
2570 | 67.5k | }/*extract style elements loop*/ |
2571 | | |
2572 | 50.5k | }/*pString loop*/ |
2573 | | |
2574 | | /* emit class definitions */ |
2575 | 173k | for ( pClassDef = ClassDefHead.pNext; pClassDef; pClassDef = pClassDef->pNext ) |
2576 | 158k | {/*pClassDef loop*/ |
2577 | | |
2578 | 158k | ElementValue * pEV; |
2579 | 158k | MVGPrintf(svg_info->file,"push class '%s'\n",pClassDef->pName); |
2580 | | /* NOTE: we allow class definitions that are empty */ |
2581 | 397k | for ( pEV = pClassDef->ElementValueHead.pNext; pEV; pEV = pEV->pNext ) |
2582 | 238k | {/*pEV loop*/ |
2583 | | |
2584 | 238k | char * keyword = pEV->pKeyword; |
2585 | 238k | char * value = pEV->pValue; |
2586 | | /* switch below was copied/pasted from elsewhere and modified */ |
2587 | 238k | switch (*keyword) |
2588 | 238k | {/*keyword*/ |
2589 | | |
2590 | 4.68k | case 'C': |
2591 | 18.9k | case 'c': |
2592 | 18.9k | {/*Cc*/ |
2593 | 18.9k | if (LocaleCompare(keyword,"clip-path") == 0) |
2594 | 0 | { |
2595 | 0 | MVGPrintf(svg_info->file,"clip-path '%s'\n",value); |
2596 | 0 | break; |
2597 | 0 | } |
2598 | 18.9k | if (LocaleCompare(keyword,"clip-rule") == 0) |
2599 | 0 | { |
2600 | 0 | MVGPrintf(svg_info->file,"clip-rule '%s'\n",value); |
2601 | 0 | break; |
2602 | 0 | } |
2603 | 18.9k | if (LocaleCompare(keyword,"clipPathUnits") == 0) |
2604 | 0 | { |
2605 | 0 | MVGPrintf(svg_info->file,"clip-units '%s'\n",value); |
2606 | 0 | break; |
2607 | 0 | } |
2608 | 18.9k | break; |
2609 | 18.9k | }/*Cc*/ |
2610 | | |
2611 | 18.9k | case 'F': |
2612 | 29.8k | case 'f': |
2613 | 29.8k | {/*Ff*/ |
2614 | 29.8k | if ( (LocaleCompare(keyword,"fill") == 0) || (LocaleCompare(keyword,"fillcolor") == 0) ) |
2615 | 1.48k | { |
2616 | 1.48k | MVGPrintf(svg_info->file,"fill '%s'\n",value); |
2617 | 1.48k | break; |
2618 | 1.48k | } |
2619 | 28.3k | if (LocaleCompare(keyword,"fill-rule") == 0) |
2620 | 0 | { |
2621 | 0 | MVGPrintf(svg_info->file,"fill-rule '%s'\n",value); |
2622 | 0 | break; |
2623 | 0 | } |
2624 | 28.3k | if (LocaleCompare(keyword,"fill-opacity") == 0) |
2625 | 0 | { |
2626 | 0 | MVGPrintf(svg_info->file,"fill-opacity '%s'\n",value); |
2627 | 0 | break; |
2628 | 0 | } |
2629 | 28.3k | if (LocaleCompare(keyword,"font-family") == 0) |
2630 | 4.62k | { |
2631 | | /* |
2632 | | Deal with Adobe Illustrator 10.0 which double-quotes |
2633 | | font-family. Maybe we need a generalized solution for |
2634 | | this. |
2635 | | */ |
2636 | 4.62k | size_t n; |
2637 | 4.62k | if ( (value[0] == '\'') && (value[(n = (strlen(value)-1))] == '\'') ) |
2638 | 972 | { |
2639 | 972 | size_t i; |
2640 | 972 | FILE * fp = svg_info->file; |
2641 | 972 | MVGPrintf(fp,"font-family '"); |
2642 | 6.18k | for(i = 1; i < n; i++) |
2643 | 5.21k | fputc(value[i],fp); |
2644 | 972 | MVGPrintf(fp,"'\n"); |
2645 | 972 | } |
2646 | 3.65k | else |
2647 | 3.65k | MVGPrintf(svg_info->file,"font-family '%s'\n",value); |
2648 | 4.62k | break; |
2649 | 4.62k | } |
2650 | 23.7k | if (LocaleCompare(keyword,"font-stretch") == 0) |
2651 | 0 | { |
2652 | 0 | MVGPrintf(svg_info->file,"font-stretch '%s'\n",value); |
2653 | 0 | break; |
2654 | 0 | } |
2655 | 23.7k | if (LocaleCompare(keyword,"font-style") == 0) |
2656 | 2.64k | { |
2657 | 2.64k | MVGPrintf(svg_info->file,"font-style '%s'\n",value); |
2658 | 2.64k | break; |
2659 | 2.64k | } |
2660 | 21.1k | if (LocaleCompare(keyword,"font-size") == 0) |
2661 | 8.94k | { |
2662 | 8.94k | if (LocaleCompare(value,"medium") == 0) |
2663 | 2.51k | svg_info->pointsize = 12; |
2664 | 6.43k | else |
2665 | 6.43k | svg_info->pointsize = GetUserSpaceCoordinateValue(svg_info,0,value,MagickTrue); |
2666 | 8.94k | MVGPrintf(svg_info->file,"font-size %g\n",svg_info->pointsize); |
2667 | 8.94k | break; |
2668 | 8.94k | } |
2669 | 12.1k | if (LocaleCompare(keyword,"font-weight") == 0) |
2670 | 6.47k | { |
2671 | 6.47k | MVGPrintf(svg_info->file,"font-weight '%s'\n",value); |
2672 | 6.47k | break; |
2673 | 6.47k | } |
2674 | 5.68k | break; |
2675 | 12.1k | }/*Ff*/ |
2676 | | |
2677 | 5.68k | case 'O': |
2678 | 58.8k | case 'o': |
2679 | 58.8k | {/*Oo*/ |
2680 | 58.8k | if (LocaleCompare(keyword,"offset") == 0) |
2681 | 224 | { |
2682 | 224 | MVGPrintf(svg_info->file,"offset %g\n",GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse)); |
2683 | 224 | break; |
2684 | 224 | } |
2685 | 58.6k | if (LocaleCompare(keyword,"opacity") == 0) |
2686 | 5.24k | { |
2687 | 5.24k | MVGPrintf(svg_info->file,"opacity '%s'\n",value); |
2688 | 5.24k | break; |
2689 | 5.24k | } |
2690 | 53.3k | break; |
2691 | 58.6k | }/*Oo*/ |
2692 | | |
2693 | 53.3k | case 'S': |
2694 | 23.4k | case 's': |
2695 | 23.4k | {/*Ss*/ |
2696 | 23.4k | if (LocaleCompare(keyword,"stop-color") == 0) |
2697 | 0 | { |
2698 | 0 | (void) CloneString(&svg_info->stop_color,value); |
2699 | 0 | break; |
2700 | 0 | } |
2701 | 23.4k | if (LocaleCompare(keyword,"stroke") == 0) |
2702 | 275 | { |
2703 | 275 | MVGPrintf(svg_info->file,"stroke '%s'\n",value); |
2704 | 275 | break; |
2705 | 275 | } |
2706 | 23.2k | if (LocaleCompare(keyword,"stroke-antialiasing") == 0) |
2707 | 0 | { |
2708 | 0 | MVGPrintf(svg_info->file,"stroke-antialias %d\n",LocaleCompare(value,"true") == 0); |
2709 | 0 | break; |
2710 | 0 | } |
2711 | 23.2k | if (LocaleCompare(keyword,"stroke-dasharray") == 0) |
2712 | 0 | { |
2713 | 0 | MVGPrintf(svg_info->file,"stroke-dasharray %s\n",value); |
2714 | 0 | break; |
2715 | 0 | } |
2716 | 23.2k | if (LocaleCompare(keyword,"stroke-dashoffset") == 0) |
2717 | 0 | { |
2718 | 0 | double dashoffset=GetUserSpaceCoordinateValue(svg_info,1,value,MagickFalse); |
2719 | 0 | MVGPrintf(svg_info->file,"stroke-dashoffset %g\n",dashoffset); |
2720 | 0 | break; |
2721 | 0 | } |
2722 | 23.2k | if (LocaleCompare(keyword,"stroke-linecap") == 0) |
2723 | 0 | { |
2724 | 0 | MVGPrintf(svg_info->file,"stroke-linecap '%s'\n",value); |
2725 | 0 | break; |
2726 | 0 | } |
2727 | 23.2k | if (LocaleCompare(keyword,"stroke-linejoin") == 0) |
2728 | 0 | { |
2729 | 0 | MVGPrintf(svg_info->file,"stroke-linejoin '%s'\n",value); |
2730 | 0 | break; |
2731 | 0 | } |
2732 | 23.2k | if (LocaleCompare(keyword,"stroke-miterlimit") == 0) |
2733 | 0 | { |
2734 | 0 | double stroke_miterlimit; |
2735 | 0 | if ((MagickAtoFChk(value,&stroke_miterlimit) != MagickPass) || stroke_miterlimit < 1.0) |
2736 | 0 | { |
2737 | 0 | errno=0; |
2738 | 0 | ThrowException(svg_info->exception,DrawError,InvalidPrimitiveArgument,value); |
2739 | 0 | break; |
2740 | 0 | } |
2741 | 0 | MVGPrintf(svg_info->file,"stroke-miterlimit '%ld'\n",(long) stroke_miterlimit); |
2742 | 0 | break; |
2743 | 0 | } |
2744 | 23.2k | if (LocaleCompare(keyword,"stroke-opacity") == 0) |
2745 | 0 | { |
2746 | 0 | MVGPrintf(svg_info->file,"stroke-opacity '%s'\n",value); |
2747 | 0 | break; |
2748 | 0 | } |
2749 | 23.2k | if (LocaleCompare(keyword,"stroke-width") == 0) |
2750 | 0 | { |
2751 | 0 | MVGPrintf(svg_info->file,"stroke-width %g\n",GetUserSpaceCoordinateValue(svg_info,1,value,MagickTrue)); |
2752 | 0 | break; |
2753 | 0 | } |
2754 | 23.2k | break; |
2755 | 23.2k | }/*Ss*/ |
2756 | | |
2757 | 23.2k | case 'T': |
2758 | 28.3k | case 't': |
2759 | 28.3k | {/*Tt*/ |
2760 | 28.3k | if (LocaleCompare(keyword,"text-align") == 0) |
2761 | 969 | { |
2762 | 969 | MVGPrintf(svg_info->file,"text-align '%s'\n",value); |
2763 | 969 | break; |
2764 | 969 | } |
2765 | 27.3k | if (LocaleCompare(keyword,"text-anchor") == 0) |
2766 | 0 | { |
2767 | 0 | MVGPrintf(svg_info->file,"text-anchor '%s'\n",value); |
2768 | 0 | break; |
2769 | 0 | } |
2770 | 27.3k | if (LocaleCompare(keyword,"text-decoration") == 0) |
2771 | 970 | { |
2772 | 970 | if (LocaleCompare(value,"underline") == 0) |
2773 | 87 | MVGPrintf(svg_info->file,"decorate underline\n"); |
2774 | 970 | if (LocaleCompare(value,"line-through") == 0) |
2775 | 207 | MVGPrintf(svg_info->file,"decorate line-through\n"); |
2776 | 970 | if (LocaleCompare(value,"overline") == 0) |
2777 | 229 | MVGPrintf(svg_info->file,"decorate overline\n"); |
2778 | 970 | break; |
2779 | 970 | } |
2780 | 26.3k | if (LocaleCompare(keyword,"text-antialiasing") == 0) |
2781 | 0 | { |
2782 | 0 | MVGPrintf(svg_info->file,"text-antialias %d\n",LocaleCompare(value,"true") == 0); |
2783 | 0 | break; |
2784 | 0 | } |
2785 | 26.3k | if (LocaleCompare(keyword,"transform") == 0) |
2786 | 7.63k | {/*style="transform: ...*/ |
2787 | 7.63k | SVGProcessTransformString(svg_info,value); |
2788 | 7.63k | break; |
2789 | 7.63k | }/*style="transform: ...*/ |
2790 | 18.7k | break; |
2791 | 26.3k | }/*Tt*/ |
2792 | | |
2793 | 79.5k | default: |
2794 | 79.5k | break; |
2795 | | |
2796 | 238k | }/*keyword*/ |
2797 | | |
2798 | 238k | }/*pEV loop*/ |
2799 | | |
2800 | 158k | MVGPrintf(svg_info->file,"pop class\n"); |
2801 | | |
2802 | 158k | }/*pClassDef loop*/ |
2803 | | |
2804 | | /* clean up */ |
2805 | 18.0k | process_style_class_defs_abort:; |
2806 | 18.0k | { |
2807 | 18.0k | ClassDef * pClassDef; |
2808 | 189k | for(pClassDef = ClassDefHead.pNext; pClassDef; ) |
2809 | 171k | { |
2810 | 171k | ElementValue *pEV; |
2811 | 171k | ClassDef * pClassDefTemp = pClassDef; |
2812 | 629k | for(pEV = pClassDef->ElementValueHead.pNext; pEV; ) |
2813 | 457k | { |
2814 | 457k | ElementValue * pEVTemp = pEV; |
2815 | 457k | pEV = pEV->pNext; |
2816 | 457k | MagickFreeMemory(pEVTemp); |
2817 | 457k | } |
2818 | 171k | pClassDef = pClassDef->pNext; |
2819 | 171k | MagickFreeMemory(pClassDefTemp); |
2820 | 171k | } |
2821 | 18.0k | } |
2822 | 18.0k | MagickFreeMemory(pCopyOfText); |
2823 | | |
2824 | 18.0k | #undef ADD_NEW_STRUCT |
2825 | 18.0k | }/*ProcessStyleClassDefs*/ |
2826 | | |
2827 | | static void |
2828 | | SVGEndElement(void *context,const xmlChar *name) |
2829 | 393k | { |
2830 | 393k | SVGInfo |
2831 | 393k | *svg_info; |
2832 | | |
2833 | 393k | xmlParserCtxtPtr |
2834 | 393k | parser; |
2835 | | |
2836 | | /* |
2837 | | Called when the end of an element has been detected. |
2838 | | */ |
2839 | 393k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2840 | 393k | " SAX.endElement(%.1024s)",name); |
2841 | 393k | parser=(xmlParserCtxtPtr) context; |
2842 | 393k | svg_info=(SVGInfo *) parser->_private; |
2843 | 393k | assert(svg_info->signature == MagickSignature); |
2844 | 393k | if (strchr((char *) name,':') != (char *) NULL) |
2845 | 27.0k | { |
2846 | | /* |
2847 | | Skip over namespace. |
2848 | | */ |
2849 | 82.7k | for ( ; *name != ':'; name++) ; |
2850 | 27.0k | name++; |
2851 | 27.0k | } |
2852 | 393k | switch (*name) |
2853 | 393k | { |
2854 | 883 | case 'C': |
2855 | 26.9k | case 'c': |
2856 | 26.9k | { |
2857 | 26.9k | if (LocaleCompare((char *) name,"circle") == 0) |
2858 | 5.30k | { |
2859 | 5.30k | MVGPrintf(svg_info->file,"circle %g,%g %g,%g\n",svg_info->element.cx, |
2860 | 5.30k | svg_info->element.cy,svg_info->element.cx,svg_info->element.cy+ |
2861 | 5.30k | svg_info->element.minor); |
2862 | 5.30k | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
2863 | 5.30k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "pop id" if warranted */ |
2864 | 109 | { |
2865 | 109 | svg_info->idLevelInsideDefs = 0; |
2866 | 109 | MVGPrintf(svg_info->file,"pop id\n"); |
2867 | 109 | } |
2868 | 5.30k | break; |
2869 | 5.30k | } |
2870 | 21.6k | if (LocaleCompare((char *) name,"clipPath") == 0) |
2871 | 85 | { |
2872 | 85 | MVGPrintf(svg_info->file,"pop clip-path\n"); |
2873 | 85 | break; |
2874 | 85 | } |
2875 | 21.6k | break; |
2876 | 21.6k | } |
2877 | 21.6k | case 'D': |
2878 | 6.19k | case 'd': |
2879 | 6.19k | { |
2880 | 6.19k | if (LocaleCompare((char *) name,"defs") == 0) |
2881 | 453 | { |
2882 | 453 | svg_info->defsPushCount--; |
2883 | 453 | MVGPrintf(svg_info->file,"pop defs\n"); |
2884 | 453 | break; |
2885 | 453 | } |
2886 | 5.74k | if (LocaleCompare((char *) name,"desc") == 0) |
2887 | 2.11k | { |
2888 | 2.11k | register char |
2889 | 2.11k | *p; |
2890 | | |
2891 | 2.11k | svg_info->text_len=MagickStripString(svg_info->text); |
2892 | 2.11k | if (*svg_info->text == '\0') |
2893 | 775 | break; |
2894 | 1.33k | (void) fputc('#',svg_info->file); |
2895 | 191k | for (p=svg_info->text; *p != '\0'; p++) |
2896 | 189k | { |
2897 | 189k | (void) fputc(*p,svg_info->file); |
2898 | 189k | if (*p == '\n') |
2899 | 3.67k | (void) fputc('#',svg_info->file); |
2900 | 189k | } |
2901 | 1.33k | (void) fputc('\n',svg_info->file); |
2902 | 1.33k | *svg_info->text='\0'; |
2903 | 1.33k | svg_info->text_len=strlen(svg_info->text); |
2904 | 1.33k | break; |
2905 | 2.11k | } |
2906 | 3.62k | break; |
2907 | 5.74k | } |
2908 | 43.2k | case 'E': |
2909 | 49.8k | case 'e': |
2910 | 49.8k | { |
2911 | 49.8k | if (LocaleCompare((char *) name,"ellipse") == 0) |
2912 | 3.90k | { |
2913 | 3.90k | double |
2914 | 3.90k | angle; |
2915 | | |
2916 | 3.90k | angle=svg_info->element.angle; |
2917 | 3.90k | MVGPrintf(svg_info->file,"ellipse %g,%g %g,%g 0,360\n", |
2918 | 3.90k | svg_info->element.cx,svg_info->element.cy, |
2919 | 3.90k | angle == 0.0 ? svg_info->element.major : svg_info->element.minor, |
2920 | 3.90k | angle == 0.0 ? svg_info->element.minor : svg_info->element.major); |
2921 | 3.90k | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
2922 | 3.90k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "pop id" if warranted */ |
2923 | 36 | { |
2924 | 36 | svg_info->idLevelInsideDefs = 0; |
2925 | 36 | MVGPrintf(svg_info->file,"pop id\n"); |
2926 | 36 | } |
2927 | 3.90k | break; |
2928 | 3.90k | } |
2929 | 45.9k | break; |
2930 | 49.8k | } |
2931 | 45.9k | case 'F': |
2932 | 5.58k | case 'f': |
2933 | 5.58k | { |
2934 | | /* |
2935 | | For now we are ignoring "foreignObject". However, we do a push/pop |
2936 | | graphic-context so that any settings (e.g., fill) are consumed and |
2937 | | discarded. Otherwise they might persist and "leak" into the graphic |
2938 | | elements that follow. |
2939 | | */ |
2940 | 5.58k | if (LocaleCompare((char *) name,"foreignObject") == 0) |
2941 | 314 | { |
2942 | 314 | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
2943 | 314 | break; |
2944 | 314 | } |
2945 | 5.26k | break; |
2946 | 5.58k | } |
2947 | 5.26k | case 'G': |
2948 | 11.8k | case 'g': |
2949 | 11.8k | { |
2950 | 11.8k | if (LocaleCompare((char *) name,"g") == 0) |
2951 | 10.9k | { |
2952 | 10.9k | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
2953 | 10.9k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "pop id" if warranted */ |
2954 | 507 | { |
2955 | 507 | svg_info->idLevelInsideDefs = 0; |
2956 | 507 | MVGPrintf(svg_info->file,"pop id\n"); |
2957 | 507 | } |
2958 | 10.9k | break; |
2959 | 10.9k | } |
2960 | 940 | break; |
2961 | 11.8k | } |
2962 | 1.77k | case 'I': |
2963 | 8.65k | case 'i': |
2964 | 8.65k | { |
2965 | 8.65k | if (LocaleCompare((char *) name,"image") == 0) |
2966 | 1.47k | { |
2967 | 1.47k | MVGPrintf(svg_info->file,"image Copy %g,%g %g,%g '%s'\n", |
2968 | 1.47k | svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width, |
2969 | 1.47k | svg_info->bounds.height,svg_info->url); |
2970 | 1.47k | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
2971 | 1.47k | break; |
2972 | 1.47k | } |
2973 | 7.18k | break; |
2974 | 8.65k | } |
2975 | 7.18k | case 'L': |
2976 | 10.8k | case 'l': |
2977 | 10.8k | { |
2978 | 10.8k | if (LocaleCompare((char *) name,"line") == 0) |
2979 | 2.63k | { |
2980 | 2.63k | MVGPrintf(svg_info->file,"line %g,%g %g,%g\n",svg_info->segment.x1, |
2981 | 2.63k | svg_info->segment.y1,svg_info->segment.x2,svg_info->segment.y2); |
2982 | 2.63k | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
2983 | 2.63k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "pop id" if warranted */ |
2984 | 90 | { |
2985 | 90 | svg_info->idLevelInsideDefs = 0; |
2986 | 90 | MVGPrintf(svg_info->file,"pop id\n"); |
2987 | 90 | } |
2988 | 2.63k | break; |
2989 | 2.63k | } |
2990 | 8.24k | if (LocaleCompare((char *) name,"linearGradient") == 0) |
2991 | 2.18k | { |
2992 | 2.18k | MVGPrintf(svg_info->file,"pop gradient\n"); |
2993 | 2.18k | break; |
2994 | 2.18k | } |
2995 | 6.06k | break; |
2996 | 8.24k | } |
2997 | 8.75k | case 'M': |
2998 | 11.4k | case 'm': |
2999 | 11.4k | { |
3000 | 11.4k | if (LocaleCompare((char *) name,"mask") == 0) /* added mask */ |
3001 | 375 | { |
3002 | 375 | MVGPrintf(svg_info->file,"pop mask\n"); |
3003 | 375 | break; |
3004 | 375 | } |
3005 | 11.0k | break; |
3006 | 11.4k | } |
3007 | 11.0k | case 'P': |
3008 | 19.7k | case 'p': |
3009 | 19.7k | { |
3010 | 19.7k | if (LocaleCompare((char *) name,"pattern") == 0) |
3011 | 3.42k | { |
3012 | 3.42k | MVGPrintf(svg_info->file,"pop pattern\n"); |
3013 | 3.42k | break; |
3014 | 3.42k | } |
3015 | 16.2k | if (LocaleCompare((char *) name,"path") == 0) |
3016 | 12.0k | { |
3017 | 12.0k | MVGPrintf(svg_info->file,"path '%s'\n",svg_info->vertices); |
3018 | 12.0k | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
3019 | 12.0k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "pop id" if warranted */ |
3020 | 482 | { |
3021 | 482 | svg_info->idLevelInsideDefs = 0; |
3022 | 482 | MVGPrintf(svg_info->file,"pop id\n"); |
3023 | 482 | } |
3024 | 12.0k | break; |
3025 | 12.0k | } |
3026 | 4.26k | if (LocaleCompare((char *) name,"polygon") == 0) |
3027 | 298 | { |
3028 | 298 | MVGPrintf(svg_info->file,"polygon %s\n",svg_info->vertices); |
3029 | 298 | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
3030 | 298 | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "pop id" if warranted */ |
3031 | 7 | { |
3032 | 7 | svg_info->idLevelInsideDefs = 0; |
3033 | 7 | MVGPrintf(svg_info->file,"pop id\n"); |
3034 | 7 | } |
3035 | 298 | break; |
3036 | 298 | } |
3037 | 3.97k | if (LocaleCompare((char *) name,"polyline") == 0) |
3038 | 192 | { |
3039 | 192 | MVGPrintf(svg_info->file,"polyline %s\n",svg_info->vertices); |
3040 | 192 | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
3041 | 192 | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "pop id" if warranted */ |
3042 | 18 | { |
3043 | 18 | svg_info->idLevelInsideDefs = 0; |
3044 | 18 | MVGPrintf(svg_info->file,"pop id\n"); |
3045 | 18 | } |
3046 | 192 | break; |
3047 | 192 | } |
3048 | 3.77k | break; |
3049 | 3.97k | } |
3050 | 3.77k | case 'R': |
3051 | 46.1k | case 'r': |
3052 | 46.1k | { |
3053 | 46.1k | if (LocaleCompare((char *) name,"radialGradient") == 0) |
3054 | 279 | { |
3055 | 279 | MVGPrintf(svg_info->file,"pop gradient\n"); |
3056 | 279 | break; |
3057 | 279 | } |
3058 | 45.8k | if (LocaleCompare((char *) name,"rect") == 0) |
3059 | 9.36k | { |
3060 | 9.36k | if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0)) |
3061 | 6.52k | { |
3062 | 6.52k | MVGPrintf(svg_info->file,"rectangle %g,%g %g,%g\n", |
3063 | 6.52k | svg_info->bounds.x,svg_info->bounds.y, |
3064 | 6.52k | svg_info->bounds.x+svg_info->bounds.width, |
3065 | 6.52k | svg_info->bounds.y+svg_info->bounds.height); |
3066 | 6.52k | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
3067 | 6.52k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "pop id" if warranted */ |
3068 | 553 | { |
3069 | 553 | svg_info->idLevelInsideDefs = 0; |
3070 | 553 | MVGPrintf(svg_info->file,"pop id\n"); |
3071 | 553 | } |
3072 | 6.52k | break; |
3073 | 6.52k | } |
3074 | 2.84k | if (svg_info->radius.x == 0.0) |
3075 | 1.07k | svg_info->radius.x=svg_info->radius.y; |
3076 | 2.84k | if (svg_info->radius.y == 0.0) |
3077 | 743 | svg_info->radius.y=svg_info->radius.x; |
3078 | 2.84k | MVGPrintf(svg_info->file,"roundRectangle %g,%g %g,%g %g,%g\n", |
3079 | 2.84k | svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+ |
3080 | 2.84k | svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height, |
3081 | 2.84k | svg_info->radius.x,svg_info->radius.y); |
3082 | 2.84k | svg_info->radius.x=0.0; |
3083 | 2.84k | svg_info->radius.y=0.0; |
3084 | 2.84k | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
3085 | 2.84k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "pop id" if warranted */ |
3086 | 297 | { |
3087 | 297 | svg_info->idLevelInsideDefs = 0; |
3088 | 297 | MVGPrintf(svg_info->file,"pop id\n"); |
3089 | 297 | } |
3090 | 2.84k | break; |
3091 | 9.36k | } |
3092 | 36.4k | break; |
3093 | 45.8k | } |
3094 | 36.4k | case 'S': |
3095 | 36.3k | case 's': |
3096 | 36.3k | { |
3097 | 36.3k | if (LocaleCompare((char *) name,"stop") == 0) |
3098 | 5.58k | { |
3099 | 5.58k | MVGPrintf(svg_info->file,"stop-color '%s' %s\n",svg_info->stop_color, |
3100 | 5.58k | svg_info->offset); |
3101 | 5.58k | break; |
3102 | 5.58k | } |
3103 | | /* element "style" inside <defs> */ |
3104 | 30.7k | if (LocaleCompare((char *) name,"style") == 0) |
3105 | 18.0k | { |
3106 | | /* the style definitions are in svg_info->text */ |
3107 | 18.0k | ProcessStyleClassDefs(svg_info); |
3108 | 18.0k | break; |
3109 | 18.0k | } |
3110 | 12.7k | if (LocaleCompare((char *) name,"svg") == 0) |
3111 | 6.35k | { |
3112 | 6.35k | svg_info->svgPushCount--; |
3113 | 6.35k | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
3114 | 6.35k | break; |
3115 | 6.35k | } |
3116 | 6.38k | break; |
3117 | 12.7k | } |
3118 | 6.38k | case 'T': |
3119 | 101k | case 't': |
3120 | 101k | { |
3121 | 101k | if (LocaleCompare((char *) name,"text") == 0) |
3122 | 6.02k | { |
3123 | 6.02k | svg_info->text_len=MagickStripString(svg_info->text); |
3124 | 6.02k | if (*svg_info->text != '\0') |
3125 | 4.58k | { |
3126 | 4.58k | char |
3127 | 4.58k | *text; |
3128 | | |
3129 | 4.58k | text=EscapeString(svg_info->text,'\''); |
3130 | 4.58k | MVGPrintf(svg_info->file,"textc '%s'\n",text); /* write text at current position */ |
3131 | 4.58k | MagickFreeMemory(text); |
3132 | 4.58k | *svg_info->text='\0'; |
3133 | 4.58k | svg_info->text_len=strlen(svg_info->text); |
3134 | 4.58k | } |
3135 | 6.02k | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
3136 | 6.02k | if ( svg_info->idLevelInsideDefs == svg_info->n ) /* emit a "pop id" if warranted */ |
3137 | 87 | { |
3138 | 87 | svg_info->idLevelInsideDefs = 0; |
3139 | 87 | MVGPrintf(svg_info->file,"pop id\n"); |
3140 | 87 | } |
3141 | 6.02k | break; |
3142 | 6.02k | } |
3143 | 95.7k | if (LocaleCompare((char *) name,"tspan") == 0) |
3144 | 6.37k | { |
3145 | 6.37k | svg_info->text_len=MagickStripString(svg_info->text); |
3146 | 6.37k | if (*svg_info->text != '\0') |
3147 | 759 | { |
3148 | 759 | char |
3149 | 759 | *text; |
3150 | | |
3151 | 759 | text=EscapeString(svg_info->text,'\''); |
3152 | 759 | MVGPrintf(svg_info->file,"textc '%s'\n",text); /* write text at current position */ |
3153 | 759 | MagickFreeMemory(text); |
3154 | | /* |
3155 | | The code that used to be here to compute the next text position has been eliminated. |
3156 | | The reason is that at this point in the code we may not know the font or font size |
3157 | | (they may be hidden in a "class" definition), so we can't really do that computation. |
3158 | | This functionality is now handled by DrawImage() in render.c. |
3159 | | */ |
3160 | 759 | *svg_info->text='\0'; |
3161 | 759 | svg_info->text_len=strlen(svg_info->text); |
3162 | 759 | } |
3163 | 6.37k | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
3164 | 6.37k | break; |
3165 | 6.37k | } |
3166 | 89.3k | if (LocaleCompare((char *) name,"title") == 0) |
3167 | 2.02k | { |
3168 | 2.02k | svg_info->text_len=MagickStripString(svg_info->text); |
3169 | 2.02k | if (*svg_info->text == '\0') |
3170 | 388 | break; |
3171 | 1.64k | (void) CloneString(&svg_info->title,svg_info->text); |
3172 | 1.64k | *svg_info->text='\0'; |
3173 | 1.64k | svg_info->text_len=strlen(svg_info->text); |
3174 | 1.64k | break; |
3175 | 2.02k | } |
3176 | 87.3k | break; |
3177 | 89.3k | } |
3178 | 87.3k | case 'U': |
3179 | 11.1k | case 'u': |
3180 | 11.1k | { |
3181 | 11.1k | if (LocaleCompare((char *) name,"use") == 0) |
3182 | 4.16k | { |
3183 | | /* |
3184 | | If the "use" had a "transform" attribute it has already been output to the MVG file. |
3185 | | |
3186 | | According to the SVG spec for "use": |
3187 | | |
3188 | | In the generated content, the 'use' will be replaced by 'g', where all attributes |
3189 | | from the 'use' element except for 'x', 'y', 'width', 'height' and 'xlink:href' are |
3190 | | transferred to the generated 'g' element. An additional transformation translate(x,y) |
3191 | | is appended to the end (i.e., right-side) of the 'transform' attribute on the generated |
3192 | | 'g', where x and y represent the values of the 'x' and 'y' attributes on the 'use' |
3193 | | element. The referenced object and its contents are deep-cloned into the generated tree. |
3194 | | */ |
3195 | 4.16k | if ( (svg_info->bounds.x != 0.0) || (svg_info->bounds.y != 0.0) ) |
3196 | 1.71k | MVGPrintf(svg_info->file,"translate %g,%g\n",svg_info->bounds.x,svg_info->bounds.y); |
3197 | | |
3198 | | /* NOTE: not implementing "width" and "height" for now */ |
3199 | | |
3200 | 4.16k | MVGPrintf(svg_info->file,"use '%s'\n",svg_info->url); |
3201 | 4.16k | MVGPrintf(svg_info->file,"pop graphic-context\n"); |
3202 | 4.16k | break; |
3203 | 4.16k | } |
3204 | 6.99k | break; |
3205 | 11.1k | } |
3206 | 47.1k | default: |
3207 | 47.1k | break; |
3208 | 393k | } |
3209 | 393k | (void) memset(&svg_info->segment,0,sizeof(svg_info->segment)); |
3210 | 393k | (void) memset(&svg_info->element,0,sizeof(svg_info->element)); |
3211 | 393k | *svg_info->text='\0'; |
3212 | 393k | svg_info->text_len=strlen(svg_info->text); |
3213 | 393k | svg_info->n--; |
3214 | 393k | } |
3215 | | |
3216 | | static void |
3217 | | SVGCharacters(void *context,const xmlChar *c,int length) |
3218 | 1.24M | { |
3219 | 1.24M | SVGInfo |
3220 | 1.24M | *svg_info; |
3221 | | |
3222 | 1.24M | xmlParserCtxtPtr |
3223 | 1.24M | parser; |
3224 | | |
3225 | 1.24M | char |
3226 | 1.24M | *p; |
3227 | | |
3228 | 1.24M | char |
3229 | 1.24M | *new_text; |
3230 | | |
3231 | 1.24M | register int |
3232 | 1.24M | i; |
3233 | | |
3234 | 1.24M | size_t |
3235 | 1.24M | new_text_len; |
3236 | | |
3237 | | /* |
3238 | | Receiving some characters from the parser. |
3239 | | */ |
3240 | 1.24M | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3241 | 1.24M | " SAX.characters(%.1024s,%d)",c,length); |
3242 | | |
3243 | 1.24M | parser=(xmlParserCtxtPtr) context; |
3244 | 1.24M | svg_info=(SVGInfo *) parser->_private; |
3245 | 1.24M | assert(svg_info->signature == MagickSignature); |
3246 | | |
3247 | 1.24M | new_text_len = svg_info->text_len+length; |
3248 | 1.24M | new_text = MagickReallocateResourceLimitedMemory(char *,svg_info->text,new_text_len+1); |
3249 | 1.24M | if (new_text != NULL) |
3250 | 1.24M | { |
3251 | 1.24M | svg_info->text = new_text; |
3252 | 1.24M | } |
3253 | 0 | else |
3254 | 0 | { |
3255 | | /* There was a problem! Retain existing buffer. */ |
3256 | 0 | return; |
3257 | 0 | } |
3258 | | |
3259 | 1.24M | if (svg_info->text == (char *) NULL) |
3260 | 0 | return; |
3261 | 1.24M | p=svg_info->text+svg_info->text_len; |
3262 | 93.8M | for (i=0; i < length; i++) |
3263 | 92.6M | *p++=c[i]; |
3264 | 1.24M | *p='\0'; |
3265 | 1.24M | svg_info->text_len = new_text_len; |
3266 | 1.24M | } |
3267 | | |
3268 | | static void |
3269 | | SVGComment(void *context,const xmlChar *value) |
3270 | 299k | { |
3271 | 299k | SVGInfo |
3272 | 299k | *svg_info; |
3273 | | |
3274 | 299k | xmlParserCtxtPtr |
3275 | 299k | parser; |
3276 | | |
3277 | 299k | char |
3278 | 299k | *p; |
3279 | | |
3280 | 299k | char |
3281 | 299k | *new_comment; |
3282 | | |
3283 | 299k | size_t |
3284 | 299k | i; |
3285 | | |
3286 | 299k | size_t |
3287 | 299k | new_comment_len, |
3288 | 299k | length; |
3289 | | |
3290 | | /* |
3291 | | A comment has been parsed. |
3292 | | */ |
3293 | 299k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3294 | 299k | " SAX.comment(%.1024s)",value); |
3295 | 299k | parser=(xmlParserCtxtPtr) context; |
3296 | 299k | svg_info=(SVGInfo *) parser->_private; |
3297 | 299k | assert(svg_info->signature == MagickSignature); |
3298 | | /* |
3299 | | Old way concatenated all comments like this: |
3300 | | if (svg_info->comment != (char *) NULL) |
3301 | | (void) ConcatenateString(&svg_info->comment,"\n"); |
3302 | | (void) ConcatenateString(&svg_info->comment,(char *) value); |
3303 | | */ |
3304 | 5.58M | for (length = 0; value[length] != 0; length++) |
3305 | 5.28M | ; |
3306 | 299k | new_comment_len = svg_info->comment_len+length+(svg_info->comment_len ? 1 : 0); |
3307 | | |
3308 | | /* Cap the maximum collected comment size */ |
3309 | 299k | if (new_comment_len > 4*MaxTextExtent) |
3310 | 251k | return; |
3311 | | |
3312 | 47.8k | new_comment = MagickReallocateResourceLimitedMemory(char *,svg_info->comment,new_comment_len+1); |
3313 | 47.8k | if (new_comment != NULL) |
3314 | 47.8k | { |
3315 | 47.8k | svg_info->comment = new_comment; |
3316 | 47.8k | } |
3317 | 0 | else |
3318 | 0 | { |
3319 | | /* There was a problem! Retain existing buffer. */ |
3320 | 0 | return; |
3321 | 0 | } |
3322 | 47.8k | if (svg_info->comment == (char *) NULL) |
3323 | 0 | return; |
3324 | | |
3325 | 47.8k | p=svg_info->comment+svg_info->comment_len; |
3326 | 47.8k | if (svg_info->comment_len) |
3327 | 46.1k | { |
3328 | | /* Add a new-line when appending */ |
3329 | 46.1k | *p='\n'; |
3330 | 46.1k | p++; |
3331 | 46.1k | } |
3332 | 1.06M | for (i=0; i < length; i++) |
3333 | 1.01M | *p++=value[i]; |
3334 | 47.8k | *p='\0'; |
3335 | | |
3336 | 47.8k | svg_info->comment_len = new_comment_len; |
3337 | 47.8k | } |
3338 | | |
3339 | | static void |
3340 | | SVGWarning(void *context,const char *format,...) MAGICK_FUNC_PRINTF_FORMAT(2,3); |
3341 | | |
3342 | | static void |
3343 | | SVGWarning(void *context,const char *format,...) |
3344 | 14.5k | { |
3345 | 14.5k | char |
3346 | 14.5k | reason[MaxTextExtent]; |
3347 | | |
3348 | 14.5k | SVGInfo |
3349 | 14.5k | *svg_info; |
3350 | | |
3351 | 14.5k | xmlParserCtxtPtr |
3352 | 14.5k | parser; |
3353 | | |
3354 | 14.5k | va_list |
3355 | 14.5k | operands; |
3356 | | |
3357 | | /** |
3358 | | Display and format a warning messages, gives file, line, position and |
3359 | | extra parameters. |
3360 | | */ |
3361 | 14.5k | va_start(operands,format); |
3362 | 14.5k | parser=(xmlParserCtxtPtr) context; |
3363 | 14.5k | svg_info=(SVGInfo *) parser->_private; |
3364 | 14.5k | assert(svg_info->signature == MagickSignature); |
3365 | 14.5k | (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.warning: "); |
3366 | 14.5k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands); |
3367 | | #if !defined(HAVE_VSNPRINTF) |
3368 | | (void) vsprintf(reason,format,operands); |
3369 | | #else |
3370 | 14.5k | (void) vsnprintf(reason,MaxTextExtent,format,operands); |
3371 | 14.5k | #endif |
3372 | 14.5k | ThrowException2(svg_info->exception,CoderWarning,reason,(char *) NULL); |
3373 | 14.5k | va_end(operands); |
3374 | 14.5k | } |
3375 | | |
3376 | | static void |
3377 | | SVGError(void *context,const char *format,...) MAGICK_FUNC_PRINTF_FORMAT(2,3); |
3378 | | |
3379 | | static void |
3380 | | SVGError(void *context,const char *format,...) |
3381 | 294k | { |
3382 | 294k | char |
3383 | 294k | reason[MaxTextExtent]; |
3384 | | |
3385 | 294k | SVGInfo |
3386 | 294k | *svg_info; |
3387 | | |
3388 | 294k | xmlParserCtxtPtr |
3389 | 294k | parser; |
3390 | | |
3391 | 294k | va_list |
3392 | 294k | operands; |
3393 | | |
3394 | | /* |
3395 | | Display and format a error formats, gives file, line, position and |
3396 | | extra parameters. |
3397 | | */ |
3398 | 294k | va_start(operands,format); |
3399 | 294k | parser=(xmlParserCtxtPtr) context; |
3400 | 294k | svg_info=(SVGInfo *) parser->_private; |
3401 | 294k | assert(svg_info->signature == MagickSignature); |
3402 | 294k | (void) LogMagickEvent(CoderEvent,GetMagickModule()," SAX.error: "); |
3403 | 294k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands); |
3404 | | #if !defined(HAVE_VSNPRINTF) |
3405 | | (void) vsprintf(reason,format,operands); |
3406 | | #else |
3407 | 294k | (void) vsnprintf(reason,MaxTextExtent,format,operands); |
3408 | 294k | #endif |
3409 | 294k | ThrowException2(svg_info->exception,CoderError,reason,(char *) NULL); |
3410 | 294k | va_end(operands); |
3411 | 294k | xmlStopParser(parser); |
3412 | 294k | } |
3413 | | |
3414 | | static Image * |
3415 | | ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception) |
3416 | 27.4k | { |
3417 | 27.4k | char |
3418 | 27.4k | filename[MaxTextExtent], |
3419 | 27.4k | geometry[MaxTextExtent], |
3420 | 27.4k | message[MaxTextExtent]; |
3421 | | |
3422 | 27.4k | FILE |
3423 | 27.4k | *file; |
3424 | | |
3425 | 27.4k | Image |
3426 | 27.4k | *image; |
3427 | | |
3428 | 27.4k | size_t |
3429 | 27.4k | n = 0; |
3430 | | |
3431 | 27.4k | SVGInfo |
3432 | 27.4k | svg_info; |
3433 | | |
3434 | 27.4k | unsigned int |
3435 | 27.4k | status; |
3436 | | |
3437 | 27.4k | xmlSAXHandler |
3438 | 27.4k | SAXModules; |
3439 | | |
3440 | 27.4k | xmlSAXHandlerPtr |
3441 | 27.4k | SAXHandler; |
3442 | | |
3443 | 27.4k | xmlParserCtxtPtr |
3444 | 27.4k | parser; |
3445 | | |
3446 | 27.4k | assert(image_info != (const ImageInfo *) NULL); |
3447 | 27.4k | assert(image_info->signature == MagickSignature); |
3448 | 27.4k | assert(exception != (ExceptionInfo *) NULL); |
3449 | 27.4k | assert(exception->signature == MagickSignature); |
3450 | | |
3451 | 27.4k | filename[0] = '\0'; |
3452 | 27.4k | geometry[0] = '\0'; |
3453 | 27.4k | message[0] = '\0'; |
3454 | | |
3455 | | /* |
3456 | | Libxml initialization. We call it here since initialization can |
3457 | | be expensive and we don't know when/if libxml will be |
3458 | | needed. Should normally be called at program start-up but may be |
3459 | | called several times since libxml uses a flag to know if it has |
3460 | | already been initialized. When libxml2 is built to support |
3461 | | threads, it tests if it is already initialized under a lock and |
3462 | | holds a lock while it is being initialized so calling this |
3463 | | function from multiple threads is ok. |
3464 | | */ |
3465 | 27.4k | xmlInitParser(); |
3466 | | |
3467 | | /* |
3468 | | Open image file. |
3469 | | */ |
3470 | 27.4k | image=AllocateImage(image_info); |
3471 | | /* |
3472 | | If there is a geometry string in image_info->size (e.g., gm convert |
3473 | | -size "50x50%" in.svg out.png), AllocateImage() sets image->columns |
3474 | | and image->rows to the width and height values from the size string. |
3475 | | However, this makes no sense if the size string was something like |
3476 | | "50x50%" (we'll get columns = rows = 50). So we set columns and |
3477 | | rows to 0 here, which is the same as if no size string was supplied |
3478 | | by the client. This also results in svg_info.bounds to be set to |
3479 | | 0,0 below (i.e., unknown), so that svg_info.bounds will be set using |
3480 | | the image size information from either the svg "canvas" width/height |
3481 | | or from the viewbox. Later, variable "page" is set from |
3482 | | svg_info->bounds. Then the geometry string in image_info->size gets |
3483 | | applied to the (now known) "page" width and height when |
3484 | | SvgStartElement() calls GetMagickGeometry(), and the intended result |
3485 | | is obtained. |
3486 | | */ |
3487 | 27.4k | image->columns = 0; |
3488 | 27.4k | image->rows = 0; |
3489 | 27.4k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
3490 | 27.4k | if (status == False) |
3491 | 27.4k | ThrowReaderException(FileOpenError,UnableToOpenFile,image); |
3492 | | /* |
3493 | | Open draw file. |
3494 | | */ |
3495 | 27.4k | file=AcquireTemporaryFileStream(filename,TextFileIOMode); |
3496 | 27.4k | if (file == (FILE *) NULL) |
3497 | 27.4k | ThrowReaderTemporaryFileException(filename); |
3498 | | /* |
3499 | | Parse SVG file. |
3500 | | */ |
3501 | 27.4k | (void) memset(&svg_info,0,sizeof(SVGInfo)); |
3502 | 27.4k | svg_info.file=file; |
3503 | 27.4k | svg_info.exception=exception; |
3504 | 27.4k | svg_info.image=image; |
3505 | 27.4k | svg_info.image_info=image_info; |
3506 | 27.4k | svg_info.text=MagickAllocateResourceLimitedMemory(char *, MaxTextExtent); |
3507 | 27.4k | svg_info.scale=MagickAllocateMemory(double *,sizeof(double)); |
3508 | 27.4k | if ((svg_info.text == (char *) NULL) || (svg_info.scale == (double *) NULL)) |
3509 | 0 | { |
3510 | 0 | (void) fclose(file); |
3511 | 0 | (void) LiberateTemporaryFile(filename); |
3512 | 0 | MagickFreeResourceLimitedMemory(svg_info.text); |
3513 | 0 | MagickFreeMemory(svg_info.scale); |
3514 | 0 | ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
3515 | 0 | } |
3516 | 27.4k | svg_info.text[0] = '\0'; |
3517 | 27.4k | svg_info.text_len=strlen(svg_info.text); |
3518 | 27.4k | IdentityAffine(&svg_info.affine); |
3519 | 27.4k | svg_info.affine.sx= |
3520 | 27.4k | image->x_resolution == 0.0 ? 1.0 : image->x_resolution/72.0; |
3521 | 27.4k | svg_info.affine.sy= |
3522 | 27.4k | image->y_resolution == 0.0 ? 1.0 : image->y_resolution/72.0; |
3523 | 27.4k | svg_info.scale[0]=ExpandAffine(&svg_info.affine); |
3524 | 27.4k | svg_info.bounds.width=image->columns; |
3525 | 27.4k | svg_info.bounds.height=image->rows; |
3526 | 27.4k | svg_info.defsPushCount = 0; |
3527 | 27.4k | svg_info.idLevelInsideDefs = 0; |
3528 | 27.4k | svg_info.svgPushCount = 0; |
3529 | 27.4k | if (image_info->size != (char *) NULL) |
3530 | 0 | (void) CloneString(&svg_info.size,image_info->size); |
3531 | 27.4k | svg_info.signature=MagickSignature; |
3532 | 27.4k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX"); |
3533 | | |
3534 | | /* |
3535 | | TODO: Upgrade to SAX version 2 (startElementNs/endElementNs) |
3536 | | */ |
3537 | 27.4k | xmlSAXVersion(&SAXModules,1); |
3538 | 27.4k | SAXModules.startElement=SVGStartElement; |
3539 | 27.4k | SAXModules.endElement=SVGEndElement; |
3540 | 27.4k | SAXModules.reference=(referenceSAXFunc) NULL; |
3541 | 27.4k | SAXModules.characters=SVGCharacters; |
3542 | 27.4k | SAXModules.ignorableWhitespace=(ignorableWhitespaceSAXFunc) NULL; |
3543 | 27.4k | SAXModules.processingInstruction=(processingInstructionSAXFunc) NULL; |
3544 | 27.4k | SAXModules.comment=SVGComment; |
3545 | 27.4k | SAXModules.warning=SVGWarning; |
3546 | 27.4k | SAXModules.error=SVGError; |
3547 | 27.4k | SAXModules.fatalError=SVGError; |
3548 | 27.4k | SAXModules.cdataBlock=SVGCharacters; |
3549 | | |
3550 | 27.4k | SAXHandler=(&SAXModules); |
3551 | 27.4k | parser=(xmlParserCtxtPtr) NULL; |
3552 | | |
3553 | | /* |
3554 | | https://gnome.pages.gitlab.gnome.org/libxml2/html/parser_8h.html#xmlCreatePushParserCtxt |
3555 | | */ |
3556 | 27.4k | parser=xmlCreatePushParserCtxt(SAXHandler, /* a SAX handler (optional) */ |
3557 | 27.4k | (void *) NULL, /* user data for SAX callbacks (optional) */ |
3558 | 27.4k | (void *) NULL, /* initial chunk (optional, deprecated) */ |
3559 | 27.4k | 0, /* size of initial chunk in bytes */ |
3560 | 27.4k | image->filename /* file name or URI (optional) */ |
3561 | 27.4k | ); |
3562 | | |
3563 | 27.4k | if (parser == (xmlParserCtxtPtr) NULL) |
3564 | 0 | { |
3565 | 0 | ThrowException(exception,DrawError,UnableToDrawOnImage, |
3566 | 0 | "Failed to push XML parser context"); |
3567 | 0 | } |
3568 | 27.4k | if (parser != (xmlParserCtxtPtr) NULL) |
3569 | 27.4k | { |
3570 | | /* |
3571 | | Enable substituting entity values in the output. |
3572 | | |
3573 | | xmlCtxtUseOptions(svg_info.parser,XML_PARSE_NOENT) enables external ENTITY support |
3574 | | (e.g. SVGResolveEntity() which allows XML to be loaded from an |
3575 | | external source (url or local file). This may be a security |
3576 | | hazard if the input is not trustworthy or if connecting to the |
3577 | | correct source is not assured. If the XML is parsed on the |
3578 | | backside of a firewall then it may be able to access unintended |
3579 | | resources. |
3580 | | |
3581 | | See "https://www.w3.org/TR/SVG11/svgdtd.html#DTD.1.16". |
3582 | | |
3583 | | https://gnome.pages.gitlab.gnome.org/libxml2/devhelp/libxml2-parser.html#xmlParserOption |
3584 | | https://gnome.pages.gitlab.gnome.org/libxml2/devhelp/libxml2-parser.html#XML_PARSE_NOENT |
3585 | | |
3586 | | (void) xmlCtxtUseOptions(svg_info.parser,XML_PARSE_NOENT); |
3587 | | */ |
3588 | | /* |
3589 | | Disable network access via XML_PARSE_NONET |
3590 | | (void) xmlCtxtUseOptions(svg_info.parser,XML_PARSE_NONET); |
3591 | | */ |
3592 | | |
3593 | 27.4k | parser->_private=(SVGInfo *) &svg_info; |
3594 | 286k | while ((n=ReadBlob(image,MaxTextExtent-1,message)) != 0) |
3595 | 265k | { |
3596 | 265k | message[n]='\0'; |
3597 | 265k | status=xmlParseChunk(parser,message,(int) n,False); |
3598 | 265k | if (status != 0) |
3599 | 6.27k | break; |
3600 | 265k | } |
3601 | 27.4k | (void) xmlParseChunk(parser,message,0,True); |
3602 | | /* |
3603 | | Assure that our private context is freed, even if we abort before |
3604 | | seeing the document end. |
3605 | | */ |
3606 | | /* SVGEndDocument(&svg_info); FIXME */ |
3607 | 27.4k | if (parser->myDoc != (xmlDocPtr) NULL) |
3608 | 26.3k | { |
3609 | 26.3k | xmlFreeDoc(parser->myDoc); |
3610 | 26.3k | parser->myDoc = (xmlDocPtr) NULL; |
3611 | 26.3k | } |
3612 | | /* |
3613 | | Free all the memory used by a parser context. However the parsed |
3614 | | document in ctxt->myDoc is not freed (so we just did that). |
3615 | | */ |
3616 | 27.4k | xmlFreeParserCtxt(parser); |
3617 | 27.4k | parser=(xmlParserCtxtPtr) NULL; |
3618 | 27.4k | } |
3619 | 27.4k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX"); |
3620 | 27.4k | (void) fclose(file); |
3621 | 27.4k | CloseBlob(image); |
3622 | 27.4k | image->columns=svg_info.width; |
3623 | 27.4k | image->rows=svg_info.height; |
3624 | 27.4k | if (!image_info->ping && (exception->severity == UndefinedException)) |
3625 | 1.90k | { |
3626 | 1.90k | ImageInfo |
3627 | 1.90k | *clone_info; |
3628 | | |
3629 | | /* |
3630 | | Draw image. |
3631 | | */ |
3632 | 1.90k | DestroyImage(image); |
3633 | 1.90k | image=(Image *) NULL; |
3634 | 1.90k | clone_info=CloneImageInfo(image_info); |
3635 | 1.90k | clone_info->blob=(_BlobInfoPtr_) NULL; |
3636 | 1.90k | clone_info->length=0; |
3637 | 1.90k | FormatString(geometry,"%ldx%ld",svg_info.width,svg_info.height); |
3638 | 1.90k | (void) CloneString(&clone_info->size,geometry); |
3639 | 1.90k | FormatString(clone_info->filename,"mvg:%.1024s",filename); |
3640 | 1.90k | if (clone_info->density != (char *) NULL) |
3641 | 0 | MagickFreeMemory(clone_info->density); |
3642 | 1.90k | image=ReadImage(clone_info,exception); |
3643 | 1.90k | DestroyImageInfo(clone_info); |
3644 | 1.90k | if (image != (Image *) NULL) |
3645 | 819 | (void) strlcpy(image->filename,image_info->filename,MaxTextExtent); |
3646 | 1.90k | } |
3647 | | /* |
3648 | | Add/update image attributes |
3649 | | */ |
3650 | 27.4k | if (image != (Image *) NULL) |
3651 | 26.3k | { |
3652 | | /* Title */ |
3653 | 26.3k | if (svg_info.title != (char *) NULL) |
3654 | 190 | (void) SetImageAttribute(image,"title",svg_info.title); |
3655 | | |
3656 | | /* Comment */ |
3657 | 26.3k | if (svg_info.comment != (char *) NULL) |
3658 | 831 | (void) SetImageAttribute(image,"comment",svg_info.comment); |
3659 | 26.3k | } |
3660 | | /* |
3661 | | Free allocated resources from struct SVGInfo |
3662 | | */ |
3663 | 27.4k | MagickFreeMemory(svg_info.size); /* char ptr */ |
3664 | 27.4k | MagickFreeMemory(svg_info.title); /* char ptr */ |
3665 | 27.4k | MagickFreeResourceLimitedMemory(svg_info.comment); /* char ptr */ |
3666 | 27.4k | MagickFreeMemory(svg_info.scale); /* double ptr */ |
3667 | 27.4k | MagickFreeMemory(svg_info.stop_color); /* char ptr */ |
3668 | 27.4k | MagickFreeMemory(svg_info.offset); /* char ptr */ |
3669 | 27.4k | MagickFreeResourceLimitedMemory(svg_info.text); /* char ptr */ |
3670 | 27.4k | MagickFreeMemory(svg_info.vertices); /* char ptr */ |
3671 | 27.4k | MagickFreeMemory(svg_info.url); /* char ptr */ |
3672 | | |
3673 | 27.4k | (void) memset(&svg_info,0xbf,sizeof(SVGInfo)); |
3674 | 27.4k | (void) LiberateTemporaryFile(filename); |
3675 | 27.4k | if (image != (Image *) NULL) |
3676 | 26.3k | StopTimer(&image->timer); |
3677 | 27.4k | return(image); |
3678 | 27.4k | } |
3679 | | #endif |
3680 | | |
3681 | | /* |
3682 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3683 | | % % |
3684 | | % % |
3685 | | % % |
3686 | | % R e g i s t e r S V G I m a g e % |
3687 | | % % |
3688 | | % % |
3689 | | % % |
3690 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3691 | | % |
3692 | | % Method RegisterSVGImage adds attributes for the SVG image format to |
3693 | | % the list of supported formats. The attributes include the image format |
3694 | | % tag, a method to read and/or write the format, whether the format |
3695 | | % supports the saving of more than one frame to the same file or blob, |
3696 | | % whether the format supports native in-memory I/O, and a brief |
3697 | | % description of the format. |
3698 | | % |
3699 | | % The SVG writer is disabled by default since it does not work. |
3700 | | % |
3701 | | % The format of the RegisterSVGImage method is: |
3702 | | % |
3703 | | % RegisterSVGImage(void) |
3704 | | % |
3705 | | */ |
3706 | | ModuleExport void |
3707 | | RegisterSVGImage(void) |
3708 | 4 | { |
3709 | 4 | #if defined(LIBXML_DOTTED_VERSION) |
3710 | 4 | static const char |
3711 | 4 | version[] = "XML " LIBXML_DOTTED_VERSION; |
3712 | 4 | #define HAS_VERSION 1 |
3713 | 4 | #endif |
3714 | | |
3715 | 4 | MagickInfo |
3716 | 4 | *entry; |
3717 | | |
3718 | 4 | entry=SetMagickInfo("SVG"); |
3719 | 4 | #if defined(HasXML) |
3720 | 4 | entry->decoder=(DecoderHandler) ReadSVGImage; |
3721 | 4 | #endif /* defined(HasXML) */ |
3722 | | #if defined(ENABLE_SVG_WRITER) && ENABLE_SVG_WRITER |
3723 | | entry->encoder=(EncoderHandler) WriteSVGImage; |
3724 | | #endif /* if defined(ENABLE_SVG_WRITER) && ENABLE_SVG_WRITER */ |
3725 | 4 | entry->description="Scalable Vector Graphics"; |
3726 | 4 | #if defined(HAS_VERSION) |
3727 | 4 | entry->version=version; |
3728 | 4 | #endif |
3729 | 4 | entry->module="SVG"; |
3730 | 4 | (void) RegisterMagickInfo(entry); |
3731 | | |
3732 | 4 | entry=SetMagickInfo("SVGZ"); |
3733 | 4 | #if defined(HasXML) |
3734 | 4 | entry->decoder=(DecoderHandler) ReadSVGImage; |
3735 | 4 | #endif /* defined(HasXML) */ |
3736 | | #if defined(ENABLE_SVG_WRITER) && ENABLE_SVG_WRITER |
3737 | | entry->encoder=(EncoderHandler) WriteSVGImage; |
3738 | | #endif /* if defined(ENABLE_SVG_WRITER) && ENABLE_SVG_WRITER */ |
3739 | 4 | entry->description="Scalable Vector Graphics (ZIP compressed)"; |
3740 | 4 | #if defined(HAS_VERSION) |
3741 | 4 | entry->version=version; |
3742 | 4 | #endif |
3743 | 4 | entry->module="SVG"; |
3744 | 4 | (void) RegisterMagickInfo(entry); |
3745 | 4 | } |
3746 | | |
3747 | | /* |
3748 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3749 | | % % |
3750 | | % % |
3751 | | % % |
3752 | | % U n r e g i s t e r S V G I m a g e % |
3753 | | % % |
3754 | | % % |
3755 | | % % |
3756 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3757 | | % |
3758 | | % Method UnregisterSVGImage removes format registrations made by the |
3759 | | % SVG module from the list of supported formats. |
3760 | | % |
3761 | | % The format of the UnregisterSVGImage method is: |
3762 | | % |
3763 | | % UnregisterSVGImage(void) |
3764 | | % |
3765 | | */ |
3766 | | ModuleExport void |
3767 | | UnregisterSVGImage(void) |
3768 | 0 | { |
3769 | | /* |
3770 | | Libxml clean-up. Should only be called just before exit(). |
3771 | | */ |
3772 | | /* xmlCleanupParser(); */ |
3773 | 0 | (void) UnregisterMagickInfo("SVG"); |
3774 | 0 | (void) UnregisterMagickInfo("SVGZ"); |
3775 | 0 | } |
3776 | | |
3777 | | #if defined(ENABLE_SVG_WRITER) && ENABLE_SVG_WRITER |
3778 | | /* |
3779 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3780 | | % % |
3781 | | % % |
3782 | | % % |
3783 | | % W r i t e S V G I m a g e % |
3784 | | % % |
3785 | | % % |
3786 | | % % |
3787 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3788 | | % |
3789 | | % Method WriteSVGImage writes a image in the SVG - XML based W3C standard |
3790 | | % format. |
3791 | | % |
3792 | | % The format of the WriteSVGImage method is: |
3793 | | % |
3794 | | % unsigned int WriteSVGImage(const ImageInfo *image_info,Image *image) |
3795 | | % |
3796 | | % A description of each parameter follows. |
3797 | | % |
3798 | | % o status: Method WriteSVGImage return True if the image is written. |
3799 | | % False is returned is there is a memory shortage or if the image file |
3800 | | % fails to write. |
3801 | | % |
3802 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
3803 | | % |
3804 | | % o image: A pointer to an Image structure. |
3805 | | % |
3806 | | % |
3807 | | */ |
3808 | | |
3809 | | #if defined(HasAUTOTRACE) |
3810 | | static unsigned int |
3811 | | WriteSVGImage(const ImageInfo *image_info,Image *image) |
3812 | | { |
3813 | | FILE |
3814 | | *output_file; |
3815 | | |
3816 | | fitting_opts_type |
3817 | | fit_info; |
3818 | | |
3819 | | image_header_type |
3820 | | image_header; |
3821 | | |
3822 | | bitmap_type |
3823 | | bitmap; |
3824 | | |
3825 | | ImageType |
3826 | | image_type; |
3827 | | |
3828 | | int |
3829 | | j; |
3830 | | |
3831 | | pixel_outline_list_type |
3832 | | pixels; |
3833 | | |
3834 | | PixelPacket |
3835 | | p; |
3836 | | |
3837 | | PixelPacket |
3838 | | *pixel; |
3839 | | |
3840 | | QuantizeObj |
3841 | | *quantize_info; |
3842 | | |
3843 | | spline_list_array_type |
3844 | | splines; |
3845 | | |
3846 | | output_write |
3847 | | output_writer; |
3848 | | |
3849 | | register long |
3850 | | i; |
3851 | | |
3852 | | unsigned long |
3853 | | number_pixels, |
3854 | | number_planes, |
3855 | | point, |
3856 | | thin; |
3857 | | |
3858 | | thin=False; |
3859 | | quantize_info=(QuantizeObj *) NULL; |
3860 | | pixel=(&p); |
3861 | | fit_info=new_fitting_opts(); |
3862 | | output_writer=output_get_handler("svg"); |
3863 | | if (output_writer == NULL) |
3864 | | ThrowWriterException(DelegateError,UnableToWriteSVGFormat,image); |
3865 | | image_type=GetImageType(image); |
3866 | | number_planes=3; |
3867 | | if ((image_type == BilevelType) || (image_type == GrayscaleType)) |
3868 | | number_planes=1; |
3869 | | bitmap.np=number_planes; |
3870 | | bitmap.dimensions.width=image->columns; |
3871 | | bitmap.dimensions.height=image->rows; |
3872 | | number_pixels=image->columns*image->rows; |
3873 | | bitmap.bitmap=MagickAllocateMemory(unsigned char *,number_planes*number_pixels); |
3874 | | if (bitmap.bitmap == (unsigned char *) NULL) |
3875 | | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
3876 | | point=0; |
3877 | | for (j=0; j < image->rows; j++) |
3878 | | { |
3879 | | for (i=0; i < image->columns; i++) |
3880 | | { |
3881 | | p=AcquireOnePixel(image,i,j,&image->exception); |
3882 | | bitmap.bitmap[point++]=pixel->red; |
3883 | | if (number_planes == 3) |
3884 | | { |
3885 | | bitmap.bitmap[point++]=pixel->green; |
3886 | | bitmap.bitmap[point++]=pixel->blue; |
3887 | | } |
3888 | | } |
3889 | | } |
3890 | | image_header.width=DIMENSIONS_WIDTH(bitmap.dimensions); |
3891 | | image_header.height=DIMENSIONS_HEIGHT(bitmap.dimensions); |
3892 | | if ((fit_info.color_count > 0) && (BITMAP_PLANES(bitmap) == 3)) |
3893 | | quantize(bitmap.bitmap,bitmap.bitmap,DIMENSIONS_WIDTH(bitmap.dimensions), |
3894 | | DIMENSIONS_HEIGHT(bitmap.dimensions),fit_info.color_count, |
3895 | | fit_info.bgColor,&quantize_info); |
3896 | | if (thin) |
3897 | | thin_image(&bitmap); |
3898 | | pixels=find_outline_pixels (bitmap); |
3899 | | MagickFreeMemory((bitmap.bitmap)); |
3900 | | splines=fitted_splines(pixels,&fit_info); |
3901 | | output_file=fopen(image->filename,"w"); |
3902 | | if (output_file == (FILE *) NULL) |
3903 | | ThrowWriterException(FileOpenError,UnableOpenFile,image); |
3904 | | output_writer(output_file,image->filename,0,0,image_header.width, |
3905 | | image_header.height,splines); |
3906 | | return(True); |
3907 | | } |
3908 | | #else |
3909 | | static void |
3910 | | AffineToTransform(Image *image,AffineMatrix *affine) |
3911 | | { |
3912 | | char |
3913 | | transform[MaxTextExtent]; |
3914 | | |
3915 | | if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon)) |
3916 | | { |
3917 | | if ((fabs(affine->rx) < MagickEpsilon) && |
3918 | | (fabs(affine->ry) < MagickEpsilon)) |
3919 | | { |
3920 | | if ((fabs(affine->sx-1.0) < MagickEpsilon) && |
3921 | | (fabs(affine->sy-1.0) < MagickEpsilon)) |
3922 | | { |
3923 | | (void) WriteBlobString(image,"\">\n"); |
3924 | | return; |
3925 | | } |
3926 | | FormatString(transform,"\" transform=\"scale(%g,%g)\">\n", |
3927 | | affine->sx,affine->sy); |
3928 | | (void) WriteBlobString(image,transform); |
3929 | | return; |
3930 | | } |
3931 | | else |
3932 | | { |
3933 | | if ((fabs(affine->sx-affine->sy) < MagickEpsilon) && |
3934 | | (fabs(affine->rx+affine->ry) < MagickEpsilon) && |
3935 | | (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) < |
3936 | | 2*MagickEpsilon)) |
3937 | | { |
3938 | | double |
3939 | | theta; |
3940 | | |
3941 | | theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx); |
3942 | | FormatString(transform,"\" transform=\"rotate(%g)\">\n",theta); |
3943 | | (void) WriteBlobString(image,transform); |
3944 | | return; |
3945 | | } |
3946 | | } |
3947 | | } |
3948 | | else |
3949 | | { |
3950 | | if ((fabs(affine->sx-1.0) < MagickEpsilon) && |
3951 | | (fabs(affine->rx) < MagickEpsilon) && |
3952 | | (fabs(affine->ry) < MagickEpsilon) && |
3953 | | (fabs(affine->sy-1.0) < MagickEpsilon)) |
3954 | | { |
3955 | | FormatString(transform,"\" transform=\"translate(%g,%g)\">\n", |
3956 | | affine->tx,affine->ty); |
3957 | | (void) WriteBlobString(image,transform); |
3958 | | return; |
3959 | | } |
3960 | | } |
3961 | | FormatString(transform,"\" transform=\"matrix(%g %g %g %g %g %g)\">\n", |
3962 | | affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty); |
3963 | | (void) WriteBlobString(image,transform); |
3964 | | } |
3965 | | |
3966 | | static inline unsigned int |
3967 | | IsPoint(const char *point) |
3968 | | { |
3969 | | char |
3970 | | *p; |
3971 | | |
3972 | | (void) strtol(point,&p,10); |
3973 | | return(p != point); |
3974 | | } |
3975 | | |
3976 | | static unsigned int |
3977 | | WriteSVGImage(const ImageInfo *image_info,Image *image) |
3978 | | { |
3979 | | #define BezierQuantum 200 |
3980 | | |
3981 | | AffineMatrix |
3982 | | affine; |
3983 | | |
3984 | | char |
3985 | | keyword[MaxTextExtent], |
3986 | | message[MaxTextExtent], |
3987 | | name[MaxTextExtent], |
3988 | | *p, |
3989 | | *q, |
3990 | | *token, |
3991 | | type[MaxTextExtent]; |
3992 | | |
3993 | | const ImageAttribute |
3994 | | *attribute; |
3995 | | |
3996 | | int |
3997 | | n; |
3998 | | |
3999 | | long |
4000 | | j; |
4001 | | |
4002 | | PointInfo |
4003 | | point; |
4004 | | |
4005 | | PrimitiveInfo |
4006 | | *primitive_info; |
4007 | | |
4008 | | PrimitiveType |
4009 | | primitive_type; |
4010 | | |
4011 | | register long |
4012 | | x; |
4013 | | |
4014 | | register long |
4015 | | i; |
4016 | | |
4017 | | size_t |
4018 | | length, |
4019 | | token_max_length; |
4020 | | |
4021 | | SVGInfo |
4022 | | svg_info; |
4023 | | |
4024 | | unsigned int |
4025 | | active, |
4026 | | status; |
4027 | | |
4028 | | unsigned long |
4029 | | number_points; |
4030 | | |
4031 | | /* |
4032 | | Open output image file. |
4033 | | */ |
4034 | | assert(image_info != (const ImageInfo *) NULL); |
4035 | | assert(image_info->signature == MagickSignature); |
4036 | | assert(image != (Image *) NULL); |
4037 | | assert(image->signature == MagickSignature); |
4038 | | attribute=GetImageAttribute(image,"[MVG]"); |
4039 | | if ((attribute == (ImageAttribute *) NULL) || |
4040 | | (attribute->value == (char *) NULL)) |
4041 | | ThrowWriterException(CoderError,NoImageVectorGraphics,image); |
4042 | | status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); |
4043 | | if (status == False) |
4044 | | ThrowWriterException(FileOpenError,UnableToOpenFile,image); |
4045 | | /* |
4046 | | Write SVG header. |
4047 | | */ |
4048 | | (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n"); |
4049 | | (void) WriteBlobString(image, |
4050 | | "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n"); |
4051 | | (void) WriteBlobString(image, |
4052 | | " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"); |
4053 | | (void) FormatString(message,"<svg width=\"%lu\" height=\"%lu\">\n", |
4054 | | image->columns,image->rows); |
4055 | | (void) WriteBlobString(image,message); |
4056 | | /* |
4057 | | Allocate primitive info memory. |
4058 | | */ |
4059 | | number_points=2047; |
4060 | | primitive_info=MagickAllocateMemory(PrimitiveInfo *, |
4061 | | number_points*sizeof(PrimitiveInfo)); |
4062 | | if (primitive_info == (PrimitiveInfo *) NULL) |
4063 | | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
4064 | | IdentityAffine(&affine); |
4065 | | token=AcquireString(attribute->value); |
4066 | | token_max_length=strlen(token); |
4067 | | active=False; |
4068 | | n=0; |
4069 | | status=True; |
4070 | | for (q=attribute->value; *q != '\0'; ) |
4071 | | { |
4072 | | /* |
4073 | | Interpret graphic primitive. |
4074 | | */ |
4075 | | MagickGetToken(q,&q,keyword,MaxTextExtent); |
4076 | | if (*keyword == '\0') |
4077 | | break; |
4078 | | if (*keyword == '#') |
4079 | | { |
4080 | | /* |
4081 | | Comment. |
4082 | | */ |
4083 | | if (active) |
4084 | | { |
4085 | | AffineToTransform(image,&affine); |
4086 | | active=False; |
4087 | | } |
4088 | | (void) WriteBlobString(image,"<desc>"); |
4089 | | (void) WriteBlobString(image,keyword+1); |
4090 | | for ( ; (*q != '\n') && (*q != '\0'); q++) |
4091 | | switch (*q) |
4092 | | { |
4093 | | case '<': (void) WriteBlobString(image,"<"); break; |
4094 | | case '>': (void) WriteBlobString(image,">"); break; |
4095 | | case '&': (void) WriteBlobString(image,"&"); break; |
4096 | | default: (void) WriteBlobByte(image,*q); break; |
4097 | | } |
4098 | | (void) WriteBlobString(image,"</desc>\n"); |
4099 | | continue; |
4100 | | } |
4101 | | primitive_type=UndefinedPrimitive; |
4102 | | switch (*keyword) |
4103 | | { |
4104 | | case ';': |
4105 | | break; |
4106 | | case 'a': |
4107 | | case 'A': |
4108 | | { |
4109 | | if (LocaleCompare("affine",keyword) == 0) |
4110 | | { |
4111 | | MagickGetToken(q,&q,token,token_max_length); |
4112 | | affine.sx=MagickAtoF(token); |
4113 | | MagickGetToken(q,&q,token,token_max_length); |
4114 | | if (*token == ',') |
4115 | | MagickGetToken(q,&q,token,token_max_length); |
4116 | | affine.rx=MagickAtoF(token); |
4117 | | MagickGetToken(q,&q,token,token_max_length); |
4118 | | if (*token == ',') |
4119 | | MagickGetToken(q,&q,token,token_max_length); |
4120 | | affine.ry=MagickAtoF(token); |
4121 | | MagickGetToken(q,&q,token,token_max_length); |
4122 | | if (*token == ',') |
4123 | | MagickGetToken(q,&q,token,token_max_length); |
4124 | | affine.sy=MagickAtoF(token); |
4125 | | MagickGetToken(q,&q,token,token_max_length); |
4126 | | if (*token == ',') |
4127 | | MagickGetToken(q,&q,token,token_max_length); |
4128 | | affine.tx=MagickAtoF(token); |
4129 | | MagickGetToken(q,&q,token,token_max_length); |
4130 | | if (*token == ',') |
4131 | | MagickGetToken(q,&q,token,token_max_length); |
4132 | | affine.ty=MagickAtoF(token); |
4133 | | break; |
4134 | | } |
4135 | | if (LocaleCompare("angle",keyword) == 0) |
4136 | | { |
4137 | | MagickGetToken(q,&q,token,token_max_length); |
4138 | | affine.rx=MagickAtoF(token); |
4139 | | affine.ry=MagickAtoF(token); |
4140 | | break; |
4141 | | } |
4142 | | if (LocaleCompare("arc",keyword) == 0) |
4143 | | { |
4144 | | primitive_type=ArcPrimitive; |
4145 | | break; |
4146 | | } |
4147 | | status=False; |
4148 | | break; |
4149 | | } |
4150 | | case 'b': |
4151 | | case 'B': |
4152 | | { |
4153 | | if (LocaleCompare("bezier",keyword) == 0) |
4154 | | { |
4155 | | primitive_type=BezierPrimitive; |
4156 | | break; |
4157 | | } |
4158 | | status=False; |
4159 | | break; |
4160 | | } |
4161 | | case 'c': |
4162 | | case 'C': |
4163 | | { |
4164 | | if (LocaleCompare("clip-path",keyword) == 0) |
4165 | | { |
4166 | | MagickGetToken(q,&q,token,token_max_length); |
4167 | | FormatString(message,"clip-path:url(#%.1024s);",token); |
4168 | | (void) WriteBlobString(image,message); |
4169 | | break; |
4170 | | } |
4171 | | if (LocaleCompare("clip-rule",keyword) == 0) |
4172 | | { |
4173 | | MagickGetToken(q,&q,token,token_max_length); |
4174 | | FormatString(message,"clip-rule:%.1024s;",token); |
4175 | | (void) WriteBlobString(image,message); |
4176 | | break; |
4177 | | } |
4178 | | if (LocaleCompare("clip-units",keyword) == 0) |
4179 | | { |
4180 | | MagickGetToken(q,&q,token,token_max_length); |
4181 | | FormatString(message,"clipPathUnits=%.1024s;",token); |
4182 | | (void) WriteBlobString(image,message); |
4183 | | break; |
4184 | | } |
4185 | | if (LocaleCompare("circle",keyword) == 0) |
4186 | | { |
4187 | | primitive_type=CirclePrimitive; |
4188 | | break; |
4189 | | } |
4190 | | if (LocaleCompare("color",keyword) == 0) |
4191 | | { |
4192 | | primitive_type=ColorPrimitive; |
4193 | | break; |
4194 | | } |
4195 | | status=False; |
4196 | | break; |
4197 | | } |
4198 | | case 'd': |
4199 | | case 'D': |
4200 | | { |
4201 | | if (LocaleCompare("decorate",keyword) == 0) |
4202 | | { |
4203 | | MagickGetToken(q,&q,token,token_max_length); |
4204 | | FormatString(message,"text-decoration:%.1024s;",token); |
4205 | | (void) WriteBlobString(image,message); |
4206 | | break; |
4207 | | } |
4208 | | status=False; |
4209 | | break; |
4210 | | } |
4211 | | case 'e': |
4212 | | case 'E': |
4213 | | { |
4214 | | if (LocaleCompare("ellipse",keyword) == 0) |
4215 | | { |
4216 | | primitive_type=EllipsePrimitive; |
4217 | | break; |
4218 | | } |
4219 | | status=False; |
4220 | | break; |
4221 | | } |
4222 | | case 'f': |
4223 | | case 'F': |
4224 | | { |
4225 | | if (LocaleCompare("fill",keyword) == 0) |
4226 | | { |
4227 | | MagickGetToken(q,&q,token,token_max_length); |
4228 | | FormatString(message,"fill:%.1024s;",token); |
4229 | | (void) WriteBlobString(image,message); |
4230 | | break; |
4231 | | } |
4232 | | if (LocaleCompare("fill-rule",keyword) == 0) |
4233 | | { |
4234 | | MagickGetToken(q,&q,token,token_max_length); |
4235 | | FormatString(message,"fill-rule:%.1024s;",token); |
4236 | | (void) WriteBlobString(image,message); |
4237 | | break; |
4238 | | } |
4239 | | if (LocaleCompare("fill-opacity",keyword) == 0) |
4240 | | { |
4241 | | MagickGetToken(q,&q,token,token_max_length); |
4242 | | FormatString(message,"fill-opacity:%.1024s;",token); |
4243 | | (void) WriteBlobString(image,message); |
4244 | | break; |
4245 | | } |
4246 | | if (LocaleCompare("font-family",keyword) == 0) |
4247 | | { |
4248 | | MagickGetToken(q,&q,token,token_max_length); |
4249 | | FormatString(message,"font-family:%.1024s;",token); |
4250 | | (void) WriteBlobString(image,message); |
4251 | | break; |
4252 | | } |
4253 | | if (LocaleCompare("font-stretch",keyword) == 0) |
4254 | | { |
4255 | | MagickGetToken(q,&q,token,token_max_length); |
4256 | | FormatString(message,"font-stretch:%.1024s;",token); |
4257 | | (void) WriteBlobString(image,message); |
4258 | | break; |
4259 | | } |
4260 | | if (LocaleCompare("font-style",keyword) == 0) |
4261 | | { |
4262 | | MagickGetToken(q,&q,token,token_max_length); |
4263 | | FormatString(message,"font-style:%.1024s;",token); |
4264 | | (void) WriteBlobString(image,message); |
4265 | | break; |
4266 | | } |
4267 | | if (LocaleCompare("font-size",keyword) == 0) |
4268 | | { |
4269 | | MagickGetToken(q,&q,token,token_max_length); |
4270 | | FormatString(message,"font-size:%.1024s;",token); |
4271 | | (void) WriteBlobString(image,message); |
4272 | | break; |
4273 | | } |
4274 | | if (LocaleCompare("font-weight",keyword) == 0) |
4275 | | { |
4276 | | MagickGetToken(q,&q,token,token_max_length); |
4277 | | FormatString(message,"font-weight:%.1024s;",token); |
4278 | | (void) WriteBlobString(image,message); |
4279 | | break; |
4280 | | } |
4281 | | status=False; |
4282 | | break; |
4283 | | } |
4284 | | case 'g': |
4285 | | case 'G': |
4286 | | { |
4287 | | if (LocaleCompare("gradient-units",keyword) == 0) |
4288 | | { |
4289 | | MagickGetToken(q,&q,token,token_max_length); |
4290 | | break; |
4291 | | } |
4292 | | if (LocaleCompare("text-align",keyword) == 0) |
4293 | | { |
4294 | | MagickGetToken(q,&q,token,token_max_length); |
4295 | | FormatString(message,"text-align %.1024s ",token); |
4296 | | (void) WriteBlobString(image,message); |
4297 | | break; |
4298 | | } |
4299 | | if (LocaleCompare("text-anchor",keyword) == 0) |
4300 | | { |
4301 | | MagickGetToken(q,&q,token,token_max_length); |
4302 | | FormatString(message,"text-anchor %.1024s ",token); |
4303 | | (void) WriteBlobString(image,message); |
4304 | | break; |
4305 | | } |
4306 | | status=False; |
4307 | | break; |
4308 | | } |
4309 | | case 'i': |
4310 | | case 'I': |
4311 | | { |
4312 | | if (LocaleCompare("image",keyword) == 0) |
4313 | | { |
4314 | | MagickGetToken(q,&q,token,token_max_length); |
4315 | | primitive_type=ImagePrimitive; |
4316 | | break; |
4317 | | } |
4318 | | status=False; |
4319 | | break; |
4320 | | } |
4321 | | case 'l': |
4322 | | case 'L': |
4323 | | { |
4324 | | if (LocaleCompare("line",keyword) == 0) |
4325 | | { |
4326 | | primitive_type=LinePrimitive; |
4327 | | break; |
4328 | | } |
4329 | | status=False; |
4330 | | break; |
4331 | | } |
4332 | | case 'm': |
4333 | | case 'M': |
4334 | | { |
4335 | | if (LocaleCompare("matte",keyword) == 0) |
4336 | | { |
4337 | | primitive_type=MattePrimitive; |
4338 | | break; |
4339 | | } |
4340 | | status=False; |
4341 | | break; |
4342 | | } |
4343 | | case 'o': |
4344 | | case 'O': |
4345 | | { |
4346 | | if (LocaleCompare("opacity",keyword) == 0) |
4347 | | { |
4348 | | MagickGetToken(q,&q,token,token_max_length); |
4349 | | FormatString(message,"opacity %.1024s ",token); |
4350 | | (void) WriteBlobString(image,message); |
4351 | | break; |
4352 | | } |
4353 | | status=False; |
4354 | | break; |
4355 | | } |
4356 | | case 'p': |
4357 | | case 'P': |
4358 | | { |
4359 | | if (LocaleCompare("path",keyword) == 0) |
4360 | | { |
4361 | | primitive_type=PathPrimitive; |
4362 | | break; |
4363 | | } |
4364 | | if (LocaleCompare("point",keyword) == 0) |
4365 | | { |
4366 | | primitive_type=PointPrimitive; |
4367 | | break; |
4368 | | } |
4369 | | if (LocaleCompare("polyline",keyword) == 0) |
4370 | | { |
4371 | | primitive_type=PolylinePrimitive; |
4372 | | break; |
4373 | | } |
4374 | | if (LocaleCompare("polygon",keyword) == 0) |
4375 | | { |
4376 | | primitive_type=PolygonPrimitive; |
4377 | | break; |
4378 | | } |
4379 | | if (LocaleCompare("pop",keyword) == 0) |
4380 | | { |
4381 | | MagickGetToken(q,&q,token,token_max_length); |
4382 | | if (LocaleCompare("clip-path",token) == 0) |
4383 | | { |
4384 | | (void) WriteBlobString(image,"</clipPath>\n"); |
4385 | | break; |
4386 | | } |
4387 | | if (LocaleCompare("defs",token) == 0) |
4388 | | { |
4389 | | (void) WriteBlobString(image,"</defs>\n"); |
4390 | | break; |
4391 | | } |
4392 | | if (LocaleCompare("gradient",token) == 0) |
4393 | | { |
4394 | | FormatString(message,"</%sGradient>\n",type); |
4395 | | (void) WriteBlobString(image,message); |
4396 | | break; |
4397 | | } |
4398 | | if (LocaleCompare("graphic-context",token) == 0) |
4399 | | { |
4400 | | n--; |
4401 | | if (n < 0) |
4402 | | ThrowWriterException(DrawError, |
4403 | | UnbalancedGraphicContextPushPop,image); |
4404 | | (void) WriteBlobString(image,"</g>\n"); |
4405 | | } |
4406 | | if (LocaleCompare("pattern",token) == 0) |
4407 | | { |
4408 | | (void) WriteBlobString(image,"</pattern>\n"); |
4409 | | break; |
4410 | | } |
4411 | | if (LocaleCompare("defs",token) == 0) |
4412 | | (void) WriteBlobString(image,"</g>\n"); |
4413 | | break; |
4414 | | } |
4415 | | if (LocaleCompare("push",keyword) == 0) |
4416 | | { |
4417 | | MagickGetToken(q,&q,token,token_max_length); |
4418 | | if (LocaleCompare("clip-path",token) == 0) |
4419 | | { |
4420 | | MagickGetToken(q,&q,token,token_max_length); |
4421 | | FormatString(message,"<clipPath id=\"%s\">\n",token); |
4422 | | (void) WriteBlobString(image,message); |
4423 | | break; |
4424 | | } |
4425 | | if (LocaleCompare("defs",token) == 0) |
4426 | | { |
4427 | | (void) WriteBlobString(image,"<defs>\n"); |
4428 | | break; |
4429 | | } |
4430 | | if (LocaleCompare("gradient",token) == 0) |
4431 | | { |
4432 | | MagickGetToken(q,&q,token,token_max_length); |
4433 | | (void) strlcpy(name,token,MaxTextExtent); |
4434 | | MagickGetToken(q,&q,token,token_max_length); |
4435 | | (void) strlcpy(type,token,MaxTextExtent); |
4436 | | MagickGetToken(q,&q,token,token_max_length); |
4437 | | svg_info.segment.x1=MagickAtoF(token); |
4438 | | svg_info.element.cx=MagickAtoF(token); |
4439 | | MagickGetToken(q,&q,token,token_max_length); |
4440 | | if (*token == ',') |
4441 | | MagickGetToken(q,&q,token,token_max_length); |
4442 | | svg_info.segment.y1=MagickAtoF(token); |
4443 | | svg_info.element.cy=MagickAtoF(token); |
4444 | | MagickGetToken(q,&q,token,token_max_length); |
4445 | | if (*token == ',') |
4446 | | MagickGetToken(q,&q,token,token_max_length); |
4447 | | svg_info.segment.x2=MagickAtoF(token); |
4448 | | svg_info.element.major=MagickAtoF(token); |
4449 | | MagickGetToken(q,&q,token,token_max_length); |
4450 | | if (*token == ',') |
4451 | | MagickGetToken(q,&q,token,token_max_length); |
4452 | | svg_info.segment.y2=MagickAtoF(token); |
4453 | | svg_info.element.minor=MagickAtoF(token); |
4454 | | FormatString(message,"<%sGradient id=\"%s\" x1=\"%g\" " |
4455 | | "y1=\"%g\" x2=\"%g\" y2=\"%g\">\n",type,name, |
4456 | | svg_info.segment.x1,svg_info.segment.y1,svg_info.segment.x2, |
4457 | | svg_info.segment.y2); |
4458 | | if (LocaleCompare(type,"radial") == 0) |
4459 | | { |
4460 | | MagickGetToken(q,&q,token,token_max_length); |
4461 | | if (*token == ',') |
4462 | | MagickGetToken(q,&q,token,token_max_length); |
4463 | | svg_info.element.angle=MagickAtoF(token); |
4464 | | FormatString(message,"<%sGradient id=\"%s\" cx=\"%g\" " |
4465 | | "cy=\"%g\" r=\"%g\" fx=\"%g\" fy=\"%g\">\n",type,name, |
4466 | | svg_info.element.cx,svg_info.element.cy, |
4467 | | svg_info.element.angle,svg_info.element.major, |
4468 | | svg_info.element.minor); |
4469 | | } |
4470 | | (void) WriteBlobString(image,message); |
4471 | | break; |
4472 | | } |
4473 | | if (LocaleCompare("graphic-context",token) == 0) |
4474 | | { |
4475 | | n++; |
4476 | | if (active) |
4477 | | { |
4478 | | AffineToTransform(image,&affine); |
4479 | | active=False; |
4480 | | } |
4481 | | (void) WriteBlobString(image,"<g style=\""); |
4482 | | active=True; |
4483 | | } |
4484 | | if (LocaleCompare("pattern",token) == 0) |
4485 | | { |
4486 | | MagickGetToken(q,&q,token,token_max_length); |
4487 | | (void) strlcpy(name,token,MaxTextExtent); |
4488 | | MagickGetToken(q,&q,token,token_max_length); |
4489 | | svg_info.bounds.x=MagickAtoF(token); |
4490 | | MagickGetToken(q,&q,token,token_max_length); |
4491 | | if (*token == ',') |
4492 | | MagickGetToken(q,&q,token,token_max_length); |
4493 | | svg_info.bounds.y=MagickAtoF(token); |
4494 | | MagickGetToken(q,&q,token,token_max_length); |
4495 | | if (*token == ',') |
4496 | | MagickGetToken(q,&q,token,token_max_length); |
4497 | | svg_info.bounds.width=MagickAtoF(token); |
4498 | | MagickGetToken(q,&q,token,token_max_length); |
4499 | | if (*token == ',') |
4500 | | MagickGetToken(q,&q,token,token_max_length); |
4501 | | svg_info.bounds.height=MagickAtoF(token); |
4502 | | FormatString(message,"<pattern id=\"%s\" x=\"%g\" y=\"%g\" " |
4503 | | "width=\"%g\" height=\"%g\">\n",name,svg_info.bounds.x, |
4504 | | svg_info.bounds.y,svg_info.bounds.width, |
4505 | | svg_info.bounds.height); |
4506 | | (void) WriteBlobString(image,message); |
4507 | | break; |
4508 | | } |
4509 | | break; |
4510 | | } |
4511 | | status=False; |
4512 | | break; |
4513 | | } |
4514 | | case 'r': |
4515 | | case 'R': |
4516 | | { |
4517 | | if (LocaleCompare("rectangle",keyword) == 0) |
4518 | | { |
4519 | | primitive_type=RectanglePrimitive; |
4520 | | break; |
4521 | | } |
4522 | | if (LocaleCompare("roundRectangle",keyword) == 0) |
4523 | | { |
4524 | | primitive_type=RoundRectanglePrimitive; |
4525 | | break; |
4526 | | } |
4527 | | if (LocaleCompare("rotate",keyword) == 0) |
4528 | | { |
4529 | | MagickGetToken(q,&q,token,token_max_length); |
4530 | | FormatString(message,"rotate(%.1024s) ",token); |
4531 | | (void) WriteBlobString(image,message); |
4532 | | break; |
4533 | | } |
4534 | | status=False; |
4535 | | break; |
4536 | | } |
4537 | | case 's': |
4538 | | case 'S': |
4539 | | { |
4540 | | if (LocaleCompare("scale",keyword) == 0) |
4541 | | { |
4542 | | MagickGetToken(q,&q,token,token_max_length); |
4543 | | affine.sx=MagickAtoF(token); |
4544 | | MagickGetToken(q,&q,token,token_max_length); |
4545 | | if (*token == ',') |
4546 | | MagickGetToken(q,&q,token,token_max_length); |
4547 | | affine.sy=MagickAtoF(token); |
4548 | | break; |
4549 | | } |
4550 | | if (LocaleCompare("skewX",keyword) == 0) |
4551 | | { |
4552 | | MagickGetToken(q,&q,token,token_max_length); |
4553 | | FormatString(message,"skewX(%.1024s) ",token); |
4554 | | (void) WriteBlobString(image,message); |
4555 | | break; |
4556 | | } |
4557 | | if (LocaleCompare("skewY",keyword) == 0) |
4558 | | { |
4559 | | MagickGetToken(q,&q,token,token_max_length); |
4560 | | FormatString(message,"skewY(%.1024s) ",token); |
4561 | | (void) WriteBlobString(image,message); |
4562 | | break; |
4563 | | } |
4564 | | if (LocaleCompare("stop-color",keyword) == 0) |
4565 | | { |
4566 | | char |
4567 | | color[MaxTextExtent]; |
4568 | | |
4569 | | MagickGetToken(q,&q,token,token_max_length); |
4570 | | (void) strlcpy(color,token,MaxTextExtent); |
4571 | | MagickGetToken(q,&q,token,token_max_length); |
4572 | | FormatString(message, |
4573 | | " <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color); |
4574 | | (void) WriteBlobString(image,message); |
4575 | | break; |
4576 | | } |
4577 | | if (LocaleCompare("stroke",keyword) == 0) |
4578 | | { |
4579 | | MagickGetToken(q,&q,token,token_max_length); |
4580 | | FormatString(message,"stroke:%.1024s;",token); |
4581 | | (void) WriteBlobString(image,message); |
4582 | | break; |
4583 | | } |
4584 | | if (LocaleCompare("stroke-antialias",keyword) == 0) |
4585 | | { |
4586 | | MagickGetToken(q,&q,token,token_max_length); |
4587 | | FormatString(message,"stroke-antialias:%.1024s;",token); |
4588 | | (void) WriteBlobString(image,message); |
4589 | | break; |
4590 | | } |
4591 | | if (LocaleCompare("stroke-dasharray",keyword) == 0) |
4592 | | { |
4593 | | if (IsPoint(q)) |
4594 | | { |
4595 | | long |
4596 | | k; |
4597 | | |
4598 | | p=q; |
4599 | | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
4600 | | for (k=0; IsPoint(token); k++) |
4601 | | (void) MagickGetToken(p,&p,token,MaxTextExtent); |
4602 | | (void) WriteBlobString(image,"stroke-dasharray:"); |
4603 | | for (j=0; j < k; j++) |
4604 | | { |
4605 | | MagickGetToken(q,&q,token,token_max_length); |
4606 | | FormatString(message,"%.1024s ",token); |
4607 | | (void) WriteBlobString(image,message); |
4608 | | } |
4609 | | (void) WriteBlobString(image,";"); |
4610 | | break; |
4611 | | } |
4612 | | MagickGetToken(q,&q,token,token_max_length); |
4613 | | FormatString(message,"stroke-dasharray:%.1024s;",token); |
4614 | | (void) WriteBlobString(image,message); |
4615 | | break; |
4616 | | } |
4617 | | if (LocaleCompare("stroke-dashoffset",keyword) == 0) |
4618 | | { |
4619 | | MagickGetToken(q,&q,token,token_max_length); |
4620 | | FormatString(message,"stroke-dashoffset:%.1024s;",token); |
4621 | | (void) WriteBlobString(image,message); |
4622 | | break; |
4623 | | } |
4624 | | if (LocaleCompare("stroke-linecap",keyword) == 0) |
4625 | | { |
4626 | | MagickGetToken(q,&q,token,token_max_length); |
4627 | | FormatString(message,"stroke-linecap:%.1024s;",token); |
4628 | | (void) WriteBlobString(image,message); |
4629 | | break; |
4630 | | } |
4631 | | if (LocaleCompare("stroke-linejoin",keyword) == 0) |
4632 | | { |
4633 | | MagickGetToken(q,&q,token,token_max_length); |
4634 | | FormatString(message,"stroke-linejoin:%.1024s;",token); |
4635 | | (void) WriteBlobString(image,message); |
4636 | | break; |
4637 | | } |
4638 | | if (LocaleCompare("stroke-miterlimit",keyword) == 0) |
4639 | | { |
4640 | | MagickGetToken(q,&q,token,token_max_length); |
4641 | | FormatString(message,"stroke-miterlimit:%.1024s;",token); |
4642 | | (void) WriteBlobString(image,message); |
4643 | | break; |
4644 | | } |
4645 | | if (LocaleCompare("stroke-opacity",keyword) == 0) |
4646 | | { |
4647 | | MagickGetToken(q,&q,token,token_max_length); |
4648 | | FormatString(message,"stroke-opacity:%.1024s;",token); |
4649 | | (void) WriteBlobString(image,message); |
4650 | | break; |
4651 | | } |
4652 | | if (LocaleCompare("stroke-width",keyword) == 0) |
4653 | | { |
4654 | | MagickGetToken(q,&q,token,token_max_length); |
4655 | | FormatString(message,"stroke-width:%.1024s;",token); |
4656 | | (void) WriteBlobString(image,message); |
4657 | | continue; |
4658 | | } |
4659 | | status=False; |
4660 | | break; |
4661 | | } |
4662 | | case 't': |
4663 | | case 'T': |
4664 | | { |
4665 | | if (LocaleCompare("text",keyword) == 0) |
4666 | | { |
4667 | | primitive_type=TextPrimitive; |
4668 | | break; |
4669 | | } |
4670 | | if (LocaleCompare("text-antialias",keyword) == 0) |
4671 | | { |
4672 | | MagickGetToken(q,&q,token,token_max_length); |
4673 | | FormatString(message,"text-antialias:%.1024s;",token); |
4674 | | (void) WriteBlobString(image,message); |
4675 | | break; |
4676 | | } |
4677 | | if (LocaleCompare("tspan",keyword) == 0) |
4678 | | { |
4679 | | primitive_type=TextPrimitive; |
4680 | | break; |
4681 | | } |
4682 | | if (LocaleCompare("translate",keyword) == 0) |
4683 | | { |
4684 | | MagickGetToken(q,&q,token,token_max_length); |
4685 | | affine.tx=MagickAtoF(token); |
4686 | | MagickGetToken(q,&q,token,token_max_length); |
4687 | | if (*token == ',') |
4688 | | MagickGetToken(q,&q,token,token_max_length); |
4689 | | affine.ty=MagickAtoF(token); |
4690 | | break; |
4691 | | } |
4692 | | status=False; |
4693 | | break; |
4694 | | } |
4695 | | case 'v': |
4696 | | case 'V': |
4697 | | { |
4698 | | if (LocaleCompare("viewbox",keyword) == 0) |
4699 | | { |
4700 | | MagickGetToken(q,&q,token,token_max_length); |
4701 | | if (*token == ',') |
4702 | | MagickGetToken(q,&q,token,token_max_length); |
4703 | | MagickGetToken(q,&q,token,token_max_length); |
4704 | | if (*token == ',') |
4705 | | MagickGetToken(q,&q,token,token_max_length); |
4706 | | MagickGetToken(q,&q,token,token_max_length); |
4707 | | if (*token == ',') |
4708 | | MagickGetToken(q,&q,token,token_max_length); |
4709 | | MagickGetToken(q,&q,token,token_max_length); |
4710 | | break; |
4711 | | } |
4712 | | status=False; |
4713 | | break; |
4714 | | } |
4715 | | default: |
4716 | | { |
4717 | | status=False; |
4718 | | break; |
4719 | | } |
4720 | | } |
4721 | | if (status == False) |
4722 | | break; |
4723 | | if (primitive_type == UndefinedPrimitive) |
4724 | | continue; |
4725 | | /* |
4726 | | Parse the primitive attributes. |
4727 | | */ |
4728 | | i=0; |
4729 | | j=0; |
4730 | | for (x=0; *q != '\0'; x++) |
4731 | | { |
4732 | | /* |
4733 | | Define points. |
4734 | | */ |
4735 | | if (!IsPoint(q)) |
4736 | | break; |
4737 | | MagickGetToken(q,&q,token,token_max_length); |
4738 | | point.x=MagickAtoF(token); |
4739 | | MagickGetToken(q,&q,token,token_max_length); |
4740 | | if (*token == ',') |
4741 | | MagickGetToken(q,&q,token,token_max_length); |
4742 | | point.y=MagickAtoF(token); |
4743 | | MagickGetToken(q,(char **) NULL,token,token_max_length); |
4744 | | if (*token == ',') |
4745 | | MagickGetToken(q,&q,token,token_max_length); |
4746 | | primitive_info[i].primitive=primitive_type; |
4747 | | primitive_info[i].point=point; |
4748 | | primitive_info[i].coordinates=0; |
4749 | | primitive_info[i].method=FloodfillMethod; |
4750 | | i++; |
4751 | | if (i < (long) (number_points-6*BezierQuantum-360)) |
4752 | | continue; |
4753 | | number_points+=6*BezierQuantum+360; |
4754 | | MagickReallocMemory(PrimitiveInfo *,primitive_info, |
4755 | | number_points*sizeof(PrimitiveInfo)); |
4756 | | if (primitive_info == (PrimitiveInfo *) NULL) |
4757 | | { |
4758 | | ThrowException3(&image->exception,ResourceLimitError, |
4759 | | MemoryAllocationFailed,UnableToDrawOnImage); |
4760 | | break; |
4761 | | } |
4762 | | } |
4763 | | primitive_info[j].primitive=primitive_type; |
4764 | | primitive_info[j].coordinates=x; |
4765 | | primitive_info[j].method=FloodfillMethod; |
4766 | | primitive_info[j].text=(char *) NULL; |
4767 | | if (active) |
4768 | | { |
4769 | | AffineToTransform(image,&affine); |
4770 | | active=False; |
4771 | | } |
4772 | | active=False; |
4773 | | switch (primitive_type) |
4774 | | { |
4775 | | case PointPrimitive: |
4776 | | default: |
4777 | | { |
4778 | | if (primitive_info[j].coordinates != 1) |
4779 | | { |
4780 | | status=False; |
4781 | | break; |
4782 | | } |
4783 | | break; |
4784 | | } |
4785 | | case LinePrimitive: |
4786 | | { |
4787 | | if (primitive_info[j].coordinates != 2) |
4788 | | { |
4789 | | status=False; |
4790 | | break; |
4791 | | } |
4792 | | (void) FormatString(message, |
4793 | | " <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n", |
4794 | | primitive_info[j].point.x,primitive_info[j].point.y, |
4795 | | primitive_info[j+1].point.x,primitive_info[j+1].point.y); |
4796 | | (void) WriteBlobString(image,message); |
4797 | | break; |
4798 | | } |
4799 | | case RectanglePrimitive: |
4800 | | { |
4801 | | if (primitive_info[j].coordinates != 2) |
4802 | | { |
4803 | | status=False; |
4804 | | break; |
4805 | | } |
4806 | | (void) FormatString(message, |
4807 | | " <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n", |
4808 | | primitive_info[j].point.x,primitive_info[j].point.y, |
4809 | | primitive_info[j+1].point.x-primitive_info[j].point.x, |
4810 | | primitive_info[j+1].point.y-primitive_info[j].point.y); |
4811 | | (void) WriteBlobString(image,message); |
4812 | | break; |
4813 | | } |
4814 | | case RoundRectanglePrimitive: |
4815 | | { |
4816 | | if (primitive_info[j].coordinates != 3) |
4817 | | { |
4818 | | status=False; |
4819 | | break; |
4820 | | } |
4821 | | (void) FormatString(message," <rect x=\"%g\" y=\"%g\" " |
4822 | | "width=\"%g\" height=\"%g\" rx=\"%g\" ry=\"%g\"/>\n", |
4823 | | primitive_info[j].point.x,primitive_info[j].point.y, |
4824 | | primitive_info[j+1].point.x-primitive_info[j].point.x, |
4825 | | primitive_info[j+1].point.y-primitive_info[j].point.y, |
4826 | | primitive_info[j+2].point.x,primitive_info[j+2].point.y); |
4827 | | (void) WriteBlobString(image,message); |
4828 | | break; |
4829 | | } |
4830 | | case ArcPrimitive: |
4831 | | { |
4832 | | if (primitive_info[j].coordinates != 3) |
4833 | | { |
4834 | | status=False; |
4835 | | break; |
4836 | | } |
4837 | | break; |
4838 | | } |
4839 | | case EllipsePrimitive: |
4840 | | { |
4841 | | if (primitive_info[j].coordinates != 3) |
4842 | | { |
4843 | | status=False; |
4844 | | break; |
4845 | | } |
4846 | | (void) FormatString(message, |
4847 | | " <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n", |
4848 | | primitive_info[j].point.x,primitive_info[j].point.y, |
4849 | | primitive_info[j+1].point.x,primitive_info[j+1].point.y); |
4850 | | (void) WriteBlobString(image,message); |
4851 | | break; |
4852 | | } |
4853 | | case CirclePrimitive: |
4854 | | { |
4855 | | double |
4856 | | alpha, |
4857 | | beta; |
4858 | | |
4859 | | if (primitive_info[j].coordinates != 2) |
4860 | | { |
4861 | | status=False; |
4862 | | break; |
4863 | | } |
4864 | | alpha=primitive_info[j+1].point.x-primitive_info[j].point.x; |
4865 | | beta=primitive_info[j+1].point.y-primitive_info[j].point.y; |
4866 | | (void) FormatString(message," <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n", |
4867 | | primitive_info[j].point.x,primitive_info[j].point.y, |
4868 | | sqrt(alpha*alpha+beta*beta)); |
4869 | | (void) WriteBlobString(image,message); |
4870 | | break; |
4871 | | } |
4872 | | case PolylinePrimitive: |
4873 | | { |
4874 | | if (primitive_info[j].coordinates < 2) |
4875 | | { |
4876 | | status=False; |
4877 | | break; |
4878 | | } |
4879 | | (void) strlcpy(message," <polyline points=\"",sizeof(message)); |
4880 | | (void) WriteBlobString(image,message); |
4881 | | length=strlen(message); |
4882 | | for ( ; j < i; j++) |
4883 | | { |
4884 | | FormatString(message,"%g,%g ",primitive_info[j].point.x, |
4885 | | primitive_info[j].point.y); |
4886 | | length+=strlen(message); |
4887 | | if (length >= 80) |
4888 | | { |
4889 | | (void) WriteBlobString(image,"\n "); |
4890 | | length=strlen(message)+5; |
4891 | | } |
4892 | | (void) WriteBlobString(image,message); |
4893 | | } |
4894 | | (void) WriteBlobString(image,"\"/>\n"); |
4895 | | break; |
4896 | | } |
4897 | | case PolygonPrimitive: |
4898 | | { |
4899 | | if (primitive_info[j].coordinates < 3) |
4900 | | { |
4901 | | status=False; |
4902 | | break; |
4903 | | } |
4904 | | primitive_info[i]=primitive_info[j]; |
4905 | | primitive_info[i].coordinates=0; |
4906 | | primitive_info[j].coordinates++; |
4907 | | i++; |
4908 | | (void) strlcpy(message," <polygon points=\"",sizeof(message)); |
4909 | | (void) WriteBlobString(image,message); |
4910 | | length=strlen(message); |
4911 | | for ( ; j < i; j++) |
4912 | | { |
4913 | | FormatString(message,"%g,%g ",primitive_info[j].point.x, |
4914 | | primitive_info[j].point.y); |
4915 | | length+=strlen(message); |
4916 | | if (length >= 80) |
4917 | | { |
4918 | | (void) WriteBlobString(image,"\n "); |
4919 | | length=strlen(message)+5; |
4920 | | } |
4921 | | (void) WriteBlobString(image,message); |
4922 | | } |
4923 | | (void) WriteBlobString(image,"\"/>\n"); |
4924 | | break; |
4925 | | } |
4926 | | case BezierPrimitive: |
4927 | | { |
4928 | | if (primitive_info[j].coordinates < 3) |
4929 | | { |
4930 | | status=False; |
4931 | | break; |
4932 | | } |
4933 | | break; |
4934 | | } |
4935 | | case PathPrimitive: |
4936 | | { |
4937 | | int |
4938 | | number_attributes; |
4939 | | |
4940 | | MagickGetToken(q,&q,token,token_max_length); |
4941 | | number_attributes=1; |
4942 | | for (p=token; *p != '\0'; p++) |
4943 | | if (isalpha((int) *p)) |
4944 | | number_attributes++; |
4945 | | if (i > (long) (number_points-6*BezierQuantum*number_attributes-1)) |
4946 | | { |
4947 | | number_points+=6*BezierQuantum*number_attributes; |
4948 | | MagickReallocMemory(PrimitiveInfo *,primitive_info, |
4949 | | number_points*sizeof(PrimitiveInfo)); |
4950 | | if (primitive_info == (PrimitiveInfo *) NULL) |
4951 | | { |
4952 | | ThrowException3(&image->exception,ResourceLimitError, |
4953 | | MemoryAllocationFailed,UnableToDrawOnImage); |
4954 | | break; |
4955 | | } |
4956 | | } |
4957 | | (void) WriteBlobString(image," <path d=\""); |
4958 | | (void) WriteBlobString(image,token); |
4959 | | (void) WriteBlobString(image,"\"/>\n"); |
4960 | | break; |
4961 | | } |
4962 | | case ColorPrimitive: |
4963 | | case MattePrimitive: |
4964 | | { |
4965 | | if (primitive_info[j].coordinates != 1) |
4966 | | { |
4967 | | status=False; |
4968 | | break; |
4969 | | } |
4970 | | MagickGetToken(q,&q,token,token_max_length); |
4971 | | if (LocaleCompare("point",token) == 0) |
4972 | | primitive_info[j].method=PointMethod; |
4973 | | if (LocaleCompare("replace",token) == 0) |
4974 | | primitive_info[j].method=ReplaceMethod; |
4975 | | if (LocaleCompare("floodfill",token) == 0) |
4976 | | primitive_info[j].method=FloodfillMethod; |
4977 | | if (LocaleCompare("filltoborder",token) == 0) |
4978 | | primitive_info[j].method=FillToBorderMethod; |
4979 | | if (LocaleCompare("reset",token) == 0) |
4980 | | primitive_info[j].method=ResetMethod; |
4981 | | break; |
4982 | | } |
4983 | | case TextPrimitive: |
4984 | | { |
4985 | | register char |
4986 | | *p; |
4987 | | |
4988 | | if (primitive_info[j].coordinates != 1) |
4989 | | { |
4990 | | status=False; |
4991 | | break; |
4992 | | } |
4993 | | MagickGetToken(q,&q,token,token_max_length); |
4994 | | (void) FormatString(message," <text x=\"%g\" y=\"%g\">", |
4995 | | primitive_info[j].point.x,primitive_info[j].point.y); |
4996 | | (void) WriteBlobString(image,message); |
4997 | | for (p=token; *p != '\0'; p++) |
4998 | | switch (*p) |
4999 | | { |
5000 | | case '<': (void) WriteBlobString(image,"<"); break; |
5001 | | case '>': (void) WriteBlobString(image,">"); break; |
5002 | | case '&': (void) WriteBlobString(image,"&"); break; |
5003 | | default: (void) WriteBlobByte(image,*p); break; |
5004 | | } |
5005 | | (void) WriteBlobString(image,"</text>\n"); |
5006 | | break; |
5007 | | } |
5008 | | case ImagePrimitive: |
5009 | | { |
5010 | | if (primitive_info[j].coordinates != 2) |
5011 | | { |
5012 | | status=False; |
5013 | | break; |
5014 | | } |
5015 | | MagickGetToken(q,&q,token,token_max_length); |
5016 | | (void) FormatString(message," <image x=\"%g\" y=\"%g\" " |
5017 | | "width=\"%g\" height=\"%g\" xlink:href=\"%.1024s\"/>\n", |
5018 | | primitive_info[j].point.x,primitive_info[j].point.y, |
5019 | | primitive_info[j+1].point.x,primitive_info[j+1].point.y,token); |
5020 | | (void) WriteBlobString(image,message); |
5021 | | break; |
5022 | | } |
5023 | | } |
5024 | | if (primitive_info == (PrimitiveInfo *) NULL) |
5025 | | break; |
5026 | | primitive_info[i].primitive=UndefinedPrimitive; |
5027 | | if (status == False) |
5028 | | break; |
5029 | | } |
5030 | | (void) WriteBlobString(image,"</svg>\n"); |
5031 | | /* |
5032 | | Free resources. |
5033 | | */ |
5034 | | MagickFreeMemory(token); |
5035 | | if (primitive_info != (PrimitiveInfo *) NULL) |
5036 | | MagickFreeMemory(primitive_info); |
5037 | | status &= CloseBlob(image); |
5038 | | return(status); |
5039 | | } |
5040 | | #endif |
5041 | | #endif /* if defined(ENABLE_SVG_WRITER) && ENABLE_SVG_WRITER */ |