summaryrefslogtreecommitdiff
path: root/gs/base/gdevpdfv.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/base/gdevpdfv.c')
-rw-r--r--gs/base/gdevpdfv.c962
1 files changed, 962 insertions, 0 deletions
diff --git a/gs/base/gdevpdfv.c b/gs/base/gdevpdfv.c
new file mode 100644
index 000000000..1963775c0
--- /dev/null
+++ b/gs/base/gdevpdfv.c
@@ -0,0 +1,962 @@
+/* Copyright (C) 2001-2006 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied, modified
+ or distributed except as expressly authorized under the terms of that
+ license. Refer to licensing information at http://www.artifex.com/
+ or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+ San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* $Id$ */
+/* Color value writing for pdfwrite driver */
+#include "math_.h"
+#include "string_.h"
+#include "gx.h"
+#include "gscindex.h"
+#include "gserrors.h"
+#include "gsiparm3.h" /* for pattern colors */
+#include "gsmatrix.h" /* for gspcolor.h */
+#include "gscoord.h" /* for gs_currentmatrix, requires gsmatrix.h */
+#include "gsptype2.h"
+#include "gxcolor2.h" /* for gxpcolor.h */
+#include "gxdcolor.h" /* for gxpcolor.h */
+#include "gxpcolor.h" /* for pattern device color types */
+#include "gxshade.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfo.h"
+#include "szlibx.h"
+
+/* Import the PatternType 2 Pattern device color type. */
+extern const gx_device_color_type_t gx_dc_pattern2;
+
+/*
+ * Define the scaling and range of values written for mesh shadings.
+ * BitsPerCoordinate is always 24; BitsPerComponent (for colors) is
+ * always 16.
+ */
+#define ENCODE_VALUE(v, emax, vmin, vmax)\
+ ( ((v) - (vmin)) * ((double)(emax) / ((vmax) - (vmin))) )
+/*
+ * Because of the Acrobat Reader limitation noted in gdevpdfx.h,
+ * we must limit coordinate values to 14 bits.
+ */
+#define MIN_MESH_COORDINATE (-0x400000 / 256.0)
+#define MAX_MESH_COORDINATE ( 0x3fffff / 256.0)
+#define ENCODE_MESH_COORDINATE(v)\
+ ENCODE_VALUE(v, 0xffffff, MIN_MESH_COORDINATE, MAX_MESH_COORDINATE)
+
+#define MIN_MESH_COLOR_INDEX 0
+#define MAX_MESH_COLOR_INDEX 0xffff
+#define ENCODE_MESH_COLOR_INDEX(v) ((v) + MIN_MESH_COLOR_INDEX)
+
+#define ENCODE_MESH_COMPONENT(v, vmin, vmax)\
+ ENCODE_VALUE(v, 0xffff, vmin, vmax)
+
+/* ---------------- Utilities ---------------- */
+
+/* Write a matrix parameter. */
+static int
+cos_dict_put_matrix(cos_dict_t *pscd, const char *key, const gs_matrix *pmat)
+{
+ float matrix[6];
+
+ matrix[0] = pmat->xx;
+ matrix[1] = pmat->xy;
+ matrix[2] = pmat->yx;
+ matrix[3] = pmat->yy;
+ matrix[4] = pmat->tx;
+ matrix[5] = pmat->ty;
+ return cos_dict_put_c_key_floats(pscd, key, matrix, 6);
+}
+
+/* ---------------- PatternType 1 colors ---------------- */
+
+/*
+ * Create a Pattern resource referencing an image (currently only an XObject).
+ * p_tile is NULL for uncolored patterns or the NULL pattern.
+ * m_tile is NULL for colored patterns that fill their bounding box,
+ * including the NULL pattern.
+ ****** WE DON'T HANDLE NULL PATTERNS YET ******
+ */
+static uint
+tile_size(const gx_strip_bitmap *tile, int depth)
+{
+ return (tile->rep_width * depth + 7) / 8 * tile->rep_height;
+}
+static bool
+tile_size_ok(const gx_device_pdf *pdev, const gx_color_tile *p_tile,
+ const gx_color_tile *m_tile)
+{
+ /*
+ * Acrobat Reader can't handle image Patterns with more than
+ * 64K of data. :-(
+ */
+ uint p_size =
+ (p_tile == 0 ? 0 : tile_size(&p_tile->tbits, p_tile->depth));
+ uint m_size =
+ (m_tile == 0 ? 0 : tile_size(&m_tile->tmask, 1));
+ return (max(p_size, m_size) <= 65500);
+}
+
+static int
+pdf_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc,
+ const gx_color_tile *p_tile, const gx_color_tile *m_tile,
+ cos_stream_t *pcs_image, pdf_resource_t **ppres)
+{
+ pdf_resource_t *pres;
+ int code = pdf_alloc_resource(pdev, resourcePattern, pdc->mask.id, ppres,
+ 0L);
+ cos_stream_t *pcos;
+ cos_dict_t *pcd;
+ cos_dict_t *pcd_Resources = cos_dict_alloc(pdev, "pdf_pattern(Resources)");
+ const gx_color_tile *tile = (p_tile ? p_tile : m_tile);
+ const gx_strip_bitmap *btile = (p_tile ? &p_tile->tbits : &m_tile->tmask);
+ bool mask = p_tile == 0;
+ gs_point step;
+ gs_matrix smat;
+
+ if (code < 0)
+ return code;
+ if (!tile_size_ok(pdev, p_tile, m_tile))
+ return_error(gs_error_limitcheck);
+ /*
+ * We currently can't handle Patterns whose X/Y step isn't parallel
+ * to the coordinate axes.
+ */
+ if (is_xxyy(&tile->step_matrix))
+ step.x = tile->step_matrix.xx, step.y = tile->step_matrix.yy;
+ else if (is_xyyx(&tile->step_matrix))
+ step.x = tile->step_matrix.yx, step.y = tile->step_matrix.xy;
+ else
+ return_error(gs_error_rangecheck);
+ if (pcd_Resources == 0)
+ return_error(gs_error_VMerror);
+ gs_make_identity(&smat);
+ smat.xx = btile->rep_width / (pdev->HWResolution[0] / 72.0);
+ smat.yy = btile->rep_height / (pdev->HWResolution[1] / 72.0);
+ smat.tx = tile->step_matrix.tx / (pdev->HWResolution[0] / 72.0);
+ smat.ty = tile->step_matrix.ty / (pdev->HWResolution[1] / 72.0);
+ pres = *ppres;
+ {
+ cos_dict_t *pcd_XObject = cos_dict_alloc(pdev, "pdf_pattern(XObject)");
+ char key[MAX_REF_CHARS + 3];
+ cos_value_t v;
+
+ if (pcd_XObject == 0)
+ return_error(gs_error_VMerror);
+ sprintf(key, "/R%ld", pcs_image->id);
+ COS_OBJECT_VALUE(&v, pcs_image);
+ if ((code = cos_dict_put(pcd_XObject, (byte *)key, strlen(key), &v)) < 0 ||
+ (code = cos_dict_put_c_key_object(pcd_Resources, "/XObject",
+ COS_OBJECT(pcd_XObject))) < 0
+ )
+ return code;
+ }
+ if ((code = cos_dict_put_c_strings(pcd_Resources, "/ProcSet",
+ (mask ? "[/PDF/ImageB]" :
+ "[/PDF/ImageC]"))) < 0)
+ return code;
+ cos_become(pres->object, cos_type_stream);
+ pcos = (cos_stream_t *)pres->object;
+ pcd = cos_stream_dict(pcos);
+ if ((code = cos_dict_put_c_key_int(pcd, "/PatternType", 1)) < 0 ||
+ (code = cos_dict_put_c_key_int(pcd, "/PaintType",
+ (mask ? 2 : 1))) < 0 ||
+ (code = cos_dict_put_c_key_int(pcd, "/TilingType",
+ tile->tiling_type)) < 0 ||
+ (code = cos_dict_put_c_key_object(pcd, "/Resources",
+ COS_OBJECT(pcd_Resources))) < 0 ||
+ (code = cos_dict_put_c_strings(pcd, "/BBox", "[0 0 1 1]")) < 0 ||
+ (code = cos_dict_put_matrix(pcd, "/Matrix", &smat)) < 0 ||
+ (code = cos_dict_put_c_key_real(pcd, "/XStep", step.x / btile->rep_width)) < 0 ||
+ (code = cos_dict_put_c_key_real(pcd, "/YStep", step.y / btile->rep_height)) < 0
+ ) {
+ return code;
+ }
+
+ {
+ char buf[MAX_REF_CHARS + 6 + 1]; /* +6 for /R# Do\n */
+
+ sprintf(buf, "/R%ld Do\n", pcs_image->id);
+ cos_stream_add_bytes(pcos, (const byte *)buf, strlen(buf));
+ }
+
+ return 0;
+}
+
+/* Store pattern 1 parameters to cos dictionary. */
+int
+pdf_store_pattern1_params(gx_device_pdf *pdev, pdf_resource_t *pres,
+ gs_pattern1_instance_t *pinst)
+{
+ gs_pattern1_template_t *t = &pinst->template;
+ gs_matrix smat = ctm_only((gs_imager_state *)pinst->saved);
+ double scale_x = pdev->HWResolution[0] / 72.0;
+ double scale_y = pdev->HWResolution[1] / 72.0;
+ cos_dict_t *pcd = cos_stream_dict((cos_stream_t *)pres->object);
+ cos_dict_t *pcd_Resources = cos_dict_alloc(pdev, "pdf_pattern(Resources)");
+ float bbox[4];
+ int code;
+
+ if (pcd == NULL || pcd_Resources == NULL)
+ return_error(gs_error_VMerror);
+ pdev->substream_Resources = pcd_Resources;
+ bbox[0] = t->BBox.p.x;
+ bbox[1] = t->BBox.p.y;
+ bbox[2] = t->BBox.q.x;
+ bbox[3] = t->BBox.q.y;
+ /* The graphics library assumes a shifted origin to provide
+ positive bitmap pixel indices. Compensate it now. */
+ smat.tx += pinst->step_matrix.tx;
+ smat.ty += pinst->step_matrix.ty;
+ smat.xx /= scale_x;
+ smat.xy /= scale_x;
+ smat.yx /= scale_y;
+ smat.yy /= scale_y;
+ smat.tx /= scale_x;
+ smat.ty /= scale_y;
+ if (any_abs(smat.tx) < 0.0001) /* Noise. */
+ smat.tx = 0;
+ if (any_abs(smat.ty) < 0.0001)
+ smat.ty = 0;
+ code = cos_dict_put_c_strings(pcd, "/Type", "/Pattern");
+ if (code >= 0)
+ code = cos_dict_put_c_key_int(pcd, "/PatternType", 1);
+ if (code >= 0)
+ code = cos_dict_put_c_key_int(pcd, "/PaintType", t->PaintType);
+ if (code >= 0)
+ code = cos_dict_put_c_key_int(pcd, "/TilingType", t->TilingType);
+ if (code >= 0)
+ code = cos_dict_put_c_key_floats(pcd, "/BBox", bbox, 4);
+ if (code >= 0)
+ code = cos_dict_put_matrix(pcd, "/Matrix", &smat);
+ if (code >= 0)
+ code = cos_dict_put_c_key_real(pcd, "/XStep", t->XStep);
+ if (code >= 0)
+ code = cos_dict_put_c_key_real(pcd, "/YStep", t->YStep);
+ if (code >= 0)
+ code = cos_dict_put_c_key_object(pcd, "/Resources", COS_OBJECT(pcd_Resources));
+ pdev->skip_colors = (t->PaintType == 2);
+ return code;
+}
+
+/* Set the ImageMatrix, Width, and Height for a Pattern image. */
+static void
+pdf_set_pattern_image(gs_data_image_t *pic, const gx_strip_bitmap *tile)
+{
+ pic->ImageMatrix.xx = (float)(pic->Width = tile->rep_width);
+ pic->ImageMatrix.yy = (float)(pic->Height = tile->rep_height);
+}
+
+/* Write the mask for a Pattern (colored or uncolored). */
+static int
+pdf_put_pattern_mask(gx_device_pdf *pdev, const gx_color_tile *m_tile,
+ cos_stream_t **ppcs_mask)
+{
+ int w = m_tile->tmask.rep_width, h = m_tile->tmask.rep_height;
+ gs_image1_t image;
+ pdf_image_writer writer;
+ int code;
+
+ gs_image_t_init_mask_adjust(&image, true, false);
+ pdf_set_pattern_image((gs_data_image_t *)&image, &m_tile->tmask);
+ pdf_image_writer_init(&writer);
+ if ((code = pdf_begin_write_image(pdev, &writer, gs_no_id, w, h, NULL, false)) < 0 ||
+ (pdev->params.MonoImage.Encode &&
+ (code = psdf_CFE_binary(&writer.binary[0], w, h, true)) < 0) ||
+ (code = pdf_begin_image_data(pdev, &writer, (const gs_pixel_image_t *)&image, NULL, 0)) < 0
+ )
+ return code;
+ /* Pattern masks are specified in device coordinates, so invert Y. */
+ if ((code = pdf_copy_mask_bits(writer.binary[0].strm, m_tile->tmask.data + (h - 1) * m_tile->tmask.raster, 0, -m_tile->tmask.raster, w, h, 0)) < 0 ||
+ (code = pdf_end_image_binary(pdev, &writer, h)) < 0 ||
+ (code = pdf_end_write_image(pdev, &writer)) < 0
+ )
+ return code;
+ *ppcs_mask = (cos_stream_t *)writer.pres->object;
+ return 0;
+}
+
+/* Write an uncolored Pattern color. */
+int
+pdf_put_uncolored_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc,
+ const gs_color_space *pcs,
+ const psdf_set_color_commands_t *ppscc,
+ bool have_pattern_streams, pdf_resource_t **ppres)
+{
+ const gx_color_tile *m_tile = pdc->mask.m_tile;
+ gx_drawing_color dc_pure;
+
+ if (!have_pattern_streams && m_tile == 0) {
+ /*
+ * If m_tile == 0, this uncolored Pattern is all 1's,
+ * equivalent to a pure color.
+ */
+ *ppres = 0;
+ set_nonclient_dev_color(&dc_pure, gx_dc_pure_color(pdc));
+ return psdf_set_color((gx_device_vector *)pdev, &dc_pure, ppscc);
+ } else {
+ cos_value_t v;
+ stream *s = pdev->strm;
+ int code;
+ cos_stream_t *pcs_image;
+ static const psdf_set_color_commands_t no_scc = {0, 0, 0};
+
+ if (!tile_size_ok(pdev, NULL, m_tile))
+ return_error(gs_error_limitcheck);
+ if (!have_pattern_streams) {
+ if ((code = pdf_cs_Pattern_uncolored(pdev, &v)) < 0 ||
+ (code = pdf_put_pattern_mask(pdev, m_tile, &pcs_image)) < 0 ||
+ (code = pdf_pattern(pdev, pdc, NULL, m_tile, pcs_image, ppres)) < 0
+ )
+ return code;
+ } else {
+ code = pdf_cs_Pattern_uncolored_hl(pdev, pcs, &v);
+ if (code < 0)
+ return code;
+ *ppres = pdf_find_resource_by_gs_id(pdev, resourcePattern, pdc->mask.id);
+ *ppres = pdf_substitute_pattern(*ppres);
+ if (!pdev->AR4_save_bug && pdev->CompatibilityLevel <= 1.3) {
+ /* We reconnized AR4 behavior as reserving "q Q" stack elements
+ * on demand. It looks as processing a pattern stream
+ * with PaintType 1 AR4 replaces the topmost stack element
+ * instead allocating a new one, if it was not previousely allocated.
+ * AR 5 doesn't have this bug. Working around the AR4 bug here.
+ */
+ stream_puts(pdev->strm, "q q Q Q\n");
+ pdev->AR4_save_bug = true;
+ }
+ }
+ cos_value_write(&v, pdev);
+ pprints1(s, " %s ", ppscc->setcolorspace);
+ if (have_pattern_streams)
+ return 0;
+ set_nonclient_dev_color(&dc_pure, gx_dc_pure_color(pdc));
+ return psdf_set_color((gx_device_vector *)pdev, &dc_pure, &no_scc);
+ }
+}
+
+int
+pdf_put_colored_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc,
+ const gs_color_space *pcs,
+ const psdf_set_color_commands_t *ppscc,
+ bool have_pattern_streams, pdf_resource_t **ppres)
+{
+ const gx_color_tile *p_tile = pdc->colors.pattern.p_tile;
+ gs_color_space *pcs_Device;
+ cos_value_t cs_value;
+ cos_value_t v;
+ int code;
+ gs_image1_t image;
+ const gx_color_tile *m_tile = NULL;
+ pdf_image_writer writer;
+ int w = p_tile->tbits.rep_width, h = p_tile->tbits.rep_height;
+
+ if (!have_pattern_streams) {
+ /*
+ * NOTE: We assume here that the color space of the cached Pattern
+ * is the same as the native color space of the device. This will
+ * have to change in the future!
+ */
+ /*
+ * Check whether this colored pattern is actually a masked pure color,
+ * by testing whether all the colored pixels have the same color.
+ */
+ m_tile = pdc->mask.m_tile;
+ if (m_tile) {
+ if (p_tile && !(p_tile->depth & 7) && p_tile->depth <= arch_sizeof_color_index * 8) {
+ int depth_bytes = p_tile->depth >> 3;
+ int width = p_tile->tbits.rep_width;
+ int skip = p_tile->tbits.raster -
+ p_tile->tbits.rep_width * depth_bytes;
+ const byte *bp;
+ const byte *mp;
+ int i, j, k;
+ gx_color_index color = 0; /* init is arbitrary if not empty */
+ bool first = true;
+
+ for (i = 0, bp = p_tile->tbits.data, mp = p_tile->tmask.data;
+ i < p_tile->tbits.rep_height;
+ ++i, bp += skip, mp += p_tile->tmask.raster) {
+
+ for (j = 0; j < width; ++j) {
+ if (mp[j >> 3] & (0x80 >> (j & 7))) {
+ gx_color_index ci = 0;
+
+ for (k = 0; k < depth_bytes; ++k)
+ ci = (ci << 8) + *bp++;
+ if (first)
+ color = ci, first = false;
+ else if (ci != color)
+ goto not_pure;
+ } else
+ bp += depth_bytes;
+ }
+ }
+ {
+ /* Set the color, then handle as an uncolored pattern. */
+ gx_drawing_color dcolor;
+
+ dcolor = *pdc;
+ dcolor.colors.pure = color;
+ return pdf_put_uncolored_pattern(pdev, &dcolor, pcs, ppscc,
+ have_pattern_streams, ppres);
+ }
+ not_pure:
+ DO_NOTHING; /* required by MSVC */
+ }
+ if (pdev->CompatibilityLevel < 1.3) {
+ /* Masked images are only supported starting in PDF 1.3. */
+ return_error(gs_error_rangecheck);
+ }
+ }
+ /* Acrobat Reader has a size limit for image Patterns. */
+ if (!tile_size_ok(pdev, p_tile, m_tile))
+ return_error(gs_error_limitcheck);
+ }
+ code = pdf_cs_Pattern_colored(pdev, &v);
+ if (code < 0)
+ return code;
+ pdf_cspace_init_Device(pdev->memory, &pcs_Device, pdev->color_info.num_components);
+ /*
+ * We don't have to worry about color space scaling: the color
+ * space is always a Device space.
+ */
+ code = pdf_color_space(pdev, &cs_value, NULL, pcs_Device,
+ &pdf_color_space_names, true);
+ if (code < 0)
+ return code;
+ if (!have_pattern_streams) {
+ cos_stream_t *pcs_mask = 0;
+ cos_stream_t *pcs_image;
+
+ gs_image_t_init_adjust(&image, pcs_Device, false);
+ image.BitsPerComponent = 8;
+ pdf_set_pattern_image((gs_data_image_t *)&image, &p_tile->tbits);
+ if (m_tile) {
+ if ((code = pdf_put_pattern_mask(pdev, m_tile, &pcs_mask)) < 0)
+ return code;
+ }
+ pdf_image_writer_init(&writer);
+ pdev->ParamCompatibilityLevel = pdev->CompatibilityLevel;
+ if ((code = pdf_begin_write_image(pdev, &writer, gs_no_id, w, h, NULL, false)) < 0 ||
+ (code = psdf_setup_lossless_filters((gx_device_psdf *)pdev,
+ &writer.binary[0],
+ (gs_pixel_image_t *)&image, false)) < 0 ||
+ (code = pdf_begin_image_data(pdev, &writer, (const gs_pixel_image_t *)&image, &cs_value, 0)) < 0
+ )
+ return code;
+ /* Pattern masks are specified in device coordinates, so invert Y. */
+ if ((code = pdf_copy_color_bits(writer.binary[0].strm, p_tile->tbits.data + (h - 1) * p_tile->tbits.raster, 0, -p_tile->tbits.raster, w, h, pdev->color_info.depth >> 3)) < 0 ||
+ (code = pdf_end_image_binary(pdev, &writer, h)) < 0
+ )
+ return code;
+ pcs_image = (cos_stream_t *)writer.pres->object;
+ if ((pcs_mask != 0 &&
+ (code = cos_dict_put_c_key_object(cos_stream_dict(pcs_image), "/Mask",
+ COS_OBJECT(pcs_mask))) < 0) ||
+ (code = pdf_end_write_image(pdev, &writer)) < 0
+ )
+ return code;
+ pcs_image = (cos_stream_t *)writer.pres->object; /* pdf_end_write_image may change it. */
+ code = pdf_pattern(pdev, pdc, p_tile, m_tile, pcs_image, ppres);
+ if (code < 0)
+ return code;
+ } else {
+ *ppres = pdf_find_resource_by_gs_id(pdev, resourcePattern, p_tile->id);
+ *ppres = pdf_substitute_pattern(*ppres);
+ }
+ /* pcs_Device will leak (picked up by GC in PS) on error, but we'll
+ tolerate that for now. */
+ rc_decrement(pcs_Device, "pdf_put_colored_pattern");
+ cos_value_write(&v, pdev);
+ pprints1(pdev->strm, " %s", ppscc->setcolorspace);
+ return 0;
+}
+
+
+/* ---------------- PatternType 2 colors ---------------- */
+
+/* Write parameters common to all Shadings. */
+static int
+pdf_put_shading_common(cos_dict_t *pscd, const gs_shading_t *psh,
+ bool shfill, const gs_range_t **ppranges)
+{
+ gs_shading_type_t type = ShadingType(psh);
+ const gs_color_space *pcs = psh->params.ColorSpace;
+ int code = cos_dict_put_c_key_int(pscd, "/ShadingType", (int)type);
+ cos_value_t cs_value;
+
+ if (code < 0 ||
+ (psh->params.AntiAlias &&
+ (code = cos_dict_put_c_strings(pscd, "/AntiAlias", "true")) < 0) ||
+ (code = pdf_color_space(pscd->pdev, &cs_value, ppranges, pcs,
+ &pdf_color_space_names, false)) < 0 ||
+ (code = cos_dict_put_c_key(pscd, "/ColorSpace", &cs_value)) < 0
+ )
+ return code;
+ if (psh->params.Background && !shfill) {
+ /****** SCALE Background ******/
+ code = cos_dict_put_c_key_floats(pscd, "/Background",
+ psh->params.Background->paint.values,
+ gs_color_space_num_components(pcs));
+ if (code < 0)
+ return code;
+ }
+ if (psh->params.have_BBox) {
+ float bbox[4];
+
+ bbox[0] = psh->params.BBox.p.x;
+ bbox[1] = psh->params.BBox.p.y;
+ bbox[2] = psh->params.BBox.q.x;
+ bbox[3] = psh->params.BBox.q.y;
+ code = cos_dict_put_c_key_floats(pscd, "/BBox", bbox, 4);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* Write an optional Function parameter. */
+static int
+pdf_put_shading_Function(cos_dict_t *pscd, const gs_function_t *pfn,
+ const gs_range_t *pranges)
+{
+ int code = 0;
+
+ if (pfn != 0) {
+ cos_value_t fn_value;
+
+ if ((code = pdf_function_scaled(pscd->pdev, pfn, pranges, &fn_value)) >= 0)
+ code = cos_dict_put_c_key(pscd, "/Function", &fn_value);
+ }
+ return code;
+}
+
+/* Write a linear (Axial / Radial) Shading. */
+static int
+pdf_put_linear_shading(cos_dict_t *pscd, const float *Coords,
+ int num_coords, const float *Domain /*[2]*/,
+ const gs_function_t *Function,
+ const bool *Extend /*[2]*/,
+ const gs_range_t *pranges)
+{
+ int code = cos_dict_put_c_key_floats(pscd, "/Coords", Coords, num_coords);
+
+ if (code < 0 ||
+ ((Domain[0] != 0 || Domain[1] != 1) &&
+ (code = cos_dict_put_c_key_floats(pscd, "/Domain", Domain, 2)) < 0) ||
+ (code = pdf_put_shading_Function(pscd, Function, pranges)) < 0
+ )
+ return code;
+ if (Extend[0] | Extend[1]) {
+ char extend_str[1 + 5 + 1 + 5 + 1 + 1]; /* [bool bool] */
+
+ sprintf(extend_str, "[%s %s]",
+ (Extend[0] ? "true" : "false"),
+ (Extend[1] ? "true" : "false"));
+ code = cos_dict_put_c_key_string(pscd, "/Extend",
+ (const byte *)extend_str,
+ strlen(extend_str));
+ }
+ return code;
+}
+
+/* Write a scalar (non-mesh) Shading. */
+/* (Single-use procedure for readability.) */
+static int
+pdf_put_scalar_shading(cos_dict_t *pscd, const gs_shading_t *psh,
+ const gs_range_t *pranges)
+{
+ int code;
+
+ switch (ShadingType(psh)) {
+ case shading_type_Function_based: {
+ const gs_shading_Fb_params_t *const params =
+ (const gs_shading_Fb_params_t *)&psh->params;
+
+ if ((code = cos_dict_put_c_key_floats(pscd, "/Domain", params->Domain, 4)) < 0 ||
+ (code = pdf_put_shading_Function(pscd, params->Function, pranges)) < 0 ||
+ (code = cos_dict_put_matrix(pscd, "/Matrix", &params->Matrix)) < 0
+ )
+ return code;
+ return 0;
+ }
+ case shading_type_Axial: {
+ const gs_shading_A_params_t *const params =
+ (const gs_shading_A_params_t *)&psh->params;
+
+ return pdf_put_linear_shading(pscd, params->Coords, 4,
+ params->Domain, params->Function,
+ params->Extend, pranges);
+ }
+ case shading_type_Radial: {
+ const gs_shading_R_params_t *const params =
+ (const gs_shading_R_params_t *)&psh->params;
+
+ return pdf_put_linear_shading(pscd, params->Coords, 6,
+ params->Domain, params->Function,
+ params->Extend, pranges);
+ }
+ default:
+ return_error(gs_error_rangecheck);
+ }
+}
+
+/* Add a floating point range to an array. */
+static int
+pdf_array_add_real2(cos_array_t *pca, floatp lower, floatp upper)
+{
+ int code = cos_array_add_real(pca, lower);
+
+ if (code >= 0)
+ code = cos_array_add_real(pca, upper);
+ return code;
+}
+
+/* Define a parameter structure for mesh data. */
+typedef struct pdf_mesh_data_params_s {
+ int num_points;
+ int num_components;
+ bool is_indexed;
+ const float *Domain; /* iff Function */
+ const gs_range_t *ranges;
+} pdf_mesh_data_params_t;
+
+/* Put a clamped value into a data stream. num_bytes < sizeof(int). */
+static void
+put_clamped(byte *p, floatp v, int num_bytes)
+{
+ int limit = 1 << (num_bytes * 8);
+ int i, shift;
+
+ if (v <= -limit)
+ i = -limit + 1;
+ else if (v >= limit)
+ i = limit - 1;
+ else
+ i = (int)v;
+ for (shift = (num_bytes - 1) * 8; shift >= 0; shift -= 8)
+ *p++ = (byte)(i >> shift);
+}
+static inline void
+put_clamped_coord(byte *p, floatp v, int num_bytes)
+{
+ put_clamped(p, ENCODE_MESH_COORDINATE(v), num_bytes);
+}
+
+/* Convert floating-point mesh data to packed binary. */
+/* BitsPerFlag = 8, BitsPerCoordinate = 24, BitsPerComponent = 16, */
+/* scaling is as defined below. */
+static int
+put_float_mesh_data(cos_stream_t *pscs, shade_coord_stream_t *cs,
+ int flag, const pdf_mesh_data_params_t *pmdp)
+{
+ int num_points = pmdp->num_points;
+ byte b[1 + (3 + 3) * 16]; /* flag + x + y or c */
+ gs_fixed_point pts[16];
+ const float *domain = pmdp->Domain;
+ const gs_range_t *pranges = pmdp->ranges;
+ int i, code;
+
+ b[0] = (byte)flag; /* may be -1 */
+ if ((code = shade_next_coords(cs, pts, num_points)) < 0)
+ return code;
+ for (i = 0; i < num_points; ++i) {
+ put_clamped_coord(b + 1 + i * 6, fixed2float(pts[i].x), 3);
+ put_clamped_coord(b + 4 + i * 6, fixed2float(pts[i].y), 3);
+ }
+ if ((code = cos_stream_add_bytes(pscs, b + (flag < 0),
+ (flag >= 0) + num_points * 6)) < 0)
+ return code;
+ for (i = 0; i < pmdp->num_components; ++i) {
+ float c;
+ double v;
+
+ cs->get_decoded(cs, 0, NULL, &c);
+ if (pmdp->is_indexed)
+ v = ENCODE_MESH_COLOR_INDEX(c);
+ else {
+ /*
+ * We don't rescale stream data values, only the Decode ranges.
+ * (We do have to rescale data values from an array, unless
+ * they are the input parameter for a Function.)
+ * This makes everything come out as it should.
+ */
+ double vmin, vmax;
+
+ if (domain)
+ vmin = domain[2 * i], vmax = domain[2 * i + 1];
+ else
+ vmin = 0.0, vmax = 1.0;
+ if (pranges) {
+ double base = pranges[i].rmin, factor = pranges[i].rmax - base;
+
+ vmin = vmin * factor + base;
+ vmax = vmax * factor + base;
+ }
+ v = ENCODE_MESH_COMPONENT(c, vmin, vmax);
+ }
+ put_clamped(b, v, 2);
+ if ((code = cos_stream_add_bytes(pscs, b, 2)) < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* Write a mesh Shading. */
+static int
+pdf_put_mesh_shading(cos_stream_t *pscs, const gs_shading_t *psh,
+ const gs_range_t *pranges)
+{
+ cos_dict_t *const pscd = cos_stream_dict(pscs);
+ gs_color_space *pcs = psh->params.ColorSpace;
+ const gs_shading_mesh_params_t *const pmp =
+ (const gs_shading_mesh_params_t *)&psh->params;
+ int code, code1;
+ int bits_per_coordinate, bits_per_component, bits_per_flag;
+ int num_comp;
+ bool from_array = data_source_is_array(pmp->DataSource);
+ pdf_mesh_data_params_t data_params;
+ shade_coord_stream_t cs;
+ gs_matrix_fixed ctm_ident;
+ int flag;
+
+ if (pmp->Function) {
+ data_params.Domain = 0;
+ num_comp = 1;
+ } else {
+ data_params.Domain = (pmp->Decode != 0 ? pmp->Decode + 4 : NULL);
+ num_comp = gs_color_space_num_components(pcs);
+ }
+ data_params.ranges = pranges;
+
+ /* Write parameters common to all mesh Shadings. */
+ shade_next_init(&cs, pmp, NULL);
+ if (from_array) {
+ cos_array_t *pca = cos_array_alloc(pscd->pdev, "pdf_put_mesh_shading");
+ int i;
+
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < 2; ++i)
+ if ((code = pdf_array_add_real2(pca, MIN_MESH_COORDINATE,
+ MAX_MESH_COORDINATE)) < 0)
+ return code;
+ data_params.is_indexed = false;
+ if (gs_color_space_get_index(pcs) == gs_color_space_index_Indexed) {
+ data_params.is_indexed = true;
+ if ((code = pdf_array_add_real2(pca, MIN_MESH_COLOR_INDEX,
+ MAX_MESH_COLOR_INDEX)) < 0)
+ return code;
+ } else {
+ for (i = 0; i < num_comp; ++i) {
+ double rmin, rmax;
+
+ if (pmp->Function || pranges || data_params.Domain == 0)
+ rmin = 0.0, rmax = 1.0;
+ else
+ rmin = data_params.Domain[2 * i],
+ rmax = data_params.Domain[2 * i + 1];
+ if ((code =
+ pdf_array_add_real2(pca, rmin, rmax)) < 0)
+ return code;
+ }
+ }
+ code = cos_dict_put_c_key_object(pscd, "/Decode", COS_OBJECT(pca));
+ if (code < 0)
+ return code;
+ bits_per_coordinate = 24;
+ bits_per_component = 16;
+ bits_per_flag = 8;
+ gs_make_identity((gs_matrix *)&ctm_ident);
+ ctm_ident.tx_fixed = ctm_ident.ty_fixed = 0;
+ ctm_ident.txy_fixed_valid = true;
+ cs.pctm = &ctm_ident;
+ if (pmp->Function)
+ data_params.ranges = 0; /* don't scale function parameter */
+ } else {
+ /****** SCALE Decode ******/
+ code = cos_dict_put_c_key_floats(pscd, "/Decode", pmp->Decode,
+ 4 + num_comp * 2);
+ if (code >= 0)
+ code = cos_stream_add_stream_contents(pscs, cs.s);
+ bits_per_coordinate = pmp->BitsPerCoordinate;
+ bits_per_component = pmp->BitsPerComponent;
+ bits_per_flag = -1;
+ }
+ if (code < 0 ||
+ (code = pdf_put_shading_Function(pscd, pmp->Function, pranges)) < 0 ||
+ (code = cos_dict_put_c_key_int(pscd, "/BitsPerCoordinate",
+ bits_per_coordinate)) < 0 ||
+ (code = cos_dict_put_c_key_int(pscd, "/BitsPerComponent",
+ bits_per_component)) < 0
+ )
+ return code;
+
+ switch (ShadingType(psh)) {
+ case shading_type_Free_form_Gouraud_triangle: {
+ const gs_shading_FfGt_params_t *const params =
+ (const gs_shading_FfGt_params_t *)pmp;
+
+ data_params.num_points = 1;
+ data_params.num_components = num_comp;
+ if (from_array) {
+ while ((flag = shade_next_flag(&cs, 0)) >= 0)
+ if ((code = put_float_mesh_data(pscs, &cs, flag,
+ &data_params)) < 0)
+ return code;
+ if (!seofp(cs.s))
+ code = gs_note_error(gs_error_rangecheck);
+ }
+ if (bits_per_flag < 0)
+ bits_per_flag = params->BitsPerFlag;
+ break;
+ }
+ case shading_type_Lattice_form_Gouraud_triangle: {
+ const gs_shading_LfGt_params_t *const params =
+ (const gs_shading_LfGt_params_t *)pmp;
+
+ data_params.num_points = 1;
+ data_params.num_components = num_comp;
+ if (from_array)
+ while (!seofp(cs.s))
+ if ((code = put_float_mesh_data(pscs, &cs, -1,
+ &data_params)) < 0)
+ return code;
+ code = cos_dict_put_c_key_int(pscd, "/VerticesPerRow",
+ params->VerticesPerRow);
+ return code;
+ }
+ case shading_type_Coons_patch: {
+ const gs_shading_Cp_params_t *const params =
+ (const gs_shading_Cp_params_t *)pmp;
+
+ if (from_array) {
+ while ((flag = shade_next_flag(&cs, 0)) >= 0) {
+ data_params.num_points = (flag == 0 ? 12 : 8);
+ data_params.num_components = num_comp * (flag == 0 ? 4 : 2);
+ if ((code = put_float_mesh_data(pscs, &cs, flag,
+ &data_params)) < 0)
+ return code;
+ }
+ if (!seofp(cs.s))
+ code = gs_note_error(gs_error_rangecheck);
+ }
+ if (bits_per_flag < 0)
+ bits_per_flag = params->BitsPerFlag;
+ break;
+ }
+ case shading_type_Tensor_product_patch: {
+ const gs_shading_Tpp_params_t *const params =
+ (const gs_shading_Tpp_params_t *)pmp;
+
+ if (from_array) {
+ while ((flag = shade_next_flag(&cs, 0)) >= 0) {
+ data_params.num_points = (flag == 0 ? 16 : 12);
+ data_params.num_components = num_comp * (flag == 0 ? 4 : 2);
+ if ((code = put_float_mesh_data(pscs, &cs, flag,
+ &data_params)) < 0)
+ return code;
+ }
+ if (!seofp(cs.s))
+ code = gs_note_error(gs_error_rangecheck);
+ }
+ if (bits_per_flag < 0)
+ bits_per_flag = params->BitsPerFlag;
+ break;
+ }
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ code1 = cos_dict_put_c_key_int(pscd, "/BitsPerFlag", bits_per_flag);
+ if (code1 < 0)
+ return code;
+ return code;
+}
+
+/* Write a PatternType 2 (shading pattern) color. */
+int
+pdf_put_pattern2(gx_device_pdf *pdev, const gx_drawing_color *pdc,
+ const psdf_set_color_commands_t *ppscc,
+ pdf_resource_t **ppres)
+{
+ const gs_pattern2_instance_t *pinst =
+ (gs_pattern2_instance_t *)pdc->ccolor.pattern;
+ const gs_shading_t *psh = pinst->template.Shading;
+ cos_value_t v;
+ pdf_resource_t *pres;
+ pdf_resource_t *psres;
+ cos_dict_t *pcd;
+ cos_object_t *psco;
+ const gs_range_t *pranges;
+ int code = pdf_cs_Pattern_colored(pdev, &v);
+ int code1 = 0;
+ gs_matrix smat;
+
+ if (code < 0)
+ return code;
+ code = pdf_alloc_resource(pdev, resourcePattern, gs_no_id, ppres, 0L);
+ if (code < 0)
+ return code;
+ pres = *ppres;
+ cos_become(pres->object, cos_type_dict);
+ pcd = (cos_dict_t *)pres->object;
+ code = pdf_alloc_resource(pdev, resourceShading, gs_no_id, &psres, 0L);
+ if (code < 0)
+ return code;
+ psco = psres->object;
+ if (ShadingType(psh) >= 4) {
+ /* Shading has an associated data stream. */
+ cos_become(psco, cos_type_stream);
+ code = pdf_put_shading_common(cos_stream_dict((cos_stream_t *)psco),
+ psh, pinst->shfill, &pranges);
+ if (code >= 0)
+ code1 = pdf_put_mesh_shading((cos_stream_t *)psco, psh, pranges);
+ } else {
+ cos_become(psco, cos_type_dict);
+ code = pdf_put_shading_common((cos_dict_t *)psco, psh, pinst->shfill, &pranges);
+ if (code >= 0)
+ code = pdf_put_scalar_shading((cos_dict_t *)psco, psh, pranges);
+ }
+ /*
+ * In PDF, the Matrix is the transformation from the pattern space to
+ * the *default* user coordinate space, not the current space.
+ */
+ gs_currentmatrix(pinst->saved, &smat);
+ {
+ double xscale = 72.0 / pdev->HWResolution[0],
+ yscale = 72.0 / pdev->HWResolution[1];
+
+ smat.xx *= xscale, smat.yx *= xscale, smat.tx *= xscale;
+ smat.xy *= yscale, smat.yy *= yscale, smat.ty *= yscale;
+ }
+ if (code < 0 ||
+ (code = cos_dict_put_c_key_int(pcd, "/PatternType", 2)) < 0 ||
+ (code = cos_dict_put_c_key_object(pcd, "/Shading", psco)) < 0 ||
+ (code = cos_dict_put_matrix(pcd, "/Matrix", &smat)) < 0
+ /****** ExtGState ******/
+ )
+ return code;
+ cos_value_write(&v, pdev);
+ pprints1(pdev->strm, " %s", ppscc->setcolorspace);
+ return code1;
+}
+
+/*
+ Include color space.
+ */
+int
+gdev_pdf_include_color_space(gx_device *dev, gs_color_space *cspace, const byte *res_name, int name_length)
+{
+ gx_device_pdf * pdev = (gx_device_pdf *)dev;
+ cos_value_t cs_value;
+
+ return pdf_color_space_named(pdev, &cs_value, NULL, cspace,
+ &pdf_color_space_names, true, res_name, name_length);
+}