Coverage Report

Created: 2026-05-16 07:22

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