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