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
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() */
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"
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.
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.
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,
91 #define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
93 static const char *_cairo_svg_supported_mime_types[] =
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);
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] =
118 static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] =
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;
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,
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);
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,
180 static cairo_surface_t *
181 _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
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
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.
213 cairo_svg_surface_create_for_stream (cairo_write_func_t write_func,
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
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
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
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.
266 cairo_svg_surface_create (const char *filename,
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);
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.
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;
298 if (surface->finished) {
299 status_ignored = _cairo_surface_set_error (surface,
300 _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
304 if (! _cairo_surface_is_paginated (surface)) {
305 status_ignored = _cairo_surface_set_error (surface,
306 _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
310 target = _cairo_paginated_surface_get_target (surface);
311 if (target->status) {
312 status_ignored = _cairo_surface_set_error (surface,
316 if (target->finished) {
317 status_ignored = _cairo_surface_set_error (surface,
318 _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
322 if (! _cairo_surface_is_svg (target)) {
323 status_ignored = _cairo_surface_set_error (surface,
324 _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
328 *svg_surface = (cairo_svg_surface_t *) target;
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
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))
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().
371 cairo_svg_get_versions (cairo_svg_version_t const **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.
394 cairo_svg_version_to_string (cairo_svg_version_t version)
396 if (version >= CAIRO_SVG_VERSION_LAST)
399 return _cairo_svg_version_strings[version];
403 _cliprect_covers_surface (cairo_svg_surface_t *surface,
404 cairo_path_fixed_t *path)
408 if (_cairo_path_fixed_is_box (path, &box)) {
411 _cairo_fixed_to_double (box.p2.x) >= surface->width &&
412 _cairo_fixed_to_double (box.p2.y) >= surface->height)
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,
426 cairo_antialias_t antialias)
428 cairo_svg_surface_t *surface = cairo_container_of (clipper,
431 cairo_svg_document_t *document = surface->document;
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"
450 _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL);
452 _cairo_output_stream_printf (document->xml_node_defs,
456 _cairo_output_stream_printf (surface->xml_node,
457 "<g clip-path=\"url(#clip%d)\" "
458 "clip-rule=\"%s\">\n",
460 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
461 "evenodd" : "nonzero");
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,
475 cairo_svg_surface_t *surface;
476 cairo_surface_t *paginated = NULL;
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,
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))
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",
513 status = _cairo_output_stream_get_status (surface->xml_node);
514 if (unlikely (status))
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,
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);
532 /* ignore status as we are on the error path */
534 status_ignored = _cairo_output_stream_destroy (surface->xml_node);
535 status_ignored = _cairo_svg_document_destroy (document);
538 cairo_surface_destroy (paginated);
540 return _cairo_surface_create_in_error (status);
543 static cairo_surface_t *
544 _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
547 cairo_svg_version_t version)
549 cairo_svg_document_t *document = NULL; /* silence compiler */
550 cairo_surface_t *surface;
551 cairo_status_t status;
553 status = _cairo_svg_document_create (stream,
554 width, height, version,
556 if (unlikely (status)) {
557 surface = _cairo_surface_create_in_error (status);
558 /* consume the output stream on behalf of caller */
559 status = _cairo_output_stream_destroy (stream);
563 surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA,
565 if (surface->status) {
566 status = _cairo_svg_document_destroy (document);
570 document->owner = surface;
571 status = _cairo_svg_document_destroy (document);
572 /* the ref count should be 2 at this point */
573 assert (status == CAIRO_STATUS_SUCCESS);
578 static cairo_svg_page_t *
579 _cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
581 cairo_svg_page_t page;
582 cairo_output_stream_t *stream;
583 cairo_int_status_t status;
586 stream = _cairo_memory_stream_create ();
587 if (_cairo_output_stream_get_status (stream)) {
588 status = _cairo_output_stream_destroy (stream);
592 page.surface_id = surface->base.unique_id;
593 page.clip_level = surface->clip_level;
594 page.xml_node = surface->xml_node;
596 if (_cairo_array_append (&surface->page_set, &page)) {
597 status = _cairo_output_stream_destroy (stream);
601 surface->xml_node = stream;
602 surface->clip_level = 0;
603 for (i = 0; i < page.clip_level; i++)
604 _cairo_output_stream_printf (page.xml_node, "</g>\n");
606 _cairo_surface_clipper_reset (&surface->clipper);
608 return _cairo_array_index (&surface->page_set,
609 surface->page_set.num_elements - 1);
612 static cairo_int_status_t
613 _cairo_svg_surface_copy_page (void *abstract_surface)
615 cairo_svg_surface_t *surface = abstract_surface;
616 cairo_svg_page_t *page;
618 page = _cairo_svg_surface_store_page (surface);
619 if (unlikely (page == NULL))
620 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
622 _cairo_memory_stream_copy (page->xml_node, surface->xml_node);
624 return CAIRO_STATUS_SUCCESS;
627 static cairo_int_status_t
628 _cairo_svg_surface_show_page (void *abstract_surface)
630 cairo_svg_surface_t *surface = abstract_surface;
632 if (unlikely (_cairo_svg_surface_store_page (surface) == NULL))
633 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
635 return CAIRO_STATUS_SUCCESS;
639 _cairo_svg_surface_emit_transform (cairo_output_stream_t *output,
640 char const *attribute_str,
641 const cairo_matrix_t *object_matrix,
642 const cairo_matrix_t *parent_matrix)
644 cairo_matrix_t matrix = *object_matrix;
646 if (parent_matrix != NULL)
647 cairo_matrix_multiply (&matrix, &matrix, parent_matrix);
649 if (!_cairo_matrix_is_identity (&matrix))
650 _cairo_output_stream_printf (output,
651 "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"",
653 matrix.xx, matrix.yx,
654 matrix.xy, matrix.yy,
655 matrix.x0, matrix.y0);
659 cairo_output_stream_t *output;
660 const cairo_matrix_t *ctm_inverse;
663 static cairo_status_t
664 _cairo_svg_path_move_to (void *closure,
665 const cairo_point_t *point)
667 svg_path_info_t *info = closure;
668 double x = _cairo_fixed_to_double (point->x);
669 double y = _cairo_fixed_to_double (point->y);
671 if (info->ctm_inverse)
672 cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
674 _cairo_output_stream_printf (info->output, "M %f %f ", x, y);
676 return CAIRO_STATUS_SUCCESS;
679 static cairo_status_t
680 _cairo_svg_path_line_to (void *closure,
681 const cairo_point_t *point)
683 svg_path_info_t *info = closure;
684 double x = _cairo_fixed_to_double (point->x);
685 double y = _cairo_fixed_to_double (point->y);
687 if (info->ctm_inverse)
688 cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
690 _cairo_output_stream_printf (info->output, "L %f %f ", x, y);
692 return CAIRO_STATUS_SUCCESS;
695 static cairo_status_t
696 _cairo_svg_path_curve_to (void *closure,
697 const cairo_point_t *b,
698 const cairo_point_t *c,
699 const cairo_point_t *d)
701 svg_path_info_t *info = closure;
702 double bx = _cairo_fixed_to_double (b->x);
703 double by = _cairo_fixed_to_double (b->y);
704 double cx = _cairo_fixed_to_double (c->x);
705 double cy = _cairo_fixed_to_double (c->y);
706 double dx = _cairo_fixed_to_double (d->x);
707 double dy = _cairo_fixed_to_double (d->y);
709 if (info->ctm_inverse) {
710 cairo_matrix_transform_point (info->ctm_inverse, &bx, &by);
711 cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy);
712 cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy);
715 _cairo_output_stream_printf (info->output,
716 "C %f %f %f %f %f %f ",
717 bx, by, cx, cy, dx, dy);
719 return CAIRO_STATUS_SUCCESS;
722 static cairo_status_t
723 _cairo_svg_path_close_path (void *closure)
725 svg_path_info_t *info = closure;
727 _cairo_output_stream_printf (info->output, "Z ");
729 return CAIRO_STATUS_SUCCESS;
733 _cairo_svg_surface_emit_path (cairo_output_stream_t *output,
734 const cairo_path_fixed_t *path,
735 const cairo_matrix_t *ctm_inverse)
737 cairo_status_t status;
738 svg_path_info_t info;
740 _cairo_output_stream_printf (output, "d=\"");
742 info.output = output;
743 info.ctm_inverse = ctm_inverse;
744 status = _cairo_path_fixed_interpret (path,
745 _cairo_svg_path_move_to,
746 _cairo_svg_path_line_to,
747 _cairo_svg_path_curve_to,
748 _cairo_svg_path_close_path,
750 assert (status == CAIRO_STATUS_SUCCESS);
752 _cairo_output_stream_printf (output, "\"");
755 static cairo_int_status_t
756 _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document,
757 cairo_scaled_font_t *scaled_font,
758 unsigned long glyph_index)
760 cairo_scaled_glyph_t *scaled_glyph;
761 cairo_int_status_t status;
763 status = _cairo_scaled_glyph_lookup (scaled_font,
765 CAIRO_SCALED_GLYPH_INFO_METRICS|
766 CAIRO_SCALED_GLYPH_INFO_PATH,
768 if (unlikely (status))
771 _cairo_output_stream_printf (document->xml_node_glyphs,
772 "<path style=\"stroke:none;\" ");
774 _cairo_svg_surface_emit_path (document->xml_node_glyphs,
775 scaled_glyph->path, NULL);
777 _cairo_output_stream_printf (document->xml_node_glyphs,
783 static cairo_int_status_t
784 _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document,
785 cairo_scaled_font_t *scaled_font,
786 unsigned long glyph_index)
788 cairo_scaled_glyph_t *scaled_glyph;
789 cairo_image_surface_t *image;
790 cairo_status_t status;
795 status = _cairo_scaled_glyph_lookup (scaled_font,
797 CAIRO_SCALED_GLYPH_INFO_METRICS |
798 CAIRO_SCALED_GLYPH_INFO_SURFACE,
800 if (unlikely (status))
803 image = _cairo_image_surface_coerce_to_format (scaled_glyph->surface,
805 status = image->base.status;
806 if (unlikely (status)) {
807 cairo_surface_destroy (&image->base);
811 _cairo_output_stream_printf (document->xml_node_glyphs, "<g");
812 _cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform",
813 &image->base.device_transform_inverse, NULL);
814 _cairo_output_stream_printf (document->xml_node_glyphs, ">\n");
816 for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) {
817 for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
818 uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
819 for (bit = 7; bit >= 0 && x < image->width; bit--, x++) {
820 if (output_byte & (1 << bit)) {
821 _cairo_output_stream_printf (document->xml_node_glyphs,
822 "<rect x=\"%d\" y=\"%d\" width=\"1\" height=\"1\"/>\n",
828 _cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n");
830 cairo_surface_destroy (&image->base);
832 return CAIRO_STATUS_SUCCESS;
835 static cairo_int_status_t
836 _cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
837 cairo_scaled_font_t *scaled_font,
838 unsigned long scaled_font_glyph_index,
839 unsigned int font_id,
840 unsigned int subset_glyph_index)
842 cairo_int_status_t status;
844 _cairo_output_stream_printf (document->xml_node_glyphs,
845 "<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n",
849 status = _cairo_svg_document_emit_outline_glyph_data (document,
851 scaled_font_glyph_index);
852 if (status == CAIRO_INT_STATUS_UNSUPPORTED)
853 status = _cairo_svg_document_emit_bitmap_glyph_data (document,
855 scaled_font_glyph_index);
856 if (unlikely (status))
859 _cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n");
861 return CAIRO_INT_STATUS_SUCCESS;
864 static cairo_int_status_t
865 _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
868 cairo_svg_document_t *document = closure;
869 cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
872 _cairo_scaled_font_freeze_cache (font_subset->scaled_font);
873 for (i = 0; i < font_subset->num_glyphs; i++) {
874 status = _cairo_svg_document_emit_glyph (document,
875 font_subset->scaled_font,
876 font_subset->glyphs[i],
877 font_subset->font_id, i);
878 if (unlikely (status))
881 _cairo_scaled_font_thaw_cache (font_subset->scaled_font);
886 static cairo_status_t
887 _cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
889 cairo_status_t status;
891 status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
892 _cairo_svg_document_emit_font_subset,
894 if (unlikely (status))
897 status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
898 _cairo_svg_document_emit_font_subset,
902 _cairo_scaled_font_subsets_destroy (document->font_subsets);
903 document->font_subsets = NULL;
909 _cairo_svg_surface_operators[] = {
912 "src", "src-over", "src-in",
913 "src-out", "src-atop",
915 "dst", "dst-over", "dst-in",
916 "dst-out", "dst-atop",
919 "color-dodge", /* FIXME: saturate ? */
921 "multiply", "screen", "overlay",
923 "color-dodge", "color-burn",
924 "hard-light", "soft-light",
925 "difference", "exclusion"
929 _cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface,
932 /* guard against newly added operators */
933 if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators))
934 return CAIRO_INT_STATUS_UNSUPPORTED;
936 /* allow operators being NULL if they are unsupported */
937 if (_cairo_svg_surface_operators[op] == NULL)
938 return CAIRO_INT_STATUS_UNSUPPORTED;
940 return CAIRO_STATUS_SUCCESS;
943 static cairo_int_status_t
944 _cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface,
946 const cairo_pattern_t *pattern)
948 cairo_svg_document_t *document = surface->document;
950 if (surface->force_fallbacks &&
951 surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
953 return CAIRO_INT_STATUS_UNSUPPORTED;
956 if (pattern->type == CAIRO_PATTERN_TYPE_MESH)
957 return CAIRO_INT_STATUS_UNSUPPORTED;
959 /* SVG doesn't support extend reflect for image pattern */
960 if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
961 pattern->extend == CAIRO_EXTEND_REFLECT)
962 return CAIRO_INT_STATUS_UNSUPPORTED;
964 if (document->svg_version >= CAIRO_SVG_VERSION_1_2)
965 return _cairo_svg_surface_analyze_operator (surface, op);
967 if (op == CAIRO_OPERATOR_OVER)
968 return CAIRO_STATUS_SUCCESS;
970 /* The SOURCE operator is only supported if there is nothing
971 * painted underneath. */
972 if (op == CAIRO_OPERATOR_SOURCE)
973 return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
975 return CAIRO_INT_STATUS_UNSUPPORTED;
978 static cairo_int_status_t
979 _cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface,
981 const cairo_pattern_t *pattern)
983 return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED;
986 static cairo_status_t
987 _cairo_svg_surface_finish (void *abstract_surface)
989 cairo_status_t status, status2;
990 cairo_svg_surface_t *surface = abstract_surface;
991 cairo_svg_document_t *document = surface->document;
992 cairo_svg_page_t *page;
995 if (_cairo_paginated_surface_get_target (document->owner) == &surface->base)
996 status = _cairo_svg_document_finish (document);
998 status = CAIRO_STATUS_SUCCESS;
1000 if (surface->xml_node != NULL) {
1001 status2 = _cairo_output_stream_destroy (surface->xml_node);
1002 if (status == CAIRO_STATUS_SUCCESS)
1006 for (i = 0; i < surface->page_set.num_elements; i++) {
1007 page = _cairo_array_index (&surface->page_set, i);
1008 status2 = _cairo_output_stream_destroy (page->xml_node);
1009 if (status == CAIRO_STATUS_SUCCESS)
1012 _cairo_array_fini (&surface->page_set);
1014 _cairo_surface_clipper_reset (&surface->clipper);
1016 status2 = _cairo_svg_document_destroy (document);
1017 if (status == CAIRO_STATUS_SUCCESS)
1025 _cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document)
1027 if (document->alpha_filter)
1030 _cairo_output_stream_printf (document->xml_node_defs,
1031 "<filter id=\"alpha\" "
1032 "filterUnits=\"objectBoundingBox\" "
1033 "x=\"0%%\" y=\"0%%\" "
1034 "width=\"100%%\" height=\"100%%\">\n"
1035 " <feColorMatrix type=\"matrix\" "
1036 "in=\"SourceGraphic\" "
1037 "values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n"
1040 document->alpha_filter = TRUE;
1044 cairo_output_stream_t *output;
1045 unsigned int in_mem;
1046 unsigned int trailing;
1047 unsigned char src[3];
1048 } base64_write_closure_t;
1050 static char const base64_table[64] =
1051 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1053 static cairo_status_t
1054 base64_write_func (void *closure,
1055 const unsigned char *data,
1056 unsigned int length)
1058 base64_write_closure_t *info = (base64_write_closure_t *) closure;
1064 if (info->in_mem + length < 3) {
1065 for (i = 0; i < length; i++) {
1066 src[i + info->in_mem] = *data++;
1068 info->in_mem += length;
1069 return CAIRO_STATUS_SUCCESS;
1073 unsigned char dst[4];
1075 for (i = info->in_mem; i < 3; i++) {
1081 dst[0] = base64_table[src[0] >> 2];
1082 dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4];
1083 dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6];
1084 dst[3] = base64_table[src[2] & 0xfc >> 2];
1085 /* Special case for the last missing bits */
1086 switch (info->trailing) {
1094 _cairo_output_stream_write (info->output, dst, 4);
1095 } while (length >= 3);
1097 for (i = 0; i < length; i++) {
1100 info->in_mem = length;
1102 return _cairo_output_stream_get_status (info->output);
1105 static cairo_int_status_t
1106 _cairo_surface_base64_encode_jpeg (cairo_surface_t *surface,
1107 cairo_output_stream_t *output)
1109 const unsigned char *mime_data;
1110 unsigned long mime_data_length;
1111 cairo_image_info_t image_info;
1112 base64_write_closure_t info;
1113 cairo_status_t status;
1115 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG,
1116 &mime_data, &mime_data_length);
1117 if (mime_data == NULL)
1118 return CAIRO_INT_STATUS_UNSUPPORTED;
1120 status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length);
1121 if (unlikely (status))
1124 if (image_info.num_components == 4)
1125 return CAIRO_INT_STATUS_UNSUPPORTED;
1127 _cairo_output_stream_printf (output, "data:image/jpeg;base64,");
1129 info.output = output;
1133 status = base64_write_func (&info, mime_data, mime_data_length);
1134 if (unlikely (status))
1137 if (info.in_mem > 0) {
1138 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1139 info.trailing = 3 - info.in_mem;
1141 status = base64_write_func (&info, NULL, 0);
1147 static cairo_int_status_t
1148 _cairo_surface_base64_encode_png (cairo_surface_t *surface,
1149 cairo_output_stream_t *output)
1151 const unsigned char *mime_data;
1152 unsigned long mime_data_length;
1153 base64_write_closure_t info;
1154 cairo_status_t status;
1156 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG,
1157 &mime_data, &mime_data_length);
1158 if (unlikely (surface->status))
1159 return surface->status;
1160 if (mime_data == NULL)
1161 return CAIRO_INT_STATUS_UNSUPPORTED;
1163 _cairo_output_stream_printf (output, "data:image/png;base64,");
1165 info.output = output;
1169 status = base64_write_func (&info, mime_data, mime_data_length);
1170 if (unlikely (status))
1173 if (info.in_mem > 0) {
1174 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1175 info.trailing = 3 - info.in_mem;
1177 status = base64_write_func (&info, NULL, 0);
1183 static cairo_int_status_t
1184 _cairo_surface_base64_encode (cairo_surface_t *surface,
1185 cairo_output_stream_t *output)
1187 cairo_int_status_t status;
1188 base64_write_closure_t info;
1190 status = _cairo_surface_base64_encode_jpeg (surface, output);
1191 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1194 status = _cairo_surface_base64_encode_png (surface, output);
1195 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1198 info.output = output;
1202 _cairo_output_stream_printf (info.output, "data:image/png;base64,");
1204 status = cairo_surface_write_to_png_stream (surface, base64_write_func,
1207 if (unlikely (status))
1210 if (info.in_mem > 0) {
1211 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1212 info.trailing = 3 - info.in_mem;
1214 status = base64_write_func (&info, NULL, 0);
1221 _cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
1222 cairo_svg_surface_t *surface,
1223 cairo_operator_t op)
1225 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1226 op != CAIRO_OPERATOR_OVER) {
1227 _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]);
1228 if (!_cairo_operator_bounded_by_source (op))
1229 _cairo_output_stream_printf (output, " clip-to-self=\"true\"");
1234 _cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output,
1235 cairo_svg_surface_t *surface,
1236 cairo_operator_t op)
1238 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1239 op != CAIRO_OPERATOR_OVER) {
1240 _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]);
1241 if (!_cairo_operator_bounded_by_source (op))
1242 _cairo_output_stream_printf (output, "clip-to-self:true;");
1247 * _cairo_svg_surface_emit_attr_value:
1249 * Write the value to output the stream as a sequence of characters,
1250 * while escaping those which have special meaning in the XML
1251 * attribute's value context: & and ".
1254 _cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream,
1255 const unsigned char *value,
1256 unsigned int length)
1258 const unsigned char *p;
1259 const unsigned char *q;
1262 /* we'll accumulate non-special chars in [q, p) range */
1265 for (i = 0; i < length; i++, p++) {
1266 if (*p == '&' || *p == '"') {
1267 /* flush what's left before special char */
1269 _cairo_output_stream_write (stream, q, p - q);
1274 _cairo_output_stream_printf (stream, "&");
1276 _cairo_output_stream_printf (stream, """);
1280 /* flush the trailing chars if any */
1282 _cairo_output_stream_write (stream, q, p - q);
1285 static cairo_status_t
1286 _cairo_svg_surface_emit_surface (cairo_svg_document_t *document,
1287 cairo_surface_t *surface)
1289 cairo_rectangle_int_t extents;
1290 cairo_bool_t is_bounded;
1291 cairo_status_t status;
1292 const unsigned char *uri;
1293 unsigned long uri_len;
1295 if (_cairo_user_data_array_get_data (&surface->user_data,
1296 (cairo_user_data_key_t *) document))
1298 return CAIRO_STATUS_SUCCESS;
1301 is_bounded = _cairo_surface_get_extents (surface, &extents);
1302 assert (is_bounded);
1304 _cairo_output_stream_printf (document->xml_node_defs,
1305 "<image id=\"image%d\" width=\"%d\" height=\"%d\"",
1307 extents.width, extents.height);
1309 _cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\"");
1311 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_URI,
1314 _cairo_svg_surface_emit_attr_value (document->xml_node_defs,
1317 status = _cairo_surface_base64_encode (surface,
1318 document->xml_node_defs);
1319 if (unlikely (status))
1323 _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n");
1326 return _cairo_user_data_array_set_data (&surface->user_data,
1327 (cairo_user_data_key_t *) document,
1331 static cairo_status_t
1332 _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *output,
1333 cairo_svg_surface_t *svg_surface,
1334 cairo_operator_t op,
1335 cairo_surface_pattern_t *pattern,
1337 const cairo_matrix_t *parent_matrix,
1338 const char *extra_attributes)
1340 cairo_status_t status;
1343 p2u = pattern->base.matrix;
1344 status = cairo_matrix_invert (&p2u);
1345 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1346 assert (status == CAIRO_STATUS_SUCCESS);
1348 status = _cairo_svg_surface_emit_surface (svg_surface->document,
1350 if (unlikely (status))
1353 if (pattern_id != invalid_pattern_id) {
1354 cairo_rectangle_int_t extents;
1355 cairo_bool_t is_bounded;
1357 is_bounded = _cairo_surface_get_extents (pattern->surface, &extents);
1358 assert (is_bounded);
1360 _cairo_output_stream_printf (output,
1361 "<pattern id=\"pattern%d\" "
1362 "patternUnits=\"userSpaceOnUse\" "
1363 "width=\"%d\" height=\"%d\" ",
1365 extents.width, extents.height);
1366 _cairo_svg_surface_emit_transform (output,
1367 " patternTransform",
1368 &p2u, parent_matrix);
1369 _cairo_output_stream_printf (output, ">\n ");
1372 _cairo_output_stream_printf (output,
1373 "<use xlink:href=\"#image%d\"",
1374 pattern->surface->unique_id);
1375 if (extra_attributes)
1376 _cairo_output_stream_printf (output, " %s", extra_attributes);
1378 if (pattern_id == invalid_pattern_id) {
1379 _cairo_svg_surface_emit_operator (output, svg_surface, op);
1380 _cairo_svg_surface_emit_transform (output,
1382 &p2u, parent_matrix);
1384 _cairo_output_stream_printf (output, "/>\n");
1387 if (pattern_id != invalid_pattern_id)
1388 _cairo_output_stream_printf (output, "</pattern>\n");
1390 return CAIRO_STATUS_SUCCESS;
1393 static cairo_status_t
1394 _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document,
1395 cairo_recording_surface_t *source)
1397 cairo_status_t status;
1398 cairo_surface_t *paginated_surface = NULL;
1399 cairo_svg_surface_t *svg_surface;
1400 cairo_array_t *page_set;
1402 cairo_output_stream_t *contents;
1404 if (_cairo_user_data_array_get_data (&source->base.user_data,
1405 (cairo_user_data_key_t *) document))
1407 return CAIRO_STATUS_SUCCESS;
1410 paginated_surface = _cairo_svg_surface_create_for_document (document,
1411 source->base.content,
1412 source->extents_pixels.width,
1413 source->extents_pixels.height);
1414 if (unlikely (paginated_surface->status)) {
1415 status = paginated_surface->status;
1416 cairo_surface_destroy (paginated_surface);
1420 svg_surface = (cairo_svg_surface_t *)
1421 _cairo_paginated_surface_get_target (paginated_surface);
1422 cairo_surface_set_fallback_resolution (paginated_surface,
1423 document->owner->x_fallback_resolution,
1424 document->owner->y_fallback_resolution);
1425 cairo_surface_set_device_offset (&svg_surface->base,
1426 -source->extents_pixels.x,
1427 -source->extents_pixels.y);
1429 status = _cairo_recording_surface_replay (&source->base, paginated_surface);
1430 if (unlikely (status)) {
1431 cairo_surface_destroy (paginated_surface);
1435 cairo_surface_show_page (paginated_surface);
1436 status = cairo_surface_status (paginated_surface);
1437 if (unlikely (status)) {
1438 cairo_surface_destroy (paginated_surface);
1442 if (! svg_surface->is_base_clip_emitted) {
1443 svg_surface->is_base_clip_emitted = TRUE;
1444 _cairo_output_stream_printf (document->xml_node_defs,
1445 "<clipPath id=\"clip%d\">\n"
1446 " <rect width=\"%f\" height=\"%f\"/>\n"
1448 svg_surface->base_clip,
1450 svg_surface->height);
1453 if (source->base.content == CAIRO_CONTENT_ALPHA) {
1454 _cairo_svg_surface_emit_alpha_filter (document);
1455 _cairo_output_stream_printf (document->xml_node_defs,
1456 "<g id=\"surface%d\" "
1457 "clip-path=\"url(#clip%d)\" "
1458 "filter=\"url(#alpha)\">\n",
1459 source->base.unique_id,
1460 svg_surface->base_clip);
1462 _cairo_output_stream_printf (document->xml_node_defs,
1463 "<g id=\"surface%d\" "
1464 "clip-path=\"url(#clip%d)\">\n",
1465 source->base.unique_id,
1466 svg_surface->base_clip);
1469 contents = svg_surface->xml_node;
1470 page_set = &svg_surface->page_set;
1472 if (_cairo_memory_stream_length (contents) > 0) {
1473 if (unlikely (_cairo_svg_surface_store_page (svg_surface) == NULL)) {
1474 cairo_surface_destroy (paginated_surface);
1475 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1479 if (page_set->num_elements > 0) {
1480 cairo_svg_page_t *page;
1482 page = _cairo_array_index (page_set, page_set->num_elements - 1);
1483 _cairo_memory_stream_copy (page->xml_node, document->xml_node_defs);
1486 _cairo_output_stream_printf (document->xml_node_defs, "</g>\n");
1488 status = cairo_surface_status (paginated_surface);
1489 cairo_surface_destroy (paginated_surface);
1491 if (unlikely (status))
1495 return _cairo_user_data_array_set_data (&source->base.user_data,
1496 (cairo_user_data_key_t *) document,
1500 static cairo_recording_surface_t *
1501 to_recording_surface (const cairo_surface_pattern_t *pattern)
1503 cairo_surface_t *surface = pattern->surface;
1504 if (_cairo_surface_is_paginated (surface))
1505 surface = _cairo_paginated_surface_get_recording (surface);
1506 if (_cairo_surface_is_snapshot (surface))
1507 surface = _cairo_surface_snapshot_get_target (surface);
1508 return (cairo_recording_surface_t *) surface;
1511 static cairo_status_t
1512 _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *output,
1513 cairo_svg_surface_t *surface,
1514 cairo_operator_t op,
1515 cairo_surface_pattern_t *pattern,
1517 const cairo_matrix_t *parent_matrix,
1518 const char *extra_attributes)
1520 cairo_svg_document_t *document = surface->document;
1521 cairo_recording_surface_t *recording_surface;
1523 cairo_status_t status;
1525 p2u = pattern->base.matrix;
1526 status = cairo_matrix_invert (&p2u);
1527 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1528 assert (status == CAIRO_STATUS_SUCCESS);
1530 recording_surface = to_recording_surface (pattern);
1531 status = _cairo_svg_surface_emit_recording_surface (document, recording_surface);
1532 if (unlikely (status))
1535 if (pattern_id != invalid_pattern_id) {
1536 _cairo_output_stream_printf (output,
1537 "<pattern id=\"pattern%d\" "
1538 "patternUnits=\"userSpaceOnUse\" "
1539 "width=\"%d\" height=\"%d\"",
1541 recording_surface->extents.width,
1542 recording_surface->extents.height);
1543 _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
1544 _cairo_output_stream_printf (output, ">\n");
1547 _cairo_output_stream_printf (output,
1548 "<use xlink:href=\"#surface%d\"",
1549 recording_surface->base.unique_id);
1551 if (pattern_id == invalid_pattern_id) {
1552 _cairo_svg_surface_emit_operator (output, surface, op);
1553 _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
1556 if (extra_attributes)
1557 _cairo_output_stream_printf (output, " %s", extra_attributes);
1559 _cairo_output_stream_printf (output, "/>\n");
1561 if (pattern_id != invalid_pattern_id)
1562 _cairo_output_stream_printf (output, "</pattern>\n");
1564 return CAIRO_STATUS_SUCCESS;
1567 static cairo_status_t
1568 _cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output,
1569 cairo_svg_surface_t *surface,
1570 cairo_operator_t op,
1571 cairo_surface_pattern_t *pattern,
1573 const cairo_matrix_t *parent_matrix,
1574 const char *extra_attributes)
1577 if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
1578 return _cairo_svg_surface_emit_composite_recording_pattern (output, surface,
1585 return _cairo_svg_surface_emit_composite_surface_pattern (output, surface,
1592 static cairo_status_t
1593 _cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface,
1594 cairo_solid_pattern_t *pattern,
1595 cairo_output_stream_t *style,
1596 cairo_bool_t is_stroke)
1598 _cairo_output_stream_printf (style, is_stroke ?
1599 "stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;":
1600 "fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;",
1601 pattern->color.red * 100.0,
1602 pattern->color.green * 100.0,
1603 pattern->color.blue * 100.0,
1604 pattern->color.alpha);
1606 return CAIRO_STATUS_SUCCESS;
1609 static cairo_status_t
1610 _cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface,
1611 cairo_surface_pattern_t *pattern,
1612 cairo_output_stream_t *style,
1613 cairo_bool_t is_stroke,
1614 const cairo_matrix_t *parent_matrix)
1616 cairo_svg_document_t *document = surface->document;
1617 cairo_status_t status;
1620 pattern_id = document->pattern_id++;
1621 status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs,
1622 surface, CAIRO_OPERATOR_SOURCE, pattern,
1623 pattern_id, parent_matrix, NULL);
1624 if (unlikely (status))
1627 _cairo_output_stream_printf (style,
1628 "%s:url(#pattern%d);",
1629 is_stroke ? "stroke" : "fill",
1632 return CAIRO_STATUS_SUCCESS;
1635 static cairo_status_t
1636 _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output,
1637 cairo_gradient_pattern_t const *pattern,
1638 double start_offset,
1639 cairo_bool_t reverse_stops,
1640 cairo_bool_t emulate_reflect)
1642 cairo_gradient_stop_t *stops;
1644 unsigned int n_stops;
1647 if (pattern->n_stops < 1)
1648 return CAIRO_STATUS_SUCCESS;
1650 if (pattern->n_stops == 1) {
1651 _cairo_output_stream_printf (output,
1652 "<stop offset=\"%f\" style=\""
1653 "stop-color:rgb(%f%%,%f%%,%f%%);"
1654 "stop-opacity:%f;\"/>\n",
1655 pattern->stops[0].offset,
1656 pattern->stops[0].color.red * 100.0,
1657 pattern->stops[0].color.green * 100.0,
1658 pattern->stops[0].color.blue * 100.0,
1659 pattern->stops[0].color.alpha);
1660 return CAIRO_STATUS_SUCCESS;
1663 if (emulate_reflect || reverse_stops) {
1664 n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops;
1665 stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t));
1666 if (unlikely (stops == NULL))
1667 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1669 for (i = 0; i < pattern->n_stops; i++) {
1670 if (reverse_stops) {
1671 stops[i] = pattern->stops[pattern->n_stops - i - 1];
1672 stops[i].offset = 1.0 - stops[i].offset;
1674 stops[i] = pattern->stops[i];
1675 if (emulate_reflect) {
1676 stops[i].offset /= 2;
1677 if (i > 0 && i < (pattern->n_stops - 1)) {
1678 if (reverse_stops) {
1679 stops[i + pattern->n_stops - 1] = pattern->stops[i];
1680 stops[i + pattern->n_stops - 1].offset =
1681 0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset;
1683 stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1];
1684 stops[i + pattern->n_stops - 1].offset =
1685 1 - 0.5 * stops[i + pattern->n_stops - 1].offset;
1691 n_stops = pattern->n_stops;
1692 stops = pattern->stops;
1695 if (start_offset >= 0.0)
1696 for (i = 0; i < n_stops; i++) {
1697 offset = start_offset + (1 - start_offset ) * stops[i].offset;
1698 _cairo_output_stream_printf (output,
1699 "<stop offset=\"%f\" style=\""
1700 "stop-color:rgb(%f%%,%f%%,%f%%);"
1701 "stop-opacity:%f;\"/>\n",
1703 stops[i].color.red * 100.0,
1704 stops[i].color.green * 100.0,
1705 stops[i].color.blue * 100.0,
1706 stops[i].color.alpha);
1709 cairo_bool_t found = FALSE;
1710 unsigned int offset_index;
1711 cairo_color_stop_t offset_color_start, offset_color_stop;
1712 _cairo_color_init_rgba ((cairo_color_t *) &offset_color_start, 0, 0, 0, 0);
1714 for (i = 0; i < n_stops; i++) {
1715 if (stops[i].offset >= -start_offset) {
1717 if (stops[i].offset != stops[i-1].offset) {
1719 cairo_color_stop_t *color0, *color1;
1721 x0 = stops[i-1].offset;
1722 x1 = stops[i].offset;
1723 color0 = &stops[i-1].color;
1724 color1 = &stops[i].color;
1725 offset_color_start.red = color0->red + (color1->red - color0->red)
1726 * (-start_offset - x0) / (x1 - x0);
1727 offset_color_start.green = color0->green + (color1->green - color0->green)
1728 * (-start_offset - x0) / (x1 - x0);
1729 offset_color_start.blue = color0->blue + (color1->blue - color0->blue)
1730 * (-start_offset - x0) / (x1 - x0);
1731 offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha)
1732 * (-start_offset - x0) / (x1 - x0);
1733 offset_color_stop = offset_color_start;
1735 offset_color_stop = stops[i-1].color;
1736 offset_color_start = stops[i].color;
1739 offset_color_stop = offset_color_start = stops[i].color;
1747 offset_index = n_stops - 1;
1748 offset_color_stop = offset_color_start = stops[offset_index].color;
1751 _cairo_output_stream_printf (output,
1752 "<stop offset=\"0\" style=\""
1753 "stop-color:rgb(%f%%,%f%%,%f%%);"
1754 "stop-opacity:%f;\"/>\n",
1755 offset_color_start.red * 100.0,
1756 offset_color_start.green * 100.0,
1757 offset_color_start.blue * 100.0,
1758 offset_color_start.alpha);
1759 for (i = offset_index; i < n_stops; i++) {
1760 _cairo_output_stream_printf (output,
1761 "<stop offset=\"%f\" style=\""
1762 "stop-color:rgb(%f%%,%f%%,%f%%);"
1763 "stop-opacity:%f;\"/>\n",
1764 stops[i].offset + start_offset,
1765 stops[i].color.red * 100.0,
1766 stops[i].color.green * 100.0,
1767 stops[i].color.blue * 100.0,
1768 stops[i].color.alpha);
1770 for (i = 0; i < offset_index; i++) {
1771 _cairo_output_stream_printf (output,
1772 "<stop offset=\"%f\" style=\""
1773 "stop-color:rgb(%f%%,%f%%,%f%%);"
1774 "stop-opacity:%f;\"/>\n",
1775 1.0 + stops[i].offset + start_offset,
1776 stops[i].color.red * 100.0,
1777 stops[i].color.green * 100.0,
1778 stops[i].color.blue * 100.0,
1779 stops[i].color.alpha);
1782 _cairo_output_stream_printf (output,
1783 "<stop offset=\"1\" style=\""
1784 "stop-color:rgb(%f%%,%f%%,%f%%);"
1785 "stop-opacity:%f;\"/>\n",
1786 offset_color_stop.red * 100.0,
1787 offset_color_stop.green * 100.0,
1788 offset_color_stop.blue * 100.0,
1789 offset_color_stop.alpha);
1793 if (reverse_stops || emulate_reflect)
1796 return CAIRO_STATUS_SUCCESS;
1800 _cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output,
1801 cairo_pattern_t *pattern)
1803 switch (pattern->extend) {
1804 case CAIRO_EXTEND_REPEAT:
1805 _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" ");
1807 case CAIRO_EXTEND_REFLECT:
1808 _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" ");
1810 case CAIRO_EXTEND_NONE:
1811 case CAIRO_EXTEND_PAD:
1816 static cairo_status_t
1817 _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface,
1818 cairo_linear_pattern_t *pattern,
1819 cairo_output_stream_t *style,
1820 cairo_bool_t is_stroke,
1821 const cairo_matrix_t *parent_matrix)
1823 cairo_svg_document_t *document = surface->document;
1825 cairo_status_t status;
1827 p2u = pattern->base.base.matrix;
1828 status = cairo_matrix_invert (&p2u);
1829 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1830 assert (status == CAIRO_STATUS_SUCCESS);
1832 _cairo_output_stream_printf (document->xml_node_defs,
1833 "<linearGradient id=\"linear%d\" "
1834 "gradientUnits=\"userSpaceOnUse\" "
1835 "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
1836 document->linear_pattern_id,
1837 pattern->pd1.x, pattern->pd1.y,
1838 pattern->pd2.x, pattern->pd2.y);
1840 _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
1841 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1842 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1844 status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
1845 &pattern->base, 0.0,
1847 if (unlikely (status))
1850 _cairo_output_stream_printf (document->xml_node_defs,
1851 "</linearGradient>\n");
1853 _cairo_output_stream_printf (style,
1854 "%s:url(#linear%d);",
1855 is_stroke ? "stroke" : "fill",
1856 document->linear_pattern_id);
1858 document->linear_pattern_id++;
1860 return CAIRO_STATUS_SUCCESS;
1863 static cairo_status_t
1864 _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface,
1865 cairo_radial_pattern_t *pattern,
1866 cairo_output_stream_t *style,
1867 cairo_bool_t is_stroke,
1868 const cairo_matrix_t *parent_matrix)
1870 cairo_svg_document_t *document = surface->document;
1872 cairo_extend_t extend;
1873 double x0, y0, x1, y1, r0, r1;
1875 cairo_bool_t reverse_stops;
1876 cairo_status_t status;
1877 cairo_circle_double_t *c0, *c1;
1879 extend = pattern->base.base.extend;
1881 if (pattern->cd1.radius < pattern->cd2.radius) {
1884 reverse_stops = FALSE;
1888 reverse_stops = TRUE;
1898 p2u = pattern->base.base.matrix;
1899 status = cairo_matrix_invert (&p2u);
1900 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1901 assert (status == CAIRO_STATUS_SUCCESS);
1904 unsigned int n_stops = pattern->base.n_stops;
1906 _cairo_output_stream_printf (document->xml_node_defs,
1907 "<radialGradient id=\"radial%d\" "
1908 "gradientUnits=\"userSpaceOnUse\" "
1909 "cx=\"%f\" cy=\"%f\" "
1910 "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1911 document->radial_pattern_id,
1914 _cairo_svg_surface_emit_transform (document->xml_node_defs,
1915 "gradientTransform",
1916 &p2u, parent_matrix);
1917 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1919 if (extend == CAIRO_EXTEND_NONE || n_stops < 1)
1920 _cairo_output_stream_printf (document->xml_node_defs,
1921 "<stop offset=\"0\" style=\""
1922 "stop-color:rgb(0%%,0%%,0%%);"
1923 "stop-opacity:0;\"/>\n");
1925 _cairo_output_stream_printf (document->xml_node_defs,
1926 "<stop offset=\"0\" style=\""
1927 "stop-color:rgb(%f%%,%f%%,%f%%);"
1928 "stop-opacity %f;\"/>\n",
1929 pattern->base.stops[0].color.red * 100.0,
1930 pattern->base.stops[0].color.green * 100.0,
1931 pattern->base.stops[0].color.blue * 100.0,
1932 pattern->base.stops[0].color.alpha);
1934 _cairo_output_stream_printf (document->xml_node_defs,
1935 "<stop offset=\"0\" style=\""
1936 "stop-color:rgb(%f%%,%f%%,%f%%);"
1937 "stop-opacity:%f;\"/>\n",
1938 pattern->base.stops[n_stops - 1].color.red * 100.0,
1939 pattern->base.stops[n_stops - 1].color.green * 100.0,
1940 pattern->base.stops[n_stops - 1].color.blue * 100.0,
1941 pattern->base.stops[n_stops - 1].color.alpha);
1945 double offset, r, x, y;
1946 cairo_bool_t emulate_reflect = FALSE;
1948 fx = (r1 * x0 - r0 * x1) / (r1 - r0);
1949 fy = (r1 * y0 - r0 * y1) / (r1 - r0);
1951 /* SVG doesn't support the inner circle and use instead a gradient focal.
1952 * That means we need to emulate the cairo behaviour by processing the
1953 * cairo gradient stops.
1954 * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
1955 * it's just a matter of stop position translation and calculation of
1956 * the corresponding SVG radial gradient focal.
1957 * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
1958 * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
1959 * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
1960 * list that maps to the original cairo stop list.
1962 if ((extend == CAIRO_EXTEND_REFLECT
1963 || extend == CAIRO_EXTEND_REPEAT)
1967 if (extend == CAIRO_EXTEND_REFLECT) {
1969 emulate_reflect = TRUE;
1972 offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
1975 /* New position of outer circle. */
1976 x = r * (x1 - fx) / r_org + fx;
1977 y = r * (y1 - fy) / r_org + fy;
1987 _cairo_output_stream_printf (document->xml_node_defs,
1988 "<radialGradient id=\"radial%d\" "
1989 "gradientUnits=\"userSpaceOnUse\" "
1990 "cx=\"%f\" cy=\"%f\" "
1991 "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1992 document->radial_pattern_id,
1996 if (emulate_reflect)
1997 _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
1999 _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
2000 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
2001 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
2003 /* To support cairo's EXTEND_NONE, (for which SVG has no similar
2004 * notion), we add transparent color stops on either end of the
2005 * user-provided stops. */
2006 if (extend == CAIRO_EXTEND_NONE) {
2007 _cairo_output_stream_printf (document->xml_node_defs,
2008 "<stop offset=\"0\" style=\""
2009 "stop-color:rgb(0%%,0%%,0%%);"
2010 "stop-opacity:0;\"/>\n");
2012 _cairo_output_stream_printf (document->xml_node_defs,
2013 "<stop offset=\"%f\" style=\""
2014 "stop-color:rgb(0%%,0%%,0%%);"
2015 "stop-opacity:0;\"/>\n",
2018 status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
2019 &pattern->base, offset,
2022 if (unlikely (status))
2025 if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
2026 _cairo_output_stream_printf (document->xml_node_defs,
2027 "<stop offset=\"1.0\" style=\""
2028 "stop-color:rgb(0%%,0%%,0%%);"
2029 "stop-opacity:0;\"/>\n");
2032 _cairo_output_stream_printf (document->xml_node_defs,
2033 "</radialGradient>\n");
2035 _cairo_output_stream_printf (style,
2036 "%s:url(#radial%d);",
2037 is_stroke ? "stroke" : "fill",
2038 document->radial_pattern_id);
2040 document->radial_pattern_id++;
2042 return CAIRO_STATUS_SUCCESS;
2045 static cairo_status_t
2046 _cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface,
2047 const cairo_pattern_t *pattern,
2048 cairo_output_stream_t *output,
2049 cairo_bool_t is_stroke,
2050 const cairo_matrix_t *parent_matrix)
2052 switch (pattern->type) {
2053 case CAIRO_PATTERN_TYPE_SOLID:
2054 return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern,
2057 case CAIRO_PATTERN_TYPE_SURFACE:
2058 return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern,
2059 output, is_stroke, parent_matrix);
2061 case CAIRO_PATTERN_TYPE_LINEAR:
2062 return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern,
2063 output, is_stroke, parent_matrix);
2065 case CAIRO_PATTERN_TYPE_RADIAL:
2066 return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern,
2067 output, is_stroke, parent_matrix);
2069 case CAIRO_PATTERN_TYPE_MESH:
2070 case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
2073 return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
2076 static cairo_status_t
2077 _cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output,
2078 cairo_svg_surface_t *surface,
2079 cairo_operator_t op,
2080 const cairo_pattern_t *source,
2081 cairo_fill_rule_t fill_rule,
2082 const cairo_matrix_t *parent_matrix)
2084 _cairo_output_stream_printf (output,
2086 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
2087 "evenodd" : "nonzero");
2088 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2089 return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix);
2092 static cairo_status_t
2093 _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output,
2094 cairo_svg_surface_t *surface,
2095 cairo_operator_t op,
2096 const cairo_pattern_t *source,
2097 const cairo_stroke_style_t *stroke_style,
2098 const cairo_matrix_t *parent_matrix)
2100 cairo_status_t status;
2101 const char *line_cap, *line_join;
2104 switch (stroke_style->line_cap) {
2105 case CAIRO_LINE_CAP_BUTT:
2108 case CAIRO_LINE_CAP_ROUND:
2111 case CAIRO_LINE_CAP_SQUARE:
2112 line_cap = "square";
2118 switch (stroke_style->line_join) {
2119 case CAIRO_LINE_JOIN_MITER:
2120 line_join = "miter";
2122 case CAIRO_LINE_JOIN_ROUND:
2123 line_join = "round";
2125 case CAIRO_LINE_JOIN_BEVEL:
2126 line_join = "bevel";
2132 _cairo_output_stream_printf (output,
2134 "stroke-linecap:%s;"
2135 "stroke-linejoin:%s;",
2136 stroke_style->line_width,
2140 status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix);
2141 if (unlikely (status))
2144 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2146 if (stroke_style->num_dashes > 0) {
2147 _cairo_output_stream_printf (output, "stroke-dasharray:");
2148 for (i = 0; i < stroke_style->num_dashes; i++) {
2149 _cairo_output_stream_printf (output, "%f",
2150 stroke_style->dash[i]);
2151 if (i + 1 < stroke_style->num_dashes)
2152 _cairo_output_stream_printf (output, ",");
2154 _cairo_output_stream_printf (output, ";");
2156 if (stroke_style->dash_offset != 0.0) {
2157 _cairo_output_stream_printf (output,
2158 "stroke-dashoffset:%f;",
2159 stroke_style->dash_offset);
2163 _cairo_output_stream_printf (output,
2164 "stroke-miterlimit:%f;",
2165 stroke_style->miter_limit);
2167 return CAIRO_STATUS_SUCCESS;
2170 static cairo_int_status_t
2171 _cairo_svg_surface_fill_stroke (void *abstract_surface,
2172 cairo_operator_t fill_op,
2173 const cairo_pattern_t *fill_source,
2174 cairo_fill_rule_t fill_rule,
2175 double fill_tolerance,
2176 cairo_antialias_t fill_antialias,
2177 const cairo_path_fixed_t*path,
2178 cairo_operator_t stroke_op,
2179 const cairo_pattern_t *stroke_source,
2180 const cairo_stroke_style_t *stroke_style,
2181 const cairo_matrix_t *stroke_ctm,
2182 const cairo_matrix_t *stroke_ctm_inverse,
2183 double stroke_tolerance,
2184 cairo_antialias_t stroke_antialias,
2185 const cairo_clip_t *clip)
2187 cairo_svg_surface_t *surface = abstract_surface;
2188 cairo_status_t status;
2190 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2191 if (unlikely (status))
2194 _cairo_output_stream_printf (surface->xml_node, "<path style=\"");
2195 status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, fill_op,
2196 fill_source, fill_rule, stroke_ctm_inverse);
2197 if (unlikely (status))
2200 status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op,
2201 stroke_source, stroke_style, stroke_ctm_inverse);
2202 if (unlikely (status))
2205 _cairo_output_stream_printf (surface->xml_node, "\" ");
2207 _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse);
2209 _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL);
2210 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2212 return CAIRO_STATUS_SUCCESS;
2215 static cairo_int_status_t
2216 _cairo_svg_surface_fill (void *abstract_surface,
2217 cairo_operator_t op,
2218 const cairo_pattern_t *source,
2219 const cairo_path_fixed_t*path,
2220 cairo_fill_rule_t fill_rule,
2222 cairo_antialias_t antialias,
2223 const cairo_clip_t *clip)
2225 cairo_svg_surface_t *surface = abstract_surface;
2226 cairo_status_t status;
2228 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2229 return _cairo_svg_surface_analyze_operation (surface, op, source);
2231 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2233 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2234 if (unlikely (status))
2237 _cairo_output_stream_printf (surface->xml_node, "<path style=\" stroke:none;");
2238 status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, op, source, fill_rule, NULL);
2239 if (unlikely (status))
2242 _cairo_output_stream_printf (surface->xml_node, "\" ");
2244 _cairo_svg_surface_emit_path (surface->xml_node, path, NULL);
2246 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2248 return CAIRO_STATUS_SUCCESS;
2252 _cairo_svg_surface_get_extents (void *abstract_surface,
2253 cairo_rectangle_int_t *rectangle)
2255 cairo_svg_surface_t *surface = abstract_surface;
2260 /* XXX: The conversion to integers here is pretty bogus, (not to
2261 * mention the arbitrary limitation of width to a short(!). We
2262 * may need to come up with a better interface for get_size.
2264 rectangle->width = ceil (surface->width);
2265 rectangle->height = ceil (surface->height);
2270 static cairo_status_t
2271 _cairo_svg_surface_emit_paint (cairo_output_stream_t *output,
2272 cairo_svg_surface_t *surface,
2273 cairo_operator_t op,
2274 const cairo_pattern_t *source,
2275 const cairo_pattern_t *mask_source,
2276 const char *extra_attributes)
2278 cairo_status_t status;
2280 if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
2281 source->extend == CAIRO_EXTEND_NONE)
2282 return _cairo_svg_surface_emit_composite_pattern (output,
2285 (cairo_surface_pattern_t *) source,
2287 mask_source ? &mask_source->matrix :NULL,
2290 _cairo_output_stream_printf (output,
2291 "<rect x=\"0\" y=\"0\" "
2292 "width=\"%f\" height=\"%f\" "
2294 surface->width, surface->height);
2295 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2296 status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL);
2297 if (unlikely (status))
2300 _cairo_output_stream_printf (output, "stroke:none;\"");
2302 if (extra_attributes)
2303 _cairo_output_stream_printf (output, " %s", extra_attributes);
2305 _cairo_output_stream_printf (output, "/>\n");
2307 return CAIRO_STATUS_SUCCESS;
2310 static cairo_int_status_t
2311 _cairo_svg_surface_paint (void *abstract_surface,
2312 cairo_operator_t op,
2313 const cairo_pattern_t *source,
2314 const cairo_clip_t *clip)
2316 cairo_status_t status;
2317 cairo_svg_surface_t *surface = abstract_surface;
2319 /* Emulation of clear and source operators, when no clipping region
2320 * is defined. We just delete existing content of surface root node,
2321 * and exit early if operator is clear.
2323 if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) &&
2326 switch (surface->paginated_mode) {
2327 case CAIRO_PAGINATED_MODE_FALLBACK:
2329 case CAIRO_PAGINATED_MODE_ANALYZE:
2330 return CAIRO_STATUS_SUCCESS;
2332 case CAIRO_PAGINATED_MODE_RENDER:
2333 status = _cairo_output_stream_destroy (surface->xml_node);
2334 if (unlikely (status)) {
2335 surface->xml_node = NULL;
2339 surface->xml_node = _cairo_memory_stream_create ();
2340 if (_cairo_output_stream_get_status (surface->xml_node)) {
2341 status = _cairo_output_stream_destroy (surface->xml_node);
2342 surface->xml_node = NULL;
2346 if (op == CAIRO_OPERATOR_CLEAR) {
2347 if (surface->content == CAIRO_CONTENT_COLOR) {
2348 _cairo_output_stream_printf (surface->xml_node,
2350 "width=\"%f\" height=\"%f\" "
2351 "style=\"opacity:1;"
2353 "fill:rgb(0,0,0);\"/>\n",
2354 surface->width, surface->height);
2356 return CAIRO_STATUS_SUCCESS;
2361 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2362 return _cairo_svg_surface_analyze_operation (surface, op, source);
2364 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2367 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2368 if (unlikely (status))
2371 return _cairo_svg_surface_emit_paint (surface->xml_node,
2372 surface, op, source, 0, NULL);
2375 static cairo_int_status_t
2376 _cairo_svg_surface_mask (void *abstract_surface,
2377 cairo_operator_t op,
2378 const cairo_pattern_t *source,
2379 const cairo_pattern_t *mask,
2380 const cairo_clip_t *clip)
2382 cairo_status_t status;
2383 cairo_svg_surface_t *surface = abstract_surface;
2384 cairo_svg_document_t *document = surface->document;
2385 cairo_output_stream_t *mask_stream;
2387 cairo_bool_t discard_filter = FALSE;
2388 unsigned int mask_id;
2390 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2391 cairo_status_t source_status, mask_status;
2393 source_status = _cairo_svg_surface_analyze_operation (surface, op, source);
2394 if (_cairo_status_is_error (source_status))
2395 return source_status;
2397 if (mask->has_component_alpha) {
2398 mask_status = CAIRO_INT_STATUS_UNSUPPORTED;
2400 mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask);
2401 if (_cairo_status_is_error (mask_status))
2405 return _cairo_analysis_surface_merge_status (source_status,
2409 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2410 assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask));
2412 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2413 if (unlikely (status))
2416 if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
2417 const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask;
2418 cairo_content_t content = surface_pattern->surface->content;
2419 if (content == CAIRO_CONTENT_ALPHA)
2420 discard_filter = TRUE;
2423 if (!discard_filter)
2424 _cairo_svg_surface_emit_alpha_filter (document);
2426 /* _cairo_svg_surface_emit_paint() will output a pattern definition to
2427 * document->xml_node_defs so we need to write the mask element to
2428 * a temporary stream and then copy that to xml_node_defs. */
2429 mask_stream = _cairo_memory_stream_create ();
2430 if (_cairo_output_stream_get_status (mask_stream))
2431 return _cairo_output_stream_destroy (mask_stream);
2433 mask_id = _cairo_svg_document_allocate_mask_id (document);
2435 _cairo_output_stream_printf (mask_stream,
2436 "<mask id=\"mask%d\">\n"
2439 discard_filter ? "" : " <g filter=\"url(#alpha)\">\n");
2440 status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL);
2441 if (unlikely (status)) {
2442 cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream);
2447 _cairo_output_stream_printf (mask_stream,
2450 discard_filter ? "" : " </g>\n");
2451 _cairo_memory_stream_copy (mask_stream, document->xml_node_defs);
2453 status = _cairo_output_stream_destroy (mask_stream);
2454 if (unlikely (status))
2457 snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"",
2459 status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer);
2460 if (unlikely (status))
2463 return CAIRO_STATUS_SUCCESS;
2466 static cairo_int_status_t
2467 _cairo_svg_surface_stroke (void *abstract_dst,
2468 cairo_operator_t op,
2469 const cairo_pattern_t *source,
2470 const cairo_path_fixed_t*path,
2471 const cairo_stroke_style_t *stroke_style,
2472 const cairo_matrix_t *ctm,
2473 const cairo_matrix_t *ctm_inverse,
2475 cairo_antialias_t antialias,
2476 const cairo_clip_t *clip)
2478 cairo_svg_surface_t *surface = abstract_dst;
2479 cairo_status_t status;
2481 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2482 return _cairo_svg_surface_analyze_operation (surface, op, source);
2484 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2486 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2487 if (unlikely (status))
2490 _cairo_output_stream_printf (surface->xml_node, "<path style=\"fill:none;");
2491 status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, op,
2492 source, stroke_style, ctm_inverse);
2493 if (unlikely (status))
2496 _cairo_output_stream_printf (surface->xml_node, "\" ");
2498 _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse);
2500 _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL);
2501 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2503 return CAIRO_STATUS_SUCCESS;
2506 static cairo_int_status_t
2507 _cairo_svg_surface_show_glyphs (void *abstract_surface,
2508 cairo_operator_t op,
2509 const cairo_pattern_t *pattern,
2510 cairo_glyph_t *glyphs,
2512 cairo_scaled_font_t *scaled_font,
2513 const cairo_clip_t *clip)
2515 cairo_svg_surface_t *surface = abstract_surface;
2516 cairo_svg_document_t *document = surface->document;
2517 cairo_path_fixed_t path;
2518 cairo_int_status_t status;
2519 cairo_scaled_font_subsets_glyph_t subset_glyph;
2522 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2523 return _cairo_svg_surface_analyze_operation (surface, op, pattern);
2525 assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
2527 if (num_glyphs <= 0)
2528 return CAIRO_STATUS_SUCCESS;
2530 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2531 if (unlikely (status))
2534 /* FIXME it's probably possible to apply a pattern of a gradient to
2535 * a group of symbols, but I don't know how yet. Gradients or patterns
2536 * are translated by x and y properties of use element. */
2537 if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
2540 _cairo_output_stream_printf (surface->xml_node, "<g style=\"");
2541 status = _cairo_svg_surface_emit_pattern (surface, pattern,
2542 surface->xml_node, FALSE, NULL);
2543 if (unlikely (status))
2546 _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op);
2548 _cairo_output_stream_printf (surface->xml_node, "\">\n");
2550 for (i = 0; i < num_glyphs; i++) {
2551 status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets,
2552 scaled_font, glyphs[i].index,
2555 if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
2556 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2563 if (unlikely (status))
2566 _cairo_output_stream_printf (surface->xml_node,
2567 " <use xlink:href=\"#glyph%d-%d\" "
2568 "x=\"%f\" y=\"%f\"/>\n",
2569 subset_glyph.font_id,
2570 subset_glyph.subset_glyph_index,
2571 glyphs[i].x, glyphs[i].y);
2574 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2576 return CAIRO_STATUS_SUCCESS;
2579 _cairo_path_fixed_init (&path);
2581 status = _cairo_scaled_font_glyph_path (scaled_font,
2582 (cairo_glyph_t *) glyphs,
2585 if (unlikely (status)) {
2586 _cairo_path_fixed_fini (&path);
2590 status = _cairo_svg_surface_fill (abstract_surface, op, pattern,
2591 &path, CAIRO_FILL_RULE_WINDING,
2592 0.0, CAIRO_ANTIALIAS_SUBPIXEL,
2595 _cairo_path_fixed_fini (&path);
2601 _cairo_svg_surface_get_font_options (void *abstract_surface,
2602 cairo_font_options_t *options)
2604 _cairo_font_options_init_default (options);
2606 cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
2607 cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
2608 cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
2609 _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
2613 static const char **
2614 _cairo_svg_surface_get_supported_mime_types (void *abstract_surface)
2616 return _cairo_svg_supported_mime_types;
2619 static const cairo_surface_backend_t cairo_svg_surface_backend = {
2620 CAIRO_SURFACE_TYPE_SVG,
2621 _cairo_svg_surface_finish,
2623 _cairo_default_context_create,
2625 NULL, /* create_similar: handled by wrapper */
2626 NULL, /* create_similar_image */
2627 NULL, /* map to image */
2628 NULL, /* unmap image */
2630 _cairo_surface_default_source,
2631 NULL, /* acquire_source_image */
2632 NULL, /* release_source_image */
2633 NULL, /* snapshot */
2635 _cairo_svg_surface_copy_page,
2636 _cairo_svg_surface_show_page,
2638 _cairo_svg_surface_get_extents,
2639 _cairo_svg_surface_get_font_options,
2642 NULL, /* mark dirty rectangle */
2644 _cairo_svg_surface_paint,
2645 _cairo_svg_surface_mask,
2646 _cairo_svg_surface_stroke,
2647 _cairo_svg_surface_fill,
2648 _cairo_svg_surface_fill_stroke,
2649 _cairo_svg_surface_show_glyphs,
2650 NULL, /* has_show_text_glyphs */
2651 NULL, /* show_text_glyphs */
2652 _cairo_svg_surface_get_supported_mime_types,
2655 static cairo_status_t
2656 _cairo_svg_document_create (cairo_output_stream_t *output_stream,
2659 cairo_svg_version_t version,
2660 cairo_svg_document_t **document_out)
2662 cairo_svg_document_t *document;
2663 cairo_status_t status, status_ignored;
2665 if (output_stream->status)
2666 return output_stream->status;
2668 document = malloc (sizeof (cairo_svg_document_t));
2669 if (unlikely (document == NULL))
2670 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2672 /* The use of defs for font glyphs imposes no per-subset limit. */
2673 document->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
2674 if (unlikely (document->font_subsets == NULL)) {
2675 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2676 goto CLEANUP_DOCUMENT;
2679 document->output_stream = output_stream;
2680 document->refcount = 1;
2681 document->owner = NULL;
2682 document->finished = FALSE;
2683 document->width = width;
2684 document->height = height;
2686 document->linear_pattern_id = 0;
2687 document->radial_pattern_id = 0;
2688 document->pattern_id = 0;
2689 document->filter_id = 0;
2690 document->clip_id = 0;
2691 document->mask_id = 0;
2693 document->xml_node_defs = _cairo_memory_stream_create ();
2694 status = _cairo_output_stream_get_status (document->xml_node_defs);
2695 if (unlikely (status))
2696 goto CLEANUP_NODE_DEFS;
2698 document->xml_node_glyphs = _cairo_memory_stream_create ();
2699 status = _cairo_output_stream_get_status (document->xml_node_glyphs);
2700 if (unlikely (status))
2701 goto CLEANUP_NODE_GLYPHS;
2703 document->alpha_filter = FALSE;
2705 document->svg_version = version;
2707 *document_out = document;
2708 return CAIRO_STATUS_SUCCESS;
2710 CLEANUP_NODE_GLYPHS:
2711 status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs);
2713 status_ignored = _cairo_output_stream_destroy (document->xml_node_defs);
2714 _cairo_scaled_font_subsets_destroy (document->font_subsets);
2720 static cairo_svg_document_t *
2721 _cairo_svg_document_reference (cairo_svg_document_t *document)
2723 document->refcount++;
2729 _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document)
2731 return document->mask_id++;
2734 static cairo_status_t
2735 _cairo_svg_document_destroy (cairo_svg_document_t *document)
2737 cairo_status_t status;
2739 document->refcount--;
2740 if (document->refcount > 0)
2741 return CAIRO_STATUS_SUCCESS;
2743 status = _cairo_svg_document_finish (document);
2750 static cairo_status_t
2751 _cairo_svg_document_finish (cairo_svg_document_t *document)
2753 cairo_status_t status, status2;
2754 cairo_output_stream_t *output = document->output_stream;
2755 cairo_svg_page_t *page;
2758 if (document->finished)
2759 return CAIRO_STATUS_SUCCESS;
2762 * Should we add DOCTYPE?
2766 * http://tech.groups.yahoo.com/group/svg-developers/message/48562:
2767 * There's a bunch of issues, but just to pick a few:
2768 * - they'll give false positives.
2769 * - they'll give false negatives.
2770 * - they're namespace-unaware.
2771 * - they don't wildcard.
2772 * So when they say OK they really haven't checked anything, when
2773 * they say NOT OK they might be on crack, and like all
2774 * namespace-unaware things they're a dead branch of the XML tree.
2776 * http://jwatt.org/svg/authoring/:
2777 * Unfortunately the SVG DTDs are a source of so many issues that the
2778 * SVG WG has decided not to write one for the upcoming SVG 1.2
2779 * standard. In fact SVG WG members are even telling people not to use
2780 * a DOCTYPE declaration in SVG 1.0 and 1.1 documents.
2783 _cairo_output_stream_printf (output,
2784 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2785 "<svg xmlns=\"http://www.w3.org/2000/svg\" "
2786 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
2787 "width=\"%fpt\" height=\"%fpt\" "
2788 "viewBox=\"0 0 %f %f\" version=\"%s\">\n",
2789 document->width, document->height,
2790 document->width, document->height,
2791 _cairo_svg_internal_version_strings [document->svg_version]);
2793 status = _cairo_svg_document_emit_font_subsets (document);
2795 if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 ||
2796 _cairo_memory_stream_length (document->xml_node_defs) > 0) {
2797 _cairo_output_stream_printf (output, "<defs>\n");
2798 if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
2799 _cairo_output_stream_printf (output, "<g>\n");
2800 _cairo_memory_stream_copy (document->xml_node_glyphs, output);
2801 _cairo_output_stream_printf (output, "</g>\n");
2803 _cairo_memory_stream_copy (document->xml_node_defs, output);
2804 _cairo_output_stream_printf (output, "</defs>\n");
2807 if (document->owner != NULL) {
2808 cairo_svg_surface_t *surface;
2810 surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner);
2811 if (surface->xml_node != NULL &&
2812 _cairo_memory_stream_length (surface->xml_node) > 0) {
2813 if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) {
2814 if (status == CAIRO_STATUS_SUCCESS)
2815 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2819 if (surface->page_set.num_elements > 1 &&
2820 _cairo_svg_version_has_page_set_support (document->svg_version)) {
2821 _cairo_output_stream_printf (output, "<pageSet>\n");
2822 for (i = 0; i < surface->page_set.num_elements; i++) {
2823 page = _cairo_array_index (&surface->page_set, i);
2824 _cairo_output_stream_printf (output, "<page>\n");
2825 _cairo_output_stream_printf (output,
2826 "<g id=\"surface%d\">\n",
2828 _cairo_memory_stream_copy (page->xml_node, output);
2829 _cairo_output_stream_printf (output, "</g>\n</page>\n");
2831 _cairo_output_stream_printf (output, "</pageSet>\n");
2832 } else if (surface->page_set.num_elements > 0) {
2833 page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
2834 _cairo_output_stream_printf (output,
2835 "<g id=\"surface%d\">\n",
2837 _cairo_memory_stream_copy (page->xml_node, output);
2838 _cairo_output_stream_printf (output, "</g>\n");
2842 _cairo_output_stream_printf (output, "</svg>\n");
2844 status2 = _cairo_output_stream_destroy (document->xml_node_glyphs);
2845 if (status == CAIRO_STATUS_SUCCESS)
2848 status2 = _cairo_output_stream_destroy (document->xml_node_defs);
2849 if (status == CAIRO_STATUS_SUCCESS)
2852 status2 = _cairo_output_stream_destroy (output);
2853 if (status == CAIRO_STATUS_SUCCESS)
2856 document->finished = TRUE;
2862 _cairo_svg_surface_set_paginated_mode (void *abstract_surface,
2863 cairo_paginated_mode_t paginated_mode)
2865 cairo_svg_surface_t *surface = abstract_surface;
2867 surface->paginated_mode = paginated_mode;
2871 _cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface)
2873 cairo_svg_surface_t *surface = abstract_surface;
2874 cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
2876 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) {
2877 status = _cairo_svg_surface_analyze_operator (surface,
2878 CAIRO_OPERATOR_SOURCE);
2881 return status == CAIRO_INT_STATUS_SUCCESS;
2884 static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
2885 NULL /*_cairo_svg_surface_start_page*/,
2886 _cairo_svg_surface_set_paginated_mode,
2887 NULL, /* _cairo_svg_surface_set_bounding_box */
2888 NULL, /* _cairo_svg_surface_set_fallback_images_required */
2889 _cairo_svg_surface_supports_fine_grained_fallbacks,