~ubuntu-branches/ubuntu/karmic/moon/karmic

« back to all changes in this revision

Viewing changes to cairo/src/cairo-svg-surface.c

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2009-02-14 12:01:08 UTC
  • Revision ID: james.westby@ubuntu.com-20090214120108-06539vb25vhbd8bn
Tags: upstream-1.0
ImportĀ upstreamĀ versionĀ 1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
3
 *
 
4
 * Copyright Ā© 2004 Red Hat, Inc
 
5
 * Copyright Ā© 2005-2007 Emmanuel Pacaud <emmanuel.pacaud@free.fr>
 
6
 * Copyright Ā© 2006 Red Hat, Inc
 
7
 *
 
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.
 
15
 *
 
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
 
21
 *
 
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/
 
26
 *
 
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.
 
30
 *
 
31
 * The Original Code is the cairo graphics library.
 
32
 *
 
33
 * The Initial Developer of the Original Code is University of Southern
 
34
 * California.
 
35
 *
 
36
 * Contributor(s):
 
37
 *      Kristian HĆøgsberg <krh@redhat.com>
 
38
 *      Emmanuel Pacaud <emmanuel.pacaud@free.fr>
 
39
 *      Carl Worth <cworth@cworth.org>
 
40
 */
 
41
 
 
42
#define _BSD_SOURCE /* for snprintf() */
 
43
#include "cairoint.h"
 
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"
 
51
 
 
52
typedef struct cairo_svg_page cairo_svg_page_t;
 
53
 
 
54
static const int invalid_pattern_id = -1;
 
55
 
 
56
static const cairo_svg_version_t _cairo_svg_versions[] =
 
57
{
 
58
    CAIRO_SVG_VERSION_1_1,
 
59
    CAIRO_SVG_VERSION_1_2
 
60
};
 
61
 
 
62
#define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
 
63
 
 
64
static cairo_bool_t
 
65
_cairo_svg_version_has_page_set_support (cairo_svg_version_t version)
 
66
{
 
67
    return version > CAIRO_SVG_VERSION_1_1;
 
68
}
 
69
 
 
70
static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] =
 
71
{
 
72
    "SVG 1.1",
 
73
    "SVG 1.2"
 
74
};
 
75
 
 
76
static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] =
 
77
{
 
78
    "1.1",
 
79
    "1.2"
 
80
};
 
81
 
 
82
struct cairo_svg_page {
 
83
    unsigned int surface_id;
 
84
    unsigned int clip_level;
 
85
    cairo_output_stream_t *xml_node;
 
86
};
 
87
 
 
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;
 
93
 
 
94
    double width;
 
95
    double height;
 
96
 
 
97
    cairo_output_stream_t *xml_node_defs;
 
98
    cairo_output_stream_t *xml_node_glyphs;
 
99
 
 
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;
 
107
 
 
108
    cairo_bool_t alpha_filter;
 
109
 
 
110
    cairo_array_t meta_snapshots;
 
111
 
 
112
    cairo_svg_version_t svg_version;
 
113
 
 
114
    cairo_scaled_font_subsets_t *font_subsets;
 
115
};
 
116
 
 
117
typedef struct {
 
118
    unsigned int id;
 
119
    cairo_meta_surface_t *meta;
 
120
} cairo_meta_snapshot_t;
 
121
 
 
122
static cairo_status_t
 
123
_cairo_svg_document_create (cairo_output_stream_t        *stream,
 
124
                            double                        width,
 
125
                            double                        height,
 
126
                            cairo_svg_version_t           version,
 
127
                            cairo_svg_document_t        **document_out);
 
128
 
 
129
static cairo_status_t
 
130
_cairo_svg_document_destroy (cairo_svg_document_t *document);
 
131
 
 
132
static cairo_status_t
 
133
_cairo_svg_document_finish (cairo_svg_document_t *document);
 
134
 
 
135
static cairo_svg_document_t *
 
136
_cairo_svg_document_reference (cairo_svg_document_t *document);
 
137
 
 
138
static unsigned int
 
139
_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document);
 
140
 
 
141
static cairo_surface_t *
 
142
_cairo_svg_surface_create_for_document (cairo_svg_document_t    *document,
 
143
                                        cairo_content_t          content,
 
144
                                        double                   width,
 
145
                                        double                   height);
 
146
static cairo_surface_t *
 
147
_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t    *stream,
 
148
                                               double                    width,
 
149
                                               double                    height,
 
150
                                               cairo_svg_version_t       version);
 
151
 
 
152
static const cairo_surface_backend_t cairo_svg_surface_backend;
 
153
static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend;
 
154
 
 
155
/**
 
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)
 
161
 *
 
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.
 
164
 *
 
165
 * Return value: a pointer to the newly created surface. The caller
 
166
 * owns the surface and should call cairo_surface_destroy() when done
 
167
 * with it.
 
168
 *
 
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.
 
172
 *
 
173
 * Since: 1.2
 
174
 */
 
175
cairo_surface_t *
 
176
cairo_svg_surface_create_for_stream (cairo_write_func_t          write_func,
 
177
                                     void                       *closure,
 
178
                                     double                      width,
 
179
                                     double                      height)
 
180
{
 
181
    cairo_output_stream_t *stream;
 
182
 
 
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));
 
186
 
 
187
    return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
 
188
}
 
189
 
 
190
/**
 
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)
 
195
 *
 
196
 * Creates a SVG surface of the specified size in points to be written
 
197
 * to @filename.
 
198
 *
 
199
 * Return value: a pointer to the newly created surface. The caller
 
200
 * owns the surface and should call cairo_surface_destroy() when done
 
201
 * with it.
 
202
 *
 
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.
 
206
 *
 
207
 * Since: 1.2
 
208
 **/
 
209
cairo_surface_t *
 
210
cairo_svg_surface_create (const char    *filename,
 
211
                          double         width,
 
212
                          double         height)
 
213
{
 
214
    cairo_output_stream_t *stream;
 
215
 
 
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));
 
219
 
 
220
    return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
 
221
}
 
222
 
 
223
static cairo_bool_t
 
224
_cairo_surface_is_svg (cairo_surface_t *surface)
 
225
{
 
226
    return surface->backend == &cairo_svg_surface_backend;
 
227
}
 
228
 
 
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.
 
232
 */
 
233
static cairo_status_t
 
234
_extract_svg_surface (cairo_surface_t            *surface,
 
235
                      cairo_svg_surface_t       **svg_surface)
 
236
{
 
237
    cairo_surface_t *target;
 
238
 
 
239
    if (surface->status)
 
240
        return surface->status;
 
241
 
 
242
    if (! _cairo_surface_is_paginated (surface))
 
243
        return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
244
 
 
245
    target = _cairo_paginated_surface_get_target (surface);
 
246
    if (target->status)
 
247
        return target->status;
 
248
 
 
249
    if (! _cairo_surface_is_svg (target))
 
250
        return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
251
 
 
252
    *svg_surface = (cairo_svg_surface_t *) target;
 
253
 
 
254
    return CAIRO_STATUS_SUCCESS;
 
255
}
 
256
 
 
257
/**
 
258
 * cairo_svg_surface_restrict_to_version:
 
259
 * @surface: a SVG #cairo_surface_t
 
260
 * @version: SVG version
 
261
 *
 
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.
 
264
 *
 
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
 
268
 * surface.
 
269
 *
 
270
 * Since: 1.2
 
271
 **/
 
272
void
 
273
cairo_svg_surface_restrict_to_version (cairo_surface_t          *abstract_surface,
 
274
                                       cairo_svg_version_t       version)
 
275
{
 
276
    cairo_svg_surface_t *surface = NULL; /* hide compiler warning */
 
277
    cairo_status_t status;
 
278
 
 
279
    status = _extract_svg_surface (abstract_surface, &surface);
 
280
    if (status) {
 
281
        status = _cairo_surface_set_error (abstract_surface, status);
 
282
        return;
 
283
    }
 
284
 
 
285
    if (version < CAIRO_SVG_VERSION_LAST)
 
286
        surface->document->svg_version = version;
 
287
}
 
288
 
 
289
/**
 
290
 * cairo_svg_get_versions:
 
291
 * @versions: supported version list
 
292
 * @num_versions: list length
 
293
 *
 
294
 * Used to retrieve the list of supported versions. See
 
295
 * cairo_svg_surface_restrict_to_version().
 
296
 *
 
297
 * Since: 1.2
 
298
 **/
 
299
void
 
300
cairo_svg_get_versions (cairo_svg_version_t const       **versions,
 
301
                        int                              *num_versions)
 
302
{
 
303
    if (versions != NULL)
 
304
        *versions = _cairo_svg_versions;
 
305
 
 
306
    if (num_versions != NULL)
 
307
        *num_versions = CAIRO_SVG_VERSION_LAST;
 
308
}
 
309
 
 
310
/**
 
311
 * cairo_svg_version_to_string:
 
312
 * @version: a version id
 
313
 *
 
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.
 
317
 *
 
318
 * Return value: the string associated to given version.
 
319
 *
 
320
 * Since: 1.2
 
321
 **/
 
322
const char *
 
323
cairo_svg_version_to_string (cairo_svg_version_t version)
 
324
{
 
325
    if (version >= CAIRO_SVG_VERSION_LAST)
 
326
        return NULL;
 
327
 
 
328
    return _cairo_svg_version_strings[version];
 
329
}
 
330
 
 
331
static cairo_surface_t *
 
332
_cairo_svg_surface_create_for_document (cairo_svg_document_t    *document,
 
333
                                        cairo_content_t          content,
 
334
                                        double                   width,
 
335
                                        double                   height)
 
336
{
 
337
    cairo_svg_surface_t *surface;
 
338
    cairo_surface_t *paginated;
 
339
    cairo_status_t status, status_ignored;
 
340
 
 
341
    surface = malloc (sizeof (cairo_svg_surface_t));
 
342
    if (surface == NULL)
 
343
        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
344
 
 
345
    _cairo_surface_init (&surface->base, &cairo_svg_surface_backend,
 
346
                         content);
 
347
 
 
348
    surface->width = width;
 
349
    surface->height = height;
 
350
 
 
351
    surface->document = _cairo_svg_document_reference (document);
 
352
 
 
353
    surface->clip_level = 0;
 
354
 
 
355
    surface->id = document->surface_id++;
 
356
    surface->base_clip = document->clip_id++;
 
357
    surface->is_base_clip_emitted = FALSE;
 
358
 
 
359
    surface->xml_node = _cairo_memory_stream_create ();
 
360
    status = _cairo_output_stream_get_status (surface->xml_node);
 
361
    if (status)
 
362
        goto CLEANUP;
 
363
 
 
364
    _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t));
 
365
 
 
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",
 
371
                                     width, height);
 
372
        status = _cairo_output_stream_get_status (surface->xml_node);
 
373
        if (status)
 
374
            goto CLEANUP;
 
375
    }
 
376
 
 
377
    surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
 
378
    surface->force_fallbacks = FALSE;
 
379
    surface->content = content;
 
380
 
 
381
    paginated = _cairo_paginated_surface_create (&surface->base,
 
382
                                                 surface->content,
 
383
                                                 surface->width,
 
384
                                                 surface->height,
 
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);
 
390
        return paginated;
 
391
    }
 
392
 
 
393
    /* ignore status as we are on the error path */
 
394
CLEANUP:
 
395
    status_ignored = _cairo_output_stream_destroy (surface->xml_node);
 
396
    status_ignored = _cairo_svg_document_destroy (document);
 
397
 
 
398
    free (surface);
 
399
 
 
400
    return _cairo_surface_create_in_error (status);
 
401
}
 
402
 
 
403
static cairo_surface_t *
 
404
_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t    *stream,
 
405
                                               double                    width,
 
406
                                               double                    height,
 
407
                                               cairo_svg_version_t       version)
 
408
{
 
409
    cairo_svg_document_t *document = NULL; /* silence compiler */
 
410
    cairo_surface_t *surface;
 
411
    cairo_status_t status;
 
412
 
 
413
    status = _cairo_svg_document_create (stream,
 
414
                                         width, height, version,
 
415
                                         &document);
 
416
    if (status) {
 
417
        surface =  _cairo_surface_create_in_error (status);
 
418
        /* consume the output stream on behalf of caller */
 
419
        status = _cairo_output_stream_destroy (stream);
 
420
        return surface;
 
421
    }
 
422
 
 
423
    surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA,
 
424
                                                      width, height);
 
425
    if (surface->status) {
 
426
        status = _cairo_svg_document_destroy (document);
 
427
        return surface;
 
428
    }
 
429
 
 
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);
 
434
 
 
435
    return surface;
 
436
}
 
437
 
 
438
static cairo_svg_page_t *
 
439
_cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
 
440
{
 
441
    unsigned int i;
 
442
    cairo_svg_page_t page;
 
443
    cairo_output_stream_t *stream;
 
444
    cairo_status_t status;
 
445
 
 
446
    stream = _cairo_memory_stream_create ();
 
447
    if (_cairo_output_stream_get_status (stream)) {
 
448
        status = _cairo_output_stream_destroy (stream);
 
449
        return NULL;
 
450
    }
 
451
 
 
452
    page.surface_id = surface->id;
 
453
    page.clip_level = surface->clip_level;
 
454
    page.xml_node = surface->xml_node;
 
455
 
 
456
    if (_cairo_array_append (&surface->page_set, &page)) {
 
457
        status = _cairo_output_stream_destroy (stream);
 
458
        return NULL;
 
459
    }
 
460
 
 
461
    surface->xml_node = stream;
 
462
    surface->clip_level = 0;
 
463
 
 
464
    for (i = 0; i < page.clip_level; i++)
 
465
        _cairo_output_stream_printf (page.xml_node, "</g>\n");
 
466
 
 
467
    return _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
 
468
}
 
469
 
 
470
static cairo_int_status_t
 
471
_cairo_svg_surface_copy_page (void *abstract_surface)
 
472
{
 
473
    cairo_svg_surface_t *surface = abstract_surface;
 
474
    cairo_svg_page_t *page;
 
475
 
 
476
    page = _cairo_svg_surface_store_page (surface);
 
477
    if (page == NULL)
 
478
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
479
 
 
480
    _cairo_memory_stream_copy (page->xml_node, surface->xml_node);
 
481
    surface->clip_level = page->clip_level;
 
482
 
 
483
    return CAIRO_STATUS_SUCCESS;
 
484
}
 
485
 
 
486
static cairo_int_status_t
 
487
_cairo_svg_surface_show_page (void *abstract_surface)
 
488
{
 
489
    cairo_svg_surface_t *surface = abstract_surface;
 
490
 
 
491
    if (_cairo_svg_surface_store_page (surface) == NULL)
 
492
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
493
 
 
494
    return CAIRO_STATUS_SUCCESS;
 
495
}
 
496
 
 
497
static void
 
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)
 
502
{
 
503
    cairo_matrix_t matrix = *object_matrix;
 
504
 
 
505
    if (parent_matrix != NULL)
 
506
        cairo_matrix_multiply (&matrix, &matrix, parent_matrix);
 
507
 
 
508
    if (!_cairo_matrix_is_identity (&matrix))
 
509
        _cairo_output_stream_printf (output,
 
510
                                     "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"",
 
511
                                     attribute_str,
 
512
                                     matrix.xx, matrix.yx,
 
513
                                     matrix.xy, matrix.yy,
 
514
                                     matrix.x0, matrix.y0);
 
515
}
 
516
 
 
517
typedef struct
 
518
{
 
519
    cairo_output_stream_t *output;
 
520
    cairo_matrix_t *ctm_inverse;
 
521
} svg_path_info_t;
 
522
 
 
523
static cairo_status_t
 
524
_cairo_svg_path_move_to (void *closure, cairo_point_t *point)
 
525
{
 
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);
 
529
 
 
530
    if (info->ctm_inverse)
 
531
        cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
 
532
 
 
533
    _cairo_output_stream_printf (info->output, "M %f %f ", x, y);
 
534
 
 
535
    return CAIRO_STATUS_SUCCESS;
 
536
}
 
537
 
 
538
static cairo_status_t
 
539
_cairo_svg_path_line_to (void *closure, cairo_point_t *point)
 
540
{
 
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);
 
544
 
 
545
    if (info->ctm_inverse)
 
546
        cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
 
547
 
 
548
    _cairo_output_stream_printf (info->output, "L %f %f ", x, y);
 
549
 
 
550
    return CAIRO_STATUS_SUCCESS;
 
551
}
 
552
 
 
553
static cairo_status_t
 
554
_cairo_svg_path_curve_to (void          *closure,
 
555
                          cairo_point_t *b,
 
556
                          cairo_point_t *c,
 
557
                          cairo_point_t *d)
 
558
{
 
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);
 
566
 
 
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);
 
571
    }
 
572
 
 
573
    _cairo_output_stream_printf (info->output,
 
574
                                 "C %f %f %f %f %f %f ",
 
575
                                 bx, by, cx, cy, dx, dy);
 
576
 
 
577
    return CAIRO_STATUS_SUCCESS;
 
578
}
 
579
 
 
580
static cairo_status_t
 
581
_cairo_svg_path_close_path (void *closure)
 
582
{
 
583
    svg_path_info_t *info = closure;
 
584
 
 
585
    _cairo_output_stream_printf (info->output, "Z ");
 
586
 
 
587
    return CAIRO_STATUS_SUCCESS;
 
588
}
 
589
 
 
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)
 
594
{
 
595
    cairo_status_t status;
 
596
    svg_path_info_t info;
 
597
 
 
598
    _cairo_output_stream_printf (output, "d=\"");
 
599
 
 
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,
 
608
                                          &info);
 
609
    if (status)
 
610
        return status;
 
611
 
 
612
    _cairo_output_stream_printf (output, "\"");
 
613
 
 
614
    return status;
 
615
}
 
616
 
 
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)
 
621
{
 
622
    cairo_scaled_glyph_t *scaled_glyph;
 
623
    cairo_int_status_t status;
 
624
 
 
625
    status = _cairo_scaled_glyph_lookup (scaled_font,
 
626
                                         glyph_index,
 
627
                                         CAIRO_SCALED_GLYPH_INFO_METRICS|
 
628
                                         CAIRO_SCALED_GLYPH_INFO_PATH,
 
629
                                         &scaled_glyph);
 
630
    if (status)
 
631
        return status;
 
632
 
 
633
    _cairo_output_stream_printf (document->xml_node_glyphs,
 
634
                                 "<path style=\"stroke: none;\" ");
 
635
 
 
636
    status = _cairo_svg_surface_emit_path (document->xml_node_glyphs, scaled_glyph->path, NULL);
 
637
    if (status)
 
638
        return status;
 
639
 
 
640
    _cairo_output_stream_printf (document->xml_node_glyphs,
 
641
                                 "/>\n");
 
642
 
 
643
    return status;
 
644
}
 
645
 
 
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)
 
650
{
 
651
    cairo_image_surface_t *image;
 
652
    cairo_scaled_glyph_t *scaled_glyph;
 
653
    cairo_status_t status;
 
654
    uint8_t *row, *byte;
 
655
    int rows, cols;
 
656
    int x, y, bit;
 
657
 
 
658
    status = _cairo_scaled_glyph_lookup (scaled_font,
 
659
                                         glyph_index,
 
660
                                         CAIRO_SCALED_GLYPH_INFO_METRICS|
 
661
                                         CAIRO_SCALED_GLYPH_INFO_SURFACE,
 
662
                                         &scaled_glyph);
 
663
    if (status)
 
664
        return status;
 
665
 
 
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);
 
671
    }
 
672
 
 
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");
 
677
 
 
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",
 
685
                                                 x, y);
 
686
                }
 
687
            }
 
688
        }
 
689
    }
 
690
    _cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n");
 
691
 
 
692
    if (image != scaled_glyph->surface)
 
693
        cairo_surface_destroy (&image->base);
 
694
 
 
695
    return CAIRO_STATUS_SUCCESS;
 
696
}
 
697
 
 
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)
 
704
{
 
705
    cairo_status_t           status;
 
706
 
 
707
    _cairo_output_stream_printf (document->xml_node_glyphs,
 
708
                                 "<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n",
 
709
                                 font_id,
 
710
                                 subset_glyph_index);
 
711
 
 
712
    status = _cairo_svg_document_emit_outline_glyph_data (document,
 
713
                                                          scaled_font,
 
714
                                                          scaled_font_glyph_index);
 
715
    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
 
716
        status = _cairo_svg_document_emit_bitmap_glyph_data (document,
 
717
                                                             scaled_font,
 
718
                                                             scaled_font_glyph_index);
 
719
    if (status)
 
720
        return status;
 
721
 
 
722
    _cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n");
 
723
 
 
724
    return CAIRO_STATUS_SUCCESS;
 
725
}
 
726
 
 
727
static cairo_status_t
 
728
_cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t        *font_subset,
 
729
                                      void                              *closure)
 
730
{
 
731
    cairo_svg_document_t *document = closure;
 
732
    unsigned int i;
 
733
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
734
 
 
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);
 
740
        if (status)
 
741
            break;
 
742
    }
 
743
 
 
744
    return status;
 
745
}
 
746
 
 
747
static cairo_status_t
 
748
_cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
 
749
{
 
750
    cairo_status_t status;
 
751
 
 
752
    status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
 
753
                                                        _cairo_svg_document_emit_font_subset,
 
754
                                                        document);
 
755
    if (status)
 
756
        goto FAIL;
 
757
 
 
758
    status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
 
759
                                                      _cairo_svg_document_emit_font_subset,
 
760
                                                      document);
 
761
 
 
762
  FAIL:
 
763
    _cairo_scaled_font_subsets_destroy (document->font_subsets);
 
764
    document->font_subsets = NULL;
 
765
 
 
766
    return status;
 
767
}
 
768
 
 
769
static cairo_bool_t cairo_svg_force_fallbacks = FALSE;
 
770
 
 
771
static cairo_int_status_t
 
772
_cairo_svg_surface_analyze_operation (cairo_svg_surface_t   *surface,
 
773
                                      cairo_operator_t       op,
 
774
                                      const cairo_pattern_t *pattern)
 
775
{
 
776
    cairo_svg_document_t *document = surface->document;
 
777
 
 
778
    if (cairo_svg_force_fallbacks)
 
779
        return FALSE;
 
780
 
 
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;
 
785
 
 
786
    if (document->svg_version >= CAIRO_SVG_VERSION_1_2)
 
787
        return CAIRO_STATUS_SUCCESS;
 
788
 
 
789
    if (op == CAIRO_OPERATOR_OVER)
 
790
        return CAIRO_STATUS_SUCCESS;
 
791
 
 
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;
 
796
 
 
797
    return CAIRO_INT_STATUS_UNSUPPORTED;
 
798
}
 
799
 
 
800
static cairo_int_status_t
 
801
_cairo_svg_surface_operation_supported (cairo_svg_surface_t     *surface,
 
802
                                        cairo_operator_t         op,
 
803
                                        const cairo_pattern_t   *pattern)
 
804
{
 
805
    if (_cairo_svg_surface_analyze_operation (surface, op, pattern)
 
806
        != CAIRO_INT_STATUS_UNSUPPORTED)
 
807
    {
 
808
        return TRUE;
 
809
    } else {
 
810
        return FALSE;
 
811
    }
 
812
}
 
813
 
 
814
static cairo_surface_t *
 
815
_cairo_svg_surface_create_similar (void                 *abstract_src,
 
816
                                   cairo_content_t       content,
 
817
                                   int                   width,
 
818
                                   int                   height)
 
819
{
 
820
    return _cairo_meta_surface_create (content, width, height);
 
821
}
 
822
 
 
823
static cairo_status_t
 
824
_cairo_svg_surface_finish (void *abstract_surface)
 
825
{
 
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;
 
830
    unsigned int i;
 
831
 
 
832
    if (_cairo_paginated_surface_get_target (document->owner) == &surface->base)
 
833
        status = _cairo_svg_document_finish (document);
 
834
    else
 
835
        status = CAIRO_STATUS_SUCCESS;
 
836
 
 
837
    if (surface->xml_node != NULL) {
 
838
        status2 = _cairo_output_stream_destroy (surface->xml_node);
 
839
        if (status == CAIRO_STATUS_SUCCESS)
 
840
            status = status2;
 
841
    }
 
842
 
 
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)
 
847
            status = status2;
 
848
    }
 
849
    _cairo_array_fini (&surface->page_set);
 
850
 
 
851
    status2 = _cairo_svg_document_destroy (document);
 
852
    if (status == CAIRO_STATUS_SUCCESS)
 
853
        status = status2;
 
854
 
 
855
    return status;
 
856
}
 
857
 
 
858
static void
 
859
_cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document)
 
860
{
 
861
    if (document->alpha_filter)
 
862
        return;
 
863
 
 
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"
 
872
                                 "</filter>\n");
 
873
 
 
874
    document->alpha_filter = TRUE;
 
875
}
 
876
 
 
877
typedef struct {
 
878
    cairo_output_stream_t *output;
 
879
    unsigned int in_mem;
 
880
    unsigned int trailing;
 
881
    unsigned char src[3];
 
882
} base64_write_closure_t;
 
883
 
 
884
static char const base64_table[64] =
 
885
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
886
 
 
887
static cairo_status_t
 
888
base64_write_func (void *closure,
 
889
                   const unsigned char *data,
 
890
                   unsigned int length)
 
891
{
 
892
    base64_write_closure_t *info = (base64_write_closure_t *) closure;
 
893
    unsigned int i;
 
894
    unsigned char *src;
 
895
 
 
896
    src = info->src;
 
897
 
 
898
    if (info->in_mem + length < 3) {
 
899
        for (i = 0; i < length; i++) {
 
900
            src[i + info->in_mem] = *data++;
 
901
        }
 
902
        info->in_mem += length;
 
903
        return CAIRO_STATUS_SUCCESS;
 
904
    }
 
905
 
 
906
    do {
 
907
        unsigned char dst[4];
 
908
 
 
909
        for (i = info->in_mem; i < 3; i++) {
 
910
            src[i] = *data++;
 
911
            length--;
 
912
        }
 
913
        info->in_mem = 0;
 
914
 
 
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) {
 
921
            case 2:
 
922
                dst[2] = '=';
 
923
            case 1:
 
924
                dst[3] = '=';
 
925
            default:
 
926
                break;
 
927
        }
 
928
        _cairo_output_stream_write (info->output, dst, 4);
 
929
    } while (length >= 3);
 
930
 
 
931
    for (i = 0; i < length; i++) {
 
932
        src[i] = *data++;
 
933
    }
 
934
    info->in_mem = length;
 
935
 
 
936
    return _cairo_output_stream_get_status (info->output);
 
937
}
 
938
 
 
939
static cairo_int_status_t
 
940
_cairo_surface_base64_encode (cairo_surface_t       *surface,
 
941
                              cairo_output_stream_t *output)
 
942
{
 
943
    cairo_status_t status;
 
944
    base64_write_closure_t info;
 
945
    unsigned int i;
 
946
 
 
947
    info.output = output;
 
948
    info.in_mem = 0;
 
949
    info.trailing = 0;
 
950
 
 
951
    _cairo_output_stream_printf (info.output, "data:image/png;base64,");
 
952
 
 
953
    status = cairo_surface_write_to_png_stream (surface, base64_write_func,
 
954
                                                (void *) &info);
 
955
 
 
956
    if (status)
 
957
        return status;
 
958
 
 
959
    if (info.in_mem > 0) {
 
960
        for (i = info.in_mem; i < 3; i++)
 
961
            info.src[i] = '\x0';
 
962
        info.trailing = 3 - info.in_mem;
 
963
        info.in_mem = 3;
 
964
        status = base64_write_func (&info, NULL, 0);
 
965
    }
 
966
 
 
967
    return status;
 
968
}
 
969
 
 
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,
 
974
                                                 int                      pattern_id,
 
975
                                                 const cairo_matrix_t    *parent_matrix,
 
976
                                                 const char              *extra_attributes)
 
977
{
 
978
    cairo_surface_t *surface;
 
979
    cairo_surface_attributes_t surface_attr;
 
980
    cairo_rectangle_int_t extents;
 
981
    cairo_status_t status;
 
982
    cairo_matrix_t p2u;
 
983
 
 
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);
 
988
    if (status)
 
989
        return status;
 
990
 
 
991
    status = _cairo_surface_get_extents (surface, &extents);
 
992
    if (status)
 
993
        goto FAIL;
 
994
 
 
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);
 
999
 
 
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\"",
 
1005
                                     pattern_id,
 
1006
                                     extents.width, extents.height);
 
1007
        _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
 
1008
        _cairo_output_stream_printf (output, ">\n");
 
1009
    }
 
1010
 
 
1011
    _cairo_output_stream_printf (output,
 
1012
                                 "  <image width=\"%d\" height=\"%d\"",
 
1013
                                 extents.width, extents.height);
 
1014
 
 
1015
    if (pattern_id == invalid_pattern_id)
 
1016
        _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
 
1017
 
 
1018
    if (extra_attributes)
 
1019
        _cairo_output_stream_printf (output, " %s", extra_attributes);
 
1020
 
 
1021
    _cairo_output_stream_printf (output, " xlink:href=\"");
 
1022
 
 
1023
    status = _cairo_surface_base64_encode (surface, output);
 
1024
 
 
1025
    _cairo_output_stream_printf (output, "\"/>\n");
 
1026
 
 
1027
    if (pattern_id != invalid_pattern_id)
 
1028
        _cairo_output_stream_printf (output, "</pattern>\n");
 
1029
 
 
1030
  FAIL:
 
1031
    _cairo_pattern_release_surface ((cairo_pattern_t *)pattern,
 
1032
                                    surface, &surface_attr);
 
1033
 
 
1034
    return status;
 
1035
}
 
1036
 
 
1037
static cairo_status_t
 
1038
_cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document,
 
1039
                                      cairo_meta_surface_t *surface,
 
1040
                                      int                  *id)
 
1041
{
 
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;
 
1047
 
 
1048
    cairo_output_stream_t *contents;
 
1049
    cairo_meta_surface_t *meta;
 
1050
    cairo_meta_snapshot_t *snapshot;
 
1051
    unsigned int num_elements;
 
1052
    unsigned int i;
 
1053
 
 
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)) {
 
1061
            *id = snapshot->id;
 
1062
            return CAIRO_STATUS_SUCCESS;
 
1063
        }
 
1064
    }
 
1065
 
 
1066
    meta = (cairo_meta_surface_t *) _cairo_surface_snapshot (&surface->base);
 
1067
    paginated_surface = _cairo_svg_surface_create_for_document (document,
 
1068
                                                                meta->content,
 
1069
                                                                meta->width_pixels,
 
1070
                                                                meta->height_pixels);
 
1071
    if (paginated_surface->status) {
 
1072
        cairo_surface_destroy (&meta->base);
 
1073
        return paginated_surface->status;
 
1074
    }
 
1075
 
 
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);
 
1080
 
 
1081
    status = _cairo_meta_surface_replay (&meta->base, paginated_surface);
 
1082
    if (status) {
 
1083
        cairo_surface_destroy (&meta->base);
 
1084
        cairo_surface_destroy (paginated_surface);
 
1085
        return status;
 
1086
    }
 
1087
 
 
1088
    cairo_surface_show_page (paginated_surface);
 
1089
    status = cairo_surface_status (paginated_surface);
 
1090
    if (status) {
 
1091
        cairo_surface_destroy (&meta->base);
 
1092
        cairo_surface_destroy (paginated_surface);
 
1093
        return status;
 
1094
    }
 
1095
 
 
1096
    new_snapshot.meta = meta;
 
1097
    new_snapshot.id = svg_surface->id;
 
1098
    status = _cairo_array_append (&document->meta_snapshots, &new_snapshot);
 
1099
    if (status) {
 
1100
        cairo_surface_destroy (&meta->base);
 
1101
        cairo_surface_destroy (paginated_surface);
 
1102
        return status;
 
1103
    }
 
1104
 
 
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"
 
1110
                                     "</clipPath>\n",
 
1111
                                     svg_surface->base_clip,
 
1112
                                     svg_surface->width,
 
1113
                                     svg_surface->height);
 
1114
    }
 
1115
 
 
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",
 
1122
                                     svg_surface->id,
 
1123
                                     svg_surface->base_clip);
 
1124
    } else {
 
1125
        _cairo_output_stream_printf (document->xml_node_defs,
 
1126
                                     "<g id=\"surface%d\" "
 
1127
                                     "clip-path=\"url(#clip%d)\">\n",
 
1128
                                     svg_surface->id,
 
1129
                                     svg_surface->base_clip);
 
1130
    }
 
1131
 
 
1132
    contents = svg_surface->xml_node;
 
1133
    page_set = &svg_surface->page_set;
 
1134
 
 
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);
 
1139
        }
 
1140
    }
 
1141
 
 
1142
    if (page_set->num_elements > 0) {
 
1143
        cairo_svg_page_t *page;
 
1144
 
 
1145
        page = _cairo_array_index (page_set, page_set->num_elements - 1);
 
1146
        _cairo_memory_stream_copy (page->xml_node, document->xml_node_defs);
 
1147
    }
 
1148
 
 
1149
    _cairo_output_stream_printf (document->xml_node_defs, "</g>\n");
 
1150
 
 
1151
    *id = new_snapshot.id;
 
1152
 
 
1153
    status = cairo_surface_status (paginated_surface);
 
1154
    cairo_surface_destroy (paginated_surface);
 
1155
 
 
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. */
 
1160
 
 
1161
    /* cairo_surface_destroy (svg_surface); */
 
1162
 
 
1163
    return status;
 
1164
}
 
1165
 
 
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,
 
1170
                                                int                      pattern_id,
 
1171
                                                const cairo_matrix_t    *parent_matrix,
 
1172
                                                const char              *extra_attributes)
 
1173
{
 
1174
    cairo_svg_document_t *document = surface->document;
 
1175
    cairo_meta_surface_t *meta_surface;
 
1176
    cairo_matrix_t p2u;
 
1177
    cairo_status_t status;
 
1178
    int id = 0;
 
1179
 
 
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);
 
1184
 
 
1185
    meta_surface = (cairo_meta_surface_t *) pattern->surface;
 
1186
 
 
1187
    status = _cairo_svg_surface_emit_meta_surface (document, meta_surface, &id);
 
1188
    if (status)
 
1189
        return status;
 
1190
 
 
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\"",
 
1196
                                     pattern_id,
 
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");
 
1201
    }
 
1202
 
 
1203
    _cairo_output_stream_printf (output,
 
1204
                                 "<use xlink:href=\"#surface%d\"",
 
1205
                                 id);
 
1206
 
 
1207
    if (pattern_id == invalid_pattern_id)
 
1208
        _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
 
1209
 
 
1210
    if (extra_attributes)
 
1211
        _cairo_output_stream_printf (output, " %s", extra_attributes);
 
1212
 
 
1213
    _cairo_output_stream_printf (output, "/>\n");
 
1214
 
 
1215
    if (pattern_id != invalid_pattern_id)
 
1216
        _cairo_output_stream_printf (output, "</pattern>\n");
 
1217
 
 
1218
    return CAIRO_STATUS_SUCCESS;
 
1219
}
 
1220
 
 
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,
 
1225
                                           int                      pattern_id,
 
1226
                                           const cairo_matrix_t    *parent_matrix,
 
1227
                                           const char              *extra_attributes)
 
1228
{
 
1229
 
 
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);
 
1233
    }
 
1234
 
 
1235
    return _cairo_svg_surface_emit_composite_image_pattern (output, surface, pattern,
 
1236
                                         pattern_id, parent_matrix, extra_attributes);
 
1237
}
 
1238
 
 
1239
static void
 
1240
_cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
 
1241
                                  cairo_svg_surface_t   *surface,
 
1242
                                  cairo_operator_t       op)
 
1243
{
 
1244
    char const *op_str[] = {
 
1245
        "clear",
 
1246
 
 
1247
        "src",          "src-over",     "src-in",
 
1248
        "src-out",      "src-atop",
 
1249
 
 
1250
        "dst",          "dst-over",     "dst-in",
 
1251
        "dst-out",      "dst-atop",
 
1252
 
 
1253
        "xor", "plus",
 
1254
        "color-dodge"   /* FIXME: saturate ? */
 
1255
    };
 
1256
 
 
1257
    if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2)
 
1258
        _cairo_output_stream_printf (output, "comp-op: %s; ", op_str[op]);
 
1259
}
 
1260
 
 
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)
 
1266
{
 
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);
 
1274
 
 
1275
    return CAIRO_STATUS_SUCCESS;
 
1276
}
 
1277
 
 
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)
 
1284
{
 
1285
    cairo_svg_document_t *document = surface->document;
 
1286
    cairo_status_t status;
 
1287
    int pattern_id;
 
1288
 
 
1289
    pattern_id = document->pattern_id++;
 
1290
    status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs,
 
1291
                                                        surface, pattern,
 
1292
                                                        pattern_id, parent_matrix, NULL);
 
1293
    if (status)
 
1294
        return status;
 
1295
 
 
1296
    _cairo_output_stream_printf (style,
 
1297
                                 "%s: url(#pattern%d);",
 
1298
                                 is_stroke ? "stroke" : "fill",
 
1299
                                 pattern_id);
 
1300
 
 
1301
    return CAIRO_STATUS_SUCCESS;
 
1302
}
 
1303
 
 
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)
 
1310
{
 
1311
    cairo_gradient_stop_t *stops;
 
1312
    double offset;
 
1313
    unsigned int n_stops;
 
1314
    unsigned int i;
 
1315
 
 
1316
    if (pattern->n_stops < 1)
 
1317
        return CAIRO_STATUS_SUCCESS;
 
1318
 
 
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;
 
1330
    }
 
1331
 
 
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));
 
1335
        if (stops == NULL)
 
1336
            return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
1337
 
 
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;
 
1342
            } else
 
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;
 
1351
                    } else {
 
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;
 
1355
                    }
 
1356
                }
 
1357
            }
 
1358
        }
 
1359
    } else {
 
1360
        n_stops = pattern->n_stops;
 
1361
        stops = pattern->stops;
 
1362
    }
 
1363
 
 
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",
 
1371
                                         offset,
 
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);
 
1376
        }
 
1377
    else {
 
1378
        cairo_bool_t found = FALSE;
 
1379
        unsigned int offset_index;
 
1380
        cairo_color_t offset_color_start, offset_color_stop;
 
1381
 
 
1382
        for (i = 0; i < n_stops; i++) {
 
1383
            if (stops[i].offset >= -start_offset) {
 
1384
                if (i > 0) {
 
1385
                    if (stops[i].offset != stops[i-1].offset) {
 
1386
                        double x0, x1;
 
1387
                        cairo_color_t *color0, *color1;
 
1388
 
 
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;
 
1402
                    } else {
 
1403
                        offset_color_stop = stops[i-1].color;
 
1404
                        offset_color_start = stops[i].color;
 
1405
                    }
 
1406
                } else
 
1407
                        offset_color_stop = offset_color_start = stops[i].color;
 
1408
            offset_index = i;
 
1409
            found = TRUE;
 
1410
            break;
 
1411
            }
 
1412
        }
 
1413
 
 
1414
        if (!found) {
 
1415
            offset_index = n_stops - 1;
 
1416
            offset_color_stop = offset_color_start = stops[offset_index].color;
 
1417
        }
 
1418
 
 
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);
 
1437
        }
 
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);
 
1448
        }
 
1449
 
 
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);
 
1458
 
 
1459
    }
 
1460
 
 
1461
    if (reverse_stops || emulate_reflect)
 
1462
        free (stops);
 
1463
 
 
1464
    return CAIRO_STATUS_SUCCESS;
 
1465
}
 
1466
 
 
1467
static void
 
1468
_cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output,
 
1469
                                        cairo_pattern_t       *pattern)
 
1470
{
 
1471
    switch (pattern->extend) {
 
1472
        case CAIRO_EXTEND_REPEAT:
 
1473
            _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" ");
 
1474
            break;
 
1475
        case CAIRO_EXTEND_REFLECT:
 
1476
            _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" ");
 
1477
            break;
 
1478
        case CAIRO_EXTEND_NONE:
 
1479
        case CAIRO_EXTEND_PAD:
 
1480
            break;
 
1481
    }
 
1482
}
 
1483
 
 
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)
 
1490
{
 
1491
    cairo_svg_document_t *document = surface->document;
 
1492
    double x0, y0, x1, y1;
 
1493
    cairo_matrix_t p2u;
 
1494
    cairo_status_t status;
 
1495
 
 
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);
 
1500
 
 
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);
 
1505
 
 
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,
 
1511
                                 x0, y0, x1, y1);
 
1512
 
 
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");
 
1516
 
 
1517
    status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
 
1518
                                                    &pattern->base, 0.0,
 
1519
                                                    FALSE, FALSE);
 
1520
    if (status)
 
1521
        return status;
 
1522
 
 
1523
    _cairo_output_stream_printf (document->xml_node_defs,
 
1524
                                 "</linearGradient>\n");
 
1525
 
 
1526
    _cairo_output_stream_printf (style,
 
1527
                                 "%s: url(#linear%d);",
 
1528
                                 is_stroke ? "stroke" : "fill",
 
1529
                                 document->linear_pattern_id);
 
1530
 
 
1531
    document->linear_pattern_id++;
 
1532
 
 
1533
    return CAIRO_STATUS_SUCCESS;
 
1534
}
 
1535
 
 
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)
 
1542
{
 
1543
    cairo_svg_document_t *document = surface->document;
 
1544
    cairo_matrix_t p2u;
 
1545
    cairo_extend_t extend;
 
1546
    double x0, y0, x1, y1, r0, r1;
 
1547
    double fx, fy;
 
1548
    cairo_bool_t reverse_stops;
 
1549
    cairo_status_t status;
 
1550
    cairo_point_t *c0, *c1;
 
1551
    cairo_fixed_t radius0, radius1;
 
1552
 
 
1553
    extend = pattern->base.base.extend;
 
1554
 
 
1555
    if (pattern->r1 < pattern->r2) {
 
1556
        c0 = &pattern->c1;
 
1557
        c1 = &pattern->c2;
 
1558
        radius0 = pattern->r1;
 
1559
        radius1 = pattern->r2;
 
1560
        reverse_stops = FALSE;
 
1561
    } else {
 
1562
        c0 = &pattern->c2;
 
1563
        c1 = &pattern->c1;
 
1564
        radius0 = pattern->r2;
 
1565
        radius1 = pattern->r1;
 
1566
        reverse_stops = TRUE;
 
1567
    }
 
1568
 
 
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);
 
1575
 
 
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);
 
1580
 
 
1581
    if (pattern->r1 == pattern->r2) {
 
1582
        unsigned int n_stops = pattern->base.n_stops;
 
1583
 
 
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,
 
1590
                                     x1, y1,
 
1591
                                     x1, y1, r1);
 
1592
        _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
 
1593
        _cairo_output_stream_printf (document->xml_node_defs, ">\n");
 
1594
 
 
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");
 
1600
        else {
 
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);
 
1609
            if (n_stops > 1)
 
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);
 
1618
        }
 
1619
 
 
1620
    } else {
 
1621
        double offset, r, x, y;
 
1622
        cairo_bool_t emulate_reflect = FALSE;
 
1623
 
 
1624
        fx = (r1 * x0 - r0 * x1) / (r1 - r0);
 
1625
        fy = (r1 * y0 - r0 * y1) / (r1 - r0);
 
1626
 
 
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.
 
1637
         */
 
1638
        if ((extend == CAIRO_EXTEND_REFLECT
 
1639
             || extend == CAIRO_EXTEND_REPEAT)
 
1640
            && r0 > 0.0) {
 
1641
            double r_org = r1;
 
1642
 
 
1643
            if (extend == CAIRO_EXTEND_REFLECT) {
 
1644
                r1 = 2 * r1 - r0;
 
1645
                emulate_reflect = TRUE;
 
1646
            }
 
1647
 
 
1648
            offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
 
1649
            r = r1 - r0;
 
1650
 
 
1651
            /* New position of outer circle. */
 
1652
            x = r * (x1 - fx) / r_org + fx;
 
1653
            y = r * (y1 - fy) / r_org + fy;
 
1654
 
 
1655
            x1 = x;
 
1656
            y1 = y;
 
1657
            r1 = r;
 
1658
            r0 = 0.0;
 
1659
        } else {
 
1660
            offset = r0 / r1;
 
1661
        }
 
1662
 
 
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,
 
1669
                                     x1, y1,
 
1670
                                     fx, fy, r1);
 
1671
 
 
1672
        if (emulate_reflect)
 
1673
            _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
 
1674
        else
 
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");
 
1678
 
 
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");
 
1687
            if (r0 != 0.0)
 
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",
 
1692
                                             r0 / r1);
 
1693
        }
 
1694
        status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
 
1695
                                                        &pattern->base, offset,
 
1696
                                                        reverse_stops,
 
1697
                                                        emulate_reflect);
 
1698
        if (status)
 
1699
            return status;
 
1700
 
 
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");
 
1706
    }
 
1707
 
 
1708
    _cairo_output_stream_printf (document->xml_node_defs,
 
1709
                                 "</radialGradient>\n");
 
1710
 
 
1711
    _cairo_output_stream_printf (style,
 
1712
                                 "%s: url(#radial%d);",
 
1713
                                 is_stroke ? "stroke" : "fill",
 
1714
                                 document->radial_pattern_id);
 
1715
 
 
1716
    document->radial_pattern_id++;
 
1717
 
 
1718
    return CAIRO_STATUS_SUCCESS;
 
1719
}
 
1720
 
 
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)
 
1727
{
 
1728
    switch (pattern->type) {
 
1729
    case CAIRO_PATTERN_TYPE_SOLID:
 
1730
        return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern,
 
1731
                                                      output, is_stroke);
 
1732
 
 
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);
 
1736
 
 
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);
 
1740
 
 
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);
 
1744
    }
 
1745
    return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
 
1746
}
 
1747
 
 
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)
 
1755
{
 
1756
    _cairo_output_stream_printf (output,
 
1757
                                 "fill-rule: %s; ",
 
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);
 
1762
}
 
1763
 
 
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)
 
1771
{
 
1772
    cairo_status_t status;
 
1773
    const char *line_cap, *line_join;
 
1774
    unsigned int i;
 
1775
 
 
1776
    switch (stroke_style->line_cap) {
 
1777
        case CAIRO_LINE_CAP_BUTT:
 
1778
            line_cap = "butt";
 
1779
            break;
 
1780
        case CAIRO_LINE_CAP_ROUND:
 
1781
            line_cap = "round";
 
1782
            break;
 
1783
        case CAIRO_LINE_CAP_SQUARE:
 
1784
            line_cap = "square";
 
1785
            break;
 
1786
        default:
 
1787
            ASSERT_NOT_REACHED;
 
1788
    }
 
1789
 
 
1790
    switch (stroke_style->line_join) {
 
1791
        case CAIRO_LINE_JOIN_MITER:
 
1792
            line_join = "miter";
 
1793
            break;
 
1794
        case CAIRO_LINE_JOIN_ROUND:
 
1795
            line_join = "round";
 
1796
            break;
 
1797
        case CAIRO_LINE_JOIN_BEVEL:
 
1798
            line_join = "bevel";
 
1799
            break;
 
1800
        default:
 
1801
            ASSERT_NOT_REACHED;
 
1802
    }
 
1803
 
 
1804
    _cairo_output_stream_printf (output,
 
1805
                                 "stroke-width: %f; "
 
1806
                                 "stroke-linecap: %s; "
 
1807
                                 "stroke-linejoin: %s; ",
 
1808
                                 stroke_style->line_width,
 
1809
                                 line_cap,
 
1810
                                 line_join);
 
1811
 
 
1812
     status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix);
 
1813
     if (status)
 
1814
         return status;
 
1815
 
 
1816
     _cairo_svg_surface_emit_operator (output, surface, op);
 
1817
 
 
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, ",");
 
1825
            else
 
1826
                _cairo_output_stream_printf (output, "; ");
 
1827
        }
 
1828
        if (stroke_style->dash_offset != 0.0) {
 
1829
            _cairo_output_stream_printf (output,
 
1830
                                         "stroke-dashoffset: %f; ",
 
1831
                                         stroke_style->dash_offset);
 
1832
        }
 
1833
    }
 
1834
 
 
1835
    _cairo_output_stream_printf (output,
 
1836
                                 "stroke-miterlimit: %f; ",
 
1837
                                 stroke_style->miter_limit);
 
1838
 
 
1839
    return CAIRO_STATUS_SUCCESS;
 
1840
}
 
1841
 
 
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)
 
1857
{
 
1858
    cairo_svg_surface_t *surface = abstract_surface;
 
1859
    cairo_status_t status;
 
1860
 
 
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);
 
1864
    if (status)
 
1865
        return status;
 
1866
 
 
1867
    status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op,
 
1868
                                                   stroke_source, stroke_style, stroke_ctm_inverse);
 
1869
    if (status)
 
1870
        return status;
 
1871
 
 
1872
    _cairo_output_stream_printf (surface->xml_node, "\" ");
 
1873
 
 
1874
    status = _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse);
 
1875
    if (status)
 
1876
        return status;
 
1877
 
 
1878
    _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL);
 
1879
    _cairo_output_stream_printf (surface->xml_node, "/>\n");
 
1880
 
 
1881
    return CAIRO_STATUS_SUCCESS;
 
1882
}
 
1883
 
 
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,
 
1890
                         double                  tolerance,
 
1891
                         cairo_antialias_t       antialias)
 
1892
{
 
1893
    cairo_svg_surface_t *surface = abstract_surface;
 
1894
    cairo_status_t status;
 
1895
 
 
1896
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
 
1897
        return _cairo_svg_surface_analyze_operation (surface, op, source);
 
1898
 
 
1899
    assert (_cairo_svg_surface_operation_supported (surface, op, source));
 
1900
 
 
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);
 
1903
    if (status)
 
1904
        return status;
 
1905
 
 
1906
    _cairo_output_stream_printf (surface->xml_node, "\" ");
 
1907
 
 
1908
    status = _cairo_svg_surface_emit_path (surface->xml_node, path, NULL);
 
1909
    if (status)
 
1910
        return status;
 
1911
 
 
1912
    _cairo_output_stream_printf (surface->xml_node, "/>\n");
 
1913
 
 
1914
    return CAIRO_STATUS_SUCCESS;
 
1915
}
 
1916
 
 
1917
static cairo_int_status_t
 
1918
_cairo_svg_surface_get_extents (void                    *abstract_surface,
 
1919
                                cairo_rectangle_int_t   *rectangle)
 
1920
{
 
1921
    cairo_svg_surface_t *surface = abstract_surface;
 
1922
 
 
1923
    rectangle->x = 0;
 
1924
    rectangle->y = 0;
 
1925
 
 
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.
 
1929
     */
 
1930
    rectangle->width  = (int) ceil (surface->width);
 
1931
    rectangle->height = (int) ceil (surface->height);
 
1932
 
 
1933
    return CAIRO_STATUS_SUCCESS;
 
1934
}
 
1935
 
 
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)
 
1943
{
 
1944
    cairo_status_t status;
 
1945
 
 
1946
    if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
 
1947
        source->extend == CAIRO_EXTEND_NONE)
 
1948
        return _cairo_svg_surface_emit_composite_pattern (output,
 
1949
                                       surface,
 
1950
                                       (cairo_surface_pattern_t *) source,
 
1951
                                       invalid_pattern_id,
 
1952
                                       mask_source ? &mask_source->matrix :NULL,
 
1953
                                       extra_attributes);
 
1954
 
 
1955
    _cairo_output_stream_printf (output,
 
1956
                                 "<rect x=\"0\" y=\"0\" "
 
1957
                                 "width=\"%f\" height=\"%f\" "
 
1958
                                 "style=\"",
 
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);
 
1962
    if (status)
 
1963
        return status;
 
1964
 
 
1965
    _cairo_output_stream_printf (output, " stroke: none;\"");
 
1966
 
 
1967
    if (extra_attributes)
 
1968
        _cairo_output_stream_printf (output, " %s", extra_attributes);
 
1969
 
 
1970
    _cairo_output_stream_printf (output, "/>\n");
 
1971
 
 
1972
    return CAIRO_STATUS_SUCCESS;
 
1973
}
 
1974
 
 
1975
static cairo_int_status_t
 
1976
_cairo_svg_surface_paint (void              *abstract_surface,
 
1977
                          cairo_operator_t   op,
 
1978
                          cairo_pattern_t   *source)
 
1979
{
 
1980
    cairo_status_t status;
 
1981
    cairo_svg_surface_t *surface = abstract_surface;
 
1982
 
 
1983
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
 
1984
        return _cairo_svg_surface_analyze_operation (surface, op, source);
 
1985
 
 
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. */
 
1992
    /*
 
1993
    assert (_cairo_svg_surface_operation_supported (surface, op, source));
 
1994
    */
 
1995
 
 
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))
 
2007
    {
 
2008
        status = _cairo_output_stream_destroy (surface->xml_node);
 
2009
        if (status) {
 
2010
            surface->xml_node = NULL;
 
2011
            return status;
 
2012
        }
 
2013
 
 
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;
 
2018
            return status;
 
2019
        }
 
2020
 
 
2021
        if (op == CAIRO_OPERATOR_CLEAR) {
 
2022
            if (surface->content == CAIRO_CONTENT_COLOR) {
 
2023
                _cairo_output_stream_printf (surface->xml_node,
 
2024
                                             "<rect "
 
2025
                                             "width=\"%f\" height=\"%f\" "
 
2026
                                             "style=\"opacity: 1; "
 
2027
                                             "stroke: none; "
 
2028
                                             "fill: rgb(0,0,0);\"/>\n",
 
2029
                                             surface->width, surface->height);
 
2030
            }
 
2031
            return CAIRO_STATUS_SUCCESS;
 
2032
        }
 
2033
    }
 
2034
 
 
2035
    return _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, NULL);
 
2036
}
 
2037
 
 
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)
 
2043
{
 
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;
 
2048
    char buffer[64];
 
2049
    cairo_bool_t discard_filter = FALSE;
 
2050
    unsigned int mask_id;
 
2051
 
 
2052
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
 
2053
        return _cairo_svg_surface_analyze_operation (surface, op, source);
 
2054
 
 
2055
    assert (_cairo_svg_surface_operation_supported (surface, op, source));
 
2056
 
 
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;
 
2062
    }
 
2063
 
 
2064
    if (!discard_filter)
 
2065
        _cairo_svg_surface_emit_alpha_filter (document);
 
2066
 
 
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);
 
2073
 
 
2074
    mask_id = _cairo_svg_document_allocate_mask_id (document);
 
2075
 
 
2076
    _cairo_output_stream_printf (mask_stream,
 
2077
                                 "<mask id=\"mask%d\">\n"
 
2078
                                 "%s",
 
2079
                                 mask_id,
 
2080
                                 discard_filter ? "" : "  <g filter=\"url(#alpha)\">\n");
 
2081
    status = _cairo_svg_surface_emit_paint (mask_stream, surface, op, mask, source, NULL);
 
2082
    if (status) {
 
2083
        cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream);
 
2084
        return status;
 
2085
        (void) ignore;
 
2086
    }
 
2087
 
 
2088
    _cairo_output_stream_printf (mask_stream,
 
2089
                                 "%s"
 
2090
                                 "</mask>\n",
 
2091
                                 discard_filter ? "" : "  </g>\n");
 
2092
    _cairo_memory_stream_copy (mask_stream, document->xml_node_defs);
 
2093
 
 
2094
    status = _cairo_output_stream_destroy (mask_stream);
 
2095
    if (status)
 
2096
        return status;
 
2097
 
 
2098
    snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"",
 
2099
              mask_id);
 
2100
    status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer);
 
2101
    if (status)
 
2102
        return status;
 
2103
 
 
2104
    return CAIRO_STATUS_SUCCESS;
 
2105
}
 
2106
 
 
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,
 
2115
                           double                tolerance,
 
2116
                           cairo_antialias_t     antialias)
 
2117
{
 
2118
    cairo_svg_surface_t *surface = abstract_dst;
 
2119
    cairo_status_t status;
 
2120
 
 
2121
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
 
2122
        return _cairo_svg_surface_analyze_operation (surface, op, source);
 
2123
 
 
2124
    assert (_cairo_svg_surface_operation_supported (surface, op, source));
 
2125
 
 
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);
 
2129
    if (status)
 
2130
        return status;
 
2131
 
 
2132
    _cairo_output_stream_printf (surface->xml_node, "\" ");
 
2133
 
 
2134
    status = _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse);
 
2135
    if (status)
 
2136
        return status;
 
2137
 
 
2138
    _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL);
 
2139
    _cairo_output_stream_printf (surface->xml_node, "/>\n");
 
2140
 
 
2141
    return CAIRO_STATUS_SUCCESS;
 
2142
}
 
2143
 
 
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,
 
2149
                                int                      num_glyphs,
 
2150
                                cairo_scaled_font_t     *scaled_font,
 
2151
                                int                     *remaining_glyphs)
 
2152
{
 
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;
 
2158
    int i;
 
2159
 
 
2160
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
 
2161
        return _cairo_svg_surface_analyze_operation (surface, op, pattern);
 
2162
 
 
2163
    assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
 
2164
 
 
2165
    if (num_glyphs <= 0)
 
2166
        return CAIRO_STATUS_SUCCESS;
 
2167
 
 
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)
 
2172
        goto FALLBACK;
 
2173
 
 
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);
 
2177
    if (status)
 
2178
        return status;
 
2179
 
 
2180
    _cairo_output_stream_printf (surface->xml_node, "\">\n");
 
2181
 
 
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,
 
2185
                                                       NULL, 0,
 
2186
                                                       &subset_glyph);
 
2187
        if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
 
2188
            _cairo_output_stream_printf (surface->xml_node, "</g>\n");
 
2189
 
 
2190
            glyphs += i;
 
2191
            num_glyphs -= i;
 
2192
            goto FALLBACK;
 
2193
        }
 
2194
 
 
2195
        if (status)
 
2196
            return status;
 
2197
 
 
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);
 
2204
    }
 
2205
 
 
2206
    _cairo_output_stream_printf (surface->xml_node, "</g>\n");
 
2207
 
 
2208
    return CAIRO_STATUS_SUCCESS;
 
2209
 
 
2210
FALLBACK:
 
2211
    _cairo_path_fixed_init (&path);
 
2212
 
 
2213
    status = _cairo_scaled_font_glyph_path (scaled_font,(cairo_glyph_t *) glyphs, num_glyphs, &path);
 
2214
 
 
2215
    if (status) {
 
2216
        _cairo_path_fixed_fini (&path);
 
2217
        return status;
 
2218
    }
 
2219
 
 
2220
    status = _cairo_svg_surface_fill (abstract_surface, op, pattern,
 
2221
                                      &path, CAIRO_FILL_RULE_WINDING, 0.0, CAIRO_ANTIALIAS_SUBPIXEL);
 
2222
 
 
2223
    _cairo_path_fixed_fini (&path);
 
2224
 
 
2225
    return status;
 
2226
}
 
2227
 
 
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,
 
2232
                                        double                   tolerance,
 
2233
                                        cairo_antialias_t        antialias)
 
2234
{
 
2235
    cairo_svg_surface_t *surface = dst;
 
2236
    cairo_svg_document_t *document = surface->document;
 
2237
    cairo_status_t status;
 
2238
    unsigned int i;
 
2239
 
 
2240
    if (path == NULL) {
 
2241
        for (i = 0; i < surface->clip_level; i++)
 
2242
            _cairo_output_stream_printf (surface->xml_node, "</g>\n");
 
2243
 
 
2244
        surface->clip_level = 0;
 
2245
        return CAIRO_STATUS_SUCCESS;
 
2246
    }
 
2247
 
 
2248
    _cairo_output_stream_printf (document->xml_node_defs,
 
2249
                                 "<clipPath id=\"clip%d\">\n"
 
2250
                                 "  <path ",
 
2251
                                 document->clip_id);
 
2252
    status = _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL);
 
2253
    if (status)
 
2254
        return status;
 
2255
 
 
2256
    _cairo_output_stream_printf (document->xml_node_defs,
 
2257
                                 "/>\n"
 
2258
                                 "</clipPath>\n");
 
2259
 
 
2260
    _cairo_output_stream_printf (surface->xml_node,
 
2261
                                 "<g clip-path=\"url(#clip%d)\" "
 
2262
                                 "clip-rule=\"%s\">\n",
 
2263
                                 document->clip_id,
 
2264
                                 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
 
2265
                                 "evenodd" : "nonzero");
 
2266
 
 
2267
    document->clip_id++;
 
2268
    surface->clip_level++;
 
2269
 
 
2270
    return CAIRO_STATUS_SUCCESS;
 
2271
}
 
2272
 
 
2273
static void
 
2274
_cairo_svg_surface_get_font_options (void                  *abstract_surface,
 
2275
                                     cairo_font_options_t  *options)
 
2276
{
 
2277
    _cairo_font_options_init_default (options);
 
2278
 
 
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);
 
2282
}
 
2283
 
 
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,
 
2303
        NULL, /* flush */
 
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 */
 
2314
        NULL, /* reset */
 
2315
        _cairo_svg_surface_fill_stroke
 
2316
};
 
2317
 
 
2318
static cairo_status_t
 
2319
_cairo_svg_document_create (cairo_output_stream_t        *output_stream,
 
2320
                            double                        width,
 
2321
                            double                        height,
 
2322
                            cairo_svg_version_t           version,
 
2323
                            cairo_svg_document_t        **document_out)
 
2324
{
 
2325
    cairo_svg_document_t *document;
 
2326
    cairo_status_t status, status_ignored;
 
2327
 
 
2328
    if (output_stream->status)
 
2329
        return output_stream->status;
 
2330
 
 
2331
    document = malloc (sizeof (cairo_svg_document_t));
 
2332
    if (document == NULL)
 
2333
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
2334
 
 
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;
 
2340
    }
 
2341
 
 
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;
 
2348
 
 
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;
 
2356
 
 
2357
    document->xml_node_defs = _cairo_memory_stream_create ();
 
2358
    status = _cairo_output_stream_get_status (document->xml_node_defs);
 
2359
    if (status)
 
2360
        goto CLEANUP_NODE_DEFS;
 
2361
 
 
2362
    document->xml_node_glyphs = _cairo_memory_stream_create ();
 
2363
    status = _cairo_output_stream_get_status (document->xml_node_glyphs);
 
2364
    if (status)
 
2365
        goto CLEANUP_NODE_GLYPHS;
 
2366
 
 
2367
    document->alpha_filter = FALSE;
 
2368
 
 
2369
    _cairo_array_init (&document->meta_snapshots,
 
2370
                       sizeof (cairo_meta_snapshot_t));
 
2371
 
 
2372
    document->svg_version = version;
 
2373
 
 
2374
    *document_out = document;
 
2375
    return CAIRO_STATUS_SUCCESS;
 
2376
 
 
2377
  CLEANUP_NODE_GLYPHS:
 
2378
    status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs);
 
2379
  CLEANUP_NODE_DEFS:
 
2380
    status_ignored = _cairo_output_stream_destroy (document->xml_node_defs);
 
2381
    _cairo_scaled_font_subsets_destroy (document->font_subsets);
 
2382
  CLEANUP_DOCUMENT:
 
2383
    free (document);
 
2384
    return status;
 
2385
}
 
2386
 
 
2387
static cairo_svg_document_t *
 
2388
_cairo_svg_document_reference (cairo_svg_document_t *document)
 
2389
{
 
2390
    document->refcount++;
 
2391
 
 
2392
    return document;
 
2393
}
 
2394
 
 
2395
static unsigned int
 
2396
_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document)
 
2397
{
 
2398
    return document->mask_id++;
 
2399
}
 
2400
 
 
2401
static cairo_status_t
 
2402
_cairo_svg_document_destroy (cairo_svg_document_t *document)
 
2403
{
 
2404
    cairo_status_t status;
 
2405
 
 
2406
    document->refcount--;
 
2407
    if (document->refcount > 0)
 
2408
      return CAIRO_STATUS_SUCCESS;
 
2409
 
 
2410
    status = _cairo_svg_document_finish (document);
 
2411
 
 
2412
    free (document);
 
2413
 
 
2414
    return status;
 
2415
}
 
2416
 
 
2417
static cairo_status_t
 
2418
_cairo_svg_document_finish (cairo_svg_document_t *document)
 
2419
{
 
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;
 
2424
    unsigned int i;
 
2425
 
 
2426
    if (document->finished)
 
2427
        return CAIRO_STATUS_SUCCESS;
 
2428
 
 
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]);
 
2438
 
 
2439
    status = _cairo_svg_document_emit_font_subsets (document);
 
2440
 
 
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");
 
2448
        }
 
2449
        _cairo_memory_stream_copy (document->xml_node_defs, output);
 
2450
        _cairo_output_stream_printf (output, "</defs>\n");
 
2451
    }
 
2452
 
 
2453
    if (document->owner != NULL) {
 
2454
        cairo_svg_surface_t *surface;
 
2455
 
 
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);
 
2462
            }
 
2463
        }
 
2464
 
 
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",
 
2473
                                             page->surface_id);
 
2474
                _cairo_memory_stream_copy (page->xml_node, output);
 
2475
                _cairo_output_stream_printf (output, "</g>\n</page>\n");
 
2476
            }
 
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",
 
2482
                                         page->surface_id);
 
2483
            _cairo_memory_stream_copy (page->xml_node, output);
 
2484
            _cairo_output_stream_printf (output, "</g>\n");
 
2485
        }
 
2486
    }
 
2487
 
 
2488
    _cairo_output_stream_printf (output, "</svg>\n");
 
2489
 
 
2490
    status2 = _cairo_output_stream_destroy (document->xml_node_glyphs);
 
2491
    if (status == CAIRO_STATUS_SUCCESS)
 
2492
        status = status2;
 
2493
 
 
2494
    status2 = _cairo_output_stream_destroy (document->xml_node_defs);
 
2495
    if (status == CAIRO_STATUS_SUCCESS)
 
2496
        status = status2;
 
2497
 
 
2498
    status2 = _cairo_output_stream_destroy (output);
 
2499
    if (status == CAIRO_STATUS_SUCCESS)
 
2500
        status = status2;
 
2501
 
 
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)
 
2507
            status = status2;
 
2508
    }
 
2509
    _cairo_array_fini (&document->meta_snapshots);
 
2510
 
 
2511
    document->finished = TRUE;
 
2512
 
 
2513
    return status;
 
2514
}
 
2515
 
 
2516
static void
 
2517
_cairo_svg_surface_set_paginated_mode (void                     *abstract_surface,
 
2518
                                       cairo_paginated_mode_t    paginated_mode)
 
2519
{
 
2520
    cairo_svg_surface_t *surface = abstract_surface;
 
2521
 
 
2522
    surface->paginated_mode = paginated_mode;
 
2523
}
 
2524
 
 
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
 
2528
};