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.33 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Versionsinformation zu Columbo

Bemerkung:

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Anfrage:

Dauer der Verarbeitung:

Sekunden

sprechenden Kalenders