mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-01-02 11:10:22 +01:00
ps: add CAIRO_MIME_TYPE_EPS mime type for embedding EPS files
This commit is contained in:
parent
7d3ba77b6c
commit
5ffbaf9e2f
7 changed files with 252 additions and 8 deletions
|
|
@ -244,6 +244,8 @@ cairo_device_observer_stroke_elapsed
|
|||
CAIRO_HAS_MIME_SURFACE
|
||||
CAIRO_MIME_TYPE_CCITT_FAX
|
||||
CAIRO_MIME_TYPE_CCITT_FAX_PARAMS
|
||||
CAIRO_MIME_TYPE_EPS
|
||||
CAIRO_MIME_TYPE_EPS_PARAMS
|
||||
CAIRO_MIME_TYPE_JBIG2
|
||||
CAIRO_MIME_TYPE_JBIG2_GLOBAL
|
||||
CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ typedef struct cairo_ps_surface {
|
|||
cairo_output_stream_t *stream;
|
||||
|
||||
cairo_bool_t eps;
|
||||
cairo_bool_t contains_eps;
|
||||
cairo_content_t content;
|
||||
double width;
|
||||
double height;
|
||||
|
|
|
|||
|
|
@ -116,7 +116,9 @@
|
|||
*
|
||||
* The following mime types are supported: %CAIRO_MIME_TYPE_JPEG,
|
||||
* %CAIRO_MIME_TYPE_UNIQUE_ID,
|
||||
* %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS.
|
||||
* %CAIRO_MIME_TYPE_CCITT_FAX, %CAIRO_MIME_TYPE_CCITT_FAX_PARAMS,
|
||||
* %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
|
||||
|
|
@ -126,6 +128,24 @@
|
|||
*
|
||||
* 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.
|
||||
*
|
||||
**/
|
||||
|
||||
/**
|
||||
|
|
@ -153,6 +173,8 @@ typedef struct {
|
|||
/* input params */
|
||||
cairo_surface_t *src_surface;
|
||||
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 */
|
||||
|
|
@ -162,6 +184,7 @@ typedef struct {
|
|||
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;
|
||||
|
||||
static const cairo_surface_backend_t cairo_ps_surface_backend;
|
||||
|
|
@ -354,7 +377,6 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface)
|
|||
|
||||
if (surface->eps) {
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
"save\n"
|
||||
"50 dict begin\n");
|
||||
} else {
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
|
|
@ -457,6 +479,22 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface)
|
|||
" } ifelse\n"
|
||||
"} def\n");
|
||||
}
|
||||
if (surface->contains_eps) {
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
"/cairo_eps_begin {\n"
|
||||
" /cairo_save_state save def\n"
|
||||
" /dict_count countdictstack def\n"
|
||||
" /op_count count 1 sub def\n"
|
||||
" userdict begin\n"
|
||||
" /showpage { } def\n"
|
||||
" 0 g 0 J 1 w 0 j 10 M [ ] 0 d n\n"
|
||||
"} bind def\n"
|
||||
"/cairo_eps_end {\n"
|
||||
" count op_count sub { pop } repeat\n"
|
||||
" countdictstack dict_count sub { end } repeat\n"
|
||||
" cairo_save_state restore\n"
|
||||
"} bind def\n");
|
||||
}
|
||||
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
"%%%%EndProlog\n");
|
||||
|
|
@ -954,7 +992,7 @@ _cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface)
|
|||
|
||||
if (surface->eps) {
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
"end restore\n");
|
||||
"end\n");
|
||||
}
|
||||
|
||||
_cairo_output_stream_printf (surface->final_stream,
|
||||
|
|
@ -1162,6 +1200,7 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream,
|
|||
surface->document_bbox_p2.x = 0;
|
||||
surface->document_bbox_p2.y = 0;
|
||||
surface->total_form_size = 0;
|
||||
surface->contains_eps = FALSE;
|
||||
|
||||
_cairo_surface_clipper_init (&surface->clipper,
|
||||
_cairo_ps_surface_clipper_intersect_clip_path);
|
||||
|
|
@ -3160,6 +3199,127 @@ _cairo_ps_surface_emit_ccitt_image (cairo_ps_surface_t *surface,
|
|||
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. */
|
||||
static int
|
||||
count_eod_strings (const unsigned char *data, unsigned long data_len)
|
||||
{
|
||||
const unsigned char *p = data;
|
||||
const unsigned char *end;
|
||||
int first_char, len, count;
|
||||
const char *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;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_ps_surface_emit_eps (cairo_ps_surface_t *surface,
|
||||
cairo_emit_surface_mode_t mode,
|
||||
cairo_emit_surface_params_t *params)
|
||||
{
|
||||
cairo_status_t status;
|
||||
const unsigned char *eps_data = NULL;
|
||||
unsigned long eps_data_len;
|
||||
const unsigned char *eps_params_string = NULL;
|
||||
unsigned long eps_params_string_len;
|
||||
char *params_string = NULL;
|
||||
cairo_eps_params_t eps_params;
|
||||
cairo_matrix_t mat;
|
||||
double eps_width, eps_height;
|
||||
|
||||
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;
|
||||
|
||||
cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_EPS,
|
||||
&eps_data, &eps_data_len);
|
||||
if (eps_data == NULL)
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
cairo_surface_get_mime_data (params->src_surface, CAIRO_MIME_TYPE_EPS_PARAMS,
|
||||
&eps_params_string, &eps_params_string_len);
|
||||
if (eps_params_string == NULL)
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
/* ensure params_string is null terminated */
|
||||
params_string = malloc (eps_params_string_len + 1);
|
||||
memcpy (params_string, eps_params_string, eps_params_string_len);
|
||||
params_string[eps_params_string_len] = 0;
|
||||
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 occurences 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;
|
||||
}
|
||||
|
||||
surface->ps_level_used = CAIRO_PS_LEVEL_3;
|
||||
_cairo_output_stream_printf (surface->stream, "cairo_eps_begin\n");
|
||||
|
||||
eps_width = eps_params.bbox.p2.x - eps_params.bbox.p1.x;
|
||||
eps_height = eps_params.bbox.p2.y - eps_params.bbox.p1.y;
|
||||
cairo_matrix_init_translate (&mat,
|
||||
params->src_surface_extents->x,
|
||||
params->src_surface_extents->y);
|
||||
cairo_matrix_scale (&mat,
|
||||
params->src_surface_extents->width/eps_width,
|
||||
params->src_surface_extents->height/eps_height);
|
||||
cairo_matrix_scale (&mat, 1, -1);
|
||||
cairo_matrix_translate (&mat, -eps_params.bbox.p1.x, -eps_params.bbox.p2.y);
|
||||
|
||||
if (! _cairo_matrix_is_identity (&mat)) {
|
||||
_cairo_output_stream_printf (surface->stream, "[ ");
|
||||
_cairo_output_stream_print_matrix (surface->stream, &mat);
|
||||
_cairo_output_stream_printf (surface->stream, " ] concat\n");
|
||||
}
|
||||
|
||||
_cairo_output_stream_printf (surface->stream,
|
||||
"%f %f %f %f rectclip\n",
|
||||
eps_params.bbox.p1.x,
|
||||
eps_params.bbox.p1.y,
|
||||
eps_width,
|
||||
eps_height);
|
||||
|
||||
_cairo_output_stream_write (surface->stream, eps_data, eps_data_len);
|
||||
_cairo_output_stream_printf (surface->stream, "\ncairo_eps_end\n");
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface,
|
||||
cairo_surface_t *recording_surface,
|
||||
|
|
@ -3457,6 +3617,14 @@ _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface,
|
|||
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;
|
||||
|
|
@ -3527,11 +3695,6 @@ _cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface,
|
|||
return status;
|
||||
}
|
||||
|
||||
/* The '|' character is not used in PS (including ASCII85) so we can
|
||||
* use it as a /SubFileDecode EOD marker and assume EODCount will be 0.
|
||||
*/
|
||||
#define SUBFILE_FILTER_EOD "|EOD|"
|
||||
|
||||
static void
|
||||
_cairo_ps_form_emit (void *entry, void *closure)
|
||||
{
|
||||
|
|
@ -3771,6 +3934,8 @@ _cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface,
|
|||
|
||||
params.src_surface = image ? &image->base : source_surface;
|
||||
params.op = op;
|
||||
params.src_surface_extents = &src_surface_extents;
|
||||
params.src_surface_bounded = src_surface_bounded;
|
||||
params.src_op_extents = &src_op_extents;
|
||||
params.filter = pattern->filter;
|
||||
params.stencil_mask = stencil_mask;
|
||||
|
|
@ -3920,6 +4085,8 @@ _cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface,
|
|||
|
||||
params.src_surface = image ? &image->base : source_surface;
|
||||
params.op = op;
|
||||
params.src_surface_extents = &pattern_extents;
|
||||
params.src_surface_bounded = bounded;
|
||||
params.src_op_extents = &src_op_extents;
|
||||
params.filter = pattern->filter;
|
||||
params.stencil_mask = FALSE;
|
||||
|
|
|
|||
|
|
@ -1280,6 +1280,24 @@ _cairo_surface_has_mime_image (cairo_surface_t *surface)
|
|||
* Since: 1.16
|
||||
**/
|
||||
|
||||
/**
|
||||
* CAIRO_MIME_TYPE_EPS:
|
||||
*
|
||||
* Encapsulated PostScript file.
|
||||
* [Encapsulated PostScript File Format Specification](http://wwwimages.adobe.com/content/dam/Adobe/endevnet/postscript/pdfs/5002.EPSF_Spec.pdf)
|
||||
*
|
||||
* Since: 1.16
|
||||
**/
|
||||
|
||||
/**
|
||||
* CAIRO_MIME_TYPE_EPS_PARAMS:
|
||||
*
|
||||
* Embedding parameters Encapsulated PostScript data.
|
||||
* See [Embedding EPS files][eps].
|
||||
*
|
||||
* Since: 1.16
|
||||
**/
|
||||
|
||||
/**
|
||||
* CAIRO_MIME_TYPE_JBIG2:
|
||||
*
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
#include "cairo-array-private.h"
|
||||
#include "cairo-error-private.h"
|
||||
#include "cairo-types-private.h"
|
||||
|
||||
typedef enum {
|
||||
TAG_LINK_INVALID = 0,
|
||||
|
|
@ -79,6 +80,9 @@ typedef struct _cairo_ccitt_params {
|
|||
int damaged_rows_before_error;
|
||||
} cairo_ccitt_params_t;
|
||||
|
||||
typedef struct _cairo_eps_params {
|
||||
cairo_box_double_t bbox;
|
||||
} cairo_eps_params_t;
|
||||
|
||||
cairo_private cairo_int_status_t
|
||||
_cairo_tag_parse_link_attributes (const char *attributes, cairo_link_attrs_t *link_attrs);
|
||||
|
|
@ -89,4 +93,7 @@ _cairo_tag_parse_dest_attributes (const char *attributes, cairo_dest_attrs_t *de
|
|||
cairo_private cairo_int_status_t
|
||||
_cairo_tag_parse_ccitt_params (const char *attributes, cairo_ccitt_params_t *dest_attrs);
|
||||
|
||||
cairo_private cairo_int_status_t
|
||||
_cairo_tag_parse_eps_params (const char *attributes, cairo_eps_params_t *dest_attrs);
|
||||
|
||||
#endif /* CAIRO_TAG_ATTRIBUTES_PRIVATE_H */
|
||||
|
|
|
|||
|
|
@ -143,6 +143,20 @@ static attribute_spec_t _ccitt_params_spec[] =
|
|||
{ NULL }
|
||||
};
|
||||
|
||||
/*
|
||||
* bbox - Bounding box of EPS file. The format is [ llx lly urx ury ]
|
||||
* llx - lower left x xoordinate
|
||||
* lly - lower left y xoordinate
|
||||
* urx - upper right x xoordinate
|
||||
* ury - upper right y xoordinate
|
||||
* all cordinates are in PostScript coordinates.
|
||||
*/
|
||||
static attribute_spec_t _eps_params_spec[] =
|
||||
{
|
||||
{ "bbox", ATTRIBUTE_FLOAT, 4 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
typedef union {
|
||||
cairo_bool_t b;
|
||||
int i;
|
||||
|
|
@ -649,3 +663,36 @@ _cairo_tag_parse_ccitt_params (const char *attributes, cairo_ccitt_params_t *cci
|
|||
|
||||
return status;
|
||||
}
|
||||
|
||||
cairo_int_status_t
|
||||
_cairo_tag_parse_eps_params (const char *attributes, cairo_eps_params_t *eps_params)
|
||||
{
|
||||
cairo_list_t list;
|
||||
cairo_int_status_t status;
|
||||
attribute_t *attr;
|
||||
attrib_val_t val;
|
||||
|
||||
cairo_list_init (&list);
|
||||
status = parse_attributes (attributes, _eps_params_spec, &list);
|
||||
if (unlikely (status))
|
||||
goto cleanup;
|
||||
|
||||
cairo_list_foreach_entry (attr, attribute_t, &list, link)
|
||||
{
|
||||
if (strcmp (attr->name, "bbox") == 0) {
|
||||
_cairo_array_copy_element (&attr->array, 0, &val);
|
||||
eps_params->bbox.p1.x = val.f;
|
||||
_cairo_array_copy_element (&attr->array, 1, &val);
|
||||
eps_params->bbox.p1.y = val.f;
|
||||
_cairo_array_copy_element (&attr->array, 2, &val);
|
||||
eps_params->bbox.p2.x = val.f;
|
||||
_cairo_array_copy_element (&attr->array, 3, &val);
|
||||
eps_params->bbox.p2.y = val.f;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free_attributes_list (&list);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2455,6 +2455,8 @@ cairo_surface_set_user_data (cairo_surface_t *surface,
|
|||
#define CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID "application/x-cairo.jbig2-global-id"
|
||||
#define CAIRO_MIME_TYPE_CCITT_FAX "image/g3fax"
|
||||
#define CAIRO_MIME_TYPE_CCITT_FAX_PARAMS "application/x-cairo.ccitt.params"
|
||||
#define CAIRO_MIME_TYPE_EPS "application/postscript"
|
||||
#define CAIRO_MIME_TYPE_EPS_PARAMS "application/x-cairo.eps.params"
|
||||
|
||||
cairo_public void
|
||||
cairo_surface_get_mime_data (cairo_surface_t *surface,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue