diff options
Diffstat (limited to 'Build/source/libs/cairo/cairo-src/src/cairo-pdf-surface.c')
-rw-r--r-- | Build/source/libs/cairo/cairo-src/src/cairo-pdf-surface.c | 8521 |
1 files changed, 0 insertions, 8521 deletions
diff --git a/Build/source/libs/cairo/cairo-src/src/cairo-pdf-surface.c b/Build/source/libs/cairo/cairo-src/src/cairo-pdf-surface.c deleted file mode 100644 index 7eb61aa1e05..00000000000 --- a/Build/source/libs/cairo/cairo-src/src/cairo-pdf-surface.c +++ /dev/null @@ -1,8521 +0,0 @@ -/* -*- 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-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 - * - * 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. - */ - -/** - * 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: %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 -}; - -#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" -}; - -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 -}; - -typedef struct _cairo_pdf_object { - long offset; -} cairo_pdf_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); - -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 void -_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface); - -static cairo_pdf_resource_t -_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface); - -static long -_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface); - -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_write_page (cairo_pdf_surface_t *surface); - -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 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; - - object.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->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; - _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_resource_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)); - _cairo_array_init (&surface->page_heights, sizeof (double)); - 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; - } - - _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 BAIL1; - } - - _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 BAIL2; - } - - surface->struct_tree_root.id = 0; - surface->pdf_version = CAIRO_PDF_VERSION_1_5; - surface->compress_content = 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->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; - - surface->force_fallbacks = FALSE; - surface->select_pattern_gstate_saved = FALSE; - surface->current_pattern_is_solid_color = FALSE; - surface->current_operator = CAIRO_OPERATOR_OVER; - 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 BAIL2; - - 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; - - if (getenv ("CAIRO_DEBUG_PDF") != NULL) - surface->compress_content = 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; - } - -BAIL2: - _cairo_scaled_font_subsets_destroy (surface->font_subsets); -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 >= 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_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) -{ - int i, size; - cairo_pdf_pattern_t *pattern; - cairo_pdf_source_surface_t *src_surface; - cairo_pdf_smask_group_t *group; - - 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); - 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); - - cairo_surface_destroy (&surface->thumbnail_image->base); - surface->thumbnail_image = NULL; -} - -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) -{ - 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"); - - 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->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; - } -} - -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 - * @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, - 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; - - 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; - surface_key.interpolate = interpolate; - cairo_surface_get_mime_data (source_surface, CAIRO_MIME_TYPE_UNIQUE_ID, - (const unsigned char **) &surface_key.unique_id, - &surface_key.unique_id_length); - _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->operator = op; - surface_entry->interpolate = interpolate; - 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; - 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; - } - - 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; - } - - /* Test if surface will be emitted as image or recording */ - status = _cairo_pdf_surface_emit_surface (surface, &src_surface, TRUE, &surface_entry->emit_image); - if (unlikely (status)) - 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, - 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; - - 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; - - 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 -_cairo_pdf_surface_add_pdf_shading (cairo_pdf_surface_t *surface, - const cairo_pattern_t *pattern, - cairo_operator_t op, - const cairo_rectangle_int_t *extents, - cairo_pdf_resource_t *shading_res, - cairo_pdf_resource_t *gstate_res) -{ - return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface, - pattern, - op, - extents, - TRUE, - shading_res, - gstate_res); -} - -static cairo_int_status_t -_cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface, - const cairo_pattern_t *pattern, - cairo_operator_t op, - const cairo_rectangle_int_t *extents, - cairo_pdf_resource_t *pattern_res, - cairo_pdf_resource_t *gstate_res) -{ - return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface, - pattern, - op, - extents, - FALSE, - pattern_res, - gstate_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, - ...) -{ - va_list ap; - cairo_pdf_resource_t self, length; - cairo_output_stream_t *output = NULL; - - if (resource) { - self = *resource; - _cairo_pdf_surface_update_object (surface, self); - } else { - self = _cairo_pdf_surface_new_object (surface); - if (self.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - length = _cairo_pdf_surface_new_object (surface); - if (length.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (compressed) { - output = _cairo_deflate_stream_create (surface->output); - if (_cairo_output_stream_get_status (output)) - return _cairo_output_stream_destroy (output); - } - - surface->pdf_stream.active = TRUE; - surface->pdf_stream.self = self; - surface->pdf_stream.length = length; - surface->pdf_stream.compressed = compressed; - surface->current_pattern_is_solid_color = FALSE; - surface->current_operator = CAIRO_OPERATOR_OVER; - _cairo_pdf_operators_reset (&surface->pdf_operators); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Length %d 0 R\n", - surface->pdf_stream.self.id, - surface->pdf_stream.length.id); - if (compressed) - _cairo_output_stream_printf (surface->output, - " /Filter /FlateDecode\n"); - - if (fmt != NULL) { - va_start (ap, fmt); - _cairo_output_stream_vprintf (surface->output, fmt, ap); - va_end (ap); - } - - _cairo_output_stream_printf (surface->output, - ">>\n" - "stream\n"); - - surface->pdf_stream.start_offset = _cairo_output_stream_get_position (surface->output); - - if (compressed) { - assert (surface->pdf_stream.old_output == NULL); - surface->pdf_stream.old_output = surface->output; - surface->output = output; - _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); - } - _cairo_pdf_operators_reset (&surface->pdf_operators); - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_int_status_t -_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) -{ - cairo_int_status_t status; - long length; - - if (! surface->pdf_stream.active) - return CAIRO_INT_STATUS_SUCCESS; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - - if (surface->pdf_stream.compressed) { - cairo_int_status_t status2; - - status2 = _cairo_output_stream_destroy (surface->output); - if (likely (status == CAIRO_INT_STATUS_SUCCESS)) - status = status2; - - surface->output = surface->pdf_stream.old_output; - _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); - surface->pdf_stream.old_output = NULL; - } - - length = _cairo_output_stream_get_position (surface->output) - - surface->pdf_stream.start_offset; - _cairo_output_stream_printf (surface->output, - "\n" - "endstream\n" - "endobj\n"); - - _cairo_pdf_surface_update_object (surface, - surface->pdf_stream.length); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - " %ld\n" - "endobj\n", - surface->pdf_stream.length.id, - length); - - surface->pdf_stream.active = FALSE; - - if (likely (status == CAIRO_INT_STATUS_SUCCESS)) - status = _cairo_output_stream_get_status (surface->output); - - return status; -} - -static void -_cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface, - cairo_output_stream_t *mem_stream, - cairo_pdf_resource_t resource, - cairo_pdf_group_resources_t *resources, - cairo_bool_t is_knockout_group, - const cairo_box_double_t *bbox) -{ - _cairo_pdf_surface_update_object (surface, resource); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /XObject\n" - " /Length %d\n", - resource.id, - _cairo_memory_stream_length (mem_stream)); - - if (surface->compress_content) { - _cairo_output_stream_printf (surface->output, - " /Filter /FlateDecode\n"); - } - - _cairo_output_stream_printf (surface->output, - " /Subtype /Form\n" - " /BBox [ %f %f %f %f ]\n" - " /Group <<\n" - " /Type /Group\n" - " /S /Transparency\n" - " /I true\n" - " /CS /DeviceRGB\n", - bbox->p1.x, bbox->p1.y, bbox->p2.x, bbox->p2.y); - - if (is_knockout_group) - _cairo_output_stream_printf (surface->output, - " /K true\n"); - - _cairo_output_stream_printf (surface->output, - " >>\n" - " /Resources\n"); - _cairo_pdf_surface_emit_group_resources (surface, resources); - _cairo_output_stream_printf (surface->output, - ">>\n" - "stream\n"); - _cairo_memory_stream_copy (mem_stream, surface->output); - _cairo_output_stream_printf (surface->output, - "endstream\n" - "endobj\n"); -} - -static cairo_int_status_t -_cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, - const cairo_box_double_t *bbox, - cairo_pdf_resource_t *resource) -{ - cairo_int_status_t status; - - assert (surface->pdf_stream.active == FALSE); - assert (surface->group_stream.active == FALSE); - - surface->group_stream.active = TRUE; - surface->current_pattern_is_solid_color = FALSE; - surface->current_operator = CAIRO_OPERATOR_OVER; - _cairo_pdf_operators_reset (&surface->pdf_operators); - - surface->group_stream.mem_stream = _cairo_memory_stream_create (); - - if (surface->compress_content) { - surface->group_stream.stream = - _cairo_deflate_stream_create (surface->group_stream.mem_stream); - } else { - surface->group_stream.stream = surface->group_stream.mem_stream; - } - status = _cairo_output_stream_get_status (surface->group_stream.stream); - - surface->group_stream.old_output = surface->output; - surface->output = surface->group_stream.stream; - _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); - _cairo_pdf_group_resources_clear (&surface->resources); - - if (resource) { - surface->group_stream.resource = *resource; - } else { - surface->group_stream.resource = _cairo_pdf_surface_new_object (surface); - if (surface->group_stream.resource.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - surface->group_stream.is_knockout = FALSE; - surface->group_stream.bbox = *bbox; - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface, - const cairo_box_double_t *bbox) -{ - cairo_int_status_t status; - - status = _cairo_pdf_surface_open_group (surface, bbox, NULL); - if (unlikely (status)) - return status; - - surface->group_stream.is_knockout = TRUE; - - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, - cairo_pdf_resource_t *group) -{ - cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS, status2; - - assert (surface->pdf_stream.active == FALSE); - assert (surface->group_stream.active == TRUE); - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - if (surface->compress_content) { - status = _cairo_output_stream_destroy (surface->group_stream.stream); - surface->group_stream.stream = NULL; - - _cairo_output_stream_printf (surface->group_stream.mem_stream, - "\n"); - } - surface->output = surface->group_stream.old_output; - _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); - surface->group_stream.active = FALSE; - _cairo_pdf_surface_write_memory_stream (surface, - surface->group_stream.mem_stream, - surface->group_stream.resource, - &surface->resources, - surface->group_stream.is_knockout, - &surface->group_stream.bbox); - if (group) - *group = surface->group_stream.resource; - - status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream); - if (status == CAIRO_INT_STATUS_SUCCESS) - status = status2; - - surface->group_stream.mem_stream = NULL; - surface->group_stream.stream = NULL; - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, - const cairo_box_double_t *bbox, - cairo_pdf_resource_t *resource, - cairo_bool_t is_form, - cairo_bool_t is_group) -{ - cairo_int_status_t status; - - assert (surface->pdf_stream.active == FALSE); - assert (surface->group_stream.active == FALSE); - - surface->content_resources = _cairo_pdf_surface_new_object (surface); - if (surface->content_resources.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (is_form) { - assert (bbox != NULL); - - if (is_group) { - status = - _cairo_pdf_surface_open_stream (surface, - resource, - surface->compress_content, - " /Type /XObject\n" - " /Subtype /Form\n" - " /BBox [ %f %f %f %f ]\n" - " /Group <<\n" - " /Type /Group\n" - " /S /Transparency\n" - " /I true\n" - " /CS /DeviceRGB\n" - " >>\n" - " /Resources %d 0 R\n", - bbox->p1.x, - bbox->p1.y, - bbox->p2.x, - bbox->p2.y, - surface->content_resources.id); - } else { - status = - _cairo_pdf_surface_open_stream (surface, - resource, - surface->compress_content, - " /Type /XObject\n" - " /Subtype /Form\n" - " /BBox [ %f %f %f %f ]\n" - " /Resources %d 0 R\n", - bbox->p1.x, - bbox->p1.y, - bbox->p2.x, - bbox->p2.y, - surface->content_resources.id); - } - } else { - status = - _cairo_pdf_surface_open_stream (surface, - resource, - surface->compress_content, - NULL); - _cairo_output_stream_printf (surface->output, - "1 0 0 -1 0 %f cm\n", - surface->height); - } - if (unlikely (status)) - return status; - - surface->content = surface->pdf_stream.self; - - _cairo_output_stream_printf (surface->output, "q\n"); - _cairo_pdf_operators_reset (&surface->pdf_operators); - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_int_status_t -_cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface) -{ - cairo_int_status_t status; - - assert (surface->pdf_stream.active == TRUE); - assert (surface->group_stream.active == FALSE); - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "Q\n"); - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) - return status; - - _cairo_pdf_surface_update_object (surface, surface->content_resources); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n", - surface->content_resources.id); - _cairo_pdf_surface_emit_group_resources (surface, &surface->resources); - _cairo_output_stream_printf (surface->output, - "endobj\n"); - - return _cairo_output_stream_get_status (surface->output); -} - -static void -_cairo_pdf_source_surface_entry_pluck (void *entry, void *closure) -{ - cairo_pdf_source_surface_entry_t *surface_entry = entry; - cairo_hash_table_t *patterns = closure; - - _cairo_hash_table_remove (patterns, &surface_entry->base); - free (surface_entry->unique_id); - - free (surface_entry); -} - -static cairo_status_t -_cairo_pdf_surface_finish (void *abstract_surface) -{ - cairo_pdf_surface_t *surface = abstract_surface; - long offset; - cairo_pdf_resource_t catalog; - cairo_status_t status, status2; - int size, i; - cairo_pdf_jbig2_global_t *global; - char *label; - - _cairo_pdf_surface_clear (surface); - - /* Emit unbounded surfaces */ - _cairo_pdf_surface_write_patterns_and_smask_groups (surface, TRUE); - - status = surface->base.status; - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_pdf_surface_emit_font_subsets (surface); - - _cairo_pdf_surface_write_pages (surface); - - status = _cairo_pdf_interchange_write_document_objects (surface); - if (unlikely (status)) - return status; - - catalog = _cairo_pdf_surface_write_catalog (surface); - if (catalog.id == 0 && status == CAIRO_STATUS_SUCCESS) - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - - offset = _cairo_pdf_surface_write_xref (surface); - - _cairo_output_stream_printf (surface->output, - "trailer\n" - "<< /Size %d\n" - " /Root %d 0 R\n" - " /Info %d 0 R\n" - ">>\n", - surface->next_available_resource.id, - catalog.id, - surface->docinfo_res.id); - - _cairo_output_stream_printf (surface->output, - "startxref\n" - "%ld\n" - "%%%%EOF\n", - offset); - - /* pdf_operators has already been flushed when the last stream was - * closed so we should never be writing anything here - however, - * the stream may itself be in an error state. */ - status2 = _cairo_pdf_operators_fini (&surface->pdf_operators); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - /* close any active streams still open due to fatal errors */ - status2 = _cairo_pdf_surface_close_stream (surface); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - if (surface->group_stream.stream != NULL) { - status2 = _cairo_output_stream_destroy (surface->group_stream.stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - if (surface->group_stream.mem_stream != NULL) { - status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - if (surface->pdf_stream.active) - surface->output = surface->pdf_stream.old_output; - if (surface->group_stream.active) - surface->output = surface->group_stream.old_output; - - /* and finish the pdf surface */ - status2 = _cairo_output_stream_destroy (surface->output); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - - _cairo_pdf_group_resources_fini (&surface->resources); - - _cairo_array_fini (&surface->objects); - _cairo_array_fini (&surface->pages); - _cairo_array_fini (&surface->rgb_linear_functions); - _cairo_array_fini (&surface->alpha_linear_functions); - _cairo_array_fini (&surface->page_patterns); - _cairo_array_fini (&surface->page_surfaces); - _cairo_array_fini (&surface->doc_surfaces); - _cairo_hash_table_foreach (surface->all_surfaces, - _cairo_pdf_source_surface_entry_pluck, - surface->all_surfaces); - _cairo_hash_table_destroy (surface->all_surfaces); - _cairo_array_fini (&surface->smask_groups); - _cairo_array_fini (&surface->fonts); - _cairo_array_fini (&surface->knockout_group); - _cairo_array_fini (&surface->page_annots); - - if (surface->font_subsets) { - _cairo_scaled_font_subsets_destroy (surface->font_subsets); - surface->font_subsets = NULL; - } - - size = _cairo_array_num_elements (&surface->jbig2_global); - for (i = 0; i < size; i++) { - global = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, i); - free(global->id); - if (!global->emitted) - return _cairo_error (CAIRO_STATUS_JBIG2_GLOBAL_MISSING); - } - _cairo_array_fini (&surface->jbig2_global); - _cairo_array_fini (&surface->page_heights); - - size = _cairo_array_num_elements (&surface->page_labels); - for (i = 0; i < size; i++) { - _cairo_array_copy_element (&surface->page_labels, i, &label); - free (label); - } - _cairo_array_fini (&surface->page_labels); - - _cairo_surface_clipper_reset (&surface->clipper); - - return _cairo_pdf_interchange_fini (surface); -} - -static cairo_int_status_t -_cairo_pdf_surface_start_page (void *abstract_surface) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_resource_t page; - cairo_int_status_t status; - - /* Document header */ - if (! surface->header_emitted) { - const char *version; - - switch (surface->pdf_version) { - case CAIRO_PDF_VERSION_1_4: - version = "1.4"; - break; - default: - case CAIRO_PDF_VERSION_1_5: - version = "1.5"; - break; - } - - _cairo_output_stream_printf (surface->output, - "%%PDF-%s\n", version); - _cairo_output_stream_printf (surface->output, - "%%%c%c%c%c\n", 181, 237, 174, 251); - surface->header_emitted = TRUE; - } - - _cairo_pdf_group_resources_clear (&surface->resources); - surface->in_xobject = FALSE; - - page = _cairo_pdf_surface_new_object (surface); - if (page.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_array_append (&surface->pages, &page); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_has_fallback_images (void *abstract_surface, - cairo_bool_t has_fallbacks) -{ - cairo_int_status_t status; - cairo_pdf_surface_t *surface = abstract_surface; - cairo_box_double_t bbox; - - surface->has_fallback_images = has_fallbacks; - surface->in_xobject = has_fallbacks; - bbox.p1.x = 0; - bbox.p1.y = 0; - bbox.p2.x = surface->width; - bbox.p2.y = surface->height; - status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, has_fallbacks, has_fallbacks); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface) -{ - return TRUE; -} - -static cairo_bool_t -_cairo_pdf_surface_requires_thumbnail_image (void *abstract_surface, - int *width, - int *height) -{ - cairo_pdf_surface_t *surface = abstract_surface; - - if (surface->thumbnail_width > 0 && surface->thumbnail_height > 0) { - *width = surface->thumbnail_width; - *height = surface->thumbnail_height; - return TRUE; - } - - return FALSE; -} - -static cairo_int_status_t -_cairo_pdf_surface_set_thumbnail_image (void *abstract_surface, - cairo_image_surface_t *image) -{ - cairo_pdf_surface_t *surface = abstract_surface; - - surface->thumbnail_image = (cairo_image_surface_t *)cairo_surface_reference(&image->base); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t *surface, - const cairo_pattern_t *source, - const cairo_rectangle_int_t *extents, - cairo_pdf_source_surface_entry_t **pdf_source, - double *x_offset, - double *y_offset, - cairo_rectangle_int_t *source_extents) -{ - cairo_image_surface_t *image; - cairo_surface_t *pad_image; - void *image_extra; - cairo_int_status_t status; - int w, h; - cairo_box_t box; - cairo_rectangle_int_t rect; - cairo_surface_pattern_t pad_pattern; - - status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source, - &image, &image_extra); - if (unlikely (status)) - return status; - - pad_image = &image->base; - - /* get the operation extents in pattern space */ - _cairo_box_from_rectangle (&box, extents); - _cairo_matrix_transform_bounding_box_fixed (&source->matrix, &box, NULL); - _cairo_box_round_to_rectangle (&box, &rect); - - /* Check if image needs padding to fill extents */ - w = image->width; - h = image->height; - if (_cairo_fixed_integer_ceil(box.p1.x) < 0 || - _cairo_fixed_integer_ceil(box.p1.y) < 0 || - _cairo_fixed_integer_floor(box.p2.x) > w || - _cairo_fixed_integer_floor(box.p2.y) > h) - { - pad_image = _cairo_image_surface_create_with_content (image->base.content, - rect.width, - rect.height); - if (pad_image->status) { - status = pad_image->status; - goto BAIL; - } - - _cairo_pattern_init_for_surface (&pad_pattern, &image->base); - cairo_matrix_init_translate (&pad_pattern.base.matrix, rect.x, rect.y); - pad_pattern.base.extend = CAIRO_EXTEND_PAD; - status = _cairo_surface_paint (pad_image, - CAIRO_OPERATOR_SOURCE, &pad_pattern.base, - NULL); - _cairo_pattern_fini (&pad_pattern.base); - if (unlikely (status)) - goto BAIL; - } - - status = _cairo_pdf_surface_add_source_surface (surface, - pad_image, - NULL, - CAIRO_OPERATOR_OVER, /* not used for images */ - source->filter, - FALSE, /* stencil mask */ - FALSE, /* smask */ - FALSE, /* need_transp_group */ - extents, - NULL, /* smask_res */ - pdf_source, - x_offset, - y_offset, - source_extents); - if (unlikely (status)) - goto BAIL; - - if (pad_image != &image->base) { - /* If using a padded image, replace _add_source_surface - * x/y_offset with padded image offset. Note: - * _add_source_surface only sets a non zero x/y_offset for - * RASTER_SOURCE patterns. _add_source_surface will always set - * x/y_offset to 0 for surfaces so we can ignore the returned - * offset and replace it with the offset required for the - * padded image */ - *x_offset = rect.x; - *y_offset = rect.y; - } - -BAIL: - if (pad_image != &image->base) - cairo_surface_destroy (pad_image); - - _cairo_pdf_surface_release_source_image_from_pattern (surface, source, image, image_extra); - - return status; -} - -/* Emit alpha channel from the image into stream_res. - */ -static cairo_int_status_t -_cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, - cairo_image_surface_t *image, - cairo_bool_t stencil_mask, - cairo_bool_t interpolate, - cairo_pdf_resource_t *stream_res) -{ - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - char *alpha; - unsigned long alpha_size; - uint32_t *pixel32; - uint8_t *pixel8; - int i, x, y, bit, a; - cairo_image_transparency_t transparency; - - /* This is the only image format we support, which simplifies things. */ - assert (image->format == CAIRO_FORMAT_ARGB32 || - image->format == CAIRO_FORMAT_RGB24 || - image->format == CAIRO_FORMAT_A8 || - image->format == CAIRO_FORMAT_A1 ); - - transparency = _cairo_image_analyze_transparency (image); - if (stencil_mask) { - assert (transparency == CAIRO_IMAGE_IS_OPAQUE || - transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA); - } else { - assert (transparency != CAIRO_IMAGE_IS_OPAQUE); - } - - if (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA || transparency == CAIRO_IMAGE_IS_OPAQUE) { - alpha_size = (image->width + 7) / 8 * image->height; - alpha = _cairo_malloc_ab ((image->width+7) / 8, image->height); - } else { - alpha_size = image->height * image->width; - alpha = _cairo_malloc_ab (image->height, image->width); - } - - if (unlikely (alpha == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - i = 0; - for (y = 0; y < image->height; y++) { - if (transparency == CAIRO_IMAGE_IS_OPAQUE) { - for (x = 0; x < (image->width + 7) / 8; x++) - alpha[i++] = 0xff; - } else if (image->format == CAIRO_FORMAT_A1) { - pixel8 = (uint8_t *) (image->data + y * image->stride); - - for (x = 0; x < (image->width + 7) / 8; x++, pixel8++) { - a = *pixel8; - a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a); - alpha[i++] = a; - } - } else { - pixel8 = (uint8_t *) (image->data + y * image->stride); - pixel32 = (uint32_t *) (image->data + y * image->stride); - bit = 7; - for (x = 0; x < image->width; x++) { - if (image->format == CAIRO_FORMAT_ARGB32) { - a = (*pixel32 & 0xff000000) >> 24; - pixel32++; - } else { - a = *pixel8; - pixel8++; - } - - if (transparency == CAIRO_IMAGE_HAS_ALPHA) { - alpha[i++] = a; - } else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */ - if (bit == 7) - alpha[i] = 0; - if (a != 0) - alpha[i] |= (1 << bit); - bit--; - if (bit < 0) { - bit = 7; - i++; - } - } - } - if (bit != 7) - i++; - } - } - - if (stencil_mask) { - status = _cairo_pdf_surface_open_stream (surface, - stream_res, - TRUE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /ImageMask true\n" - " /Width %d\n" - " /Height %d\n" - " /Interpolate %s\n" - " /BitsPerComponent 1\n" - " /Decode [1 0]\n", - image->width, image->height, - interpolate ? "true" : "false"); - } else { - status = _cairo_pdf_surface_open_stream (surface, - stream_res, - TRUE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /ColorSpace /DeviceGray\n" - " /Interpolate %s\n" - " /BitsPerComponent %d\n", - image->width, image->height, - interpolate ? "true" : "false", - transparency == CAIRO_IMAGE_HAS_ALPHA ? 8 : 1); - } - if (unlikely (status)) - goto CLEANUP_ALPHA; - - _cairo_output_stream_write (surface->output, alpha, alpha_size); - status = _cairo_pdf_surface_close_stream (surface); - - CLEANUP_ALPHA: - free (alpha); - CLEANUP: - return status; -} - -/** - * _cairo_pdf_surface_emit_image: - * @surface: the pdf surface - * @image_surf: The image to write - * @surface_entry: Contains image resource, smask resource, interpolate and stencil mask parameters. - * - * Emit an image stream using the @image_res resource and write out - * the image data from @image_surf. If @smask_res is not null, @smask_res will - * be specified as the smask for the image. Otherwise emit the an smask if - * the image is requires one. - **/ -static cairo_int_status_t -_cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface, - cairo_image_surface_t *image_surf, - cairo_pdf_source_surface_entry_t *surface_entry) -{ - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - char *data; - unsigned long data_size; - uint32_t *pixel; - int i, x, y, bit; - cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */ - cairo_bool_t need_smask; - cairo_image_color_t color; - cairo_image_surface_t *image; - cairo_image_transparency_t transparency; - char smask_buf[30]; - - image = image_surf; - if (image->format != CAIRO_FORMAT_RGB24 && - image->format != CAIRO_FORMAT_ARGB32 && - image->format != CAIRO_FORMAT_A8 && - image->format != CAIRO_FORMAT_A1) - { - cairo_surface_t *surf; - cairo_surface_pattern_t pattern; - - surf = _cairo_image_surface_create_with_content (image_surf->base.content, - image_surf->width, - image_surf->height); - image = (cairo_image_surface_t *) surf; - if (surf->status) { - status = surf->status; - goto CLEANUP; - } - - _cairo_pattern_init_for_surface (&pattern, &image_surf->base); - status = _cairo_surface_paint (surf, - CAIRO_OPERATOR_SOURCE, &pattern.base, - NULL); - _cairo_pattern_fini (&pattern.base); - if (unlikely (status)) - goto CLEANUP; - } - - if (surface_entry->smask || surface_entry->stencil_mask) { - return _cairo_pdf_surface_emit_smask (surface, image, - surface_entry->stencil_mask, - surface_entry->interpolate, - &surface_entry->surface_res); - } - - color = _cairo_image_analyze_color (image); - switch (color) { - default: - case CAIRO_IMAGE_UNKNOWN_COLOR: - ASSERT_NOT_REACHED; - case CAIRO_IMAGE_IS_COLOR: - data_size = image->height * image->width * 3; - data = _cairo_malloc_abc (image->width, image->height, 3); - break; - - case CAIRO_IMAGE_IS_GRAYSCALE: - data_size = image->height * image->width; - data = _cairo_malloc_ab (image->width, image->height); - break; - case CAIRO_IMAGE_IS_MONOCHROME: - data_size = (image->width + 7) / 8 * image->height; - data = _cairo_malloc_ab ((image->width+7) / 8, image->height); - break; - } - if (unlikely (data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP; - } - - i = 0; - for (y = 0; y < image->height; y++) { - pixel = (uint32_t *) (image->data + y * image->stride); - - bit = 7; - for (x = 0; x < image->width; x++, pixel++) { - int r, g, b; - - /* XXX: We're un-premultiplying alpha here. My reading of the PDF - * specification suggests that we should be able to avoid having - * to do this by filling in the SMask's Matte dictionary - * appropriately, but my attempts to do that so far have - * failed. */ - if (image->format == CAIRO_FORMAT_ARGB32) { - uint8_t a; - a = (*pixel & 0xff000000) >> 24; - if (a == 0) { - r = g = b = 0; - } else { - r = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a; - g = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a; - b = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a; - } - } else if (image->format == CAIRO_FORMAT_RGB24) { - r = (*pixel & 0x00ff0000) >> 16; - g = (*pixel & 0x0000ff00) >> 8; - b = (*pixel & 0x000000ff) >> 0; - } else { - r = g = b = 0; - } - - switch (color) { - case CAIRO_IMAGE_IS_COLOR: - case CAIRO_IMAGE_UNKNOWN_COLOR: - data[i++] = r; - data[i++] = g; - data[i++] = b; - break; - - case CAIRO_IMAGE_IS_GRAYSCALE: - data[i++] = r; - break; - - case CAIRO_IMAGE_IS_MONOCHROME: - if (bit == 7) - data[i] = 0; - if (r != 0) - data[i] |= (1 << bit); - bit--; - if (bit < 0) { - bit = 7; - i++; - } - break; - } - } - if (bit != 7) - i++; - } - - if (surface_entry->smask_res.id != 0) { - need_smask = TRUE; - smask = surface_entry->smask_res; - } else { - need_smask = FALSE; - if (image->format == CAIRO_FORMAT_ARGB32 || - image->format == CAIRO_FORMAT_A8 || - image->format == CAIRO_FORMAT_A1) - { - transparency = _cairo_image_analyze_transparency (image); - if (transparency != CAIRO_IMAGE_IS_OPAQUE) { - need_smask = TRUE; - smask = _cairo_pdf_surface_new_object (surface); - if (smask.id == 0) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_RGB; - } - - status = _cairo_pdf_surface_emit_smask (surface, image, FALSE, surface_entry->interpolate, &smask); - if (unlikely (status)) - goto CLEANUP_RGB; - } - } - } - - if (need_smask) - snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", smask.id); - else - smask_buf[0] = 0; - - status = _cairo_pdf_surface_open_stream (surface, - &surface_entry->surface_res, - TRUE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /ColorSpace %s\n" - " /Interpolate %s\n" - " /BitsPerComponent %d\n" - "%s", - image->width, - image->height, - color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray", - surface_entry->interpolate ? "true" : "false", - color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8, - smask_buf); - if (unlikely (status)) - goto CLEANUP_RGB; - -#undef IMAGE_DICTIONARY - - _cairo_output_stream_write (surface->output, data, data_size); - status = _cairo_pdf_surface_close_stream (surface); - -CLEANUP_RGB: - free (data); -CLEANUP: - if (image != image_surf) - cairo_surface_destroy (&image->base); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_lookup_jbig2_global (cairo_pdf_surface_t *surface, - const unsigned char *global_id, - unsigned long global_id_length, - cairo_pdf_jbig2_global_t **entry) -{ - cairo_pdf_jbig2_global_t global; - int size, i; - cairo_int_status_t status; - - size = _cairo_array_num_elements (&surface->jbig2_global); - for (i = 0; i < size; i++) { - *entry = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, i); - if ((*entry)->id && global_id && (*entry)->id_length == global_id_length - && memcmp((*entry)->id, global_id, global_id_length) == 0) { - return CAIRO_STATUS_SUCCESS; - } - } - - global.id = _cairo_malloc (global_id_length); - if (unlikely (global.id == NULL)) { - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - memcpy (global.id, global_id, global_id_length); - global.id_length = global_id_length; - global.res = _cairo_pdf_surface_new_object (surface); - if (global.res.id == 0) { - free(global.id); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - global.emitted = FALSE; - status = _cairo_array_append (&surface->jbig2_global, &global); - if (unlikely(status)) - return status; - - size = _cairo_array_num_elements (&surface->jbig2_global); - *entry = (cairo_pdf_jbig2_global_t *) _cairo_array_index (&surface->jbig2_global, size - 1); - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_jbig2_image (cairo_pdf_surface_t *surface, - cairo_surface_t *source, - cairo_pdf_source_surface_entry_t *surface_entry, - cairo_bool_t test) -{ - cairo_int_status_t status; - const unsigned char *mime_data; - unsigned long mime_data_length; - cairo_image_info_t info; - const unsigned char *global_id; - unsigned long global_id_length; - const unsigned char *global_data; - unsigned long global_data_length; - cairo_pdf_jbig2_global_t *global_entry = NULL; /* hide compiler warning */ - char smask_buf[30]; - char decode_parms_buf[100]; - - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2, - &mime_data, &mime_data_length); - if (mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_image_info_get_jbig2_info (&info, mime_data, mime_data_length); - if (status) - return status; - - /* At this point we know emitting jbig2 will succeed. */ - if (test) - return CAIRO_STATUS_SUCCESS; - - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, - &global_id, &global_id_length); - if (global_id && global_id_length > 0) { - status = _cairo_pdf_surface_lookup_jbig2_global (surface, global_id, global_id_length, &global_entry); - if (unlikely(status)) - return status; - - if (!global_entry->emitted) { - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JBIG2_GLOBAL, - &global_data, &global_data_length); - if (global_data) { - status = _cairo_pdf_surface_open_stream (surface, &global_entry->res, FALSE, NULL); - if (unlikely(status)) - return status; - - _cairo_output_stream_write (surface->output, global_data, global_data_length); - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely(status)) - return status; - - global_entry->emitted = TRUE; - } - } - - snprintf(decode_parms_buf, sizeof(decode_parms_buf), - " /DecodeParms << /JBIG2Globals %d 0 R >>\n", global_entry->res.id); - } else { - decode_parms_buf[0] = 0; - } - - if (surface_entry->smask_res.id) - snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", surface_entry->smask_res.id); - else - smask_buf[0] = 0; - - if (surface_entry->stencil_mask) { - status = _cairo_pdf_surface_open_stream (surface, - &surface_entry->surface_res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /ImageMask true\n" - " /Width %d\n" - " /Height %d\n" - " /Interpolate %s\n" - " /BitsPerComponent 1\n" - " /Decode [1 0]\n" - " /Filter /JPXDecode\n" - "%s", - info.width, - info.height, - surface_entry->interpolate ? "true" : "false", - decode_parms_buf); - } else { - status = _cairo_pdf_surface_open_stream (surface, - &surface_entry->surface_res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /ColorSpace /DeviceGray\n" - " /BitsPerComponent 1\n" - " /Interpolate %s\n" - "%s" - " /Filter /JBIG2Decode\n" - "%s", - info.width, - info.height, - surface_entry->interpolate ? "true" : "false", - smask_buf, - decode_parms_buf); - } - if (unlikely(status)) - return status; - - _cairo_output_stream_write (surface->output, mime_data, mime_data_length); - status = _cairo_pdf_surface_close_stream (surface); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface, - cairo_surface_t *source, - cairo_pdf_source_surface_entry_t *surface_entry, - cairo_bool_t test) -{ - cairo_int_status_t status; - const unsigned char *mime_data; - unsigned long mime_data_length; - cairo_image_info_t info; - char smask_buf[30]; - - if (surface->pdf_version < CAIRO_PDF_VERSION_1_5) - return CAIRO_INT_STATUS_UNSUPPORTED; - - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2, - &mime_data, &mime_data_length); - if (mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_image_info_get_jpx_info (&info, mime_data, mime_data_length); - if (status) - return status; - - if ((surface_entry->smask || surface_entry->stencil_mask) && info.num_components != 1) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if ((surface_entry->stencil_mask) && info.bits_per_component != 1) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (surface_entry->smask_res.id) - snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", surface_entry->smask_res.id); - else - smask_buf[0] = 0; - - /* At this point we know emitting jpx will succeed. */ - if (test) - return CAIRO_STATUS_SUCCESS; - - if (surface_entry->stencil_mask) { - status = _cairo_pdf_surface_open_stream (surface, - &surface_entry->surface_res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /ImageMask true\n" - " /Width %d\n" - " /Height %d\n" - " /Interpolate %s\n" - " /BitsPerComponent 1\n" - " /Decode [1 0]\n" - " /Filter /JPXDecode\n", - info.width, - info.height, - surface_entry->interpolate ? "true" : "false"); - } else { - status = _cairo_pdf_surface_open_stream (surface, - &surface_entry->surface_res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /Interpolate %s\n" - "%s" - " /Filter /JPXDecode\n", - info.width, - info.height, - surface_entry->interpolate ? "true" : "false", - smask_buf); - } - if (status) - return status; - - _cairo_output_stream_write (surface->output, mime_data, mime_data_length); - status = _cairo_pdf_surface_close_stream (surface); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, - cairo_surface_t *source, - cairo_pdf_source_surface_entry_t *surface_entry, - cairo_bool_t test) -{ - cairo_int_status_t status; - const unsigned char *mime_data; - unsigned long mime_data_length; - cairo_image_info_t info; - const char *colorspace; - char smask_buf[30]; - - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, - &mime_data, &mime_data_length); - if (unlikely (source->status)) - return source->status; - if (mime_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length); - if (unlikely (status)) - return status; - - if ((surface_entry->smask || surface_entry->stencil_mask) && info.num_components != 1) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if ((surface_entry->stencil_mask) && info.bits_per_component != 1) - return CAIRO_INT_STATUS_UNSUPPORTED; - - switch (info.num_components) { - case 1: - colorspace = "/DeviceGray"; - break; - case 3: - colorspace = "/DeviceRGB"; - break; - case 4: - colorspace = "/DeviceCMYK"; - break; - default: - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - /* At this point we know emitting jpeg will succeed. */ - if (test) - return CAIRO_STATUS_SUCCESS; - - if (surface_entry->smask_res.id) - snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", surface_entry->smask_res.id); - else - smask_buf[0] = 0; - - if (surface_entry->stencil_mask) { - status = _cairo_pdf_surface_open_stream (surface, - &surface_entry->surface_res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /ImageMask true\n" - " /Width %d\n" - " /Height %d\n" - " /Interpolate %s\n" - " /BitsPerComponent 1\n" - " /Decode [1 0]\n" - " /Filter /DCTDecode\n", - info.width, - info.height, - surface_entry->interpolate ? "true" : "false"); - } else { - status = _cairo_pdf_surface_open_stream (surface, - &surface_entry->surface_res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /ColorSpace %s\n" - " /Interpolate %s\n" - " /BitsPerComponent %d\n" - "%s" - " /Filter /DCTDecode\n", - info.width, - info.height, - colorspace, - surface_entry->interpolate ? "true" : "false", - info.bits_per_component, - smask_buf); - } - if (unlikely (status)) - return status; - - _cairo_output_stream_write (surface->output, mime_data, mime_data_length); - status = _cairo_pdf_surface_close_stream (surface); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_ccitt_image (cairo_pdf_surface_t *surface, - cairo_surface_t *source, - cairo_pdf_source_surface_entry_t *surface_entry, - cairo_bool_t test) -{ - cairo_status_t status; - const unsigned char *ccitt_data; - unsigned long ccitt_data_len; - const unsigned char *ccitt_params_string; - unsigned long ccitt_params_string_len; - char *params, *p, *end; - cairo_ccitt_params_t ccitt_params; - char buf[300]; - - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_CCITT_FAX, - &ccitt_data, &ccitt_data_len); - if (unlikely (source->status)) - return source->status; - if (ccitt_data == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, - &ccitt_params_string, &ccitt_params_string_len); - if (unlikely (source->status)) - return source->status; - if (ccitt_params_string == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* ensure params_string is null terminated */ - params = malloc (ccitt_params_string_len + 1); - memcpy (params, ccitt_params_string, ccitt_params_string_len); - params[ccitt_params_string_len] = 0; - status = _cairo_tag_parse_ccitt_params (params, &ccitt_params); - if (unlikely(status)) - return source->status; - - free (params); - - /* At this point we know emitting jbig2 will succeed. */ - if (test) - return CAIRO_STATUS_SUCCESS; - - p = buf; - *p = 0; - end = buf + sizeof(buf) - 1; - p += snprintf (p, end - p, "/Columns %d /Rows %d /K %d", - ccitt_params.columns, - ccitt_params.rows, - ccitt_params.k); - if (ccitt_params.end_of_line) - p += snprintf (p, end - p, " /EndOfLine true"); - - if (ccitt_params.encoded_byte_align) - p += snprintf (p, end - p, " /EncodedByteAlign true"); - - if (!ccitt_params.end_of_block) - p += snprintf (p, end - p, " /EndOfBlock false"); - - if (ccitt_params.black_is_1) - p += snprintf (p, end - p, " /BlackIs1 true"); - - if (ccitt_params.damaged_rows_before_error > 0) { - p += snprintf (p, end - p, " /DamagedRowsBeforeError %d", - ccitt_params.damaged_rows_before_error); - } - - if (surface_entry->stencil_mask) { - status = _cairo_pdf_surface_open_stream (surface, - &surface_entry->surface_res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /ImageMask true\n" - " /Width %d\n" - " /Height %d\n" - " /Interpolate %s\n" - " /BitsPerComponent 1\n" - " /Decode [1 0]\n" - " /Filter /CCITTFaxDecode\n" - " /DecodeParms << %s >> ", - ccitt_params.columns, - ccitt_params.rows, - surface_entry->interpolate ? "true" : "false", - buf); - } else { - status = _cairo_pdf_surface_open_stream (surface, - &surface_entry->surface_res, - FALSE, - " /Type /XObject\n" - " /Subtype /Image\n" - " /Width %d\n" - " /Height %d\n" - " /ColorSpace /DeviceGray\n" - " /BitsPerComponent 1\n" - " /Interpolate %s\n" - " /Filter /CCITTFaxDecode\n" - " /DecodeParms << %s >> ", - ccitt_params.columns, - ccitt_params.rows, - surface_entry->interpolate ? "true" : "false", - buf); - } - if (unlikely (status)) - return status; - - _cairo_output_stream_write (surface->output, ccitt_data, ccitt_data_len); - status = _cairo_pdf_surface_close_stream (surface); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, - cairo_pdf_source_surface_t *pdf_source) -{ - cairo_rectangle_int_t old_surface_extents; - cairo_bool_t old_surface_bounded; - cairo_paginated_mode_t old_paginated_mode; - cairo_surface_clipper_t old_clipper; - cairo_bool_t old_in_xobject; - cairo_box_double_t bbox; - cairo_int_status_t status; - int alpha = 0; - cairo_surface_t *free_me = NULL; - cairo_surface_t *source; - const cairo_rectangle_int_t *extents; - cairo_bool_t is_subsurface; - cairo_bool_t transparency_group; - cairo_recording_surface_t *recording; - - assert (pdf_source->type == CAIRO_PATTERN_TYPE_SURFACE); - - if (pdf_source->hash_entry->bounded) { - extents = &pdf_source->hash_entry->extents; - } else { - extents = &pdf_source->hash_entry->required_extents; - } - - is_subsurface = FALSE; - source = pdf_source->surface; - 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; - - source = sub->target; - extents = &sub->extents; - is_subsurface = TRUE; - } - - assert (source->type == CAIRO_SURFACE_TYPE_RECORDING); - recording = (cairo_recording_surface_t *) source; - - old_in_xobject = surface->in_xobject; - old_surface_extents = surface->surface_extents; - old_surface_bounded = surface->surface_bounded; - old_paginated_mode = surface->paginated_mode; - old_clipper = surface->clipper; - surface->surface_extents = *extents; - _cairo_surface_clipper_init (&surface->clipper, - _cairo_pdf_surface_clipper_intersect_clip_path); - - _cairo_pdf_operators_reset (&surface->pdf_operators); - surface->in_xobject = TRUE; - surface->surface_extents = *extents; - surface->surface_bounded = TRUE; - - /* Patterns are emitted after fallback images. The paginated mode - * needs to be set to _RENDER while the recording surface is replayed - * back to this surface. - */ - surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER; - _cairo_pdf_group_resources_clear (&surface->resources); - _get_bbox_from_extents (extents, &bbox); - - /* We can optimize away the transparency group allowing the viewer - * to replay the group in place when: - * - ca/CA when painting this groups is 1.0 (need_transp_group is FALSE), - * - all operators are OVER, and - * - the recording contains only opaque and/or clear alpha. - */ - transparency_group = pdf_source->hash_entry->need_transp_group || - !(pdf_source->hash_entry->operator == CAIRO_OPERATOR_OVER && - _cairo_recording_surface_has_only_bilevel_alpha (recording) && - _cairo_recording_surface_has_only_op_over (recording)); - - status = _cairo_pdf_surface_open_content_stream (surface, - &bbox, - &pdf_source->hash_entry->surface_res, - TRUE, - transparency_group); - if (unlikely (status)) - goto err; - - if (source->content == CAIRO_CONTENT_COLOR) { - status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); - if (unlikely (status)) - goto err; - - _cairo_output_stream_printf (surface->output, - "q /a%d gs 0 0 0 rg %d %d %d %d re f Q\n", - alpha, - extents->x, - extents->y, - extents->width, - extents->height); - } - - status = _cairo_recording_surface_replay_region (source, - is_subsurface ? extents : NULL, - &surface->base, - CAIRO_RECORDING_REGION_NATIVE); - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - if (unlikely (status)) - goto err; - - status = _cairo_pdf_surface_close_content_stream (surface); - - _cairo_surface_clipper_reset (&surface->clipper); - surface->clipper = old_clipper; - _cairo_pdf_operators_reset (&surface->pdf_operators); - surface->in_xobject = old_in_xobject; - surface->paginated_mode = old_paginated_mode; - surface->surface_extents = old_surface_extents; - surface->surface_bounded = old_surface_bounded; - -err: - cairo_surface_destroy (free_me); - return status; -} - -/** - * _cairo_pdf_surface_emit_surface: - * @surface: [in] the pdf surface - * @source: [in] #cairo_pdf_source_surface_t containing the surface to write - * @test: [in] if true, test what type of surface will be emitted. - * @is_image: [out] if @test is true, returns TRUE if the surface will be emitted - * as an Image XObject. - * - * If @test is FALSE, emit @src_surface as an XObject. - * If @test is TRUE, don't emit anything. Set @is_image based on the output that would be emitted. - **/ -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) -{ - cairo_image_surface_t *image; - void *image_extra; - cairo_int_status_t status; - - /* Try all the supported mime types and recording type, falling through - * each option if unsupported */ - if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - status = _cairo_pdf_surface_emit_jbig2_image (surface, - source->surface, - source->hash_entry, - test); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - *is_image = TRUE; - return status; - } - - status = _cairo_pdf_surface_emit_jpx_image (surface, - source->surface, - source->hash_entry, - test); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - *is_image = TRUE; - return status; - } - - status = _cairo_pdf_surface_emit_jpeg_image (surface, - source->surface, - source->hash_entry, - test); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - *is_image = TRUE; - return status; - } - - status = _cairo_pdf_surface_emit_ccitt_image (surface, - source->surface, - source->hash_entry, - test); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - *is_image = TRUE; - return status; - } - - if (source->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (test) { - *is_image = FALSE; - return CAIRO_INT_STATUS_SUCCESS; - } else { - return _cairo_pdf_surface_emit_recording_surface (surface, source); - } - } - } - - /* The only option left is to emit as an image surface */ - - if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - status = _cairo_surface_acquire_source_image (source->surface, &image, &image_extra); - } else { - status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, - source->raster_pattern, - &image, - &image_extra); - } - if (unlikely (status)) - return status; - - if (test) { - *is_image = TRUE; - } else { - status = _cairo_pdf_surface_emit_image (surface, - image, - source->hash_entry); - } - - if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { - _cairo_surface_release_source_image (source->surface, image, image_extra); - } else { - _cairo_pdf_surface_release_source_image_from_pattern (surface, - source->raster_pattern, - image, - image_extra); - } - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern) -{ - cairo_pattern_t *pattern = pdf_pattern->pattern; - cairo_int_status_t status; - cairo_matrix_t cairo_p2d, pdf_p2d; - cairo_extend_t extend = cairo_pattern_get_extend (pattern); - double xstep, ystep; - cairo_rectangle_int_t pattern_extents; - double x_offset; - double y_offset; - char draw_surface[50]; - char draw_surface2[200]; - cairo_box_double_t bbox; - cairo_matrix_t mat; - cairo_pdf_source_surface_entry_t *pdf_source; - cairo_rectangle_int_t op_extents; - - assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); - if (pattern->extend == CAIRO_EXTEND_PAD) { - status = _cairo_pdf_surface_add_padded_image_surface (surface, - pattern, - &pdf_pattern->extents, - &pdf_source, - &x_offset, - &y_offset, - &op_extents); - } else { - status = _cairo_pdf_surface_add_source_surface (surface, - NULL, - pattern, - pdf_pattern->operator, - pattern->filter, - FALSE, /* stencil mask */ - FALSE, /* smask */ - FALSE, /* need_transp_group */ - &pdf_pattern->extents, - NULL, /* smask_res */ - &pdf_source, - &x_offset, - &y_offset, - &op_extents); - } - if (unlikely (status)) - return status; - - pattern_extents = pdf_source->extents; - if (!pdf_source->bounded) - { - extend = CAIRO_EXTEND_NONE; - _cairo_rectangle_intersect (&pattern_extents, &op_extents); - } - - switch (extend) { - case CAIRO_EXTEND_PAD: - case CAIRO_EXTEND_NONE: - { - /* In PS/PDF, (as far as I can tell), all patterns are - * repeating. So we support cairo's EXTEND_NONE semantics - * by setting the repeat step size to a size large enough - * to guarantee that no more than a single occurrence will - * be visible. - * - * First, map the surface extents into pattern space (since - * xstep and ystep are in pattern space). Then use an upper - * bound on the length of the diagonal of the pattern image - * and the surface as repeat size. This guarantees to never - * repeat visibly. - */ - double x1 = 0.0, y1 = 0.0; - double x2 = surface->surface_extents.width; - double y2 = surface->surface_extents.height; - _cairo_matrix_transform_bounding_box (&pattern->matrix, - &x1, &y1, &x2, &y2, - NULL); - - /* Rather than computing precise bounds of the union, just - * add the surface extents unconditionally. We only - * required an answer that's large enough, we don't really - * care if it's not as tight as possible.*/ - xstep = ystep = ceil ((x2 - x1) + (y2 - y1) + - pattern_extents.width + pattern_extents.height); - } - break; - case CAIRO_EXTEND_REPEAT: - xstep = pattern_extents.width; - ystep = pattern_extents.height; - break; - - case CAIRO_EXTEND_REFLECT: - pattern_extents.width *= 2; - pattern_extents.height *= 2; - xstep = pattern_extents.width; - ystep = pattern_extents.height; - break; - - /* All the rest (if any) should have been analyzed away, so this - * case should be unreachable. */ - default: - ASSERT_NOT_REACHED; - xstep = 0; - ystep = 0; - } - - /* At this point, (that is, within the surface backend interface), - * the pattern's matrix maps from cairo's device space to cairo's - * pattern space, (both with their origin at the upper-left, and - * cairo's pattern space of size width,height). - * - * Then, we must emit a PDF pattern object that maps from its own - * pattern space, (which has a size that we establish in the BBox - * dictionary entry), to the PDF page's *initial* space, (which - * does not benefit from the Y-axis flipping matrix that we emit - * on each page). So the PDF patterns matrix maps from a - * (width,height) pattern space to a device space with the origin - * in the lower-left corner. - * - * So to handle all of that, we start with an identity matrix for - * the PDF pattern to device matrix. We translate it up by the - * image height then flip it in the Y direction, (moving us from - * the PDF origin to cairo's origin). We then multiply in the - * inverse of the cairo pattern matrix, (since it maps from device - * to pattern, while we're setting up pattern to device). Finally, - * we translate back down by the image height and flip again to - * end up at the lower-left origin that PDF expects. - * - * Additionally, within the stream that paints the pattern itself, - * we are using a PDF image object that has a size of (1,1) so we - * have to scale it up by the image width and height to fill our - * pattern cell. - */ - cairo_p2d = pattern->matrix; - status = cairo_matrix_invert (&cairo_p2d); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_INT_STATUS_SUCCESS); - - if (pdf_pattern->inverted_y_axis) - cairo_matrix_init (&mat, 1, 0, 0, 1, 0, 0); - else - cairo_matrix_init (&mat, 1, 0, 0, -1, 0, surface->height); - - cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &mat); - cairo_matrix_translate (&pdf_p2d, x_offset, y_offset); - if (pdf_source->emit_image) { - cairo_matrix_translate (&pdf_p2d, 0.0, pdf_source->extents.height); - cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); - } - - _get_bbox_from_extents (&pattern_extents, &bbox); - _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); - status = _cairo_pdf_surface_open_stream (surface, - &pdf_pattern->pattern_res, - FALSE, - " /PatternType 1\n" - " /BBox [ %f %f %f %f ]\n" - " /XStep %f\n" - " /YStep %f\n" - " /TilingType 1\n" - " /PaintType 1\n" - " /Matrix [ %f %f %f %f %f %f ]\n" - " /Resources << /XObject << /x%d %d 0 R >> >>\n", - bbox.p1.x, bbox.p1.y, bbox.p2.x, bbox.p2.y, - xstep, ystep, - pdf_p2d.xx, pdf_p2d.yx, - pdf_p2d.xy, pdf_p2d.yy, - pdf_p2d.x0, pdf_p2d.y0, - pdf_source->surface_res.id, - pdf_source->surface_res.id); - if (unlikely (status)) - return status; - - if (pdf_source->emit_image) { - snprintf(draw_surface, - sizeof (draw_surface), - "q %d 0 0 %d 0 0 cm /x%d Do Q", - pdf_source->extents.width, - pdf_source->extents.height, - pdf_source->surface_res.id); - } else { - snprintf(draw_surface, - sizeof (draw_surface), - "/x%d Do", - pdf_source->surface_res.id); - } - - if (extend == CAIRO_EXTEND_REFLECT) { - cairo_rectangle_int_t p_extents = pdf_source->extents; - snprintf(draw_surface2, - sizeof (draw_surface2), - "%d %d %d %d re W n %s", - p_extents.x, p_extents.y, - p_extents.width, p_extents.height, - draw_surface); - - _cairo_output_stream_printf (surface->output, "q %s Q\n", draw_surface2); - - cairo_matrix_init_translate (&mat, p_extents.x, p_extents.y); - cairo_matrix_scale (&mat, -1, 1); - cairo_matrix_translate (&mat, -2*p_extents.width, 0); - cairo_matrix_translate (&mat, -p_extents.x, -p_extents.y); - _cairo_output_stream_printf (surface->output, "q "); - _cairo_output_stream_print_matrix (surface->output, &mat); - _cairo_output_stream_printf (surface->output, " cm %s Q\n", draw_surface2); - - cairo_matrix_init_translate (&mat, p_extents.x, p_extents.y); - cairo_matrix_scale (&mat, 1, -1); - cairo_matrix_translate (&mat, 0, -2*p_extents.height); - cairo_matrix_translate (&mat, -p_extents.x, -p_extents.y); - _cairo_output_stream_printf (surface->output, "q "); - _cairo_output_stream_print_matrix (surface->output, &mat); - _cairo_output_stream_printf (surface->output, " cm %s Q\n", draw_surface2); - - cairo_matrix_init_translate (&mat, p_extents.x, p_extents.y); - cairo_matrix_scale (&mat, -1, -1); - cairo_matrix_translate (&mat, -2*p_extents.width, -2*p_extents.height); - cairo_matrix_translate (&mat, -p_extents.x, -p_extents.y); - _cairo_output_stream_printf (surface->output, "q "); - _cairo_output_stream_print_matrix (surface->output, &mat); - _cairo_output_stream_printf (surface->output, " cm %s Q\n", draw_surface2); - } else { - _cairo_output_stream_printf (surface->output, - " %s \n", - draw_surface); - } - - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) - return status; - - return _cairo_output_stream_get_status (surface->output); -} - -typedef struct _cairo_pdf_color_stop { - double offset; - double color[4]; - cairo_pdf_resource_t resource; -} cairo_pdf_color_stop_t; - -static cairo_int_status_t -cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t *surface, - cairo_pdf_color_stop_t *stop1, - cairo_pdf_color_stop_t *stop2, - cairo_pdf_resource_t *function) -{ - int num_elems, i; - cairo_pdf_rgb_linear_function_t elem; - cairo_pdf_resource_t res; - cairo_int_status_t status; - - num_elems = _cairo_array_num_elements (&surface->rgb_linear_functions); - for (i = 0; i < num_elems; i++) { - _cairo_array_copy_element (&surface->rgb_linear_functions, i, &elem); - if (memcmp (&elem.color1[0], &stop1->color[0], sizeof (double)*3) != 0) - continue; - if (memcmp (&elem.color2[0], &stop2->color[0], sizeof (double)*3) != 0) - continue; - *function = elem.resource; - return CAIRO_STATUS_SUCCESS; - } - - res = _cairo_pdf_surface_new_object (surface); - if (res.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /FunctionType 2\n" - " /Domain [ 0 1 ]\n" - " /C0 [ %f %f %f ]\n" - " /C1 [ %f %f %f ]\n" - " /N 1\n" - ">>\n" - "endobj\n", - res.id, - stop1->color[0], - stop1->color[1], - stop1->color[2], - stop2->color[0], - stop2->color[1], - stop2->color[2]); - - elem.resource = res; - memcpy (&elem.color1[0], &stop1->color[0], sizeof (double)*3); - memcpy (&elem.color2[0], &stop2->color[0], sizeof (double)*3); - - status = _cairo_array_append (&surface->rgb_linear_functions, &elem); - *function = res; - - return status; -} - -static cairo_int_status_t -cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t *surface, - cairo_pdf_color_stop_t *stop1, - cairo_pdf_color_stop_t *stop2, - cairo_pdf_resource_t *function) -{ - int num_elems, i; - cairo_pdf_alpha_linear_function_t elem; - cairo_pdf_resource_t res; - cairo_int_status_t status; - - num_elems = _cairo_array_num_elements (&surface->alpha_linear_functions); - for (i = 0; i < num_elems; i++) { - _cairo_array_copy_element (&surface->alpha_linear_functions, i, &elem); - if (elem.alpha1 != stop1->color[3]) - continue; - if (elem.alpha2 != stop2->color[3]) - continue; - *function = elem.resource; - return CAIRO_STATUS_SUCCESS; - } - - res = _cairo_pdf_surface_new_object (surface); - if (res.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /FunctionType 2\n" - " /Domain [ 0 1 ]\n" - " /C0 [ %f ]\n" - " /C1 [ %f ]\n" - " /N 1\n" - ">>\n" - "endobj\n", - res.id, - stop1->color[3], - stop2->color[3]); - - elem.resource = res; - elem.alpha1 = stop1->color[3]; - elem.alpha2 = stop2->color[3]; - - status = _cairo_array_append (&surface->alpha_linear_functions, &elem); - *function = res; - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t *surface, - unsigned int n_stops, - cairo_pdf_color_stop_t *stops, - cairo_bool_t is_alpha, - cairo_pdf_resource_t *function) -{ - cairo_pdf_resource_t res; - unsigned int i; - cairo_int_status_t status; - - /* emit linear gradients between pairs of subsequent stops... */ - for (i = 0; i < n_stops-1; i++) { - if (is_alpha) { - status = cairo_pdf_surface_emit_alpha_linear_function (surface, - &stops[i], - &stops[i+1], - &stops[i].resource); - if (unlikely (status)) - return status; - } else { - status = cairo_pdf_surface_emit_rgb_linear_function (surface, - &stops[i], - &stops[i+1], - &stops[i].resource); - if (unlikely (status)) - return status; - } - } - - /* ... and stitch them together */ - res = _cairo_pdf_surface_new_object (surface); - if (res.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /FunctionType 3\n" - " /Domain [ %f %f ]\n", - res.id, - stops[0].offset, - stops[n_stops - 1].offset); - - _cairo_output_stream_printf (surface->output, - " /Functions [ "); - for (i = 0; i < n_stops-1; i++) - _cairo_output_stream_printf (surface->output, - "%d 0 R ", stops[i].resource.id); - _cairo_output_stream_printf (surface->output, - "]\n"); - - _cairo_output_stream_printf (surface->output, - " /Bounds [ "); - for (i = 1; i < n_stops-1; i++) - _cairo_output_stream_printf (surface->output, - "%f ", stops[i].offset); - _cairo_output_stream_printf (surface->output, - "]\n"); - - _cairo_output_stream_printf (surface->output, - " /Encode [ "); - for (i = 1; i < n_stops; i++) - _cairo_output_stream_printf (surface->output, - "0 1 "); - _cairo_output_stream_printf (surface->output, - "]\n"); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - *function = res; - - return _cairo_output_stream_get_status (surface->output); -} - - -static void -calc_gradient_color (cairo_pdf_color_stop_t *new_stop, - cairo_pdf_color_stop_t *stop1, - cairo_pdf_color_stop_t *stop2) -{ - int i; - double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset); - - for (i = 0; i < 4; i++) - new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]); -} - -#define COLOR_STOP_EPSILON 1e-6 - -static cairo_int_status_t -_cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface, - cairo_gradient_pattern_t *pattern, - cairo_pdf_resource_t *color_function, - cairo_pdf_resource_t *alpha_function) -{ - cairo_pdf_color_stop_t *allstops, *stops; - unsigned int n_stops; - unsigned int i; - cairo_bool_t emit_alpha = FALSE; - cairo_int_status_t status; - - color_function->id = 0; - alpha_function->id = 0; - - allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_pdf_color_stop_t)); - if (unlikely (allstops == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - stops = &allstops[1]; - n_stops = pattern->n_stops; - - for (i = 0; i < n_stops; i++) { - stops[i].color[0] = pattern->stops[i].color.red; - stops[i].color[1] = pattern->stops[i].color.green; - stops[i].color[2] = pattern->stops[i].color.blue; - stops[i].color[3] = pattern->stops[i].color.alpha; - if (!CAIRO_ALPHA_IS_OPAQUE (stops[i].color[3])) - emit_alpha = TRUE; - stops[i].offset = pattern->stops[i].offset; - } - - if (pattern->base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.extend == CAIRO_EXTEND_REFLECT) { - if (stops[0].offset > COLOR_STOP_EPSILON) { - if (pattern->base.extend == CAIRO_EXTEND_REFLECT) - memcpy (allstops, stops, sizeof (cairo_pdf_color_stop_t)); - else - calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]); - stops = allstops; - n_stops++; - } - stops[0].offset = 0.0; - - if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) { - if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { - memcpy (&stops[n_stops], - &stops[n_stops - 1], - sizeof (cairo_pdf_color_stop_t)); - } else { - calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]); - } - n_stops++; - } - stops[n_stops-1].offset = 1.0; - } - - if (stops[0].offset == stops[n_stops - 1].offset) { - /* - * The first and the last stops have the same offset, but we - * don't want a function with an empty domain, because that - * would provoke underdefined behaviour from rasterisers. - * This can only happen with EXTEND_PAD, because EXTEND_NONE - * is optimised into a clear pattern in cairo-gstate, and - * REFLECT/REPEAT are always transformed to have the first - * stop at t=0 and the last stop at t=1. Thus we want a step - * function going from the first color to the last one. - * - * This can be accomplished by stitching three functions: - * - a constant first color function, - * - a step from the first color to the last color (with empty domain) - * - a constant last color function - */ - cairo_pdf_color_stop_t pad_stops[4]; - - assert (pattern->base.extend == CAIRO_EXTEND_PAD); - - pad_stops[0] = pad_stops[1] = stops[0]; - pad_stops[2] = pad_stops[3] = stops[n_stops - 1]; - - pad_stops[0].offset = 0; - pad_stops[3].offset = 1; - - status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, - 4, - pad_stops, - FALSE, - color_function); - if (unlikely (status)) - goto BAIL; - - if (emit_alpha) { - status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, - 4, - pad_stops, - TRUE, - alpha_function); - if (unlikely (status)) - goto BAIL; - } - } else if (n_stops == 2) { - /* no need for stitched function */ - status = cairo_pdf_surface_emit_rgb_linear_function (surface, - &stops[0], - &stops[n_stops - 1], - color_function); - if (unlikely (status)) - goto BAIL; - - if (emit_alpha) { - status = cairo_pdf_surface_emit_alpha_linear_function (surface, - &stops[0], - &stops[n_stops - 1], - alpha_function); - if (unlikely (status)) - goto BAIL; - } - } else { - /* multiple stops: stitch. XXX possible optimization: regularly spaced - * stops do not require stitching. XXX */ - status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, - n_stops, - stops, - FALSE, - color_function); - if (unlikely (status)) - goto BAIL; - - if (emit_alpha) { - status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, - n_stops, - stops, - TRUE, - alpha_function); - if (unlikely (status)) - goto BAIL; - } - } - -BAIL: - free (allstops); - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_repeating_function (cairo_pdf_surface_t *surface, - cairo_gradient_pattern_t *pattern, - cairo_pdf_resource_t *function, - int begin, - int end) -{ - cairo_pdf_resource_t res; - int i; - - res = _cairo_pdf_surface_new_object (surface); - if (res.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /FunctionType 3\n" - " /Domain [ %d %d ]\n", - res.id, - begin, - end); - - _cairo_output_stream_printf (surface->output, - " /Functions [ "); - for (i = begin; i < end; i++) - _cairo_output_stream_printf (surface->output, - "%d 0 R ", function->id); - _cairo_output_stream_printf (surface->output, - "]\n"); - - _cairo_output_stream_printf (surface->output, - " /Bounds [ "); - for (i = begin + 1; i < end; i++) - _cairo_output_stream_printf (surface->output, - "%d ", i); - _cairo_output_stream_printf (surface->output, - "]\n"); - - _cairo_output_stream_printf (surface->output, - " /Encode [ "); - for (i = begin; i < end; i++) { - if ((i % 2) && pattern->base.extend == CAIRO_EXTEND_REFLECT) { - _cairo_output_stream_printf (surface->output, - "1 0 "); - } else { - _cairo_output_stream_printf (surface->output, - "0 1 "); - } - } - _cairo_output_stream_printf (surface->output, - "]\n"); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - *function = res; - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_int_status_t -cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern, - cairo_pdf_resource_t gstate_resource, - cairo_pdf_resource_t gradient_mask) -{ - cairo_pdf_resource_t smask_resource; - cairo_int_status_t status; - char buf[100]; - double x1, y1, x2, y2; - - if (pdf_pattern->is_shading) { - snprintf(buf, sizeof(buf), - " /Shading\n" - " << /sh%d %d 0 R >>\n", - gradient_mask.id, - gradient_mask.id); - } else { - snprintf(buf, sizeof(buf), - " /Pattern\n" - " << /p%d %d 0 R >>\n", - gradient_mask.id, - gradient_mask.id); - } - - if (pdf_pattern->is_shading) { - cairo_box_t box; - - /* When emitting a shading operator we are in cairo pattern - * coordinates. _cairo_pdf_surface_paint_gradient has set the - * ctm to the pattern matrix (including the conversion from - * pdf to cairo coordinates) */ - _cairo_box_from_rectangle (&box, &pdf_pattern->extents); - _cairo_box_to_doubles (&box, &x1, &y1, &x2, &y2); - _cairo_matrix_transform_bounding_box (&pdf_pattern->pattern->matrix, &x1, &y1, &x2, &y2, NULL); - } else { - cairo_box_double_t box; - - /* When emitting a shading pattern we are in pdf page - * coordinates. The color and alpha shading patterns painted - * in the XObject below contain the cairo pattern to pdf page - * matrix in the /Matrix entry of the pattern. */ - _get_bbox_from_extents (&pdf_pattern->extents, &box); - x1 = box.p1.x; - y1 = box.p1.y; - x2 = box.p2.x; - y2 = box.p2.y; - } - status = _cairo_pdf_surface_open_stream (surface, - NULL, - surface->compress_content, - " /Type /XObject\n" - " /Subtype /Form\n" - " /FormType 1\n" - " /BBox [ %f %f %f %f ]\n" - " /Resources\n" - " << /ExtGState\n" - " << /a0 << /ca 1 /CA 1 >>" - " >>\n" - "%s" - " >>\n" - " /Group\n" - " << /Type /Group\n" - " /S /Transparency\n" - " /I true\n" - " /CS /DeviceGray\n" - " >>\n", - x1,y1,x2,y2, - buf); - if (unlikely (status)) - return status; - - if (pdf_pattern->is_shading) { - _cairo_output_stream_printf (surface->output, - "/a0 gs /sh%d sh\n", - gradient_mask.id); - } else { - _cairo_output_stream_printf (surface->output, - "q\n" - "/a0 gs\n" - "/Pattern cs /p%d scn\n" - "0 0 %f %f re\n" - "f\n" - "Q\n", - gradient_mask.id, - surface->width, - surface->height); - } - - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) - return status; - - smask_resource = _cairo_pdf_surface_new_object (surface); - if (smask_resource.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Mask\n" - " /S /Luminosity\n" - " /G %d 0 R\n" - ">>\n" - "endobj\n", - smask_resource.id, - surface->pdf_stream.self.id); - - /* Create GState which uses the transparency group as an SMask. */ - _cairo_pdf_surface_update_object (surface, gstate_resource); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /ExtGState\n" - " /SMask %d 0 R\n" - " /ca 1\n" - " /CA 1\n" - " /AIS false\n" - ">>\n" - "endobj\n", - gstate_resource.id, - smask_resource.id); - - return _cairo_output_stream_get_status (surface->output); -} - -static void -_cairo_pdf_surface_output_gradient (cairo_pdf_surface_t *surface, - const cairo_pdf_pattern_t *pdf_pattern, - cairo_pdf_resource_t pattern_resource, - const cairo_matrix_t *pat_to_pdf, - const cairo_circle_double_t*start, - const cairo_circle_double_t*end, - const double *domain, - const char *colorspace, - cairo_pdf_resource_t color_function) -{ - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n", - pattern_resource.id); - - if (!pdf_pattern->is_shading) { - _cairo_output_stream_printf (surface->output, - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ "); - _cairo_output_stream_print_matrix (surface->output, pat_to_pdf); - _cairo_output_stream_printf (surface->output, - " ]\n" - " /Shading\n"); - } - - if (pdf_pattern->pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { - _cairo_output_stream_printf (surface->output, - " << /ShadingType 2\n" - " /ColorSpace %s\n" - " /Coords [ %f %f %f %f ]\n", - colorspace, - start->center.x, start->center.y, - end->center.x, end->center.y); - } else { - _cairo_output_stream_printf (surface->output, - " << /ShadingType 3\n" - " /ColorSpace %s\n" - " /Coords [ %f %f %f %f %f %f ]\n", - colorspace, - start->center.x, start->center.y, - MAX (start->radius, 0), - end->center.x, end->center.y, - MAX (end->radius, 0)); - } - - _cairo_output_stream_printf (surface->output, - " /Domain [ %f %f ]\n", - domain[0], domain[1]); - - if (pdf_pattern->pattern->extend != CAIRO_EXTEND_NONE) { - _cairo_output_stream_printf (surface->output, - " /Extend [ true true ]\n"); - } else { - _cairo_output_stream_printf (surface->output, - " /Extend [ false false ]\n"); - } - - _cairo_output_stream_printf (surface->output, - " /Function %d 0 R\n" - " >>\n", - color_function.id); - - if (!pdf_pattern->is_shading) { - _cairo_output_stream_printf (surface->output, - ">>\n"); - } - - _cairo_output_stream_printf (surface->output, - "endobj\n"); -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern) -{ - cairo_gradient_pattern_t *pattern = (cairo_gradient_pattern_t *) pdf_pattern->pattern; - cairo_pdf_resource_t color_function, alpha_function; - cairo_matrix_t pat_to_pdf; - cairo_circle_double_t start, end; - double domain[2]; - cairo_int_status_t status; - cairo_matrix_t mat; - - assert (pattern->n_stops != 0); - - status = _cairo_pdf_surface_emit_pattern_stops (surface, - pattern, - &color_function, - &alpha_function); - if (unlikely (status)) - return status; - - pat_to_pdf = pattern->base.matrix; - status = cairo_matrix_invert (&pat_to_pdf); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_INT_STATUS_SUCCESS); - - if (pdf_pattern->inverted_y_axis) - cairo_matrix_init (&mat, 1, 0, 0, 1, 0, 0); - else - cairo_matrix_init (&mat, 1, 0, 0, -1, 0, surface->height); - - cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &mat); - - if (pattern->base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.extend == CAIRO_EXTEND_REFLECT) - { - double bounds_x1, bounds_x2, bounds_y1, bounds_y2; - double x_scale, y_scale, tolerance; - - /* TODO: use tighter extents */ - bounds_x1 = 0; - bounds_y1 = 0; - bounds_x2 = surface->width; - bounds_y2 = surface->height; - _cairo_matrix_transform_bounding_box (&pattern->base.matrix, - &bounds_x1, &bounds_y1, - &bounds_x2, &bounds_y2, - NULL); - - x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution; - y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution; - - tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix)); - tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1); - tolerance *= MIN (x_scale, y_scale); - - _cairo_gradient_pattern_box_to_parameter (pattern, - bounds_x1, bounds_y1, - bounds_x2, bounds_y2, - tolerance, domain); - } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) { - /* - * If the first and the last stop offset are the same, then - * the color function is a step function. - * _cairo_ps_surface_emit_pattern_stops emits it as a stitched - * function no matter how many stops the pattern has. The - * domain of the stitched function will be [0 1] in this case. - * - * This is done to avoid emitting degenerate gradients for - * EXTEND_PAD patterns having a step color function. - */ - domain[0] = 0.0; - domain[1] = 1.0; - - assert (pattern->base.extend == CAIRO_EXTEND_PAD); - } else { - domain[0] = pattern->stops[0].offset; - domain[1] = pattern->stops[pattern->n_stops - 1].offset; - } - - /* PDF requires the first and last stop to be the same as the - * extreme coordinates. For repeating patterns this moves the - * extreme coordinates out to the begin/end of the repeating - * function. For non repeating patterns this may move the extreme - * coordinates in if there are not stops at offset 0 and 1. */ - _cairo_gradient_pattern_interpolate (pattern, domain[0], &start); - _cairo_gradient_pattern_interpolate (pattern, domain[1], &end); - - if (pattern->base.extend == CAIRO_EXTEND_REPEAT || - pattern->base.extend == CAIRO_EXTEND_REFLECT) - { - int repeat_begin, repeat_end; - - repeat_begin = floor (domain[0]); - repeat_end = ceil (domain[1]); - - status = _cairo_pdf_surface_emit_repeating_function (surface, - pattern, - &color_function, - repeat_begin, - repeat_end); - if (unlikely (status)) - return status; - - if (alpha_function.id != 0) { - status = _cairo_pdf_surface_emit_repeating_function (surface, - pattern, - &alpha_function, - repeat_begin, - repeat_end); - if (unlikely (status)) - return status; - } - } else if (pattern->n_stops <= 2) { - /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a - * Type 2 function is used by itself without a stitching - * function. Type 2 functions always have the domain [0 1] */ - domain[0] = 0.0; - domain[1] = 1.0; - } - - _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); - _cairo_pdf_surface_output_gradient (surface, pdf_pattern, - pdf_pattern->pattern_res, - &pat_to_pdf, &start, &end, domain, - "/DeviceRGB", color_function); - - if (alpha_function.id != 0) { - cairo_pdf_resource_t mask_resource; - - assert (pdf_pattern->gstate_res.id != 0); - - /* Create pattern for SMask. */ - mask_resource = _cairo_pdf_surface_new_object (surface); - if (mask_resource.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_pdf_surface_output_gradient (surface, pdf_pattern, - mask_resource, - &pat_to_pdf, &start, &end, domain, - "/DeviceGray", alpha_function); - - status = cairo_pdf_surface_emit_transparency_group (surface, - pdf_pattern, - pdf_pattern->gstate_res, - mask_resource); - if (unlikely (status)) - return status; - } - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface, - cairo_pdf_pattern_t *pdf_pattern) -{ - cairo_matrix_t pat_to_pdf; - cairo_int_status_t status; - cairo_pattern_t *pattern = pdf_pattern->pattern; - cairo_pdf_shading_t shading; - int i; - cairo_pdf_resource_t res; - cairo_matrix_t mat; - - pat_to_pdf = pattern->matrix; - status = cairo_matrix_invert (&pat_to_pdf); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_INT_STATUS_SUCCESS); - - if (pdf_pattern->inverted_y_axis) - cairo_matrix_init (&mat, 1, 0, 0, 1, 0, 0); - else - cairo_matrix_init (&mat, 1, 0, 0, -1, 0, surface->height); - - cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &mat); - - status = _cairo_pdf_shading_init_color (&shading, (cairo_mesh_pattern_t *) pattern); - if (unlikely (status)) - return status; - - res = _cairo_pdf_surface_new_object (surface); - if (unlikely (res.id == 0)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /ShadingType %d\n" - " /ColorSpace /DeviceRGB\n" - " /BitsPerCoordinate %d\n" - " /BitsPerComponent %d\n" - " /BitsPerFlag %d\n" - " /Decode [", - res.id, - shading.shading_type, - shading.bits_per_coordinate, - shading.bits_per_component, - shading.bits_per_flag); - - for (i = 0; i < shading.decode_array_length; i++) - _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]); - - _cairo_output_stream_printf (surface->output, - "]\n" - " /Length %ld\n" - ">>\n" - "stream\n", - shading.data_length); - - _cairo_output_stream_write (surface->output, shading.data, shading.data_length); - - _cairo_output_stream_printf (surface->output, - "\nendstream\n" - "endobj\n"); - - _cairo_pdf_shading_fini (&shading); - - _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ ", - pdf_pattern->pattern_res.id); - _cairo_output_stream_print_matrix (surface->output, &pat_to_pdf); - _cairo_output_stream_printf (surface->output, - " ]\n" - " /Shading %d 0 R\n" - ">>\n" - "endobj\n", - res.id); - - if (pdf_pattern->gstate_res.id != 0) { - cairo_pdf_resource_t mask_resource; - - /* Create pattern for SMask. */ - res = _cairo_pdf_surface_new_object (surface); - if (unlikely (res.id == 0)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - status = _cairo_pdf_shading_init_alpha (&shading, (cairo_mesh_pattern_t *) pattern); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /ShadingType %d\n" - " /ColorSpace /DeviceGray\n" - " /BitsPerCoordinate %d\n" - " /BitsPerComponent %d\n" - " /BitsPerFlag %d\n" - " /Decode [", - res.id, - shading.shading_type, - shading.bits_per_coordinate, - shading.bits_per_component, - shading.bits_per_flag); - - for (i = 0; i < shading.decode_array_length; i++) - _cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]); - - _cairo_output_stream_printf (surface->output, - "]\n" - " /Length %ld\n" - ">>\n" - "stream\n", - shading.data_length); - - _cairo_output_stream_write (surface->output, shading.data, shading.data_length); - - _cairo_output_stream_printf (surface->output, - "\nendstream\n" - "endobj\n"); - _cairo_pdf_shading_fini (&shading); - - mask_resource = _cairo_pdf_surface_new_object (surface); - if (unlikely (mask_resource.id == 0)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pattern\n" - " /PatternType 2\n" - " /Matrix [ ", - mask_resource.id); - _cairo_output_stream_print_matrix (surface->output, &pat_to_pdf); - _cairo_output_stream_printf (surface->output, - " ]\n" - " /Shading %d 0 R\n" - ">>\n" - "endobj\n", - res.id); - - status = cairo_pdf_surface_emit_transparency_group (surface, - pdf_pattern, - pdf_pattern->gstate_res, - mask_resource); - if (unlikely (status)) - return status; - } - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) -{ - cairo_int_status_t status; - - switch (pdf_pattern->pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - ASSERT_NOT_REACHED; - status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - break; - - case CAIRO_PATTERN_TYPE_SURFACE: - case CAIRO_PATTERN_TYPE_RASTER_SOURCE: - status = _cairo_pdf_surface_emit_surface_pattern (surface, pdf_pattern); - break; - - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - status = _cairo_pdf_surface_emit_gradient (surface, pdf_pattern); - break; - - case CAIRO_PATTERN_TYPE_MESH: - status = _cairo_pdf_surface_emit_mesh_pattern (surface, pdf_pattern); - break; - - default: - ASSERT_NOT_REACHED; - status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); - break; - } - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_rectangle_int_t *extents, - double alpha, - cairo_pdf_resource_t *smask_res, - cairo_bool_t stencil_mask) -{ - cairo_matrix_t cairo_p2d, pdf_p2d; - cairo_int_status_t status; - int alpha_id; - double x_offset; - double y_offset; - cairo_pdf_source_surface_entry_t *pdf_source; - - if (source->extend == CAIRO_EXTEND_PAD && - !(source->type == CAIRO_PATTERN_TYPE_SURFACE && - ((cairo_surface_pattern_t *)source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)) - { - status = _cairo_pdf_surface_add_padded_image_surface (surface, - source, - extents, - &pdf_source, - &x_offset, - &y_offset, - NULL); - } else { - status = _cairo_pdf_surface_add_source_surface (surface, - NULL, - source, - op, - source->filter, - stencil_mask, - FALSE, /* smask */ - alpha != 1.0, /* need_transp_group */ - extents, - smask_res, - &pdf_source, - &x_offset, - &y_offset, - NULL); - } - if (unlikely (status)) - return status; - - cairo_p2d = source->matrix; - status = cairo_matrix_invert (&cairo_p2d); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_INT_STATUS_SUCCESS); - - pdf_p2d = surface->cairo_to_pdf; - cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d); - cairo_matrix_translate (&pdf_p2d, x_offset, y_offset); - if (pdf_source->emit_image) { - int width, height; - - if (pdf_source->bounded) { - width = pdf_source->extents.width; - height = pdf_source->extents.height; - } else { - /* We can't scale an image to an unbounded surface size so just set the size to 1 */ - width = 1; - height = 1; - } - - cairo_matrix_translate (&pdf_p2d, 0.0, height); - cairo_matrix_scale (&pdf_p2d, 1.0, -1.0); - cairo_matrix_scale (&pdf_p2d, width, height); - } - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - if (! _cairo_matrix_is_identity (&pdf_p2d)) { - _cairo_output_stream_print_matrix (surface->output, &pdf_p2d); - _cairo_output_stream_printf (surface->output, " cm\n"); - } - - status = _cairo_pdf_surface_add_alpha (surface, alpha, &alpha_id); - if (unlikely (status)) - return status; - - if (stencil_mask) { - _cairo_output_stream_printf (surface->output, - "/x%d Do\n", - pdf_source->surface_res.id); - } else { - _cairo_output_stream_printf (surface->output, - "/a%d gs /x%d Do\n", - alpha_id, - pdf_source->surface_res.id); - } - - return _cairo_pdf_surface_add_xobject (surface, pdf_source->surface_res); -} - -static cairo_int_status_t -_cairo_pdf_surface_paint_gradient (cairo_pdf_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_rectangle_int_t *extents, - double alpha) -{ - cairo_pdf_resource_t shading_res, gstate_res; - cairo_matrix_t pat_to_pdf; - cairo_int_status_t status; - int alpha_id; - - status = _cairo_pdf_surface_add_pdf_shading (surface, source, - op, extents, - &shading_res, &gstate_res); - if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) - return CAIRO_INT_STATUS_SUCCESS; - if (unlikely (status)) - return status; - - pat_to_pdf = source->matrix; - status = cairo_matrix_invert (&pat_to_pdf); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_INT_STATUS_SUCCESS); - cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf); - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - if (! _cairo_matrix_is_identity (&pat_to_pdf)) { - _cairo_output_stream_print_matrix (surface->output, &pat_to_pdf); - _cairo_output_stream_printf (surface->output, " cm\n"); - } - - status = _cairo_pdf_surface_add_shading (surface, shading_res); - if (unlikely (status)) - return status; - - if (gstate_res.id != 0) { - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "/s%d gs /sh%d sh\n", - gstate_res.id, - shading_res.id); - } else { - status = _cairo_pdf_surface_add_alpha (surface, alpha, &alpha_id); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "/a%d gs /sh%d sh\n", - alpha_id, - shading_res.id); - } - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_rectangle_int_t *extents, - double alpha, - cairo_bool_t mask) -{ - switch (source->type) { - case CAIRO_PATTERN_TYPE_SURFACE: - case CAIRO_PATTERN_TYPE_RASTER_SOURCE: - return _cairo_pdf_surface_paint_surface_pattern (surface, - op, - source, - extents, - alpha, - NULL, - mask); - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - case CAIRO_PATTERN_TYPE_MESH: - return _cairo_pdf_surface_paint_gradient (surface, - op, - source, - extents, - alpha); - - case CAIRO_PATTERN_TYPE_SOLID: - default: - ASSERT_NOT_REACHED; - return CAIRO_STATUS_SUCCESS; - } -} - -static cairo_bool_t -_can_paint_pattern (const cairo_pattern_t *pattern) -{ - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - return FALSE; - - case CAIRO_PATTERN_TYPE_SURFACE: - case CAIRO_PATTERN_TYPE_RASTER_SOURCE: - return (pattern->extend == CAIRO_EXTEND_NONE || - pattern->extend == CAIRO_EXTEND_PAD); - - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - return TRUE; - - case CAIRO_PATTERN_TYPE_MESH: - return FALSE; - - default: - ASSERT_NOT_REACHED; - return FALSE; - } -} - -static cairo_int_status_t -_cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface, - cairo_operator_t op) -{ - cairo_int_status_t status; - - if (op == surface->current_operator) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "/b%d gs\n", op); - surface->current_operator = op; - _cairo_pdf_surface_add_operator (surface, op); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface, - const cairo_pattern_t *pattern, - cairo_pdf_resource_t pattern_res, - cairo_bool_t is_stroke) -{ - cairo_int_status_t status; - int alpha; - const cairo_color_t *solid_color = NULL; - - if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - const cairo_solid_pattern_t *solid = (const cairo_solid_pattern_t *) pattern; - - solid_color = &solid->color; - } - - if (solid_color != NULL) { - if (surface->current_pattern_is_solid_color == FALSE || - surface->current_color_red != solid_color->red || - surface->current_color_green != solid_color->green || - surface->current_color_blue != solid_color->blue || - surface->current_color_is_stroke != is_stroke) - { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "%f %f %f ", - solid_color->red, - solid_color->green, - solid_color->blue); - - if (is_stroke) - _cairo_output_stream_printf (surface->output, "RG "); - else - _cairo_output_stream_printf (surface->output, "rg "); - - surface->current_color_red = solid_color->red; - surface->current_color_green = solid_color->green; - surface->current_color_blue = solid_color->blue; - surface->current_color_is_stroke = is_stroke; - } - - if (surface->current_pattern_is_solid_color == FALSE || - surface->current_color_alpha != solid_color->alpha) - { - status = _cairo_pdf_surface_add_alpha (surface, solid_color->alpha, &alpha); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "/a%d gs\n", - alpha); - surface->current_color_alpha = solid_color->alpha; - } - - surface->current_pattern_is_solid_color = TRUE; - } else { - status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_add_pattern (surface, pattern_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - /* fill-stroke calls select_pattern twice. Don't save if the - * gstate is already saved. */ - if (!surface->select_pattern_gstate_saved) - _cairo_output_stream_printf (surface->output, "q "); - - if (is_stroke) { - _cairo_output_stream_printf (surface->output, - "/Pattern CS /p%d SCN ", - pattern_res.id); - } else { - _cairo_output_stream_printf (surface->output, - "/Pattern cs /p%d scn ", - pattern_res.id); - } - _cairo_output_stream_printf (surface->output, - "/a%d gs\n", - alpha); - surface->select_pattern_gstate_saved = TRUE; - surface->current_pattern_is_solid_color = FALSE; - } - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_int_status_t -_cairo_pdf_surface_unselect_pattern (cairo_pdf_surface_t *surface) -{ - cairo_int_status_t status; - - if (surface->select_pattern_gstate_saved) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "Q\n"); - _cairo_pdf_operators_reset (&surface->pdf_operators); - surface->current_pattern_is_solid_color = FALSE; - } - surface->select_pattern_gstate_saved = FALSE; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_show_page (void *abstract_surface) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_int_status_t status; - - status = _cairo_array_append (&surface->page_heights, &surface->height); - if (unlikely (status)) - return status; - - status = _cairo_array_append (&surface->page_labels, &surface->current_page_label); - if (unlikely (status)) - return status; - - surface->current_page_label = NULL; - - status = _cairo_pdf_interchange_end_page_content (surface); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_close_content_stream (surface); - if (unlikely (status)) - return status; - - _cairo_surface_clipper_reset (&surface->clipper); - - status = _cairo_pdf_surface_write_page (surface); - if (unlikely (status)) - return status; - - _cairo_pdf_surface_clear (surface); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t -_cairo_pdf_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_pdf_surface_t *surface = abstract_surface; - - if (surface->surface_bounded) - *rectangle = surface->surface_extents; - - return surface->surface_bounded; -} - -static void -_cairo_pdf_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - _cairo_font_options_init_default (options); - - cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); - cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); - cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); - _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); -} - -static void -_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) -{ - cairo_pdf_resource_t page; - int num_pages, i; - - _cairo_pdf_surface_update_object (surface, surface->pages_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Pages\n" - " /Kids [ ", - surface->pages_resource.id); - - num_pages = _cairo_array_num_elements (&surface->pages); - for (i = 0; i < num_pages; i++) { - _cairo_array_copy_element (&surface->pages, i, &page); - _cairo_output_stream_printf (surface->output, "%d 0 R ", page.id); - } - - _cairo_output_stream_printf (surface->output, "]\n"); - _cairo_output_stream_printf (surface->output, " /Count %d\n", num_pages); - - - /* TODO: Figure out which other defaults to be inherited by /Page - * objects. */ - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); -} - -cairo_int_status_t -_cairo_utf8_to_pdf_string (const char *utf8, char **str_out) -{ - int i; - int len; - cairo_bool_t ascii; - char *str; - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - - ascii = TRUE; - len = strlen (utf8); - for (i = 0; i < len; i++) { - unsigned c = utf8[i]; - if (c < 32 || c > 126 || c == '(' || c == ')' || c == '\\') { - ascii = FALSE; - break; - } - } - - if (ascii) { - str = _cairo_malloc (len + 3); - if (str == NULL) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - str[0] = '('; - for (i = 0; i < len; i++) - str[i+1] = utf8[i]; - str[i+1] = ')'; - str[i+2] = 0; - } else { - uint16_t *utf16 = NULL; - int utf16_len = 0; - - status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); - if (unlikely (status)) - return status; - - str = _cairo_malloc (utf16_len*4 + 7); - if (str == NULL) { - free (utf16); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - strcpy (str, "<FEFF"); - for (i = 0; i < utf16_len; i++) - snprintf (str + 4*i + 5, 5, "%04X", utf16[i]); - - strcat (str, ">"); - free (utf16); - } - *str_out = str; - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface, - const char *utf8) -{ - uint16_t *utf16 = NULL; - int utf16_len = 0; - cairo_int_status_t status; - int i; - - if (utf8 && *utf8) { - status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); - if (unlikely (status == CAIRO_INT_STATUS_INVALID_STRING)) { - utf16 = NULL; - utf16_len = 0; - } else if (unlikely (status)) { - return status; - } - } - - _cairo_output_stream_printf (surface->output, "<"); - if (utf16 == NULL || utf16_len == 0) { - /* According to the "ToUnicode Mapping File Tutorial" - * http://www.adobe.com/devnet/acrobat/pdfs/5411.ToUnicode.pdf - * - * Glyphs that do not map to a Unicode code point must be - * mapped to 0xfffd "REPLACEMENT CHARACTER". - */ - _cairo_output_stream_printf (surface->output, - "fffd"); - } else { - for (i = 0; i < utf16_len; i++) - _cairo_output_stream_printf (surface->output, - "%04x", (int) (utf16[i])); - } - _cairo_output_stream_printf (surface->output, ">"); - - free (utf16); - - return CAIRO_STATUS_SUCCESS; -} - -/* Bob Jenkins hash - * - * Public domain code from: - * http://burtleburtle.net/bob/hash/doobs.html - */ - -#define HASH_MIX(a,b,c) \ -{ \ - a -= b; a -= c; a ^= (c>>13); \ - b -= c; b -= a; b ^= (a<<8); \ - c -= a; c -= b; c ^= (b>>13); \ - a -= b; a -= c; a ^= (c>>12); \ - b -= c; b -= a; b ^= (a<<16); \ - c -= a; c -= b; c ^= (b>>5); \ - a -= b; a -= c; a ^= (c>>3); \ - b -= c; b -= a; b ^= (a<<10); \ - c -= a; c -= b; c ^= (b>>15); \ -} - -static uint32_t -_hash_data (const unsigned char *data, int length, uint32_t initval) -{ - uint32_t a, b, c, len; - - len = length; - a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ - c = initval; /* the previous hash value */ - - while (len >= 12) { - a += (data[0] + ((uint32_t)data[1]<<8) + ((uint32_t)data[2]<<16) + ((uint32_t)data[3]<<24)); - b += (data[4] + ((uint32_t)data[5]<<8) + ((uint32_t)data[6]<<16) + ((uint32_t)data[7]<<24)); - c += (data[8] + ((uint32_t)data[9]<<8) + ((uint32_t)data[10]<<16)+ ((uint32_t)data[11]<<24)); - HASH_MIX (a,b,c); - data += 12; - len -= 12; - } - - c += length; - switch(len) { - case 11: c+= ((uint32_t) data[10] << 24); - case 10: c+= ((uint32_t) data[9] << 16); - case 9 : c+= ((uint32_t) data[8] << 8); - case 8 : b+= ((uint32_t) data[7] << 24); - case 7 : b+= ((uint32_t) data[6] << 16); - case 6 : b+= ((uint32_t) data[5] << 8); - case 5 : b+= data[4]; - case 4 : a+= ((uint32_t) data[3] << 24); - case 3 : a+= ((uint32_t) data[2] << 16); - case 2 : a+= ((uint32_t) data[1] << 8); - case 1 : a+= data[0]; - } - HASH_MIX (a,b,c); - - return c; -} - -static void -_create_font_subset_tag (cairo_scaled_font_subset_t *font_subset, - const char *font_name, - char *tag) -{ - uint32_t hash; - int i; - long numerator; - ldiv_t d; - - hash = _hash_data ((unsigned char *) font_name, strlen(font_name), 0); - hash = _hash_data ((unsigned char *) (font_subset->glyphs), - font_subset->num_glyphs * sizeof(unsigned long), hash); - - numerator = abs (hash); - for (i = 0; i < 6; i++) { - d = ldiv (numerator, 26); - numerator = d.quot; - tag[i] = 'A' + d.rem; - } - tag[i] = 0; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset, - cairo_pdf_resource_t *stream) -{ - unsigned int i, num_bfchar; - cairo_int_status_t status; - - stream->id = 0; - - status = _cairo_pdf_surface_open_stream (surface, - NULL, - surface->compress_content, - NULL); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "/CIDInit /ProcSet findresource begin\n" - "12 dict begin\n" - "begincmap\n" - "/CIDSystemInfo\n" - "<< /Registry (Adobe)\n" - " /Ordering (UCS)\n" - " /Supplement 0\n" - ">> def\n" - "/CMapName /Adobe-Identity-UCS def\n" - "/CMapType 2 def\n" - "1 begincodespacerange\n"); - - if (font_subset->is_composite && !font_subset->is_latin) { - _cairo_output_stream_printf (surface->output, - "<0000> <ffff>\n"); - } else { - _cairo_output_stream_printf (surface->output, - "<00> <ff>\n"); - } - - _cairo_output_stream_printf (surface->output, - "endcodespacerange\n"); - - if (font_subset->is_scaled) { - /* Type 3 fonts include glyph 0 in the subset */ - num_bfchar = font_subset->num_glyphs; - - /* The CMap specification has a limit of 100 characters per beginbfchar operator */ - _cairo_output_stream_printf (surface->output, - "%d beginbfchar\n", - num_bfchar > 100 ? 100 : num_bfchar); - - for (i = 0; i < num_bfchar; i++) { - if (i != 0 && i % 100 == 0) { - _cairo_output_stream_printf (surface->output, - "endbfchar\n" - "%d beginbfchar\n", - num_bfchar - i > 100 ? 100 : num_bfchar - i); - } - _cairo_output_stream_printf (surface->output, "<%02x> ", i); - status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, - font_subset->utf8[i]); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "\n"); - } - } else { - /* Other fonts reserve glyph 0 for .notdef. Omit glyph 0 from the /ToUnicode map */ - num_bfchar = font_subset->num_glyphs - 1; - - /* The CMap specification has a limit of 100 characters per beginbfchar operator */ - _cairo_output_stream_printf (surface->output, - "%d beginbfchar\n", - num_bfchar > 100 ? 100 : num_bfchar); - - for (i = 0; i < num_bfchar; i++) { - if (i != 0 && i % 100 == 0) { - _cairo_output_stream_printf (surface->output, - "endbfchar\n" - "%d beginbfchar\n", - num_bfchar - i > 100 ? 100 : num_bfchar - i); - } - if (font_subset->is_latin) - _cairo_output_stream_printf (surface->output, "<%02x> ", font_subset->to_latin_char[i + 1]); - else if (font_subset->is_composite) - _cairo_output_stream_printf (surface->output, "<%04x> ", i + 1); - else - _cairo_output_stream_printf (surface->output, "<%02x> ", i + 1); - - status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, - font_subset->utf8[i + 1]); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "\n"); - } - } - - _cairo_output_stream_printf (surface->output, - "endbfchar\n"); - - _cairo_output_stream_printf (surface->output, - "endcmap\n" - "CMapName currentdict /CMap defineresource pop\n" - "end\n" - "end\n"); - - *stream = surface->pdf_stream.self; - return _cairo_pdf_surface_close_stream (surface); -} - -#define PDF_UNITS_PER_EM 1000 - -static cairo_int_status_t -_cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset, - cairo_cff_subset_t *subset) -{ - cairo_pdf_resource_t stream, descriptor, cidfont_dict; - cairo_pdf_resource_t subset_resource, to_unicode_stream; - cairo_pdf_font_t font; - unsigned int i, last_glyph; - cairo_int_status_t status; - char tag[10]; - - _create_font_subset_tag (font_subset, subset->ps_name, tag); - - subset_resource = _cairo_pdf_surface_get_font_resource (surface, - font_subset->font_id, - font_subset->subset_id); - if (subset_resource.id == 0) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_pdf_surface_open_stream (surface, - NULL, - TRUE, - font_subset->is_latin ? - " /Subtype /Type1C\n" : - " /Subtype /CIDFontType0C\n"); - if (unlikely (status)) - return status; - - stream = surface->pdf_stream.self; - _cairo_output_stream_write (surface->output, - subset->data, subset->data_length); - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, - &to_unicode_stream); - if (_cairo_int_status_is_error (status)) - return status; - - descriptor = _cairo_pdf_surface_new_object (surface); - if (descriptor.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /FontDescriptor\n" - " /FontName /%s+%s\n", - descriptor.id, - tag, - subset->ps_name); - - if (subset->family_name_utf8) { - char *pdf_str; - - status = _cairo_utf8_to_pdf_string (subset->family_name_utf8, &pdf_str); - if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { - _cairo_output_stream_printf (surface->output, - " /FontFamily %s\n", - pdf_str); - free (pdf_str); - } else if (status != CAIRO_INT_STATUS_INVALID_STRING) { - return status; - } - } - - _cairo_output_stream_printf (surface->output, - " /Flags 4\n" - " /FontBBox [ %ld %ld %ld %ld ]\n" - " /ItalicAngle 0\n" - " /Ascent %ld\n" - " /Descent %ld\n" - " /CapHeight %ld\n" - " /StemV 80\n" - " /StemH 80\n" - " /FontFile3 %u 0 R\n" - ">>\n" - "endobj\n", - (long)(subset->x_min*PDF_UNITS_PER_EM), - (long)(subset->y_min*PDF_UNITS_PER_EM), - (long)(subset->x_max*PDF_UNITS_PER_EM), - (long)(subset->y_max*PDF_UNITS_PER_EM), - (long)(subset->ascent*PDF_UNITS_PER_EM), - (long)(subset->descent*PDF_UNITS_PER_EM), - (long)(subset->y_max*PDF_UNITS_PER_EM), - stream.id); - - if (font_subset->is_latin) { - /* find last glyph used */ - for (i = 255; i >= 32; i--) - if (font_subset->latin_to_subset_glyph_index[i] > 0) - break; - - last_glyph = i; - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type1\n" - " /BaseFont /%s+%s\n" - " /FirstChar 32\n" - " /LastChar %d\n" - " /FontDescriptor %d 0 R\n" - " /Encoding /WinAnsiEncoding\n" - " /Widths [", - subset_resource.id, - tag, - subset->ps_name, - last_glyph, - descriptor.id); - - for (i = 32; i < last_glyph + 1; i++) { - int glyph = font_subset->latin_to_subset_glyph_index[i]; - if (glyph > 0) { - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset->widths[glyph]*PDF_UNITS_PER_EM)); - } else { - _cairo_output_stream_printf (surface->output, " 0"); - } - } - - _cairo_output_stream_printf (surface->output, - " ]\n"); - - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - } else { - cidfont_dict = _cairo_pdf_surface_new_object (surface); - if (cidfont_dict.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /CIDFontType0\n" - " /BaseFont /%s+%s\n" - " /CIDSystemInfo\n" - " << /Registry (Adobe)\n" - " /Ordering (Identity)\n" - " /Supplement 0\n" - " >>\n" - " /FontDescriptor %d 0 R\n" - " /W [0 [", - cidfont_dict.id, - tag, - subset->ps_name, - descriptor.id); - - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset->widths[i]*PDF_UNITS_PER_EM)); - - _cairo_output_stream_printf (surface->output, - " ]]\n" - ">>\n" - "endobj\n"); - - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type0\n" - " /BaseFont /%s+%s\n" - " /Encoding /Identity-H\n" - " /DescendantFonts [ %d 0 R]\n", - subset_resource.id, - tag, - subset->ps_name, - cidfont_dict.id); - - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - } - - font.font_id = font_subset->font_id; - font.subset_id = font_subset->subset_id; - font.subset_resource = subset_resource; - status = _cairo_array_append (&surface->fonts, &font); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_int_status_t status; - cairo_cff_subset_t subset; - char name[64]; - - snprintf (name, sizeof name, "CairoFont-%d-%d", - font_subset->font_id, font_subset->subset_id); - status = _cairo_cff_subset_init (&subset, name, font_subset); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset); - - _cairo_cff_subset_fini (&subset); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_int_status_t status; - cairo_cff_subset_t subset; - char name[64]; - - /* CFF fallback subsetting does not work with 8-bit glyphs unless - * they are a latin subset */ - if (!font_subset->is_composite && !font_subset->is_latin) - return CAIRO_INT_STATUS_UNSUPPORTED; - - snprintf (name, sizeof name, "CairoFont-%d-%d", - font_subset->font_id, font_subset->subset_id); - status = _cairo_cff_fallback_init (&subset, name, font_subset); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset); - - _cairo_cff_fallback_fini (&subset); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset, - cairo_type1_subset_t *subset) -{ - cairo_pdf_resource_t stream, descriptor, subset_resource, to_unicode_stream; - cairo_pdf_font_t font; - cairo_int_status_t status; - unsigned long length; - unsigned int i, last_glyph; - char tag[10]; - - _create_font_subset_tag (font_subset, subset->base_font, tag); - - subset_resource = _cairo_pdf_surface_get_font_resource (surface, - font_subset->font_id, - font_subset->subset_id); - if (subset_resource.id == 0) - return CAIRO_STATUS_SUCCESS; - - length = subset->header_length + subset->data_length + subset->trailer_length; - status = _cairo_pdf_surface_open_stream (surface, - NULL, - TRUE, - " /Length1 %lu\n" - " /Length2 %lu\n" - " /Length3 %lu\n", - subset->header_length, - subset->data_length, - subset->trailer_length); - if (unlikely (status)) - return status; - - stream = surface->pdf_stream.self; - _cairo_output_stream_write (surface->output, subset->data, length); - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, - &to_unicode_stream); - if (_cairo_int_status_is_error (status)) - return status; - - last_glyph = font_subset->num_glyphs - 1; - if (font_subset->is_latin) { - /* find last glyph used */ - for (i = 255; i >= 32; i--) - if (font_subset->latin_to_subset_glyph_index[i] > 0) - break; - - last_glyph = i; - } - - descriptor = _cairo_pdf_surface_new_object (surface); - if (descriptor.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /FontDescriptor\n" - " /FontName /%s+%s\n" - " /Flags 4\n" - " /FontBBox [ %ld %ld %ld %ld ]\n" - " /ItalicAngle 0\n" - " /Ascent %ld\n" - " /Descent %ld\n" - " /CapHeight %ld\n" - " /StemV 80\n" - " /StemH 80\n" - " /FontFile %u 0 R\n" - ">>\n" - "endobj\n", - descriptor.id, - tag, - subset->base_font, - (long)(subset->x_min*PDF_UNITS_PER_EM), - (long)(subset->y_min*PDF_UNITS_PER_EM), - (long)(subset->x_max*PDF_UNITS_PER_EM), - (long)(subset->y_max*PDF_UNITS_PER_EM), - (long)(subset->ascent*PDF_UNITS_PER_EM), - (long)(subset->descent*PDF_UNITS_PER_EM), - (long)(subset->y_max*PDF_UNITS_PER_EM), - stream.id); - - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type1\n" - " /BaseFont /%s+%s\n" - " /FirstChar %d\n" - " /LastChar %d\n" - " /FontDescriptor %d 0 R\n", - subset_resource.id, - tag, - subset->base_font, - font_subset->is_latin ? 32 : 0, - last_glyph, - descriptor.id); - - if (font_subset->is_latin) - _cairo_output_stream_printf (surface->output, " /Encoding /WinAnsiEncoding\n"); - - _cairo_output_stream_printf (surface->output, " /Widths ["); - if (font_subset->is_latin) { - for (i = 32; i < last_glyph + 1; i++) { - int glyph = font_subset->latin_to_subset_glyph_index[i]; - if (glyph > 0) { - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset->widths[glyph]*PDF_UNITS_PER_EM)); - } else { - _cairo_output_stream_printf (surface->output, " 0"); - } - } - } else { - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset->widths[i]*PDF_UNITS_PER_EM)); - } - - _cairo_output_stream_printf (surface->output, - " ]\n"); - - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - font.font_id = font_subset->font_id; - font.subset_id = font_subset->subset_id; - font.subset_resource = subset_resource; - return _cairo_array_append (&surface->fonts, &font); -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_int_status_t status; - cairo_type1_subset_t subset; - char name[64]; - - /* 16-bit glyphs not compatible with Type 1 fonts */ - if (font_subset->is_composite && !font_subset->is_latin) - return CAIRO_INT_STATUS_UNSUPPORTED; - - snprintf (name, sizeof name, "CairoFont-%d-%d", - font_subset->font_id, font_subset->subset_id); - status = _cairo_type1_subset_init (&subset, name, font_subset, FALSE); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset); - - _cairo_type1_subset_fini (&subset); - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_int_status_t status; - cairo_type1_subset_t subset; - char name[64]; - - /* 16-bit glyphs not compatible with Type 1 fonts */ - if (font_subset->is_composite && !font_subset->is_latin) - return CAIRO_INT_STATUS_UNSUPPORTED; - - snprintf (name, sizeof name, "CairoFont-%d-%d", - font_subset->font_id, font_subset->subset_id); - status = _cairo_type1_fallback_init_binary (&subset, name, font_subset); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset); - - _cairo_type1_fallback_fini (&subset); - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_pdf_resource_t stream, descriptor, cidfont_dict; - cairo_pdf_resource_t subset_resource, to_unicode_stream; - cairo_int_status_t status; - cairo_pdf_font_t font; - cairo_truetype_subset_t subset; - unsigned int i, last_glyph; - char tag[10]; - - subset_resource = _cairo_pdf_surface_get_font_resource (surface, - font_subset->font_id, - font_subset->subset_id); - if (subset_resource.id == 0) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_truetype_subset_init_pdf (&subset, font_subset); - if (unlikely (status)) - return status; - - _create_font_subset_tag (font_subset, subset.ps_name, tag); - - status = _cairo_pdf_surface_open_stream (surface, - NULL, - TRUE, - " /Length1 %lu\n", - subset.data_length); - if (unlikely (status)) { - _cairo_truetype_subset_fini (&subset); - return status; - } - - stream = surface->pdf_stream.self; - _cairo_output_stream_write (surface->output, - subset.data, subset.data_length); - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) { - _cairo_truetype_subset_fini (&subset); - return status; - } - - status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, - &to_unicode_stream); - if (_cairo_int_status_is_error (status)) { - _cairo_truetype_subset_fini (&subset); - return status; - } - - descriptor = _cairo_pdf_surface_new_object (surface); - if (descriptor.id == 0) { - _cairo_truetype_subset_fini (&subset); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /FontDescriptor\n" - " /FontName /%s+%s\n", - descriptor.id, - tag, - subset.ps_name); - - if (subset.family_name_utf8) { - char *pdf_str; - - status = _cairo_utf8_to_pdf_string (subset.family_name_utf8, &pdf_str); - if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { - _cairo_output_stream_printf (surface->output, - " /FontFamily %s\n", - pdf_str); - free (pdf_str); - } else if (status != CAIRO_INT_STATUS_INVALID_STRING) { - return status; - } - } - - _cairo_output_stream_printf (surface->output, - " /Flags %d\n" - " /FontBBox [ %ld %ld %ld %ld ]\n" - " /ItalicAngle 0\n" - " /Ascent %ld\n" - " /Descent %ld\n" - " /CapHeight %ld\n" - " /StemV 80\n" - " /StemH 80\n" - " /FontFile2 %u 0 R\n" - ">>\n" - "endobj\n", - font_subset->is_latin ? 32 : 4, - (long)(subset.x_min*PDF_UNITS_PER_EM), - (long)(subset.y_min*PDF_UNITS_PER_EM), - (long)(subset.x_max*PDF_UNITS_PER_EM), - (long)(subset.y_max*PDF_UNITS_PER_EM), - (long)(subset.ascent*PDF_UNITS_PER_EM), - (long)(subset.descent*PDF_UNITS_PER_EM), - (long)(subset.y_max*PDF_UNITS_PER_EM), - stream.id); - - if (font_subset->is_latin) { - /* find last glyph used */ - for (i = 255; i >= 32; i--) - if (font_subset->latin_to_subset_glyph_index[i] > 0) - break; - - last_glyph = i; - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /TrueType\n" - " /BaseFont /%s+%s\n" - " /FirstChar 32\n" - " /LastChar %d\n" - " /FontDescriptor %d 0 R\n" - " /Encoding /WinAnsiEncoding\n" - " /Widths [", - subset_resource.id, - tag, - subset.ps_name, - last_glyph, - descriptor.id); - - for (i = 32; i < last_glyph + 1; i++) { - int glyph = font_subset->latin_to_subset_glyph_index[i]; - if (glyph > 0) { - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset.widths[glyph]*PDF_UNITS_PER_EM)); - } else { - _cairo_output_stream_printf (surface->output, " 0"); - } - } - - _cairo_output_stream_printf (surface->output, - " ]\n"); - - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - } else { - cidfont_dict = _cairo_pdf_surface_new_object (surface); - if (cidfont_dict.id == 0) { - _cairo_truetype_subset_fini (&subset); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /CIDFontType2\n" - " /BaseFont /%s+%s\n" - " /CIDSystemInfo\n" - " << /Registry (Adobe)\n" - " /Ordering (Identity)\n" - " /Supplement 0\n" - " >>\n" - " /FontDescriptor %d 0 R\n" - " /W [0 [", - cidfont_dict.id, - tag, - subset.ps_name, - descriptor.id); - - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " %ld", - (long)(subset.widths[i]*PDF_UNITS_PER_EM)); - - _cairo_output_stream_printf (surface->output, - " ]]\n" - ">>\n" - "endobj\n"); - - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type0\n" - " /BaseFont /%s+%s\n" - " /Encoding /Identity-H\n" - " /DescendantFonts [ %d 0 R]\n", - subset_resource.id, - tag, - subset.ps_name, - cidfont_dict.id); - - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - } - - font.font_id = font_subset->font_id; - font.subset_id = font_subset->subset_id; - font.subset_resource = subset_resource; - status = _cairo_array_append (&surface->fonts, &font); - - _cairo_truetype_subset_fini (&subset); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_emit_imagemask (cairo_image_surface_t *image, - cairo_output_stream_t *stream) -{ - uint8_t *byte, output_byte; - int row, col, num_cols; - - /* The only image type supported by Type 3 fonts are 1-bit image - * masks */ - assert (image->format == CAIRO_FORMAT_A1); - - _cairo_output_stream_printf (stream, - "BI\n" - "/IM true\n" - "/W %d\n" - "/H %d\n" - "/BPC 1\n" - "/D [1 0]\n", - image->width, - image->height); - - _cairo_output_stream_printf (stream, - "ID "); - - num_cols = (image->width + 7) / 8; - for (row = 0; row < image->height; row++) { - byte = image->data + row * image->stride; - for (col = 0; col < num_cols; col++) { - output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); - _cairo_output_stream_write (stream, &output_byte, 1); - byte++; - } - } - - _cairo_output_stream_printf (stream, - "\nEI\n"); - - return _cairo_output_stream_get_status (stream); -} - -static cairo_int_status_t -_cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset, - void *closure) -{ - cairo_pdf_surface_t *surface = closure; - cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; - cairo_int_status_t status2; - unsigned int i; - cairo_surface_t *type3_surface; - cairo_output_stream_t *null_stream; - - null_stream = _cairo_null_stream_create (); - type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, - null_stream, - _cairo_pdf_emit_imagemask, - surface->font_subsets, - FALSE); - if (unlikely (type3_surface->status)) { - status2 = _cairo_output_stream_destroy (null_stream); - return type3_surface->status; - } - - _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, - _cairo_pdf_surface_add_font, - surface); - - for (i = 0; i < font_subset->num_glyphs; i++) { - status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, - font_subset->glyphs[i]); - if (unlikely (status)) - break; - } - - cairo_surface_destroy (type3_surface); - status2 = _cairo_output_stream_destroy (null_stream); - if (status == CAIRO_INT_STATUS_SUCCESS) - status = status2; - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, - cairo_scaled_font_subset_t *font_subset) -{ - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - cairo_pdf_resource_t *glyphs, encoding, char_procs, subset_resource, to_unicode_stream; - cairo_pdf_font_t font; - double *widths; - unsigned int i; - cairo_box_t font_bbox = {{0,0},{0,0}}; - cairo_box_t bbox = {{0,0},{0,0}}; - cairo_surface_t *type3_surface; - - if (font_subset->num_glyphs == 0) - return CAIRO_STATUS_SUCCESS; - - subset_resource = _cairo_pdf_surface_get_font_resource (surface, - font_subset->font_id, - font_subset->subset_id); - if (subset_resource.id == 0) - return CAIRO_STATUS_SUCCESS; - - glyphs = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (cairo_pdf_resource_t)); - if (unlikely (glyphs == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - widths = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (double)); - if (unlikely (widths == NULL)) { - free (glyphs); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _cairo_pdf_group_resources_clear (&surface->resources); - type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, - NULL, - _cairo_pdf_emit_imagemask, - surface->font_subsets, - FALSE); - if (unlikely (type3_surface->status)) { - free (glyphs); - free (widths); - return type3_surface->status; - } - - _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, - _cairo_pdf_surface_add_font, - surface); - - for (i = 0; i < font_subset->num_glyphs; i++) { - status = _cairo_pdf_surface_open_stream (surface, - NULL, - surface->compress_content, - NULL); - if (unlikely (status)) - break; - - glyphs[i] = surface->pdf_stream.self; - status = _cairo_type3_glyph_surface_emit_glyph (type3_surface, - surface->output, - font_subset->glyphs[i], - &bbox, - &widths[i]); - if (unlikely (status)) - break; - - status = _cairo_pdf_surface_close_stream (surface); - if (unlikely (status)) - break; - - if (i == 0) { - font_bbox.p1.x = bbox.p1.x; - font_bbox.p1.y = bbox.p1.y; - font_bbox.p2.x = bbox.p2.x; - font_bbox.p2.y = bbox.p2.y; - } else { - if (bbox.p1.x < font_bbox.p1.x) - font_bbox.p1.x = bbox.p1.x; - if (bbox.p1.y < font_bbox.p1.y) - font_bbox.p1.y = bbox.p1.y; - if (bbox.p2.x > font_bbox.p2.x) - font_bbox.p2.x = bbox.p2.x; - if (bbox.p2.y > font_bbox.p2.y) - font_bbox.p2.y = bbox.p2.y; - } - } - cairo_surface_destroy (type3_surface); - if (unlikely (status)) { - free (glyphs); - free (widths); - return status; - } - - encoding = _cairo_pdf_surface_new_object (surface); - if (encoding.id == 0) { - free (glyphs); - free (widths); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Encoding\n" - " /Differences [0", encoding.id); - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " /%d", i); - _cairo_output_stream_printf (surface->output, - "]\n" - ">>\n" - "endobj\n"); - - char_procs = _cairo_pdf_surface_new_object (surface); - if (char_procs.id == 0) { - free (glyphs); - free (widths); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<<\n", char_procs.id); - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, - " /%d %d 0 R\n", - i, glyphs[i].id); - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - free (glyphs); - - status = _cairo_pdf_surface_emit_to_unicode_stream (surface, - font_subset, - &to_unicode_stream); - if (_cairo_int_status_is_error (status)) { - free (widths); - return status; - } - - _cairo_pdf_surface_update_object (surface, subset_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Font\n" - " /Subtype /Type3\n" - " /FontBBox [%f %f %f %f]\n" - " /FontMatrix [ 1 0 0 -1 0 0 ]\n" - " /Encoding %d 0 R\n" - " /CharProcs %d 0 R\n" - " /FirstChar 0\n" - " /LastChar %d\n", - subset_resource.id, - _cairo_fixed_to_double (font_bbox.p1.x), - _cairo_fixed_to_double (font_bbox.p1.y), - _cairo_fixed_to_double (font_bbox.p2.x), - _cairo_fixed_to_double (font_bbox.p2.y), - encoding.id, - char_procs.id, - font_subset->num_glyphs - 1); - - _cairo_output_stream_printf (surface->output, - " /Widths ["); - for (i = 0; i < font_subset->num_glyphs; i++) - _cairo_output_stream_printf (surface->output, " %f", widths[i]); - _cairo_output_stream_printf (surface->output, - "]\n"); - free (widths); - - _cairo_output_stream_printf (surface->output, - " /Resources\n"); - _cairo_pdf_surface_emit_group_resources (surface, &surface->resources); - - if (to_unicode_stream.id != 0) - _cairo_output_stream_printf (surface->output, - " /ToUnicode %d 0 R\n", - to_unicode_stream.id); - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - font.font_id = font_subset->font_id; - font.subset_id = font_subset->subset_id; - font.subset_resource = subset_resource; - return _cairo_array_append (&surface->fonts, &font); -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset, - void *closure) -{ - cairo_pdf_surface_t *surface = closure; - cairo_int_status_t status; - - status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - ASSERT_NOT_REACHED; - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset, - void *closure) -{ - cairo_pdf_surface_t *surface = closure; - cairo_int_status_t status; - - status = _cairo_pdf_surface_emit_type3_font_subset (surface, font_subset); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - ASSERT_NOT_REACHED; - return CAIRO_INT_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface) -{ - cairo_int_status_t status; - - status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, - _cairo_pdf_surface_analyze_user_font_subset, - surface); - if (unlikely (status)) - goto BAIL; - - status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, - _cairo_pdf_surface_emit_unscaled_font_subset, - surface); - if (unlikely (status)) - goto BAIL; - - status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, - _cairo_pdf_surface_emit_scaled_font_subset, - surface); - if (unlikely (status)) - goto BAIL; - - status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, - _cairo_pdf_surface_emit_scaled_font_subset, - surface); - -BAIL: - _cairo_scaled_font_subsets_destroy (surface->font_subsets); - surface->font_subsets = NULL; - - return status; -} - -static cairo_pdf_resource_t -_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface) -{ - cairo_pdf_resource_t catalog; - - catalog = _cairo_pdf_surface_new_object (surface); - if (catalog.id == 0) - return catalog; - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Catalog\n" - " /Pages %d 0 R\n", - catalog.id, - surface->pages_resource.id); - - if (surface->struct_tree_root.id != 0) { - _cairo_output_stream_printf (surface->output, - " /StructTreeRoot %d 0 R\n", - surface->struct_tree_root.id); - if (surface->tagged) { - _cairo_output_stream_printf (surface->output, - " /MarkInfo << /Marked true >>\n"); - } - } - - if (surface->outlines_dict_res.id != 0) { - _cairo_output_stream_printf (surface->output, - " /Outlines %d 0 R\n", - surface->outlines_dict_res.id); - } - - if (surface->page_labels_res.id != 0) { - _cairo_output_stream_printf (surface->output, - " /PageLabels %d 0 R\n", - surface->page_labels_res.id); - } - - if (surface->names_dict_res.id != 0) { - _cairo_output_stream_printf (surface->output, - " /Names %d 0 R\n", - surface->names_dict_res.id); - } - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - return catalog; -} - -static long -_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface) -{ - cairo_pdf_object_t *object; - int num_objects, i; - long offset; - char buffer[11]; - - num_objects = _cairo_array_num_elements (&surface->objects); - - offset = _cairo_output_stream_get_position (surface->output); - _cairo_output_stream_printf (surface->output, - "xref\n" - "%d %d\n", - 0, num_objects + 1); - - _cairo_output_stream_printf (surface->output, - "0000000000 65535 f \n"); - for (i = 0; i < num_objects; i++) { - object = _cairo_array_index (&surface->objects, i); - snprintf (buffer, sizeof buffer, "%010ld", object->offset); - _cairo_output_stream_printf (surface->output, - "%s 00000 n \n", buffer); - } - - return offset; -} - -static cairo_int_status_t -_cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, - cairo_pdf_smask_group_t *group) -{ - cairo_pdf_resource_t mask_group; - cairo_pdf_resource_t smask; - cairo_pdf_smask_group_t *smask_group; - cairo_pdf_resource_t pattern_res, gstate_res; - cairo_int_status_t status; - cairo_box_double_t bbox; - - /* Create mask group */ - _get_bbox_from_extents (&group->extents, &bbox); - status = _cairo_pdf_surface_open_group (surface, &bbox, NULL); - if (unlikely (status)) - return status; - - if (_can_paint_pattern (group->mask)) { - _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_surface_paint_pattern (surface, - CAIRO_OPERATOR_OVER, - group->mask, - &group->extents, - 1.0, /* alpha */ - FALSE); /* mask */ - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "Q\n"); - } else { - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, - CAIRO_OPERATOR_OVER, - NULL, - &pattern_res, &gstate_res); - if (unlikely (status)) - return status; - - if (gstate_res.id != 0) { - smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents); - if (unlikely (smask_group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - smask_group->width = group->width; - smask_group->height = group->height; - smask_group->operation = PDF_PAINT; - smask_group->source = cairo_pattern_reference (group->mask); - smask_group->source_res = pattern_res; - status = _cairo_pdf_surface_add_smask_group (surface, smask_group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (smask_group); - return status; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - smask_group->group_res.id); - } else { - status = _cairo_pdf_surface_select_pattern (surface, group->mask, pattern_res, FALSE); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "%f %f %f %f re f\n", - bbox.p1.x, - bbox.p1.y, - bbox.p2.x - bbox.p1.x, - bbox.p2.y - bbox.p1.y); - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; - } - } - - status = _cairo_pdf_surface_close_group (surface, &mask_group); - if (unlikely (status)) - return status; - - /* Create source group */ - status = _cairo_pdf_surface_open_group (surface, &bbox, &group->source_res); - if (unlikely (status)) - return status; - - if (_can_paint_pattern (group->source)) { - _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_surface_paint_pattern (surface, - CAIRO_OPERATOR_OVER, - group->source, - &group->extents, - 1.0, /* alpha */ - FALSE); /* mask */ - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "Q\n"); - } else { - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, - CAIRO_OPERATOR_OVER, - NULL, - &pattern_res, &gstate_res); - if (unlikely (status)) - return status; - - if (gstate_res.id != 0) { - smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents); - if (unlikely (smask_group == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - smask_group->operation = PDF_PAINT; - smask_group->source = cairo_pattern_reference (group->source); - smask_group->source_res = pattern_res; - status = _cairo_pdf_surface_add_smask_group (surface, smask_group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (smask_group); - return status; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - smask_group->group_res.id); - } else { - status = _cairo_pdf_surface_select_pattern (surface, group->source, pattern_res, FALSE); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "%f %f %f %f re f\n", - bbox.p1.x, - bbox.p1.y, - bbox.p2.x - bbox.p1.x, - bbox.p2.y - bbox.p1.y); - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; - } - } - - status = _cairo_pdf_surface_close_group (surface, NULL); - if (unlikely (status)) - return status; - - /* Create an smask based on the alpha component of mask_group */ - smask = _cairo_pdf_surface_new_object (surface); - if (smask.id == 0) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Mask\n" - " /S /Alpha\n" - " /G %d 0 R\n" - ">>\n" - "endobj\n", - smask.id, - mask_group.id); - - /* Create a GState that uses the smask */ - _cairo_pdf_surface_update_object (surface, group->group_res); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /ExtGState\n" - " /SMask %d 0 R\n" - " /ca 1\n" - " /CA 1\n" - " /AIS false\n" - ">>\n" - "endobj\n", - group->group_res.id, - smask.id); - - return _cairo_output_stream_get_status (surface->output); -} - -static cairo_int_status_t -_cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface, - cairo_pdf_smask_group_t *group) -{ - double old_width, old_height; - cairo_bool_t old_in_xobject; - cairo_int_status_t status; - cairo_box_double_t bbox; - cairo_rectangle_int_t old_surface_extents; - - old_width = surface->width; - old_height = surface->height; - old_surface_extents = surface->surface_extents; - old_in_xobject = surface->in_xobject; - surface->in_xobject = TRUE; - _cairo_pdf_surface_set_size_internal (surface, - group->width, - group->height); - _cairo_pdf_operators_reset (&surface->pdf_operators); - /* _mask is a special case that requires two groups - source - * and mask as well as a smask and gstate dictionary */ - if (group->operation == PDF_MASK) { - status = _cairo_pdf_surface_write_mask_group (surface, group); - goto RESTORE_SIZE; - } - - _get_bbox_from_extents (&group->extents, &bbox); - status = _cairo_pdf_surface_open_group (surface, &bbox, &group->group_res); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_select_pattern (surface, - group->source, - group->source_res, - group->operation == PDF_STROKE); - if (unlikely (status)) - return status; - - switch (group->operation) { - case PDF_PAINT: - _cairo_output_stream_printf (surface->output, - "0 0 %f %f re f\n", - surface->width, surface->height); - break; - case PDF_MASK: - ASSERT_NOT_REACHED; - break; - case PDF_FILL: - status = _cairo_pdf_operators_fill (&surface->pdf_operators, - &group->path, - group->fill_rule); - break; - case PDF_STROKE: - status = _cairo_pdf_operators_stroke (&surface->pdf_operators, - &group->path, - &group->style, - &group->ctm, - &group->ctm_inverse); - break; - case PDF_SHOW_GLYPHS: - status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, - group->utf8, group->utf8_len, - group->glyphs, group->num_glyphs, - group->clusters, group->num_clusters, - group->cluster_flags, - group->scaled_font); - break; - } - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_close_group (surface, NULL); - -RESTORE_SIZE: - surface->in_xobject = old_in_xobject; - _cairo_pdf_surface_set_size_internal (surface, - old_width, - old_height); - surface->surface_extents = old_surface_extents; - _cairo_pdf_operators_reset (&surface->pdf_operators); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface, - cairo_bool_t finish) -{ - cairo_pdf_pattern_t pattern; - cairo_pdf_smask_group_t *group; - cairo_pdf_source_surface_t src_surface; - unsigned int pattern_index, group_index, surface_index, doc_surface_index; - cairo_int_status_t status; - cairo_bool_t is_image; - - /* Writing out PDF_MASK groups will cause additional smask groups - * to be appended to surface->smask_groups. Additional patterns - * may also be appended to surface->patterns. - * - * Writing recording surface patterns will cause additional patterns - * and groups to be appended. - */ - pattern_index = 0; - group_index = 0; - surface_index = 0; - doc_surface_index = 0; - while ((pattern_index < _cairo_array_num_elements (&surface->page_patterns)) || - (group_index < _cairo_array_num_elements (&surface->smask_groups)) || - (surface_index < _cairo_array_num_elements (&surface->page_surfaces)) || - (finish && (doc_surface_index < _cairo_array_num_elements (&surface->doc_surfaces)))) - { - for (; group_index < _cairo_array_num_elements (&surface->smask_groups); group_index++) { - _cairo_array_copy_element (&surface->smask_groups, group_index, &group); - status = _cairo_pdf_surface_write_smask_group (surface, group); - if (unlikely (status)) - return status; - } - - for (; pattern_index < _cairo_array_num_elements (&surface->page_patterns); pattern_index++) { - _cairo_array_copy_element (&surface->page_patterns, pattern_index, &pattern); - status = _cairo_pdf_surface_emit_pattern (surface, &pattern); - if (unlikely (status)) - return status; - } - - for (; surface_index < _cairo_array_num_elements (&surface->page_surfaces); surface_index++) { - _cairo_array_copy_element (&surface->page_surfaces, surface_index, &src_surface); - status = _cairo_pdf_surface_emit_surface (surface, &src_surface, FALSE, &is_image); - if (unlikely (status)) - return status; - } - - if (finish) { - for (; doc_surface_index < _cairo_array_num_elements (&surface->doc_surfaces); doc_surface_index++) { - _cairo_array_copy_element (&surface->doc_surfaces, doc_surface_index, &src_surface); - status = _cairo_pdf_surface_emit_surface (surface, &src_surface, FALSE, &is_image); - if (unlikely (status)) - return status; - } - } - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) -{ - cairo_pdf_resource_t knockout, res, thumbnail_res; - cairo_pdf_resource_t *page; - cairo_int_status_t status; - unsigned int i, len, page_num, num_annots; - - status = _cairo_pdf_interchange_write_page_objects (surface); - if (unlikely (status)) - return status; - - _cairo_pdf_group_resources_clear (&surface->resources); - if (surface->has_fallback_images) { - cairo_rectangle_int_t extents; - cairo_box_double_t bbox; - - extents.x = 0; - extents.y = 0; - extents.width = ceil (surface->width); - extents.height = ceil (surface->height); - _get_bbox_from_extents (&extents, &bbox); - status = _cairo_pdf_surface_open_knockout_group (surface, &bbox); - if (unlikely (status)) - return status; - - len = _cairo_array_num_elements (&surface->knockout_group); - for (i = 0; i < len; i++) { - _cairo_array_copy_element (&surface->knockout_group, i, &res); - _cairo_output_stream_printf (surface->output, - "/x%d Do\n", - res.id); - status = _cairo_pdf_surface_add_xobject (surface, res); - if (unlikely (status)) - return status; - } - _cairo_output_stream_printf (surface->output, - "/x%d Do\n", - surface->content.id); - status = _cairo_pdf_surface_add_xobject (surface, surface->content); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_close_group (surface, &knockout); - if (unlikely (status)) - return status; - - _cairo_pdf_group_resources_clear (&surface->resources); - status = _cairo_pdf_surface_open_content_stream (surface, NULL, NULL, FALSE, FALSE); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, - "/x%d Do\n", - knockout.id); - status = _cairo_pdf_surface_add_xobject (surface, knockout); - if (unlikely (status)) - return status; - - status = _cairo_pdf_surface_close_content_stream (surface); - if (unlikely (status)) - return status; - } - - thumbnail_res.id = 0; - if (surface->thumbnail_image) { - cairo_pdf_source_surface_entry_t entry; - - memset (&entry, 0, sizeof (entry)); - thumbnail_res = _cairo_pdf_surface_new_object (surface); - entry.surface_res = thumbnail_res; - _cairo_pdf_surface_emit_image (surface, surface->thumbnail_image, &entry); - } - - page_num = _cairo_array_num_elements (&surface->pages); - page = _cairo_array_index (&surface->pages, page_num - 1); - _cairo_pdf_surface_update_object (surface, *page); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" - "<< /Type /Page %% %d\n" - " /Parent %d 0 R\n" - " /MediaBox [ 0 0 %f %f ]\n" - " /Contents %d 0 R\n" - " /Group <<\n" - " /Type /Group\n" - " /S /Transparency\n" - " /I true\n" - " /CS /DeviceRGB\n" - " >>\n" - " /Resources %d 0 R\n", - page->id, - page_num, - surface->pages_resource.id, - surface->width, - surface->height, - surface->content.id, - surface->content_resources.id); - - if (surface->page_parent_tree >= 0) { - _cairo_output_stream_printf (surface->output, - " /StructParents %d\n", - surface->page_parent_tree); - } - - num_annots = _cairo_array_num_elements (&surface->page_annots); - if (num_annots > 0) { - _cairo_output_stream_printf (surface->output, - " /Annots [ "); - for (i = 0; i < num_annots; i++) { - _cairo_array_copy_element (&surface->page_annots, i, &res); - _cairo_output_stream_printf (surface->output, - "%d 0 R ", - res.id); - } - _cairo_output_stream_printf (surface->output, "]\n"); - } - - if (thumbnail_res.id) { - _cairo_output_stream_printf (surface->output, - " /Thumb %d 0 R\n", - thumbnail_res.id); - } - - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); - - status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface, FALSE); - if (unlikely (status)) - return status; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_pdf_surface_analyze_surface_pattern_transparency (cairo_pdf_surface_t *surface, - cairo_surface_pattern_t *pattern) -{ - cairo_image_surface_t *image; - void *image_extra; - cairo_int_status_t status; - cairo_image_transparency_t transparency; - - status = _cairo_surface_acquire_source_image (pattern->surface, - &image, - &image_extra); - if (unlikely (status)) - return status; - - if (image->base.status) - return image->base.status; - - transparency = _cairo_image_analyze_transparency (image); - if (transparency == CAIRO_IMAGE_IS_OPAQUE) - status = CAIRO_STATUS_SUCCESS; - else - status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; - - _cairo_surface_release_source_image (pattern->surface, image, image_extra); - - return status; -} - -static cairo_bool_t -_surface_pattern_supported (cairo_surface_pattern_t *pattern) -{ - cairo_extend_t extend; - - if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) - return TRUE; - - if (pattern->surface->backend->acquire_source_image == NULL) - return FALSE; - - /* Does an ALPHA-only source surface even make sense? Maybe, but I - * don't think it's worth the extra code to support it. */ - -/* XXX: Need to write this function here... - if (pattern->surface->content == CAIRO_CONTENT_ALPHA) - return FALSE; -*/ - - extend = cairo_pattern_get_extend (&pattern->base); - switch (extend) { - case CAIRO_EXTEND_NONE: - case CAIRO_EXTEND_REPEAT: - case CAIRO_EXTEND_REFLECT: - /* There's no point returning FALSE for EXTEND_PAD, as the image - * surface does not currently implement it either */ - case CAIRO_EXTEND_PAD: - return TRUE; - } - - ASSERT_NOT_REACHED; - return FALSE; -} - -static cairo_bool_t -_pattern_supported (const cairo_pattern_t *pattern) -{ - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - case CAIRO_PATTERN_TYPE_MESH: - case CAIRO_PATTERN_TYPE_RASTER_SOURCE: - return TRUE; - - case CAIRO_PATTERN_TYPE_SURFACE: - return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern); - - default: - ASSERT_NOT_REACHED; - return FALSE; - } -} - -static cairo_bool_t -_pdf_operator_supported (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return TRUE; - - default: - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - 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 FALSE; - } -} - -static cairo_int_status_t -_cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *extents) -{ - if (surface->force_fallbacks && - surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (! _pattern_supported (pattern)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (_pdf_operator_supported (op)) { - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - - if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (pattern->extend == CAIRO_EXTEND_PAD) { - cairo_box_t box; - cairo_rectangle_int_t rect; - cairo_rectangle_int_t rec_extents; - - /* get the operation extents in pattern space */ - _cairo_box_from_rectangle (&box, extents); - _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL); - _cairo_box_round_to_rectangle (&box, &rect); - - /* Check if surface needs padding to fill extents */ - if (_cairo_surface_get_extents (surface_pattern->surface, &rec_extents)) { - if (_cairo_fixed_integer_ceil(box.p1.x) < rec_extents.x || - _cairo_fixed_integer_ceil(box.p1.y) < rec_extents.y || - _cairo_fixed_integer_floor(box.p2.y) > rec_extents.x + rec_extents.width || - _cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } - } - return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; - } - } - - return CAIRO_STATUS_SUCCESS; - } - - - /* The SOURCE operator is supported if the pattern is opaque or if - * there is nothing painted underneath. */ - if (op == CAIRO_OPERATOR_SOURCE) { - if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; - - if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { - if (_cairo_pattern_is_opaque (pattern, extents)) { - return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; - } else { - /* FIXME: The analysis surface does not yet have - * the capability to analyze a non opaque recording - * surface and mark it supported if there is - * nothing underneath. For now recording surfaces of - * type CONTENT_COLOR_ALPHA painted with - * OPERATOR_SOURCE will result in a fallback - * image. */ - - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } else { - return _cairo_pdf_surface_analyze_surface_pattern_transparency (surface, - surface_pattern); - } - } - - if (_cairo_pattern_is_opaque (pattern, extents)) - return CAIRO_STATUS_SUCCESS; - else - return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_bool_t -_cairo_pdf_surface_operation_supported (cairo_pdf_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *extents) -{ - return _cairo_pdf_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) -{ - cairo_box_double_t bbox; - cairo_int_status_t status; - - status = _cairo_pdf_surface_close_content_stream (surface); - if (unlikely (status)) - return status; - - status = _cairo_array_append (&surface->knockout_group, &surface->content); - if (unlikely (status)) - return status; - - _cairo_pdf_group_resources_clear (&surface->resources); - bbox.p1.x = 0; - bbox.p1.y = 0; - bbox.p2.x = surface->width; - bbox.p2.y = surface->height; - status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE, TRUE); - if (unlikely (status)) - return status; - - return _cairo_pdf_interchange_begin_page_content (surface); -} - -/* If source is an opaque image and mask is an image and both images - * have the same bounding box we can emit them as a image/smask pair. - */ -static cairo_int_status_t -_cairo_pdf_surface_emit_combined_smask (cairo_pdf_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - const cairo_rectangle_int_t *extents) -{ - cairo_int_status_t status; - cairo_image_surface_t *image; - void *image_extra; - cairo_image_transparency_t transparency; - int src_width, src_height; - int mask_width, mask_height; - double src_x_offset, src_y_offset; - double mask_x_offset, mask_y_offset; - double src_x1, src_y1, src_x2, src_y2; - double mask_x1, mask_y1, mask_x2, mask_y2; - cairo_matrix_t p2u; - double src_radius, mask_radius, e; - cairo_bool_t need_smask; - cairo_pdf_source_surface_entry_t *pdf_source; - - /* Check that source and mask are images */ - - if (!((source->type == CAIRO_PATTERN_TYPE_SURFACE || source->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) && - (mask->type == CAIRO_PATTERN_TYPE_SURFACE || mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE))) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (source->type == CAIRO_PATTERN_TYPE_SURFACE && - ((cairo_surface_pattern_t *) source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && - ((cairo_surface_pattern_t *) mask)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (source->extend != CAIRO_EXTEND_NONE || mask->extend != CAIRO_EXTEND_NONE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Check that source is opaque and get image sizes */ - - status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source, - &image, &image_extra); - if (unlikely (status)) - return status; - - if (image->base.status) - return image->base.status; - - src_width = image->width; - src_height = image->height; - if (source->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { - cairo_surface_get_device_offset (&image->base, &src_x_offset, &src_y_offset); - } else { - src_x_offset = 0; - src_y_offset = 0; - } - - transparency = _cairo_image_analyze_transparency (image); - _cairo_pdf_surface_release_source_image_from_pattern (surface, source, image, image_extra); - - if (transparency != CAIRO_IMAGE_IS_OPAQUE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, mask, - &image, &image_extra); - if (unlikely (status)) - return status; - - if (image->base.status) - return image->base.status; - - mask_width = image->width; - mask_height = image->height; - if (mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) { - cairo_surface_get_device_offset (&image->base, &mask_x_offset, &mask_y_offset); - } else { - mask_x_offset = 0; - mask_y_offset = 0; - } - - transparency = _cairo_image_analyze_transparency (image); - need_smask = transparency != CAIRO_IMAGE_IS_OPAQUE; - - _cairo_pdf_surface_release_source_image_from_pattern (surface, mask, image, image_extra); - - /* Check that both images have the same extents with a tolerance - * of half the smallest source pixel. */ - - p2u = source->matrix; - status = cairo_matrix_invert (&p2u); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_INT_STATUS_SUCCESS); - src_x1 = 0; - src_y1 = 0; - src_x2 = src_width; - src_y2 = src_height; - cairo_matrix_transform_point (&p2u, &src_x1, &src_y1); - cairo_matrix_transform_point (&p2u, &src_x2, &src_y2); - src_radius = _cairo_matrix_transformed_circle_major_axis (&p2u, 0.5); - - p2u = mask->matrix; - status = cairo_matrix_invert (&p2u); - /* cairo_pattern_set_matrix ensures the matrix is invertible */ - assert (status == CAIRO_INT_STATUS_SUCCESS); - mask_x1 = 0; - mask_y1 = 0; - mask_x2 = mask_width; - mask_y2 = mask_height; - cairo_matrix_transform_point (&p2u, &mask_x1, &mask_y1); - cairo_matrix_transform_point (&p2u, &mask_x2, &mask_y2); - mask_radius = _cairo_matrix_transformed_circle_major_axis (&p2u, 0.5); - - if (src_radius < mask_radius) - e = src_radius; - else - e = mask_radius; - - if (fabs(src_x1 - mask_x1) > e || - fabs(src_x2 - mask_x2) > e || - fabs(src_y1 - mask_y1) > e || - fabs(src_y2 - mask_y2) > e) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Check both images have same device offset */ - if (fabs(src_x_offset - mask_x_offset) > e || - fabs(src_y_offset - mask_y_offset) > e) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (need_smask) { - status = _cairo_pdf_surface_add_source_surface (surface, - NULL, - mask, - op, - source->filter, - FALSE, /* stencil mask */ - TRUE, /* smask */ - FALSE, /* need_transp_group */ - extents, - NULL, /* smask_res */ - &pdf_source, - NULL, - NULL, - NULL); - if (unlikely (status)) - return status; - } - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_surface_paint_surface_pattern (surface, op, source, extents, - 1.0, /* alpha */ - need_smask ? &pdf_source->surface_res : NULL, - FALSE); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "Q\n"); - - status = _cairo_output_stream_get_status (surface->output); - - - return status; -} - -/* A PDF stencil mask is an A1 mask used with the current color */ -static cairo_int_status_t -_cairo_pdf_surface_emit_stencil_mask (cairo_pdf_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - const cairo_rectangle_int_t *extents) -{ - cairo_int_status_t status; - cairo_image_surface_t *image; - void *image_extra; - cairo_image_transparency_t transparency; - cairo_pdf_resource_t pattern_res = {0}; - - if (! (source->type == CAIRO_PATTERN_TYPE_SOLID && - (mask->type == CAIRO_PATTERN_TYPE_SURFACE || mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE))) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && - ((cairo_surface_pattern_t *) mask)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, mask, - &image, &image_extra); - if (unlikely (status)) - return status; - - if (image->base.status) - return image->base.status; - - transparency = _cairo_image_analyze_transparency (image); - if (transparency != CAIRO_IMAGE_IS_OPAQUE && - transparency != CAIRO_IMAGE_HAS_BILEVEL_ALPHA) - { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto cleanup; - } - - status = _cairo_pdf_surface_select_pattern (surface, source, - pattern_res, FALSE); - if (unlikely (status)) - return status; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_surface_paint_surface_pattern (surface, op, mask, extents, 1.0, NULL, TRUE); - if (unlikely (status)) - return status; - - _cairo_output_stream_printf (surface->output, "Q\n"); - - status = _cairo_output_stream_get_status (surface->output); - -cleanup: - _cairo_pdf_surface_release_source_image_from_pattern (surface, mask, image, image_extra); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_set_clip (cairo_pdf_surface_t *surface, - cairo_composite_rectangles_t *composite) -{ - cairo_clip_t *clip = composite->clip; - - if (_cairo_composite_rectangles_can_reduce_clip (composite, clip)) - clip = NULL; - - if (clip == NULL) { - if (_cairo_composite_rectangles_can_reduce_clip (composite, - surface->clipper.clip)) - return CAIRO_STATUS_SUCCESS; - } - - return _cairo_surface_clipper_set_clip (&surface->clipper, clip); -} - -static cairo_int_status_t -_cairo_pdf_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_smask_group_t *group; - cairo_pdf_resource_t pattern_res, gstate_res; - cairo_composite_rectangles_t extents; - cairo_int_status_t status; - - status = _cairo_composite_rectangles_init_for_paint (&extents, - &surface->base, - op, source, clip); - if (unlikely (status)) - return status; - - status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); - if (unlikely (status)) - return status; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); - goto cleanup; - } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { - status = _cairo_pdf_surface_start_fallback (surface); - if (unlikely (status)) - goto cleanup; - } - - assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - - status = _cairo_pdf_surface_set_clip (surface, &extents); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_select_operator (surface, op); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - goto cleanup; - - if (_can_paint_pattern (source)) { - _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_surface_paint_pattern (surface, - op, - source, - &extents.bounded, - 1.0, /* alpha */ - FALSE); /* mask */ - if (unlikely (status)) - goto cleanup; - - _cairo_output_stream_printf (surface->output, "Q\n"); - _cairo_composite_rectangles_fini (&extents); - return _cairo_output_stream_get_status (surface->output); - } - - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, - &extents.bounded, - &pattern_res, &gstate_res); - if (unlikely (status)) - goto cleanup; - - if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); - if (unlikely (group == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto cleanup; - } - - group->operation = PDF_PAINT; - status = _cairo_pattern_create_copy (&group->source, source); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - group->source_res = pattern_res; - status = _cairo_pdf_surface_add_smask_group (surface, group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_add_xobject (surface, group->group_res); - if (unlikely (status)) - goto cleanup; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - group->group_res.id); - } else { - status = _cairo_pdf_surface_select_pattern (surface, source, - pattern_res, FALSE); - if (unlikely (status)) - goto cleanup; - - _cairo_output_stream_printf (surface->output, - "%d %d %d %d re f\n", - surface->surface_extents.x, - surface->surface_extents.y, - surface->surface_extents.width, - surface->surface_extents.height); - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - goto cleanup; - } - - _cairo_composite_rectangles_fini (&extents); - return _cairo_output_stream_get_status (surface->output); - -cleanup: - _cairo_composite_rectangles_fini (&extents); - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_mask (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - const cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_smask_group_t *group; - cairo_composite_rectangles_t extents; - cairo_int_status_t status; - cairo_rectangle_int_t r; - cairo_box_t box; - double alpha; - - status = _cairo_composite_rectangles_init_for_mask (&extents, - &surface->base, - op, source, mask, clip); - if (unlikely (status)) - return status; - - status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); - if (unlikely (status)) - return status; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - cairo_int_status_t source_status, mask_status; - - status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); - if (_cairo_int_status_is_error (status)) - goto cleanup; - source_status = status; - - if (mask->has_component_alpha) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - } else { - status = _cairo_pdf_surface_analyze_operation (surface, op, mask, &extents.bounded); - if (_cairo_int_status_is_error (status)) - goto cleanup; - } - mask_status = status; - - _cairo_composite_rectangles_fini (&extents); - return _cairo_analysis_surface_merge_status (source_status, - mask_status); - } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { - status = _cairo_pdf_surface_start_fallback (surface); - if (unlikely (status)) - goto cleanup; - } - - assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - assert (_cairo_pdf_surface_operation_supported (surface, op, mask, &extents.bounded)); - - /* get the accurate extents */ - status = _cairo_pattern_get_ink_extents (source, &r); - if (unlikely (status)) - goto cleanup; - - /* XXX slight impedance mismatch */ - _cairo_box_from_rectangle (&box, &r); - status = _cairo_composite_rectangles_intersect_source_extents (&extents, - &box); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pattern_get_ink_extents (mask, &r); - if (unlikely (status)) - goto cleanup; - - _cairo_box_from_rectangle (&box, &r); - status = _cairo_composite_rectangles_intersect_mask_extents (&extents, - &box); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_set_clip (surface, &extents); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_select_operator (surface, op); - if (unlikely (status)) - goto cleanup; - - /* Check if we can combine source and mask into a smask image */ - status = _cairo_pdf_surface_emit_combined_smask (surface, op, source, mask, &extents.bounded); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto cleanup; - - /* Check if we can use a stencil mask */ - status = _cairo_pdf_surface_emit_stencil_mask (surface, op, source, mask, &extents.bounded); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto cleanup; - - /* Check if we can set ca/CA instead of an smask. We could handle - * other source patterns as well but for now this is the easiest, - * and most common, case to handle. */ - if (_cairo_pattern_is_constant_alpha (mask, &extents.bounded, &alpha) && - _can_paint_pattern (source)) { - _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_surface_paint_pattern (surface, - op, - source, - &extents.bounded, - alpha, - FALSE); /* mask */ - if (unlikely (status)) - goto cleanup; - - _cairo_output_stream_printf (surface->output, "Q\n"); - _cairo_composite_rectangles_fini (&extents); - return _cairo_output_stream_get_status (surface->output); - } - - group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); - if (unlikely (group == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto cleanup; - } - - group->operation = PDF_MASK; - status = _cairo_pattern_create_copy (&group->source, source); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - status = _cairo_pattern_create_copy (&group->mask, mask); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - group->source_res = _cairo_pdf_surface_new_object (surface); - if (group->source_res.id == 0) { - _cairo_pdf_smask_group_destroy (group); - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto cleanup; - } - - status = _cairo_pdf_surface_add_smask_group (surface, group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - - status = _cairo_pdf_surface_add_smask (surface, group->group_res); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_add_xobject (surface, group->source_res); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - goto cleanup; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - group->group_res.id, - group->source_res.id); - - _cairo_composite_rectangles_fini (&extents); - return _cairo_output_stream_get_status (surface->output); - -cleanup: - _cairo_composite_rectangles_fini (&extents); - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_stroke (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t *path, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_antialias_t antialias, - const cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_smask_group_t *group; - cairo_pdf_resource_t pattern_res, gstate_res; - cairo_composite_rectangles_t extents; - cairo_int_status_t status; - - status = _cairo_composite_rectangles_init_for_stroke (&extents, - &surface->base, - op, source, - path, style, ctm, - clip); - if (unlikely (status)) - return status; - - /* use the more accurate extents */ - if (extents.is_bounded) { - cairo_rectangle_int_t mask; - cairo_box_t box; - - status = _cairo_path_fixed_stroke_extents (path, style, - ctm, ctm_inverse, - tolerance, - &mask); - if (unlikely (status)) - goto cleanup; - - _cairo_box_from_rectangle (&box, &mask); - status = _cairo_composite_rectangles_intersect_mask_extents (&extents, - &box); - if (unlikely (status)) - goto cleanup; - } - - status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); - if (unlikely (status)) - goto cleanup; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); - goto cleanup; - } - - assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - - status = _cairo_pdf_surface_set_clip (surface, &extents); - if (unlikely (status)) - goto cleanup; - - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, - &extents.bounded, - &pattern_res, &gstate_res); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_select_operator (surface, op); - if (unlikely (status)) - goto cleanup; - - if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); - if (unlikely (group == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto cleanup; - } - - group->operation = PDF_STROKE; - status = _cairo_pattern_create_copy (&group->source, source); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - group->source_res = pattern_res; - status = _cairo_path_fixed_init_copy (&group->path, path); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - - group->style = *style; - group->ctm = *ctm; - group->ctm_inverse = *ctm_inverse; - status = _cairo_pdf_surface_add_smask_group (surface, group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_add_xobject (surface, group->group_res); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - goto cleanup; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - group->group_res.id); - } else { - status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, TRUE); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_operators_stroke (&surface->pdf_operators, - path, - style, - ctm, - ctm_inverse); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - goto cleanup; - } - - _cairo_composite_rectangles_fini (&extents); - return _cairo_output_stream_get_status (surface->output); - -cleanup: - _cairo_composite_rectangles_fini (&extents); - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_fill (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t*path, - cairo_fill_rule_t fill_rule, - double tolerance, - cairo_antialias_t antialias, - const cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_int_status_t status; - cairo_pdf_smask_group_t *group; - cairo_pdf_resource_t pattern_res, gstate_res; - cairo_composite_rectangles_t extents; - - status = _cairo_composite_rectangles_init_for_fill (&extents, - &surface->base, - op, source, path, - clip); - if (unlikely (status)) - return status; - - /* use the more accurate extents */ - if (extents.is_bounded) { - cairo_rectangle_int_t mask; - cairo_box_t box; - - _cairo_path_fixed_fill_extents (path, - fill_rule, - tolerance, - &mask); - - _cairo_box_from_rectangle (&box, &mask); - status = _cairo_composite_rectangles_intersect_mask_extents (&extents, - &box); - if (unlikely (status)) - goto cleanup; - } - - status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); - if (unlikely (status)) - goto cleanup; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); - goto cleanup; - } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { - status = _cairo_pdf_surface_start_fallback (surface); - if (unlikely (status)) - goto cleanup; - } - - assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - - status = _cairo_pdf_surface_set_clip (surface, &extents); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_select_operator (surface, op); - if (unlikely (status)) - goto cleanup; - - if (_can_paint_pattern (source)) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - goto cleanup; - - _cairo_output_stream_printf (surface->output, "q\n"); - status = _cairo_pdf_operators_clip (&surface->pdf_operators, - path, - fill_rule); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_paint_pattern (surface, - op, - source, - &extents.bounded, - 1.0, /* alpha */ - FALSE); /* mask */ - if (unlikely (status)) - goto cleanup; - - _cairo_output_stream_printf (surface->output, "Q\n"); - status = _cairo_output_stream_get_status (surface->output); - goto cleanup; - } - - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, - &extents.bounded, - &pattern_res, &gstate_res); - if (unlikely (status)) - goto cleanup; - - if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); - if (unlikely (group == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto cleanup; - } - - group->operation = PDF_FILL; - status = _cairo_pattern_create_copy (&group->source, source); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - group->source_res = pattern_res; - status = _cairo_path_fixed_init_copy (&group->path, path); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - - group->fill_rule = fill_rule; - status = _cairo_pdf_surface_add_smask_group (surface, group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_add_xobject (surface, group->group_res); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - goto cleanup; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - group->group_res.id); - } else { - status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_operators_fill (&surface->pdf_operators, - path, - fill_rule); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - goto cleanup; - } - - _cairo_composite_rectangles_fini (&extents); - return _cairo_output_stream_get_status (surface->output); - -cleanup: - _cairo_composite_rectangles_fini (&extents); - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_fill_stroke (void *abstract_surface, - cairo_operator_t fill_op, - const cairo_pattern_t *fill_source, - cairo_fill_rule_t fill_rule, - double fill_tolerance, - cairo_antialias_t fill_antialias, - const cairo_path_fixed_t*path, - cairo_operator_t stroke_op, - const cairo_pattern_t *stroke_source, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *stroke_ctm, - const cairo_matrix_t *stroke_ctm_inverse, - double stroke_tolerance, - cairo_antialias_t stroke_antialias, - const cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_int_status_t status; - cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res; - cairo_composite_rectangles_t extents; - - /* During analysis we return unsupported and let the _fill and - * _stroke functions that are on the fallback path do the analysis - * for us. During render we may still encounter unsupported - * combinations of fill/stroke patterns. However we can return - * unsupported anytime to let the _fill and _stroke functions take - * over. - */ - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* PDF rendering of fill-stroke is not the same as cairo when - * either the fill or stroke is not opaque. - */ - if ( !_cairo_pattern_is_opaque (fill_source, NULL) || - !_cairo_pattern_is_opaque (stroke_source, NULL)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (fill_op != stroke_op) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* Compute the operation extents using the stroke which will naturally - * be larger than the fill extents. - */ - status = _cairo_composite_rectangles_init_for_stroke (&extents, - &surface->base, - stroke_op, stroke_source, - path, stroke_style, stroke_ctm, - clip); - if (unlikely (status)) - return status; - - /* use the more accurate extents */ - if (extents.is_bounded) { - cairo_rectangle_int_t mask; - cairo_box_t box; - - status = _cairo_path_fixed_stroke_extents (path, stroke_style, - stroke_ctm, stroke_ctm_inverse, - stroke_tolerance, - &mask); - if (unlikely (status)) - goto cleanup; - - _cairo_box_from_rectangle (&box, &mask); - status = _cairo_composite_rectangles_intersect_mask_extents (&extents, - &box); - if (unlikely (status)) - goto cleanup; - } - - status = _cairo_pdf_surface_set_clip (surface, &extents); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_select_operator (surface, fill_op); - if (unlikely (status)) - goto cleanup; - - /* use the more accurate extents */ - if (extents.is_bounded) { - cairo_rectangle_int_t mask; - cairo_box_t box; - - _cairo_path_fixed_fill_extents (path, - fill_rule, - fill_tolerance, - &mask); - - _cairo_box_from_rectangle (&box, &mask); - status = _cairo_composite_rectangles_intersect_mask_extents (&extents, - &box); - if (unlikely (status)) - goto cleanup; - } - - status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); - if (unlikely (status)) - goto cleanup; - - fill_pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, - fill_op, - &extents.bounded, - &fill_pattern_res, - &gstate_res); - if (unlikely (status)) - goto cleanup; - - assert (gstate_res.id == 0); - - stroke_pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, - stroke_source, - stroke_op, - &extents.bounded, - &stroke_pattern_res, - &gstate_res); - if (unlikely (status)) - goto cleanup; - - assert (gstate_res.id == 0); - - /* As PDF has separate graphics state for fill and stroke we can - * select both at the same time */ - status = _cairo_pdf_surface_select_pattern (surface, fill_source, - fill_pattern_res, FALSE); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_select_pattern (surface, stroke_source, - stroke_pattern_res, TRUE); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators, - path, - fill_rule, - stroke_style, - stroke_ctm, - stroke_ctm_inverse); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - goto cleanup; - - _cairo_composite_rectangles_fini (&extents); - return _cairo_output_stream_get_status (surface->output); - -cleanup: - _cairo_composite_rectangles_fini (&extents); - return status; -} - -static cairo_bool_t -_cairo_pdf_surface_has_show_text_glyphs (void *abstract_surface) -{ - return TRUE; -} - -static cairo_int_status_t -_cairo_pdf_surface_show_text_glyphs (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const char *utf8, - int utf8_len, - cairo_glyph_t *glyphs, - int num_glyphs, - const cairo_text_cluster_t *clusters, - int num_clusters, - cairo_text_cluster_flags_t cluster_flags, - cairo_scaled_font_t *scaled_font, - const cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_smask_group_t *group; - cairo_pdf_resource_t pattern_res, gstate_res; - cairo_composite_rectangles_t extents; - cairo_bool_t overlap; - cairo_int_status_t status; - - status = _cairo_composite_rectangles_init_for_glyphs (&extents, - &surface->base, - op, source, - scaled_font, - glyphs, num_glyphs, - clip, - &overlap); - if (unlikely (status)) - return status; - - status = _cairo_pdf_interchange_add_operation_extents (surface, &extents.bounded); - if (unlikely (status)) - return status; - - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { - status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); - goto cleanup; - } - - assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); - - status = _cairo_pdf_surface_set_clip (surface, &extents); - if (unlikely (status)) - goto cleanup; - - pattern_res.id = 0; - gstate_res.id = 0; - status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, - &extents.bounded, - &pattern_res, &gstate_res); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_select_operator (surface, op); - if (unlikely (status)) - goto cleanup; - - if (gstate_res.id != 0) { - group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded); - if (unlikely (group == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto cleanup; - } - - group->operation = PDF_SHOW_GLYPHS; - status = _cairo_pattern_create_copy (&group->source, source); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - group->source_res = pattern_res; - - if (utf8_len) { - group->utf8 = _cairo_malloc (utf8_len); - if (unlikely (group->utf8 == NULL)) { - _cairo_pdf_smask_group_destroy (group); - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto cleanup; - } - memcpy (group->utf8, utf8, utf8_len); - } - group->utf8_len = utf8_len; - - if (num_glyphs) { - group->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); - if (unlikely (group->glyphs == NULL)) { - _cairo_pdf_smask_group_destroy (group); - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto cleanup; - } - memcpy (group->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); - } - group->num_glyphs = num_glyphs; - - if (num_clusters) { - group->clusters = _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t)); - if (unlikely (group->clusters == NULL)) { - _cairo_pdf_smask_group_destroy (group); - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto cleanup; - } - memcpy (group->clusters, clusters, sizeof (cairo_text_cluster_t) * num_clusters); - } - group->num_clusters = num_clusters; - - group->scaled_font = cairo_scaled_font_reference (scaled_font); - status = _cairo_pdf_surface_add_smask_group (surface, group); - if (unlikely (status)) { - _cairo_pdf_smask_group_destroy (group); - goto cleanup; - } - - status = _cairo_pdf_surface_add_smask (surface, gstate_res); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_add_xobject (surface, group->group_res); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - goto cleanup; - - _cairo_output_stream_printf (surface->output, - "q /s%d gs /x%d Do Q\n", - gstate_res.id, - group->group_res.id); - } else { - status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); - if (unlikely (status)) - goto cleanup; - - /* Each call to show_glyphs() with a transclucent pattern must - * be in a separate text object otherwise overlapping text - * from separate calls to show_glyphs will not composite with - * each other. */ - if (! _cairo_pattern_is_opaque (source, &extents.bounded)) { - status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - goto cleanup; - } - - status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags, - scaled_font); - if (unlikely (status)) - goto cleanup; - - status = _cairo_pdf_surface_unselect_pattern (surface); - if (unlikely (status)) - goto cleanup; - } - - _cairo_composite_rectangles_fini (&extents); - return _cairo_output_stream_get_status (surface->output); - -cleanup: - _cairo_composite_rectangles_fini (&extents); - return status; -} - -static const char ** -_cairo_pdf_surface_get_supported_mime_types (void *abstract_surface) -{ - return _cairo_pdf_supported_mime_types; -} - -static cairo_int_status_t -_cairo_pdf_surface_tag (void *abstract_surface, - cairo_bool_t begin, - const char *tag_name, - const char *attributes, - const cairo_pattern_t *source, - const cairo_stroke_style_t *style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - const cairo_clip_t *clip) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_int_status_t status = 0; - - if (begin) - status = _cairo_pdf_interchange_tag_begin (surface, tag_name, attributes); - else - status = _cairo_pdf_interchange_tag_end (surface, tag_name); - - return status; -} - -static cairo_int_status_t -_cairo_pdf_surface_set_paginated_mode (void *abstract_surface, - cairo_paginated_mode_t paginated_mode) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_int_status_t status; - - surface->paginated_mode = paginated_mode; - status = _cairo_pdf_interchange_begin_page_content (surface); - if (unlikely (status)) - return status; - - if (paginated_mode == CAIRO_PAGINATED_MODE_RENDER) { - surface->surface_extents.x = 0; - surface->surface_extents.y = 0; - surface->surface_extents.width = ceil (surface->width); - surface->surface_extents.height = ceil (surface->height); - } - - return CAIRO_INT_STATUS_SUCCESS; -} - -static const cairo_surface_backend_t cairo_pdf_surface_backend = { - CAIRO_SURFACE_TYPE_PDF, - _cairo_pdf_surface_finish, - - _cairo_default_context_create, - - NULL, /* create similar: handled by wrapper */ - NULL, /* create similar image */ - NULL, /* map to image */ - NULL, /* unmap image */ - - _cairo_surface_default_source, - NULL, /* acquire_source_image */ - NULL, /* release_source_image */ - NULL, /* snapshot */ - - NULL, /* _cairo_pdf_surface_copy_page */ - _cairo_pdf_surface_show_page, - - _cairo_pdf_surface_get_extents, - _cairo_pdf_surface_get_font_options, - - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - - /* Here are the drawing functions */ - _cairo_pdf_surface_paint, - _cairo_pdf_surface_mask, - _cairo_pdf_surface_stroke, - _cairo_pdf_surface_fill, - _cairo_pdf_surface_fill_stroke, - NULL, /* show_glyphs */ - _cairo_pdf_surface_has_show_text_glyphs, - _cairo_pdf_surface_show_text_glyphs, - _cairo_pdf_surface_get_supported_mime_types, - _cairo_pdf_surface_tag, -}; - -static const cairo_paginated_surface_backend_t -cairo_pdf_surface_paginated_backend = { - _cairo_pdf_surface_start_page, - _cairo_pdf_surface_set_paginated_mode, - NULL, /* set_bounding_box */ - _cairo_pdf_surface_has_fallback_images, - _cairo_pdf_surface_supports_fine_grained_fallbacks, - _cairo_pdf_surface_requires_thumbnail_image, - _cairo_pdf_surface_set_thumbnail_image, -}; |