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
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() */
44
#include "cairo-svg.h"
45
#include "cairo-svg-surface-private.h"
46
#include "cairo-path-fixed-private.h"
47
#include "cairo-meta-surface-private.h"
48
#include "cairo-paginated-private.h"
49
#include "cairo-scaled-font-subsets-private.h"
50
#include "cairo-output-stream-private.h"
52
typedef struct cairo_svg_page cairo_svg_page_t;
54
static const int invalid_pattern_id = -1;
56
static const cairo_svg_version_t _cairo_svg_versions[] =
58
CAIRO_SVG_VERSION_1_1,
62
#define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
65
_cairo_svg_version_has_page_set_support (cairo_svg_version_t version)
67
return version > CAIRO_SVG_VERSION_1_1;
70
static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] =
76
static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] =
82
struct cairo_svg_page {
83
unsigned int surface_id;
84
unsigned int clip_level;
85
cairo_output_stream_t *xml_node;
88
struct cairo_svg_document {
89
cairo_output_stream_t *output_stream;
90
unsigned long refcount;
91
cairo_surface_t *owner;
92
cairo_bool_t finished;
97
cairo_output_stream_t *xml_node_defs;
98
cairo_output_stream_t *xml_node_glyphs;
100
unsigned int surface_id;
101
unsigned int linear_pattern_id;
102
unsigned int radial_pattern_id;
103
unsigned int pattern_id;
104
unsigned int filter_id;
105
unsigned int clip_id;
106
unsigned int mask_id;
108
cairo_bool_t alpha_filter;
110
cairo_array_t meta_snapshots;
112
cairo_svg_version_t svg_version;
114
cairo_scaled_font_subsets_t *font_subsets;
119
cairo_meta_surface_t *meta;
120
} cairo_meta_snapshot_t;
122
static cairo_status_t
123
_cairo_svg_document_create (cairo_output_stream_t *stream,
126
cairo_svg_version_t version,
127
cairo_svg_document_t **document_out);
129
static cairo_status_t
130
_cairo_svg_document_destroy (cairo_svg_document_t *document);
132
static cairo_status_t
133
_cairo_svg_document_finish (cairo_svg_document_t *document);
135
static cairo_svg_document_t *
136
_cairo_svg_document_reference (cairo_svg_document_t *document);
139
_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document);
141
static cairo_surface_t *
142
_cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
143
cairo_content_t content,
146
static cairo_surface_t *
147
_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
150
cairo_svg_version_t version);
152
static const cairo_surface_backend_t cairo_svg_surface_backend;
153
static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend;
156
* cairo_svg_surface_create_for_stream:
157
* @write_func: a #cairo_write_func_t to accept the output data
158
* @closure: the closure argument for @write_func
159
* @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
160
* @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
162
* Creates a SVG surface of the specified size in points to be written
163
* incrementally to the stream represented by @write_func and @closure.
165
* Return value: a pointer to the newly created surface. The caller
166
* owns the surface and should call cairo_surface_destroy() when done
169
* This function always returns a valid pointer, but it will return a
170
* pointer to a "nil" surface if an error such as out of memory
171
* occurs. You can use cairo_surface_status() to check for this.
176
cairo_svg_surface_create_for_stream (cairo_write_func_t write_func,
181
cairo_output_stream_t *stream;
183
stream = _cairo_output_stream_create (write_func, NULL, closure);
184
if (_cairo_output_stream_get_status (stream))
185
return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
187
return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
191
* cairo_svg_surface_create:
192
* @filename: a filename for the SVG output (must be writable)
193
* @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
194
* @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
196
* Creates a SVG surface of the specified size in points to be written
199
* Return value: a pointer to the newly created surface. The caller
200
* owns the surface and should call cairo_surface_destroy() when done
203
* This function always returns a valid pointer, but it will return a
204
* pointer to a "nil" surface if an error such as out of memory
205
* occurs. You can use cairo_surface_status() to check for this.
210
cairo_svg_surface_create (const char *filename,
214
cairo_output_stream_t *stream;
216
stream = _cairo_output_stream_create_for_filename (filename);
217
if (_cairo_output_stream_get_status (stream))
218
return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
220
return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
224
_cairo_surface_is_svg (cairo_surface_t *surface)
226
return surface->backend == &cairo_svg_surface_backend;
229
/* If the abstract_surface is a paginated surface, and that paginated
230
* surface's target is a svg_surface, then set svg_surface to that
231
* target. Otherwise return %CAIRO_STATUS_SURFACE_TYPE_MISMATCH.
233
static cairo_status_t
234
_extract_svg_surface (cairo_surface_t *surface,
235
cairo_svg_surface_t **svg_surface)
237
cairo_surface_t *target;
240
return surface->status;
242
if (! _cairo_surface_is_paginated (surface))
243
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
245
target = _cairo_paginated_surface_get_target (surface);
247
return target->status;
249
if (! _cairo_surface_is_svg (target))
250
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
252
*svg_surface = (cairo_svg_surface_t *) target;
254
return CAIRO_STATUS_SUCCESS;
258
* cairo_svg_surface_restrict_to_version:
259
* @surface: a SVG #cairo_surface_t
260
* @version: SVG version
262
* Restricts the generated SVG file to @version. See cairo_svg_get_versions()
263
* for a list of available version values that can be used here.
265
* This function should only be called before any drawing operations
266
* have been performed on the given surface. The simplest way to do
267
* this is to call this function immediately after creating the
273
cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface,
274
cairo_svg_version_t version)
276
cairo_svg_surface_t *surface = NULL; /* hide compiler warning */
277
cairo_status_t status;
279
status = _extract_svg_surface (abstract_surface, &surface);
281
status = _cairo_surface_set_error (abstract_surface, status);
285
if (version < CAIRO_SVG_VERSION_LAST)
286
surface->document->svg_version = version;
290
* cairo_svg_get_versions:
291
* @versions: supported version list
292
* @num_versions: list length
294
* Used to retrieve the list of supported versions. See
295
* cairo_svg_surface_restrict_to_version().
300
cairo_svg_get_versions (cairo_svg_version_t const **versions,
303
if (versions != NULL)
304
*versions = _cairo_svg_versions;
306
if (num_versions != NULL)
307
*num_versions = CAIRO_SVG_VERSION_LAST;
311
* cairo_svg_version_to_string:
312
* @version: a version id
314
* Get the string representation of the given @version id. This function
315
* will return %NULL if @version isn't valid. See cairo_svg_get_versions()
316
* for a way to get the list of valid version ids.
318
* Return value: the string associated to given version.
323
cairo_svg_version_to_string (cairo_svg_version_t version)
325
if (version >= CAIRO_SVG_VERSION_LAST)
328
return _cairo_svg_version_strings[version];
331
static cairo_surface_t *
332
_cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
333
cairo_content_t content,
337
cairo_svg_surface_t *surface;
338
cairo_surface_t *paginated;
339
cairo_status_t status, status_ignored;
341
surface = malloc (sizeof (cairo_svg_surface_t));
343
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
345
_cairo_surface_init (&surface->base, &cairo_svg_surface_backend,
348
surface->width = width;
349
surface->height = height;
351
surface->document = _cairo_svg_document_reference (document);
353
surface->clip_level = 0;
355
surface->id = document->surface_id++;
356
surface->base_clip = document->clip_id++;
357
surface->is_base_clip_emitted = FALSE;
359
surface->xml_node = _cairo_memory_stream_create ();
360
status = _cairo_output_stream_get_status (surface->xml_node);
364
_cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t));
366
if (content == CAIRO_CONTENT_COLOR) {
367
_cairo_output_stream_printf (surface->xml_node,
368
"<rect width=\"%f\" height=\"%f\" "
369
"style=\"opacity: 1; stroke: none; "
370
"fill: rgb(0,0,0);\"/>\n",
372
status = _cairo_output_stream_get_status (surface->xml_node);
377
surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
378
surface->force_fallbacks = FALSE;
379
surface->content = content;
381
paginated = _cairo_paginated_surface_create (&surface->base,
385
&cairo_svg_surface_paginated_backend);
386
status = paginated->status;
387
if (status == CAIRO_STATUS_SUCCESS) {
388
/* paginated keeps the only reference to surface now, drop ours */
389
cairo_surface_destroy (&surface->base);
393
/* ignore status as we are on the error path */
395
status_ignored = _cairo_output_stream_destroy (surface->xml_node);
396
status_ignored = _cairo_svg_document_destroy (document);
400
return _cairo_surface_create_in_error (status);
403
static cairo_surface_t *
404
_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
407
cairo_svg_version_t version)
409
cairo_svg_document_t *document = NULL; /* silence compiler */
410
cairo_surface_t *surface;
411
cairo_status_t status;
413
status = _cairo_svg_document_create (stream,
414
width, height, version,
417
surface = _cairo_surface_create_in_error (status);
418
/* consume the output stream on behalf of caller */
419
status = _cairo_output_stream_destroy (stream);
423
surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA,
425
if (surface->status) {
426
status = _cairo_svg_document_destroy (document);
430
document->owner = surface;
431
status = _cairo_svg_document_destroy (document);
432
/* the ref count should be 2 at this point */
433
assert (status == CAIRO_STATUS_SUCCESS);
438
static cairo_svg_page_t *
439
_cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
442
cairo_svg_page_t page;
443
cairo_output_stream_t *stream;
444
cairo_status_t status;
446
stream = _cairo_memory_stream_create ();
447
if (_cairo_output_stream_get_status (stream)) {
448
status = _cairo_output_stream_destroy (stream);
452
page.surface_id = surface->id;
453
page.clip_level = surface->clip_level;
454
page.xml_node = surface->xml_node;
456
if (_cairo_array_append (&surface->page_set, &page)) {
457
status = _cairo_output_stream_destroy (stream);
461
surface->xml_node = stream;
462
surface->clip_level = 0;
464
for (i = 0; i < page.clip_level; i++)
465
_cairo_output_stream_printf (page.xml_node, "</g>\n");
467
return _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
470
static cairo_int_status_t
471
_cairo_svg_surface_copy_page (void *abstract_surface)
473
cairo_svg_surface_t *surface = abstract_surface;
474
cairo_svg_page_t *page;
476
page = _cairo_svg_surface_store_page (surface);
478
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
480
_cairo_memory_stream_copy (page->xml_node, surface->xml_node);
481
surface->clip_level = page->clip_level;
483
return CAIRO_STATUS_SUCCESS;
486
static cairo_int_status_t
487
_cairo_svg_surface_show_page (void *abstract_surface)
489
cairo_svg_surface_t *surface = abstract_surface;
491
if (_cairo_svg_surface_store_page (surface) == NULL)
492
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
494
return CAIRO_STATUS_SUCCESS;
498
_cairo_svg_surface_emit_transform (cairo_output_stream_t *output,
499
char const *attribute_str,
500
const cairo_matrix_t *object_matrix,
501
const cairo_matrix_t *parent_matrix)
503
cairo_matrix_t matrix = *object_matrix;
505
if (parent_matrix != NULL)
506
cairo_matrix_multiply (&matrix, &matrix, parent_matrix);
508
if (!_cairo_matrix_is_identity (&matrix))
509
_cairo_output_stream_printf (output,
510
"%s=\"matrix(%f,%f,%f,%f,%f,%f)\"",
512
matrix.xx, matrix.yx,
513
matrix.xy, matrix.yy,
514
matrix.x0, matrix.y0);
519
cairo_output_stream_t *output;
520
cairo_matrix_t *ctm_inverse;
523
static cairo_status_t
524
_cairo_svg_path_move_to (void *closure, cairo_point_t *point)
526
svg_path_info_t *info = closure;
527
double x = _cairo_fixed_to_double (point->x);
528
double y = _cairo_fixed_to_double (point->y);
530
if (info->ctm_inverse)
531
cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
533
_cairo_output_stream_printf (info->output, "M %f %f ", x, y);
535
return CAIRO_STATUS_SUCCESS;
538
static cairo_status_t
539
_cairo_svg_path_line_to (void *closure, cairo_point_t *point)
541
svg_path_info_t *info = closure;
542
double x = _cairo_fixed_to_double (point->x);
543
double y = _cairo_fixed_to_double (point->y);
545
if (info->ctm_inverse)
546
cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
548
_cairo_output_stream_printf (info->output, "L %f %f ", x, y);
550
return CAIRO_STATUS_SUCCESS;
553
static cairo_status_t
554
_cairo_svg_path_curve_to (void *closure,
559
svg_path_info_t *info = closure;
560
double bx = _cairo_fixed_to_double (b->x);
561
double by = _cairo_fixed_to_double (b->y);
562
double cx = _cairo_fixed_to_double (c->x);
563
double cy = _cairo_fixed_to_double (c->y);
564
double dx = _cairo_fixed_to_double (d->x);
565
double dy = _cairo_fixed_to_double (d->y);
567
if (info->ctm_inverse) {
568
cairo_matrix_transform_point (info->ctm_inverse, &bx, &by);
569
cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy);
570
cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy);
573
_cairo_output_stream_printf (info->output,
574
"C %f %f %f %f %f %f ",
575
bx, by, cx, cy, dx, dy);
577
return CAIRO_STATUS_SUCCESS;
580
static cairo_status_t
581
_cairo_svg_path_close_path (void *closure)
583
svg_path_info_t *info = closure;
585
_cairo_output_stream_printf (info->output, "Z ");
587
return CAIRO_STATUS_SUCCESS;
590
static cairo_status_t
591
_cairo_svg_surface_emit_path (cairo_output_stream_t *output,
592
cairo_path_fixed_t *path,
593
cairo_matrix_t *ctm_inverse)
595
cairo_status_t status;
596
svg_path_info_t info;
598
_cairo_output_stream_printf (output, "d=\"");
600
info.output = output;
601
info.ctm_inverse = ctm_inverse;
602
status = _cairo_path_fixed_interpret (path,
603
CAIRO_DIRECTION_FORWARD,
604
_cairo_svg_path_move_to,
605
_cairo_svg_path_line_to,
606
_cairo_svg_path_curve_to,
607
_cairo_svg_path_close_path,
612
_cairo_output_stream_printf (output, "\"");
617
static cairo_int_status_t
618
_cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document,
619
cairo_scaled_font_t *scaled_font,
620
unsigned long glyph_index)
622
cairo_scaled_glyph_t *scaled_glyph;
623
cairo_int_status_t status;
625
status = _cairo_scaled_glyph_lookup (scaled_font,
627
CAIRO_SCALED_GLYPH_INFO_METRICS|
628
CAIRO_SCALED_GLYPH_INFO_PATH,
633
_cairo_output_stream_printf (document->xml_node_glyphs,
634
"<path style=\"stroke: none;\" ");
636
status = _cairo_svg_surface_emit_path (document->xml_node_glyphs, scaled_glyph->path, NULL);
640
_cairo_output_stream_printf (document->xml_node_glyphs,
646
static cairo_int_status_t
647
_cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document,
648
cairo_scaled_font_t *scaled_font,
649
unsigned long glyph_index)
651
cairo_image_surface_t *image;
652
cairo_scaled_glyph_t *scaled_glyph;
653
cairo_status_t status;
658
status = _cairo_scaled_glyph_lookup (scaled_font,
660
CAIRO_SCALED_GLYPH_INFO_METRICS|
661
CAIRO_SCALED_GLYPH_INFO_SURFACE,
666
image = scaled_glyph->surface;
667
if (image->format != CAIRO_FORMAT_A1) {
668
image = _cairo_image_surface_clone (image, CAIRO_FORMAT_A1);
669
if (cairo_surface_status (&image->base))
670
return cairo_surface_status (&image->base);
673
_cairo_output_stream_printf (document->xml_node_glyphs, "<g");
674
_cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform",
675
&image->base.device_transform_inverse, NULL);
676
_cairo_output_stream_printf (document->xml_node_glyphs, ">/n");
678
for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) {
679
for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
680
uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
681
for (bit = 7; bit >= 0 && x < image->width; bit--, x++) {
682
if (output_byte & (1 << bit)) {
683
_cairo_output_stream_printf (document->xml_node_glyphs,
684
"<rect x=\"%d\" y=\"%d\" width=\"1\" height=\"1\"/>\n",
690
_cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n");
692
if (image != scaled_glyph->surface)
693
cairo_surface_destroy (&image->base);
695
return CAIRO_STATUS_SUCCESS;
698
static cairo_status_t
699
_cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
700
cairo_scaled_font_t *scaled_font,
701
unsigned long scaled_font_glyph_index,
702
unsigned int font_id,
703
unsigned int subset_glyph_index)
705
cairo_status_t status;
707
_cairo_output_stream_printf (document->xml_node_glyphs,
708
"<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n",
712
status = _cairo_svg_document_emit_outline_glyph_data (document,
714
scaled_font_glyph_index);
715
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
716
status = _cairo_svg_document_emit_bitmap_glyph_data (document,
718
scaled_font_glyph_index);
722
_cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n");
724
return CAIRO_STATUS_SUCCESS;
727
static cairo_status_t
728
_cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
731
cairo_svg_document_t *document = closure;
733
cairo_status_t status = CAIRO_STATUS_SUCCESS;
735
for (i = 0; i < font_subset->num_glyphs; i++) {
736
status = _cairo_svg_document_emit_glyph (document,
737
font_subset->scaled_font,
738
font_subset->glyphs[i],
739
font_subset->font_id, i);
747
static cairo_status_t
748
_cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
750
cairo_status_t status;
752
status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
753
_cairo_svg_document_emit_font_subset,
758
status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
759
_cairo_svg_document_emit_font_subset,
763
_cairo_scaled_font_subsets_destroy (document->font_subsets);
764
document->font_subsets = NULL;
769
static cairo_bool_t cairo_svg_force_fallbacks = FALSE;
771
static cairo_int_status_t
772
_cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface,
774
const cairo_pattern_t *pattern)
776
cairo_svg_document_t *document = surface->document;
778
if (cairo_svg_force_fallbacks)
781
/* SVG doesn't support extend reflect for image pattern */
782
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
783
pattern->extend == CAIRO_EXTEND_REFLECT)
784
return CAIRO_INT_STATUS_UNSUPPORTED;
786
if (document->svg_version >= CAIRO_SVG_VERSION_1_2)
787
return CAIRO_STATUS_SUCCESS;
789
if (op == CAIRO_OPERATOR_OVER)
790
return CAIRO_STATUS_SUCCESS;
792
/* The SOURCE operator is only supported if there is nothing
793
* painted underneath. */
794
if (op == CAIRO_OPERATOR_SOURCE)
795
return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
797
return CAIRO_INT_STATUS_UNSUPPORTED;
800
static cairo_int_status_t
801
_cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface,
803
const cairo_pattern_t *pattern)
805
if (_cairo_svg_surface_analyze_operation (surface, op, pattern)
806
!= CAIRO_INT_STATUS_UNSUPPORTED)
814
static cairo_surface_t *
815
_cairo_svg_surface_create_similar (void *abstract_src,
816
cairo_content_t content,
820
return _cairo_meta_surface_create (content, width, height);
823
static cairo_status_t
824
_cairo_svg_surface_finish (void *abstract_surface)
826
cairo_status_t status, status2;
827
cairo_svg_surface_t *surface = abstract_surface;
828
cairo_svg_document_t *document = surface->document;
829
cairo_svg_page_t *page;
832
if (_cairo_paginated_surface_get_target (document->owner) == &surface->base)
833
status = _cairo_svg_document_finish (document);
835
status = CAIRO_STATUS_SUCCESS;
837
if (surface->xml_node != NULL) {
838
status2 = _cairo_output_stream_destroy (surface->xml_node);
839
if (status == CAIRO_STATUS_SUCCESS)
843
for (i = 0; i < surface->page_set.num_elements; i++) {
844
page = _cairo_array_index (&surface->page_set, i);
845
status2 = _cairo_output_stream_destroy (page->xml_node);
846
if (status == CAIRO_STATUS_SUCCESS)
849
_cairo_array_fini (&surface->page_set);
851
status2 = _cairo_svg_document_destroy (document);
852
if (status == CAIRO_STATUS_SUCCESS)
859
_cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document)
861
if (document->alpha_filter)
864
_cairo_output_stream_printf (document->xml_node_defs,
865
"<filter id=\"alpha\" "
866
"filterUnits=\"objectBoundingBox\" "
867
"x=\"0%%\" y=\"0%%\" "
868
"width=\"100%%\" height=\"100%%\">\n"
869
" <feColorMatrix type=\"matrix\" "
870
"in=\"SourceGraphic\" "
871
"values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n"
874
document->alpha_filter = TRUE;
878
cairo_output_stream_t *output;
880
unsigned int trailing;
881
unsigned char src[3];
882
} base64_write_closure_t;
884
static char const base64_table[64] =
885
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
887
static cairo_status_t
888
base64_write_func (void *closure,
889
const unsigned char *data,
892
base64_write_closure_t *info = (base64_write_closure_t *) closure;
898
if (info->in_mem + length < 3) {
899
for (i = 0; i < length; i++) {
900
src[i + info->in_mem] = *data++;
902
info->in_mem += length;
903
return CAIRO_STATUS_SUCCESS;
907
unsigned char dst[4];
909
for (i = info->in_mem; i < 3; i++) {
915
dst[0] = base64_table[src[0] >> 2];
916
dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4];
917
dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6];
918
dst[3] = base64_table[src[2] & 0xfc >> 2];
919
/* Special case for the last missing bits */
920
switch (info->trailing) {
928
_cairo_output_stream_write (info->output, dst, 4);
929
} while (length >= 3);
931
for (i = 0; i < length; i++) {
934
info->in_mem = length;
936
return _cairo_output_stream_get_status (info->output);
939
static cairo_int_status_t
940
_cairo_surface_base64_encode (cairo_surface_t *surface,
941
cairo_output_stream_t *output)
943
cairo_status_t status;
944
base64_write_closure_t info;
947
info.output = output;
951
_cairo_output_stream_printf (info.output, "data:image/png;base64,");
953
status = cairo_surface_write_to_png_stream (surface, base64_write_func,
959
if (info.in_mem > 0) {
960
for (i = info.in_mem; i < 3; i++)
962
info.trailing = 3 - info.in_mem;
964
status = base64_write_func (&info, NULL, 0);
970
static cairo_status_t
971
_cairo_svg_surface_emit_composite_image_pattern (cairo_output_stream_t *output,
972
cairo_svg_surface_t *svg_surface,
973
cairo_surface_pattern_t *pattern,
975
const cairo_matrix_t *parent_matrix,
976
const char *extra_attributes)
978
cairo_surface_t *surface;
979
cairo_surface_attributes_t surface_attr;
980
cairo_rectangle_int_t extents;
981
cairo_status_t status;
984
status = _cairo_pattern_acquire_surface ((cairo_pattern_t *)pattern,
985
(cairo_surface_t *)svg_surface,
986
0, 0, (unsigned int)-1, (unsigned int)-1,
987
&surface, &surface_attr);
991
status = _cairo_surface_get_extents (surface, &extents);
995
p2u = pattern->base.matrix;
996
status = cairo_matrix_invert (&p2u);
997
/* cairo_pattern_set_matrix ensures the matrix is invertible */
998
assert (status == CAIRO_STATUS_SUCCESS);
1000
if (pattern_id != invalid_pattern_id) {
1001
_cairo_output_stream_printf (output,
1002
"<pattern id=\"pattern%d\" "
1003
"patternUnits=\"userSpaceOnUse\" "
1004
"width=\"%d\" height=\"%d\"",
1006
extents.width, extents.height);
1007
_cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
1008
_cairo_output_stream_printf (output, ">\n");
1011
_cairo_output_stream_printf (output,
1012
" <image width=\"%d\" height=\"%d\"",
1013
extents.width, extents.height);
1015
if (pattern_id == invalid_pattern_id)
1016
_cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
1018
if (extra_attributes)
1019
_cairo_output_stream_printf (output, " %s", extra_attributes);
1021
_cairo_output_stream_printf (output, " xlink:href=\"");
1023
status = _cairo_surface_base64_encode (surface, output);
1025
_cairo_output_stream_printf (output, "\"/>\n");
1027
if (pattern_id != invalid_pattern_id)
1028
_cairo_output_stream_printf (output, "</pattern>\n");
1031
_cairo_pattern_release_surface ((cairo_pattern_t *)pattern,
1032
surface, &surface_attr);
1037
static cairo_status_t
1038
_cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document,
1039
cairo_meta_surface_t *surface,
1042
cairo_status_t status;
1043
cairo_surface_t *paginated_surface;
1044
cairo_svg_surface_t *svg_surface;
1045
cairo_meta_snapshot_t new_snapshot;
1046
cairo_array_t *page_set;
1048
cairo_output_stream_t *contents;
1049
cairo_meta_surface_t *meta;
1050
cairo_meta_snapshot_t *snapshot;
1051
unsigned int num_elements;
1054
/* search in already emitted meta snapshots */
1055
num_elements = document->meta_snapshots.num_elements;
1056
for (i = 0; i < num_elements; i++) {
1057
snapshot = _cairo_array_index (&document->meta_snapshots, i);
1058
meta = snapshot->meta;
1059
if (meta->commands.num_elements == surface->commands.num_elements &&
1060
_cairo_array_index (&meta->commands, 0) == _cairo_array_index (&surface->commands, 0)) {
1062
return CAIRO_STATUS_SUCCESS;
1066
meta = (cairo_meta_surface_t *) _cairo_surface_snapshot (&surface->base);
1067
paginated_surface = _cairo_svg_surface_create_for_document (document,
1070
meta->height_pixels);
1071
if (paginated_surface->status) {
1072
cairo_surface_destroy (&meta->base);
1073
return paginated_surface->status;
1076
svg_surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (paginated_surface);
1077
cairo_surface_set_fallback_resolution (paginated_surface,
1078
document->owner->x_fallback_resolution,
1079
document->owner->y_fallback_resolution);
1081
status = _cairo_meta_surface_replay (&meta->base, paginated_surface);
1083
cairo_surface_destroy (&meta->base);
1084
cairo_surface_destroy (paginated_surface);
1088
cairo_surface_show_page (paginated_surface);
1089
status = cairo_surface_status (paginated_surface);
1091
cairo_surface_destroy (&meta->base);
1092
cairo_surface_destroy (paginated_surface);
1096
new_snapshot.meta = meta;
1097
new_snapshot.id = svg_surface->id;
1098
status = _cairo_array_append (&document->meta_snapshots, &new_snapshot);
1100
cairo_surface_destroy (&meta->base);
1101
cairo_surface_destroy (paginated_surface);
1105
if (!svg_surface->is_base_clip_emitted) {
1106
svg_surface->is_base_clip_emitted = TRUE;
1107
_cairo_output_stream_printf (document->xml_node_defs,
1108
"<clipPath id=\"clip%d\">\n"
1109
" <rect width=\"%f\" height=\"%f\"/>\n"
1111
svg_surface->base_clip,
1113
svg_surface->height);
1116
if (meta->content == CAIRO_CONTENT_ALPHA) {
1117
_cairo_svg_surface_emit_alpha_filter (document);
1118
_cairo_output_stream_printf (document->xml_node_defs,
1119
"<g id=\"surface%d\" "
1120
"clip-path=\"url(#clip%d)\" "
1121
"filter=\"url(#alpha)\">\n",
1123
svg_surface->base_clip);
1125
_cairo_output_stream_printf (document->xml_node_defs,
1126
"<g id=\"surface%d\" "
1127
"clip-path=\"url(#clip%d)\">\n",
1129
svg_surface->base_clip);
1132
contents = svg_surface->xml_node;
1133
page_set = &svg_surface->page_set;
1135
if (_cairo_memory_stream_length (contents) > 0) {
1136
if (_cairo_svg_surface_store_page (svg_surface) == NULL) {
1137
cairo_surface_destroy (paginated_surface);
1138
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1142
if (page_set->num_elements > 0) {
1143
cairo_svg_page_t *page;
1145
page = _cairo_array_index (page_set, page_set->num_elements - 1);
1146
_cairo_memory_stream_copy (page->xml_node, document->xml_node_defs);
1149
_cairo_output_stream_printf (document->xml_node_defs, "</g>\n");
1151
*id = new_snapshot.id;
1153
status = cairo_surface_status (paginated_surface);
1154
cairo_surface_destroy (paginated_surface);
1156
/* FIXME: cairo_paginated_surface doesn't take a ref to the
1157
* passed in target surface so we can't call destroy here.
1158
* cairo_paginated_surface should be fixed, but for now just
1159
* work around it. */
1161
/* cairo_surface_destroy (svg_surface); */
1166
static cairo_status_t
1167
_cairo_svg_surface_emit_composite_meta_pattern (cairo_output_stream_t *output,
1168
cairo_svg_surface_t *surface,
1169
cairo_surface_pattern_t *pattern,
1171
const cairo_matrix_t *parent_matrix,
1172
const char *extra_attributes)
1174
cairo_svg_document_t *document = surface->document;
1175
cairo_meta_surface_t *meta_surface;
1177
cairo_status_t status;
1180
p2u = pattern->base.matrix;
1181
status = cairo_matrix_invert (&p2u);
1182
/* cairo_pattern_set_matrix ensures the matrix is invertible */
1183
assert (status == CAIRO_STATUS_SUCCESS);
1185
meta_surface = (cairo_meta_surface_t *) pattern->surface;
1187
status = _cairo_svg_surface_emit_meta_surface (document, meta_surface, &id);
1191
if (pattern_id != invalid_pattern_id) {
1192
_cairo_output_stream_printf (output,
1193
"<pattern id=\"pattern%d\" "
1194
"patternUnits=\"userSpaceOnUse\" "
1195
"width=\"%d\" height=\"%d\"",
1197
meta_surface->width_pixels,
1198
meta_surface->height_pixels);
1199
_cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
1200
_cairo_output_stream_printf (output, ">\n");
1203
_cairo_output_stream_printf (output,
1204
"<use xlink:href=\"#surface%d\"",
1207
if (pattern_id == invalid_pattern_id)
1208
_cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
1210
if (extra_attributes)
1211
_cairo_output_stream_printf (output, " %s", extra_attributes);
1213
_cairo_output_stream_printf (output, "/>\n");
1215
if (pattern_id != invalid_pattern_id)
1216
_cairo_output_stream_printf (output, "</pattern>\n");
1218
return CAIRO_STATUS_SUCCESS;
1221
static cairo_status_t
1222
_cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output,
1223
cairo_svg_surface_t *surface,
1224
cairo_surface_pattern_t *pattern,
1226
const cairo_matrix_t *parent_matrix,
1227
const char *extra_attributes)
1230
if (_cairo_surface_is_meta (pattern->surface)) {
1231
return _cairo_svg_surface_emit_composite_meta_pattern (output, surface, pattern,
1232
pattern_id, parent_matrix, extra_attributes);
1235
return _cairo_svg_surface_emit_composite_image_pattern (output, surface, pattern,
1236
pattern_id, parent_matrix, extra_attributes);
1240
_cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
1241
cairo_svg_surface_t *surface,
1242
cairo_operator_t op)
1244
char const *op_str[] = {
1247
"src", "src-over", "src-in",
1248
"src-out", "src-atop",
1250
"dst", "dst-over", "dst-in",
1251
"dst-out", "dst-atop",
1254
"color-dodge" /* FIXME: saturate ? */
1257
if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2)
1258
_cairo_output_stream_printf (output, "comp-op: %s; ", op_str[op]);
1261
static cairo_status_t
1262
_cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface,
1263
cairo_solid_pattern_t *pattern,
1264
cairo_output_stream_t *style,
1265
cairo_bool_t is_stroke)
1267
_cairo_output_stream_printf (style, is_stroke ?
1268
"stroke: rgb(%f%%,%f%%,%f%%); stroke-opacity: %f;":
1269
"fill: rgb(%f%%,%f%%,%f%%); fill-opacity: %f;",
1270
pattern->color.red * 100.0,
1271
pattern->color.green * 100.0,
1272
pattern->color.blue * 100.0,
1273
pattern->color.alpha);
1275
return CAIRO_STATUS_SUCCESS;
1278
static cairo_status_t
1279
_cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface,
1280
cairo_surface_pattern_t *pattern,
1281
cairo_output_stream_t *style,
1282
cairo_bool_t is_stroke,
1283
const cairo_matrix_t *parent_matrix)
1285
cairo_svg_document_t *document = surface->document;
1286
cairo_status_t status;
1289
pattern_id = document->pattern_id++;
1290
status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs,
1292
pattern_id, parent_matrix, NULL);
1296
_cairo_output_stream_printf (style,
1297
"%s: url(#pattern%d);",
1298
is_stroke ? "stroke" : "fill",
1301
return CAIRO_STATUS_SUCCESS;
1304
static cairo_status_t
1305
_cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output,
1306
cairo_gradient_pattern_t const *pattern,
1307
double start_offset,
1308
cairo_bool_t reverse_stops,
1309
cairo_bool_t emulate_reflect)
1311
cairo_gradient_stop_t *stops;
1313
unsigned int n_stops;
1316
if (pattern->n_stops < 1)
1317
return CAIRO_STATUS_SUCCESS;
1319
if (pattern->n_stops == 1) {
1320
_cairo_output_stream_printf (output,
1321
"<stop offset=\"%f\" style=\""
1322
"stop-color: rgb(%f%%,%f%%,%f%%); "
1323
"stop-opacity: %f;\"/>\n",
1324
pattern->stops[0].offset,
1325
pattern->stops[0].color.red * 100.0,
1326
pattern->stops[0].color.green * 100.0,
1327
pattern->stops[0].color.blue * 100.0,
1328
pattern->stops[0].color.alpha);
1329
return CAIRO_STATUS_SUCCESS;
1332
if (emulate_reflect || reverse_stops) {
1333
n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops;
1334
stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t));
1336
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1338
for (i = 0; i < pattern->n_stops; i++) {
1339
if (reverse_stops) {
1340
stops[i] = pattern->stops[pattern->n_stops - i - 1];
1341
stops[i].offset = 1.0 - stops[i].offset;
1343
stops[i] = pattern->stops[i];
1344
if (emulate_reflect) {
1345
stops[i].offset /= 2;
1346
if (i > 0 && i < (pattern->n_stops - 1)) {
1347
if (reverse_stops) {
1348
stops[i + pattern->n_stops - 1] = pattern->stops[i];
1349
stops[i + pattern->n_stops - 1].offset =
1350
0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset;
1352
stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1];
1353
stops[i + pattern->n_stops - 1].offset =
1354
1 - 0.5 * stops[i + pattern->n_stops - 1].offset;
1360
n_stops = pattern->n_stops;
1361
stops = pattern->stops;
1364
if (start_offset >= 0.0)
1365
for (i = 0; i < n_stops; i++) {
1366
offset = start_offset + (1 - start_offset ) * stops[i].offset;
1367
_cairo_output_stream_printf (output,
1368
"<stop offset=\"%f\" style=\""
1369
"stop-color: rgb(%f%%,%f%%,%f%%); "
1370
"stop-opacity: %f;\"/>\n",
1372
stops[i].color.red * 100.0,
1373
stops[i].color.green * 100.0,
1374
stops[i].color.blue * 100.0,
1375
stops[i].color.alpha);
1378
cairo_bool_t found = FALSE;
1379
unsigned int offset_index;
1380
cairo_color_t offset_color_start, offset_color_stop;
1382
for (i = 0; i < n_stops; i++) {
1383
if (stops[i].offset >= -start_offset) {
1385
if (stops[i].offset != stops[i-1].offset) {
1387
cairo_color_t *color0, *color1;
1389
x0 = stops[i-1].offset;
1390
x1 = stops[i].offset;
1391
color0 = &stops[i-1].color;
1392
color1 = &stops[i].color;
1393
offset_color_start.red = color0->red + (color1->red - color0->red)
1394
* (-start_offset - x0) / (x1 - x0);
1395
offset_color_start.green = color0->green + (color1->green - color0->green)
1396
* (-start_offset - x0) / (x1 - x0);
1397
offset_color_start.blue = color0->blue + (color1->blue - color0->blue)
1398
* (-start_offset - x0) / (x1 - x0);
1399
offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha)
1400
* (-start_offset - x0) / (x1 - x0);
1401
offset_color_stop = offset_color_start;
1403
offset_color_stop = stops[i-1].color;
1404
offset_color_start = stops[i].color;
1407
offset_color_stop = offset_color_start = stops[i].color;
1415
offset_index = n_stops - 1;
1416
offset_color_stop = offset_color_start = stops[offset_index].color;
1419
_cairo_output_stream_printf (output,
1420
"<stop offset=\"0\" style=\""
1421
"stop-color: rgb(%f%%,%f%%,%f%%); "
1422
"stop-opacity: %f;\"/>\n",
1423
offset_color_start.red * 100.0,
1424
offset_color_start.green * 100.0,
1425
offset_color_start.blue * 100.0,
1426
offset_color_start.alpha);
1427
for (i = offset_index; i < n_stops; i++) {
1428
_cairo_output_stream_printf (output,
1429
"<stop offset=\"%f\" style=\""
1430
"stop-color: rgb(%f%%,%f%%,%f%%); "
1431
"stop-opacity: %f;\"/>\n",
1432
stops[i].offset + start_offset,
1433
stops[i].color.red * 100.0,
1434
stops[i].color.green * 100.0,
1435
stops[i].color.blue * 100.0,
1436
stops[i].color.alpha);
1438
for (i = 0; i < offset_index; i++) {
1439
_cairo_output_stream_printf (output,
1440
"<stop offset=\"%f\" style=\""
1441
"stop-color: rgb(%f%%,%f%%,%f%%); "
1442
"stop-opacity: %f;\"/>\n",
1443
1.0 + stops[i].offset + start_offset,
1444
stops[i].color.red * 100.0,
1445
stops[i].color.green * 100.0,
1446
stops[i].color.blue * 100.0,
1447
stops[i].color.alpha);
1450
_cairo_output_stream_printf (output,
1451
"<stop offset=\"1\" style=\""
1452
"stop-color: rgb(%f%%,%f%%,%f%%); "
1453
"stop-opacity: %f;\"/>\n",
1454
offset_color_stop.red * 100.0,
1455
offset_color_stop.green * 100.0,
1456
offset_color_stop.blue * 100.0,
1457
offset_color_stop.alpha);
1461
if (reverse_stops || emulate_reflect)
1464
return CAIRO_STATUS_SUCCESS;
1468
_cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output,
1469
cairo_pattern_t *pattern)
1471
switch (pattern->extend) {
1472
case CAIRO_EXTEND_REPEAT:
1473
_cairo_output_stream_printf (output, "spreadMethod=\"repeat\" ");
1475
case CAIRO_EXTEND_REFLECT:
1476
_cairo_output_stream_printf (output, "spreadMethod=\"reflect\" ");
1478
case CAIRO_EXTEND_NONE:
1479
case CAIRO_EXTEND_PAD:
1484
static cairo_status_t
1485
_cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface,
1486
cairo_linear_pattern_t *pattern,
1487
cairo_output_stream_t *style,
1488
cairo_bool_t is_stroke,
1489
const cairo_matrix_t *parent_matrix)
1491
cairo_svg_document_t *document = surface->document;
1492
double x0, y0, x1, y1;
1494
cairo_status_t status;
1496
p2u = pattern->base.base.matrix;
1497
status = cairo_matrix_invert (&p2u);
1498
/* cairo_pattern_set_matrix ensures the matrix is invertible */
1499
assert (status == CAIRO_STATUS_SUCCESS);
1501
x0 = _cairo_fixed_to_double (pattern->p1.x);
1502
y0 = _cairo_fixed_to_double (pattern->p1.y);
1503
x1 = _cairo_fixed_to_double (pattern->p2.x);
1504
y1 = _cairo_fixed_to_double (pattern->p2.y);
1506
_cairo_output_stream_printf (document->xml_node_defs,
1507
"<linearGradient id=\"linear%d\" "
1508
"gradientUnits=\"userSpaceOnUse\" "
1509
"x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
1510
document->linear_pattern_id,
1513
_cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
1514
_cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1515
_cairo_output_stream_printf (document->xml_node_defs, ">\n");
1517
status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
1518
&pattern->base, 0.0,
1523
_cairo_output_stream_printf (document->xml_node_defs,
1524
"</linearGradient>\n");
1526
_cairo_output_stream_printf (style,
1527
"%s: url(#linear%d);",
1528
is_stroke ? "stroke" : "fill",
1529
document->linear_pattern_id);
1531
document->linear_pattern_id++;
1533
return CAIRO_STATUS_SUCCESS;
1536
static cairo_status_t
1537
_cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface,
1538
cairo_radial_pattern_t *pattern,
1539
cairo_output_stream_t *style,
1540
cairo_bool_t is_stroke,
1541
const cairo_matrix_t *parent_matrix)
1543
cairo_svg_document_t *document = surface->document;
1545
cairo_extend_t extend;
1546
double x0, y0, x1, y1, r0, r1;
1548
cairo_bool_t reverse_stops;
1549
cairo_status_t status;
1550
cairo_point_t *c0, *c1;
1551
cairo_fixed_t radius0, radius1;
1553
extend = pattern->base.base.extend;
1555
if (pattern->r1 < pattern->r2) {
1558
radius0 = pattern->r1;
1559
radius1 = pattern->r2;
1560
reverse_stops = FALSE;
1564
radius0 = pattern->r2;
1565
radius1 = pattern->r1;
1566
reverse_stops = TRUE;
1569
x0 = _cairo_fixed_to_double (c0->x);
1570
y0 = _cairo_fixed_to_double (c0->y);
1571
r0 = _cairo_fixed_to_double (radius0);
1572
x1 = _cairo_fixed_to_double (c1->x);
1573
y1 = _cairo_fixed_to_double (c1->y);
1574
r1 = _cairo_fixed_to_double (radius1);
1576
p2u = pattern->base.base.matrix;
1577
status = cairo_matrix_invert (&p2u);
1578
/* cairo_pattern_set_matrix ensures the matrix is invertible */
1579
assert (status == CAIRO_STATUS_SUCCESS);
1581
if (pattern->r1 == pattern->r2) {
1582
unsigned int n_stops = pattern->base.n_stops;
1584
_cairo_output_stream_printf (document->xml_node_defs,
1585
"<radialGradient id=\"radial%d\" "
1586
"gradientUnits=\"userSpaceOnUse\" "
1587
"cx=\"%f\" cy=\"%f\" "
1588
"fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1589
document->radial_pattern_id,
1592
_cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1593
_cairo_output_stream_printf (document->xml_node_defs, ">\n");
1595
if (extend == CAIRO_EXTEND_NONE || n_stops < 1)
1596
_cairo_output_stream_printf (document->xml_node_defs,
1597
"<stop offset=\"0\" style=\""
1598
"stop-color: rgb(0%%,0%%,0%%); "
1599
"stop-opacity: 0;\"/>\n");
1601
_cairo_output_stream_printf (document->xml_node_defs,
1602
"<stop offset=\"0\" style=\""
1603
"stop-color: rgb(%f%%,%f%%,%f%%); "
1604
"stop-opacity: %f;\"/>\n",
1605
pattern->base.stops[0].color.red * 100.0,
1606
pattern->base.stops[0].color.green * 100.0,
1607
pattern->base.stops[0].color.blue * 100.0,
1608
pattern->base.stops[0].color.alpha);
1610
_cairo_output_stream_printf (document->xml_node_defs,
1611
"<stop offset=\"0\" style=\""
1612
"stop-color: rgb(%f%%,%f%%,%f%%); "
1613
"stop-opacity: %f;\"/>\n",
1614
pattern->base.stops[n_stops - 1].color.red * 100.0,
1615
pattern->base.stops[n_stops - 1].color.green * 100.0,
1616
pattern->base.stops[n_stops - 1].color.blue * 100.0,
1617
pattern->base.stops[n_stops - 1].color.alpha);
1621
double offset, r, x, y;
1622
cairo_bool_t emulate_reflect = FALSE;
1624
fx = (r1 * x0 - r0 * x1) / (r1 - r0);
1625
fy = (r1 * y0 - r0 * y1) / (r1 - r0);
1627
/* SVG doesn't support the inner circle and use instead a gradient focal.
1628
* That means we need to emulate the cairo behaviour by processing the
1629
* cairo gradient stops.
1630
* The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
1631
* it's just a matter of stop position translation and calculation of
1632
* the corresponding SVG radial gradient focal.
1633
* The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
1634
* radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
1635
* case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
1636
* list that maps to the original cairo stop list.
1638
if ((extend == CAIRO_EXTEND_REFLECT
1639
|| extend == CAIRO_EXTEND_REPEAT)
1643
if (extend == CAIRO_EXTEND_REFLECT) {
1645
emulate_reflect = TRUE;
1648
offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
1651
/* New position of outer circle. */
1652
x = r * (x1 - fx) / r_org + fx;
1653
y = r * (y1 - fy) / r_org + fy;
1663
_cairo_output_stream_printf (document->xml_node_defs,
1664
"<radialGradient id=\"radial%d\" "
1665
"gradientUnits=\"userSpaceOnUse\" "
1666
"cx=\"%f\" cy=\"%f\" "
1667
"fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1668
document->radial_pattern_id,
1672
if (emulate_reflect)
1673
_cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
1675
_cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
1676
_cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1677
_cairo_output_stream_printf (document->xml_node_defs, ">\n");
1679
/* To support cairo's EXTEND_NONE, (for which SVG has no similar
1680
* notion), we add transparent color stops on either end of the
1681
* user-provided stops. */
1682
if (extend == CAIRO_EXTEND_NONE) {
1683
_cairo_output_stream_printf (document->xml_node_defs,
1684
"<stop offset=\"0\" style=\""
1685
"stop-color: rgb(0%%,0%%,0%%); "
1686
"stop-opacity: 0;\"/>\n");
1688
_cairo_output_stream_printf (document->xml_node_defs,
1689
"<stop offset=\"%f\" style=\""
1690
"stop-color: rgb(0%%,0%%,0%%); "
1691
"stop-opacity: 0;\"/>\n",
1694
status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
1695
&pattern->base, offset,
1701
if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
1702
_cairo_output_stream_printf (document->xml_node_defs,
1703
"<stop offset=\"1.0\" style=\""
1704
"stop-color: rgb(0%%,0%%,0%%); "
1705
"stop-opacity: 0;\"/>\n");
1708
_cairo_output_stream_printf (document->xml_node_defs,
1709
"</radialGradient>\n");
1711
_cairo_output_stream_printf (style,
1712
"%s: url(#radial%d);",
1713
is_stroke ? "stroke" : "fill",
1714
document->radial_pattern_id);
1716
document->radial_pattern_id++;
1718
return CAIRO_STATUS_SUCCESS;
1721
static cairo_status_t
1722
_cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface,
1723
cairo_pattern_t *pattern,
1724
cairo_output_stream_t *output,
1725
cairo_bool_t is_stroke,
1726
const cairo_matrix_t *parent_matrix)
1728
switch (pattern->type) {
1729
case CAIRO_PATTERN_TYPE_SOLID:
1730
return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern,
1733
case CAIRO_PATTERN_TYPE_SURFACE:
1734
return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern,
1735
output, is_stroke, parent_matrix);
1737
case CAIRO_PATTERN_TYPE_LINEAR:
1738
return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern,
1739
output, is_stroke, parent_matrix);
1741
case CAIRO_PATTERN_TYPE_RADIAL:
1742
return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern,
1743
output, is_stroke, parent_matrix);
1745
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
1748
static cairo_status_t
1749
_cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output,
1750
cairo_svg_surface_t *surface,
1751
cairo_operator_t op,
1752
cairo_pattern_t *source,
1753
cairo_fill_rule_t fill_rule,
1754
cairo_matrix_t *parent_matrix)
1756
_cairo_output_stream_printf (output,
1758
fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
1759
"evenodd" : "nonzero");
1760
_cairo_svg_surface_emit_operator (output, surface, op);
1761
return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix);
1764
static cairo_status_t
1765
_cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output,
1766
cairo_svg_surface_t *surface,
1767
cairo_operator_t op,
1768
cairo_pattern_t *source,
1769
cairo_stroke_style_t *stroke_style,
1770
cairo_matrix_t *parent_matrix)
1772
cairo_status_t status;
1773
const char *line_cap, *line_join;
1776
switch (stroke_style->line_cap) {
1777
case CAIRO_LINE_CAP_BUTT:
1780
case CAIRO_LINE_CAP_ROUND:
1783
case CAIRO_LINE_CAP_SQUARE:
1784
line_cap = "square";
1790
switch (stroke_style->line_join) {
1791
case CAIRO_LINE_JOIN_MITER:
1792
line_join = "miter";
1794
case CAIRO_LINE_JOIN_ROUND:
1795
line_join = "round";
1797
case CAIRO_LINE_JOIN_BEVEL:
1798
line_join = "bevel";
1804
_cairo_output_stream_printf (output,
1805
"stroke-width: %f; "
1806
"stroke-linecap: %s; "
1807
"stroke-linejoin: %s; ",
1808
stroke_style->line_width,
1812
status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix);
1816
_cairo_svg_surface_emit_operator (output, surface, op);
1818
if (stroke_style->num_dashes > 0) {
1819
_cairo_output_stream_printf (output, "stroke-dasharray: ");
1820
for (i = 0; i < stroke_style->num_dashes; i++) {
1821
_cairo_output_stream_printf (output, "%f",
1822
stroke_style->dash[i]);
1823
if (i + 1 < stroke_style->num_dashes)
1824
_cairo_output_stream_printf (output, ",");
1826
_cairo_output_stream_printf (output, "; ");
1828
if (stroke_style->dash_offset != 0.0) {
1829
_cairo_output_stream_printf (output,
1830
"stroke-dashoffset: %f; ",
1831
stroke_style->dash_offset);
1835
_cairo_output_stream_printf (output,
1836
"stroke-miterlimit: %f; ",
1837
stroke_style->miter_limit);
1839
return CAIRO_STATUS_SUCCESS;
1842
static cairo_int_status_t
1843
_cairo_svg_surface_fill_stroke (void *abstract_surface,
1844
cairo_operator_t fill_op,
1845
cairo_pattern_t *fill_source,
1846
cairo_fill_rule_t fill_rule,
1847
double fill_tolerance,
1848
cairo_antialias_t fill_antialias,
1849
cairo_path_fixed_t *path,
1850
cairo_operator_t stroke_op,
1851
cairo_pattern_t *stroke_source,
1852
cairo_stroke_style_t *stroke_style,
1853
cairo_matrix_t *stroke_ctm,
1854
cairo_matrix_t *stroke_ctm_inverse,
1855
double stroke_tolerance,
1856
cairo_antialias_t stroke_antialias)
1858
cairo_svg_surface_t *surface = abstract_surface;
1859
cairo_status_t status;
1861
_cairo_output_stream_printf (surface->xml_node, "<path style=\"");
1862
status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, fill_op,
1863
fill_source, fill_rule, stroke_ctm_inverse);
1867
status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op,
1868
stroke_source, stroke_style, stroke_ctm_inverse);
1872
_cairo_output_stream_printf (surface->xml_node, "\" ");
1874
status = _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse);
1878
_cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL);
1879
_cairo_output_stream_printf (surface->xml_node, "/>\n");
1881
return CAIRO_STATUS_SUCCESS;
1884
static cairo_int_status_t
1885
_cairo_svg_surface_fill (void *abstract_surface,
1886
cairo_operator_t op,
1887
cairo_pattern_t *source,
1888
cairo_path_fixed_t *path,
1889
cairo_fill_rule_t fill_rule,
1891
cairo_antialias_t antialias)
1893
cairo_svg_surface_t *surface = abstract_surface;
1894
cairo_status_t status;
1896
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
1897
return _cairo_svg_surface_analyze_operation (surface, op, source);
1899
assert (_cairo_svg_surface_operation_supported (surface, op, source));
1901
_cairo_output_stream_printf (surface->xml_node, "<path style=\" stroke:none;");
1902
status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, op, source, fill_rule, NULL);
1906
_cairo_output_stream_printf (surface->xml_node, "\" ");
1908
status = _cairo_svg_surface_emit_path (surface->xml_node, path, NULL);
1912
_cairo_output_stream_printf (surface->xml_node, "/>\n");
1914
return CAIRO_STATUS_SUCCESS;
1917
static cairo_int_status_t
1918
_cairo_svg_surface_get_extents (void *abstract_surface,
1919
cairo_rectangle_int_t *rectangle)
1921
cairo_svg_surface_t *surface = abstract_surface;
1926
/* XXX: The conversion to integers here is pretty bogus, (not to
1927
* mention the aribitray limitation of width to a short(!). We
1928
* may need to come up with a better interface for get_size.
1930
rectangle->width = (int) ceil (surface->width);
1931
rectangle->height = (int) ceil (surface->height);
1933
return CAIRO_STATUS_SUCCESS;
1936
static cairo_status_t
1937
_cairo_svg_surface_emit_paint (cairo_output_stream_t *output,
1938
cairo_svg_surface_t *surface,
1939
cairo_operator_t op,
1940
cairo_pattern_t *source,
1941
cairo_pattern_t *mask_source,
1942
const char *extra_attributes)
1944
cairo_status_t status;
1946
if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
1947
source->extend == CAIRO_EXTEND_NONE)
1948
return _cairo_svg_surface_emit_composite_pattern (output,
1950
(cairo_surface_pattern_t *) source,
1952
mask_source ? &mask_source->matrix :NULL,
1955
_cairo_output_stream_printf (output,
1956
"<rect x=\"0\" y=\"0\" "
1957
"width=\"%f\" height=\"%f\" "
1959
surface->width, surface->height);
1960
_cairo_svg_surface_emit_operator (output, surface, op);
1961
status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL);
1965
_cairo_output_stream_printf (output, " stroke: none;\"");
1967
if (extra_attributes)
1968
_cairo_output_stream_printf (output, " %s", extra_attributes);
1970
_cairo_output_stream_printf (output, "/>\n");
1972
return CAIRO_STATUS_SUCCESS;
1975
static cairo_int_status_t
1976
_cairo_svg_surface_paint (void *abstract_surface,
1977
cairo_operator_t op,
1978
cairo_pattern_t *source)
1980
cairo_status_t status;
1981
cairo_svg_surface_t *surface = abstract_surface;
1983
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
1984
return _cairo_svg_surface_analyze_operation (surface, op, source);
1986
/* XXX: It would be nice to be able to assert this condition
1987
* here. But, we actually allow one 'cheat' that is used when
1988
* painting the final image-based fallbacks. The final fallbacks
1989
* do have alpha which we support by blending with white. This is
1990
* possible only because there is nothing between the fallback
1991
* images and the paper, nor is anything painted above. */
1993
assert (_cairo_svg_surface_operation_supported (surface, op, source));
1996
/* Emulation of clear and source operators, when no clipping region
1997
* is defined. We just delete existing content of surface root node,
1998
* and exit early if operator is clear.
1999
* XXX: optimization of SOURCE operator doesn't work, since analyze
2000
* above always return FALSE. In order to make it work, we need a way
2001
* to know if there's an active clipping path.
2002
* Optimization of CLEAR works because of a test in paginated surface,
2003
* and an optimiszation in meta surface. */
2004
if (surface->clip_level == 0 &&
2005
(op == CAIRO_OPERATOR_CLEAR ||
2006
op == CAIRO_OPERATOR_SOURCE))
2008
status = _cairo_output_stream_destroy (surface->xml_node);
2010
surface->xml_node = NULL;
2014
surface->xml_node = _cairo_memory_stream_create ();
2015
if (_cairo_output_stream_get_status (surface->xml_node)) {
2016
status = _cairo_output_stream_destroy (surface->xml_node);
2017
surface->xml_node = NULL;
2021
if (op == CAIRO_OPERATOR_CLEAR) {
2022
if (surface->content == CAIRO_CONTENT_COLOR) {
2023
_cairo_output_stream_printf (surface->xml_node,
2025
"width=\"%f\" height=\"%f\" "
2026
"style=\"opacity: 1; "
2028
"fill: rgb(0,0,0);\"/>\n",
2029
surface->width, surface->height);
2031
return CAIRO_STATUS_SUCCESS;
2035
return _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, NULL);
2038
static cairo_int_status_t
2039
_cairo_svg_surface_mask (void *abstract_surface,
2040
cairo_operator_t op,
2041
cairo_pattern_t *source,
2042
cairo_pattern_t *mask)
2044
cairo_status_t status;
2045
cairo_svg_surface_t *surface = abstract_surface;
2046
cairo_svg_document_t *document = surface->document;
2047
cairo_output_stream_t *mask_stream;
2049
cairo_bool_t discard_filter = FALSE;
2050
unsigned int mask_id;
2052
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2053
return _cairo_svg_surface_analyze_operation (surface, op, source);
2055
assert (_cairo_svg_surface_operation_supported (surface, op, source));
2057
if (cairo_pattern_get_type (mask) == CAIRO_PATTERN_TYPE_SURFACE) {
2058
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t*) mask;
2059
cairo_content_t content = cairo_surface_get_content (surface_pattern->surface);
2060
if (content == CAIRO_CONTENT_ALPHA)
2061
discard_filter = TRUE;
2064
if (!discard_filter)
2065
_cairo_svg_surface_emit_alpha_filter (document);
2067
/* _cairo_svg_surface_emit_paint() will output a pattern definition to
2068
* document->xml_node_defs so we need to write the mask element to
2069
* a temporary stream and then copy that to xml_node_defs. */
2070
mask_stream = _cairo_memory_stream_create ();
2071
if (_cairo_output_stream_get_status (mask_stream))
2072
return _cairo_output_stream_destroy (mask_stream);
2074
mask_id = _cairo_svg_document_allocate_mask_id (document);
2076
_cairo_output_stream_printf (mask_stream,
2077
"<mask id=\"mask%d\">\n"
2080
discard_filter ? "" : " <g filter=\"url(#alpha)\">\n");
2081
status = _cairo_svg_surface_emit_paint (mask_stream, surface, op, mask, source, NULL);
2083
cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream);
2088
_cairo_output_stream_printf (mask_stream,
2091
discard_filter ? "" : " </g>\n");
2092
_cairo_memory_stream_copy (mask_stream, document->xml_node_defs);
2094
status = _cairo_output_stream_destroy (mask_stream);
2098
snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"",
2100
status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer);
2104
return CAIRO_STATUS_SUCCESS;
2107
static cairo_int_status_t
2108
_cairo_svg_surface_stroke (void *abstract_dst,
2109
cairo_operator_t op,
2110
cairo_pattern_t *source,
2111
cairo_path_fixed_t *path,
2112
cairo_stroke_style_t *stroke_style,
2113
cairo_matrix_t *ctm,
2114
cairo_matrix_t *ctm_inverse,
2116
cairo_antialias_t antialias)
2118
cairo_svg_surface_t *surface = abstract_dst;
2119
cairo_status_t status;
2121
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2122
return _cairo_svg_surface_analyze_operation (surface, op, source);
2124
assert (_cairo_svg_surface_operation_supported (surface, op, source));
2126
_cairo_output_stream_printf (surface->xml_node, "<path style=\"fill: none; ");
2127
status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, op,
2128
source, stroke_style, ctm_inverse);
2132
_cairo_output_stream_printf (surface->xml_node, "\" ");
2134
status = _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse);
2138
_cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL);
2139
_cairo_output_stream_printf (surface->xml_node, "/>\n");
2141
return CAIRO_STATUS_SUCCESS;
2144
static cairo_int_status_t
2145
_cairo_svg_surface_show_glyphs (void *abstract_surface,
2146
cairo_operator_t op,
2147
cairo_pattern_t *pattern,
2148
cairo_glyph_t *glyphs,
2150
cairo_scaled_font_t *scaled_font,
2151
int *remaining_glyphs)
2153
cairo_svg_surface_t *surface = abstract_surface;
2154
cairo_svg_document_t *document = surface->document;
2155
cairo_path_fixed_t path;
2156
cairo_status_t status;
2157
cairo_scaled_font_subsets_glyph_t subset_glyph;
2160
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2161
return _cairo_svg_surface_analyze_operation (surface, op, pattern);
2163
assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
2165
if (num_glyphs <= 0)
2166
return CAIRO_STATUS_SUCCESS;
2168
/* FIXME it's probably possible to apply a pattern of a gradient to
2169
* a group of symbols, but I don't know how yet. Gradients or patterns
2170
* are translated by x and y properties of use element. */
2171
if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
2174
_cairo_output_stream_printf (surface->xml_node, "<g style=\"");
2175
status = _cairo_svg_surface_emit_pattern (surface, pattern,
2176
surface->xml_node, FALSE, NULL);
2180
_cairo_output_stream_printf (surface->xml_node, "\">\n");
2182
for (i = 0; i < num_glyphs; i++) {
2183
status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets,
2184
scaled_font, glyphs[i].index,
2187
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
2188
_cairo_output_stream_printf (surface->xml_node, "</g>\n");
2198
_cairo_output_stream_printf (surface->xml_node,
2199
" <use xlink:href=\"#glyph%d-%d\" "
2200
"x=\"%f\" y=\"%f\"/>\n",
2201
subset_glyph.font_id,
2202
subset_glyph.subset_glyph_index,
2203
glyphs[i].x, glyphs[i].y);
2206
_cairo_output_stream_printf (surface->xml_node, "</g>\n");
2208
return CAIRO_STATUS_SUCCESS;
2211
_cairo_path_fixed_init (&path);
2213
status = _cairo_scaled_font_glyph_path (scaled_font,(cairo_glyph_t *) glyphs, num_glyphs, &path);
2216
_cairo_path_fixed_fini (&path);
2220
status = _cairo_svg_surface_fill (abstract_surface, op, pattern,
2221
&path, CAIRO_FILL_RULE_WINDING, 0.0, CAIRO_ANTIALIAS_SUBPIXEL);
2223
_cairo_path_fixed_fini (&path);
2228
static cairo_int_status_t
2229
_cairo_svg_surface_intersect_clip_path (void *dst,
2230
cairo_path_fixed_t *path,
2231
cairo_fill_rule_t fill_rule,
2233
cairo_antialias_t antialias)
2235
cairo_svg_surface_t *surface = dst;
2236
cairo_svg_document_t *document = surface->document;
2237
cairo_status_t status;
2241
for (i = 0; i < surface->clip_level; i++)
2242
_cairo_output_stream_printf (surface->xml_node, "</g>\n");
2244
surface->clip_level = 0;
2245
return CAIRO_STATUS_SUCCESS;
2248
_cairo_output_stream_printf (document->xml_node_defs,
2249
"<clipPath id=\"clip%d\">\n"
2252
status = _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL);
2256
_cairo_output_stream_printf (document->xml_node_defs,
2260
_cairo_output_stream_printf (surface->xml_node,
2261
"<g clip-path=\"url(#clip%d)\" "
2262
"clip-rule=\"%s\">\n",
2264
fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
2265
"evenodd" : "nonzero");
2267
document->clip_id++;
2268
surface->clip_level++;
2270
return CAIRO_STATUS_SUCCESS;
2274
_cairo_svg_surface_get_font_options (void *abstract_surface,
2275
cairo_font_options_t *options)
2277
_cairo_font_options_init_default (options);
2279
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
2280
cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
2281
cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
2284
static const cairo_surface_backend_t cairo_svg_surface_backend = {
2285
CAIRO_SURFACE_TYPE_SVG,
2286
_cairo_svg_surface_create_similar,
2287
_cairo_svg_surface_finish,
2288
NULL, /* acquire_source_image */
2289
NULL, /* release_source_image */
2290
NULL, /* acquire_dest_image */
2291
NULL, /* release_dest_image */
2292
NULL, /* clone_similar */
2293
NULL, /* _cairo_svg_surface_composite, */
2294
NULL, /* _cairo_svg_surface_fill_rectangles, */
2295
NULL, /* _cairo_svg_surface_composite_trapezoids,*/
2296
_cairo_svg_surface_copy_page,
2297
_cairo_svg_surface_show_page,
2298
NULL, /* set_clip_region */
2299
_cairo_svg_surface_intersect_clip_path,
2300
_cairo_svg_surface_get_extents,
2301
NULL, /* _cairo_svg_surface_old_show_glyphs, */
2302
_cairo_svg_surface_get_font_options,
2304
NULL, /* mark dirty rectangle */
2305
NULL, /* scaled font fini */
2306
NULL, /* scaled glyph fini */
2307
_cairo_svg_surface_paint,
2308
_cairo_svg_surface_mask,
2309
_cairo_svg_surface_stroke,
2310
_cairo_svg_surface_fill,
2311
_cairo_svg_surface_show_glyphs,
2312
NULL, /* snapshot */
2313
NULL, /* is_similar */
2315
_cairo_svg_surface_fill_stroke
2318
static cairo_status_t
2319
_cairo_svg_document_create (cairo_output_stream_t *output_stream,
2322
cairo_svg_version_t version,
2323
cairo_svg_document_t **document_out)
2325
cairo_svg_document_t *document;
2326
cairo_status_t status, status_ignored;
2328
if (output_stream->status)
2329
return output_stream->status;
2331
document = malloc (sizeof (cairo_svg_document_t));
2332
if (document == NULL)
2333
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2335
/* The use of defs for font glyphs imposes no per-subset limit. */
2336
document->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
2337
if (document->font_subsets == NULL) {
2338
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2339
goto CLEANUP_DOCUMENT;
2342
document->output_stream = output_stream;
2343
document->refcount = 1;
2344
document->owner = NULL;
2345
document->finished = FALSE;
2346
document->width = width;
2347
document->height = height;
2349
document->surface_id = 0;
2350
document->linear_pattern_id = 0;
2351
document->radial_pattern_id = 0;
2352
document->pattern_id = 0;
2353
document->filter_id = 0;
2354
document->clip_id = 0;
2355
document->mask_id = 0;
2357
document->xml_node_defs = _cairo_memory_stream_create ();
2358
status = _cairo_output_stream_get_status (document->xml_node_defs);
2360
goto CLEANUP_NODE_DEFS;
2362
document->xml_node_glyphs = _cairo_memory_stream_create ();
2363
status = _cairo_output_stream_get_status (document->xml_node_glyphs);
2365
goto CLEANUP_NODE_GLYPHS;
2367
document->alpha_filter = FALSE;
2369
_cairo_array_init (&document->meta_snapshots,
2370
sizeof (cairo_meta_snapshot_t));
2372
document->svg_version = version;
2374
*document_out = document;
2375
return CAIRO_STATUS_SUCCESS;
2377
CLEANUP_NODE_GLYPHS:
2378
status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs);
2380
status_ignored = _cairo_output_stream_destroy (document->xml_node_defs);
2381
_cairo_scaled_font_subsets_destroy (document->font_subsets);
2387
static cairo_svg_document_t *
2388
_cairo_svg_document_reference (cairo_svg_document_t *document)
2390
document->refcount++;
2396
_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document)
2398
return document->mask_id++;
2401
static cairo_status_t
2402
_cairo_svg_document_destroy (cairo_svg_document_t *document)
2404
cairo_status_t status;
2406
document->refcount--;
2407
if (document->refcount > 0)
2408
return CAIRO_STATUS_SUCCESS;
2410
status = _cairo_svg_document_finish (document);
2417
static cairo_status_t
2418
_cairo_svg_document_finish (cairo_svg_document_t *document)
2420
cairo_status_t status, status2;
2421
cairo_output_stream_t *output = document->output_stream;
2422
cairo_meta_snapshot_t *snapshot;
2423
cairo_svg_page_t *page;
2426
if (document->finished)
2427
return CAIRO_STATUS_SUCCESS;
2429
_cairo_output_stream_printf (output,
2430
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2431
"<svg xmlns=\"http://www.w3.org/2000/svg\" "
2432
"xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
2433
"width=\"%fpt\" height=\"%fpt\" "
2434
"viewBox=\"0 0 %f %f\" version=\"%s\">\n",
2435
document->width, document->height,
2436
document->width, document->height,
2437
_cairo_svg_internal_version_strings [document->svg_version]);
2439
status = _cairo_svg_document_emit_font_subsets (document);
2441
if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 ||
2442
_cairo_memory_stream_length (document->xml_node_defs) > 0) {
2443
_cairo_output_stream_printf (output, "<defs>\n");
2444
if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
2445
_cairo_output_stream_printf (output, "<g>\n");
2446
_cairo_memory_stream_copy (document->xml_node_glyphs, output);
2447
_cairo_output_stream_printf (output, "</g>\n");
2449
_cairo_memory_stream_copy (document->xml_node_defs, output);
2450
_cairo_output_stream_printf (output, "</defs>\n");
2453
if (document->owner != NULL) {
2454
cairo_svg_surface_t *surface;
2456
surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner);
2457
if (surface->xml_node != NULL &&
2458
_cairo_memory_stream_length (surface->xml_node) > 0) {
2459
if (_cairo_svg_surface_store_page (surface) == NULL) {
2460
if (status == CAIRO_STATUS_SUCCESS)
2461
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2465
if (surface->page_set.num_elements > 1 &&
2466
_cairo_svg_version_has_page_set_support (document->svg_version)) {
2467
_cairo_output_stream_printf (output, "<pageSet>\n");
2468
for (i = 0; i < surface->page_set.num_elements; i++) {
2469
page = _cairo_array_index (&surface->page_set, i);
2470
_cairo_output_stream_printf (output, "<page>\n");
2471
_cairo_output_stream_printf (output,
2472
"<g id=\"surface%d\">\n",
2474
_cairo_memory_stream_copy (page->xml_node, output);
2475
_cairo_output_stream_printf (output, "</g>\n</page>\n");
2477
_cairo_output_stream_printf (output, "</pageSet>\n");
2478
} else if (surface->page_set.num_elements > 0) {
2479
page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
2480
_cairo_output_stream_printf (output,
2481
"<g id=\"surface%d\">\n",
2483
_cairo_memory_stream_copy (page->xml_node, output);
2484
_cairo_output_stream_printf (output, "</g>\n");
2488
_cairo_output_stream_printf (output, "</svg>\n");
2490
status2 = _cairo_output_stream_destroy (document->xml_node_glyphs);
2491
if (status == CAIRO_STATUS_SUCCESS)
2494
status2 = _cairo_output_stream_destroy (document->xml_node_defs);
2495
if (status == CAIRO_STATUS_SUCCESS)
2498
status2 = _cairo_output_stream_destroy (output);
2499
if (status == CAIRO_STATUS_SUCCESS)
2502
for (i = 0; i < document->meta_snapshots.num_elements; i++) {
2503
snapshot = _cairo_array_index (&document->meta_snapshots, i);
2504
status2 = cairo_surface_status (&snapshot->meta->base);
2505
cairo_surface_destroy (&snapshot->meta->base);
2506
if (status == CAIRO_STATUS_SUCCESS)
2509
_cairo_array_fini (&document->meta_snapshots);
2511
document->finished = TRUE;
2517
_cairo_svg_surface_set_paginated_mode (void *abstract_surface,
2518
cairo_paginated_mode_t paginated_mode)
2520
cairo_svg_surface_t *surface = abstract_surface;
2522
surface->paginated_mode = paginated_mode;
2525
static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
2526
NULL /*_cairo_svg_surface_start_page*/,
2527
_cairo_svg_surface_set_paginated_mode