~ubuntu-branches/ubuntu/intrepid/graphicsmagick/intrepid

« back to all changes in this revision

Viewing changes to coders/svg.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Kobras
  • Date: 2006-05-06 16:28:08 UTC
  • Revision ID: james.westby@ubuntu.com-20060506162808-vt2ni3r5nytcszms
Tags: upstream-1.1.7
ImportĀ upstreamĀ versionĀ 1.1.7

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
% Copyright (C) 2003 GraphicsMagick Group
 
3
% Copyright (C) 2002 ImageMagick Studio
 
4
%
 
5
% This program is covered by multiple licenses, which are described in
 
6
% Copyright.txt. You should have received a copy of Copyright.txt with this
 
7
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
 
8
%
 
9
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
10
%                                                                             %
 
11
%                                                                             %
 
12
%                                                                             %
 
13
%                            SSSSS  V   V   GGGG                              %
 
14
%                            SS     V   V  G                                  %
 
15
%                             SSS   V   V  G GG                               %
 
16
%                               SS   V V   G   G                              %
 
17
%                            SSSSS    V     GGG                               %
 
18
%                                                                             %
 
19
%                                                                             %
 
20
%                 Read/Write Scalable Vector Graphics Format.                 %
 
21
%                                                                             %
 
22
%                                                                             %
 
23
%                              Software Design                                %
 
24
%                                John Cristy                                  %
 
25
%                             William Radcliffe                               %
 
26
%                                March 2000                                   %
 
27
%                                                                             %
 
28
%                                                                             %
 
29
%                                                                             %
 
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
31
%
 
32
%
 
33
*/
 
34
 
 
35
/*
 
36
  Include declarations.
 
37
*/
 
38
#include "magick/studio.h"
 
39
#include "magick/attribute.h"
 
40
#include "magick/blob.h"
 
41
#include "magick/color.h"
 
42
#include "magick/constitute.h"
 
43
#include "magick/gem.h"
 
44
#include "magick/log.h"
 
45
#include "magick/magick.h"
 
46
#include "magick/render.h"
 
47
#include "magick/tempfile.h"
 
48
#include "magick/utility.h"
 
49
#if defined(HasXML)
 
50
#  if defined(WIN32)
 
51
#    if defined(__MINGW32__)
 
52
#      define _MSC_VER
 
53
#    endif
 
54
#    include <win32config.h>
 
55
#  endif
 
56
#  include <libxml/parser.h>
 
57
#  include <libxml/xmlmemory.h>
 
58
#  include <libxml/parserInternals.h>
 
59
#  include <libxml/xmlerror.h>
 
60
#endif
 
61
 
 
62
#if defined(HasAUTOTRACE)
 
63
#include "types.h"
 
64
#include "image-header.h"
 
65
#include "fit.h"
 
66
#include "output.h"
 
67
#include "pxl-outline.h"
 
68
#include "atquantize.h"
 
69
#include "thin-image.h"
 
70
 
 
71
char
 
72
  *version_string = "AutoTrace version 0.24a";
 
73
#endif
 
74
 
 
75
/*
 
76
  Define declarations.
 
77
*/
 
78
#define MVGPrintf  (void) fprintf
 
79
 
 
80
/*
 
81
  Typedef declarations.
 
82
*/
 
83
typedef struct _BoundingBox
 
84
{
 
85
  double
 
86
    x,
 
87
    y,
 
88
    width,
 
89
    height;
 
90
} BoundingBox;
 
91
 
 
92
typedef struct _SVGInfo
 
93
{
 
94
  FILE
 
95
    *file;
 
96
 
 
97
  ExceptionInfo
 
98
    *exception;
 
99
 
 
100
  Image
 
101
    *image;
 
102
 
 
103
  const ImageInfo
 
104
    *image_info;
 
105
 
 
106
  AffineMatrix
 
107
    affine;
 
108
 
 
109
  unsigned long
 
110
    width,
 
111
    height;
 
112
 
 
113
  char
 
114
    *size,
 
115
    *title,
 
116
    *comment;
 
117
 
 
118
  int
 
119
    n;
 
120
 
 
121
  double
 
122
    *scale,
 
123
    pointsize;
 
124
 
 
125
  ElementInfo
 
126
    element;
 
127
 
 
128
  SegmentInfo
 
129
    segment;
 
130
 
 
131
  BoundingBox
 
132
    bounds,
 
133
    view_box;
 
134
 
 
135
  PointInfo
 
136
    radius;
 
137
 
 
138
  char
 
139
    *stop_color,
 
140
    *offset,
 
141
    *text,
 
142
    *vertices,
 
143
    *url;
 
144
 
 
145
#if defined(HasXML)
 
146
  xmlParserCtxtPtr
 
147
    parser;
 
148
 
 
149
  xmlDocPtr
 
150
    document;
 
151
#endif
 
152
} SVGInfo;
 
153
 
 
154
/*
 
155
  Forward declarations.
 
156
*/
 
157
static unsigned int
 
158
  WriteSVGImage(const ImageInfo *,Image *);
 
159
 
 
160
#if defined(HasXML)
 
161
/*
 
162
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
163
%                                                                             %
 
164
%                                                                             %
 
165
%                                                                             %
 
166
%   R e a d S V G I m a g e                                                   %
 
167
%                                                                             %
 
168
%                                                                             %
 
169
%                                                                             %
 
170
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
171
%
 
172
%  Method ReadSVGImage reads a Scalable Vector Gaphics file and returns it.  It
 
173
%  allocates the memory necessary for the new Image structure and returns a
 
174
%  pointer to the new image.
 
175
%
 
176
%  The format of the ReadSVGImage method is:
 
177
%
 
178
%      Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
 
179
%
 
180
%  A description of each parameter follows:
 
181
%
 
182
%    o image:  Method ReadSVGImage returns a pointer to the image after
 
183
%      reading. A null image is returned if there is a memory shortage or if
 
184
%      the image cannot be read.
 
185
%
 
186
%    o image_info: Specifies a pointer to a ImageInfo structure.
 
187
%
 
188
%    o exception: return any errors or warnings in this structure.
 
189
%
 
190
%
 
191
*/
 
192
 
 
193
static double GetUserSpaceCoordinateValue(const SVGInfo *svg_info,int type,
 
194
  const char *string)
 
195
{
 
196
  char
 
197
    *p,
 
198
    token[MaxTextExtent];
 
199
 
 
200
  double
 
201
    value;
 
202
 
 
203
  assert(string != (const char *) NULL);
 
204
  p=(char *) string;
 
205
  GetToken(p,&p,token);
 
206
  value=atof(token);
 
207
  if (strchr(token,'%') != (char *) NULL)
 
208
    {
 
209
      double
 
210
        alpha,
 
211
        beta;
 
212
 
 
213
      if (type > 0)
 
214
        return(svg_info->view_box.width*value/100.0);
 
215
      if (type < 0)
 
216
        return(svg_info->view_box.height*value/100.0);
 
217
      alpha=value-svg_info->view_box.width;
 
218
      beta=value-svg_info->view_box.height;
 
219
      return(sqrt(alpha*alpha+beta*beta)/sqrt(2.0)/100.0);
 
220
    }
 
221
  GetToken(p,&p,token);
 
222
  if (LocaleNCompare(token,"cm",2) == 0)
 
223
    return(72.0*svg_info->scale[0]/2.54*value);
 
224
  if (LocaleNCompare(token,"em",2) == 0)
 
225
    return(svg_info->pointsize*value);
 
226
  if (LocaleNCompare(token,"ex",2) == 0)
 
227
    return(svg_info->pointsize*value/2.0);
 
228
  if (LocaleNCompare(token,"in",2) == 0)
 
229
    return(72.0*svg_info->scale[0]*value);
 
230
  if (LocaleNCompare(token,"mm",2) == 0)
 
231
    return(72.0*svg_info->scale[0]/25.4*value);
 
232
  if (LocaleNCompare(token,"pc",2) == 0)
 
233
    return(72.0*svg_info->scale[0]/6.0*value);
 
234
  if (LocaleNCompare(token,"pt",2) == 0)
 
235
    return(svg_info->scale[0]*value);
 
236
  if (LocaleNCompare(token,"px",2) == 0)
 
237
    return(value);
 
238
  return(value);
 
239
}
 
240
 
 
241
static char **GetStyleTokens(void *context,const char *text,int *number_tokens)
 
242
{
 
243
  char
 
244
    **tokens;
 
245
 
 
246
  register const char
 
247
    *p,
 
248
    *q;
 
249
 
 
250
  register long
 
251
    i;
 
252
 
 
253
  SVGInfo
 
254
    *svg_info;
 
255
 
 
256
  svg_info=(SVGInfo *) context;
 
257
  *number_tokens=0;
 
258
  if (text == (const char *) NULL)
 
259
    return((char **) NULL);
 
260
  /*
 
261
    Determine the number of arguments.
 
262
  */
 
263
  for (p=text; *p != '\0'; p++)
 
264
    if (*p == ':')
 
265
      (*number_tokens)+=2;
 
266
  tokens=MagickAllocateMemory(char **,(*number_tokens+2)*sizeof(*tokens));
 
267
  if (tokens == (char **) NULL)
 
268
    {
 
269
      ThrowException3(svg_info->exception,ResourceLimitError,
 
270
        MemoryAllocationFailed,UnableToConvertStringToTokens);
 
271
      return((char **) NULL);
 
272
    }
 
273
  /*
 
274
    Convert string to an ASCII list.
 
275
  */
 
276
  i=0;
 
277
  p=text;
 
278
  for (q=p; *q != '\0'; q++)
 
279
  {
 
280
    if ((*q != ':') && (*q != ';') && (*q != '\0'))
 
281
      continue;
 
282
    tokens[i]=AllocateString(p);
 
283
    (void) strncpy(tokens[i],p,q-p);
 
284
    tokens[i][q-p]='\0';
 
285
    Strip(tokens[i++]);
 
286
    p=q+1;
 
287
  }
 
288
  tokens[i]=AllocateString(p);
 
289
  (void) strncpy(tokens[i],p,q-p);
 
290
  tokens[i][q-p]='\0';
 
291
  Strip(tokens[i++]);
 
292
  tokens[i]=(char *) NULL;
 
293
  return(tokens);
 
294
}
 
295
 
 
296
static char **GetTransformTokens(void *context,const char *text,
 
297
  int *number_tokens)
 
298
{
 
299
  char
 
300
    **tokens;
 
301
 
 
302
  register const char
 
303
    *p,
 
304
    *q;
 
305
 
 
306
  register long
 
307
    i;
 
308
 
 
309
  SVGInfo
 
310
    *svg_info;
 
311
 
 
312
  svg_info=(SVGInfo *) context;
 
313
  *number_tokens=0;
 
314
  if (text == (const char *) NULL)
 
315
    return((char **) NULL);
 
316
  /*
 
317
    Determine the number of arguments.
 
318
  */
 
319
  for (p=text; *p != '\0'; p++)
 
320
  {
 
321
    if (*p == '(')
 
322
      (*number_tokens)+=2;
 
323
  }
 
324
  tokens=MagickAllocateMemory(char **,(*number_tokens+2)*sizeof(*tokens));
 
325
  if (tokens == (char **) NULL)
 
326
    {
 
327
      ThrowException3(svg_info->exception,ResourceLimitError,
 
328
        MemoryAllocationFailed,UnableToConvertStringToTokens);
 
329
      return((char **) NULL);
 
330
    }
 
331
  /*
 
332
    Convert string to an ASCII list.
 
333
  */
 
334
  i=0;
 
335
  p=text;
 
336
  for (q=p; *q != '\0'; q++)
 
337
  {
 
338
    if ((*q != '(') && (*q != ')') && (*q != '\0'))
 
339
      continue;
 
340
    tokens[i]=AllocateString(p);
 
341
    (void) strncpy(tokens[i],p,q-p);
 
342
    tokens[i][q-p]='\0';
 
343
    Strip(tokens[i++]);
 
344
    p=q+1;
 
345
  }
 
346
  tokens[i]=AllocateString(p);
 
347
  (void) strncpy(tokens[i],p,q-p);
 
348
  tokens[i][q-p]='\0';
 
349
  Strip(tokens[i++]);
 
350
  tokens[i]=(char *) NULL;
 
351
  return(tokens);
 
352
}
 
353
 
 
354
#if defined(__cplusplus) || defined(c_plusplus)
 
355
extern "C" {
 
356
#endif
 
357
 
 
358
static int SVGIsStandalone(void *context)
 
359
{
 
360
  SVGInfo
 
361
    *svg_info;
 
362
 
 
363
  /*
 
364
    Is this document tagged standalone?
 
365
  */
 
366
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.SVGIsStandalone()");
 
367
  svg_info=(SVGInfo *) context;
 
368
  return(svg_info->document->standalone == 1);
 
369
}
 
370
 
 
371
static int SVGHasInternalSubset(void *context)
 
372
{
 
373
  SVGInfo
 
374
    *svg_info;
 
375
 
 
376
  /*
 
377
    Does this document has an internal subset?
 
378
  */
 
379
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
380
    "  SAX.SVGHasInternalSubset()");
 
381
  svg_info=(SVGInfo *) context;
 
382
  return(svg_info->document->intSubset != NULL);
 
383
}
 
384
 
 
385
static int SVGHasExternalSubset(void *context)
 
386
{
 
387
  SVGInfo
 
388
    *svg_info;
 
389
 
 
390
  /*
 
391
    Does this document has an external subset?
 
392
  */
 
393
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
394
    "  SAX.SVGHasExternalSubset()");
 
395
  svg_info=(SVGInfo *) context;
 
396
  return(svg_info->document->extSubset != NULL);
 
397
}
 
398
 
 
399
static void SVGInternalSubset(void *context,const xmlChar *name,
 
400
  const xmlChar *external_id,const xmlChar *system_id)
 
401
{
 
402
  SVGInfo
 
403
    *svg_info;
 
404
 
 
405
  /*
 
406
    Does this document has an internal subset?
 
407
  */
 
408
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
409
    "  SAX.internalSubset(%.1024s, %.1024s, %.1024s)",(char *) name,
 
410
    (external_id != (const xmlChar *) NULL ? (char *) external_id : "none"),
 
411
    (system_id != (const xmlChar *) NULL ? (char *) system_id : "none"));
 
412
  svg_info=(SVGInfo *) context;
 
413
  (void) xmlCreateIntSubset(svg_info->document,name,external_id,system_id);
 
414
}
 
415
 
 
416
static xmlParserInputPtr SVGResolveEntity(void *context,
 
417
  const xmlChar *public_id,const xmlChar *system_id)
 
418
{
 
419
  SVGInfo
 
420
    *svg_info;
 
421
 
 
422
  xmlParserInputPtr
 
423
    stream;
 
424
 
 
425
  /*
 
426
    Special entity resolver, better left to the parser, it has more
 
427
    context than the application layer.  The default behaviour is to
 
428
    not resolve the entities, in that case the ENTITY_REF nodes are
 
429
    built in the structure (and the parameter values).
 
430
  */
 
431
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
432
    "  SAX.resolveEntity(%.1024s, %.1024s)",
 
433
    (public_id != (const xmlChar *) NULL ? (char *) public_id : "none"),
 
434
    (system_id != (const xmlChar *) NULL ? (char *) system_id : "none"));
 
435
  svg_info=(SVGInfo *) context;
 
436
  stream=xmlLoadExternalEntity((const char *) system_id,(const char *)
 
437
    public_id,svg_info->parser);
 
438
  return(stream);
 
439
}
 
440
 
 
441
static xmlEntityPtr SVGGetEntity(void *context,const xmlChar *name)
 
442
{
 
443
  SVGInfo
 
444
    *svg_info;
 
445
 
 
446
  /*
 
447
    Get an entity by name.
 
448
  */
 
449
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
450
    "  SAX.SVGGetEntity(%.1024s)",name);
 
451
  svg_info=(SVGInfo *) context;
 
452
  return(xmlGetDocEntity(svg_info->document,name));
 
453
}
 
454
 
 
455
static xmlEntityPtr SVGGetParameterEntity(void *context,const xmlChar *name)
 
456
{
 
457
  SVGInfo
 
458
    *svg_info;
 
459
 
 
460
  /*
 
461
    Get a parameter entity by name.
 
462
  */
 
463
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
464
    "  SAX.getParameterEntity(%.1024s)",name);
 
465
  svg_info=(SVGInfo *) context;
 
466
  return(xmlGetParameterEntity(svg_info->document,name));
 
467
}
 
468
 
 
469
static void SVGEntityDeclaration(void *context,const xmlChar *name,int type,
 
470
  const xmlChar *public_id,const xmlChar *system_id,xmlChar *content)
 
471
{
 
472
  SVGInfo
 
473
    *svg_info;
 
474
 
 
475
  /*
 
476
    An entity definition has been parsed.
 
477
  */
 
478
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
479
    "  SAX.entityDecl(%.1024s, %d, %.1024s, %.1024s, %.1024s)",name,type,
 
480
    public_id != (xmlChar *) NULL ? (char *) public_id : "none",
 
481
    system_id != (xmlChar *) NULL ? (char *) system_id : "none",content);
 
482
  svg_info=(SVGInfo *) context;
 
483
  if (svg_info->parser->inSubset == 1)
 
484
    (void) xmlAddDocEntity(svg_info->document,name,type,public_id,system_id,
 
485
      content);
 
486
  else
 
487
    if (svg_info->parser->inSubset == 2)
 
488
      (void) xmlAddDtdEntity(svg_info->document,name,type,public_id,system_id,
 
489
        content);
 
490
}
 
491
 
 
492
static void SVGAttributeDeclaration(void *context,const xmlChar *element,
 
493
  const xmlChar *name,int type,int value,const xmlChar *default_value,
 
494
  xmlEnumerationPtr tree)
 
495
{
 
496
  SVGInfo
 
497
    *svg_info;
 
498
 
 
499
  xmlChar
 
500
    *fullname,
 
501
    *prefix;
 
502
 
 
503
  xmlParserCtxtPtr
 
504
    parser;
 
505
 
 
506
  /*
 
507
    An attribute definition has been parsed.
 
508
  */
 
509
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
510
    "  SAX.attributeDecl(%.1024s, %.1024s, %d, %d, %.1024s, ...)",element,
 
511
    name,type,value,default_value);
 
512
  svg_info=(SVGInfo *) context;
 
513
  fullname=(xmlChar *) NULL;
 
514
  prefix=(xmlChar *) NULL;
 
515
  parser=svg_info->parser;
 
516
  fullname=(xmlChar *) xmlSplitQName(parser,name,&prefix);
 
517
  if (parser->inSubset == 1)
 
518
    (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->intSubset,
 
519
      element,fullname,prefix,(xmlAttributeType) type,
 
520
      (xmlAttributeDefault) value,default_value,tree);
 
521
  else
 
522
    if (parser->inSubset == 2)
 
523
      (void) xmlAddAttributeDecl(&parser->vctxt,svg_info->document->extSubset,
 
524
        element,fullname,prefix,(xmlAttributeType) type,
 
525
        (xmlAttributeDefault) value,default_value,tree);
 
526
  if (prefix != (xmlChar *) NULL)
 
527
    xmlFree(prefix);
 
528
  if (fullname != (xmlChar *) NULL)
 
529
    xmlFree(fullname);
 
530
}
 
531
 
 
532
static void SVGElementDeclaration(void *context,const xmlChar *name,int type,
 
533
  xmlElementContentPtr content)
 
534
{
 
535
  SVGInfo
 
536
    *svg_info;
 
537
 
 
538
  xmlParserCtxtPtr
 
539
    parser;
 
540
 
 
541
  /*
 
542
    An element definition has been parsed.
 
543
  */
 
544
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
545
    "  SAX.elementDecl(%.1024s, %d, ...)",name,type);
 
546
  svg_info=(SVGInfo *) context;
 
547
  parser=svg_info->parser;
 
548
  if (parser->inSubset == 1)
 
549
    (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->intSubset,
 
550
      name,(xmlElementTypeVal) type,content);
 
551
  else
 
552
    if (parser->inSubset == 2)
 
553
      (void) xmlAddElementDecl(&parser->vctxt,svg_info->document->extSubset,
 
554
        name,(xmlElementTypeVal) type,content);
 
555
}
 
556
 
 
557
static void SVGNotationDeclaration(void *context,const xmlChar *name,
 
558
  const xmlChar *public_id,const xmlChar *system_id)
 
559
{
 
560
  SVGInfo
 
561
    *svg_info;
 
562
 
 
563
  xmlParserCtxtPtr
 
564
    parser;
 
565
 
 
566
  /*
 
567
    What to do when a notation declaration has been parsed.
 
568
  */
 
569
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
570
    "  SAX.notationDecl(%.1024s, %.1024s, %.1024s)",name,
 
571
    public_id != (const xmlChar *) NULL ? (char *) public_id : "none",
 
572
    system_id != (const xmlChar *) NULL ? (char *) system_id : "none");
 
573
  svg_info=(SVGInfo *) context;
 
574
  parser=svg_info->parser;
 
575
  if (parser->inSubset == 1)
 
576
    (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
 
577
      name,public_id,system_id);
 
578
  else
 
579
    if (parser->inSubset == 2)
 
580
      (void) xmlAddNotationDecl(&parser->vctxt,svg_info->document->intSubset,
 
581
        name,public_id,system_id);
 
582
}
 
583
 
 
584
static void SVGUnparsedEntityDeclaration(void *context,const xmlChar *name,
 
585
  const xmlChar *public_id,const xmlChar *system_id,const xmlChar *notation)
 
586
{
 
587
  SVGInfo
 
588
    *svg_info;
 
589
 
 
590
  /*
 
591
    What to do when an unparsed entity declaration is parsed.
 
592
  */
 
593
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
594
    "  SAX.unparsedEntityDecl(%.1024s, %.1024s, %.1024s, %.1024s)",name,
 
595
    public_id != (xmlChar *) NULL ? (char *) public_id : "none",
 
596
    system_id != (xmlChar *) NULL ? (char *) system_id : "none",notation);
 
597
  svg_info=(SVGInfo *) context;
 
598
  (void) xmlAddDocEntity(svg_info->document,name,
 
599
    XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,public_id,system_id,notation);
 
600
 
 
601
}
 
602
 
 
603
static void SVGSetDocumentLocator(void *context,xmlSAXLocatorPtr location)
 
604
{
 
605
/*   SVGInfo */
 
606
/*     *svg_info; */
 
607
 
 
608
  /*
 
609
    Receive the document locator at startup, actually xmlDefaultSAXLocator.
 
610
  */
 
611
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.setDocumentLocator()");
 
612
/*   svg_info=(SVGInfo *) context; */
 
613
}
 
614
 
 
615
static void SVGStartDocument(void *context)
 
616
{
 
617
  SVGInfo
 
618
    *svg_info;
 
619
 
 
620
  xmlParserCtxtPtr
 
621
    parser;
 
622
 
 
623
  /*
 
624
    Called when the document start being processed.
 
625
  */
 
626
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.startDocument()");
 
627
  svg_info=(SVGInfo *) context;
 
628
  GetExceptionInfo(svg_info->exception);
 
629
  parser=svg_info->parser;
 
630
  svg_info->document=xmlNewDoc(parser->version);
 
631
  if (svg_info->document == (xmlDocPtr) NULL)
 
632
    return;
 
633
  if (parser->encoding == NULL)
 
634
    svg_info->document->encoding=(const xmlChar *) NULL;
 
635
  else
 
636
    svg_info->document->encoding=xmlStrdup(parser->encoding);
 
637
  svg_info->document->standalone=parser->standalone;
 
638
}
 
639
 
 
640
static void SVGEndDocument(void *context)
 
641
{
 
642
  SVGInfo
 
643
    *svg_info;
 
644
 
 
645
  /*
 
646
    Called when the document end has been detected.
 
647
  */
 
648
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.endDocument()");
 
649
  svg_info=(SVGInfo *) context;
 
650
  MagickFreeMemory(svg_info->offset);
 
651
  MagickFreeMemory(svg_info->stop_color);
 
652
  MagickFreeMemory(svg_info->scale);
 
653
  MagickFreeMemory(svg_info->text);
 
654
  MagickFreeMemory(svg_info->vertices);
 
655
  MagickFreeMemory(svg_info->url);
 
656
  if (svg_info->document != (xmlDocPtr) NULL)
 
657
    {
 
658
      xmlFreeDoc(svg_info->document);
 
659
      svg_info->document=(xmlDocPtr) NULL;
 
660
    }
 
661
}
 
662
 
 
663
static void SVGStartElement(void *context,const xmlChar *name,
 
664
  const xmlChar **attributes)
 
665
{
 
666
  char
 
667
    *color,
 
668
    id[MaxTextExtent],
 
669
    *p,
 
670
    token[MaxTextExtent],
 
671
    **tokens,
 
672
    *units;
 
673
 
 
674
  const char
 
675
    *keyword,
 
676
    *value;
 
677
 
 
678
  int
 
679
    number_tokens;
 
680
 
 
681
  SVGInfo
 
682
    *svg_info;
 
683
 
 
684
  register long
 
685
    i,
 
686
    j;
 
687
 
 
688
  /*
 
689
    Called when an opening tag has been processed.
 
690
  */
 
691
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
692
    "  SAX.startElement(%.1024s",name);
 
693
  svg_info=(SVGInfo *) context;
 
694
  svg_info->n++;
 
695
  MagickReallocMemory(svg_info->scale,(svg_info->n+1)*sizeof(double));
 
696
  if (svg_info->scale == (double *) NULL)
 
697
    {
 
698
      ThrowException(svg_info->exception,ResourceLimitError,
 
699
        MemoryAllocationFailed,"unable to convert SVG image");
 
700
      return;
 
701
    }
 
702
  svg_info->scale[svg_info->n]=svg_info->scale[svg_info->n-1];
 
703
  color=AllocateString("none");
 
704
  units=AllocateString("userSpaceOnUse");
 
705
  value=(const char *) NULL;
 
706
  if (attributes != (const xmlChar **) NULL)
 
707
    for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
 
708
    {
 
709
      keyword=(const char *) attributes[i];
 
710
      value=(const char *) attributes[i+1];
 
711
      switch (*keyword)
 
712
      {
 
713
        case 'C':
 
714
        case 'c':
 
715
        {
 
716
          if (LocaleCompare(keyword,"cx") == 0)
 
717
            {
 
718
              svg_info->element.cx=
 
719
                GetUserSpaceCoordinateValue(svg_info,1,value);
 
720
              break;
 
721
            }
 
722
          if (LocaleCompare(keyword,"cy") == 0)
 
723
            {
 
724
              svg_info->element.cy=
 
725
                GetUserSpaceCoordinateValue(svg_info,-1,value);
 
726
              break;
 
727
            }
 
728
          break;
 
729
        }
 
730
        case 'F':
 
731
        case 'f':
 
732
        {
 
733
          if (LocaleCompare(keyword,"fx") == 0)
 
734
            {
 
735
              svg_info->element.major=
 
736
                GetUserSpaceCoordinateValue(svg_info,1,value);
 
737
              break;
 
738
            }
 
739
          if (LocaleCompare(keyword,"fy") == 0)
 
740
            {
 
741
              svg_info->element.minor=
 
742
                GetUserSpaceCoordinateValue(svg_info,-1,value);
 
743
              break;
 
744
            }
 
745
          break;
 
746
        }
 
747
        case 'H':
 
748
        case 'h':
 
749
        {
 
750
          if (LocaleCompare(keyword,"height") == 0)
 
751
            {
 
752
              svg_info->bounds.height=
 
753
                GetUserSpaceCoordinateValue(svg_info,-1,value);
 
754
              break;
 
755
            }
 
756
          break;
 
757
        }
 
758
        case 'I':
 
759
        case 'i':
 
760
        {
 
761
          if (LocaleCompare(keyword,"id") == 0)
 
762
            {
 
763
              (void) strncpy(id,value,MaxTextExtent-1);
 
764
              break;
 
765
            }
 
766
          break;
 
767
        }
 
768
        case 'R':
 
769
        case 'r':
 
770
        {
 
771
          if (LocaleCompare(keyword,"r") == 0)
 
772
            {
 
773
              svg_info->element.angle=
 
774
                GetUserSpaceCoordinateValue(svg_info,0,value);
 
775
              break;
 
776
            }
 
777
          break;
 
778
        }
 
779
        case 'W':
 
780
        case 'w':
 
781
        {
 
782
          if (LocaleCompare(keyword,"width") == 0)
 
783
            {
 
784
              svg_info->bounds.width=
 
785
                GetUserSpaceCoordinateValue(svg_info,1,value);
 
786
              break;
 
787
            }
 
788
          break;
 
789
        }
 
790
        case 'X':
 
791
        case 'x':
 
792
        {
 
793
          if (LocaleCompare(keyword,"x") == 0)
 
794
            {
 
795
              svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
 
796
              break;
 
797
            }
 
798
          if (LocaleCompare(keyword,"x1") == 0)
 
799
            {
 
800
              svg_info->segment.x1=
 
801
                GetUserSpaceCoordinateValue(svg_info,1,value);
 
802
              break;
 
803
            }
 
804
          if (LocaleCompare(keyword,"x2") == 0)
 
805
            {
 
806
              svg_info->segment.x2=
 
807
                GetUserSpaceCoordinateValue(svg_info,1,value);
 
808
              break;
 
809
            }
 
810
          break;
 
811
        }
 
812
        case 'Y':
 
813
        case 'y':
 
814
        {
 
815
          if (LocaleCompare(keyword,"y") == 0)
 
816
            {
 
817
              svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
 
818
              break;
 
819
            }
 
820
          if (LocaleCompare(keyword,"y1") == 0)
 
821
            {
 
822
              svg_info->segment.y1=
 
823
                GetUserSpaceCoordinateValue(svg_info,-1,value);
 
824
              break;
 
825
            }
 
826
          if (LocaleCompare(keyword,"y2") == 0)
 
827
            {
 
828
              svg_info->segment.y2=
 
829
                GetUserSpaceCoordinateValue(svg_info,-1,value);
 
830
              break;
 
831
            }
 
832
          break;
 
833
        }
 
834
        default:
 
835
          break;
 
836
      }
 
837
    }
 
838
  switch (*name)
 
839
  {
 
840
    case 'C':
 
841
    case 'c':
 
842
    {
 
843
      if (LocaleCompare((char *) name,"circle") == 0)
 
844
        {
 
845
          MVGPrintf(svg_info->file,"push graphic-context\n");
 
846
          break;
 
847
        }
 
848
      if (LocaleCompare((char *) name,"clipPath") == 0)
 
849
        {
 
850
          MVGPrintf(svg_info->file,"push clip-path '%s'\n",id);
 
851
          break;
 
852
        }
 
853
      break;
 
854
    }
 
855
    case 'D':
 
856
    case 'd':
 
857
    {
 
858
      if (LocaleCompare((char *) name,"defs") == 0)
 
859
        {
 
860
          MVGPrintf(svg_info->file,"push defs\n");
 
861
          break;
 
862
        }
 
863
      break;
 
864
    }
 
865
    case 'E':
 
866
    case 'e':
 
867
    {
 
868
      if (LocaleCompare((char *) name,"ellipse") == 0)
 
869
        {
 
870
          MVGPrintf(svg_info->file,"push graphic-context\n");
 
871
          break;
 
872
        }
 
873
      break;
 
874
    }
 
875
    case 'G':
 
876
    case 'g':
 
877
    {
 
878
      if (LocaleCompare((char *) name,"g") == 0)
 
879
        {
 
880
          MVGPrintf(svg_info->file,"push graphic-context\n");
 
881
          break;
 
882
        }
 
883
      break;
 
884
    }
 
885
    case 'I':
 
886
    case 'i':
 
887
    {
 
888
      if (LocaleCompare((char *) name,"image") == 0)
 
889
        {
 
890
          MVGPrintf(svg_info->file,"push graphic-context\n");
 
891
          break;
 
892
        }
 
893
      break;
 
894
    }
 
895
    case 'L':
 
896
    case 'l':
 
897
    {
 
898
      if (LocaleCompare((char *) name,"line") == 0)
 
899
        {
 
900
          MVGPrintf(svg_info->file,"push graphic-context\n");
 
901
          break;
 
902
        }
 
903
      if (LocaleCompare((char *) name,"linearGradient") == 0)
 
904
        {
 
905
          MVGPrintf(svg_info->file,"push gradient '%s' linear %g,%g %g,%g\n",id,
 
906
            svg_info->segment.x1,svg_info->segment.y1,svg_info->segment.x2,
 
907
            svg_info->segment.y2);
 
908
          break;
 
909
        }
 
910
      break;
 
911
    }
 
912
    case 'P':
 
913
    case 'p':
 
914
    {
 
915
      if (LocaleCompare((char *) name,"path") == 0)
 
916
        {
 
917
          MVGPrintf(svg_info->file,"push graphic-context\n");
 
918
          break;
 
919
        }
 
920
      if (LocaleCompare((char *) name,"pattern") == 0)
 
921
        {
 
922
          MVGPrintf(svg_info->file,"push pattern '%s' %g,%g %g,%g\n",id,
 
923
            svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
 
924
            svg_info->bounds.height);
 
925
          break;
 
926
        }
 
927
      if (LocaleCompare((char *) name,"polygon") == 0)
 
928
        {
 
929
          MVGPrintf(svg_info->file,"push graphic-context\n");
 
930
          break;
 
931
        }
 
932
      if (LocaleCompare((char *) name,"polyline") == 0)
 
933
        {
 
934
          MVGPrintf(svg_info->file,"push graphic-context\n");
 
935
          break;
 
936
        }
 
937
      break;
 
938
    }
 
939
    case 'R':
 
940
    case 'r':
 
941
    {
 
942
      if (LocaleCompare((char *) name,"radialGradient") == 0)
 
943
        {
 
944
          MVGPrintf(svg_info->file,"push gradient '%s' radial %g,%g %g,%g %g\n",
 
945
            id,svg_info->element.cx,svg_info->element.cy,
 
946
            svg_info->element.major,svg_info->element.minor,
 
947
            svg_info->element.angle);
 
948
          break;
 
949
        }
 
950
      if (LocaleCompare((char *) name,"rect") == 0)
 
951
        {
 
952
          MVGPrintf(svg_info->file,"push graphic-context\n");
 
953
          break;
 
954
        }
 
955
      break;
 
956
    }
 
957
    case 'S':
 
958
    case 's':
 
959
    {
 
960
      if (LocaleCompare((char *) name,"svg") == 0)
 
961
        {
 
962
          MVGPrintf(svg_info->file,"push graphic-context\n");
 
963
          break;
 
964
        }
 
965
      break;
 
966
    }
 
967
    case 'T':
 
968
    case 't':
 
969
    {
 
970
      if (LocaleCompare((char *) name,"text") == 0)
 
971
        {
 
972
          MVGPrintf(svg_info->file,"push graphic-context\n");
 
973
          break;
 
974
        }
 
975
      if (LocaleCompare((char *) name,"tspan") == 0)
 
976
        {
 
977
          Strip(svg_info->text);
 
978
          if (*svg_info->text != '\0')
 
979
            {
 
980
              DrawInfo
 
981
                *draw_info;
 
982
 
 
983
              TypeMetric
 
984
                metrics;
 
985
 
 
986
              char
 
987
                *text;
 
988
 
 
989
              text=EscapeString(svg_info->text,'\'');
 
990
              MVGPrintf(svg_info->file,"text %g,%g '%s'\n",svg_info->bounds.x,
 
991
                svg_info->bounds.y,text);
 
992
              MagickFreeMemory(text);
 
993
              draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
 
994
              draw_info->pointsize=svg_info->pointsize;
 
995
              draw_info->text=AllocateString(svg_info->text);
 
996
              (void) ConcatenateString(&draw_info->text," ");
 
997
              GetTypeMetrics(svg_info->image,draw_info,&metrics);
 
998
              svg_info->bounds.x+=metrics.width;
 
999
              DestroyDrawInfo(draw_info);
 
1000
              *svg_info->text='\0';
 
1001
            }
 
1002
          MVGPrintf(svg_info->file,"push graphic-context\n");
 
1003
          break;
 
1004
        }
 
1005
      break;
 
1006
    }
 
1007
    default:
 
1008
      break;
 
1009
  }
 
1010
  if (attributes != (const xmlChar **) NULL)
 
1011
    for (i=0; (attributes[i] != (const xmlChar *) NULL); i+=2)
 
1012
    {
 
1013
      keyword=(const char *) attributes[i];
 
1014
      value=(const char *) attributes[i+1];
 
1015
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
1016
        "    %.1024s = %.1024s",keyword,value);
 
1017
      switch (*keyword)
 
1018
      {
 
1019
        case 'A':
 
1020
        case 'a':
 
1021
        {
 
1022
          if (LocaleCompare(keyword,"angle") == 0)
 
1023
            {
 
1024
              MVGPrintf(svg_info->file,"angle %g\n",
 
1025
                GetUserSpaceCoordinateValue(svg_info,0,value));
 
1026
              break;
 
1027
            }
 
1028
          break;
 
1029
        }
 
1030
        case 'C':
 
1031
        case 'c':
 
1032
        {
 
1033
          if (LocaleCompare(keyword,"clip-path") == 0)
 
1034
            {
 
1035
              MVGPrintf(svg_info->file,"clip-path '%s'\n",value);
 
1036
              break;
 
1037
            }
 
1038
          if (LocaleCompare(keyword,"clip-rule") == 0)
 
1039
            {
 
1040
              MVGPrintf(svg_info->file,"clip-rule '%s'\n",value);
 
1041
              break;
 
1042
            }
 
1043
          if (LocaleCompare(keyword,"clipPathUnits") == 0)
 
1044
            {
 
1045
              (void) CloneString(&units,value);
 
1046
              MVGPrintf(svg_info->file,"clip-units '%s'\n",value);
 
1047
              break;
 
1048
            }
 
1049
          if (LocaleCompare(keyword,"color") == 0)
 
1050
            {
 
1051
              (void) CloneString(&color,value);
 
1052
              break;
 
1053
            }
 
1054
          if (LocaleCompare(keyword,"cx") == 0)
 
1055
            {
 
1056
              svg_info->element.cx=
 
1057
                GetUserSpaceCoordinateValue(svg_info,1,value);
 
1058
              break;
 
1059
            }
 
1060
          if (LocaleCompare(keyword,"cy") == 0)
 
1061
            {
 
1062
              svg_info->element.cy=
 
1063
                GetUserSpaceCoordinateValue(svg_info,-1,value);
 
1064
              break;
 
1065
            }
 
1066
          break;
 
1067
        }
 
1068
        case 'D':
 
1069
        case 'd':
 
1070
        {
 
1071
          if (LocaleCompare(keyword,"d") == 0)
 
1072
            {
 
1073
              (void) CloneString(&svg_info->vertices,value);
 
1074
              break;
 
1075
            }
 
1076
          if (LocaleCompare(keyword,"dx") == 0)
 
1077
            {
 
1078
              svg_info->bounds.x+=
 
1079
                GetUserSpaceCoordinateValue(svg_info,1,value);
 
1080
              break;
 
1081
            }
 
1082
          if (LocaleCompare(keyword,"dy") == 0)
 
1083
            {
 
1084
              svg_info->bounds.y+=
 
1085
                GetUserSpaceCoordinateValue(svg_info,-1,value);
 
1086
              break;
 
1087
            }
 
1088
          break;
 
1089
        }
 
1090
        case 'F':
 
1091
        case 'f':
 
1092
        {
 
1093
          if (LocaleCompare(keyword,"fill") == 0)
 
1094
            {
 
1095
              if (LocaleCompare(value,"currentColor") == 0)
 
1096
                {
 
1097
                  MVGPrintf(svg_info->file,"fill '%s'\n",color);
 
1098
                  break;
 
1099
                }
 
1100
              MVGPrintf(svg_info->file,"fill '%s'\n",value);
 
1101
              break;
 
1102
            }
 
1103
          if (LocaleCompare(keyword,"fillcolor") == 0)
 
1104
            {
 
1105
              MVGPrintf(svg_info->file,"fill '%s'\n",value);
 
1106
              break;
 
1107
            }
 
1108
          if (LocaleCompare(keyword,"fill-rule") == 0)
 
1109
            {
 
1110
              MVGPrintf(svg_info->file,"fill-rule '%s'\n",value);
 
1111
              break;
 
1112
            }
 
1113
          if (LocaleCompare(keyword,"fill-opacity") == 0)
 
1114
            {
 
1115
              MVGPrintf(svg_info->file,"fill-opacity '%s'\n",value);
 
1116
              break;
 
1117
            }
 
1118
          if (LocaleCompare(keyword,"font-family") == 0)
 
1119
            {
 
1120
              MVGPrintf(svg_info->file,"font-family '%s'\n",value);
 
1121
              break;
 
1122
            }
 
1123
          if (LocaleCompare(keyword,"font-stretch") == 0)
 
1124
            {
 
1125
              MVGPrintf(svg_info->file,"font-stretch '%s'\n",value);
 
1126
              break;
 
1127
            }
 
1128
          if (LocaleCompare(keyword,"font-style") == 0)
 
1129
            {
 
1130
              MVGPrintf(svg_info->file,"font-style '%s'\n",value);
 
1131
              break;
 
1132
            }
 
1133
          if (LocaleCompare(keyword,"font-size") == 0)
 
1134
            {
 
1135
              svg_info->pointsize=
 
1136
                GetUserSpaceCoordinateValue(svg_info,0,value);
 
1137
              MVGPrintf(svg_info->file,"font-size %g\n",svg_info->pointsize);
 
1138
              break;
 
1139
            }
 
1140
          if (LocaleCompare(keyword,"font-weight") == 0)
 
1141
            {
 
1142
              MVGPrintf(svg_info->file,"font-weight '%s'\n",value);
 
1143
              break;
 
1144
            }
 
1145
          break;
 
1146
        }
 
1147
        case 'G':
 
1148
        case 'g':
 
1149
        {
 
1150
          if (LocaleCompare(keyword,"gradientTransform") == 0)
 
1151
            {
 
1152
              AffineMatrix
 
1153
                affine,
 
1154
                current,
 
1155
                transform;
 
1156
 
 
1157
              IdentityAffine(&transform);
 
1158
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
 
1159
              tokens=GetTransformTokens(context,value,&number_tokens);
 
1160
              for (j=0; j < (number_tokens-1); j+=2)
 
1161
              {
 
1162
                keyword=(char *) tokens[j];
 
1163
                value=(char *) tokens[j+1];
 
1164
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
1165
                  "    %.1024s: %.1024s",keyword,value);
 
1166
                current=transform;
 
1167
                IdentityAffine(&affine);
 
1168
                switch (*keyword)
 
1169
                {
 
1170
                  case 'M':
 
1171
                  case 'm':
 
1172
                  {
 
1173
                    if (LocaleCompare(keyword,"matrix") == 0)
 
1174
                      {
 
1175
                        p=(char *) value;
 
1176
                        GetToken(p,&p,token);
 
1177
                        affine.sx=atof(value);
 
1178
                        GetToken(p,&p,token);
 
1179
                        if (*token == ',')
 
1180
                          GetToken(p,&p,token);
 
1181
                        affine.rx=atof(token);
 
1182
                        GetToken(p,&p,token);
 
1183
                        if (*token == ',')
 
1184
                          GetToken(p,&p,token);
 
1185
                        affine.ry=atof(token);
 
1186
                        GetToken(p,&p,token);
 
1187
                        if (*token == ',')
 
1188
                          GetToken(p,&p,token);
 
1189
                        affine.sy=atof(token);
 
1190
                        GetToken(p,&p,token);
 
1191
                        if (*token == ',')
 
1192
                          GetToken(p,&p,token);
 
1193
                        affine.tx=atof(token);
 
1194
                        GetToken(p,&p,token);
 
1195
                        if (*token == ',')
 
1196
                          GetToken(p,&p,token);
 
1197
                        affine.ty=atof(token);
 
1198
                        break;
 
1199
                      }
 
1200
                    break;
 
1201
                  }
 
1202
                  case 'R':
 
1203
                  case 'r':
 
1204
                  {
 
1205
                    if (LocaleCompare(keyword,"rotate") == 0)
 
1206
                      {
 
1207
                        double
 
1208
                          angle;
 
1209
 
 
1210
                        angle=GetUserSpaceCoordinateValue(svg_info,0,value);
 
1211
                        affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
 
1212
                        affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
 
1213
                        affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
 
1214
                        affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
 
1215
                        break;
 
1216
                      }
 
1217
                    break;
 
1218
                  }
 
1219
                  case 'S':
 
1220
                  case 's':
 
1221
                  {
 
1222
                    if (LocaleCompare(keyword,"scale") == 0)
 
1223
                      {
 
1224
                        for (p=(char *) value; *p != '\0'; p++)
 
1225
                          if (isspace((int) (*p)) || (*p == ','))
 
1226
                            break;
 
1227
                        affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
 
1228
                        affine.sy=affine.sx;
 
1229
                        if (*p != '\0')
 
1230
                          affine.sy=
 
1231
                            GetUserSpaceCoordinateValue(svg_info,-1,p+1);
 
1232
                        svg_info->scale[svg_info->n]=ExpandAffine(&affine);
 
1233
                        break;
 
1234
                      }
 
1235
                    if (LocaleCompare(keyword,"skewX") == 0)
 
1236
                      {
 
1237
                        affine.sx=svg_info->affine.sx;
 
1238
                        affine.ry=tan(DegreesToRadians(fmod(
 
1239
                          GetUserSpaceCoordinateValue(svg_info,1,value),
 
1240
                          360.0)));
 
1241
                        affine.sy=svg_info->affine.sy;
 
1242
                        break;
 
1243
                      }
 
1244
                    if (LocaleCompare(keyword,"skewY") == 0)
 
1245
                      {
 
1246
                        affine.sx=svg_info->affine.sx;
 
1247
                        affine.rx=tan(DegreesToRadians(fmod(
 
1248
                          GetUserSpaceCoordinateValue(svg_info,-1,value),
 
1249
                          360.0)));
 
1250
                        affine.sy=svg_info->affine.sy;
 
1251
                        break;
 
1252
                      }
 
1253
                    break;
 
1254
                  }
 
1255
                  case 'T':
 
1256
                  case 't':
 
1257
                  {
 
1258
                    if (LocaleCompare(keyword,"translate") == 0)
 
1259
                      {
 
1260
                        for (p=(char *) value; *p != '\0'; p++)
 
1261
                          if (isspace((int) (*p)) || (*p == ','))
 
1262
                            break;
 
1263
                        affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
 
1264
                        affine.ty=affine.tx;
 
1265
                        if (*p != '\0')
 
1266
                          affine.ty=
 
1267
                            GetUserSpaceCoordinateValue(svg_info,-1,p+1);
 
1268
                        break;
 
1269
                      }
 
1270
                    break;
 
1271
                  }
 
1272
                  default:
 
1273
                    break;
 
1274
                }
 
1275
                transform.sx=current.sx*affine.sx+current.ry*affine.rx;
 
1276
                transform.rx=current.rx*affine.sx+current.sy*affine.rx;
 
1277
                transform.ry=current.sx*affine.ry+current.ry*affine.sy;
 
1278
                transform.sy=current.rx*affine.ry+current.sy*affine.sy;
 
1279
                transform.tx=current.sx*affine.tx+current.ry*affine.ty+
 
1280
                  current.tx;
 
1281
                transform.ty=current.rx*affine.tx+current.sy*affine.ty+
 
1282
                  current.ty;
 
1283
              }
 
1284
              MVGPrintf(svg_info->file,"affine %g %g %g %g %g %g\n",
 
1285
                transform.sx,transform.rx,transform.ry,transform.sy,
 
1286
                transform.tx,transform.ty);
 
1287
              for (j=0; tokens[j] != (char *) NULL; j++)
 
1288
                MagickFreeMemory(tokens[j]);
 
1289
              MagickFreeMemory(tokens);
 
1290
              break;
 
1291
            }
 
1292
          if (LocaleCompare(keyword,"gradientUnits") == 0)
 
1293
            {
 
1294
              (void) CloneString(&units,value);
 
1295
              MVGPrintf(svg_info->file,"gradient-units '%s'\n",value);
 
1296
              break;
 
1297
            }
 
1298
          break;
 
1299
        }
 
1300
        case 'H':
 
1301
        case 'h':
 
1302
        {
 
1303
          if (LocaleCompare(keyword,"height") == 0)
 
1304
            {
 
1305
              svg_info->bounds.height=
 
1306
                GetUserSpaceCoordinateValue(svg_info,-1,value);
 
1307
              break;
 
1308
            }
 
1309
          if (LocaleCompare(keyword,"href") == 0)
 
1310
            {
 
1311
              (void) CloneString(&svg_info->url,value);
 
1312
              break;
 
1313
            }
 
1314
          break;
 
1315
        }
 
1316
        case 'M':
 
1317
        case 'm':
 
1318
        {
 
1319
          if (LocaleCompare(keyword,"major") == 0)
 
1320
            {
 
1321
              svg_info->element.major=
 
1322
                GetUserSpaceCoordinateValue(svg_info,1,value);
 
1323
              break;
 
1324
            }
 
1325
          if (LocaleCompare(keyword,"minor") == 0)
 
1326
            {
 
1327
              svg_info->element.minor=
 
1328
                GetUserSpaceCoordinateValue(svg_info,-1,value);
 
1329
              break;
 
1330
            }
 
1331
          break;
 
1332
        }
 
1333
        case 'O':
 
1334
        case 'o':
 
1335
        {
 
1336
          if (LocaleCompare(keyword,"offset") == 0)
 
1337
            {
 
1338
              (void) CloneString(&svg_info->offset,value);
 
1339
              break;
 
1340
            }
 
1341
          if (LocaleCompare(keyword,"opacity") == 0)
 
1342
            {
 
1343
              MVGPrintf(svg_info->file,"opacity '%s'\n",value);
 
1344
              break;
 
1345
            }
 
1346
          break;
 
1347
        }
 
1348
        case 'P':
 
1349
        case 'p':
 
1350
        {
 
1351
          if (LocaleCompare(keyword,"path") == 0)
 
1352
            {
 
1353
              (void) CloneString(&svg_info->url,value);
 
1354
              break;
 
1355
            }
 
1356
          if (LocaleCompare(keyword,"points") == 0)
 
1357
            {
 
1358
              (void) CloneString(&svg_info->vertices,value);
 
1359
              break;
 
1360
            }
 
1361
          break;
 
1362
        }
 
1363
        case 'R':
 
1364
        case 'r':
 
1365
        {
 
1366
          if (LocaleCompare(keyword,"r") == 0)
 
1367
            {
 
1368
              svg_info->element.major=
 
1369
                GetUserSpaceCoordinateValue(svg_info,1,value);
 
1370
              svg_info->element.minor=
 
1371
                GetUserSpaceCoordinateValue(svg_info,-1,value);
 
1372
              break;
 
1373
            }
 
1374
          if (LocaleCompare(keyword,"rotate") == 0)
 
1375
            {
 
1376
              double
 
1377
                angle;
 
1378
 
 
1379
              angle=GetUserSpaceCoordinateValue(svg_info,0,value);
 
1380
              MVGPrintf(svg_info->file,"translate %g,%g\n",svg_info->bounds.x,
 
1381
                svg_info->bounds.y);
 
1382
              svg_info->bounds.x=0;
 
1383
              svg_info->bounds.y=0;
 
1384
              MVGPrintf(svg_info->file,"rotate %g\n",angle);
 
1385
              break;
 
1386
            }
 
1387
          if (LocaleCompare(keyword,"rx") == 0)
 
1388
            {
 
1389
              if (LocaleCompare((char *) name,"ellipse") == 0)
 
1390
                svg_info->element.major=
 
1391
                  GetUserSpaceCoordinateValue(svg_info,1,value);
 
1392
              else
 
1393
                svg_info->radius.x=
 
1394
                  GetUserSpaceCoordinateValue(svg_info,1,value);
 
1395
              break;
 
1396
            }
 
1397
          if (LocaleCompare(keyword,"ry") == 0)
 
1398
            {
 
1399
              if (LocaleCompare((char *) name,"ellipse") == 0)
 
1400
                svg_info->element.minor=
 
1401
                  GetUserSpaceCoordinateValue(svg_info,-1,value);
 
1402
              else
 
1403
                svg_info->radius.y=
 
1404
                  GetUserSpaceCoordinateValue(svg_info,-1,value);
 
1405
              break;
 
1406
            }
 
1407
          break;
 
1408
        }
 
1409
        case 'S':
 
1410
        case 's':
 
1411
        {
 
1412
          if (LocaleCompare(keyword,"stop-color") == 0)
 
1413
            {
 
1414
              (void) CloneString(&svg_info->stop_color,value);
 
1415
              break;
 
1416
            }
 
1417
          if (LocaleCompare(keyword,"stroke") == 0)
 
1418
            {
 
1419
              if (LocaleCompare(value,"currentColor") == 0)
 
1420
                {
 
1421
                  MVGPrintf(svg_info->file,"stroke '%s'\n",color);
 
1422
                  break;
 
1423
                }
 
1424
              MVGPrintf(svg_info->file,"stroke '%s'\n",value);
 
1425
              break;
 
1426
            }
 
1427
          if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
 
1428
            {
 
1429
              MVGPrintf(svg_info->file,"stroke-antialias %d\n",
 
1430
                LocaleCompare(value,"true") == 0);
 
1431
              break;
 
1432
            }
 
1433
          if (LocaleCompare(keyword,"stroke-dasharray") == 0)
 
1434
            {
 
1435
              MVGPrintf(svg_info->file,"stroke-dasharray %s\n",value);
 
1436
              break;
 
1437
            }
 
1438
          if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
 
1439
            {
 
1440
              MVGPrintf(svg_info->file,"stroke-dashoffset %s\n",value);
 
1441
              break;
 
1442
            }
 
1443
          if (LocaleCompare(keyword,"stroke-linecap") == 0)
 
1444
            {
 
1445
              MVGPrintf(svg_info->file,"stroke-linecap '%s'\n",value);
 
1446
              break;
 
1447
            }
 
1448
          if (LocaleCompare(keyword,"stroke-linejoin") == 0)
 
1449
            {
 
1450
              MVGPrintf(svg_info->file,"stroke-linejoin '%s'\n",value);
 
1451
              break;
 
1452
            }
 
1453
          if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
 
1454
            {
 
1455
              MVGPrintf(svg_info->file,"stroke-miterlimit '%s'\n",value);
 
1456
              break;
 
1457
            }
 
1458
          if (LocaleCompare(keyword,"stroke-opacity") == 0)
 
1459
            {
 
1460
              MVGPrintf(svg_info->file,"stroke-opacity '%s'\n",value);
 
1461
              break;
 
1462
            }
 
1463
          if (LocaleCompare(keyword,"stroke-width") == 0)
 
1464
            {
 
1465
              MVGPrintf(svg_info->file,"stroke-width %g\n",
 
1466
                GetUserSpaceCoordinateValue(svg_info,1,value));
 
1467
              break;
 
1468
            }
 
1469
          if (LocaleCompare(keyword,"style") == 0)
 
1470
            {
 
1471
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
 
1472
              tokens=GetStyleTokens(context,value,&number_tokens);
 
1473
              for (j=0; j < (number_tokens-1); j+=2)
 
1474
              {
 
1475
                keyword=(char *) tokens[j];
 
1476
                value=(char *) tokens[j+1];
 
1477
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
1478
                  "    %.1024s: %.1024s",keyword,value);
 
1479
                switch (*keyword)
 
1480
                {
 
1481
                  case 'C':
 
1482
                  case 'c':
 
1483
                  {
 
1484
                     if (LocaleCompare(keyword,"clip-path") == 0)
 
1485
                       {
 
1486
                         MVGPrintf(svg_info->file,"clip-path '%s'\n",value);
 
1487
                         break;
 
1488
                       }
 
1489
                    if (LocaleCompare(keyword,"clip-rule") == 0)
 
1490
                      {
 
1491
                        MVGPrintf(svg_info->file,"clip-rule '%s'\n",value);
 
1492
                        break;
 
1493
                      }
 
1494
                     if (LocaleCompare(keyword,"clipPathUnits") == 0)
 
1495
                       {
 
1496
                         (void) CloneString(&units,value);
 
1497
                         MVGPrintf(svg_info->file,"clip-units '%s'\n",value);
 
1498
                         break;
 
1499
                       }
 
1500
                    if (LocaleCompare(keyword,"color") == 0)
 
1501
                      {
 
1502
                        (void) CloneString(&color,value);
 
1503
                        break;
 
1504
                      }
 
1505
                    break;
 
1506
                  }
 
1507
                  case 'F':
 
1508
                  case 'f':
 
1509
                  {
 
1510
                    if (LocaleCompare(keyword,"fill") == 0)
 
1511
                      {
 
1512
                         if (LocaleCompare(value,"currentColor") == 0)
 
1513
                           {
 
1514
                             MVGPrintf(svg_info->file,"fill '%s'\n",color);
 
1515
                             break;
 
1516
                           }
 
1517
                        MVGPrintf(svg_info->file,"fill '%s'\n",value);
 
1518
                        break;
 
1519
                      }
 
1520
                    if (LocaleCompare(keyword,"fillcolor") == 0)
 
1521
                      {
 
1522
                        MVGPrintf(svg_info->file,"fill '%s'\n",value);
 
1523
                        break;
 
1524
                      }
 
1525
                    if (LocaleCompare(keyword,"fill-rule") == 0)
 
1526
                      {
 
1527
                        MVGPrintf(svg_info->file,"fill-rule '%s'\n",value);
 
1528
                        break;
 
1529
                      }
 
1530
                    if (LocaleCompare(keyword,"fill-opacity") == 0)
 
1531
                      {
 
1532
                        MVGPrintf(svg_info->file,"fill-opacity '%s'\n",value);
 
1533
                        break;
 
1534
                      }
 
1535
                    if (LocaleCompare(keyword,"font-family") == 0)
 
1536
                      {
 
1537
                        MVGPrintf(svg_info->file,"font-family '%s'\n",value);
 
1538
                        break;
 
1539
                      }
 
1540
                    if (LocaleCompare(keyword,"font-stretch") == 0)
 
1541
                      {
 
1542
                        MVGPrintf(svg_info->file,"font-stretch '%s'\n",value);
 
1543
                        break;
 
1544
                      }
 
1545
                    if (LocaleCompare(keyword,"font-style") == 0)
 
1546
                      {
 
1547
                        MVGPrintf(svg_info->file,"font-style '%s'\n",value);
 
1548
                        break;
 
1549
                      }
 
1550
                    if (LocaleCompare(keyword,"font-size") == 0)
 
1551
                      {
 
1552
                        svg_info->pointsize=
 
1553
                          GetUserSpaceCoordinateValue(svg_info,0,value);
 
1554
                        MVGPrintf(svg_info->file,"font-size %g\n",
 
1555
                          svg_info->pointsize);
 
1556
                        break;
 
1557
                      }
 
1558
                    if (LocaleCompare(keyword,"font-weight") == 0)
 
1559
                      {
 
1560
                        MVGPrintf(svg_info->file,"font-weight '%s'\n",value);
 
1561
                        break;
 
1562
                      }
 
1563
                    break;
 
1564
                  }
 
1565
                  case 'O':
 
1566
                  case 'o':
 
1567
                  {
 
1568
                    if (LocaleCompare(keyword,"offset") == 0)
 
1569
                      {
 
1570
                        MVGPrintf(svg_info->file,"offset %g\n",
 
1571
                          GetUserSpaceCoordinateValue(svg_info,1,value));
 
1572
                        break;
 
1573
                      }
 
1574
                    if (LocaleCompare(keyword,"opacity") == 0)
 
1575
                      {
 
1576
                        MVGPrintf(svg_info->file,"opacity '%s'\n",value);
 
1577
                        break;
 
1578
                      }
 
1579
                    break;
 
1580
                  }
 
1581
                  case 'S':
 
1582
                  case 's':
 
1583
                  {
 
1584
                    if (LocaleCompare(keyword,"stop-color") == 0)
 
1585
                      {
 
1586
                        (void) CloneString(&svg_info->stop_color,value);
 
1587
                        break;
 
1588
                      }
 
1589
                    if (LocaleCompare(keyword,"stroke") == 0)
 
1590
                      {
 
1591
                         if (LocaleCompare(value,"currentColor") == 0)
 
1592
                           {
 
1593
                             MVGPrintf(svg_info->file,"stroke '%s'\n",color);
 
1594
                             break;
 
1595
                           }
 
1596
                        MVGPrintf(svg_info->file,"stroke '%s'\n",value);
 
1597
                        break;
 
1598
                      }
 
1599
                    if (LocaleCompare(keyword,"stroke-antialiasing") == 0)
 
1600
                      {
 
1601
                        MVGPrintf(svg_info->file,"stroke-antialias %d\n",
 
1602
                          LocaleCompare(value,"true") == 0);
 
1603
                        break;
 
1604
                      }
 
1605
                    if (LocaleCompare(keyword,"stroke-dasharray") == 0)
 
1606
                      {
 
1607
                        MVGPrintf(svg_info->file,"stroke-dasharray %s\n",value);
 
1608
                        break;
 
1609
                      }
 
1610
                    if (LocaleCompare(keyword,"stroke-dashoffset") == 0)
 
1611
                      {
 
1612
                        MVGPrintf(svg_info->file,"stroke-dashoffset %s\n",
 
1613
                          value);
 
1614
                        break;
 
1615
                      }
 
1616
                    if (LocaleCompare(keyword,"stroke-linecap") == 0)
 
1617
                      {
 
1618
                        MVGPrintf(svg_info->file,"stroke-linecap '%s'\n",value);
 
1619
                        break;
 
1620
                      }
 
1621
                    if (LocaleCompare(keyword,"stroke-linejoin") == 0)
 
1622
                      {
 
1623
                        MVGPrintf(svg_info->file,"stroke-linejoin '%s'\n",
 
1624
                          value);
 
1625
                        break;
 
1626
                      }
 
1627
                    if (LocaleCompare(keyword,"stroke-miterlimit") == 0)
 
1628
                      {
 
1629
                        MVGPrintf(svg_info->file,"stroke-miterlimit '%s'\n",
 
1630
                          value);
 
1631
                        break;
 
1632
                      }
 
1633
                    if (LocaleCompare(keyword,"stroke-opacity") == 0)
 
1634
                      {
 
1635
                        MVGPrintf(svg_info->file,"stroke-opacity '%s'\n",value);
 
1636
                        break;
 
1637
                      }
 
1638
                    if (LocaleCompare(keyword,"stroke-width") == 0)
 
1639
                      {
 
1640
                        MVGPrintf(svg_info->file,"stroke-width %g\n",
 
1641
                          GetUserSpaceCoordinateValue(svg_info,1,value));
 
1642
                        break;
 
1643
                      }
 
1644
                    break;
 
1645
                  }
 
1646
                  case 't':
 
1647
                  case 'T':
 
1648
                  {
 
1649
                    if (LocaleCompare(keyword,"text-align") == 0)
 
1650
                      {
 
1651
                        MVGPrintf(svg_info->file,"text-align '%s'\n",value);
 
1652
                        break;
 
1653
                      }
 
1654
                    if (LocaleCompare(keyword,"text-anchor") == 0)
 
1655
                      {
 
1656
                        MVGPrintf(svg_info->file,"text-anchor '%s'\n",value);
 
1657
                        break;
 
1658
                      }
 
1659
                    if (LocaleCompare(keyword,"text-decoration") == 0)
 
1660
                      {
 
1661
                        if (LocaleCompare(value,"underline") == 0)
 
1662
                          MVGPrintf(svg_info->file,"decorate underline\n");
 
1663
                        if (LocaleCompare(value,"line-through") == 0)
 
1664
                          MVGPrintf(svg_info->file,"decorate line-through\n");
 
1665
                        if (LocaleCompare(value,"overline") == 0)
 
1666
                          MVGPrintf(svg_info->file,"decorate overline\n");
 
1667
                        break;
 
1668
                      }
 
1669
                    if (LocaleCompare(keyword,"text-antialiasing") == 0)
 
1670
                      {
 
1671
                        MVGPrintf(svg_info->file,"text-antialias %d\n",
 
1672
                          LocaleCompare(value,"true") == 0);
 
1673
                        break;
 
1674
                      }
 
1675
                    break;
 
1676
                  }
 
1677
                  default:
 
1678
                    break;
 
1679
                }
 
1680
              }
 
1681
              for (j=0; tokens[j] != (char *) NULL; j++)
 
1682
                MagickFreeMemory(tokens[j]);
 
1683
              MagickFreeMemory(tokens);
 
1684
              break;
 
1685
            }
 
1686
          break;
 
1687
        }
 
1688
        case 'T':
 
1689
        case 't':
 
1690
        {
 
1691
          if (LocaleCompare(keyword,"text-align") == 0)
 
1692
            {
 
1693
              MVGPrintf(svg_info->file,"text-align '%s'\n",value);
 
1694
              break;
 
1695
            }
 
1696
          if (LocaleCompare(keyword,"text-anchor") == 0)
 
1697
            {
 
1698
              MVGPrintf(svg_info->file,"text-anchor '%s'\n",value);
 
1699
              break;
 
1700
            }
 
1701
          if (LocaleCompare(keyword,"text-decoration") == 0)
 
1702
            {
 
1703
              if (LocaleCompare(value,"underline") == 0)
 
1704
                MVGPrintf(svg_info->file,"decorate underline\n");
 
1705
              if (LocaleCompare(value,"line-through") == 0)
 
1706
                MVGPrintf(svg_info->file,"decorate line-through\n");
 
1707
              if (LocaleCompare(value,"overline") == 0)
 
1708
                MVGPrintf(svg_info->file,"decorate overline\n");
 
1709
              break;
 
1710
            }
 
1711
          if (LocaleCompare(keyword,"text-antialiasing") == 0)
 
1712
            {
 
1713
              MVGPrintf(svg_info->file,"text-antialias %d\n",
 
1714
                LocaleCompare(value,"true") == 0);
 
1715
              break;
 
1716
            }
 
1717
          if (LocaleCompare(keyword,"transform") == 0)
 
1718
            {
 
1719
              AffineMatrix
 
1720
                affine,
 
1721
                current,
 
1722
                transform;
 
1723
 
 
1724
              IdentityAffine(&transform);
 
1725
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  ");
 
1726
              tokens=GetTransformTokens(context,value,&number_tokens);
 
1727
              for (j=0; j < (number_tokens-1); j+=2)
 
1728
              {
 
1729
                keyword=(char *) tokens[j];
 
1730
                value=(char *) tokens[j+1];
 
1731
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
1732
                  "    %.1024s: %.1024s",keyword,value);
 
1733
                current=transform;
 
1734
                IdentityAffine(&affine);
 
1735
                switch (*keyword)
 
1736
                {
 
1737
                  case 'M':
 
1738
                  case 'm':
 
1739
                  {
 
1740
                    if (LocaleCompare(keyword,"matrix") == 0)
 
1741
                      {
 
1742
                        p=(char *) value;
 
1743
                        GetToken(p,&p,token);
 
1744
                        affine.sx=atof(value);
 
1745
                        GetToken(p,&p,token);
 
1746
                        if (*token == ',')
 
1747
                          GetToken(p,&p,token);
 
1748
                        affine.rx=atof(token);
 
1749
                        GetToken(p,&p,token);
 
1750
                        if (*token == ',')
 
1751
                          GetToken(p,&p,token);
 
1752
                        affine.ry=atof(token);
 
1753
                        GetToken(p,&p,token);
 
1754
                        if (*token == ',')
 
1755
                          GetToken(p,&p,token);
 
1756
                        affine.sy=atof(token);
 
1757
                        GetToken(p,&p,token);
 
1758
                        if (*token == ',')
 
1759
                          GetToken(p,&p,token);
 
1760
                        affine.tx=atof(token);
 
1761
                        GetToken(p,&p,token);
 
1762
                        if (*token == ',')
 
1763
                          GetToken(p,&p,token);
 
1764
                        affine.ty=atof(token);
 
1765
                        break;
 
1766
                      }
 
1767
                    break;
 
1768
                  }
 
1769
                  case 'R':
 
1770
                  case 'r':
 
1771
                  {
 
1772
                    if (LocaleCompare(keyword,"rotate") == 0)
 
1773
                      {
 
1774
                        double
 
1775
                          angle;
 
1776
 
 
1777
                        angle=GetUserSpaceCoordinateValue(svg_info,0,value);
 
1778
                        affine.sx=cos(DegreesToRadians(fmod(angle,360.0)));
 
1779
                        affine.rx=sin(DegreesToRadians(fmod(angle,360.0)));
 
1780
                        affine.ry=(-sin(DegreesToRadians(fmod(angle,360.0))));
 
1781
                        affine.sy=cos(DegreesToRadians(fmod(angle,360.0)));
 
1782
                        break;
 
1783
                      }
 
1784
                    break;
 
1785
                  }
 
1786
                  case 'S':
 
1787
                  case 's':
 
1788
                  {
 
1789
                    if (LocaleCompare(keyword,"scale") == 0)
 
1790
                      {
 
1791
                        for (p=(char *) value; *p != '\0'; p++)
 
1792
                          if (isspace((int) (*p)) || (*p == ','))
 
1793
                            break;
 
1794
                        affine.sx=GetUserSpaceCoordinateValue(svg_info,1,value);
 
1795
                        affine.sy=affine.sx;
 
1796
                        if (*p != '\0')
 
1797
                          affine.sy=
 
1798
                            GetUserSpaceCoordinateValue(svg_info,-1,p+1);
 
1799
                        svg_info->scale[svg_info->n]=ExpandAffine(&affine);
 
1800
                        break;
 
1801
                      }
 
1802
                    if (LocaleCompare(keyword,"skewX") == 0)
 
1803
                      {
 
1804
                        affine.sx=svg_info->affine.sx;
 
1805
                        affine.ry=tan(DegreesToRadians(fmod(
 
1806
                          GetUserSpaceCoordinateValue(svg_info,1,value),
 
1807
                          360.0)));
 
1808
                        affine.sy=svg_info->affine.sy;
 
1809
                        break;
 
1810
                      }
 
1811
                    if (LocaleCompare(keyword,"skewY") == 0)
 
1812
                      {
 
1813
                        affine.sx=svg_info->affine.sx;
 
1814
                        affine.rx=tan(DegreesToRadians(fmod(
 
1815
                          GetUserSpaceCoordinateValue(svg_info,-1,value),
 
1816
                          360.0)));
 
1817
                        affine.sy=svg_info->affine.sy;
 
1818
                        break;
 
1819
                      }
 
1820
                    break;
 
1821
                  }
 
1822
                  case 'T':
 
1823
                  case 't':
 
1824
                  {
 
1825
                    if (LocaleCompare(keyword,"translate") == 0)
 
1826
                      {
 
1827
                        for (p=(char *) value; *p != '\0'; p++)
 
1828
                          if (isspace((int) (*p)) || (*p == ','))
 
1829
                            break;
 
1830
                        affine.tx=GetUserSpaceCoordinateValue(svg_info,1,value);
 
1831
                        affine.ty=affine.tx;
 
1832
                        if (*p != '\0')
 
1833
                          affine.ty=
 
1834
                            GetUserSpaceCoordinateValue(svg_info,-1,p+1);
 
1835
                        break;
 
1836
                      }
 
1837
                    break;
 
1838
                  }
 
1839
                  default:
 
1840
                    break;
 
1841
                }
 
1842
                transform.sx=current.sx*affine.sx+current.ry*affine.rx;
 
1843
                transform.rx=current.rx*affine.sx+current.sy*affine.rx;
 
1844
                transform.ry=current.sx*affine.ry+current.ry*affine.sy;
 
1845
                transform.sy=current.rx*affine.ry+current.sy*affine.sy;
 
1846
                transform.tx=current.sx*affine.tx+current.ry*affine.ty+
 
1847
                  current.tx;
 
1848
                transform.ty=current.rx*affine.tx+current.sy*affine.ty+
 
1849
                  current.ty;
 
1850
              }
 
1851
              MVGPrintf(svg_info->file,"affine %g %g %g %g %g %g\n",
 
1852
                transform.sx,transform.rx,transform.ry,transform.sy,
 
1853
                transform.tx,transform.ty);
 
1854
              for (j=0; tokens[j] != (char *) NULL; j++)
 
1855
                MagickFreeMemory(tokens[j]);
 
1856
              MagickFreeMemory(tokens);
 
1857
              break;
 
1858
            }
 
1859
          break;
 
1860
        }
 
1861
        case 'V':
 
1862
        case 'v':
 
1863
        {
 
1864
          if (LocaleCompare(keyword,"verts") == 0)
 
1865
            {
 
1866
              (void) CloneString(&svg_info->vertices,value);
 
1867
              break;
 
1868
            }
 
1869
          if (LocaleCompare(keyword,"viewBox") == 0)
 
1870
            {
 
1871
              p=(char *) value;
 
1872
              GetToken(p,&p,token);
 
1873
              svg_info->view_box.x=atof(token);
 
1874
              GetToken(p,&p,token);
 
1875
              if (*token == ',')
 
1876
                GetToken(p,&p,token);
 
1877
              svg_info->view_box.y=atof(token);
 
1878
              GetToken(p,&p,token);
 
1879
              if (*token == ',')
 
1880
                GetToken(p,&p,token);
 
1881
              svg_info->view_box.width=atof(token);
 
1882
              if (svg_info->bounds.width == 0)
 
1883
                svg_info->bounds.width=svg_info->view_box.width;
 
1884
              GetToken(p,&p,token);
 
1885
              if (*token == ',')
 
1886
                GetToken(p,&p,token);
 
1887
              svg_info->view_box.height=atof(token);
 
1888
              if (svg_info->bounds.height == 0)
 
1889
                svg_info->bounds.height=svg_info->view_box.height;
 
1890
              break;
 
1891
            }
 
1892
          break;
 
1893
        }
 
1894
        case 'W':
 
1895
        case 'w':
 
1896
        {
 
1897
          if (LocaleCompare(keyword,"width") == 0)
 
1898
            {
 
1899
              svg_info->bounds.width=
 
1900
                GetUserSpaceCoordinateValue(svg_info,1,value);
 
1901
              break;
 
1902
            }
 
1903
          break;
 
1904
        }
 
1905
        case 'X':
 
1906
        case 'x':
 
1907
        {
 
1908
          if (LocaleCompare(keyword,"x") == 0)
 
1909
            {
 
1910
              svg_info->bounds.x=GetUserSpaceCoordinateValue(svg_info,1,value);
 
1911
              break;
 
1912
            }
 
1913
          if (LocaleCompare(keyword,"xlink:href") == 0)
 
1914
            {
 
1915
              (void) CloneString(&svg_info->url,value);
 
1916
              break;
 
1917
            }
 
1918
          if (LocaleCompare(keyword,"x1") == 0)
 
1919
            {
 
1920
              svg_info->segment.x1=
 
1921
                GetUserSpaceCoordinateValue(svg_info,1,value);
 
1922
              break;
 
1923
            }
 
1924
          if (LocaleCompare(keyword,"x2") == 0)
 
1925
            {
 
1926
              svg_info->segment.x2=
 
1927
                GetUserSpaceCoordinateValue(svg_info,1,value);
 
1928
              break;
 
1929
            }
 
1930
          break;
 
1931
        }
 
1932
        case 'Y':
 
1933
        case 'y':
 
1934
        {
 
1935
          if (LocaleCompare(keyword,"y") == 0)
 
1936
            {
 
1937
              svg_info->bounds.y=GetUserSpaceCoordinateValue(svg_info,-1,value);
 
1938
              break;
 
1939
            }
 
1940
          if (LocaleCompare(keyword,"y1") == 0)
 
1941
            {
 
1942
              svg_info->segment.y1=
 
1943
                GetUserSpaceCoordinateValue(svg_info,-1,value);
 
1944
              break;
 
1945
            }
 
1946
          if (LocaleCompare(keyword,"y2") == 0)
 
1947
            {
 
1948
              svg_info->segment.y2=
 
1949
                GetUserSpaceCoordinateValue(svg_info,-1,value);
 
1950
              break;
 
1951
            }
 
1952
          break;
 
1953
        }
 
1954
        default:
 
1955
          break;
 
1956
      }
 
1957
    }
 
1958
#ifdef BROKEN_SIZE_OPTION
 
1959
  if (LocaleCompare((char *) name,"svg") == 0)
 
1960
    {
 
1961
      if (svg_info->document->encoding != (const xmlChar *) NULL)
 
1962
        MVGPrintf(svg_info->file,"encoding %.1024s\n",
 
1963
          (char *) svg_info->document->encoding);
 
1964
      if (attributes != (const xmlChar **) NULL)
 
1965
        {
 
1966
          double
 
1967
            sx,
 
1968
            sy;
 
1969
 
 
1970
          if ((svg_info->view_box.width == 0.0) ||
 
1971
              (svg_info->view_box.height == 0.0))
 
1972
            svg_info->view_box=svg_info->bounds;
 
1973
          svg_info->width=(unsigned long) svg_info->bounds.width;
 
1974
          svg_info->height=(unsigned long) svg_info->bounds.height;
 
1975
          MVGPrintf(svg_info->file,"viewbox 0 0 %lu %lu\n",svg_info->width,
 
1976
                    svg_info->height);
 
1977
          /*
 
1978
            Set initial canvas background color to user specified value
 
1979
          */
 
1980
          {
 
1981
            char
 
1982
              tuple[MaxTextExtent];
 
1983
 
 
1984
            GetColorTuple(&svg_info->image_info->background_color,8,True,True,
 
1985
                          tuple);
 
1986
 
 
1987
            MVGPrintf(svg_info->file,"push graphic-context\n");
 
1988
            MVGPrintf(svg_info->file,"fill %s\n", tuple);
 
1989
            MVGPrintf(svg_info->file,"rectangle 0,0 %g,%g\n",
 
1990
                      svg_info->view_box.width,svg_info->view_box.height);
 
1991
            MVGPrintf(svg_info->file,"pop graphic-context\n");
 
1992
          }
 
1993
          sx=(double) svg_info->width/svg_info->view_box.width;
 
1994
          sy=(double) svg_info->height/svg_info->view_box.height;
 
1995
          MVGPrintf(svg_info->file,"affine %g 0 0 %g %g %g\n",sx,sy,
 
1996
            -sx*svg_info->view_box.x,-sy*svg_info->view_box.y);
 
1997
        }
 
1998
    }
 
1999
#else
 
2000
  if (LocaleCompare((char *) name,"svg") == 0)
 
2001
    {
 
2002
      if (svg_info->document->encoding != (const xmlChar *) NULL)
 
2003
        (void) fprintf(svg_info->file,"encoding %.1024s\n",
 
2004
          (char *) svg_info->document->encoding);
 
2005
      if (attributes != (const xmlChar **) NULL)
 
2006
        {
 
2007
          char
 
2008
            *geometry,
 
2009
            *p;
 
2010
 
 
2011
          RectangleInfo
 
2012
            page;
 
2013
 
 
2014
          double
 
2015
            sx,
 
2016
            sy;
 
2017
 
 
2018
          if ((svg_info->view_box.width == 0.0) ||
 
2019
              (svg_info->view_box.height == 0.0))
 
2020
            svg_info->view_box=svg_info->bounds;
 
2021
          SetGeometry(svg_info->image,&page);
 
2022
          page.width=(unsigned long) svg_info->bounds.width;
 
2023
          page.height=(unsigned long) svg_info->bounds.height;
 
2024
          geometry=(char *) NULL;
 
2025
          /* at one point we use to try to use either page geometry
 
2026
             or size to set the dimensions of the output page, but
 
2027
             now we only look for size
 
2028
           */
 
2029
#ifdef PARSE_PAGE_FIRST
 
2030
          if (svg_info->page != (char *) NULL)
 
2031
            geometry=GetPageGeometry(svg_info->page);
 
2032
          else
 
2033
#endif
 
2034
            if (svg_info->size != (char *) NULL)
 
2035
              geometry=GetPageGeometry(svg_info->size);
 
2036
          if (geometry != (char *) NULL)
 
2037
            {
 
2038
              p=strchr(geometry,'>');
 
2039
              if (p != (char *) NULL)
 
2040
                *p='\0';
 
2041
              (void) GetMagickGeometry(geometry,&page.x,&page.y,
 
2042
                &page.width,&page.height);
 
2043
              MagickFreeMemory(geometry);
 
2044
            }
 
2045
          if (svg_info->affine.sx != 1.0)
 
2046
            page.width=(unsigned long)
 
2047
              ceil(ExpandAffine(&svg_info->affine)*page.width-0.5);
 
2048
          if (svg_info->affine.sy != 0.0)
 
2049
            page.height=(unsigned long)
 
2050
              ceil(ExpandAffine(&svg_info->affine)*page.height-0.5);
 
2051
          (void) MVGPrintf(svg_info->file,"viewbox 0 0 %g %g\n",
 
2052
            svg_info->view_box.width,svg_info->view_box.height);
 
2053
          sx=(double) page.width/svg_info->view_box.width;
 
2054
          sy=(double) page.height/svg_info->view_box.height;
 
2055
          MVGPrintf(svg_info->file,"affine %g 0 0 %g %g %g\n",sx,sy,
 
2056
            -sx*svg_info->view_box.x,-sy*svg_info->view_box.y);
 
2057
          svg_info->width=page.width;
 
2058
          svg_info->height=page.height;
 
2059
        }
 
2060
    }
 
2061
#endif
 
2062
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  )");
 
2063
  MagickFreeMemory(units);
 
2064
  if (color != (char *) NULL)
 
2065
    MagickFreeMemory(color);
 
2066
}
 
2067
 
 
2068
static void SVGEndElement(void *context,const xmlChar *name)
 
2069
{
 
2070
  SVGInfo
 
2071
    *svg_info;
 
2072
 
 
2073
  /*
 
2074
    Called when the end of an element has been detected.
 
2075
  */
 
2076
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
2077
    "  SAX.endElement(%.1024s)",name);
 
2078
  svg_info=(SVGInfo *) context;
 
2079
  switch (*name)
 
2080
  {
 
2081
    case 'C':
 
2082
    case 'c':
 
2083
    {
 
2084
      if (LocaleCompare((char *) name,"circle") == 0)
 
2085
        {
 
2086
          MVGPrintf(svg_info->file,"circle %g,%g %g,%g\n",svg_info->element.cx,
 
2087
            svg_info->element.cy,svg_info->element.cx,svg_info->element.cy+
 
2088
            svg_info->element.minor);
 
2089
          MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2090
          break;
 
2091
        }
 
2092
      if (LocaleCompare((char *) name,"clipPath") == 0)
 
2093
        {
 
2094
          MVGPrintf(svg_info->file,"pop clip-path\n");
 
2095
          break;
 
2096
        }
 
2097
      break;
 
2098
    }
 
2099
    case 'D':
 
2100
    case 'd':
 
2101
    {
 
2102
      if (LocaleCompare((char *) name,"defs") == 0)
 
2103
        {
 
2104
          MVGPrintf(svg_info->file,"pop defs\n");
 
2105
          break;
 
2106
        }
 
2107
      if (LocaleCompare((char *) name,"desc") == 0)
 
2108
        {
 
2109
          register char
 
2110
            *p;
 
2111
 
 
2112
          Strip(svg_info->text);
 
2113
          if (*svg_info->text == '\0')
 
2114
            break;
 
2115
          (void) fputc('#',svg_info->file);
 
2116
          for (p=svg_info->text; *p != '\0'; p++)
 
2117
          {
 
2118
            (void) fputc(*p,svg_info->file);
 
2119
            if (*p == '\n')
 
2120
              (void) fputc('#',svg_info->file);
 
2121
          }
 
2122
          (void) fputc('\n',svg_info->file);
 
2123
          *svg_info->text='\0';
 
2124
          break;
 
2125
        }
 
2126
      break;
 
2127
    }
 
2128
    case 'E':
 
2129
    case 'e':
 
2130
    {
 
2131
      if (LocaleCompare((char *) name,"ellipse") == 0)
 
2132
        {
 
2133
          double
 
2134
            angle;
 
2135
 
 
2136
          angle=svg_info->element.angle;
 
2137
          MVGPrintf(svg_info->file,"ellipse %g,%g %g,%g 0,360\n",
 
2138
            svg_info->element.cx,svg_info->element.cy,
 
2139
            angle == 0.0 ? svg_info->element.major : svg_info->element.minor,
 
2140
            angle == 0.0 ? svg_info->element.minor : svg_info->element.major);
 
2141
          MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2142
          break;
 
2143
        }
 
2144
      break;
 
2145
    }
 
2146
    case 'G':
 
2147
    case 'g':
 
2148
    {
 
2149
      if (LocaleCompare((char *) name,"g") == 0)
 
2150
        {
 
2151
          MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2152
          break;
 
2153
        }
 
2154
      break;
 
2155
    }
 
2156
    case 'I':
 
2157
    case 'i':
 
2158
    {
 
2159
      if (LocaleCompare((char *) name,"image") == 0)
 
2160
        {
 
2161
          MVGPrintf(svg_info->file,"image Copy %g,%g %g,%g '%s'\n",
 
2162
            svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.width,
 
2163
            svg_info->bounds.height,svg_info->url);
 
2164
          MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2165
          break;
 
2166
        }
 
2167
      break;
 
2168
    }
 
2169
    case 'L':
 
2170
    case 'l':
 
2171
    {
 
2172
      if (LocaleCompare((char *) name,"line") == 0)
 
2173
        {
 
2174
          MVGPrintf(svg_info->file,"line %g,%g %g,%g\n",svg_info->segment.x1,
 
2175
            svg_info->segment.y1,svg_info->segment.x2,svg_info->segment.y2);
 
2176
          MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2177
          break;
 
2178
        }
 
2179
      if (LocaleCompare((char *) name,"linearGradient") == 0)
 
2180
        {
 
2181
          MVGPrintf(svg_info->file,"pop gradient\n");
 
2182
          break;
 
2183
        }
 
2184
      break;
 
2185
    }
 
2186
    case 'P':
 
2187
    case 'p':
 
2188
    {
 
2189
      if (LocaleCompare((char *) name,"pattern") == 0)
 
2190
        {
 
2191
          MVGPrintf(svg_info->file,"pop pattern\n");
 
2192
          break;
 
2193
        }
 
2194
      if (LocaleCompare((char *) name,"path") == 0)
 
2195
        {
 
2196
          MVGPrintf(svg_info->file,"path '%s'\n",svg_info->vertices);
 
2197
          MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2198
          break;
 
2199
        }
 
2200
      if (LocaleCompare((char *) name,"polygon") == 0)
 
2201
        {
 
2202
          MVGPrintf(svg_info->file,"polygon %s\n",svg_info->vertices);
 
2203
          MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2204
          break;
 
2205
        }
 
2206
      if (LocaleCompare((char *) name,"polyline") == 0)
 
2207
        {
 
2208
          MVGPrintf(svg_info->file,"polyline %s\n",svg_info->vertices);
 
2209
          MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2210
          break;
 
2211
        }
 
2212
      break;
 
2213
    }
 
2214
    case 'R':
 
2215
    case 'r':
 
2216
    {
 
2217
      if (LocaleCompare((char *) name,"radialGradient") == 0)
 
2218
        {
 
2219
          MVGPrintf(svg_info->file,"pop gradient\n");
 
2220
          break;
 
2221
        }
 
2222
      if (LocaleCompare((char *) name,"rect") == 0)
 
2223
        {
 
2224
          if ((svg_info->radius.x == 0.0) && (svg_info->radius.y == 0.0))
 
2225
            {
 
2226
              MVGPrintf(svg_info->file,"rectangle %g,%g %g,%g\n",
 
2227
                svg_info->bounds.x,svg_info->bounds.y,
 
2228
                svg_info->bounds.x+svg_info->bounds.width,
 
2229
                svg_info->bounds.y+svg_info->bounds.height);
 
2230
              MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2231
              break;
 
2232
            }
 
2233
          if (svg_info->radius.x == 0.0)
 
2234
            svg_info->radius.x=svg_info->radius.y;
 
2235
          if (svg_info->radius.y == 0.0)
 
2236
            svg_info->radius.y=svg_info->radius.x;
 
2237
          MVGPrintf(svg_info->file,"roundRectangle %g,%g %g,%g %g,%g\n",
 
2238
            svg_info->bounds.x,svg_info->bounds.y,svg_info->bounds.x+
 
2239
            svg_info->bounds.width,svg_info->bounds.y+svg_info->bounds.height,
 
2240
            svg_info->radius.x,svg_info->radius.y);
 
2241
          svg_info->radius.x=0.0;
 
2242
          svg_info->radius.y=0.0;
 
2243
          MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2244
          break;
 
2245
        }
 
2246
      break;
 
2247
    }
 
2248
    case 'S':
 
2249
    case 's':
 
2250
    {
 
2251
      if (LocaleCompare((char *) name,"stop") == 0)
 
2252
        {
 
2253
          MVGPrintf(svg_info->file,"stop-color '%s' %s\n",svg_info->stop_color,
 
2254
            svg_info->offset);
 
2255
          break;
 
2256
        }
 
2257
      if (LocaleCompare((char *) name,"svg") == 0)
 
2258
        {
 
2259
          MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2260
          break;
 
2261
        }
 
2262
      break;
 
2263
    }
 
2264
    case 'T':
 
2265
    case 't':
 
2266
    {
 
2267
      if (LocaleCompare((char *) name,"text") == 0)
 
2268
        {
 
2269
          Strip(svg_info->text);
 
2270
          if (*svg_info->text != '\0')
 
2271
            {
 
2272
              char
 
2273
                *text;
 
2274
 
 
2275
              text=EscapeString(svg_info->text,'\'');
 
2276
              MVGPrintf(svg_info->file,"text %g,%g '%s'\n",svg_info->bounds.x,
 
2277
                svg_info->bounds.y,text);
 
2278
              MagickFreeMemory(text);
 
2279
              *svg_info->text='\0';
 
2280
            }
 
2281
          MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2282
          break;
 
2283
        }
 
2284
      if (LocaleCompare((char *) name,"tspan") == 0)
 
2285
        {
 
2286
          Strip(svg_info->text);
 
2287
          if (*svg_info->text != '\0')
 
2288
            {
 
2289
              DrawInfo
 
2290
                *draw_info;
 
2291
 
 
2292
              TypeMetric
 
2293
                metrics;
 
2294
 
 
2295
              char
 
2296
                *text;
 
2297
 
 
2298
              text=EscapeString(svg_info->text,'\'');
 
2299
              MVGPrintf(svg_info->file,"text %g,%g '%s'\n",svg_info->bounds.x,
 
2300
                svg_info->bounds.y,text);
 
2301
              MagickFreeMemory(text);
 
2302
              draw_info=CloneDrawInfo(svg_info->image_info,(DrawInfo *) NULL);
 
2303
              draw_info->pointsize=svg_info->pointsize;
 
2304
              draw_info->text=AllocateString(svg_info->text);
 
2305
              (void) ConcatenateString(&draw_info->text," ");
 
2306
              GetTypeMetrics(svg_info->image,draw_info,&metrics);
 
2307
              svg_info->bounds.x+=metrics.width;
 
2308
              DestroyDrawInfo(draw_info);
 
2309
              *svg_info->text='\0';
 
2310
            }
 
2311
          MVGPrintf(svg_info->file,"pop graphic-context\n");
 
2312
          break;
 
2313
        }
 
2314
      if (LocaleCompare((char *) name,"title") == 0)
 
2315
        {
 
2316
          Strip(svg_info->text);
 
2317
          if (*svg_info->text == '\0')
 
2318
            break;
 
2319
          (void) CloneString(&svg_info->title,svg_info->text);
 
2320
          *svg_info->text='\0';
 
2321
          break;
 
2322
        }
 
2323
      break;
 
2324
    }
 
2325
    default:
 
2326
      break;
 
2327
  }
 
2328
  (void) memset(&svg_info->segment,0,sizeof(svg_info->segment));
 
2329
  svg_info->n--;
 
2330
}
 
2331
 
 
2332
static void SVGCharacters(void *context,const xmlChar *c,int length)
 
2333
{
 
2334
  register char
 
2335
    *p;
 
2336
 
 
2337
  register long
 
2338
    i;
 
2339
 
 
2340
  SVGInfo
 
2341
    *svg_info;
 
2342
 
 
2343
  /*
 
2344
    Receiving some characters from the parser.
 
2345
  */
 
2346
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
2347
    "  SAX.characters(%.1024s,%d)",c,length);
 
2348
  svg_info=(SVGInfo *) context;
 
2349
  if (svg_info->text != (char *) NULL)
 
2350
    {
 
2351
      MagickReallocMemory(svg_info->text,strlen(svg_info->text)+length+1);
 
2352
    }
 
2353
  else
 
2354
    {
 
2355
      svg_info->text=MagickAllocateMemory(char *,length+1);
 
2356
      if (svg_info->text != (char *) NULL)
 
2357
        *svg_info->text='\0';
 
2358
    }
 
2359
  if (svg_info->text == (char *) NULL)
 
2360
    return;
 
2361
  p=svg_info->text+strlen(svg_info->text);
 
2362
  for (i=0; i < length; i++)
 
2363
    *p++=c[i];
 
2364
  *p='\0';
 
2365
}
 
2366
 
 
2367
static void SVGReference(void *context,const xmlChar *name)
 
2368
{
 
2369
  SVGInfo
 
2370
    *svg_info;
 
2371
 
 
2372
  xmlParserCtxtPtr
 
2373
    parser;
 
2374
 
 
2375
  /*
 
2376
    Called when an entity reference is detected.
 
2377
  */
 
2378
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
2379
    "  SAX.reference(%.1024s)",name);
 
2380
  svg_info=(SVGInfo *) context;
 
2381
  parser=svg_info->parser;
 
2382
  if (*name == '#')
 
2383
    (void) xmlAddChild(parser->node,xmlNewCharRef(svg_info->document,name));
 
2384
  else
 
2385
    (void) xmlAddChild(parser->node,xmlNewReference(svg_info->document,name));
 
2386
}
 
2387
 
 
2388
static void SVGIgnorableWhitespace(void *context,const xmlChar *c,int length)
 
2389
{
 
2390
/*   SVGInfo */
 
2391
/*     *svg_info; */
 
2392
 
 
2393
  /*
 
2394
    Receiving some ignorable whitespaces from the parser.
 
2395
  */
 
2396
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
2397
    "  SAX.ignorableWhitespace(%.30s, %d)",c,length);
 
2398
/*   svg_info=(SVGInfo *) context; */
 
2399
}
 
2400
 
 
2401
static void SVGProcessingInstructions(void *context,const xmlChar *target,
 
2402
  const xmlChar *data)
 
2403
{
 
2404
/*   SVGInfo */
 
2405
/*     *svg_info; */
 
2406
 
 
2407
  /*
 
2408
    A processing instruction has been parsed.
 
2409
  */
 
2410
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
2411
    "  SAX.processingInstruction(%.1024s, %.1024s)",
 
2412
    target,data);
 
2413
/*   svg_info=(SVGInfo *) context; */
 
2414
}
 
2415
 
 
2416
static void SVGComment(void *context,const xmlChar *value)
 
2417
{
 
2418
  SVGInfo
 
2419
    *svg_info;
 
2420
 
 
2421
  /*
 
2422
    A comment has been parsed.
 
2423
  */
 
2424
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
2425
    "  SAX.comment(%.1024s)",value);
 
2426
  svg_info=(SVGInfo *) context;
 
2427
  if (svg_info->comment != (char *) NULL)
 
2428
    (void) ConcatenateString(&svg_info->comment,"\n");
 
2429
  (void) ConcatenateString(&svg_info->comment,(char *) value);
 
2430
}
 
2431
 
 
2432
static void SVGWarning(void *context,const char *format,...)
 
2433
{
 
2434
  char
 
2435
    reason[MaxTextExtent];
 
2436
 
 
2437
  SVGInfo
 
2438
    *svg_info;
 
2439
 
 
2440
  va_list
 
2441
    operands;
 
2442
 
 
2443
  /**
 
2444
    Display and format a warning messages, gives file, line, position and
 
2445
    extra parameters.
 
2446
  */
 
2447
  va_start(operands,format);
 
2448
  svg_info=(SVGInfo *) context;
 
2449
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.warning: ");
 
2450
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
 
2451
#if !defined(HAVE_VSNPRINTF)
 
2452
  (void) vsprintf(reason,format,operands);
 
2453
#else
 
2454
  (void) vsnprintf(reason,MaxTextExtent,format,operands);
 
2455
#endif
 
2456
  ThrowException2(svg_info->exception,DelegateWarning,reason,(char *) NULL);
 
2457
  va_end(operands);
 
2458
}
 
2459
 
 
2460
static void SVGError(void *context,const char *format,...)
 
2461
{
 
2462
  char
 
2463
    reason[MaxTextExtent];
 
2464
 
 
2465
  SVGInfo
 
2466
    *svg_info;
 
2467
 
 
2468
  va_list
 
2469
    operands;
 
2470
 
 
2471
  /*
 
2472
    Display and format a error formats, gives file, line, position and
 
2473
    extra parameters.
 
2474
  */
 
2475
  va_start(operands,format);
 
2476
  svg_info=(SVGInfo *) context;
 
2477
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  SAX.error: ");
 
2478
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),format,operands);
 
2479
#if !defined(HAVE_VSNPRINTF)
 
2480
  (void) vsprintf(reason,format,operands);
 
2481
#else
 
2482
  (void) vsnprintf(reason,MaxTextExtent,format,operands);
 
2483
#endif
 
2484
  ThrowException2(svg_info->exception,CoderError,reason,(char *) NULL);
 
2485
  va_end(operands);
 
2486
}
 
2487
 
 
2488
static void SVGCDataBlock(void *context,const xmlChar *value,int length)
 
2489
{
 
2490
  SVGInfo
 
2491
    *svg_info;
 
2492
 
 
2493
   xmlNodePtr
 
2494
     child;
 
2495
 
 
2496
  xmlParserCtxtPtr
 
2497
    parser;
 
2498
 
 
2499
  /*
 
2500
    Called when a pcdata block has been parsed.
 
2501
  */
 
2502
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
2503
    "  SAX.pcdata(%.1024s, %d)",value,length);
 
2504
  svg_info=(SVGInfo *) context;
 
2505
  parser=svg_info->parser;
 
2506
  child=xmlGetLastChild(parser->node);
 
2507
  if ((child != (xmlNodePtr) NULL) && (child->type == XML_CDATA_SECTION_NODE))
 
2508
    {
 
2509
      xmlTextConcat(child,value,length);
 
2510
      return;
 
2511
    }
 
2512
  (void) xmlAddChild(parser->node,xmlNewCDataBlock(parser->myDoc,value,length));
 
2513
}
 
2514
 
 
2515
static void SVGExternalSubset(void *context,const xmlChar *name,
 
2516
  const xmlChar *external_id,const xmlChar *system_id)
 
2517
{
 
2518
  SVGInfo
 
2519
    *svg_info;
 
2520
 
 
2521
  xmlParserCtxt
 
2522
    parser_context;
 
2523
 
 
2524
  xmlParserCtxtPtr
 
2525
    parser;
 
2526
 
 
2527
  xmlParserInputPtr
 
2528
    input;
 
2529
 
 
2530
  /*
 
2531
    Does this document has an external subset?
 
2532
  */
 
2533
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
 
2534
    "  SAX.externalSubset(%.1024s, %.1024s, %.1024s)",name,
 
2535
    (external_id != (const xmlChar *) NULL ? (char *) external_id : "none"),
 
2536
    (system_id != (const xmlChar *) NULL ? (char *) system_id : "none"));
 
2537
  svg_info=(SVGInfo *) context;
 
2538
  parser=svg_info->parser;
 
2539
  if (((external_id == NULL) && (system_id == NULL)) ||
 
2540
      (!parser->validate || !parser->wellFormed || !svg_info->document))
 
2541
    return;
 
2542
  input=SVGResolveEntity(context,external_id,system_id);
 
2543
  if (input == NULL)
 
2544
    return;
 
2545
  (void) xmlNewDtd(svg_info->document,name,external_id,system_id);
 
2546
  parser_context=(*parser);
 
2547
  parser->inputTab=(xmlParserInputPtr *) xmlMalloc(5*sizeof(*parser->inputTab));
 
2548
  if (parser->inputTab == (xmlParserInputPtr *) NULL)
 
2549
    {
 
2550
      parser->errNo=XML_ERR_NO_MEMORY;
 
2551
      parser->input=parser_context.input;
 
2552
      parser->inputNr=parser_context.inputNr;
 
2553
      parser->inputMax=parser_context.inputMax;
 
2554
      parser->inputTab=parser_context.inputTab;
 
2555
      return;
 
2556
  }
 
2557
  parser->inputNr=0;
 
2558
  parser->inputMax=5;
 
2559
  parser->input=NULL;
 
2560
  xmlPushInput(parser,input);
 
2561
  (void) xmlSwitchEncoding(parser,xmlDetectCharEncoding(parser->input->cur,4));
 
2562
  if (input->filename == (char *) NULL)
 
2563
    input->filename=(char *) xmlStrdup(system_id);
 
2564
  input->line=1;
 
2565
  input->col=1;
 
2566
  input->base=parser->input->cur;
 
2567
  input->cur=parser->input->cur;
 
2568
  input->free=NULL;
 
2569
  xmlParseExternalSubset(parser,external_id,system_id);
 
2570
  while (parser->inputNr > 1)
 
2571
    (void) xmlPopInput(parser);
 
2572
  xmlFreeInputStream(parser->input);
 
2573
  xmlFree(parser->inputTab);
 
2574
  parser->input=parser_context.input;
 
2575
  parser->inputNr=parser_context.inputNr;
 
2576
  parser->inputMax=parser_context.inputMax;
 
2577
  parser->inputTab=parser_context.inputTab;
 
2578
}
 
2579
 
 
2580
#if defined(__cplusplus) || defined(c_plusplus)
 
2581
}
 
2582
#endif
 
2583
 
 
2584
static Image *ReadSVGImage(const ImageInfo *image_info,ExceptionInfo *exception)
 
2585
{
 
2586
  xmlSAXHandler
 
2587
    SAXModules =
 
2588
    {
 
2589
      SVGInternalSubset,
 
2590
      SVGIsStandalone,
 
2591
      SVGHasInternalSubset,
 
2592
      SVGHasExternalSubset,
 
2593
      SVGResolveEntity,
 
2594
      SVGGetEntity,
 
2595
      SVGEntityDeclaration,
 
2596
      SVGNotationDeclaration,
 
2597
      SVGAttributeDeclaration,
 
2598
      SVGElementDeclaration,
 
2599
      SVGUnparsedEntityDeclaration,
 
2600
      SVGSetDocumentLocator,
 
2601
      SVGStartDocument,
 
2602
      SVGEndDocument,
 
2603
      SVGStartElement,
 
2604
      SVGEndElement,
 
2605
      SVGReference,
 
2606
      SVGCharacters,
 
2607
      SVGIgnorableWhitespace,
 
2608
      SVGProcessingInstructions,
 
2609
      SVGComment,
 
2610
      SVGWarning,
 
2611
      SVGError,
 
2612
      SVGError,
 
2613
      SVGGetParameterEntity,
 
2614
      SVGCDataBlock,
 
2615
      SVGExternalSubset
 
2616
    };
 
2617
 
 
2618
  char
 
2619
    filename[MaxTextExtent],
 
2620
    geometry[MaxTextExtent],
 
2621
    message[MaxTextExtent];
 
2622
 
 
2623
  FILE
 
2624
    *file;
 
2625
 
 
2626
  Image
 
2627
    *image;
 
2628
 
 
2629
  size_t
 
2630
    n;
 
2631
 
 
2632
  SVGInfo
 
2633
    svg_info;
 
2634
 
 
2635
  unsigned int
 
2636
    status;
 
2637
 
 
2638
  xmlSAXHandlerPtr
 
2639
    SAXHandler;
 
2640
 
 
2641
  /*
 
2642
    Open image file.
 
2643
  */
 
2644
  assert(image_info != (const ImageInfo *) NULL);
 
2645
  assert(image_info->signature == MagickSignature);
 
2646
  assert(exception != (ExceptionInfo *) NULL);
 
2647
  assert(exception->signature == MagickSignature);
 
2648
  image=AllocateImage(image_info);
 
2649
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
 
2650
  if (status == False)
 
2651
    ThrowReaderException(FileOpenError,UnableToOpenFile,image);
 
2652
  /*
 
2653
    Open draw file.
 
2654
  */
 
2655
  file=AcquireTemporaryFileStream(filename,TextFileIOMode);
 
2656
  if (file == (FILE *) NULL)
 
2657
    ThrowReaderTemporaryFileException(filename);
 
2658
  /*
 
2659
    Parse SVG file.
 
2660
  */
 
2661
  (void) memset(&svg_info,0,sizeof(SVGInfo));
 
2662
  svg_info.file=file;
 
2663
  svg_info.exception=exception;
 
2664
  svg_info.image=image;
 
2665
  svg_info.image_info=image_info;
 
2666
  svg_info.text=AllocateString("");
 
2667
  svg_info.scale=MagickAllocateMemory(double *,sizeof(double));
 
2668
  if (svg_info.scale == (double *) NULL)
 
2669
    {
 
2670
      (void) fclose(file);
 
2671
      LiberateTemporaryFile(filename);
 
2672
      ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
 
2673
    }
 
2674
  IdentityAffine(&svg_info.affine);
 
2675
  svg_info.affine.sx=
 
2676
    image->x_resolution == 0.0 ? 1.0 : image->x_resolution/72.0;
 
2677
  svg_info.affine.sy=
 
2678
    image->y_resolution == 0.0 ? 1.0 : image->y_resolution/72.0;
 
2679
  svg_info.scale[0]=ExpandAffine(&svg_info.affine);
 
2680
  svg_info.bounds.width=image->columns;
 
2681
  svg_info.bounds.height=image->rows;
 
2682
  if (image_info->size != (char *) NULL)
 
2683
    (void) CloneString(&svg_info.size,image_info->size);
 
2684
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"begin SAX");
 
2685
  (void) xmlSubstituteEntitiesDefault(1);
 
2686
  SAXHandler=(&SAXModules);
 
2687
  svg_info.parser=xmlCreatePushParserCtxt(SAXHandler,&svg_info,(char *) NULL,0,
 
2688
    image->filename);
 
2689
  while ((n=ReadBlob(image,MaxTextExtent,message)) != 0)
 
2690
  {
 
2691
    status=xmlParseChunk(svg_info.parser,message,(int) n,False);
 
2692
    if (status != 0)
 
2693
      break;
 
2694
  }
 
2695
  (void) xmlParseChunk(svg_info.parser,message,0,True);
 
2696
  xmlFreeParserCtxt(svg_info.parser);
 
2697
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"end SAX");
 
2698
  xmlCleanupParser();
 
2699
  (void) fclose(file);
 
2700
  CloseBlob(image);
 
2701
  DestroyImage(image);
 
2702
  image=(Image *) NULL;
 
2703
  if (!image_info->ping && (exception->severity == UndefinedException))
 
2704
    {
 
2705
      ImageInfo
 
2706
        *clone_info;
 
2707
 
 
2708
      /*
 
2709
        Draw image.
 
2710
      */
 
2711
      clone_info=CloneImageInfo(image_info);
 
2712
      clone_info->blob=(void *) NULL;
 
2713
      clone_info->length=0;
 
2714
      FormatString(geometry,"%ldx%ld",svg_info.width,svg_info.height);
 
2715
      (void) CloneString(&clone_info->size,geometry);
 
2716
      FormatString(clone_info->filename,"mvg:%.1024s",filename);
 
2717
      if (clone_info->density != (char *) NULL)
 
2718
        MagickFreeMemory(clone_info->density);
 
2719
      image=ReadImage(clone_info,exception);
 
2720
      DestroyImageInfo(clone_info);
 
2721
      if (image != (Image *) NULL)
 
2722
        (void) strncpy(image->filename,image_info->filename,MaxTextExtent-1);
 
2723
    }
 
2724
  /*
 
2725
    Free resources.
 
2726
  */
 
2727
  if (svg_info.title != (char *) NULL)
 
2728
    {
 
2729
      if (image != (Image *) NULL)
 
2730
       (void) SetImageAttribute(image,"title",svg_info.title);
 
2731
      MagickFreeMemory(svg_info.title);
 
2732
    }
 
2733
  if (svg_info.comment != (char *) NULL)
 
2734
    {
 
2735
      if (image != (Image *) NULL)
 
2736
        (void) SetImageAttribute(image,"comment",svg_info.comment);
 
2737
      MagickFreeMemory(svg_info.comment);
 
2738
    }
 
2739
  LiberateTemporaryFile(filename);
 
2740
  return(image);
 
2741
}
 
2742
#endif
 
2743
 
 
2744
/*
 
2745
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2746
%                                                                             %
 
2747
%                                                                             %
 
2748
%                                                                             %
 
2749
%   R e g i s t e r S V G I m a g e                                           %
 
2750
%                                                                             %
 
2751
%                                                                             %
 
2752
%                                                                             %
 
2753
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2754
%
 
2755
%  Method RegisterSVGImage adds attributes for the SVG image format to
 
2756
%  the list of supported formats.  The attributes include the image format
 
2757
%  tag, a method to read and/or write the format, whether the format
 
2758
%  supports the saving of more than one frame to the same file or blob,
 
2759
%  whether the format supports native in-memory I/O, and a brief
 
2760
%  description of the format.
 
2761
%
 
2762
%  The format of the RegisterSVGImage method is:
 
2763
%
 
2764
%      RegisterSVGImage(void)
 
2765
%
 
2766
*/
 
2767
ModuleExport void RegisterSVGImage(void)
 
2768
{
 
2769
  char
 
2770
    version[MaxTextExtent];
 
2771
 
 
2772
  MagickInfo
 
2773
    *entry;
 
2774
 
 
2775
  *version='\0';
 
2776
#if defined(LIBXML_DOTTED_VERSION)
 
2777
  (void) strncpy(version,"XML " LIBXML_DOTTED_VERSION,MaxTextExtent-1);
 
2778
#endif
 
2779
  entry=SetMagickInfo("SVG");
 
2780
#if defined(HasXML)
 
2781
  entry->decoder=(DecoderHandler) ReadSVGImage;
 
2782
#endif
 
2783
  entry->encoder=(EncoderHandler) WriteSVGImage;
 
2784
  entry->description=AcquireString("Scalable Vector Gaphics");
 
2785
  if (*version != '\0')
 
2786
    entry->version=AcquireString(version);
 
2787
  entry->module=AcquireString("SVG");
 
2788
  (void) RegisterMagickInfo(entry);
 
2789
 
 
2790
  entry=SetMagickInfo("SVGZ");
 
2791
#if defined(HasXML)
 
2792
  entry->decoder=(DecoderHandler) ReadSVGImage;
 
2793
#endif
 
2794
  entry->encoder=(EncoderHandler) WriteSVGImage;
 
2795
  entry->description=AcquireString("Scalable Vector Gaphics (ZIP compressed)");
 
2796
  if (*version != '\0')
 
2797
    entry->version=AcquireString(version);
 
2798
  entry->module=AcquireString("SVG");
 
2799
  (void) RegisterMagickInfo(entry);
 
2800
}
 
2801
 
 
2802
/*
 
2803
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2804
%                                                                             %
 
2805
%                                                                             %
 
2806
%                                                                             %
 
2807
%   U n r e g i s t e r S V G I m a g e                                       %
 
2808
%                                                                             %
 
2809
%                                                                             %
 
2810
%                                                                             %
 
2811
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2812
%
 
2813
%  Method UnregisterSVGImage removes format registrations made by the
 
2814
%  SVG module from the list of supported formats.
 
2815
%
 
2816
%  The format of the UnregisterSVGImage method is:
 
2817
%
 
2818
%      UnregisterSVGImage(void)
 
2819
%
 
2820
*/
 
2821
ModuleExport void UnregisterSVGImage(void)
 
2822
{
 
2823
  (void) UnregisterMagickInfo("SVG");
 
2824
  (void) UnregisterMagickInfo("SVGZ");
 
2825
}
 
2826
 
 
2827
/*
 
2828
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2829
%                                                                             %
 
2830
%                                                                             %
 
2831
%                                                                             %
 
2832
%   W r i t e S V G I m a g e                                                 %
 
2833
%                                                                             %
 
2834
%                                                                             %
 
2835
%                                                                             %
 
2836
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2837
%
 
2838
%  Method WriteSVGImage writes a image in the SVG - XML based W3C standard
 
2839
%  format.
 
2840
%
 
2841
%  The format of the WriteSVGImage method is:
 
2842
%
 
2843
%      unsigned int WriteSVGImage(const ImageInfo *image_info,Image *image)
 
2844
%
 
2845
%  A description of each parameter follows.
 
2846
%
 
2847
%    o status: Method WriteSVGImage return True if the image is written.
 
2848
%      False is returned is there is a memory shortage or if the image file
 
2849
%      fails to write.
 
2850
%
 
2851
%    o image_info: Specifies a pointer to a ImageInfo structure.
 
2852
%
 
2853
%    o image:  A pointer to an Image structure.
 
2854
%
 
2855
%
 
2856
*/
 
2857
 
 
2858
#if defined(HasAUTOTRACE)
 
2859
static unsigned int WriteSVGImage(const ImageInfo *image_info,Image *image)
 
2860
{
 
2861
  FILE
 
2862
    *output_file;
 
2863
 
 
2864
  fitting_opts_type
 
2865
    fit_info;
 
2866
 
 
2867
  image_header_type
 
2868
    image_header;
 
2869
 
 
2870
  bitmap_type
 
2871
    bitmap;
 
2872
 
 
2873
  ImageType
 
2874
    image_type;
 
2875
 
 
2876
  int
 
2877
    j;
 
2878
 
 
2879
  pixel_outline_list_type
 
2880
    pixels;
 
2881
 
 
2882
  PixelPacket
 
2883
    p;
 
2884
 
 
2885
  PixelPacket
 
2886
    *pixel;
 
2887
 
 
2888
  QuantizeObj
 
2889
    *quantize_info;
 
2890
 
 
2891
  spline_list_array_type
 
2892
    splines;
 
2893
 
 
2894
  output_write
 
2895
    output_writer;
 
2896
 
 
2897
  register long
 
2898
    i;
 
2899
 
 
2900
  unsigned long
 
2901
    number_pixels,
 
2902
    number_planes,
 
2903
    point,
 
2904
    thin;
 
2905
 
 
2906
  thin=False;
 
2907
  quantize_info=(QuantizeObj *) NULL;
 
2908
  pixel=(&p);
 
2909
  fit_info=new_fitting_opts();
 
2910
  output_writer=output_get_handler("svg");
 
2911
  if (output_writer == NULL)
 
2912
    ThrowWriterException(DelegateError,UnableToWriteSVGFormat,image);
 
2913
  image_type=GetImageType(image);
 
2914
  number_planes=3;
 
2915
  if ((image_type == BilevelType) || (image_type == GrayscaleType))
 
2916
    number_planes=1;
 
2917
  bitmap.np=number_planes;
 
2918
  bitmap.dimensions.width=image->columns;
 
2919
  bitmap.dimensions.height=image->rows;
 
2920
  number_pixels=image->columns*image->rows;
 
2921
  bitmap.bitmap=MagickAllocateMemory(unsigned char *,number_planes*number_pixels);
 
2922
  if (bitmap.bitmap == (unsigned char *) NULL)
 
2923
    ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);
 
2924
  point=0;
 
2925
  for (j=0; j < image->rows; j++)
 
2926
  {
 
2927
    for (i=0; i < image->columns; i++)
 
2928
    {
 
2929
      p=GetOnePixel(image,i,j);
 
2930
      bitmap.bitmap[point++]=pixel->red;
 
2931
      if (number_planes == 3)
 
2932
        {
 
2933
          bitmap.bitmap[point++]=pixel->green;
 
2934
          bitmap.bitmap[point++]=pixel->blue;
 
2935
        }
 
2936
    }
 
2937
  }
 
2938
  image_header.width=DIMENSIONS_WIDTH(bitmap.dimensions);
 
2939
  image_header.height=DIMENSIONS_HEIGHT(bitmap.dimensions);
 
2940
  if ((fit_info.color_count > 0) && (BITMAP_PLANES(bitmap) == 3))
 
2941
    quantize(bitmap.bitmap,bitmap.bitmap,DIMENSIONS_WIDTH(bitmap.dimensions),
 
2942
      DIMENSIONS_HEIGHT(bitmap.dimensions),fit_info.color_count,
 
2943
      fit_info.bgColor,&quantize_info);
 
2944
  if (thin)
 
2945
    thin_image(&bitmap);
 
2946
  pixels=find_outline_pixels (bitmap);
 
2947
  MagickFreeMemory((bitmap.bitmap));
 
2948
  splines=fitted_splines(pixels,&fit_info);
 
2949
  output_file=fopen(image->filename,"w");
 
2950
  if (output_file == (FILE *) NULL)
 
2951
    ThrowWriterException(FileOpenError,UnableOpenFile,image);
 
2952
  output_writer(output_file,image->filename,0,0,image_header.width,
 
2953
    image_header.height,splines);
 
2954
  return(True);
 
2955
}
 
2956
#else
 
2957
static void AffineToTransform(Image *image,AffineMatrix *affine)
 
2958
{
 
2959
  char
 
2960
    transform[MaxTextExtent];
 
2961
 
 
2962
  if ((fabs(affine->tx) < MagickEpsilon) && (fabs(affine->ty) < MagickEpsilon))
 
2963
    {
 
2964
      if ((fabs(affine->rx) < MagickEpsilon) &&
 
2965
          (fabs(affine->ry) < MagickEpsilon))
 
2966
        {
 
2967
          if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
 
2968
              (fabs(affine->sy-1.0) < MagickEpsilon))
 
2969
            {
 
2970
              (void) WriteBlobString(image,"\">\n");
 
2971
              return;
 
2972
            }
 
2973
          FormatString(transform,"\" transform=\"scale(%g,%g)\">\n",
 
2974
            affine->sx,affine->sy);
 
2975
          (void) WriteBlobString(image,transform);
 
2976
          return;
 
2977
        }
 
2978
      else
 
2979
        {
 
2980
          if ((fabs(affine->sx-affine->sy) < MagickEpsilon) &&
 
2981
              (fabs(affine->rx+affine->ry) < MagickEpsilon) &&
 
2982
              (fabs(affine->sx*affine->sx+affine->rx*affine->rx-1.0) <
 
2983
               2*MagickEpsilon))
 
2984
            {
 
2985
              double
 
2986
                theta;
 
2987
 
 
2988
              theta=(180.0/MagickPI)*atan2(affine->rx,affine->sx);
 
2989
              FormatString(transform,"\" transform=\"rotate(%g)\">\n",theta);
 
2990
              (void) WriteBlobString(image,transform);
 
2991
              return;
 
2992
            }
 
2993
        }
 
2994
    }
 
2995
  else
 
2996
    {
 
2997
      if ((fabs(affine->sx-1.0) < MagickEpsilon) &&
 
2998
          (fabs(affine->rx) < MagickEpsilon) &&
 
2999
          (fabs(affine->ry) < MagickEpsilon) &&
 
3000
          (fabs(affine->sy-1.0) < MagickEpsilon))
 
3001
        {
 
3002
          FormatString(transform,"\" transform=\"translate(%g,%g)\">\n",
 
3003
            affine->tx,affine->ty);
 
3004
          (void) WriteBlobString(image,transform);
 
3005
          return;
 
3006
        }
 
3007
    }
 
3008
  FormatString(transform,"\" transform=\"matrix(%g %g %g %g %g %g)\">\n",
 
3009
    affine->sx,affine->rx,affine->ry,affine->sy,affine->tx,affine->ty);
 
3010
  (void) WriteBlobString(image,transform);
 
3011
}
 
3012
 
 
3013
static inline unsigned int IsPoint(const char *point)
 
3014
{
 
3015
  char
 
3016
    *p;
 
3017
 
 
3018
  (void) strtol(point,&p,10);
 
3019
  return(p != point);
 
3020
}
 
3021
 
 
3022
static unsigned int WriteSVGImage(const ImageInfo *image_info,Image *image)
 
3023
{
 
3024
#define BezierQuantum  200
 
3025
 
 
3026
  AffineMatrix
 
3027
    affine;
 
3028
 
 
3029
  char
 
3030
    keyword[MaxTextExtent],
 
3031
    message[MaxTextExtent],
 
3032
    name[MaxTextExtent],
 
3033
    *p,
 
3034
    *q,
 
3035
    *token,
 
3036
    type[MaxTextExtent];
 
3037
 
 
3038
  const ImageAttribute
 
3039
    *attribute;
 
3040
 
 
3041
  int
 
3042
    n;
 
3043
 
 
3044
  long
 
3045
    j;
 
3046
 
 
3047
  PointInfo
 
3048
    point;
 
3049
 
 
3050
  PrimitiveInfo
 
3051
    *primitive_info;
 
3052
 
 
3053
  PrimitiveType
 
3054
    primitive_type;
 
3055
 
 
3056
  register long
 
3057
    x;
 
3058
 
 
3059
  register long
 
3060
    i;
 
3061
 
 
3062
  size_t
 
3063
    length;
 
3064
 
 
3065
  SVGInfo
 
3066
    svg_info;
 
3067
 
 
3068
  unsigned int
 
3069
    active,
 
3070
    status;
 
3071
 
 
3072
  unsigned long
 
3073
    number_points;
 
3074
 
 
3075
  /*
 
3076
    Open output image file.
 
3077
  */
 
3078
  assert(image_info != (const ImageInfo *) NULL);
 
3079
  assert(image_info->signature == MagickSignature);
 
3080
  assert(image != (Image *) NULL);
 
3081
  assert(image->signature == MagickSignature);
 
3082
  attribute=GetImageAttribute(image,"[MVG]");
 
3083
  if ((attribute == (ImageAttribute *) NULL) ||
 
3084
      (attribute->value == (char *) NULL))
 
3085
    ThrowWriterException(CoderError,NoImageVectorGraphics,image);
 
3086
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
 
3087
  if (status == False)
 
3088
    ThrowWriterException(FileOpenError,UnableToOpenFile,image);
 
3089
  /*
 
3090
    Write SVG header.
 
3091
  */
 
3092
  (void) WriteBlobString(image,"<?xml version=\"1.0\" standalone=\"no\"?>\n");
 
3093
  (void) WriteBlobString(image,
 
3094
    "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n");
 
3095
  (void) WriteBlobString(image,
 
3096
    "  \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
 
3097
  (void) FormatString(message,"<svg width=\"%lu\" height=\"%lu\">\n",
 
3098
    image->columns,image->rows);
 
3099
  (void) WriteBlobString(image,message);
 
3100
  /*
 
3101
    Allocate primitive info memory.
 
3102
  */
 
3103
  number_points=2047;
 
3104
  primitive_info=MagickAllocateMemory(PrimitiveInfo *,
 
3105
    number_points*sizeof(PrimitiveInfo));
 
3106
  if (primitive_info == (PrimitiveInfo *) NULL)
 
3107
    ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);
 
3108
  IdentityAffine(&affine);
 
3109
  token=AllocateString(attribute->value);
 
3110
  active=False;
 
3111
  n=0;
 
3112
  status=True;
 
3113
  for (q=attribute->value; *q != '\0'; )
 
3114
  {
 
3115
    /*
 
3116
      Interpret graphic primitive.
 
3117
    */
 
3118
    GetToken(q,&q,keyword);
 
3119
    if (*keyword == '\0')
 
3120
      break;
 
3121
    if (*keyword == '#')
 
3122
      {
 
3123
        /*
 
3124
          Comment.
 
3125
        */
 
3126
        if (active)
 
3127
          {
 
3128
            AffineToTransform(image,&affine);
 
3129
            active=False;
 
3130
          }
 
3131
        (void) WriteBlobString(image,"<desc>");
 
3132
        (void) WriteBlobString(image,keyword+1);
 
3133
        for ( ; (*q != '\n') && (*q != '\0'); q++)
 
3134
          switch (*q)
 
3135
          {
 
3136
            case '<': (void) WriteBlobString(image,"&lt;"); break;
 
3137
            case '>': (void) WriteBlobString(image,"&gt;"); break;
 
3138
            case '&': (void) WriteBlobString(image,"&amp;"); break;
 
3139
            default: (void) WriteBlobByte(image,*q); break;
 
3140
          }
 
3141
        (void) WriteBlobString(image,"</desc>\n");
 
3142
        continue;
 
3143
      }
 
3144
    primitive_type=UndefinedPrimitive;
 
3145
    switch (*keyword)
 
3146
    {
 
3147
      case ';':
 
3148
        break;
 
3149
      case 'a':
 
3150
      case 'A':
 
3151
      {
 
3152
        if (LocaleCompare("affine",keyword) == 0)
 
3153
          {
 
3154
            GetToken(q,&q,token);
 
3155
            affine.sx=atof(token);
 
3156
            GetToken(q,&q,token);
 
3157
            if (*token == ',')
 
3158
              GetToken(q,&q,token);
 
3159
            affine.rx=atof(token);
 
3160
            GetToken(q,&q,token);
 
3161
            if (*token == ',')
 
3162
              GetToken(q,&q,token);
 
3163
            affine.ry=atof(token);
 
3164
            GetToken(q,&q,token);
 
3165
            if (*token == ',')
 
3166
              GetToken(q,&q,token);
 
3167
            affine.sy=atof(token);
 
3168
            GetToken(q,&q,token);
 
3169
            if (*token == ',')
 
3170
              GetToken(q,&q,token);
 
3171
            affine.tx=atof(token);
 
3172
            GetToken(q,&q,token);
 
3173
            if (*token == ',')
 
3174
              GetToken(q,&q,token);
 
3175
            affine.ty=atof(token);
 
3176
            break;
 
3177
          }
 
3178
        if (LocaleCompare("angle",keyword) == 0)
 
3179
          {
 
3180
            GetToken(q,&q,token);
 
3181
            affine.rx=atof(token);
 
3182
            affine.ry=atof(token);
 
3183
            break;
 
3184
          }
 
3185
        if (LocaleCompare("arc",keyword) == 0)
 
3186
          {
 
3187
            primitive_type=ArcPrimitive;
 
3188
            break;
 
3189
          }
 
3190
        status=False;
 
3191
        break;
 
3192
      }
 
3193
      case 'b':
 
3194
      case 'B':
 
3195
      {
 
3196
        if (LocaleCompare("bezier",keyword) == 0)
 
3197
          {
 
3198
            primitive_type=BezierPrimitive;
 
3199
            break;
 
3200
          }
 
3201
        status=False;
 
3202
        break;
 
3203
      }
 
3204
      case 'c':
 
3205
      case 'C':
 
3206
      {
 
3207
        if (LocaleCompare("clip-path",keyword) == 0)
 
3208
          {
 
3209
            GetToken(q,&q,token);
 
3210
            FormatString(message,"clip-path:url(#%.1024s);",token);
 
3211
            (void) WriteBlobString(image,message);
 
3212
            break;
 
3213
          }
 
3214
        if (LocaleCompare("clip-rule",keyword) == 0)
 
3215
          {
 
3216
            GetToken(q,&q,token);
 
3217
            FormatString(message,"clip-rule:%.1024s;",token);
 
3218
            (void) WriteBlobString(image,message);
 
3219
            break;
 
3220
          }
 
3221
        if (LocaleCompare("clip-units",keyword) == 0)
 
3222
          {
 
3223
            GetToken(q,&q,token);
 
3224
            FormatString(message,"clipPathUnits=%.1024s;",token);
 
3225
            (void) WriteBlobString(image,message);
 
3226
            break;
 
3227
          }
 
3228
        if (LocaleCompare("circle",keyword) == 0)
 
3229
          {
 
3230
            primitive_type=CirclePrimitive;
 
3231
            break;
 
3232
          }
 
3233
        if (LocaleCompare("color",keyword) == 0)
 
3234
          {
 
3235
            primitive_type=ColorPrimitive;
 
3236
            break;
 
3237
          }
 
3238
        status=False;
 
3239
        break;
 
3240
      }
 
3241
      case 'd':
 
3242
      case 'D':
 
3243
      {
 
3244
        if (LocaleCompare("decorate",keyword) == 0)
 
3245
          {
 
3246
            GetToken(q,&q,token);
 
3247
            FormatString(message,"text-decoration:%.1024s;",token);
 
3248
            (void) WriteBlobString(image,message);
 
3249
            break;
 
3250
          }
 
3251
        status=False;
 
3252
        break;
 
3253
      }
 
3254
      case 'e':
 
3255
      case 'E':
 
3256
      {
 
3257
        if (LocaleCompare("ellipse",keyword) == 0)
 
3258
          {
 
3259
            primitive_type=EllipsePrimitive;
 
3260
            break;
 
3261
          }
 
3262
        status=False;
 
3263
        break;
 
3264
      }
 
3265
      case 'f':
 
3266
      case 'F':
 
3267
      {
 
3268
        if (LocaleCompare("fill",keyword) == 0)
 
3269
          {
 
3270
            GetToken(q,&q,token);
 
3271
            FormatString(message,"fill:%.1024s;",token);
 
3272
            (void) WriteBlobString(image,message);
 
3273
            break;
 
3274
          }
 
3275
        if (LocaleCompare("fill-rule",keyword) == 0)
 
3276
          {
 
3277
            GetToken(q,&q,token);
 
3278
            FormatString(message,"fill-rule:%.1024s;",token);
 
3279
            (void) WriteBlobString(image,message);
 
3280
            break;
 
3281
          }
 
3282
        if (LocaleCompare("fill-opacity",keyword) == 0)
 
3283
          {
 
3284
            GetToken(q,&q,token);
 
3285
            FormatString(message,"fill-opacity:%.1024s;",token);
 
3286
            (void) WriteBlobString(image,message);
 
3287
            break;
 
3288
          }
 
3289
        if (LocaleCompare("font-family",keyword) == 0)
 
3290
          {
 
3291
            GetToken(q,&q,token);
 
3292
            FormatString(message,"font-family:%.1024s;",token);
 
3293
            (void) WriteBlobString(image,message);
 
3294
            break;
 
3295
          }
 
3296
        if (LocaleCompare("font-stretch",keyword) == 0)
 
3297
          {
 
3298
            GetToken(q,&q,token);
 
3299
            FormatString(message,"font-stretch:%.1024s;",token);
 
3300
            (void) WriteBlobString(image,message);
 
3301
            break;
 
3302
          }
 
3303
        if (LocaleCompare("font-style",keyword) == 0)
 
3304
          {
 
3305
            GetToken(q,&q,token);
 
3306
            FormatString(message,"font-style:%.1024s;",token);
 
3307
            (void) WriteBlobString(image,message);
 
3308
            break;
 
3309
          }
 
3310
        if (LocaleCompare("font-size",keyword) == 0)
 
3311
          {
 
3312
            GetToken(q,&q,token);
 
3313
            FormatString(message,"font-size:%.1024s;",token);
 
3314
            (void) WriteBlobString(image,message);
 
3315
            break;
 
3316
          }
 
3317
        if (LocaleCompare("font-weight",keyword) == 0)
 
3318
          {
 
3319
            GetToken(q,&q,token);
 
3320
            FormatString(message,"font-weight:%.1024s;",token);
 
3321
            (void) WriteBlobString(image,message);
 
3322
            break;
 
3323
          }
 
3324
        status=False;
 
3325
        break;
 
3326
      }
 
3327
      case 'g':
 
3328
      case 'G':
 
3329
      {
 
3330
        if (LocaleCompare("gradient-units",keyword) == 0)
 
3331
          {
 
3332
            GetToken(q,&q,token);
 
3333
            break;
 
3334
          }
 
3335
        if (LocaleCompare("text-align",keyword) == 0)
 
3336
          {
 
3337
            GetToken(q,&q,token);
 
3338
            FormatString(message,"text-align %.1024s ",token);
 
3339
            (void) WriteBlobString(image,message);
 
3340
            break;
 
3341
          }
 
3342
        if (LocaleCompare("text-anchor",keyword) == 0)
 
3343
          {
 
3344
            GetToken(q,&q,token);
 
3345
            FormatString(message,"text-anchor %.1024s ",token);
 
3346
            (void) WriteBlobString(image,message);
 
3347
            break;
 
3348
          }
 
3349
        status=False;
 
3350
        break;
 
3351
      }
 
3352
      case 'i':
 
3353
      case 'I':
 
3354
      {
 
3355
        if (LocaleCompare("image",keyword) == 0)
 
3356
          {
 
3357
            GetToken(q,&q,token);
 
3358
            primitive_type=ImagePrimitive;
 
3359
            break;
 
3360
          }
 
3361
        status=False;
 
3362
        break;
 
3363
      }
 
3364
      case 'l':
 
3365
      case 'L':
 
3366
      {
 
3367
        if (LocaleCompare("line",keyword) == 0)
 
3368
          {
 
3369
            primitive_type=LinePrimitive;
 
3370
            break;
 
3371
          }
 
3372
        status=False;
 
3373
        break;
 
3374
      }
 
3375
      case 'm':
 
3376
      case 'M':
 
3377
      {
 
3378
        if (LocaleCompare("matte",keyword) == 0)
 
3379
          {
 
3380
            primitive_type=MattePrimitive;
 
3381
            break;
 
3382
          }
 
3383
        status=False;
 
3384
        break;
 
3385
      }
 
3386
      case 'o':
 
3387
      case 'O':
 
3388
      {
 
3389
        if (LocaleCompare("opacity",keyword) == 0)
 
3390
          {
 
3391
            GetToken(q,&q,token);
 
3392
            FormatString(message,"opacity %.1024s ",token);
 
3393
            (void) WriteBlobString(image,message);
 
3394
            break;
 
3395
          }
 
3396
        status=False;
 
3397
        break;
 
3398
      }
 
3399
      case 'p':
 
3400
      case 'P':
 
3401
      {
 
3402
        if (LocaleCompare("path",keyword) == 0)
 
3403
          {
 
3404
            primitive_type=PathPrimitive;
 
3405
            break;
 
3406
          }
 
3407
        if (LocaleCompare("point",keyword) == 0)
 
3408
          {
 
3409
            primitive_type=PointPrimitive;
 
3410
            break;
 
3411
          }
 
3412
        if (LocaleCompare("polyline",keyword) == 0)
 
3413
          {
 
3414
            primitive_type=PolylinePrimitive;
 
3415
            break;
 
3416
          }
 
3417
        if (LocaleCompare("polygon",keyword) == 0)
 
3418
          {
 
3419
            primitive_type=PolygonPrimitive;
 
3420
            break;
 
3421
          }
 
3422
        if (LocaleCompare("pop",keyword) == 0)
 
3423
          {
 
3424
            GetToken(q,&q,token);
 
3425
            if (LocaleCompare("clip-path",token) == 0)
 
3426
              {
 
3427
                (void) WriteBlobString(image,"</clipPath>\n");
 
3428
                break;
 
3429
              }
 
3430
            if (LocaleCompare("defs",token) == 0)
 
3431
              {
 
3432
                (void) WriteBlobString(image,"</defs>\n");
 
3433
                break;
 
3434
              }
 
3435
            if (LocaleCompare("gradient",token) == 0)
 
3436
              {
 
3437
                FormatString(message,"</%sGradient>\n",type);
 
3438
                (void) WriteBlobString(image,message);
 
3439
                break;
 
3440
              }
 
3441
            if (LocaleCompare("graphic-context",token) == 0)
 
3442
              {
 
3443
                n--;
 
3444
                if (n < 0)
 
3445
                  ThrowWriterException(DrawError,
 
3446
                    UnbalancedGraphicContextPushPop,image);
 
3447
                (void) WriteBlobString(image,"</g>\n");
 
3448
              }
 
3449
            if (LocaleCompare("pattern",token) == 0)
 
3450
              {
 
3451
                (void) WriteBlobString(image,"</pattern>\n");
 
3452
                break;
 
3453
              }
 
3454
            if (LocaleCompare("defs",token) == 0)
 
3455
            (void) WriteBlobString(image,"</g>\n");
 
3456
            break;
 
3457
          }
 
3458
        if (LocaleCompare("push",keyword) == 0)
 
3459
          {
 
3460
            GetToken(q,&q,token);
 
3461
            if (LocaleCompare("clip-path",token) == 0)
 
3462
              {
 
3463
                GetToken(q,&q,token);
 
3464
                FormatString(message,"<clipPath id=\"%s\">\n",token);
 
3465
                (void) WriteBlobString(image,message);
 
3466
                break;
 
3467
              }
 
3468
            if (LocaleCompare("defs",token) == 0)
 
3469
              {
 
3470
                (void) WriteBlobString(image,"<defs>\n");
 
3471
                break;
 
3472
              }
 
3473
            if (LocaleCompare("gradient",token) == 0)
 
3474
              {
 
3475
                GetToken(q,&q,token);
 
3476
                (void) strncpy(name,token,MaxTextExtent-1);
 
3477
                GetToken(q,&q,token);
 
3478
                (void) strncpy(type,token,MaxTextExtent-1);
 
3479
                GetToken(q,&q,token);
 
3480
                svg_info.segment.x1=atof(token);
 
3481
                svg_info.element.cx=atof(token);
 
3482
                GetToken(q,&q,token);
 
3483
                if (*token == ',')
 
3484
                  GetToken(q,&q,token);
 
3485
                svg_info.segment.y1=atof(token);
 
3486
                svg_info.element.cy=atof(token);
 
3487
                GetToken(q,&q,token);
 
3488
                if (*token == ',')
 
3489
                  GetToken(q,&q,token);
 
3490
                svg_info.segment.x2=atof(token);
 
3491
                svg_info.element.major=atof(token);
 
3492
                GetToken(q,&q,token);
 
3493
                if (*token == ',')
 
3494
                  GetToken(q,&q,token);
 
3495
                svg_info.segment.y2=atof(token);
 
3496
                svg_info.element.minor=atof(token);
 
3497
                FormatString(message,"<%sGradient id=\"%s\" x1=\"%g\" "
 
3498
                  "y1=\"%g\" x2=\"%g\" y2=\"%g\">\n",type,name,
 
3499
                  svg_info.segment.x1,svg_info.segment.y1,svg_info.segment.x2,
 
3500
                  svg_info.segment.y2);
 
3501
                if (LocaleCompare(type,"radial") == 0)
 
3502
                  {
 
3503
                    GetToken(q,&q,token);
 
3504
                    if (*token == ',')
 
3505
                      GetToken(q,&q,token);
 
3506
                    svg_info.element.angle=atof(token);
 
3507
                    FormatString(message,"<%sGradient id=\"%s\" cx=\"%g\" "
 
3508
                      "cy=\"%g\" r=\"%g\" fx=\"%g\" fy=\"%g\">\n",type,name,
 
3509
                      svg_info.element.cx,svg_info.element.cy,
 
3510
                      svg_info.element.angle,svg_info.element.major,
 
3511
                      svg_info.element.minor);
 
3512
                  }
 
3513
                (void) WriteBlobString(image,message);
 
3514
                break;
 
3515
              }
 
3516
            if (LocaleCompare("graphic-context",token) == 0)
 
3517
              {
 
3518
                n++;
 
3519
                if (active)
 
3520
                  {
 
3521
                    AffineToTransform(image,&affine);
 
3522
                    active=False;
 
3523
                  }
 
3524
                (void) WriteBlobString(image,"<g style=\"");
 
3525
                active=True;
 
3526
              }
 
3527
            if (LocaleCompare("pattern",token) == 0)
 
3528
              {
 
3529
                GetToken(q,&q,token);
 
3530
                (void) strncpy(name,token,MaxTextExtent-1);
 
3531
                GetToken(q,&q,token);
 
3532
                svg_info.bounds.x=atof(token);
 
3533
                GetToken(q,&q,token);
 
3534
                if (*token == ',')
 
3535
                  GetToken(q,&q,token);
 
3536
                svg_info.bounds.y=atof(token);
 
3537
                GetToken(q,&q,token);
 
3538
                if (*token == ',')
 
3539
                  GetToken(q,&q,token);
 
3540
                svg_info.bounds.width=atof(token);
 
3541
                GetToken(q,&q,token);
 
3542
                if (*token == ',')
 
3543
                  GetToken(q,&q,token);
 
3544
                svg_info.bounds.height=atof(token);
 
3545
                FormatString(message,"<pattern id=\"%s\" x=\"%g\" y=\"%g\" "
 
3546
                  "width=\"%g\" height=\"%g\">\n",name,svg_info.bounds.x,
 
3547
                  svg_info.bounds.y,svg_info.bounds.width,
 
3548
                  svg_info.bounds.height);
 
3549
                (void) WriteBlobString(image,message);
 
3550
                break;
 
3551
              }
 
3552
            break;
 
3553
          }
 
3554
        status=False;
 
3555
        break;
 
3556
      }
 
3557
      case 'r':
 
3558
      case 'R':
 
3559
      {
 
3560
        if (LocaleCompare("rectangle",keyword) == 0)
 
3561
          {
 
3562
            primitive_type=RectanglePrimitive;
 
3563
            break;
 
3564
          }
 
3565
        if (LocaleCompare("roundRectangle",keyword) == 0)
 
3566
          {
 
3567
            primitive_type=RoundRectanglePrimitive;
 
3568
            break;
 
3569
          }
 
3570
        if (LocaleCompare("rotate",keyword) == 0)
 
3571
          {
 
3572
            GetToken(q,&q,token);
 
3573
            FormatString(message,"rotate(%.1024s) ",token);
 
3574
            (void) WriteBlobString(image,message);
 
3575
            break;
 
3576
          }
 
3577
        status=False;
 
3578
        break;
 
3579
      }
 
3580
      case 's':
 
3581
      case 'S':
 
3582
      {
 
3583
        if (LocaleCompare("scale",keyword) == 0)
 
3584
          {
 
3585
            GetToken(q,&q,token);
 
3586
            affine.sx=atof(token);
 
3587
            GetToken(q,&q,token);
 
3588
            if (*token == ',')
 
3589
              GetToken(q,&q,token);
 
3590
            affine.sy=atof(token);
 
3591
            break;
 
3592
          }
 
3593
        if (LocaleCompare("skewX",keyword) == 0)
 
3594
          {
 
3595
            GetToken(q,&q,token);
 
3596
            FormatString(message,"skewX(%.1024s) ",token);
 
3597
            (void) WriteBlobString(image,message);
 
3598
            break;
 
3599
          }
 
3600
        if (LocaleCompare("skewY",keyword) == 0)
 
3601
          {
 
3602
            GetToken(q,&q,token);
 
3603
            FormatString(message,"skewY(%.1024s) ",token);
 
3604
            (void) WriteBlobString(image,message);
 
3605
            break;
 
3606
          }
 
3607
        if (LocaleCompare("stop-color",keyword) == 0)
 
3608
          {
 
3609
            char
 
3610
              color[MaxTextExtent];
 
3611
 
 
3612
            GetToken(q,&q,token);
 
3613
            (void) strncpy(color,token,MaxTextExtent-1);
 
3614
            GetToken(q,&q,token);
 
3615
            FormatString(message,
 
3616
              "  <stop offset=\"%s\" stop-color=\"%s\" />\n",token,color);
 
3617
            (void) WriteBlobString(image,message);
 
3618
            break;
 
3619
          }
 
3620
        if (LocaleCompare("stroke",keyword) == 0)
 
3621
          {
 
3622
            GetToken(q,&q,token);
 
3623
            FormatString(message,"stroke:%.1024s;",token);
 
3624
            (void) WriteBlobString(image,message);
 
3625
            break;
 
3626
          }
 
3627
        if (LocaleCompare("stroke-antialias",keyword) == 0)
 
3628
          {
 
3629
            GetToken(q,&q,token);
 
3630
            FormatString(message,"stroke-antialias:%.1024s;",token);
 
3631
            (void) WriteBlobString(image,message);
 
3632
            break;
 
3633
          }
 
3634
        if (LocaleCompare("stroke-dasharray",keyword) == 0)
 
3635
          {
 
3636
            if (IsPoint(q))
 
3637
              {
 
3638
                long
 
3639
                  k;
 
3640
 
 
3641
                p=q;
 
3642
                GetToken(p,&p,token);
 
3643
                for (k=0; IsPoint(token); k++)
 
3644
                  GetToken(p,&p,token);
 
3645
                (void) WriteBlobString(image,"stroke-dasharray:");
 
3646
                for (j=0; j < k; j++)
 
3647
                {
 
3648
                  GetToken(q,&q,token);
 
3649
                  FormatString(message,"%.1024s ",token);
 
3650
                  (void) WriteBlobString(image,message);
 
3651
                }
 
3652
                (void) WriteBlobString(image,";");
 
3653
                break;
 
3654
              }
 
3655
            GetToken(q,&q,token);
 
3656
            FormatString(message,"stroke-dasharray:%.1024s;",token);
 
3657
            (void) WriteBlobString(image,message);
 
3658
            break;
 
3659
          }
 
3660
        if (LocaleCompare("stroke-dashoffset",keyword) == 0)
 
3661
          {
 
3662
            GetToken(q,&q,token);
 
3663
            FormatString(message,"stroke-dashoffset:%.1024s;",token);
 
3664
            (void) WriteBlobString(image,message);
 
3665
            break;
 
3666
          }
 
3667
        if (LocaleCompare("stroke-linecap",keyword) == 0)
 
3668
          {
 
3669
            GetToken(q,&q,token);
 
3670
            FormatString(message,"stroke-linecap:%.1024s;",token);
 
3671
            (void) WriteBlobString(image,message);
 
3672
            break;
 
3673
          }
 
3674
        if (LocaleCompare("stroke-linejoin",keyword) == 0)
 
3675
          {
 
3676
            GetToken(q,&q,token);
 
3677
            FormatString(message,"stroke-linejoin:%.1024s;",token);
 
3678
            (void) WriteBlobString(image,message);
 
3679
            break;
 
3680
          }
 
3681
        if (LocaleCompare("stroke-miterlimit",keyword) == 0)
 
3682
          {
 
3683
            GetToken(q,&q,token);
 
3684
            FormatString(message,"stroke-miterlimit:%.1024s;",token);
 
3685
            (void) WriteBlobString(image,message);
 
3686
            break;
 
3687
          }
 
3688
        if (LocaleCompare("stroke-opacity",keyword) == 0)
 
3689
          {
 
3690
            GetToken(q,&q,token);
 
3691
            FormatString(message,"stroke-opacity:%.1024s;",token);
 
3692
            (void) WriteBlobString(image,message);
 
3693
            break;
 
3694
          }
 
3695
        if (LocaleCompare("stroke-width",keyword) == 0)
 
3696
          {
 
3697
            GetToken(q,&q,token);
 
3698
            FormatString(message,"stroke-width:%.1024s;",token);
 
3699
            (void) WriteBlobString(image,message);
 
3700
            continue;
 
3701
          }
 
3702
        status=False;
 
3703
        break;
 
3704
      }
 
3705
      case 't':
 
3706
      case 'T':
 
3707
      {
 
3708
        if (LocaleCompare("text",keyword) == 0)
 
3709
          {
 
3710
            primitive_type=TextPrimitive;
 
3711
            break;
 
3712
          }
 
3713
        if (LocaleCompare("text-antialias",keyword) == 0)
 
3714
          {
 
3715
            GetToken(q,&q,token);
 
3716
            FormatString(message,"text-antialias:%.1024s;",token);
 
3717
            (void) WriteBlobString(image,message);
 
3718
            break;
 
3719
          }
 
3720
        if (LocaleCompare("tspan",keyword) == 0)
 
3721
          {
 
3722
            primitive_type=TextPrimitive;
 
3723
            break;
 
3724
          }
 
3725
        if (LocaleCompare("translate",keyword) == 0)
 
3726
          {
 
3727
            GetToken(q,&q,token);
 
3728
            affine.tx=atof(token);
 
3729
            GetToken(q,&q,token);
 
3730
            if (*token == ',')
 
3731
              GetToken(q,&q,token);
 
3732
            affine.ty=atof(token);
 
3733
            break;
 
3734
          }
 
3735
        status=False;
 
3736
        break;
 
3737
      }
 
3738
      case 'v':
 
3739
      case 'V':
 
3740
      {
 
3741
        if (LocaleCompare("viewbox",keyword) == 0)
 
3742
          {
 
3743
            GetToken(q,&q,token);
 
3744
            if (*token == ',')
 
3745
              GetToken(q,&q,token);
 
3746
            GetToken(q,&q,token);
 
3747
            if (*token == ',')
 
3748
              GetToken(q,&q,token);
 
3749
            GetToken(q,&q,token);
 
3750
            if (*token == ',')
 
3751
              GetToken(q,&q,token);
 
3752
            GetToken(q,&q,token);
 
3753
            break;
 
3754
          }
 
3755
        status=False;
 
3756
        break;
 
3757
      }
 
3758
      default:
 
3759
      {
 
3760
        status=False;
 
3761
        break;
 
3762
      }
 
3763
    }
 
3764
    if (status == False)
 
3765
      break;
 
3766
    if (primitive_type == UndefinedPrimitive)
 
3767
      continue;
 
3768
    /*
 
3769
      Parse the primitive attributes.
 
3770
    */
 
3771
    i=0;
 
3772
    j=0;
 
3773
    for (x=0; *q != '\0'; x++)
 
3774
    {
 
3775
      /*
 
3776
        Define points.
 
3777
      */
 
3778
      if (!IsPoint(q))
 
3779
        break;
 
3780
      GetToken(q,&q,token);
 
3781
      point.x=atof(token);
 
3782
      GetToken(q,&q,token);
 
3783
      if (*token == ',')
 
3784
        GetToken(q,&q,token);
 
3785
      point.y=atof(token);
 
3786
      GetToken(q,(char **) NULL,token);
 
3787
      if (*token == ',')
 
3788
        GetToken(q,&q,token);
 
3789
      primitive_info[i].primitive=primitive_type;
 
3790
      primitive_info[i].point=point;
 
3791
      primitive_info[i].coordinates=0;
 
3792
      primitive_info[i].method=FloodfillMethod;
 
3793
      i++;
 
3794
      if (i < (long) (number_points-6*BezierQuantum-360))
 
3795
        continue;
 
3796
      number_points+=6*BezierQuantum+360;
 
3797
      MagickReallocMemory(primitive_info,
 
3798
        number_points*sizeof(PrimitiveInfo));
 
3799
      if (primitive_info == (PrimitiveInfo *) NULL)
 
3800
        {
 
3801
          ThrowException3(&image->exception,ResourceLimitError,
 
3802
            MemoryAllocationFailed,UnableToDrawOnImage);
 
3803
          break;
 
3804
        }
 
3805
    }
 
3806
    primitive_info[j].primitive=primitive_type;
 
3807
    primitive_info[j].coordinates=x;
 
3808
    primitive_info[j].method=FloodfillMethod;
 
3809
    primitive_info[j].text=(char *) NULL;
 
3810
    if (active)
 
3811
      {
 
3812
        AffineToTransform(image,&affine);
 
3813
        active=False;
 
3814
      }
 
3815
    active=False;
 
3816
    switch (primitive_type)
 
3817
    {
 
3818
      case PointPrimitive:
 
3819
      default:
 
3820
      {
 
3821
        if (primitive_info[j].coordinates != 1)
 
3822
          {
 
3823
            status=False;
 
3824
            break;
 
3825
          }
 
3826
        break;
 
3827
      }
 
3828
      case LinePrimitive:
 
3829
      {
 
3830
        if (primitive_info[j].coordinates != 2)
 
3831
          {
 
3832
            status=False;
 
3833
            break;
 
3834
          }
 
3835
        (void) FormatString(message,
 
3836
          "  <line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"/>\n",
 
3837
          primitive_info[j].point.x,primitive_info[j].point.y,
 
3838
          primitive_info[j+1].point.x,primitive_info[j+1].point.y);
 
3839
        (void) WriteBlobString(image,message);
 
3840
        break;
 
3841
      }
 
3842
      case RectanglePrimitive:
 
3843
      {
 
3844
        if (primitive_info[j].coordinates != 2)
 
3845
          {
 
3846
            status=False;
 
3847
            break;
 
3848
          }
 
3849
        (void) FormatString(message,
 
3850
          "  <rect x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\"/>\n",
 
3851
          primitive_info[j].point.x,primitive_info[j].point.y,
 
3852
          primitive_info[j+1].point.x-primitive_info[j].point.x,
 
3853
          primitive_info[j+1].point.y-primitive_info[j].point.y);
 
3854
        (void) WriteBlobString(image,message);
 
3855
        break;
 
3856
      }
 
3857
      case RoundRectanglePrimitive:
 
3858
      {
 
3859
        if (primitive_info[j].coordinates != 3)
 
3860
          {
 
3861
            status=False;
 
3862
            break;
 
3863
          }
 
3864
        (void) FormatString(message,"  <rect x=\"%g\" y=\"%g\" "
 
3865
          "width=\"%g\" height=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
 
3866
          primitive_info[j].point.x,primitive_info[j].point.y,
 
3867
          primitive_info[j+1].point.x-primitive_info[j].point.x,
 
3868
          primitive_info[j+1].point.y-primitive_info[j].point.y,
 
3869
          primitive_info[j+2].point.x,primitive_info[j+2].point.y);
 
3870
        (void) WriteBlobString(image,message);
 
3871
        break;
 
3872
      }
 
3873
      case ArcPrimitive:
 
3874
      {
 
3875
        if (primitive_info[j].coordinates != 3)
 
3876
          {
 
3877
            status=False;
 
3878
            break;
 
3879
          }
 
3880
        break;
 
3881
      }
 
3882
      case EllipsePrimitive:
 
3883
      {
 
3884
        if (primitive_info[j].coordinates != 3)
 
3885
          {
 
3886
            status=False;
 
3887
            break;
 
3888
          }
 
3889
        (void) FormatString(message,
 
3890
          "  <ellipse cx=\"%g\" cy=\"%g\" rx=\"%g\" ry=\"%g\"/>\n",
 
3891
          primitive_info[j].point.x,primitive_info[j].point.y,
 
3892
          primitive_info[j+1].point.x,primitive_info[j+1].point.y);
 
3893
        (void) WriteBlobString(image,message);
 
3894
        break;
 
3895
      }
 
3896
      case CirclePrimitive:
 
3897
      {
 
3898
        double
 
3899
          alpha,
 
3900
          beta;
 
3901
 
 
3902
        if (primitive_info[j].coordinates != 2)
 
3903
          {
 
3904
            status=False;
 
3905
            break;
 
3906
          }
 
3907
        alpha=primitive_info[j+1].point.x-primitive_info[j].point.x;
 
3908
        beta=primitive_info[j+1].point.y-primitive_info[j].point.y;
 
3909
        (void) FormatString(message,"  <circle cx=\"%g\" cy=\"%g\" r=\"%g\"/>\n",
 
3910
          primitive_info[j].point.x,primitive_info[j].point.y,
 
3911
          sqrt(alpha*alpha+beta*beta));
 
3912
        (void) WriteBlobString(image,message);
 
3913
        break;
 
3914
      }
 
3915
      case PolylinePrimitive:
 
3916
      {
 
3917
        if (primitive_info[j].coordinates < 2)
 
3918
          {
 
3919
            status=False;
 
3920
            break;
 
3921
          }
 
3922
        (void) strcpy(message,"  <polyline points=\"");
 
3923
        (void) WriteBlobString(image,message);
 
3924
        length=strlen(message);
 
3925
        for ( ; j < i; j++)
 
3926
        {
 
3927
          FormatString(message,"%g,%g ",primitive_info[j].point.x,
 
3928
            primitive_info[j].point.y);
 
3929
          length+=strlen(message);
 
3930
          if (length >= 80)
 
3931
            {
 
3932
              (void) WriteBlobString(image,"\n    ");
 
3933
              length=strlen(message)+5;
 
3934
            }
 
3935
          (void) WriteBlobString(image,message);
 
3936
        }
 
3937
        (void) WriteBlobString(image,"\"/>\n");
 
3938
        break;
 
3939
      }
 
3940
      case PolygonPrimitive:
 
3941
      {
 
3942
        if (primitive_info[j].coordinates < 3)
 
3943
          {
 
3944
            status=False;
 
3945
            break;
 
3946
          }
 
3947
        primitive_info[i]=primitive_info[j];
 
3948
        primitive_info[i].coordinates=0;
 
3949
        primitive_info[j].coordinates++;
 
3950
        i++;
 
3951
        (void) strcpy(message,"  <polygon points=\"");
 
3952
        (void) WriteBlobString(image,message);
 
3953
        length=strlen(message);
 
3954
        for ( ; j < i; j++)
 
3955
        {
 
3956
          FormatString(message,"%g,%g ",primitive_info[j].point.x,
 
3957
            primitive_info[j].point.y);
 
3958
          length+=strlen(message);
 
3959
          if (length >= 80)
 
3960
            {
 
3961
              (void) WriteBlobString(image,"\n    ");
 
3962
              length=strlen(message)+5;
 
3963
            }
 
3964
          (void) WriteBlobString(image,message);
 
3965
        }
 
3966
        (void) WriteBlobString(image,"\"/>\n");
 
3967
        break;
 
3968
      }
 
3969
      case BezierPrimitive:
 
3970
      {
 
3971
        if (primitive_info[j].coordinates < 3)
 
3972
          {
 
3973
            status=False;
 
3974
            break;
 
3975
          }
 
3976
        break;
 
3977
      }
 
3978
      case PathPrimitive:
 
3979
      {
 
3980
        int
 
3981
          number_attributes;
 
3982
 
 
3983
        GetToken(q,&q,token);
 
3984
        number_attributes=1;
 
3985
        for (p=token; *p != '\0'; p++)
 
3986
          if (isalpha((int) *p))
 
3987
            number_attributes++;
 
3988
        if (i > (long) (number_points-6*BezierQuantum*number_attributes-1))
 
3989
          {
 
3990
            number_points+=6*BezierQuantum*number_attributes;
 
3991
            MagickReallocMemory(primitive_info,
 
3992
              number_points*sizeof(PrimitiveInfo));
 
3993
            if (primitive_info == (PrimitiveInfo *) NULL)
 
3994
              {
 
3995
                ThrowException3(&image->exception,ResourceLimitError,
 
3996
                  MemoryAllocationFailed,UnableToDrawOnImage);
 
3997
                break;
 
3998
              }
 
3999
          }
 
4000
        (void) WriteBlobString(image,"  <path d=\"");
 
4001
        (void) WriteBlobString(image,token);
 
4002
        (void) WriteBlobString(image,"\"/>\n");
 
4003
        break;
 
4004
      }
 
4005
      case ColorPrimitive:
 
4006
      case MattePrimitive:
 
4007
      {
 
4008
        if (primitive_info[j].coordinates != 1)
 
4009
          {
 
4010
            status=False;
 
4011
            break;
 
4012
          }
 
4013
        GetToken(q,&q,token);
 
4014
        if (LocaleCompare("point",token) == 0)
 
4015
          primitive_info[j].method=PointMethod;
 
4016
        if (LocaleCompare("replace",token) == 0)
 
4017
          primitive_info[j].method=ReplaceMethod;
 
4018
        if (LocaleCompare("floodfill",token) == 0)
 
4019
          primitive_info[j].method=FloodfillMethod;
 
4020
        if (LocaleCompare("filltoborder",token) == 0)
 
4021
          primitive_info[j].method=FillToBorderMethod;
 
4022
        if (LocaleCompare("reset",token) == 0)
 
4023
          primitive_info[j].method=ResetMethod;
 
4024
        break;
 
4025
      }
 
4026
      case TextPrimitive:
 
4027
      {
 
4028
        register char
 
4029
          *p;
 
4030
 
 
4031
        if (primitive_info[j].coordinates != 1)
 
4032
          {
 
4033
            status=False;
 
4034
            break;
 
4035
          }
 
4036
        GetToken(q,&q,token);
 
4037
        (void) FormatString(message,"  <text x=\"%g\" y=\"%g\">",
 
4038
          primitive_info[j].point.x,primitive_info[j].point.y);
 
4039
        (void) WriteBlobString(image,message);
 
4040
        for (p=token; *p != '\0'; p++)
 
4041
          switch (*p)
 
4042
          {
 
4043
            case '<': (void) WriteBlobString(image,"&lt;"); break;
 
4044
            case '>': (void) WriteBlobString(image,"&gt;"); break;
 
4045
            case '&': (void) WriteBlobString(image,"&amp;"); break;
 
4046
            default: (void) WriteBlobByte(image,*p); break;
 
4047
          }
 
4048
        (void) WriteBlobString(image,"</text>\n");
 
4049
        break;
 
4050
      }
 
4051
      case ImagePrimitive:
 
4052
      {
 
4053
        if (primitive_info[j].coordinates != 2)
 
4054
          {
 
4055
            status=False;
 
4056
            break;
 
4057
          }
 
4058
        GetToken(q,&q,token);
 
4059
        (void) FormatString(message,"  <image x=\"%g\" y=\"%g\" "
 
4060
          "width=\"%g\" height=\"%g\" xlink:href=\"%.1024s\"/>\n",
 
4061
          primitive_info[j].point.x,primitive_info[j].point.y,
 
4062
          primitive_info[j+1].point.x,primitive_info[j+1].point.y,token);
 
4063
        (void) WriteBlobString(image,message);
 
4064
        break;
 
4065
      }
 
4066
    }
 
4067
    if (primitive_info == (PrimitiveInfo *) NULL)
 
4068
      break;
 
4069
    primitive_info[i].primitive=UndefinedPrimitive;
 
4070
    if (status == False)
 
4071
      break;
 
4072
  }
 
4073
  (void) WriteBlobString(image,"</svg>\n");
 
4074
  /*
 
4075
    Free resources.
 
4076
  */
 
4077
  MagickFreeMemory(token);
 
4078
  if (primitive_info != (PrimitiveInfo *) NULL)
 
4079
    MagickFreeMemory(primitive_info);
 
4080
  CloseBlob(image);
 
4081
  return(status);
 
4082
}
 
4083
#endif