Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

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