~ubuntu-branches/debian/squeeze/ghostscript/squeeze

« back to all changes in this revision

Viewing changes to src/gdevsvg.c

  • Committer: Bazaar Package Importer
  • Author(s): Masayuki Hatta (mhatta)
  • Date: 2009-01-04 12:09:59 UTC
  • mfrom: (16.1.1 sid)
  • Revision ID: james.westby@ubuntu.com-20090104120959-m9lbagj775ucg0h3
Tags: 8.63.dfsg.1-2
libgs-dev: put versioned dependency on libgs8 - closes: #510691

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2007-2008 Artifex Software, Inc.
 
2
   All Rights Reserved.
 
3
 
 
4
   This software is provided AS-IS with no warranty, either express or
 
5
   implied.
 
6
 
 
7
   This software is distributed under license and may not be copied, modified
 
8
   or distributed except as expressly authorized under the terms of that
 
9
   license.  Refer to licensing information at http://www.artifex.com/
 
10
   or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
 
11
   San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
 
12
*/
 
13
 
 
14
/* $Id: gdevsvg.c 8798 2008-06-22 06:43:28Z giles $ */
 
15
/* SVG (Scalable Vector Graphics) output device */
 
16
 
 
17
#include "string_.h"
 
18
#include "gx.h"
 
19
#include "gserrors.h"
 
20
#include "gdevvec.h"
 
21
#include "stream.h"
 
22
 
 
23
/* SVG data constants */
 
24
 
 
25
#define XML_DECL    "<?xml version=\"1.0\" standalone=\"no\"?>"
 
26
#define SVG_DOCTYPE "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \n\
 
27
         \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">"
 
28
#define SVG_XMLNS   "http://www.w3.org/2000/svg"
 
29
#define SVG_VERSION "1.1"
 
30
 
 
31
/* default resolution. */
 
32
#ifndef X_DPI
 
33
#  define X_DPI 300
 
34
#endif
 
35
#ifndef Y_DPI
 
36
#  define Y_DPI 300
 
37
#endif
 
38
 
 
39
/* internal line buffer */
 
40
#define SVG_LINESIZE 100
 
41
 
 
42
/* default constants */
 
43
#define SVG_DEFAULT_LINEWIDTH   1.0
 
44
#define SVG_DEFAULT_LINECAP     gs_cap_butt
 
45
#define SVG_DEFAULT_LINEJOIN    gs_join_miter
 
46
#define SVG_DEFAULT_MITERLIMIT  4.0
 
47
 
 
48
/* ---------------- Device definition ---------------- */
 
49
 
 
50
typedef struct gx_device_svg_s {
 
51
    /* superclass state */
 
52
    gx_device_vector_common;
 
53
    /* local state */
 
54
    int header;         /* whether we've written the file header */
 
55
    int dirty;          /* whether we need to rewrite the <g> element */
 
56
    int mark;           /* <g> nesting level */
 
57
    int page_count;     /* how many output_page calls we've seen */
 
58
    char *strokecolor, *fillcolor;
 
59
    double linewidth;
 
60
    gs_line_cap linecap;
 
61
    gs_line_join linejoin;
 
62
    double miterlimit;
 
63
} gx_device_svg;
 
64
 
 
65
#define svg_device_body(dname, depth)\
 
66
  std_device_dci_type_body(gx_device_svg, 0, dname, &st_device_svg, \
 
67
                           DEFAULT_WIDTH_10THS * X_DPI / 10, \
 
68
                           DEFAULT_HEIGHT_10THS * Y_DPI / 10, \
 
69
                           X_DPI, Y_DPI, \
 
70
                           (depth > 8 ? 3 : 1), depth, \
 
71
                           (depth > 1 ? 255 : 1), (depth > 8 ? 255 : 0), \
 
72
                           (depth > 1 ? 256 : 2), (depth > 8 ? 256 : 1))
 
73
 
 
74
static dev_proc_open_device(svg_open_device);
 
75
static dev_proc_output_page(svg_output_page);
 
76
static dev_proc_close_device(svg_close_device);
 
77
 
 
78
static dev_proc_get_params(svg_get_params);
 
79
static dev_proc_put_params(svg_put_params);
 
80
 
 
81
#define svg_device_procs \
 
82
{ \
 
83
        svg_open_device, \
 
84
        NULL,                   /* get_initial_matrix */\
 
85
        NULL,                   /* sync_output */\
 
86
        svg_output_page,\
 
87
        svg_close_device,\
 
88
        gx_default_rgb_map_rgb_color,\
 
89
        gx_default_rgb_map_color_rgb,\
 
90
        gdev_vector_fill_rectangle,\
 
91
        NULL,                   /* tile_rectangle */\
 
92
        NULL,                   /* copy_mono */\
 
93
        NULL,                   /* copy_color */\
 
94
        NULL,                   /* draw_line */\
 
95
        NULL,                   /* get_bits */\
 
96
        svg_get_params,\
 
97
        svg_put_params,\
 
98
        NULL,                   /* map_cmyk_color */\
 
99
        NULL,                   /* get_xfont_procs */\
 
100
        NULL,                   /* get_xfont_device */\
 
101
        NULL,                   /* map_rgb_alpha_color */\
 
102
        gx_page_device_get_page_device,\
 
103
        NULL,                   /* get_alpha_bits */\
 
104
        NULL,                   /* copy_alpha */\
 
105
        NULL,                   /* get_band */\
 
106
        NULL,                   /* copy_rop */\
 
107
        gdev_vector_fill_path,\
 
108
        gdev_vector_stroke_path,\
 
109
        NULL,                   /* fill_mask */\
 
110
        gdev_vector_fill_trapezoid,\
 
111
        gdev_vector_fill_parallelogram,\
 
112
        gdev_vector_fill_triangle,\
 
113
        NULL,                   /* draw_thin_line */\
 
114
        NULL,                   /* begin_image */\
 
115
        NULL,                   /* image_data */\
 
116
        NULL,                   /* end_image */\
 
117
        NULL,                   /* strip_tile_rectangle */\
 
118
        NULL                    /* strip_copy_rop */\
 
119
}
 
120
 
 
121
gs_public_st_suffix_add0_final(st_device_svg, gx_device_svg,
 
122
                               "gx_device_svg",
 
123
                               device_svg_enum_ptrs, 
 
124
                               device_svg_reloc_ptrs,
 
125
                               gx_device_finalize, 
 
126
                               st_device_vector);
 
127
 
 
128
/* The output device is named 'svg' but we're referred to as the
 
129
   'svgwrite' device by the build system to avoid conflicts with
 
130
   the svg interpreter */
 
131
const gx_device_svg gs_svgwrite_device = {
 
132
    svg_device_body("svg", 24),
 
133
    svg_device_procs
 
134
};
 
135
 
 
136
/* Vector device procedures */
 
137
 
 
138
static int
 
139
svg_beginpage(gx_device_vector *vdev);
 
140
static int
 
141
svg_setlinewidth(gx_device_vector *vdev, floatp width);
 
142
static int
 
143
svg_setlinecap(gx_device_vector *vdev, gs_line_cap cap);
 
144
static int
 
145
svg_setlinejoin(gx_device_vector *vdev, gs_line_join join);
 
146
static int
 
147
svg_setmiterlimit(gx_device_vector *vdev, floatp limit);
 
148
static int
 
149
svg_setdash(gx_device_vector *vdev, const float *pattern,
 
150
            uint count, floatp offset);
 
151
static int
 
152
svg_setlogop(gx_device_vector *vdev, gs_logical_operation_t lop,
 
153
             gs_logical_operation_t diff);
 
154
 
 
155
static int
 
156
svg_can_handle_hl_color(gx_device_vector *vdev, const gs_imager_state *pis,
 
157
                        const gx_drawing_color * pdc);
 
158
static int
 
159
svg_setfillcolor(gx_device_vector *vdev, const gs_imager_state *pis,
 
160
                 const gx_drawing_color *pdc);
 
161
static int
 
162
svg_setstrokecolor(gx_device_vector *vdev, const gs_imager_state *pis,
 
163
                   const gx_drawing_color *pdc);
 
164
 
 
165
static int
 
166
svg_dorect(gx_device_vector *vdev, fixed x0, fixed y0,
 
167
           fixed x1, fixed y1, gx_path_type_t type);
 
168
static int
 
169
svg_beginpath(gx_device_vector *vdev, gx_path_type_t type);
 
170
 
 
171
static int
 
172
svg_moveto(gx_device_vector *vdev, floatp x0, floatp y0,
 
173
           floatp x, floatp y, gx_path_type_t type);
 
174
static int
 
175
svg_lineto(gx_device_vector *vdev, floatp x0, floatp y0,
 
176
           floatp x, floatp y, gx_path_type_t type);
 
177
static int
 
178
svg_curveto(gx_device_vector *vdev, floatp x0, floatp y0,
 
179
            floatp x1, floatp y1, floatp x2, floatp y2,
 
180
            floatp x3, floatp y3, gx_path_type_t type);
 
181
static int
 
182
svg_closepath(gx_device_vector *vdev, floatp x, floatp y,
 
183
              floatp x_start, floatp y_start, gx_path_type_t type);
 
184
static int
 
185
svg_endpath(gx_device_vector *vdev, gx_path_type_t type);
 
186
 
 
187
/* Vector device function table */
 
188
 
 
189
static const gx_device_vector_procs svg_vector_procs = {
 
190
        /* Page management */
 
191
    svg_beginpage,
 
192
        /* Imager state */
 
193
    svg_setlinewidth,
 
194
    svg_setlinecap,
 
195
    svg_setlinejoin,
 
196
    svg_setmiterlimit,
 
197
    svg_setdash,
 
198
    gdev_vector_setflat,
 
199
    svg_setlogop,
 
200
        /* Other state */
 
201
    svg_can_handle_hl_color,
 
202
    svg_setfillcolor,
 
203
    svg_setstrokecolor,
 
204
        /* Paths */
 
205
    gdev_vector_dopath,
 
206
    svg_dorect,
 
207
    svg_beginpath,
 
208
    svg_moveto,
 
209
    svg_lineto,
 
210
    svg_curveto,
 
211
    svg_closepath,
 
212
    svg_endpath
 
213
};
 
214
 
 
215
/* local utility prototypes */
 
216
 
 
217
static int svg_write_bytes(gx_device_svg *svg,
 
218
                const char *string, uint length);
 
219
static int svg_write(gx_device_svg *svg, const char *string);
 
220
 
 
221
static int svg_write_header(gx_device_svg *svg);
 
222
 
 
223
/* Driver procedure implementation */
 
224
 
 
225
/* Open the device */
 
226
static int
 
227
svg_open_device(gx_device *dev)
 
228
{
 
229
    gx_device_vector *const vdev = (gx_device_vector*)dev;
 
230
    gx_device_svg *const svg = (gx_device_svg*)dev;
 
231
    int code = 0;
 
232
 
 
233
    vdev->v_memory = dev->memory;
 
234
    vdev->vec_procs = &svg_vector_procs;
 
235
    gdev_vector_init(vdev);
 
236
    code = gdev_vector_open_file_options(vdev, 512,
 
237
        VECTOR_OPEN_FILE_SEQUENTIAL);
 
238
    if (code < 0) return code;
 
239
 
 
240
    /* svg-specific initialization goes here */
 
241
    svg->header = 0;
 
242
    svg->dirty = 0;
 
243
    svg->mark = 0;
 
244
    svg->page_count = 0;
 
245
    svg->strokecolor = NULL;
 
246
    svg->fillcolor = NULL;
 
247
    /* these should be the graphics library defaults instead? */
 
248
    svg->linewidth = SVG_DEFAULT_LINEWIDTH;
 
249
    svg->linecap = SVG_DEFAULT_LINECAP;
 
250
    svg->linejoin = SVG_DEFAULT_LINEJOIN;
 
251
    svg->miterlimit = SVG_DEFAULT_MITERLIMIT;
 
252
    return code;
 
253
}
 
254
 
 
255
/* Complete a page */
 
256
static int
 
257
svg_output_page(gx_device *dev, int num_copies, int flush)
 
258
{
 
259
    gx_device_svg *const svg = (gx_device_svg*)dev;
 
260
 
 
261
    svg->page_count++;
 
262
 
 
263
    svg_write(svg, "\n<!-- svg_output_page -->\n");
 
264
    if (ferror(svg->file)) return_error(gs_error_ioerror);
 
265
 
 
266
    return gx_finish_output_page(dev, num_copies, flush);
 
267
}
 
268
 
 
269
/* Close the device */
 
270
static int
 
271
svg_close_device(gx_device *dev)
 
272
{
 
273
    gx_device_svg *const svg = (gx_device_svg*)dev;
 
274
 
 
275
    svg_write(svg, "\n<!-- svg_close_device -->\n");
 
276
    /* close any open group elements */
 
277
    while (svg->mark > 0) {
 
278
      svg_write(svg, "</g>\n");
 
279
      svg->mark--;
 
280
    }
 
281
    if (svg->header) {
 
282
      svg_write(svg, "</svg>\n");
 
283
      svg->header = 0;
 
284
    }
 
285
 
 
286
    if (svg->fillcolor) gs_free_string(svg->memory, svg->fillcolor, 8,
 
287
        "svg_close_device");
 
288
    if (svg->strokecolor) gs_free_string(svg->memory, svg->strokecolor, 8,
 
289
        "svg_close_device");
 
290
 
 
291
    if (ferror(svg->file)) return_error(gs_error_ioerror);
 
292
 
 
293
    return gdev_vector_close_file((gx_device_vector*)dev);
 
294
}
 
295
 
 
296
/* Respond to a device parameter query from the client */
 
297
static int
 
298
svg_get_params(gx_device *dev, gs_param_list *plist)
 
299
{
 
300
    int code = 0;
 
301
 
 
302
    dprintf("svg_get_params\n");
 
303
 
 
304
    /* call our superclass to add its standard set */
 
305
    code = gdev_vector_get_params(dev, plist);
 
306
    if (code < 0) return code;
 
307
 
 
308
    /* svg specific parameters are added to plist here */
 
309
 
 
310
    return code;
 
311
}
 
312
 
 
313
/* Read the device parameters passed to us by the client */
 
314
static int
 
315
svg_put_params(gx_device *dev, gs_param_list *plist)
 
316
{
 
317
    int code = 0;
 
318
 
 
319
    dprintf("svg_put_params\n");
 
320
 
 
321
    /* svg specific parameters are parsed here */
 
322
 
 
323
    /* call our superclass to get its parameters, like OutputFile */
 
324
    code = gdev_vector_put_params(dev, plist);
 
325
    if (code < 0) return code;
 
326
 
 
327
    return code;
 
328
}
 
329
 
 
330
/* write a length-limited char buffer */
 
331
static int
 
332
svg_write_bytes(gx_device_svg *svg, const char *string, uint length)
 
333
{
 
334
    /* calling the accessor ensures beginpage is called */
 
335
    stream *s = gdev_vector_stream((gx_device_vector*)svg);
 
336
    uint used;
 
337
 
 
338
    sputs(s, (const byte *)string, length, &used);
 
339
 
 
340
    return !(length == used);
 
341
}
 
342
 
 
343
/* write a null terminated string */
 
344
static int
 
345
svg_write(gx_device_svg *svg, const char *string)
 
346
{
 
347
    return svg_write_bytes(svg, string, strlen(string));
 
348
}
 
349
 
 
350
static int
 
351
svg_write_header(gx_device_svg *svg)
 
352
{
 
353
    /* we're called from beginpage, so we can't use
 
354
       svg_write() which calls gdev_vector_stream()
 
355
       which calls beginpage! */
 
356
    stream *s = svg->strm;
 
357
    uint used;
 
358
    char line[300];
 
359
 
 
360
    dprintf("svg_write_header\n");
 
361
 
 
362
    /* only write the header once */
 
363
    if (svg->header) return 1;
 
364
 
 
365
    /* write the initial boilerplate */
 
366
    sprintf(line, "%s\n", XML_DECL);
 
367
    /* svg_write(svg, line); */
 
368
    sputs(s, line, strlen(line), &used);
 
369
    sprintf(line, "%s\n", SVG_DOCTYPE);
 
370
    /* svg_write(svg, line); */
 
371
    sputs(s, line, strlen(line), &used);
 
372
    sprintf(line, "<svg xmlns='%s' version='%s'",
 
373
        SVG_XMLNS, SVG_VERSION);
 
374
    /* svg_write(svg, line); */
 
375
    sputs(s, line, strlen(line), &used);
 
376
    sprintf(line, "\n\twidth='%dpt' height='%dpt'>\n",
 
377
        (int)svg->MediaSize[0], (int)svg->MediaSize[1]);
 
378
    sputs(s, line, strlen(line), &used);
 
379
 
 
380
    /* Scale drawing so our coordinates are in pixels */
 
381
    sprintf(line, "<g transform='scale(%lf,%lf)'>\n",
 
382
        72.0 / svg->HWResolution[0],
 
383
        72.0 / svg->HWResolution[1]);
 
384
    /* svg_write(svg, line); */
 
385
    sputs(s, line, strlen(line), &used);
 
386
    svg->mark++;
 
387
 
 
388
    /* mark that we've been called */
 
389
    svg->header = 1;
 
390
 
 
391
    return 0;
 
392
}
 
393
 
 
394
static const char *
 
395
svg_make_color(gx_device_svg *svg, gx_drawing_color *pdc)
 
396
{
 
397
    char *paint = gs_alloc_string(svg->memory, 8, "svg_make_color");
 
398
 
 
399
    if (!paint) {
 
400
      gs_note_error(gs_error_VMerror);
 
401
      return NULL;
 
402
    }
 
403
 
 
404
    if (gx_dc_is_pure(pdc)) {
 
405
      gx_color_index color = gx_dc_pure_color(pdc);
 
406
      sprintf(paint, "#%06x", color & 0xffffff);
 
407
    } else if (gx_dc_is_null(pdc)) {
 
408
      sprintf(paint, "None");
 
409
    } else {
 
410
      gs_free_string(svg->memory, paint, 8, "svg_make_color");
 
411
      gs_note_error(gs_error_rangecheck);
 
412
      return NULL;
 
413
    }
 
414
 
 
415
    return paint;
 
416
}
 
417
 
 
418
static int
 
419
svg_write_state(gx_device_svg *svg)
 
420
{
 
421
    char line[SVG_LINESIZE];
 
422
 
 
423
    /* has anything changed? */
 
424
    if (!svg->dirty) return 0;
 
425
 
 
426
    /* close the current graphics state element, if any */
 
427
    if (svg->mark > 1) {
 
428
      svg_write(svg, "</g>\n");
 
429
      svg->mark--;
 
430
    }
 
431
    /* write out the new current state */
 
432
    svg_write(svg, "<g ");
 
433
    if (svg->strokecolor) {
 
434
        sprintf(line, " stroke='%s'", svg->strokecolor);
 
435
        svg_write(svg, line);
 
436
    } else {
 
437
        svg_write(svg, " stroke='none'");
 
438
    }
 
439
    if (svg->fillcolor) {
 
440
        sprintf(line, " fill='%s'", svg->fillcolor);
 
441
        svg_write(svg, line);
 
442
    } else {
 
443
      svg_write(svg, " fill='none'");
 
444
    }
 
445
    if (svg->linewidth != 1.0) {
 
446
      sprintf(line, " stroke-width='%lf'", svg->linewidth);
 
447
      svg_write(svg, line);
 
448
    }
 
449
    if (svg->linecap != SVG_DEFAULT_LINECAP) {
 
450
        switch (svg->linecap) {
 
451
          case gs_cap_round:
 
452
            svg_write(svg, " stroke-linecap='round'");
 
453
            break;
 
454
          case gs_cap_square:
 
455
            svg_write(svg, " stroke-linecap='square'");
 
456
            break;
 
457
          case gs_cap_butt:
 
458
          default:
 
459
            /* treat all the other options as the default */
 
460
            svg_write(svg, " stroke-linecap='butt'");
 
461
            break;
 
462
        }
 
463
    }
 
464
    if (svg->linejoin != SVG_DEFAULT_LINEJOIN) {
 
465
        switch (svg->linejoin) {
 
466
          case gs_join_round:
 
467
            svg_write(svg, " stroke-linejoin='round'");
 
468
            break;
 
469
          case gs_join_bevel:
 
470
            svg_write(svg, " stroke-linejoin='bevel'");
 
471
            break;
 
472
          case gs_join_miter:
 
473
          default:
 
474
            /* SVG doesn't support any other variants */
 
475
            svg_write(svg, " stroke-linejoin='miter'");
 
476
            break;
 
477
        }
 
478
    }
 
479
    if (svg->miterlimit != SVG_DEFAULT_MITERLIMIT) {
 
480
        sprintf(line, " stroke-miterlimit='%lf'", svg->miterlimit);
 
481
        svg_write(svg, line);
 
482
    }
 
483
    svg_write(svg, ">\n");
 
484
    svg->mark++;
 
485
 
 
486
    svg->dirty = 0;
 
487
    return 0;
 
488
}
 
489
 
 
490
/* vector device implementation */
 
491
 
 
492
        /* Page management */
 
493
static int
 
494
svg_beginpage(gx_device_vector *vdev)
 
495
{
 
496
    gx_device_svg *svg = (gx_device_svg *)vdev;
 
497
 
 
498
    svg_write_header(svg);
 
499
 
 
500
    dprintf1("svg_beginpage (page count %d)\n", svg->page_count);
 
501
    return 0;
 
502
}
 
503
 
 
504
        /* Imager state */
 
505
static int
 
506
svg_setlinewidth(gx_device_vector *vdev, floatp width)
 
507
{
 
508
    gx_device_svg *svg = (gx_device_svg *)vdev;
 
509
 
 
510
    dprintf1("svg_setlinewidth(%lf)\n", width);
 
511
 
 
512
    svg->linewidth = width;
 
513
    svg->dirty++;
 
514
 
 
515
    return 0;
 
516
}
 
517
static int
 
518
svg_setlinecap(gx_device_vector *vdev, gs_line_cap cap)
 
519
{
 
520
    gx_device_svg *svg = (gx_device_svg *)vdev;
 
521
    const char *linecap_names[] = {"butt", "round", "square",
 
522
        "triangle", "unknown"};
 
523
 
 
524
    if (cap < 0 || cap > gs_cap_unknown)
 
525
        return_error(gs_error_rangecheck);
 
526
    dprintf1("svg_setlinecap(%s)\n", linecap_names[cap]);
 
527
 
 
528
    svg->linecap = cap;
 
529
    svg->dirty++;
 
530
 
 
531
    return 0;
 
532
}
 
533
static int
 
534
svg_setlinejoin(gx_device_vector *vdev, gs_line_join join)
 
535
{
 
536
    gx_device_svg *svg = (gx_device_svg *)vdev;
 
537
    const char *linejoin_names[] = {"miter", "round", "bevel",
 
538
        "none", "triangle", "unknown"};
 
539
 
 
540
    if (join < 0 || join > gs_join_unknown)
 
541
        return_error(gs_error_rangecheck);
 
542
    dprintf1("svg_setlinejoin(%s)\n", linejoin_names[join]);
 
543
 
 
544
    svg->linejoin = join;
 
545
    svg->dirty++;
 
546
 
 
547
    return 0;
 
548
}
 
549
static int
 
550
svg_setmiterlimit(gx_device_vector *vdev, floatp limit)
 
551
{
 
552
    dprintf1("svg_setmiterlimit(%lf)\n", limit);
 
553
    return 0;
 
554
}
 
555
static int
 
556
svg_setdash(gx_device_vector *vdev, const float *pattern,
 
557
            uint count, floatp offset)
 
558
{
 
559
    dprintf("svg_setdash\n");
 
560
    return 0;
 
561
}
 
562
static int
 
563
svg_setlogop(gx_device_vector *vdev, gs_logical_operation_t lop,
 
564
             gs_logical_operation_t diff)
 
565
{
 
566
    dprintf2("svg_setlogop(%u,%u) set logical operation\n",
 
567
        lop, diff);
 
568
    /* SVG can fake some simpler modes, but we ignore this for now. */
 
569
    return 0;
 
570
}
 
571
 
 
572
        /* Other state */
 
573
 
 
574
static int
 
575
svg_can_handle_hl_color(gx_device_vector *vdev, const gs_imager_state *pis,
 
576
                          const gx_drawing_color * pdc)
 
577
{
 
578
    dprintf("svg_can_handle_hl_color\n");
 
579
    return 0;
 
580
}
 
581
 
 
582
static int
 
583
svg_setfillcolor(gx_device_vector *vdev, const gs_imager_state *pis,
 
584
                 const gx_drawing_color *pdc)
 
585
{
 
586
    gx_device_svg *svg = (gx_device_svg*)vdev;
 
587
    char *fill;
 
588
 
 
589
    dprintf("svg_setfillcolor\n");
 
590
 
 
591
    fill = svg_make_color(svg, pdc);
 
592
    if (!fill) return gs_error_VMerror;
 
593
    if (svg->fillcolor && !strcmp(fill, svg->fillcolor))
 
594
      return 0; /* not a new color */
 
595
 
 
596
    /* update our state with the new color */
 
597
    if (svg->fillcolor) gs_free_string(svg->memory, svg->fillcolor, 8,
 
598
        "svg_setfillcolor");
 
599
    svg->fillcolor = fill;
 
600
    /* request a new group element */
 
601
    svg->dirty++;
 
602
 
 
603
    return 0;
 
604
}
 
605
 
 
606
static int
 
607
svg_setstrokecolor(gx_device_vector *vdev, const gs_imager_state *pis,
 
608
                   const gx_drawing_color *pdc)
 
609
{
 
610
    gx_device_svg *svg = (gx_device_svg*)vdev;
 
611
    char *stroke;
 
612
 
 
613
    dprintf("svg_setstrokecolor\n");
 
614
 
 
615
    stroke = svg_make_color(svg, pdc);
 
616
    if (!stroke) return gs_error_VMerror;
 
617
    if (svg->strokecolor && !strcmp(stroke, svg->strokecolor)) 
 
618
      return 0; /* not a new color */
 
619
 
 
620
    /* update our state with the new color */
 
621
    if (svg->strokecolor) gs_free_string(svg->memory, svg->strokecolor, 8,
 
622
        "svg_setstrokecolor");
 
623
    svg->strokecolor = stroke;
 
624
    /* request a new group element */
 
625
    svg->dirty++;
 
626
 
 
627
    return 0;
 
628
}
 
629
 
 
630
        /* Paths */
 
631
/*    gdev_vector_dopath */
 
632
 
 
633
static int svg_print_path_type(gx_device_svg *svg, gx_path_type_t type)
 
634
{
 
635
    const char *path_type_names[] = {"winding number", "fill", "stroke",
 
636
        "fill and stroke", "clip"};
 
637
 
 
638
    if (type <= 4)
 
639
        dprintf2("type %d (%s)", type, path_type_names[type]);
 
640
    else
 
641
        dprintf1("type %d", type);
 
642
 
 
643
    return 0;
 
644
}
 
645
 
 
646
static int
 
647
svg_dorect(gx_device_vector *vdev, fixed x0, fixed y0,
 
648
           fixed x1, fixed y1, gx_path_type_t type)
 
649
{
 
650
    gx_device_svg *svg = (gx_device_svg *)vdev;
 
651
    char line[300];
 
652
 
 
653
    if (svg->page_count) return 0; /* hack single-page output */
 
654
 
 
655
    dprintf("svg_dorect ");
 
656
    svg_print_path_type(svg, type);
 
657
    dprintf("\n");
 
658
 
 
659
    svg_write_state(svg);
 
660
 
 
661
    if (type & gx_path_type_clip) {
 
662
        svg_write(svg, "<clipPath>\n");
 
663
    }
 
664
 
 
665
    sprintf(line, "<rect x='%lf' y='%lf' width='%lf' height='%lf'",
 
666
        fixed2float(x0), fixed2float(y0),
 
667
        fixed2float(x1 - x0), fixed2float(y1 - y0));
 
668
    svg_write(svg, line);
 
669
    /* override the inherited stroke attribute if we're not stroking */
 
670
    if (!(type & gx_path_type_stroke) && svg->strokecolor)
 
671
        svg_write(svg, " stroke='none'");
 
672
    /* override the inherited fill attribute if we're not filling */
 
673
    if (!(type & gx_path_type_fill) && svg->fillcolor)
 
674
        svg_write(svg, " fill='none'");
 
675
    svg_write(svg, "/>\n");
 
676
 
 
677
    if (type & gx_path_type_clip) {
 
678
        svg_write(svg, "</clipPath>\n");
 
679
    }
 
680
 
 
681
    return 0;
 
682
}
 
683
 
 
684
static int
 
685
svg_beginpath(gx_device_vector *vdev, gx_path_type_t type)
 
686
{
 
687
    gx_device_svg *svg = (gx_device_svg *)vdev;
 
688
 
 
689
    if (svg->page_count) return 0; /* hack single-page output */
 
690
    if (!(type & gx_path_type_fill) && !(type & gx_path_type_stroke))
 
691
        return 0; /* skip non-drawing paths for now */
 
692
 
 
693
    dprintf("svg_beginpath ");
 
694
    svg_print_path_type(svg, type);
 
695
    dprintf("\n");
 
696
 
 
697
    svg_write_state(svg);
 
698
    svg_write(svg, "<path d='");
 
699
 
 
700
    return 0;
 
701
}
 
702
 
 
703
static int
 
704
svg_moveto(gx_device_vector *vdev, floatp x0, floatp y0,
 
705
           floatp x, floatp y, gx_path_type_t type)
 
706
{
 
707
    gx_device_svg *svg = (gx_device_svg *)vdev;
 
708
    char line[SVG_LINESIZE];
 
709
 
 
710
    if (svg->page_count) return 0; /* hack single-page output */
 
711
    if (!(type & gx_path_type_fill) && !(type & gx_path_type_stroke))
 
712
        return 0; /* skip non-drawing paths for now */
 
713
 
 
714
    dprintf4("svg_moveto(%lf,%lf,%lf,%lf) ", x0, y0, x, y);
 
715
    svg_print_path_type(svg, type);
 
716
    dprintf("\n");
 
717
 
 
718
    sprintf(line, " M%lf,%lf", x, y);
 
719
    svg_write(svg, line);
 
720
 
 
721
    return 0;
 
722
}
 
723
 
 
724
static int
 
725
svg_lineto(gx_device_vector *vdev, floatp x0, floatp y0,
 
726
           floatp x, floatp y, gx_path_type_t type)
 
727
{
 
728
    gx_device_svg *svg = (gx_device_svg *)vdev;
 
729
    char line[SVG_LINESIZE];
 
730
 
 
731
    if (svg->page_count) return 0; /* hack single-page output */
 
732
    if (!(type & gx_path_type_fill) && !(type & gx_path_type_stroke))
 
733
        return 0; /* skip non-drawing paths for now */
 
734
 
 
735
    dprintf4("svg_lineto(%lf,%lf,%lf,%lf) ", x0,y0, x,y);
 
736
    svg_print_path_type(svg, type);
 
737
    dprintf("\n");
 
738
 
 
739
    sprintf(line, " L%lf,%lf", x, y);
 
740
    svg_write(svg, line);
 
741
 
 
742
    return 0;
 
743
}
 
744
 
 
745
static int
 
746
svg_curveto(gx_device_vector *vdev, floatp x0, floatp y0,
 
747
            floatp x1, floatp y1, floatp x2, floatp y2,
 
748
            floatp x3, floatp y3, gx_path_type_t type)
 
749
{
 
750
    gx_device_svg *svg = (gx_device_svg *)vdev;
 
751
    char line[SVG_LINESIZE];
 
752
 
 
753
    if (svg->page_count) return 0; /* hack single-page output */
 
754
    if (!(type & gx_path_type_fill) && !(type & gx_path_type_stroke))
 
755
        return 0; /* skip non-drawing paths for now */
 
756
 
 
757
    dprintf8("svg_curveto(%lf,%lf, %lf,%lf, %lf,%lf, %lf,%lf) ",
 
758
        x0,y0, x1,y1, x2,y2, x3,y3);
 
759
    svg_print_path_type(svg, type);
 
760
    dprintf("\n");
 
761
 
 
762
    sprintf(line, " C%lf,%lf %lf,%lf %lf,%lf", x1,y1, x2,y2, x3,y3);
 
763
    svg_write(svg, line);
 
764
 
 
765
    return 0;
 
766
}
 
767
 
 
768
static int
 
769
svg_closepath(gx_device_vector *vdev, floatp x, floatp y,
 
770
              floatp x_start, floatp y_start, gx_path_type_t type)
 
771
{
 
772
    gx_device_svg *svg = (gx_device_svg *)vdev;
 
773
 
 
774
    if (svg->page_count) return 0; /* hack single-page output */
 
775
    if (!(type & gx_path_type_fill) && !(type & gx_path_type_stroke))
 
776
        return 0; /* skip non-drawing paths for now */
 
777
 
 
778
    dprintf("svg_closepath ");
 
779
    svg_print_path_type(svg, type);
 
780
    dprintf("\n");
 
781
 
 
782
    svg_write(svg, " z");
 
783
 
 
784
    return 0;
 
785
}
 
786
 
 
787
static int
 
788
svg_endpath(gx_device_vector *vdev, gx_path_type_t type)
 
789
{
 
790
    gx_device_svg *svg = (gx_device_svg *)vdev;
 
791
    char line[SVG_LINESIZE];
 
792
 
 
793
    if (svg->page_count) return 0; /* hack single-page output */
 
794
    if (!(type & gx_path_type_fill) && !(type & gx_path_type_stroke))
 
795
        return 0; /* skip non-drawing paths for now */
 
796
 
 
797
    dprintf("svg_endpath ");
 
798
    svg_print_path_type(svg, type);
 
799
    dprintf("\n");
 
800
 
 
801
    /* close the path data attribute */
 
802
    svg_write(svg, "'");
 
803
 
 
804
    /* override the inherited stroke attribute if we're not stroking */
 
805
    if (!(type & gx_path_type_stroke) && svg->strokecolor) {
 
806
        svg_write(svg, " stroke='none'");
 
807
    }
 
808
    /* override the inherited fill attribute if we're not filling */
 
809
    if (!(type & gx_path_type_fill) && svg->fillcolor) {
 
810
        svg_write(svg, " fill='none'");
 
811
    }
 
812
 
 
813
    svg_write(svg, "/>\n");
 
814
 
 
815
    return 0;
 
816
}
 
817