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
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() */
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
,
64 #define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
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
] =
78 static const char * _cairo_svg_internal_version_strings
[CAIRO_SVG_VERSION_LAST
] =
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
;
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
;
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
,
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
);
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
,
148 static cairo_surface_t
*
149 _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t
*stream
,
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
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.
181 cairo_svg_surface_create_for_stream (cairo_write_func_t write_func
,
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
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
207 * Return value: a pointer to the newly created surface. The caller
208 * owns the surface and should call cairo_surface_destroy() when done
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.
218 cairo_svg_surface_create (const char *filename
,
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
);
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
;
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
);
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
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
);
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().
308 cairo_svg_get_versions (cairo_svg_version_t
const **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.
331 cairo_svg_version_to_string (cairo_svg_version_t version
)
333 if (version
>= CAIRO_SVG_VERSION_LAST
)
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
,
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
,
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
))
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",
380 status
= _cairo_output_stream_get_status (surface
->xml_node
);
381 if (unlikely (status
))
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
,
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
);
401 /* ignore status as we are on the error path */
403 status_ignored
= _cairo_output_stream_destroy (surface
->xml_node
);
404 status_ignored
= _cairo_svg_document_destroy (document
);
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
,
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
,
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
);
431 surface
= _cairo_svg_surface_create_for_document (document
, CAIRO_CONTENT_COLOR_ALPHA
,
433 if (surface
->status
) {
434 status
= _cairo_svg_document_destroy (document
);
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
);
446 static cairo_svg_page_t
*
447 _cairo_svg_surface_store_page (cairo_svg_surface_t
*surface
)
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
);
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
);
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
;
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)\"",
520 matrix
.xx
, matrix
.yx
,
521 matrix
.xy
, matrix
.yy
,
522 matrix
.x0
, matrix
.y0
);
526 cairo_output_stream_t
*output
;
527 cairo_matrix_t
*ctm_inverse
;
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
,
618 if (unlikely (status
))
621 _cairo_output_stream_printf (output
, "\"");
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
,
636 CAIRO_SCALED_GLYPH_INFO_METRICS
|
637 CAIRO_SCALED_GLYPH_INFO_PATH
,
639 if (unlikely (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
))
649 _cairo_output_stream_printf (document
->xml_node_glyphs
,
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
;
667 status
= _cairo_scaled_glyph_lookup (scaled_font
,
669 CAIRO_SCALED_GLYPH_INFO_METRICS
|
670 CAIRO_SCALED_GLYPH_INFO_SURFACE
,
672 if (unlikely (status
))
675 image
= _cairo_image_surface_coerce (scaled_glyph
->surface
,
677 status
= image
->base
.status
;
678 if (unlikely (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",
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",
719 status
= _cairo_svg_document_emit_outline_glyph_data (document
,
721 scaled_font_glyph_index
);
722 if (status
== CAIRO_INT_STATUS_UNSUPPORTED
)
723 status
= _cairo_svg_document_emit_bitmap_glyph_data (document
,
725 scaled_font_glyph_index
);
726 if (unlikely (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
,
738 cairo_svg_document_t
*document
= closure
;
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
))
751 _cairo_scaled_font_thaw_cache (font_subset
->scaled_font
);
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
,
764 if (unlikely (status
))
767 status
= _cairo_scaled_font_subsets_foreach_user (document
->font_subsets
,
768 _cairo_svg_document_emit_font_subset
,
772 _cairo_scaled_font_subsets_destroy (document
->font_subsets
);
773 document
->font_subsets
= NULL
;
779 _cairo_svg_surface_operators
[] = {
782 "src", "src-over", "src-in",
783 "src-out", "src-atop",
785 "dst", "dst-over", "dst-in",
786 "dst-out", "dst-atop",
789 "color-dodge", /* FIXME: saturate ? */
793 _cairo_svg_surface_analyze_operator (cairo_svg_surface_t
*surface
,
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
,
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
,
842 const cairo_pattern_t
*pattern
)
844 if (_cairo_svg_surface_analyze_operation (surface
, op
, pattern
)
845 != CAIRO_INT_STATUS_UNSUPPORTED
)
853 static cairo_surface_t
*
854 _cairo_svg_surface_create_similar (void *abstract_src
,
855 cairo_content_t content
,
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
;
871 if (_cairo_paginated_surface_get_target (document
->owner
) == &surface
->base
)
872 status
= _cairo_svg_document_finish (document
);
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
)
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
)
888 _cairo_array_fini (&surface
->page_set
);
890 status2
= _cairo_svg_document_destroy (document
);
891 if (status
== CAIRO_STATUS_SUCCESS
)
898 _cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t
*document
)
900 if (document
->alpha_filter
)
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"
913 document
->alpha_filter
= TRUE
;
917 cairo_output_stream_t
*output
;
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
,
931 base64_write_closure_t
*info
= (base64_write_closure_t
*) closure
;
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
;
946 unsigned char dst
[4];
948 for (i
= info
->in_mem
; i
< 3; i
++) {
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
) {
967 _cairo_output_stream_write (info
->output
, dst
, 4);
968 } while (length
>= 3);
970 for (i
= 0; i
< length
; i
++) {
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
))
997 _cairo_output_stream_printf (output
, "data:image/jpeg;base64,");
999 info
.output
= output
;
1003 status
= base64_write_func (&info
, mime_data
, mime_data_length
);
1004 if (unlikely (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
;
1011 status
= base64_write_func (&info
, NULL
, 0);
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
;
1039 status
= base64_write_func (&info
, mime_data
, mime_data_length
);
1040 if (unlikely (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
;
1047 status
= base64_write_func (&info
, NULL
, 0);
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
)
1064 status
= _cairo_surface_base64_encode_png (surface
, output
);
1065 if (status
!= CAIRO_INT_STATUS_UNSUPPORTED
)
1068 info
.output
= output
;
1072 _cairo_output_stream_printf (info
.output
, "data:image/png;base64,");
1074 status
= cairo_surface_write_to_png_stream (surface
, base64_write_func
,
1077 if (unlikely (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
;
1084 status
= base64_write_func (&info
, NULL
, 0);
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\"");
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
,
1122 const cairo_matrix_t
*parent_matrix
,
1123 const char *extra_attributes
)
1125 cairo_rectangle_int_t extents
;
1126 cairo_status_t status
;
1129 status
= _cairo_surface_get_extents (pattern
->surface
, &extents
);
1130 if (unlikely (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\"",
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");
1173 static cairo_status_t
1174 _cairo_svg_surface_emit_meta_surface (cairo_svg_document_t
*document
,
1175 cairo_meta_surface_t
*surface
,
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
;
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)) {
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
,
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
);
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
);
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
);
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"
1250 svg_surface
->base_clip
,
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",
1262 svg_surface
->base_clip
);
1264 _cairo_output_stream_printf (document
->xml_node_defs
,
1265 "<g id=\"surface%d\" "
1266 "clip-path=\"url(#clip%d)\">\n",
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); */
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
,
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
;
1317 cairo_status_t status
;
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
))
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\"",
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\"",
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
,
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
;
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
))
1417 _cairo_output_stream_printf (style
,
1418 "%s:url(#pattern%d);",
1419 is_stroke
? "stroke" : "fill",
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
;
1434 unsigned int n_stops
;
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
;
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
;
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
;
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",
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
);
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
) {
1506 if (stops
[i
].offset
!= stops
[i
-1].offset
) {
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
;
1524 offset_color_stop
= stops
[i
-1].color
;
1525 offset_color_start
= stops
[i
].color
;
1528 offset_color_stop
= offset_color_start
= stops
[i
].color
;
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
)
1585 return CAIRO_STATUS_SUCCESS
;
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\" ");
1596 case CAIRO_EXTEND_REFLECT
:
1597 _cairo_output_stream_printf (output
, "spreadMethod=\"reflect\" ");
1599 case CAIRO_EXTEND_NONE
:
1600 case CAIRO_EXTEND_PAD
:
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
;
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
,
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,
1641 if (unlikely (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
;
1666 cairo_extend_t extend
;
1667 double x0
, y0
, x1
, y1
, r0
, r1
;
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
) {
1679 radius0
= pattern
->r1
;
1680 radius1
= pattern
->r2
;
1681 reverse_stops
= FALSE
;
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
,
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");
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
);
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
);
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
)
1764 if (extend
== CAIRO_EXTEND_REFLECT
) {
1766 emulate_reflect
= TRUE
;
1769 offset
= fmod (r1
, r1
- r0
) / (r1
- r0
) - 1.0;
1772 /* New position of outer circle. */
1773 x
= r
* (x1
- fx
) / r_org
+ fx
;
1774 y
= r
* (y1
- fy
) / r_org
+ fy
;
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
,
1793 if (emulate_reflect
)
1794 _cairo_output_stream_printf (document
->xml_node_defs
, "spreadMethod=\"repeat\" ");
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");
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",
1815 status
= _cairo_svg_surface_emit_pattern_stops (document
->xml_node_defs
,
1816 &pattern
->base
, offset
,
1819 if (unlikely (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
,
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
,
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
;
1897 switch (stroke_style
->line_cap
) {
1898 case CAIRO_LINE_CAP_BUTT
:
1901 case CAIRO_LINE_CAP_ROUND
:
1904 case CAIRO_LINE_CAP_SQUARE
:
1905 line_cap
= "square";
1911 switch (stroke_style
->line_join
) {
1912 case CAIRO_LINE_JOIN_MITER
:
1913 line_join
= "miter";
1915 case CAIRO_LINE_JOIN_ROUND
:
1916 line_join
= "round";
1918 case CAIRO_LINE_JOIN_BEVEL
:
1919 line_join
= "bevel";
1925 _cairo_output_stream_printf (output
,
1927 "stroke-linecap:%s;"
1928 "stroke-linejoin:%s;",
1929 stroke_style
->line_width
,
1933 status
= _cairo_svg_surface_emit_pattern (surface
, source
, output
, TRUE
, parent_matrix
);
1934 if (unlikely (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
, ",");
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
))
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
))
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
))
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
,
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
))
2029 _cairo_output_stream_printf (surface
->xml_node
, "\" ");
2031 status
= _cairo_svg_surface_emit_path (surface
->xml_node
, path
, NULL
);
2032 if (unlikely (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
;
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
,
2074 (cairo_surface_pattern_t
*) source
,
2076 mask_source
? &mask_source
->matrix
:NULL
,
2079 _cairo_output_stream_printf (output
,
2080 "<rect x=\"0\" y=\"0\" "
2081 "width=\"%f\" height=\"%f\" "
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
))
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
;
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
;
2135 if (op
== CAIRO_OPERATOR_CLEAR
) {
2136 if (surface
->content
== CAIRO_CONTENT_COLOR
) {
2137 _cairo_output_stream_printf (surface
->xml_node
,
2139 "width=\"%f\" height=\"%f\" "
2140 "style=\"opacity:1;"
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
;
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
))
2179 return _cairo_analysis_surface_merge_status (source_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"
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
);
2217 _cairo_output_stream_printf (mask_stream
,
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
))
2227 snprintf (buffer
, sizeof buffer
, "mask=\"url(#mask%d)\"",
2229 status
= _cairo_svg_surface_emit_paint (surface
->xml_node
, surface
, op
, source
, 0, buffer
);
2230 if (unlikely (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
,
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
))
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
))
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
,
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
;
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
)
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
))
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
,
2320 if (status
== CAIRO_INT_STATUS_UNSUPPORTED
) {
2321 _cairo_output_stream_printf (surface
->xml_node
, "</g>\n");
2328 if (unlikely (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
;
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
);
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
);
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
,
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
;
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"
2385 status
= _cairo_svg_surface_emit_path (document
->xml_node_defs
, path
, NULL
);
2386 if (unlikely (status
))
2389 _cairo_output_stream_printf (document
->xml_node_defs
,
2393 _cairo_output_stream_printf (surface
->xml_node
,
2394 "<g clip-path=\"url(#clip%d)\" "
2395 "clip-rule=\"%s\">\n",
2397 fill_rule
== CAIRO_FILL_RULE_EVEN_ODD
?
2398 "evenodd" : "nonzero");
2400 document
->clip_id
++;
2401 surface
->clip_level
++;
2403 return CAIRO_STATUS_SUCCESS
;
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
,
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 */
2450 _cairo_svg_surface_fill_stroke
2453 static cairo_status_t
2454 _cairo_svg_document_create (cairo_output_stream_t
*output_stream
,
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
);
2515 status_ignored
= _cairo_output_stream_destroy (document
->xml_node_defs
);
2516 _cairo_scaled_font_subsets_destroy (document
->font_subsets
);
2522 static cairo_svg_document_t
*
2523 _cairo_svg_document_reference (cairo_svg_document_t
*document
)
2525 document
->refcount
++;
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
);
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
;
2561 if (document
->finished
)
2562 return CAIRO_STATUS_SUCCESS
;
2565 * Should we add DOCTYPE?
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",
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",
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
)
2651 status2
= _cairo_output_stream_destroy (document
->xml_node_defs
);
2652 if (status
== CAIRO_STATUS_SUCCESS
)
2655 status2
= _cairo_output_stream_destroy (output
);
2656 if (status
== CAIRO_STATUS_SUCCESS
)
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
)
2667 _cairo_array_fini (&document
->meta_snapshots
);
2669 document
->finished
= TRUE
;
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
;
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
,