Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/gfx/cairo/cairo/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 144 kB image not shown  

Quelle  cairo-svg-surface.c   Sprache: C

 
/* vim: set sw=4 sts=4: -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
 *
 * Copyright © 2004 Red Hat, Inc
 * Copyright © 2005-2007 Emmanuel Pacaud <emmanuel.pacaud@free.fr>
 * Copyright © 2006 Red Hat, Inc
 * Copyright © 2020-2021 Anton Danilkin <afdw@yandex.ru>
 *
 * This library is free software; you can redistribute it and/or
 * modify it either under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation
 * (the "LGPL") or, at your option, under the terms of the Mozilla
 * Public License Version 1.1 (the "MPL"). If you do not alter this
 * notice, a recipient may use your version of this file under either
 * the MPL or the LGPL.
 *
 * You should have received a copy of the LGPL along with this library
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
 * You should have received a copy of the MPL along with this library
 * in the file COPYING-MPL-1.1
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
 * the specific language governing rights and limitations.
 *
 * The Original Code is the cairo graphics library.
 *
 * The Initial Developer of the Original Code is University of Southern
 * California.
 *
 * Contributor(s):
 * Kristian Høgsberg <krh@redhat.com>
 * Emmanuel Pacaud <emmanuel.pacaud@free.fr>
 * Carl Worth <cworth@cworth.org>
 * Anton Danilkin <afdw@yandex.ru>
 */


#include "cairoint.h"

#include "cairo-svg.h"

#include "cairo-array-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-info-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-output-stream-private.h"
#include "cairo-paginated-private.h"
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-surface-snapshot-inline.h"
#include "cairo-svg-surface-private.h"

/**
 * SECTION:cairo-svg
 * @Title: SVG Surfaces
 * @Short_Description: Rendering SVG documents
 * @See_Also: #cairo_surface_t
 *
 * The SVG surface is used to render cairo graphics to
 * SVG files and is a multi-page vector surface backend.
 **/


typedef struct _cairo_svg_source_surface {
    cairo_hash_entry_t base;
    unsigned int id;
    unsigned char *unique_id;
    unsigned long unique_id_length;
    cairo_bool_t transitive_paint_used;
} cairo_svg_source_surface_t;

/*
 * _cairo_svg_paint_element and _cairo_svg_paint are used to implement paints in transformed recording patterns.
 */


typedef struct _cairo_svg_paint_element {
    unsigned int source_id;
    cairo_matrix_t matrix;
} cairo_svg_paint_element_t;

typedef struct _cairo_svg_paint {
    cairo_hash_entry_t base;
    unsigned int source_id;
    cairo_array_t paint_elements;
    cairo_box_double_t box;
} cairo_svg_paint_t;

static void
_cairo_svg_source_surface_init_key (cairo_svg_source_surface_t *source_surface)
{
    if (source_surface->unique_id && source_surface->unique_id_length > 0) {
 source_surface->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE,
             source_surface->unique_id,
             source_surface->unique_id_length);
    } else {
 source_surface->base.hash = source_surface->id;
    }
}

static cairo_bool_t
_cairo_svg_source_surface_equal (const void *key_a, const void *key_b)
{
    const cairo_svg_source_surface_t *a = key_a;
    const cairo_svg_source_surface_t *b = key_b;

    if (a->unique_id && b->unique_id && a->unique_id_length == b->unique_id_length) {
 return memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0;
    }

    return a->id == b->id;
}

static void
_cairo_svg_source_surface_pluck (void *entry, void *closure)
{
    cairo_svg_source_surface_t *source_surface = entry;
    cairo_hash_table_t *patterns = closure;

    _cairo_hash_table_remove (patterns, &source_surface->base);
    free (source_surface->unique_id);
    free (source_surface);
}

static void
_cairo_svg_paint_init_key (cairo_svg_paint_t *paint)
{
    paint->base.hash = paint->source_id;
}

static cairo_bool_t
_cairo_svg_paint_equal (const void *key_a, const void *key_b)
{
    const cairo_svg_paint_t *a = key_a;
    const cairo_svg_paint_t *b = key_b;

    return a->source_id == b->source_id;
}

static void
_cairo_svg_paint_pluck (void *entry, void *closure)
{
    cairo_svg_paint_t *paint = entry;
    cairo_hash_table_t *patterns = closure;

    _cairo_hash_table_remove (patterns, &paint->base);
    _cairo_array_fini (&paint->paint_elements);
    free (paint);
}

static void
_cairo_svg_paint_box_add_padding (cairo_box_double_t *box)
{
    double width = box->p2.x - box->p1.x;
    double height = box->p2.y - box->p1.y;

    box->p1.x -= width / 10.0;
    box->p1.y -= height / 10.0;
    box->p2.x += width / 10.0;
    box->p2.y += height / 10.0;
}

enum cairo_svg_stream_element_type {
    CAIRO_SVG_STREAM_ELEMENT_TYPE_TEXT,
    CAIRO_SVG_STREAM_ELEMENT_TYPE_PAINT_DEPENDENT,
};

enum cairo_svg_stream_paint_dependent_element_type {
    CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_RECTANGLE,
    CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_RECTANGLE_AT_ORIGIN,
    CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_TRANSLATION,
    CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_INVERSE_TRANSLATION,
};

typedef struct _cairo_svg_stream_element {
    enum cairo_svg_stream_element_type type;
    union {
        struct {
     cairo_output_stream_t *output_stream;
 } text;
        struct {
     unsigned int source_id;
     enum cairo_svg_stream_paint_dependent_element_type type;
        } paint_dependent;
    };
} cairo_svg_stream_element_t;

typedef struct _cairo_svg_stream {
    cairo_status_t status;
    cairo_array_t elements;
} cairo_svg_stream_t;

static cairo_svg_stream_t
_cairo_svg_stream_create ()
{
    cairo_svg_stream_t svg_stream;
    svg_stream.status = CAIRO_STATUS_SUCCESS;
    _cairo_array_init (&svg_stream.elements, sizeof (cairo_svg_stream_element_t));
    return svg_stream;
}

static void
_cairo_svg_stream_write (cairo_svg_stream_t *svg_stream,
    const void *data,
    size_t length)
{
    cairo_status_t status;

    cairo_svg_stream_element_t *last_element = NULL;
    if (svg_stream->elements.num_elements > 0) {
 last_element = _cairo_array_index (&svg_stream->elements,
        svg_stream->elements.num_elements - 1);
    }

    if (last_element == NULL || last_element->type != CAIRO_SVG_STREAM_ELEMENT_TYPE_TEXT) {
 cairo_svg_stream_element_t element;
 element.type = CAIRO_SVG_STREAM_ELEMENT_TYPE_TEXT;
 element.text.output_stream = _cairo_memory_stream_create();
 status = _cairo_array_append (&svg_stream->elements, &element);
 if (unlikely (status)) {
     if (svg_stream->status == CAIRO_STATUS_SUCCESS) {
  svg_stream->status = status;
     }
     return;
 }
 last_element = _cairo_array_index (&svg_stream->elements,
        svg_stream->elements.num_elements - 1);
    }

    _cairo_output_stream_write (last_element->text.output_stream, data, length);
}

static void CAIRO_PRINTF_FORMAT (2, 0)
_cairo_svg_stream_printf (cairo_svg_stream_t *svg_stream,
     const char *fmt,
     ...)
{
    cairo_status_t status;

    cairo_svg_stream_element_t *last_element = NULL;
    if (svg_stream->elements.num_elements > 0) {
 last_element = _cairo_array_index (&svg_stream->elements,
        svg_stream->elements.num_elements - 1);
    }

    if (last_element == NULL || last_element->type != CAIRO_SVG_STREAM_ELEMENT_TYPE_TEXT) {
        cairo_svg_stream_element_t element;
 element.type = CAIRO_SVG_STREAM_ELEMENT_TYPE_TEXT;
 element.text.output_stream = _cairo_memory_stream_create();
 status = _cairo_array_append (&svg_stream->elements, &element);
 if (unlikely (status)) {
     if (svg_stream->status == CAIRO_STATUS_SUCCESS) {
  svg_stream->status = status;
     }
     return;
 }
 last_element = _cairo_array_index (&svg_stream->elements,
        svg_stream->elements.num_elements - 1);
    }

    va_list ap;
    va_start (ap, fmt);
    _cairo_output_stream_vprintf (last_element->text.output_stream, fmt, ap);
    va_end (ap);
}

static void
_cairo_svg_stream_append_paint_dependent (cairo_svg_stream_t *svg_stream,
       unsigned int source_id,
       enum cairo_svg_stream_paint_dependent_element_type type)
{
    cairo_status_t status;

    cairo_svg_stream_element_t element;
    element.type = CAIRO_SVG_STREAM_ELEMENT_TYPE_PAINT_DEPENDENT;
    element.paint_dependent.source_id = source_id;
    element.paint_dependent.type = type;
    status = _cairo_array_append (&svg_stream->elements, &element);
    if (svg_stream->status == CAIRO_STATUS_SUCCESS) {
 svg_stream->status = status;
    }
}

static void
_cairo_svg_stream_copy (cairo_svg_stream_t *from,
   cairo_svg_stream_t *to)
{
    cairo_status_t status;

    if (unlikely (from->status)) {
 if (to->status == CAIRO_STATUS_SUCCESS) {
     to->status = from->status;
 }
 return;
    }

    for (unsigned int i = 0; i < from->elements.num_elements; i++) {
 cairo_svg_stream_element_t *element = _cairo_array_index (&from->elements, i);
 cairo_svg_stream_element_t element_copy = *element;
 if (element->type == CAIRO_SVG_STREAM_ELEMENT_TYPE_TEXT) {
     element_copy.text.output_stream = _cairo_memory_stream_create ();
     _cairo_memory_stream_copy (element->text.output_stream, element_copy.text.output_stream);
     if (to->status == CAIRO_STATUS_SUCCESS) {
  to->status = element->text.output_stream->status;
     }
 }
 status = _cairo_array_append (&to->elements, &element_copy);
 if (unlikely (status)) {
     if (to->status == CAIRO_STATUS_SUCCESS) {
  to->status = status;
     }
     return;
 }
    }
}

static void
_cairo_svg_stream_copy_to_output_stream (cairo_svg_stream_t *from,
      cairo_output_stream_t *to,
      cairo_hash_table_t *paints)
{
    if (unlikely (from->status)) {
 if (to->status == CAIRO_STATUS_SUCCESS) {
     to->status = from->status;
 }
 return;
    }

    for (unsigned int i = 0; i < from->elements.num_elements; i++) {
 cairo_svg_stream_element_t *element = _cairo_array_index (&from->elements, i);
 if (element->type == CAIRO_SVG_STREAM_ELEMENT_TYPE_TEXT) {
     _cairo_memory_stream_copy (element->text.output_stream, to);
 }
 if (element->type == CAIRO_SVG_STREAM_ELEMENT_TYPE_PAINT_DEPENDENT) {
     cairo_svg_paint_t paint_key;
     paint_key.source_id = element->paint_dependent.source_id;
     _cairo_svg_paint_init_key (&paint_key);

     cairo_svg_paint_t *found_paint_entry = _cairo_hash_table_lookup (paints,
              &paint_key.base);
     assert (found_paint_entry);

     switch (element->paint_dependent.type) {
     case CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_RECTANGLE:
  _cairo_output_stream_printf (to,
          " x=\"%f\" y=\"%f\" width=\"%f\" height=\"%f\"",
          found_paint_entry->box.p1.x,
          found_paint_entry->box.p1.y,
          found_paint_entry->box.p2.x - found_paint_entry->box.p1.x,
          found_paint_entry->box.p2.y - found_paint_entry->box.p1.y);
  break;
     case CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_RECTANGLE_AT_ORIGIN:
  _cairo_output_stream_printf (to,
          " x=\"0\" y=\"0\" width=\"%f\" height=\"%f\"",
          found_paint_entry->box.p2.x - found_paint_entry->box.p1.x,
          found_paint_entry->box.p2.y - found_paint_entry->box.p1.y);
  break;
     case CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_TRANSLATION:
  _cairo_output_stream_printf (to,
          " transform=\"translate(%f, %f)\"",
          found_paint_entry->box.p1.x,
          found_paint_entry->box.p1.y);
  break;
     case CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_INVERSE_TRANSLATION:
  _cairo_output_stream_printf (to,
          " transform=\"translate(%f, %f)\"",
          -found_paint_entry->box.p1.x,
          -found_paint_entry->box.p1.y);
  break;
     }
 }
    }
}

static cairo_status_t
_cairo_svg_stream_destroy (cairo_svg_stream_t *svg_stream)
{
    cairo_status_t status = svg_stream->status;
    for (unsigned int i = 0; i < svg_stream->elements.num_elements; i++) {
 cairo_svg_stream_element_t *element = _cairo_array_index (&svg_stream->elements, i);
 if (element->type == CAIRO_SVG_STREAM_ELEMENT_TYPE_TEXT) {
     cairo_status_t element_status = _cairo_output_stream_destroy (element->text.output_stream);
     if (status == CAIRO_STATUS_SUCCESS) {
  status = element_status;
     }
 }
    }
    _cairo_array_fini (&svg_stream->elements);
    return status;
}

/**
 * CAIRO_HAS_SVG_SURFACE:
 *
 * Defined if the SVG surface backend is available.
 * This macro can be used to conditionally compile backend-specific code.
 *
 * Since: 1.2
 **/


static const unsigned int invalid_pattern_id = -1;

static const cairo_svg_version_t _cairo_svg_versions[] =
{
    CAIRO_SVG_VERSION_1_1,
    CAIRO_SVG_VERSION_1_2
};

#define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)

static const char *_cairo_svg_supported_mime_types[] =
{
    CAIRO_MIME_TYPE_JPEG,
    CAIRO_MIME_TYPE_PNG,
    CAIRO_MIME_TYPE_UNIQUE_ID,
    CAIRO_MIME_TYPE_URI,
    NULL
};

static void
_cairo_svg_surface_emit_path (cairo_svg_stream_t *output,
         const cairo_path_fixed_t *path,
         const cairo_matrix_t *ctm_inverse);

static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] =
{
    "SVG 1.1",
    "SVG 1.2"
};

static const char * _cairo_svg_unit_strings[] =
{
    "",
    "em",
    "ex",
    "px",
    "in",
    "cm",
    "mm",
    "pt",
    "pc",
    "%"
};

enum cairo_svg_filter {
    CAIRO_SVG_FILTER_REMOVE_COLOR,
    CAIRO_SVG_FILTER_REMOVE_COLOR_AND_INVERT_ALPHA,
    CAIRO_SVG_FILTER_COLOR_TO_ALPHA,
    CAIRO_SVG_FILTER_LAST_STATIC_FILTER,
    CAIRO_SVG_FILTER_OVER,
    CAIRO_SVG_FILTER_IN,
    CAIRO_SVG_FILTER_OUT,
    CAIRO_SVG_FILTER_ATOP,
    CAIRO_SVG_FILTER_XOR,
    CAIRO_SVG_FILTER_ADD,
    CAIRO_SVG_FILTER_MULTIPLY,
    CAIRO_SVG_FILTER_SCREEN,
    CAIRO_SVG_FILTER_OVERLAY,
    CAIRO_SVG_FILTER_DARKEN,
    CAIRO_SVG_FILTER_LIGHTEN,
    CAIRO_SVG_FILTER_COLOR_DODGE,
    CAIRO_SVG_FILTER_COLOR_BURN,
    CAIRO_SVG_FILTER_HARD_LIGHT,
    CAIRO_SVG_FILTER_SOFT_LIGHT,
    CAIRO_SVG_FILTER_DIFFERENCE,
    CAIRO_SVG_FILTER_EXCLUSION,
    CAIRO_SVG_FILTER_HUE,
    CAIRO_SVG_FILTER_SATURATION,
    CAIRO_SVG_FILTER_COLOR,
    CAIRO_SVG_FILTER_LUMINOSITY,
};

typedef struct _cairo_svg_page {
    cairo_svg_stream_t xml_node;
} cairo_svg_page_t;

typedef struct _cairo_svg_document {
    cairo_output_stream_t *output_stream;
    unsigned long refcount;
    cairo_surface_t *owner;
    cairo_bool_t finished;

    double width;
    double height;
    cairo_svg_unit_t unit;

    cairo_svg_stream_t xml_node_defs;
    cairo_svg_stream_t xml_node_glyphs;
    cairo_svg_stream_t xml_node_filters;

    unsigned int linear_pattern_id;
    unsigned int radial_pattern_id;
    unsigned int pattern_id;
    unsigned int clip_id;
    unsigned int mask_id;
    unsigned int compositing_group_id;
    unsigned int filter_id;

    cairo_bool_t filters_emitted[CAIRO_SVG_FILTER_LAST_STATIC_FILTER];

    cairo_svg_version_t svg_version;

    cairo_scaled_font_subsets_t *font_subsets;

    cairo_hash_table_t *paints;
} cairo_svg_document_t;

// Must be compatible with the struct _cairo_svg_surface_start.
typedef struct _cairo_svg_surface {
    cairo_surface_t base;

    cairo_bool_t force_fallbacks;

    unsigned int source_id;
    unsigned int depth;

    double width;
    double height;
    cairo_bool_t surface_bounded;

    cairo_svg_document_t *document;

    cairo_svg_stream_t xml_node;
    cairo_array_t page_set;

    cairo_hash_table_t *source_surfaces;

    cairo_surface_clipper_t clipper;
    cairo_svg_stream_t *current_clipper_stream;
    unsigned int clip_level;

    cairo_bool_t transitive_paint_used;

    cairo_paginated_mode_t paginated_mode;
} cairo_svg_surface_t;

static cairo_status_t
_cairo_svg_document_create (cairo_output_stream_t *stream,
       double width,
       double height,
       cairo_svg_version_t version,
       cairo_svg_document_t **document_out);

static cairo_status_t
_cairo_svg_document_destroy (cairo_svg_document_t *document);

static cairo_status_t
_cairo_svg_document_finish (cairo_svg_document_t *document);

static cairo_svg_document_t *
_cairo_svg_document_reference (cairo_svg_document_t *document);

static cairo_surface_t *
_cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
     cairo_content_t content,
     double width,
     double height,
     cairo_bool_t bounded);

static cairo_surface_t *
_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
            double width,
            double height,
            cairo_svg_version_t version);

static cairo_status_t
_cairo_svg_surface_emit_composite_pattern (cairo_svg_stream_t *output,
        cairo_svg_surface_t *surface,
        cairo_surface_pattern_t *pattern,
        unsigned int pattern_id,
        const cairo_matrix_t *parent_matrix);

static cairo_status_t
_cairo_svg_surface_emit_paint (cairo_svg_stream_t *output,
          cairo_svg_surface_t *surface,
          const cairo_pattern_t *source,
          cairo_bool_t at_origin);

static const cairo_surface_backend_t cairo_svg_surface_backend;
static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend;

/**
 * cairo_svg_surface_create_for_stream:
 * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
 *              to indicate a no-op @write_func. With a no-op @write_func,
 *              the surface may be queried or used as a source without
 *              generating any temporary files.
 * @closure: the closure argument for @write_func
 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
 *
 * Creates a SVG surface of the specified size in points to be written
 * incrementally to the stream represented by @write_func and @closure.
 *
 * Return value: a pointer to the newly created surface. The caller
 * owns the surface and should call cairo_surface_destroy() when done
 * with it.
 *
 * This function always returns a valid pointer, but it will return a
 * pointer to a "nil" surface if an error such as out of memory
 * occurs. You can use cairo_surface_status() to check for this.
 *
 * Since: 1.2
 **/

cairo_surface_t *
cairo_svg_surface_create_for_stream (cairo_write_func_t   write_func,
         void   *closure,
         double    width,
         double    height)
{
    cairo_output_stream_t *stream;

    stream = _cairo_output_stream_create (write_func, NULL, closure);
    if (_cairo_output_stream_get_status (stream))
 return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));

    return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
}

/**
 * cairo_svg_surface_create:
 * @filename: a filename for the SVG output (must be writable), %NULL may be
 *            used to specify no output. This will generate a SVG surface that
 *            may be queried and used as a source, without generating a
 *            temporary file.
 * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
 * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
 *
 * Creates a SVG surface of the specified size in points to be written
 * to @filename.
 *
 * The SVG surface backend recognizes the following MIME types for the
 * data attached to a surface (see cairo_surface_set_mime_data()) when
 * it is used as a source pattern for drawing on this surface:
 * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG,
 * %CAIRO_MIME_TYPE_URI. If any of them is specified, the SVG backend
 * emits a href with the content of MIME data instead of a surface
 * snapshot (PNG, Base64-encoded) in the corresponding image tag.
 *
 * The unofficial MIME type %CAIRO_MIME_TYPE_URI is examined
 * first. If present, the URI is emitted as is: assuring the
 * correctness of URI is left to the client code.
 *
 * If %CAIRO_MIME_TYPE_URI is not present, but %CAIRO_MIME_TYPE_JPEG
 * or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is
 * Base64-encoded and emitted.
 *
 * If %CAIRO_MIME_TYPE_UNIQUE_ID is present, all surfaces with the same
 * unique identifier will only be embedded once.
 *
 * Return value: a pointer to the newly created surface. The caller
 * owns the surface and should call cairo_surface_destroy() when done
 * with it.
 *
 * This function always returns a valid pointer, but it will return a
 * pointer to a "nil" surface if an error such as out of memory
 * occurs. You can use cairo_surface_status() to check for this.
 *
 * Since: 1.2
 **/

cairo_surface_t *
cairo_svg_surface_create (const char *filename,
     double  width,
     double  height)
{
    cairo_output_stream_t *stream;

    stream = _cairo_output_stream_create_for_filename (filename);
    if (_cairo_output_stream_get_status (stream))
 return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));

    return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
}

static cairo_bool_t
_cairo_surface_is_svg (cairo_surface_t *surface)
{
    return surface->backend == &cairo_svg_surface_backend;
}

/* If the abstract_surface is a paginated surface, and that paginated
 * surface's target is a svg_surface, then set svg_surface to that
 * target. Otherwise return FALSE.
 */

static cairo_bool_t
_extract_svg_surface (cairo_surface_t *surface,
        cairo_svg_surface_t **svg_surface)
{
    cairo_surface_t *target;

    if (surface->status)
 return FALSE;
    if (surface->finished) {
 (void) _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 return FALSE;
    }

    if (!_cairo_surface_is_paginated (surface)) {
 (void) _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 return FALSE;
    }

    target = _cairo_paginated_surface_get_target (surface);
    if (target->status) {
 (void) _cairo_surface_set_error (surface, target->status);
 return FALSE;
    }
    if (target->finished) {
 (void) _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 return FALSE;
    }

    if (!_cairo_surface_is_svg (target)) {
 (void) _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 return FALSE;
    }

    *svg_surface = (cairo_svg_surface_t *) target;
    return TRUE;
}

/**
 * cairo_svg_surface_restrict_to_version:
 * @surface: a SVG #cairo_surface_t
 * @version: SVG version
 *
 * Restricts the generated SVG file to @version. See cairo_svg_get_versions()
 * for a list of available version values that can be used here.
 *
 * This function should only be called before any drawing operations
 * have been performed on the given surface. The simplest way to do
 * this is to call this function immediately after creating the
 * surface.
 *
 * Since: 1.2
 **/

void
cairo_svg_surface_restrict_to_version (cairo_surface_t  *abstract_surface,
           cairo_svg_version_t  version)
{
    cairo_svg_surface_t *surface;

    if (! _extract_svg_surface (abstract_surface, &surface))
 return;

    if (version < CAIRO_SVG_VERSION_LAST)
 surface->document->svg_version = version;
}

/**
 * cairo_svg_get_versions:
 * @versions: supported version list
 * @num_versions: list length
 *
 * Used to retrieve the list of supported versions. See
 * cairo_svg_surface_restrict_to_version().
 *
 * Since: 1.2
 **/

void
cairo_svg_get_versions (cairo_svg_version_t const **versions,
                        int     *num_versions)
{
    if (versions != NULL)
 *versions = _cairo_svg_versions;

    if (num_versions != NULL)
 *num_versions = CAIRO_SVG_VERSION_LAST;
}

/**
 * cairo_svg_version_to_string:
 * @version: a version id
 *
 * Get the string representation of the given @version id. This function
 * will return %NULL if @version isn't valid. See cairo_svg_get_versions()
 * for a way to get the list of valid version ids.
 *
 * Return value: the string associated to given version.
 *
 * Since: 1.2
 **/

const char *
cairo_svg_version_to_string (cairo_svg_version_t version)
{
    if (version >= CAIRO_SVG_VERSION_LAST)
 return NULL;

    return _cairo_svg_version_strings[version];
}

/**
 * cairo_svg_surface_set_document_unit:
 * @surface: a SVG #cairo_surface_t
 * @unit: SVG unit
 *
 * Use the specified unit for the width and height of the generated SVG file.
 * See #cairo_svg_unit_t for a list of available unit values that can be used
 * here.
 *
 * This function can be called at any time before generating the SVG file.
 *
 * However to minimize the risk of ambiguities it's recommended to call it
 * before any drawing operations have been performed on the given surface, to
 * make it clearer what the unit used in the drawing operations is.
 *
 * The simplest way to do this is to call this function immediately after
 * creating the SVG surface.
 *
 * Note if this function is never called, the default unit for SVG documents
 * generated by cairo will be user unit.
 *
 * Since: 1.16
 **/

void
cairo_svg_surface_set_document_unit (cairo_surface_t *abstract_surface,
         cairo_svg_unit_t  unit)
{
    cairo_svg_surface_t *surface;

    if (! _extract_svg_surface (abstract_surface, &surface))
 return;

    if (unit <= CAIRO_SVG_UNIT_PERCENT)
 surface->document->unit = unit;
}

/**
 * cairo_svg_surface_get_document_unit:
 * @surface: a SVG #cairo_surface_t
 *
 * Get the unit of the SVG surface.
 *
 * If the surface passed as an argument is not a SVG surface, the function
 * sets the error status to CAIRO_STATUS_SURFACE_TYPE_MISMATCH and returns
 * CAIRO_SVG_UNIT_USER.
 *
 * Return value: the SVG unit of the SVG surface.
 *
 * Since: 1.16
 **/

cairo_svg_unit_t
cairo_svg_surface_get_document_unit (cairo_surface_t *abstract_surface)
{
    cairo_svg_surface_t *surface;

    if (! _extract_svg_surface (abstract_surface, &surface)) {
 _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 return CAIRO_SVG_UNIT_USER;
    }

    return surface->document->unit;
}

static void
_cairo_svg_paint_compute (cairo_svg_document_t *document, cairo_svg_paint_t *paint) {
    for (unsigned int i = 0; i < paint->paint_elements.num_elements; i++) {
 cairo_svg_paint_element_t *paint_element = _cairo_array_index (&paint->paint_elements, i);

 cairo_svg_paint_t paint_key;
 paint_key.source_id = paint_element->source_id;
 _cairo_svg_paint_init_key (&paint_key);

 cairo_svg_paint_t *found_paint_entry = _cairo_hash_table_lookup (document->paints,
          &paint_key.base);
 assert (found_paint_entry);

 _cairo_svg_paint_compute (document, found_paint_entry);

 cairo_box_double_t box = found_paint_entry->box;
 _cairo_matrix_transform_bounding_box (&paint_element->matrix,
           &box.p1.x, &box.p1.y,
           &box.p2.x, &box.p2.y,
           NULL);
 _cairo_svg_paint_box_add_padding (&box);

 if (i == 0) {
     paint->box = box;
 } else {
     paint->box.p1.x = MIN (paint->box.p1.x, box.p1.x);
     paint->box.p1.y = MIN (paint->box.p1.y, box.p1.y);
     paint->box.p2.x = MAX (paint->box.p2.x, box.p2.x);
     paint->box.p2.y = MAX (paint->box.p2.y, box.p2.y);
 }
    }
    _cairo_array_truncate (&paint->paint_elements, 0);
}

static void
_cairo_svg_paint_compute_func (void *entry, void *closure)
{
    cairo_svg_paint_t *paint = entry;
    cairo_svg_document_t *document = closure;

    _cairo_svg_paint_compute (document, paint);
}

static cairo_status_t
_cairo_svg_surface_add_source_surface (cairo_svg_surface_t *surface,
           cairo_surface_t *source_surface,
           cairo_bool_t *is_new,
           cairo_svg_source_surface_t **result_source_surface)
{
    cairo_status_t status;

    cairo_svg_source_surface_t source_surface_key;
    source_surface_key.id = source_surface->unique_id;
    cairo_surface_get_mime_data (source_surface,
     CAIRO_MIME_TYPE_UNIQUE_ID,
     (const unsigned char **) &source_surface_key.unique_id,
     &source_surface_key.unique_id_length);
    _cairo_svg_source_surface_init_key (&source_surface_key);

    cairo_svg_source_surface_t *found_source_surface_entry = _cairo_hash_table_lookup (surface->source_surfaces,
                 &source_surface_key.base);
    if (found_source_surface_entry) {
 *is_new = FALSE;
 *result_source_surface = found_source_surface_entry;
 return CAIRO_STATUS_SUCCESS;
    }

    unsigned char *unique_id = NULL;
    unsigned long unique_id_length = 0;
    if (source_surface_key.unique_id && source_surface_key.unique_id_length > 0) {
 unique_id = _cairo_malloc (source_surface_key.unique_id_length);
 if (unique_id == NULL) {
     return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 }

 unique_id_length = source_surface_key.unique_id_length;
 memcpy (unique_id, source_surface_key.unique_id, unique_id_length);
    } else {
 unique_id = NULL;
 unique_id_length = 0;
    }

    cairo_svg_source_surface_t *source_surface_entry = malloc (sizeof (cairo_svg_source_surface_t));
    if (source_surface_entry == NULL) {
 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 goto fail;
    }
    source_surface_entry->id = source_surface_key.id;
    source_surface_entry->unique_id_length = unique_id_length;
    source_surface_entry->unique_id = unique_id;
    _cairo_svg_source_surface_init_key (source_surface_entry);
    status = _cairo_hash_table_insert (surface->source_surfaces, &source_surface_entry->base);
    if (unlikely (status)) {
 goto fail;
    }

    *is_new = TRUE;
    *result_source_surface = source_surface_entry;
    return CAIRO_STATUS_SUCCESS;

    fail:
    free (unique_id);
    free (source_surface_entry);
    return status;
}

static cairo_bool_t
_cairo_svg_surface_cliprect_covers_surface (cairo_svg_surface_t *surface,
         cairo_path_fixed_t *path)
{
    cairo_box_t box;

    return surface->surface_bounded &&
    _cairo_path_fixed_is_box (path, &box) &&
    box.p1.x <= 0 &&
    box.p1.y <= 0 &&
    _cairo_fixed_to_double (box.p2.x) >= surface->width &&
    _cairo_fixed_to_double (box.p2.y) >= surface->height;
}

static cairo_status_t
_cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
      cairo_path_fixed_t *path,
      cairo_fill_rule_t fill_rule,
      double tolerance,
      cairo_antialias_t antialias)
{
    cairo_svg_surface_t *surface = cairo_container_of (clipper,
             cairo_svg_surface_t,
             clipper);
    cairo_svg_document_t *document = surface->document;

    if (path == NULL) {
 for (unsigned int i = 0; i < surface->clip_level; i++) {
     _cairo_svg_stream_printf (surface->current_clipper_stream, "\n");
 }
 surface->clip_level = 0;
 return CAIRO_STATUS_SUCCESS;
    }

    /* skip trivial whole-page clips */
    if (_cairo_svg_surface_cliprect_covers_surface (surface, path)) {
 return CAIRO_STATUS_SUCCESS;
    }

    _cairo_svg_stream_printf (&document->xml_node_defs,
         "clip-%d\">\n",
         document->clip_id);

    _cairo_svg_stream_printf (&document->xml_node_defs,
         "%s\"",
         fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? "evenodd" : "nonzero");
    _cairo_svg_surface_emit_path (&document->xml_node_defs, path, NULL);
    _cairo_svg_stream_printf (&document->xml_node_defs, "/>\n");

    _cairo_svg_stream_printf (&document->xml_node_defs, "
\n"
);

    _cairo_svg_stream_printf (surface->current_clipper_stream,
         "url(#clip-%d)\">\n",
         document->clip_id);

    document->clip_id++;
    surface->clip_level++;

    return CAIRO_STATUS_SUCCESS;
}

static void
_cairo_svg_surface_reset_clip (cairo_svg_surface_t *surface)
{
    _cairo_surface_clipper_reset (&surface->clipper);
    if (surface->current_clipper_stream != NULL) {
 for (unsigned int i = 0; i < surface->clip_level; i++) {
     _cairo_svg_stream_printf (surface->current_clipper_stream, "
\n"
);
 }
    }
    surface->clip_level = 0;
}

static cairo_status_t
_cairo_svg_surface_set_clip (cairo_svg_surface_t *surface,
        cairo_svg_stream_t *clipper_stream,
        const cairo_clip_t *clip)
{
    if (surface->current_clipper_stream != clipper_stream) {
 _cairo_svg_surface_reset_clip (surface);
 surface->current_clipper_stream = clipper_stream;
    }
    return _cairo_surface_clipper_set_clip (&surface->clipper, clip);
}

static cairo_surface_t *
_cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
     cairo_content_t content,
     double width,
     double height,
     cairo_bool_t bounded)
{
    cairo_svg_surface_t *surface;
    cairo_surface_t *paginated;
    cairo_status_t status;

    surface = _cairo_malloc (sizeof (cairo_svg_surface_t));
    if (unlikely (surface == NULL)) {
 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
    }

    _cairo_surface_init (&surface->base,
    &cairo_svg_surface_backend,
    NULL, /* device */
    content,
    TRUE); /* is_vector */

    surface->source_id = surface->base.unique_id;
    surface->depth = 0;

    surface->width = width;
    surface->height = height;
    surface->surface_bounded = bounded;

    surface->document = _cairo_svg_document_reference (document);

    surface->xml_node = _cairo_svg_stream_create ();
    _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t));

    surface->source_surfaces = _cairo_hash_table_create (_cairo_svg_source_surface_equal);
    if (unlikely (surface->source_surfaces == NULL)) {
 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 goto CLEANUP;
    }

    _cairo_surface_clipper_init (&surface->clipper, _cairo_svg_surface_clipper_intersect_clip_path);
    surface->current_clipper_stream = NULL;
    surface->clip_level = 0;
    surface->transitive_paint_used = FALSE;

    surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;

    surface->force_fallbacks = FALSE;


    paginated = _cairo_paginated_surface_create (&surface->base,
       surface->base.content,
       &cairo_svg_surface_paginated_backend);
    status = paginated->status;
    if (status == CAIRO_STATUS_SUCCESS) {
 /* paginated keeps the only reference to surface now, drop ours */
 cairo_surface_destroy (&surface->base);
 return paginated;
    }

    /* ignore status as we are on the error path */
    CLEANUP:
    (void) _cairo_svg_stream_destroy (&surface->xml_node);
    (void) _cairo_svg_document_destroy (document);

    free (surface);

    return _cairo_surface_create_in_error (status);
}

static cairo_surface_t *
_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
            double    width,
            double    height,
            cairo_svg_version_t  version)
{
    cairo_svg_document_t *document;
    cairo_surface_t *surface;
    cairo_status_t status;

    status = _cairo_svg_document_create (stream,
                                  width, height, version,
      &document);
    if (unlikely (status)) {
 surface =  _cairo_surface_create_in_error (status);
 /* consume the output stream on behalf of caller */
 status = _cairo_output_stream_destroy (stream);
 return surface;
    }

    surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA,
            width, height, TRUE);
    if (surface->status) {
 return surface;
    }

    document->owner = surface;
    status = _cairo_svg_document_destroy (document);
    /* the ref count should be 2 at this point */
    assert (status == CAIRO_STATUS_SUCCESS);

    return surface;
}

static cairo_svg_page_t *
_cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
{
    _cairo_svg_surface_reset_clip (surface);
    cairo_svg_page_t page;
    page.xml_node = surface->xml_node;
    if (_cairo_array_append (&surface->page_set, &page)) {
 return NULL;
    }
    surface->xml_node = _cairo_svg_stream_create ();
    return _cairo_array_index (&surface->page_set,
          surface->page_set.num_elements - 1);
}

static cairo_int_status_t
_cairo_svg_surface_copy_page (void *abstract_surface)
{
    cairo_svg_surface_t *surface = abstract_surface;

    cairo_svg_page_t *page = _cairo_svg_surface_store_page (surface);
    if (unlikely (page == NULL)) {
 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }

    _cairo_svg_stream_copy (&page->xml_node, &surface->xml_node);

    return CAIRO_STATUS_SUCCESS;
}

static cairo_int_status_t
_cairo_svg_surface_show_page (void *abstract_surface)
{
    cairo_svg_surface_t *surface = abstract_surface;

    cairo_svg_page_t *page = _cairo_svg_surface_store_page (surface);
    if (unlikely (page == NULL)) {
 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }

    return CAIRO_STATUS_SUCCESS;
}

static void
_cairo_svg_surface_emit_transform (cairo_svg_stream_t *output,
       char const *attribute_name,
       const cairo_matrix_t *object_matrix,
       const cairo_matrix_t *parent_matrix)
{
    cairo_matrix_t matrix = *object_matrix;

    if (parent_matrix != NULL) {
 cairo_matrix_multiply (&matrix, &matrix, parent_matrix);
    }

    if (!_cairo_matrix_is_identity (&matrix)) {
 _cairo_svg_stream_printf (output,
      " %s=\"matrix(%f, %f, %f, %f, %f, %f)\"",
      attribute_name,
      matrix.xx, matrix.yx,
      matrix.xy, matrix.yy,
      matrix.x0, matrix.y0);
    }
}

typedef struct {
    cairo_svg_stream_t *output;
    const cairo_matrix_t *ctm_inverse;
} svg_path_info_t;

static cairo_status_t
_cairo_svg_path_move_to (void *closure,
    const cairo_point_t *point)
{
    svg_path_info_t *info = closure;
    double x = _cairo_fixed_to_double (point->x);
    double y = _cairo_fixed_to_double (point->y);

    if (info->ctm_inverse)
 cairo_matrix_transform_point (info->ctm_inverse, &x, &y);

    _cairo_svg_stream_printf (info->output, "M %f %f ", x, y);

    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_svg_path_line_to (void *closure,
    const cairo_point_t *point)
{
    svg_path_info_t *info = closure;
    double x = _cairo_fixed_to_double (point->x);
    double y = _cairo_fixed_to_double (point->y);

    if (info->ctm_inverse)
 cairo_matrix_transform_point (info->ctm_inverse, &x, &y);

    _cairo_svg_stream_printf (info->output, "L %f %f ", x, y);

    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_svg_path_curve_to (void          *closure,
     const cairo_point_t *b,
     const cairo_point_t *c,
     const cairo_point_t *d)
{
    svg_path_info_t *info = closure;
    double bx = _cairo_fixed_to_double (b->x);
    double by = _cairo_fixed_to_double (b->y);
    double cx = _cairo_fixed_to_double (c->x);
    double cy = _cairo_fixed_to_double (c->y);
    double dx = _cairo_fixed_to_double (d->x);
    double dy = _cairo_fixed_to_double (d->y);

    if (info->ctm_inverse) {
 cairo_matrix_transform_point (info->ctm_inverse, &bx, &by);
 cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy);
 cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy);
    }

    _cairo_svg_stream_printf (info->output,
         "C %f %f %f %f %f %f ",
         bx, by, cx, cy, dx, dy);

    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_svg_path_close_path (void *closure)
{
    svg_path_info_t *info = closure;

    _cairo_svg_stream_printf (info->output, "Z ");

    return CAIRO_STATUS_SUCCESS;
}

static void
_cairo_svg_surface_emit_path (cairo_svg_stream_t *output,
         const cairo_path_fixed_t *path,
         const cairo_matrix_t *ctm_inverse)
{
    cairo_status_t status;
    svg_path_info_t info;

    _cairo_svg_stream_printf (output, " d=\"");

    info.output = output;
    info.ctm_inverse = ctm_inverse;
    status = _cairo_path_fixed_interpret (path,
       _cairo_svg_path_move_to,
       _cairo_svg_path_line_to,
       _cairo_svg_path_curve_to,
       _cairo_svg_path_close_path,
       &info);
    assert (status == CAIRO_STATUS_SUCCESS);

    _cairo_svg_stream_printf (output, "\"");
}

static cairo_int_status_t
_cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document,
          cairo_scaled_font_t *scaled_font,
          unsigned long glyph_index)
{
    cairo_scaled_glyph_t *scaled_glyph;
    cairo_int_status_t status;

    status = _cairo_scaled_glyph_lookup (scaled_font,
      glyph_index,
      CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_PATH,
      NULL, /* foreground color */
      &scaled_glyph);
    if (unlikely (status)) {
 return status;
    }

    if (_cairo_path_fixed_size (scaled_glyph->path) != 0) {
 _cairo_svg_stream_printf (&document->xml_node_glyphs,
      ");

 _cairo_svg_surface_emit_path (&document->xml_node_glyphs,
          scaled_glyph->path,
          NULL);

 _cairo_svg_stream_printf (&document->xml_node_glyphs,
      "/>\n");
    }

    return status;
}

static cairo_int_status_t
_cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document,
         cairo_scaled_font_t *scaled_font,
         unsigned long glyph_index)
{
    cairo_status_t status;

    cairo_scaled_glyph_t *scaled_glyph;
    status = _cairo_scaled_glyph_lookup (scaled_font,
      glyph_index,
      CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_SURFACE,
      NULL, /* foreground color */
      &scaled_glyph);
    if (unlikely (status)) {
 return status;
    }

    cairo_bool_t use_recording_surface = (scaled_glyph->has_info & CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE) != 0;
    cairo_matrix_t glyph_matrix = scaled_glyph->surface->base.device_transform_inverse;
    cairo_image_surface_t *glyph_image_surface = scaled_glyph->surface;

    // Attempt to recognize a common pattern for a bitmap font and extract the original glyph image from it
    cairo_surface_t *extracted_surface;
    cairo_image_surface_t *extracted_image = NULL;
    void *extracted_image_extra;
    if (use_recording_surface) {
 cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) scaled_glyph->recording_surface;
 if (recording_surface->commands.num_elements == 1) {
     cairo_command_t *command = *((cairo_command_t **) _cairo_array_index (&recording_surface->commands, 0));
     if (command->header.type == CAIRO_COMMAND_MASK &&
  command->header.op == CAIRO_OPERATOR_OVER &&
  command->header.clip == NULL &&
  command->mask.source.base.type == CAIRO_PATTERN_TYPE_SOLID &&
  _cairo_color_equal (&command->mask.source.solid.color, _cairo_stock_color (CAIRO_STOCK_BLACK)) &&
  command->mask.mask.base.extend == CAIRO_EXTEND_NONE &&
  command->mask.mask.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
  command->mask.mask.surface.surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
  extracted_surface = command->mask.mask.surface.surface;
  if (_cairo_surface_acquire_source_image (extracted_surface,
        &extracted_image,
        &extracted_image_extra) == CAIRO_STATUS_SUCCESS) {
      if (extracted_image->format == CAIRO_FORMAT_A1 || extracted_image->format == CAIRO_FORMAT_A8) {
   use_recording_surface = FALSE;
   glyph_image_surface = extracted_image;
   glyph_matrix = command->mask.mask.base.matrix;
   status = cairo_matrix_invert (&glyph_matrix);
   assert (status == CAIRO_STATUS_SUCCESS);
      }
  }
     }
 }
    }

    cairo_surface_t *paginated_surface = _cairo_svg_surface_create_for_document (document,
           CAIRO_CONTENT_COLOR_ALPHA,
           0,
           0,
           FALSE);
    cairo_svg_surface_t *svg_surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (paginated_surface);
    status = paginated_surface->status;
    if (unlikely (status)) {
 goto cleanup;
    }

    unsigned int source_id = svg_surface->base.unique_id;

    cairo_surface_set_fallback_resolution (paginated_surface,
        document->owner->x_fallback_resolution,
        document->owner->y_fallback_resolution);

    cairo_svg_stream_t temporary_stream = _cairo_svg_stream_create ();

    unsigned int mask_id = document->mask_id++;

    _cairo_svg_stream_printf (&temporary_stream,
         "mask-%d\">\n",
         mask_id);

    cairo_pattern_t *pattern = cairo_pattern_create_for_surface (use_recording_surface ? scaled_glyph->recording_surface
                 : &glyph_image_surface->base);
    _cairo_svg_surface_emit_composite_pattern (&temporary_stream,
            svg_surface,
            (cairo_surface_pattern_t *) pattern,
            invalid_pattern_id,
            NULL);
    cairo_pattern_destroy (pattern);

    _cairo_svg_stream_printf (&temporary_stream, "
\n"
);

    _cairo_svg_stream_copy (&temporary_stream, &document->xml_node_defs);

    status = _cairo_svg_stream_destroy (&temporary_stream);
    if (unlikely (status)) {
 goto cleanup;
    }

    svg_surface->transitive_paint_used = TRUE;

    _cairo_svg_stream_printf (&document->xml_node_glyphs, ");
    _cairo_svg_stream_append_paint_dependent (&document->xml_node_glyphs,
           source_id,
           CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_RECTANGLE);
    _cairo_svg_stream_printf (&document->xml_node_glyphs,
         " mask=\"url(#mask-%d)\"",
         mask_id);
    if (!use_recording_surface) {
 _cairo_svg_surface_emit_transform (&document->xml_node_glyphs,
        "transform",
        &glyph_matrix,
        NULL);
    }
    _cairo_svg_stream_printf (&document->xml_node_glyphs, "/>\n");

    cairo_svg_paint_t *paint_entry = malloc (sizeof (cairo_svg_paint_t));
    if (paint_entry == NULL) {
 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 goto cleanup;
    }
    paint_entry->source_id = source_id;
    paint_entry->box.p1.x = 0;
    paint_entry->box.p1.y = 0;
    paint_entry->box.p2.x = glyph_image_surface->width;
    paint_entry->box.p2.y = glyph_image_surface->height;
    if (use_recording_surface) {
 _cairo_matrix_transform_bounding_box (&glyph_matrix,
           &paint_entry->box.p1.x, &paint_entry->box.p1.y,
           &paint_entry->box.p2.x, &paint_entry->box.p2.y,
           NULL);
    }
    _cairo_svg_paint_box_add_padding (&paint_entry->box);
    _cairo_array_init (&paint_entry->paint_elements, sizeof (cairo_svg_paint_element_t));
    _cairo_svg_paint_init_key (paint_entry);
    status = _cairo_hash_table_insert (document->paints, &paint_entry->base);
    if (unlikely (status)) {
 goto cleanup;
    }

    cleanup:
    if (status == CAIRO_STATUS_SUCCESS) {
 status = cairo_surface_status (paginated_surface);
    }
    cairo_surface_destroy (paginated_surface);

    if (extracted_image != NULL) {
 _cairo_surface_release_source_image (extracted_surface, extracted_image, extracted_image_extra);
    }

    return status;
}

static cairo_int_status_t
_cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
    cairo_scaled_font_t *scaled_font,
    unsigned long   scaled_font_glyph_index,
    unsigned int   font_id,
    unsigned int   subset_glyph_index)
{
    cairo_int_status_t      status;

    _cairo_svg_stream_printf (&document->xml_node_glyphs,
         "glyph-%d-%d\">\n",
         font_id,
         subset_glyph_index);

    status = _cairo_svg_document_emit_outline_glyph_data (document,
         scaled_font,
         scaled_font_glyph_index);
    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
 status = _cairo_svg_document_emit_bitmap_glyph_data (document,
            scaled_font,
            scaled_font_glyph_index);
    if (unlikely (status))
 return status;

    _cairo_svg_stream_printf (&document->xml_node_glyphs, "
\n"
);

    return CAIRO_INT_STATUS_SUCCESS;
}

static cairo_int_status_t
_cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
          void    *closure)
{
    cairo_svg_document_t *document = closure;
    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
    unsigned int i;

    _cairo_scaled_font_freeze_cache (font_subset->scaled_font);
    for (i = 0; i < font_subset->num_glyphs; i++) {
 status = _cairo_svg_document_emit_glyph (document,
              font_subset->scaled_font,
              font_subset->glyphs[i],
              font_subset->font_id, i);
 if (unlikely (status))
     break;
    }
    _cairo_scaled_font_thaw_cache (font_subset->scaled_font);

    return status;
}

static cairo_status_t
_cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
{
    cairo_status_t status;

    status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
                                                        _cairo_svg_document_emit_font_subset,
                                                        document);
    _cairo_scaled_font_subsets_destroy (document->font_subsets);
    document->font_subsets = NULL;

    return status;
}

static cairo_bool_t
_cairo_svg_surface_are_operation_and_pattern_supported (cairo_svg_surface_t *surface,
       cairo_operator_t op,
       const cairo_pattern_t *pattern)
{
    if (surface->force_fallbacks) {
 return FALSE;
    }

    if (op == CAIRO_OPERATOR_SATURATE) {
        return FALSE;
    }

    /* SVG 1.1 does not support these operators. We already have code for them for SVG 2
     * that can be enabled when SVG 2 becomes widespread.  */

    if (op == CAIRO_OPERATOR_OVERLAY ||
 op == CAIRO_OPERATOR_COLOR_DODGE ||
 op == CAIRO_OPERATOR_COLOR_BURN ||
 op == CAIRO_OPERATOR_HARD_LIGHT ||
 op == CAIRO_OPERATOR_SOFT_LIGHT ||
 op == CAIRO_OPERATOR_DIFFERENCE ||
 op == CAIRO_OPERATOR_EXCLUSION ||
 op == CAIRO_OPERATOR_HSL_HUE ||
 op == CAIRO_OPERATOR_HSL_SATURATION ||
 op == CAIRO_OPERATOR_HSL_COLOR ||
 op == CAIRO_OPERATOR_HSL_LUMINOSITY) {
 return FALSE;
    }

    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
        /* Do not cause stack overflow because of too deep or infinite recording surfaces. */
 if (((cairo_surface_pattern_t *) pattern)->surface->type == CAIRO_SURFACE_TYPE_RECORDING &&
     surface->depth > 1000) {
     return FALSE;
 }
 /* SVG doesn't support extends reflect and pad for surface pattern. */
        if (pattern->extend != CAIRO_EXTEND_NONE && pattern->extend != CAIRO_EXTEND_REPEAT) {
     return FALSE;
 }
    }

    /* SVG 1.1 does not support the focal point (fx, fy) that is outside of the circle defined by (cx, cy) and r. */
    if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
 cairo_radial_pattern_t *radial_pattern = (cairo_radial_pattern_t *) pattern;
 double max_radius;
 if (radial_pattern->cd1.radius > radial_pattern->cd2.radius) {
     max_radius = radial_pattern->cd1.radius;
 } else {
     max_radius = radial_pattern->cd2.radius;
 }
 cairo_point_double_t c1 = radial_pattern->cd1.center;
 cairo_point_double_t c2 = radial_pattern->cd2.center;
 if ((c1.x - c2.x) * (c1.x - c2.x) + (c1.y - c2.y) * (c1.y - c2.y) >= max_radius * max_radius) {
     return FALSE;
 }
    }

    if (pattern->type == CAIRO_PATTERN_TYPE_MESH) {
 return FALSE;
    }

    if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
 return FALSE;
    }

    return TRUE;
}

static cairo_status_t
_cairo_svg_surface_finish (void *abstract_surface)
{
    cairo_status_t status, final_status;
    cairo_svg_surface_t *surface = abstract_surface;

    if (_cairo_paginated_surface_get_target (surface->document->owner) == &surface->base) {
 final_status = _cairo_svg_document_finish (surface->document);
    } else {
 final_status = CAIRO_STATUS_SUCCESS;
    }

    status = _cairo_svg_stream_destroy (&surface->xml_node);
    if (final_status == CAIRO_STATUS_SUCCESS) {
 final_status = status;
    }

    for (unsigned int i = 0; i < surface->page_set.num_elements; i++) {
 cairo_svg_page_t *page = _cairo_array_index (&surface->page_set, i);
 status = _cairo_svg_stream_destroy (&page->xml_node);
 if (final_status == CAIRO_STATUS_SUCCESS) {
     final_status = status;
 }
    }
    _cairo_array_fini (&surface->page_set);

    _cairo_surface_clipper_reset (&surface->clipper);

    _cairo_hash_table_foreach (surface->source_surfaces, _cairo_svg_source_surface_pluck, surface->source_surfaces);
    _cairo_hash_table_destroy (surface->source_surfaces);

    status = _cairo_svg_document_destroy (surface->document);
    if (final_status == CAIRO_STATUS_SUCCESS) {
 final_status = status;
    }

    return final_status;
}

static const char *
_cairo_svg_surface_emit_static_filter (cairo_svg_document_t *document, enum cairo_svg_filter filter)
{
    if (!document->filters_emitted[filter]) {
 document->filters_emitted[filter] = TRUE;
 if (filter == CAIRO_SVG_FILTER_REMOVE_COLOR) {
     // (r, g, b, a) -> (1, 1, 1, a)
     _cairo_svg_stream_printf (&document->xml_node_filters,
          "filter-remove-color\" "
          "x=\"0%%\" y=\"0%%\" width=\"100%%\" height=\"100%%\">\n"
          "sRGB\" "
                               "values=\"0 0 0 0 1 "
          /*    */ "0 0 0 0 1 "
          /*    */ "0 0 0 0 1 "
          /*    */ "0 0 0 1 0\" />\n"
          "
\n"
);
 } else if (filter == CAIRO_SVG_FILTER_REMOVE_COLOR_AND_INVERT_ALPHA) {
     // (r, g, b, a) -> (1, 1, 1, 1 - a)
     _cairo_svg_stream_printf (&document->xml_node_filters,
          "filter-remove-color-and-invert-alpha\" "
          "x=\"0%%\" y=\"0%%\" width=\"100%%\" height=\"100%%\">\n"
          "sRGB\" "
          "values=\"0 0 0 0 1 "
          /*    */ "0 0 0 0 1 "
          /*    */ "0 0 0 0 1 "
          /*    */ "0 0 0 -1 1\"/>\n"
          "
\n"
);
 } else if (filter ==  CAIRO_SVG_FILTER_COLOR_TO_ALPHA) {
     // (r, g, b, a) -> (1, 1, 1, 0.2126 * r + 0.7152 * g + 0.0722 * b)
     _cairo_svg_stream_printf (&document->xml_node_filters,
          "filter-color-to-alpha\" "
          "x=\"0%%\" y=\"0%%\" width=\"100%%\" height=\"100%%\">\n"
          "sRGB\" "
          "values=\"0 0 0 0 1 "
          /*    */ "0 0 0 0 1 "
          /*    */ "0 0 0 0 1 "
          /*    */ "0.2126 0.7152 0.0722 0 0\"/>\n"
          "
\n"
);
 }
    }

    if (filter == CAIRO_SVG_FILTER_REMOVE_COLOR) {
 return "remove-color";
    } else if (filter == CAIRO_SVG_FILTER_REMOVE_COLOR_AND_INVERT_ALPHA) {
 return "remove-color-and-invert-alpha";
    } else if (filter ==  CAIRO_SVG_FILTER_COLOR_TO_ALPHA) {
 return "color-to-alpha";
    } else {
 ASSERT_NOT_REACHED;
    }
    return FALSE/* squelch warning */
}

#define _CAIRO_SVG_SURFACE_OUTPUT_FE_COMPOSITE_FILTER(operation) \
    _cairo_svg_stream_printf (&surface->document->xml_node_filters, \
                              "filter-%d\" " \
                              "x=\"0%%\" y=\"0%%\" width=\"100%%\" height=\"100%%\">\n" \
                              "#compositing-group-%d\" result=\"source\"", \
                              filter_id, \
                              source_compositing_group_id); \
    _cairo_svg_stream_append_paint_dependent (&surface->document->xml_node_filters, \
                                              surface->source_id, \
                                              CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_RECTANGLE_AT_ORIGIN); \
    _cairo_svg_stream_printf (&surface->document->xml_node_filters, \
                              "/>\n" \
                              "#compositing-group-%d\" result=\"destination\"", \
                              destination_compositing_group_id); \
    _cairo_svg_stream_append_paint_dependent (&surface->document->xml_node_filters, \
                                              surface->source_id, \
                                              CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_RECTANGLE_AT_ORIGIN); \
    _cairo_svg_stream_printf (&surface->document->xml_node_filters, \
                              "/>\n" \
                              "source\" in2=\"destination\" " \
                              "operator=\"" operation "\" " \
                              "color-interpolation-filters=\"sRGB\"/>\n" \
                              "
\n"
, \
                              filter_id, \
                              source_compositing_group_id, \
                              destination_compositing_group_id);

#define _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER(mode) \
    _cairo_svg_stream_printf (&surface->document->xml_node_filters, \
                              "filter-%d\" " \
                              "x=\"0%%\" y=\"0%%\" width=\"100%%\" height=\"100%%\">\n" \
                              "#compositing-group-%d\" result=\"source\"", \
                              filter_id, \
                              source_compositing_group_id); \
    _cairo_svg_stream_append_paint_dependent (&surface->document->xml_node_filters, \
                                              surface->source_id, \
                                              CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_RECTANGLE_AT_ORIGIN); \
    _cairo_svg_stream_printf (&surface->document->xml_node_filters, \
                              "/>\n" \
                              "#compositing-group-%d\" result=\"destination\"", \
                              destination_compositing_group_id); \
    _cairo_svg_stream_append_paint_dependent (&surface->document->xml_node_filters, \
                                              surface->source_id, \
                                              CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_RECTANGLE_AT_ORIGIN); \
    _cairo_svg_stream_printf (&surface->document->xml_node_filters, \
                              "/>\n" \
                              "source\" in2=\"destination\" " \
                              "mode=\"" mode "\" " \
                              "color-interpolation-filters=\"sRGB\"/>\n" \
                              "
\n"
, \
                              filter_id, \
                              source_compositing_group_id, \
                              destination_compositing_group_id);

static unsigned int
_cairo_svg_surface_emit_parametric_filter (cairo_svg_surface_t *surface,
        enum cairo_svg_filter filter,
        unsigned int source_compositing_group_id,
        unsigned int destination_compositing_group_id)
{
    unsigned int filter_id = surface->document->filter_id++;
    switch (filter) {
    case CAIRO_SVG_FILTER_OVER:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_COMPOSITE_FILTER ("over")
 break;
    case CAIRO_SVG_FILTER_IN:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_COMPOSITE_FILTER ("in")
 break;
    case CAIRO_SVG_FILTER_OUT:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_COMPOSITE_FILTER ("out")
 break;
    case CAIRO_SVG_FILTER_ATOP:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_COMPOSITE_FILTER ("atop")
 break;
    case CAIRO_SVG_FILTER_XOR:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_COMPOSITE_FILTER ("xor")
 break;
    case CAIRO_SVG_FILTER_ADD:
 // This can also be done with <feComposite operator="lighter"/>, but it is not in SVG 1.1
 _cairo_svg_stream_printf (&surface->document->xml_node_filters,
      "filter-%d\" "
      "x=\"0%%\" y=\"0%%\" width=\"100%%\" height=\"100%%\">\n"
      "#compositing-group-%d\" result=\"source\"",
      filter_id,
      source_compositing_group_id);
 _cairo_svg_stream_append_paint_dependent (&surface->document->xml_node_filters,
        surface->source_id,
        CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_RECTANGLE_AT_ORIGIN);
 _cairo_svg_stream_printf (&surface->document->xml_node_filters,
      "/>\n"
      "#compositing-group-%d\" result=\"destination\"",
      destination_compositing_group_id);
 _cairo_svg_stream_append_paint_dependent (&surface->document->xml_node_filters,
        surface->source_id,
        CAIRO_SVG_STREAM_PAINT_DEPENDENT_ELEMENT_TYPE_RECTANGLE_AT_ORIGIN);
 _cairo_svg_stream_printf (&surface->document->xml_node_filters,
      "/>\n"
      "source\" in2=\"destination\" "
      "operator=\"arithmetic\" k1=\"0\" k2=\"1\" k3=\"1\" k4=\"0\" "
      "color-interpolation-filters=\"sRGB\"/>\n"
      "
\n"
);
 break;
    case CAIRO_SVG_FILTER_MULTIPLY:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("multiply")
 break;
    case CAIRO_SVG_FILTER_SCREEN:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("screen")
 break;
    case CAIRO_SVG_FILTER_OVERLAY:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("overlay")
 break;
    case CAIRO_SVG_FILTER_DARKEN:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("darken")
 break;
    case CAIRO_SVG_FILTER_LIGHTEN:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("lighten")
 break;
    case CAIRO_SVG_FILTER_COLOR_DODGE:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("color-dodge")
 break;
    case CAIRO_SVG_FILTER_COLOR_BURN:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("color-burn")
 break;
    case CAIRO_SVG_FILTER_HARD_LIGHT:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("hard-light")
 break;
    case CAIRO_SVG_FILTER_SOFT_LIGHT:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("soft-light")
 break;
    case CAIRO_SVG_FILTER_DIFFERENCE:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("difference")
 break;
    case CAIRO_SVG_FILTER_EXCLUSION:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("exclusion")
 break;
    case CAIRO_SVG_FILTER_HUE:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("hue")
 break;
    case CAIRO_SVG_FILTER_SATURATION:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("saturation")
 break;
    case CAIRO_SVG_FILTER_COLOR:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("color")
 break;
    case CAIRO_SVG_FILTER_LUMINOSITY:
 _CAIRO_SVG_SURFACE_OUTPUT_FE_BLEND_FILTER ("luminosity")
 break;
    case CAIRO_SVG_FILTER_REMOVE_COLOR:
    case CAIRO_SVG_FILTER_REMOVE_COLOR_AND_INVERT_ALPHA:
    case CAIRO_SVG_FILTER_COLOR_TO_ALPHA:
    case CAIRO_SVG_FILTER_LAST_STATIC_FILTER:
    default:
 ASSERT_NOT_REACHED;
    }
    return filter_id;
}

typedef struct {
    cairo_svg_stream_t *output;
    unsigned int in_mem;
    unsigned int trailing;
    unsigned char src[3];
} base64_write_closure_t;

static char const base64_table[64] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static cairo_status_t
base64_write_func (void *closure,
     const unsigned char *data,
     unsigned int length)
{
    base64_write_closure_t *info = (base64_write_closure_t *) closure;
    unsigned int i;
    unsigned char *src;

    src = info->src;

    if (info->in_mem + length < 3) {
 for (i = 0; i < length; i++) {
     src[i + info->in_mem] = *data++;
 }
 info->in_mem += length;
 return CAIRO_STATUS_SUCCESS;
    }

    do {
 unsigned char dst[4];

 for (i = info->in_mem; i < 3; i++) {
     src[i] = *data++;
     length--;
 }
 info->in_mem = 0;

 dst[0] = base64_table[src[0] >> 2];
 dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4];
 dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6];
 dst[3] = base64_table[src[2] & 0xfc >> 2];
 /* Special case for the last missing bits */
 switch (info->trailing) {
     case 2:
  dst[2] = '=';
  /* fall through */
     case 1:
  dst[3] = '=';
     default:
  break;
 }
 _cairo_svg_stream_write (info->output, dst, 4);
    } while (length >= 3);

    for (i = 0; i < length; i++) {
 src[i] = *data++;
    }
    info->in_mem = length;

    return info->output->status;
}

static cairo_int_status_t
_cairo_surface_base64_encode_jpeg (cairo_surface_t       *surface,
       cairo_svg_stream_t *output)
{
    const unsigned char *mime_data;
    unsigned long mime_data_length;
    cairo_image_info_t image_info;
    base64_write_closure_t info;
    cairo_status_t status;

    cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG,
     &mime_data, &mime_data_length);
    if (mime_data == NULL)
 return CAIRO_INT_STATUS_UNSUPPORTED;

    status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length);
    if (unlikely (status))
 return status;

    if (image_info.num_components == 4)
 return CAIRO_INT_STATUS_UNSUPPORTED;

    _cairo_svg_stream_printf (output, "data:image/jpeg;base64,");

    info.output = output;
    info.in_mem = 0;
    info.trailing = 0;

    status = base64_write_func (&info, mime_data, mime_data_length);
    if (unlikely (status))
 return status;

    if (info.in_mem > 0) {
 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
 info.trailing = 3 - info.in_mem;
 info.in_mem = 3;
 status = base64_write_func (&info, NULL, 0);
    }

    return status;
}

static cairo_int_status_t
_cairo_surface_base64_encode_png (cairo_surface_t       *surface,
      cairo_svg_stream_t *output)
{
    const unsigned char *mime_data;
    unsigned long mime_data_length;
    base64_write_closure_t info;
    cairo_status_t status;

    cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG,
     &mime_data, &mime_data_length);
    if (unlikely (surface->status))
 return surface->status;
    if (mime_data == NULL)
 return CAIRO_INT_STATUS_UNSUPPORTED;

    _cairo_svg_stream_printf (output, "data:image/png;base64,");

    info.output = output;
    info.in_mem = 0;
    info.trailing = 0;

    status = base64_write_func (&info, mime_data, mime_data_length);
    if (unlikely (status))
 return status;

    if (info.in_mem > 0) {
 memset (info.src + info.in_mem, 0, 3 - info.in_mem);
 info.trailing = 3 - info.in_mem;
 info.in_mem = 3;
 status = base64_write_func (&info, NULL, 0);
    }

    return status;
}

static cairo_int_status_t
_cairo_surface_base64_encode (cairo_surface_t       *surface,
         cairo_svg_stream_t *output)
{
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=91 G=93

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.