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