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