Coverage Report

Created: 2025-07-23 08:18

/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,"&lt;"); break;
4094
              case '>': (void) WriteBlobString(image,"&gt;"); break;
4095
              case '&': (void) WriteBlobString(image,"&amp;"); 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,"&lt;"); break;
5001
                case '>': (void) WriteBlobString(image,"&gt;"); break;
5002
                case '&': (void) WriteBlobString(image,"&amp;"); 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 */