Subversion Repositories Kolibri OS

Rev

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