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