Coverage Report

Created: 2025-06-16 07:00

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