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;
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);
538 return _cairo_surface_create_in_error (status);
541 static cairo_surface_t *
542 _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
545 cairo_svg_version_t version)
547 cairo_svg_document_t *document = NULL; /* silence compiler */
548 cairo_surface_t *surface;
549 cairo_status_t status;
551 status = _cairo_svg_document_create (stream,
552 width, height, version,
554 if (unlikely (status)) {
555 surface = _cairo_surface_create_in_error (status);
556 /* consume the output stream on behalf of caller */
557 status = _cairo_output_stream_destroy (stream);
561 surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA,
563 if (surface->status) {
564 status = _cairo_svg_document_destroy (document);
568 document->owner = surface;
569 status = _cairo_svg_document_destroy (document);
570 /* the ref count should be 2 at this point */
571 assert (status == CAIRO_STATUS_SUCCESS);
576 static cairo_svg_page_t *
577 _cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
579 cairo_svg_page_t page;
580 cairo_output_stream_t *stream;
581 cairo_int_status_t status;
584 stream = _cairo_memory_stream_create ();
585 if (_cairo_output_stream_get_status (stream)) {
586 status = _cairo_output_stream_destroy (stream);
590 page.surface_id = surface->base.unique_id;
591 page.clip_level = surface->clip_level;
592 page.xml_node = surface->xml_node;
594 if (_cairo_array_append (&surface->page_set, &page)) {
595 status = _cairo_output_stream_destroy (stream);
599 surface->xml_node = stream;
600 surface->clip_level = 0;
601 for (i = 0; i < page.clip_level; i++)
602 _cairo_output_stream_printf (page.xml_node, "</g>\n");
604 _cairo_surface_clipper_reset (&surface->clipper);
606 return _cairo_array_index (&surface->page_set,
607 surface->page_set.num_elements - 1);
610 static cairo_int_status_t
611 _cairo_svg_surface_copy_page (void *abstract_surface)
613 cairo_svg_surface_t *surface = abstract_surface;
614 cairo_svg_page_t *page;
616 page = _cairo_svg_surface_store_page (surface);
617 if (unlikely (page == NULL))
618 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
620 _cairo_memory_stream_copy (page->xml_node, surface->xml_node);
622 return CAIRO_STATUS_SUCCESS;
625 static cairo_int_status_t
626 _cairo_svg_surface_show_page (void *abstract_surface)
628 cairo_svg_surface_t *surface = abstract_surface;
630 if (unlikely (_cairo_svg_surface_store_page (surface) == NULL))
631 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
633 return CAIRO_STATUS_SUCCESS;
637 _cairo_svg_surface_emit_transform (cairo_output_stream_t *output,
638 char const *attribute_str,
639 const cairo_matrix_t *object_matrix,
640 const cairo_matrix_t *parent_matrix)
642 cairo_matrix_t matrix = *object_matrix;
644 if (parent_matrix != NULL)
645 cairo_matrix_multiply (&matrix, &matrix, parent_matrix);
647 if (!_cairo_matrix_is_identity (&matrix))
648 _cairo_output_stream_printf (output,
649 "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"",
651 matrix.xx, matrix.yx,
652 matrix.xy, matrix.yy,
653 matrix.x0, matrix.y0);
657 cairo_output_stream_t *output;
658 const cairo_matrix_t *ctm_inverse;
661 static cairo_status_t
662 _cairo_svg_path_move_to (void *closure,
663 const cairo_point_t *point)
665 svg_path_info_t *info = closure;
666 double x = _cairo_fixed_to_double (point->x);
667 double y = _cairo_fixed_to_double (point->y);
669 if (info->ctm_inverse)
670 cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
672 _cairo_output_stream_printf (info->output, "M %f %f ", x, y);
674 return CAIRO_STATUS_SUCCESS;
677 static cairo_status_t
678 _cairo_svg_path_line_to (void *closure,
679 const cairo_point_t *point)
681 svg_path_info_t *info = closure;
682 double x = _cairo_fixed_to_double (point->x);
683 double y = _cairo_fixed_to_double (point->y);
685 if (info->ctm_inverse)
686 cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
688 _cairo_output_stream_printf (info->output, "L %f %f ", x, y);
690 return CAIRO_STATUS_SUCCESS;
693 static cairo_status_t
694 _cairo_svg_path_curve_to (void *closure,
695 const cairo_point_t *b,
696 const cairo_point_t *c,
697 const cairo_point_t *d)
699 svg_path_info_t *info = closure;
700 double bx = _cairo_fixed_to_double (b->x);
701 double by = _cairo_fixed_to_double (b->y);
702 double cx = _cairo_fixed_to_double (c->x);
703 double cy = _cairo_fixed_to_double (c->y);
704 double dx = _cairo_fixed_to_double (d->x);
705 double dy = _cairo_fixed_to_double (d->y);
707 if (info->ctm_inverse) {
708 cairo_matrix_transform_point (info->ctm_inverse, &bx, &by);
709 cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy);
710 cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy);
713 _cairo_output_stream_printf (info->output,
714 "C %f %f %f %f %f %f ",
715 bx, by, cx, cy, dx, dy);
717 return CAIRO_STATUS_SUCCESS;
720 static cairo_status_t
721 _cairo_svg_path_close_path (void *closure)
723 svg_path_info_t *info = closure;
725 _cairo_output_stream_printf (info->output, "Z ");
727 return CAIRO_STATUS_SUCCESS;
731 _cairo_svg_surface_emit_path (cairo_output_stream_t *output,
732 const cairo_path_fixed_t *path,
733 const cairo_matrix_t *ctm_inverse)
735 cairo_status_t status;
736 svg_path_info_t info;
738 _cairo_output_stream_printf (output, "d=\"");
740 info.output = output;
741 info.ctm_inverse = ctm_inverse;
742 status = _cairo_path_fixed_interpret (path,
743 _cairo_svg_path_move_to,
744 _cairo_svg_path_line_to,
745 _cairo_svg_path_curve_to,
746 _cairo_svg_path_close_path,
748 assert (status == CAIRO_STATUS_SUCCESS);
750 _cairo_output_stream_printf (output, "\"");
753 static cairo_int_status_t
754 _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document,
755 cairo_scaled_font_t *scaled_font,
756 unsigned long glyph_index)
758 cairo_scaled_glyph_t *scaled_glyph;
759 cairo_int_status_t status;
761 status = _cairo_scaled_glyph_lookup (scaled_font,
763 CAIRO_SCALED_GLYPH_INFO_METRICS|
764 CAIRO_SCALED_GLYPH_INFO_PATH,
766 if (unlikely (status))
769 _cairo_output_stream_printf (document->xml_node_glyphs,
770 "<path style=\"stroke:none;\" ");
772 _cairo_svg_surface_emit_path (document->xml_node_glyphs,
773 scaled_glyph->path, NULL);
775 _cairo_output_stream_printf (document->xml_node_glyphs,
781 static cairo_int_status_t
782 _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document,
783 cairo_scaled_font_t *scaled_font,
784 unsigned long glyph_index)
786 cairo_scaled_glyph_t *scaled_glyph;
787 cairo_image_surface_t *image;
788 cairo_status_t status;
793 status = _cairo_scaled_glyph_lookup (scaled_font,
795 CAIRO_SCALED_GLYPH_INFO_METRICS |
796 CAIRO_SCALED_GLYPH_INFO_SURFACE,
798 if (unlikely (status))
801 image = _cairo_image_surface_coerce_to_format (scaled_glyph->surface,
803 status = image->base.status;
804 if (unlikely (status))
807 _cairo_output_stream_printf (document->xml_node_glyphs, "<g");
808 _cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform",
809 &image->base.device_transform_inverse, NULL);
810 _cairo_output_stream_printf (document->xml_node_glyphs, ">/n");
812 for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) {
813 for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
814 uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
815 for (bit = 7; bit >= 0 && x < image->width; bit--, x++) {
816 if (output_byte & (1 << bit)) {
817 _cairo_output_stream_printf (document->xml_node_glyphs,
818 "<rect x=\"%d\" y=\"%d\" width=\"1\" height=\"1\"/>\n",
824 _cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n");
826 cairo_surface_destroy (&image->base);
828 return CAIRO_STATUS_SUCCESS;
831 static cairo_int_status_t
832 _cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
833 cairo_scaled_font_t *scaled_font,
834 unsigned long scaled_font_glyph_index,
835 unsigned int font_id,
836 unsigned int subset_glyph_index)
838 cairo_int_status_t status;
840 _cairo_output_stream_printf (document->xml_node_glyphs,
841 "<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n",
845 status = _cairo_svg_document_emit_outline_glyph_data (document,
847 scaled_font_glyph_index);
848 if (status == CAIRO_INT_STATUS_UNSUPPORTED)
849 status = _cairo_svg_document_emit_bitmap_glyph_data (document,
851 scaled_font_glyph_index);
852 if (unlikely (status))
855 _cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n");
857 return CAIRO_INT_STATUS_SUCCESS;
860 static cairo_int_status_t
861 _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
864 cairo_svg_document_t *document = closure;
865 cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
868 _cairo_scaled_font_freeze_cache (font_subset->scaled_font);
869 for (i = 0; i < font_subset->num_glyphs; i++) {
870 status = _cairo_svg_document_emit_glyph (document,
871 font_subset->scaled_font,
872 font_subset->glyphs[i],
873 font_subset->font_id, i);
874 if (unlikely (status))
877 _cairo_scaled_font_thaw_cache (font_subset->scaled_font);
882 static cairo_status_t
883 _cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
885 cairo_status_t status;
887 status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
888 _cairo_svg_document_emit_font_subset,
890 if (unlikely (status))
893 status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
894 _cairo_svg_document_emit_font_subset,
898 _cairo_scaled_font_subsets_destroy (document->font_subsets);
899 document->font_subsets = NULL;
905 _cairo_svg_surface_operators[] = {
908 "src", "src-over", "src-in",
909 "src-out", "src-atop",
911 "dst", "dst-over", "dst-in",
912 "dst-out", "dst-atop",
915 "color-dodge", /* FIXME: saturate ? */
917 "multiply", "screen", "overlay",
919 "color-dodge", "color-burn",
920 "hard-light", "soft-light",
921 "difference", "exclusion"
925 _cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface,
928 /* guard against newly added operators */
929 if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators))
930 return CAIRO_INT_STATUS_UNSUPPORTED;
932 /* allow operators being NULL if they are unsupported */
933 if (_cairo_svg_surface_operators[op] == NULL)
934 return CAIRO_INT_STATUS_UNSUPPORTED;
936 return CAIRO_STATUS_SUCCESS;
939 static cairo_int_status_t
940 _cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface,
942 const cairo_pattern_t *pattern)
944 cairo_svg_document_t *document = surface->document;
946 if (surface->force_fallbacks &&
947 surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
949 return CAIRO_INT_STATUS_UNSUPPORTED;
952 if (pattern->type == CAIRO_PATTERN_TYPE_MESH)
953 return CAIRO_INT_STATUS_UNSUPPORTED;
955 /* SVG doesn't support extend reflect for image pattern */
956 if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
957 pattern->extend == CAIRO_EXTEND_REFLECT)
958 return CAIRO_INT_STATUS_UNSUPPORTED;
960 if (document->svg_version >= CAIRO_SVG_VERSION_1_2)
961 return _cairo_svg_surface_analyze_operator (surface, op);
963 if (op == CAIRO_OPERATOR_OVER)
964 return CAIRO_STATUS_SUCCESS;
966 /* The SOURCE operator is only supported if there is nothing
967 * painted underneath. */
968 if (op == CAIRO_OPERATOR_SOURCE)
969 return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
971 return CAIRO_INT_STATUS_UNSUPPORTED;
974 static cairo_int_status_t
975 _cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface,
977 const cairo_pattern_t *pattern)
979 return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED;
982 static cairo_status_t
983 _cairo_svg_surface_finish (void *abstract_surface)
985 cairo_status_t status, status2;
986 cairo_svg_surface_t *surface = abstract_surface;
987 cairo_svg_document_t *document = surface->document;
988 cairo_svg_page_t *page;
991 if (_cairo_paginated_surface_get_target (document->owner) == &surface->base)
992 status = _cairo_svg_document_finish (document);
994 status = CAIRO_STATUS_SUCCESS;
996 if (surface->xml_node != NULL) {
997 status2 = _cairo_output_stream_destroy (surface->xml_node);
998 if (status == CAIRO_STATUS_SUCCESS)
1002 for (i = 0; i < surface->page_set.num_elements; i++) {
1003 page = _cairo_array_index (&surface->page_set, i);
1004 status2 = _cairo_output_stream_destroy (page->xml_node);
1005 if (status == CAIRO_STATUS_SUCCESS)
1008 _cairo_array_fini (&surface->page_set);
1010 _cairo_surface_clipper_reset (&surface->clipper);
1012 status2 = _cairo_svg_document_destroy (document);
1013 if (status == CAIRO_STATUS_SUCCESS)
1021 _cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document)
1023 if (document->alpha_filter)
1026 _cairo_output_stream_printf (document->xml_node_defs,
1027 "<filter id=\"alpha\" "
1028 "filterUnits=\"objectBoundingBox\" "
1029 "x=\"0%%\" y=\"0%%\" "
1030 "width=\"100%%\" height=\"100%%\">\n"
1031 " <feColorMatrix type=\"matrix\" "
1032 "in=\"SourceGraphic\" "
1033 "values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n"
1036 document->alpha_filter = TRUE;
1040 cairo_output_stream_t *output;
1041 unsigned int in_mem;
1042 unsigned int trailing;
1043 unsigned char src[3];
1044 } base64_write_closure_t;
1046 static char const base64_table[64] =
1047 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1049 static cairo_status_t
1050 base64_write_func (void *closure,
1051 const unsigned char *data,
1052 unsigned int length)
1054 base64_write_closure_t *info = (base64_write_closure_t *) closure;
1060 if (info->in_mem + length < 3) {
1061 for (i = 0; i < length; i++) {
1062 src[i + info->in_mem] = *data++;
1064 info->in_mem += length;
1065 return CAIRO_STATUS_SUCCESS;
1069 unsigned char dst[4];
1071 for (i = info->in_mem; i < 3; i++) {
1077 dst[0] = base64_table[src[0] >> 2];
1078 dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4];
1079 dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6];
1080 dst[3] = base64_table[src[2] & 0xfc >> 2];
1081 /* Special case for the last missing bits */
1082 switch (info->trailing) {
1090 _cairo_output_stream_write (info->output, dst, 4);
1091 } while (length >= 3);
1093 for (i = 0; i < length; i++) {
1096 info->in_mem = length;
1098 return _cairo_output_stream_get_status (info->output);
1101 static cairo_int_status_t
1102 _cairo_surface_base64_encode_jpeg (cairo_surface_t *surface,
1103 cairo_output_stream_t *output)
1105 const unsigned char *mime_data;
1106 unsigned long mime_data_length;
1107 cairo_image_info_t image_info;
1108 base64_write_closure_t info;
1109 cairo_status_t status;
1111 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG,
1112 &mime_data, &mime_data_length);
1113 if (mime_data == NULL)
1114 return CAIRO_INT_STATUS_UNSUPPORTED;
1116 status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length);
1117 if (unlikely (status))
1120 _cairo_output_stream_printf (output, "data:image/jpeg;base64,");
1122 info.output = output;
1126 status = base64_write_func (&info, mime_data, mime_data_length);
1127 if (unlikely (status))
1130 if (info.in_mem > 0) {
1131 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1132 info.trailing = 3 - info.in_mem;
1134 status = base64_write_func (&info, NULL, 0);
1140 static cairo_int_status_t
1141 _cairo_surface_base64_encode_png (cairo_surface_t *surface,
1142 cairo_output_stream_t *output)
1144 const unsigned char *mime_data;
1145 unsigned long mime_data_length;
1146 base64_write_closure_t info;
1147 cairo_status_t status;
1149 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG,
1150 &mime_data, &mime_data_length);
1151 if (unlikely (surface->status))
1152 return surface->status;
1153 if (mime_data == NULL)
1154 return CAIRO_INT_STATUS_UNSUPPORTED;
1156 _cairo_output_stream_printf (output, "data:image/png;base64,");
1158 info.output = output;
1162 status = base64_write_func (&info, mime_data, mime_data_length);
1163 if (unlikely (status))
1166 if (info.in_mem > 0) {
1167 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1168 info.trailing = 3 - info.in_mem;
1170 status = base64_write_func (&info, NULL, 0);
1176 static cairo_int_status_t
1177 _cairo_surface_base64_encode (cairo_surface_t *surface,
1178 cairo_output_stream_t *output)
1180 cairo_int_status_t status;
1181 base64_write_closure_t info;
1183 status = _cairo_surface_base64_encode_jpeg (surface, output);
1184 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1187 status = _cairo_surface_base64_encode_png (surface, output);
1188 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1191 info.output = output;
1195 _cairo_output_stream_printf (info.output, "data:image/png;base64,");
1197 status = cairo_surface_write_to_png_stream (surface, base64_write_func,
1200 if (unlikely (status))
1203 if (info.in_mem > 0) {
1204 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1205 info.trailing = 3 - info.in_mem;
1207 status = base64_write_func (&info, NULL, 0);
1214 _cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
1215 cairo_svg_surface_t *surface,
1216 cairo_operator_t op)
1218 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1219 op != CAIRO_OPERATOR_OVER) {
1220 _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]);
1221 if (!_cairo_operator_bounded_by_source (op))
1222 _cairo_output_stream_printf (output, " clip-to-self=\"true\"");
1227 _cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output,
1228 cairo_svg_surface_t *surface,
1229 cairo_operator_t op)
1231 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1232 op != CAIRO_OPERATOR_OVER) {
1233 _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]);
1234 if (!_cairo_operator_bounded_by_source (op))
1235 _cairo_output_stream_printf (output, "clip-to-self:true;");
1240 * _cairo_svg_surface_emit_attr_value:
1242 * Write the value to output the stream as a sequence of characters,
1243 * while escaping those which have special meaning in the XML
1244 * attribute's value context: & and ".
1247 _cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream,
1248 const unsigned char *value,
1249 unsigned int length)
1251 const unsigned char *p;
1252 const unsigned char *q;
1255 /* we'll accumulate non-special chars in [q, p) range */
1258 for (i = 0; i < length; i++, p++) {
1259 if (*p == '&' || *p == '"') {
1260 /* flush what's left before special char */
1262 _cairo_output_stream_write (stream, q, p - q);
1267 _cairo_output_stream_printf (stream, "&");
1269 _cairo_output_stream_printf (stream, """);
1273 /* flush the trailing chars if any */
1275 _cairo_output_stream_write (stream, q, p - q);
1278 static cairo_status_t
1279 _cairo_svg_surface_emit_surface (cairo_svg_document_t *document,
1280 cairo_surface_t *surface)
1282 cairo_rectangle_int_t extents;
1283 cairo_bool_t is_bounded;
1284 cairo_status_t status;
1285 const unsigned char *uri;
1286 unsigned long uri_len;
1288 if (_cairo_user_data_array_get_data (&surface->user_data,
1289 (cairo_user_data_key_t *) document))
1291 return CAIRO_STATUS_SUCCESS;
1294 is_bounded = _cairo_surface_get_extents (surface, &extents);
1295 assert (is_bounded);
1297 _cairo_output_stream_printf (document->xml_node_defs,
1298 "<image id=\"image%d\" width=\"%d\" height=\"%d\"",
1300 extents.width, extents.height);
1302 _cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\"");
1304 cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_URI,
1307 _cairo_svg_surface_emit_attr_value (document->xml_node_defs,
1310 status = _cairo_surface_base64_encode (surface,
1311 document->xml_node_defs);
1312 if (unlikely (status))
1316 _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n");
1319 return _cairo_user_data_array_set_data (&surface->user_data,
1320 (cairo_user_data_key_t *) document,
1324 static cairo_status_t
1325 _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *output,
1326 cairo_svg_surface_t *svg_surface,
1327 cairo_operator_t op,
1328 cairo_surface_pattern_t *pattern,
1330 const cairo_matrix_t *parent_matrix,
1331 const char *extra_attributes)
1333 cairo_status_t status;
1336 p2u = pattern->base.matrix;
1337 status = cairo_matrix_invert (&p2u);
1338 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1339 assert (status == CAIRO_STATUS_SUCCESS);
1341 status = _cairo_svg_surface_emit_surface (svg_surface->document,
1343 if (unlikely (status))
1346 if (pattern_id != invalid_pattern_id) {
1347 cairo_rectangle_int_t extents;
1348 cairo_bool_t is_bounded;
1350 is_bounded = _cairo_surface_get_extents (pattern->surface, &extents);
1351 assert (is_bounded);
1353 _cairo_output_stream_printf (output,
1354 "<pattern id=\"pattern%d\" "
1355 "patternUnits=\"userSpaceOnUse\" "
1356 "width=\"%d\" height=\"%d\" ",
1358 extents.width, extents.height);
1359 _cairo_svg_surface_emit_transform (output,
1360 " patternTransform",
1361 &p2u, parent_matrix);
1362 _cairo_output_stream_printf (output, ">\n ");
1365 _cairo_output_stream_printf (output,
1366 "<use xlink:href=\"#image%d\"",
1367 pattern->surface->unique_id);
1368 if (extra_attributes)
1369 _cairo_output_stream_printf (output, " %s", extra_attributes);
1371 if (pattern_id == invalid_pattern_id) {
1372 _cairo_svg_surface_emit_operator (output, svg_surface, op);
1373 _cairo_svg_surface_emit_transform (output,
1375 &p2u, parent_matrix);
1377 _cairo_output_stream_printf (output, "/>\n");
1380 if (pattern_id != invalid_pattern_id)
1381 _cairo_output_stream_printf (output, "</pattern>\n");
1383 return CAIRO_STATUS_SUCCESS;
1386 static cairo_status_t
1387 _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document,
1388 cairo_recording_surface_t *source)
1390 cairo_status_t status;
1391 cairo_surface_t *paginated_surface;
1392 cairo_svg_surface_t *svg_surface;
1393 cairo_array_t *page_set;
1395 cairo_output_stream_t *contents;
1397 if (_cairo_user_data_array_get_data (&source->base.user_data,
1398 (cairo_user_data_key_t *) document))
1400 return CAIRO_STATUS_SUCCESS;
1403 paginated_surface = _cairo_svg_surface_create_for_document (document,
1404 source->base.content,
1405 source->extents_pixels.width,
1406 source->extents_pixels.height);
1407 if (unlikely (paginated_surface->status))
1408 return paginated_surface->status;
1410 svg_surface = (cairo_svg_surface_t *)
1411 _cairo_paginated_surface_get_target (paginated_surface);
1412 cairo_surface_set_fallback_resolution (paginated_surface,
1413 document->owner->x_fallback_resolution,
1414 document->owner->y_fallback_resolution);
1415 cairo_surface_set_device_offset (&svg_surface->base,
1416 -source->extents_pixels.x,
1417 -source->extents_pixels.y);
1419 status = _cairo_recording_surface_replay (&source->base, paginated_surface);
1420 if (unlikely (status)) {
1421 cairo_surface_destroy (paginated_surface);
1425 cairo_surface_show_page (paginated_surface);
1426 status = cairo_surface_status (paginated_surface);
1427 if (unlikely (status)) {
1428 cairo_surface_destroy (paginated_surface);
1432 if (! svg_surface->is_base_clip_emitted) {
1433 svg_surface->is_base_clip_emitted = TRUE;
1434 _cairo_output_stream_printf (document->xml_node_defs,
1435 "<clipPath id=\"clip%d\">\n"
1436 " <rect width=\"%f\" height=\"%f\"/>\n"
1438 svg_surface->base_clip,
1440 svg_surface->height);
1443 if (source->base.content == CAIRO_CONTENT_ALPHA) {
1444 _cairo_svg_surface_emit_alpha_filter (document);
1445 _cairo_output_stream_printf (document->xml_node_defs,
1446 "<g id=\"surface%d\" "
1447 "clip-path=\"url(#clip%d)\" "
1448 "filter=\"url(#alpha)\">\n",
1449 source->base.unique_id,
1450 svg_surface->base_clip);
1452 _cairo_output_stream_printf (document->xml_node_defs,
1453 "<g id=\"surface%d\" "
1454 "clip-path=\"url(#clip%d)\">\n",
1455 source->base.unique_id,
1456 svg_surface->base_clip);
1459 contents = svg_surface->xml_node;
1460 page_set = &svg_surface->page_set;
1462 if (_cairo_memory_stream_length (contents) > 0) {
1463 if (unlikely (_cairo_svg_surface_store_page (svg_surface) == NULL)) {
1464 cairo_surface_destroy (paginated_surface);
1465 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1469 if (page_set->num_elements > 0) {
1470 cairo_svg_page_t *page;
1472 page = _cairo_array_index (page_set, page_set->num_elements - 1);
1473 _cairo_memory_stream_copy (page->xml_node, document->xml_node_defs);
1476 _cairo_output_stream_printf (document->xml_node_defs, "</g>\n");
1478 status = cairo_surface_status (paginated_surface);
1479 cairo_surface_destroy (paginated_surface);
1481 if (unlikely (status))
1485 return _cairo_user_data_array_set_data (&source->base.user_data,
1486 (cairo_user_data_key_t *) document,
1490 static cairo_status_t
1491 _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *output,
1492 cairo_svg_surface_t *surface,
1493 cairo_operator_t op,
1494 cairo_surface_pattern_t *pattern,
1496 const cairo_matrix_t *parent_matrix,
1497 const char *extra_attributes)
1499 cairo_svg_document_t *document = surface->document;
1500 cairo_recording_surface_t *recording_surface;
1502 cairo_status_t status;
1504 p2u = pattern->base.matrix;
1505 status = cairo_matrix_invert (&p2u);
1506 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1507 assert (status == CAIRO_STATUS_SUCCESS);
1509 recording_surface = (cairo_recording_surface_t *) pattern->surface;
1510 status = _cairo_svg_surface_emit_recording_surface (document, recording_surface);
1511 if (unlikely (status))
1514 if (pattern_id != invalid_pattern_id) {
1515 _cairo_output_stream_printf (output,
1516 "<pattern id=\"pattern%d\" "
1517 "patternUnits=\"userSpaceOnUse\" "
1518 "width=\"%d\" height=\"%d\"",
1520 recording_surface->extents.width,
1521 recording_surface->extents.height);
1522 _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
1523 _cairo_output_stream_printf (output, ">\n");
1526 _cairo_output_stream_printf (output,
1527 "<use xlink:href=\"#surface%d\"",
1528 recording_surface->base.unique_id);
1530 if (pattern_id == invalid_pattern_id) {
1531 _cairo_svg_surface_emit_operator (output, surface, op);
1532 _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
1535 if (extra_attributes)
1536 _cairo_output_stream_printf (output, " %s", extra_attributes);
1538 _cairo_output_stream_printf (output, "/>\n");
1540 if (pattern_id != invalid_pattern_id)
1541 _cairo_output_stream_printf (output, "</pattern>\n");
1543 return CAIRO_STATUS_SUCCESS;
1546 static cairo_status_t
1547 _cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output,
1548 cairo_svg_surface_t *surface,
1549 cairo_operator_t op,
1550 cairo_surface_pattern_t *pattern,
1552 const cairo_matrix_t *parent_matrix,
1553 const char *extra_attributes)
1556 if (_cairo_surface_is_recording (pattern->surface)) {
1557 return _cairo_svg_surface_emit_composite_recording_pattern (output, surface,
1564 return _cairo_svg_surface_emit_composite_surface_pattern (output, surface,
1571 static cairo_status_t
1572 _cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface,
1573 cairo_solid_pattern_t *pattern,
1574 cairo_output_stream_t *style,
1575 cairo_bool_t is_stroke)
1577 _cairo_output_stream_printf (style, is_stroke ?
1578 "stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;":
1579 "fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;",
1580 pattern->color.red * 100.0,
1581 pattern->color.green * 100.0,
1582 pattern->color.blue * 100.0,
1583 pattern->color.alpha);
1585 return CAIRO_STATUS_SUCCESS;
1588 static cairo_status_t
1589 _cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface,
1590 cairo_surface_pattern_t *pattern,
1591 cairo_output_stream_t *style,
1592 cairo_bool_t is_stroke,
1593 const cairo_matrix_t *parent_matrix)
1595 cairo_svg_document_t *document = surface->document;
1596 cairo_status_t status;
1599 pattern_id = document->pattern_id++;
1600 status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs,
1601 surface, CAIRO_OPERATOR_SOURCE, pattern,
1602 pattern_id, parent_matrix, NULL);
1603 if (unlikely (status))
1606 _cairo_output_stream_printf (style,
1607 "%s:url(#pattern%d);",
1608 is_stroke ? "stroke" : "fill",
1611 return CAIRO_STATUS_SUCCESS;
1614 static cairo_status_t
1615 _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output,
1616 cairo_gradient_pattern_t const *pattern,
1617 double start_offset,
1618 cairo_bool_t reverse_stops,
1619 cairo_bool_t emulate_reflect)
1621 cairo_gradient_stop_t *stops;
1623 unsigned int n_stops;
1626 if (pattern->n_stops < 1)
1627 return CAIRO_STATUS_SUCCESS;
1629 if (pattern->n_stops == 1) {
1630 _cairo_output_stream_printf (output,
1631 "<stop offset=\"%f\" style=\""
1632 "stop-color:rgb(%f%%,%f%%,%f%%);"
1633 "stop-opacity:%f;\"/>\n",
1634 pattern->stops[0].offset,
1635 pattern->stops[0].color.red * 100.0,
1636 pattern->stops[0].color.green * 100.0,
1637 pattern->stops[0].color.blue * 100.0,
1638 pattern->stops[0].color.alpha);
1639 return CAIRO_STATUS_SUCCESS;
1642 if (emulate_reflect || reverse_stops) {
1643 n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops;
1644 stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t));
1645 if (unlikely (stops == NULL))
1646 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1648 for (i = 0; i < pattern->n_stops; i++) {
1649 if (reverse_stops) {
1650 stops[i] = pattern->stops[pattern->n_stops - i - 1];
1651 stops[i].offset = 1.0 - stops[i].offset;
1653 stops[i] = pattern->stops[i];
1654 if (emulate_reflect) {
1655 stops[i].offset /= 2;
1656 if (i > 0 && i < (pattern->n_stops - 1)) {
1657 if (reverse_stops) {
1658 stops[i + pattern->n_stops - 1] = pattern->stops[i];
1659 stops[i + pattern->n_stops - 1].offset =
1660 0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset;
1662 stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1];
1663 stops[i + pattern->n_stops - 1].offset =
1664 1 - 0.5 * stops[i + pattern->n_stops - 1].offset;
1670 n_stops = pattern->n_stops;
1671 stops = pattern->stops;
1674 if (start_offset >= 0.0)
1675 for (i = 0; i < n_stops; i++) {
1676 offset = start_offset + (1 - start_offset ) * stops[i].offset;
1677 _cairo_output_stream_printf (output,
1678 "<stop offset=\"%f\" style=\""
1679 "stop-color:rgb(%f%%,%f%%,%f%%);"
1680 "stop-opacity:%f;\"/>\n",
1682 stops[i].color.red * 100.0,
1683 stops[i].color.green * 100.0,
1684 stops[i].color.blue * 100.0,
1685 stops[i].color.alpha);
1688 cairo_bool_t found = FALSE;
1689 unsigned int offset_index;
1690 cairo_color_stop_t offset_color_start, offset_color_stop;
1692 for (i = 0; i < n_stops; i++) {
1693 if (stops[i].offset >= -start_offset) {
1695 if (stops[i].offset != stops[i-1].offset) {
1697 cairo_color_stop_t *color0, *color1;
1699 x0 = stops[i-1].offset;
1700 x1 = stops[i].offset;
1701 color0 = &stops[i-1].color;
1702 color1 = &stops[i].color;
1703 offset_color_start.red = color0->red + (color1->red - color0->red)
1704 * (-start_offset - x0) / (x1 - x0);
1705 offset_color_start.green = color0->green + (color1->green - color0->green)
1706 * (-start_offset - x0) / (x1 - x0);
1707 offset_color_start.blue = color0->blue + (color1->blue - color0->blue)
1708 * (-start_offset - x0) / (x1 - x0);
1709 offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha)
1710 * (-start_offset - x0) / (x1 - x0);
1711 offset_color_stop = offset_color_start;
1713 offset_color_stop = stops[i-1].color;
1714 offset_color_start = stops[i].color;
1717 offset_color_stop = offset_color_start = stops[i].color;
1725 offset_index = n_stops - 1;
1726 offset_color_stop = offset_color_start = stops[offset_index].color;
1729 _cairo_output_stream_printf (output,
1730 "<stop offset=\"0\" style=\""
1731 "stop-color:rgb(%f%%,%f%%,%f%%);"
1732 "stop-opacity:%f;\"/>\n",
1733 offset_color_start.red * 100.0,
1734 offset_color_start.green * 100.0,
1735 offset_color_start.blue * 100.0,
1736 offset_color_start.alpha);
1737 for (i = offset_index; i < n_stops; i++) {
1738 _cairo_output_stream_printf (output,
1739 "<stop offset=\"%f\" style=\""
1740 "stop-color:rgb(%f%%,%f%%,%f%%);"
1741 "stop-opacity:%f;\"/>\n",
1742 stops[i].offset + start_offset,
1743 stops[i].color.red * 100.0,
1744 stops[i].color.green * 100.0,
1745 stops[i].color.blue * 100.0,
1746 stops[i].color.alpha);
1748 for (i = 0; i < offset_index; i++) {
1749 _cairo_output_stream_printf (output,
1750 "<stop offset=\"%f\" style=\""
1751 "stop-color:rgb(%f%%,%f%%,%f%%);"
1752 "stop-opacity:%f;\"/>\n",
1753 1.0 + stops[i].offset + start_offset,
1754 stops[i].color.red * 100.0,
1755 stops[i].color.green * 100.0,
1756 stops[i].color.blue * 100.0,
1757 stops[i].color.alpha);
1760 _cairo_output_stream_printf (output,
1761 "<stop offset=\"1\" style=\""
1762 "stop-color:rgb(%f%%,%f%%,%f%%);"
1763 "stop-opacity:%f;\"/>\n",
1764 offset_color_stop.red * 100.0,
1765 offset_color_stop.green * 100.0,
1766 offset_color_stop.blue * 100.0,
1767 offset_color_stop.alpha);
1771 if (reverse_stops || emulate_reflect)
1774 return CAIRO_STATUS_SUCCESS;
1778 _cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output,
1779 cairo_pattern_t *pattern)
1781 switch (pattern->extend) {
1782 case CAIRO_EXTEND_REPEAT:
1783 _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" ");
1785 case CAIRO_EXTEND_REFLECT:
1786 _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" ");
1788 case CAIRO_EXTEND_NONE:
1789 case CAIRO_EXTEND_PAD:
1794 static cairo_status_t
1795 _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface,
1796 cairo_linear_pattern_t *pattern,
1797 cairo_output_stream_t *style,
1798 cairo_bool_t is_stroke,
1799 const cairo_matrix_t *parent_matrix)
1801 cairo_svg_document_t *document = surface->document;
1803 cairo_status_t status;
1805 p2u = pattern->base.base.matrix;
1806 status = cairo_matrix_invert (&p2u);
1807 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1808 assert (status == CAIRO_STATUS_SUCCESS);
1810 _cairo_output_stream_printf (document->xml_node_defs,
1811 "<linearGradient id=\"linear%d\" "
1812 "gradientUnits=\"userSpaceOnUse\" "
1813 "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
1814 document->linear_pattern_id,
1815 pattern->pd1.x, pattern->pd1.y,
1816 pattern->pd2.x, pattern->pd2.y);
1818 _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
1819 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1820 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1822 status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
1823 &pattern->base, 0.0,
1825 if (unlikely (status))
1828 _cairo_output_stream_printf (document->xml_node_defs,
1829 "</linearGradient>\n");
1831 _cairo_output_stream_printf (style,
1832 "%s:url(#linear%d);",
1833 is_stroke ? "stroke" : "fill",
1834 document->linear_pattern_id);
1836 document->linear_pattern_id++;
1838 return CAIRO_STATUS_SUCCESS;
1841 static cairo_status_t
1842 _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface,
1843 cairo_radial_pattern_t *pattern,
1844 cairo_output_stream_t *style,
1845 cairo_bool_t is_stroke,
1846 const cairo_matrix_t *parent_matrix)
1848 cairo_svg_document_t *document = surface->document;
1850 cairo_extend_t extend;
1851 double x0, y0, x1, y1, r0, r1;
1853 cairo_bool_t reverse_stops;
1854 cairo_status_t status;
1855 cairo_circle_double_t *c0, *c1;
1857 extend = pattern->base.base.extend;
1859 if (pattern->cd1.radius < pattern->cd2.radius) {
1862 reverse_stops = FALSE;
1866 reverse_stops = TRUE;
1876 p2u = pattern->base.base.matrix;
1877 status = cairo_matrix_invert (&p2u);
1878 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1879 assert (status == CAIRO_STATUS_SUCCESS);
1882 unsigned int n_stops = pattern->base.n_stops;
1884 _cairo_output_stream_printf (document->xml_node_defs,
1885 "<radialGradient id=\"radial%d\" "
1886 "gradientUnits=\"userSpaceOnUse\" "
1887 "cx=\"%f\" cy=\"%f\" "
1888 "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1889 document->radial_pattern_id,
1892 _cairo_svg_surface_emit_transform (document->xml_node_defs,
1893 "gradientTransform",
1894 &p2u, parent_matrix);
1895 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1897 if (extend == CAIRO_EXTEND_NONE || n_stops < 1)
1898 _cairo_output_stream_printf (document->xml_node_defs,
1899 "<stop offset=\"0\" style=\""
1900 "stop-color:rgb(0%%,0%%,0%%);"
1901 "stop-opacity:0;\"/>\n");
1903 _cairo_output_stream_printf (document->xml_node_defs,
1904 "<stop offset=\"0\" style=\""
1905 "stop-color:rgb(%f%%,%f%%,%f%%);"
1906 "stop-opacity %f;\"/>\n",
1907 pattern->base.stops[0].color.red * 100.0,
1908 pattern->base.stops[0].color.green * 100.0,
1909 pattern->base.stops[0].color.blue * 100.0,
1910 pattern->base.stops[0].color.alpha);
1912 _cairo_output_stream_printf (document->xml_node_defs,
1913 "<stop offset=\"0\" style=\""
1914 "stop-color:rgb(%f%%,%f%%,%f%%);"
1915 "stop-opacity:%f;\"/>\n",
1916 pattern->base.stops[n_stops - 1].color.red * 100.0,
1917 pattern->base.stops[n_stops - 1].color.green * 100.0,
1918 pattern->base.stops[n_stops - 1].color.blue * 100.0,
1919 pattern->base.stops[n_stops - 1].color.alpha);
1923 double offset, r, x, y;
1924 cairo_bool_t emulate_reflect = FALSE;
1926 fx = (r1 * x0 - r0 * x1) / (r1 - r0);
1927 fy = (r1 * y0 - r0 * y1) / (r1 - r0);
1929 /* SVG doesn't support the inner circle and use instead a gradient focal.
1930 * That means we need to emulate the cairo behaviour by processing the
1931 * cairo gradient stops.
1932 * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
1933 * it's just a matter of stop position translation and calculation of
1934 * the corresponding SVG radial gradient focal.
1935 * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
1936 * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
1937 * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
1938 * list that maps to the original cairo stop list.
1940 if ((extend == CAIRO_EXTEND_REFLECT
1941 || extend == CAIRO_EXTEND_REPEAT)
1945 if (extend == CAIRO_EXTEND_REFLECT) {
1947 emulate_reflect = TRUE;
1950 offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
1953 /* New position of outer circle. */
1954 x = r * (x1 - fx) / r_org + fx;
1955 y = r * (y1 - fy) / r_org + fy;
1965 _cairo_output_stream_printf (document->xml_node_defs,
1966 "<radialGradient id=\"radial%d\" "
1967 "gradientUnits=\"userSpaceOnUse\" "
1968 "cx=\"%f\" cy=\"%f\" "
1969 "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1970 document->radial_pattern_id,
1974 if (emulate_reflect)
1975 _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
1977 _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
1978 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1979 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1981 /* To support cairo's EXTEND_NONE, (for which SVG has no similar
1982 * notion), we add transparent color stops on either end of the
1983 * user-provided stops. */
1984 if (extend == CAIRO_EXTEND_NONE) {
1985 _cairo_output_stream_printf (document->xml_node_defs,
1986 "<stop offset=\"0\" style=\""
1987 "stop-color:rgb(0%%,0%%,0%%);"
1988 "stop-opacity:0;\"/>\n");
1990 _cairo_output_stream_printf (document->xml_node_defs,
1991 "<stop offset=\"%f\" style=\""
1992 "stop-color:rgb(0%%,0%%,0%%);"
1993 "stop-opacity:0;\"/>\n",
1996 status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
1997 &pattern->base, offset,
2000 if (unlikely (status))
2003 if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
2004 _cairo_output_stream_printf (document->xml_node_defs,
2005 "<stop offset=\"1.0\" style=\""
2006 "stop-color:rgb(0%%,0%%,0%%);"
2007 "stop-opacity:0;\"/>\n");
2010 _cairo_output_stream_printf (document->xml_node_defs,
2011 "</radialGradient>\n");
2013 _cairo_output_stream_printf (style,
2014 "%s:url(#radial%d);",
2015 is_stroke ? "stroke" : "fill",
2016 document->radial_pattern_id);
2018 document->radial_pattern_id++;
2020 return CAIRO_STATUS_SUCCESS;
2023 static cairo_status_t
2024 _cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface,
2025 const cairo_pattern_t *pattern,
2026 cairo_output_stream_t *output,
2027 cairo_bool_t is_stroke,
2028 const cairo_matrix_t *parent_matrix)
2030 switch (pattern->type) {
2031 case CAIRO_PATTERN_TYPE_SOLID:
2032 return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern,
2035 case CAIRO_PATTERN_TYPE_SURFACE:
2036 return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern,
2037 output, is_stroke, parent_matrix);
2039 case CAIRO_PATTERN_TYPE_LINEAR:
2040 return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern,
2041 output, is_stroke, parent_matrix);
2043 case CAIRO_PATTERN_TYPE_RADIAL:
2044 return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern,
2045 output, is_stroke, parent_matrix);
2047 case CAIRO_PATTERN_TYPE_MESH:
2048 case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
2051 return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
2054 static cairo_status_t
2055 _cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output,
2056 cairo_svg_surface_t *surface,
2057 cairo_operator_t op,
2058 const cairo_pattern_t *source,
2059 cairo_fill_rule_t fill_rule,
2060 const cairo_matrix_t *parent_matrix)
2062 _cairo_output_stream_printf (output,
2064 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
2065 "evenodd" : "nonzero");
2066 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2067 return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix);
2070 static cairo_status_t
2071 _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output,
2072 cairo_svg_surface_t *surface,
2073 cairo_operator_t op,
2074 const cairo_pattern_t *source,
2075 const cairo_stroke_style_t *stroke_style,
2076 const cairo_matrix_t *parent_matrix)
2078 cairo_status_t status;
2079 const char *line_cap, *line_join;
2082 switch (stroke_style->line_cap) {
2083 case CAIRO_LINE_CAP_BUTT:
2086 case CAIRO_LINE_CAP_ROUND:
2089 case CAIRO_LINE_CAP_SQUARE:
2090 line_cap = "square";
2096 switch (stroke_style->line_join) {
2097 case CAIRO_LINE_JOIN_MITER:
2098 line_join = "miter";
2100 case CAIRO_LINE_JOIN_ROUND:
2101 line_join = "round";
2103 case CAIRO_LINE_JOIN_BEVEL:
2104 line_join = "bevel";
2110 _cairo_output_stream_printf (output,
2112 "stroke-linecap:%s;"
2113 "stroke-linejoin:%s;",
2114 stroke_style->line_width,
2118 status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix);
2119 if (unlikely (status))
2122 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2124 if (stroke_style->num_dashes > 0) {
2125 _cairo_output_stream_printf (output, "stroke-dasharray:");
2126 for (i = 0; i < stroke_style->num_dashes; i++) {
2127 _cairo_output_stream_printf (output, "%f",
2128 stroke_style->dash[i]);
2129 if (i + 1 < stroke_style->num_dashes)
2130 _cairo_output_stream_printf (output, ",");
2132 _cairo_output_stream_printf (output, ";");
2134 if (stroke_style->dash_offset != 0.0) {
2135 _cairo_output_stream_printf (output,
2136 "stroke-dashoffset:%f;",
2137 stroke_style->dash_offset);
2141 _cairo_output_stream_printf (output,
2142 "stroke-miterlimit:%f;",
2143 stroke_style->miter_limit);
2145 return CAIRO_STATUS_SUCCESS;
2148 static cairo_int_status_t
2149 _cairo_svg_surface_fill_stroke (void *abstract_surface,
2150 cairo_operator_t fill_op,
2151 const cairo_pattern_t *fill_source,
2152 cairo_fill_rule_t fill_rule,
2153 double fill_tolerance,
2154 cairo_antialias_t fill_antialias,
2155 const cairo_path_fixed_t*path,
2156 cairo_operator_t stroke_op,
2157 const cairo_pattern_t *stroke_source,
2158 const cairo_stroke_style_t *stroke_style,
2159 const cairo_matrix_t *stroke_ctm,
2160 const cairo_matrix_t *stroke_ctm_inverse,
2161 double stroke_tolerance,
2162 cairo_antialias_t stroke_antialias,
2163 const cairo_clip_t *clip)
2165 cairo_svg_surface_t *surface = abstract_surface;
2166 cairo_status_t status;
2168 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2169 if (unlikely (status))
2172 _cairo_output_stream_printf (surface->xml_node, "<path style=\"");
2173 status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, fill_op,
2174 fill_source, fill_rule, stroke_ctm_inverse);
2175 if (unlikely (status))
2178 status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op,
2179 stroke_source, stroke_style, stroke_ctm_inverse);
2180 if (unlikely (status))
2183 _cairo_output_stream_printf (surface->xml_node, "\" ");
2185 _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse);
2187 _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL);
2188 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2190 return CAIRO_STATUS_SUCCESS;
2193 static cairo_int_status_t
2194 _cairo_svg_surface_fill (void *abstract_surface,
2195 cairo_operator_t op,
2196 const cairo_pattern_t *source,
2197 const cairo_path_fixed_t*path,
2198 cairo_fill_rule_t fill_rule,
2200 cairo_antialias_t antialias,
2201 const cairo_clip_t *clip)
2203 cairo_svg_surface_t *surface = abstract_surface;
2204 cairo_status_t status;
2206 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2207 return _cairo_svg_surface_analyze_operation (surface, op, source);
2209 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2211 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2212 if (unlikely (status))
2215 _cairo_output_stream_printf (surface->xml_node, "<path style=\" stroke:none;");
2216 status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, op, source, fill_rule, NULL);
2217 if (unlikely (status))
2220 _cairo_output_stream_printf (surface->xml_node, "\" ");
2222 _cairo_svg_surface_emit_path (surface->xml_node, path, NULL);
2224 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2226 return CAIRO_STATUS_SUCCESS;
2230 _cairo_svg_surface_get_extents (void *abstract_surface,
2231 cairo_rectangle_int_t *rectangle)
2233 cairo_svg_surface_t *surface = abstract_surface;
2238 /* XXX: The conversion to integers here is pretty bogus, (not to
2239 * mention the arbitrary limitation of width to a short(!). We
2240 * may need to come up with a better interface for get_size.
2242 rectangle->width = ceil (surface->width);
2243 rectangle->height = ceil (surface->height);
2248 static cairo_status_t
2249 _cairo_svg_surface_emit_paint (cairo_output_stream_t *output,
2250 cairo_svg_surface_t *surface,
2251 cairo_operator_t op,
2252 const cairo_pattern_t *source,
2253 const cairo_pattern_t *mask_source,
2254 const char *extra_attributes)
2256 cairo_status_t status;
2258 if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
2259 source->extend == CAIRO_EXTEND_NONE)
2260 return _cairo_svg_surface_emit_composite_pattern (output,
2263 (cairo_surface_pattern_t *) source,
2265 mask_source ? &mask_source->matrix :NULL,
2268 _cairo_output_stream_printf (output,
2269 "<rect x=\"0\" y=\"0\" "
2270 "width=\"%f\" height=\"%f\" "
2272 surface->width, surface->height);
2273 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2274 status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL);
2275 if (unlikely (status))
2278 _cairo_output_stream_printf (output, "stroke:none;\"");
2280 if (extra_attributes)
2281 _cairo_output_stream_printf (output, " %s", extra_attributes);
2283 _cairo_output_stream_printf (output, "/>\n");
2285 return CAIRO_STATUS_SUCCESS;
2288 static cairo_int_status_t
2289 _cairo_svg_surface_paint (void *abstract_surface,
2290 cairo_operator_t op,
2291 const cairo_pattern_t *source,
2292 const cairo_clip_t *clip)
2294 cairo_status_t status;
2295 cairo_svg_surface_t *surface = abstract_surface;
2297 /* Emulation of clear and source operators, when no clipping region
2298 * is defined. We just delete existing content of surface root node,
2299 * and exit early if operator is clear.
2301 if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) &&
2304 switch (surface->paginated_mode) {
2305 case CAIRO_PAGINATED_MODE_FALLBACK:
2307 case CAIRO_PAGINATED_MODE_ANALYZE:
2308 return CAIRO_STATUS_SUCCESS;
2310 case CAIRO_PAGINATED_MODE_RENDER:
2311 status = _cairo_output_stream_destroy (surface->xml_node);
2312 if (unlikely (status)) {
2313 surface->xml_node = NULL;
2317 surface->xml_node = _cairo_memory_stream_create ();
2318 if (_cairo_output_stream_get_status (surface->xml_node)) {
2319 status = _cairo_output_stream_destroy (surface->xml_node);
2320 surface->xml_node = NULL;
2324 if (op == CAIRO_OPERATOR_CLEAR) {
2325 if (surface->content == CAIRO_CONTENT_COLOR) {
2326 _cairo_output_stream_printf (surface->xml_node,
2328 "width=\"%f\" height=\"%f\" "
2329 "style=\"opacity:1;"
2331 "fill:rgb(0,0,0);\"/>\n",
2332 surface->width, surface->height);
2334 return CAIRO_STATUS_SUCCESS;
2339 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2340 return _cairo_svg_surface_analyze_operation (surface, op, source);
2342 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2345 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2346 if (unlikely (status))
2349 return _cairo_svg_surface_emit_paint (surface->xml_node,
2350 surface, op, source, 0, NULL);
2353 static cairo_int_status_t
2354 _cairo_svg_surface_mask (void *abstract_surface,
2355 cairo_operator_t op,
2356 const cairo_pattern_t *source,
2357 const cairo_pattern_t *mask,
2358 const cairo_clip_t *clip)
2360 cairo_status_t status;
2361 cairo_svg_surface_t *surface = abstract_surface;
2362 cairo_svg_document_t *document = surface->document;
2363 cairo_output_stream_t *mask_stream;
2365 cairo_bool_t discard_filter = FALSE;
2366 unsigned int mask_id;
2368 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2369 cairo_status_t source_status, mask_status;
2371 source_status = _cairo_svg_surface_analyze_operation (surface, op, source);
2372 if (_cairo_status_is_error (source_status))
2373 return source_status;
2375 if (mask->has_component_alpha) {
2376 mask_status = CAIRO_INT_STATUS_UNSUPPORTED;
2378 mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask);
2379 if (_cairo_status_is_error (mask_status))
2383 return _cairo_analysis_surface_merge_status (source_status,
2387 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2388 assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask));
2390 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2391 if (unlikely (status))
2394 if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
2395 const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask;
2396 cairo_content_t content = cairo_surface_get_content (surface_pattern->surface);
2397 if (content == CAIRO_CONTENT_ALPHA)
2398 discard_filter = TRUE;
2401 if (!discard_filter)
2402 _cairo_svg_surface_emit_alpha_filter (document);
2404 /* _cairo_svg_surface_emit_paint() will output a pattern definition to
2405 * document->xml_node_defs so we need to write the mask element to
2406 * a temporary stream and then copy that to xml_node_defs. */
2407 mask_stream = _cairo_memory_stream_create ();
2408 if (_cairo_output_stream_get_status (mask_stream))
2409 return _cairo_output_stream_destroy (mask_stream);
2411 mask_id = _cairo_svg_document_allocate_mask_id (document);
2413 _cairo_output_stream_printf (mask_stream,
2414 "<mask id=\"mask%d\">\n"
2417 discard_filter ? "" : " <g filter=\"url(#alpha)\">\n");
2418 status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL);
2419 if (unlikely (status)) {
2420 cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream);
2425 _cairo_output_stream_printf (mask_stream,
2428 discard_filter ? "" : " </g>\n");
2429 _cairo_memory_stream_copy (mask_stream, document->xml_node_defs);
2431 status = _cairo_output_stream_destroy (mask_stream);
2432 if (unlikely (status))
2435 snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"",
2437 status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer);
2438 if (unlikely (status))
2441 return CAIRO_STATUS_SUCCESS;
2444 static cairo_int_status_t
2445 _cairo_svg_surface_stroke (void *abstract_dst,
2446 cairo_operator_t op,
2447 const cairo_pattern_t *source,
2448 const cairo_path_fixed_t*path,
2449 const cairo_stroke_style_t *stroke_style,
2450 const cairo_matrix_t *ctm,
2451 const cairo_matrix_t *ctm_inverse,
2453 cairo_antialias_t antialias,
2454 const cairo_clip_t *clip)
2456 cairo_svg_surface_t *surface = abstract_dst;
2457 cairo_status_t status;
2459 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2460 return _cairo_svg_surface_analyze_operation (surface, op, source);
2462 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2464 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2465 if (unlikely (status))
2468 _cairo_output_stream_printf (surface->xml_node, "<path style=\"fill:none;");
2469 status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, op,
2470 source, stroke_style, ctm_inverse);
2471 if (unlikely (status))
2474 _cairo_output_stream_printf (surface->xml_node, "\" ");
2476 _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse);
2478 _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL);
2479 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2481 return CAIRO_STATUS_SUCCESS;
2484 static cairo_int_status_t
2485 _cairo_svg_surface_show_glyphs (void *abstract_surface,
2486 cairo_operator_t op,
2487 const cairo_pattern_t *pattern,
2488 cairo_glyph_t *glyphs,
2490 cairo_scaled_font_t *scaled_font,
2491 const cairo_clip_t *clip)
2493 cairo_svg_surface_t *surface = abstract_surface;
2494 cairo_svg_document_t *document = surface->document;
2495 cairo_path_fixed_t path;
2496 cairo_int_status_t status;
2497 cairo_scaled_font_subsets_glyph_t subset_glyph;
2500 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2501 return _cairo_svg_surface_analyze_operation (surface, op, pattern);
2503 assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
2505 if (num_glyphs <= 0)
2506 return CAIRO_STATUS_SUCCESS;
2508 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2509 if (unlikely (status))
2512 /* FIXME it's probably possible to apply a pattern of a gradient to
2513 * a group of symbols, but I don't know how yet. Gradients or patterns
2514 * are translated by x and y properties of use element. */
2515 if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
2518 _cairo_output_stream_printf (surface->xml_node, "<g style=\"");
2519 status = _cairo_svg_surface_emit_pattern (surface, pattern,
2520 surface->xml_node, FALSE, NULL);
2521 if (unlikely (status))
2524 _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op);
2526 _cairo_output_stream_printf (surface->xml_node, "\">\n");
2528 for (i = 0; i < num_glyphs; i++) {
2529 status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets,
2530 scaled_font, glyphs[i].index,
2533 if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
2534 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2541 if (unlikely (status))
2544 _cairo_output_stream_printf (surface->xml_node,
2545 " <use xlink:href=\"#glyph%d-%d\" "
2546 "x=\"%f\" y=\"%f\"/>\n",
2547 subset_glyph.font_id,
2548 subset_glyph.subset_glyph_index,
2549 glyphs[i].x, glyphs[i].y);
2552 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2554 return CAIRO_STATUS_SUCCESS;
2557 _cairo_path_fixed_init (&path);
2559 status = _cairo_scaled_font_glyph_path (scaled_font,
2560 (cairo_glyph_t *) glyphs,
2563 if (unlikely (status)) {
2564 _cairo_path_fixed_fini (&path);
2568 status = _cairo_svg_surface_fill (abstract_surface, op, pattern,
2569 &path, CAIRO_FILL_RULE_WINDING,
2570 0.0, CAIRO_ANTIALIAS_SUBPIXEL,
2573 _cairo_path_fixed_fini (&path);
2579 _cairo_svg_surface_get_font_options (void *abstract_surface,
2580 cairo_font_options_t *options)
2582 _cairo_font_options_init_default (options);
2584 cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
2585 cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
2586 cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
2587 _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
2591 static const char **
2592 _cairo_svg_surface_get_supported_mime_types (void *abstract_surface)
2594 return _cairo_svg_supported_mime_types;
2597 static const cairo_surface_backend_t cairo_svg_surface_backend = {
2598 CAIRO_SURFACE_TYPE_SVG,
2599 _cairo_svg_surface_finish,
2601 _cairo_default_context_create,
2603 NULL, /* create_similar: handled by wrapper */
2604 NULL, /* create_similar_image */
2605 NULL, /* map to image */
2606 NULL, /* unmap image */
2608 _cairo_surface_default_source,
2609 NULL, /* acquire_source_image */
2610 NULL, /* release_source_image */
2611 NULL, /* snapshot */
2613 _cairo_svg_surface_copy_page,
2614 _cairo_svg_surface_show_page,
2616 _cairo_svg_surface_get_extents,
2617 _cairo_svg_surface_get_font_options,
2620 NULL, /* mark dirty rectangle */
2622 _cairo_svg_surface_paint,
2623 _cairo_svg_surface_mask,
2624 _cairo_svg_surface_stroke,
2625 _cairo_svg_surface_fill,
2626 _cairo_svg_surface_fill_stroke,
2627 _cairo_svg_surface_show_glyphs,
2628 NULL, /* has_show_text_glyphs */
2629 NULL, /* show_text_glyphs */
2630 _cairo_svg_surface_get_supported_mime_types,
2633 static cairo_status_t
2634 _cairo_svg_document_create (cairo_output_stream_t *output_stream,
2637 cairo_svg_version_t version,
2638 cairo_svg_document_t **document_out)
2640 cairo_svg_document_t *document;
2641 cairo_status_t status, status_ignored;
2643 if (output_stream->status)
2644 return output_stream->status;
2646 document = malloc (sizeof (cairo_svg_document_t));
2647 if (unlikely (document == NULL))
2648 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2650 /* The use of defs for font glyphs imposes no per-subset limit. */
2651 document->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
2652 if (unlikely (document->font_subsets == NULL)) {
2653 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2654 goto CLEANUP_DOCUMENT;
2657 document->output_stream = output_stream;
2658 document->refcount = 1;
2659 document->owner = NULL;
2660 document->finished = FALSE;
2661 document->width = width;
2662 document->height = height;
2664 document->linear_pattern_id = 0;
2665 document->radial_pattern_id = 0;
2666 document->pattern_id = 0;
2667 document->filter_id = 0;
2668 document->clip_id = 0;
2669 document->mask_id = 0;
2671 document->xml_node_defs = _cairo_memory_stream_create ();
2672 status = _cairo_output_stream_get_status (document->xml_node_defs);
2673 if (unlikely (status))
2674 goto CLEANUP_NODE_DEFS;
2676 document->xml_node_glyphs = _cairo_memory_stream_create ();
2677 status = _cairo_output_stream_get_status (document->xml_node_glyphs);
2678 if (unlikely (status))
2679 goto CLEANUP_NODE_GLYPHS;
2681 document->alpha_filter = FALSE;
2683 document->svg_version = version;
2685 *document_out = document;
2686 return CAIRO_STATUS_SUCCESS;
2688 CLEANUP_NODE_GLYPHS:
2689 status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs);
2691 status_ignored = _cairo_output_stream_destroy (document->xml_node_defs);
2692 _cairo_scaled_font_subsets_destroy (document->font_subsets);
2698 static cairo_svg_document_t *
2699 _cairo_svg_document_reference (cairo_svg_document_t *document)
2701 document->refcount++;
2707 _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document)
2709 return document->mask_id++;
2712 static cairo_status_t
2713 _cairo_svg_document_destroy (cairo_svg_document_t *document)
2715 cairo_status_t status;
2717 document->refcount--;
2718 if (document->refcount > 0)
2719 return CAIRO_STATUS_SUCCESS;
2721 status = _cairo_svg_document_finish (document);
2728 static cairo_status_t
2729 _cairo_svg_document_finish (cairo_svg_document_t *document)
2731 cairo_status_t status, status2;
2732 cairo_output_stream_t *output = document->output_stream;
2733 cairo_svg_page_t *page;
2736 if (document->finished)
2737 return CAIRO_STATUS_SUCCESS;
2740 * Should we add DOCTYPE?
2744 * http://tech.groups.yahoo.com/group/svg-developers/message/48562:
2745 * There's a bunch of issues, but just to pick a few:
2746 * - they'll give false positives.
2747 * - they'll give false negatives.
2748 * - they're namespace-unaware.
2749 * - they don't wildcard.
2750 * So when they say OK they really haven't checked anything, when
2751 * they say NOT OK they might be on crack, and like all
2752 * namespace-unaware things they're a dead branch of the XML tree.
2754 * http://jwatt.org/svg/authoring/:
2755 * Unfortunately the SVG DTDs are a source of so many issues that the
2756 * SVG WG has decided not to write one for the upcoming SVG 1.2
2757 * standard. In fact SVG WG members are even telling people not to use
2758 * a DOCTYPE declaration in SVG 1.0 and 1.1 documents.
2761 _cairo_output_stream_printf (output,
2762 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2763 "<svg xmlns=\"http://www.w3.org/2000/svg\" "
2764 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
2765 "width=\"%fpt\" height=\"%fpt\" "
2766 "viewBox=\"0 0 %f %f\" version=\"%s\">\n",
2767 document->width, document->height,
2768 document->width, document->height,
2769 _cairo_svg_internal_version_strings [document->svg_version]);
2771 status = _cairo_svg_document_emit_font_subsets (document);
2773 if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 ||
2774 _cairo_memory_stream_length (document->xml_node_defs) > 0) {
2775 _cairo_output_stream_printf (output, "<defs>\n");
2776 if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
2777 _cairo_output_stream_printf (output, "<g>\n");
2778 _cairo_memory_stream_copy (document->xml_node_glyphs, output);
2779 _cairo_output_stream_printf (output, "</g>\n");
2781 _cairo_memory_stream_copy (document->xml_node_defs, output);
2782 _cairo_output_stream_printf (output, "</defs>\n");
2785 if (document->owner != NULL) {
2786 cairo_svg_surface_t *surface;
2788 surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner);
2789 if (surface->xml_node != NULL &&
2790 _cairo_memory_stream_length (surface->xml_node) > 0) {
2791 if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) {
2792 if (status == CAIRO_STATUS_SUCCESS)
2793 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2797 if (surface->page_set.num_elements > 1 &&
2798 _cairo_svg_version_has_page_set_support (document->svg_version)) {
2799 _cairo_output_stream_printf (output, "<pageSet>\n");
2800 for (i = 0; i < surface->page_set.num_elements; i++) {
2801 page = _cairo_array_index (&surface->page_set, i);
2802 _cairo_output_stream_printf (output, "<page>\n");
2803 _cairo_output_stream_printf (output,
2804 "<g id=\"surface%d\">\n",
2806 _cairo_memory_stream_copy (page->xml_node, output);
2807 _cairo_output_stream_printf (output, "</g>\n</page>\n");
2809 _cairo_output_stream_printf (output, "</pageSet>\n");
2810 } else if (surface->page_set.num_elements > 0) {
2811 page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
2812 _cairo_output_stream_printf (output,
2813 "<g id=\"surface%d\">\n",
2815 _cairo_memory_stream_copy (page->xml_node, output);
2816 _cairo_output_stream_printf (output, "</g>\n");
2820 _cairo_output_stream_printf (output, "</svg>\n");
2822 status2 = _cairo_output_stream_destroy (document->xml_node_glyphs);
2823 if (status == CAIRO_STATUS_SUCCESS)
2826 status2 = _cairo_output_stream_destroy (document->xml_node_defs);
2827 if (status == CAIRO_STATUS_SUCCESS)
2830 status2 = _cairo_output_stream_destroy (output);
2831 if (status == CAIRO_STATUS_SUCCESS)
2834 document->finished = TRUE;
2840 _cairo_svg_surface_set_paginated_mode (void *abstract_surface,
2841 cairo_paginated_mode_t paginated_mode)
2843 cairo_svg_surface_t *surface = abstract_surface;
2845 surface->paginated_mode = paginated_mode;
2849 _cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface)
2851 cairo_svg_surface_t *surface = abstract_surface;
2852 cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
2854 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) {
2855 status = _cairo_svg_surface_analyze_operator (surface,
2856 CAIRO_OPERATOR_SOURCE);
2859 return status == CAIRO_INT_STATUS_SUCCESS;
2862 static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
2863 NULL /*_cairo_svg_surface_start_page*/,
2864 _cairo_svg_surface_set_paginated_mode,
2865 NULL, /* _cairo_svg_surface_set_bounding_box */
2866 NULL, /* _cairo_svg_surface_set_fallback_images_required */
2867 _cairo_svg_surface_supports_fine_grained_fallbacks,