beta-0.89.2
[luatex.git] / source / libs / cairo / cairo-src / src / cairo-svg-surface.c
blob372ef45b86e5fe1135599fec0861de71d9f1e8d2
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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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"
45 #include "cairo-svg.h"
47 #include "cairo-array-private.h"
48 #include "cairo-analysis-surface-private.h"
49 #include "cairo-default-context-private.h"
50 #include "cairo-error-private.h"
51 #include "cairo-image-info-private.h"
52 #include "cairo-image-surface-private.h"
53 #include "cairo-recording-surface-inline.h"
54 #include "cairo-output-stream-private.h"
55 #include "cairo-path-fixed-private.h"
56 #include "cairo-paginated-private.h"
57 #include "cairo-scaled-font-subsets-private.h"
58 #include "cairo-surface-clipper-private.h"
59 #include "cairo-surface-snapshot-inline.h"
60 #include "cairo-svg-surface-private.h"
62 /**
63 * SECTION:cairo-svg
64 * @Title: SVG Surfaces
65 * @Short_Description: Rendering SVG documents
66 * @See_Also: #cairo_surface_t
68 * The SVG surface is used to render cairo graphics to
69 * SVG files and is a multi-page vector surface backend.
70 **/
72 /**
73 * CAIRO_HAS_SVG_SURFACE:
75 * Defined if the SVG surface backend is available.
76 * This macro can be used to conditionally compile backend-specific code.
78 * Since: 1.2
79 **/
81 typedef struct cairo_svg_page cairo_svg_page_t;
83 static const int invalid_pattern_id = -1;
85 static const cairo_svg_version_t _cairo_svg_versions[] =
87 CAIRO_SVG_VERSION_1_1,
88 CAIRO_SVG_VERSION_1_2
91 #define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
93 static const char *_cairo_svg_supported_mime_types[] =
95 CAIRO_MIME_TYPE_JPEG,
96 CAIRO_MIME_TYPE_PNG,
97 CAIRO_MIME_TYPE_URI,
98 NULL
101 static void
102 _cairo_svg_surface_emit_path (cairo_output_stream_t *output,
103 const cairo_path_fixed_t *path,
104 const cairo_matrix_t *ctm_inverse);
106 static cairo_bool_t
107 _cairo_svg_version_has_page_set_support (cairo_svg_version_t version)
109 return version > CAIRO_SVG_VERSION_1_1;
112 static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] =
114 "SVG 1.1",
115 "SVG 1.2"
118 static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] =
120 "1.1",
121 "1.2"
124 struct cairo_svg_page {
125 unsigned int surface_id;
126 unsigned int clip_level;
127 cairo_output_stream_t *xml_node;
130 struct cairo_svg_document {
131 cairo_output_stream_t *output_stream;
132 unsigned long refcount;
133 cairo_surface_t *owner;
134 cairo_bool_t finished;
136 double width;
137 double height;
139 cairo_output_stream_t *xml_node_defs;
140 cairo_output_stream_t *xml_node_glyphs;
142 unsigned int linear_pattern_id;
143 unsigned int radial_pattern_id;
144 unsigned int pattern_id;
145 unsigned int filter_id;
146 unsigned int clip_id;
147 unsigned int mask_id;
149 cairo_bool_t alpha_filter;
151 cairo_svg_version_t svg_version;
153 cairo_scaled_font_subsets_t *font_subsets;
156 static cairo_status_t
157 _cairo_svg_document_create (cairo_output_stream_t *stream,
158 double width,
159 double height,
160 cairo_svg_version_t version,
161 cairo_svg_document_t **document_out);
163 static cairo_status_t
164 _cairo_svg_document_destroy (cairo_svg_document_t *document);
166 static cairo_status_t
167 _cairo_svg_document_finish (cairo_svg_document_t *document);
169 static cairo_svg_document_t *
170 _cairo_svg_document_reference (cairo_svg_document_t *document);
172 static unsigned int
173 _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document);
175 static cairo_surface_t *
176 _cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
177 cairo_content_t content,
178 double width,
179 double height);
180 static cairo_surface_t *
181 _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
182 double width,
183 double height,
184 cairo_svg_version_t version);
186 static const cairo_surface_backend_t cairo_svg_surface_backend;
187 static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend;
190 * cairo_svg_surface_create_for_stream:
191 * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
192 * to indicate a no-op @write_func. With a no-op @write_func,
193 * the surface may be queried or used as a source without
194 * generating any temporary files.
195 * @closure: the closure argument for @write_func
196 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
197 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
199 * Creates a SVG surface of the specified size in points to be written
200 * incrementally to the stream represented by @write_func and @closure.
202 * Return value: a pointer to the newly created surface. The caller
203 * owns the surface and should call cairo_surface_destroy() when done
204 * with it.
206 * This function always returns a valid pointer, but it will return a
207 * pointer to a "nil" surface if an error such as out of memory
208 * occurs. You can use cairo_surface_status() to check for this.
210 * Since: 1.2
212 cairo_surface_t *
213 cairo_svg_surface_create_for_stream (cairo_write_func_t write_func,
214 void *closure,
215 double width,
216 double height)
218 cairo_output_stream_t *stream;
220 stream = _cairo_output_stream_create (write_func, NULL, closure);
221 if (_cairo_output_stream_get_status (stream))
222 return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
224 return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
228 * cairo_svg_surface_create:
229 * @filename: a filename for the SVG output (must be writable), %NULL may be
230 * used to specify no output. This will generate a SVG surface that
231 * may be queried and used as a source, without generating a
232 * temporary file.
233 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
234 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
236 * Creates a SVG surface of the specified size in points to be written
237 * to @filename.
239 * The SVG surface backend recognizes the following MIME types for the
240 * data attached to a surface (see cairo_surface_set_mime_data()) when
241 * it is used as a source pattern for drawing on this surface:
242 * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG,
243 * %CAIRO_MIME_TYPE_URI. If any of them is specified, the SVG backend
244 * emits a href with the content of MIME data instead of a surface
245 * snapshot (PNG, Base64-encoded) in the corresponding image tag.
247 * The unofficial MIME type %CAIRO_MIME_TYPE_URI is examined
248 * first. If present, the URI is emitted as is: assuring the
249 * correctness of URI is left to the client code.
251 * If %CAIRO_MIME_TYPE_URI is not present, but %CAIRO_MIME_TYPE_JPEG
252 * or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is
253 * Base64-encoded and emitted.
255 * Return value: a pointer to the newly created surface. The caller
256 * owns the surface and should call cairo_surface_destroy() when done
257 * with it.
259 * This function always returns a valid pointer, but it will return a
260 * pointer to a "nil" surface if an error such as out of memory
261 * occurs. You can use cairo_surface_status() to check for this.
263 * Since: 1.2
265 cairo_surface_t *
266 cairo_svg_surface_create (const char *filename,
267 double width,
268 double height)
270 cairo_output_stream_t *stream;
272 stream = _cairo_output_stream_create_for_filename (filename);
273 if (_cairo_output_stream_get_status (stream))
274 return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
276 return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
279 static cairo_bool_t
280 _cairo_surface_is_svg (cairo_surface_t *surface)
282 return surface->backend == &cairo_svg_surface_backend;
285 /* If the abstract_surface is a paginated surface, and that paginated
286 * surface's target is a svg_surface, then set svg_surface to that
287 * target. Otherwise return FALSE.
289 static cairo_bool_t
290 _extract_svg_surface (cairo_surface_t *surface,
291 cairo_svg_surface_t **svg_surface)
293 cairo_surface_t *target;
294 cairo_status_t status_ignored;
296 if (surface->status)
297 return FALSE;
298 if (surface->finished) {
299 status_ignored = _cairo_surface_set_error (surface,
300 _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
301 return FALSE;
304 if (! _cairo_surface_is_paginated (surface)) {
305 status_ignored = _cairo_surface_set_error (surface,
306 _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
307 return FALSE;
310 target = _cairo_paginated_surface_get_target (surface);
311 if (target->status) {
312 status_ignored = _cairo_surface_set_error (surface,
313 target->status);
314 return FALSE;
316 if (target->finished) {
317 status_ignored = _cairo_surface_set_error (surface,
318 _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
319 return FALSE;
322 if (! _cairo_surface_is_svg (target)) {
323 status_ignored = _cairo_surface_set_error (surface,
324 _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
325 return FALSE;
328 *svg_surface = (cairo_svg_surface_t *) target;
329 return TRUE;
333 * cairo_svg_surface_restrict_to_version:
334 * @surface: a SVG #cairo_surface_t
335 * @version: SVG version
337 * Restricts the generated SVG file to @version. See cairo_svg_get_versions()
338 * for a list of available version values that can be used here.
340 * This function should only be called before any drawing operations
341 * have been performed on the given surface. The simplest way to do
342 * this is to call this function immediately after creating the
343 * surface.
345 * Since: 1.2
347 void
348 cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface,
349 cairo_svg_version_t version)
351 cairo_svg_surface_t *surface = NULL; /* hide compiler warning */
353 if (! _extract_svg_surface (abstract_surface, &surface))
354 return;
356 if (version < CAIRO_SVG_VERSION_LAST)
357 surface->document->svg_version = version;
361 * cairo_svg_get_versions:
362 * @versions: supported version list
363 * @num_versions: list length
365 * Used to retrieve the list of supported versions. See
366 * cairo_svg_surface_restrict_to_version().
368 * Since: 1.2
370 void
371 cairo_svg_get_versions (cairo_svg_version_t const **versions,
372 int *num_versions)
374 if (versions != NULL)
375 *versions = _cairo_svg_versions;
377 if (num_versions != NULL)
378 *num_versions = CAIRO_SVG_VERSION_LAST;
382 * cairo_svg_version_to_string:
383 * @version: a version id
385 * Get the string representation of the given @version id. This function
386 * will return %NULL if @version isn't valid. See cairo_svg_get_versions()
387 * for a way to get the list of valid version ids.
389 * Return value: the string associated to given version.
391 * Since: 1.2
393 const char *
394 cairo_svg_version_to_string (cairo_svg_version_t version)
396 if (version >= CAIRO_SVG_VERSION_LAST)
397 return NULL;
399 return _cairo_svg_version_strings[version];
402 static cairo_bool_t
403 _cliprect_covers_surface (cairo_svg_surface_t *surface,
404 cairo_path_fixed_t *path)
406 cairo_box_t box;
408 if (_cairo_path_fixed_is_box (path, &box)) {
409 if (box.p1.x <= 0 &&
410 box.p1.y <= 0 &&
411 _cairo_fixed_to_double (box.p2.x) >= surface->width &&
412 _cairo_fixed_to_double (box.p2.y) >= surface->height)
414 return TRUE;
418 return FALSE;
421 static cairo_status_t
422 _cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
423 cairo_path_fixed_t *path,
424 cairo_fill_rule_t fill_rule,
425 double tolerance,
426 cairo_antialias_t antialias)
428 cairo_svg_surface_t *surface = cairo_container_of (clipper,
429 cairo_svg_surface_t,
430 clipper);
431 cairo_svg_document_t *document = surface->document;
432 unsigned int i;
434 if (path == NULL) {
435 for (i = 0; i < surface->clip_level; i++)
436 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
438 surface->clip_level = 0;
439 return CAIRO_STATUS_SUCCESS;
442 /* skip trivial whole-page clips */
443 if (_cliprect_covers_surface (surface, path))
444 return CAIRO_STATUS_SUCCESS;
446 _cairo_output_stream_printf (document->xml_node_defs,
447 "<clipPath id=\"clip%d\">\n"
448 " <path ",
449 document->clip_id);
450 _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL);
452 _cairo_output_stream_printf (document->xml_node_defs,
453 "/>\n"
454 "</clipPath>\n");
456 _cairo_output_stream_printf (surface->xml_node,
457 "<g clip-path=\"url(#clip%d)\" "
458 "clip-rule=\"%s\">\n",
459 document->clip_id,
460 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
461 "evenodd" : "nonzero");
463 document->clip_id++;
464 surface->clip_level++;
466 return CAIRO_STATUS_SUCCESS;
469 static cairo_surface_t *
470 _cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
471 cairo_content_t content,
472 double width,
473 double height)
475 cairo_svg_surface_t *surface;
476 cairo_surface_t *paginated;
477 cairo_status_t status, status_ignored;
479 surface = malloc (sizeof (cairo_svg_surface_t));
480 if (unlikely (surface == NULL))
481 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
483 _cairo_surface_init (&surface->base,
484 &cairo_svg_surface_backend,
485 NULL, /* device */
486 content);
488 surface->width = width;
489 surface->height = height;
491 surface->document = _cairo_svg_document_reference (document);
493 surface->clip_level = 0;
494 _cairo_surface_clipper_init (&surface->clipper,
495 _cairo_svg_surface_clipper_intersect_clip_path);
497 surface->base_clip = document->clip_id++;
498 surface->is_base_clip_emitted = FALSE;
500 surface->xml_node = _cairo_memory_stream_create ();
501 status = _cairo_output_stream_get_status (surface->xml_node);
502 if (unlikely (status))
503 goto CLEANUP;
505 _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t));
507 if (content == CAIRO_CONTENT_COLOR) {
508 _cairo_output_stream_printf (surface->xml_node,
509 "<rect width=\"%f\" height=\"%f\" "
510 "style=\"opacity:1;stroke:none;"
511 "fill:rgb(0,0,0);\"/>\n",
512 width, height);
513 status = _cairo_output_stream_get_status (surface->xml_node);
514 if (unlikely (status))
515 goto CLEANUP;
518 surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
519 surface->force_fallbacks = FALSE;
520 surface->content = content;
522 paginated = _cairo_paginated_surface_create (&surface->base,
523 surface->content,
524 &cairo_svg_surface_paginated_backend);
525 status = paginated->status;
526 if (status == CAIRO_STATUS_SUCCESS) {
527 /* paginated keeps the only reference to surface now, drop ours */
528 cairo_surface_destroy (&surface->base);
529 return paginated;
532 /* ignore status as we are on the error path */
533 CLEANUP:
534 status_ignored = _cairo_output_stream_destroy (surface->xml_node);
535 status_ignored = _cairo_svg_document_destroy (document);
537 free (surface);
539 return _cairo_surface_create_in_error (status);
542 static cairo_surface_t *
543 _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
544 double width,
545 double height,
546 cairo_svg_version_t version)
548 cairo_svg_document_t *document = NULL; /* silence compiler */
549 cairo_surface_t *surface;
550 cairo_status_t status;
552 status = _cairo_svg_document_create (stream,
553 width, height, version,
554 &document);
555 if (unlikely (status)) {
556 surface = _cairo_surface_create_in_error (status);
557 /* consume the output stream on behalf of caller */
558 status = _cairo_output_stream_destroy (stream);
559 return surface;
562 surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA,
563 width, height);
564 if (surface->status) {
565 status = _cairo_svg_document_destroy (document);
566 return surface;
569 document->owner = surface;
570 status = _cairo_svg_document_destroy (document);
571 /* the ref count should be 2 at this point */
572 assert (status == CAIRO_STATUS_SUCCESS);
574 return surface;
577 static cairo_svg_page_t *
578 _cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
580 cairo_svg_page_t page;
581 cairo_output_stream_t *stream;
582 cairo_int_status_t status;
583 unsigned int i;
585 stream = _cairo_memory_stream_create ();
586 if (_cairo_output_stream_get_status (stream)) {
587 status = _cairo_output_stream_destroy (stream);
588 return NULL;
591 page.surface_id = surface->base.unique_id;
592 page.clip_level = surface->clip_level;
593 page.xml_node = surface->xml_node;
595 if (_cairo_array_append (&surface->page_set, &page)) {
596 status = _cairo_output_stream_destroy (stream);
597 return NULL;
600 surface->xml_node = stream;
601 surface->clip_level = 0;
602 for (i = 0; i < page.clip_level; i++)
603 _cairo_output_stream_printf (page.xml_node, "</g>\n");
605 _cairo_surface_clipper_reset (&surface->clipper);
607 return _cairo_array_index (&surface->page_set,
608 surface->page_set.num_elements - 1);
611 static cairo_int_status_t
612 _cairo_svg_surface_copy_page (void *abstract_surface)
614 cairo_svg_surface_t *surface = abstract_surface;
615 cairo_svg_page_t *page;
617 page = _cairo_svg_surface_store_page (surface);
618 if (unlikely (page == NULL))
619 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
621 _cairo_memory_stream_copy (page->xml_node, surface->xml_node);
623 return CAIRO_STATUS_SUCCESS;
626 static cairo_int_status_t
627 _cairo_svg_surface_show_page (void *abstract_surface)
629 cairo_svg_surface_t *surface = abstract_surface;
631 if (unlikely (_cairo_svg_surface_store_page (surface) == NULL))
632 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
634 return CAIRO_STATUS_SUCCESS;
637 static void
638 _cairo_svg_surface_emit_transform (cairo_output_stream_t *output,
639 char const *attribute_str,
640 const cairo_matrix_t *object_matrix,
641 const cairo_matrix_t *parent_matrix)
643 cairo_matrix_t matrix = *object_matrix;
645 if (parent_matrix != NULL)
646 cairo_matrix_multiply (&matrix, &matrix, parent_matrix);
648 if (!_cairo_matrix_is_identity (&matrix))
649 _cairo_output_stream_printf (output,
650 "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"",
651 attribute_str,
652 matrix.xx, matrix.yx,
653 matrix.xy, matrix.yy,
654 matrix.x0, matrix.y0);
657 typedef struct {
658 cairo_output_stream_t *output;
659 const cairo_matrix_t *ctm_inverse;
660 } svg_path_info_t;
662 static cairo_status_t
663 _cairo_svg_path_move_to (void *closure,
664 const cairo_point_t *point)
666 svg_path_info_t *info = closure;
667 double x = _cairo_fixed_to_double (point->x);
668 double y = _cairo_fixed_to_double (point->y);
670 if (info->ctm_inverse)
671 cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
673 _cairo_output_stream_printf (info->output, "M %f %f ", x, y);
675 return CAIRO_STATUS_SUCCESS;
678 static cairo_status_t
679 _cairo_svg_path_line_to (void *closure,
680 const cairo_point_t *point)
682 svg_path_info_t *info = closure;
683 double x = _cairo_fixed_to_double (point->x);
684 double y = _cairo_fixed_to_double (point->y);
686 if (info->ctm_inverse)
687 cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
689 _cairo_output_stream_printf (info->output, "L %f %f ", x, y);
691 return CAIRO_STATUS_SUCCESS;
694 static cairo_status_t
695 _cairo_svg_path_curve_to (void *closure,
696 const cairo_point_t *b,
697 const cairo_point_t *c,
698 const cairo_point_t *d)
700 svg_path_info_t *info = closure;
701 double bx = _cairo_fixed_to_double (b->x);
702 double by = _cairo_fixed_to_double (b->y);
703 double cx = _cairo_fixed_to_double (c->x);
704 double cy = _cairo_fixed_to_double (c->y);
705 double dx = _cairo_fixed_to_double (d->x);
706 double dy = _cairo_fixed_to_double (d->y);
708 if (info->ctm_inverse) {
709 cairo_matrix_transform_point (info->ctm_inverse, &bx, &by);
710 cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy);
711 cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy);
714 _cairo_output_stream_printf (info->output,
715 "C %f %f %f %f %f %f ",
716 bx, by, cx, cy, dx, dy);
718 return CAIRO_STATUS_SUCCESS;
721 static cairo_status_t
722 _cairo_svg_path_close_path (void *closure)
724 svg_path_info_t *info = closure;
726 _cairo_output_stream_printf (info->output, "Z ");
728 return CAIRO_STATUS_SUCCESS;
731 static void
732 _cairo_svg_surface_emit_path (cairo_output_stream_t *output,
733 const cairo_path_fixed_t *path,
734 const cairo_matrix_t *ctm_inverse)
736 cairo_status_t status;
737 svg_path_info_t info;
739 _cairo_output_stream_printf (output, "d=\"");
741 info.output = output;
742 info.ctm_inverse = ctm_inverse;
743 status = _cairo_path_fixed_interpret (path,
744 _cairo_svg_path_move_to,
745 _cairo_svg_path_line_to,
746 _cairo_svg_path_curve_to,
747 _cairo_svg_path_close_path,
748 &info);
749 assert (status == CAIRO_STATUS_SUCCESS);
751 _cairo_output_stream_printf (output, "\"");
754 static cairo_int_status_t
755 _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document,
756 cairo_scaled_font_t *scaled_font,
757 unsigned long glyph_index)
759 cairo_scaled_glyph_t *scaled_glyph;
760 cairo_int_status_t status;
762 status = _cairo_scaled_glyph_lookup (scaled_font,
763 glyph_index,
764 CAIRO_SCALED_GLYPH_INFO_METRICS|
765 CAIRO_SCALED_GLYPH_INFO_PATH,
766 &scaled_glyph);
767 if (unlikely (status))
768 return status;
770 _cairo_output_stream_printf (document->xml_node_glyphs,
771 "<path style=\"stroke:none;\" ");
773 _cairo_svg_surface_emit_path (document->xml_node_glyphs,
774 scaled_glyph->path, NULL);
776 _cairo_output_stream_printf (document->xml_node_glyphs,
777 "/>\n");
779 return status;
782 static cairo_int_status_t
783 _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document,
784 cairo_scaled_font_t *scaled_font,
785 unsigned long glyph_index)
787 cairo_scaled_glyph_t *scaled_glyph;
788 cairo_image_surface_t *image;
789 cairo_status_t status;
790 uint8_t *row, *byte;
791 int rows, cols;
792 int x, y, bit;
794 status = _cairo_scaled_glyph_lookup (scaled_font,
795 glyph_index,
796 CAIRO_SCALED_GLYPH_INFO_METRICS |
797 CAIRO_SCALED_GLYPH_INFO_SURFACE,
798 &scaled_glyph);
799 if (unlikely (status))
800 return status;
802 image = _cairo_image_surface_coerce_to_format (scaled_glyph->surface,
803 CAIRO_FORMAT_A1);
804 status = image->base.status;
805 if (unlikely (status))
806 return status;
808 _cairo_output_stream_printf (document->xml_node_glyphs, "<g");
809 _cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform",
810 &image->base.device_transform_inverse, NULL);
811 _cairo_output_stream_printf (document->xml_node_glyphs, ">\n");
813 for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) {
814 for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
815 uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
816 for (bit = 7; bit >= 0 && x < image->width; bit--, x++) {
817 if (output_byte & (1 << bit)) {
818 _cairo_output_stream_printf (document->xml_node_glyphs,
819 "<rect x=\"%d\" y=\"%d\" width=\"1\" height=\"1\"/>\n",
820 x, y);
825 _cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n");
827 cairo_surface_destroy (&image->base);
829 return CAIRO_STATUS_SUCCESS;
832 static cairo_int_status_t
833 _cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
834 cairo_scaled_font_t *scaled_font,
835 unsigned long scaled_font_glyph_index,
836 unsigned int font_id,
837 unsigned int subset_glyph_index)
839 cairo_int_status_t status;
841 _cairo_output_stream_printf (document->xml_node_glyphs,
842 "<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n",
843 font_id,
844 subset_glyph_index);
846 status = _cairo_svg_document_emit_outline_glyph_data (document,
847 scaled_font,
848 scaled_font_glyph_index);
849 if (status == CAIRO_INT_STATUS_UNSUPPORTED)
850 status = _cairo_svg_document_emit_bitmap_glyph_data (document,
851 scaled_font,
852 scaled_font_glyph_index);
853 if (unlikely (status))
854 return status;
856 _cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n");
858 return CAIRO_INT_STATUS_SUCCESS;
861 static cairo_int_status_t
862 _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
863 void *closure)
865 cairo_svg_document_t *document = closure;
866 cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
867 unsigned int i;
869 _cairo_scaled_font_freeze_cache (font_subset->scaled_font);
870 for (i = 0; i < font_subset->num_glyphs; i++) {
871 status = _cairo_svg_document_emit_glyph (document,
872 font_subset->scaled_font,
873 font_subset->glyphs[i],
874 font_subset->font_id, i);
875 if (unlikely (status))
876 break;
878 _cairo_scaled_font_thaw_cache (font_subset->scaled_font);
880 return status;
883 static cairo_status_t
884 _cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
886 cairo_status_t status;
888 status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
889 _cairo_svg_document_emit_font_subset,
890 document);
891 if (unlikely (status))
892 goto FAIL;
894 status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
895 _cairo_svg_document_emit_font_subset,
896 document);
898 FAIL:
899 _cairo_scaled_font_subsets_destroy (document->font_subsets);
900 document->font_subsets = NULL;
902 return status;
905 static char const *
906 _cairo_svg_surface_operators[] = {
907 "clear",
909 "src", "src-over", "src-in",
910 "src-out", "src-atop",
912 "dst", "dst-over", "dst-in",
913 "dst-out", "dst-atop",
915 "xor", "plus",
916 "color-dodge", /* FIXME: saturate ? */
918 "multiply", "screen", "overlay",
919 "darken", "lighten",
920 "color-dodge", "color-burn",
921 "hard-light", "soft-light",
922 "difference", "exclusion"
925 static cairo_bool_t
926 _cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface,
927 cairo_operator_t op)
929 /* guard against newly added operators */
930 if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators))
931 return CAIRO_INT_STATUS_UNSUPPORTED;
933 /* allow operators being NULL if they are unsupported */
934 if (_cairo_svg_surface_operators[op] == NULL)
935 return CAIRO_INT_STATUS_UNSUPPORTED;
937 return CAIRO_STATUS_SUCCESS;
940 static cairo_int_status_t
941 _cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface,
942 cairo_operator_t op,
943 const cairo_pattern_t *pattern)
945 cairo_svg_document_t *document = surface->document;
947 if (surface->force_fallbacks &&
948 surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
950 return CAIRO_INT_STATUS_UNSUPPORTED;
953 if (pattern->type == CAIRO_PATTERN_TYPE_MESH)
954 return CAIRO_INT_STATUS_UNSUPPORTED;
956 /* SVG doesn't support extend reflect for image pattern */
957 if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
958 pattern->extend == CAIRO_EXTEND_REFLECT)
959 return CAIRO_INT_STATUS_UNSUPPORTED;
961 if (document->svg_version >= CAIRO_SVG_VERSION_1_2)
962 return _cairo_svg_surface_analyze_operator (surface, op);
964 if (op == CAIRO_OPERATOR_OVER)
965 return CAIRO_STATUS_SUCCESS;
967 /* The SOURCE operator is only supported if there is nothing
968 * painted underneath. */
969 if (op == CAIRO_OPERATOR_SOURCE)
970 return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
972 return CAIRO_INT_STATUS_UNSUPPORTED;
975 static cairo_int_status_t
976 _cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface,
977 cairo_operator_t op,
978 const cairo_pattern_t *pattern)
980 return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED;
983 static cairo_status_t
984 _cairo_svg_surface_finish (void *abstract_surface)
986 cairo_status_t status, status2;
987 cairo_svg_surface_t *surface = abstract_surface;
988 cairo_svg_document_t *document = surface->document;
989 cairo_svg_page_t *page;
990 unsigned int i;
992 if (_cairo_paginated_surface_get_target (document->owner) == &surface->base)
993 status = _cairo_svg_document_finish (document);
994 else
995 status = CAIRO_STATUS_SUCCESS;
997 if (surface->xml_node != NULL) {
998 status2 = _cairo_output_stream_destroy (surface->xml_node);
999 if (status == CAIRO_STATUS_SUCCESS)
1000 status = status2;
1003 for (i = 0; i < surface->page_set.num_elements; i++) {
1004 page = _cairo_array_index (&surface->page_set, i);
1005 status2 = _cairo_output_stream_destroy (page->xml_node);
1006 if (status == CAIRO_STATUS_SUCCESS)
1007 status = status2;
1009 _cairo_array_fini (&surface->page_set);
1011 _cairo_surface_clipper_reset (&surface->clipper);
1013 status2 = _cairo_svg_document_destroy (document);
1014 if (status == CAIRO_STATUS_SUCCESS)
1015 status = status2;
1017 return status;
1021 static void
1022 _cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document)
1024 if (document->alpha_filter)
1025 return;
1027 _cairo_output_stream_printf (document->xml_node_defs,
1028 "<filter id=\"alpha\" "
1029 "filterUnits=\"objectBoundingBox\" "
1030 "x=\"0%%\" y=\"0%%\" "
1031 "width=\"100%%\" height=\"100%%\">\n"
1032 " <feColorMatrix type=\"matrix\" "
1033 "in=\"SourceGraphic\" "
1034 "values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n"
1035 "</filter>\n");
1037 document->alpha_filter = TRUE;
1040 typedef struct {
1041 cairo_output_stream_t *output;
1042 unsigned int in_mem;
1043 unsigned int trailing;
1044 unsigned char src[3];
1045 } base64_write_closure_t;
1047 static char const base64_table[64] =
1048 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1050 static cairo_status_t
1051 base64_write_func (void *closure,
1052 const unsigned char *data,
1053 unsigned int length)
1055 base64_write_closure_t *info = (base64_write_closure_t *) closure;
1056 unsigned int i;
1057 unsigned char *src;
1059 src = info->src;
1061 if (info->in_mem + length < 3) {
1062 for (i = 0; i < length; i++) {
1063 src[i + info->in_mem] = *data++;
1065 info->in_mem += length;
1066 return CAIRO_STATUS_SUCCESS;
1069 do {
1070 unsigned char dst[4];
1072 for (i = info->in_mem; i < 3; i++) {
1073 src[i] = *data++;
1074 length--;
1076 info->in_mem = 0;
1078 dst[0] = base64_table[src[0] >> 2];
1079 dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4];
1080 dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6];
1081 dst[3] = base64_table[src[2] & 0xfc >> 2];
1082 /* Special case for the last missing bits */
1083 switch (info->trailing) {
1084 case 2:
1085 dst[2] = '=';
1086 case 1:
1087 dst[3] = '=';
1088 default:
1089 break;
1091 _cairo_output_stream_write (info->output, dst, 4);
1092 } while (length >= 3);
1094 for (i = 0; i < length; i++) {
1095 src[i] = *data++;
1097 info->in_mem = length;
1099 return _cairo_output_stream_get_status (info->output);
1102 static cairo_int_status_t
1103 _cairo_surface_base64_encode_jpeg (cairo_surface_t *surface,
1104 cairo_output_stream_t *output)
1106 const unsigned char *mime_data;
1107 unsigned long mime_data_length;
1108 cairo_image_info_t image_info;
1109 base64_write_closure_t info;
1110 cairo_status_t status;
1112 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG,
1113 &mime_data, &mime_data_length);
1114 if (mime_data == NULL)
1115 return CAIRO_INT_STATUS_UNSUPPORTED;
1117 status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length);
1118 if (unlikely (status))
1119 return status;
1121 if (image_info.num_components == 4)
1122 return CAIRO_INT_STATUS_UNSUPPORTED;
1124 _cairo_output_stream_printf (output, "data:image/jpeg;base64,");
1126 info.output = output;
1127 info.in_mem = 0;
1128 info.trailing = 0;
1130 status = base64_write_func (&info, mime_data, mime_data_length);
1131 if (unlikely (status))
1132 return status;
1134 if (info.in_mem > 0) {
1135 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1136 info.trailing = 3 - info.in_mem;
1137 info.in_mem = 3;
1138 status = base64_write_func (&info, NULL, 0);
1141 return status;
1144 static cairo_int_status_t
1145 _cairo_surface_base64_encode_png (cairo_surface_t *surface,
1146 cairo_output_stream_t *output)
1148 const unsigned char *mime_data;
1149 unsigned long mime_data_length;
1150 base64_write_closure_t info;
1151 cairo_status_t status;
1153 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG,
1154 &mime_data, &mime_data_length);
1155 if (unlikely (surface->status))
1156 return surface->status;
1157 if (mime_data == NULL)
1158 return CAIRO_INT_STATUS_UNSUPPORTED;
1160 _cairo_output_stream_printf (output, "data:image/png;base64,");
1162 info.output = output;
1163 info.in_mem = 0;
1164 info.trailing = 0;
1166 status = base64_write_func (&info, mime_data, mime_data_length);
1167 if (unlikely (status))
1168 return status;
1170 if (info.in_mem > 0) {
1171 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1172 info.trailing = 3 - info.in_mem;
1173 info.in_mem = 3;
1174 status = base64_write_func (&info, NULL, 0);
1177 return status;
1180 static cairo_int_status_t
1181 _cairo_surface_base64_encode (cairo_surface_t *surface,
1182 cairo_output_stream_t *output)
1184 cairo_int_status_t status;
1185 base64_write_closure_t info;
1187 status = _cairo_surface_base64_encode_jpeg (surface, output);
1188 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1189 return status;
1191 status = _cairo_surface_base64_encode_png (surface, output);
1192 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1193 return status;
1195 info.output = output;
1196 info.in_mem = 0;
1197 info.trailing = 0;
1199 _cairo_output_stream_printf (info.output, "data:image/png;base64,");
1201 status = cairo_surface_write_to_png_stream (surface, base64_write_func,
1202 (void *) &info);
1204 if (unlikely (status))
1205 return status;
1207 if (info.in_mem > 0) {
1208 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1209 info.trailing = 3 - info.in_mem;
1210 info.in_mem = 3;
1211 status = base64_write_func (&info, NULL, 0);
1214 return status;
1217 static void
1218 _cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
1219 cairo_svg_surface_t *surface,
1220 cairo_operator_t op)
1222 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1223 op != CAIRO_OPERATOR_OVER) {
1224 _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]);
1225 if (!_cairo_operator_bounded_by_source (op))
1226 _cairo_output_stream_printf (output, " clip-to-self=\"true\"");
1230 static void
1231 _cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output,
1232 cairo_svg_surface_t *surface,
1233 cairo_operator_t op)
1235 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1236 op != CAIRO_OPERATOR_OVER) {
1237 _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]);
1238 if (!_cairo_operator_bounded_by_source (op))
1239 _cairo_output_stream_printf (output, "clip-to-self:true;");
1244 * _cairo_svg_surface_emit_attr_value:
1246 * Write the value to output the stream as a sequence of characters,
1247 * while escaping those which have special meaning in the XML
1248 * attribute's value context: &amp; and &quot;.
1250 static void
1251 _cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream,
1252 const unsigned char *value,
1253 unsigned int length)
1255 const unsigned char *p;
1256 const unsigned char *q;
1257 unsigned int i;
1259 /* we'll accumulate non-special chars in [q, p) range */
1260 p = value;
1261 q = p;
1262 for (i = 0; i < length; i++, p++) {
1263 if (*p == '&' || *p == '"') {
1264 /* flush what's left before special char */
1265 if (p != q) {
1266 _cairo_output_stream_write (stream, q, p - q);
1267 q = p + 1;
1270 if (*p == '&')
1271 _cairo_output_stream_printf (stream, "&amp;");
1272 else // p == '"'
1273 _cairo_output_stream_printf (stream, "&quot;");
1277 /* flush the trailing chars if any */
1278 if (p != q)
1279 _cairo_output_stream_write (stream, q, p - q);
1282 static cairo_status_t
1283 _cairo_svg_surface_emit_surface (cairo_svg_document_t *document,
1284 cairo_surface_t *surface)
1286 cairo_rectangle_int_t extents;
1287 cairo_bool_t is_bounded;
1288 cairo_status_t status;
1289 const unsigned char *uri;
1290 unsigned long uri_len;
1292 if (_cairo_user_data_array_get_data (&surface->user_data,
1293 (cairo_user_data_key_t *) document))
1295 return CAIRO_STATUS_SUCCESS;
1298 is_bounded = _cairo_surface_get_extents (surface, &extents);
1299 assert (is_bounded);
1301 _cairo_output_stream_printf (document->xml_node_defs,
1302 "<image id=\"image%d\" width=\"%d\" height=\"%d\"",
1303 surface->unique_id,
1304 extents.width, extents.height);
1306 _cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\"");
1308 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_URI,
1309 &uri, &uri_len);
1310 if (uri != NULL) {
1311 _cairo_svg_surface_emit_attr_value (document->xml_node_defs,
1312 uri, uri_len);
1313 } else {
1314 status = _cairo_surface_base64_encode (surface,
1315 document->xml_node_defs);
1316 if (unlikely (status))
1317 return status;
1320 _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n");
1322 /* and tag it */
1323 return _cairo_user_data_array_set_data (&surface->user_data,
1324 (cairo_user_data_key_t *) document,
1325 document, NULL);
1328 static cairo_status_t
1329 _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *output,
1330 cairo_svg_surface_t *svg_surface,
1331 cairo_operator_t op,
1332 cairo_surface_pattern_t *pattern,
1333 int pattern_id,
1334 const cairo_matrix_t *parent_matrix,
1335 const char *extra_attributes)
1337 cairo_status_t status;
1338 cairo_matrix_t p2u;
1340 p2u = pattern->base.matrix;
1341 status = cairo_matrix_invert (&p2u);
1342 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1343 assert (status == CAIRO_STATUS_SUCCESS);
1345 status = _cairo_svg_surface_emit_surface (svg_surface->document,
1346 pattern->surface);
1347 if (unlikely (status))
1348 return status;
1350 if (pattern_id != invalid_pattern_id) {
1351 cairo_rectangle_int_t extents;
1352 cairo_bool_t is_bounded;
1354 is_bounded = _cairo_surface_get_extents (pattern->surface, &extents);
1355 assert (is_bounded);
1357 _cairo_output_stream_printf (output,
1358 "<pattern id=\"pattern%d\" "
1359 "patternUnits=\"userSpaceOnUse\" "
1360 "width=\"%d\" height=\"%d\" ",
1361 pattern_id,
1362 extents.width, extents.height);
1363 _cairo_svg_surface_emit_transform (output,
1364 " patternTransform",
1365 &p2u, parent_matrix);
1366 _cairo_output_stream_printf (output, ">\n ");
1369 _cairo_output_stream_printf (output,
1370 "<use xlink:href=\"#image%d\"",
1371 pattern->surface->unique_id);
1372 if (extra_attributes)
1373 _cairo_output_stream_printf (output, " %s", extra_attributes);
1375 if (pattern_id == invalid_pattern_id) {
1376 _cairo_svg_surface_emit_operator (output, svg_surface, op);
1377 _cairo_svg_surface_emit_transform (output,
1378 " transform",
1379 &p2u, parent_matrix);
1381 _cairo_output_stream_printf (output, "/>\n");
1384 if (pattern_id != invalid_pattern_id)
1385 _cairo_output_stream_printf (output, "</pattern>\n");
1387 return CAIRO_STATUS_SUCCESS;
1390 static cairo_status_t
1391 _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document,
1392 cairo_recording_surface_t *source)
1394 cairo_status_t status;
1395 cairo_surface_t *paginated_surface;
1396 cairo_svg_surface_t *svg_surface;
1397 cairo_array_t *page_set;
1399 cairo_output_stream_t *contents;
1401 if (_cairo_user_data_array_get_data (&source->base.user_data,
1402 (cairo_user_data_key_t *) document))
1404 return CAIRO_STATUS_SUCCESS;
1407 paginated_surface = _cairo_svg_surface_create_for_document (document,
1408 source->base.content,
1409 source->extents_pixels.width,
1410 source->extents_pixels.height);
1411 if (unlikely (paginated_surface->status))
1412 return paginated_surface->status;
1414 svg_surface = (cairo_svg_surface_t *)
1415 _cairo_paginated_surface_get_target (paginated_surface);
1416 cairo_surface_set_fallback_resolution (paginated_surface,
1417 document->owner->x_fallback_resolution,
1418 document->owner->y_fallback_resolution);
1419 cairo_surface_set_device_offset (&svg_surface->base,
1420 -source->extents_pixels.x,
1421 -source->extents_pixels.y);
1423 status = _cairo_recording_surface_replay (&source->base, paginated_surface);
1424 if (unlikely (status)) {
1425 cairo_surface_destroy (paginated_surface);
1426 return status;
1429 cairo_surface_show_page (paginated_surface);
1430 status = cairo_surface_status (paginated_surface);
1431 if (unlikely (status)) {
1432 cairo_surface_destroy (paginated_surface);
1433 return status;
1436 if (! svg_surface->is_base_clip_emitted) {
1437 svg_surface->is_base_clip_emitted = TRUE;
1438 _cairo_output_stream_printf (document->xml_node_defs,
1439 "<clipPath id=\"clip%d\">\n"
1440 " <rect width=\"%f\" height=\"%f\"/>\n"
1441 "</clipPath>\n",
1442 svg_surface->base_clip,
1443 svg_surface->width,
1444 svg_surface->height);
1447 if (source->base.content == CAIRO_CONTENT_ALPHA) {
1448 _cairo_svg_surface_emit_alpha_filter (document);
1449 _cairo_output_stream_printf (document->xml_node_defs,
1450 "<g id=\"surface%d\" "
1451 "clip-path=\"url(#clip%d)\" "
1452 "filter=\"url(#alpha)\">\n",
1453 source->base.unique_id,
1454 svg_surface->base_clip);
1455 } else {
1456 _cairo_output_stream_printf (document->xml_node_defs,
1457 "<g id=\"surface%d\" "
1458 "clip-path=\"url(#clip%d)\">\n",
1459 source->base.unique_id,
1460 svg_surface->base_clip);
1463 contents = svg_surface->xml_node;
1464 page_set = &svg_surface->page_set;
1466 if (_cairo_memory_stream_length (contents) > 0) {
1467 if (unlikely (_cairo_svg_surface_store_page (svg_surface) == NULL)) {
1468 cairo_surface_destroy (paginated_surface);
1469 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1473 if (page_set->num_elements > 0) {
1474 cairo_svg_page_t *page;
1476 page = _cairo_array_index (page_set, page_set->num_elements - 1);
1477 _cairo_memory_stream_copy (page->xml_node, document->xml_node_defs);
1480 _cairo_output_stream_printf (document->xml_node_defs, "</g>\n");
1482 status = cairo_surface_status (paginated_surface);
1483 cairo_surface_destroy (paginated_surface);
1485 if (unlikely (status))
1486 return status;
1488 /* and tag it */
1489 return _cairo_user_data_array_set_data (&source->base.user_data,
1490 (cairo_user_data_key_t *) document,
1491 document, NULL);
1494 static cairo_recording_surface_t *
1495 to_recording_surface (const cairo_surface_pattern_t *pattern)
1497 cairo_surface_t *surface = pattern->surface;
1498 if (_cairo_surface_is_paginated (surface))
1499 surface = _cairo_paginated_surface_get_recording (surface);
1500 if (_cairo_surface_is_snapshot (surface))
1501 surface = _cairo_surface_snapshot_get_target (surface);
1502 return (cairo_recording_surface_t *) surface;
1505 static cairo_status_t
1506 _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *output,
1507 cairo_svg_surface_t *surface,
1508 cairo_operator_t op,
1509 cairo_surface_pattern_t *pattern,
1510 int pattern_id,
1511 const cairo_matrix_t *parent_matrix,
1512 const char *extra_attributes)
1514 cairo_svg_document_t *document = surface->document;
1515 cairo_recording_surface_t *recording_surface;
1516 cairo_matrix_t p2u;
1517 cairo_status_t status;
1519 p2u = pattern->base.matrix;
1520 status = cairo_matrix_invert (&p2u);
1521 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1522 assert (status == CAIRO_STATUS_SUCCESS);
1524 recording_surface = to_recording_surface (pattern);
1525 status = _cairo_svg_surface_emit_recording_surface (document, recording_surface);
1526 if (unlikely (status))
1527 return status;
1529 if (pattern_id != invalid_pattern_id) {
1530 _cairo_output_stream_printf (output,
1531 "<pattern id=\"pattern%d\" "
1532 "patternUnits=\"userSpaceOnUse\" "
1533 "width=\"%d\" height=\"%d\"",
1534 pattern_id,
1535 recording_surface->extents.width,
1536 recording_surface->extents.height);
1537 _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
1538 _cairo_output_stream_printf (output, ">\n");
1541 _cairo_output_stream_printf (output,
1542 "<use xlink:href=\"#surface%d\"",
1543 recording_surface->base.unique_id);
1545 if (pattern_id == invalid_pattern_id) {
1546 _cairo_svg_surface_emit_operator (output, surface, op);
1547 _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
1550 if (extra_attributes)
1551 _cairo_output_stream_printf (output, " %s", extra_attributes);
1553 _cairo_output_stream_printf (output, "/>\n");
1555 if (pattern_id != invalid_pattern_id)
1556 _cairo_output_stream_printf (output, "</pattern>\n");
1558 return CAIRO_STATUS_SUCCESS;
1561 static cairo_status_t
1562 _cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output,
1563 cairo_svg_surface_t *surface,
1564 cairo_operator_t op,
1565 cairo_surface_pattern_t *pattern,
1566 int pattern_id,
1567 const cairo_matrix_t *parent_matrix,
1568 const char *extra_attributes)
1571 if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
1572 return _cairo_svg_surface_emit_composite_recording_pattern (output, surface,
1573 op, pattern,
1574 pattern_id,
1575 parent_matrix,
1576 extra_attributes);
1579 return _cairo_svg_surface_emit_composite_surface_pattern (output, surface,
1580 op, pattern,
1581 pattern_id,
1582 parent_matrix,
1583 extra_attributes);
1586 static cairo_status_t
1587 _cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface,
1588 cairo_solid_pattern_t *pattern,
1589 cairo_output_stream_t *style,
1590 cairo_bool_t is_stroke)
1592 _cairo_output_stream_printf (style, is_stroke ?
1593 "stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;":
1594 "fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;",
1595 pattern->color.red * 100.0,
1596 pattern->color.green * 100.0,
1597 pattern->color.blue * 100.0,
1598 pattern->color.alpha);
1600 return CAIRO_STATUS_SUCCESS;
1603 static cairo_status_t
1604 _cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface,
1605 cairo_surface_pattern_t *pattern,
1606 cairo_output_stream_t *style,
1607 cairo_bool_t is_stroke,
1608 const cairo_matrix_t *parent_matrix)
1610 cairo_svg_document_t *document = surface->document;
1611 cairo_status_t status;
1612 int pattern_id;
1614 pattern_id = document->pattern_id++;
1615 status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs,
1616 surface, CAIRO_OPERATOR_SOURCE, pattern,
1617 pattern_id, parent_matrix, NULL);
1618 if (unlikely (status))
1619 return status;
1621 _cairo_output_stream_printf (style,
1622 "%s:url(#pattern%d);",
1623 is_stroke ? "stroke" : "fill",
1624 pattern_id);
1626 return CAIRO_STATUS_SUCCESS;
1629 static cairo_status_t
1630 _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output,
1631 cairo_gradient_pattern_t const *pattern,
1632 double start_offset,
1633 cairo_bool_t reverse_stops,
1634 cairo_bool_t emulate_reflect)
1636 cairo_gradient_stop_t *stops;
1637 double offset;
1638 unsigned int n_stops;
1639 unsigned int i;
1641 if (pattern->n_stops < 1)
1642 return CAIRO_STATUS_SUCCESS;
1644 if (pattern->n_stops == 1) {
1645 _cairo_output_stream_printf (output,
1646 "<stop offset=\"%f\" style=\""
1647 "stop-color:rgb(%f%%,%f%%,%f%%);"
1648 "stop-opacity:%f;\"/>\n",
1649 pattern->stops[0].offset,
1650 pattern->stops[0].color.red * 100.0,
1651 pattern->stops[0].color.green * 100.0,
1652 pattern->stops[0].color.blue * 100.0,
1653 pattern->stops[0].color.alpha);
1654 return CAIRO_STATUS_SUCCESS;
1657 if (emulate_reflect || reverse_stops) {
1658 n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops;
1659 stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t));
1660 if (unlikely (stops == NULL))
1661 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1663 for (i = 0; i < pattern->n_stops; i++) {
1664 if (reverse_stops) {
1665 stops[i] = pattern->stops[pattern->n_stops - i - 1];
1666 stops[i].offset = 1.0 - stops[i].offset;
1667 } else
1668 stops[i] = pattern->stops[i];
1669 if (emulate_reflect) {
1670 stops[i].offset /= 2;
1671 if (i > 0 && i < (pattern->n_stops - 1)) {
1672 if (reverse_stops) {
1673 stops[i + pattern->n_stops - 1] = pattern->stops[i];
1674 stops[i + pattern->n_stops - 1].offset =
1675 0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset;
1676 } else {
1677 stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1];
1678 stops[i + pattern->n_stops - 1].offset =
1679 1 - 0.5 * stops[i + pattern->n_stops - 1].offset;
1684 } else {
1685 n_stops = pattern->n_stops;
1686 stops = pattern->stops;
1689 if (start_offset >= 0.0)
1690 for (i = 0; i < n_stops; i++) {
1691 offset = start_offset + (1 - start_offset ) * stops[i].offset;
1692 _cairo_output_stream_printf (output,
1693 "<stop offset=\"%f\" style=\""
1694 "stop-color:rgb(%f%%,%f%%,%f%%);"
1695 "stop-opacity:%f;\"/>\n",
1696 offset,
1697 stops[i].color.red * 100.0,
1698 stops[i].color.green * 100.0,
1699 stops[i].color.blue * 100.0,
1700 stops[i].color.alpha);
1702 else {
1703 cairo_bool_t found = FALSE;
1704 unsigned int offset_index;
1705 cairo_color_stop_t offset_color_start, offset_color_stop;
1707 for (i = 0; i < n_stops; i++) {
1708 if (stops[i].offset >= -start_offset) {
1709 if (i > 0) {
1710 if (stops[i].offset != stops[i-1].offset) {
1711 double x0, x1;
1712 cairo_color_stop_t *color0, *color1;
1714 x0 = stops[i-1].offset;
1715 x1 = stops[i].offset;
1716 color0 = &stops[i-1].color;
1717 color1 = &stops[i].color;
1718 offset_color_start.red = color0->red + (color1->red - color0->red)
1719 * (-start_offset - x0) / (x1 - x0);
1720 offset_color_start.green = color0->green + (color1->green - color0->green)
1721 * (-start_offset - x0) / (x1 - x0);
1722 offset_color_start.blue = color0->blue + (color1->blue - color0->blue)
1723 * (-start_offset - x0) / (x1 - x0);
1724 offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha)
1725 * (-start_offset - x0) / (x1 - x0);
1726 offset_color_stop = offset_color_start;
1727 } else {
1728 offset_color_stop = stops[i-1].color;
1729 offset_color_start = stops[i].color;
1731 } else
1732 offset_color_stop = offset_color_start = stops[i].color;
1733 offset_index = i;
1734 found = TRUE;
1735 break;
1739 if (!found) {
1740 offset_index = n_stops - 1;
1741 offset_color_stop = offset_color_start = stops[offset_index].color;
1744 _cairo_output_stream_printf (output,
1745 "<stop offset=\"0\" style=\""
1746 "stop-color:rgb(%f%%,%f%%,%f%%);"
1747 "stop-opacity:%f;\"/>\n",
1748 offset_color_start.red * 100.0,
1749 offset_color_start.green * 100.0,
1750 offset_color_start.blue * 100.0,
1751 offset_color_start.alpha);
1752 for (i = offset_index; i < n_stops; i++) {
1753 _cairo_output_stream_printf (output,
1754 "<stop offset=\"%f\" style=\""
1755 "stop-color:rgb(%f%%,%f%%,%f%%);"
1756 "stop-opacity:%f;\"/>\n",
1757 stops[i].offset + start_offset,
1758 stops[i].color.red * 100.0,
1759 stops[i].color.green * 100.0,
1760 stops[i].color.blue * 100.0,
1761 stops[i].color.alpha);
1763 for (i = 0; i < offset_index; i++) {
1764 _cairo_output_stream_printf (output,
1765 "<stop offset=\"%f\" style=\""
1766 "stop-color:rgb(%f%%,%f%%,%f%%);"
1767 "stop-opacity:%f;\"/>\n",
1768 1.0 + stops[i].offset + start_offset,
1769 stops[i].color.red * 100.0,
1770 stops[i].color.green * 100.0,
1771 stops[i].color.blue * 100.0,
1772 stops[i].color.alpha);
1775 _cairo_output_stream_printf (output,
1776 "<stop offset=\"1\" style=\""
1777 "stop-color:rgb(%f%%,%f%%,%f%%);"
1778 "stop-opacity:%f;\"/>\n",
1779 offset_color_stop.red * 100.0,
1780 offset_color_stop.green * 100.0,
1781 offset_color_stop.blue * 100.0,
1782 offset_color_stop.alpha);
1786 if (reverse_stops || emulate_reflect)
1787 free (stops);
1789 return CAIRO_STATUS_SUCCESS;
1792 static void
1793 _cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output,
1794 cairo_pattern_t *pattern)
1796 switch (pattern->extend) {
1797 case CAIRO_EXTEND_REPEAT:
1798 _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" ");
1799 break;
1800 case CAIRO_EXTEND_REFLECT:
1801 _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" ");
1802 break;
1803 case CAIRO_EXTEND_NONE:
1804 case CAIRO_EXTEND_PAD:
1805 break;
1809 static cairo_status_t
1810 _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface,
1811 cairo_linear_pattern_t *pattern,
1812 cairo_output_stream_t *style,
1813 cairo_bool_t is_stroke,
1814 const cairo_matrix_t *parent_matrix)
1816 cairo_svg_document_t *document = surface->document;
1817 cairo_matrix_t p2u;
1818 cairo_status_t status;
1820 p2u = pattern->base.base.matrix;
1821 status = cairo_matrix_invert (&p2u);
1822 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1823 assert (status == CAIRO_STATUS_SUCCESS);
1825 _cairo_output_stream_printf (document->xml_node_defs,
1826 "<linearGradient id=\"linear%d\" "
1827 "gradientUnits=\"userSpaceOnUse\" "
1828 "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
1829 document->linear_pattern_id,
1830 pattern->pd1.x, pattern->pd1.y,
1831 pattern->pd2.x, pattern->pd2.y);
1833 _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
1834 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1835 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1837 status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
1838 &pattern->base, 0.0,
1839 FALSE, FALSE);
1840 if (unlikely (status))
1841 return status;
1843 _cairo_output_stream_printf (document->xml_node_defs,
1844 "</linearGradient>\n");
1846 _cairo_output_stream_printf (style,
1847 "%s:url(#linear%d);",
1848 is_stroke ? "stroke" : "fill",
1849 document->linear_pattern_id);
1851 document->linear_pattern_id++;
1853 return CAIRO_STATUS_SUCCESS;
1856 static cairo_status_t
1857 _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface,
1858 cairo_radial_pattern_t *pattern,
1859 cairo_output_stream_t *style,
1860 cairo_bool_t is_stroke,
1861 const cairo_matrix_t *parent_matrix)
1863 cairo_svg_document_t *document = surface->document;
1864 cairo_matrix_t p2u;
1865 cairo_extend_t extend;
1866 double x0, y0, x1, y1, r0, r1;
1867 double fx, fy;
1868 cairo_bool_t reverse_stops;
1869 cairo_status_t status;
1870 cairo_circle_double_t *c0, *c1;
1872 extend = pattern->base.base.extend;
1874 if (pattern->cd1.radius < pattern->cd2.radius) {
1875 c0 = &pattern->cd1;
1876 c1 = &pattern->cd2;
1877 reverse_stops = FALSE;
1878 } else {
1879 c0 = &pattern->cd2;
1880 c1 = &pattern->cd1;
1881 reverse_stops = TRUE;
1884 x0 = c0->center.x;
1885 y0 = c0->center.y;
1886 r0 = c0->radius;
1887 x1 = c1->center.x;
1888 y1 = c1->center.y;
1889 r1 = c1->radius;
1891 p2u = pattern->base.base.matrix;
1892 status = cairo_matrix_invert (&p2u);
1893 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1894 assert (status == CAIRO_STATUS_SUCCESS);
1896 if (r0 == r1) {
1897 unsigned int n_stops = pattern->base.n_stops;
1899 _cairo_output_stream_printf (document->xml_node_defs,
1900 "<radialGradient id=\"radial%d\" "
1901 "gradientUnits=\"userSpaceOnUse\" "
1902 "cx=\"%f\" cy=\"%f\" "
1903 "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1904 document->radial_pattern_id,
1905 x1, y1,
1906 x1, y1, r1);
1907 _cairo_svg_surface_emit_transform (document->xml_node_defs,
1908 "gradientTransform",
1909 &p2u, parent_matrix);
1910 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1912 if (extend == CAIRO_EXTEND_NONE || n_stops < 1)
1913 _cairo_output_stream_printf (document->xml_node_defs,
1914 "<stop offset=\"0\" style=\""
1915 "stop-color:rgb(0%%,0%%,0%%);"
1916 "stop-opacity:0;\"/>\n");
1917 else {
1918 _cairo_output_stream_printf (document->xml_node_defs,
1919 "<stop offset=\"0\" style=\""
1920 "stop-color:rgb(%f%%,%f%%,%f%%);"
1921 "stop-opacity %f;\"/>\n",
1922 pattern->base.stops[0].color.red * 100.0,
1923 pattern->base.stops[0].color.green * 100.0,
1924 pattern->base.stops[0].color.blue * 100.0,
1925 pattern->base.stops[0].color.alpha);
1926 if (n_stops > 1)
1927 _cairo_output_stream_printf (document->xml_node_defs,
1928 "<stop offset=\"0\" style=\""
1929 "stop-color:rgb(%f%%,%f%%,%f%%);"
1930 "stop-opacity:%f;\"/>\n",
1931 pattern->base.stops[n_stops - 1].color.red * 100.0,
1932 pattern->base.stops[n_stops - 1].color.green * 100.0,
1933 pattern->base.stops[n_stops - 1].color.blue * 100.0,
1934 pattern->base.stops[n_stops - 1].color.alpha);
1937 } else {
1938 double offset, r, x, y;
1939 cairo_bool_t emulate_reflect = FALSE;
1941 fx = (r1 * x0 - r0 * x1) / (r1 - r0);
1942 fy = (r1 * y0 - r0 * y1) / (r1 - r0);
1944 /* SVG doesn't support the inner circle and use instead a gradient focal.
1945 * That means we need to emulate the cairo behaviour by processing the
1946 * cairo gradient stops.
1947 * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
1948 * it's just a matter of stop position translation and calculation of
1949 * the corresponding SVG radial gradient focal.
1950 * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
1951 * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
1952 * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
1953 * list that maps to the original cairo stop list.
1955 if ((extend == CAIRO_EXTEND_REFLECT
1956 || extend == CAIRO_EXTEND_REPEAT)
1957 && r0 > 0.0) {
1958 double r_org = r1;
1960 if (extend == CAIRO_EXTEND_REFLECT) {
1961 r1 = 2 * r1 - r0;
1962 emulate_reflect = TRUE;
1965 offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
1966 r = r1 - r0;
1968 /* New position of outer circle. */
1969 x = r * (x1 - fx) / r_org + fx;
1970 y = r * (y1 - fy) / r_org + fy;
1972 x1 = x;
1973 y1 = y;
1974 r1 = r;
1975 r0 = 0.0;
1976 } else {
1977 offset = r0 / r1;
1980 _cairo_output_stream_printf (document->xml_node_defs,
1981 "<radialGradient id=\"radial%d\" "
1982 "gradientUnits=\"userSpaceOnUse\" "
1983 "cx=\"%f\" cy=\"%f\" "
1984 "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1985 document->radial_pattern_id,
1986 x1, y1,
1987 fx, fy, r1);
1989 if (emulate_reflect)
1990 _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
1991 else
1992 _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
1993 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1994 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1996 /* To support cairo's EXTEND_NONE, (for which SVG has no similar
1997 * notion), we add transparent color stops on either end of the
1998 * user-provided stops. */
1999 if (extend == CAIRO_EXTEND_NONE) {
2000 _cairo_output_stream_printf (document->xml_node_defs,
2001 "<stop offset=\"0\" style=\""
2002 "stop-color:rgb(0%%,0%%,0%%);"
2003 "stop-opacity:0;\"/>\n");
2004 if (r0 != 0.0)
2005 _cairo_output_stream_printf (document->xml_node_defs,
2006 "<stop offset=\"%f\" style=\""
2007 "stop-color:rgb(0%%,0%%,0%%);"
2008 "stop-opacity:0;\"/>\n",
2009 r0 / r1);
2011 status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
2012 &pattern->base, offset,
2013 reverse_stops,
2014 emulate_reflect);
2015 if (unlikely (status))
2016 return status;
2018 if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
2019 _cairo_output_stream_printf (document->xml_node_defs,
2020 "<stop offset=\"1.0\" style=\""
2021 "stop-color:rgb(0%%,0%%,0%%);"
2022 "stop-opacity:0;\"/>\n");
2025 _cairo_output_stream_printf (document->xml_node_defs,
2026 "</radialGradient>\n");
2028 _cairo_output_stream_printf (style,
2029 "%s:url(#radial%d);",
2030 is_stroke ? "stroke" : "fill",
2031 document->radial_pattern_id);
2033 document->radial_pattern_id++;
2035 return CAIRO_STATUS_SUCCESS;
2038 static cairo_status_t
2039 _cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface,
2040 const cairo_pattern_t *pattern,
2041 cairo_output_stream_t *output,
2042 cairo_bool_t is_stroke,
2043 const cairo_matrix_t *parent_matrix)
2045 switch (pattern->type) {
2046 case CAIRO_PATTERN_TYPE_SOLID:
2047 return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern,
2048 output, is_stroke);
2050 case CAIRO_PATTERN_TYPE_SURFACE:
2051 return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern,
2052 output, is_stroke, parent_matrix);
2054 case CAIRO_PATTERN_TYPE_LINEAR:
2055 return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern,
2056 output, is_stroke, parent_matrix);
2058 case CAIRO_PATTERN_TYPE_RADIAL:
2059 return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern,
2060 output, is_stroke, parent_matrix);
2062 case CAIRO_PATTERN_TYPE_MESH:
2063 case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
2064 ASSERT_NOT_REACHED;
2066 return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
2069 static cairo_status_t
2070 _cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output,
2071 cairo_svg_surface_t *surface,
2072 cairo_operator_t op,
2073 const cairo_pattern_t *source,
2074 cairo_fill_rule_t fill_rule,
2075 const cairo_matrix_t *parent_matrix)
2077 _cairo_output_stream_printf (output,
2078 "fill-rule:%s;",
2079 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
2080 "evenodd" : "nonzero");
2081 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2082 return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix);
2085 static cairo_status_t
2086 _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output,
2087 cairo_svg_surface_t *surface,
2088 cairo_operator_t op,
2089 const cairo_pattern_t *source,
2090 const cairo_stroke_style_t *stroke_style,
2091 const cairo_matrix_t *parent_matrix)
2093 cairo_status_t status;
2094 const char *line_cap, *line_join;
2095 unsigned int i;
2097 switch (stroke_style->line_cap) {
2098 case CAIRO_LINE_CAP_BUTT:
2099 line_cap = "butt";
2100 break;
2101 case CAIRO_LINE_CAP_ROUND:
2102 line_cap = "round";
2103 break;
2104 case CAIRO_LINE_CAP_SQUARE:
2105 line_cap = "square";
2106 break;
2107 default:
2108 ASSERT_NOT_REACHED;
2111 switch (stroke_style->line_join) {
2112 case CAIRO_LINE_JOIN_MITER:
2113 line_join = "miter";
2114 break;
2115 case CAIRO_LINE_JOIN_ROUND:
2116 line_join = "round";
2117 break;
2118 case CAIRO_LINE_JOIN_BEVEL:
2119 line_join = "bevel";
2120 break;
2121 default:
2122 ASSERT_NOT_REACHED;
2125 _cairo_output_stream_printf (output,
2126 "stroke-width:%f;"
2127 "stroke-linecap:%s;"
2128 "stroke-linejoin:%s;",
2129 stroke_style->line_width,
2130 line_cap,
2131 line_join);
2133 status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix);
2134 if (unlikely (status))
2135 return status;
2137 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2139 if (stroke_style->num_dashes > 0) {
2140 _cairo_output_stream_printf (output, "stroke-dasharray:");
2141 for (i = 0; i < stroke_style->num_dashes; i++) {
2142 _cairo_output_stream_printf (output, "%f",
2143 stroke_style->dash[i]);
2144 if (i + 1 < stroke_style->num_dashes)
2145 _cairo_output_stream_printf (output, ",");
2146 else
2147 _cairo_output_stream_printf (output, ";");
2149 if (stroke_style->dash_offset != 0.0) {
2150 _cairo_output_stream_printf (output,
2151 "stroke-dashoffset:%f;",
2152 stroke_style->dash_offset);
2156 _cairo_output_stream_printf (output,
2157 "stroke-miterlimit:%f;",
2158 stroke_style->miter_limit);
2160 return CAIRO_STATUS_SUCCESS;
2163 static cairo_int_status_t
2164 _cairo_svg_surface_fill_stroke (void *abstract_surface,
2165 cairo_operator_t fill_op,
2166 const cairo_pattern_t *fill_source,
2167 cairo_fill_rule_t fill_rule,
2168 double fill_tolerance,
2169 cairo_antialias_t fill_antialias,
2170 const cairo_path_fixed_t*path,
2171 cairo_operator_t stroke_op,
2172 const cairo_pattern_t *stroke_source,
2173 const cairo_stroke_style_t *stroke_style,
2174 const cairo_matrix_t *stroke_ctm,
2175 const cairo_matrix_t *stroke_ctm_inverse,
2176 double stroke_tolerance,
2177 cairo_antialias_t stroke_antialias,
2178 const cairo_clip_t *clip)
2180 cairo_svg_surface_t *surface = abstract_surface;
2181 cairo_status_t status;
2183 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2184 if (unlikely (status))
2185 return status;
2187 _cairo_output_stream_printf (surface->xml_node, "<path style=\"");
2188 status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, fill_op,
2189 fill_source, fill_rule, stroke_ctm_inverse);
2190 if (unlikely (status))
2191 return status;
2193 status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op,
2194 stroke_source, stroke_style, stroke_ctm_inverse);
2195 if (unlikely (status))
2196 return status;
2198 _cairo_output_stream_printf (surface->xml_node, "\" ");
2200 _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse);
2202 _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL);
2203 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2205 return CAIRO_STATUS_SUCCESS;
2208 static cairo_int_status_t
2209 _cairo_svg_surface_fill (void *abstract_surface,
2210 cairo_operator_t op,
2211 const cairo_pattern_t *source,
2212 const cairo_path_fixed_t*path,
2213 cairo_fill_rule_t fill_rule,
2214 double tolerance,
2215 cairo_antialias_t antialias,
2216 const cairo_clip_t *clip)
2218 cairo_svg_surface_t *surface = abstract_surface;
2219 cairo_status_t status;
2221 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2222 return _cairo_svg_surface_analyze_operation (surface, op, source);
2224 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2226 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2227 if (unlikely (status))
2228 return status;
2230 _cairo_output_stream_printf (surface->xml_node, "<path style=\" stroke:none;");
2231 status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, op, source, fill_rule, NULL);
2232 if (unlikely (status))
2233 return status;
2235 _cairo_output_stream_printf (surface->xml_node, "\" ");
2237 _cairo_svg_surface_emit_path (surface->xml_node, path, NULL);
2239 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2241 return CAIRO_STATUS_SUCCESS;
2244 static cairo_bool_t
2245 _cairo_svg_surface_get_extents (void *abstract_surface,
2246 cairo_rectangle_int_t *rectangle)
2248 cairo_svg_surface_t *surface = abstract_surface;
2250 rectangle->x = 0;
2251 rectangle->y = 0;
2253 /* XXX: The conversion to integers here is pretty bogus, (not to
2254 * mention the arbitrary limitation of width to a short(!). We
2255 * may need to come up with a better interface for get_size.
2257 rectangle->width = ceil (surface->width);
2258 rectangle->height = ceil (surface->height);
2260 return TRUE;
2263 static cairo_status_t
2264 _cairo_svg_surface_emit_paint (cairo_output_stream_t *output,
2265 cairo_svg_surface_t *surface,
2266 cairo_operator_t op,
2267 const cairo_pattern_t *source,
2268 const cairo_pattern_t *mask_source,
2269 const char *extra_attributes)
2271 cairo_status_t status;
2273 if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
2274 source->extend == CAIRO_EXTEND_NONE)
2275 return _cairo_svg_surface_emit_composite_pattern (output,
2276 surface,
2278 (cairo_surface_pattern_t *) source,
2279 invalid_pattern_id,
2280 mask_source ? &mask_source->matrix :NULL,
2281 extra_attributes);
2283 _cairo_output_stream_printf (output,
2284 "<rect x=\"0\" y=\"0\" "
2285 "width=\"%f\" height=\"%f\" "
2286 "style=\"",
2287 surface->width, surface->height);
2288 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2289 status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL);
2290 if (unlikely (status))
2291 return status;
2293 _cairo_output_stream_printf (output, "stroke:none;\"");
2295 if (extra_attributes)
2296 _cairo_output_stream_printf (output, " %s", extra_attributes);
2298 _cairo_output_stream_printf (output, "/>\n");
2300 return CAIRO_STATUS_SUCCESS;
2303 static cairo_int_status_t
2304 _cairo_svg_surface_paint (void *abstract_surface,
2305 cairo_operator_t op,
2306 const cairo_pattern_t *source,
2307 const cairo_clip_t *clip)
2309 cairo_status_t status;
2310 cairo_svg_surface_t *surface = abstract_surface;
2312 /* Emulation of clear and source operators, when no clipping region
2313 * is defined. We just delete existing content of surface root node,
2314 * and exit early if operator is clear.
2316 if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) &&
2317 clip == NULL)
2319 switch (surface->paginated_mode) {
2320 case CAIRO_PAGINATED_MODE_FALLBACK:
2321 ASSERT_NOT_REACHED;
2322 case CAIRO_PAGINATED_MODE_ANALYZE:
2323 return CAIRO_STATUS_SUCCESS;
2325 case CAIRO_PAGINATED_MODE_RENDER:
2326 status = _cairo_output_stream_destroy (surface->xml_node);
2327 if (unlikely (status)) {
2328 surface->xml_node = NULL;
2329 return status;
2332 surface->xml_node = _cairo_memory_stream_create ();
2333 if (_cairo_output_stream_get_status (surface->xml_node)) {
2334 status = _cairo_output_stream_destroy (surface->xml_node);
2335 surface->xml_node = NULL;
2336 return status;
2339 if (op == CAIRO_OPERATOR_CLEAR) {
2340 if (surface->content == CAIRO_CONTENT_COLOR) {
2341 _cairo_output_stream_printf (surface->xml_node,
2342 "<rect "
2343 "width=\"%f\" height=\"%f\" "
2344 "style=\"opacity:1;"
2345 "stroke:none;"
2346 "fill:rgb(0,0,0);\"/>\n",
2347 surface->width, surface->height);
2349 return CAIRO_STATUS_SUCCESS;
2351 break;
2353 } else {
2354 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2355 return _cairo_svg_surface_analyze_operation (surface, op, source);
2357 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2360 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2361 if (unlikely (status))
2362 return status;
2364 return _cairo_svg_surface_emit_paint (surface->xml_node,
2365 surface, op, source, 0, NULL);
2368 static cairo_int_status_t
2369 _cairo_svg_surface_mask (void *abstract_surface,
2370 cairo_operator_t op,
2371 const cairo_pattern_t *source,
2372 const cairo_pattern_t *mask,
2373 const cairo_clip_t *clip)
2375 cairo_status_t status;
2376 cairo_svg_surface_t *surface = abstract_surface;
2377 cairo_svg_document_t *document = surface->document;
2378 cairo_output_stream_t *mask_stream;
2379 char buffer[64];
2380 cairo_bool_t discard_filter = FALSE;
2381 unsigned int mask_id;
2383 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2384 cairo_status_t source_status, mask_status;
2386 source_status = _cairo_svg_surface_analyze_operation (surface, op, source);
2387 if (_cairo_status_is_error (source_status))
2388 return source_status;
2390 if (mask->has_component_alpha) {
2391 mask_status = CAIRO_INT_STATUS_UNSUPPORTED;
2392 } else {
2393 mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask);
2394 if (_cairo_status_is_error (mask_status))
2395 return mask_status;
2398 return _cairo_analysis_surface_merge_status (source_status,
2399 mask_status);
2402 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2403 assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask));
2405 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2406 if (unlikely (status))
2407 return status;
2409 if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
2410 const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask;
2411 cairo_content_t content = surface_pattern->surface->content;
2412 if (content == CAIRO_CONTENT_ALPHA)
2413 discard_filter = TRUE;
2416 if (!discard_filter)
2417 _cairo_svg_surface_emit_alpha_filter (document);
2419 /* _cairo_svg_surface_emit_paint() will output a pattern definition to
2420 * document->xml_node_defs so we need to write the mask element to
2421 * a temporary stream and then copy that to xml_node_defs. */
2422 mask_stream = _cairo_memory_stream_create ();
2423 if (_cairo_output_stream_get_status (mask_stream))
2424 return _cairo_output_stream_destroy (mask_stream);
2426 mask_id = _cairo_svg_document_allocate_mask_id (document);
2428 _cairo_output_stream_printf (mask_stream,
2429 "<mask id=\"mask%d\">\n"
2430 "%s",
2431 mask_id,
2432 discard_filter ? "" : " <g filter=\"url(#alpha)\">\n");
2433 status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL);
2434 if (unlikely (status)) {
2435 cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream);
2436 return status;
2437 (void) ignore;
2440 _cairo_output_stream_printf (mask_stream,
2441 "%s"
2442 "</mask>\n",
2443 discard_filter ? "" : " </g>\n");
2444 _cairo_memory_stream_copy (mask_stream, document->xml_node_defs);
2446 status = _cairo_output_stream_destroy (mask_stream);
2447 if (unlikely (status))
2448 return status;
2450 snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"",
2451 mask_id);
2452 status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer);
2453 if (unlikely (status))
2454 return status;
2456 return CAIRO_STATUS_SUCCESS;
2459 static cairo_int_status_t
2460 _cairo_svg_surface_stroke (void *abstract_dst,
2461 cairo_operator_t op,
2462 const cairo_pattern_t *source,
2463 const cairo_path_fixed_t*path,
2464 const cairo_stroke_style_t *stroke_style,
2465 const cairo_matrix_t *ctm,
2466 const cairo_matrix_t *ctm_inverse,
2467 double tolerance,
2468 cairo_antialias_t antialias,
2469 const cairo_clip_t *clip)
2471 cairo_svg_surface_t *surface = abstract_dst;
2472 cairo_status_t status;
2474 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2475 return _cairo_svg_surface_analyze_operation (surface, op, source);
2477 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2479 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2480 if (unlikely (status))
2481 return status;
2483 _cairo_output_stream_printf (surface->xml_node, "<path style=\"fill:none;");
2484 status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, op,
2485 source, stroke_style, ctm_inverse);
2486 if (unlikely (status))
2487 return status;
2489 _cairo_output_stream_printf (surface->xml_node, "\" ");
2491 _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse);
2493 _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL);
2494 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2496 return CAIRO_STATUS_SUCCESS;
2499 static cairo_int_status_t
2500 _cairo_svg_surface_show_glyphs (void *abstract_surface,
2501 cairo_operator_t op,
2502 const cairo_pattern_t *pattern,
2503 cairo_glyph_t *glyphs,
2504 int num_glyphs,
2505 cairo_scaled_font_t *scaled_font,
2506 const cairo_clip_t *clip)
2508 cairo_svg_surface_t *surface = abstract_surface;
2509 cairo_svg_document_t *document = surface->document;
2510 cairo_path_fixed_t path;
2511 cairo_int_status_t status;
2512 cairo_scaled_font_subsets_glyph_t subset_glyph;
2513 int i;
2515 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2516 return _cairo_svg_surface_analyze_operation (surface, op, pattern);
2518 assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
2520 if (num_glyphs <= 0)
2521 return CAIRO_STATUS_SUCCESS;
2523 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2524 if (unlikely (status))
2525 return status;
2527 /* FIXME it's probably possible to apply a pattern of a gradient to
2528 * a group of symbols, but I don't know how yet. Gradients or patterns
2529 * are translated by x and y properties of use element. */
2530 if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
2531 goto FALLBACK;
2533 _cairo_output_stream_printf (surface->xml_node, "<g style=\"");
2534 status = _cairo_svg_surface_emit_pattern (surface, pattern,
2535 surface->xml_node, FALSE, NULL);
2536 if (unlikely (status))
2537 return status;
2539 _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op);
2541 _cairo_output_stream_printf (surface->xml_node, "\">\n");
2543 for (i = 0; i < num_glyphs; i++) {
2544 status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets,
2545 scaled_font, glyphs[i].index,
2546 NULL, 0,
2547 &subset_glyph);
2548 if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
2549 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2551 glyphs += i;
2552 num_glyphs -= i;
2553 goto FALLBACK;
2556 if (unlikely (status))
2557 return status;
2559 _cairo_output_stream_printf (surface->xml_node,
2560 " <use xlink:href=\"#glyph%d-%d\" "
2561 "x=\"%f\" y=\"%f\"/>\n",
2562 subset_glyph.font_id,
2563 subset_glyph.subset_glyph_index,
2564 glyphs[i].x, glyphs[i].y);
2567 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2569 return CAIRO_STATUS_SUCCESS;
2571 FALLBACK:
2572 _cairo_path_fixed_init (&path);
2574 status = _cairo_scaled_font_glyph_path (scaled_font,
2575 (cairo_glyph_t *) glyphs,
2576 num_glyphs, &path);
2578 if (unlikely (status)) {
2579 _cairo_path_fixed_fini (&path);
2580 return status;
2583 status = _cairo_svg_surface_fill (abstract_surface, op, pattern,
2584 &path, CAIRO_FILL_RULE_WINDING,
2585 0.0, CAIRO_ANTIALIAS_SUBPIXEL,
2586 clip);
2588 _cairo_path_fixed_fini (&path);
2590 return status;
2593 static void
2594 _cairo_svg_surface_get_font_options (void *abstract_surface,
2595 cairo_font_options_t *options)
2597 _cairo_font_options_init_default (options);
2599 cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
2600 cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
2601 cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
2602 _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
2606 static const char **
2607 _cairo_svg_surface_get_supported_mime_types (void *abstract_surface)
2609 return _cairo_svg_supported_mime_types;
2612 static const cairo_surface_backend_t cairo_svg_surface_backend = {
2613 CAIRO_SURFACE_TYPE_SVG,
2614 _cairo_svg_surface_finish,
2616 _cairo_default_context_create,
2618 NULL, /* create_similar: handled by wrapper */
2619 NULL, /* create_similar_image */
2620 NULL, /* map to image */
2621 NULL, /* unmap image */
2623 _cairo_surface_default_source,
2624 NULL, /* acquire_source_image */
2625 NULL, /* release_source_image */
2626 NULL, /* snapshot */
2628 _cairo_svg_surface_copy_page,
2629 _cairo_svg_surface_show_page,
2631 _cairo_svg_surface_get_extents,
2632 _cairo_svg_surface_get_font_options,
2634 NULL, /* flush */
2635 NULL, /* mark dirty rectangle */
2637 _cairo_svg_surface_paint,
2638 _cairo_svg_surface_mask,
2639 _cairo_svg_surface_stroke,
2640 _cairo_svg_surface_fill,
2641 _cairo_svg_surface_fill_stroke,
2642 _cairo_svg_surface_show_glyphs,
2643 NULL, /* has_show_text_glyphs */
2644 NULL, /* show_text_glyphs */
2645 _cairo_svg_surface_get_supported_mime_types,
2648 static cairo_status_t
2649 _cairo_svg_document_create (cairo_output_stream_t *output_stream,
2650 double width,
2651 double height,
2652 cairo_svg_version_t version,
2653 cairo_svg_document_t **document_out)
2655 cairo_svg_document_t *document;
2656 cairo_status_t status, status_ignored;
2658 if (output_stream->status)
2659 return output_stream->status;
2661 document = malloc (sizeof (cairo_svg_document_t));
2662 if (unlikely (document == NULL))
2663 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2665 /* The use of defs for font glyphs imposes no per-subset limit. */
2666 document->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
2667 if (unlikely (document->font_subsets == NULL)) {
2668 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2669 goto CLEANUP_DOCUMENT;
2672 document->output_stream = output_stream;
2673 document->refcount = 1;
2674 document->owner = NULL;
2675 document->finished = FALSE;
2676 document->width = width;
2677 document->height = height;
2679 document->linear_pattern_id = 0;
2680 document->radial_pattern_id = 0;
2681 document->pattern_id = 0;
2682 document->filter_id = 0;
2683 document->clip_id = 0;
2684 document->mask_id = 0;
2686 document->xml_node_defs = _cairo_memory_stream_create ();
2687 status = _cairo_output_stream_get_status (document->xml_node_defs);
2688 if (unlikely (status))
2689 goto CLEANUP_NODE_DEFS;
2691 document->xml_node_glyphs = _cairo_memory_stream_create ();
2692 status = _cairo_output_stream_get_status (document->xml_node_glyphs);
2693 if (unlikely (status))
2694 goto CLEANUP_NODE_GLYPHS;
2696 document->alpha_filter = FALSE;
2698 document->svg_version = version;
2700 *document_out = document;
2701 return CAIRO_STATUS_SUCCESS;
2703 CLEANUP_NODE_GLYPHS:
2704 status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs);
2705 CLEANUP_NODE_DEFS:
2706 status_ignored = _cairo_output_stream_destroy (document->xml_node_defs);
2707 _cairo_scaled_font_subsets_destroy (document->font_subsets);
2708 CLEANUP_DOCUMENT:
2709 free (document);
2710 return status;
2713 static cairo_svg_document_t *
2714 _cairo_svg_document_reference (cairo_svg_document_t *document)
2716 document->refcount++;
2718 return document;
2721 static unsigned int
2722 _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document)
2724 return document->mask_id++;
2727 static cairo_status_t
2728 _cairo_svg_document_destroy (cairo_svg_document_t *document)
2730 cairo_status_t status;
2732 document->refcount--;
2733 if (document->refcount > 0)
2734 return CAIRO_STATUS_SUCCESS;
2736 status = _cairo_svg_document_finish (document);
2738 free (document);
2740 return status;
2743 static cairo_status_t
2744 _cairo_svg_document_finish (cairo_svg_document_t *document)
2746 cairo_status_t status, status2;
2747 cairo_output_stream_t *output = document->output_stream;
2748 cairo_svg_page_t *page;
2749 unsigned int i;
2751 if (document->finished)
2752 return CAIRO_STATUS_SUCCESS;
2755 * Should we add DOCTYPE?
2757 * Google says no.
2759 * http://tech.groups.yahoo.com/group/svg-developers/message/48562:
2760 * There's a bunch of issues, but just to pick a few:
2761 * - they'll give false positives.
2762 * - they'll give false negatives.
2763 * - they're namespace-unaware.
2764 * - they don't wildcard.
2765 * So when they say OK they really haven't checked anything, when
2766 * they say NOT OK they might be on crack, and like all
2767 * namespace-unaware things they're a dead branch of the XML tree.
2769 * http://jwatt.org/svg/authoring/:
2770 * Unfortunately the SVG DTDs are a source of so many issues that the
2771 * SVG WG has decided not to write one for the upcoming SVG 1.2
2772 * standard. In fact SVG WG members are even telling people not to use
2773 * a DOCTYPE declaration in SVG 1.0 and 1.1 documents.
2776 _cairo_output_stream_printf (output,
2777 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2778 "<svg xmlns=\"http://www.w3.org/2000/svg\" "
2779 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
2780 "width=\"%fpt\" height=\"%fpt\" "
2781 "viewBox=\"0 0 %f %f\" version=\"%s\">\n",
2782 document->width, document->height,
2783 document->width, document->height,
2784 _cairo_svg_internal_version_strings [document->svg_version]);
2786 status = _cairo_svg_document_emit_font_subsets (document);
2788 if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 ||
2789 _cairo_memory_stream_length (document->xml_node_defs) > 0) {
2790 _cairo_output_stream_printf (output, "<defs>\n");
2791 if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
2792 _cairo_output_stream_printf (output, "<g>\n");
2793 _cairo_memory_stream_copy (document->xml_node_glyphs, output);
2794 _cairo_output_stream_printf (output, "</g>\n");
2796 _cairo_memory_stream_copy (document->xml_node_defs, output);
2797 _cairo_output_stream_printf (output, "</defs>\n");
2800 if (document->owner != NULL) {
2801 cairo_svg_surface_t *surface;
2803 surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner);
2804 if (surface->xml_node != NULL &&
2805 _cairo_memory_stream_length (surface->xml_node) > 0) {
2806 if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) {
2807 if (status == CAIRO_STATUS_SUCCESS)
2808 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2812 if (surface->page_set.num_elements > 1 &&
2813 _cairo_svg_version_has_page_set_support (document->svg_version)) {
2814 _cairo_output_stream_printf (output, "<pageSet>\n");
2815 for (i = 0; i < surface->page_set.num_elements; i++) {
2816 page = _cairo_array_index (&surface->page_set, i);
2817 _cairo_output_stream_printf (output, "<page>\n");
2818 _cairo_output_stream_printf (output,
2819 "<g id=\"surface%d\">\n",
2820 page->surface_id);
2821 _cairo_memory_stream_copy (page->xml_node, output);
2822 _cairo_output_stream_printf (output, "</g>\n</page>\n");
2824 _cairo_output_stream_printf (output, "</pageSet>\n");
2825 } else if (surface->page_set.num_elements > 0) {
2826 page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
2827 _cairo_output_stream_printf (output,
2828 "<g id=\"surface%d\">\n",
2829 page->surface_id);
2830 _cairo_memory_stream_copy (page->xml_node, output);
2831 _cairo_output_stream_printf (output, "</g>\n");
2835 _cairo_output_stream_printf (output, "</svg>\n");
2837 status2 = _cairo_output_stream_destroy (document->xml_node_glyphs);
2838 if (status == CAIRO_STATUS_SUCCESS)
2839 status = status2;
2841 status2 = _cairo_output_stream_destroy (document->xml_node_defs);
2842 if (status == CAIRO_STATUS_SUCCESS)
2843 status = status2;
2845 status2 = _cairo_output_stream_destroy (output);
2846 if (status == CAIRO_STATUS_SUCCESS)
2847 status = status2;
2849 document->finished = TRUE;
2851 return status;
2854 static void
2855 _cairo_svg_surface_set_paginated_mode (void *abstract_surface,
2856 cairo_paginated_mode_t paginated_mode)
2858 cairo_svg_surface_t *surface = abstract_surface;
2860 surface->paginated_mode = paginated_mode;
2863 static cairo_bool_t
2864 _cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface)
2866 cairo_svg_surface_t *surface = abstract_surface;
2867 cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
2869 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) {
2870 status = _cairo_svg_surface_analyze_operator (surface,
2871 CAIRO_OPERATOR_SOURCE);
2874 return status == CAIRO_INT_STATUS_SUCCESS;
2877 static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
2878 NULL /*_cairo_svg_surface_start_page*/,
2879 _cairo_svg_surface_set_paginated_mode,
2880 NULL, /* _cairo_svg_surface_set_bounding_box */
2881 NULL, /* _cairo_svg_surface_set_fallback_images_required */
2882 _cairo_svg_surface_supports_fine_grained_fallbacks,