Changes.
[cairo/gpu.git] / src / cairo-svg-surface.c
blobd74e1f8985fc053072bd766603190601f295b95e
1 /* vim: set sw=4 sts=4: -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
2 /* cairo - a vector graphics library with display and print output
4 * Copyright © 2004 Red Hat, Inc
5 * Copyright © 2005-2007 Emmanuel Pacaud <emmanuel.pacaud@free.fr>
6 * Copyright © 2006 Red Hat, Inc
8 * This library is free software; you can redistribute it and/or
9 * modify it either under the terms of the GNU Lesser General Public
10 * License version 2.1 as published by the Free Software Foundation
11 * (the "LGPL") or, at your option, under the terms of the Mozilla
12 * Public License Version 1.1 (the "MPL"). If you do not alter this
13 * notice, a recipient may use your version of this file under either
14 * the MPL or the LGPL.
16 * You should have received a copy of the LGPL along with this library
17 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 * You should have received a copy of the MPL along with this library
20 * in the file COPYING-MPL-1.1
22 * The contents of this file are subject to the Mozilla Public License
23 * Version 1.1 (the "License"); you may not use this file except in
24 * compliance with the License. You may obtain a copy of the License at
25 * http://www.mozilla.org/MPL/
27 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
28 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
29 * the specific language governing rights and limitations.
31 * The Original Code is the cairo graphics library.
33 * The Initial Developer of the Original Code is University of Southern
34 * California.
36 * Contributor(s):
37 * Kristian Høgsberg <krh@redhat.com>
38 * Emmanuel Pacaud <emmanuel.pacaud@free.fr>
39 * Carl Worth <cworth@cworth.org>
42 #define _BSD_SOURCE /* for snprintf() */
43 #include "cairoint.h"
44 #include "cairo-svg.h"
45 #include "cairo-analysis-surface-private.h"
46 #include "cairo-image-info-private.h"
47 #include "cairo-meta-surface-private.h"
48 #include "cairo-output-stream-private.h"
49 #include "cairo-path-fixed-private.h"
50 #include "cairo-paginated-private.h"
51 #include "cairo-scaled-font-subsets-private.h"
52 #include "cairo-svg-surface-private.h"
54 typedef struct cairo_svg_page cairo_svg_page_t;
56 static const int invalid_pattern_id = -1;
58 static const cairo_svg_version_t _cairo_svg_versions[] =
60 CAIRO_SVG_VERSION_1_1,
61 CAIRO_SVG_VERSION_1_2
64 #define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
66 static cairo_bool_t
67 _cairo_svg_version_has_page_set_support (cairo_svg_version_t version)
69 return version > CAIRO_SVG_VERSION_1_1;
72 static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] =
74 "SVG 1.1",
75 "SVG 1.2"
78 static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] =
80 "1.1",
81 "1.2"
84 struct cairo_svg_page {
85 unsigned int surface_id;
86 unsigned int clip_level;
87 cairo_output_stream_t *xml_node;
90 struct cairo_svg_document {
91 cairo_output_stream_t *output_stream;
92 unsigned long refcount;
93 cairo_surface_t *owner;
94 cairo_bool_t finished;
96 double width;
97 double height;
99 cairo_output_stream_t *xml_node_defs;
100 cairo_output_stream_t *xml_node_glyphs;
102 unsigned int surface_id;
103 unsigned int linear_pattern_id;
104 unsigned int radial_pattern_id;
105 unsigned int pattern_id;
106 unsigned int filter_id;
107 unsigned int clip_id;
108 unsigned int mask_id;
110 cairo_bool_t alpha_filter;
112 cairo_array_t meta_snapshots;
114 cairo_svg_version_t svg_version;
116 cairo_scaled_font_subsets_t *font_subsets;
119 typedef struct {
120 unsigned int id;
121 cairo_meta_surface_t *meta;
122 } cairo_meta_snapshot_t;
124 static cairo_status_t
125 _cairo_svg_document_create (cairo_output_stream_t *stream,
126 double width,
127 double height,
128 cairo_svg_version_t version,
129 cairo_svg_document_t **document_out);
131 static cairo_status_t
132 _cairo_svg_document_destroy (cairo_svg_document_t *document);
134 static cairo_status_t
135 _cairo_svg_document_finish (cairo_svg_document_t *document);
137 static cairo_svg_document_t *
138 _cairo_svg_document_reference (cairo_svg_document_t *document);
140 static unsigned int
141 _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document);
143 static cairo_surface_t *
144 _cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
145 cairo_content_t content,
146 double width,
147 double height);
148 static cairo_surface_t *
149 _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
150 double width,
151 double height,
152 cairo_svg_version_t version);
154 static const cairo_surface_backend_t cairo_svg_surface_backend;
155 static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend;
158 * cairo_svg_surface_create_for_stream:
159 * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
160 * to indicate a no-op @write_func. With a no-op @write_func,
161 * the surface may be queried or used as a source without
162 * generating any temporary files.
163 * @closure: the closure argument for @write_func
164 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
165 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
167 * Creates a SVG surface of the specified size in points to be written
168 * incrementally to the stream represented by @write_func and @closure.
170 * Return value: a pointer to the newly created surface. The caller
171 * owns the surface and should call cairo_surface_destroy() when done
172 * with it.
174 * This function always returns a valid pointer, but it will return a
175 * pointer to a "nil" surface if an error such as out of memory
176 * occurs. You can use cairo_surface_status() to check for this.
178 * Since: 1.2
180 cairo_surface_t *
181 cairo_svg_surface_create_for_stream (cairo_write_func_t write_func,
182 void *closure,
183 double width,
184 double height)
186 cairo_output_stream_t *stream;
188 stream = _cairo_output_stream_create (write_func, NULL, closure);
189 if (_cairo_output_stream_get_status (stream))
190 return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
192 return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
196 * cairo_svg_surface_create:
197 * @filename: a filename for the SVG output (must be writable), %NULL may be
198 * used to specify no output. This will generate a SVG surface that
199 * may be queried and used as a source, without generating a
200 * temporary file.
201 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
202 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
204 * Creates a SVG surface of the specified size in points to be written
205 * to @filename.
207 * Return value: a pointer to the newly created surface. The caller
208 * owns the surface and should call cairo_surface_destroy() when done
209 * with it.
211 * This function always returns a valid pointer, but it will return a
212 * pointer to a "nil" surface if an error such as out of memory
213 * occurs. You can use cairo_surface_status() to check for this.
215 * Since: 1.2
217 cairo_surface_t *
218 cairo_svg_surface_create (const char *filename,
219 double width,
220 double height)
222 cairo_output_stream_t *stream;
224 stream = _cairo_output_stream_create_for_filename (filename);
225 if (_cairo_output_stream_get_status (stream))
226 return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
228 return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
231 static cairo_bool_t
232 _cairo_surface_is_svg (cairo_surface_t *surface)
234 return surface->backend == &cairo_svg_surface_backend;
237 /* If the abstract_surface is a paginated surface, and that paginated
238 * surface's target is a svg_surface, then set svg_surface to that
239 * target. Otherwise return %CAIRO_STATUS_SURFACE_TYPE_MISMATCH.
241 static cairo_status_t
242 _extract_svg_surface (cairo_surface_t *surface,
243 cairo_svg_surface_t **svg_surface)
245 cairo_surface_t *target;
247 if (surface->status)
248 return surface->status;
250 if (! _cairo_surface_is_paginated (surface))
251 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
253 target = _cairo_paginated_surface_get_target (surface);
254 if (target->status)
255 return target->status;
257 if (! _cairo_surface_is_svg (target))
258 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
260 *svg_surface = (cairo_svg_surface_t *) target;
262 return CAIRO_STATUS_SUCCESS;
266 * cairo_svg_surface_restrict_to_version:
267 * @surface: a SVG #cairo_surface_t
268 * @version: SVG version
270 * Restricts the generated SVG file to @version. See cairo_svg_get_versions()
271 * for a list of available version values that can be used here.
273 * This function should only be called before any drawing operations
274 * have been performed on the given surface. The simplest way to do
275 * this is to call this function immediately after creating the
276 * surface.
278 * Since: 1.2
280 void
281 cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface,
282 cairo_svg_version_t version)
284 cairo_svg_surface_t *surface = NULL; /* hide compiler warning */
285 cairo_status_t status;
287 status = _extract_svg_surface (abstract_surface, &surface);
288 if (unlikely (status)) {
289 status = _cairo_surface_set_error (abstract_surface, status);
290 return;
293 if (version < CAIRO_SVG_VERSION_LAST)
294 surface->document->svg_version = version;
298 * cairo_svg_get_versions:
299 * @versions: supported version list
300 * @num_versions: list length
302 * Used to retrieve the list of supported versions. See
303 * cairo_svg_surface_restrict_to_version().
305 * Since: 1.2
307 void
308 cairo_svg_get_versions (cairo_svg_version_t const **versions,
309 int *num_versions)
311 if (versions != NULL)
312 *versions = _cairo_svg_versions;
314 if (num_versions != NULL)
315 *num_versions = CAIRO_SVG_VERSION_LAST;
319 * cairo_svg_version_to_string:
320 * @version: a version id
322 * Get the string representation of the given @version id. This function
323 * will return %NULL if @version isn't valid. See cairo_svg_get_versions()
324 * for a way to get the list of valid version ids.
326 * Return value: the string associated to given version.
328 * Since: 1.2
330 const char *
331 cairo_svg_version_to_string (cairo_svg_version_t version)
333 if (version >= CAIRO_SVG_VERSION_LAST)
334 return NULL;
336 return _cairo_svg_version_strings[version];
339 static cairo_surface_t *
340 _cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
341 cairo_content_t content,
342 double width,
343 double height)
345 cairo_svg_surface_t *surface;
346 cairo_surface_t *paginated;
347 cairo_status_t status, status_ignored;
349 surface = malloc (sizeof (cairo_svg_surface_t));
350 if (unlikely (surface == NULL))
351 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
353 _cairo_surface_init (&surface->base, &cairo_svg_surface_backend,
354 content);
356 surface->width = width;
357 surface->height = height;
359 surface->document = _cairo_svg_document_reference (document);
361 surface->clip_level = 0;
363 surface->id = document->surface_id++;
364 surface->base_clip = document->clip_id++;
365 surface->is_base_clip_emitted = FALSE;
367 surface->xml_node = _cairo_memory_stream_create ();
368 status = _cairo_output_stream_get_status (surface->xml_node);
369 if (unlikely (status))
370 goto CLEANUP;
372 _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t));
374 if (content == CAIRO_CONTENT_COLOR) {
375 _cairo_output_stream_printf (surface->xml_node,
376 "<rect width=\"%f\" height=\"%f\" "
377 "style=\"opacity:1;stroke:none;"
378 "fill:rgb(0,0,0);\"/>\n",
379 width, height);
380 status = _cairo_output_stream_get_status (surface->xml_node);
381 if (unlikely (status))
382 goto CLEANUP;
385 surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
386 surface->force_fallbacks = FALSE;
387 surface->content = content;
389 paginated = _cairo_paginated_surface_create (&surface->base,
390 surface->content,
391 surface->width,
392 surface->height,
393 &cairo_svg_surface_paginated_backend);
394 status = paginated->status;
395 if (status == CAIRO_STATUS_SUCCESS) {
396 /* paginated keeps the only reference to surface now, drop ours */
397 cairo_surface_destroy (&surface->base);
398 return paginated;
401 /* ignore status as we are on the error path */
402 CLEANUP:
403 status_ignored = _cairo_output_stream_destroy (surface->xml_node);
404 status_ignored = _cairo_svg_document_destroy (document);
406 free (surface);
408 return _cairo_surface_create_in_error (status);
411 static cairo_surface_t *
412 _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
413 double width,
414 double height,
415 cairo_svg_version_t version)
417 cairo_svg_document_t *document = NULL; /* silence compiler */
418 cairo_surface_t *surface;
419 cairo_status_t status;
421 status = _cairo_svg_document_create (stream,
422 width, height, version,
423 &document);
424 if (unlikely (status)) {
425 surface = _cairo_surface_create_in_error (status);
426 /* consume the output stream on behalf of caller */
427 status = _cairo_output_stream_destroy (stream);
428 return surface;
431 surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA,
432 width, height);
433 if (surface->status) {
434 status = _cairo_svg_document_destroy (document);
435 return surface;
438 document->owner = surface;
439 status = _cairo_svg_document_destroy (document);
440 /* the ref count should be 2 at this point */
441 assert (status == CAIRO_STATUS_SUCCESS);
443 return surface;
446 static cairo_svg_page_t *
447 _cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
449 unsigned int i;
450 cairo_svg_page_t page;
451 cairo_output_stream_t *stream;
452 cairo_status_t status;
454 stream = _cairo_memory_stream_create ();
455 if (_cairo_output_stream_get_status (stream)) {
456 status = _cairo_output_stream_destroy (stream);
457 return NULL;
460 page.surface_id = surface->id;
461 page.clip_level = surface->clip_level;
462 page.xml_node = surface->xml_node;
464 if (_cairo_array_append (&surface->page_set, &page)) {
465 status = _cairo_output_stream_destroy (stream);
466 return NULL;
469 surface->xml_node = stream;
470 surface->clip_level = 0;
472 for (i = 0; i < page.clip_level; i++)
473 _cairo_output_stream_printf (page.xml_node, "</g>\n");
475 return _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
478 static cairo_int_status_t
479 _cairo_svg_surface_copy_page (void *abstract_surface)
481 cairo_svg_surface_t *surface = abstract_surface;
482 cairo_svg_page_t *page;
484 page = _cairo_svg_surface_store_page (surface);
485 if (unlikely (page == NULL))
486 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
488 _cairo_memory_stream_copy (page->xml_node, surface->xml_node);
489 surface->clip_level = page->clip_level;
491 return CAIRO_STATUS_SUCCESS;
494 static cairo_int_status_t
495 _cairo_svg_surface_show_page (void *abstract_surface)
497 cairo_svg_surface_t *surface = abstract_surface;
499 if (unlikely (_cairo_svg_surface_store_page (surface) == NULL))
500 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
502 return CAIRO_STATUS_SUCCESS;
505 static void
506 _cairo_svg_surface_emit_transform (cairo_output_stream_t *output,
507 char const *attribute_str,
508 const cairo_matrix_t *object_matrix,
509 const cairo_matrix_t *parent_matrix)
511 cairo_matrix_t matrix = *object_matrix;
513 if (parent_matrix != NULL)
514 cairo_matrix_multiply (&matrix, &matrix, parent_matrix);
516 if (!_cairo_matrix_is_identity (&matrix))
517 _cairo_output_stream_printf (output,
518 "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"",
519 attribute_str,
520 matrix.xx, matrix.yx,
521 matrix.xy, matrix.yy,
522 matrix.x0, matrix.y0);
525 typedef struct {
526 cairo_output_stream_t *output;
527 cairo_matrix_t *ctm_inverse;
528 } svg_path_info_t;
530 static cairo_status_t
531 _cairo_svg_path_move_to (void *closure,
532 const cairo_point_t *point)
534 svg_path_info_t *info = closure;
535 double x = _cairo_fixed_to_double (point->x);
536 double y = _cairo_fixed_to_double (point->y);
538 if (info->ctm_inverse)
539 cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
541 _cairo_output_stream_printf (info->output, "M %f %f ", x, y);
543 return CAIRO_STATUS_SUCCESS;
546 static cairo_status_t
547 _cairo_svg_path_line_to (void *closure,
548 const cairo_point_t *point)
550 svg_path_info_t *info = closure;
551 double x = _cairo_fixed_to_double (point->x);
552 double y = _cairo_fixed_to_double (point->y);
554 if (info->ctm_inverse)
555 cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
557 _cairo_output_stream_printf (info->output, "L %f %f ", x, y);
559 return CAIRO_STATUS_SUCCESS;
562 static cairo_status_t
563 _cairo_svg_path_curve_to (void *closure,
564 const cairo_point_t *b,
565 const cairo_point_t *c,
566 const cairo_point_t *d)
568 svg_path_info_t *info = closure;
569 double bx = _cairo_fixed_to_double (b->x);
570 double by = _cairo_fixed_to_double (b->y);
571 double cx = _cairo_fixed_to_double (c->x);
572 double cy = _cairo_fixed_to_double (c->y);
573 double dx = _cairo_fixed_to_double (d->x);
574 double dy = _cairo_fixed_to_double (d->y);
576 if (info->ctm_inverse) {
577 cairo_matrix_transform_point (info->ctm_inverse, &bx, &by);
578 cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy);
579 cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy);
582 _cairo_output_stream_printf (info->output,
583 "C %f %f %f %f %f %f ",
584 bx, by, cx, cy, dx, dy);
586 return CAIRO_STATUS_SUCCESS;
589 static cairo_status_t
590 _cairo_svg_path_close_path (void *closure)
592 svg_path_info_t *info = closure;
594 _cairo_output_stream_printf (info->output, "Z ");
596 return CAIRO_STATUS_SUCCESS;
599 static cairo_status_t
600 _cairo_svg_surface_emit_path (cairo_output_stream_t *output,
601 cairo_path_fixed_t *path,
602 cairo_matrix_t *ctm_inverse)
604 cairo_status_t status;
605 svg_path_info_t info;
607 _cairo_output_stream_printf (output, "d=\"");
609 info.output = output;
610 info.ctm_inverse = ctm_inverse;
611 status = _cairo_path_fixed_interpret (path,
612 CAIRO_DIRECTION_FORWARD,
613 _cairo_svg_path_move_to,
614 _cairo_svg_path_line_to,
615 _cairo_svg_path_curve_to,
616 _cairo_svg_path_close_path,
617 &info);
618 if (unlikely (status))
619 return status;
621 _cairo_output_stream_printf (output, "\"");
623 return status;
626 static cairo_int_status_t
627 _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document,
628 cairo_scaled_font_t *scaled_font,
629 unsigned long glyph_index)
631 cairo_scaled_glyph_t *scaled_glyph;
632 cairo_int_status_t status;
634 status = _cairo_scaled_glyph_lookup (scaled_font,
635 glyph_index,
636 CAIRO_SCALED_GLYPH_INFO_METRICS|
637 CAIRO_SCALED_GLYPH_INFO_PATH,
638 &scaled_glyph);
639 if (unlikely (status))
640 return status;
642 _cairo_output_stream_printf (document->xml_node_glyphs,
643 "<path style=\"stroke:none;\" ");
645 status = _cairo_svg_surface_emit_path (document->xml_node_glyphs, scaled_glyph->path, NULL);
646 if (unlikely (status))
647 return status;
649 _cairo_output_stream_printf (document->xml_node_glyphs,
650 "/>\n");
652 return status;
655 static cairo_int_status_t
656 _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document,
657 cairo_scaled_font_t *scaled_font,
658 unsigned long glyph_index)
660 cairo_scaled_glyph_t *scaled_glyph;
661 cairo_image_surface_t *image;
662 cairo_status_t status;
663 uint8_t *row, *byte;
664 int rows, cols;
665 int x, y, bit;
667 status = _cairo_scaled_glyph_lookup (scaled_font,
668 glyph_index,
669 CAIRO_SCALED_GLYPH_INFO_METRICS |
670 CAIRO_SCALED_GLYPH_INFO_SURFACE,
671 &scaled_glyph);
672 if (unlikely (status))
673 return status;
675 image = _cairo_image_surface_coerce (scaled_glyph->surface,
676 CAIRO_FORMAT_A1);
677 status = image->base.status;
678 if (unlikely (status))
679 return status;
681 _cairo_output_stream_printf (document->xml_node_glyphs, "<g");
682 _cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform",
683 &image->base.device_transform_inverse, NULL);
684 _cairo_output_stream_printf (document->xml_node_glyphs, ">/n");
686 for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) {
687 for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
688 uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
689 for (bit = 7; bit >= 0 && x < image->width; bit--, x++) {
690 if (output_byte & (1 << bit)) {
691 _cairo_output_stream_printf (document->xml_node_glyphs,
692 "<rect x=\"%d\" y=\"%d\" width=\"1\" height=\"1\"/>\n",
693 x, y);
698 _cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n");
700 cairo_surface_destroy (&image->base);
702 return CAIRO_STATUS_SUCCESS;
705 static cairo_status_t
706 _cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
707 cairo_scaled_font_t *scaled_font,
708 unsigned long scaled_font_glyph_index,
709 unsigned int font_id,
710 unsigned int subset_glyph_index)
712 cairo_status_t status;
714 _cairo_output_stream_printf (document->xml_node_glyphs,
715 "<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n",
716 font_id,
717 subset_glyph_index);
719 status = _cairo_svg_document_emit_outline_glyph_data (document,
720 scaled_font,
721 scaled_font_glyph_index);
722 if (status == CAIRO_INT_STATUS_UNSUPPORTED)
723 status = _cairo_svg_document_emit_bitmap_glyph_data (document,
724 scaled_font,
725 scaled_font_glyph_index);
726 if (unlikely (status))
727 return status;
729 _cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n");
731 return CAIRO_STATUS_SUCCESS;
734 static cairo_status_t
735 _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
736 void *closure)
738 cairo_svg_document_t *document = closure;
739 unsigned int i;
740 cairo_status_t status = CAIRO_STATUS_SUCCESS;
742 _cairo_scaled_font_freeze_cache (font_subset->scaled_font);
743 for (i = 0; i < font_subset->num_glyphs; i++) {
744 status = _cairo_svg_document_emit_glyph (document,
745 font_subset->scaled_font,
746 font_subset->glyphs[i],
747 font_subset->font_id, i);
748 if (unlikely (status))
749 break;
751 _cairo_scaled_font_thaw_cache (font_subset->scaled_font);
753 return status;
756 static cairo_status_t
757 _cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
759 cairo_status_t status;
761 status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
762 _cairo_svg_document_emit_font_subset,
763 document);
764 if (unlikely (status))
765 goto FAIL;
767 status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
768 _cairo_svg_document_emit_font_subset,
769 document);
771 FAIL:
772 _cairo_scaled_font_subsets_destroy (document->font_subsets);
773 document->font_subsets = NULL;
775 return status;
778 static char const *
779 _cairo_svg_surface_operators[] = {
780 "clear",
782 "src", "src-over", "src-in",
783 "src-out", "src-atop",
785 "dst", "dst-over", "dst-in",
786 "dst-out", "dst-atop",
788 "xor", "plus",
789 "color-dodge", /* FIXME: saturate ? */
792 static cairo_bool_t
793 _cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface,
794 cairo_operator_t op)
796 /* guard against newly added operators */
797 if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators))
798 return CAIRO_INT_STATUS_UNSUPPORTED;
800 /* allow operators being NULL if they are unsupported */
801 if (_cairo_svg_surface_operators[op] == NULL)
802 return CAIRO_INT_STATUS_UNSUPPORTED;
804 return CAIRO_STATUS_SUCCESS;
807 static cairo_int_status_t
808 _cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface,
809 cairo_operator_t op,
810 const cairo_pattern_t *pattern)
812 cairo_svg_document_t *document = surface->document;
814 if (surface->force_fallbacks &&
815 surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
817 return CAIRO_INT_STATUS_UNSUPPORTED;
820 /* SVG doesn't support extend reflect for image pattern */
821 if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
822 pattern->extend == CAIRO_EXTEND_REFLECT)
823 return CAIRO_INT_STATUS_UNSUPPORTED;
825 if (document->svg_version >= CAIRO_SVG_VERSION_1_2)
826 return _cairo_svg_surface_analyze_operator (surface, op);
828 if (op == CAIRO_OPERATOR_OVER)
829 return CAIRO_STATUS_SUCCESS;
831 /* The SOURCE operator is only supported if there is nothing
832 * painted underneath. */
833 if (op == CAIRO_OPERATOR_SOURCE)
834 return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
836 return CAIRO_INT_STATUS_UNSUPPORTED;
839 static cairo_int_status_t
840 _cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface,
841 cairo_operator_t op,
842 const cairo_pattern_t *pattern)
844 if (_cairo_svg_surface_analyze_operation (surface, op, pattern)
845 != CAIRO_INT_STATUS_UNSUPPORTED)
847 return TRUE;
848 } else {
849 return FALSE;
853 static cairo_surface_t *
854 _cairo_svg_surface_create_similar (void *abstract_src,
855 cairo_content_t content,
856 int width,
857 int height)
859 return _cairo_meta_surface_create (content, width, height);
862 static cairo_status_t
863 _cairo_svg_surface_finish (void *abstract_surface)
865 cairo_status_t status, status2;
866 cairo_svg_surface_t *surface = abstract_surface;
867 cairo_svg_document_t *document = surface->document;
868 cairo_svg_page_t *page;
869 unsigned int i;
871 if (_cairo_paginated_surface_get_target (document->owner) == &surface->base)
872 status = _cairo_svg_document_finish (document);
873 else
874 status = CAIRO_STATUS_SUCCESS;
876 if (surface->xml_node != NULL) {
877 status2 = _cairo_output_stream_destroy (surface->xml_node);
878 if (status == CAIRO_STATUS_SUCCESS)
879 status = status2;
882 for (i = 0; i < surface->page_set.num_elements; i++) {
883 page = _cairo_array_index (&surface->page_set, i);
884 status2 = _cairo_output_stream_destroy (page->xml_node);
885 if (status == CAIRO_STATUS_SUCCESS)
886 status = status2;
888 _cairo_array_fini (&surface->page_set);
890 status2 = _cairo_svg_document_destroy (document);
891 if (status == CAIRO_STATUS_SUCCESS)
892 status = status2;
894 return status;
897 static void
898 _cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document)
900 if (document->alpha_filter)
901 return;
903 _cairo_output_stream_printf (document->xml_node_defs,
904 "<filter id=\"alpha\" "
905 "filterUnits=\"objectBoundingBox\" "
906 "x=\"0%%\" y=\"0%%\" "
907 "width=\"100%%\" height=\"100%%\">\n"
908 " <feColorMatrix type=\"matrix\" "
909 "in=\"SourceGraphic\" "
910 "values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n"
911 "</filter>\n");
913 document->alpha_filter = TRUE;
916 typedef struct {
917 cairo_output_stream_t *output;
918 unsigned int in_mem;
919 unsigned int trailing;
920 unsigned char src[3];
921 } base64_write_closure_t;
923 static char const base64_table[64] =
924 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
926 static cairo_status_t
927 base64_write_func (void *closure,
928 const unsigned char *data,
929 unsigned int length)
931 base64_write_closure_t *info = (base64_write_closure_t *) closure;
932 unsigned int i;
933 unsigned char *src;
935 src = info->src;
937 if (info->in_mem + length < 3) {
938 for (i = 0; i < length; i++) {
939 src[i + info->in_mem] = *data++;
941 info->in_mem += length;
942 return CAIRO_STATUS_SUCCESS;
945 do {
946 unsigned char dst[4];
948 for (i = info->in_mem; i < 3; i++) {
949 src[i] = *data++;
950 length--;
952 info->in_mem = 0;
954 dst[0] = base64_table[src[0] >> 2];
955 dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4];
956 dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6];
957 dst[3] = base64_table[src[2] & 0xfc >> 2];
958 /* Special case for the last missing bits */
959 switch (info->trailing) {
960 case 2:
961 dst[2] = '=';
962 case 1:
963 dst[3] = '=';
964 default:
965 break;
967 _cairo_output_stream_write (info->output, dst, 4);
968 } while (length >= 3);
970 for (i = 0; i < length; i++) {
971 src[i] = *data++;
973 info->in_mem = length;
975 return _cairo_output_stream_get_status (info->output);
978 static cairo_int_status_t
979 _cairo_surface_base64_encode_jpeg (cairo_surface_t *surface,
980 cairo_output_stream_t *output)
982 const unsigned char *mime_data;
983 unsigned int mime_data_length;
984 cairo_image_info_t image_info;
985 base64_write_closure_t info;
986 cairo_status_t status;
988 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG,
989 &mime_data, &mime_data_length);
990 if (mime_data == NULL)
991 return CAIRO_INT_STATUS_UNSUPPORTED;
993 status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length);
994 if (unlikely (status))
995 return status;
997 _cairo_output_stream_printf (output, "data:image/jpeg;base64,");
999 info.output = output;
1000 info.in_mem = 0;
1001 info.trailing = 0;
1003 status = base64_write_func (&info, mime_data, mime_data_length);
1004 if (unlikely (status))
1005 return status;
1007 if (info.in_mem > 0) {
1008 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1009 info.trailing = 3 - info.in_mem;
1010 info.in_mem = 3;
1011 status = base64_write_func (&info, NULL, 0);
1014 return status;
1017 static cairo_int_status_t
1018 _cairo_surface_base64_encode_png (cairo_surface_t *surface,
1019 cairo_output_stream_t *output)
1021 const unsigned char *mime_data;
1022 unsigned int mime_data_length;
1023 base64_write_closure_t info;
1024 cairo_status_t status;
1026 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG,
1027 &mime_data, &mime_data_length);
1028 if (unlikely (surface->status))
1029 return surface->status;
1030 if (mime_data == NULL)
1031 return CAIRO_INT_STATUS_UNSUPPORTED;
1033 _cairo_output_stream_printf (output, "data:image/png;base64,");
1035 info.output = output;
1036 info.in_mem = 0;
1037 info.trailing = 0;
1039 status = base64_write_func (&info, mime_data, mime_data_length);
1040 if (unlikely (status))
1041 return status;
1043 if (info.in_mem > 0) {
1044 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1045 info.trailing = 3 - info.in_mem;
1046 info.in_mem = 3;
1047 status = base64_write_func (&info, NULL, 0);
1050 return status;
1053 static cairo_int_status_t
1054 _cairo_surface_base64_encode (cairo_surface_t *surface,
1055 cairo_output_stream_t *output)
1057 cairo_status_t status;
1058 base64_write_closure_t info;
1060 status = _cairo_surface_base64_encode_jpeg (surface, output);
1061 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1062 return status;
1064 status = _cairo_surface_base64_encode_png (surface, output);
1065 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1066 return status;
1068 info.output = output;
1069 info.in_mem = 0;
1070 info.trailing = 0;
1072 _cairo_output_stream_printf (info.output, "data:image/png;base64,");
1074 status = cairo_surface_write_to_png_stream (surface, base64_write_func,
1075 (void *) &info);
1077 if (unlikely (status))
1078 return status;
1080 if (info.in_mem > 0) {
1081 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1082 info.trailing = 3 - info.in_mem;
1083 info.in_mem = 3;
1084 status = base64_write_func (&info, NULL, 0);
1087 return status;
1090 static void
1091 _cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
1092 cairo_svg_surface_t *surface,
1093 cairo_operator_t op)
1095 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1096 op != CAIRO_OPERATOR_OVER) {
1097 _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]);
1098 if (!_cairo_operator_bounded_by_source (op))
1099 _cairo_output_stream_printf (output, " clip-to-self=\"true\"");
1103 static void
1104 _cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output,
1105 cairo_svg_surface_t *surface,
1106 cairo_operator_t op)
1108 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1109 op != CAIRO_OPERATOR_OVER) {
1110 _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]);
1111 if (!_cairo_operator_bounded_by_source (op))
1112 _cairo_output_stream_printf (output, "clip-to-self:true;");
1116 static cairo_status_t
1117 _cairo_svg_surface_emit_composite_image_pattern (cairo_output_stream_t *output,
1118 cairo_svg_surface_t *svg_surface,
1119 cairo_operator_t op,
1120 cairo_surface_pattern_t *pattern,
1121 int pattern_id,
1122 const cairo_matrix_t *parent_matrix,
1123 const char *extra_attributes)
1125 cairo_rectangle_int_t extents;
1126 cairo_status_t status;
1127 cairo_matrix_t p2u;
1129 status = _cairo_surface_get_extents (pattern->surface, &extents);
1130 if (unlikely (status))
1131 return status;
1133 p2u = pattern->base.matrix;
1134 status = cairo_matrix_invert (&p2u);
1135 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1136 assert (status == CAIRO_STATUS_SUCCESS);
1138 if (pattern_id != invalid_pattern_id) {
1139 _cairo_output_stream_printf (output,
1140 "<pattern id=\"pattern%d\" "
1141 "patternUnits=\"userSpaceOnUse\" "
1142 "width=\"%d\" height=\"%d\"",
1143 pattern_id,
1144 extents.width, extents.height);
1145 _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
1146 _cairo_output_stream_printf (output, ">\n");
1149 _cairo_output_stream_printf (output,
1150 " <image width=\"%d\" height=\"%d\"",
1151 extents.width, extents.height);
1153 if (pattern_id == invalid_pattern_id) {
1154 _cairo_svg_surface_emit_operator (output, svg_surface, op);
1155 _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
1158 if (extra_attributes)
1159 _cairo_output_stream_printf (output, " %s", extra_attributes);
1161 _cairo_output_stream_printf (output, " xlink:href=\"");
1163 status = _cairo_surface_base64_encode (pattern->surface, output);
1165 _cairo_output_stream_printf (output, "\"/>\n");
1167 if (pattern_id != invalid_pattern_id)
1168 _cairo_output_stream_printf (output, "</pattern>\n");
1170 return status;
1173 static cairo_status_t
1174 _cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document,
1175 cairo_meta_surface_t *surface,
1176 int *id)
1178 cairo_status_t status;
1179 cairo_surface_t *paginated_surface;
1180 cairo_svg_surface_t *svg_surface;
1181 cairo_meta_snapshot_t new_snapshot;
1182 cairo_array_t *page_set;
1184 cairo_output_stream_t *contents;
1185 cairo_meta_surface_t *meta;
1186 cairo_meta_snapshot_t *snapshot;
1187 unsigned int num_elements;
1188 unsigned int i;
1190 /* search in already emitted meta snapshots */
1191 num_elements = document->meta_snapshots.num_elements;
1192 for (i = 0; i < num_elements; i++) {
1193 snapshot = _cairo_array_index (&document->meta_snapshots, i);
1194 meta = snapshot->meta;
1195 if (meta->commands.num_elements == surface->commands.num_elements &&
1196 _cairo_array_index (&meta->commands, 0) == _cairo_array_index (&surface->commands, 0)) {
1197 *id = snapshot->id;
1198 return CAIRO_STATUS_SUCCESS;
1202 meta = (cairo_meta_surface_t *) _cairo_surface_snapshot (&surface->base);
1203 if (unlikely (meta->base.status))
1204 return meta->base.status;
1206 paginated_surface = _cairo_svg_surface_create_for_document (document,
1207 meta->content,
1208 meta->width_pixels,
1209 meta->height_pixels);
1210 if (paginated_surface->status) {
1211 cairo_surface_destroy (&meta->base);
1212 return paginated_surface->status;
1215 svg_surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (paginated_surface);
1216 cairo_surface_set_fallback_resolution (paginated_surface,
1217 document->owner->x_fallback_resolution,
1218 document->owner->y_fallback_resolution);
1220 status = _cairo_meta_surface_replay (&meta->base, paginated_surface);
1221 if (unlikely (status)) {
1222 cairo_surface_destroy (&meta->base);
1223 cairo_surface_destroy (paginated_surface);
1224 return status;
1227 cairo_surface_show_page (paginated_surface);
1228 status = cairo_surface_status (paginated_surface);
1229 if (unlikely (status)) {
1230 cairo_surface_destroy (&meta->base);
1231 cairo_surface_destroy (paginated_surface);
1232 return status;
1235 new_snapshot.meta = meta;
1236 new_snapshot.id = svg_surface->id;
1237 status = _cairo_array_append (&document->meta_snapshots, &new_snapshot);
1238 if (unlikely (status)) {
1239 cairo_surface_destroy (&meta->base);
1240 cairo_surface_destroy (paginated_surface);
1241 return status;
1244 if (!svg_surface->is_base_clip_emitted) {
1245 svg_surface->is_base_clip_emitted = TRUE;
1246 _cairo_output_stream_printf (document->xml_node_defs,
1247 "<clipPath id=\"clip%d\">\n"
1248 " <rect width=\"%f\" height=\"%f\"/>\n"
1249 "</clipPath>\n",
1250 svg_surface->base_clip,
1251 svg_surface->width,
1252 svg_surface->height);
1255 if (meta->content == CAIRO_CONTENT_ALPHA) {
1256 _cairo_svg_surface_emit_alpha_filter (document);
1257 _cairo_output_stream_printf (document->xml_node_defs,
1258 "<g id=\"surface%d\" "
1259 "clip-path=\"url(#clip%d)\" "
1260 "filter=\"url(#alpha)\">\n",
1261 svg_surface->id,
1262 svg_surface->base_clip);
1263 } else {
1264 _cairo_output_stream_printf (document->xml_node_defs,
1265 "<g id=\"surface%d\" "
1266 "clip-path=\"url(#clip%d)\">\n",
1267 svg_surface->id,
1268 svg_surface->base_clip);
1271 contents = svg_surface->xml_node;
1272 page_set = &svg_surface->page_set;
1274 if (_cairo_memory_stream_length (contents) > 0) {
1275 if (unlikely (_cairo_svg_surface_store_page (svg_surface) == NULL)) {
1276 cairo_surface_destroy (paginated_surface);
1277 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1281 if (page_set->num_elements > 0) {
1282 cairo_svg_page_t *page;
1284 page = _cairo_array_index (page_set, page_set->num_elements - 1);
1285 _cairo_memory_stream_copy (page->xml_node, document->xml_node_defs);
1288 _cairo_output_stream_printf (document->xml_node_defs, "</g>\n");
1290 *id = new_snapshot.id;
1292 status = cairo_surface_status (paginated_surface);
1293 cairo_surface_destroy (paginated_surface);
1295 /* FIXME: cairo_paginated_surface doesn't take a ref to the
1296 * passed in target surface so we can't call destroy here.
1297 * cairo_paginated_surface should be fixed, but for now just
1298 * work around it. */
1300 /* cairo_surface_destroy (svg_surface); */
1302 return status;
1305 static cairo_status_t
1306 _cairo_svg_surface_emit_composite_meta_pattern (cairo_output_stream_t *output,
1307 cairo_svg_surface_t *surface,
1308 cairo_operator_t op,
1309 cairo_surface_pattern_t *pattern,
1310 int pattern_id,
1311 const cairo_matrix_t *parent_matrix,
1312 const char *extra_attributes)
1314 cairo_svg_document_t *document = surface->document;
1315 cairo_meta_surface_t *meta_surface;
1316 cairo_matrix_t p2u;
1317 cairo_status_t status;
1318 int id = 0;
1320 p2u = pattern->base.matrix;
1321 status = cairo_matrix_invert (&p2u);
1322 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1323 assert (status == CAIRO_STATUS_SUCCESS);
1325 meta_surface = (cairo_meta_surface_t *) pattern->surface;
1327 status = _cairo_svg_surface_emit_meta_surface (document, meta_surface, &id);
1328 if (unlikely (status))
1329 return status;
1331 if (pattern_id != invalid_pattern_id) {
1332 _cairo_output_stream_printf (output,
1333 "<pattern id=\"pattern%d\" "
1334 "patternUnits=\"userSpaceOnUse\" "
1335 "width=\"%d\" height=\"%d\"",
1336 pattern_id,
1337 meta_surface->width_pixels,
1338 meta_surface->height_pixels);
1339 _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
1340 _cairo_output_stream_printf (output, ">\n");
1343 _cairo_output_stream_printf (output,
1344 "<use xlink:href=\"#surface%d\"",
1345 id);
1347 if (pattern_id == invalid_pattern_id) {
1348 _cairo_svg_surface_emit_operator (output, surface, op);
1349 _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
1352 if (extra_attributes)
1353 _cairo_output_stream_printf (output, " %s", extra_attributes);
1355 _cairo_output_stream_printf (output, "/>\n");
1357 if (pattern_id != invalid_pattern_id)
1358 _cairo_output_stream_printf (output, "</pattern>\n");
1360 return CAIRO_STATUS_SUCCESS;
1363 static cairo_status_t
1364 _cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output,
1365 cairo_svg_surface_t *surface,
1366 cairo_operator_t op,
1367 cairo_surface_pattern_t *pattern,
1368 int pattern_id,
1369 const cairo_matrix_t *parent_matrix,
1370 const char *extra_attributes)
1373 if (_cairo_surface_is_meta (pattern->surface)) {
1374 return _cairo_svg_surface_emit_composite_meta_pattern (output, surface, op, pattern,
1375 pattern_id, parent_matrix, extra_attributes);
1378 return _cairo_svg_surface_emit_composite_image_pattern (output, surface, op, pattern,
1379 pattern_id, parent_matrix, extra_attributes);
1382 static cairo_status_t
1383 _cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface,
1384 cairo_solid_pattern_t *pattern,
1385 cairo_output_stream_t *style,
1386 cairo_bool_t is_stroke)
1388 _cairo_output_stream_printf (style, is_stroke ?
1389 "stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;":
1390 "fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;",
1391 pattern->color.red * 100.0,
1392 pattern->color.green * 100.0,
1393 pattern->color.blue * 100.0,
1394 pattern->color.alpha);
1396 return CAIRO_STATUS_SUCCESS;
1399 static cairo_status_t
1400 _cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface,
1401 cairo_surface_pattern_t *pattern,
1402 cairo_output_stream_t *style,
1403 cairo_bool_t is_stroke,
1404 const cairo_matrix_t *parent_matrix)
1406 cairo_svg_document_t *document = surface->document;
1407 cairo_status_t status;
1408 int pattern_id;
1410 pattern_id = document->pattern_id++;
1411 status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs,
1412 surface, CAIRO_OPERATOR_SOURCE, pattern,
1413 pattern_id, parent_matrix, NULL);
1414 if (unlikely (status))
1415 return status;
1417 _cairo_output_stream_printf (style,
1418 "%s:url(#pattern%d);",
1419 is_stroke ? "stroke" : "fill",
1420 pattern_id);
1422 return CAIRO_STATUS_SUCCESS;
1425 static cairo_status_t
1426 _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output,
1427 cairo_gradient_pattern_t const *pattern,
1428 double start_offset,
1429 cairo_bool_t reverse_stops,
1430 cairo_bool_t emulate_reflect)
1432 cairo_gradient_stop_t *stops;
1433 double offset;
1434 unsigned int n_stops;
1435 unsigned int i;
1437 if (pattern->n_stops < 1)
1438 return CAIRO_STATUS_SUCCESS;
1440 if (pattern->n_stops == 1) {
1441 _cairo_output_stream_printf (output,
1442 "<stop offset=\"%f\" style=\""
1443 "stop-color:rgb(%f%%,%f%%,%f%%);"
1444 "stop-opacity:%f;\"/>\n",
1445 pattern->stops[0].offset,
1446 pattern->stops[0].color.red * 100.0,
1447 pattern->stops[0].color.green * 100.0,
1448 pattern->stops[0].color.blue * 100.0,
1449 pattern->stops[0].color.alpha);
1450 return CAIRO_STATUS_SUCCESS;
1453 if (emulate_reflect || reverse_stops) {
1454 n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops;
1455 stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t));
1456 if (unlikely (stops == NULL))
1457 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1459 for (i = 0; i < pattern->n_stops; i++) {
1460 if (reverse_stops) {
1461 stops[i] = pattern->stops[pattern->n_stops - i - 1];
1462 stops[i].offset = 1.0 - stops[i].offset;
1463 } else
1464 stops[i] = pattern->stops[i];
1465 if (emulate_reflect) {
1466 stops[i].offset /= 2;
1467 if (i > 0 && i < (pattern->n_stops - 1)) {
1468 if (reverse_stops) {
1469 stops[i + pattern->n_stops - 1] = pattern->stops[i];
1470 stops[i + pattern->n_stops - 1].offset =
1471 0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset;
1472 } else {
1473 stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1];
1474 stops[i + pattern->n_stops - 1].offset =
1475 1 - 0.5 * stops[i + pattern->n_stops - 1].offset;
1480 } else {
1481 n_stops = pattern->n_stops;
1482 stops = pattern->stops;
1485 if (start_offset >= 0.0)
1486 for (i = 0; i < n_stops; i++) {
1487 offset = start_offset + (1 - start_offset ) * stops[i].offset;
1488 _cairo_output_stream_printf (output,
1489 "<stop offset=\"%f\" style=\""
1490 "stop-color:rgb(%f%%,%f%%,%f%%);"
1491 "stop-opacity:%f;\"/>\n",
1492 offset,
1493 stops[i].color.red * 100.0,
1494 stops[i].color.green * 100.0,
1495 stops[i].color.blue * 100.0,
1496 stops[i].color.alpha);
1498 else {
1499 cairo_bool_t found = FALSE;
1500 unsigned int offset_index;
1501 cairo_color_t offset_color_start, offset_color_stop;
1503 for (i = 0; i < n_stops; i++) {
1504 if (stops[i].offset >= -start_offset) {
1505 if (i > 0) {
1506 if (stops[i].offset != stops[i-1].offset) {
1507 double x0, x1;
1508 cairo_color_t *color0, *color1;
1510 x0 = stops[i-1].offset;
1511 x1 = stops[i].offset;
1512 color0 = &stops[i-1].color;
1513 color1 = &stops[i].color;
1514 offset_color_start.red = color0->red + (color1->red - color0->red)
1515 * (-start_offset - x0) / (x1 - x0);
1516 offset_color_start.green = color0->green + (color1->green - color0->green)
1517 * (-start_offset - x0) / (x1 - x0);
1518 offset_color_start.blue = color0->blue + (color1->blue - color0->blue)
1519 * (-start_offset - x0) / (x1 - x0);
1520 offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha)
1521 * (-start_offset - x0) / (x1 - x0);
1522 offset_color_stop = offset_color_start;
1523 } else {
1524 offset_color_stop = stops[i-1].color;
1525 offset_color_start = stops[i].color;
1527 } else
1528 offset_color_stop = offset_color_start = stops[i].color;
1529 offset_index = i;
1530 found = TRUE;
1531 break;
1535 if (!found) {
1536 offset_index = n_stops - 1;
1537 offset_color_stop = offset_color_start = stops[offset_index].color;
1540 _cairo_output_stream_printf (output,
1541 "<stop offset=\"0\" style=\""
1542 "stop-color:rgb(%f%%,%f%%,%f%%);"
1543 "stop-opacity:%f;\"/>\n",
1544 offset_color_start.red * 100.0,
1545 offset_color_start.green * 100.0,
1546 offset_color_start.blue * 100.0,
1547 offset_color_start.alpha);
1548 for (i = offset_index; i < n_stops; i++) {
1549 _cairo_output_stream_printf (output,
1550 "<stop offset=\"%f\" style=\""
1551 "stop-color:rgb(%f%%,%f%%,%f%%);"
1552 "stop-opacity:%f;\"/>\n",
1553 stops[i].offset + start_offset,
1554 stops[i].color.red * 100.0,
1555 stops[i].color.green * 100.0,
1556 stops[i].color.blue * 100.0,
1557 stops[i].color.alpha);
1559 for (i = 0; i < offset_index; i++) {
1560 _cairo_output_stream_printf (output,
1561 "<stop offset=\"%f\" style=\""
1562 "stop-color:rgb(%f%%,%f%%,%f%%);"
1563 "stop-opacity:%f;\"/>\n",
1564 1.0 + stops[i].offset + start_offset,
1565 stops[i].color.red * 100.0,
1566 stops[i].color.green * 100.0,
1567 stops[i].color.blue * 100.0,
1568 stops[i].color.alpha);
1571 _cairo_output_stream_printf (output,
1572 "<stop offset=\"1\" style=\""
1573 "stop-color:rgb(%f%%,%f%%,%f%%);"
1574 "stop-opacity:%f;\"/>\n",
1575 offset_color_stop.red * 100.0,
1576 offset_color_stop.green * 100.0,
1577 offset_color_stop.blue * 100.0,
1578 offset_color_stop.alpha);
1582 if (reverse_stops || emulate_reflect)
1583 free (stops);
1585 return CAIRO_STATUS_SUCCESS;
1588 static void
1589 _cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output,
1590 cairo_pattern_t *pattern)
1592 switch (pattern->extend) {
1593 case CAIRO_EXTEND_REPEAT:
1594 _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" ");
1595 break;
1596 case CAIRO_EXTEND_REFLECT:
1597 _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" ");
1598 break;
1599 case CAIRO_EXTEND_NONE:
1600 case CAIRO_EXTEND_PAD:
1601 break;
1605 static cairo_status_t
1606 _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface,
1607 cairo_linear_pattern_t *pattern,
1608 cairo_output_stream_t *style,
1609 cairo_bool_t is_stroke,
1610 const cairo_matrix_t *parent_matrix)
1612 cairo_svg_document_t *document = surface->document;
1613 double x0, y0, x1, y1;
1614 cairo_matrix_t p2u;
1615 cairo_status_t status;
1617 p2u = pattern->base.base.matrix;
1618 status = cairo_matrix_invert (&p2u);
1619 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1620 assert (status == CAIRO_STATUS_SUCCESS);
1622 x0 = _cairo_fixed_to_double (pattern->p1.x);
1623 y0 = _cairo_fixed_to_double (pattern->p1.y);
1624 x1 = _cairo_fixed_to_double (pattern->p2.x);
1625 y1 = _cairo_fixed_to_double (pattern->p2.y);
1627 _cairo_output_stream_printf (document->xml_node_defs,
1628 "<linearGradient id=\"linear%d\" "
1629 "gradientUnits=\"userSpaceOnUse\" "
1630 "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
1631 document->linear_pattern_id,
1632 x0, y0, x1, y1);
1634 _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
1635 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1636 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1638 status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
1639 &pattern->base, 0.0,
1640 FALSE, FALSE);
1641 if (unlikely (status))
1642 return status;
1644 _cairo_output_stream_printf (document->xml_node_defs,
1645 "</linearGradient>\n");
1647 _cairo_output_stream_printf (style,
1648 "%s:url(#linear%d);",
1649 is_stroke ? "stroke" : "fill",
1650 document->linear_pattern_id);
1652 document->linear_pattern_id++;
1654 return CAIRO_STATUS_SUCCESS;
1657 static cairo_status_t
1658 _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface,
1659 cairo_radial_pattern_t *pattern,
1660 cairo_output_stream_t *style,
1661 cairo_bool_t is_stroke,
1662 const cairo_matrix_t *parent_matrix)
1664 cairo_svg_document_t *document = surface->document;
1665 cairo_matrix_t p2u;
1666 cairo_extend_t extend;
1667 double x0, y0, x1, y1, r0, r1;
1668 double fx, fy;
1669 cairo_bool_t reverse_stops;
1670 cairo_status_t status;
1671 cairo_point_t *c0, *c1;
1672 cairo_fixed_t radius0, radius1;
1674 extend = pattern->base.base.extend;
1676 if (pattern->r1 < pattern->r2) {
1677 c0 = &pattern->c1;
1678 c1 = &pattern->c2;
1679 radius0 = pattern->r1;
1680 radius1 = pattern->r2;
1681 reverse_stops = FALSE;
1682 } else {
1683 c0 = &pattern->c2;
1684 c1 = &pattern->c1;
1685 radius0 = pattern->r2;
1686 radius1 = pattern->r1;
1687 reverse_stops = TRUE;
1690 x0 = _cairo_fixed_to_double (c0->x);
1691 y0 = _cairo_fixed_to_double (c0->y);
1692 r0 = _cairo_fixed_to_double (radius0);
1693 x1 = _cairo_fixed_to_double (c1->x);
1694 y1 = _cairo_fixed_to_double (c1->y);
1695 r1 = _cairo_fixed_to_double (radius1);
1697 p2u = pattern->base.base.matrix;
1698 status = cairo_matrix_invert (&p2u);
1699 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1700 assert (status == CAIRO_STATUS_SUCCESS);
1702 if (pattern->r1 == pattern->r2) {
1703 unsigned int n_stops = pattern->base.n_stops;
1705 _cairo_output_stream_printf (document->xml_node_defs,
1706 "<radialGradient id=\"radial%d\" "
1707 "gradientUnits=\"userSpaceOnUse\" "
1708 "cx=\"%f\" cy=\"%f\" "
1709 "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1710 document->radial_pattern_id,
1711 x1, y1,
1712 x1, y1, r1);
1713 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1714 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1716 if (extend == CAIRO_EXTEND_NONE || n_stops < 1)
1717 _cairo_output_stream_printf (document->xml_node_defs,
1718 "<stop offset=\"0\" style=\""
1719 "stop-color:rgb(0%%,0%%,0%%);"
1720 "stop-opacity:0;\"/>\n");
1721 else {
1722 _cairo_output_stream_printf (document->xml_node_defs,
1723 "<stop offset=\"0\" style=\""
1724 "stop-color:rgb(%f%%,%f%%,%f%%);"
1725 "stop-opacity %f;\"/>\n",
1726 pattern->base.stops[0].color.red * 100.0,
1727 pattern->base.stops[0].color.green * 100.0,
1728 pattern->base.stops[0].color.blue * 100.0,
1729 pattern->base.stops[0].color.alpha);
1730 if (n_stops > 1)
1731 _cairo_output_stream_printf (document->xml_node_defs,
1732 "<stop offset=\"0\" style=\""
1733 "stop-color:rgb(%f%%,%f%%,%f%%);"
1734 "stop-opacity:%f;\"/>\n",
1735 pattern->base.stops[n_stops - 1].color.red * 100.0,
1736 pattern->base.stops[n_stops - 1].color.green * 100.0,
1737 pattern->base.stops[n_stops - 1].color.blue * 100.0,
1738 pattern->base.stops[n_stops - 1].color.alpha);
1741 } else {
1742 double offset, r, x, y;
1743 cairo_bool_t emulate_reflect = FALSE;
1745 fx = (r1 * x0 - r0 * x1) / (r1 - r0);
1746 fy = (r1 * y0 - r0 * y1) / (r1 - r0);
1748 /* SVG doesn't support the inner circle and use instead a gradient focal.
1749 * That means we need to emulate the cairo behaviour by processing the
1750 * cairo gradient stops.
1751 * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
1752 * it's just a matter of stop position translation and calculation of
1753 * the corresponding SVG radial gradient focal.
1754 * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
1755 * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
1756 * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
1757 * list that maps to the original cairo stop list.
1759 if ((extend == CAIRO_EXTEND_REFLECT
1760 || extend == CAIRO_EXTEND_REPEAT)
1761 && r0 > 0.0) {
1762 double r_org = r1;
1764 if (extend == CAIRO_EXTEND_REFLECT) {
1765 r1 = 2 * r1 - r0;
1766 emulate_reflect = TRUE;
1769 offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
1770 r = r1 - r0;
1772 /* New position of outer circle. */
1773 x = r * (x1 - fx) / r_org + fx;
1774 y = r * (y1 - fy) / r_org + fy;
1776 x1 = x;
1777 y1 = y;
1778 r1 = r;
1779 r0 = 0.0;
1780 } else {
1781 offset = r0 / r1;
1784 _cairo_output_stream_printf (document->xml_node_defs,
1785 "<radialGradient id=\"radial%d\" "
1786 "gradientUnits=\"userSpaceOnUse\" "
1787 "cx=\"%f\" cy=\"%f\" "
1788 "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1789 document->radial_pattern_id,
1790 x1, y1,
1791 fx, fy, r1);
1793 if (emulate_reflect)
1794 _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
1795 else
1796 _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
1797 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1798 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1800 /* To support cairo's EXTEND_NONE, (for which SVG has no similar
1801 * notion), we add transparent color stops on either end of the
1802 * user-provided stops. */
1803 if (extend == CAIRO_EXTEND_NONE) {
1804 _cairo_output_stream_printf (document->xml_node_defs,
1805 "<stop offset=\"0\" style=\""
1806 "stop-color:rgb(0%%,0%%,0%%);"
1807 "stop-opacity:0;\"/>\n");
1808 if (r0 != 0.0)
1809 _cairo_output_stream_printf (document->xml_node_defs,
1810 "<stop offset=\"%f\" style=\""
1811 "stop-color:rgb(0%%,0%%,0%%);"
1812 "stop-opacity:0;\"/>\n",
1813 r0 / r1);
1815 status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
1816 &pattern->base, offset,
1817 reverse_stops,
1818 emulate_reflect);
1819 if (unlikely (status))
1820 return status;
1822 if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
1823 _cairo_output_stream_printf (document->xml_node_defs,
1824 "<stop offset=\"1.0\" style=\""
1825 "stop-color:rgb(0%%,0%%,0%%);"
1826 "stop-opacity:0;\"/>\n");
1829 _cairo_output_stream_printf (document->xml_node_defs,
1830 "</radialGradient>\n");
1832 _cairo_output_stream_printf (style,
1833 "%s:url(#radial%d);",
1834 is_stroke ? "stroke" : "fill",
1835 document->radial_pattern_id);
1837 document->radial_pattern_id++;
1839 return CAIRO_STATUS_SUCCESS;
1842 static cairo_status_t
1843 _cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface,
1844 const cairo_pattern_t *pattern,
1845 cairo_output_stream_t *output,
1846 cairo_bool_t is_stroke,
1847 const cairo_matrix_t *parent_matrix)
1849 switch (pattern->type) {
1850 case CAIRO_PATTERN_TYPE_SOLID:
1851 return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern,
1852 output, is_stroke);
1854 case CAIRO_PATTERN_TYPE_SURFACE:
1855 return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern,
1856 output, is_stroke, parent_matrix);
1858 case CAIRO_PATTERN_TYPE_LINEAR:
1859 return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern,
1860 output, is_stroke, parent_matrix);
1862 case CAIRO_PATTERN_TYPE_RADIAL:
1863 return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern,
1864 output, is_stroke, parent_matrix);
1866 return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
1869 static cairo_status_t
1870 _cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output,
1871 cairo_svg_surface_t *surface,
1872 cairo_operator_t op,
1873 const cairo_pattern_t *source,
1874 cairo_fill_rule_t fill_rule,
1875 cairo_matrix_t *parent_matrix)
1877 _cairo_output_stream_printf (output,
1878 "fill-rule:%s;",
1879 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
1880 "evenodd" : "nonzero");
1881 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
1882 return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix);
1885 static cairo_status_t
1886 _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output,
1887 cairo_svg_surface_t *surface,
1888 cairo_operator_t op,
1889 const cairo_pattern_t *source,
1890 cairo_stroke_style_t *stroke_style,
1891 cairo_matrix_t *parent_matrix)
1893 cairo_status_t status;
1894 const char *line_cap, *line_join;
1895 unsigned int i;
1897 switch (stroke_style->line_cap) {
1898 case CAIRO_LINE_CAP_BUTT:
1899 line_cap = "butt";
1900 break;
1901 case CAIRO_LINE_CAP_ROUND:
1902 line_cap = "round";
1903 break;
1904 case CAIRO_LINE_CAP_SQUARE:
1905 line_cap = "square";
1906 break;
1907 default:
1908 ASSERT_NOT_REACHED;
1911 switch (stroke_style->line_join) {
1912 case CAIRO_LINE_JOIN_MITER:
1913 line_join = "miter";
1914 break;
1915 case CAIRO_LINE_JOIN_ROUND:
1916 line_join = "round";
1917 break;
1918 case CAIRO_LINE_JOIN_BEVEL:
1919 line_join = "bevel";
1920 break;
1921 default:
1922 ASSERT_NOT_REACHED;
1925 _cairo_output_stream_printf (output,
1926 "stroke-width:%f;"
1927 "stroke-linecap:%s;"
1928 "stroke-linejoin:%s;",
1929 stroke_style->line_width,
1930 line_cap,
1931 line_join);
1933 status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix);
1934 if (unlikely (status))
1935 return status;
1937 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
1939 if (stroke_style->num_dashes > 0) {
1940 _cairo_output_stream_printf (output, "stroke-dasharray:");
1941 for (i = 0; i < stroke_style->num_dashes; i++) {
1942 _cairo_output_stream_printf (output, "%f",
1943 stroke_style->dash[i]);
1944 if (i + 1 < stroke_style->num_dashes)
1945 _cairo_output_stream_printf (output, ",");
1946 else
1947 _cairo_output_stream_printf (output, ";");
1949 if (stroke_style->dash_offset != 0.0) {
1950 _cairo_output_stream_printf (output,
1951 "stroke-dashoffset:%f;",
1952 stroke_style->dash_offset);
1956 _cairo_output_stream_printf (output,
1957 "stroke-miterlimit:%f;",
1958 stroke_style->miter_limit);
1960 return CAIRO_STATUS_SUCCESS;
1963 static cairo_int_status_t
1964 _cairo_svg_surface_fill_stroke (void *abstract_surface,
1965 cairo_operator_t fill_op,
1966 const cairo_pattern_t *fill_source,
1967 cairo_fill_rule_t fill_rule,
1968 double fill_tolerance,
1969 cairo_antialias_t fill_antialias,
1970 cairo_path_fixed_t *path,
1971 cairo_operator_t stroke_op,
1972 const cairo_pattern_t *stroke_source,
1973 cairo_stroke_style_t *stroke_style,
1974 cairo_matrix_t *stroke_ctm,
1975 cairo_matrix_t *stroke_ctm_inverse,
1976 double stroke_tolerance,
1977 cairo_antialias_t stroke_antialias,
1978 cairo_rectangle_int_t *extents)
1980 cairo_svg_surface_t *surface = abstract_surface;
1981 cairo_status_t status;
1983 _cairo_output_stream_printf (surface->xml_node, "<path style=\"");
1984 status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, fill_op,
1985 fill_source, fill_rule, stroke_ctm_inverse);
1986 if (unlikely (status))
1987 return status;
1989 status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op,
1990 stroke_source, stroke_style, stroke_ctm_inverse);
1991 if (unlikely (status))
1992 return status;
1994 _cairo_output_stream_printf (surface->xml_node, "\" ");
1996 status = _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse);
1997 if (unlikely (status))
1998 return status;
2000 _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL);
2001 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2003 return CAIRO_STATUS_SUCCESS;
2006 static cairo_int_status_t
2007 _cairo_svg_surface_fill (void *abstract_surface,
2008 cairo_operator_t op,
2009 const cairo_pattern_t *source,
2010 cairo_path_fixed_t *path,
2011 cairo_fill_rule_t fill_rule,
2012 double tolerance,
2013 cairo_antialias_t antialias,
2014 cairo_rectangle_int_t *extents)
2016 cairo_svg_surface_t *surface = abstract_surface;
2017 cairo_status_t status;
2019 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2020 return _cairo_svg_surface_analyze_operation (surface, op, source);
2022 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2024 _cairo_output_stream_printf (surface->xml_node, "<path style=\" stroke:none;");
2025 status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, op, source, fill_rule, NULL);
2026 if (unlikely (status))
2027 return status;
2029 _cairo_output_stream_printf (surface->xml_node, "\" ");
2031 status = _cairo_svg_surface_emit_path (surface->xml_node, path, NULL);
2032 if (unlikely (status))
2033 return status;
2035 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2037 return CAIRO_STATUS_SUCCESS;
2040 static cairo_int_status_t
2041 _cairo_svg_surface_get_extents (void *abstract_surface,
2042 cairo_rectangle_int_t *rectangle)
2044 cairo_svg_surface_t *surface = abstract_surface;
2046 rectangle->x = 0;
2047 rectangle->y = 0;
2049 /* XXX: The conversion to integers here is pretty bogus, (not to
2050 * mention the aribitray limitation of width to a short(!). We
2051 * may need to come up with a better interface for get_size.
2053 rectangle->width = (int) ceil (surface->width);
2054 rectangle->height = (int) ceil (surface->height);
2056 return CAIRO_STATUS_SUCCESS;
2059 static cairo_status_t
2060 _cairo_svg_surface_emit_paint (cairo_output_stream_t *output,
2061 cairo_svg_surface_t *surface,
2062 cairo_operator_t op,
2063 const cairo_pattern_t *source,
2064 const cairo_pattern_t *mask_source,
2065 const char *extra_attributes)
2067 cairo_status_t status;
2069 if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
2070 source->extend == CAIRO_EXTEND_NONE)
2071 return _cairo_svg_surface_emit_composite_pattern (output,
2072 surface,
2074 (cairo_surface_pattern_t *) source,
2075 invalid_pattern_id,
2076 mask_source ? &mask_source->matrix :NULL,
2077 extra_attributes);
2079 _cairo_output_stream_printf (output,
2080 "<rect x=\"0\" y=\"0\" "
2081 "width=\"%f\" height=\"%f\" "
2082 "style=\"",
2083 surface->width, surface->height);
2084 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2085 status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL);
2086 if (unlikely (status))
2087 return status;
2089 _cairo_output_stream_printf (output, "stroke:none;\"");
2091 if (extra_attributes)
2092 _cairo_output_stream_printf (output, " %s", extra_attributes);
2094 _cairo_output_stream_printf (output, "/>\n");
2096 return CAIRO_STATUS_SUCCESS;
2099 static cairo_int_status_t
2100 _cairo_svg_surface_paint (void *abstract_surface,
2101 cairo_operator_t op,
2102 const cairo_pattern_t *source,
2103 cairo_rectangle_int_t *extents)
2105 cairo_status_t status;
2106 cairo_svg_surface_t *surface = abstract_surface;
2108 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2109 return _cairo_svg_surface_analyze_operation (surface, op, source);
2111 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2113 /* Emulation of clear and source operators, when no clipping region
2114 * is defined. We just delete existing content of surface root node,
2115 * and exit early if operator is clear.
2116 * XXX: optimization of SOURCE operator doesn't work, since analyze
2117 * above always return FALSE. In order to make it work, we need a way
2118 * to know if there's an active clipping path.
2119 * Optimization of CLEAR works because of a test in paginated surface,
2120 * and an optimization in meta surface. */
2121 if (surface->clip_level == 0 && op == CAIRO_OPERATOR_CLEAR) {
2122 status = _cairo_output_stream_destroy (surface->xml_node);
2123 if (unlikely (status)) {
2124 surface->xml_node = NULL;
2125 return status;
2128 surface->xml_node = _cairo_memory_stream_create ();
2129 if (_cairo_output_stream_get_status (surface->xml_node)) {
2130 status = _cairo_output_stream_destroy (surface->xml_node);
2131 surface->xml_node = NULL;
2132 return status;
2135 if (op == CAIRO_OPERATOR_CLEAR) {
2136 if (surface->content == CAIRO_CONTENT_COLOR) {
2137 _cairo_output_stream_printf (surface->xml_node,
2138 "<rect "
2139 "width=\"%f\" height=\"%f\" "
2140 "style=\"opacity:1;"
2141 "stroke:none;"
2142 "fill:rgb(0,0,0);\"/>\n",
2143 surface->width, surface->height);
2145 return CAIRO_STATUS_SUCCESS;
2149 return _cairo_svg_surface_emit_paint (surface->xml_node,
2150 surface, op, source, 0, NULL);
2153 static cairo_int_status_t
2154 _cairo_svg_surface_mask (void *abstract_surface,
2155 cairo_operator_t op,
2156 const cairo_pattern_t *source,
2157 const cairo_pattern_t *mask,
2158 cairo_rectangle_int_t *extents)
2160 cairo_status_t status;
2161 cairo_svg_surface_t *surface = abstract_surface;
2162 cairo_svg_document_t *document = surface->document;
2163 cairo_output_stream_t *mask_stream;
2164 char buffer[64];
2165 cairo_bool_t discard_filter = FALSE;
2166 unsigned int mask_id;
2168 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2169 cairo_status_t source_status, mask_status;
2171 source_status = _cairo_svg_surface_analyze_operation (surface, op, source);
2172 if (_cairo_status_is_error (source_status))
2173 return source_status;
2175 mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask);
2176 if (_cairo_status_is_error (mask_status))
2177 return mask_status;
2179 return _cairo_analysis_surface_merge_status (source_status,
2180 mask_status);
2183 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2184 assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask));
2186 if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
2187 const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask;
2188 cairo_content_t content = cairo_surface_get_content (surface_pattern->surface);
2189 if (content == CAIRO_CONTENT_ALPHA)
2190 discard_filter = TRUE;
2193 if (!discard_filter)
2194 _cairo_svg_surface_emit_alpha_filter (document);
2196 /* _cairo_svg_surface_emit_paint() will output a pattern definition to
2197 * document->xml_node_defs so we need to write the mask element to
2198 * a temporary stream and then copy that to xml_node_defs. */
2199 mask_stream = _cairo_memory_stream_create ();
2200 if (_cairo_output_stream_get_status (mask_stream))
2201 return _cairo_output_stream_destroy (mask_stream);
2203 mask_id = _cairo_svg_document_allocate_mask_id (document);
2205 _cairo_output_stream_printf (mask_stream,
2206 "<mask id=\"mask%d\">\n"
2207 "%s",
2208 mask_id,
2209 discard_filter ? "" : " <g filter=\"url(#alpha)\">\n");
2210 status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL);
2211 if (unlikely (status)) {
2212 cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream);
2213 return status;
2214 (void) ignore;
2217 _cairo_output_stream_printf (mask_stream,
2218 "%s"
2219 "</mask>\n",
2220 discard_filter ? "" : " </g>\n");
2221 _cairo_memory_stream_copy (mask_stream, document->xml_node_defs);
2223 status = _cairo_output_stream_destroy (mask_stream);
2224 if (unlikely (status))
2225 return status;
2227 snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"",
2228 mask_id);
2229 status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer);
2230 if (unlikely (status))
2231 return status;
2233 return CAIRO_STATUS_SUCCESS;
2236 static cairo_int_status_t
2237 _cairo_svg_surface_stroke (void *abstract_dst,
2238 cairo_operator_t op,
2239 const cairo_pattern_t *source,
2240 cairo_path_fixed_t *path,
2241 cairo_stroke_style_t *stroke_style,
2242 cairo_matrix_t *ctm,
2243 cairo_matrix_t *ctm_inverse,
2244 double tolerance,
2245 cairo_antialias_t antialias,
2246 cairo_rectangle_int_t *extents)
2248 cairo_svg_surface_t *surface = abstract_dst;
2249 cairo_status_t status;
2251 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2252 return _cairo_svg_surface_analyze_operation (surface, op, source);
2254 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2256 _cairo_output_stream_printf (surface->xml_node, "<path style=\"fill:none;");
2257 status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, op,
2258 source, stroke_style, ctm_inverse);
2259 if (unlikely (status))
2260 return status;
2262 _cairo_output_stream_printf (surface->xml_node, "\" ");
2264 status = _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse);
2265 if (unlikely (status))
2266 return status;
2268 _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL);
2269 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2271 return CAIRO_STATUS_SUCCESS;
2274 static cairo_int_status_t
2275 _cairo_svg_surface_show_glyphs (void *abstract_surface,
2276 cairo_operator_t op,
2277 const cairo_pattern_t *pattern,
2278 cairo_glyph_t *glyphs,
2279 int num_glyphs,
2280 cairo_scaled_font_t *scaled_font,
2281 int *remaining_glyphs,
2282 cairo_rectangle_int_t *extents)
2284 cairo_svg_surface_t *surface = abstract_surface;
2285 cairo_svg_document_t *document = surface->document;
2286 cairo_path_fixed_t path;
2287 cairo_status_t status;
2288 cairo_scaled_font_subsets_glyph_t subset_glyph;
2289 int i;
2291 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2292 return _cairo_svg_surface_analyze_operation (surface, op, pattern);
2294 assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
2296 if (num_glyphs <= 0)
2297 return CAIRO_STATUS_SUCCESS;
2299 /* FIXME it's probably possible to apply a pattern of a gradient to
2300 * a group of symbols, but I don't know how yet. Gradients or patterns
2301 * are translated by x and y properties of use element. */
2302 if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
2303 goto FALLBACK;
2305 _cairo_output_stream_printf (surface->xml_node, "<g style=\"");
2306 status = _cairo_svg_surface_emit_pattern (surface, pattern,
2307 surface->xml_node, FALSE, NULL);
2308 if (unlikely (status))
2309 return status;
2311 _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op);
2313 _cairo_output_stream_printf (surface->xml_node, "\">\n");
2315 for (i = 0; i < num_glyphs; i++) {
2316 status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets,
2317 scaled_font, glyphs[i].index,
2318 NULL, 0,
2319 &subset_glyph);
2320 if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
2321 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2323 glyphs += i;
2324 num_glyphs -= i;
2325 goto FALLBACK;
2328 if (unlikely (status))
2329 return status;
2331 _cairo_output_stream_printf (surface->xml_node,
2332 " <use xlink:href=\"#glyph%d-%d\" "
2333 "x=\"%f\" y=\"%f\"/>\n",
2334 subset_glyph.font_id,
2335 subset_glyph.subset_glyph_index,
2336 glyphs[i].x, glyphs[i].y);
2339 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2341 return CAIRO_STATUS_SUCCESS;
2343 FALLBACK:
2344 _cairo_path_fixed_init (&path);
2346 status = _cairo_scaled_font_glyph_path (scaled_font,(cairo_glyph_t *) glyphs, num_glyphs, &path);
2348 if (unlikely (status)) {
2349 _cairo_path_fixed_fini (&path);
2350 return status;
2353 status = _cairo_svg_surface_fill (abstract_surface, op, pattern,
2354 &path, CAIRO_FILL_RULE_WINDING, 0.0, CAIRO_ANTIALIAS_SUBPIXEL, NULL);
2356 _cairo_path_fixed_fini (&path);
2358 return status;
2361 static cairo_int_status_t
2362 _cairo_svg_surface_intersect_clip_path (void *dst,
2363 cairo_path_fixed_t *path,
2364 cairo_fill_rule_t fill_rule,
2365 double tolerance,
2366 cairo_antialias_t antialias)
2368 cairo_svg_surface_t *surface = dst;
2369 cairo_svg_document_t *document = surface->document;
2370 cairo_status_t status;
2371 unsigned int i;
2373 if (path == NULL) {
2374 for (i = 0; i < surface->clip_level; i++)
2375 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2377 surface->clip_level = 0;
2378 return CAIRO_STATUS_SUCCESS;
2381 _cairo_output_stream_printf (document->xml_node_defs,
2382 "<clipPath id=\"clip%d\">\n"
2383 " <path ",
2384 document->clip_id);
2385 status = _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL);
2386 if (unlikely (status))
2387 return status;
2389 _cairo_output_stream_printf (document->xml_node_defs,
2390 "/>\n"
2391 "</clipPath>\n");
2393 _cairo_output_stream_printf (surface->xml_node,
2394 "<g clip-path=\"url(#clip%d)\" "
2395 "clip-rule=\"%s\">\n",
2396 document->clip_id,
2397 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
2398 "evenodd" : "nonzero");
2400 document->clip_id++;
2401 surface->clip_level++;
2403 return CAIRO_STATUS_SUCCESS;
2406 static void
2407 _cairo_svg_surface_get_font_options (void *abstract_surface,
2408 cairo_font_options_t *options)
2410 _cairo_font_options_init_default (options);
2412 cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
2413 cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
2414 cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
2417 static const cairo_surface_backend_t cairo_svg_surface_backend = {
2418 CAIRO_SURFACE_TYPE_SVG,
2419 _cairo_svg_surface_create_similar,
2420 _cairo_svg_surface_finish,
2421 NULL, /* acquire_source_image */
2422 NULL, /* release_source_image */
2423 NULL, /* acquire_dest_image */
2424 NULL, /* release_dest_image */
2425 NULL, /* clone_similar */
2426 NULL, /* _cairo_svg_surface_composite, */
2427 NULL, /* _cairo_svg_surface_fill_rectangles, */
2428 NULL, /* _cairo_svg_surface_composite_trapezoids,*/
2429 NULL, /* create_span_renderer */
2430 NULL, /* check_span_renderer */
2431 _cairo_svg_surface_copy_page,
2432 _cairo_svg_surface_show_page,
2433 NULL, /* set_clip_region */
2434 _cairo_svg_surface_intersect_clip_path,
2435 _cairo_svg_surface_get_extents,
2436 NULL, /* _cairo_svg_surface_old_show_glyphs, */
2437 _cairo_svg_surface_get_font_options,
2438 NULL, /* flush */
2439 NULL, /* mark dirty rectangle */
2440 NULL, /* scaled font fini */
2441 NULL, /* scaled glyph fini */
2442 _cairo_svg_surface_paint,
2443 _cairo_svg_surface_mask,
2444 _cairo_svg_surface_stroke,
2445 _cairo_svg_surface_fill,
2446 _cairo_svg_surface_show_glyphs,
2447 NULL, /* snapshot */
2448 NULL, /* is_similar */
2449 NULL, /* reset */
2450 _cairo_svg_surface_fill_stroke
2453 static cairo_status_t
2454 _cairo_svg_document_create (cairo_output_stream_t *output_stream,
2455 double width,
2456 double height,
2457 cairo_svg_version_t version,
2458 cairo_svg_document_t **document_out)
2460 cairo_svg_document_t *document;
2461 cairo_status_t status, status_ignored;
2463 if (output_stream->status)
2464 return output_stream->status;
2466 document = malloc (sizeof (cairo_svg_document_t));
2467 if (unlikely (document == NULL))
2468 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2470 /* The use of defs for font glyphs imposes no per-subset limit. */
2471 document->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
2472 if (unlikely (document->font_subsets == NULL)) {
2473 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2474 goto CLEANUP_DOCUMENT;
2477 document->output_stream = output_stream;
2478 document->refcount = 1;
2479 document->owner = NULL;
2480 document->finished = FALSE;
2481 document->width = width;
2482 document->height = height;
2484 document->surface_id = 0;
2485 document->linear_pattern_id = 0;
2486 document->radial_pattern_id = 0;
2487 document->pattern_id = 0;
2488 document->filter_id = 0;
2489 document->clip_id = 0;
2490 document->mask_id = 0;
2492 document->xml_node_defs = _cairo_memory_stream_create ();
2493 status = _cairo_output_stream_get_status (document->xml_node_defs);
2494 if (unlikely (status))
2495 goto CLEANUP_NODE_DEFS;
2497 document->xml_node_glyphs = _cairo_memory_stream_create ();
2498 status = _cairo_output_stream_get_status (document->xml_node_glyphs);
2499 if (unlikely (status))
2500 goto CLEANUP_NODE_GLYPHS;
2502 document->alpha_filter = FALSE;
2504 _cairo_array_init (&document->meta_snapshots,
2505 sizeof (cairo_meta_snapshot_t));
2507 document->svg_version = version;
2509 *document_out = document;
2510 return CAIRO_STATUS_SUCCESS;
2512 CLEANUP_NODE_GLYPHS:
2513 status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs);
2514 CLEANUP_NODE_DEFS:
2515 status_ignored = _cairo_output_stream_destroy (document->xml_node_defs);
2516 _cairo_scaled_font_subsets_destroy (document->font_subsets);
2517 CLEANUP_DOCUMENT:
2518 free (document);
2519 return status;
2522 static cairo_svg_document_t *
2523 _cairo_svg_document_reference (cairo_svg_document_t *document)
2525 document->refcount++;
2527 return document;
2530 static unsigned int
2531 _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document)
2533 return document->mask_id++;
2536 static cairo_status_t
2537 _cairo_svg_document_destroy (cairo_svg_document_t *document)
2539 cairo_status_t status;
2541 document->refcount--;
2542 if (document->refcount > 0)
2543 return CAIRO_STATUS_SUCCESS;
2545 status = _cairo_svg_document_finish (document);
2547 free (document);
2549 return status;
2552 static cairo_status_t
2553 _cairo_svg_document_finish (cairo_svg_document_t *document)
2555 cairo_status_t status, status2;
2556 cairo_output_stream_t *output = document->output_stream;
2557 cairo_meta_snapshot_t *snapshot;
2558 cairo_svg_page_t *page;
2559 unsigned int i;
2561 if (document->finished)
2562 return CAIRO_STATUS_SUCCESS;
2565 * Should we add DOCTYPE?
2567 * Google says no.
2569 * http://tech.groups.yahoo.com/group/svg-developers/message/48562:
2570 * There's a bunch of issues, but just to pick a few:
2571 * - they'll give false positives.
2572 * - they'll give false negatives.
2573 * - they're namespace-unaware.
2574 * - they don't wildcard.
2575 * So when they say OK they really haven't checked anything, when
2576 * they say NOT OK they might be on crack, and like all
2577 * namespace-unaware things they're a dead branch of the XML tree.
2579 * http://jwatt.org/svg/authoring/:
2580 * Unfortunately the SVG DTDs are a source of so many issues that the
2581 * SVG WG has decided not to write one for the upcoming SVG 1.2
2582 * standard. In fact SVG WG members are even telling people not to use
2583 * a DOCTYPE declaration in SVG 1.0 and 1.1 documents.
2586 _cairo_output_stream_printf (output,
2587 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2588 "<svg xmlns=\"http://www.w3.org/2000/svg\" "
2589 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
2590 "width=\"%fpt\" height=\"%fpt\" "
2591 "viewBox=\"0 0 %f %f\" version=\"%s\">\n",
2592 document->width, document->height,
2593 document->width, document->height,
2594 _cairo_svg_internal_version_strings [document->svg_version]);
2596 status = _cairo_svg_document_emit_font_subsets (document);
2598 if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 ||
2599 _cairo_memory_stream_length (document->xml_node_defs) > 0) {
2600 _cairo_output_stream_printf (output, "<defs>\n");
2601 if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
2602 _cairo_output_stream_printf (output, "<g>\n");
2603 _cairo_memory_stream_copy (document->xml_node_glyphs, output);
2604 _cairo_output_stream_printf (output, "</g>\n");
2606 _cairo_memory_stream_copy (document->xml_node_defs, output);
2607 _cairo_output_stream_printf (output, "</defs>\n");
2610 if (document->owner != NULL) {
2611 cairo_svg_surface_t *surface;
2613 surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner);
2614 if (surface->xml_node != NULL &&
2615 _cairo_memory_stream_length (surface->xml_node) > 0) {
2616 if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) {
2617 if (status == CAIRO_STATUS_SUCCESS)
2618 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2622 if (surface->page_set.num_elements > 1 &&
2623 _cairo_svg_version_has_page_set_support (document->svg_version)) {
2624 _cairo_output_stream_printf (output, "<pageSet>\n");
2625 for (i = 0; i < surface->page_set.num_elements; i++) {
2626 page = _cairo_array_index (&surface->page_set, i);
2627 _cairo_output_stream_printf (output, "<page>\n");
2628 _cairo_output_stream_printf (output,
2629 "<g id=\"surface%d\">\n",
2630 page->surface_id);
2631 _cairo_memory_stream_copy (page->xml_node, output);
2632 _cairo_output_stream_printf (output, "</g>\n</page>\n");
2634 _cairo_output_stream_printf (output, "</pageSet>\n");
2635 } else if (surface->page_set.num_elements > 0) {
2636 page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
2637 _cairo_output_stream_printf (output,
2638 "<g id=\"surface%d\">\n",
2639 page->surface_id);
2640 _cairo_memory_stream_copy (page->xml_node, output);
2641 _cairo_output_stream_printf (output, "</g>\n");
2645 _cairo_output_stream_printf (output, "</svg>\n");
2647 status2 = _cairo_output_stream_destroy (document->xml_node_glyphs);
2648 if (status == CAIRO_STATUS_SUCCESS)
2649 status = status2;
2651 status2 = _cairo_output_stream_destroy (document->xml_node_defs);
2652 if (status == CAIRO_STATUS_SUCCESS)
2653 status = status2;
2655 status2 = _cairo_output_stream_destroy (output);
2656 if (status == CAIRO_STATUS_SUCCESS)
2657 status = status2;
2659 for (i = 0; i < document->meta_snapshots.num_elements; i++) {
2660 snapshot = _cairo_array_index (&document->meta_snapshots, i);
2661 cairo_surface_finish (&snapshot->meta->base);
2662 status2 = cairo_surface_status (&snapshot->meta->base);
2663 cairo_surface_destroy (&snapshot->meta->base);
2664 if (status == CAIRO_STATUS_SUCCESS)
2665 status = status2;
2667 _cairo_array_fini (&document->meta_snapshots);
2669 document->finished = TRUE;
2671 return status;
2674 static void
2675 _cairo_svg_surface_set_paginated_mode (void *abstract_surface,
2676 cairo_paginated_mode_t paginated_mode)
2678 cairo_svg_surface_t *surface = abstract_surface;
2680 surface->paginated_mode = paginated_mode;
2683 static cairo_bool_t
2684 _cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface)
2686 cairo_svg_surface_t *surface = abstract_surface;
2687 cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
2689 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) {
2690 status = _cairo_svg_surface_analyze_operator (surface,
2691 CAIRO_OPERATOR_SOURCE);
2694 return status == CAIRO_STATUS_SUCCESS;
2697 static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
2698 NULL /*_cairo_svg_surface_start_page*/,
2699 _cairo_svg_surface_set_paginated_mode,
2700 NULL, /* _cairo_svg_surface_set_bounding_box */
2701 NULL, /* _cairo_svg_surface_set_fallback_images_required */
2702 _cairo_svg_surface_supports_fine_grained_fallbacks,