summaryrefslogtreecommitdiff
path: root/src/cairo-ps-surface.c
diff options
context:
space:
mode:
authorAdrian Johnson <ajohnson@redneon.com>2017-11-09 20:52:36 +1030
committerAdrian Johnson <ajohnson@redneon.com>2017-11-09 22:02:05 +1030
commit5ffbaf9e2f7da103da8d015b5f928e25f9433b60 (patch)
treec1da169396ded05b1518e33d8385eaf2d2661d1d /src/cairo-ps-surface.c
parent7d3ba77b6cd20f68cfbda3d8008811265aa030cb (diff)
downloadcairo-5ffbaf9e2f7da103da8d015b5f928e25f9433b60.tar.gz
ps: add CAIRO_MIME_TYPE_EPS mime type for embedding EPS files
Diffstat (limited to 'src/cairo-ps-surface.c')
-rw-r--r--src/cairo-ps-surface.c183
1 files changed, 175 insertions, 8 deletions
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 076fe9eb6..fe09ed663 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -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;