/* * 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;
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.