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-svg-surface-private.h"
63 * @Title: SVG Surfaces
64 * @Short_Description: Rendering SVG documents
65 * @See_Also: #cairo_surface_t
67 * The SVG surface is used to render cairo graphics to
68 * SVG files and is a multi-page vector surface backend.
72 * CAIRO_HAS_SVG_SURFACE:
74 * Defined if the SVG surface backend is available.
75 * This macro can be used to conditionally compile backend-specific code.
80 typedef struct cairo_svg_page cairo_svg_page_t;
82 static const int invalid_pattern_id = -1;
84 static const cairo_svg_version_t _cairo_svg_versions[] =
86 CAIRO_SVG_VERSION_1_1,
90 #define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
92 static const char *_cairo_svg_supported_mime_types[] =
101 _cairo_svg_surface_emit_path (cairo_output_stream_t *output,
102 const cairo_path_fixed_t *path,
103 const cairo_matrix_t *ctm_inverse);
106 _cairo_svg_version_has_page_set_support (cairo_svg_version_t version)
108 return version > CAIRO_SVG_VERSION_1_1;
111 static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] =
117 static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] =
123 struct cairo_svg_page {
124 unsigned int surface_id;
125 unsigned int clip_level;
126 cairo_output_stream_t *xml_node;
129 struct cairo_svg_document {
130 cairo_output_stream_t *output_stream;
131 unsigned long refcount;
132 cairo_surface_t *owner;
133 cairo_bool_t finished;
138 cairo_output_stream_t *xml_node_defs;
139 cairo_output_stream_t *xml_node_glyphs;
141 unsigned int linear_pattern_id;
142 unsigned int radial_pattern_id;
143 unsigned int pattern_id;
144 unsigned int filter_id;
145 unsigned int clip_id;
146 unsigned int mask_id;
148 cairo_bool_t alpha_filter;
150 cairo_svg_version_t svg_version;
152 cairo_scaled_font_subsets_t *font_subsets;
155 static cairo_status_t
156 _cairo_svg_document_create (cairo_output_stream_t *stream,
159 cairo_svg_version_t version,
160 cairo_svg_document_t **document_out);
162 static cairo_status_t
163 _cairo_svg_document_destroy (cairo_svg_document_t *document);
165 static cairo_status_t
166 _cairo_svg_document_finish (cairo_svg_document_t *document);
168 static cairo_svg_document_t *
169 _cairo_svg_document_reference (cairo_svg_document_t *document);
172 _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document);
174 static cairo_surface_t *
175 _cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
176 cairo_content_t content,
179 static cairo_surface_t *
180 _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
183 cairo_svg_version_t version);
185 static const cairo_surface_backend_t cairo_svg_surface_backend;
186 static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend;
189 * cairo_svg_surface_create_for_stream:
190 * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
191 * to indicate a no-op @write_func. With a no-op @write_func,
192 * the surface may be queried or used as a source without
193 * generating any temporary files.
194 * @closure: the closure argument for @write_func
195 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
196 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
198 * Creates a SVG surface of the specified size in points to be written
199 * incrementally to the stream represented by @write_func and @closure.
201 * Return value: a pointer to the newly created surface. The caller
202 * owns the surface and should call cairo_surface_destroy() when done
205 * This function always returns a valid pointer, but it will return a
206 * pointer to a "nil" surface if an error such as out of memory
207 * occurs. You can use cairo_surface_status() to check for this.
212 cairo_svg_surface_create_for_stream (cairo_write_func_t write_func,
217 cairo_output_stream_t *stream;
219 stream = _cairo_output_stream_create (write_func, NULL, closure);
220 if (_cairo_output_stream_get_status (stream))
221 return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
223 return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
227 * cairo_svg_surface_create:
228 * @filename: a filename for the SVG output (must be writable), %NULL may be
229 * used to specify no output. This will generate a SVG surface that
230 * may be queried and used as a source, without generating a
232 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
233 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
235 * Creates a SVG surface of the specified size in points to be written
238 * The SVG surface backend recognizes the following MIME types for the
239 * data attached to a surface (see cairo_surface_set_mime_data()) when
240 * it is used as a source pattern for drawing on this surface:
241 * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG,
242 * %CAIRO_MIME_TYPE_URI. If any of them is specified, the SVG backend
243 * emits a href with the content of MIME data instead of a surface
244 * snapshot (PNG, Base64-encoded) in the corresponding image tag.
246 * The unofficial MIME type %CAIRO_MIME_TYPE_URI is examined
247 * first. If present, the URI is emitted as is: assuring the
248 * correctness of URI is left to the client code.
250 * If %CAIRO_MIME_TYPE_URI is not present, but %CAIRO_MIME_TYPE_JPEG
251 * or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is
252 * Base64-encoded and emitted.
254 * Return value: a pointer to the newly created surface. The caller
255 * owns the surface and should call cairo_surface_destroy() when done
258 * This function always returns a valid pointer, but it will return a
259 * pointer to a "nil" surface if an error such as out of memory
260 * occurs. You can use cairo_surface_status() to check for this.
265 cairo_svg_surface_create (const char *filename,
269 cairo_output_stream_t *stream;
271 stream = _cairo_output_stream_create_for_filename (filename);
272 if (_cairo_output_stream_get_status (stream))
273 return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
275 return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
279 _cairo_surface_is_svg (cairo_surface_t *surface)
281 return surface->backend == &cairo_svg_surface_backend;
284 /* If the abstract_surface is a paginated surface, and that paginated
285 * surface's target is a svg_surface, then set svg_surface to that
286 * target. Otherwise return FALSE.
289 _extract_svg_surface (cairo_surface_t *surface,
290 cairo_svg_surface_t **svg_surface)
292 cairo_surface_t *target;
293 cairo_status_t status_ignored;
297 if (surface->finished) {
298 status_ignored = _cairo_surface_set_error (surface,
299 _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
303 if (! _cairo_surface_is_paginated (surface)) {
304 status_ignored = _cairo_surface_set_error (surface,
305 _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
309 target = _cairo_paginated_surface_get_target (surface);
310 if (target->status) {
311 status_ignored = _cairo_surface_set_error (surface,
315 if (target->finished) {
316 status_ignored = _cairo_surface_set_error (surface,
317 _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
321 if (! _cairo_surface_is_svg (target)) {
322 status_ignored = _cairo_surface_set_error (surface,
323 _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
327 *svg_surface = (cairo_svg_surface_t *) target;
332 * cairo_svg_surface_restrict_to_version:
333 * @surface: a SVG #cairo_surface_t
334 * @version: SVG version
336 * Restricts the generated SVG file to @version. See cairo_svg_get_versions()
337 * for a list of available version values that can be used here.
339 * This function should only be called before any drawing operations
340 * have been performed on the given surface. The simplest way to do
341 * this is to call this function immediately after creating the
347 cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface,
348 cairo_svg_version_t version)
350 cairo_svg_surface_t *surface = NULL; /* hide compiler warning */
352 if (! _extract_svg_surface (abstract_surface, &surface))
355 if (version < CAIRO_SVG_VERSION_LAST)
356 surface->document->svg_version = version;
360 * cairo_svg_get_versions:
361 * @versions: supported version list
362 * @num_versions: list length
364 * Used to retrieve the list of supported versions. See
365 * cairo_svg_surface_restrict_to_version().
370 cairo_svg_get_versions (cairo_svg_version_t const **versions,
373 if (versions != NULL)
374 *versions = _cairo_svg_versions;
376 if (num_versions != NULL)
377 *num_versions = CAIRO_SVG_VERSION_LAST;
381 * cairo_svg_version_to_string:
382 * @version: a version id
384 * Get the string representation of the given @version id. This function
385 * will return %NULL if @version isn't valid. See cairo_svg_get_versions()
386 * for a way to get the list of valid version ids.
388 * Return value: the string associated to given version.
393 cairo_svg_version_to_string (cairo_svg_version_t version)
395 if (version >= CAIRO_SVG_VERSION_LAST)
398 return _cairo_svg_version_strings[version];
402 _cliprect_covers_surface (cairo_svg_surface_t *surface,
403 cairo_path_fixed_t *path)
407 if (_cairo_path_fixed_is_box (path, &box)) {
410 _cairo_fixed_to_double (box.p2.x) >= surface->width &&
411 _cairo_fixed_to_double (box.p2.y) >= surface->height)
420 static cairo_status_t
421 _cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
422 cairo_path_fixed_t *path,
423 cairo_fill_rule_t fill_rule,
425 cairo_antialias_t antialias)
427 cairo_svg_surface_t *surface = cairo_container_of (clipper,
430 cairo_svg_document_t *document = surface->document;
434 for (i = 0; i < surface->clip_level; i++)
435 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
437 surface->clip_level = 0;
438 return CAIRO_STATUS_SUCCESS;
441 /* skip trivial whole-page clips */
442 if (_cliprect_covers_surface (surface, path))
443 return CAIRO_STATUS_SUCCESS;
445 _cairo_output_stream_printf (document->xml_node_defs,
446 "<clipPath id=\"clip%d\">\n"
449 _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL);
451 _cairo_output_stream_printf (document->xml_node_defs,
455 _cairo_output_stream_printf (surface->xml_node,
456 "<g clip-path=\"url(#clip%d)\" "
457 "clip-rule=\"%s\">\n",
459 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
460 "evenodd" : "nonzero");
463 surface->clip_level++;
465 return CAIRO_STATUS_SUCCESS;
468 static cairo_surface_t *
469 _cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
470 cairo_content_t content,
474 cairo_svg_surface_t *surface;
475 cairo_surface_t *paginated = NULL;
476 cairo_status_t status, status_ignored;
478 surface = malloc (sizeof (cairo_svg_surface_t));
479 if (unlikely (surface == NULL))
480 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
482 _cairo_surface_init (&surface->base,
483 &cairo_svg_surface_backend,
487 surface->width = width;
488 surface->height = height;
490 surface->document = _cairo_svg_document_reference (document);
492 surface->clip_level = 0;
493 _cairo_surface_clipper_init (&surface->clipper,
494 _cairo_svg_surface_clipper_intersect_clip_path);
496 surface->base_clip = document->clip_id++;
497 surface->is_base_clip_emitted = FALSE;
499 surface->xml_node = _cairo_memory_stream_create ();
500 status = _cairo_output_stream_get_status (surface->xml_node);
501 if (unlikely (status))
504 _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t));
506 if (content == CAIRO_CONTENT_COLOR) {
507 _cairo_output_stream_printf (surface->xml_node,
508 "<rect width=\"%f\" height=\"%f\" "
509 "style=\"opacity:1;stroke:none;"
510 "fill:rgb(0,0,0);\"/>\n",
512 status = _cairo_output_stream_get_status (surface->xml_node);
513 if (unlikely (status))
517 surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
518 surface->force_fallbacks = FALSE;
519 surface->content = content;
521 paginated = _cairo_paginated_surface_create (&surface->base,
523 &cairo_svg_surface_paginated_backend);
524 status = paginated->status;
525 if (status == CAIRO_STATUS_SUCCESS) {
526 /* paginated keeps the only reference to surface now, drop ours */
527 cairo_surface_destroy (&surface->base);
531 /* ignore status as we are on the error path */
533 status_ignored = _cairo_output_stream_destroy (surface->xml_node);
534 status_ignored = _cairo_svg_document_destroy (document);
537 cairo_surface_destroy (paginated);
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,
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,
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);
562 surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA,
564 if (surface->status) {
565 status = _cairo_svg_document_destroy (document);
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);
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;
585 stream = _cairo_memory_stream_create ();
586 if (_cairo_output_stream_get_status (stream)) {
587 status = _cairo_output_stream_destroy (stream);
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);
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;
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)\"",
652 matrix.xx, matrix.yx,
653 matrix.xy, matrix.yy,
654 matrix.x0, matrix.y0);
658 cairo_output_stream_t *output;
659 const cairo_matrix_t *ctm_inverse;
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;
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,
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,
764 CAIRO_SCALED_GLYPH_INFO_METRICS|
765 CAIRO_SCALED_GLYPH_INFO_PATH,
767 if (unlikely (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,
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;
794 status = _cairo_scaled_glyph_lookup (scaled_font,
796 CAIRO_SCALED_GLYPH_INFO_METRICS |
797 CAIRO_SCALED_GLYPH_INFO_SURFACE,
799 if (unlikely (status))
802 image = _cairo_image_surface_coerce_to_format (scaled_glyph->surface,
804 status = image->base.status;
805 if (unlikely (status)) {
806 cairo_surface_destroy (&image->base);
810 _cairo_output_stream_printf (document->xml_node_glyphs, "<g");
811 _cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform",
812 &image->base.device_transform_inverse, NULL);
813 _cairo_output_stream_printf (document->xml_node_glyphs, ">/n");
815 for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) {
816 for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
817 uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
818 for (bit = 7; bit >= 0 && x < image->width; bit--, x++) {
819 if (output_byte & (1 << bit)) {
820 _cairo_output_stream_printf (document->xml_node_glyphs,
821 "<rect x=\"%d\" y=\"%d\" width=\"1\" height=\"1\"/>\n",
827 _cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n");
829 cairo_surface_destroy (&image->base);
831 return CAIRO_STATUS_SUCCESS;
834 static cairo_int_status_t
835 _cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
836 cairo_scaled_font_t *scaled_font,
837 unsigned long scaled_font_glyph_index,
838 unsigned int font_id,
839 unsigned int subset_glyph_index)
841 cairo_int_status_t status;
843 _cairo_output_stream_printf (document->xml_node_glyphs,
844 "<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n",
848 status = _cairo_svg_document_emit_outline_glyph_data (document,
850 scaled_font_glyph_index);
851 if (status == CAIRO_INT_STATUS_UNSUPPORTED)
852 status = _cairo_svg_document_emit_bitmap_glyph_data (document,
854 scaled_font_glyph_index);
855 if (unlikely (status))
858 _cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n");
860 return CAIRO_INT_STATUS_SUCCESS;
863 static cairo_int_status_t
864 _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
867 cairo_svg_document_t *document = closure;
868 cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
871 _cairo_scaled_font_freeze_cache (font_subset->scaled_font);
872 for (i = 0; i < font_subset->num_glyphs; i++) {
873 status = _cairo_svg_document_emit_glyph (document,
874 font_subset->scaled_font,
875 font_subset->glyphs[i],
876 font_subset->font_id, i);
877 if (unlikely (status))
880 _cairo_scaled_font_thaw_cache (font_subset->scaled_font);
885 static cairo_status_t
886 _cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
888 cairo_status_t status;
890 status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
891 _cairo_svg_document_emit_font_subset,
893 if (unlikely (status))
896 status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
897 _cairo_svg_document_emit_font_subset,
901 _cairo_scaled_font_subsets_destroy (document->font_subsets);
902 document->font_subsets = NULL;
908 _cairo_svg_surface_operators[] = {
911 "src", "src-over", "src-in",
912 "src-out", "src-atop",
914 "dst", "dst-over", "dst-in",
915 "dst-out", "dst-atop",
918 "color-dodge", /* FIXME: saturate ? */
920 "multiply", "screen", "overlay",
922 "color-dodge", "color-burn",
923 "hard-light", "soft-light",
924 "difference", "exclusion"
928 _cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface,
931 /* guard against newly added operators */
932 if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators))
933 return CAIRO_INT_STATUS_UNSUPPORTED;
935 /* allow operators being NULL if they are unsupported */
936 if (_cairo_svg_surface_operators[op] == NULL)
937 return CAIRO_INT_STATUS_UNSUPPORTED;
939 return CAIRO_STATUS_SUCCESS;
942 static cairo_int_status_t
943 _cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface,
945 const cairo_pattern_t *pattern)
947 cairo_svg_document_t *document = surface->document;
949 if (surface->force_fallbacks &&
950 surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
952 return CAIRO_INT_STATUS_UNSUPPORTED;
955 if (pattern->type == CAIRO_PATTERN_TYPE_MESH)
956 return CAIRO_INT_STATUS_UNSUPPORTED;
958 /* SVG doesn't support extend reflect for image pattern */
959 if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
960 pattern->extend == CAIRO_EXTEND_REFLECT)
961 return CAIRO_INT_STATUS_UNSUPPORTED;
963 if (document->svg_version >= CAIRO_SVG_VERSION_1_2)
964 return _cairo_svg_surface_analyze_operator (surface, op);
966 if (op == CAIRO_OPERATOR_OVER)
967 return CAIRO_STATUS_SUCCESS;
969 /* The SOURCE operator is only supported if there is nothing
970 * painted underneath. */
971 if (op == CAIRO_OPERATOR_SOURCE)
972 return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
974 return CAIRO_INT_STATUS_UNSUPPORTED;
977 static cairo_int_status_t
978 _cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface,
980 const cairo_pattern_t *pattern)
982 return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED;
985 static cairo_status_t
986 _cairo_svg_surface_finish (void *abstract_surface)
988 cairo_status_t status, status2;
989 cairo_svg_surface_t *surface = abstract_surface;
990 cairo_svg_document_t *document = surface->document;
991 cairo_svg_page_t *page;
994 if (_cairo_paginated_surface_get_target (document->owner) == &surface->base)
995 status = _cairo_svg_document_finish (document);
997 status = CAIRO_STATUS_SUCCESS;
999 if (surface->xml_node != NULL) {
1000 status2 = _cairo_output_stream_destroy (surface->xml_node);
1001 if (status == CAIRO_STATUS_SUCCESS)
1005 for (i = 0; i < surface->page_set.num_elements; i++) {
1006 page = _cairo_array_index (&surface->page_set, i);
1007 status2 = _cairo_output_stream_destroy (page->xml_node);
1008 if (status == CAIRO_STATUS_SUCCESS)
1011 _cairo_array_fini (&surface->page_set);
1013 _cairo_surface_clipper_reset (&surface->clipper);
1015 status2 = _cairo_svg_document_destroy (document);
1016 if (status == CAIRO_STATUS_SUCCESS)
1024 _cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document)
1026 if (document->alpha_filter)
1029 _cairo_output_stream_printf (document->xml_node_defs,
1030 "<filter id=\"alpha\" "
1031 "filterUnits=\"objectBoundingBox\" "
1032 "x=\"0%%\" y=\"0%%\" "
1033 "width=\"100%%\" height=\"100%%\">\n"
1034 " <feColorMatrix type=\"matrix\" "
1035 "in=\"SourceGraphic\" "
1036 "values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n"
1039 document->alpha_filter = TRUE;
1043 cairo_output_stream_t *output;
1044 unsigned int in_mem;
1045 unsigned int trailing;
1046 unsigned char src[3];
1047 } base64_write_closure_t;
1049 static char const base64_table[64] =
1050 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1052 static cairo_status_t
1053 base64_write_func (void *closure,
1054 const unsigned char *data,
1055 unsigned int length)
1057 base64_write_closure_t *info = (base64_write_closure_t *) closure;
1063 if (info->in_mem + length < 3) {
1064 for (i = 0; i < length; i++) {
1065 src[i + info->in_mem] = *data++;
1067 info->in_mem += length;
1068 return CAIRO_STATUS_SUCCESS;
1072 unsigned char dst[4];
1074 for (i = info->in_mem; i < 3; i++) {
1080 dst[0] = base64_table[src[0] >> 2];
1081 dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4];
1082 dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6];
1083 dst[3] = base64_table[src[2] & 0xfc >> 2];
1084 /* Special case for the last missing bits */
1085 switch (info->trailing) {
1093 _cairo_output_stream_write (info->output, dst, 4);
1094 } while (length >= 3);
1096 for (i = 0; i < length; i++) {
1099 info->in_mem = length;
1101 return _cairo_output_stream_get_status (info->output);
1104 static cairo_int_status_t
1105 _cairo_surface_base64_encode_jpeg (cairo_surface_t *surface,
1106 cairo_output_stream_t *output)
1108 const unsigned char *mime_data;
1109 unsigned long mime_data_length;
1110 cairo_image_info_t image_info;
1111 base64_write_closure_t info;
1112 cairo_status_t status;
1114 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG,
1115 &mime_data, &mime_data_length);
1116 if (mime_data == NULL)
1117 return CAIRO_INT_STATUS_UNSUPPORTED;
1119 status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length);
1120 if (unlikely (status))
1123 _cairo_output_stream_printf (output, "data:image/jpeg;base64,");
1125 info.output = output;
1129 status = base64_write_func (&info, mime_data, mime_data_length);
1130 if (unlikely (status))
1133 if (info.in_mem > 0) {
1134 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1135 info.trailing = 3 - info.in_mem;
1137 status = base64_write_func (&info, NULL, 0);
1143 static cairo_int_status_t
1144 _cairo_surface_base64_encode_png (cairo_surface_t *surface,
1145 cairo_output_stream_t *output)
1147 const unsigned char *mime_data;
1148 unsigned long mime_data_length;
1149 base64_write_closure_t info;
1150 cairo_status_t status;
1152 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG,
1153 &mime_data, &mime_data_length);
1154 if (unlikely (surface->status))
1155 return surface->status;
1156 if (mime_data == NULL)
1157 return CAIRO_INT_STATUS_UNSUPPORTED;
1159 _cairo_output_stream_printf (output, "data:image/png;base64,");
1161 info.output = output;
1165 status = base64_write_func (&info, mime_data, mime_data_length);
1166 if (unlikely (status))
1169 if (info.in_mem > 0) {
1170 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1171 info.trailing = 3 - info.in_mem;
1173 status = base64_write_func (&info, NULL, 0);
1179 static cairo_int_status_t
1180 _cairo_surface_base64_encode (cairo_surface_t *surface,
1181 cairo_output_stream_t *output)
1183 cairo_int_status_t status;
1184 base64_write_closure_t info;
1186 status = _cairo_surface_base64_encode_jpeg (surface, output);
1187 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1190 status = _cairo_surface_base64_encode_png (surface, output);
1191 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1194 info.output = output;
1198 _cairo_output_stream_printf (info.output, "data:image/png;base64,");
1200 status = cairo_surface_write_to_png_stream (surface, base64_write_func,
1203 if (unlikely (status))
1206 if (info.in_mem > 0) {
1207 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1208 info.trailing = 3 - info.in_mem;
1210 status = base64_write_func (&info, NULL, 0);
1217 _cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
1218 cairo_svg_surface_t *surface,
1219 cairo_operator_t op)
1221 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1222 op != CAIRO_OPERATOR_OVER) {
1223 _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]);
1224 if (!_cairo_operator_bounded_by_source (op))
1225 _cairo_output_stream_printf (output, " clip-to-self=\"true\"");
1230 _cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output,
1231 cairo_svg_surface_t *surface,
1232 cairo_operator_t op)
1234 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1235 op != CAIRO_OPERATOR_OVER) {
1236 _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]);
1237 if (!_cairo_operator_bounded_by_source (op))
1238 _cairo_output_stream_printf (output, "clip-to-self:true;");
1243 * _cairo_svg_surface_emit_attr_value:
1245 * Write the value to output the stream as a sequence of characters,
1246 * while escaping those which have special meaning in the XML
1247 * attribute's value context: & and ".
1250 _cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream,
1251 const unsigned char *value,
1252 unsigned int length)
1254 const unsigned char *p;
1255 const unsigned char *q;
1258 /* we'll accumulate non-special chars in [q, p) range */
1261 for (i = 0; i < length; i++, p++) {
1262 if (*p == '&' || *p == '"') {
1263 /* flush what's left before special char */
1265 _cairo_output_stream_write (stream, q, p - q);
1270 _cairo_output_stream_printf (stream, "&");
1272 _cairo_output_stream_printf (stream, """);
1276 /* flush the trailing chars if any */
1278 _cairo_output_stream_write (stream, q, p - q);
1281 static cairo_status_t
1282 _cairo_svg_surface_emit_surface (cairo_svg_document_t *document,
1283 cairo_surface_t *surface)
1285 cairo_rectangle_int_t extents;
1286 cairo_bool_t is_bounded;
1287 cairo_status_t status;
1288 const unsigned char *uri;
1289 unsigned long uri_len;
1291 if (_cairo_user_data_array_get_data (&surface->user_data,
1292 (cairo_user_data_key_t *) document))
1294 return CAIRO_STATUS_SUCCESS;
1297 is_bounded = _cairo_surface_get_extents (surface, &extents);
1298 assert (is_bounded);
1300 _cairo_output_stream_printf (document->xml_node_defs,
1301 "<image id=\"image%d\" width=\"%d\" height=\"%d\"",
1303 extents.width, extents.height);
1305 _cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\"");
1307 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_URI,
1310 _cairo_svg_surface_emit_attr_value (document->xml_node_defs,
1313 status = _cairo_surface_base64_encode (surface,
1314 document->xml_node_defs);
1315 if (unlikely (status))
1319 _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n");
1322 return _cairo_user_data_array_set_data (&surface->user_data,
1323 (cairo_user_data_key_t *) document,
1327 static cairo_status_t
1328 _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *output,
1329 cairo_svg_surface_t *svg_surface,
1330 cairo_operator_t op,
1331 cairo_surface_pattern_t *pattern,
1333 const cairo_matrix_t *parent_matrix,
1334 const char *extra_attributes)
1336 cairo_status_t status;
1339 p2u = pattern->base.matrix;
1340 status = cairo_matrix_invert (&p2u);
1341 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1342 assert (status == CAIRO_STATUS_SUCCESS);
1344 status = _cairo_svg_surface_emit_surface (svg_surface->document,
1346 if (unlikely (status))
1349 if (pattern_id != invalid_pattern_id) {
1350 cairo_rectangle_int_t extents;
1351 cairo_bool_t is_bounded;
1353 is_bounded = _cairo_surface_get_extents (pattern->surface, &extents);
1354 assert (is_bounded);
1356 _cairo_output_stream_printf (output,
1357 "<pattern id=\"pattern%d\" "
1358 "patternUnits=\"userSpaceOnUse\" "
1359 "width=\"%d\" height=\"%d\" ",
1361 extents.width, extents.height);
1362 _cairo_svg_surface_emit_transform (output,
1363 " patternTransform",
1364 &p2u, parent_matrix);
1365 _cairo_output_stream_printf (output, ">\n ");
1368 _cairo_output_stream_printf (output,
1369 "<use xlink:href=\"#image%d\"",
1370 pattern->surface->unique_id);
1371 if (extra_attributes)
1372 _cairo_output_stream_printf (output, " %s", extra_attributes);
1374 if (pattern_id == invalid_pattern_id) {
1375 _cairo_svg_surface_emit_operator (output, svg_surface, op);
1376 _cairo_svg_surface_emit_transform (output,
1378 &p2u, parent_matrix);
1380 _cairo_output_stream_printf (output, "/>\n");
1383 if (pattern_id != invalid_pattern_id)
1384 _cairo_output_stream_printf (output, "</pattern>\n");
1386 return CAIRO_STATUS_SUCCESS;
1389 static cairo_status_t
1390 _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document,
1391 cairo_recording_surface_t *source)
1393 cairo_status_t status;
1394 cairo_surface_t *paginated_surface = NULL;
1395 cairo_svg_surface_t *svg_surface;
1396 cairo_array_t *page_set;
1398 cairo_output_stream_t *contents;
1400 if (_cairo_user_data_array_get_data (&source->base.user_data,
1401 (cairo_user_data_key_t *) document))
1403 return CAIRO_STATUS_SUCCESS;
1406 paginated_surface = _cairo_svg_surface_create_for_document (document,
1407 source->base.content,
1408 source->extents_pixels.width,
1409 source->extents_pixels.height);
1410 if (unlikely (paginated_surface->status)) {
1411 status = paginated_surface->status;
1412 cairo_surface_destroy (paginated_surface);
1416 svg_surface = (cairo_svg_surface_t *)
1417 _cairo_paginated_surface_get_target (paginated_surface);
1418 cairo_surface_set_fallback_resolution (paginated_surface,
1419 document->owner->x_fallback_resolution,
1420 document->owner->y_fallback_resolution);
1421 cairo_surface_set_device_offset (&svg_surface->base,
1422 -source->extents_pixels.x,
1423 -source->extents_pixels.y);
1425 status = _cairo_recording_surface_replay (&source->base, paginated_surface);
1426 if (unlikely (status)) {
1427 cairo_surface_destroy (paginated_surface);
1431 cairo_surface_show_page (paginated_surface);
1432 status = cairo_surface_status (paginated_surface);
1433 if (unlikely (status)) {
1434 cairo_surface_destroy (paginated_surface);
1438 if (! svg_surface->is_base_clip_emitted) {
1439 svg_surface->is_base_clip_emitted = TRUE;
1440 _cairo_output_stream_printf (document->xml_node_defs,
1441 "<clipPath id=\"clip%d\">\n"
1442 " <rect width=\"%f\" height=\"%f\"/>\n"
1444 svg_surface->base_clip,
1446 svg_surface->height);
1449 if (source->base.content == CAIRO_CONTENT_ALPHA) {
1450 _cairo_svg_surface_emit_alpha_filter (document);
1451 _cairo_output_stream_printf (document->xml_node_defs,
1452 "<g id=\"surface%d\" "
1453 "clip-path=\"url(#clip%d)\" "
1454 "filter=\"url(#alpha)\">\n",
1455 source->base.unique_id,
1456 svg_surface->base_clip);
1458 _cairo_output_stream_printf (document->xml_node_defs,
1459 "<g id=\"surface%d\" "
1460 "clip-path=\"url(#clip%d)\">\n",
1461 source->base.unique_id,
1462 svg_surface->base_clip);
1465 contents = svg_surface->xml_node;
1466 page_set = &svg_surface->page_set;
1468 if (_cairo_memory_stream_length (contents) > 0) {
1469 if (unlikely (_cairo_svg_surface_store_page (svg_surface) == NULL)) {
1470 cairo_surface_destroy (paginated_surface);
1471 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1475 if (page_set->num_elements > 0) {
1476 cairo_svg_page_t *page;
1478 page = _cairo_array_index (page_set, page_set->num_elements - 1);
1479 _cairo_memory_stream_copy (page->xml_node, document->xml_node_defs);
1482 _cairo_output_stream_printf (document->xml_node_defs, "</g>\n");
1484 status = cairo_surface_status (paginated_surface);
1485 cairo_surface_destroy (paginated_surface);
1487 if (unlikely (status))
1491 return _cairo_user_data_array_set_data (&source->base.user_data,
1492 (cairo_user_data_key_t *) document,
1496 static cairo_status_t
1497 _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *output,
1498 cairo_svg_surface_t *surface,
1499 cairo_operator_t op,
1500 cairo_surface_pattern_t *pattern,
1502 const cairo_matrix_t *parent_matrix,
1503 const char *extra_attributes)
1505 cairo_svg_document_t *document = surface->document;
1506 cairo_recording_surface_t *recording_surface;
1508 cairo_status_t status;
1510 p2u = pattern->base.matrix;
1511 status = cairo_matrix_invert (&p2u);
1512 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1513 assert (status == CAIRO_STATUS_SUCCESS);
1515 recording_surface = (cairo_recording_surface_t *) pattern->surface;
1516 status = _cairo_svg_surface_emit_recording_surface (document, recording_surface);
1517 if (unlikely (status))
1520 if (pattern_id != invalid_pattern_id) {
1521 _cairo_output_stream_printf (output,
1522 "<pattern id=\"pattern%d\" "
1523 "patternUnits=\"userSpaceOnUse\" "
1524 "width=\"%d\" height=\"%d\"",
1526 recording_surface->extents.width,
1527 recording_surface->extents.height);
1528 _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
1529 _cairo_output_stream_printf (output, ">\n");
1532 _cairo_output_stream_printf (output,
1533 "<use xlink:href=\"#surface%d\"",
1534 recording_surface->base.unique_id);
1536 if (pattern_id == invalid_pattern_id) {
1537 _cairo_svg_surface_emit_operator (output, surface, op);
1538 _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
1541 if (extra_attributes)
1542 _cairo_output_stream_printf (output, " %s", extra_attributes);
1544 _cairo_output_stream_printf (output, "/>\n");
1546 if (pattern_id != invalid_pattern_id)
1547 _cairo_output_stream_printf (output, "</pattern>\n");
1549 return CAIRO_STATUS_SUCCESS;
1552 static cairo_status_t
1553 _cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output,
1554 cairo_svg_surface_t *surface,
1555 cairo_operator_t op,
1556 cairo_surface_pattern_t *pattern,
1558 const cairo_matrix_t *parent_matrix,
1559 const char *extra_attributes)
1562 if (_cairo_surface_is_recording (pattern->surface)) {
1563 return _cairo_svg_surface_emit_composite_recording_pattern (output, surface,
1570 return _cairo_svg_surface_emit_composite_surface_pattern (output, surface,
1577 static cairo_status_t
1578 _cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface,
1579 cairo_solid_pattern_t *pattern,
1580 cairo_output_stream_t *style,
1581 cairo_bool_t is_stroke)
1583 _cairo_output_stream_printf (style, is_stroke ?
1584 "stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;":
1585 "fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;",
1586 pattern->color.red * 100.0,
1587 pattern->color.green * 100.0,
1588 pattern->color.blue * 100.0,
1589 pattern->color.alpha);
1591 return CAIRO_STATUS_SUCCESS;
1594 static cairo_status_t
1595 _cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface,
1596 cairo_surface_pattern_t *pattern,
1597 cairo_output_stream_t *style,
1598 cairo_bool_t is_stroke,
1599 const cairo_matrix_t *parent_matrix)
1601 cairo_svg_document_t *document = surface->document;
1602 cairo_status_t status;
1605 pattern_id = document->pattern_id++;
1606 status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs,
1607 surface, CAIRO_OPERATOR_SOURCE, pattern,
1608 pattern_id, parent_matrix, NULL);
1609 if (unlikely (status))
1612 _cairo_output_stream_printf (style,
1613 "%s:url(#pattern%d);",
1614 is_stroke ? "stroke" : "fill",
1617 return CAIRO_STATUS_SUCCESS;
1620 static cairo_status_t
1621 _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output,
1622 cairo_gradient_pattern_t const *pattern,
1623 double start_offset,
1624 cairo_bool_t reverse_stops,
1625 cairo_bool_t emulate_reflect)
1627 cairo_gradient_stop_t *stops;
1629 unsigned int n_stops;
1632 if (pattern->n_stops < 1)
1633 return CAIRO_STATUS_SUCCESS;
1635 if (pattern->n_stops == 1) {
1636 _cairo_output_stream_printf (output,
1637 "<stop offset=\"%f\" style=\""
1638 "stop-color:rgb(%f%%,%f%%,%f%%);"
1639 "stop-opacity:%f;\"/>\n",
1640 pattern->stops[0].offset,
1641 pattern->stops[0].color.red * 100.0,
1642 pattern->stops[0].color.green * 100.0,
1643 pattern->stops[0].color.blue * 100.0,
1644 pattern->stops[0].color.alpha);
1645 return CAIRO_STATUS_SUCCESS;
1648 if (emulate_reflect || reverse_stops) {
1649 n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops;
1650 stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t));
1651 if (unlikely (stops == NULL))
1652 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1654 for (i = 0; i < pattern->n_stops; i++) {
1655 if (reverse_stops) {
1656 stops[i] = pattern->stops[pattern->n_stops - i - 1];
1657 stops[i].offset = 1.0 - stops[i].offset;
1659 stops[i] = pattern->stops[i];
1660 if (emulate_reflect) {
1661 stops[i].offset /= 2;
1662 if (i > 0 && i < (pattern->n_stops - 1)) {
1663 if (reverse_stops) {
1664 stops[i + pattern->n_stops - 1] = pattern->stops[i];
1665 stops[i + pattern->n_stops - 1].offset =
1666 0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset;
1668 stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1];
1669 stops[i + pattern->n_stops - 1].offset =
1670 1 - 0.5 * stops[i + pattern->n_stops - 1].offset;
1676 n_stops = pattern->n_stops;
1677 stops = pattern->stops;
1680 if (start_offset >= 0.0)
1681 for (i = 0; i < n_stops; i++) {
1682 offset = start_offset + (1 - start_offset ) * stops[i].offset;
1683 _cairo_output_stream_printf (output,
1684 "<stop offset=\"%f\" style=\""
1685 "stop-color:rgb(%f%%,%f%%,%f%%);"
1686 "stop-opacity:%f;\"/>\n",
1688 stops[i].color.red * 100.0,
1689 stops[i].color.green * 100.0,
1690 stops[i].color.blue * 100.0,
1691 stops[i].color.alpha);
1694 cairo_bool_t found = FALSE;
1695 unsigned int offset_index;
1696 cairo_color_stop_t offset_color_start, offset_color_stop;
1697 _cairo_color_init_rgba ((cairo_color_t *) &offset_color_start, 0, 0, 0, 0);
1699 for (i = 0; i < n_stops; i++) {
1700 if (stops[i].offset >= -start_offset) {
1702 if (stops[i].offset != stops[i-1].offset) {
1704 cairo_color_stop_t *color0, *color1;
1706 x0 = stops[i-1].offset;
1707 x1 = stops[i].offset;
1708 color0 = &stops[i-1].color;
1709 color1 = &stops[i].color;
1710 offset_color_start.red = color0->red + (color1->red - color0->red)
1711 * (-start_offset - x0) / (x1 - x0);
1712 offset_color_start.green = color0->green + (color1->green - color0->green)
1713 * (-start_offset - x0) / (x1 - x0);
1714 offset_color_start.blue = color0->blue + (color1->blue - color0->blue)
1715 * (-start_offset - x0) / (x1 - x0);
1716 offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha)
1717 * (-start_offset - x0) / (x1 - x0);
1718 offset_color_stop = offset_color_start;
1720 offset_color_stop = stops[i-1].color;
1721 offset_color_start = stops[i].color;
1724 offset_color_stop = offset_color_start = stops[i].color;
1732 offset_index = n_stops - 1;
1733 offset_color_stop = offset_color_start = stops[offset_index].color;
1736 _cairo_output_stream_printf (output,
1737 "<stop offset=\"0\" style=\""
1738 "stop-color:rgb(%f%%,%f%%,%f%%);"
1739 "stop-opacity:%f;\"/>\n",
1740 offset_color_start.red * 100.0,
1741 offset_color_start.green * 100.0,
1742 offset_color_start.blue * 100.0,
1743 offset_color_start.alpha);
1744 for (i = offset_index; i < n_stops; i++) {
1745 _cairo_output_stream_printf (output,
1746 "<stop offset=\"%f\" style=\""
1747 "stop-color:rgb(%f%%,%f%%,%f%%);"
1748 "stop-opacity:%f;\"/>\n",
1749 stops[i].offset + start_offset,
1750 stops[i].color.red * 100.0,
1751 stops[i].color.green * 100.0,
1752 stops[i].color.blue * 100.0,
1753 stops[i].color.alpha);
1755 for (i = 0; i < offset_index; i++) {
1756 _cairo_output_stream_printf (output,
1757 "<stop offset=\"%f\" style=\""
1758 "stop-color:rgb(%f%%,%f%%,%f%%);"
1759 "stop-opacity:%f;\"/>\n",
1760 1.0 + stops[i].offset + start_offset,
1761 stops[i].color.red * 100.0,
1762 stops[i].color.green * 100.0,
1763 stops[i].color.blue * 100.0,
1764 stops[i].color.alpha);
1767 _cairo_output_stream_printf (output,
1768 "<stop offset=\"1\" style=\""
1769 "stop-color:rgb(%f%%,%f%%,%f%%);"
1770 "stop-opacity:%f;\"/>\n",
1771 offset_color_stop.red * 100.0,
1772 offset_color_stop.green * 100.0,
1773 offset_color_stop.blue * 100.0,
1774 offset_color_stop.alpha);
1778 if (reverse_stops || emulate_reflect)
1781 return CAIRO_STATUS_SUCCESS;
1785 _cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output,
1786 cairo_pattern_t *pattern)
1788 switch (pattern->extend) {
1789 case CAIRO_EXTEND_REPEAT:
1790 _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" ");
1792 case CAIRO_EXTEND_REFLECT:
1793 _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" ");
1795 case CAIRO_EXTEND_NONE:
1796 case CAIRO_EXTEND_PAD:
1801 static cairo_status_t
1802 _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface,
1803 cairo_linear_pattern_t *pattern,
1804 cairo_output_stream_t *style,
1805 cairo_bool_t is_stroke,
1806 const cairo_matrix_t *parent_matrix)
1808 cairo_svg_document_t *document = surface->document;
1810 cairo_status_t status;
1812 p2u = pattern->base.base.matrix;
1813 status = cairo_matrix_invert (&p2u);
1814 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1815 assert (status == CAIRO_STATUS_SUCCESS);
1817 _cairo_output_stream_printf (document->xml_node_defs,
1818 "<linearGradient id=\"linear%d\" "
1819 "gradientUnits=\"userSpaceOnUse\" "
1820 "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
1821 document->linear_pattern_id,
1822 pattern->pd1.x, pattern->pd1.y,
1823 pattern->pd2.x, pattern->pd2.y);
1825 _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
1826 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1827 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1829 status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
1830 &pattern->base, 0.0,
1832 if (unlikely (status))
1835 _cairo_output_stream_printf (document->xml_node_defs,
1836 "</linearGradient>\n");
1838 _cairo_output_stream_printf (style,
1839 "%s:url(#linear%d);",
1840 is_stroke ? "stroke" : "fill",
1841 document->linear_pattern_id);
1843 document->linear_pattern_id++;
1845 return CAIRO_STATUS_SUCCESS;
1848 static cairo_status_t
1849 _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface,
1850 cairo_radial_pattern_t *pattern,
1851 cairo_output_stream_t *style,
1852 cairo_bool_t is_stroke,
1853 const cairo_matrix_t *parent_matrix)
1855 cairo_svg_document_t *document = surface->document;
1857 cairo_extend_t extend;
1858 double x0, y0, x1, y1, r0, r1;
1860 cairo_bool_t reverse_stops;
1861 cairo_status_t status;
1862 cairo_circle_double_t *c0, *c1;
1864 extend = pattern->base.base.extend;
1866 if (pattern->cd1.radius < pattern->cd2.radius) {
1869 reverse_stops = FALSE;
1873 reverse_stops = TRUE;
1883 p2u = pattern->base.base.matrix;
1884 status = cairo_matrix_invert (&p2u);
1885 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1886 assert (status == CAIRO_STATUS_SUCCESS);
1889 unsigned int n_stops = pattern->base.n_stops;
1891 _cairo_output_stream_printf (document->xml_node_defs,
1892 "<radialGradient id=\"radial%d\" "
1893 "gradientUnits=\"userSpaceOnUse\" "
1894 "cx=\"%f\" cy=\"%f\" "
1895 "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1896 document->radial_pattern_id,
1899 _cairo_svg_surface_emit_transform (document->xml_node_defs,
1900 "gradientTransform",
1901 &p2u, parent_matrix);
1902 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1904 if (extend == CAIRO_EXTEND_NONE || n_stops < 1)
1905 _cairo_output_stream_printf (document->xml_node_defs,
1906 "<stop offset=\"0\" style=\""
1907 "stop-color:rgb(0%%,0%%,0%%);"
1908 "stop-opacity:0;\"/>\n");
1910 _cairo_output_stream_printf (document->xml_node_defs,
1911 "<stop offset=\"0\" style=\""
1912 "stop-color:rgb(%f%%,%f%%,%f%%);"
1913 "stop-opacity %f;\"/>\n",
1914 pattern->base.stops[0].color.red * 100.0,
1915 pattern->base.stops[0].color.green * 100.0,
1916 pattern->base.stops[0].color.blue * 100.0,
1917 pattern->base.stops[0].color.alpha);
1919 _cairo_output_stream_printf (document->xml_node_defs,
1920 "<stop offset=\"0\" style=\""
1921 "stop-color:rgb(%f%%,%f%%,%f%%);"
1922 "stop-opacity:%f;\"/>\n",
1923 pattern->base.stops[n_stops - 1].color.red * 100.0,
1924 pattern->base.stops[n_stops - 1].color.green * 100.0,
1925 pattern->base.stops[n_stops - 1].color.blue * 100.0,
1926 pattern->base.stops[n_stops - 1].color.alpha);
1930 double offset, r, x, y;
1931 cairo_bool_t emulate_reflect = FALSE;
1933 fx = (r1 * x0 - r0 * x1) / (r1 - r0);
1934 fy = (r1 * y0 - r0 * y1) / (r1 - r0);
1936 /* SVG doesn't support the inner circle and use instead a gradient focal.
1937 * That means we need to emulate the cairo behaviour by processing the
1938 * cairo gradient stops.
1939 * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
1940 * it's just a matter of stop position translation and calculation of
1941 * the corresponding SVG radial gradient focal.
1942 * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
1943 * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
1944 * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
1945 * list that maps to the original cairo stop list.
1947 if ((extend == CAIRO_EXTEND_REFLECT
1948 || extend == CAIRO_EXTEND_REPEAT)
1952 if (extend == CAIRO_EXTEND_REFLECT) {
1954 emulate_reflect = TRUE;
1957 offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
1960 /* New position of outer circle. */
1961 x = r * (x1 - fx) / r_org + fx;
1962 y = r * (y1 - fy) / r_org + fy;
1972 _cairo_output_stream_printf (document->xml_node_defs,
1973 "<radialGradient id=\"radial%d\" "
1974 "gradientUnits=\"userSpaceOnUse\" "
1975 "cx=\"%f\" cy=\"%f\" "
1976 "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1977 document->radial_pattern_id,
1981 if (emulate_reflect)
1982 _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
1984 _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
1985 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1986 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1988 /* To support cairo's EXTEND_NONE, (for which SVG has no similar
1989 * notion), we add transparent color stops on either end of the
1990 * user-provided stops. */
1991 if (extend == CAIRO_EXTEND_NONE) {
1992 _cairo_output_stream_printf (document->xml_node_defs,
1993 "<stop offset=\"0\" style=\""
1994 "stop-color:rgb(0%%,0%%,0%%);"
1995 "stop-opacity:0;\"/>\n");
1997 _cairo_output_stream_printf (document->xml_node_defs,
1998 "<stop offset=\"%f\" style=\""
1999 "stop-color:rgb(0%%,0%%,0%%);"
2000 "stop-opacity:0;\"/>\n",
2003 status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
2004 &pattern->base, offset,
2007 if (unlikely (status))
2010 if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
2011 _cairo_output_stream_printf (document->xml_node_defs,
2012 "<stop offset=\"1.0\" style=\""
2013 "stop-color:rgb(0%%,0%%,0%%);"
2014 "stop-opacity:0;\"/>\n");
2017 _cairo_output_stream_printf (document->xml_node_defs,
2018 "</radialGradient>\n");
2020 _cairo_output_stream_printf (style,
2021 "%s:url(#radial%d);",
2022 is_stroke ? "stroke" : "fill",
2023 document->radial_pattern_id);
2025 document->radial_pattern_id++;
2027 return CAIRO_STATUS_SUCCESS;
2030 static cairo_status_t
2031 _cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface,
2032 const cairo_pattern_t *pattern,
2033 cairo_output_stream_t *output,
2034 cairo_bool_t is_stroke,
2035 const cairo_matrix_t *parent_matrix)
2037 switch (pattern->type) {
2038 case CAIRO_PATTERN_TYPE_SOLID:
2039 return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern,
2042 case CAIRO_PATTERN_TYPE_SURFACE:
2043 return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern,
2044 output, is_stroke, parent_matrix);
2046 case CAIRO_PATTERN_TYPE_LINEAR:
2047 return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern,
2048 output, is_stroke, parent_matrix);
2050 case CAIRO_PATTERN_TYPE_RADIAL:
2051 return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern,
2052 output, is_stroke, parent_matrix);
2054 case CAIRO_PATTERN_TYPE_MESH:
2055 case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
2058 return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
2061 static cairo_status_t
2062 _cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output,
2063 cairo_svg_surface_t *surface,
2064 cairo_operator_t op,
2065 const cairo_pattern_t *source,
2066 cairo_fill_rule_t fill_rule,
2067 const cairo_matrix_t *parent_matrix)
2069 _cairo_output_stream_printf (output,
2071 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
2072 "evenodd" : "nonzero");
2073 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2074 return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix);
2077 static cairo_status_t
2078 _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output,
2079 cairo_svg_surface_t *surface,
2080 cairo_operator_t op,
2081 const cairo_pattern_t *source,
2082 const cairo_stroke_style_t *stroke_style,
2083 const cairo_matrix_t *parent_matrix)
2085 cairo_status_t status;
2086 const char *line_cap, *line_join;
2089 switch (stroke_style->line_cap) {
2090 case CAIRO_LINE_CAP_BUTT:
2093 case CAIRO_LINE_CAP_ROUND:
2096 case CAIRO_LINE_CAP_SQUARE:
2097 line_cap = "square";
2103 switch (stroke_style->line_join) {
2104 case CAIRO_LINE_JOIN_MITER:
2105 line_join = "miter";
2107 case CAIRO_LINE_JOIN_ROUND:
2108 line_join = "round";
2110 case CAIRO_LINE_JOIN_BEVEL:
2111 line_join = "bevel";
2117 _cairo_output_stream_printf (output,
2119 "stroke-linecap:%s;"
2120 "stroke-linejoin:%s;",
2121 stroke_style->line_width,
2125 status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix);
2126 if (unlikely (status))
2129 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2131 if (stroke_style->num_dashes > 0) {
2132 _cairo_output_stream_printf (output, "stroke-dasharray:");
2133 for (i = 0; i < stroke_style->num_dashes; i++) {
2134 _cairo_output_stream_printf (output, "%f",
2135 stroke_style->dash[i]);
2136 if (i + 1 < stroke_style->num_dashes)
2137 _cairo_output_stream_printf (output, ",");
2139 _cairo_output_stream_printf (output, ";");
2141 if (stroke_style->dash_offset != 0.0) {
2142 _cairo_output_stream_printf (output,
2143 "stroke-dashoffset:%f;",
2144 stroke_style->dash_offset);
2148 _cairo_output_stream_printf (output,
2149 "stroke-miterlimit:%f;",
2150 stroke_style->miter_limit);
2152 return CAIRO_STATUS_SUCCESS;
2155 static cairo_int_status_t
2156 _cairo_svg_surface_fill_stroke (void *abstract_surface,
2157 cairo_operator_t fill_op,
2158 const cairo_pattern_t *fill_source,
2159 cairo_fill_rule_t fill_rule,
2160 double fill_tolerance,
2161 cairo_antialias_t fill_antialias,
2162 const cairo_path_fixed_t*path,
2163 cairo_operator_t stroke_op,
2164 const cairo_pattern_t *stroke_source,
2165 const cairo_stroke_style_t *stroke_style,
2166 const cairo_matrix_t *stroke_ctm,
2167 const cairo_matrix_t *stroke_ctm_inverse,
2168 double stroke_tolerance,
2169 cairo_antialias_t stroke_antialias,
2170 const cairo_clip_t *clip)
2172 cairo_svg_surface_t *surface = abstract_surface;
2173 cairo_status_t status;
2175 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2176 if (unlikely (status))
2179 _cairo_output_stream_printf (surface->xml_node, "<path style=\"");
2180 status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, fill_op,
2181 fill_source, fill_rule, stroke_ctm_inverse);
2182 if (unlikely (status))
2185 status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op,
2186 stroke_source, stroke_style, stroke_ctm_inverse);
2187 if (unlikely (status))
2190 _cairo_output_stream_printf (surface->xml_node, "\" ");
2192 _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse);
2194 _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL);
2195 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2197 return CAIRO_STATUS_SUCCESS;
2200 static cairo_int_status_t
2201 _cairo_svg_surface_fill (void *abstract_surface,
2202 cairo_operator_t op,
2203 const cairo_pattern_t *source,
2204 const cairo_path_fixed_t*path,
2205 cairo_fill_rule_t fill_rule,
2207 cairo_antialias_t antialias,
2208 const cairo_clip_t *clip)
2210 cairo_svg_surface_t *surface = abstract_surface;
2211 cairo_status_t status;
2213 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2214 return _cairo_svg_surface_analyze_operation (surface, op, source);
2216 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2218 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2219 if (unlikely (status))
2222 _cairo_output_stream_printf (surface->xml_node, "<path style=\" stroke:none;");
2223 status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, op, source, fill_rule, NULL);
2224 if (unlikely (status))
2227 _cairo_output_stream_printf (surface->xml_node, "\" ");
2229 _cairo_svg_surface_emit_path (surface->xml_node, path, NULL);
2231 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2233 return CAIRO_STATUS_SUCCESS;
2237 _cairo_svg_surface_get_extents (void *abstract_surface,
2238 cairo_rectangle_int_t *rectangle)
2240 cairo_svg_surface_t *surface = abstract_surface;
2245 /* XXX: The conversion to integers here is pretty bogus, (not to
2246 * mention the arbitrary limitation of width to a short(!). We
2247 * may need to come up with a better interface for get_size.
2249 rectangle->width = ceil (surface->width);
2250 rectangle->height = ceil (surface->height);
2255 static cairo_status_t
2256 _cairo_svg_surface_emit_paint (cairo_output_stream_t *output,
2257 cairo_svg_surface_t *surface,
2258 cairo_operator_t op,
2259 const cairo_pattern_t *source,
2260 const cairo_pattern_t *mask_source,
2261 const char *extra_attributes)
2263 cairo_status_t status;
2265 if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
2266 source->extend == CAIRO_EXTEND_NONE)
2267 return _cairo_svg_surface_emit_composite_pattern (output,
2270 (cairo_surface_pattern_t *) source,
2272 mask_source ? &mask_source->matrix :NULL,
2275 _cairo_output_stream_printf (output,
2276 "<rect x=\"0\" y=\"0\" "
2277 "width=\"%f\" height=\"%f\" "
2279 surface->width, surface->height);
2280 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2281 status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL);
2282 if (unlikely (status))
2285 _cairo_output_stream_printf (output, "stroke:none;\"");
2287 if (extra_attributes)
2288 _cairo_output_stream_printf (output, " %s", extra_attributes);
2290 _cairo_output_stream_printf (output, "/>\n");
2292 return CAIRO_STATUS_SUCCESS;
2295 static cairo_int_status_t
2296 _cairo_svg_surface_paint (void *abstract_surface,
2297 cairo_operator_t op,
2298 const cairo_pattern_t *source,
2299 const cairo_clip_t *clip)
2301 cairo_status_t status;
2302 cairo_svg_surface_t *surface = abstract_surface;
2304 /* Emulation of clear and source operators, when no clipping region
2305 * is defined. We just delete existing content of surface root node,
2306 * and exit early if operator is clear.
2308 if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) &&
2311 switch (surface->paginated_mode) {
2312 case CAIRO_PAGINATED_MODE_FALLBACK:
2314 case CAIRO_PAGINATED_MODE_ANALYZE:
2315 return CAIRO_STATUS_SUCCESS;
2317 case CAIRO_PAGINATED_MODE_RENDER:
2318 status = _cairo_output_stream_destroy (surface->xml_node);
2319 if (unlikely (status)) {
2320 surface->xml_node = NULL;
2324 surface->xml_node = _cairo_memory_stream_create ();
2325 if (_cairo_output_stream_get_status (surface->xml_node)) {
2326 status = _cairo_output_stream_destroy (surface->xml_node);
2327 surface->xml_node = NULL;
2331 if (op == CAIRO_OPERATOR_CLEAR) {
2332 if (surface->content == CAIRO_CONTENT_COLOR) {
2333 _cairo_output_stream_printf (surface->xml_node,
2335 "width=\"%f\" height=\"%f\" "
2336 "style=\"opacity:1;"
2338 "fill:rgb(0,0,0);\"/>\n",
2339 surface->width, surface->height);
2341 return CAIRO_STATUS_SUCCESS;
2346 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2347 return _cairo_svg_surface_analyze_operation (surface, op, source);
2349 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2352 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2353 if (unlikely (status))
2356 return _cairo_svg_surface_emit_paint (surface->xml_node,
2357 surface, op, source, 0, NULL);
2360 static cairo_int_status_t
2361 _cairo_svg_surface_mask (void *abstract_surface,
2362 cairo_operator_t op,
2363 const cairo_pattern_t *source,
2364 const cairo_pattern_t *mask,
2365 const cairo_clip_t *clip)
2367 cairo_status_t status;
2368 cairo_svg_surface_t *surface = abstract_surface;
2369 cairo_svg_document_t *document = surface->document;
2370 cairo_output_stream_t *mask_stream;
2372 cairo_bool_t discard_filter = FALSE;
2373 unsigned int mask_id;
2375 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2376 cairo_status_t source_status, mask_status;
2378 source_status = _cairo_svg_surface_analyze_operation (surface, op, source);
2379 if (_cairo_status_is_error (source_status))
2380 return source_status;
2382 if (mask->has_component_alpha) {
2383 mask_status = CAIRO_INT_STATUS_UNSUPPORTED;
2385 mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask);
2386 if (_cairo_status_is_error (mask_status))
2390 return _cairo_analysis_surface_merge_status (source_status,
2394 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2395 assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask));
2397 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2398 if (unlikely (status))
2401 if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
2402 const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask;
2403 cairo_content_t content = surface_pattern->surface->content;
2404 if (content == CAIRO_CONTENT_ALPHA)
2405 discard_filter = TRUE;
2408 if (!discard_filter)
2409 _cairo_svg_surface_emit_alpha_filter (document);
2411 /* _cairo_svg_surface_emit_paint() will output a pattern definition to
2412 * document->xml_node_defs so we need to write the mask element to
2413 * a temporary stream and then copy that to xml_node_defs. */
2414 mask_stream = _cairo_memory_stream_create ();
2415 if (_cairo_output_stream_get_status (mask_stream))
2416 return _cairo_output_stream_destroy (mask_stream);
2418 mask_id = _cairo_svg_document_allocate_mask_id (document);
2420 _cairo_output_stream_printf (mask_stream,
2421 "<mask id=\"mask%d\">\n"
2424 discard_filter ? "" : " <g filter=\"url(#alpha)\">\n");
2425 status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL);
2426 if (unlikely (status)) {
2427 cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream);
2432 _cairo_output_stream_printf (mask_stream,
2435 discard_filter ? "" : " </g>\n");
2436 _cairo_memory_stream_copy (mask_stream, document->xml_node_defs);
2438 status = _cairo_output_stream_destroy (mask_stream);
2439 if (unlikely (status))
2442 snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"",
2444 status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer);
2445 if (unlikely (status))
2448 return CAIRO_STATUS_SUCCESS;
2451 static cairo_int_status_t
2452 _cairo_svg_surface_stroke (void *abstract_dst,
2453 cairo_operator_t op,
2454 const cairo_pattern_t *source,
2455 const cairo_path_fixed_t*path,
2456 const cairo_stroke_style_t *stroke_style,
2457 const cairo_matrix_t *ctm,
2458 const cairo_matrix_t *ctm_inverse,
2460 cairo_antialias_t antialias,
2461 const cairo_clip_t *clip)
2463 cairo_svg_surface_t *surface = abstract_dst;
2464 cairo_status_t status;
2466 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2467 return _cairo_svg_surface_analyze_operation (surface, op, source);
2469 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2471 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2472 if (unlikely (status))
2475 _cairo_output_stream_printf (surface->xml_node, "<path style=\"fill:none;");
2476 status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, op,
2477 source, stroke_style, ctm_inverse);
2478 if (unlikely (status))
2481 _cairo_output_stream_printf (surface->xml_node, "\" ");
2483 _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse);
2485 _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL);
2486 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2488 return CAIRO_STATUS_SUCCESS;
2491 static cairo_int_status_t
2492 _cairo_svg_surface_show_glyphs (void *abstract_surface,
2493 cairo_operator_t op,
2494 const cairo_pattern_t *pattern,
2495 cairo_glyph_t *glyphs,
2497 cairo_scaled_font_t *scaled_font,
2498 const cairo_clip_t *clip)
2500 cairo_svg_surface_t *surface = abstract_surface;
2501 cairo_svg_document_t *document = surface->document;
2502 cairo_path_fixed_t path;
2503 cairo_int_status_t status;
2504 cairo_scaled_font_subsets_glyph_t subset_glyph;
2507 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2508 return _cairo_svg_surface_analyze_operation (surface, op, pattern);
2510 assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
2512 if (num_glyphs <= 0)
2513 return CAIRO_STATUS_SUCCESS;
2515 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2516 if (unlikely (status))
2519 /* FIXME it's probably possible to apply a pattern of a gradient to
2520 * a group of symbols, but I don't know how yet. Gradients or patterns
2521 * are translated by x and y properties of use element. */
2522 if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
2525 _cairo_output_stream_printf (surface->xml_node, "<g style=\"");
2526 status = _cairo_svg_surface_emit_pattern (surface, pattern,
2527 surface->xml_node, FALSE, NULL);
2528 if (unlikely (status))
2531 _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op);
2533 _cairo_output_stream_printf (surface->xml_node, "\">\n");
2535 for (i = 0; i < num_glyphs; i++) {
2536 status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets,
2537 scaled_font, glyphs[i].index,
2540 if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
2541 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2548 if (unlikely (status))
2551 _cairo_output_stream_printf (surface->xml_node,
2552 " <use xlink:href=\"#glyph%d-%d\" "
2553 "x=\"%f\" y=\"%f\"/>\n",
2554 subset_glyph.font_id,
2555 subset_glyph.subset_glyph_index,
2556 glyphs[i].x, glyphs[i].y);
2559 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2561 return CAIRO_STATUS_SUCCESS;
2564 _cairo_path_fixed_init (&path);
2566 status = _cairo_scaled_font_glyph_path (scaled_font,
2567 (cairo_glyph_t *) glyphs,
2570 if (unlikely (status)) {
2571 _cairo_path_fixed_fini (&path);
2575 status = _cairo_svg_surface_fill (abstract_surface, op, pattern,
2576 &path, CAIRO_FILL_RULE_WINDING,
2577 0.0, CAIRO_ANTIALIAS_SUBPIXEL,
2580 _cairo_path_fixed_fini (&path);
2586 _cairo_svg_surface_get_font_options (void *abstract_surface,
2587 cairo_font_options_t *options)
2589 _cairo_font_options_init_default (options);
2591 cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
2592 cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
2593 cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
2594 _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
2598 static const char **
2599 _cairo_svg_surface_get_supported_mime_types (void *abstract_surface)
2601 return _cairo_svg_supported_mime_types;
2604 static const cairo_surface_backend_t cairo_svg_surface_backend = {
2605 CAIRO_SURFACE_TYPE_SVG,
2606 _cairo_svg_surface_finish,
2608 _cairo_default_context_create,
2610 NULL, /* create_similar: handled by wrapper */
2611 NULL, /* create_similar_image */
2612 NULL, /* map to image */
2613 NULL, /* unmap image */
2615 _cairo_surface_default_source,
2616 NULL, /* acquire_source_image */
2617 NULL, /* release_source_image */
2618 NULL, /* snapshot */
2620 _cairo_svg_surface_copy_page,
2621 _cairo_svg_surface_show_page,
2623 _cairo_svg_surface_get_extents,
2624 _cairo_svg_surface_get_font_options,
2627 NULL, /* mark dirty rectangle */
2629 _cairo_svg_surface_paint,
2630 _cairo_svg_surface_mask,
2631 _cairo_svg_surface_stroke,
2632 _cairo_svg_surface_fill,
2633 _cairo_svg_surface_fill_stroke,
2634 _cairo_svg_surface_show_glyphs,
2635 NULL, /* has_show_text_glyphs */
2636 NULL, /* show_text_glyphs */
2637 _cairo_svg_surface_get_supported_mime_types,
2640 static cairo_status_t
2641 _cairo_svg_document_create (cairo_output_stream_t *output_stream,
2644 cairo_svg_version_t version,
2645 cairo_svg_document_t **document_out)
2647 cairo_svg_document_t *document;
2648 cairo_status_t status, status_ignored;
2650 if (output_stream->status)
2651 return output_stream->status;
2653 document = malloc (sizeof (cairo_svg_document_t));
2654 if (unlikely (document == NULL))
2655 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2657 /* The use of defs for font glyphs imposes no per-subset limit. */
2658 document->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
2659 if (unlikely (document->font_subsets == NULL)) {
2660 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2661 goto CLEANUP_DOCUMENT;
2664 document->output_stream = output_stream;
2665 document->refcount = 1;
2666 document->owner = NULL;
2667 document->finished = FALSE;
2668 document->width = width;
2669 document->height = height;
2671 document->linear_pattern_id = 0;
2672 document->radial_pattern_id = 0;
2673 document->pattern_id = 0;
2674 document->filter_id = 0;
2675 document->clip_id = 0;
2676 document->mask_id = 0;
2678 document->xml_node_defs = _cairo_memory_stream_create ();
2679 status = _cairo_output_stream_get_status (document->xml_node_defs);
2680 if (unlikely (status))
2681 goto CLEANUP_NODE_DEFS;
2683 document->xml_node_glyphs = _cairo_memory_stream_create ();
2684 status = _cairo_output_stream_get_status (document->xml_node_glyphs);
2685 if (unlikely (status))
2686 goto CLEANUP_NODE_GLYPHS;
2688 document->alpha_filter = FALSE;
2690 document->svg_version = version;
2692 *document_out = document;
2693 return CAIRO_STATUS_SUCCESS;
2695 CLEANUP_NODE_GLYPHS:
2696 status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs);
2698 status_ignored = _cairo_output_stream_destroy (document->xml_node_defs);
2699 _cairo_scaled_font_subsets_destroy (document->font_subsets);
2705 static cairo_svg_document_t *
2706 _cairo_svg_document_reference (cairo_svg_document_t *document)
2708 document->refcount++;
2714 _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document)
2716 return document->mask_id++;
2719 static cairo_status_t
2720 _cairo_svg_document_destroy (cairo_svg_document_t *document)
2722 cairo_status_t status;
2724 document->refcount--;
2725 if (document->refcount > 0)
2726 return CAIRO_STATUS_SUCCESS;
2728 status = _cairo_svg_document_finish (document);
2735 static cairo_status_t
2736 _cairo_svg_document_finish (cairo_svg_document_t *document)
2738 cairo_status_t status, status2;
2739 cairo_output_stream_t *output = document->output_stream;
2740 cairo_svg_page_t *page;
2743 if (document->finished)
2744 return CAIRO_STATUS_SUCCESS;
2747 * Should we add DOCTYPE?
2751 * http://tech.groups.yahoo.com/group/svg-developers/message/48562:
2752 * There's a bunch of issues, but just to pick a few:
2753 * - they'll give false positives.
2754 * - they'll give false negatives.
2755 * - they're namespace-unaware.
2756 * - they don't wildcard.
2757 * So when they say OK they really haven't checked anything, when
2758 * they say NOT OK they might be on crack, and like all
2759 * namespace-unaware things they're a dead branch of the XML tree.
2761 * http://jwatt.org/svg/authoring/:
2762 * Unfortunately the SVG DTDs are a source of so many issues that the
2763 * SVG WG has decided not to write one for the upcoming SVG 1.2
2764 * standard. In fact SVG WG members are even telling people not to use
2765 * a DOCTYPE declaration in SVG 1.0 and 1.1 documents.
2768 _cairo_output_stream_printf (output,
2769 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2770 "<svg xmlns=\"http://www.w3.org/2000/svg\" "
2771 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
2772 "width=\"%fpt\" height=\"%fpt\" "
2773 "viewBox=\"0 0 %f %f\" version=\"%s\">\n",
2774 document->width, document->height,
2775 document->width, document->height,
2776 _cairo_svg_internal_version_strings [document->svg_version]);
2778 status = _cairo_svg_document_emit_font_subsets (document);
2780 if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 ||
2781 _cairo_memory_stream_length (document->xml_node_defs) > 0) {
2782 _cairo_output_stream_printf (output, "<defs>\n");
2783 if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
2784 _cairo_output_stream_printf (output, "<g>\n");
2785 _cairo_memory_stream_copy (document->xml_node_glyphs, output);
2786 _cairo_output_stream_printf (output, "</g>\n");
2788 _cairo_memory_stream_copy (document->xml_node_defs, output);
2789 _cairo_output_stream_printf (output, "</defs>\n");
2792 if (document->owner != NULL) {
2793 cairo_svg_surface_t *surface;
2795 surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner);
2796 if (surface->xml_node != NULL &&
2797 _cairo_memory_stream_length (surface->xml_node) > 0) {
2798 if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) {
2799 if (status == CAIRO_STATUS_SUCCESS)
2800 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2804 if (surface->page_set.num_elements > 1 &&
2805 _cairo_svg_version_has_page_set_support (document->svg_version)) {
2806 _cairo_output_stream_printf (output, "<pageSet>\n");
2807 for (i = 0; i < surface->page_set.num_elements; i++) {
2808 page = _cairo_array_index (&surface->page_set, i);
2809 _cairo_output_stream_printf (output, "<page>\n");
2810 _cairo_output_stream_printf (output,
2811 "<g id=\"surface%d\">\n",
2813 _cairo_memory_stream_copy (page->xml_node, output);
2814 _cairo_output_stream_printf (output, "</g>\n</page>\n");
2816 _cairo_output_stream_printf (output, "</pageSet>\n");
2817 } else if (surface->page_set.num_elements > 0) {
2818 page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
2819 _cairo_output_stream_printf (output,
2820 "<g id=\"surface%d\">\n",
2822 _cairo_memory_stream_copy (page->xml_node, output);
2823 _cairo_output_stream_printf (output, "</g>\n");
2827 _cairo_output_stream_printf (output, "</svg>\n");
2829 status2 = _cairo_output_stream_destroy (document->xml_node_glyphs);
2830 if (status == CAIRO_STATUS_SUCCESS)
2833 status2 = _cairo_output_stream_destroy (document->xml_node_defs);
2834 if (status == CAIRO_STATUS_SUCCESS)
2837 status2 = _cairo_output_stream_destroy (output);
2838 if (status == CAIRO_STATUS_SUCCESS)
2841 document->finished = TRUE;
2847 _cairo_svg_surface_set_paginated_mode (void *abstract_surface,
2848 cairo_paginated_mode_t paginated_mode)
2850 cairo_svg_surface_t *surface = abstract_surface;
2852 surface->paginated_mode = paginated_mode;
2856 _cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface)
2858 cairo_svg_surface_t *surface = abstract_surface;
2859 cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
2861 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) {
2862 status = _cairo_svg_surface_analyze_operator (surface,
2863 CAIRO_OPERATOR_SOURCE);
2866 return status == CAIRO_INT_STATUS_SUCCESS;
2869 static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
2870 NULL /*_cairo_svg_surface_start_page*/,
2871 _cairo_svg_surface_set_paginated_mode,
2872 NULL, /* _cairo_svg_surface_set_bounding_box */
2873 NULL, /* _cairo_svg_surface_set_fallback_images_required */
2874 _cairo_svg_surface_supports_fine_grained_fallbacks,