ShaderEffect subclasses from Effect, not DependencyObject
[moon.git] / cairo / src / cairo-svg-surface.c
blobf392c8ed22aef9336c8ee516dc3bc6814882d312
1 /* vim: set sw=4 sts=4: -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
2 /* cairo - a vector graphics library with display and print output
4 * Copyright © 2004 Red Hat, Inc
5 * Copyright © 2005-2007 Emmanuel Pacaud <emmanuel.pacaud@free.fr>
6 * Copyright © 2006 Red Hat, Inc
8 * This library is free software; you can redistribute it and/or
9 * modify it either under the terms of the GNU Lesser General Public
10 * License version 2.1 as published by the Free Software Foundation
11 * (the "LGPL") or, at your option, under the terms of the Mozilla
12 * Public License Version 1.1 (the "MPL"). If you do not alter this
13 * notice, a recipient may use your version of this file under either
14 * the MPL or the LGPL.
16 * You should have received a copy of the LGPL along with this library
17 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 * You should have received a copy of the MPL along with this library
20 * in the file COPYING-MPL-1.1
22 * The contents of this file are subject to the Mozilla Public License
23 * Version 1.1 (the "License"); you may not use this file except in
24 * compliance with the License. You may obtain a copy of the License at
25 * http://www.mozilla.org/MPL/
27 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
28 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
29 * the specific language governing rights and limitations.
31 * The Original Code is the cairo graphics library.
33 * The Initial Developer of the Original Code is University of Southern
34 * California.
36 * Contributor(s):
37 * Kristian Høgsberg <krh@redhat.com>
38 * Emmanuel Pacaud <emmanuel.pacaud@free.fr>
39 * Carl Worth <cworth@cworth.org>
42 #define _BSD_SOURCE /* for snprintf() */
43 #include "cairoint.h"
44 #include "cairo-svg.h"
45 #include "cairo-analysis-surface-private.h"
46 #include "cairo-svg-surface-private.h"
47 #include "cairo-meta-surface-private.h"
48 #include "cairo-output-stream-private.h"
49 #include "cairo-path-fixed-private.h"
50 #include "cairo-paginated-private.h"
51 #include "cairo-scaled-font-subsets-private.h"
53 typedef struct cairo_svg_page cairo_svg_page_t;
55 static const int invalid_pattern_id = -1;
57 static const cairo_svg_version_t _cairo_svg_versions[] =
59 CAIRO_SVG_VERSION_1_1,
60 CAIRO_SVG_VERSION_1_2
63 #define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
65 static cairo_bool_t
66 _cairo_svg_version_has_page_set_support (cairo_svg_version_t version)
68 return version > CAIRO_SVG_VERSION_1_1;
71 static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] =
73 "SVG 1.1",
74 "SVG 1.2"
77 static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] =
79 "1.1",
80 "1.2"
83 struct cairo_svg_page {
84 unsigned int surface_id;
85 unsigned int clip_level;
86 cairo_output_stream_t *xml_node;
89 struct cairo_svg_document {
90 cairo_output_stream_t *output_stream;
91 unsigned long refcount;
92 cairo_surface_t *owner;
93 cairo_bool_t finished;
95 double width;
96 double height;
98 cairo_output_stream_t *xml_node_defs;
99 cairo_output_stream_t *xml_node_glyphs;
101 unsigned int surface_id;
102 unsigned int linear_pattern_id;
103 unsigned int radial_pattern_id;
104 unsigned int pattern_id;
105 unsigned int filter_id;
106 unsigned int clip_id;
107 unsigned int mask_id;
109 cairo_bool_t alpha_filter;
111 cairo_array_t meta_snapshots;
113 cairo_svg_version_t svg_version;
115 cairo_scaled_font_subsets_t *font_subsets;
118 typedef struct {
119 unsigned int id;
120 cairo_meta_surface_t *meta;
121 } cairo_meta_snapshot_t;
123 static cairo_status_t
124 _cairo_svg_document_create (cairo_output_stream_t *stream,
125 double width,
126 double height,
127 cairo_svg_version_t version,
128 cairo_svg_document_t **document_out);
130 static cairo_status_t
131 _cairo_svg_document_destroy (cairo_svg_document_t *document);
133 static cairo_status_t
134 _cairo_svg_document_finish (cairo_svg_document_t *document);
136 static cairo_svg_document_t *
137 _cairo_svg_document_reference (cairo_svg_document_t *document);
139 static unsigned int
140 _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document);
142 static cairo_surface_t *
143 _cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
144 cairo_content_t content,
145 double width,
146 double height);
147 static cairo_surface_t *
148 _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
149 double width,
150 double height,
151 cairo_svg_version_t version);
153 static const cairo_surface_backend_t cairo_svg_surface_backend;
154 static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend;
157 * cairo_svg_surface_create_for_stream:
158 * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
159 * to indicate a no-op @write_func. With a no-op @write_func,
160 * the surface may be queried or used as a source without
161 * generating any temporary files.
162 * @closure: the closure argument for @write_func
163 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
164 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
166 * Creates a SVG surface of the specified size in points to be written
167 * incrementally to the stream represented by @write_func and @closure.
169 * Return value: a pointer to the newly created surface. The caller
170 * owns the surface and should call cairo_surface_destroy() when done
171 * with it.
173 * This function always returns a valid pointer, but it will return a
174 * pointer to a "nil" surface if an error such as out of memory
175 * occurs. You can use cairo_surface_status() to check for this.
177 * Since: 1.2
179 cairo_surface_t *
180 cairo_svg_surface_create_for_stream (cairo_write_func_t write_func,
181 void *closure,
182 double width,
183 double height)
185 cairo_output_stream_t *stream;
187 stream = _cairo_output_stream_create (write_func, NULL, closure);
188 if (_cairo_output_stream_get_status (stream))
189 return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
191 return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
195 * cairo_svg_surface_create:
196 * @filename: a filename for the SVG output (must be writable), %NULL may be
197 * used to specify no output. This will generate a SVG surface that
198 * may be queried and used as a source, without generating a
199 * temporary file.
200 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
201 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
203 * Creates a SVG surface of the specified size in points to be written
204 * to @filename.
206 * Return value: a pointer to the newly created surface. The caller
207 * owns the surface and should call cairo_surface_destroy() when done
208 * with it.
210 * This function always returns a valid pointer, but it will return a
211 * pointer to a "nil" surface if an error such as out of memory
212 * occurs. You can use cairo_surface_status() to check for this.
214 * Since: 1.2
216 cairo_surface_t *
217 cairo_svg_surface_create (const char *filename,
218 double width,
219 double height)
221 cairo_output_stream_t *stream;
223 stream = _cairo_output_stream_create_for_filename (filename);
224 if (_cairo_output_stream_get_status (stream))
225 return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
227 return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
230 static cairo_bool_t
231 _cairo_surface_is_svg (cairo_surface_t *surface)
233 return surface->backend == &cairo_svg_surface_backend;
236 /* If the abstract_surface is a paginated surface, and that paginated
237 * surface's target is a svg_surface, then set svg_surface to that
238 * target. Otherwise return %CAIRO_STATUS_SURFACE_TYPE_MISMATCH.
240 static cairo_status_t
241 _extract_svg_surface (cairo_surface_t *surface,
242 cairo_svg_surface_t **svg_surface)
244 cairo_surface_t *target;
246 if (surface->status)
247 return surface->status;
249 if (! _cairo_surface_is_paginated (surface))
250 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
252 target = _cairo_paginated_surface_get_target (surface);
253 if (target->status)
254 return target->status;
256 if (! _cairo_surface_is_svg (target))
257 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
259 *svg_surface = (cairo_svg_surface_t *) target;
261 return CAIRO_STATUS_SUCCESS;
265 * cairo_svg_surface_restrict_to_version:
266 * @surface: a SVG #cairo_surface_t
267 * @version: SVG version
269 * Restricts the generated SVG file to @version. See cairo_svg_get_versions()
270 * for a list of available version values that can be used here.
272 * This function should only be called before any drawing operations
273 * have been performed on the given surface. The simplest way to do
274 * this is to call this function immediately after creating the
275 * surface.
277 * Since: 1.2
279 void
280 cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface,
281 cairo_svg_version_t version)
283 cairo_svg_surface_t *surface = NULL; /* hide compiler warning */
284 cairo_status_t status;
286 status = _extract_svg_surface (abstract_surface, &surface);
287 if (status) {
288 status = _cairo_surface_set_error (abstract_surface, status);
289 return;
292 if (version < CAIRO_SVG_VERSION_LAST)
293 surface->document->svg_version = version;
297 * cairo_svg_get_versions:
298 * @versions: supported version list
299 * @num_versions: list length
301 * Used to retrieve the list of supported versions. See
302 * cairo_svg_surface_restrict_to_version().
304 * Since: 1.2
306 void
307 cairo_svg_get_versions (cairo_svg_version_t const **versions,
308 int *num_versions)
310 if (versions != NULL)
311 *versions = _cairo_svg_versions;
313 if (num_versions != NULL)
314 *num_versions = CAIRO_SVG_VERSION_LAST;
318 * cairo_svg_version_to_string:
319 * @version: a version id
321 * Get the string representation of the given @version id. This function
322 * will return %NULL if @version isn't valid. See cairo_svg_get_versions()
323 * for a way to get the list of valid version ids.
325 * Return value: the string associated to given version.
327 * Since: 1.2
329 const char *
330 cairo_svg_version_to_string (cairo_svg_version_t version)
332 if (version >= CAIRO_SVG_VERSION_LAST)
333 return NULL;
335 return _cairo_svg_version_strings[version];
338 static cairo_surface_t *
339 _cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
340 cairo_content_t content,
341 double width,
342 double height)
344 cairo_svg_surface_t *surface;
345 cairo_surface_t *paginated;
346 cairo_status_t status, status_ignored;
348 surface = malloc (sizeof (cairo_svg_surface_t));
349 if (surface == NULL)
350 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
352 _cairo_surface_init (&surface->base, &cairo_svg_surface_backend,
353 content);
355 surface->width = width;
356 surface->height = height;
358 surface->document = _cairo_svg_document_reference (document);
360 surface->clip_level = 0;
362 surface->id = document->surface_id++;
363 surface->base_clip = document->clip_id++;
364 surface->is_base_clip_emitted = FALSE;
366 surface->xml_node = _cairo_memory_stream_create ();
367 status = _cairo_output_stream_get_status (surface->xml_node);
368 if (status)
369 goto CLEANUP;
371 _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t));
373 if (content == CAIRO_CONTENT_COLOR) {
374 _cairo_output_stream_printf (surface->xml_node,
375 "<rect width=\"%f\" height=\"%f\" "
376 "style=\"opacity:1;stroke:none;"
377 "fill:rgb(0,0,0);\"/>\n",
378 width, height);
379 status = _cairo_output_stream_get_status (surface->xml_node);
380 if (status)
381 goto CLEANUP;
384 surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
385 surface->force_fallbacks = FALSE;
386 surface->content = content;
388 paginated = _cairo_paginated_surface_create (&surface->base,
389 surface->content,
390 surface->width,
391 surface->height,
392 &cairo_svg_surface_paginated_backend);
393 status = paginated->status;
394 if (status == CAIRO_STATUS_SUCCESS) {
395 /* paginated keeps the only reference to surface now, drop ours */
396 cairo_surface_destroy (&surface->base);
397 return paginated;
400 /* ignore status as we are on the error path */
401 CLEANUP:
402 status_ignored = _cairo_output_stream_destroy (surface->xml_node);
403 status_ignored = _cairo_svg_document_destroy (document);
405 free (surface);
407 return _cairo_surface_create_in_error (status);
410 static cairo_surface_t *
411 _cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
412 double width,
413 double height,
414 cairo_svg_version_t version)
416 cairo_svg_document_t *document = NULL; /* silence compiler */
417 cairo_surface_t *surface;
418 cairo_status_t status;
420 status = _cairo_svg_document_create (stream,
421 width, height, version,
422 &document);
423 if (status) {
424 surface = _cairo_surface_create_in_error (status);
425 /* consume the output stream on behalf of caller */
426 status = _cairo_output_stream_destroy (stream);
427 return surface;
430 surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA,
431 width, height);
432 if (surface->status) {
433 status = _cairo_svg_document_destroy (document);
434 return surface;
437 document->owner = surface;
438 status = _cairo_svg_document_destroy (document);
439 /* the ref count should be 2 at this point */
440 assert (status == CAIRO_STATUS_SUCCESS);
442 return surface;
445 static cairo_svg_page_t *
446 _cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
448 unsigned int i;
449 cairo_svg_page_t page;
450 cairo_output_stream_t *stream;
451 cairo_status_t status;
453 stream = _cairo_memory_stream_create ();
454 if (_cairo_output_stream_get_status (stream)) {
455 status = _cairo_output_stream_destroy (stream);
456 return NULL;
459 page.surface_id = surface->id;
460 page.clip_level = surface->clip_level;
461 page.xml_node = surface->xml_node;
463 if (_cairo_array_append (&surface->page_set, &page)) {
464 status = _cairo_output_stream_destroy (stream);
465 return NULL;
468 surface->xml_node = stream;
469 surface->clip_level = 0;
471 for (i = 0; i < page.clip_level; i++)
472 _cairo_output_stream_printf (page.xml_node, "</g>\n");
474 return _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
477 static cairo_int_status_t
478 _cairo_svg_surface_copy_page (void *abstract_surface)
480 cairo_svg_surface_t *surface = abstract_surface;
481 cairo_svg_page_t *page;
483 page = _cairo_svg_surface_store_page (surface);
484 if (page == NULL)
485 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
487 _cairo_memory_stream_copy (page->xml_node, surface->xml_node);
488 surface->clip_level = page->clip_level;
490 return CAIRO_STATUS_SUCCESS;
493 static cairo_int_status_t
494 _cairo_svg_surface_show_page (void *abstract_surface)
496 cairo_svg_surface_t *surface = abstract_surface;
498 if (_cairo_svg_surface_store_page (surface) == NULL)
499 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
501 return CAIRO_STATUS_SUCCESS;
504 static void
505 _cairo_svg_surface_emit_transform (cairo_output_stream_t *output,
506 char const *attribute_str,
507 const cairo_matrix_t *object_matrix,
508 const cairo_matrix_t *parent_matrix)
510 cairo_matrix_t matrix = *object_matrix;
512 if (parent_matrix != NULL)
513 cairo_matrix_multiply (&matrix, &matrix, parent_matrix);
515 if (!_cairo_matrix_is_identity (&matrix))
516 _cairo_output_stream_printf (output,
517 "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"",
518 attribute_str,
519 matrix.xx, matrix.yx,
520 matrix.xy, matrix.yy,
521 matrix.x0, matrix.y0);
524 typedef struct
526 cairo_output_stream_t *output;
527 cairo_matrix_t *ctm_inverse;
528 } svg_path_info_t;
530 static cairo_status_t
531 _cairo_svg_path_move_to (void *closure, cairo_point_t *point)
533 svg_path_info_t *info = closure;
534 double x = _cairo_fixed_to_double (point->x);
535 double y = _cairo_fixed_to_double (point->y);
537 if (info->ctm_inverse)
538 cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
540 _cairo_output_stream_printf (info->output, "M %f %f ", x, y);
542 return CAIRO_STATUS_SUCCESS;
545 static cairo_status_t
546 _cairo_svg_path_line_to (void *closure, cairo_point_t *point)
548 svg_path_info_t *info = closure;
549 double x = _cairo_fixed_to_double (point->x);
550 double y = _cairo_fixed_to_double (point->y);
552 if (info->ctm_inverse)
553 cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
555 _cairo_output_stream_printf (info->output, "L %f %f ", x, y);
557 return CAIRO_STATUS_SUCCESS;
560 static cairo_status_t
561 _cairo_svg_path_curve_to (void *closure,
562 cairo_point_t *b,
563 cairo_point_t *c,
564 cairo_point_t *d)
566 svg_path_info_t *info = closure;
567 double bx = _cairo_fixed_to_double (b->x);
568 double by = _cairo_fixed_to_double (b->y);
569 double cx = _cairo_fixed_to_double (c->x);
570 double cy = _cairo_fixed_to_double (c->y);
571 double dx = _cairo_fixed_to_double (d->x);
572 double dy = _cairo_fixed_to_double (d->y);
574 if (info->ctm_inverse) {
575 cairo_matrix_transform_point (info->ctm_inverse, &bx, &by);
576 cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy);
577 cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy);
580 _cairo_output_stream_printf (info->output,
581 "C %f %f %f %f %f %f ",
582 bx, by, cx, cy, dx, dy);
584 return CAIRO_STATUS_SUCCESS;
587 static cairo_status_t
588 _cairo_svg_path_close_path (void *closure)
590 svg_path_info_t *info = closure;
592 _cairo_output_stream_printf (info->output, "Z ");
594 return CAIRO_STATUS_SUCCESS;
597 static cairo_status_t
598 _cairo_svg_surface_emit_path (cairo_output_stream_t *output,
599 cairo_path_fixed_t *path,
600 cairo_matrix_t *ctm_inverse)
602 cairo_status_t status;
603 svg_path_info_t info;
605 _cairo_output_stream_printf (output, "d=\"");
607 info.output = output;
608 info.ctm_inverse = ctm_inverse;
609 status = _cairo_path_fixed_interpret (path,
610 CAIRO_DIRECTION_FORWARD,
611 _cairo_svg_path_move_to,
612 _cairo_svg_path_line_to,
613 _cairo_svg_path_curve_to,
614 _cairo_svg_path_close_path,
615 &info);
616 if (status)
617 return status;
619 _cairo_output_stream_printf (output, "\"");
621 return status;
624 static cairo_int_status_t
625 _cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document,
626 cairo_scaled_font_t *scaled_font,
627 unsigned long glyph_index)
629 cairo_scaled_glyph_t *scaled_glyph;
630 cairo_int_status_t status;
632 status = _cairo_scaled_glyph_lookup (scaled_font,
633 glyph_index,
634 CAIRO_SCALED_GLYPH_INFO_METRICS|
635 CAIRO_SCALED_GLYPH_INFO_PATH,
636 &scaled_glyph);
637 if (status)
638 return status;
640 _cairo_output_stream_printf (document->xml_node_glyphs,
641 "<path style=\"stroke:none;\" ");
643 status = _cairo_svg_surface_emit_path (document->xml_node_glyphs, scaled_glyph->path, NULL);
644 if (status)
645 return status;
647 _cairo_output_stream_printf (document->xml_node_glyphs,
648 "/>\n");
650 return status;
653 static cairo_int_status_t
654 _cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document,
655 cairo_scaled_font_t *scaled_font,
656 unsigned long glyph_index)
658 cairo_image_surface_t *image;
659 cairo_scaled_glyph_t *scaled_glyph;
660 cairo_status_t status;
661 uint8_t *row, *byte;
662 int rows, cols;
663 int x, y, bit;
665 status = _cairo_scaled_glyph_lookup (scaled_font,
666 glyph_index,
667 CAIRO_SCALED_GLYPH_INFO_METRICS|
668 CAIRO_SCALED_GLYPH_INFO_SURFACE,
669 &scaled_glyph);
670 if (status)
671 return status;
673 image = scaled_glyph->surface;
674 if (image->format != CAIRO_FORMAT_A1) {
675 image = _cairo_image_surface_clone (image, CAIRO_FORMAT_A1);
676 if (cairo_surface_status (&image->base))
677 return cairo_surface_status (&image->base);
680 _cairo_output_stream_printf (document->xml_node_glyphs, "<g");
681 _cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform",
682 &image->base.device_transform_inverse, NULL);
683 _cairo_output_stream_printf (document->xml_node_glyphs, ">/n");
685 for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) {
686 for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
687 uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
688 for (bit = 7; bit >= 0 && x < image->width; bit--, x++) {
689 if (output_byte & (1 << bit)) {
690 _cairo_output_stream_printf (document->xml_node_glyphs,
691 "<rect x=\"%d\" y=\"%d\" width=\"1\" height=\"1\"/>\n",
692 x, y);
697 _cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n");
699 if (image != scaled_glyph->surface)
700 cairo_surface_destroy (&image->base);
702 return CAIRO_STATUS_SUCCESS;
705 static cairo_status_t
706 _cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
707 cairo_scaled_font_t *scaled_font,
708 unsigned long scaled_font_glyph_index,
709 unsigned int font_id,
710 unsigned int subset_glyph_index)
712 cairo_status_t status;
714 _cairo_output_stream_printf (document->xml_node_glyphs,
715 "<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n",
716 font_id,
717 subset_glyph_index);
719 status = _cairo_svg_document_emit_outline_glyph_data (document,
720 scaled_font,
721 scaled_font_glyph_index);
722 if (status == CAIRO_INT_STATUS_UNSUPPORTED)
723 status = _cairo_svg_document_emit_bitmap_glyph_data (document,
724 scaled_font,
725 scaled_font_glyph_index);
726 if (status)
727 return status;
729 _cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n");
731 return CAIRO_STATUS_SUCCESS;
734 static cairo_status_t
735 _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
736 void *closure)
738 cairo_svg_document_t *document = closure;
739 unsigned int i;
740 cairo_status_t status = CAIRO_STATUS_SUCCESS;
742 _cairo_scaled_font_freeze_cache (font_subset->scaled_font);
743 for (i = 0; i < font_subset->num_glyphs; i++) {
744 status = _cairo_svg_document_emit_glyph (document,
745 font_subset->scaled_font,
746 font_subset->glyphs[i],
747 font_subset->font_id, i);
748 if (status)
749 break;
751 _cairo_scaled_font_thaw_cache (font_subset->scaled_font);
753 return status;
756 static cairo_status_t
757 _cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
759 cairo_status_t status;
761 status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
762 _cairo_svg_document_emit_font_subset,
763 document);
764 if (status)
765 goto FAIL;
767 status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
768 _cairo_svg_document_emit_font_subset,
769 document);
771 FAIL:
772 _cairo_scaled_font_subsets_destroy (document->font_subsets);
773 document->font_subsets = NULL;
775 return status;
778 static char const *
779 _cairo_svg_surface_operators[] = {
780 "clear",
782 "src", "src-over", "src-in",
783 "src-out", "src-atop",
785 "dst", "dst-over", "dst-in",
786 "dst-out", "dst-atop",
788 "xor", "plus",
789 "color-dodge", /* FIXME: saturate ? */
792 static cairo_bool_t
793 _cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface,
794 cairo_operator_t op)
796 /* guard against newly added operators */
797 if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators))
798 return CAIRO_INT_STATUS_UNSUPPORTED;
800 /* allow operators being NULL if they are unsupported */
801 if (_cairo_svg_surface_operators[op] == NULL)
802 return CAIRO_INT_STATUS_UNSUPPORTED;
804 return CAIRO_STATUS_SUCCESS;
807 static cairo_int_status_t
808 _cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface,
809 cairo_operator_t op,
810 const cairo_pattern_t *pattern)
812 cairo_svg_document_t *document = surface->document;
814 if (surface->force_fallbacks &&
815 surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
817 return CAIRO_INT_STATUS_UNSUPPORTED;
820 /* SVG doesn't support extend reflect for image pattern */
821 if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
822 pattern->extend == CAIRO_EXTEND_REFLECT)
823 return CAIRO_INT_STATUS_UNSUPPORTED;
825 if (document->svg_version >= CAIRO_SVG_VERSION_1_2)
826 return _cairo_svg_surface_analyze_operator (surface, op);
828 if (op == CAIRO_OPERATOR_OVER)
829 return CAIRO_STATUS_SUCCESS;
831 /* The SOURCE operator is only supported if there is nothing
832 * painted underneath. */
833 if (op == CAIRO_OPERATOR_SOURCE)
834 return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
836 return CAIRO_INT_STATUS_UNSUPPORTED;
839 static cairo_int_status_t
840 _cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface,
841 cairo_operator_t op,
842 const cairo_pattern_t *pattern)
844 if (_cairo_svg_surface_analyze_operation (surface, op, pattern)
845 != CAIRO_INT_STATUS_UNSUPPORTED)
847 return TRUE;
848 } else {
849 return FALSE;
853 static cairo_surface_t *
854 _cairo_svg_surface_create_similar (void *abstract_src,
855 cairo_content_t content,
856 int width,
857 int height)
859 return _cairo_meta_surface_create (content, width, height);
862 static cairo_status_t
863 _cairo_svg_surface_finish (void *abstract_surface)
865 cairo_status_t status, status2;
866 cairo_svg_surface_t *surface = abstract_surface;
867 cairo_svg_document_t *document = surface->document;
868 cairo_svg_page_t *page;
869 unsigned int i;
871 if (_cairo_paginated_surface_get_target (document->owner) == &surface->base)
872 status = _cairo_svg_document_finish (document);
873 else
874 status = CAIRO_STATUS_SUCCESS;
876 if (surface->xml_node != NULL) {
877 status2 = _cairo_output_stream_destroy (surface->xml_node);
878 if (status == CAIRO_STATUS_SUCCESS)
879 status = status2;
882 for (i = 0; i < surface->page_set.num_elements; i++) {
883 page = _cairo_array_index (&surface->page_set, i);
884 status2 = _cairo_output_stream_destroy (page->xml_node);
885 if (status == CAIRO_STATUS_SUCCESS)
886 status = status2;
888 _cairo_array_fini (&surface->page_set);
890 status2 = _cairo_svg_document_destroy (document);
891 if (status == CAIRO_STATUS_SUCCESS)
892 status = status2;
894 return status;
897 static void
898 _cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document)
900 if (document->alpha_filter)
901 return;
903 _cairo_output_stream_printf (document->xml_node_defs,
904 "<filter id=\"alpha\" "
905 "filterUnits=\"objectBoundingBox\" "
906 "x=\"0%%\" y=\"0%%\" "
907 "width=\"100%%\" height=\"100%%\">\n"
908 " <feColorMatrix type=\"matrix\" "
909 "in=\"SourceGraphic\" "
910 "values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n"
911 "</filter>\n");
913 document->alpha_filter = TRUE;
916 typedef struct {
917 cairo_output_stream_t *output;
918 unsigned int in_mem;
919 unsigned int trailing;
920 unsigned char src[3];
921 } base64_write_closure_t;
923 static char const base64_table[64] =
924 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
926 static cairo_status_t
927 base64_write_func (void *closure,
928 const unsigned char *data,
929 unsigned int length)
931 base64_write_closure_t *info = (base64_write_closure_t *) closure;
932 unsigned int i;
933 unsigned char *src;
935 src = info->src;
937 if (info->in_mem + length < 3) {
938 for (i = 0; i < length; i++) {
939 src[i + info->in_mem] = *data++;
941 info->in_mem += length;
942 return CAIRO_STATUS_SUCCESS;
945 do {
946 unsigned char dst[4];
948 for (i = info->in_mem; i < 3; i++) {
949 src[i] = *data++;
950 length--;
952 info->in_mem = 0;
954 dst[0] = base64_table[src[0] >> 2];
955 dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4];
956 dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6];
957 dst[3] = base64_table[src[2] & 0xfc >> 2];
958 /* Special case for the last missing bits */
959 switch (info->trailing) {
960 case 2:
961 dst[2] = '=';
962 case 1:
963 dst[3] = '=';
964 default:
965 break;
967 _cairo_output_stream_write (info->output, dst, 4);
968 } while (length >= 3);
970 for (i = 0; i < length; i++) {
971 src[i] = *data++;
973 info->in_mem = length;
975 return _cairo_output_stream_get_status (info->output);
978 static cairo_int_status_t
979 _cairo_surface_base64_encode (cairo_surface_t *surface,
980 cairo_output_stream_t *output)
982 cairo_status_t status;
983 base64_write_closure_t info;
984 unsigned int i;
986 info.output = output;
987 info.in_mem = 0;
988 info.trailing = 0;
990 _cairo_output_stream_printf (info.output, "data:image/png;base64,");
992 status = cairo_surface_write_to_png_stream (surface, base64_write_func,
993 (void *) &info);
995 if (status)
996 return status;
998 if (info.in_mem > 0) {
999 for (i = info.in_mem; i < 3; i++)
1000 info.src[i] = '\x0';
1001 info.trailing = 3 - info.in_mem;
1002 info.in_mem = 3;
1003 status = base64_write_func (&info, NULL, 0);
1006 return status;
1009 static void
1010 _cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
1011 cairo_svg_surface_t *surface,
1012 cairo_operator_t op)
1014 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1015 op != CAIRO_OPERATOR_OVER) {
1016 _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]);
1017 if (!_cairo_operator_bounded_by_source (op))
1018 _cairo_output_stream_printf (output, " clip-to-self=\"true\"");
1022 static void
1023 _cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output,
1024 cairo_svg_surface_t *surface,
1025 cairo_operator_t op)
1027 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1028 op != CAIRO_OPERATOR_OVER) {
1029 _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]);
1030 if (!_cairo_operator_bounded_by_source (op))
1031 _cairo_output_stream_printf (output, "clip-to-self:true;");
1035 static cairo_status_t
1036 _cairo_svg_surface_emit_composite_image_pattern (cairo_output_stream_t *output,
1037 cairo_svg_surface_t *svg_surface,
1038 cairo_operator_t op,
1039 cairo_surface_pattern_t *pattern,
1040 int pattern_id,
1041 const cairo_matrix_t *parent_matrix,
1042 const char *extra_attributes)
1044 cairo_rectangle_int_t extents;
1045 cairo_status_t status;
1046 cairo_matrix_t p2u;
1048 status = _cairo_surface_get_extents (pattern->surface, &extents);
1049 if (status)
1050 return status;
1052 p2u = pattern->base.matrix;
1053 status = cairo_matrix_invert (&p2u);
1054 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1055 assert (status == CAIRO_STATUS_SUCCESS);
1057 if (pattern_id != invalid_pattern_id) {
1058 _cairo_output_stream_printf (output,
1059 "<pattern id=\"pattern%d\" "
1060 "patternUnits=\"userSpaceOnUse\" "
1061 "width=\"%d\" height=\"%d\"",
1062 pattern_id,
1063 extents.width, extents.height);
1064 _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
1065 _cairo_output_stream_printf (output, ">\n");
1068 _cairo_output_stream_printf (output,
1069 " <image width=\"%d\" height=\"%d\"",
1070 extents.width, extents.height);
1072 if (pattern_id == invalid_pattern_id) {
1073 _cairo_svg_surface_emit_operator (output, svg_surface, op);
1074 _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
1077 if (extra_attributes)
1078 _cairo_output_stream_printf (output, " %s", extra_attributes);
1080 _cairo_output_stream_printf (output, " xlink:href=\"");
1082 status = _cairo_surface_base64_encode (pattern->surface, output);
1084 _cairo_output_stream_printf (output, "\"/>\n");
1086 if (pattern_id != invalid_pattern_id)
1087 _cairo_output_stream_printf (output, "</pattern>\n");
1089 return status;
1092 static cairo_status_t
1093 _cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document,
1094 cairo_meta_surface_t *surface,
1095 int *id)
1097 cairo_status_t status;
1098 cairo_surface_t *paginated_surface;
1099 cairo_svg_surface_t *svg_surface;
1100 cairo_meta_snapshot_t new_snapshot;
1101 cairo_array_t *page_set;
1103 cairo_output_stream_t *contents;
1104 cairo_meta_surface_t *meta;
1105 cairo_meta_snapshot_t *snapshot;
1106 unsigned int num_elements;
1107 unsigned int i;
1109 /* search in already emitted meta snapshots */
1110 num_elements = document->meta_snapshots.num_elements;
1111 for (i = 0; i < num_elements; i++) {
1112 snapshot = _cairo_array_index (&document->meta_snapshots, i);
1113 meta = snapshot->meta;
1114 if (meta->commands.num_elements == surface->commands.num_elements &&
1115 _cairo_array_index (&meta->commands, 0) == _cairo_array_index (&surface->commands, 0)) {
1116 *id = snapshot->id;
1117 return CAIRO_STATUS_SUCCESS;
1121 meta = (cairo_meta_surface_t *) _cairo_surface_snapshot (&surface->base);
1122 paginated_surface = _cairo_svg_surface_create_for_document (document,
1123 meta->content,
1124 meta->width_pixels,
1125 meta->height_pixels);
1126 if (paginated_surface->status) {
1127 cairo_surface_destroy (&meta->base);
1128 return paginated_surface->status;
1131 svg_surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (paginated_surface);
1132 cairo_surface_set_fallback_resolution (paginated_surface,
1133 document->owner->x_fallback_resolution,
1134 document->owner->y_fallback_resolution);
1136 status = _cairo_meta_surface_replay (&meta->base, paginated_surface);
1137 if (status) {
1138 cairo_surface_destroy (&meta->base);
1139 cairo_surface_destroy (paginated_surface);
1140 return status;
1143 cairo_surface_show_page (paginated_surface);
1144 status = cairo_surface_status (paginated_surface);
1145 if (status) {
1146 cairo_surface_destroy (&meta->base);
1147 cairo_surface_destroy (paginated_surface);
1148 return status;
1151 new_snapshot.meta = meta;
1152 new_snapshot.id = svg_surface->id;
1153 status = _cairo_array_append (&document->meta_snapshots, &new_snapshot);
1154 if (status) {
1155 cairo_surface_destroy (&meta->base);
1156 cairo_surface_destroy (paginated_surface);
1157 return status;
1160 if (!svg_surface->is_base_clip_emitted) {
1161 svg_surface->is_base_clip_emitted = TRUE;
1162 _cairo_output_stream_printf (document->xml_node_defs,
1163 "<clipPath id=\"clip%d\">\n"
1164 " <rect width=\"%f\" height=\"%f\"/>\n"
1165 "</clipPath>\n",
1166 svg_surface->base_clip,
1167 svg_surface->width,
1168 svg_surface->height);
1171 if (meta->content == CAIRO_CONTENT_ALPHA) {
1172 _cairo_svg_surface_emit_alpha_filter (document);
1173 _cairo_output_stream_printf (document->xml_node_defs,
1174 "<g id=\"surface%d\" "
1175 "clip-path=\"url(#clip%d)\" "
1176 "filter=\"url(#alpha)\">\n",
1177 svg_surface->id,
1178 svg_surface->base_clip);
1179 } else {
1180 _cairo_output_stream_printf (document->xml_node_defs,
1181 "<g id=\"surface%d\" "
1182 "clip-path=\"url(#clip%d)\">\n",
1183 svg_surface->id,
1184 svg_surface->base_clip);
1187 contents = svg_surface->xml_node;
1188 page_set = &svg_surface->page_set;
1190 if (_cairo_memory_stream_length (contents) > 0) {
1191 if (_cairo_svg_surface_store_page (svg_surface) == NULL) {
1192 cairo_surface_destroy (paginated_surface);
1193 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1197 if (page_set->num_elements > 0) {
1198 cairo_svg_page_t *page;
1200 page = _cairo_array_index (page_set, page_set->num_elements - 1);
1201 _cairo_memory_stream_copy (page->xml_node, document->xml_node_defs);
1204 _cairo_output_stream_printf (document->xml_node_defs, "</g>\n");
1206 *id = new_snapshot.id;
1208 status = cairo_surface_status (paginated_surface);
1209 cairo_surface_destroy (paginated_surface);
1211 /* FIXME: cairo_paginated_surface doesn't take a ref to the
1212 * passed in target surface so we can't call destroy here.
1213 * cairo_paginated_surface should be fixed, but for now just
1214 * work around it. */
1216 /* cairo_surface_destroy (svg_surface); */
1218 return status;
1221 static cairo_status_t
1222 _cairo_svg_surface_emit_composite_meta_pattern (cairo_output_stream_t *output,
1223 cairo_svg_surface_t *surface,
1224 cairo_operator_t op,
1225 cairo_surface_pattern_t *pattern,
1226 int pattern_id,
1227 const cairo_matrix_t *parent_matrix,
1228 const char *extra_attributes)
1230 cairo_svg_document_t *document = surface->document;
1231 cairo_meta_surface_t *meta_surface;
1232 cairo_matrix_t p2u;
1233 cairo_status_t status;
1234 int id = 0;
1236 p2u = pattern->base.matrix;
1237 status = cairo_matrix_invert (&p2u);
1238 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1239 assert (status == CAIRO_STATUS_SUCCESS);
1241 meta_surface = (cairo_meta_surface_t *) pattern->surface;
1243 status = _cairo_svg_surface_emit_meta_surface (document, meta_surface, &id);
1244 if (status)
1245 return status;
1247 if (pattern_id != invalid_pattern_id) {
1248 _cairo_output_stream_printf (output,
1249 "<pattern id=\"pattern%d\" "
1250 "patternUnits=\"userSpaceOnUse\" "
1251 "width=\"%d\" height=\"%d\"",
1252 pattern_id,
1253 meta_surface->width_pixels,
1254 meta_surface->height_pixels);
1255 _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
1256 _cairo_output_stream_printf (output, ">\n");
1259 _cairo_output_stream_printf (output,
1260 "<use xlink:href=\"#surface%d\"",
1261 id);
1263 if (pattern_id == invalid_pattern_id) {
1264 _cairo_svg_surface_emit_operator (output, surface, op);
1265 _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
1268 if (extra_attributes)
1269 _cairo_output_stream_printf (output, " %s", extra_attributes);
1271 _cairo_output_stream_printf (output, "/>\n");
1273 if (pattern_id != invalid_pattern_id)
1274 _cairo_output_stream_printf (output, "</pattern>\n");
1276 return CAIRO_STATUS_SUCCESS;
1279 static cairo_status_t
1280 _cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output,
1281 cairo_svg_surface_t *surface,
1282 cairo_operator_t op,
1283 cairo_surface_pattern_t *pattern,
1284 int pattern_id,
1285 const cairo_matrix_t *parent_matrix,
1286 const char *extra_attributes)
1289 if (_cairo_surface_is_meta (pattern->surface)) {
1290 return _cairo_svg_surface_emit_composite_meta_pattern (output, surface, op, pattern,
1291 pattern_id, parent_matrix, extra_attributes);
1294 return _cairo_svg_surface_emit_composite_image_pattern (output, surface, op, pattern,
1295 pattern_id, parent_matrix, extra_attributes);
1298 static cairo_status_t
1299 _cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface,
1300 cairo_solid_pattern_t *pattern,
1301 cairo_output_stream_t *style,
1302 cairo_bool_t is_stroke)
1304 _cairo_output_stream_printf (style, is_stroke ?
1305 "stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;":
1306 "fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;",
1307 pattern->color.red * 100.0,
1308 pattern->color.green * 100.0,
1309 pattern->color.blue * 100.0,
1310 pattern->color.alpha);
1312 return CAIRO_STATUS_SUCCESS;
1315 static cairo_status_t
1316 _cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface,
1317 cairo_surface_pattern_t *pattern,
1318 cairo_output_stream_t *style,
1319 cairo_bool_t is_stroke,
1320 const cairo_matrix_t *parent_matrix)
1322 cairo_svg_document_t *document = surface->document;
1323 cairo_status_t status;
1324 int pattern_id;
1326 pattern_id = document->pattern_id++;
1327 status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs,
1328 surface, CAIRO_OPERATOR_SOURCE, pattern,
1329 pattern_id, parent_matrix, NULL);
1330 if (status)
1331 return status;
1333 _cairo_output_stream_printf (style,
1334 "%s:url(#pattern%d);",
1335 is_stroke ? "stroke" : "fill",
1336 pattern_id);
1338 return CAIRO_STATUS_SUCCESS;
1341 static cairo_status_t
1342 _cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output,
1343 cairo_gradient_pattern_t const *pattern,
1344 double start_offset,
1345 cairo_bool_t reverse_stops,
1346 cairo_bool_t emulate_reflect)
1348 cairo_gradient_stop_t *stops;
1349 double offset;
1350 unsigned int n_stops;
1351 unsigned int i;
1353 if (pattern->n_stops < 1)
1354 return CAIRO_STATUS_SUCCESS;
1356 if (pattern->n_stops == 1) {
1357 _cairo_output_stream_printf (output,
1358 "<stop offset=\"%f\" style=\""
1359 "stop-color:rgb(%f%%,%f%%,%f%%);"
1360 "stop-opacity:%f;\"/>\n",
1361 pattern->stops[0].offset,
1362 pattern->stops[0].color.red * 100.0,
1363 pattern->stops[0].color.green * 100.0,
1364 pattern->stops[0].color.blue * 100.0,
1365 pattern->stops[0].color.alpha);
1366 return CAIRO_STATUS_SUCCESS;
1369 if (emulate_reflect || reverse_stops) {
1370 n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops;
1371 stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t));
1372 if (stops == NULL)
1373 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1375 for (i = 0; i < pattern->n_stops; i++) {
1376 if (reverse_stops) {
1377 stops[i] = pattern->stops[pattern->n_stops - i - 1];
1378 stops[i].offset = 1.0 - stops[i].offset;
1379 } else
1380 stops[i] = pattern->stops[i];
1381 if (emulate_reflect) {
1382 stops[i].offset /= 2;
1383 if (i > 0 && i < (pattern->n_stops - 1)) {
1384 if (reverse_stops) {
1385 stops[i + pattern->n_stops - 1] = pattern->stops[i];
1386 stops[i + pattern->n_stops - 1].offset =
1387 0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset;
1388 } else {
1389 stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1];
1390 stops[i + pattern->n_stops - 1].offset =
1391 1 - 0.5 * stops[i + pattern->n_stops - 1].offset;
1396 } else {
1397 n_stops = pattern->n_stops;
1398 stops = pattern->stops;
1401 if (start_offset >= 0.0)
1402 for (i = 0; i < n_stops; i++) {
1403 offset = start_offset + (1 - start_offset ) * stops[i].offset;
1404 _cairo_output_stream_printf (output,
1405 "<stop offset=\"%f\" style=\""
1406 "stop-color:rgb(%f%%,%f%%,%f%%);"
1407 "stop-opacity:%f;\"/>\n",
1408 offset,
1409 stops[i].color.red * 100.0,
1410 stops[i].color.green * 100.0,
1411 stops[i].color.blue * 100.0,
1412 stops[i].color.alpha);
1414 else {
1415 cairo_bool_t found = FALSE;
1416 unsigned int offset_index;
1417 cairo_color_t offset_color_start, offset_color_stop;
1419 for (i = 0; i < n_stops; i++) {
1420 if (stops[i].offset >= -start_offset) {
1421 if (i > 0) {
1422 if (stops[i].offset != stops[i-1].offset) {
1423 double x0, x1;
1424 cairo_color_t *color0, *color1;
1426 x0 = stops[i-1].offset;
1427 x1 = stops[i].offset;
1428 color0 = &stops[i-1].color;
1429 color1 = &stops[i].color;
1430 offset_color_start.red = color0->red + (color1->red - color0->red)
1431 * (-start_offset - x0) / (x1 - x0);
1432 offset_color_start.green = color0->green + (color1->green - color0->green)
1433 * (-start_offset - x0) / (x1 - x0);
1434 offset_color_start.blue = color0->blue + (color1->blue - color0->blue)
1435 * (-start_offset - x0) / (x1 - x0);
1436 offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha)
1437 * (-start_offset - x0) / (x1 - x0);
1438 offset_color_stop = offset_color_start;
1439 } else {
1440 offset_color_stop = stops[i-1].color;
1441 offset_color_start = stops[i].color;
1443 } else
1444 offset_color_stop = offset_color_start = stops[i].color;
1445 offset_index = i;
1446 found = TRUE;
1447 break;
1451 if (!found) {
1452 offset_index = n_stops - 1;
1453 offset_color_stop = offset_color_start = stops[offset_index].color;
1456 _cairo_output_stream_printf (output,
1457 "<stop offset=\"0\" style=\""
1458 "stop-color:rgb(%f%%,%f%%,%f%%);"
1459 "stop-opacity:%f;\"/>\n",
1460 offset_color_start.red * 100.0,
1461 offset_color_start.green * 100.0,
1462 offset_color_start.blue * 100.0,
1463 offset_color_start.alpha);
1464 for (i = offset_index; i < n_stops; i++) {
1465 _cairo_output_stream_printf (output,
1466 "<stop offset=\"%f\" style=\""
1467 "stop-color:rgb(%f%%,%f%%,%f%%);"
1468 "stop-opacity:%f;\"/>\n",
1469 stops[i].offset + start_offset,
1470 stops[i].color.red * 100.0,
1471 stops[i].color.green * 100.0,
1472 stops[i].color.blue * 100.0,
1473 stops[i].color.alpha);
1475 for (i = 0; i < offset_index; i++) {
1476 _cairo_output_stream_printf (output,
1477 "<stop offset=\"%f\" style=\""
1478 "stop-color:rgb(%f%%,%f%%,%f%%);"
1479 "stop-opacity:%f;\"/>\n",
1480 1.0 + stops[i].offset + start_offset,
1481 stops[i].color.red * 100.0,
1482 stops[i].color.green * 100.0,
1483 stops[i].color.blue * 100.0,
1484 stops[i].color.alpha);
1487 _cairo_output_stream_printf (output,
1488 "<stop offset=\"1\" style=\""
1489 "stop-color:rgb(%f%%,%f%%,%f%%);"
1490 "stop-opacity:%f;\"/>\n",
1491 offset_color_stop.red * 100.0,
1492 offset_color_stop.green * 100.0,
1493 offset_color_stop.blue * 100.0,
1494 offset_color_stop.alpha);
1498 if (reverse_stops || emulate_reflect)
1499 free (stops);
1501 return CAIRO_STATUS_SUCCESS;
1504 static void
1505 _cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output,
1506 cairo_pattern_t *pattern)
1508 switch (pattern->extend) {
1509 case CAIRO_EXTEND_REPEAT:
1510 _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" ");
1511 break;
1512 case CAIRO_EXTEND_REFLECT:
1513 _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" ");
1514 break;
1515 case CAIRO_EXTEND_NONE:
1516 case CAIRO_EXTEND_PAD:
1517 break;
1521 static cairo_status_t
1522 _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface,
1523 cairo_linear_pattern_t *pattern,
1524 cairo_output_stream_t *style,
1525 cairo_bool_t is_stroke,
1526 const cairo_matrix_t *parent_matrix)
1528 cairo_svg_document_t *document = surface->document;
1529 double x0, y0, x1, y1;
1530 cairo_matrix_t p2u;
1531 cairo_status_t status;
1533 p2u = pattern->base.base.matrix;
1534 status = cairo_matrix_invert (&p2u);
1535 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1536 assert (status == CAIRO_STATUS_SUCCESS);
1538 x0 = _cairo_fixed_to_double (pattern->p1.x);
1539 y0 = _cairo_fixed_to_double (pattern->p1.y);
1540 x1 = _cairo_fixed_to_double (pattern->p2.x);
1541 y1 = _cairo_fixed_to_double (pattern->p2.y);
1543 _cairo_output_stream_printf (document->xml_node_defs,
1544 "<linearGradient id=\"linear%d\" "
1545 "gradientUnits=\"userSpaceOnUse\" "
1546 "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
1547 document->linear_pattern_id,
1548 x0, y0, x1, y1);
1550 _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
1551 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1552 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1554 status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
1555 &pattern->base, 0.0,
1556 FALSE, FALSE);
1557 if (status)
1558 return status;
1560 _cairo_output_stream_printf (document->xml_node_defs,
1561 "</linearGradient>\n");
1563 _cairo_output_stream_printf (style,
1564 "%s:url(#linear%d);",
1565 is_stroke ? "stroke" : "fill",
1566 document->linear_pattern_id);
1568 document->linear_pattern_id++;
1570 return CAIRO_STATUS_SUCCESS;
1573 static cairo_status_t
1574 _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface,
1575 cairo_radial_pattern_t *pattern,
1576 cairo_output_stream_t *style,
1577 cairo_bool_t is_stroke,
1578 const cairo_matrix_t *parent_matrix)
1580 cairo_svg_document_t *document = surface->document;
1581 cairo_matrix_t p2u;
1582 cairo_extend_t extend;
1583 double x0, y0, x1, y1, r0, r1;
1584 double fx, fy;
1585 cairo_bool_t reverse_stops;
1586 cairo_status_t status;
1587 cairo_point_t *c0, *c1;
1588 cairo_fixed_t radius0, radius1;
1590 extend = pattern->base.base.extend;
1592 if (pattern->r1 < pattern->r2) {
1593 c0 = &pattern->c1;
1594 c1 = &pattern->c2;
1595 radius0 = pattern->r1;
1596 radius1 = pattern->r2;
1597 reverse_stops = FALSE;
1598 } else {
1599 c0 = &pattern->c2;
1600 c1 = &pattern->c1;
1601 radius0 = pattern->r2;
1602 radius1 = pattern->r1;
1603 reverse_stops = TRUE;
1606 x0 = _cairo_fixed_to_double (c0->x);
1607 y0 = _cairo_fixed_to_double (c0->y);
1608 r0 = _cairo_fixed_to_double (radius0);
1609 x1 = _cairo_fixed_to_double (c1->x);
1610 y1 = _cairo_fixed_to_double (c1->y);
1611 r1 = _cairo_fixed_to_double (radius1);
1613 p2u = pattern->base.base.matrix;
1614 status = cairo_matrix_invert (&p2u);
1615 /* cairo_pattern_set_matrix ensures the matrix is invertible */
1616 assert (status == CAIRO_STATUS_SUCCESS);
1618 if (pattern->r1 == pattern->r2) {
1619 unsigned int n_stops = pattern->base.n_stops;
1621 _cairo_output_stream_printf (document->xml_node_defs,
1622 "<radialGradient id=\"radial%d\" "
1623 "gradientUnits=\"userSpaceOnUse\" "
1624 "cx=\"%f\" cy=\"%f\" "
1625 "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1626 document->radial_pattern_id,
1627 x1, y1,
1628 x1, y1, r1);
1629 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1630 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1632 if (extend == CAIRO_EXTEND_NONE || n_stops < 1)
1633 _cairo_output_stream_printf (document->xml_node_defs,
1634 "<stop offset=\"0\" style=\""
1635 "stop-color:rgb(0%%,0%%,0%%);"
1636 "stop-opacity:0;\"/>\n");
1637 else {
1638 _cairo_output_stream_printf (document->xml_node_defs,
1639 "<stop offset=\"0\" style=\""
1640 "stop-color:rgb(%f%%,%f%%,%f%%);"
1641 "stop-opacity %f;\"/>\n",
1642 pattern->base.stops[0].color.red * 100.0,
1643 pattern->base.stops[0].color.green * 100.0,
1644 pattern->base.stops[0].color.blue * 100.0,
1645 pattern->base.stops[0].color.alpha);
1646 if (n_stops > 1)
1647 _cairo_output_stream_printf (document->xml_node_defs,
1648 "<stop offset=\"0\" style=\""
1649 "stop-color:rgb(%f%%,%f%%,%f%%);"
1650 "stop-opacity:%f;\"/>\n",
1651 pattern->base.stops[n_stops - 1].color.red * 100.0,
1652 pattern->base.stops[n_stops - 1].color.green * 100.0,
1653 pattern->base.stops[n_stops - 1].color.blue * 100.0,
1654 pattern->base.stops[n_stops - 1].color.alpha);
1657 } else {
1658 double offset, r, x, y;
1659 cairo_bool_t emulate_reflect = FALSE;
1661 fx = (r1 * x0 - r0 * x1) / (r1 - r0);
1662 fy = (r1 * y0 - r0 * y1) / (r1 - r0);
1664 /* SVG doesn't support the inner circle and use instead a gradient focal.
1665 * That means we need to emulate the cairo behaviour by processing the
1666 * cairo gradient stops.
1667 * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
1668 * it's just a matter of stop position translation and calculation of
1669 * the corresponding SVG radial gradient focal.
1670 * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
1671 * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
1672 * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
1673 * list that maps to the original cairo stop list.
1675 if ((extend == CAIRO_EXTEND_REFLECT
1676 || extend == CAIRO_EXTEND_REPEAT)
1677 && r0 > 0.0) {
1678 double r_org = r1;
1680 if (extend == CAIRO_EXTEND_REFLECT) {
1681 r1 = 2 * r1 - r0;
1682 emulate_reflect = TRUE;
1685 offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
1686 r = r1 - r0;
1688 /* New position of outer circle. */
1689 x = r * (x1 - fx) / r_org + fx;
1690 y = r * (y1 - fy) / r_org + fy;
1692 x1 = x;
1693 y1 = y;
1694 r1 = r;
1695 r0 = 0.0;
1696 } else {
1697 offset = r0 / r1;
1700 _cairo_output_stream_printf (document->xml_node_defs,
1701 "<radialGradient id=\"radial%d\" "
1702 "gradientUnits=\"userSpaceOnUse\" "
1703 "cx=\"%f\" cy=\"%f\" "
1704 "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1705 document->radial_pattern_id,
1706 x1, y1,
1707 fx, fy, r1);
1709 if (emulate_reflect)
1710 _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
1711 else
1712 _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
1713 _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1714 _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1716 /* To support cairo's EXTEND_NONE, (for which SVG has no similar
1717 * notion), we add transparent color stops on either end of the
1718 * user-provided stops. */
1719 if (extend == CAIRO_EXTEND_NONE) {
1720 _cairo_output_stream_printf (document->xml_node_defs,
1721 "<stop offset=\"0\" style=\""
1722 "stop-color:rgb(0%%,0%%,0%%);"
1723 "stop-opacity:0;\"/>\n");
1724 if (r0 != 0.0)
1725 _cairo_output_stream_printf (document->xml_node_defs,
1726 "<stop offset=\"%f\" style=\""
1727 "stop-color:rgb(0%%,0%%,0%%);"
1728 "stop-opacity:0;\"/>\n",
1729 r0 / r1);
1731 status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
1732 &pattern->base, offset,
1733 reverse_stops,
1734 emulate_reflect);
1735 if (status)
1736 return status;
1738 if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
1739 _cairo_output_stream_printf (document->xml_node_defs,
1740 "<stop offset=\"1.0\" style=\""
1741 "stop-color:rgb(0%%,0%%,0%%);"
1742 "stop-opacity:0;\"/>\n");
1745 _cairo_output_stream_printf (document->xml_node_defs,
1746 "</radialGradient>\n");
1748 _cairo_output_stream_printf (style,
1749 "%s:url(#radial%d);",
1750 is_stroke ? "stroke" : "fill",
1751 document->radial_pattern_id);
1753 document->radial_pattern_id++;
1755 return CAIRO_STATUS_SUCCESS;
1758 static cairo_status_t
1759 _cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface,
1760 cairo_pattern_t *pattern,
1761 cairo_output_stream_t *output,
1762 cairo_bool_t is_stroke,
1763 const cairo_matrix_t *parent_matrix)
1765 switch (pattern->type) {
1766 case CAIRO_PATTERN_TYPE_SOLID:
1767 return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern,
1768 output, is_stroke);
1770 case CAIRO_PATTERN_TYPE_SURFACE:
1771 return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern,
1772 output, is_stroke, parent_matrix);
1774 case CAIRO_PATTERN_TYPE_LINEAR:
1775 return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern,
1776 output, is_stroke, parent_matrix);
1778 case CAIRO_PATTERN_TYPE_RADIAL:
1779 return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern,
1780 output, is_stroke, parent_matrix);
1782 return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
1785 static cairo_status_t
1786 _cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output,
1787 cairo_svg_surface_t *surface,
1788 cairo_operator_t op,
1789 cairo_pattern_t *source,
1790 cairo_fill_rule_t fill_rule,
1791 cairo_matrix_t *parent_matrix)
1793 _cairo_output_stream_printf (output,
1794 "fill-rule:%s;",
1795 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
1796 "evenodd" : "nonzero");
1797 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
1798 return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix);
1801 static cairo_status_t
1802 _cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output,
1803 cairo_svg_surface_t *surface,
1804 cairo_operator_t op,
1805 cairo_pattern_t *source,
1806 cairo_stroke_style_t *stroke_style,
1807 cairo_matrix_t *parent_matrix)
1809 cairo_status_t status;
1810 const char *line_cap, *line_join;
1811 unsigned int i;
1813 switch (stroke_style->line_cap) {
1814 case CAIRO_LINE_CAP_BUTT:
1815 line_cap = "butt";
1816 break;
1817 case CAIRO_LINE_CAP_ROUND:
1818 line_cap = "round";
1819 break;
1820 case CAIRO_LINE_CAP_SQUARE:
1821 line_cap = "square";
1822 break;
1823 default:
1824 ASSERT_NOT_REACHED;
1827 switch (stroke_style->line_join) {
1828 case CAIRO_LINE_JOIN_MITER:
1829 line_join = "miter";
1830 break;
1831 case CAIRO_LINE_JOIN_ROUND:
1832 line_join = "round";
1833 break;
1834 case CAIRO_LINE_JOIN_BEVEL:
1835 line_join = "bevel";
1836 break;
1837 default:
1838 ASSERT_NOT_REACHED;
1841 _cairo_output_stream_printf (output,
1842 "stroke-width:%f;"
1843 "stroke-linecap:%s;"
1844 "stroke-linejoin:%s;",
1845 stroke_style->line_width,
1846 line_cap,
1847 line_join);
1849 status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix);
1850 if (status)
1851 return status;
1853 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
1855 if (stroke_style->num_dashes > 0) {
1856 _cairo_output_stream_printf (output, "stroke-dasharray:");
1857 for (i = 0; i < stroke_style->num_dashes; i++) {
1858 _cairo_output_stream_printf (output, "%f",
1859 stroke_style->dash[i]);
1860 if (i + 1 < stroke_style->num_dashes)
1861 _cairo_output_stream_printf (output, ",");
1862 else
1863 _cairo_output_stream_printf (output, ";");
1865 if (stroke_style->dash_offset != 0.0) {
1866 _cairo_output_stream_printf (output,
1867 "stroke-dashoffset:%f;",
1868 stroke_style->dash_offset);
1872 _cairo_output_stream_printf (output,
1873 "stroke-miterlimit:%f;",
1874 stroke_style->miter_limit);
1876 return CAIRO_STATUS_SUCCESS;
1879 static cairo_int_status_t
1880 _cairo_svg_surface_fill_stroke (void *abstract_surface,
1881 cairo_operator_t fill_op,
1882 cairo_pattern_t *fill_source,
1883 cairo_fill_rule_t fill_rule,
1884 double fill_tolerance,
1885 cairo_antialias_t fill_antialias,
1886 cairo_path_fixed_t *path,
1887 cairo_operator_t stroke_op,
1888 cairo_pattern_t *stroke_source,
1889 cairo_stroke_style_t *stroke_style,
1890 cairo_matrix_t *stroke_ctm,
1891 cairo_matrix_t *stroke_ctm_inverse,
1892 double stroke_tolerance,
1893 cairo_antialias_t stroke_antialias)
1895 cairo_svg_surface_t *surface = abstract_surface;
1896 cairo_status_t status;
1898 _cairo_output_stream_printf (surface->xml_node, "<path style=\"");
1899 status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, fill_op,
1900 fill_source, fill_rule, stroke_ctm_inverse);
1901 if (status)
1902 return status;
1904 status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op,
1905 stroke_source, stroke_style, stroke_ctm_inverse);
1906 if (status)
1907 return status;
1909 _cairo_output_stream_printf (surface->xml_node, "\" ");
1911 status = _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse);
1912 if (status)
1913 return status;
1915 _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL);
1916 _cairo_output_stream_printf (surface->xml_node, "/>\n");
1918 return CAIRO_STATUS_SUCCESS;
1921 static cairo_int_status_t
1922 _cairo_svg_surface_fill (void *abstract_surface,
1923 cairo_operator_t op,
1924 cairo_pattern_t *source,
1925 cairo_path_fixed_t *path,
1926 cairo_fill_rule_t fill_rule,
1927 double tolerance,
1928 cairo_antialias_t antialias)
1930 cairo_svg_surface_t *surface = abstract_surface;
1931 cairo_status_t status;
1933 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
1934 return _cairo_svg_surface_analyze_operation (surface, op, source);
1936 assert (_cairo_svg_surface_operation_supported (surface, op, source));
1938 _cairo_output_stream_printf (surface->xml_node, "<path style=\" stroke:none;");
1939 status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, op, source, fill_rule, NULL);
1940 if (status)
1941 return status;
1943 _cairo_output_stream_printf (surface->xml_node, "\" ");
1945 status = _cairo_svg_surface_emit_path (surface->xml_node, path, NULL);
1946 if (status)
1947 return status;
1949 _cairo_output_stream_printf (surface->xml_node, "/>\n");
1951 return CAIRO_STATUS_SUCCESS;
1954 static cairo_int_status_t
1955 _cairo_svg_surface_get_extents (void *abstract_surface,
1956 cairo_rectangle_int_t *rectangle)
1958 cairo_svg_surface_t *surface = abstract_surface;
1960 rectangle->x = 0;
1961 rectangle->y = 0;
1963 /* XXX: The conversion to integers here is pretty bogus, (not to
1964 * mention the aribitray limitation of width to a short(!). We
1965 * may need to come up with a better interface for get_size.
1967 rectangle->width = (int) ceil (surface->width);
1968 rectangle->height = (int) ceil (surface->height);
1970 return CAIRO_STATUS_SUCCESS;
1973 static cairo_status_t
1974 _cairo_svg_surface_emit_paint (cairo_output_stream_t *output,
1975 cairo_svg_surface_t *surface,
1976 cairo_operator_t op,
1977 cairo_pattern_t *source,
1978 cairo_pattern_t *mask_source,
1979 const char *extra_attributes)
1981 cairo_status_t status;
1983 if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
1984 source->extend == CAIRO_EXTEND_NONE)
1985 return _cairo_svg_surface_emit_composite_pattern (output,
1986 surface,
1988 (cairo_surface_pattern_t *) source,
1989 invalid_pattern_id,
1990 mask_source ? &mask_source->matrix :NULL,
1991 extra_attributes);
1993 _cairo_output_stream_printf (output,
1994 "<rect x=\"0\" y=\"0\" "
1995 "width=\"%f\" height=\"%f\" "
1996 "style=\"",
1997 surface->width, surface->height);
1998 _cairo_svg_surface_emit_operator_for_style (output, surface, op);
1999 status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL);
2000 if (status)
2001 return status;
2003 _cairo_output_stream_printf (output, "stroke:none;\"");
2005 if (extra_attributes)
2006 _cairo_output_stream_printf (output, " %s", extra_attributes);
2008 _cairo_output_stream_printf (output, "/>\n");
2010 return CAIRO_STATUS_SUCCESS;
2013 static cairo_int_status_t
2014 _cairo_svg_surface_paint (void *abstract_surface,
2015 cairo_operator_t op,
2016 cairo_pattern_t *source)
2018 cairo_status_t status;
2019 cairo_svg_surface_t *surface = abstract_surface;
2021 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2022 return _cairo_svg_surface_analyze_operation (surface, op, source);
2024 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2026 /* Emulation of clear and source operators, when no clipping region
2027 * is defined. We just delete existing content of surface root node,
2028 * and exit early if operator is clear.
2029 * XXX: optimization of SOURCE operator doesn't work, since analyze
2030 * above always return FALSE. In order to make it work, we need a way
2031 * to know if there's an active clipping path.
2032 * Optimization of CLEAR works because of a test in paginated surface,
2033 * and an optimization in meta surface. */
2034 if (surface->clip_level == 0 && op == CAIRO_OPERATOR_CLEAR) {
2035 status = _cairo_output_stream_destroy (surface->xml_node);
2036 if (status) {
2037 surface->xml_node = NULL;
2038 return status;
2041 surface->xml_node = _cairo_memory_stream_create ();
2042 if (_cairo_output_stream_get_status (surface->xml_node)) {
2043 status = _cairo_output_stream_destroy (surface->xml_node);
2044 surface->xml_node = NULL;
2045 return status;
2048 if (op == CAIRO_OPERATOR_CLEAR) {
2049 if (surface->content == CAIRO_CONTENT_COLOR) {
2050 _cairo_output_stream_printf (surface->xml_node,
2051 "<rect "
2052 "width=\"%f\" height=\"%f\" "
2053 "style=\"opacity:1;"
2054 "stroke:none;"
2055 "fill:rgb(0,0,0);\"/>\n",
2056 surface->width, surface->height);
2058 return CAIRO_STATUS_SUCCESS;
2062 return _cairo_svg_surface_emit_paint (surface->xml_node,
2063 surface, op, source, 0, NULL);
2066 static cairo_int_status_t
2067 _cairo_svg_surface_mask (void *abstract_surface,
2068 cairo_operator_t op,
2069 cairo_pattern_t *source,
2070 cairo_pattern_t *mask)
2072 cairo_status_t status;
2073 cairo_svg_surface_t *surface = abstract_surface;
2074 cairo_svg_document_t *document = surface->document;
2075 cairo_output_stream_t *mask_stream;
2076 char buffer[64];
2077 cairo_bool_t discard_filter = FALSE;
2078 unsigned int mask_id;
2080 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2081 cairo_status_t source_status, mask_status;
2083 source_status = _cairo_svg_surface_analyze_operation (surface, op, source);
2084 if (_cairo_status_is_error (source_status))
2085 return source_status;
2087 mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask);
2088 if (_cairo_status_is_error (mask_status))
2089 return mask_status;
2091 return _cairo_analysis_surface_merge_status (source_status,
2092 mask_status);
2095 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2096 assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask));
2098 if (cairo_pattern_get_type (mask) == CAIRO_PATTERN_TYPE_SURFACE) {
2099 cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t*) mask;
2100 cairo_content_t content = cairo_surface_get_content (surface_pattern->surface);
2101 if (content == CAIRO_CONTENT_ALPHA)
2102 discard_filter = TRUE;
2105 if (!discard_filter)
2106 _cairo_svg_surface_emit_alpha_filter (document);
2108 /* _cairo_svg_surface_emit_paint() will output a pattern definition to
2109 * document->xml_node_defs so we need to write the mask element to
2110 * a temporary stream and then copy that to xml_node_defs. */
2111 mask_stream = _cairo_memory_stream_create ();
2112 if (_cairo_output_stream_get_status (mask_stream))
2113 return _cairo_output_stream_destroy (mask_stream);
2115 mask_id = _cairo_svg_document_allocate_mask_id (document);
2117 _cairo_output_stream_printf (mask_stream,
2118 "<mask id=\"mask%d\">\n"
2119 "%s",
2120 mask_id,
2121 discard_filter ? "" : " <g filter=\"url(#alpha)\">\n");
2122 status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL);
2123 if (status) {
2124 cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream);
2125 return status;
2126 (void) ignore;
2129 _cairo_output_stream_printf (mask_stream,
2130 "%s"
2131 "</mask>\n",
2132 discard_filter ? "" : " </g>\n");
2133 _cairo_memory_stream_copy (mask_stream, document->xml_node_defs);
2135 status = _cairo_output_stream_destroy (mask_stream);
2136 if (status)
2137 return status;
2139 snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"",
2140 mask_id);
2141 status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer);
2142 if (status)
2143 return status;
2145 return CAIRO_STATUS_SUCCESS;
2148 static cairo_int_status_t
2149 _cairo_svg_surface_stroke (void *abstract_dst,
2150 cairo_operator_t op,
2151 cairo_pattern_t *source,
2152 cairo_path_fixed_t *path,
2153 cairo_stroke_style_t *stroke_style,
2154 cairo_matrix_t *ctm,
2155 cairo_matrix_t *ctm_inverse,
2156 double tolerance,
2157 cairo_antialias_t antialias)
2159 cairo_svg_surface_t *surface = abstract_dst;
2160 cairo_status_t status;
2162 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2163 return _cairo_svg_surface_analyze_operation (surface, op, source);
2165 assert (_cairo_svg_surface_operation_supported (surface, op, source));
2167 _cairo_output_stream_printf (surface->xml_node, "<path style=\"fill:none;");
2168 status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, op,
2169 source, stroke_style, ctm_inverse);
2170 if (status)
2171 return status;
2173 _cairo_output_stream_printf (surface->xml_node, "\" ");
2175 status = _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse);
2176 if (status)
2177 return status;
2179 _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL);
2180 _cairo_output_stream_printf (surface->xml_node, "/>\n");
2182 return CAIRO_STATUS_SUCCESS;
2185 static cairo_int_status_t
2186 _cairo_svg_surface_show_glyphs (void *abstract_surface,
2187 cairo_operator_t op,
2188 cairo_pattern_t *pattern,
2189 cairo_glyph_t *glyphs,
2190 int num_glyphs,
2191 cairo_scaled_font_t *scaled_font,
2192 int *remaining_glyphs)
2194 cairo_svg_surface_t *surface = abstract_surface;
2195 cairo_svg_document_t *document = surface->document;
2196 cairo_path_fixed_t path;
2197 cairo_status_t status;
2198 cairo_scaled_font_subsets_glyph_t subset_glyph;
2199 int i;
2201 if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2202 return _cairo_svg_surface_analyze_operation (surface, op, pattern);
2204 assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
2206 if (num_glyphs <= 0)
2207 return CAIRO_STATUS_SUCCESS;
2209 /* FIXME it's probably possible to apply a pattern of a gradient to
2210 * a group of symbols, but I don't know how yet. Gradients or patterns
2211 * are translated by x and y properties of use element. */
2212 if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
2213 goto FALLBACK;
2215 _cairo_output_stream_printf (surface->xml_node, "<g style=\"");
2216 status = _cairo_svg_surface_emit_pattern (surface, pattern,
2217 surface->xml_node, FALSE, NULL);
2218 if (status)
2219 return status;
2221 _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op);
2223 _cairo_output_stream_printf (surface->xml_node, "\">\n");
2225 for (i = 0; i < num_glyphs; i++) {
2226 status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets,
2227 scaled_font, glyphs[i].index,
2228 NULL, 0,
2229 &subset_glyph);
2230 if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
2231 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2233 glyphs += i;
2234 num_glyphs -= i;
2235 goto FALLBACK;
2238 if (status)
2239 return status;
2241 _cairo_output_stream_printf (surface->xml_node,
2242 " <use xlink:href=\"#glyph%d-%d\" "
2243 "x=\"%f\" y=\"%f\"/>\n",
2244 subset_glyph.font_id,
2245 subset_glyph.subset_glyph_index,
2246 glyphs[i].x, glyphs[i].y);
2249 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2251 return CAIRO_STATUS_SUCCESS;
2253 FALLBACK:
2254 _cairo_path_fixed_init (&path);
2256 status = _cairo_scaled_font_glyph_path (scaled_font,(cairo_glyph_t *) glyphs, num_glyphs, &path);
2258 if (status) {
2259 _cairo_path_fixed_fini (&path);
2260 return status;
2263 status = _cairo_svg_surface_fill (abstract_surface, op, pattern,
2264 &path, CAIRO_FILL_RULE_WINDING, 0.0, CAIRO_ANTIALIAS_SUBPIXEL);
2266 _cairo_path_fixed_fini (&path);
2268 return status;
2271 static cairo_int_status_t
2272 _cairo_svg_surface_intersect_clip_path (void *dst,
2273 cairo_path_fixed_t *path,
2274 cairo_fill_rule_t fill_rule,
2275 double tolerance,
2276 cairo_antialias_t antialias)
2278 cairo_svg_surface_t *surface = dst;
2279 cairo_svg_document_t *document = surface->document;
2280 cairo_status_t status;
2281 unsigned int i;
2283 if (path == NULL) {
2284 for (i = 0; i < surface->clip_level; i++)
2285 _cairo_output_stream_printf (surface->xml_node, "</g>\n");
2287 surface->clip_level = 0;
2288 return CAIRO_STATUS_SUCCESS;
2291 _cairo_output_stream_printf (document->xml_node_defs,
2292 "<clipPath id=\"clip%d\">\n"
2293 " <path ",
2294 document->clip_id);
2295 status = _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL);
2296 if (status)
2297 return status;
2299 _cairo_output_stream_printf (document->xml_node_defs,
2300 "/>\n"
2301 "</clipPath>\n");
2303 _cairo_output_stream_printf (surface->xml_node,
2304 "<g clip-path=\"url(#clip%d)\" "
2305 "clip-rule=\"%s\">\n",
2306 document->clip_id,
2307 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
2308 "evenodd" : "nonzero");
2310 document->clip_id++;
2311 surface->clip_level++;
2313 return CAIRO_STATUS_SUCCESS;
2316 static void
2317 _cairo_svg_surface_get_font_options (void *abstract_surface,
2318 cairo_font_options_t *options)
2320 _cairo_font_options_init_default (options);
2322 cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
2323 cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
2324 cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
2327 static const cairo_surface_backend_t cairo_svg_surface_backend = {
2328 CAIRO_SURFACE_TYPE_SVG,
2329 _cairo_svg_surface_create_similar,
2330 _cairo_svg_surface_finish,
2331 NULL, /* acquire_source_image */
2332 NULL, /* release_source_image */
2333 NULL, /* acquire_dest_image */
2334 NULL, /* release_dest_image */
2335 NULL, /* clone_similar */
2336 NULL, /* _cairo_svg_surface_composite, */
2337 NULL, /* _cairo_svg_surface_fill_rectangles, */
2338 NULL, /* _cairo_svg_surface_composite_trapezoids,*/
2339 _cairo_svg_surface_copy_page,
2340 _cairo_svg_surface_show_page,
2341 NULL, /* set_clip_region */
2342 _cairo_svg_surface_intersect_clip_path,
2343 _cairo_svg_surface_get_extents,
2344 NULL, /* _cairo_svg_surface_old_show_glyphs, */
2345 _cairo_svg_surface_get_font_options,
2346 NULL, /* flush */
2347 NULL, /* mark dirty rectangle */
2348 NULL, /* scaled font fini */
2349 NULL, /* scaled glyph fini */
2350 _cairo_svg_surface_paint,
2351 _cairo_svg_surface_mask,
2352 _cairo_svg_surface_stroke,
2353 _cairo_svg_surface_fill,
2354 _cairo_svg_surface_show_glyphs,
2355 NULL, /* snapshot */
2356 NULL, /* is_similar */
2357 NULL, /* reset */
2358 _cairo_svg_surface_fill_stroke
2361 static cairo_status_t
2362 _cairo_svg_document_create (cairo_output_stream_t *output_stream,
2363 double width,
2364 double height,
2365 cairo_svg_version_t version,
2366 cairo_svg_document_t **document_out)
2368 cairo_svg_document_t *document;
2369 cairo_status_t status, status_ignored;
2371 if (output_stream->status)
2372 return output_stream->status;
2374 document = malloc (sizeof (cairo_svg_document_t));
2375 if (document == NULL)
2376 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2378 /* The use of defs for font glyphs imposes no per-subset limit. */
2379 document->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
2380 if (document->font_subsets == NULL) {
2381 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2382 goto CLEANUP_DOCUMENT;
2385 document->output_stream = output_stream;
2386 document->refcount = 1;
2387 document->owner = NULL;
2388 document->finished = FALSE;
2389 document->width = width;
2390 document->height = height;
2392 document->surface_id = 0;
2393 document->linear_pattern_id = 0;
2394 document->radial_pattern_id = 0;
2395 document->pattern_id = 0;
2396 document->filter_id = 0;
2397 document->clip_id = 0;
2398 document->mask_id = 0;
2400 document->xml_node_defs = _cairo_memory_stream_create ();
2401 status = _cairo_output_stream_get_status (document->xml_node_defs);
2402 if (status)
2403 goto CLEANUP_NODE_DEFS;
2405 document->xml_node_glyphs = _cairo_memory_stream_create ();
2406 status = _cairo_output_stream_get_status (document->xml_node_glyphs);
2407 if (status)
2408 goto CLEANUP_NODE_GLYPHS;
2410 document->alpha_filter = FALSE;
2412 _cairo_array_init (&document->meta_snapshots,
2413 sizeof (cairo_meta_snapshot_t));
2415 document->svg_version = version;
2417 *document_out = document;
2418 return CAIRO_STATUS_SUCCESS;
2420 CLEANUP_NODE_GLYPHS:
2421 status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs);
2422 CLEANUP_NODE_DEFS:
2423 status_ignored = _cairo_output_stream_destroy (document->xml_node_defs);
2424 _cairo_scaled_font_subsets_destroy (document->font_subsets);
2425 CLEANUP_DOCUMENT:
2426 free (document);
2427 return status;
2430 static cairo_svg_document_t *
2431 _cairo_svg_document_reference (cairo_svg_document_t *document)
2433 document->refcount++;
2435 return document;
2438 static unsigned int
2439 _cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document)
2441 return document->mask_id++;
2444 static cairo_status_t
2445 _cairo_svg_document_destroy (cairo_svg_document_t *document)
2447 cairo_status_t status;
2449 document->refcount--;
2450 if (document->refcount > 0)
2451 return CAIRO_STATUS_SUCCESS;
2453 status = _cairo_svg_document_finish (document);
2455 free (document);
2457 return status;
2460 static cairo_status_t
2461 _cairo_svg_document_finish (cairo_svg_document_t *document)
2463 cairo_status_t status, status2;
2464 cairo_output_stream_t *output = document->output_stream;
2465 cairo_meta_snapshot_t *snapshot;
2466 cairo_svg_page_t *page;
2467 unsigned int i;
2469 if (document->finished)
2470 return CAIRO_STATUS_SUCCESS;
2473 * Should we add DOCTYPE?
2475 * Google says no.
2477 * http://tech.groups.yahoo.com/group/svg-developers/message/48562:
2478 * There's a bunch of issues, but just to pick a few:
2479 * - they'll give false positives.
2480 * - they'll give false negatives.
2481 * - they're namespace-unaware.
2482 * - they don't wildcard.
2483 * So when they say OK they really haven't checked anything, when
2484 * they say NOT OK they might be on crack, and like all
2485 * namespace-unaware things they're a dead branch of the XML tree.
2487 * http://jwatt.org/svg/authoring/:
2488 * Unfortunately the SVG DTDs are a source of so many issues that the
2489 * SVG WG has decided not to write one for the upcoming SVG 1.2
2490 * standard. In fact SVG WG members are even telling people not to use
2491 * a DOCTYPE declaration in SVG 1.0 and 1.1 documents.
2494 _cairo_output_stream_printf (output,
2495 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2496 "<svg xmlns=\"http://www.w3.org/2000/svg\" "
2497 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
2498 "width=\"%fpt\" height=\"%fpt\" "
2499 "viewBox=\"0 0 %f %f\" version=\"%s\">\n",
2500 document->width, document->height,
2501 document->width, document->height,
2502 _cairo_svg_internal_version_strings [document->svg_version]);
2504 status = _cairo_svg_document_emit_font_subsets (document);
2506 if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 ||
2507 _cairo_memory_stream_length (document->xml_node_defs) > 0) {
2508 _cairo_output_stream_printf (output, "<defs>\n");
2509 if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
2510 _cairo_output_stream_printf (output, "<g>\n");
2511 _cairo_memory_stream_copy (document->xml_node_glyphs, output);
2512 _cairo_output_stream_printf (output, "</g>\n");
2514 _cairo_memory_stream_copy (document->xml_node_defs, output);
2515 _cairo_output_stream_printf (output, "</defs>\n");
2518 if (document->owner != NULL) {
2519 cairo_svg_surface_t *surface;
2521 surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner);
2522 if (surface->xml_node != NULL &&
2523 _cairo_memory_stream_length (surface->xml_node) > 0) {
2524 if (_cairo_svg_surface_store_page (surface) == NULL) {
2525 if (status == CAIRO_STATUS_SUCCESS)
2526 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2530 if (surface->page_set.num_elements > 1 &&
2531 _cairo_svg_version_has_page_set_support (document->svg_version)) {
2532 _cairo_output_stream_printf (output, "<pageSet>\n");
2533 for (i = 0; i < surface->page_set.num_elements; i++) {
2534 page = _cairo_array_index (&surface->page_set, i);
2535 _cairo_output_stream_printf (output, "<page>\n");
2536 _cairo_output_stream_printf (output,
2537 "<g id=\"surface%d\">\n",
2538 page->surface_id);
2539 _cairo_memory_stream_copy (page->xml_node, output);
2540 _cairo_output_stream_printf (output, "</g>\n</page>\n");
2542 _cairo_output_stream_printf (output, "</pageSet>\n");
2543 } else if (surface->page_set.num_elements > 0) {
2544 page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
2545 _cairo_output_stream_printf (output,
2546 "<g id=\"surface%d\">\n",
2547 page->surface_id);
2548 _cairo_memory_stream_copy (page->xml_node, output);
2549 _cairo_output_stream_printf (output, "</g>\n");
2553 _cairo_output_stream_printf (output, "</svg>\n");
2555 status2 = _cairo_output_stream_destroy (document->xml_node_glyphs);
2556 if (status == CAIRO_STATUS_SUCCESS)
2557 status = status2;
2559 status2 = _cairo_output_stream_destroy (document->xml_node_defs);
2560 if (status == CAIRO_STATUS_SUCCESS)
2561 status = status2;
2563 status2 = _cairo_output_stream_destroy (output);
2564 if (status == CAIRO_STATUS_SUCCESS)
2565 status = status2;
2567 for (i = 0; i < document->meta_snapshots.num_elements; i++) {
2568 snapshot = _cairo_array_index (&document->meta_snapshots, i);
2569 status2 = cairo_surface_status (&snapshot->meta->base);
2570 cairo_surface_destroy (&snapshot->meta->base);
2571 if (status == CAIRO_STATUS_SUCCESS)
2572 status = status2;
2574 _cairo_array_fini (&document->meta_snapshots);
2576 document->finished = TRUE;
2578 return status;
2581 static void
2582 _cairo_svg_surface_set_paginated_mode (void *abstract_surface,
2583 cairo_paginated_mode_t paginated_mode)
2585 cairo_svg_surface_t *surface = abstract_surface;
2587 surface->paginated_mode = paginated_mode;
2590 static cairo_bool_t
2591 _cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface)
2593 cairo_svg_surface_t *surface = abstract_surface;
2594 cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
2596 if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) {
2597 status = _cairo_svg_surface_analyze_operator (surface,
2598 CAIRO_OPERATOR_SOURCE);
2601 return status == CAIRO_STATUS_SUCCESS;
2604 static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
2605 NULL /*_cairo_svg_surface_start_page*/,
2606 _cairo_svg_surface_set_paginated_mode,
2607 NULL, /* _cairo_svg_surface_set_bounding_box */
2608 NULL, /* _cairo_svg_surface_set_fallback_images_required */
2609 _cairo_svg_surface_supports_fine_grained_fallbacks,