Subversion Repositories Kolibri OS

Rev

Rev 1897 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1897 serge 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 
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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
19
 * You should have received a copy of the MPL along with this library
20
 * in the file COPYING-MPL-1.1
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 
38
 *	Emmanuel Pacaud 
39
 *	Carl Worth 
40
 */
41
 
42
#define _BSD_SOURCE /* for snprintf() */
43
#include "cairoint.h"
3959 Serge 44
 
1897 serge 45
#include "cairo-svg.h"
3959 Serge 46
 
47
#include "cairo-array-private.h"
1897 serge 48
#include "cairo-analysis-surface-private.h"
3959 Serge 49
#include "cairo-default-context-private.h"
1897 serge 50
#include "cairo-error-private.h"
51
#include "cairo-image-info-private.h"
3959 Serge 52
#include "cairo-image-surface-private.h"
53
#include "cairo-recording-surface-inline.h"
1897 serge 54
#include "cairo-output-stream-private.h"
55
#include "cairo-path-fixed-private.h"
56
#include "cairo-paginated-private.h"
57
#include "cairo-scaled-font-subsets-private.h"
58
#include "cairo-surface-clipper-private.h"
3959 Serge 59
#include "cairo-surface-snapshot-inline.h"
1897 serge 60
#include "cairo-svg-surface-private.h"
61
 
62
/**
63
 * SECTION:cairo-svg
64
 * @Title: SVG Surfaces
65
 * @Short_Description: Rendering SVG documents
66
 * @See_Also: #cairo_surface_t
67
 *
68
 * The SVG surface is used to render cairo graphics to
69
 * SVG files and is a multi-page vector surface backend.
3959 Serge 70
 **/
1897 serge 71
 
72
/**
73
 * CAIRO_HAS_SVG_SURFACE:
74
 *
75
 * Defined if the SVG surface backend is available.
76
 * This macro can be used to conditionally compile backend-specific code.
3959 Serge 77
 *
78
 * Since: 1.2
79
 **/
1897 serge 80
 
81
typedef struct cairo_svg_page cairo_svg_page_t;
82
 
83
static const int invalid_pattern_id = -1;
84
 
85
static const cairo_svg_version_t _cairo_svg_versions[] =
86
{
87
    CAIRO_SVG_VERSION_1_1,
88
    CAIRO_SVG_VERSION_1_2
89
};
90
 
91
#define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
92
 
3959 Serge 93
static const char *_cairo_svg_supported_mime_types[] =
94
{
95
    CAIRO_MIME_TYPE_JPEG,
96
    CAIRO_MIME_TYPE_PNG,
97
    CAIRO_MIME_TYPE_URI,
98
    NULL
99
};
100
 
1897 serge 101
static void
102
_cairo_svg_surface_emit_path (cairo_output_stream_t	*output,
3959 Serge 103
			      const cairo_path_fixed_t	*path,
1897 serge 104
			      const cairo_matrix_t	*ctm_inverse);
105
 
106
static cairo_bool_t
107
_cairo_svg_version_has_page_set_support (cairo_svg_version_t version)
108
{
109
    return version > CAIRO_SVG_VERSION_1_1;
110
}
111
 
112
static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] =
113
{
114
    "SVG 1.1",
115
    "SVG 1.2"
116
};
117
 
118
static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] =
119
{
120
    "1.1",
121
    "1.2"
122
};
123
 
124
struct cairo_svg_page {
125
    unsigned int surface_id;
126
    unsigned int clip_level;
127
    cairo_output_stream_t *xml_node;
128
};
129
 
130
struct cairo_svg_document {
131
    cairo_output_stream_t *output_stream;
132
    unsigned long refcount;
133
    cairo_surface_t *owner;
134
    cairo_bool_t finished;
135
 
136
    double width;
137
    double height;
138
 
139
    cairo_output_stream_t *xml_node_defs;
140
    cairo_output_stream_t *xml_node_glyphs;
141
 
142
    unsigned int linear_pattern_id;
143
    unsigned int radial_pattern_id;
144
    unsigned int pattern_id;
145
    unsigned int filter_id;
146
    unsigned int clip_id;
147
    unsigned int mask_id;
148
 
149
    cairo_bool_t alpha_filter;
150
 
151
    cairo_svg_version_t svg_version;
152
 
153
    cairo_scaled_font_subsets_t *font_subsets;
154
};
155
 
156
static cairo_status_t
157
_cairo_svg_document_create (cairo_output_stream_t	 *stream,
158
			    double			  width,
159
			    double			  height,
160
			    cairo_svg_version_t		  version,
161
			    cairo_svg_document_t	**document_out);
162
 
163
static cairo_status_t
164
_cairo_svg_document_destroy (cairo_svg_document_t *document);
165
 
166
static cairo_status_t
167
_cairo_svg_document_finish (cairo_svg_document_t *document);
168
 
169
static cairo_svg_document_t *
170
_cairo_svg_document_reference (cairo_svg_document_t *document);
171
 
172
static unsigned int
173
_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document);
174
 
175
static cairo_surface_t *
176
_cairo_svg_surface_create_for_document (cairo_svg_document_t	*document,
177
					cairo_content_t		 content,
178
					double			 width,
179
					double			 height);
180
static cairo_surface_t *
181
_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t	*stream,
182
					       double			 width,
183
					       double			 height,
184
					       cairo_svg_version_t	 version);
185
 
186
static const cairo_surface_backend_t cairo_svg_surface_backend;
187
static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend;
188
 
189
/**
190
 * cairo_svg_surface_create_for_stream:
191
 * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
192
 *              to indicate a no-op @write_func. With a no-op @write_func,
193
 *              the surface may be queried or used as a source without
194
 *              generating any temporary files.
195
 * @closure: the closure argument for @write_func
196
 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
197
 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
198
 *
199
 * Creates a SVG surface of the specified size in points to be written
200
 * incrementally to the stream represented by @write_func and @closure.
201
 *
202
 * Return value: a pointer to the newly created surface. The caller
203
 * owns the surface and should call cairo_surface_destroy() when done
204
 * with it.
205
 *
206
 * This function always returns a valid pointer, but it will return a
207
 * pointer to a "nil" surface if an error such as out of memory
208
 * occurs. You can use cairo_surface_status() to check for this.
209
 *
210
 * Since: 1.2
3959 Serge 211
 **/
1897 serge 212
cairo_surface_t *
213
cairo_svg_surface_create_for_stream (cairo_write_func_t		 write_func,
214
				     void			*closure,
215
				     double			 width,
216
				     double			 height)
217
{
218
    cairo_output_stream_t *stream;
219
 
220
    stream = _cairo_output_stream_create (write_func, NULL, closure);
221
    if (_cairo_output_stream_get_status (stream))
222
	return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
223
 
224
    return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
225
}
226
 
227
/**
228
 * cairo_svg_surface_create:
229
 * @filename: a filename for the SVG output (must be writable), %NULL may be
230
 *            used to specify no output. This will generate a SVG surface that
231
 *            may be queried and used as a source, without generating a
232
 *            temporary file.
233
 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
234
 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
235
 *
236
 * Creates a SVG surface of the specified size in points to be written
237
 * to @filename.
238
 *
239
 * The SVG surface backend recognizes the following MIME types for the
240
 * data attached to a surface (see cairo_surface_set_mime_data()) when
241
 * it is used as a source pattern for drawing on this surface:
242
 * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG,
243
 * %CAIRO_MIME_TYPE_URI. If any of them is specified, the SVG backend
244
 * emits a href with the content of MIME data instead of a surface
245
 * snapshot (PNG, Base64-encoded) in the corresponding image tag.
246
 *
247
 * The unofficial MIME type %CAIRO_MIME_TYPE_URI is examined
248
 * first. If present, the URI is emitted as is: assuring the
249
 * correctness of URI is left to the client code.
250
 *
251
 * If %CAIRO_MIME_TYPE_URI is not present, but %CAIRO_MIME_TYPE_JPEG
252
 * or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is
253
 * Base64-encoded and emitted.
254
 *
255
 * Return value: a pointer to the newly created surface. The caller
256
 * owns the surface and should call cairo_surface_destroy() when done
257
 * with it.
258
 *
259
 * This function always returns a valid pointer, but it will return a
260
 * pointer to a "nil" surface if an error such as out of memory
261
 * occurs. You can use cairo_surface_status() to check for this.
262
 *
263
 * Since: 1.2
264
 **/
265
cairo_surface_t *
266
cairo_svg_surface_create (const char	*filename,
267
			  double	 width,
268
			  double	 height)
269
{
270
    cairo_output_stream_t *stream;
271
 
272
    stream = _cairo_output_stream_create_for_filename (filename);
273
    if (_cairo_output_stream_get_status (stream))
274
	return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
275
 
276
    return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
277
}
278
 
279
static cairo_bool_t
280
_cairo_surface_is_svg (cairo_surface_t *surface)
281
{
282
    return surface->backend == &cairo_svg_surface_backend;
283
}
284
 
285
/* If the abstract_surface is a paginated surface, and that paginated
286
 * surface's target is a svg_surface, then set svg_surface to that
287
 * target. Otherwise return FALSE.
288
 */
289
static cairo_bool_t
290
_extract_svg_surface (cairo_surface_t		 *surface,
291
		      cairo_svg_surface_t	**svg_surface)
292
{
293
    cairo_surface_t *target;
294
    cairo_status_t status_ignored;
295
 
296
    if (surface->status)
297
	return FALSE;
298
    if (surface->finished) {
299
	status_ignored = _cairo_surface_set_error (surface,
300
						   _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
301
        return FALSE;
302
    }
303
 
304
    if (! _cairo_surface_is_paginated (surface)) {
305
	status_ignored = _cairo_surface_set_error (surface,
306
						   _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
307
	return FALSE;
308
    }
309
 
310
    target = _cairo_paginated_surface_get_target (surface);
311
    if (target->status) {
312
	status_ignored = _cairo_surface_set_error (surface,
313
						   target->status);
314
	return FALSE;
315
    }
316
    if (target->finished) {
317
	status_ignored = _cairo_surface_set_error (surface,
318
						   _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
319
        return FALSE;
320
    }
321
 
322
    if (! _cairo_surface_is_svg (target)) {
323
	status_ignored = _cairo_surface_set_error (surface,
324
						   _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
325
	return FALSE;
326
    }
327
 
328
    *svg_surface = (cairo_svg_surface_t *) target;
329
    return TRUE;
330
}
331
 
332
/**
333
 * cairo_svg_surface_restrict_to_version:
334
 * @surface: a SVG #cairo_surface_t
335
 * @version: SVG version
336
 *
337
 * Restricts the generated SVG file to @version. See cairo_svg_get_versions()
338
 * for a list of available version values that can be used here.
339
 *
340
 * This function should only be called before any drawing operations
341
 * have been performed on the given surface. The simplest way to do
342
 * this is to call this function immediately after creating the
343
 * surface.
344
 *
345
 * Since: 1.2
346
 **/
347
void
348
cairo_svg_surface_restrict_to_version (cairo_surface_t		*abstract_surface,
349
				       cairo_svg_version_t	 version)
350
{
351
    cairo_svg_surface_t *surface = NULL; /* hide compiler warning */
352
 
353
    if (! _extract_svg_surface (abstract_surface, &surface))
354
	return;
355
 
356
    if (version < CAIRO_SVG_VERSION_LAST)
357
	surface->document->svg_version = version;
358
}
359
 
360
/**
361
 * cairo_svg_get_versions:
362
 * @versions: supported version list
363
 * @num_versions: list length
364
 *
365
 * Used to retrieve the list of supported versions. See
366
 * cairo_svg_surface_restrict_to_version().
367
 *
368
 * Since: 1.2
369
 **/
370
void
371
cairo_svg_get_versions (cairo_svg_version_t const	**versions,
372
                        int				 *num_versions)
373
{
374
    if (versions != NULL)
375
	*versions = _cairo_svg_versions;
376
 
377
    if (num_versions != NULL)
378
	*num_versions = CAIRO_SVG_VERSION_LAST;
379
}
380
 
381
/**
382
 * cairo_svg_version_to_string:
383
 * @version: a version id
384
 *
385
 * Get the string representation of the given @version id. This function
386
 * will return %NULL if @version isn't valid. See cairo_svg_get_versions()
387
 * for a way to get the list of valid version ids.
388
 *
389
 * Return value: the string associated to given version.
390
 *
391
 * Since: 1.2
392
 **/
393
const char *
394
cairo_svg_version_to_string (cairo_svg_version_t version)
395
{
396
    if (version >= CAIRO_SVG_VERSION_LAST)
397
	return NULL;
398
 
399
    return _cairo_svg_version_strings[version];
400
}
401
 
402
static cairo_bool_t
403
_cliprect_covers_surface (cairo_svg_surface_t *surface,
404
			  cairo_path_fixed_t *path)
405
{
406
    cairo_box_t box;
407
 
408
    if (_cairo_path_fixed_is_box (path, &box)) {
409
	if (box.p1.x <= 0 &&
410
	    box.p1.y <= 0 &&
411
	    _cairo_fixed_to_double (box.p2.x) >= surface->width &&
412
	    _cairo_fixed_to_double (box.p2.y) >= surface->height)
413
	{
414
	    return TRUE;
415
	}
416
    }
417
 
418
    return FALSE;
419
}
420
 
421
static cairo_status_t
422
_cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
423
						cairo_path_fixed_t	*path,
424
						cairo_fill_rule_t	 fill_rule,
425
						double			 tolerance,
426
						cairo_antialias_t	 antialias)
427
{
428
    cairo_svg_surface_t *surface = cairo_container_of (clipper,
429
						       cairo_svg_surface_t,
430
						       clipper);
431
    cairo_svg_document_t *document = surface->document;
432
    unsigned int i;
433
 
434
    if (path == NULL) {
435
	for (i = 0; i < surface->clip_level; i++)
436
	    _cairo_output_stream_printf (surface->xml_node, "\n");
437
 
438
	surface->clip_level = 0;
439
	return CAIRO_STATUS_SUCCESS;
440
    }
441
 
442
    /* skip trivial whole-page clips */
443
    if (_cliprect_covers_surface (surface, path))
444
	return CAIRO_STATUS_SUCCESS;
445
 
446
    _cairo_output_stream_printf (document->xml_node_defs,
447
				 "\n"
448
				 "  
449
				 document->clip_id);
450
    _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL);
451
 
452
    _cairo_output_stream_printf (document->xml_node_defs,
453
				 "/>\n"
454
				 "\n");
455
 
456
    _cairo_output_stream_printf (surface->xml_node,
457
				 "
458
				 "clip-rule=\"%s\">\n",
459
				 document->clip_id,
460
				 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
461
				 "evenodd" : "nonzero");
462
 
463
    document->clip_id++;
464
    surface->clip_level++;
465
 
466
    return CAIRO_STATUS_SUCCESS;
467
}
468
 
469
static cairo_surface_t *
470
_cairo_svg_surface_create_for_document (cairo_svg_document_t	*document,
471
					cairo_content_t		 content,
472
					double			 width,
473
					double			 height)
474
{
475
    cairo_svg_surface_t *surface;
476
    cairo_surface_t *paginated;
477
    cairo_status_t status, status_ignored;
478
 
479
    surface = malloc (sizeof (cairo_svg_surface_t));
480
    if (unlikely (surface == NULL))
481
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
482
 
483
    _cairo_surface_init (&surface->base,
484
			 &cairo_svg_surface_backend,
485
			 NULL, /* device */
486
			 content);
487
 
488
    surface->width = width;
489
    surface->height = height;
490
 
491
    surface->document = _cairo_svg_document_reference (document);
492
 
493
    surface->clip_level = 0;
494
    _cairo_surface_clipper_init (&surface->clipper,
495
				 _cairo_svg_surface_clipper_intersect_clip_path);
496
 
497
    surface->base_clip = document->clip_id++;
498
    surface->is_base_clip_emitted = FALSE;
499
 
500
    surface->xml_node = _cairo_memory_stream_create ();
501
    status = _cairo_output_stream_get_status (surface->xml_node);
502
    if (unlikely (status))
503
	goto CLEANUP;
504
 
505
    _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t));
506
 
507
    if (content == CAIRO_CONTENT_COLOR) {
508
	_cairo_output_stream_printf (surface->xml_node,
509
				     "
510
				     "style=\"opacity:1;stroke:none;"
511
				     "fill:rgb(0,0,0);\"/>\n",
512
				     width, height);
513
	status = _cairo_output_stream_get_status (surface->xml_node);
514
	if (unlikely (status))
515
	    goto CLEANUP;
516
    }
517
 
518
    surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
519
    surface->force_fallbacks = FALSE;
520
    surface->content = content;
521
 
522
    paginated = _cairo_paginated_surface_create (&surface->base,
523
					         surface->content,
524
						 &cairo_svg_surface_paginated_backend);
525
    status = paginated->status;
526
    if (status == CAIRO_STATUS_SUCCESS) {
527
	/* paginated keeps the only reference to surface now, drop ours */
528
	cairo_surface_destroy (&surface->base);
529
	return paginated;
530
    }
531
 
532
    /* ignore status as we are on the error path */
533
CLEANUP:
534
    status_ignored = _cairo_output_stream_destroy (surface->xml_node);
535
    status_ignored = _cairo_svg_document_destroy (document);
536
 
537
    free (surface);
538
 
539
    return _cairo_surface_create_in_error (status);
540
}
541
 
542
static cairo_surface_t *
543
_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t	*stream,
544
					       double			 width,
545
					       double			 height,
546
					       cairo_svg_version_t	 version)
547
{
548
    cairo_svg_document_t *document = NULL; /* silence compiler */
549
    cairo_surface_t *surface;
550
    cairo_status_t status;
551
 
552
    status = _cairo_svg_document_create (stream,
553
	                                 width, height, version,
554
					 &document);
555
    if (unlikely (status)) {
556
	surface =  _cairo_surface_create_in_error (status);
557
	/* consume the output stream on behalf of caller */
558
	status = _cairo_output_stream_destroy (stream);
559
	return surface;
560
    }
561
 
562
    surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA,
563
						      width, height);
564
    if (surface->status) {
565
	status = _cairo_svg_document_destroy (document);
566
	return surface;
567
    }
568
 
569
    document->owner = surface;
570
    status = _cairo_svg_document_destroy (document);
571
    /* the ref count should be 2 at this point */
572
    assert (status == CAIRO_STATUS_SUCCESS);
573
 
574
    return surface;
575
}
576
 
577
static cairo_svg_page_t *
578
_cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
579
{
580
    cairo_svg_page_t page;
581
    cairo_output_stream_t *stream;
3959 Serge 582
    cairo_int_status_t status;
583
    unsigned int i;
1897 serge 584
 
585
    stream = _cairo_memory_stream_create ();
586
    if (_cairo_output_stream_get_status (stream)) {
587
	status = _cairo_output_stream_destroy (stream);
588
	return NULL;
589
    }
590
 
591
    page.surface_id = surface->base.unique_id;
592
    page.clip_level = surface->clip_level;
593
    page.xml_node = surface->xml_node;
594
 
595
    if (_cairo_array_append (&surface->page_set, &page)) {
596
	status = _cairo_output_stream_destroy (stream);
597
	return NULL;
598
    }
599
 
600
    surface->xml_node = stream;
601
    surface->clip_level = 0;
602
    for (i = 0; i < page.clip_level; i++)
603
	_cairo_output_stream_printf (page.xml_node, "\n");
604
 
605
    _cairo_surface_clipper_reset (&surface->clipper);
606
 
607
    return _cairo_array_index (&surface->page_set,
608
			       surface->page_set.num_elements - 1);
609
}
610
 
611
static cairo_int_status_t
612
_cairo_svg_surface_copy_page (void *abstract_surface)
613
{
614
    cairo_svg_surface_t *surface = abstract_surface;
615
    cairo_svg_page_t *page;
616
 
617
    page = _cairo_svg_surface_store_page (surface);
618
    if (unlikely (page == NULL))
619
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
620
 
621
    _cairo_memory_stream_copy (page->xml_node, surface->xml_node);
622
 
623
    return CAIRO_STATUS_SUCCESS;
624
}
625
 
626
static cairo_int_status_t
627
_cairo_svg_surface_show_page (void *abstract_surface)
628
{
629
    cairo_svg_surface_t *surface = abstract_surface;
630
 
631
    if (unlikely (_cairo_svg_surface_store_page (surface) == NULL))
632
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
633
 
634
    return CAIRO_STATUS_SUCCESS;
635
}
636
 
637
static void
638
_cairo_svg_surface_emit_transform (cairo_output_stream_t *output,
639
				   char const		 *attribute_str,
640
				   const cairo_matrix_t	 *object_matrix,
641
				   const cairo_matrix_t  *parent_matrix)
642
{
643
    cairo_matrix_t matrix = *object_matrix;
644
 
645
    if (parent_matrix != NULL)
646
	cairo_matrix_multiply (&matrix, &matrix, parent_matrix);
647
 
648
    if (!_cairo_matrix_is_identity (&matrix))
649
	_cairo_output_stream_printf (output,
650
				     "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"",
651
				     attribute_str,
652
				     matrix.xx, matrix.yx,
653
				     matrix.xy, matrix.yy,
654
				     matrix.x0, matrix.y0);
655
}
656
 
657
typedef struct {
658
    cairo_output_stream_t *output;
659
    const cairo_matrix_t *ctm_inverse;
660
} svg_path_info_t;
661
 
662
static cairo_status_t
663
_cairo_svg_path_move_to (void *closure,
664
			 const cairo_point_t *point)
665
{
666
    svg_path_info_t *info = closure;
667
    double x = _cairo_fixed_to_double (point->x);
668
    double y = _cairo_fixed_to_double (point->y);
669
 
670
    if (info->ctm_inverse)
671
	cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
672
 
673
    _cairo_output_stream_printf (info->output, "M %f %f ", x, y);
674
 
675
    return CAIRO_STATUS_SUCCESS;
676
}
677
 
678
static cairo_status_t
679
_cairo_svg_path_line_to (void *closure,
680
			 const cairo_point_t *point)
681
{
682
    svg_path_info_t *info = closure;
683
    double x = _cairo_fixed_to_double (point->x);
684
    double y = _cairo_fixed_to_double (point->y);
685
 
686
    if (info->ctm_inverse)
687
	cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
688
 
689
    _cairo_output_stream_printf (info->output, "L %f %f ", x, y);
690
 
691
    return CAIRO_STATUS_SUCCESS;
692
}
693
 
694
static cairo_status_t
695
_cairo_svg_path_curve_to (void          *closure,
696
			  const cairo_point_t *b,
697
			  const cairo_point_t *c,
698
			  const cairo_point_t *d)
699
{
700
    svg_path_info_t *info = closure;
701
    double bx = _cairo_fixed_to_double (b->x);
702
    double by = _cairo_fixed_to_double (b->y);
703
    double cx = _cairo_fixed_to_double (c->x);
704
    double cy = _cairo_fixed_to_double (c->y);
705
    double dx = _cairo_fixed_to_double (d->x);
706
    double dy = _cairo_fixed_to_double (d->y);
707
 
708
    if (info->ctm_inverse) {
709
	cairo_matrix_transform_point (info->ctm_inverse, &bx, &by);
710
	cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy);
711
	cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy);
712
    }
713
 
714
    _cairo_output_stream_printf (info->output,
715
				 "C %f %f %f %f %f %f ",
716
				 bx, by, cx, cy, dx, dy);
717
 
718
    return CAIRO_STATUS_SUCCESS;
719
}
720
 
721
static cairo_status_t
722
_cairo_svg_path_close_path (void *closure)
723
{
724
    svg_path_info_t *info = closure;
725
 
726
    _cairo_output_stream_printf (info->output, "Z ");
727
 
728
    return CAIRO_STATUS_SUCCESS;
729
}
730
 
731
static void
732
_cairo_svg_surface_emit_path (cairo_output_stream_t	*output,
3959 Serge 733
			      const cairo_path_fixed_t	*path,
1897 serge 734
			      const cairo_matrix_t	*ctm_inverse)
735
{
736
    cairo_status_t status;
737
    svg_path_info_t info;
738
 
739
    _cairo_output_stream_printf (output, "d=\"");
740
 
741
    info.output = output;
742
    info.ctm_inverse = ctm_inverse;
743
    status = _cairo_path_fixed_interpret (path,
744
					  _cairo_svg_path_move_to,
745
					  _cairo_svg_path_line_to,
746
					  _cairo_svg_path_curve_to,
747
					  _cairo_svg_path_close_path,
748
					  &info);
749
    assert (status == CAIRO_STATUS_SUCCESS);
750
 
751
    _cairo_output_stream_printf (output, "\"");
752
}
753
 
754
static cairo_int_status_t
755
_cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t	*document,
756
					     cairo_scaled_font_t	*scaled_font,
757
					     unsigned long		 glyph_index)
758
{
759
    cairo_scaled_glyph_t *scaled_glyph;
760
    cairo_int_status_t status;
761
 
762
    status = _cairo_scaled_glyph_lookup (scaled_font,
763
					 glyph_index,
764
					 CAIRO_SCALED_GLYPH_INFO_METRICS|
765
					 CAIRO_SCALED_GLYPH_INFO_PATH,
766
					 &scaled_glyph);
767
    if (unlikely (status))
768
	return status;
769
 
770
    _cairo_output_stream_printf (document->xml_node_glyphs,
771
				 "
772
 
773
    _cairo_svg_surface_emit_path (document->xml_node_glyphs,
774
				  scaled_glyph->path, NULL);
775
 
776
    _cairo_output_stream_printf (document->xml_node_glyphs,
777
				 "/>\n");
778
 
779
    return status;
780
}
781
 
782
static cairo_int_status_t
783
_cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t	*document,
784
					    cairo_scaled_font_t		*scaled_font,
785
					    unsigned long		 glyph_index)
786
{
787
    cairo_scaled_glyph_t *scaled_glyph;
788
    cairo_image_surface_t *image;
789
    cairo_status_t status;
790
    uint8_t *row, *byte;
791
    int rows, cols;
792
    int x, y, bit;
793
 
794
    status = _cairo_scaled_glyph_lookup (scaled_font,
795
					 glyph_index,
796
					 CAIRO_SCALED_GLYPH_INFO_METRICS |
797
					 CAIRO_SCALED_GLYPH_INFO_SURFACE,
798
					 &scaled_glyph);
799
    if (unlikely (status))
800
	return status;
801
 
802
    image = _cairo_image_surface_coerce_to_format (scaled_glyph->surface,
803
					           CAIRO_FORMAT_A1);
804
    status = image->base.status;
805
    if (unlikely (status))
806
	return status;
807
 
808
    _cairo_output_stream_printf (document->xml_node_glyphs, "
809
    _cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform",
810
				       &image->base.device_transform_inverse, NULL);
811
    _cairo_output_stream_printf (document->xml_node_glyphs, ">/n");
812
 
813
    for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) {
814
	for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
815
	    uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
816
	    for (bit = 7; bit >= 0 && x < image->width; bit--, x++) {
817
		if (output_byte & (1 << bit)) {
818
		    _cairo_output_stream_printf (document->xml_node_glyphs,
819
						 "\n",
820
						 x, y);
821
		}
822
	    }
823
	}
824
    }
825
    _cairo_output_stream_printf (document->xml_node_glyphs, "\n");
826
 
827
    cairo_surface_destroy (&image->base);
828
 
829
    return CAIRO_STATUS_SUCCESS;
830
}
831
 
3959 Serge 832
static cairo_int_status_t
1897 serge 833
_cairo_svg_document_emit_glyph (cairo_svg_document_t	*document,
834
				cairo_scaled_font_t	*scaled_font,
835
				unsigned long		 scaled_font_glyph_index,
836
				unsigned int		 font_id,
837
				unsigned int		 subset_glyph_index)
838
{
3959 Serge 839
    cairo_int_status_t	     status;
1897 serge 840
 
841
    _cairo_output_stream_printf (document->xml_node_glyphs,
842
				 "\n",
843
				 font_id,
844
				 subset_glyph_index);
845
 
846
    status = _cairo_svg_document_emit_outline_glyph_data (document,
847
							  scaled_font,
848
							  scaled_font_glyph_index);
849
    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
850
	status = _cairo_svg_document_emit_bitmap_glyph_data (document,
851
							     scaled_font,
852
							     scaled_font_glyph_index);
853
    if (unlikely (status))
854
	return status;
855
 
856
    _cairo_output_stream_printf (document->xml_node_glyphs, "\n");
857
 
3959 Serge 858
    return CAIRO_INT_STATUS_SUCCESS;
1897 serge 859
}
860
 
3959 Serge 861
static cairo_int_status_t
1897 serge 862
_cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t	*font_subset,
863
				      void				*closure)
864
{
865
    cairo_svg_document_t *document = closure;
3959 Serge 866
    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
1897 serge 867
    unsigned int i;
868
 
869
    _cairo_scaled_font_freeze_cache (font_subset->scaled_font);
870
    for (i = 0; i < font_subset->num_glyphs; i++) {
871
	status = _cairo_svg_document_emit_glyph (document,
872
					         font_subset->scaled_font,
873
					         font_subset->glyphs[i],
874
					         font_subset->font_id, i);
875
	if (unlikely (status))
876
	    break;
877
    }
878
    _cairo_scaled_font_thaw_cache (font_subset->scaled_font);
879
 
880
    return status;
881
}
882
 
883
static cairo_status_t
884
_cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
885
{
886
    cairo_status_t status;
887
 
888
    status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
889
                                                        _cairo_svg_document_emit_font_subset,
890
                                                        document);
891
    if (unlikely (status))
892
	goto FAIL;
893
 
894
    status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
895
						      _cairo_svg_document_emit_font_subset,
896
						      document);
897
 
898
  FAIL:
899
    _cairo_scaled_font_subsets_destroy (document->font_subsets);
900
    document->font_subsets = NULL;
901
 
902
    return status;
903
}
904
 
905
static char const *
906
_cairo_svg_surface_operators[] = {
907
    "clear",
908
 
909
    "src", "src-over", "src-in",
910
    "src-out", "src-atop",
911
 
912
    "dst", "dst-over", "dst-in",
913
    "dst-out", "dst-atop",
914
 
915
    "xor", "plus",
916
    "color-dodge", /* FIXME: saturate ? */
917
 
918
    "multiply",	"screen", "overlay",
919
    "darken", "lighten",
920
    "color-dodge", "color-burn",
921
    "hard-light", "soft-light",
922
    "difference", "exclusion"
923
};
924
 
925
static cairo_bool_t
926
_cairo_svg_surface_analyze_operator (cairo_svg_surface_t   *surface,
927
				      cairo_operator_t	     op)
928
{
929
    /* guard against newly added operators */
930
    if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators))
931
	return CAIRO_INT_STATUS_UNSUPPORTED;
932
 
933
    /* allow operators being NULL if they are unsupported */
934
    if (_cairo_svg_surface_operators[op] == NULL)
935
	return CAIRO_INT_STATUS_UNSUPPORTED;
936
 
937
    return CAIRO_STATUS_SUCCESS;
938
}
939
 
940
static cairo_int_status_t
941
_cairo_svg_surface_analyze_operation (cairo_svg_surface_t   *surface,
942
				      cairo_operator_t	     op,
943
				      const cairo_pattern_t *pattern)
944
{
945
    cairo_svg_document_t *document = surface->document;
946
 
947
    if (surface->force_fallbacks &&
948
	surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
949
    {
950
	return CAIRO_INT_STATUS_UNSUPPORTED;
951
    }
952
 
3959 Serge 953
    if (pattern->type == CAIRO_PATTERN_TYPE_MESH)
954
	return CAIRO_INT_STATUS_UNSUPPORTED;
955
 
1897 serge 956
    /* SVG doesn't support extend reflect for image pattern */
957
    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
958
	pattern->extend == CAIRO_EXTEND_REFLECT)
959
	return CAIRO_INT_STATUS_UNSUPPORTED;
960
 
961
    if (document->svg_version >= CAIRO_SVG_VERSION_1_2)
962
	return _cairo_svg_surface_analyze_operator (surface, op);
963
 
964
    if (op == CAIRO_OPERATOR_OVER)
965
	return CAIRO_STATUS_SUCCESS;
966
 
967
    /* The SOURCE operator is only supported if there is nothing
968
     * painted underneath. */
969
    if (op == CAIRO_OPERATOR_SOURCE)
970
	return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
971
 
972
    return CAIRO_INT_STATUS_UNSUPPORTED;
973
}
974
 
975
static cairo_int_status_t
976
_cairo_svg_surface_operation_supported (cairo_svg_surface_t	*surface,
977
					cairo_operator_t	 op,
978
					const cairo_pattern_t	*pattern)
979
{
980
    return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED;
981
}
982
 
983
static cairo_status_t
984
_cairo_svg_surface_finish (void *abstract_surface)
985
{
986
    cairo_status_t status, status2;
987
    cairo_svg_surface_t *surface = abstract_surface;
988
    cairo_svg_document_t *document = surface->document;
989
    cairo_svg_page_t *page;
990
    unsigned int i;
991
 
992
    if (_cairo_paginated_surface_get_target (document->owner) == &surface->base)
993
	status = _cairo_svg_document_finish (document);
994
    else
995
	status = CAIRO_STATUS_SUCCESS;
996
 
997
    if (surface->xml_node != NULL) {
998
	status2 = _cairo_output_stream_destroy (surface->xml_node);
999
	if (status == CAIRO_STATUS_SUCCESS)
1000
	    status = status2;
1001
    }
1002
 
1003
    for (i = 0; i < surface->page_set.num_elements; i++) {
1004
	page = _cairo_array_index (&surface->page_set, i);
1005
	status2 = _cairo_output_stream_destroy (page->xml_node);
1006
	if (status == CAIRO_STATUS_SUCCESS)
1007
	    status = status2;
1008
    }
1009
    _cairo_array_fini (&surface->page_set);
1010
 
1011
    _cairo_surface_clipper_reset (&surface->clipper);
1012
 
1013
    status2 = _cairo_svg_document_destroy (document);
1014
    if (status == CAIRO_STATUS_SUCCESS)
1015
	status = status2;
1016
 
1017
    return status;
1018
}
1019
 
1020
 
1021
static void
1022
_cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document)
1023
{
1024
    if (document->alpha_filter)
1025
	return;
1026
 
1027
    _cairo_output_stream_printf (document->xml_node_defs,
1028
				 "
1029
				 "filterUnits=\"objectBoundingBox\" "
1030
				 "x=\"0%%\" y=\"0%%\" "
1031
				 "width=\"100%%\" height=\"100%%\">\n"
1032
				 "  
1033
				 "in=\"SourceGraphic\" "
1034
				 "values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n"
1035
				 "\n");
1036
 
1037
    document->alpha_filter = TRUE;
1038
}
1039
 
1040
typedef struct {
1041
    cairo_output_stream_t *output;
1042
    unsigned int in_mem;
1043
    unsigned int trailing;
1044
    unsigned char src[3];
1045
} base64_write_closure_t;
1046
 
1047
static char const base64_table[64] =
1048
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1049
 
1050
static cairo_status_t
1051
base64_write_func (void *closure,
1052
		   const unsigned char *data,
1053
		   unsigned int length)
1054
{
1055
    base64_write_closure_t *info = (base64_write_closure_t *) closure;
1056
    unsigned int i;
1057
    unsigned char *src;
1058
 
1059
    src = info->src;
1060
 
1061
    if (info->in_mem + length < 3) {
1062
	for (i = 0; i < length; i++) {
1063
	    src[i + info->in_mem] = *data++;
1064
	}
1065
	info->in_mem += length;
1066
	return CAIRO_STATUS_SUCCESS;
1067
    }
1068
 
1069
    do {
1070
	unsigned char dst[4];
1071
 
1072
	for (i = info->in_mem; i < 3; i++) {
1073
	    src[i] = *data++;
1074
	    length--;
1075
	}
1076
	info->in_mem = 0;
1077
 
1078
	dst[0] = base64_table[src[0] >> 2];
1079
	dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4];
1080
	dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6];
1081
	dst[3] = base64_table[src[2] & 0xfc >> 2];
1082
	/* Special case for the last missing bits */
1083
	switch (info->trailing) {
1084
	    case 2:
1085
		dst[2] = '=';
1086
	    case 1:
1087
		dst[3] = '=';
1088
	    default:
1089
		break;
1090
	}
1091
	_cairo_output_stream_write (info->output, dst, 4);
1092
    } while (length >= 3);
1093
 
1094
    for (i = 0; i < length; i++) {
1095
	src[i] = *data++;
1096
    }
1097
    info->in_mem = length;
1098
 
1099
    return _cairo_output_stream_get_status (info->output);
1100
}
1101
 
1102
static cairo_int_status_t
1103
_cairo_surface_base64_encode_jpeg (cairo_surface_t       *surface,
1104
				   cairo_output_stream_t *output)
1105
{
1106
    const unsigned char *mime_data;
1107
    unsigned long mime_data_length;
1108
    cairo_image_info_t image_info;
1109
    base64_write_closure_t info;
1110
    cairo_status_t status;
1111
 
1112
    cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG,
1113
				 &mime_data, &mime_data_length);
1114
    if (mime_data == NULL)
1115
	return CAIRO_INT_STATUS_UNSUPPORTED;
1116
 
1117
    status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length);
1118
    if (unlikely (status))
1119
	return status;
1120
 
1121
    _cairo_output_stream_printf (output, "data:image/jpeg;base64,");
1122
 
1123
    info.output = output;
1124
    info.in_mem = 0;
1125
    info.trailing = 0;
1126
 
1127
    status = base64_write_func (&info, mime_data, mime_data_length);
1128
    if (unlikely (status))
1129
	return status;
1130
 
1131
    if (info.in_mem > 0) {
1132
	memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1133
	info.trailing = 3 - info.in_mem;
1134
	info.in_mem = 3;
1135
	status = base64_write_func (&info, NULL, 0);
1136
    }
1137
 
1138
    return status;
1139
}
1140
 
1141
static cairo_int_status_t
1142
_cairo_surface_base64_encode_png (cairo_surface_t       *surface,
1143
				  cairo_output_stream_t *output)
1144
{
1145
    const unsigned char *mime_data;
1146
    unsigned long mime_data_length;
1147
    base64_write_closure_t info;
1148
    cairo_status_t status;
1149
 
1150
    cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG,
1151
				 &mime_data, &mime_data_length);
1152
    if (unlikely (surface->status))
1153
	return surface->status;
1154
    if (mime_data == NULL)
1155
	return CAIRO_INT_STATUS_UNSUPPORTED;
1156
 
1157
    _cairo_output_stream_printf (output, "data:image/png;base64,");
1158
 
1159
    info.output = output;
1160
    info.in_mem = 0;
1161
    info.trailing = 0;
1162
 
1163
    status = base64_write_func (&info, mime_data, mime_data_length);
1164
    if (unlikely (status))
1165
	return status;
1166
 
1167
    if (info.in_mem > 0) {
1168
	memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1169
	info.trailing = 3 - info.in_mem;
1170
	info.in_mem = 3;
1171
	status = base64_write_func (&info, NULL, 0);
1172
    }
1173
 
1174
    return status;
1175
}
1176
 
1177
static cairo_int_status_t
1178
_cairo_surface_base64_encode (cairo_surface_t       *surface,
1179
			      cairo_output_stream_t *output)
1180
{
3959 Serge 1181
    cairo_int_status_t status;
1897 serge 1182
    base64_write_closure_t info;
1183
 
1184
    status = _cairo_surface_base64_encode_jpeg (surface, output);
1185
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1186
	return status;
1187
 
1188
    status = _cairo_surface_base64_encode_png (surface, output);
1189
    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1190
	return status;
1191
 
1192
    info.output = output;
1193
    info.in_mem = 0;
1194
    info.trailing = 0;
1195
 
1196
    _cairo_output_stream_printf (info.output, "data:image/png;base64,");
1197
 
1198
    status = cairo_surface_write_to_png_stream (surface, base64_write_func,
1199
						(void *) &info);
1200
 
1201
    if (unlikely (status))
1202
	return status;
1203
 
1204
    if (info.in_mem > 0) {
1205
	memset (info.src + info.in_mem, 0, 3 - info.in_mem);
1206
	info.trailing = 3 - info.in_mem;
1207
	info.in_mem = 3;
1208
	status = base64_write_func (&info, NULL, 0);
1209
    }
1210
 
1211
    return status;
1212
}
1213
 
1214
static void
1215
_cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
1216
				  cairo_svg_surface_t   *surface,
1217
				  cairo_operator_t	 op)
1218
{
1219
    if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1220
	op != CAIRO_OPERATOR_OVER) {
1221
	_cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]);
1222
	if (!_cairo_operator_bounded_by_source (op))
1223
	   _cairo_output_stream_printf (output, " clip-to-self=\"true\"");
1224
    }
1225
}
1226
 
1227
static void
1228
_cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output,
1229
					    cairo_svg_surface_t   *surface,
1230
					    cairo_operator_t	 op)
1231
{
1232
    if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
1233
	op != CAIRO_OPERATOR_OVER) {
1234
	_cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]);
1235
	if (!_cairo_operator_bounded_by_source (op))
1236
	   _cairo_output_stream_printf (output, "clip-to-self:true;");
1237
    }
1238
}
1239
 
1240
/**
1241
 * _cairo_svg_surface_emit_attr_value:
1242
 *
1243
 * Write the value to output the stream as a sequence of characters,
1244
 * while escaping those which have special meaning in the XML
1245
 * attribute's value context: & and ".
1246
 **/
1247
static void
1248
_cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream,
1249
				    const unsigned char *value,
1250
				    unsigned int length)
1251
{
1252
    const unsigned char *p;
1253
    const unsigned char *q;
1254
    unsigned int i;
1255
 
1256
    /* we'll accumulate non-special chars in [q, p) range */
1257
    p = value;
1258
    q = p;
1259
    for (i = 0; i < length; i++, p++) {
1260
	if (*p == '&' || *p == '"') {
1261
	    /* flush what's left before special char */
1262
	    if (p != q) {
1263
		_cairo_output_stream_write (stream, q, p - q);
1264
		q = p + 1;
1265
	    }
1266
 
1267
	    if (*p == '&')
1268
		_cairo_output_stream_printf (stream, "&");
1269
	    else // p == '"'
1270
		_cairo_output_stream_printf (stream, """);
1271
	}
1272
    }
1273
 
1274
    /* flush the trailing chars if any */
1275
    if (p != q)
1276
	_cairo_output_stream_write (stream, q, p - q);
1277
}
1278
 
1279
static cairo_status_t
1280
_cairo_svg_surface_emit_surface (cairo_svg_document_t *document,
1281
				 cairo_surface_t *surface)
1282
{
1283
    cairo_rectangle_int_t extents;
1284
    cairo_bool_t is_bounded;
1285
    cairo_status_t status;
1286
    const unsigned char *uri;
1287
    unsigned long uri_len;
1288
 
1289
    if (_cairo_user_data_array_get_data (&surface->user_data,
1290
					 (cairo_user_data_key_t *) document))
1291
    {
1292
	return CAIRO_STATUS_SUCCESS;
1293
    }
1294
 
1295
    is_bounded = _cairo_surface_get_extents (surface, &extents);
1296
    assert (is_bounded);
1297
 
1298
    _cairo_output_stream_printf (document->xml_node_defs,
1299
				 "
1300
				 surface->unique_id,
1301
				 extents.width, extents.height);
1302
 
1303
    _cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\"");
1304
 
1305
    cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_URI,
1306
				 &uri, &uri_len);
1307
    if (uri != NULL) {
1308
	_cairo_svg_surface_emit_attr_value (document->xml_node_defs,
1309
					    uri, uri_len);
1310
    } else {
1311
	status = _cairo_surface_base64_encode (surface,
1312
					       document->xml_node_defs);
1313
	if (unlikely (status))
1314
	    return status;
1315
    }
1316
 
1317
    _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n");
1318
 
1319
    /* and tag it */
1320
    return _cairo_user_data_array_set_data (&surface->user_data,
1321
					    (cairo_user_data_key_t *) document,
1322
					    document, NULL);
1323
}
1324
 
1325
static cairo_status_t
1326
_cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t   *output,
1327
						   cairo_svg_surface_t	 *svg_surface,
1328
						   cairo_operator_t	  op,
1329
						   cairo_surface_pattern_t *pattern,
1330
						   int			  pattern_id,
1331
						   const cairo_matrix_t	 *parent_matrix,
1332
						   const char		 *extra_attributes)
1333
{
1334
    cairo_status_t status;
1335
    cairo_matrix_t p2u;
1336
 
1337
    p2u = pattern->base.matrix;
1338
    status = cairo_matrix_invert (&p2u);
1339
    /* cairo_pattern_set_matrix ensures the matrix is invertible */
1340
    assert (status == CAIRO_STATUS_SUCCESS);
1341
 
1342
    status = _cairo_svg_surface_emit_surface (svg_surface->document,
1343
					      pattern->surface);
1344
    if (unlikely (status))
1345
	return status;
1346
 
1347
    if (pattern_id != invalid_pattern_id) {
1348
	cairo_rectangle_int_t extents;
1349
	cairo_bool_t is_bounded;
1350
 
1351
	is_bounded = _cairo_surface_get_extents (pattern->surface, &extents);
1352
	assert (is_bounded);
1353
 
1354
	_cairo_output_stream_printf (output,
1355
				     "
1356
				     "patternUnits=\"userSpaceOnUse\" "
1357
				     "width=\"%d\" height=\"%d\" ",
1358
				     pattern_id,
1359
				     extents.width, extents.height);
1360
	_cairo_svg_surface_emit_transform (output,
1361
					   " patternTransform",
1362
					   &p2u, parent_matrix);
1363
	_cairo_output_stream_printf (output, ">\n  ");
1364
    }
1365
 
1366
    _cairo_output_stream_printf (output,
1367
				 "
1368
				 pattern->surface->unique_id);
1369
    if (extra_attributes)
1370
	_cairo_output_stream_printf (output, " %s", extra_attributes);
1371
 
1372
    if (pattern_id == invalid_pattern_id) {
1373
	_cairo_svg_surface_emit_operator (output, svg_surface, op);
1374
	_cairo_svg_surface_emit_transform (output,
1375
					   " transform",
1376
					   &p2u, parent_matrix);
1377
    }
1378
    _cairo_output_stream_printf (output, "/>\n");
1379
 
1380
 
1381
    if (pattern_id != invalid_pattern_id)
1382
	_cairo_output_stream_printf (output, "\n");
1383
 
1384
    return CAIRO_STATUS_SUCCESS;
1385
}
1386
 
1387
static cairo_status_t
1388
_cairo_svg_surface_emit_recording_surface (cairo_svg_document_t      *document,
1389
					   cairo_recording_surface_t *source)
1390
{
1391
    cairo_status_t status;
1392
    cairo_surface_t *paginated_surface;
1393
    cairo_svg_surface_t *svg_surface;
1394
    cairo_array_t *page_set;
1395
 
1396
    cairo_output_stream_t *contents;
1397
 
1398
    if (_cairo_user_data_array_get_data (&source->base.user_data,
1399
					 (cairo_user_data_key_t *) document))
1400
    {
1401
	return CAIRO_STATUS_SUCCESS;
1402
    }
1403
 
1404
    paginated_surface = _cairo_svg_surface_create_for_document (document,
3959 Serge 1405
								source->base.content,
1897 serge 1406
								source->extents_pixels.width,
1407
								source->extents_pixels.height);
1408
    if (unlikely (paginated_surface->status))
1409
	return paginated_surface->status;
1410
 
1411
    svg_surface = (cairo_svg_surface_t *)
1412
    _cairo_paginated_surface_get_target (paginated_surface);
1413
    cairo_surface_set_fallback_resolution (paginated_surface,
1414
					   document->owner->x_fallback_resolution,
1415
					   document->owner->y_fallback_resolution);
1416
    cairo_surface_set_device_offset (&svg_surface->base,
1417
				     -source->extents_pixels.x,
1418
				     -source->extents_pixels.y);
1419
 
1420
    status = _cairo_recording_surface_replay (&source->base, paginated_surface);
1421
    if (unlikely (status)) {
1422
	cairo_surface_destroy (paginated_surface);
1423
	return status;
1424
    }
1425
 
1426
    cairo_surface_show_page (paginated_surface);
1427
    status = cairo_surface_status (paginated_surface);
1428
    if (unlikely (status)) {
1429
	cairo_surface_destroy (paginated_surface);
1430
	return status;
1431
    }
1432
 
1433
    if (! svg_surface->is_base_clip_emitted) {
1434
	svg_surface->is_base_clip_emitted = TRUE;
1435
	_cairo_output_stream_printf (document->xml_node_defs,
1436
				     "\n"
1437
				     "  \n"
1438
				     "\n",
1439
				     svg_surface->base_clip,
1440
				     svg_surface->width,
1441
				     svg_surface->height);
1442
    }
1443
 
3959 Serge 1444
    if (source->base.content == CAIRO_CONTENT_ALPHA) {
1897 serge 1445
	_cairo_svg_surface_emit_alpha_filter (document);
1446
	_cairo_output_stream_printf (document->xml_node_defs,
1447
				     "
1448
				     "clip-path=\"url(#clip%d)\" "
1449
				     "filter=\"url(#alpha)\">\n",
1450
				     source->base.unique_id,
1451
				     svg_surface->base_clip);
1452
    } else {
1453
	_cairo_output_stream_printf (document->xml_node_defs,
1454
				     "
1455
				     "clip-path=\"url(#clip%d)\">\n",
1456
				     source->base.unique_id,
1457
				     svg_surface->base_clip);
1458
    }
1459
 
1460
    contents = svg_surface->xml_node;
1461
    page_set = &svg_surface->page_set;
1462
 
1463
    if (_cairo_memory_stream_length (contents) > 0) {
1464
	if (unlikely (_cairo_svg_surface_store_page (svg_surface) == NULL)) {
1465
	    cairo_surface_destroy (paginated_surface);
1466
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1467
	}
1468
    }
1469
 
1470
    if (page_set->num_elements > 0) {
1471
	cairo_svg_page_t *page;
1472
 
1473
	page = _cairo_array_index (page_set, page_set->num_elements - 1);
1474
	_cairo_memory_stream_copy (page->xml_node, document->xml_node_defs);
1475
    }
1476
 
1477
    _cairo_output_stream_printf (document->xml_node_defs, "\n");
1478
 
1479
    status = cairo_surface_status (paginated_surface);
1480
    cairo_surface_destroy (paginated_surface);
1481
 
1482
    if (unlikely (status))
1483
	return status;
1484
 
1485
    /* and tag it */
1486
    return _cairo_user_data_array_set_data (&source->base.user_data,
1487
					    (cairo_user_data_key_t *) document,
1488
					    document, NULL);
1489
}
1490
 
3959 Serge 1491
static cairo_recording_surface_t *
1492
to_recording_surface (const cairo_surface_pattern_t *pattern)
1493
{
1494
    cairo_surface_t *surface = pattern->surface;
1495
    if (_cairo_surface_is_paginated (surface))
1496
	surface = _cairo_paginated_surface_get_recording (surface);
1497
    if (_cairo_surface_is_snapshot (surface))
1498
	surface = _cairo_surface_snapshot_get_target (surface);
1499
    return (cairo_recording_surface_t *) surface;
1500
}
1501
 
1897 serge 1502
static cairo_status_t
1503
_cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t	*output,
1504
						     cairo_svg_surface_t	*surface,
1505
						     cairo_operator_t	         op,
1506
						     cairo_surface_pattern_t	*pattern,
1507
						     int			 pattern_id,
1508
						     const cairo_matrix_t	*parent_matrix,
1509
						     const char			*extra_attributes)
1510
{
1511
    cairo_svg_document_t *document = surface->document;
1512
    cairo_recording_surface_t *recording_surface;
1513
    cairo_matrix_t p2u;
1514
    cairo_status_t status;
1515
 
1516
    p2u = pattern->base.matrix;
1517
    status = cairo_matrix_invert (&p2u);
1518
    /* cairo_pattern_set_matrix ensures the matrix is invertible */
1519
    assert (status == CAIRO_STATUS_SUCCESS);
1520
 
3959 Serge 1521
    recording_surface = to_recording_surface (pattern);
1897 serge 1522
    status = _cairo_svg_surface_emit_recording_surface (document, recording_surface);
1523
    if (unlikely (status))
1524
	return status;
1525
 
1526
    if (pattern_id != invalid_pattern_id) {
1527
	_cairo_output_stream_printf (output,
1528
				     "
1529
				     "patternUnits=\"userSpaceOnUse\" "
1530
				     "width=\"%d\" height=\"%d\"",
1531
				     pattern_id,
1532
				     recording_surface->extents.width,
1533
				     recording_surface->extents.height);
1534
	_cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
1535
	_cairo_output_stream_printf (output, ">\n");
1536
    }
1537
 
1538
    _cairo_output_stream_printf (output,
1539
				 "
1540
				 recording_surface->base.unique_id);
1541
 
1542
    if (pattern_id == invalid_pattern_id) {
1543
	_cairo_svg_surface_emit_operator (output, surface, op);
1544
	_cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
1545
    }
1546
 
1547
    if (extra_attributes)
1548
	_cairo_output_stream_printf (output, " %s", extra_attributes);
1549
 
1550
    _cairo_output_stream_printf (output, "/>\n");
1551
 
1552
    if (pattern_id != invalid_pattern_id)
1553
	_cairo_output_stream_printf (output, "\n");
1554
 
1555
    return CAIRO_STATUS_SUCCESS;
1556
}
1557
 
1558
static cairo_status_t
1559
_cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t   *output,
1560
					   cairo_svg_surface_t	   *surface,
1561
					   cairo_operator_t	    op,
1562
					   cairo_surface_pattern_t *pattern,
1563
					   int			    pattern_id,
1564
					   const cairo_matrix_t	   *parent_matrix,
1565
					   const char		   *extra_attributes)
1566
{
1567
 
3959 Serge 1568
    if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
1897 serge 1569
	return _cairo_svg_surface_emit_composite_recording_pattern (output, surface,
1570
								    op, pattern,
1571
								    pattern_id,
1572
								    parent_matrix,
1573
								    extra_attributes);
1574
    }
1575
 
1576
    return _cairo_svg_surface_emit_composite_surface_pattern (output, surface,
1577
							      op, pattern,
1578
							      pattern_id,
1579
							      parent_matrix,
1580
							      extra_attributes);
1581
}
1582
 
1583
static cairo_status_t
1584
_cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t    *surface,
1585
				       cairo_solid_pattern_t  *pattern,
1586
				       cairo_output_stream_t  *style,
1587
				       cairo_bool_t	       is_stroke)
1588
{
1589
    _cairo_output_stream_printf (style, is_stroke ?
1590
				 "stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;":
1591
				 "fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;",
1592
				 pattern->color.red * 100.0,
1593
				 pattern->color.green * 100.0,
1594
				 pattern->color.blue * 100.0,
1595
				 pattern->color.alpha);
1596
 
1597
    return CAIRO_STATUS_SUCCESS;
1598
}
1599
 
1600
static cairo_status_t
1601
_cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t	 *surface,
1602
					 cairo_surface_pattern_t *pattern,
1603
					 cairo_output_stream_t   *style,
1604
					 cairo_bool_t		  is_stroke,
1605
					 const cairo_matrix_t	 *parent_matrix)
1606
{
1607
    cairo_svg_document_t *document = surface->document;
1608
    cairo_status_t status;
1609
    int pattern_id;
1610
 
1611
    pattern_id = document->pattern_id++;
1612
    status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs,
1613
	                                                surface, CAIRO_OPERATOR_SOURCE, pattern,
1614
							pattern_id, parent_matrix, NULL);
1615
    if (unlikely (status))
1616
	return status;
1617
 
1618
    _cairo_output_stream_printf (style,
1619
				 "%s:url(#pattern%d);",
1620
				 is_stroke ? "stroke" : "fill",
1621
				 pattern_id);
1622
 
1623
    return CAIRO_STATUS_SUCCESS;
1624
}
1625
 
1626
static cairo_status_t
1627
_cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t          *output,
1628
				       cairo_gradient_pattern_t const *pattern,
1629
				       double			       start_offset,
1630
				       cairo_bool_t		       reverse_stops,
1631
				       cairo_bool_t		       emulate_reflect)
1632
{
1633
    cairo_gradient_stop_t *stops;
1634
    double offset;
1635
    unsigned int n_stops;
1636
    unsigned int i;
1637
 
1638
    if (pattern->n_stops < 1)
1639
	return CAIRO_STATUS_SUCCESS;
1640
 
1641
    if (pattern->n_stops == 1) {
1642
	    _cairo_output_stream_printf (output,
1643
					 "
1644
					 "stop-color:rgb(%f%%,%f%%,%f%%);"
1645
					 "stop-opacity:%f;\"/>\n",
1646
					 pattern->stops[0].offset,
1647
					 pattern->stops[0].color.red   * 100.0,
1648
					 pattern->stops[0].color.green * 100.0,
1649
					 pattern->stops[0].color.blue  * 100.0,
1650
					 pattern->stops[0].color.alpha);
1651
	    return CAIRO_STATUS_SUCCESS;
1652
    }
1653
 
1654
    if (emulate_reflect || reverse_stops) {
1655
	n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops;
1656
	stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t));
1657
	if (unlikely (stops == NULL))
1658
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1659
 
1660
	for (i = 0; i < pattern->n_stops; i++) {
1661
	    if (reverse_stops) {
1662
		stops[i] = pattern->stops[pattern->n_stops - i - 1];
1663
		stops[i].offset = 1.0 - stops[i].offset;
1664
	    } else
1665
		stops[i] = pattern->stops[i];
1666
	    if (emulate_reflect) {
1667
		stops[i].offset /= 2;
1668
		if (i > 0 && i < (pattern->n_stops - 1)) {
1669
		    if (reverse_stops) {
1670
			stops[i + pattern->n_stops - 1] = pattern->stops[i];
1671
			stops[i + pattern->n_stops - 1].offset =
1672
			    0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset;
1673
		    } else {
1674
			stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1];
1675
			stops[i + pattern->n_stops - 1].offset =
1676
			    1 - 0.5 * stops[i + pattern->n_stops - 1].offset;
1677
		    }
1678
		}
1679
	    }
1680
	}
1681
    } else {
1682
	n_stops = pattern->n_stops;
1683
	stops = pattern->stops;
1684
    }
1685
 
1686
    if (start_offset >= 0.0)
1687
	for (i = 0; i < n_stops; i++) {
1688
	    offset = start_offset + (1 - start_offset ) * stops[i].offset;
1689
	    _cairo_output_stream_printf (output,
1690
					 "
1691
					 "stop-color:rgb(%f%%,%f%%,%f%%);"
1692
					 "stop-opacity:%f;\"/>\n",
1693
					 offset,
1694
					 stops[i].color.red   * 100.0,
1695
					 stops[i].color.green * 100.0,
1696
					 stops[i].color.blue  * 100.0,
1697
					 stops[i].color.alpha);
1698
	}
1699
    else {
1700
	cairo_bool_t found = FALSE;
1701
	unsigned int offset_index;
1702
	cairo_color_stop_t offset_color_start, offset_color_stop;
1703
 
1704
	for (i = 0; i < n_stops; i++) {
1705
	    if (stops[i].offset >= -start_offset) {
1706
		if (i > 0) {
1707
		    if (stops[i].offset != stops[i-1].offset) {
1708
			double x0, x1;
1709
			cairo_color_stop_t *color0, *color1;
1710
 
1711
			x0 = stops[i-1].offset;
1712
			x1 = stops[i].offset;
1713
			color0 = &stops[i-1].color;
1714
			color1 = &stops[i].color;
1715
			offset_color_start.red = color0->red + (color1->red - color0->red)
1716
			    * (-start_offset - x0) / (x1 - x0);
1717
			offset_color_start.green = color0->green + (color1->green - color0->green)
1718
			    * (-start_offset - x0) / (x1 - x0);
1719
			offset_color_start.blue = color0->blue + (color1->blue - color0->blue)
1720
			    * (-start_offset - x0) / (x1 - x0);
1721
			offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha)
1722
			    * (-start_offset - x0) / (x1 - x0);
1723
			offset_color_stop = offset_color_start;
1724
		    } else {
1725
			offset_color_stop = stops[i-1].color;
1726
			offset_color_start = stops[i].color;
1727
		    }
1728
		} else
1729
			offset_color_stop = offset_color_start = stops[i].color;
1730
	    offset_index = i;
1731
	    found = TRUE;
1732
	    break;
1733
	    }
1734
	}
1735
 
1736
	if (!found) {
1737
	    offset_index = n_stops - 1;
1738
	    offset_color_stop = offset_color_start = stops[offset_index].color;
1739
	}
1740
 
1741
	_cairo_output_stream_printf (output,
1742
				     "
1743
				     "stop-color:rgb(%f%%,%f%%,%f%%);"
1744
				     "stop-opacity:%f;\"/>\n",
1745
				     offset_color_start.red   * 100.0,
1746
				     offset_color_start.green * 100.0,
1747
				     offset_color_start.blue  * 100.0,
1748
				     offset_color_start.alpha);
1749
	for (i = offset_index; i < n_stops; i++) {
1750
	    _cairo_output_stream_printf (output,
1751
					 "
1752
					 "stop-color:rgb(%f%%,%f%%,%f%%);"
1753
					 "stop-opacity:%f;\"/>\n",
1754
					 stops[i].offset + start_offset,
1755
					 stops[i].color.red   * 100.0,
1756
					 stops[i].color.green * 100.0,
1757
					 stops[i].color.blue  * 100.0,
1758
					 stops[i].color.alpha);
1759
	}
1760
	for (i = 0; i < offset_index; i++) {
1761
	    _cairo_output_stream_printf (output,
1762
					 "
1763
					 "stop-color:rgb(%f%%,%f%%,%f%%);"
1764
					 "stop-opacity:%f;\"/>\n",
1765
					 1.0 + stops[i].offset + start_offset,
1766
					 stops[i].color.red   * 100.0,
1767
					 stops[i].color.green * 100.0,
1768
					 stops[i].color.blue  * 100.0,
1769
					 stops[i].color.alpha);
1770
	}
1771
 
1772
	_cairo_output_stream_printf (output,
1773
				     "
1774
				     "stop-color:rgb(%f%%,%f%%,%f%%);"
1775
				     "stop-opacity:%f;\"/>\n",
1776
				     offset_color_stop.red   * 100.0,
1777
				     offset_color_stop.green * 100.0,
1778
				     offset_color_stop.blue  * 100.0,
1779
				     offset_color_stop.alpha);
1780
 
1781
    }
1782
 
1783
    if (reverse_stops || emulate_reflect)
1784
	free (stops);
1785
 
1786
    return CAIRO_STATUS_SUCCESS;
1787
}
1788
 
1789
static void
1790
_cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output,
1791
					cairo_pattern_t       *pattern)
1792
{
1793
    switch (pattern->extend) {
1794
	case CAIRO_EXTEND_REPEAT:
1795
	    _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" ");
1796
	    break;
1797
	case CAIRO_EXTEND_REFLECT:
1798
	    _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" ");
1799
	    break;
1800
	case CAIRO_EXTEND_NONE:
1801
	case CAIRO_EXTEND_PAD:
1802
	    break;
1803
    }
1804
}
1805
 
1806
static cairo_status_t
1807
_cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t    *surface,
1808
					cairo_linear_pattern_t *pattern,
1809
					cairo_output_stream_t  *style,
1810
					cairo_bool_t	        is_stroke,
1811
					const cairo_matrix_t   *parent_matrix)
1812
{
1813
    cairo_svg_document_t *document = surface->document;
1814
    cairo_matrix_t p2u;
1815
    cairo_status_t status;
1816
 
1817
    p2u = pattern->base.base.matrix;
1818
    status = cairo_matrix_invert (&p2u);
1819
    /* cairo_pattern_set_matrix ensures the matrix is invertible */
1820
    assert (status == CAIRO_STATUS_SUCCESS);
1821
 
1822
    _cairo_output_stream_printf (document->xml_node_defs,
1823
				 "
1824
				 "gradientUnits=\"userSpaceOnUse\" "
1825
				 "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
1826
				 document->linear_pattern_id,
3959 Serge 1827
				 pattern->pd1.x, pattern->pd1.y,
1828
				 pattern->pd2.x, pattern->pd2.y);
1897 serge 1829
 
1830
    _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
1831
    _cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1832
    _cairo_output_stream_printf (document->xml_node_defs, ">\n");
1833
 
1834
    status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
1835
	                                            &pattern->base, 0.0,
1836
						    FALSE, FALSE);
1837
    if (unlikely (status))
1838
	return status;
1839
 
1840
    _cairo_output_stream_printf (document->xml_node_defs,
1841
				 "\n");
1842
 
1843
    _cairo_output_stream_printf (style,
1844
				 "%s:url(#linear%d);",
1845
				 is_stroke ? "stroke" : "fill",
1846
				 document->linear_pattern_id);
1847
 
1848
    document->linear_pattern_id++;
1849
 
1850
    return CAIRO_STATUS_SUCCESS;
1851
}
1852
 
1853
static cairo_status_t
1854
_cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t    *surface,
1855
					cairo_radial_pattern_t *pattern,
1856
					cairo_output_stream_t  *style,
1857
					cairo_bool_t            is_stroke,
1858
					const cairo_matrix_t   *parent_matrix)
1859
{
1860
    cairo_svg_document_t *document = surface->document;
1861
    cairo_matrix_t p2u;
1862
    cairo_extend_t extend;
1863
    double x0, y0, x1, y1, r0, r1;
1864
    double fx, fy;
1865
    cairo_bool_t reverse_stops;
1866
    cairo_status_t status;
3959 Serge 1867
    cairo_circle_double_t *c0, *c1;
1897 serge 1868
 
1869
    extend = pattern->base.base.extend;
1870
 
3959 Serge 1871
    if (pattern->cd1.radius < pattern->cd2.radius) {
1872
	c0 = &pattern->cd1;
1873
	c1 = &pattern->cd2;
1897 serge 1874
	reverse_stops = FALSE;
1875
    } else {
3959 Serge 1876
	c0 = &pattern->cd2;
1877
	c1 = &pattern->cd1;
1897 serge 1878
	reverse_stops = TRUE;
1879
    }
1880
 
3959 Serge 1881
    x0 = c0->center.x;
1882
    y0 = c0->center.y;
1883
    r0 = c0->radius;
1884
    x1 = c1->center.x;
1885
    y1 = c1->center.y;
1886
    r1 = c1->radius;
1897 serge 1887
 
1888
    p2u = pattern->base.base.matrix;
1889
    status = cairo_matrix_invert (&p2u);
1890
    /* cairo_pattern_set_matrix ensures the matrix is invertible */
1891
    assert (status == CAIRO_STATUS_SUCCESS);
1892
 
3959 Serge 1893
    if (r0 == r1) {
1897 serge 1894
	unsigned int n_stops = pattern->base.n_stops;
1895
 
1896
	_cairo_output_stream_printf (document->xml_node_defs,
1897
				     "
1898
				     "gradientUnits=\"userSpaceOnUse\" "
1899
				     "cx=\"%f\" cy=\"%f\" "
1900
				     "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1901
				     document->radial_pattern_id,
1902
				     x1, y1,
1903
				     x1, y1, r1);
1904
	_cairo_svg_surface_emit_transform (document->xml_node_defs,
1905
					   "gradientTransform",
1906
					   &p2u, parent_matrix);
1907
	_cairo_output_stream_printf (document->xml_node_defs, ">\n");
1908
 
1909
	if (extend == CAIRO_EXTEND_NONE || n_stops < 1)
1910
	    _cairo_output_stream_printf (document->xml_node_defs,
1911
					 "
1912
					 "stop-color:rgb(0%%,0%%,0%%);"
1913
					 "stop-opacity:0;\"/>\n");
1914
	else {
1915
	    _cairo_output_stream_printf (document->xml_node_defs,
1916
					 "
1917
					 "stop-color:rgb(%f%%,%f%%,%f%%);"
1918
					 "stop-opacity %f;\"/>\n",
1919
					 pattern->base.stops[0].color.red   * 100.0,
1920
					 pattern->base.stops[0].color.green * 100.0,
1921
					 pattern->base.stops[0].color.blue  * 100.0,
1922
					 pattern->base.stops[0].color.alpha);
1923
	    if (n_stops > 1)
1924
		_cairo_output_stream_printf (document->xml_node_defs,
1925
					     "
1926
					     "stop-color:rgb(%f%%,%f%%,%f%%);"
1927
					     "stop-opacity:%f;\"/>\n",
1928
					     pattern->base.stops[n_stops - 1].color.red   * 100.0,
1929
					     pattern->base.stops[n_stops - 1].color.green * 100.0,
1930
					     pattern->base.stops[n_stops - 1].color.blue  * 100.0,
1931
					     pattern->base.stops[n_stops - 1].color.alpha);
1932
	}
1933
 
1934
    } else {
1935
	double offset, r, x, y;
1936
	cairo_bool_t emulate_reflect = FALSE;
1937
 
1938
	fx = (r1 * x0 - r0 * x1) / (r1 - r0);
1939
	fy = (r1 * y0 - r0 * y1) / (r1 - r0);
1940
 
1941
	/* SVG doesn't support the inner circle and use instead a gradient focal.
1942
	 * That means we need to emulate the cairo behaviour by processing the
1943
	 * cairo gradient stops.
1944
	 * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
1945
	 * it's just a matter of stop position translation and calculation of
1946
	 * the corresponding SVG radial gradient focal.
1947
	 * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
1948
	 * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
1949
	 * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
1950
	 * list that maps to the original cairo stop list.
1951
	 */
1952
	if ((extend == CAIRO_EXTEND_REFLECT
1953
	     || extend == CAIRO_EXTEND_REPEAT)
1954
	    && r0 > 0.0) {
1955
	    double r_org = r1;
1956
 
1957
	    if (extend == CAIRO_EXTEND_REFLECT) {
1958
		r1 = 2 * r1 - r0;
1959
		emulate_reflect = TRUE;
1960
	    }
1961
 
1962
	    offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
1963
	    r = r1 - r0;
1964
 
1965
	    /* New position of outer circle. */
1966
	    x = r * (x1 - fx) / r_org + fx;
1967
	    y = r * (y1 - fy) / r_org + fy;
1968
 
1969
	    x1 = x;
1970
	    y1 = y;
1971
	    r1 = r;
1972
	    r0 = 0.0;
1973
	} else {
1974
	    offset = r0 / r1;
1975
	}
1976
 
1977
	_cairo_output_stream_printf (document->xml_node_defs,
1978
				     "
1979
				     "gradientUnits=\"userSpaceOnUse\" "
1980
				     "cx=\"%f\" cy=\"%f\" "
1981
				     "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
1982
				     document->radial_pattern_id,
1983
				     x1, y1,
1984
				     fx, fy, r1);
1985
 
1986
	if (emulate_reflect)
1987
	    _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
1988
	else
1989
	    _cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
1990
	_cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1991
	_cairo_output_stream_printf (document->xml_node_defs, ">\n");
1992
 
1993
	/* To support cairo's EXTEND_NONE, (for which SVG has no similar
1994
	 * notion), we add transparent color stops on either end of the
1995
	 * user-provided stops. */
1996
	if (extend == CAIRO_EXTEND_NONE) {
1997
	    _cairo_output_stream_printf (document->xml_node_defs,
1998
					 "
1999
					 "stop-color:rgb(0%%,0%%,0%%);"
2000
					 "stop-opacity:0;\"/>\n");
2001
	    if (r0 != 0.0)
2002
		_cairo_output_stream_printf (document->xml_node_defs,
2003
					     "
2004
					     "stop-color:rgb(0%%,0%%,0%%);"
2005
					     "stop-opacity:0;\"/>\n",
2006
					     r0 / r1);
2007
	}
2008
	status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
2009
		                                        &pattern->base, offset,
2010
							reverse_stops,
2011
							emulate_reflect);
2012
	if (unlikely (status))
2013
	    return status;
2014
 
2015
	if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
2016
	    _cairo_output_stream_printf (document->xml_node_defs,
2017
					 "
2018
					 "stop-color:rgb(0%%,0%%,0%%);"
2019
					 "stop-opacity:0;\"/>\n");
2020
    }
2021
 
2022
    _cairo_output_stream_printf (document->xml_node_defs,
2023
				 "\n");
2024
 
2025
    _cairo_output_stream_printf (style,
2026
				 "%s:url(#radial%d);",
2027
				 is_stroke ? "stroke" : "fill",
2028
				 document->radial_pattern_id);
2029
 
2030
    document->radial_pattern_id++;
2031
 
2032
    return CAIRO_STATUS_SUCCESS;
2033
}
2034
 
2035
static cairo_status_t
2036
_cairo_svg_surface_emit_pattern (cairo_svg_surface_t   *surface,
2037
				 const cairo_pattern_t       *pattern,
2038
				 cairo_output_stream_t *output,
2039
				 cairo_bool_t		is_stroke,
2040
				 const cairo_matrix_t  *parent_matrix)
2041
{
2042
    switch (pattern->type) {
2043
    case CAIRO_PATTERN_TYPE_SOLID:
2044
	return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern,
2045
						      output, is_stroke);
2046
 
2047
    case CAIRO_PATTERN_TYPE_SURFACE:
2048
	return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern,
2049
							output, is_stroke, parent_matrix);
2050
 
2051
    case CAIRO_PATTERN_TYPE_LINEAR:
2052
	return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern,
2053
						       output, is_stroke, parent_matrix);
2054
 
2055
    case CAIRO_PATTERN_TYPE_RADIAL:
2056
	return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern,
2057
						       output, is_stroke, parent_matrix);
3959 Serge 2058
 
2059
    case CAIRO_PATTERN_TYPE_MESH:
2060
    case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
2061
	ASSERT_NOT_REACHED;
1897 serge 2062
    }
2063
    return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
2064
}
2065
 
2066
static cairo_status_t
2067
_cairo_svg_surface_emit_fill_style (cairo_output_stream_t	*output,
2068
				    cairo_svg_surface_t		*surface,
2069
				    cairo_operator_t		 op,
2070
				    const cairo_pattern_t	*source,
2071
				    cairo_fill_rule_t		 fill_rule,
2072
				    const cairo_matrix_t	*parent_matrix)
2073
{
2074
    _cairo_output_stream_printf (output,
2075
				 "fill-rule:%s;",
2076
				 fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
2077
				 "evenodd" : "nonzero");
2078
    _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2079
    return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix);
2080
}
2081
 
2082
static cairo_status_t
2083
_cairo_svg_surface_emit_stroke_style (cairo_output_stream_t	   *output,
2084
				      cairo_svg_surface_t	   *surface,
2085
				      cairo_operator_t		    op,
2086
				      const cairo_pattern_t	   *source,
2087
				      const cairo_stroke_style_t   *stroke_style,
2088
				      const cairo_matrix_t	   *parent_matrix)
2089
{
2090
    cairo_status_t status;
2091
    const char *line_cap, *line_join;
2092
    unsigned int i;
2093
 
2094
    switch (stroke_style->line_cap) {
2095
	case CAIRO_LINE_CAP_BUTT:
2096
	    line_cap = "butt";
2097
	    break;
2098
	case CAIRO_LINE_CAP_ROUND:
2099
	    line_cap = "round";
2100
	    break;
2101
	case CAIRO_LINE_CAP_SQUARE:
2102
	    line_cap = "square";
2103
	    break;
2104
	default:
2105
	    ASSERT_NOT_REACHED;
2106
    }
2107
 
2108
    switch (stroke_style->line_join) {
2109
	case CAIRO_LINE_JOIN_MITER:
2110
	    line_join = "miter";
2111
	    break;
2112
	case CAIRO_LINE_JOIN_ROUND:
2113
	    line_join = "round";
2114
	    break;
2115
	case CAIRO_LINE_JOIN_BEVEL:
2116
	    line_join = "bevel";
2117
	    break;
2118
	default:
2119
	    ASSERT_NOT_REACHED;
2120
    }
2121
 
2122
    _cairo_output_stream_printf (output,
2123
				 "stroke-width:%f;"
2124
				 "stroke-linecap:%s;"
2125
				 "stroke-linejoin:%s;",
2126
				 stroke_style->line_width,
2127
				 line_cap,
2128
				 line_join);
2129
 
2130
     status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix);
2131
     if (unlikely (status))
2132
	 return status;
2133
 
2134
     _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2135
 
2136
    if (stroke_style->num_dashes > 0) {
2137
	_cairo_output_stream_printf (output, "stroke-dasharray:");
2138
	for (i = 0; i < stroke_style->num_dashes; i++) {
2139
	    _cairo_output_stream_printf (output, "%f",
2140
					 stroke_style->dash[i]);
2141
	    if (i + 1 < stroke_style->num_dashes)
2142
		_cairo_output_stream_printf (output, ",");
2143
	    else
2144
		_cairo_output_stream_printf (output, ";");
2145
	}
2146
	if (stroke_style->dash_offset != 0.0) {
2147
	    _cairo_output_stream_printf (output,
2148
					 "stroke-dashoffset:%f;",
2149
					 stroke_style->dash_offset);
2150
	}
2151
    }
2152
 
2153
    _cairo_output_stream_printf (output,
2154
				 "stroke-miterlimit:%f;",
2155
				 stroke_style->miter_limit);
2156
 
2157
    return CAIRO_STATUS_SUCCESS;
2158
}
2159
 
2160
static cairo_int_status_t
2161
_cairo_svg_surface_fill_stroke (void			*abstract_surface,
2162
				cairo_operator_t	 fill_op,
2163
				const cairo_pattern_t	*fill_source,
2164
				cairo_fill_rule_t	 fill_rule,
2165
				double			 fill_tolerance,
2166
				cairo_antialias_t	 fill_antialias,
3959 Serge 2167
				const cairo_path_fixed_t*path,
1897 serge 2168
				cairo_operator_t	 stroke_op,
2169
				const cairo_pattern_t	*stroke_source,
2170
				const cairo_stroke_style_t	*stroke_style,
2171
				const cairo_matrix_t		*stroke_ctm,
2172
				const cairo_matrix_t		*stroke_ctm_inverse,
2173
				double			 stroke_tolerance,
2174
				cairo_antialias_t	 stroke_antialias,
3959 Serge 2175
				const cairo_clip_t	*clip)
1897 serge 2176
{
2177
    cairo_svg_surface_t *surface = abstract_surface;
2178
    cairo_status_t status;
2179
 
2180
    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2181
    if (unlikely (status))
2182
	return status;
2183
 
2184
    _cairo_output_stream_printf (surface->xml_node, "
2185
    status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, fill_op,
2186
						 fill_source, fill_rule, stroke_ctm_inverse);
2187
    if (unlikely (status))
2188
	return status;
2189
 
2190
    status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op,
2191
						   stroke_source, stroke_style, stroke_ctm_inverse);
2192
    if (unlikely (status))
2193
	return status;
2194
 
2195
    _cairo_output_stream_printf (surface->xml_node, "\" ");
2196
 
2197
    _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse);
2198
 
2199
    _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL);
2200
    _cairo_output_stream_printf (surface->xml_node, "/>\n");
2201
 
2202
    return CAIRO_STATUS_SUCCESS;
2203
}
2204
 
2205
static cairo_int_status_t
2206
_cairo_svg_surface_fill (void			*abstract_surface,
2207
			 cairo_operator_t	 op,
2208
			 const cairo_pattern_t	*source,
3959 Serge 2209
			 const cairo_path_fixed_t*path,
1897 serge 2210
			 cairo_fill_rule_t	 fill_rule,
2211
			 double			 tolerance,
2212
			 cairo_antialias_t	 antialias,
3959 Serge 2213
			 const cairo_clip_t	*clip)
1897 serge 2214
{
2215
    cairo_svg_surface_t *surface = abstract_surface;
2216
    cairo_status_t status;
2217
 
2218
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2219
	return _cairo_svg_surface_analyze_operation (surface, op, source);
2220
 
2221
    assert (_cairo_svg_surface_operation_supported (surface, op, source));
2222
 
2223
    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2224
    if (unlikely (status))
2225
	return status;
2226
 
2227
    _cairo_output_stream_printf (surface->xml_node, "
2228
    status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, op, source, fill_rule, NULL);
2229
    if (unlikely (status))
2230
	return status;
2231
 
2232
    _cairo_output_stream_printf (surface->xml_node, "\" ");
2233
 
2234
    _cairo_svg_surface_emit_path (surface->xml_node, path, NULL);
2235
 
2236
    _cairo_output_stream_printf (surface->xml_node, "/>\n");
2237
 
2238
    return CAIRO_STATUS_SUCCESS;
2239
}
2240
 
2241
static cairo_bool_t
2242
_cairo_svg_surface_get_extents (void		        *abstract_surface,
2243
				cairo_rectangle_int_t   *rectangle)
2244
{
2245
    cairo_svg_surface_t *surface = abstract_surface;
2246
 
2247
    rectangle->x = 0;
2248
    rectangle->y = 0;
2249
 
2250
    /* XXX: The conversion to integers here is pretty bogus, (not to
2251
     * mention the arbitrary limitation of width to a short(!). We
2252
     * may need to come up with a better interface for get_size.
2253
     */
2254
    rectangle->width  = ceil (surface->width);
2255
    rectangle->height = ceil (surface->height);
2256
 
2257
    return TRUE;
2258
}
2259
 
2260
static cairo_status_t
2261
_cairo_svg_surface_emit_paint (cairo_output_stream_t *output,
2262
			       cairo_svg_surface_t   *surface,
2263
			       cairo_operator_t	      op,
2264
			       const cairo_pattern_t	     *source,
2265
			       const cairo_pattern_t	     *mask_source,
2266
			       const char	     *extra_attributes)
2267
{
2268
    cairo_status_t status;
2269
 
2270
    if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
2271
	source->extend == CAIRO_EXTEND_NONE)
2272
	return _cairo_svg_surface_emit_composite_pattern (output,
2273
							  surface,
2274
							  op,
2275
							  (cairo_surface_pattern_t *) source,
2276
							  invalid_pattern_id,
2277
							  mask_source ? &mask_source->matrix :NULL,
2278
							  extra_attributes);
2279
 
2280
    _cairo_output_stream_printf (output,
2281
				 "
2282
				 "width=\"%f\" height=\"%f\" "
2283
				 "style=\"",
2284
				 surface->width, surface->height);
2285
    _cairo_svg_surface_emit_operator_for_style (output, surface, op);
2286
    status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL);
2287
    if (unlikely (status))
2288
	return status;
2289
 
2290
    _cairo_output_stream_printf (output, "stroke:none;\"");
2291
 
2292
    if (extra_attributes)
2293
	_cairo_output_stream_printf (output, " %s", extra_attributes);
2294
 
2295
    _cairo_output_stream_printf (output, "/>\n");
2296
 
2297
    return CAIRO_STATUS_SUCCESS;
2298
}
2299
 
2300
static cairo_int_status_t
2301
_cairo_svg_surface_paint (void		    *abstract_surface,
2302
			  cairo_operator_t   op,
2303
			  const cairo_pattern_t   *source,
3959 Serge 2304
			  const cairo_clip_t	  *clip)
1897 serge 2305
{
2306
    cairo_status_t status;
2307
    cairo_svg_surface_t *surface = abstract_surface;
2308
 
2309
    /* Emulation of clear and source operators, when no clipping region
2310
     * is defined. We just delete existing content of surface root node,
2311
     * and exit early if operator is clear.
2312
     */
2313
    if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) &&
2314
	clip == NULL)
2315
    {
2316
	switch (surface->paginated_mode) {
2317
	case CAIRO_PAGINATED_MODE_FALLBACK:
2318
	    ASSERT_NOT_REACHED;
2319
	case CAIRO_PAGINATED_MODE_ANALYZE:
2320
	    return CAIRO_STATUS_SUCCESS;
2321
 
2322
	case CAIRO_PAGINATED_MODE_RENDER:
2323
	    status = _cairo_output_stream_destroy (surface->xml_node);
2324
	    if (unlikely (status)) {
2325
		surface->xml_node = NULL;
2326
		return status;
2327
	    }
2328
 
2329
	    surface->xml_node = _cairo_memory_stream_create ();
2330
	    if (_cairo_output_stream_get_status (surface->xml_node)) {
2331
		status = _cairo_output_stream_destroy (surface->xml_node);
2332
		surface->xml_node = NULL;
2333
		return status;
2334
	    }
2335
 
2336
	    if (op == CAIRO_OPERATOR_CLEAR) {
2337
		if (surface->content == CAIRO_CONTENT_COLOR) {
2338
		    _cairo_output_stream_printf (surface->xml_node,
2339
						 "
2340
						 "width=\"%f\" height=\"%f\" "
2341
						 "style=\"opacity:1;"
2342
						 "stroke:none;"
2343
						 "fill:rgb(0,0,0);\"/>\n",
2344
						 surface->width, surface->height);
2345
		}
2346
		return CAIRO_STATUS_SUCCESS;
2347
	    }
2348
	    break;
2349
	}
2350
    } else {
2351
	if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2352
	    return _cairo_svg_surface_analyze_operation (surface, op, source);
2353
 
2354
	assert (_cairo_svg_surface_operation_supported (surface, op, source));
2355
    }
2356
 
2357
    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2358
    if (unlikely (status))
2359
	return status;
2360
 
2361
    return _cairo_svg_surface_emit_paint (surface->xml_node,
2362
					  surface, op, source, 0, NULL);
2363
}
2364
 
2365
static cairo_int_status_t
2366
_cairo_svg_surface_mask (void		    *abstract_surface,
2367
			 cairo_operator_t     op,
2368
			 const cairo_pattern_t	    *source,
2369
			 const cairo_pattern_t	    *mask,
3959 Serge 2370
			 const cairo_clip_t	    *clip)
1897 serge 2371
{
2372
    cairo_status_t status;
2373
    cairo_svg_surface_t *surface = abstract_surface;
2374
    cairo_svg_document_t *document = surface->document;
2375
    cairo_output_stream_t *mask_stream;
2376
    char buffer[64];
2377
    cairo_bool_t discard_filter = FALSE;
2378
    unsigned int mask_id;
2379
 
2380
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
2381
	cairo_status_t source_status, mask_status;
2382
 
2383
	source_status = _cairo_svg_surface_analyze_operation (surface, op, source);
2384
	if (_cairo_status_is_error (source_status))
2385
	    return source_status;
2386
 
2387
	if (mask->has_component_alpha) {
2388
	    mask_status = CAIRO_INT_STATUS_UNSUPPORTED;
2389
	} else {
2390
	    mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask);
2391
	    if (_cairo_status_is_error (mask_status))
2392
		return mask_status;
2393
	}
2394
 
2395
	return _cairo_analysis_surface_merge_status (source_status,
2396
						     mask_status);
2397
    }
2398
 
2399
    assert (_cairo_svg_surface_operation_supported (surface, op, source));
2400
    assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask));
2401
 
2402
    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2403
    if (unlikely (status))
2404
	return status;
2405
 
2406
    if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
2407
	const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask;
3959 Serge 2408
	cairo_content_t content = surface_pattern->surface->content;
1897 serge 2409
	if (content == CAIRO_CONTENT_ALPHA)
2410
	    discard_filter = TRUE;
2411
    }
2412
 
2413
    if (!discard_filter)
2414
	_cairo_svg_surface_emit_alpha_filter (document);
2415
 
2416
    /* _cairo_svg_surface_emit_paint() will output a pattern definition to
2417
     * document->xml_node_defs so we need to write the mask element to
2418
     * a temporary stream and then copy that to xml_node_defs. */
2419
    mask_stream = _cairo_memory_stream_create ();
2420
    if (_cairo_output_stream_get_status (mask_stream))
2421
	return _cairo_output_stream_destroy (mask_stream);
2422
 
2423
    mask_id = _cairo_svg_document_allocate_mask_id (document);
2424
 
2425
    _cairo_output_stream_printf (mask_stream,
2426
				 "\n"
2427
				 "%s",
2428
				 mask_id,
2429
				 discard_filter ? "" : "  \n");
2430
    status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL);
2431
    if (unlikely (status)) {
2432
	cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream);
2433
	return status;
2434
	(void) ignore;
2435
    }
2436
 
2437
    _cairo_output_stream_printf (mask_stream,
2438
				 "%s"
2439
				 "\n",
2440
				 discard_filter ? "" : "  \n");
2441
    _cairo_memory_stream_copy (mask_stream, document->xml_node_defs);
2442
 
2443
    status = _cairo_output_stream_destroy (mask_stream);
2444
    if (unlikely (status))
2445
	return status;
2446
 
2447
    snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"",
2448
	      mask_id);
2449
    status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer);
2450
    if (unlikely (status))
2451
	return status;
2452
 
2453
    return CAIRO_STATUS_SUCCESS;
2454
}
2455
 
2456
static cairo_int_status_t
2457
_cairo_svg_surface_stroke (void			*abstract_dst,
2458
			   cairo_operator_t      op,
2459
			   const cairo_pattern_t *source,
3959 Serge 2460
			   const cairo_path_fixed_t*path,
1897 serge 2461
			   const cairo_stroke_style_t *stroke_style,
2462
			   const cairo_matrix_t	*ctm,
2463
			   const cairo_matrix_t	*ctm_inverse,
2464
			   double		 tolerance,
2465
			   cairo_antialias_t	 antialias,
3959 Serge 2466
			   const cairo_clip_t	*clip)
1897 serge 2467
{
2468
    cairo_svg_surface_t *surface = abstract_dst;
2469
    cairo_status_t status;
2470
 
2471
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2472
	return _cairo_svg_surface_analyze_operation (surface, op, source);
2473
 
2474
    assert (_cairo_svg_surface_operation_supported (surface, op, source));
2475
 
2476
    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2477
    if (unlikely (status))
2478
	return status;
2479
 
2480
    _cairo_output_stream_printf (surface->xml_node, "
2481
    status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, op,
2482
						   source, stroke_style, ctm_inverse);
2483
    if (unlikely (status))
2484
	return status;
2485
 
2486
    _cairo_output_stream_printf (surface->xml_node, "\" ");
2487
 
2488
    _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse);
2489
 
2490
    _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL);
2491
    _cairo_output_stream_printf (surface->xml_node, "/>\n");
2492
 
2493
    return CAIRO_STATUS_SUCCESS;
2494
}
2495
 
2496
static cairo_int_status_t
2497
_cairo_svg_surface_show_glyphs (void			*abstract_surface,
2498
				cairo_operator_t	 op,
2499
				const cairo_pattern_t	*pattern,
2500
				cairo_glyph_t		*glyphs,
2501
				int			 num_glyphs,
2502
				cairo_scaled_font_t	*scaled_font,
3959 Serge 2503
				const cairo_clip_t	*clip)
1897 serge 2504
{
2505
    cairo_svg_surface_t *surface = abstract_surface;
2506
    cairo_svg_document_t *document = surface->document;
2507
    cairo_path_fixed_t path;
3959 Serge 2508
    cairo_int_status_t status;
1897 serge 2509
    cairo_scaled_font_subsets_glyph_t subset_glyph;
2510
    int i;
2511
 
2512
    if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
2513
	return _cairo_svg_surface_analyze_operation (surface, op, pattern);
2514
 
2515
    assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
2516
 
2517
    if (num_glyphs <= 0)
2518
	return CAIRO_STATUS_SUCCESS;
2519
 
2520
    status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
2521
    if (unlikely (status))
2522
	return status;
2523
 
2524
    /* FIXME it's probably possible to apply a pattern of a gradient to
2525
     * a group of symbols, but I don't know how yet. Gradients or patterns
2526
     * are translated by x and y properties of use element. */
2527
    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
2528
	goto FALLBACK;
2529
 
2530
    _cairo_output_stream_printf (surface->xml_node, "
2531
    status = _cairo_svg_surface_emit_pattern (surface, pattern,
2532
	                                      surface->xml_node, FALSE, NULL);
2533
    if (unlikely (status))
2534
	return status;
2535
 
2536
    _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op);
2537
 
2538
    _cairo_output_stream_printf (surface->xml_node, "\">\n");
2539
 
2540
    for (i = 0; i < num_glyphs; i++) {
2541
	status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets,
2542
						       scaled_font, glyphs[i].index,
2543
						       NULL, 0,
2544
                                                       &subset_glyph);
2545
	if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
2546
	    _cairo_output_stream_printf (surface->xml_node, "\n");
2547
 
2548
	    glyphs += i;
2549
	    num_glyphs -= i;
2550
	    goto FALLBACK;
2551
	}
2552
 
2553
	if (unlikely (status))
2554
	    return status;
2555
 
2556
	_cairo_output_stream_printf (surface->xml_node,
2557
				     "  
2558
				     "x=\"%f\" y=\"%f\"/>\n",
2559
				     subset_glyph.font_id,
2560
                                     subset_glyph.subset_glyph_index,
2561
				     glyphs[i].x, glyphs[i].y);
2562
    }
2563
 
2564
    _cairo_output_stream_printf (surface->xml_node, "\n");
2565
 
2566
    return CAIRO_STATUS_SUCCESS;
2567
 
2568
FALLBACK:
2569
    _cairo_path_fixed_init (&path);
2570
 
2571
    status = _cairo_scaled_font_glyph_path (scaled_font,
2572
					    (cairo_glyph_t *) glyphs,
2573
					    num_glyphs, &path);
2574
 
2575
    if (unlikely (status)) {
2576
	_cairo_path_fixed_fini (&path);
2577
	return status;
2578
    }
2579
 
2580
    status = _cairo_svg_surface_fill (abstract_surface, op, pattern,
2581
				      &path, CAIRO_FILL_RULE_WINDING,
2582
				      0.0, CAIRO_ANTIALIAS_SUBPIXEL,
2583
				      clip);
2584
 
2585
    _cairo_path_fixed_fini (&path);
2586
 
2587
    return status;
2588
}
2589
 
2590
static void
2591
_cairo_svg_surface_get_font_options (void                  *abstract_surface,
2592
				     cairo_font_options_t  *options)
2593
{
2594
    _cairo_font_options_init_default (options);
2595
 
2596
    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
2597
    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
2598
    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
3959 Serge 2599
    _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
1897 serge 2600
}
2601
 
3959 Serge 2602
 
2603
static const char **
2604
_cairo_svg_surface_get_supported_mime_types (void	   *abstract_surface)
2605
{
2606
    return _cairo_svg_supported_mime_types;
2607
}
2608
 
1897 serge 2609
static const cairo_surface_backend_t cairo_svg_surface_backend = {
2610
	CAIRO_SURFACE_TYPE_SVG,
3959 Serge 2611
	_cairo_svg_surface_finish,
2612
 
2613
	_cairo_default_context_create,
2614
 
1897 serge 2615
	NULL, /* create_similar: handled by wrapper */
3959 Serge 2616
	NULL, /* create_similar_image */
2617
	NULL, /* map to image */
2618
	NULL, /* unmap image */
2619
 
2620
	_cairo_surface_default_source,
1897 serge 2621
	NULL, /* acquire_source_image */
2622
	NULL, /* release_source_image */
3959 Serge 2623
	NULL, /* snapshot */
2624
 
1897 serge 2625
	_cairo_svg_surface_copy_page,
2626
	_cairo_svg_surface_show_page,
3959 Serge 2627
 
1897 serge 2628
	_cairo_svg_surface_get_extents,
2629
	_cairo_svg_surface_get_font_options,
3959 Serge 2630
 
1897 serge 2631
	NULL, /* flush */
2632
	NULL, /* mark dirty rectangle */
3959 Serge 2633
 
1897 serge 2634
	_cairo_svg_surface_paint,
2635
	_cairo_svg_surface_mask,
2636
	_cairo_svg_surface_stroke,
2637
	_cairo_svg_surface_fill,
3959 Serge 2638
	_cairo_svg_surface_fill_stroke,
1897 serge 2639
	_cairo_svg_surface_show_glyphs,
3959 Serge 2640
	NULL, /* has_show_text_glyphs */
2641
	NULL, /* show_text_glyphs */
2642
	_cairo_svg_surface_get_supported_mime_types,
1897 serge 2643
};
2644
 
2645
static cairo_status_t
2646
_cairo_svg_document_create (cairo_output_stream_t	 *output_stream,
2647
			    double			  width,
2648
			    double			  height,
2649
			    cairo_svg_version_t		  version,
2650
			    cairo_svg_document_t	**document_out)
2651
{
2652
    cairo_svg_document_t *document;
2653
    cairo_status_t status, status_ignored;
2654
 
2655
    if (output_stream->status)
2656
	return output_stream->status;
2657
 
2658
    document = malloc (sizeof (cairo_svg_document_t));
2659
    if (unlikely (document == NULL))
2660
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2661
 
2662
    /* The use of defs for font glyphs imposes no per-subset limit. */
2663
    document->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
2664
    if (unlikely (document->font_subsets == NULL)) {
2665
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2666
	goto CLEANUP_DOCUMENT;
2667
    }
2668
 
2669
    document->output_stream = output_stream;
2670
    document->refcount = 1;
2671
    document->owner = NULL;
2672
    document->finished = FALSE;
2673
    document->width = width;
2674
    document->height = height;
2675
 
2676
    document->linear_pattern_id = 0;
2677
    document->radial_pattern_id = 0;
2678
    document->pattern_id = 0;
2679
    document->filter_id = 0;
2680
    document->clip_id = 0;
2681
    document->mask_id = 0;
2682
 
2683
    document->xml_node_defs = _cairo_memory_stream_create ();
2684
    status = _cairo_output_stream_get_status (document->xml_node_defs);
2685
    if (unlikely (status))
2686
	goto CLEANUP_NODE_DEFS;
2687
 
2688
    document->xml_node_glyphs = _cairo_memory_stream_create ();
2689
    status = _cairo_output_stream_get_status (document->xml_node_glyphs);
2690
    if (unlikely (status))
2691
	goto CLEANUP_NODE_GLYPHS;
2692
 
2693
    document->alpha_filter = FALSE;
2694
 
2695
    document->svg_version = version;
2696
 
2697
    *document_out = document;
2698
    return CAIRO_STATUS_SUCCESS;
2699
 
2700
  CLEANUP_NODE_GLYPHS:
2701
    status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs);
2702
  CLEANUP_NODE_DEFS:
2703
    status_ignored = _cairo_output_stream_destroy (document->xml_node_defs);
2704
    _cairo_scaled_font_subsets_destroy (document->font_subsets);
2705
  CLEANUP_DOCUMENT:
2706
    free (document);
2707
    return status;
2708
}
2709
 
2710
static cairo_svg_document_t *
2711
_cairo_svg_document_reference (cairo_svg_document_t *document)
2712
{
2713
    document->refcount++;
2714
 
2715
    return document;
2716
}
2717
 
2718
static unsigned int
2719
_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document)
2720
{
2721
    return document->mask_id++;
2722
}
2723
 
2724
static cairo_status_t
2725
_cairo_svg_document_destroy (cairo_svg_document_t *document)
2726
{
2727
    cairo_status_t status;
2728
 
2729
    document->refcount--;
2730
    if (document->refcount > 0)
2731
      return CAIRO_STATUS_SUCCESS;
2732
 
2733
    status = _cairo_svg_document_finish (document);
2734
 
2735
    free (document);
2736
 
2737
    return status;
2738
}
2739
 
2740
static cairo_status_t
2741
_cairo_svg_document_finish (cairo_svg_document_t *document)
2742
{
2743
    cairo_status_t status, status2;
2744
    cairo_output_stream_t *output = document->output_stream;
2745
    cairo_svg_page_t *page;
2746
    unsigned int i;
2747
 
2748
    if (document->finished)
2749
	return CAIRO_STATUS_SUCCESS;
2750
 
2751
    /*
2752
     * Should we add DOCTYPE?
2753
     *
2754
     * Google says no.
2755
     *
2756
     * http://tech.groups.yahoo.com/group/svg-developers/message/48562:
2757
     *   There's a bunch of issues, but just to pick a few:
2758
     *   - they'll give false positives.
2759
     *   - they'll give false negatives.
2760
     *   - they're namespace-unaware.
2761
     *   - they don't wildcard.
2762
     *   So when they say OK they really haven't checked anything, when
2763
     *   they say NOT OK they might be on crack, and like all
2764
     *   namespace-unaware things they're a dead branch of the XML tree.
2765
     *
2766
     * http://jwatt.org/svg/authoring/:
2767
     *   Unfortunately the SVG DTDs are a source of so many issues that the
2768
     *   SVG WG has decided not to write one for the upcoming SVG 1.2
2769
     *   standard. In fact SVG WG members are even telling people not to use
2770
     *   a DOCTYPE declaration in SVG 1.0 and 1.1 documents.
2771
     */
2772
 
2773
    _cairo_output_stream_printf (output,
2774
				 "\n"
2775
				 "
2776
				 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
2777
				 "width=\"%fpt\" height=\"%fpt\" "
2778
				 "viewBox=\"0 0 %f %f\" version=\"%s\">\n",
2779
				 document->width, document->height,
2780
				 document->width, document->height,
2781
				 _cairo_svg_internal_version_strings [document->svg_version]);
2782
 
2783
    status = _cairo_svg_document_emit_font_subsets (document);
2784
 
2785
    if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 ||
2786
	_cairo_memory_stream_length (document->xml_node_defs) > 0) {
2787
	_cairo_output_stream_printf (output, "\n");
2788
	if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
2789
	    _cairo_output_stream_printf (output, "\n");
2790
	    _cairo_memory_stream_copy (document->xml_node_glyphs, output);
2791
	    _cairo_output_stream_printf (output, "\n");
2792
	}
2793
	_cairo_memory_stream_copy (document->xml_node_defs, output);
2794
	_cairo_output_stream_printf (output, "\n");
2795
    }
2796
 
2797
    if (document->owner != NULL) {
2798
	cairo_svg_surface_t *surface;
2799
 
2800
	surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner);
2801
	if (surface->xml_node != NULL &&
2802
		_cairo_memory_stream_length (surface->xml_node) > 0) {
2803
	    if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) {
2804
		if (status == CAIRO_STATUS_SUCCESS)
2805
		    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2806
	    }
2807
	}
2808
 
2809
	if (surface->page_set.num_elements > 1 &&
2810
	    _cairo_svg_version_has_page_set_support (document->svg_version)) {
2811
	    _cairo_output_stream_printf (output, "\n");
2812
	    for (i = 0; i < surface->page_set.num_elements; i++) {
2813
		page = _cairo_array_index (&surface->page_set, i);
2814
		_cairo_output_stream_printf (output, "\n");
2815
		_cairo_output_stream_printf (output,
2816
					     "\n",
2817
					     page->surface_id);
2818
		_cairo_memory_stream_copy (page->xml_node, output);
2819
		_cairo_output_stream_printf (output, "\n\n");
2820
	    }
2821
	    _cairo_output_stream_printf (output, "\n");
2822
	} else if (surface->page_set.num_elements > 0) {
2823
	    page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
2824
	    _cairo_output_stream_printf (output,
2825
					 "\n",
2826
					 page->surface_id);
2827
	    _cairo_memory_stream_copy (page->xml_node, output);
2828
	    _cairo_output_stream_printf (output, "\n");
2829
	}
2830
    }
2831
 
2832
    _cairo_output_stream_printf (output, "\n");
2833
 
2834
    status2 = _cairo_output_stream_destroy (document->xml_node_glyphs);
2835
    if (status == CAIRO_STATUS_SUCCESS)
2836
	status = status2;
2837
 
2838
    status2 = _cairo_output_stream_destroy (document->xml_node_defs);
2839
    if (status == CAIRO_STATUS_SUCCESS)
2840
	status = status2;
2841
 
2842
    status2 = _cairo_output_stream_destroy (output);
2843
    if (status == CAIRO_STATUS_SUCCESS)
2844
	status = status2;
2845
 
2846
    document->finished = TRUE;
2847
 
2848
    return status;
2849
}
2850
 
2851
static void
2852
_cairo_svg_surface_set_paginated_mode (void			*abstract_surface,
2853
				       cairo_paginated_mode_t	 paginated_mode)
2854
{
2855
    cairo_svg_surface_t *surface = abstract_surface;
2856
 
2857
    surface->paginated_mode = paginated_mode;
2858
}
2859
 
2860
static cairo_bool_t
2861
_cairo_svg_surface_supports_fine_grained_fallbacks (void	*abstract_surface)
2862
{
2863
    cairo_svg_surface_t *surface = abstract_surface;
2864
    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
2865
 
2866
    if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) {
2867
	status =  _cairo_svg_surface_analyze_operator (surface,
2868
						       CAIRO_OPERATOR_SOURCE);
2869
    }
2870
 
3959 Serge 2871
    return status == CAIRO_INT_STATUS_SUCCESS;
1897 serge 2872
}
2873
 
2874
static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
2875
    NULL /*_cairo_svg_surface_start_page*/,
2876
    _cairo_svg_surface_set_paginated_mode,
2877
    NULL, /* _cairo_svg_surface_set_bounding_box */
2878
    NULL, /* _cairo_svg_surface_set_fallback_images_required */
2879
    _cairo_svg_surface_supports_fine_grained_fallbacks,
2880
 
2881
};