/* * Page Structure of the Generated PDF: * * Each page requiring fallbacks images contains a knockout group at * the top level. The first operation of the knockout group paints a * group containing all the supported drawing operations. Fallback * images (if any) are painted in the knockout group. This ensures * that fallback images do not composite with any content under the * fallback images. * * Streams: * * This PDF surface has three types of streams: * - PDF Stream * - Content Stream * - Group Stream * - Object stream * * Calling _cairo_output_stream_printf (surface->output, ...) will * write to the currently open stream. * * PDF Stream: * A PDF Stream may be opened and closed with the following functions: * _cairo_pdf_surface_open stream () * _cairo_pdf_surface_close_stream () * * PDF Streams are written directly to the PDF file. They are used for * fonts, images and patterns. * * Content Stream: * The Content Stream is opened and closed with the following functions: * _cairo_pdf_surface_open_content_stream () * _cairo_pdf_surface_close_content_stream () * * The Content Stream contains the text and graphics operators. * * Group Stream: * A Group Stream may be opened and closed with the following functions: * _cairo_pdf_surface_open_group () * _cairo_pdf_surface_close_group () * * A Group Stream is a Form XObject. It is used for short sequences * of operators. As the content is very short the group is stored in * memory until it is closed. This allows some optimization such as * including the Resource dictionary and stream length inside the * XObject instead of using an indirect object. * * Object Stream (PDF 1.5) * An Object Stream may be opened and closed with the following functions: * _cairo_pdf_surface_open_object_stream () * _cairo_pdf_surface_close_object_stream () * * An Object Stream contains one or more objects compressed into a stream. * Only non stream objects are permitted. When emitting objects intended for * the Object Stream, enclose the emit object operation with * _cairo_pdf_surface_object_begin()/_cairo_pdf_surface_object_end().
*/
/** * SECTION:cairo-pdf * @Title: PDF Surfaces * @Short_Description: Rendering PDF documents * @See_Also: #cairo_surface_t * * The PDF surface is used to render cairo graphics to Adobe * PDF files and is a multi-page vector surface backend. * * The following mime types are supported on source patterns: * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_JP2, * %CAIRO_MIME_TYPE_UNIQUE_ID, %CAIRO_MIME_TYPE_JBIG2, * %CAIRO_MIME_TYPE_JBIG2_GLOBAL, %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID, * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS. * * # JBIG2 Images # * JBIG2 data in PDF must be in the embedded format as described in * ISO/IEC 11544. Image specific JBIG2 data must be in * %CAIRO_MIME_TYPE_JBIG2. Any global segments in the JBIG2 data * (segments with page association field set to 0) must be in * %CAIRO_MIME_TYPE_JBIG2_GLOBAL. The global data may be shared by * multiple images. All images sharing the same global data must set * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID to a unique identifier. At least * one of the images must provide the global data using * %CAIRO_MIME_TYPE_JBIG2_GLOBAL. The global data will only be * embedded once and shared by all JBIG2 images with the same * %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID. * * # CCITT Fax Images # {#ccitt} * The %CAIRO_MIME_TYPE_CCITT_FAX mime data requires a number of decoding * parameters These parameters are specified using %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS. * * %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS mime data must contain a string of the form * "param1=value1 param2=value2 ...". * * @Columns: [required] An integer specifying the width of the image in pixels. * * @Rows: [required] An integer specifying the height of the image in scan lines. * * @K: [optional] An integer identifying the encoding scheme used. < 0 * is 2 dimensional Group 4, = 0 is Group3 1 dimensional, > 0 is mixed 1 * and 2 dimensional encoding. Default is 0. * * @EndOfLine: [optional] If true end-of-line bit patterns are present. Default is false. * * @EncodedByteAlign: [optional] If true the end of line is padded * with 0 bits so the next line begins on a byte boundary. Default is false. * * @EndOfBlock: [optional] If true the data contains an end-of-block pattern. Default is true. * * @BlackIs1: [optional] If true 1 bits are black pixels. Default is false. * * @DamagedRowsBeforeError: [optional] An integer specifying the * number of damages rows tolerated before an error occurs. Default is 0. * * Boolean values may be "true" or "false", or 1 or 0. * * These parameters are the same as the CCITTFaxDecode parameters in the * [PostScript Language Reference](https://www.adobe.com/products/postscript/pdfs/PLRM.pdf) * and [Portable Document Format (PDF)](https://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf). * Refer to these documents for further details. * * An example %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS string is: * * <programlisting> * "Columns=10230 Rows=40000 K=1 EndOfLine=true EncodedByteAlign=1 BlackIs1=false" * </programlisting> *
**/
/** * 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
**/
/* Default to Uncompressed. If this object is used with * _cairo_pdf_surface_object_begin() and Object Streams are
* enabled it will be changed to Compressed. */
object.type = PDF_OBJECT_UNCOMPRESSED;
object.u.offset = _cairo_output_stream_get_position (surface->output);
status = _cairo_array_append (&surface->objects, &object); if (unlikely (status)) {
resource.id = 0; return resource;
}
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;
}
/* 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;
/** * 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 (constchar *filename, double width_in_points, double height_in_points)
{
cairo_output_stream_t *output;
/* 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) returnFALSE; if (surface->finished) {
status_ignored = _cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); returnFALSE;
}
/** * 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
**/ constchar *
cairo_pdf_version_to_string (cairo_pdf_version_t version)
{ if (version < 0 || version >= CAIRO_PDF_VERSION_LAST) return NULL;
return _cairo_pdf_version_strings[version];
}
/** * cairo_pdf_surface_set_size: * @surface: a PDF #cairo_surface_t * @width_in_points: new surface width, in points (1 point == 1/72.0 inch) * @height_in_points: new surface height, in points (1 point == 1/72.0 inch) * * Changes the size of a PDF surface for the current (and * subsequent) pages. * * This function should only be called before any drawing operations * have been performed on the current page. The simplest way to do * this is to call this function immediately after creating the * surface or immediately after completing a page with either * cairo_show_page() or cairo_copy_page(). * * Since: 1.2
**/ void
cairo_pdf_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points)
{
cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
cairo_status_t status;
if (! _extract_pdf_surface (surface, &pdf_surface)) return;
_cairo_pdf_surface_set_size_internal (pdf_surface,
width_in_points,
height_in_points);
status = _cairo_paginated_surface_set_size (pdf_surface->paginated_surface,
width_in_points,
height_in_points); if (status)
status = _cairo_surface_set_error (surface, status);
}
/** * CAIRO_PDF_OUTLINE_ROOT: * * The root outline item in cairo_pdf_surface_add_outline(). * * Since: 1.16
**/
/** * cairo_pdf_surface_add_outline: * @surface: a PDF #cairo_surface_t * @parent_id: the id of the parent item or %CAIRO_PDF_OUTLINE_ROOT if this is a top level item. * @utf8: the name of the outline * @link_attribs: the link attributes specifying where this outline links to * @flags: outline item flags * * Add an item to the document outline hierarchy with the name @utf8 * that links to the location specified by @link_attribs. Link * attributes have the same keys and values as the [Link Tag][link], * excluding the "rect" attribute. The item will be a child of the * item with id @parent_id. Use %CAIRO_PDF_OUTLINE_ROOT as the parent * id of top level items. * * Return value: the id for the added item. * * Since: 1.16
**/ int
cairo_pdf_surface_add_outline (cairo_surface_t *surface, int parent_id, constchar *utf8, constchar *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, constchar *utf8)
{
cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
cairo_status_t status;
if (! _extract_pdf_surface (surface, &pdf_surface)) return;
status = _cairo_pdf_interchange_set_metadata (pdf_surface, metadata, utf8); if (status)
status = _cairo_surface_set_error (surface, status);
}
/** * cairo_pdf_surface_set_custom_metadata: * @surface: a PDF #cairo_surface_t * @name: The name of the custom metadata item to set (utf8). * @value: The value of the metadata (utf8). * * Set custom document metadata. @name may be any string except for * the following names reserved by PDF: "Title", "Author", "Subject", * "Keywords", "Creator", "Producer", "CreationDate", "ModDate", * "Trapped". * * If @value is NULL or an empty string, the @name metadata will not be set. * * For example: * <informalexample><programlisting> * cairo_pdf_surface_set_custom_metadata (surface, "ISBN", "978-0123456789"); * </programlisting></informalexample> * * Since: 1.18
**/ void
cairo_pdf_surface_set_custom_metadata (cairo_surface_t *surface, constchar *name, constchar *value)
{
cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
cairo_status_t status;
if (! _extract_pdf_surface (surface, &pdf_surface)) return;
status = _cairo_pdf_interchange_set_custom_metadata (pdf_surface, name, value); if (status)
status = _cairo_surface_set_error (surface, status);
}
/** * cairo_pdf_surface_set_page_label: * @surface: a PDF #cairo_surface_t * @utf8: The page label. * * Set page label for the current page. * * Since: 1.16
**/ void
cairo_pdf_surface_set_page_label (cairo_surface_t *surface, constchar *utf8)
{
cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
if (! _extract_pdf_surface (surface, &pdf_surface)) return;
/** * 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;
staticconstchar *
_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";
}
}
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);
}
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;
}
/** * _cairo_pdf_surface_add_source_surface: * @surface: [in] the pdf surface * @source_surface: [in] A #cairo_surface_t to use as the source surface * @source_pattern: [in] A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source * @region_id: [in] Surfaces containing tags set this to the recording * surface region id. When tags are used in a XObject, PDF requires a * separate object for each use (section 14.7.4.2) @region_id is used * as a key to ensure a separate object is emitted for each use. Set * to 0 for surfaces without tags. * @op: [in] the operator used to composite this source * @filter: [in] filter type of the source pattern * @stencil_mask: [in] if true, the surface will be written to the PDF as an /ImageMask * @smask: [in] if true, only the alpha channel will be written (images only) * @need_transp_group: [in] if true and an XObject is used, make it a Transparency group * @extents: [in] extents of the operation that is using this source * @smask_res: [in] if not NULL, the image written will specify this resource as the smask for * the image (images only) * @pdf_source: [out] return pdf_source_surface entry in hash table * @x_offset: [out] if not NULL return x offset of surface * @y_offset: [out] if not NULL return y offset of surface * @source_extents: [out] if not NULL return operation extents in source space * * Add surface or raster_source pattern to list of surfaces to be * written to the PDF file when the current page is finished. Returns * a PDF resource to reference the surface. A hash table of all * surfaces in the PDF file (keyed by CAIRO_MIME_TYPE_UNIQUE_ID or * surface unique_id) is used to ensure surfaces with the same id are * only written once to the PDF file. * * Only one of @source_pattern or @source_surface is to be * specified. Set the other to NULL.
**/ static cairo_int_status_t
_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface,
cairo_surface_t *source_surface, const cairo_pattern_t *source_pattern, int region_id,
cairo_operator_t op,
cairo_filter_t filter,
cairo_bool_t stencil_mask,
cairo_bool_t smask,
cairo_bool_t need_transp_group, const cairo_rectangle_int_t *extents,
cairo_pdf_resource_t *smask_res,
cairo_pdf_source_surface_entry_t **pdf_source, double *x_offset, double *y_offset,
cairo_rectangle_int_t *source_extents)
{
cairo_pdf_source_surface_t src_surface;
cairo_pdf_source_surface_entry_t surface_key;
cairo_pdf_source_surface_entry_t *surface_entry;
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
cairo_bool_t interpolate; unsignedchar *unique_id = NULL; unsignedlong unique_id_length = 0;
cairo_image_surface_t *image; void *image_extra;
cairo_box_t box;
cairo_rectangle_int_t op_extents; double x, y;
cairo_bool_t subsurface;
cairo_bool_t emit_image;
switch (filter) { default: case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR:
interpolate = TRUE; break; case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: case CAIRO_FILTER_GAUSSIAN:
interpolate = FALSE; break;
}
x = 0;
y = 0; if (source_pattern) { if (source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source_pattern,
&image, &image_extra); if (unlikely (status)) return status;
source_surface = &image->base;
cairo_surface_get_device_offset (source_surface, &x, &y);
} else {
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source_pattern;
source_surface = surface_pattern->surface;
}
} if (x_offset)
*x_offset = x; if (y_offset)
*y_offset = y;
/* transform operation extents to pattern space */
op_extents = *extents; if (source_pattern)
{
_cairo_box_from_rectangle (&box, extents);
_cairo_matrix_transform_bounding_box_fixed (&source_pattern->matrix, &box, NULL);
_cairo_box_round_to_rectangle (&box, &op_extents);
} if (source_extents)
*source_extents = op_extents;
surface_key.id = source_surface->unique_id;
/* Recording surfaces do not use interpolate. Ensure it is always * false for recording surfaces. This is because pdf-interchange * needs to lookup recording surfaces in the hash table using * interpolate = FALSE in the key since it does not know the * interpolate value passed to this function.
*/
emit_image = source_surface->type != CAIRO_SURFACE_TYPE_RECORDING;
surface_key.interpolate = emit_image ? interpolate : FALSE;
*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;
/* 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;
/* Document header */ if (! surface->header_emitted) { constchar *version;
switch (surface->pdf_version) { case CAIRO_PDF_VERSION_1_4:
version = "1.4"; break; case CAIRO_PDF_VERSION_1_5:
version = "1.5"; break; case CAIRO_PDF_VERSION_1_6:
version = "1.6"; break; default: case CAIRO_PDF_VERSION_1_7:
version = "1.7"; break;
}
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;
}
status = _cairo_pdf_surface_add_source_surface (surface,
pad_image,
NULL,
-1 , /* node_surface_index */
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);
/* 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; unsignedlong 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 );
/** * _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; unsignedlong 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];
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;
}
} elseif (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++;
}
/* 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_interchange_emit_recording_surface_begin (surface,
pdf_source->surface,
pdf_source->hash_entry->region_id,
pdf_source->hash_entry->surface_res,
&struct_parents); if (unlikely (status)) goto err;
status = _cairo_pdf_surface_open_content_stream (surface,
&bbox,
&pdf_source->hash_entry->surface_res, TRUE,
transparency_group,
struct_parents); if (unlikely (status)) goto err;
/** * _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;
}
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;
/* 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);
/* 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);
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];
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;
}
} elseif (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);
/* 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_streams, " /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;
_cairo_gradient_pattern_box_to_parameter (pattern,
bounds_x1, bounds_y1,
bounds_x2, bounds_y2,
tolerance, domain);
} elseif (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;
/* 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;
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;
}
} elseif (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;
}
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;
}
&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;
}
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 ");
/* TODO: Figure out which other defaults to be inherited by /Page
* objects. */
_cairo_output_stream_printf (surface->object_stream.stream, ">>\n");
_cairo_pdf_surface_object_end (surface);
return CAIRO_INT_STATUS_SUCCESS;
}
cairo_int_status_t
_cairo_utf8_to_pdf_string (constchar *utf8, char **str_out)
{ int i; int len; unsignedchar *p;
cairo_bool_t ascii; char *str;
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
ascii = TRUE;
p = (unsignedchar *)utf8;
len = 0; while (*p) { if (*p < 32 || *p > 126) {
ascii = FALSE; break;
} if (*p == '(' || *p == ')' || *p == '\\')
len += 2; else
len++;
p++;
}
if (ascii) {
str = _cairo_malloc (len + 3); if (str == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY);
str[0] = '(';
p = (unsignedchar *)utf8;
i = 1; while (*p) { if (*p == '(' || *p == ')' || *p == '\\')
str[i++] = '\\';
str[i++] = *p;
p++;
}
str[i++] = ')';
str[i++] = 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;
_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, ">");
#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 (constunsignedchar *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); /* fall through */ case 10: c+= ((uint32_t) data[9] << 16); /* fall through */ case 9 : c+= ((uint32_t) data[8] << 8); /* fall through */ case 8 : b+= ((uint32_t) data[7] << 24); /* fall through */ case 7 : b+= ((uint32_t) data[6] << 16); /* fall through */ case 6 : b+= ((uint32_t) data[5] << 8); /* fall through */ case 5 : b+= data[4]; /* fall through */ case 4 : a+= ((uint32_t) data[3] << 24); /* fall through */ case 3 : a+= ((uint32_t) data[2] << 16); /* fall through */ case 2 : a+= ((uint32_t) data[1] << 8); /* fall through */ case 1 : a+= data[0];
}
HASH_MIX (a,b,c);
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]); elseif (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;
/* 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);
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;
/* The invertability of font_matrix is tested in * pdf_operators_show_glyphs before any glyphs are mapped to the
* subset. */
assert (status2 == CAIRO_INT_STATUS_SUCCESS);
for (i = 0; i < font_subset->num_glyphs; i++) {
status = _cairo_pdf_surface_open_stream (surface,
NULL,
surface->compress_streams,
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 (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = cairo_pdf_surface_emit_color_glyph (surface,
font_subset->scaled_font,
font_subset->glyphs[i],
&bbox,
&widths[i]); if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = cairo_pdf_surface_emit_color_glyph_image (surface,
font_subset->scaled_font,
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;
}
_cairo_output_stream_printf (surface->output, "0000000000 65535 f \n"); for (i = 0; i < num_objects; i++) {
object = _cairo_array_index (&surface->objects, i);
_cairo_output_stream_printf (surface->output, "%010lld 00000 n \n", object->u.offset);
}
return offset;
}
staticvoid
_cairo_write_xref_stream_entry (cairo_output_stream_t *stream, int id, int type, int field2_size, longlong field2, int field3,
cairo_bool_t write_as_comments)
{ char buf[20]; int i;
if (write_as_comments) {
_cairo_output_stream_printf (stream, "%% %5d %2d %10lld %d\n", id, type, field2, field3);
} else { /* Each field is big endian */
buf[0] = type; /* field 1 */ for (i = field2_size - 1; i >= 0; i--) {
buf[i + 1] = field2 & 0xff;
field2 >>= 8;
}
buf[field2_size + 1] = field3 >> 8;
buf[field2_size + 2] = field3 & 0xff;
_cairo_output_stream_write (stream, buf, field2_size + 3);
}
}
staticvoid
_cairo_write_xref_stream_entries (cairo_pdf_surface_t *surface,
cairo_output_stream_t *stream, int field2_size,
cairo_bool_t write_as_comments)
{
cairo_pdf_object_t *object; int num_objects, i;
/* PDF requires this to be first entry */
_cairo_write_xref_stream_entry (stream,
0,
PDF_OBJECT_FREE,
field2_size,
0, /* next free object number */
0xffff, /* next generation number */
write_as_comments);
num_objects = _cairo_array_num_elements (&surface->objects); for (i = 0; i < num_objects; i++) {
object = _cairo_array_index (&surface->objects, i); if (object->type == PDF_OBJECT_UNCOMPRESSED) {
_cairo_write_xref_stream_entry (stream,
i + 1,
object->type,
field2_size,
object->u.offset,
0, /* generation number */
write_as_comments);
} elseif (object->type == PDF_OBJECT_COMPRESSED) {
_cairo_write_xref_stream_entry (stream,
i + 1,
object->type,
field2_size,
object->u.compressed_obj.xref_stream.id,
object->u.compressed_obj.index,
write_as_comments);
} else {
_cairo_write_xref_stream_entry (stream,
i + 1,
PDF_OBJECT_FREE,
field2_size,
0,
0xffff,
write_as_comments);
}
}
}
/* Find the minimum number of bytes required to represent offsets in the generated file (up to this point). */
offset_bytes = 0;
offset = *xref_offset; while (offset > 0) {
offset >>= 8;
offset_bytes++;
}
if (!surface->compress_streams) { /* Adobe Reader requires xref streams to be flate encoded (PDF * Reference 1.7, implementation note 20). This means * compression must always be enabled on this stream. To * facilitate debugging when compress_stream is disabled, emit * a human readable format of the xref stream as PDF comments.
*/
_cairo_output_stream_printf (surface->output, "%% id type offset/obj gen/index\n");
_cairo_write_xref_stream_entries (surface, surface->output, offset_bytes, TRUE);
}
_cairo_output_stream_printf (surface->output, "stream\n");
_cairo_memory_stream_copy (mem_stream, surface->output);
status = _cairo_output_stream_destroy (mem_stream); 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); 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);
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);
/* 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;
}
}
}
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;
if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) returnTRUE;
if (pattern->surface->backend->acquire_source_image == NULL) returnFALSE;
/* 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: returnTRUE;
}
ASSERT_NOT_REACHED; returnFALSE;
}
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: returnTRUE;
case CAIRO_PATTERN_TYPE_SURFACE: return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
default:
ASSERT_NOT_REACHED; returnFALSE;
}
}
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: returnTRUE;
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: returnFALSE;
}
}
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.x) > 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 (surface->type3_replay) return CAIRO_INT_STATUS_UNSUPPORTED;
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. */
/* 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;
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;
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};
/* 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)) {
status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) goto cleanup;
/* 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;
/* 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;
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_content (surface); 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) { /* Enabling text in Type 3 fonts currently crashes cairo. Most * PDF viewers don't seem to suport text in Type 3 so we let * this go to image fallback.
*/ if (surface->type3_replay) return CAIRO_INT_STATUS_UNSUPPORTED;
status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded); goto cleanup;
}
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_pdf_surface_t *surface = abstract_surface;
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
if (begin)
status = _cairo_pdf_interchange_tag_begin (surface, tag_name, attributes); else
status = _cairo_pdf_interchange_tag_end (surface, tag_name);
/* The Type 3 font subset support will the embed the * CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE image if vector operations * are not supported. The only case we don't currently handle is if a * foreground color is used.
*/ static cairo_bool_t
_cairo_pdf_surface_supports_color_glyph (void *abstract_surface,
cairo_scaled_font_t *scaled_font, unsignedlong glyph_index)
{
cairo_pdf_surface_t *surface = abstract_surface;
cairo_pdf_color_glyph_t glyph_key;
cairo_pdf_color_glyph_t *glyph_entry;
cairo_scaled_glyph_t *scaled_glyph;
cairo_status_t status;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.