mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-04 21:08:10 +02:00
Add Encapsulated PostScript support
The analysis surface will calculated the tight bounding box for each
page. A new paginated-surface backend function set_bounding_box() has
been added for passing the page bounding box to the target surface at
the end of the analysis phase.
The changes to the PS file when EPS is enabled are:
- Add EPS header
- Use tight bounding box instead of page size
- Use save/restore to ensure PS interpreter is left in the same state
This commit is contained in:
parent
beefbdd638
commit
77f1aa7887
6 changed files with 171 additions and 59 deletions
|
|
@ -46,12 +46,16 @@ cairo_private cairo_region_t *
|
|||
_cairo_analysis_surface_get_supported (cairo_surface_t *surface);
|
||||
|
||||
cairo_private cairo_region_t *
|
||||
_cairo_analysis_surface_get_unsupported (cairo_surface_t *unsupported);
|
||||
_cairo_analysis_surface_get_unsupported (cairo_surface_t *surface);
|
||||
|
||||
cairo_private cairo_bool_t
|
||||
_cairo_analysis_surface_has_supported (cairo_surface_t *unsupported);
|
||||
_cairo_analysis_surface_has_supported (cairo_surface_t *surface);
|
||||
|
||||
cairo_private cairo_bool_t
|
||||
_cairo_analysis_surface_has_unsupported (cairo_surface_t *unsupported);
|
||||
_cairo_analysis_surface_has_unsupported (cairo_surface_t *surface);
|
||||
|
||||
cairo_private void
|
||||
_cairo_analysis_surface_get_bounding_box (cairo_surface_t *surface,
|
||||
cairo_box_t *bbox);
|
||||
|
||||
#endif /* CAIRO_ANALYSIS_SURFACE_H */
|
||||
|
|
|
|||
|
|
@ -48,12 +48,14 @@ typedef struct {
|
|||
|
||||
cairo_surface_t *target;
|
||||
|
||||
cairo_bool_t first_op;
|
||||
cairo_bool_t has_supported;
|
||||
cairo_bool_t has_unsupported;
|
||||
|
||||
cairo_region_t supported_region;
|
||||
cairo_region_t fallback_region;
|
||||
cairo_rectangle_int_t current_clip;
|
||||
cairo_box_t page_bbox;
|
||||
|
||||
} cairo_analysis_surface_t;
|
||||
|
||||
|
|
@ -90,10 +92,30 @@ _cairo_analysis_surface_add_operation (cairo_analysis_surface_t *surface,
|
|||
cairo_int_status_t backend_status)
|
||||
{
|
||||
cairo_int_status_t status;
|
||||
cairo_box_t bbox;
|
||||
|
||||
if (rect->width == 0 || rect->height == 0)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
bbox.p1.x = _cairo_fixed_from_int (rect->x);
|
||||
bbox.p1.y = _cairo_fixed_from_int (rect->y);
|
||||
bbox.p2.x = _cairo_fixed_from_int (rect->x + rect->width);
|
||||
bbox.p2.y = _cairo_fixed_from_int (rect->y + rect->height);
|
||||
|
||||
if (surface->first_op) {
|
||||
surface->first_op = FALSE;
|
||||
surface->page_bbox = bbox;
|
||||
} else {
|
||||
if (bbox.p1.x < surface->page_bbox.p1.x)
|
||||
surface->page_bbox.p1.x = bbox.p1.x;
|
||||
if (bbox.p1.y < surface->page_bbox.p1.y)
|
||||
surface->page_bbox.p1.y = bbox.p1.y;
|
||||
if (bbox.p2.x > surface->page_bbox.p2.x)
|
||||
surface->page_bbox.p2.x = bbox.p2.x;
|
||||
if (bbox.p2.y > surface->page_bbox.p2.y)
|
||||
surface->page_bbox.p2.y = bbox.p2.y;
|
||||
}
|
||||
|
||||
/* If the operation is completely enclosed within the fallback
|
||||
* region there is no benefit in emitting a native operation as
|
||||
* the fallback image will be painted on top.
|
||||
|
|
@ -557,6 +579,7 @@ _cairo_analysis_surface_create (cairo_surface_t *target,
|
|||
surface->height = height;
|
||||
|
||||
surface->target = target;
|
||||
surface->first_op = TRUE;
|
||||
surface->has_supported = FALSE;
|
||||
surface->has_unsupported = FALSE;
|
||||
_cairo_region_init (&surface->supported_region);
|
||||
|
|
@ -604,3 +627,12 @@ _cairo_analysis_surface_has_unsupported (cairo_surface_t *abstract_surface)
|
|||
|
||||
return surface->has_unsupported;
|
||||
}
|
||||
|
||||
void
|
||||
_cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface,
|
||||
cairo_box_t *bbox)
|
||||
{
|
||||
cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
|
||||
|
||||
*bbox = surface->page_bbox;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,14 @@ struct _cairo_paginated_surface_backend {
|
|||
void
|
||||
(*set_paginated_mode) (void *surface,
|
||||
cairo_paginated_mode_t mode);
|
||||
|
||||
/* Optional. Specifies the smallest box that encloses all objects
|
||||
* on the page. Will be called at the end of the ANALYZE phase but
|
||||
* before the mode is changed to RENDER.
|
||||
*/
|
||||
cairo_warn cairo_int_status_t
|
||||
(*set_bounding_box) (void *surface,
|
||||
cairo_box_t *bbox);
|
||||
};
|
||||
|
||||
/* A cairo_paginated_surface provides a very convenient wrapper that
|
||||
|
|
@ -95,11 +103,14 @@ struct _cairo_paginated_surface_backend {
|
|||
* from each operation). This analysis stage is used to decide which
|
||||
* operations will require fallbacks.
|
||||
*
|
||||
* 4. Calls set_paginated_mode with an argument of CAIRO_PAGINATED_MODE_RENDER
|
||||
* 4. Calls set_bounding_box to provide the target surface with the
|
||||
* tight bounding box of the page.
|
||||
*
|
||||
* 5. Replays a subset of the meta-surface operations to the target surface
|
||||
* 5. Calls set_paginated_mode with an argument of CAIRO_PAGINATED_MODE_RENDER
|
||||
*
|
||||
* 6. Replays the remaining operations to an image surface, sets an
|
||||
* 6. Replays a subset of the meta-surface operations to the target surface
|
||||
*
|
||||
* 7. Replays the remaining operations to an image surface, sets an
|
||||
* appropriate clip on the target, then paints the resulting image
|
||||
* surface to the target.
|
||||
*
|
||||
|
|
@ -114,7 +125,7 @@ struct _cairo_paginated_surface_backend {
|
|||
*
|
||||
* NOTE: The paginated surface layer assumes that the target surface
|
||||
* is "blank" by default at the beginning of each page, without any
|
||||
* need for an explicit erasea operation, (as opposed to an image
|
||||
* need for an explicit erase operation, (as opposed to an image
|
||||
* surface, for example, which might have uninitialized content
|
||||
* originally). As such, it optimizes away CLEAR operations that
|
||||
* happen at the beginning of each page---the target surface will not
|
||||
|
|
|
|||
|
|
@ -291,8 +291,6 @@ _paint_page (cairo_paginated_surface_t *surface)
|
|||
|
||||
surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_ANALYZE);
|
||||
status = _cairo_meta_surface_replay_and_create_regions (surface->meta, analysis);
|
||||
surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_RENDER);
|
||||
|
||||
if (status || analysis->status) {
|
||||
if (status == CAIRO_STATUS_SUCCESS)
|
||||
status = analysis->status;
|
||||
|
|
@ -300,6 +298,19 @@ _paint_page (cairo_paginated_surface_t *surface)
|
|||
return status;
|
||||
}
|
||||
|
||||
if (surface->backend->set_bounding_box) {
|
||||
cairo_box_t bbox;
|
||||
|
||||
_cairo_analysis_surface_get_bounding_box (analysis, &bbox);
|
||||
status = surface->backend->set_bounding_box (surface->target, &bbox);
|
||||
if (status) {
|
||||
cairo_surface_destroy (analysis);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_RENDER);
|
||||
|
||||
/* Finer grained fallbacks are currently only supported for some
|
||||
* surface types */
|
||||
switch (surface->target->type) {
|
||||
|
|
|
|||
|
|
@ -61,8 +61,7 @@ typedef struct cairo_ps_surface {
|
|||
|
||||
double width;
|
||||
double height;
|
||||
double max_width;
|
||||
double max_height;
|
||||
int bbox_x1, bbox_y1, bbox_x2, bbox_y2;
|
||||
|
||||
int num_pages;
|
||||
|
||||
|
|
|
|||
|
|
@ -296,21 +296,27 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface)
|
|||
time_t now;
|
||||
char **comments;
|
||||
int i, num_comments;
|
||||
const char *eps_header = "";
|
||||
|
||||
now = time (NULL);
|
||||
|
||||
if (surface->eps)
|
||||
eps_header = " EPSF-3.0";
|
||||
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
"%%!PS-Adobe-3.0\n"
|
||||
"%%!PS-Adobe-3.0%s\n"
|
||||
"%%%%Creator: cairo %s (http://cairographics.org)\n"
|
||||
"%%%%CreationDate: %s"
|
||||
"%%%%Pages: %d\n"
|
||||
"%%%%BoundingBox: %d %d %d %d\n",
|
||||
cairo_version_string (),
|
||||
eps_header,
|
||||
cairo_version_string (),
|
||||
ctime (&now),
|
||||
surface->num_pages,
|
||||
0, 0,
|
||||
(int) ceil (surface->max_width),
|
||||
(int) ceil (surface->max_height));
|
||||
surface->bbox_x1,
|
||||
surface->bbox_y1,
|
||||
surface->bbox_x2,
|
||||
surface->bbox_y2);
|
||||
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
"%%%%DocumentData: Clean7Bit\n"
|
||||
|
|
@ -329,7 +335,17 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface)
|
|||
"%%%%EndComments\n");
|
||||
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
"%%%%BeginProlog\n"
|
||||
"%%%%BeginProlog\n");
|
||||
|
||||
if (surface->eps) {
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
"/cairo_eps_state save def\n"
|
||||
"/dict_count countdictstack def\n"
|
||||
"/op_count count 1 sub def\n"
|
||||
"userdict begin\n");
|
||||
}
|
||||
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
"/C{curveto}bind def\n"
|
||||
"/F{fill}bind def\n"
|
||||
"/G{setgray}bind def\n"
|
||||
|
|
@ -766,7 +782,16 @@ static void
|
|||
_cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface)
|
||||
{
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
"%%%%Trailer\n"
|
||||
"%%%%Trailer\n");
|
||||
|
||||
if (surface->eps) {
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
"count op_count sub {pop} repeat\n"
|
||||
"countdictstack dict_count sub {end} repeat\n"
|
||||
"cairo_eps_state restore\n");
|
||||
}
|
||||
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
"%%%%EOF\n");
|
||||
}
|
||||
|
||||
|
|
@ -802,10 +827,9 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream,
|
|||
if (! surface->font_subsets)
|
||||
goto CLEANUP_OUTPUT_STREAM;
|
||||
|
||||
surface->eps = FALSE;
|
||||
surface->width = width;
|
||||
surface->height = height;
|
||||
surface->max_width = width;
|
||||
surface->max_height = height;
|
||||
surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
|
||||
surface->force_fallbacks = FALSE;
|
||||
|
||||
|
|
@ -1300,48 +1324,11 @@ static cairo_int_status_t
|
|||
_cairo_ps_surface_start_page (void *abstract_surface)
|
||||
{
|
||||
cairo_ps_surface_t *surface = abstract_surface;
|
||||
int i, num_comments;
|
||||
char **comments;
|
||||
|
||||
/* Increment before print so page numbers start at 1. */
|
||||
surface->num_pages++;
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"%%%%Page: %d %d\n",
|
||||
surface->num_pages,
|
||||
surface->num_pages);
|
||||
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"%%%%BeginPageSetup\n");
|
||||
|
||||
num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments);
|
||||
comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0);
|
||||
for (i = 0; i < num_comments; i++) {
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"%s\n", comments[i]);
|
||||
free (comments[i]);
|
||||
comments[i] = NULL;
|
||||
}
|
||||
_cairo_array_truncate (&surface->dsc_page_setup_comments, 0);
|
||||
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"%%%%PageBoundingBox: %d %d %d %d\n",
|
||||
0, 0,
|
||||
(int) ceil (surface->width),
|
||||
(int) ceil (surface->height));
|
||||
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"gsave %f %f translate 1.0 -1.0 scale gsave\n",
|
||||
0.0, surface->height);
|
||||
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"%%%%EndPageSetup\n");
|
||||
|
||||
if (surface->width > surface->max_width)
|
||||
surface->max_width = surface->width;
|
||||
if (surface->height > surface->max_height)
|
||||
surface->max_height = surface->height;
|
||||
|
||||
return _cairo_output_stream_get_status (surface->stream);
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -2426,6 +2413,73 @@ _cairo_ps_surface_set_paginated_mode (void *abstract_surface,
|
|||
surface->paginated_mode = paginated_mode;
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
_cairo_ps_surface_set_bounding_box (void *abstract_surface,
|
||||
cairo_box_t *bbox)
|
||||
{
|
||||
cairo_ps_surface_t *surface = abstract_surface;
|
||||
int i, num_comments;
|
||||
char **comments;
|
||||
int x1, y1, x2, y2;
|
||||
|
||||
if (surface->eps) {
|
||||
x1 = (int) floor (_cairo_fixed_to_double (bbox->p1.x));
|
||||
y1 = (int) floor (surface->height - _cairo_fixed_to_double (bbox->p2.y));
|
||||
x2 = (int) ceil (_cairo_fixed_to_double (bbox->p2.x));
|
||||
y2 = (int) ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y));
|
||||
} else {
|
||||
x1 = 0;
|
||||
y1 = 0;
|
||||
x2 = (int) ceil (surface->width);
|
||||
y2 = (int) ceil (surface->height);
|
||||
}
|
||||
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"%%%%Page: %d %d\n",
|
||||
surface->num_pages,
|
||||
surface->num_pages);
|
||||
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"%%%%BeginPageSetup\n");
|
||||
|
||||
num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments);
|
||||
comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0);
|
||||
for (i = 0; i < num_comments; i++) {
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"%s\n", comments[i]);
|
||||
free (comments[i]);
|
||||
comments[i] = NULL;
|
||||
}
|
||||
_cairo_array_truncate (&surface->dsc_page_setup_comments, 0);
|
||||
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"%%%%PageBoundingBox: %d %d %d %d\n"
|
||||
"gsave %f %f translate 1.0 -1.0 scale gsave\n",
|
||||
x1, y1, x2, y2,
|
||||
0.0, surface->height);
|
||||
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"%%%%EndPageSetup\n");
|
||||
|
||||
if (surface->num_pages == 1) {
|
||||
surface->bbox_x1 = x1;
|
||||
surface->bbox_y1 = y1;
|
||||
surface->bbox_x2 = x2;
|
||||
surface->bbox_y2 = y2;
|
||||
} else {
|
||||
if (x1 < surface->bbox_x1)
|
||||
surface->bbox_x1 = x1;
|
||||
if (y1 < surface->bbox_y1)
|
||||
surface->bbox_y1 = y1;
|
||||
if (x2 > surface->bbox_x2)
|
||||
surface->bbox_x2 = x2;
|
||||
if (y2 > surface->bbox_y2)
|
||||
surface->bbox_y2 = y2;
|
||||
}
|
||||
|
||||
return _cairo_output_stream_get_status (surface->stream);
|
||||
}
|
||||
|
||||
static const cairo_surface_backend_t cairo_ps_surface_backend = {
|
||||
CAIRO_SURFACE_TYPE_PS,
|
||||
NULL, /* create_similar */
|
||||
|
|
@ -2462,5 +2516,6 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = {
|
|||
|
||||
static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend = {
|
||||
_cairo_ps_surface_start_page,
|
||||
_cairo_ps_surface_set_paginated_mode
|
||||
_cairo_ps_surface_set_paginated_mode,
|
||||
_cairo_ps_surface_set_bounding_box,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue