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 291 kB image not shown  

Quelle  cairo-pdf-surface.c   Sprache: C

 
/* -*- 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 © 2006 Red Hat, Inc
 * Copyright © 2007, 2008 Adrian Johnson
 *
 * 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>
 * Carl Worth <cworth@cworth.org>
 * Adrian Johnson <ajohnson@redneon.com>
 */


#define _DEFAULT_SOURCE /* for snprintf() */
#include "cairoint.h"

#include "cairo-pdf.h"
#include "cairo-pdf-surface-private.h"
#include "cairo-pdf-operators-private.h"
#include "cairo-pdf-shading-private.h"

#include "cairo-array-private.h"
#include "cairo-analysis-surface-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-image-info-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-recording-surface-private.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-surface-subsurface-private.h"
#include "cairo-type3-glyph-surface-private.h"

#include <zlib.h>

/*
 * Page Structure of the Generated PDF:
 *
 * Each page requiring fallbacks images contains a knockout group at
 * the top level. The first operation of the knockout group paints a
 * group containing all the supported drawing operations. Fallback
 * images (if any) are painted in the knockout group. This ensures
 * that fallback images do not composite with any content under the
 * fallback images.
 *
 * Streams:
 *
 * This PDF surface has three types of streams:
 *  - PDF Stream
 *  - Content Stream
 *  - Group Stream
 *  - Object stream
 *
 * Calling _cairo_output_stream_printf (surface->output, ...) will
 * write to the currently open stream.
 *
 * PDF Stream:
 *   A PDF Stream may be opened and closed with the following functions:
 *     _cairo_pdf_surface_open stream ()
 *     _cairo_pdf_surface_close_stream ()
 *
 *   PDF Streams are written directly to the PDF file. They are used for
 *   fonts, images and patterns.
 *
 * Content Stream:
 *   The Content Stream is opened and closed with the following functions:
 *     _cairo_pdf_surface_open_content_stream ()
 *     _cairo_pdf_surface_close_content_stream ()
 *
 *   The Content Stream contains the text and graphics operators.
 *
 * Group Stream:
 *   A Group Stream may be opened and closed with the following functions:
 *     _cairo_pdf_surface_open_group ()
 *     _cairo_pdf_surface_close_group ()
 *
 *   A Group Stream is a Form XObject. It is used for short sequences
 *   of operators. As the content is very short the group is stored in
 *   memory until it is closed. This allows some optimization such as
 *   including the Resource dictionary and stream length inside the
 *   XObject instead of using an indirect object.
 *
 * Object Stream (PDF 1.5)
 *   An Object Stream may be opened and closed with the following functions:
 *     _cairo_pdf_surface_open_object_stream ()
 *     _cairo_pdf_surface_close_object_stream ()
 *
 *  An Object Stream contains one or more objects compressed into a stream.
 *  Only non stream objects are permitted. When emitting objects intended for
 *  the Object Stream, enclose the emit object operation with
 *  _cairo_pdf_surface_object_begin()/_cairo_pdf_surface_object_end().
 */


/**
 * SECTION:cairo-pdf
 * @Title: PDF Surfaces
 * @Short_Description: Rendering PDF documents
 * @See_Also: #cairo_surface_t
 *
 * The PDF surface is used to render cairo graphics to Adobe
 * PDF files and is a multi-page vector surface backend.
 *
 * The following mime types are supported on source patterns:
 * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_JP2,
 * %CAIRO_MIME_TYPE_UNIQUE_ID, %CAIRO_MIME_TYPE_JBIG2,
 * %CAIRO_MIME_TYPE_JBIG2_GLOBAL, %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID,
 * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS.
 *
 * # JBIG2 Images #
 * JBIG2 data in PDF must be in the embedded format as described in
 * ISO/IEC 11544. Image specific JBIG2 data must be in
 * %CAIRO_MIME_TYPE_JBIG2.  Any global segments in the JBIG2 data
 * (segments with page association field set to 0) must be in
 * %CAIRO_MIME_TYPE_JBIG2_GLOBAL. The global data may be shared by
 * multiple images. All images sharing the same global data must set
 * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID to a unique identifier. At least
 * one of the images must provide the global data using
 * %CAIRO_MIME_TYPE_JBIG2_GLOBAL. The global data will only be
 * embedded once and shared by all JBIG2 images with the same
 * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID.
 *
 * # CCITT Fax Images # {#ccitt}
 * The %CAIRO_MIME_TYPE_CCITT_FAX mime data requires a number of decoding
 * parameters These parameters are specified using %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS.
 *
 * %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS mime data must contain a string of the form
 * "param1=value1 param2=value2 ...".
 *
 * @Columns: [required] An integer specifying the width of the image in pixels.
 *
 * @Rows: [required] An integer specifying the height of the image in scan lines.
 *
 * @K: [optional] An integer identifying the encoding scheme used. < 0
 * is 2 dimensional Group 4, = 0 is Group3 1 dimensional, > 0 is mixed 1
 * and 2 dimensional encoding. Default is 0.
 *
 * @EndOfLine: [optional] If true end-of-line bit patterns are present. Default is false.
 *
 * @EncodedByteAlign: [optional] If true the end of line is padded
 * with 0 bits so the next line begins on a byte boundary. Default is false.
 *
 * @EndOfBlock: [optional] If true the data contains an end-of-block pattern. Default is true.
 *
 * @BlackIs1: [optional] If true 1 bits are black pixels. Default is false.
 *
 * @DamagedRowsBeforeError: [optional] An integer specifying the
 * number of damages rows tolerated before an error occurs. Default is 0.
 *
 * Boolean values may be "true" or "false", or 1 or 0.
 *
 * These parameters are the same as the CCITTFaxDecode parameters in the
 * [PostScript Language Reference](https://www.adobe.com/products/postscript/pdfs/PLRM.pdf)
 * and [Portable Document Format (PDF)](https://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf).
 * Refer to these documents for further details.
 *
 * An example %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS string is:
 *
 * <programlisting>
 * "Columns=10230 Rows=40000 K=1 EndOfLine=true EncodedByteAlign=1 BlackIs1=false"
 * </programlisting>
 *
 **/


static cairo_bool_t
_cairo_pdf_surface_get_extents (void          *abstract_surface,
    cairo_rectangle_int_t   *rectangle);

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


static const cairo_pdf_version_t _cairo_pdf_versions[] =
{
    CAIRO_PDF_VERSION_1_4,
    CAIRO_PDF_VERSION_1_5,
    CAIRO_PDF_VERSION_1_6,
    CAIRO_PDF_VERSION_1_7
};

#define CAIRO_PDF_VERSION_LAST ARRAY_LENGTH (_cairo_pdf_versions)

static const char * _cairo_pdf_version_strings[CAIRO_PDF_VERSION_LAST] =
{
    "PDF 1.4",
    "PDF 1.5",
    "PDF 1.6",
    "PDF 1.7"
};

static const char *_cairo_pdf_supported_mime_types[] =
{
    CAIRO_MIME_TYPE_JPEG,
    CAIRO_MIME_TYPE_JP2,
    CAIRO_MIME_TYPE_UNIQUE_ID,
    CAIRO_MIME_TYPE_JBIG2,
    CAIRO_MIME_TYPE_JBIG2_GLOBAL,
    CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID,
    CAIRO_MIME_TYPE_CCITT_FAX,
    CAIRO_MIME_TYPE_CCITT_FAX_PARAMS,
    NULL
};

/* PDF cross-reference stream types */
typedef enum {
    PDF_OBJECT_FREE = 0,
    PDF_OBJECT_UNCOMPRESSED = 1,
    PDF_OBJECT_COMPRESSED = 2,
} cairo_pdf_object_type_t;

typedef struct _cairo_pdf_object {
    cairo_pdf_object_type_t type;
    union {
 long long offset; /* type == PDF_OBJECT_UNCOMPRESSED */
 struct compressed_obj {  /* type == PDF_OBJECT_COMPRESSED */
     cairo_pdf_resource_t xref_stream;
     int index;
 } compressed_obj;
    } u;
} cairo_pdf_object_t;

typedef struct _cairo_xref_stream_object {
    cairo_pdf_resource_t resource;
    long long offset;
} cairo_xref_stream_object_t;

typedef struct _cairo_pdf_font {
    unsigned int font_id;
    unsigned int subset_id;
    cairo_pdf_resource_t subset_resource;
} cairo_pdf_font_t;

typedef struct _cairo_pdf_rgb_linear_function {
    cairo_pdf_resource_t resource;
    double               color1[3];
    double               color2[3];
} cairo_pdf_rgb_linear_function_t;

typedef struct _cairo_pdf_alpha_linear_function {
    cairo_pdf_resource_t resource;
    double               alpha1;
    double               alpha2;
} cairo_pdf_alpha_linear_function_t;

static void
_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface,
     cairo_bool_t         clear_doc_surfaces);

static void
_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group);

static cairo_int_status_t
_cairo_pdf_surface_add_font (unsigned int        font_id,
        unsigned int        subset_id,
        void  *closure);

static void
_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res);

static cairo_int_status_t
_cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface,
    cairo_pdf_resource_t    *resource,
                                cairo_bool_t             compressed,
    const char  *fmt,
    ...) CAIRO_PRINTF_FORMAT(4, 5);
static cairo_int_status_t
_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface);

static cairo_int_status_t
_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t        *surface,
     cairo_pdf_source_surface_t *source,
     cairo_bool_t                test,
     cairo_bool_t               *is_image);

static cairo_int_status_t
_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface);

static cairo_int_status_t
_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface);

static cairo_int_status_t
_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface,
      cairo_pdf_resource_t catalog);

static long long
_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface);

static cairo_int_status_t
_cairo_pdf_surface_write_xref_stream (cairo_pdf_surface_t  *surface,
          cairo_pdf_resource_t  xref_res,
          cairo_pdf_resource_t  root_res,
          cairo_pdf_resource_t  info_res,
          long long            *xref_offset);

static cairo_int_status_t
_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface,
          cairo_bool_t         finish);

static cairo_int_status_t
_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface);

static cairo_bool_t
_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b);

static cairo_bool_t
_cairo_pdf_color_glyph_equal (const void *key_a, const void *key_b);

static const cairo_surface_backend_t cairo_pdf_surface_backend;
static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend;

cairo_pdf_resource_t
_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface)
{
    cairo_pdf_resource_t resource;
    cairo_int_status_t status;
    cairo_pdf_object_t object;

    /* Default to Uncompressed. If this object is used with
     * _cairo_pdf_surface_object_begin() and Object Streams are
     * enabled it will be changed to Compressed. */

    object.type = PDF_OBJECT_UNCOMPRESSED;
    object.u.offset = _cairo_output_stream_get_position (surface->output);

    status = _cairo_array_append (&surface->objects, &object);
    if (unlikely (status)) {
 resource.id = 0;
 return resource;
    }

    resource = surface->next_available_resource;
    surface->next_available_resource.id++;

    return resource;
}

void
_cairo_pdf_surface_update_object (cairo_pdf_surface_t *surface,
      cairo_pdf_resource_t  resource)
{
    cairo_pdf_object_t *object;

    object = _cairo_array_index (&surface->objects, resource.id - 1);
    object->u.offset = _cairo_output_stream_get_position (surface->output);
}

static void
_cairo_pdf_surface_set_size_internal (cairo_pdf_surface_t *surface,
          double    width,
          double    height)
{
    surface->width = width;
    surface->height = height;
    surface->surface_extents.x = 0;
    surface->surface_extents.y = 0;
    surface->surface_extents.width  = ceil (surface->width);
    surface->surface_extents.height = ceil (surface->height);
}

static cairo_bool_t
_path_covers_bbox (cairo_pdf_surface_t *surface,
     cairo_path_fixed_t *path)
{
    cairo_box_t box;

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

static cairo_status_t
_cairo_pdf_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_pdf_surface_t *surface = cairo_container_of (clipper,
             cairo_pdf_surface_t,
             clipper);
    cairo_int_status_t status;

    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
    if (unlikely (status))
 return status;

    if (path == NULL) {
 _cairo_output_stream_printf (surface->output, "Q q\n");

 surface->current_pattern_is_solid_color = FALSE;
 surface->current_operator = CAIRO_OPERATOR_OVER;
 _cairo_pdf_operators_reset (&surface->pdf_operators);

 return CAIRO_STATUS_SUCCESS;
    }

    if (_path_covers_bbox (surface, path))
 return CAIRO_STATUS_SUCCESS;

    return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule);
}

static cairo_surface_t *
_cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output,
            double    width,
            double    height)
{
    cairo_pdf_surface_t *surface;
    cairo_status_t status, status_ignored;

    surface = _cairo_malloc (sizeof (cairo_pdf_surface_t));
    if (unlikely (surface == NULL)) {
 /* destroy stream on behalf of caller */
 status = _cairo_output_stream_destroy (output);
 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
    }

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

    surface->output = output;
    surface->width = width;
    surface->height = height;
    cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, 1, 0, 0);
    surface->in_xobject = FALSE;
    surface->surface_extents.x = 0;
    surface->surface_extents.y = 0;
    surface->surface_extents.width  = ceil (surface->width);
    surface->surface_extents.height = ceil (surface->height);
    surface->surface_bounded = TRUE;

    _cairo_array_init (&surface->objects, sizeof (cairo_pdf_object_t));
    _cairo_array_init (&surface->pages, sizeof (cairo_pdf_page_info_t));
    _cairo_array_init (&surface->rgb_linear_functions, sizeof (cairo_pdf_rgb_linear_function_t));
    _cairo_array_init (&surface->alpha_linear_functions, sizeof (cairo_pdf_alpha_linear_function_t));
    _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t));
    _cairo_array_init (&surface->smask_groups, sizeof (cairo_pdf_smask_group_t *));
    _cairo_array_init (&surface->knockout_group, sizeof (cairo_pdf_resource_t));

    _cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t));
    _cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t));
    _cairo_array_init (&surface->doc_surfaces, sizeof (cairo_pdf_source_surface_t));
    _cairo_array_init (&surface->jbig2_global, sizeof (cairo_pdf_jbig2_global_t));
    surface->all_surfaces = _cairo_hash_table_create (_cairo_pdf_source_surface_equal);
    if (unlikely (surface->all_surfaces == NULL)) {
 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 goto BAIL0;
    }

    surface->color_glyphs = _cairo_hash_table_create (_cairo_pdf_color_glyph_equal);
    if (unlikely (surface->color_glyphs == NULL)) {
 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 goto BAIL1;
    }

    surface->duplicate_surface_number = 0;

    _cairo_pdf_group_resources_init (&surface->resources);

    surface->font_subsets = _cairo_scaled_font_subsets_create_composite ();
    if (! surface->font_subsets) {
 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 goto BAIL2;
    }

    _cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE);

    surface->next_available_resource.id = 1;
    surface->pages_resource = _cairo_pdf_surface_new_object (surface);
    if (surface->pages_resource.id == 0) {
 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
        goto BAIL3;
    }

    surface->struct_tree_root.id = 0;
    surface->pdf_version = CAIRO_PDF_VERSION_1_7;
    surface->compress_streams = TRUE;
    surface->pdf_stream.active = FALSE;
    surface->pdf_stream.old_output = NULL;
    surface->group_stream.active = FALSE;
    surface->group_stream.stream = NULL;
    surface->group_stream.mem_stream = NULL;
    surface->object_stream.active = FALSE;
    surface->object_stream.stream = NULL;
    _cairo_array_init (&surface->object_stream.objects, sizeof (cairo_xref_stream_object_t));

    surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
    surface->type3_replay = FALSE;

    surface->force_fallbacks = FALSE;
    surface->select_pattern_gstate_saved = FALSE;
    surface->current_pattern_is_solid_color = FALSE;
    surface->current_operator = CAIRO_OPERATOR_OVER;
    surface->reset_gs_required = FALSE;
    surface->header_emitted = FALSE;

    _cairo_surface_clipper_init (&surface->clipper,
     _cairo_pdf_surface_clipper_intersect_clip_path);

    _cairo_pdf_operators_init (&surface->pdf_operators,
          surface->output,
          &surface->cairo_to_pdf,
          surface->font_subsets,
          FALSE);
    _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators,
          _cairo_pdf_surface_add_font,
          surface);
    _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, TRUE);

    status = _cairo_pdf_interchange_init (surface);
    if (unlikely (status))
 goto BAIL3;

    surface->page_parent_tree = -1;
    _cairo_array_init (&surface->page_annots, sizeof (cairo_pdf_resource_t));
    surface->tagged = FALSE;
    surface->current_page_label = NULL;
    _cairo_array_init (&surface->page_labels, sizeof (char *));
    surface->outlines_dict_res.id = 0;
    surface->names_dict_res.id = 0;
    surface->docinfo_res.id = 0;
    surface->page_labels_res.id = 0;
    surface->thumbnail_width = 0;
    surface->thumbnail_height = 0;
    surface->thumbnail_image = NULL;

    surface->debug = FALSE;
    if (getenv ("CAIRO_DEBUG_PDF") != NULL) {
 surface->debug = TRUE;
 surface->compress_streams = FALSE;
    }

    surface->paginated_surface =  _cairo_paginated_surface_create (
                                   &surface->base,
       CAIRO_CONTENT_COLOR_ALPHA,
       &cairo_pdf_surface_paginated_backend);

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

BAIL3:
    _cairo_scaled_font_subsets_destroy (surface->font_subsets);
BAIL2:
    _cairo_hash_table_destroy (surface->color_glyphs);
BAIL1:
    _cairo_hash_table_destroy (surface->all_surfaces);
BAIL0:
    _cairo_array_fini (&surface->objects);
    free (surface);

    /* destroy stream on behalf of caller */
    status_ignored = _cairo_output_stream_destroy (output);

    return _cairo_surface_create_in_error (status);
}

/**
 * cairo_pdf_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 PDF 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_pdf_surface_create_for_stream (cairo_write_func_t   write_func,
         void   *closure,
         double    width_in_points,
         double    height_in_points)
{
    cairo_output_stream_t *output;

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

    return _cairo_pdf_surface_create_for_stream_internal (output,
         width_in_points,
         height_in_points);
}

/**
 * cairo_pdf_surface_create:
 * @filename: a filename for the PDF output (must be writable), %NULL may be
 *            used to specify no output. This will generate a PDF 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 PDF surface of the specified size in points to be written
 * to @filename.
 *
 * 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_pdf_surface_create (const char  *filename,
     double   width_in_points,
     double   height_in_points)
{
    cairo_output_stream_t *output;

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

    return _cairo_pdf_surface_create_for_stream_internal (output,
         width_in_points,
         height_in_points);
}

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

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

static cairo_bool_t
_extract_pdf_surface (cairo_surface_t   *surface,
        cairo_pdf_surface_t **pdf_surface)
{
    cairo_surface_t *target;
    cairo_status_t status_ignored;

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

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

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

    if (! _cairo_surface_is_pdf (target)) {
 status_ignored = _cairo_surface_set_error (surface,
         _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 return FALSE;
    }

    *pdf_surface = (cairo_pdf_surface_t *) target;
    return TRUE;
}

/**
 * cairo_pdf_surface_restrict_to_version:
 * @surface: a PDF #cairo_surface_t
 * @version: PDF version
 *
 * Restricts the generated PDF file to @version. See cairo_pdf_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.10
 **/

void
cairo_pdf_surface_restrict_to_version (cairo_surface_t   *abstract_surface,
           cairo_pdf_version_t    version)
{
    cairo_pdf_surface_t *surface = NULL; /* hide compiler warning */

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

    if (version < CAIRO_PDF_VERSION_LAST)
 surface->pdf_version = version;

    _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators,
         version >= CAIRO_PDF_VERSION_1_5);
}

/**
 * cairo_pdf_get_versions:
 * @versions: supported version list
 * @num_versions: list length
 *
 * Used to retrieve the list of supported versions. See
 * cairo_pdf_surface_restrict_to_version().
 *
 * Since: 1.10
 **/

void
cairo_pdf_get_versions (cairo_pdf_version_t const **versions,
                        int                       *num_versions)
{
    if (versions != NULL)
 *versions = _cairo_pdf_versions;

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

/**
 * cairo_pdf_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_pdf_get_versions()
 * for a way to get the list of valid version ids.
 *
 * Return value: the string associated to given version.
 *
 * Since: 1.10
 **/

const char *
cairo_pdf_version_to_string (cairo_pdf_version_t version)
{
    if (version < 0 || version >= CAIRO_PDF_VERSION_LAST)
 return NULL;

    return _cairo_pdf_version_strings[version];
}

/**
 * cairo_pdf_surface_set_size:
 * @surface: a PDF #cairo_surface_t
 * @width_in_points: new surface width, in points (1 point == 1/72.0 inch)
 * @height_in_points: new surface height, in points (1 point == 1/72.0 inch)
 *
 * Changes the size of a PDF surface for the current (and
 * subsequent) pages.
 *
 * This function should only be called before any drawing operations
 * have been performed on the current page. The simplest way to do
 * this is to call this function immediately after creating the
 * surface or immediately after completing a page with either
 * cairo_show_page() or cairo_copy_page().
 *
 * Since: 1.2
 **/

void
cairo_pdf_surface_set_size (cairo_surface_t *surface,
       double   width_in_points,
       double   height_in_points)
{
    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
    cairo_status_t status;

    if (! _extract_pdf_surface (surface, &pdf_surface))
 return;

    _cairo_pdf_surface_set_size_internal (pdf_surface,
       width_in_points,
       height_in_points);
    status = _cairo_paginated_surface_set_size (pdf_surface->paginated_surface,
      width_in_points,
      height_in_points);
    if (status)
 status = _cairo_surface_set_error (surface, status);
}

/**
 * CAIRO_PDF_OUTLINE_ROOT:
 *
 * The root outline item in cairo_pdf_surface_add_outline().
 *
 * Since: 1.16
 **/


/**
 * cairo_pdf_surface_add_outline:
 * @surface: a PDF #cairo_surface_t
 * @parent_id: the id of the parent item or %CAIRO_PDF_OUTLINE_ROOT if this is a top level item.
 * @utf8: the name of the outline
 * @link_attribs: the link attributes specifying where this outline links to
 * @flags: outline item flags
 *
 * Add an item to the document outline hierarchy with the name @utf8
 * that links to the location specified by @link_attribs. Link
 * attributes have the same keys and values as the [Link Tag][link],
 * excluding the "rect" attribute. The item will be a child of the
 * item with id @parent_id. Use %CAIRO_PDF_OUTLINE_ROOT as the parent
 * id of top level items.
 *
 * Return value: the id for the added item.
 *
 * Since: 1.16
 **/

int
cairo_pdf_surface_add_outline (cairo_surface_t          *surface,
          int                        parent_id,
          const char                *utf8,
          const char                *link_attribs,
          cairo_pdf_outline_flags_t  flags)
{
    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
    cairo_status_t status;
    int id = 0;

    if (! _extract_pdf_surface (surface, &pdf_surface))
 return 0;

    status = _cairo_pdf_interchange_add_outline (pdf_surface,
       parent_id,
       utf8,
       link_attribs,
       flags,
       &id);
    if (status)
 status = _cairo_surface_set_error (surface, status);

    return id;
}

/**
 * cairo_pdf_surface_set_metadata:
 * @surface: a PDF #cairo_surface_t
 * @metadata: The metadata item to set.
 * @utf8: metadata value
 *
 * Set document metadata. The %CAIRO_PDF_METADATA_CREATE_DATE and
 * %CAIRO_PDF_METADATA_MOD_DATE values must be in ISO-8601 format:
 * YYYY-MM-DDThh:mm:ss. An optional timezone of the form "[+/-]hh:mm"
 * or "Z" for UTC time can be appended. All other metadata values can be any UTF-8
 * string.
 *
 * For example:
 * <informalexample><programlisting>
 * cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_TITLE, "My Document");
 * cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_CREATE_DATE, "2015-12-31T23:59+02:00");
 * </programlisting></informalexample>
 *
 * Since: 1.16
 **/

void
cairo_pdf_surface_set_metadata (cairo_surface_t      *surface,
    cairo_pdf_metadata_t  metadata,
                                const char           *utf8)
{
    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
    cairo_status_t status;

    if (! _extract_pdf_surface (surface, &pdf_surface))
 return;

    status = _cairo_pdf_interchange_set_metadata (pdf_surface, metadata, utf8);
    if (status)
 status = _cairo_surface_set_error (surface, status);
}

/**
 * cairo_pdf_surface_set_custom_metadata:
 * @surface: a PDF #cairo_surface_t
 * @name: The name of the custom metadata item to set (utf8).
 * @value: The value of the metadata (utf8).
 *
 * Set custom document metadata. @name may be any string except for
 * the following names reserved by PDF: "Title", "Author", "Subject",
 * "Keywords", "Creator", "Producer", "CreationDate", "ModDate",
 * "Trapped".
 *
 * If @value is NULL or an empty string, the @name metadata will not be set.
 *
 * For example:
 * <informalexample><programlisting>
 * cairo_pdf_surface_set_custom_metadata (surface, "ISBN", "978-0123456789");
 * </programlisting></informalexample>
 *
 * Since: 1.18
 **/

void
cairo_pdf_surface_set_custom_metadata (cairo_surface_t     *surface,
                                       const char           *name,
                                       const char           *value)
{
    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
    cairo_status_t status;

    if (! _extract_pdf_surface (surface, &pdf_surface))
 return;

    status = _cairo_pdf_interchange_set_custom_metadata (pdf_surface, name, value);
    if (status)
 status = _cairo_surface_set_error (surface, status);
}

/**
 * cairo_pdf_surface_set_page_label:
 * @surface: a PDF #cairo_surface_t
 * @utf8: The page label.
 *
 * Set page label for the current page.
 *
 * Since: 1.16
 **/

void
cairo_pdf_surface_set_page_label (cairo_surface_t *surface,
                                  const char      *utf8)
{
    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */

    if (! _extract_pdf_surface (surface, &pdf_surface))
 return;

    free (pdf_surface->current_page_label);
    pdf_surface->current_page_label = utf8 ? strdup (utf8) : NULL;
}

/**
 * cairo_pdf_surface_set_thumbnail_size:
 * @surface: a PDF #cairo_surface_t
 * @width: Thumbnail width.
 * @height: Thumbnail height
 *
 * Set the thumbnail image size for the current and all subsequent
 * pages. Setting a width or height of 0 disables thumbnails for the
 * current and subsequent pages.
 *
 * Since: 1.16
 **/

void
cairo_pdf_surface_set_thumbnail_size (cairo_surface_t *surface,
          int              width,
          int              height)
{
    cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */

    if (! _extract_pdf_surface (surface, &pdf_surface))
 return;

    pdf_surface->thumbnail_width = width;
    pdf_surface->thumbnail_height = height;
}

static void
_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface,
     cairo_bool_t         clear_doc_surfaces)
{
    int i, size;
    cairo_pdf_pattern_t *pattern;
    cairo_pdf_source_surface_t *src_surface;
    cairo_pdf_smask_group_t *group;
    cairo_pdf_source_surface_t doc_surface;

    size = _cairo_array_num_elements (&surface->page_patterns);
    for (i = 0; i < size; i++) {
 pattern = (cairo_pdf_pattern_t *) _cairo_array_index (&surface->page_patterns, i);
 cairo_pattern_destroy (pattern->pattern);
    }
    _cairo_array_truncate (&surface->page_patterns, 0);

    size = _cairo_array_num_elements (&surface->page_surfaces);
    for (i = 0; i < size; i++) {
 src_surface = (cairo_pdf_source_surface_t *) _cairo_array_index (&surface->page_surfaces, i);
 if (src_surface->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
     cairo_pattern_destroy (src_surface->raster_pattern);
 } else {
     if (_cairo_surface_is_recording (src_surface->surface) && src_surface->region_id != 0)
  _cairo_recording_surface_region_array_remove (src_surface->surface, src_surface->region_id);
     cairo_surface_destroy (src_surface->surface);
 }
    }
    _cairo_array_truncate (&surface->page_surfaces, 0);

    size = _cairo_array_num_elements (&surface->smask_groups);
    for (i = 0; i < size; i++) {
 _cairo_array_copy_element (&surface->smask_groups, i, &group);
 _cairo_pdf_smask_group_destroy (group);
    }
    _cairo_array_truncate (&surface->smask_groups, 0);
    _cairo_array_truncate (&surface->knockout_group, 0);
    _cairo_array_truncate (&surface->page_annots, 0);

    if (surface->thumbnail_image)
 cairo_surface_destroy (&surface->thumbnail_image->base);
    surface->thumbnail_image = NULL;

    if (clear_doc_surfaces) {
 size = _cairo_array_num_elements (&surface->doc_surfaces);
 for (i = 0; i < size; i++) {
     _cairo_array_copy_element (&surface->doc_surfaces, i, &doc_surface);
     if (doc_surface.type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
  cairo_pattern_destroy (doc_surface.raster_pattern);
     } else {
  if (_cairo_surface_is_recording (doc_surface.surface) && doc_surface.region_id != 0)
      _cairo_recording_surface_region_array_remove (doc_surface.surface, doc_surface.region_id);
  cairo_surface_destroy (doc_surface.surface);
     }
 }
 _cairo_array_truncate (&surface->doc_surfaces, 0);
    }
}

static void
_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res)
{
    int i;

    for (i = 0; i < CAIRO_NUM_OPERATORS; i++)
 res->operators[i] = FALSE;

    _cairo_array_init (&res->alphas, sizeof (double));
    _cairo_array_init (&res->smasks, sizeof (cairo_pdf_resource_t));
    _cairo_array_init (&res->patterns, sizeof (cairo_pdf_resource_t));
    _cairo_array_init (&res->shadings, sizeof (cairo_pdf_resource_t));
    _cairo_array_init (&res->xobjects, sizeof (cairo_pdf_resource_t));
    _cairo_array_init (&res->fonts, sizeof (cairo_pdf_font_t));
}

static void
_cairo_pdf_group_resources_fini (cairo_pdf_group_resources_t *res)
{
    _cairo_array_fini (&res->alphas);
    _cairo_array_fini (&res->smasks);
    _cairo_array_fini (&res->patterns);
    _cairo_array_fini (&res->shadings);
    _cairo_array_fini (&res->xobjects);
    _cairo_array_fini (&res->fonts);
}

static void
_cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res)
{
    int i;

    for (i = 0; i < CAIRO_NUM_OPERATORS; i++)
 res->operators[i] = FALSE;

    _cairo_array_truncate (&res->alphas, 0);
    _cairo_array_truncate (&res->smasks, 0);
    _cairo_array_truncate (&res->patterns, 0);
    _cairo_array_truncate (&res->shadings, 0);
    _cairo_array_truncate (&res->xobjects, 0);
    _cairo_array_truncate (&res->fonts, 0);
}

static void
_cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface,
     cairo_operator_t     op)
{
    cairo_pdf_group_resources_t *res = &surface->resources;

    res->operators[op] = TRUE;
}

static cairo_int_status_t
_cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface,
         double               alpha,
         int                 *index)
{
    int num_alphas, i;
    double other;
    cairo_int_status_t status;
    cairo_pdf_group_resources_t *res = &surface->resources;

    num_alphas = _cairo_array_num_elements (&res->alphas);
    for (i = 0; i < num_alphas; i++) {
 _cairo_array_copy_element (&res->alphas, i, &other);
 if (alpha == other) {
     *index = i;
     return CAIRO_STATUS_SUCCESS;
 }
    }

    status = _cairo_array_append (&res->alphas, &alpha);
    if (unlikely (status))
 return status;

    *index = _cairo_array_num_elements (&res->alphas) - 1;

    return CAIRO_STATUS_SUCCESS;
}

static cairo_int_status_t
_cairo_pdf_surface_add_smask (cairo_pdf_surface_t  *surface,
         cairo_pdf_resource_t  smask)
{
    return _cairo_array_append (&(surface->resources.smasks), &smask);
}

static cairo_int_status_t
_cairo_pdf_surface_add_pattern (cairo_pdf_surface_t  *surface,
    cairo_pdf_resource_t  pattern)
{
    return _cairo_array_append (&(surface->resources.patterns), &pattern);
}

static cairo_int_status_t
_cairo_pdf_surface_add_shading (cairo_pdf_surface_t  *surface,
    cairo_pdf_resource_t  shading)
{
    return _cairo_array_append (&(surface->resources.shadings), &shading);
}


static cairo_int_status_t
_cairo_pdf_surface_add_xobject (cairo_pdf_surface_t  *surface,
    cairo_pdf_resource_t  xobject)
{
    return _cairo_array_append (&(surface->resources.xobjects), &xobject);
}

static cairo_int_status_t
_cairo_pdf_surface_add_font (unsigned int        font_id,
        unsigned int        subset_id,
        void  *closure)
{
    cairo_pdf_surface_t *surface = closure;
    cairo_pdf_font_t font;
    int num_fonts, i;
    cairo_int_status_t status;
    cairo_pdf_group_resources_t *res = &surface->resources;

    num_fonts = _cairo_array_num_elements (&res->fonts);
    for (i = 0; i < num_fonts; i++) {
 _cairo_array_copy_element (&res->fonts, i, &font);
 if (font.font_id == font_id &&
     font.subset_id == subset_id)
     return CAIRO_STATUS_SUCCESS;
    }

    num_fonts = _cairo_array_num_elements (&surface->fonts);
    for (i = 0; i < num_fonts; i++) {
 _cairo_array_copy_element (&surface->fonts, i, &font);
 if (font.font_id == font_id &&
     font.subset_id == subset_id)
     return _cairo_array_append (&res->fonts, &font);
    }

    font.font_id = font_id;
    font.subset_id = subset_id;
    font.subset_resource = _cairo_pdf_surface_new_object (surface);
    if (font.subset_resource.id == 0)
 return _cairo_error (CAIRO_STATUS_NO_MEMORY);

    status = _cairo_array_append (&surface->fonts, &font);
    if (unlikely (status))
 return status;

    return _cairo_array_append (&res->fonts, &font);
}

static cairo_pdf_resource_t
_cairo_pdf_surface_get_font_resource (cairo_pdf_surface_t *surface,
          unsigned int         font_id,
          unsigned int         subset_id)
{
    cairo_pdf_font_t font;
    int num_fonts, i;

    num_fonts = _cairo_array_num_elements (&surface->fonts);
    for (i = 0; i < num_fonts; i++) {
 _cairo_array_copy_element (&surface->fonts, i, &font);
 if (font.font_id == font_id && font.subset_id == subset_id)
     return font.subset_resource;
    }

    font.subset_resource.id = 0;
    return font.subset_resource;
}

static const char *
_cairo_operator_to_pdf_blend_mode (cairo_operator_t op)
{
    switch (op) {
    /* The extend blend mode operators */
    case CAIRO_OPERATOR_MULTIPLY:       return "Multiply";
    case CAIRO_OPERATOR_SCREEN:         return "Screen";
    case CAIRO_OPERATOR_OVERLAY:        return "Overlay";
    case CAIRO_OPERATOR_DARKEN:         return "Darken";
    case CAIRO_OPERATOR_LIGHTEN:        return "Lighten";
    case CAIRO_OPERATOR_COLOR_DODGE:    return "ColorDodge";
    case CAIRO_OPERATOR_COLOR_BURN:     return "ColorBurn";
    case CAIRO_OPERATOR_HARD_LIGHT:     return "HardLight";
    case CAIRO_OPERATOR_SOFT_LIGHT:     return "SoftLight";
    case CAIRO_OPERATOR_DIFFERENCE:     return "Difference";
    case CAIRO_OPERATOR_EXCLUSION:      return "Exclusion";
    case CAIRO_OPERATOR_HSL_HUE:        return "Hue";
    case CAIRO_OPERATOR_HSL_SATURATION: return "Saturation";
    case CAIRO_OPERATOR_HSL_COLOR:      return "Color";
    case CAIRO_OPERATOR_HSL_LUMINOSITY: return "Luminosity";

    default:
    /* The original Porter-Duff set */
    case CAIRO_OPERATOR_CLEAR:
    case CAIRO_OPERATOR_SOURCE:
    case CAIRO_OPERATOR_OVER:
    case CAIRO_OPERATOR_IN:
    case CAIRO_OPERATOR_OUT:
    case CAIRO_OPERATOR_ATOP:
    case CAIRO_OPERATOR_DEST:
    case CAIRO_OPERATOR_DEST_OVER:
    case CAIRO_OPERATOR_DEST_IN:
    case CAIRO_OPERATOR_DEST_OUT:
    case CAIRO_OPERATOR_DEST_ATOP:
    case CAIRO_OPERATOR_XOR:
    case CAIRO_OPERATOR_ADD:
    case CAIRO_OPERATOR_SATURATE:
 return "Normal";
    }
}

static void
_cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t         *surface,
      cairo_pdf_group_resources_t *res,
      cairo_bool_t                 gs0)
{
    int num_alphas, num_smasks, num_resources, i;
    double alpha;
    cairo_pdf_resource_t *smask, *pattern, *shading, *xobject;
    cairo_pdf_font_t *font;

    _cairo_output_stream_printf (surface->output, "<<\n");

    num_alphas = _cairo_array_num_elements (&res->alphas);
    num_smasks = _cairo_array_num_elements (&res->smasks);
    if (num_alphas > 0 || num_smasks > 0) {
 _cairo_output_stream_printf (surface->output,
         " /ExtGState <<\n");

 if (gs0) {
     _cairo_output_stream_printf (surface->output,
      " /gs0 << /BM /Normal /SMask /None /CA 1.0 /ca 1.0 >>\n");
 }

 for (i = 0; i < CAIRO_NUM_OPERATORS; i++) {
     if (res->operators[i]) {
  _cairo_output_stream_printf (surface->output,
          " /b%d << /BM /%s >>\n",
          i, _cairo_operator_to_pdf_blend_mode(i));
     }
 }

 for (i = 0; i < num_alphas; i++) {
     _cairo_array_copy_element (&res->alphas, i, &alpha);
     _cairo_output_stream_printf (surface->output,
      " /a%d << /CA %f /ca %f >>\n",
      i, alpha, alpha);
 }

 for (i = 0; i < num_smasks; i++) {
     smask = _cairo_array_index (&res->smasks, i);
     _cairo_output_stream_printf (surface->output,
      " /s%d %d 0 R\n",
      smask->id, smask->id);
 }

 _cairo_output_stream_printf (surface->output,
         " >>\n");
    }

    num_resources = _cairo_array_num_elements (&res->patterns);
    if (num_resources > 0) {
 _cairo_output_stream_printf (surface->output,
         " /Pattern <<");
 for (i = 0; i < num_resources; i++) {
     pattern = _cairo_array_index (&res->patterns, i);
     _cairo_output_stream_printf (surface->output,
      " /p%d %d 0 R",
      pattern->id, pattern->id);
 }

 _cairo_output_stream_printf (surface->output,
         " >>\n");
    }

    num_resources = _cairo_array_num_elements (&res->shadings);
    if (num_resources > 0) {
 _cairo_output_stream_printf (surface->output,
         " /Shading <<");
 for (i = 0; i < num_resources; i++) {
     shading = _cairo_array_index (&res->shadings, i);
     _cairo_output_stream_printf (surface->output,
      " /sh%d %d 0 R",
      shading->id, shading->id);
 }

 _cairo_output_stream_printf (surface->output,
         " >>\n");
    }

    num_resources = _cairo_array_num_elements (&res->xobjects);
    if (num_resources > 0) {
 _cairo_output_stream_printf (surface->output,
         " /XObject <<");

 for (i = 0; i < num_resources; i++) {
     xobject = _cairo_array_index (&res->xobjects, i);
     _cairo_output_stream_printf (surface->output,
      " /x%d %d 0 R",
      xobject->id, xobject->id);
 }

 _cairo_output_stream_printf (surface->output,
         " >>\n");
    }

    num_resources = _cairo_array_num_elements (&res->fonts);
    if (num_resources > 0) {
 _cairo_output_stream_printf (surface->output," /Font <<\n");
 for (i = 0; i < num_resources; i++) {
     font = _cairo_array_index (&res->fonts, i);
     _cairo_output_stream_printf (surface->output,
      " /f-%d-%d %d 0 R\n",
      font->font_id,
      font->subset_id,
      font->subset_resource.id);
 }
 _cairo_output_stream_printf (surface->output, " >>\n");
    }

    _cairo_output_stream_printf (surface->output,
     ">>\n");
}

static cairo_pdf_smask_group_t *
_cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t     *surface,
           const cairo_rectangle_int_t  *extents)
{
    cairo_pdf_smask_group_t *group;

    group = calloc (1, sizeof (cairo_pdf_smask_group_t));
    if (unlikely (group == NULL)) {
 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 return NULL;
    }

    group->group_res = _cairo_pdf_surface_new_object (surface);
    if (group->group_res.id == 0) {
 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 free (group);
 return NULL;
    }
    group->width = surface->width;
    group->height = surface->height;
    if (extents != NULL) {
 group->extents = *extents;
    } else {
 group->extents.x = 0;
 group->extents.y = 0;
 group->extents.width = surface->width;
 group->extents.height = surface->height;
    }

    return group;
}

static void
_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group)
{
    if (group->operation == PDF_FILL || group->operation == PDF_STROKE)
 _cairo_path_fixed_fini (&group->path);
    if (group->source)
 cairo_pattern_destroy (group->source);
    if (group->mask)
 cairo_pattern_destroy (group->mask);
    free (group->utf8);
    free (group->glyphs);
    free (group->clusters);
    if (group->scaled_font)
 cairo_scaled_font_destroy (group->scaled_font);
    free (group);
}

static cairo_int_status_t
_cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t     *surface,
        cairo_pdf_smask_group_t *group)
{
    return _cairo_array_append (&surface->smask_groups, &group);
}

static cairo_bool_t
_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b)
{
    const cairo_pdf_source_surface_entry_t *a = key_a;
    const cairo_pdf_source_surface_entry_t *b = key_b;

    if (a->interpolate != b->interpolate)
 return FALSE;

    if (a->region_id != b->region_id)
 return FALSE;

    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_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key)
{
    if (key->unique_id && key->unique_id_length > 0) {
 key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE,
         key->unique_id, key->unique_id_length);
    } else {
 key->base.hash = key->id;
    }
    key->base.hash = _cairo_hash_bytes (key->base.hash,
     &key->region_id,
     sizeof(key->region_id));
}

static cairo_bool_t
_cairo_pdf_color_glyph_equal (const void *key_a, const void *key_b)
{
    const cairo_pdf_color_glyph_t *a = key_a;
    const cairo_pdf_color_glyph_t *b = key_b;

    if (a->scaled_font != b->scaled_font)
 return FALSE;

    return (a->glyph_index == b->glyph_index);
}

static void
_cairo_pdf_color_glyph_init_key (cairo_pdf_color_glyph_t *key)
{
    key->base.hash = _cairo_hash_uintptr (_CAIRO_HASH_INIT_VALUE, (uintptr_t)key->scaled_font);
    key->base.hash = _cairo_hash_uintptr (key->base.hash, key->glyph_index);
}

static cairo_int_status_t
_cairo_pdf_surface_acquire_source_image_from_pattern (cairo_pdf_surface_t          *surface,
            const cairo_pattern_t        *pattern,
            cairo_image_surface_t       **image,
            void                        **image_extra)
{
    switch (pattern->type) {
    case CAIRO_PATTERN_TYPE_SURFACE: {
 cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern;
 return _cairo_surface_acquire_source_image (surf_pat->surface, image, image_extra);
    } break;

    case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
 cairo_surface_t *surf;
 surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, NULL);
 if (!surf)
     return CAIRO_INT_STATUS_UNSUPPORTED;
 assert (_cairo_surface_is_image (surf));
 *image = (cairo_image_surface_t *) surf;
    } break;

    case CAIRO_PATTERN_TYPE_SOLID:
    case CAIRO_PATTERN_TYPE_LINEAR:
    case CAIRO_PATTERN_TYPE_RADIAL:
    case CAIRO_PATTERN_TYPE_MESH:
    default:
 ASSERT_NOT_REACHED;
 break;
    }

    return CAIRO_STATUS_SUCCESS;
}

static void
_cairo_pdf_surface_release_source_image_from_pattern (cairo_pdf_surface_t          *surface,
            const cairo_pattern_t        *pattern,
            cairo_image_surface_t        *image,
            void                         *image_extra)
{
    switch (pattern->type) {
    case CAIRO_PATTERN_TYPE_SURFACE: {
 cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern;
 _cairo_surface_release_source_image (surf_pat->surface, image, image_extra);
    } break;

    case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
 _cairo_raster_source_pattern_release (pattern, &image->base);
 break;

    case CAIRO_PATTERN_TYPE_SOLID:
    case CAIRO_PATTERN_TYPE_LINEAR:
    case CAIRO_PATTERN_TYPE_RADIAL:
    case CAIRO_PATTERN_TYPE_MESH:
    default:

 ASSERT_NOT_REACHED;
 break;
    }
}

static cairo_int_status_t
_get_source_surface_extents (cairo_surface_t         *source,
        cairo_rectangle_int_t   *extents,
        cairo_bool_t            *bounded,
        cairo_bool_t            *subsurface)
{
    cairo_int_status_t status;

    *bounded = TRUE;
    *subsurface = FALSE;
    if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
 cairo_surface_t *free_me = NULL;

 if (_cairo_surface_is_snapshot (source))
     free_me = source = _cairo_surface_snapshot_get_target (source);

 if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
     cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;

     *extents = sub->extents;
     *subsurface = TRUE;
 } else {
     cairo_box_t box;

     *bounded = _cairo_surface_get_extents (source, extents);
     if (! *bounded) {
  status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)source,
        &box, NULL);
  if (unlikely (status)) {
      cairo_surface_destroy (free_me);
      return status;
  }
  _cairo_box_round_to_rectangle (&box, extents);
     }
 }
 cairo_surface_destroy (free_me);
    } else {
 *bounded =  _cairo_surface_get_extents (source, extents);
    }

    return CAIRO_STATUS_SUCCESS;
}

/**
 * _cairo_pdf_surface_add_source_surface:
 * @surface: [in] the pdf surface
 * @source_surface: [in] A #cairo_surface_t to use as the source surface
 * @source_pattern: [in] A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source
 * @region_id: [in] Surfaces containing tags set this to the recording
 * surface region id. When tags are used in a XObject, PDF requires a
 * separate object for each use (section 14.7.4.2) @region_id is used
 * as a key to ensure a separate object is emitted for each use. Set
 * to 0 for surfaces without tags.
 * @op: [in] the operator used to composite this source
 * @filter: [in] filter type of the source pattern
 * @stencil_mask: [in] if true, the surface will be written to the PDF as an /ImageMask
 * @smask: [in] if true, only the alpha channel will be written (images only)
 * @need_transp_group: [in] if true and an XObject is used, make it a Transparency group
 * @extents: [in] extents of the operation that is using this source
 * @smask_res: [in] if not NULL, the image written will specify this resource as the smask for
 * the image (images only)
 * @pdf_source: [out] return pdf_source_surface entry in hash table
 * @x_offset: [out] if not NULL return x offset of surface
 * @y_offset: [out] if not NULL return y offset of surface
 * @source_extents: [out] if not NULL return operation extents in source space
 *
 * Add surface or raster_source pattern to list of surfaces to be
 * written to the PDF file when the current page is finished. Returns
 * a PDF resource to reference the surface. A hash table of all
 * surfaces in the PDF file (keyed by CAIRO_MIME_TYPE_UNIQUE_ID or
 * surface unique_id) is used to ensure surfaces with the same id are
 * only written once to the PDF file.
 *
 * Only one of @source_pattern or @source_surface is to be
 * specified. Set the other to NULL.
 **/

static cairo_int_status_t
_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t          *surface,
           cairo_surface_t                  *source_surface,
           const cairo_pattern_t          *source_pattern,
           int                                region_id,
           cairo_operator_t                   op,
           cairo_filter_t            filter,
           cairo_bool_t                       stencil_mask,
           cairo_bool_t                       smask,
           cairo_bool_t                       need_transp_group,
           const cairo_rectangle_int_t       *extents,
           cairo_pdf_resource_t          *smask_res,
           cairo_pdf_source_surface_entry_t **pdf_source,
           double                            *x_offset,
           double                            *y_offset,
           cairo_rectangle_int_t             *source_extents)
{
    cairo_pdf_source_surface_t src_surface;
    cairo_pdf_source_surface_entry_t surface_key;
    cairo_pdf_source_surface_entry_t *surface_entry;
    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
    cairo_bool_t interpolate;
    unsigned char *unique_id = NULL;
    unsigned long unique_id_length = 0;
    cairo_image_surface_t *image;
    void *image_extra;
    cairo_box_t box;
    cairo_rectangle_int_t op_extents;
    double x, y;
    cairo_bool_t subsurface;
    cairo_bool_t emit_image;

    switch (filter) {
    default:
    case CAIRO_FILTER_GOOD:
    case CAIRO_FILTER_BEST:
    case CAIRO_FILTER_BILINEAR:
 interpolate = TRUE;
 break;
    case CAIRO_FILTER_FAST:
    case CAIRO_FILTER_NEAREST:
    case CAIRO_FILTER_GAUSSIAN:
 interpolate = FALSE;
 break;
    }

    x = 0;
    y = 0;
    if (source_pattern) {
 if (source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
     status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source_pattern,
            &image, &image_extra);
     if (unlikely (status))
  return status;
     source_surface = &image->base;
     cairo_surface_get_device_offset (source_surface, &x, &y);
 } else {
     cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source_pattern;
     source_surface = surface_pattern->surface;
 }
    }
    if (x_offset)
 *x_offset = x;
    if (y_offset)
 *y_offset = y;

    /* transform operation extents to pattern space */
    op_extents = *extents;
    if (source_pattern)
    {
 _cairo_box_from_rectangle (&box, extents);
 _cairo_matrix_transform_bounding_box_fixed (&source_pattern->matrix, &box, NULL);
 _cairo_box_round_to_rectangle (&box, &op_extents);
    }
    if (source_extents)
 *source_extents = op_extents;

    surface_key.id  = source_surface->unique_id;

    /* Recording surfaces do not use interpolate. Ensure it is always
     * false for recording surfaces. This is because pdf-interchange
     * needs to lookup recording surfaces in the hash table using
     * interpolate = FALSE in the key since it does not know the
     * interpolate value passed to this function.
     */

    emit_image = source_surface->type != CAIRO_SURFACE_TYPE_RECORDING;
    surface_key.interpolate = emit_image ? interpolate : FALSE;

    cairo_surface_get_mime_data (source_surface, CAIRO_MIME_TYPE_UNIQUE_ID,
     (const unsigned char **) &surface_key.unique_id,
     &surface_key.unique_id_length);

    surface_key.region_id = region_id;
    _cairo_pdf_source_surface_init_key (&surface_key);
    surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base);
    if (surface_entry) {
 if (pdf_source)
     *pdf_source = surface_entry;

 if (source_pattern && source_pattern->extend != CAIRO_EXTEND_NONE)
     _cairo_unbounded_rectangle_init (&op_extents);

 _cairo_rectangle_intersect (&op_extents, &surface_entry->extents);
 _cairo_rectangle_union (&surface_entry->required_extents, &op_extents);
 status = CAIRO_STATUS_SUCCESS;
    }

    if (status || surface_entry) {
 if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
     _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra);
 return status;
    }

    if (surface_key.unique_id && surface_key.unique_id_length > 0) {
 unique_id = _cairo_malloc (surface_key.unique_id_length);
 if (unique_id == NULL) {
     status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
     goto fail1;
 }

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

    surface_entry = _cairo_malloc (sizeof (cairo_pdf_source_surface_entry_t));
    if (surface_entry == NULL) {
 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 goto fail1;
    }

    if (pdf_source)
 *pdf_source = surface_entry;

    surface_entry->id = surface_key.id;
    surface_entry->region_id = region_id;
    surface_entry->operator = op;
    surface_entry->interpolate = emit_image ? interpolate : FALSE;
    surface_entry->emit_image = emit_image;
    surface_entry->stencil_mask = stencil_mask;
    surface_entry->smask = smask;
    surface_entry->need_transp_group = need_transp_group;
    surface_entry->unique_id_length = unique_id_length;
    surface_entry->unique_id = unique_id;
    if (smask_res)
 surface_entry->smask_res = *smask_res;
    else
 surface_entry->smask_res.id = 0;

    status = _get_source_surface_extents (source_surface,
       &surface_entry->extents,
       &surface_entry->bounded,
       &subsurface);
    if (unlikely (status))
 goto fail2;

    if (subsurface) {
 *x_offset = -surface_entry->extents.x;
 *y_offset = -surface_entry->extents.y;
    }

    if (source_pattern && source_pattern->extend != CAIRO_EXTEND_NONE)
 _cairo_unbounded_rectangle_init (&op_extents);

    _cairo_rectangle_intersect (&op_extents, &surface_entry->extents);
    surface_entry->required_extents = op_extents;

    _cairo_pdf_source_surface_init_key (surface_entry);

    src_surface.hash_entry = surface_entry;
    src_surface.region_id = 0;
    if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
 src_surface.type = CAIRO_PATTERN_TYPE_RASTER_SOURCE;
 src_surface.surface = NULL;
 status = _cairo_pattern_create_copy (&src_surface.raster_pattern, source_pattern);
 if (unlikely (status))
     goto fail2;

    } else {
 src_surface.type = CAIRO_PATTERN_TYPE_SURFACE;
 src_surface.surface = cairo_surface_reference (source_surface);
 src_surface.raster_pattern = NULL;
 if (source_pattern) {
     cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source_pattern;
     src_surface.region_id = surface_pattern->region_array_id;
     if (_cairo_surface_is_recording (surface_pattern->surface) &&
  surface_pattern->region_array_id != 0)
     {
  _cairo_recording_surface_region_array_reference (surface_pattern->surface,
         surface_pattern->region_array_id);
     }
 }
    }

    surface_entry->surface_res = _cairo_pdf_surface_new_object (surface);
    if (surface_entry->surface_res.id == 0) {
 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 goto fail3;
    }

    if (surface_entry->bounded) {
 status = _cairo_array_append (&surface->page_surfaces, &src_surface);
 if (unlikely (status))
     goto fail3;
    } else {
 status = _cairo_array_append (&surface->doc_surfaces, &src_surface);
 if (unlikely (status))
     goto fail3;
    }

    status = _cairo_hash_table_insert (surface->all_surfaces,
           &surface_entry->base);
    if (unlikely(status))
 goto fail3;

    if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
 _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra);

    return CAIRO_STATUS_SUCCESS;

fail3:
    if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
 cairo_pattern_destroy (src_surface.raster_pattern);
    else
 cairo_surface_destroy (src_surface.surface);

fail2:
    free (surface_entry);

fail1:
    if (unique_id)
 free (unique_id);

    if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
 _cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra);

    return status;
}

static cairo_int_status_t
_cairo_pdf_surface_add_pdf_pattern_or_shading (cairo_pdf_surface_t    *surface,
            const cairo_pattern_t    *pattern,
            cairo_operator_t             op,
            cairo_analysis_source_t      source_type,
            const cairo_rectangle_int_t *extents,
            cairo_bool_t                 is_shading,
            cairo_pdf_resource_t    *pattern_res,
            cairo_pdf_resource_t    *gstate_res)
{
    cairo_pdf_pattern_t pdf_pattern;
    cairo_int_status_t status;
    int region_id = 0;

    pdf_pattern.is_shading = is_shading;
    pdf_pattern.operator = op;

    /* Solid colors are emitted into the content stream */
    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
 pattern_res->id = 0;
 gstate_res->id = 0;
 return CAIRO_INT_STATUS_SUCCESS;
    }

    status = _cairo_pattern_create_copy (&pdf_pattern.pattern, pattern);
    if (unlikely (status))
 return status;

    pdf_pattern.pattern_res = _cairo_pdf_surface_new_object (surface);
    if (pdf_pattern.pattern_res.id == 0) {
 cairo_pattern_destroy (pdf_pattern.pattern);
 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }

    pdf_pattern.gstate_res.id = 0;

    /* gradient patterns require an smask object to implement transparency */
    if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
 pattern->type == CAIRO_PATTERN_TYPE_RADIAL ||
 pattern->type == CAIRO_PATTERN_TYPE_MESH)
    {
 double min_alpha;

 _cairo_pattern_alpha_range (pattern, &min_alpha, NULL);
 if (! CAIRO_ALPHA_IS_OPAQUE (min_alpha)) {
            pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface);
     if (pdf_pattern.gstate_res.id == 0) {
  cairo_pattern_destroy (pdf_pattern.pattern);
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     }
        }
    }

    pdf_pattern.width  = surface->width;
    pdf_pattern.height = surface->height;
    if (extents != NULL) {
 pdf_pattern.extents = *extents;
    } else {
 pdf_pattern.extents.x = 0;
 pdf_pattern.extents.y = 0;
 pdf_pattern.extents.width  = surface->width;
 pdf_pattern.extents.height = surface->height;
    }

    *pattern_res = pdf_pattern.pattern_res;
    *gstate_res = pdf_pattern.gstate_res;
    /* If the pattern requires a gstate it will be drawn from within
     * an XObject. The initial space of each XObject has an inverted
     * Y-axis. */

    pdf_pattern.inverted_y_axis = pdf_pattern.gstate_res.id ? TRUE : surface->in_xobject;

    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
 cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
 if (_cairo_pdf_interchange_struct_tree_requires_recording_surface (surface,
            surface_pattern,
            source_type))
 {
     region_id = surface_pattern->region_array_id;
 }
    }
    pdf_pattern.region_id = region_id;

    status = _cairo_array_append (&surface->page_patterns, &pdf_pattern);
    if (unlikely (status)) {
 cairo_pattern_destroy (pdf_pattern.pattern);
 return status;
    }

    return CAIRO_INT_STATUS_SUCCESS;
}

/* Get BBox from extents */
static void
_get_bbox_from_extents (const cairo_rectangle_int_t  *extents,
   cairo_box_double_t           *bbox)
{
    bbox->p1.x = extents->x;
    bbox->p1.y = extents->y;
    bbox->p2.x = extents->x + extents->width;
    bbox->p2.y = extents->y + extents->height;
}

static cairo_int_status_t
--> --------------------

--> maximum size reached

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

Messung V0.5
C=98 H=91 G=94

¤ Dauer der Verarbeitung: 0.23 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.