/* * Design of the PS output: * * The PS output is harmonised with the PDF operations using PS procedures * to emulate the PDF operators. * * This has a number of advantages: * 1. A large chunk of code is shared between the PDF and PS backends. * See cairo-pdf-operators. * 2. Using gs to do PS -> PDF and PDF -> PS will always work well.
*/
#define _DEFAULT_SOURCE /* for ctime_r(), snprintf(), strdup() */ #include"cairoint.h"
/* Forms are emitted at the start and stored in memory so we limit the * total size of all forms to prevent running out of memory. If this * limit is exceeded, surfaces that would be stored in forms are
* emitted each time the surface is used. */ #define MAX_L2_FORM_DATA (256*1024) #define MAX_L3_FORM_DATA (2*1024*1024) /* Assume Level 3 printers have more memory */
/** * SECTION:cairo-ps * @Title: PostScript Surfaces * @Short_Description: Rendering PostScript documents * @See_Also: #cairo_surface_t * * The PostScript surface is used to render cairo graphics to Adobe * PostScript 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_UNIQUE_ID, * %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS, * %CAIRO_MIME_TYPE_EPS, %CAIRO_MIME_TYPE_EPS_PARAMS. * * Source surfaces used by the PostScript surface that have a * %CAIRO_MIME_TYPE_UNIQUE_ID mime type will be stored in PostScript * printer memory for the duration of the print * job. %CAIRO_MIME_TYPE_UNIQUE_ID should only be used for small * frequently used sources. * * The %CAIRO_MIME_TYPE_CCITT_FAX and %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS mime types * are documented in [CCITT Fax Images][ccitt]. * * # Embedding EPS files # {#eps} * * Encapsulated PostScript files can be embedded in the PS output by * setting the CAIRO_MIME_TYPE_EPS mime data on a surface to the EPS * data and painting the surface. The EPS will be scaled and * translated to the extents of the surface the EPS data is attached * to. * * The %CAIRO_MIME_TYPE_EPS mime type requires the * %CAIRO_MIME_TYPE_EPS_PARAMS mime data to also be provided in order * to specify the embeddding parameters. %CAIRO_MIME_TYPE_EPS_PARAMS * mime data must contain a string of the form "bbox=[llx lly urx * ury]" that specifies the bounding box (in PS coordinates) of the * EPS graphics. The parameters are: lower left x, lower left y, upper * right x, upper right y. Normally the bbox data is identical to the * \%\%\%BoundingBox data in the EPS file. *
**/
/** * CAIRO_HAS_PS_SURFACE: * * Defined if the PostScript surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.2
**/
typedefstruct { /* input params */
cairo_surface_t *src_surface; unsignedint regions_id;
cairo_operator_t op; const cairo_rectangle_int_t *src_surface_extents;
cairo_bool_t src_surface_bounded; const cairo_rectangle_int_t *src_op_extents; /* operation extents in src space */
cairo_filter_t filter;
cairo_bool_t stencil_mask; /* TRUE if source is to be used as a mask */
/* output params */
cairo_bool_t is_image; /* returns TRUE if PS image will be emitted */ /* FALSE if recording will be emitted */ long approx_size; int eod_count;
} cairo_emit_surface_params_t;
/* PLRM specifies a tolerance of 5 points when matching page sizes */ static cairo_bool_t
_ps_page_dimension_equal (int a, int b)
{ return (abs (a - b) < 5);
}
/** * cairo_ps_surface_create: * @filename: a filename for the PS output (must be writable), %NULL may be * used to specify no output. This will generate a PS 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 PostScript surface of the specified size in points to be * written to @filename. See cairo_ps_surface_create_for_stream() for * a more flexible mechanism for handling the PostScript output than * simply writing it to a named file. * * Note that the size of individual pages of the PostScript output can * vary. See cairo_ps_surface_set_size(). * * 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_ps_surface_create (constchar *filename, double width_in_points, double height_in_points)
{
cairo_output_stream_t *stream;
/** * cairo_ps_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 PostScript surface of the specified size in points to be * written incrementally to the stream represented by @write_func and * @closure. See cairo_ps_surface_create() for a more convenient way * to simply direct the PostScript output to a named file. * * Note that the size of individual pages of the PostScript * output can vary. See cairo_ps_surface_set_size(). * * 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_ps_surface_create_for_stream (cairo_write_func_t write_func, void *closure, double width_in_points, double height_in_points)
{
cairo_output_stream_t *stream;
/* If the abstract_surface is a paginated surface, and that paginated * surface's target is a ps_surface, then set ps_surface to that * target. Otherwise return FALSE.
*/ static cairo_bool_t
_extract_ps_surface (cairo_surface_t *surface,
cairo_bool_t set_error_on_failure,
cairo_ps_surface_t **ps_surface)
{
cairo_surface_t *target;
if (surface->status) returnFALSE; if (surface->finished) { if (set_error_on_failure)
_cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); returnFALSE;
}
if (! _cairo_surface_is_paginated (surface)) { if (set_error_on_failure)
_cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); returnFALSE;
}
target = _cairo_paginated_surface_get_target (surface); if (target->status) { if (set_error_on_failure)
_cairo_surface_set_error (surface, target->status); returnFALSE;
} if (target->finished) { if (set_error_on_failure)
_cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); returnFALSE;
}
if (! _cairo_surface_is_ps (target)) { if (set_error_on_failure)
_cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); returnFALSE;
}
/** * cairo_ps_surface_restrict_to_level: * @surface: a PostScript #cairo_surface_t * @level: PostScript level * * Restricts the generated PostSript file to @level. See * cairo_ps_get_levels() for a list of available level 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.6
**/ void
cairo_ps_surface_restrict_to_level (cairo_surface_t *surface,
cairo_ps_level_t level)
{
cairo_ps_surface_t *ps_surface = NULL;
if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return;
if (level < CAIRO_PS_LEVEL_LAST)
ps_surface->ps_level = level;
}
/** * cairo_ps_get_levels: * @levels: supported level list * @num_levels: list length * * Used to retrieve the list of supported levels. See * cairo_ps_surface_restrict_to_level(). * * Since: 1.6
**/ void
cairo_ps_get_levels (cairo_ps_level_t const **levels, int *num_levels)
{ if (levels != NULL)
*levels = _cairo_ps_levels;
if (num_levels != NULL)
*num_levels = CAIRO_PS_LEVEL_LAST;
}
/** * cairo_ps_level_to_string: * @level: a level id * * Get the string representation of the given @level id. This function * will return %NULL if @level id isn't valid. See cairo_ps_get_levels() * for a way to get the list of valid level ids. * * Return value: the string associated to given level. * * Since: 1.6
**/ constchar *
cairo_ps_level_to_string (cairo_ps_level_t level)
{ if (level >= CAIRO_PS_LEVEL_LAST) return NULL;
return _cairo_ps_level_strings[level];
}
/** * cairo_ps_surface_set_eps: * @surface: a PostScript #cairo_surface_t * @eps: %TRUE to output EPS format PostScript * * If @eps is %TRUE, the PostScript surface will output Encapsulated * PostScript. * * 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. An Encapsulated PostScript file should never contain more * than one page. * * Since: 1.6
**/ void
cairo_ps_surface_set_eps (cairo_surface_t *surface,
cairo_bool_t eps)
{
cairo_ps_surface_t *ps_surface = NULL;
if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return;
ps_surface->eps = eps;
}
/** * cairo_ps_surface_get_eps: * @surface: a PostScript #cairo_surface_t * * Check whether the PostScript surface will output Encapsulated PostScript. * * Return value: %TRUE if the surface will output Encapsulated PostScript. * * Since: 1.6
**/
cairo_public cairo_bool_t
cairo_ps_surface_get_eps (cairo_surface_t *surface)
{
cairo_ps_surface_t *ps_surface = NULL;
if (! _extract_ps_surface (surface, FALSE, &ps_surface)) returnFALSE;
return ps_surface->eps;
}
/** * cairo_ps_surface_set_size: * @surface: a PostScript #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 PostScript 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_ps_surface_set_size (cairo_surface_t *surface, double width_in_points, double height_in_points)
{
cairo_ps_surface_t *ps_surface = NULL;
cairo_status_t status;
if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return;
/** * cairo_ps_surface_dsc_comment: * @surface: a PostScript #cairo_surface_t * @comment: a comment string to be emitted into the PostScript output * * Emit a comment into the PostScript output for the given surface. * * The comment is expected to conform to the PostScript Language * Document Structuring Conventions (DSC). Please see that manual for * details on the available comments and their meanings. In * particular, the \%\%IncludeFeature comment allows a * device-independent means of controlling printer device features. So * the PostScript Printer Description Files Specification will also be * a useful reference. * * The comment string must begin with a percent character (\%) and the * total length of the string (including any initial percent * characters) must not exceed 255 characters. Violating either of * these conditions will place @surface into an error state. But * beyond these two conditions, this function will not enforce * conformance of the comment with any particular specification. * * The comment string must not contain any newline characters. * * The DSC specifies different sections in which particular comments * can appear. This function provides for comments to be emitted * within three sections: the header, the Setup section, and the * PageSetup section. Comments appearing in the first two sections * apply to the entire document while comments in the BeginPageSetup * section apply only to a single page. * * For comments to appear in the header section, this function should * be called after the surface is created, but before a call to * cairo_ps_surface_dsc_begin_setup(). * * For comments to appear in the Setup section, this function should * be called after a call to cairo_ps_surface_dsc_begin_setup() but * before a call to cairo_ps_surface_dsc_begin_page_setup(). * * For comments to appear in the PageSetup section, this function * should be called after a call to * cairo_ps_surface_dsc_begin_page_setup(). * * Note that it is only necessary to call * cairo_ps_surface_dsc_begin_page_setup() for the first page of any * surface. After a call to cairo_show_page() or cairo_copy_page() * comments are unambiguously directed to the PageSetup section of the * current page. But it doesn't hurt to call this function at the * beginning of every page as that consistency may make the calling * code simpler. * * As a final note, cairo automatically generates several comments on * its own. As such, applications must not manually generate any of * the following comments: * * Header section: \%!PS-Adobe-3.0, \%\%Creator, \%\%CreationDate, \%\%Pages, * \%\%BoundingBox, \%\%DocumentData, \%\%LanguageLevel, \%\%EndComments. * * Setup section: \%\%BeginSetup, \%\%EndSetup * * PageSetup section: \%\%BeginPageSetup, \%\%PageBoundingBox, \%\%EndPageSetup. * * Other sections: \%\%BeginProlog, \%\%EndProlog, \%\%Page, \%\%Trailer, \%\%EOF * * Here is an example sequence showing how this function might be used: * * <informalexample><programlisting> * cairo_surface_t *surface = cairo_ps_surface_create (filename, width, height); * ... * cairo_ps_surface_dsc_comment (surface, "%%Title: My excellent document"); * cairo_ps_surface_dsc_comment (surface, "%%Copyright: Copyright (C) 2006 Cairo Lover") * ... * cairo_ps_surface_dsc_begin_setup (surface); * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor White"); * ... * cairo_ps_surface_dsc_begin_page_setup (surface); * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A3"); * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *InputSlot LargeCapacity"); * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaType Glossy"); * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor Blue"); * ... draw to first page here .. * cairo_show_page (cr); * ... * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A5"); * ... * </programlisting></informalexample> * * Since: 1.2
**/ void
cairo_ps_surface_dsc_comment (cairo_surface_t *surface, constchar *comment)
{
cairo_ps_surface_t *ps_surface = NULL;
cairo_status_t status; char *comment_copy;
if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return;
/* A couple of sanity checks on the comment value. */ if (comment == NULL) {
status = _cairo_surface_set_error (surface, CAIRO_STATUS_NULL_POINTER); return;
}
if (comment[0] != '%' || strlen (comment) > 255) {
status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_DSC_COMMENT); return;
}
/* Then, copy the comment and store it in the appropriate array. */
comment_copy = strdup (comment); if (unlikely (comment_copy == NULL)) {
status = _cairo_surface_set_error (surface, CAIRO_STATUS_NO_MEMORY); return;
}
status = _cairo_array_append (ps_surface->dsc_comment_target, &comment_copy); if (unlikely (status)) {
free (comment_copy);
status = _cairo_surface_set_error (surface, status); return;
}
}
/** * cairo_ps_surface_dsc_begin_setup: * @surface: a PostScript #cairo_surface_t * * This function indicates that subsequent calls to * cairo_ps_surface_dsc_comment() should direct comments to the Setup * section of the PostScript output. * * This function should be called at most once per surface, and must * be called before any call to cairo_ps_surface_dsc_begin_page_setup() * and before any drawing is performed to the surface. * * See cairo_ps_surface_dsc_comment() for more details. * * Since: 1.2
**/ void
cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface)
{
cairo_ps_surface_t *ps_surface = NULL;
if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return;
if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments)
ps_surface->dsc_comment_target = &ps_surface->dsc_setup_comments;
}
/** * cairo_ps_surface_dsc_begin_page_setup: * @surface: a PostScript #cairo_surface_t * * This function indicates that subsequent calls to * cairo_ps_surface_dsc_comment() should direct comments to the * PageSetup section of the PostScript output. * * This function call is only needed for the first page of a * surface. It should be called after any call to * cairo_ps_surface_dsc_begin_setup() and before any drawing is * performed to the surface. * * See cairo_ps_surface_dsc_comment() for more details. * * Since: 1.2
**/ void
cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface)
{
cairo_ps_surface_t *ps_surface = NULL;
if (! _extract_ps_surface (surface, TRUE, &ps_surface)) return;
/** * _cairo_ps_surface_acquire_source_surface_from_pattern: * @surface: [in] the ps surface * @pattern: [in] A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use * as the source * @extents: [in] extents of the operation that is using this source * @src_surface_extents: [out] return source surface extents * @src_surface_bounded: [out] return TRUE if source surface is bounded * @src_op_extents: [out] return operation extents in source space * @source_surface: [out] returns surface of type image surface or recording surface * @x_offset: [out] return x offset of surface * @y_offset: [out] return y offset of surface * * Acquire source surface or raster source pattern.
**/ static cairo_status_t
_cairo_ps_surface_acquire_source_surface_from_pattern (
cairo_ps_surface_t *surface, const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents,
cairo_rectangle_int_t *src_surface_extents,
cairo_bool_t *src_surface_bounded,
cairo_rectangle_int_t *src_op_extents,
cairo_surface_t **source_surface, double *x_offset, double *y_offset)
{
cairo_status_t status;
cairo_box_t bbox;
*x_offset = 0;
*y_offset = 0;
/* get the operation extents in pattern space */
_cairo_box_from_rectangle (&bbox, extents);
_cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &bbox, NULL);
_cairo_box_round_to_rectangle (&bbox, src_op_extents);
if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
cairo_surface_t *surf;
surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, src_op_extents); if (!surf) return CAIRO_INT_STATUS_UNSUPPORTED;
/** * _cairo_ps_surface_create_padded_image_from_image: * @surface: the ps surface * @source: The source image * @extents: extents of the operation that is using this source * @image: returns the padded image or NULL if padding not required to fill @extents * @image_extents: returns extents of padded image. These extents in are in source image space. * * Creates a padded image if the source image does not fill the extents.
**/ static cairo_status_t
_cairo_ps_surface_create_padded_image_from_image (cairo_ps_surface_t *surface,
cairo_image_surface_t *source, const cairo_matrix_t *source_matrix, const cairo_rectangle_int_t *extents,
cairo_image_surface_t **image,
cairo_rectangle_int_t *image_extents)
{
cairo_box_t box;
cairo_rectangle_int_t rect;
cairo_surface_t *pad_image;
cairo_surface_pattern_t pad_pattern; int w, h;
cairo_int_status_t status;
/* 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 = source->width;
h = source->height; if (_cairo_fixed_integer_ceil(box.p1.x) < 0 ||
_cairo_fixed_integer_ceil(box.p1.y) < 0 ||
_cairo_fixed_integer_floor(box.p2.y) > w ||
_cairo_fixed_integer_floor(box.p2.y) > h)
{
pad_image = _cairo_image_surface_create_with_content (source->base.content,
rect.width,
rect.height); if (pad_image->status) return pad_image->status;
if (surface->ps_level == CAIRO_PS_LEVEL_2) returnFALSE;
/* Alpha gradients are only supported (by flattening the alpha)
* if there is no variation in the alpha across the gradient. */
_cairo_pattern_alpha_range (pattern, &min_alpha, &max_alpha); if (min_alpha != max_alpha) returnFALSE;
if (surface->force_fallbacks &&
surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
{ return CAIRO_INT_STATUS_UNSUPPORTED;
}
if (! pattern_supported (surface, pattern)) return CAIRO_INT_STATUS_UNSUPPORTED;
if (! (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) return CAIRO_INT_STATUS_UNSUPPORTED;
/* Mask is only supported when the mask is an image with opaque or bilevel alpha. */ if (mask && !mask_supported (surface, mask, extents)) return CAIRO_INT_STATUS_UNSUPPORTED;
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;
}
}
if (op == CAIRO_OPERATOR_SOURCE) { if (mask) return CAIRO_INT_STATUS_UNSUPPORTED; else return CAIRO_STATUS_SUCCESS;
}
/* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If * the pattern contains transparency, we return * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis * surface. If the analysis surface determines that there is * anything drawn under this operation, a fallback image will be * used. Otherwise the operation will be replayed during the * render stage and we blend the transparency into the white * background to convert the pattern to opaque.
*/ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE || pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) return _cairo_ps_surface_analyze_surface_pattern_transparency (surface, pattern, extents);
/* Patterns whose drawn part is opaque are directly supported; those whose drawn part is partially transparent can be
supported by flattening the alpha. */
_cairo_pattern_alpha_range (pattern, &min_alpha, NULL); if (CAIRO_ALPHA_IS_OPAQUE (min_alpha)) return CAIRO_STATUS_SUCCESS;
while (length--) { if (stream->column == 0) { if (stream->use_strings) {
_cairo_output_stream_printf (stream->output, "<~");
stream->column = 2;
} else {
_cairo_output_stream_printf (stream->output, " ");
stream->column = 1;
}
}
c = *data++;
_cairo_output_stream_write (stream->output, &c, 1);
stream->column++;
/* Base85 encodes each 4 byte tuple with a 5 ASCII character * tuple, except for 'z' with represents 4 zero bytes. We need * to keep track of the string length after decoding.
*/ if (c == 'z') {
stream->string_size += 4;
stream->tuple_count = 0;
} else { if (++stream->tuple_count == 5) {
stream->string_size += 4;
stream->tuple_count = 0;
}
}
/* Split string at tuple boundary when there is not enough
* space for another tuple */ if (stream->use_strings &&
stream->tuple_count == 0 &&
stream->string_size > STRING_ARRAY_MAX_STRING_SIZE - 4)
{
_cairo_output_stream_printf (stream->output, "~>\n");
stream->string_size = 0;
stream->column = 0;
} if (stream->column >= STRING_ARRAY_MAX_COLUMN) {
_cairo_output_stream_printf (stream->output, "\n ");
stream->column = 1;
}
}
/* A _base85_strings_stream wraps an existing output stream. It takes * base85 encoded data and splits it into strings each limited to * STRING_ARRAY_MAX_STRING_SIZE bytes when decoded. Each string is * enclosed in "<~" and "~>".
* The string array stream is also careful to wrap the output within * STRING_ARRAY_MAX_COLUMN columns. Wrapped lines start with a space * in case an encoded line starts with %% which could be interpreted * as a DSC comment.
*/ static cairo_output_stream_t *
_base85_strings_stream_create (cairo_output_stream_t *output)
{
string_array_stream_t *stream;
/* A base85_wrap_stream wraps an existing output stream. It wraps the * output within STRING_ARRAY_MAX_COLUMN columns. A base85 EOD "~>" is * appended to the end. Wrapped lines start with a space in case an * encoded line starts with %% which could be interpreted as a DSC * comment.
*/ static cairo_output_stream_t *
_base85_wrap_stream_create (cairo_output_stream_t *output)
{
string_array_stream_t *stream;
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;
}
if (params->stencil_mask) {
use_mask = FALSE;
color = CAIRO_IMAGE_IS_MONOCHROME;
transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
} else {
transparency = _cairo_image_analyze_transparency (image);
/* PostScript can not represent the alpha channel, so we blend the current image over a white (or black for CONTENT_COLOR
surfaces) RGB surface to eliminate it. */
if (params->op == CAIRO_OPERATOR_SOURCE ||
transparency == CAIRO_IMAGE_HAS_ALPHA ||
(transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA &&
surface->ps_level == CAIRO_PS_LEVEL_2))
{
status = _cairo_ps_surface_flatten_image_transparency (surface,
image,
&ps_image); if (unlikely (status)) return status;
/* Type 2 (mask and image interleaved) has the mask and image * samples interleaved by row. The mask row is first, one bit per * pixel with (bit 7 first). The row is padded to byte
* boundaries. The image data is 3 bytes per pixel RGB format. */ switch (color) { default: case CAIRO_IMAGE_UNKNOWN_COLOR:
ASSERT_NOT_REACHED; case CAIRO_IMAGE_IS_COLOR:
data_size = ps_image->width * 3; break; case CAIRO_IMAGE_IS_GRAYSCALE:
data_size = ps_image->width; break; case CAIRO_IMAGE_IS_MONOCHROME:
data_size = (ps_image->width + 7)/8; break;
} if (use_mask)
data_size += (ps_image->width + 7)/8;
data_size *= ps_image->height;
data = _cairo_malloc (data_size); if (unlikely (data == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto bail1;
}
i = 0; for (y = 0; y < ps_image->height; y++) { if (params->stencil_mask || use_mask) { /* mask row */ if (ps_image->format == CAIRO_FORMAT_A1) {
pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride);
for (x = 0; x < (ps_image->width + 7) / 8; x++, pixel8++) {
a = *pixel8;
a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a);
data[i++] = a;
}
} else {
pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride);
pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride);
bit = 7; for (x = 0; x < ps_image->width; x++) { if (ps_image->format == CAIRO_FORMAT_ARGB32) {
a = (*pixel32 & 0xff000000) >> 24;
pixel32++;
} else {
a = *pixel8;
pixel8++;
}
if (transparency == CAIRO_IMAGE_HAS_ALPHA) {
data[i++] = a;
} else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */ if (bit == 7)
data[i] = 0; if (a != 0)
data[i] |= (1 << bit);
bit--; if (bit < 0) {
bit = 7;
i++;
}
}
} if (bit != 7)
i++;
}
} if (params->stencil_mask) continue;
/* image row*/
pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride);
bit = 7; for (x = 0; x < ps_image->width; x++, pixel32++) { int r, g, b;
if (ps_image->format == CAIRO_FORMAT_ARGB32) { /* At this point ARGB32 images are either opaque or
* bilevel alpha so we don't need to unpremultiply. */ if (((*pixel32 & 0xff000000) >> 24) == 0) {
r = g = b = 0;
} else {
r = (*pixel32 & 0x00ff0000) >> 16;
g = (*pixel32 & 0x0000ff00) >> 8;
b = (*pixel32 & 0x000000ff) >> 0;
}
} elseif (ps_image->format == CAIRO_FORMAT_RGB24) {
r = (*pixel32 & 0x00ff0000) >> 16;
g = (*pixel32 & 0x0000ff00) >> 8;
b = (*pixel32 & 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->paint_proc) { /* Emit the image data as a base85-encoded string which will
* be used as the data source for the image operator later. */
_cairo_output_stream_printf (surface->stream, "/CairoData [\n");
status = _cairo_ps_surface_emit_base85_string (surface,
data,
data_size,
compress, TRUE); if (unlikely (status)) goto bail2;
if (!surface->paint_proc) { /* Emit the image data as a base85-encoded string which will
* be used as the data source for the image operator. */
status = _cairo_ps_surface_emit_base85_string (surface,
data,
data_size,
compress, FALSE);
_cairo_output_stream_printf (surface->stream, "\n");
} else {
status = CAIRO_STATUS_SUCCESS;
}
bail2:
free (data);
bail1: if (!use_mask && ps_image != image)
cairo_surface_destroy (&ps_image->base);
bail0: if (image != image_surf)
cairo_surface_destroy (&image->base);
/* At this point we know emitting jpeg will succeed. */ if (mode == CAIRO_EMIT_SURFACE_ANALYZE) {
params->is_image = TRUE;
params->approx_size = mime_data_length; return CAIRO_STATUS_SUCCESS;
}
if (surface->paint_proc) { /* Emit the image data as a base85-encoded string which will
* be used as the data source for the image operator later. */
_cairo_output_stream_printf (surface->stream, "/CairoData [\n");
status = _cairo_ps_surface_emit_base85_string (surface,
mime_data,
mime_data_length,
CAIRO_PS_COMPRESS_NONE, TRUE); if (unlikely (status)) return status;
if (!surface->paint_proc) { /* Emit the image data as a base85-encoded string which will
* be used as the data source for the image operator. */
status = _cairo_ps_surface_emit_base85_string (surface,
mime_data,
mime_data_length,
CAIRO_PS_COMPRESS_NONE, FALSE);
}
status = _cairo_tag_parse_ccitt_params (ccitt_params_string, &ccitt_params); if (unlikely(status)) return status;
free (ccitt_params_string);
if (ccitt_params.columns <= 0 || ccitt_params.rows <= 0) return CAIRO_INT_STATUS_UNSUPPORTED;
/* At this point we know emitting ccitt will succeed. */ if (mode == CAIRO_EMIT_SURFACE_ANALYZE) {
params->is_image = TRUE;
params->approx_size = ccitt_data_len; return CAIRO_STATUS_SUCCESS;
}
if (surface->paint_proc) { /* Emit the image data as a base85-encoded string which will
* be used as the data source for the image operator later. */
_cairo_output_stream_printf (surface->stream, "/CairoData [\n");
status = _cairo_ps_surface_emit_base85_string (surface,
ccitt_data,
ccitt_data_len,
CAIRO_PS_COMPRESS_NONE, TRUE); if (unlikely (status)) return status;
if (!surface->paint_proc) { /* Emit the image data as a base85-encoded string which will
* be used as the data source for the image operator. */
status = _cairo_ps_surface_emit_base85_string (surface,
ccitt_data,
ccitt_data_len,
CAIRO_PS_COMPRESS_NONE, FALSE);
}
return status;
}
/* The '|' character is not used in PS (including ASCII85). We can * speed up the search by first searching for the first char before * comparing strings.
*/ #define SUBFILE_FILTER_EOD "|EOD|"
/* Count number of non overlapping occurrences of SUBFILE_FILTER_EOD in data. */ staticint
count_eod_strings (constunsignedchar *data, unsignedlong data_len)
{ constunsignedchar *p = data; constunsignedchar *end; int first_char, len, count; constchar *eod_str = SUBFILE_FILTER_EOD;
first_char = eod_str[0];
len = strlen (eod_str);
p = data;
end = data + data_len - len + 1;
count = 0; while (p < end) {
p = memchr (p, first_char, end - p); if (!p) break;
if (memcmp (p, eod_str, len) == 0) {
count++;
p += len;
}
}
if (unlikely (params->src_surface->status)) return params->src_surface->status;
/* We only embed EPS with level 3 as we may use ReusableStreamDecode and we
* don't know what level the EPS file requires. */ if (surface->ps_level == CAIRO_PS_LEVEL_2) return CAIRO_INT_STATUS_UNSUPPORTED;
status = _cairo_tag_parse_eps_params (params_string, &eps_params); if (unlikely(status)) return status;
/* At this point we know emitting EPS will succeed. */ if (mode == CAIRO_EMIT_SURFACE_ANALYZE) {
params->is_image = FALSE;
params->approx_size = eps_data_len;
surface->contains_eps = TRUE;
/* Find number of occurrences of SUBFILE_FILTER_EOD in the EPS data. * We will need it before emitting the data if a ReusableStream is used.
*/
params->eod_count = count_eod_strings (eps_data, eps_data_len); return CAIRO_STATUS_SUCCESS;
}
/* Prevent infinite recursion if the recording_surface references a recording
* currently being emitted */
recording_surf_stack_size = _cairo_array_num_elements (&surface->recording_surf_stack); for (i = 0; i < recording_surf_stack_size; i++) {
_cairo_array_copy_element (&surface->recording_surf_stack, i, &id); if (id == recording_surface->unique_id) return CAIRO_STATUS_SUCCESS;
}
id = recording_surface->unique_id;
status = _cairo_array_append (&surface->recording_surf_stack, &id); if (unlikely (status)) return status;
if (_cairo_surface_is_snapshot (recording_surface))
free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface);
/* * PS Forms are used for sources that have CAIRO_MIME_TYPE_UNIQUE_ID. They will be * emitted once in the PS header and can be rendered with the 'execform' operator. * * This function tries adding the source the form hash table. If the source does not * have CAIRO_MIME_TYPE_UNIQUE_ID, CAIRO_INT_STATUS_UNSUPPORTED is returned.
* @source: [in] the source for the form * @params: [in] source parameters * @test: [in] if TRUE, test if form will be used (excludes size check) * @ps_form [out] the new or existing entry int the hash table. * image or recording.
*/ static cairo_int_status_t
_cairo_ps_surface_use_form (cairo_ps_surface_t *surface,
cairo_emit_surface_params_t *params,
cairo_bool_t test,
cairo_ps_form_t **ps_form)
{
cairo_ps_form_t source_key;
cairo_ps_form_t *source_entry; unsignedchar *unique_id = NULL; unsignedlong unique_id_length = 0;
cairo_status_t status; long max_size;
if (params->op != CAIRO_OPERATOR_OVER || params->stencil_mask) return CAIRO_INT_STATUS_UNSUPPORTED;
if (params->src_surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) return CAIRO_INT_STATUS_UNSUPPORTED;
/* Don't add any more Forms if we exceed the form memory limit */ if (surface->total_form_size + params->approx_size > max_size) return CAIRO_INT_STATUS_UNSUPPORTED;
status = _cairo_ps_surface_use_form (surface,
params,
test,
&ps_form); if (test || status) return status;
/* _cairo_ps_form_emit will use Level 3 if permitted by ps_level */ if (surface->ps_level == CAIRO_PS_LEVEL_3)
surface->ps_level_used = CAIRO_PS_LEVEL_3;
/* Emit a surface. This function has three modes. * * CAIRO_EMIT_SURFACE_ANALYZE: This will determine the surface type to * be emitted and approximate size. is_image is set to TRUE if the * emitted surface is an image surface (including mime images). This * is used by the caller to setup the correct CTM. approx_size is set * to the approximate size of the emitted surface and is used as an * input by the emit mode. * * CAIRO_EMIT_SURFACE_EMIT: Emits the surface will be emitted. The * approx_size and the surface unique id values are used to determine * if a Form should be used. If a form is used, the exec form * operation is emitted and the surface is added to the forms hash * table. * * CAIRO_EMIT_SURFACE_EMIT_FORM: Emits the form definition for the surface. * * Usage is: * 1) Setup input params and call with ANALYZE. * 2) Setup CTM for surface and call with EMIT using same params struct. * The EMIT_FORM mode is used when emitting the form definitions.
*/ static cairo_int_status_t
_cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface,
cairo_emit_surface_mode_t mode,
cairo_emit_surface_params_t *params)
{
cairo_int_status_t status;
cairo_output_stream_t *old_stream = NULL;
cairo_bool_t use_form;
/* Try emitting as a form. Returns unsupported if the surface is
* deemed unsuitable for a form. */
use_form = FALSE; if (mode == CAIRO_EMIT_SURFACE_ANALYZE || mode == CAIRO_EMIT_SURFACE_EMIT) {
status = _cairo_ps_surface_emit_form (surface,
params,
mode == CAIRO_EMIT_SURFACE_ANALYZE);
use_form = (status == CAIRO_INT_STATUS_SUCCESS); if (status != CAIRO_INT_STATUS_SUCCESS && status != CAIRO_INT_STATUS_UNSUPPORTED) return status;
if (mode == CAIRO_EMIT_SURFACE_EMIT && status == CAIRO_INT_STATUS_SUCCESS) return status;
}
status = _cairo_ps_surface_emit_eps (surface, mode, params); if (status == CAIRO_INT_STATUS_SUCCESS) {
params->is_image = FALSE; goto surface_emitted;
} if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status;
status = _cairo_ps_surface_emit_jpeg_image (surface, mode, params); if (status == CAIRO_INT_STATUS_SUCCESS) {
params->is_image = TRUE; goto surface_emitted;
} if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status;
status = _cairo_ps_surface_emit_ccitt_image (surface, mode, params); if (status == CAIRO_INT_STATUS_SUCCESS) {
params->is_image = TRUE; goto surface_emitted;
} if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status;
if (mode == CAIRO_EMIT_SURFACE_ANALYZE) { /* Find size of image or recording surface by emitting to a memory stream */
status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) return status;
status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface,
pattern,
extents,
&pattern_extents,
&bounded,
&src_op_extents,
&source_surface,
&x_offset, &y_offset); if (unlikely (status)) return status;
if (extend == CAIRO_EXTEND_PAD) {
cairo_image_surface_t *img;
assert (source_surface->type == CAIRO_SURFACE_TYPE_IMAGE);
img = (cairo_image_surface_t *) source_surface;
status = _cairo_ps_surface_create_padded_image_from_image (surface,
img,
&pattern->matrix,
extents,
&image,
&pattern_extents); if (unlikely (status)) goto release_source;
} if (unlikely (status)) goto release_source;
if (!bounded)
{
extend = CAIRO_EXTEND_NONE;
_cairo_rectangle_intersect (&pattern_extents, &src_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:
xstep = pattern_extents.width*2;
ystep = pattern_extents.height*2; break; /* All the rest (if any) should have been analyzed away, so these
* cases should be unreachable. */ default:
ASSERT_NOT_REACHED;
xstep = 0;
ystep = 0;
}
_cairo_output_stream_printf (surface->stream, "/CairoFunction\n"); 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_ps_color_stop_t pad_stops[4];
_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;
/* PS 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_ps_surface_emit_repeating_function (surface,
pattern,
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;
}
case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
status = _cairo_ps_surface_emit_surface_pattern (surface,
(cairo_pattern_t *)pattern,
extents,
op); if (unlikely (status)) return status; break;
case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL:
status = _cairo_ps_surface_emit_gradient (surface,
(cairo_gradient_pattern_t *) pattern, TRUE); if (unlikely (status)) return status; break;
case CAIRO_PATTERN_TYPE_MESH:
status = _cairo_ps_surface_emit_mesh_pattern (surface,
(cairo_mesh_pattern_t *) pattern, TRUE); if (unlikely (status)) return status; break;
}
case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: case CAIRO_PATTERN_TYPE_MESH: return _cairo_ps_surface_paint_gradient (surface,
source,
extents);
case CAIRO_PATTERN_TYPE_SOLID: default:
ASSERT_NOT_REACHED; return CAIRO_STATUS_SUCCESS;
}
}
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.