summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'devices/vector/gdevpdfg.c')
-rw-r--r--devices/vector/gdevpdfg.c3266
1 files changed, 3266 insertions, 0 deletions
diff --git a/devices/vector/gdevpdfg.c b/devices/vector/gdevpdfg.c
new file mode 100644
index 00000000..bd06b9f1
--- /dev/null
+++ b/devices/vector/gdevpdfg.c
@@ -0,0 +1,3266 @@
+/* Copyright (C) 2001-2019 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 the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
+ CA 94945, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Graphics state management for pdfwrite driver */
+#include "math_.h"
+#include "string_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gsfunc0.h"
+#include "gsstate.h"
+#include "gxbitmap.h" /* for gxhttile.h in gzht.h */
+#include "gxdht.h"
+#include "gxfarith.h" /* for gs_sin/cos_degrees */
+#include "gxfmap.h"
+#include "gxht.h"
+#include "gxgstate.h"
+#include "gxdcolor.h"
+#include "gxpcolor.h"
+#include "gsptype2.h"
+#include "gzht.h"
+#include "gdevpdfx.h"
+#include "gdevpdfg.h"
+#include "gdevpdfo.h"
+#include "gscspace.h"
+#include "gsicc_manage.h"
+#include "gsicc_cache.h"
+#include "gsccolor.h"
+#include "gxcdevn.h"
+#include "gscie.h"
+
+/* ------ Exported by gdevpdfc.c for gdevpdfg.c ------ */
+int pdf_make_sampled_base_space_function(gx_device_pdf *pdev, gs_function_t **pfn,
+ int nSrcComp, int nDstComp, byte *data);
+int pdf_delete_sampled_base_space_function(gx_device_pdf *pdev, gs_function_t *pfn);
+int pdf_make_base_space_function(gx_device_pdf *pdev, gs_function_t **pfn,
+ int ncomp, float *data_low, float *data_high);
+int pdf_delete_base_space_function(gx_device_pdf *pdev, gs_function_t *pfn);
+
+/* ---------------- Miscellaneous ---------------- */
+
+/* Save the viewer's graphic state. */
+int
+pdf_save_viewer_state(gx_device_pdf *pdev, stream *s)
+{
+ const int i = pdev->vgstack_depth;
+
+ if (pdev->vgstack_depth >= pdev->vgstack_size) {
+ pdf_viewer_state *new_vgstack = (pdf_viewer_state *)gs_alloc_bytes(pdev->pdf_memory,
+ (pdev->vgstack_size + 5) * sizeof(pdf_viewer_state), "increase graphics state stack size");
+ if (new_vgstack == 0)
+ return_error(gs_error_VMerror);
+ memset(new_vgstack, 0x00, (pdev->vgstack_size + 5) * sizeof(pdf_viewer_state));
+ memcpy(new_vgstack, pdev->vgstack, pdev->vgstack_size * sizeof(pdf_viewer_state));
+ gs_free_object(pdev->pdf_memory, pdev->vgstack, "resize graphics state stack, free old stack)");
+ pdev->vgstack = new_vgstack;
+ pdev->vgstack_size += 5;
+ }
+
+ pdev->vgstack[i].transfer_ids[0] = pdev->transfer_ids[0];
+ pdev->vgstack[i].transfer_ids[1] = pdev->transfer_ids[1];
+ pdev->vgstack[i].transfer_ids[2] = pdev->transfer_ids[2];
+ pdev->vgstack[i].transfer_ids[3] = pdev->transfer_ids[3];
+ pdev->vgstack[i].transfer_not_identity = pdev->transfer_not_identity;
+ pdev->vgstack[i].opacity_alpha = pdev->state.opacity.alpha;
+ pdev->vgstack[i].shape_alpha = pdev->state.shape.alpha;
+ pdev->vgstack[i].blend_mode = pdev->state.blend_mode;
+ pdev->vgstack[i].halftone_id = pdev->halftone_id;
+ pdev->vgstack[i].black_generation_id = pdev->black_generation_id;
+ pdev->vgstack[i].undercolor_removal_id = pdev->undercolor_removal_id;
+ pdev->vgstack[i].overprint_mode = pdev->overprint_mode;
+ pdev->vgstack[i].smoothness = pdev->state.smoothness;
+ pdev->vgstack[i].flatness = pdev->state.flatness;
+ pdev->vgstack[i].text_knockout = pdev->state.text_knockout;
+ pdev->vgstack[i].fill_overprint = pdev->fill_overprint;
+ pdev->vgstack[i].stroke_overprint = pdev->stroke_overprint;
+ pdev->vgstack[i].stroke_adjust = pdev->state.stroke_adjust;
+ pdev->vgstack[i].fill_used_process_color = pdev->fill_used_process_color;
+ pdev->vgstack[i].stroke_used_process_color = pdev->stroke_used_process_color;
+ pdev->vgstack[i].saved_fill_color = pdev->saved_fill_color;
+ pdev->vgstack[i].saved_stroke_color = pdev->saved_stroke_color;
+ pdev->vgstack[i].line_params = pdev->state.line_params;
+ pdev->vgstack[i].line_params.dash.pattern = 0; /* Use pdev->dash_pattern instead. */
+ pdev->vgstack[i].soft_mask_id = pdev->state.soft_mask_id; /* Use pdev->dash_pattern instead. */
+ if (pdev->dash_pattern) {
+ if (pdev->vgstack[i].dash_pattern)
+ gs_free_object(pdev->memory->non_gc_memory, pdev->vgstack[i].dash_pattern, "free gstate copy dash");
+ pdev->vgstack[i].dash_pattern = (float *)gs_alloc_bytes(pdev->memory->non_gc_memory, pdev->dash_pattern_size * sizeof(float), "gstate copy dash");
+ if (pdev->vgstack[i].dash_pattern == NULL)
+ return_error(gs_error_VMerror);
+ memcpy(pdev->vgstack[i].dash_pattern, pdev->dash_pattern, pdev->dash_pattern_size * sizeof(float));
+ pdev->vgstack[i].dash_pattern_size = pdev->dash_pattern_size;
+ } else {
+ if (pdev->vgstack[i].dash_pattern) {
+ gs_free_object(pdev->memory->non_gc_memory, pdev->vgstack[i].dash_pattern, "free gstate copy dash");
+ pdev->vgstack[i].dash_pattern = 0;
+ pdev->vgstack[i].dash_pattern_size = 0;
+ }
+ }
+ pdev->vgstack_depth++;
+ if (s)
+ stream_puts(s, "q\n");
+ return 0;
+}
+
+/* Load the viewer's graphic state. */
+static int
+pdf_load_viewer_state(gx_device_pdf *pdev, pdf_viewer_state *s)
+{
+ pdev->transfer_ids[0] = s->transfer_ids[0];
+ pdev->transfer_ids[1] = s->transfer_ids[1];
+ pdev->transfer_ids[2] = s->transfer_ids[2];
+ pdev->transfer_ids[3] = s->transfer_ids[3];
+ pdev->transfer_not_identity = s->transfer_not_identity;
+ pdev->state.opacity.alpha = s->opacity_alpha;
+ pdev->state.shape.alpha = s->shape_alpha;
+ pdev->state.blend_mode = s->blend_mode;
+ pdev->halftone_id = s->halftone_id;
+ pdev->black_generation_id = s->black_generation_id;
+ pdev->undercolor_removal_id = s->undercolor_removal_id;
+ pdev->overprint_mode = s->overprint_mode;
+ pdev->state.smoothness = s->smoothness;
+ pdev->state.flatness = s->flatness;
+ pdev->state.text_knockout = s->text_knockout;
+ pdev->fill_overprint = s->fill_overprint;
+ pdev->stroke_overprint = s->stroke_overprint;
+ pdev->state.stroke_adjust = s->stroke_adjust;
+ pdev->fill_used_process_color = s->fill_used_process_color;
+ pdev->stroke_used_process_color = s->stroke_used_process_color;
+ pdev->saved_fill_color = s->saved_fill_color;
+ pdev->saved_stroke_color = s->saved_stroke_color;
+ pdev->state.line_params = s->line_params;
+ pdev->state.soft_mask_id = s->soft_mask_id;
+ if (s->dash_pattern) {
+ if (pdev->dash_pattern)
+ gs_free_object(pdev->memory->stable_memory, pdev->dash_pattern, "vector free dash pattern");
+ pdev->dash_pattern = (float *)gs_alloc_bytes(pdev->memory->stable_memory, s->dash_pattern_size * sizeof(float), "vector allocate dash pattern");
+ if (pdev->dash_pattern == NULL)
+ return_error(gs_error_VMerror);
+ memcpy(pdev->dash_pattern, s->dash_pattern, sizeof(float)*s->dash_pattern_size);
+ pdev->dash_pattern_size = s->dash_pattern_size;
+ } else {
+ if (pdev->dash_pattern) {
+ gs_free_object(pdev->memory->stable_memory, pdev->dash_pattern, "vector free dash pattern");
+ pdev->dash_pattern = 0;
+ pdev->dash_pattern_size = 0;
+ }
+ }
+ return 0;
+}
+
+/* Restore the viewer's graphic state. */
+int
+pdf_restore_viewer_state(gx_device_pdf *pdev, stream *s)
+{
+ const int i = --pdev->vgstack_depth;
+
+ if (i < pdev->vgstack_bottom || i < 0) {
+ if ((pdev->ObjectFilter & FILTERIMAGE) == 0)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ else
+ return 0;
+ }
+ if (s)
+ stream_puts(s, "Q\n");
+ return pdf_load_viewer_state(pdev, pdev->vgstack + i);
+}
+
+/* Set initial color. */
+void
+pdf_set_initial_color(gx_device_pdf * pdev, gx_hl_saved_color *saved_fill_color,
+ gx_hl_saved_color *saved_stroke_color,
+ bool *fill_used_process_color, bool *stroke_used_process_color)
+{
+ gx_device_color black;
+
+ pdev->black = gx_device_black((gx_device *)pdev);
+ pdev->white = gx_device_white((gx_device *)pdev);
+ set_nonclient_dev_color(&black, pdev->black);
+ gx_hld_save_color(NULL, &black, saved_fill_color);
+ gx_hld_save_color(NULL, &black, saved_stroke_color);
+ *fill_used_process_color = true;
+ *stroke_used_process_color = true;
+}
+
+/* Prepare intitial values for viewer's graphics state parameters. */
+static void
+pdf_viewer_state_from_gs_gstate_aux(pdf_viewer_state *pvs, const gs_gstate *pgs)
+{
+ pvs->transfer_not_identity =
+ (pgs->set_transfer.red != NULL ? pgs->set_transfer.red->proc != gs_identity_transfer : 0) * 1 +
+ (pgs->set_transfer.green != NULL ? pgs->set_transfer.green->proc != gs_identity_transfer : 0) * 2 +
+ (pgs->set_transfer.blue != NULL ? pgs->set_transfer.blue->proc != gs_identity_transfer : 0) * 4 +
+ (pgs->set_transfer.gray != NULL ? pgs->set_transfer.gray->proc != gs_identity_transfer : 0) * 8;
+ pvs->transfer_ids[0] = (pgs->set_transfer.red != NULL ? pgs->set_transfer.red->id : 0);
+ pvs->transfer_ids[1] = (pgs->set_transfer.green != NULL ? pgs->set_transfer.green->id : 0);
+ pvs->transfer_ids[2] = (pgs->set_transfer.blue != NULL ? pgs->set_transfer.blue->id : 0);
+ pvs->transfer_ids[3] = (pgs->set_transfer.gray != NULL ? pgs->set_transfer.gray->id : 0);
+ pvs->opacity_alpha = pgs->opacity.alpha;
+ pvs->shape_alpha = pgs->shape.alpha;
+ pvs->blend_mode = pgs->blend_mode;
+ pvs->halftone_id = (pgs->dev_ht != 0 ? pgs->dev_ht->id : 0);
+ pvs->black_generation_id = (pgs->black_generation != 0 ? pgs->black_generation->id : 0);
+ pvs->undercolor_removal_id = (pgs->undercolor_removal != 0 ? pgs->undercolor_removal->id : 0);
+ pvs->overprint_mode = 0;
+ pvs->flatness = pgs->flatness;
+ pvs->smoothness = pgs->smoothness;
+ pvs->text_knockout = pgs->text_knockout;
+ pvs->fill_overprint = false;
+ pvs->stroke_overprint = false;
+ pvs->stroke_adjust = false;
+ pvs->line_params.half_width = 0.5;
+ pvs->line_params.start_cap = 0;
+ pvs->line_params.end_cap = 0;
+ pvs->line_params.dash_cap = 0;
+ pvs->line_params.join = 0;
+ pvs->line_params.curve_join = 0;
+ pvs->line_params.miter_limit = 10.0;
+ pvs->line_params.miter_check = 0;
+ pvs->line_params.dot_length = pgs->line_params.dot_length;
+ pvs->line_params.dot_length_absolute = pgs->line_params.dot_length_absolute;
+ pvs->line_params.dot_orientation = pgs->line_params.dot_orientation;
+ memset(&pvs->line_params.dash, 0 , sizeof(pvs->line_params.dash));
+ pvs->dash_pattern = 0;
+ pvs->dash_pattern_size = 0;
+ pvs->soft_mask_id = pgs->soft_mask_id;
+}
+
+/* Copy viewer state from images state. */
+void
+pdf_viewer_state_from_gs_gstate(gx_device_pdf * pdev,
+ const gs_gstate *pgs, const gx_device_color *pdevc)
+{
+ pdf_viewer_state vs;
+
+ pdf_viewer_state_from_gs_gstate_aux(&vs, pgs);
+ /* pdf_viewer_state_from_gs_gstate_aux always returns
+ * vs with a NULL dash pattern. */
+ gx_hld_save_color(pgs, pdevc, &vs.saved_fill_color);
+ gx_hld_save_color(pgs, pdevc, &vs.saved_stroke_color);
+ vs.fill_used_process_color = 0;
+ vs.stroke_used_process_color = 0;
+ /* pdf_load_viewer_state should never fail, as vs has a NULL
+ * dash pattern, and therefore will not allocate. */
+ (void)pdf_load_viewer_state(pdev, &vs);
+}
+
+/* Prepare intitial values for viewer's graphics state parameters. */
+void
+pdf_prepare_initial_viewer_state(gx_device_pdf * pdev, const gs_gstate *pgs)
+{
+ /* Parameter values, which are specified in PDF spec, are set here.
+ * Parameter values, which are specified in PDF spec as "installation dependent",
+ * are set here to intial values used with PS interpreter.
+ * This allows to write differences to the output file
+ * and skip initial values.
+ */
+
+ pdf_set_initial_color(pdev, &pdev->vg_initial.saved_fill_color, &pdev->vg_initial.saved_stroke_color,
+ &pdev->vg_initial.fill_used_process_color, &pdev->vg_initial.stroke_used_process_color);
+ pdf_viewer_state_from_gs_gstate_aux(&pdev->vg_initial, pgs);
+ pdev->vg_initial_set = true;
+ /*
+ * Some parameters listed in PDF spec are missed here :
+ * text state - it is initialized per page.
+ * rendering intent - not sure why, fixme.
+ */
+}
+
+/* Reset the graphics state parameters to initial values. */
+/* Used if pdf_prepare_initial_viewer_state was not callad. */
+static void
+pdf_reset_graphics_old(gx_device_pdf * pdev)
+{
+
+ pdf_set_initial_color(pdev, &pdev->saved_fill_color, &pdev->saved_stroke_color,
+ &pdev->fill_used_process_color, &pdev->stroke_used_process_color);
+ pdev->state.flatness = -1;
+ {
+ static const gx_line_params lp_initial = {
+ gx_line_params_initial
+ };
+
+ pdev->state.line_params = lp_initial;
+ }
+ pdev->fill_overprint = false;
+ pdev->stroke_overprint = false;
+ pdev->remap_fill_color = false;
+ pdev->remap_stroke_color = false;
+ pdf_reset_text(pdev);
+}
+
+/* Reset the graphics state parameters to initial values. */
+void
+pdf_reset_graphics(gx_device_pdf * pdev)
+{
+ int soft_mask_id = pdev->state.soft_mask_id;
+
+ if (pdev->vg_initial_set)
+ /* The following call cannot fail as vg_initial has no
+ * dash pattern, and so no allocations are required. */
+ (void)pdf_load_viewer_state(pdev, &pdev->vg_initial);
+ else
+ pdf_reset_graphics_old(pdev);
+ pdf_reset_text(pdev);
+
+ /* Not obvious, we want to preserve any extant soft mask, not reset it */
+ pdev->state.soft_mask_id = soft_mask_id;
+}
+
+/* Write client color. */
+static int
+pdf_write_ccolor(gx_device_pdf * pdev, const gs_gstate * pgs,
+ const gs_client_color *pcc)
+{
+ int i, n = gx_hld_get_number_color_components(pgs);
+
+ pprintg1(pdev->strm, "%g", psdf_round(pcc->paint.values[0], 255, 8));
+ for (i = 1; i < n; i++) {
+ pprintg1(pdev->strm, " %g", psdf_round(pcc->paint.values[i], 255, 8));
+ }
+ return 0;
+}
+
+static inline bool
+is_cspace_allowed_in_strategy(gx_device_pdf * pdev, gs_color_space_index csi)
+{
+ if (pdev->params.ColorConversionStrategy == ccs_CMYK &&
+ csi != gs_color_space_index_DeviceCMYK &&
+ csi != gs_color_space_index_DeviceGray)
+ return false;
+ if (pdev->params.ColorConversionStrategy == ccs_sRGB &&
+ csi != gs_color_space_index_DeviceRGB &&
+ csi != gs_color_space_index_DeviceGray)
+ return false;
+ if (pdev->params.ColorConversionStrategy == ccs_RGB &&
+ csi != gs_color_space_index_DeviceRGB &&
+ csi != gs_color_space_index_DeviceGray)
+ return false;
+ if (pdev->params.ColorConversionStrategy == ccs_Gray &&
+ csi != gs_color_space_index_DeviceGray)
+ return false;
+ return true;
+}
+
+static inline bool
+is_pattern2_allowed_in_strategy(gx_device_pdf * pdev, const gx_drawing_color *pdc)
+{
+ const gs_color_space *pcs2 = gx_dc_pattern2_get_color_space(pdc);
+ gs_color_space_index csi = gs_color_space_get_index(pcs2);
+
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+
+ return is_cspace_allowed_in_strategy(pdev, csi);
+}
+
+
+static int apply_transfer_gray(gx_device_pdf * pdev, const gs_gstate * pgs, gs_client_color *pcc, gs_client_color *cc)
+{
+ unsigned short psrc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ frac conc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ gx_device_color dc;
+ const gs_color_space *pcs = gs_currentcolorspace_inline(pgs);
+ int code, color_index;
+
+ color_index = pdev->pcm_color_info_index;
+ pdf_set_process_color_model(pdev, 0);
+ psrc[0] = (unsigned short) (pcc->paint.values[0]*65535.0);;
+ conc[0] = ushort2frac(psrc[0]);
+
+ code = gx_remap_concrete_DGray(pcs, (const frac *)&conc, &dc, pgs, (gx_device *)pdev, gs_color_select_texture, NULL);
+ if (code < 0)
+ return code;
+
+ cc->paint.values[0] = (dc.colors.pure & 0xff) / 255.0;
+ pdf_set_process_color_model(pdev, color_index);
+ return 0;
+}
+
+static int apply_transfer_rgb(gx_device_pdf * pdev, const gs_gstate * pgs, gs_client_color *pcc, gs_client_color *cc)
+{
+ unsigned short psrc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ frac conc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ gx_device_color dc;
+ const gs_color_space *pcs = gs_currentcolorspace_inline(pgs);
+ int code, i, color_index;
+
+ color_index = pdev->pcm_color_info_index;
+ pdf_set_process_color_model(pdev, 1);
+ for (i=0;i<3;i++) {
+ psrc[i] = (unsigned short) (pcc->paint.values[i]*65535.0);;
+ conc[i] = ushort2frac(psrc[i]);
+ }
+ code = gx_remap_concrete_DRGB(pcs, (const frac *)&conc, &dc, pgs, (gx_device *)pdev, gs_color_select_texture, NULL);
+ if (code < 0)
+ return code;
+
+ cc->paint.values[0] = ((dc.colors.pure & 0xff0000) >> 16) / 255.0;
+ cc->paint.values[1] = ((dc.colors.pure & 0xff00) >> 8) / 255.0;
+ cc->paint.values[2] = (dc.colors.pure & 0xff) / 255.0;
+ pdf_set_process_color_model(pdev, color_index);
+ return 0;
+}
+
+static int apply_transfer_cmyk(gx_device_pdf * pdev, const gs_gstate * pgs, gs_client_color *pcc, gs_client_color *cc)
+{
+ unsigned short psrc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ frac conc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ gx_device_color dc;
+ const gs_color_space *pcs = gs_currentcolorspace_inline(pgs);
+ int code, i, color_index;
+
+ color_index = pdev->pcm_color_info_index;
+ pdf_set_process_color_model(pdev, 2);
+ for (i=0;i<4;i++) {
+ psrc[i] = (unsigned short) (pcc->paint.values[i]*65535.0);;
+ conc[i] = ushort2frac(psrc[i]);
+ }
+ code = gx_remap_concrete_DCMYK(pcs, (const frac *)&conc, &dc, pgs, (gx_device *)pdev, gs_color_select_texture, NULL);
+ if (code < 0)
+ return code;
+
+ cc->paint.values[0] = ((dc.colors.pure & 0xff000000) >> 24) / 255.0;
+ cc->paint.values[1] = ((dc.colors.pure & 0xff0000) >> 16) / 255.0;
+ cc->paint.values[2] = ((dc.colors.pure & 0xff00) >> 8) / 255.0;
+ cc->paint.values[3] = (dc.colors.pure & 0xff) / 255.0;
+ pdf_set_process_color_model(pdev, color_index);
+ return 0;
+}
+
+static int write_color_as_process(gx_device_pdf * pdev, const gs_gstate * pgs, const gs_color_space *pcs,
+ const gx_drawing_color *pdc, bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc, gs_client_color *pcc)
+{
+ int code, i;
+ unsigned char j;
+ frac conc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ gs_color_space_index csi, csi2;
+ gs_color_space *pcs2 = (gs_color_space *)pcs;
+ gx_drawing_color dc;
+ int num_des_comps;
+ cmm_dev_profile_t *dev_profile;
+
+ dc.type = gx_dc_type_pure;
+ dc.colors.pure = 0;
+ csi = gs_color_space_get_index(pcs);
+
+ if (csi == gs_color_space_index_ICC) {
+ csi = gsicc_get_default_type(pcs->cmm_icc_profile_data);
+ }
+
+ if (csi == gs_color_space_index_Indexed ||
+ csi == gs_color_space_index_DeviceN ||
+ csi == gs_color_space_index_Separation) {
+ const char *command = NULL;
+
+ *used_process_color = true;
+
+ memset (&conc, 0x00, sizeof(frac) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+ pcs->type->concretize_color(pcc, pcs, conc, pgs, (gx_device *)pdev);
+
+ do{
+ pcs2 = pcs2->base_space;
+ csi2 = gs_color_space_get_index(pcs2);
+ } while(csi2 != gs_color_space_index_ICC && pcs2->base_space);
+ csi2 = gs_color_space_get_index(pcs2);
+
+ switch (csi2) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceRGB:
+ case gs_color_space_index_DeviceCMYK:
+ switch (pdev->color_info.num_components) {
+ case 1:
+ command = ppscc->setgray;
+ break;
+ case 3:
+ command = ppscc->setrgbcolor;
+ break;
+ case 4:
+ command = ppscc->setcmykcolor;
+ break;
+ default:
+ /* Can't happen since we already check the colour space */
+ return_error(gs_error_rangecheck);
+ }
+ pprintg1(pdev->strm, "%g", psdf_round(frac2float(conc[0]), 255, 8));
+ for (j = 1; j < pdev->color_info.num_components; j++) {
+ pprintg1(pdev->strm, " %g", psdf_round(frac2float(conc[j]), 255, 8));
+ }
+ pprints1(pdev->strm, " %s\n", command);
+ return 0;
+ break;
+ case gs_color_space_index_CIEDEFG:
+ case gs_color_space_index_CIEDEF:
+ case gs_color_space_index_CIEABC:
+ case gs_color_space_index_CIEA:
+ case gs_color_space_index_ICC:
+ code = dev_proc((gx_device *)pdev, get_profile)((gx_device *)pdev, &dev_profile);
+ if (code < 0)
+ return code;
+ num_des_comps = gsicc_get_device_profile_comps(dev_profile);
+ for (i = 0;i < num_des_comps;i++)
+ dc.colors.pure = (dc.colors.pure << 8) + (int)(frac2float(conc[i]) * 255);
+ code = psdf_set_color((gx_device_vector *)pdev, &dc, ppscc);
+ return code;
+ break;
+ default: /* can't happen, simply silences compiler warnings */
+ break;
+ }
+ } else {
+ if (csi >= gs_color_space_index_CIEDEFG &&
+ csi <= gs_color_space_index_CIEA) {
+ memset (&conc, 0x00, sizeof(frac) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+ pcs->type->concretize_color(pcc, pcs, conc, pgs, (gx_device *)pdev);
+ code = dev_proc((gx_device *)pdev, get_profile)((gx_device *)pdev, &dev_profile);
+ if (code < 0)
+ return code;
+ num_des_comps = gsicc_get_device_profile_comps(dev_profile);
+ for (i = 0;i < num_des_comps;i++)
+ dc.colors.pure = (dc.colors.pure << 8) + (int)(frac2float(conc[i]) * 255);
+ code = psdf_set_color((gx_device_vector *)pdev, &dc, ppscc);
+ *used_process_color = true;
+ return code;
+ } else {
+ memset (&conc, 0x00, sizeof(frac) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+ /* Special case handling for Lab spaces */
+ if (pcs->cmm_icc_profile_data->data_cs == gsCIELAB || pcs->cmm_icc_profile_data->islab) {
+ gs_client_color cc;
+ /* Get the data in a form that is concrete for the CMM */
+ cc.paint.values[0] = pcc->paint.values[0] / 100.0;
+ cc.paint.values[1] = (pcc->paint.values[1]+128)/255.0;
+ cc.paint.values[2] = (pcc->paint.values[2]+128)/255.0;
+ pcs->type->concretize_color((const gs_client_color *)&cc, pcs, conc, pgs, (gx_device *)pdev);
+ } else {
+ if (pdev->params.TransferFunctionInfo == tfi_Apply) {
+ /* Apply transfer functions */
+ switch(csi) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceRGB:
+ case gs_color_space_index_DeviceCMYK:
+ (*pcs->type->remap_color)((const gs_client_color *)pcc, pcs, (gx_drawing_color *)pdc, pgs, (gx_device *)pdev, 0);
+ code = psdf_set_color((gx_device_vector *)pdev, pdc, ppscc);
+ return code;
+ break;
+ default:
+ pcs->type->concretize_color(pcc, pcs, conc, pgs, (gx_device *)pdev);
+ break;
+ }
+ } else {
+ pcs->type->concretize_color(pcc, pcs, conc, pgs, (gx_device *)pdev);
+ }
+ }
+ code = dev_proc((gx_device *)pdev, get_profile)((gx_device *)pdev, &dev_profile);
+ if (code < 0)
+ return code;
+ num_des_comps = gsicc_get_device_profile_comps(dev_profile);
+ for (i = 0;i < num_des_comps;i++)
+ dc.colors.pure = (dc.colors.pure << 8) + (int)(frac2float(conc[i]) * 255);
+ code = psdf_set_color((gx_device_vector *)pdev, &dc, ppscc);
+ return code;
+ }
+ }
+ return_error(gs_error_unknownerror);
+}
+
+static int write_color_unchanged(gx_device_pdf * pdev, const gs_gstate * pgs,
+ gs_client_color *pcc, gx_hl_saved_color *current,
+ gx_hl_saved_color * psc, const psdf_set_color_commands_t *ppscc,
+ bool *used_process_color, const gs_color_space *pcs,
+ const gx_drawing_color *pdc)
+{
+ gs_color_space_index csi, csi2;
+ int code, i;
+ const char *command = NULL;
+ gs_range_t *ranges = 0;
+ gs_client_color cc;
+
+
+ csi = csi2 = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_ICC) {
+ csi2 = gsicc_get_default_type(pcs->cmm_icc_profile_data);
+ }
+
+ switch (csi2) {
+ case gs_color_space_index_DeviceGray:
+ command = ppscc->setgray;
+ if (pdev->params.TransferFunctionInfo == tfi_Apply) {
+ code = apply_transfer_gray(pdev, pgs, pcc, &cc);
+ if (code < 0)
+ return_error(code);
+ } else {
+ cc.paint.values[0] = pcc->paint.values[0];
+ }
+ code = pdf_write_ccolor(pdev, pgs, &cc);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", command);
+ break;
+ case gs_color_space_index_DeviceRGB:
+ command = ppscc->setrgbcolor;
+ if (pdev->params.TransferFunctionInfo == tfi_Apply) {
+ code = apply_transfer_rgb(pdev, pgs, pcc, &cc);
+ if (code < 0)
+ return_error(code);
+ } else {
+ for (i=0;i< 3;i++)
+ cc.paint.values[i] = pcc->paint.values[i];
+ }
+ code = pdf_write_ccolor(pdev, pgs, &cc);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", command);
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ command = ppscc->setcmykcolor;
+ if (pdev->params.TransferFunctionInfo == tfi_Apply) {
+ code = apply_transfer_cmyk(pdev, pgs, pcc, &cc);
+ if (code < 0)
+ return_error(code);
+ } else {
+ for (i=0;i< 4;i++)
+ cc.paint.values[i] = pcc->paint.values[i];
+ }
+ code = pdf_write_ccolor(pdev, pgs, &cc);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", command);
+ break;
+ default:
+ if (!gx_hld_saved_color_same_cspace(current, psc) || (csi2 >= gs_color_space_index_CIEDEFG && csi2 <= gs_color_space_index_CIEA)) {
+ cos_value_t cs_value;
+
+ code = pdf_color_space_named(pdev, pgs, &cs_value, (const gs_range_t **)&ranges, pcs,
+ &pdf_color_space_names, true, NULL, 0, false);
+ /* fixme : creates redundant PDF objects. */
+ if (code == gs_error_rangecheck) {
+ *used_process_color = true;
+ if (pdev->ForOPDFRead) {
+ /* The color space can't write to PDF. This should never happen */
+ code = psdf_set_color((gx_device_vector *)pdev, pdc, ppscc);
+ } else {
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ }
+ return code;
+ }
+ if (code < 0)
+ return code;
+ code = cos_value_write(&cs_value, pdev);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorspace);
+ if (ranges && (csi2 >= gs_color_space_index_CIEDEFG && csi2 <= gs_color_space_index_CIEA)) {
+ gs_client_color dcc = *pcc;
+ switch (csi2) {
+ case gs_color_space_index_CIEDEFG:
+ rescale_cie_color(ranges, 4, pcc, &dcc);
+ break;
+ case gs_color_space_index_CIEDEF:
+ rescale_cie_color(ranges, 3, pcc, &dcc);
+ break;
+ case gs_color_space_index_CIEABC:
+ rescale_cie_color(ranges, 3, pcc, &dcc);
+ break;
+ case gs_color_space_index_CIEA:
+ rescale_cie_color(ranges, 1, pcc, &dcc);
+ break;
+ default:
+ /* can't happen but silences compiler warnings */
+ break;
+ }
+ code = pdf_write_ccolor(pdev, pgs, &dcc);
+ } else {
+ code = pdf_write_ccolor(pdev, pgs, pcc);
+ }
+ *used_process_color = false;
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ } else if (*used_process_color) {
+ *used_process_color = true;
+ if (pdev->ForOPDFRead) {
+ /* The color space can't write to PDF. This should never happen */
+ code = psdf_set_color((gx_device_vector *)pdev, pdc, ppscc);
+ } else {
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ }
+ return code;
+ }
+ else {
+ code = pdf_write_ccolor(pdev, pgs, pcc);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ }
+ break;
+ }
+ *used_process_color = false;
+
+ return 0;
+}
+
+static int write_color_as_process_ICC(gx_device_pdf * pdev, const gs_gstate * pgs, const gs_color_space *pcs,
+ const gx_drawing_color *pdc, gx_hl_saved_color * psc, bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc, gs_client_color *pcc,
+ gx_hl_saved_color *current)
+{
+ int i, code;
+ cos_value_t cs_value;
+
+ if (!gx_hld_saved_color_same_cspace(current, psc)) {
+ code = pdf_color_space_named(pdev, pgs, &cs_value, NULL, pcs,
+ &pdf_color_space_names, true, NULL, 0, true);
+ /* fixme : creates redundant PDF objects. */
+ if (code == gs_error_rangecheck) {
+ /* The color space can't write to PDF. This should never happen */
+ return write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ }
+ if (code < 0)
+ return code;
+ code = cos_value_write(&cs_value, pdev);
+ if (code < 0)
+ return code;
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorspace);
+ *used_process_color = false;
+ pprintg1(pdev->strm, "%g", psdf_round(pcc->paint.values[0], 255, 8));
+ for (i = 1; i < pcs->type->num_components(pcs); i++) {
+ pprintg1(pdev->strm, " %g", psdf_round(pcc->paint.values[i], 255, 8));
+ }
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ } else {
+ *used_process_color = false;
+ pprintg1(pdev->strm, "%g", psdf_round(pcc->paint.values[0], 255, 8));
+ for (i = 1; i < pcs->type->num_components(pcs); i++) {
+ pprintg1(pdev->strm, " %g", psdf_round(pcc->paint.values[i], 255, 8));
+ }
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ }
+ return 0;
+}
+
+int convert_DeviceN_alternate(gx_device_pdf * pdev, const gs_gstate * pgs, const gs_color_space *pcs,
+ const gx_drawing_color *pdc, bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc, gs_client_color *pcc, cos_value_t *pvalue, bool by_name)
+{
+ gs_color_space_index csi;
+ gs_function_t *new_pfn = 0;
+ int code, i, samples=0, loop;
+ cos_array_t *pca, *pca1;
+ cos_value_t v;
+ byte *data_buff;
+ pdf_resource_t *pres = NULL;
+ gs_color_space *pcs_save = NULL;
+
+ csi = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_Indexed) {
+ pcs_save = (gs_color_space *)pcs;
+ pcs = pcs->base_space;
+ }
+
+ pca = cos_array_alloc(pdev, "pdf_color_space");
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+
+ samples = (unsigned int)pow(2, pcs->params.device_n.num_components);
+ data_buff = gs_alloc_bytes(pdev->memory, pdev->color_info.num_components * samples, "Convert DeviceN");
+ if (data_buff == 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return_error(gs_error_VMerror);
+ }
+ memset(data_buff, 0x00, (unsigned long)pdev->color_info.num_components * samples);
+
+ {
+ frac conc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ gs_client_color cc, cc1;
+ unsigned char j;
+ gs_color_space *icc_space = (gs_color_space *)pcs;
+ gs_color_space *sep_space = (gs_color_space *)pcs;
+ gs_color_space_index csi2;
+ bool save_use_alt = 0;
+ separation_type save_type = SEP_OTHER;
+
+ csi = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_Indexed)
+ sep_space = pcs->base_space;
+
+ do{
+ icc_space = icc_space->base_space;
+ csi2 = gs_color_space_get_index(icc_space);
+ } while(csi2 != gs_color_space_index_ICC && icc_space->base_space);
+
+ memset(&cc.paint.values, 0x00, GS_CLIENT_COLOR_MAX_COMPONENTS);
+
+ /* Force the colour management code to use the tint transform and
+ * give us the values in the Alternate space. Otherwise, for
+ * SEP_NONE or SEP_ALL it gives us the wrong answer. For SEP_NONE
+ * it always returns 0 and for SEP_ALL it sets the first component
+ * (only!) of the space to the tint value.
+ */
+ if (sep_space->params.separation.sep_type == SEP_ALL || sep_space->params.separation.sep_type == SEP_NONE) {
+ save_use_alt = sep_space->params.separation.use_alt_cspace;
+ sep_space->params.separation.use_alt_cspace = true;
+ save_type = sep_space->params.separation.sep_type;
+ sep_space->params.separation.sep_type = SEP_OTHER;
+ }
+
+ for (loop=0;loop < samples;loop++) {
+ if (loop > 0) {
+ if (cc.paint.values[0] == 0)
+ cc.paint.values[0] = 1;
+ else {
+ int cascade = 0;
+ while (cc.paint.values[cascade] == 1 && cascade < samples) {
+ cc.paint.values[cascade++] = 0;
+ }
+ cc.paint.values[cascade] = 1;
+ }
+ }
+
+
+ memset (&conc, 0x00, sizeof(frac) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+ sep_space->type->concretize_color(&cc, sep_space, conc, pgs, (gx_device *)pdev);
+
+ for (i = 0;i < pdev->color_info.num_components;i++)
+ cc1.paint.values[i] = frac2float(conc[i]);
+
+ if (pdev->params.TransferFunctionInfo == tfi_Apply) {
+ switch (pdev->params.ColorConversionStrategy) {
+ case ccs_Gray:
+ code = apply_transfer_gray(pdev, pgs, &cc1, &cc1);
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ code = apply_transfer_rgb(pdev, pgs, &cc1, &cc1);
+ break;
+ case ccs_CMYK:
+ code = apply_transfer_cmyk(pdev, pgs, &cc1, &cc1);
+ break;
+ default:
+ code = gs_error_rangecheck;
+ break;
+ }
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ }
+ for (j = 0;j < pdev->color_info.num_components;j++)
+ data_buff[(loop * pdev->color_info.num_components) + j] = (int)(cc1.paint.values[j] * 255);
+ }
+ /* Put back the values we hacked in order to force the colour management code
+ * to do what we want.
+ */
+ if (save_type == SEP_ALL || save_type == SEP_NONE) {
+ sep_space->params.separation.use_alt_cspace = save_use_alt;
+ sep_space->params.separation.sep_type = save_type;
+ }
+ }
+
+ switch(pdev->params.ColorConversionStrategy) {
+ case ccs_Gray:
+ code = pdf_make_sampled_base_space_function(pdev, &new_pfn, pcs->params.device_n.num_components, 1, data_buff);
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ code = pdf_make_sampled_base_space_function(pdev, &new_pfn, pcs->params.device_n.num_components, 3, data_buff);
+ break;
+ case ccs_CMYK:
+ code = pdf_make_sampled_base_space_function(pdev, &new_pfn, pcs->params.device_n.num_components, 4, data_buff);
+ break;
+ default:
+ code = gs_error_rangecheck;
+ break;
+ }
+ gs_free_object(pdev->memory, data_buff, "Convert DeviceN");
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+
+ code = cos_array_add(pca, cos_c_string_value(&v, "/DeviceN"));
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+
+ if (code >= 0) {
+ byte *name_string;
+ uint name_string_length;
+ cos_value_t v_attriburtes, *va = NULL;
+ cos_array_t *psna =
+ cos_array_alloc(pdev, "pdf_color_space(DeviceN)");
+
+ if (psna == 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return_error(gs_error_VMerror);
+ }
+
+ for (i = 0; i < pcs->params.device_n.num_components; ++i) {
+ name_string = (byte *)pcs->params.device_n.names[i];
+ name_string_length = strlen(pcs->params.device_n.names[i]);
+ code = pdf_string_to_cos_name(pdev, name_string,
+ name_string_length, &v);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ code = cos_array_add(psna, &v);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ }
+ COS_OBJECT_VALUE(&v, psna);
+ code = cos_array_add(pca, &v);
+ if (code <0) {
+ COS_FREE(pca, "convert DeviceN");
+ return_error(gs_error_VMerror);
+ }
+
+ switch(pdev->params.ColorConversionStrategy) {
+ case ccs_Gray:
+ cos_c_string_value(&v, (const char *)pdf_color_space_names.DeviceGray);
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ cos_c_string_value(&v, (const char *)pdf_color_space_names.DeviceRGB);
+ break;
+ case ccs_CMYK:
+ cos_c_string_value(&v, (const char *)pdf_color_space_names.DeviceCMYK);
+ break;
+ default:
+ break;
+ }
+ code = cos_array_add(pca, &v);
+ if (code >= 0) {
+ code = pdf_function_scaled(pdev, new_pfn, 0x00, &v);
+ if (code >= 0)
+ code = cos_array_add(pca, &v);
+ else {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ }
+
+ if (pcs->params.device_n.colorants != NULL) {
+ cos_dict_t *colorants = cos_dict_alloc(pdev, "pdf_color_space(DeviceN)");
+ cos_value_t v_colorants, v_separation, v_colorant_name;
+ const gs_device_n_colorant *csa;
+ pdf_resource_t *pres_attributes;
+
+ if (colorants == NULL)
+ return_error(gs_error_VMerror);
+ code = pdf_alloc_resource(pdev, resourceOther, 0, &pres_attributes, -1);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ cos_become(pres_attributes->object, cos_type_dict);
+ COS_OBJECT_VALUE(&v_colorants, colorants);
+ code = cos_dict_put((cos_dict_t *)pres_attributes->object,
+ (const byte *)"/Colorants", 10, &v_colorants);
+ if (code < 0){
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ for (csa = pcs->params.device_n.colorants; csa != NULL; csa = csa->next) {
+ name_string = (byte *)csa->colorant_name;
+ name_string_length = strlen((const char *)name_string);
+ code = pdf_color_space_named(pdev, pgs, &v_separation, NULL, csa->cspace, &pdf_color_space_names, false, NULL, 0, false);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ code = pdf_string_to_cos_name(pdev, name_string, name_string_length, &v_colorant_name);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ code = cos_dict_put(colorants, v_colorant_name.contents.chars.data,
+ v_colorant_name.contents.chars.size, &v_separation);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ }
+ code = pdf_string_to_cos_name(pdev, (byte *)"DeviceN",
+ 7, &v);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+
+ code = cos_dict_put((cos_dict_t *)pres_attributes->object,
+ (const byte *)"/Subtype", 8, &v);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+
+ code = pdf_substitute_resource(pdev, &pres_attributes, resourceOther, NULL, true);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ pres_attributes->where_used |= pdev->used_mask;
+ va = &v_attriburtes;
+ COS_OBJECT_VALUE(va, pres_attributes->object);
+ code = cos_array_add(pca, va);
+ if (code < 0) {
+ COS_FREE(pca, "convert DeviceN");
+ return code;
+ }
+ }
+
+ }
+ pdf_delete_sampled_base_space_function(pdev, new_pfn);
+
+ /*
+ * Register the color space as a resource, since it must be referenced
+ * by name rather than directly.
+ */
+ {
+ pdf_color_space_t *ppcs;
+
+ if (code < 0 ||
+ (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id,
+ &pres, -1)) < 0
+ ) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ pdf_reserve_object_id(pdev, pres, 0);
+ ppcs = (pdf_color_space_t *)pres;
+ ppcs->serialized = NULL;
+ ppcs->serialized_size = 0;
+
+ ppcs->ranges = 0;
+ pca->id = pres->object->id;
+ COS_FREE(pres->object, "pdf_color_space");
+ pres->object = (cos_object_t *)pca;
+ cos_write_object(COS_OBJECT(pca), pdev, resourceColorSpace);
+ if (pcs_save == NULL && ppscc != NULL)
+ pprints1(pdev->strm, "/%s", ppcs->rname);
+ }
+ pres->where_used |= pdev->used_mask;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", pres);
+ if (code < 0)
+ return code;
+
+ if (pcs_save != NULL) {
+ cos_value_t value;
+
+ pcs = pcs_save;
+ discard(COS_OBJECT_VALUE(&value, pca));
+ pca1 = cos_array_alloc(pdev, "pdf_color_space");
+ code = pdf_indexed_color_space(pdev, pgs, &value, pcs, pca1, (cos_value_t *)&value);
+ pca = pca1;
+
+ /*
+ * Register the color space as a resource, since it must be referenced
+ * by name rather than directly.
+ */
+ {
+ pdf_color_space_t *ppcs;
+
+ if (code < 0 ||
+ (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id,
+ &pres, -1)) < 0
+ ) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ pdf_reserve_object_id(pdev, pres, 0);
+ ppcs = (pdf_color_space_t *)pres;
+ ppcs->serialized = NULL;
+ ppcs->serialized_size = 0;
+
+ ppcs->ranges = 0;
+ pca->id = pres->object->id;
+ COS_FREE(pres->object, "pdf_color_space");
+ pres->object = (cos_object_t *)pca;
+ cos_write_object(COS_OBJECT(pca), pdev, resourceColorSpace);
+ if (ppscc != NULL)
+ pprints1(pdev->strm, "/%s", ppcs->rname);
+ }
+ pres->where_used |= pdev->used_mask;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", pres);
+ if (code < 0)
+ return code;
+ }
+
+ if (ppscc != NULL) {
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorspace);
+ *used_process_color = false;
+ if (pcs_save == NULL) {
+ for (i = 0; i < pcs->params.device_n.num_components; ++i)
+ pprintg1(pdev->strm, "%g ", psdf_round(pcc->paint.values[i], 255, 8));
+ } else
+ pprintg1(pdev->strm, "%g ", psdf_round(pcc->paint.values[0], 255, 8));
+ pprints1(pdev->strm, "%s\n", ppscc->setcolorn);
+ }
+ if (pvalue != NULL) {
+ if (by_name) {
+ /* Return a resource name rather than an object reference. */
+ discard(COS_RESOURCE_VALUE(pvalue, pca));
+ } else
+ discard(COS_OBJECT_VALUE(pvalue, pca));
+ }
+ return 0;
+}
+
+int convert_separation_alternate(gx_device_pdf * pdev, const gs_gstate * pgs, const gs_color_space *pcs,
+ const gx_drawing_color *pdc, bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc, gs_client_color *pcc, cos_value_t *pvalue, bool by_name)
+{
+ gs_color_space_index csi;
+ gs_function_t *new_pfn = 0;
+ float out_low[4];
+ float out_high[4];
+ int code;
+ cos_array_t *pca, *pca1;
+ cos_value_t v;
+ byte *name_string;
+ uint name_string_length;
+ pdf_resource_t *pres = NULL;
+
+ pca = cos_array_alloc(pdev, "pdf_color_space");
+ if (pca == 0)
+ return_error(gs_error_VMerror);
+
+ {
+ frac conc[GS_CLIENT_COLOR_MAX_COMPONENTS];
+ gs_client_color cc;
+ unsigned char i;
+ gs_color_space *icc_space = (gs_color_space *)pcs;
+ gs_color_space *sep_space = (gs_color_space *)pcs;
+ gs_color_space_index csi2;
+ bool save_use_alt = 0;
+ separation_type save_type = SEP_OTHER;
+
+ csi = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_Indexed)
+ sep_space = pcs->base_space;
+
+ do{
+ icc_space = icc_space->base_space;
+ csi2 = gs_color_space_get_index(icc_space);
+ } while(csi2 != gs_color_space_index_ICC && icc_space->base_space);
+
+ memset(&cc.paint.values, 0x00, GS_CLIENT_COLOR_MAX_COMPONENTS);
+ cc.paint.values[0] = 0;
+ sep_space->type->concretize_color(&cc, sep_space, conc, pgs, (gx_device *)pdev);
+
+ for (i = 0;i < pdev->color_info.num_components;i++)
+ cc.paint.values[i] = frac2float(conc[i]);
+
+ if (pdev->params.TransferFunctionInfo == tfi_Apply) {
+ switch (pdev->params.ColorConversionStrategy) {
+ case ccs_Gray:
+ code = apply_transfer_gray(pdev, pgs, &cc, &cc);
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ code = apply_transfer_rgb(pdev, pgs, &cc, &cc);
+ break;
+ case ccs_CMYK:
+ code = apply_transfer_cmyk(pdev, pgs, &cc, &cc);
+ break;
+ default:
+ code = gs_error_rangecheck;
+ break;
+ }
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ }
+ for (i = 0;i < pdev->color_info.num_components;i++)
+ out_low[i] = cc.paint.values[i];
+
+ memset (&conc, 0x00, sizeof(frac) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+ /* Force the colour management code to use the tint transform and
+ * give us the values in the Alternate space. Otherwise, for
+ * SEP_NONE or SEP_ALL it gives us the wrong answer. For SEP_NONE
+ * it always returns 0 and for SEP_ALL it sets the first component
+ * (only!) of the space to the tint value.
+ */
+ if (sep_space->params.separation.sep_type == SEP_ALL || sep_space->params.separation.sep_type == SEP_NONE) {
+ save_use_alt = sep_space->params.separation.use_alt_cspace;
+ sep_space->params.separation.use_alt_cspace = true;
+ save_type = sep_space->params.separation.sep_type;
+ sep_space->params.separation.sep_type = SEP_OTHER;
+ }
+
+ cc.paint.values[0] = 1;
+ memset (&conc, 0x00, sizeof(frac) * GS_CLIENT_COLOR_MAX_COMPONENTS);
+ sep_space->type->concretize_color(&cc, sep_space, conc, pgs, (gx_device *)pdev);
+
+ for (i = 0;i < pdev->color_info.num_components;i++)
+ cc.paint.values[i] = frac2float(conc[i]);
+
+ if (pdev->params.TransferFunctionInfo == tfi_Apply) {
+ switch (pdev->params.ColorConversionStrategy) {
+ case ccs_Gray:
+ code = apply_transfer_gray(pdev, pgs, &cc, &cc);
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ code = apply_transfer_rgb(pdev, pgs, &cc, &cc);
+ break;
+ case ccs_CMYK:
+ code = apply_transfer_cmyk(pdev, pgs, &cc, &cc);
+ break;
+ default:
+ code = gs_error_rangecheck;
+ break;
+ }
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ }
+ for (i = 0;i < pdev->color_info.num_components;i++)
+ out_high[i] = cc.paint.values[i];
+
+ /* Put back the values we hacked in order to force the colour management code
+ * to do what we want.
+ */
+ if (save_type == SEP_ALL || save_type == SEP_NONE) {
+ sep_space->params.separation.use_alt_cspace = save_use_alt;
+ sep_space->params.separation.sep_type = save_type;
+ }
+ }
+
+ switch(pdev->params.ColorConversionStrategy) {
+ case ccs_Gray:
+ code = pdf_make_base_space_function(pdev, &new_pfn, 1, out_low, out_high);
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ code = pdf_make_base_space_function(pdev, &new_pfn, 3, out_low, out_high);
+ break;
+ case ccs_CMYK:
+ code = pdf_make_base_space_function(pdev, &new_pfn, 4, out_low, out_high);
+ break;
+ default:
+ code = gs_error_rangecheck;
+ break;
+ }
+
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+
+ code = cos_array_add(pca, cos_c_string_value(&v, "/Separation"));
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+
+ if (code >= 0) {
+ csi = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_Indexed) {
+ name_string = (byte *)pcs->base_space->params.separation.sep_name;
+ name_string_length = strlen(pcs->base_space->params.separation.sep_name);
+ }
+ else {
+ name_string = (byte *)pcs->params.separation.sep_name;
+ name_string_length = strlen(pcs->params.separation.sep_name);
+ }
+ code = pdf_string_to_cos_name(pdev, name_string,
+ name_string_length, &v);
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+
+ code = cos_array_add(pca, &v);
+ if (code < 0) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ if (code >= 0) {
+ switch(pdev->params.ColorConversionStrategy) {
+ case ccs_Gray:
+ cos_c_string_value(&v, (const char *)pdf_color_space_names.DeviceGray);
+ break;
+ case ccs_RGB:
+ case ccs_sRGB:
+ cos_c_string_value(&v, (const char *)pdf_color_space_names.DeviceRGB);
+ break;
+ case ccs_CMYK:
+ cos_c_string_value(&v, (const char *)pdf_color_space_names.DeviceCMYK);
+ break;
+ default:
+ break;
+ }
+ code = cos_array_add(pca, &v);
+ if (code >= 0) {
+ code = pdf_function_scaled(pdev, new_pfn, 0x00, &v);
+ if (code >= 0) {
+ code = cos_array_add(pca, &v);
+ }
+ }
+ }
+ }
+ pdf_delete_base_space_function(pdev, new_pfn);
+
+ /*
+ * Register the color space as a resource, since it must be referenced
+ * by name rather than directly.
+ */
+ {
+ pdf_color_space_t *ppcs;
+
+ if (code < 0 ||
+ (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id,
+ &pres, -1)) < 0
+ ) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ pdf_reserve_object_id(pdev, pres, 0);
+ ppcs = (pdf_color_space_t *)pres;
+ ppcs->serialized = NULL;
+ ppcs->serialized_size = 0;
+
+ ppcs->ranges = 0;
+ pca->id = pres->object->id;
+ COS_FREE(pres->object, "pdf_color_space");
+ pres->object = (cos_object_t *)pca;
+ cos_write_object(COS_OBJECT(pca), pdev, resourceColorSpace);
+ csi = gs_color_space_get_index(pcs);
+ if (csi != gs_color_space_index_Indexed && ppscc != NULL)
+ pprints1(pdev->strm, "/%s", ppcs->rname);
+ }
+ pres->where_used |= pdev->used_mask;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", pres);
+ if (code < 0)
+ return code;
+
+ csi = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_Indexed) {
+ cos_value_t value;
+
+ discard(COS_OBJECT_VALUE(&value, pca));
+ pca1 = cos_array_alloc(pdev, "pdf_color_space");
+ code = pdf_indexed_color_space(pdev, pgs, &value, pcs, pca1, (cos_value_t *)&value);
+ pca = pca1;
+
+ /*
+ * Register the color space as a resource, since it must be referenced
+ * by name rather than directly.
+ */
+ {
+ pdf_color_space_t *ppcs;
+
+ if (code < 0 ||
+ (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id,
+ &pres, -1)) < 0
+ ) {
+ COS_FREE(pca, "pdf_color_space");
+ return code;
+ }
+ pdf_reserve_object_id(pdev, pres, 0);
+ ppcs = (pdf_color_space_t *)pres;
+ ppcs->serialized = NULL;
+ ppcs->serialized_size = 0;
+
+ ppcs->ranges = 0;
+ pca->id = pres->object->id;
+ COS_FREE(pres->object, "pdf_color_space");
+ pres->object = (cos_object_t *)pca;
+ cos_write_object(COS_OBJECT(pca), pdev, resourceColorSpace);
+ if (ppscc != NULL)
+ pprints1(pdev->strm, "/%s", ppcs->rname);
+ }
+ pres->where_used |= pdev->used_mask;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/ColorSpace", pres);
+ if (code < 0)
+ return code;
+ }
+
+ if (ppscc != NULL) {
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorspace);
+ *used_process_color = false;
+ pprintg1(pdev->strm, "%g", psdf_round(pcc->paint.values[0], 255, 8));
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ }
+ if (pvalue != NULL) {
+ if (by_name) {
+ /* Return a resource name rather than an object reference. */
+ discard(COS_RESOURCE_VALUE(pvalue, pca));
+ } else
+ discard(COS_OBJECT_VALUE(pvalue, pca));
+ }
+ return 0;
+}
+
+void
+rescale_cie_color(gs_range_t *ranges, int num_colorants,
+ const gs_client_color *src, gs_client_color *des)
+{
+ int k;
+
+ for (k = 0; k < num_colorants; k++) {
+ des->paint.values[k] =
+ (src->paint.values[k]-ranges[k].rmin)/
+ (ranges[k].rmax-ranges[k].rmin);
+ }
+}
+
+/* Set the fill or stroke color. */
+int pdf_reset_color(gx_device_pdf * pdev, const gs_gstate * pgs,
+ const gx_drawing_color *pdc, gx_hl_saved_color * psc,
+ bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc)
+{
+ int code=0, code1=0;
+ gx_hl_saved_color temp;
+ gs_client_color *pcc; /* fixme: not needed due to gx_hld_get_color_component. */
+ cos_value_t cs_value;
+ gs_color_space_index csi;
+ gs_color_space_index csi2;
+ const gs_color_space *pcs, *pcs2;
+
+ if (pdev->skip_colors)
+ return 0;
+
+ /* Get a copy of the current colour so we can examine it. */
+ gx_hld_save_color(pgs, pdc, &temp);
+
+ /* Since pdfwrite never applies halftones and patterns, but monitors
+ * halftone/pattern IDs separately, we don't need to compare
+ * halftone/pattern bodies here.
+ */
+ if (gx_hld_saved_color_equal(&temp, psc))
+ /* New colour (and space) same as old colour, no need to do anything */
+ return 0;
+
+ /* Do we have a Pattern colour space ? */
+ switch (gx_hld_get_color_space_and_ccolor(pgs, pdc, &pcs, (const gs_client_color **)&pcc)) {
+ case pattern_color_space:
+ {
+ pdf_resource_t *pres;
+
+ if (pdc->type == gx_dc_type_pattern) {
+ /* Can't handle tiling patterns in levels 1.0 and 1.1, and
+ * unlike shading patterns we have no fallback.
+ */
+ if (pdev->CompatibilityLevel < 1.2) {
+ return_error(gs_error_undefined);
+ }
+ code = pdf_put_colored_pattern(pdev, pdc, pcs,
+ ppscc, pgs, &pres);
+ }
+ else if (pdc->type == &gx_dc_pure_masked) {
+ code = pdf_put_uncolored_pattern(pdev, pdc, pcs,
+ ppscc, pgs, &pres);
+ if (code < 0 || pres == 0) {
+ /* replaced a pattern with a flat fill, but we still
+ * need to change the 'saved' colour or we will
+ * get out of step with the PDF content.
+ */
+ *psc = temp;
+ return code;
+ }
+ if (pgs->have_pattern_streams)
+ code = pdf_write_ccolor(pdev, pgs, pcc);
+ } else if (pdc->type == &gx_dc_pattern2) {
+ if (pdev->CompatibilityLevel <= 1.2)
+ return_error(gs_error_rangecheck);
+ if (!is_pattern2_allowed_in_strategy(pdev, pdc))
+ return_error(gs_error_rangecheck);
+ code1 = pdf_put_pattern2(pdev, pgs, pdc, ppscc, &pres);
+ if (code1 < 0)
+ return code1;
+ } else
+ return_error(gs_error_rangecheck);
+ if (code < 0)
+ return code;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/Pattern", pres);
+ if (code >= 0) {
+ cos_value_write(cos_resource_value(&cs_value, pres->object), pdev);
+ pprints1(pdev->strm, " %s\n", ppscc->setcolorn);
+ }
+ else {
+ pres->where_used = 0;
+ return code;
+ }
+ *used_process_color = false;
+ }
+ break;
+ case non_pattern_color_space:
+ pcs2 = pcs;
+ csi = csi2 = gs_color_space_get_index(pcs);
+ if (csi == gs_color_space_index_ICC) {
+ csi2 = gsicc_get_default_type(pcs->cmm_icc_profile_data);
+ }
+ /* Figure out what to do if we are outputting to really ancient versions of PDF */
+ /* NB ps2write sets CompatibilityLevel to 1.2 so we cater for it here */
+ if (pdev->CompatibilityLevel <= 1.2) {
+
+ /* If we have an /Indexed space, we need to look at the base space */
+ if (csi2 == gs_color_space_index_Indexed) {
+ pcs2 = pcs->base_space;
+ csi2 = gs_color_space_get_index(pcs2);
+ }
+
+ switch (csi2) {
+ case gs_color_space_index_DeviceGray:
+ if (pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged ||
+ pdev->params.ColorConversionStrategy == ccs_Gray)
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ else
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ case gs_color_space_index_DeviceRGB:
+ if (pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged ||
+ pdev->params.ColorConversionStrategy == ccs_RGB)
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ else
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ if (pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged ||
+ pdev->params.ColorConversionStrategy == ccs_CMYK)
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ else
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ case gs_color_space_index_CIEA:
+ case gs_color_space_index_CIEABC:
+ case gs_color_space_index_CIEDEF:
+ case gs_color_space_index_CIEDEFG:
+ if (pdev->ForOPDFRead) {
+ switch (pdev->params.ColorConversionStrategy) {
+ case ccs_ByObjectType:
+ /* Object type not implemented yet */
+ case ccs_UseDeviceIndependentColorForImages:
+ /* If only correcting images, then leave unchanged */
+ case ccs_LeaveColorUnchanged:
+ if (pdev->transfer_not_identity && pdev->params.TransferFunctionInfo == tfi_Apply)
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ default:
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ }
+ else
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ case gs_color_space_index_Separation:
+ switch (pdev->params.ColorConversionStrategy) {
+ case ccs_ByObjectType:
+ /* Object type not implemented yet */
+ case ccs_UseDeviceIndependentColorForImages:
+ /* If only correcting images, then leave unchanged */
+ case ccs_LeaveColorUnchanged:
+ pcs2 = pcs->base_space;
+ csi2 = gs_color_space_get_index(pcs2);
+ if (csi2 == gs_color_space_index_ICC) {
+ csi2 = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ }
+ if (csi2 == gs_color_space_index_ICC) {
+ if (pcs2->cmm_icc_profile_data->islab && pdev->PreserveSeparation) {
+ if (pdev->ForOPDFRead) {
+ int saved_ccs = pdev->params.ColorConversionStrategy;
+ switch(pdev->pcm_color_info_index) {
+ case 0:
+ pdev->params.ColorConversionStrategy = ccs_Gray;
+ break;
+ case 1:
+ pdev->params.ColorConversionStrategy = ccs_RGB;
+ break;
+ case 2:
+ pdev->params.ColorConversionStrategy = ccs_CMYK;
+ break;
+ default:
+ pdev->params.ColorConversionStrategy = saved_ccs;
+ return_error(gs_error_rangecheck);
+ }
+ code = convert_separation_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ pdev->params.ColorConversionStrategy = saved_ccs;
+ }
+ } else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ } else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case ccs_UseDeviceIndependentColor:
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ case ccs_sRGB:
+ default:
+ code = convert_separation_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ break;
+ }
+ break;
+ case gs_color_space_index_DeviceN:
+ switch (pdev->params.ColorConversionStrategy) {
+ case ccs_ByObjectType:
+ /* Object type not implemented yet */
+ case ccs_UseDeviceIndependentColorForImages:
+ /* If only correcting images, then leave unchanged */
+ case ccs_LeaveColorUnchanged:
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case ccs_UseDeviceIndependentColor:
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ case ccs_sRGB:
+ default:
+ code = convert_DeviceN_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ break;
+ }
+ break;
+ case gs_color_space_index_ICC:
+ /* Note that if csi is ICC, check to see if this was one of
+ the default substitutes that we introduced for DeviceGray,
+ DeviceRGB or DeviceCMYK. If it is, then just write
+ the default color. Depending upon the flavor of PDF,
+ or other options, we may want to actually have all
+ the colors defined by ICC profiles and not do the following
+ substituion of the Device space. */
+ csi2 = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+
+ switch (csi2) {
+ case gs_color_space_index_DeviceGray:
+ if (pdev->params.ColorConversionStrategy == ccs_Gray ||
+ pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged) {
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ *psc = temp;
+ return code;
+ }
+ break;
+ case gs_color_space_index_DeviceRGB:
+ if (pdev->params.ColorConversionStrategy == ccs_RGB ||
+ pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged) {
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ *psc = temp;
+ return code;
+ }
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ if (pdev->params.ColorConversionStrategy == ccs_CMYK ||
+ pdev->params.ColorConversionStrategy == ccs_LeaveColorUnchanged) {
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ *psc = temp;
+ return code;
+ }
+ break;
+ default:
+ break;
+ }
+ /* Fall through if its not a device substitute, or not one we want to preserve */
+ case gs_color_space_index_DevicePixel:
+ case gs_color_space_index_Indexed:
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ default:
+ return (gs_note_error(gs_error_rangecheck));
+ break;
+ }
+ } else {
+ switch(pdev->params.ColorConversionStrategy) {
+ case ccs_ByObjectType:
+ /* Object type not implemented yet */
+ case ccs_UseDeviceIndependentColorForImages:
+ /* If only correcting images, then leave unchanged */
+ case ccs_LeaveColorUnchanged:
+ if (pdev->transfer_not_identity && pdev->params.TransferFunctionInfo == tfi_Apply) {
+
+ if (csi2 == gs_color_space_index_Separation || csi2 == gs_color_space_index_DeviceN) {
+ int force_process = 0, csi3 = gs_color_space_get_index(pcs->base_space);
+ if (csi3 == gs_color_space_index_ICC) {
+ csi3 = gsicc_get_default_type(pcs->base_space->cmm_icc_profile_data);
+ }
+ switch (csi3) {
+ case gs_color_space_index_DeviceGray:
+ pdev->params.ColorConversionStrategy = ccs_Gray;
+ break;
+ case gs_color_space_index_DeviceRGB:
+ pdev->params.ColorConversionStrategy = ccs_RGB;
+ break;
+ case gs_color_space_index_DeviceCMYK:
+ pdev->params.ColorConversionStrategy = ccs_CMYK;
+ break;
+ default:
+ force_process = 1;
+ break;
+ }
+
+ if (!force_process && csi2 == gs_color_space_index_Separation){
+ code = convert_separation_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ } else if (!force_process && csi2 == gs_color_space_index_DeviceN){
+ code = convert_DeviceN_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ } else
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ pdev->params.ColorConversionStrategy = ccs_LeaveColorUnchanged;
+ } else
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ }
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case ccs_UseDeviceIndependentColor:
+ code = write_color_as_process_ICC(pdev, pgs, pcs, pdc, psc, used_process_color, ppscc, pcc, &temp);
+ break;
+ case ccs_CMYK:
+ switch(csi2) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceCMYK:
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceCMYK)
+ code = convert_separation_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceCMYK)
+ code = convert_DeviceN_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Indexed:
+ pcs2 = pcs->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ switch(csi) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceCMYK:
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceCMYK)
+ code = convert_separation_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceCMYK)
+ code = convert_DeviceN_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ default:
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ break;
+ default:
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ break;
+ case ccs_Gray:
+ switch(csi2) {
+ case gs_color_space_index_DeviceGray:
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceGray)
+ code = convert_separation_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceGray)
+ code = convert_DeviceN_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Indexed:
+ pcs2 = pcs->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ switch(csi) {
+ case gs_color_space_index_DeviceGray:
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceGray)
+ code = convert_separation_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceGray)
+ code = convert_DeviceN_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ default:
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ break;
+ default:
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ break;
+ case ccs_sRGB:
+ case ccs_RGB:
+ switch(csi2) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceRGB:
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceRGB)
+ code = convert_separation_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceRGB)
+ code = convert_DeviceN_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Indexed:
+ pcs2 = pcs->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ switch(csi) {
+ case gs_color_space_index_DeviceGray:
+ case gs_color_space_index_DeviceRGB:
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_Separation:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceRGB)
+ code = convert_separation_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ case gs_color_space_index_DeviceN:
+ pcs2 = pcs2->base_space;
+ csi = gs_color_space_get_index(pcs2);
+ if (csi == gs_color_space_index_ICC)
+ csi = gsicc_get_default_type(pcs2->cmm_icc_profile_data);
+ if (csi != gs_color_space_index_DeviceRGB)
+ code = convert_DeviceN_alternate(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc, NULL, false);
+ else
+ code = write_color_unchanged(pdev, pgs, pcc, &temp, psc, ppscc, used_process_color, pcs, pdc);
+ break;
+ default:
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ break;
+ default:
+ code = write_color_as_process(pdev, pgs, pcs, pdc, used_process_color, ppscc, pcc);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default: /* must not happen. */
+ case use_process_color:
+ code = psdf_set_color((gx_device_vector *)pdev, pdc, ppscc);
+ if (code < 0)
+ return code;
+ *used_process_color = true;
+ break;
+ }
+ *psc = temp;
+ return code;
+}
+
+int
+pdf_set_drawing_color(gx_device_pdf * pdev, const gs_gstate * pgs,
+ const gx_drawing_color *pdc,
+ gx_hl_saved_color * psc,
+ bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc)
+{
+ gx_hl_saved_color temp;
+ int code;
+
+ /* This section of code was in pdf_reset_color above, but was moved into this
+ * routine (and below) in order to isolate the switch to a stream context. This
+ * now allows us the opportunity to write colours in any context, in particular
+ * when in a text context, by using pdf_reset_color.
+ */
+ if (pdev->skip_colors)
+ return 0;
+ gx_hld_save_color(pgs, pdc, &temp);
+ /* Since pdfwrite never applies halftones and patterns, but monitors
+ * halftone/pattern IDs separately, we don't need to compare
+ * halftone/pattern bodies here.
+ */
+ if (gx_hld_saved_color_equal(&temp, psc))
+ return 0;
+ /*
+ * In principle, we can set colors in either stream or text
+ * context. However, since we currently enclose all text
+ * strings inside a gsave/grestore, this causes us to lose
+ * track of the color when we leave text context. Therefore,
+ * we require stream context for setting colors.
+ */
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+
+ return pdf_reset_color(pdev, pgs, pdc, psc, used_process_color, ppscc);
+}
+int
+pdf_set_pure_color(gx_device_pdf * pdev, gx_color_index color,
+ gx_hl_saved_color * psc,
+ bool *used_process_color,
+ const psdf_set_color_commands_t *ppscc)
+{
+ gx_drawing_color dcolor;
+ gx_hl_saved_color temp;
+ int code;
+
+ set_nonclient_dev_color(&dcolor, color);
+
+ if (pdev->skip_colors)
+ return 0;
+ gx_hld_save_color(NULL, &dcolor, &temp);
+ /* Since pdfwrite never applies halftones and patterns, but monitors
+ * halftone/pattern IDs separately, we don't need to compare
+ * halftone/pattern bodies here.
+ */
+ if (gx_hld_saved_color_equal(&temp, psc))
+ return 0;
+ /*
+ * In principle, we can set colors in either stream or text
+ * context. However, since we currently enclose all text
+ * strings inside a gsave/grestore, this causes us to lose
+ * track of the color when we leave text context. Therefore,
+ * we require stream context for setting colors.
+ */
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+
+ return pdf_reset_color(pdev, NULL, &dcolor, psc, used_process_color, ppscc);
+}
+
+/*
+ * Convert a string into cos name.
+ */
+int
+pdf_string_to_cos_name(gx_device_pdf *pdev, const byte *str, uint len,
+ cos_value_t *pvalue)
+{
+ byte *chars = gs_alloc_string(pdev->pdf_memory, len + 1,
+ "pdf_string_to_cos_name");
+
+ if (chars == 0)
+ return_error(gs_error_VMerror);
+ chars[0] = '/';
+ memcpy(chars + 1, str, len);
+ cos_string_value(pvalue, chars, len + 1);
+ return 0;
+}
+
+/* ---------------- Graphics state updating ---------------- */
+
+/* ------ Functions ------ */
+
+/* Define the maximum size of a Function reference. */
+#define MAX_FN_NAME_CHARS 9 /* /Default, /Identity */
+#define MAX_FN_CHARS max(MAX_REF_CHARS + 4, MAX_FN_NAME_CHARS)
+
+/*
+ * Create and write a Function for a gx_transfer_map. We use this for
+ * transfer, BG, and UCR functions. If check_identity is true, check for
+ * an identity map. Return 1 if the map is the identity map, otherwise
+ * return 0.
+ */
+static data_source_proc_access(transfer_map_access); /* check prototype */
+static int
+transfer_map_access(const gs_data_source_t *psrc, ulong start, uint length,
+ byte *buf, const byte **ptr)
+{
+ const gx_transfer_map *map = (const gx_transfer_map *)psrc->data.str.data;
+ uint i;
+
+ if (ptr)
+ *ptr = buf;
+ for (i = 0; i < length; ++i)
+ buf[i] = frac2byte(map->values[(uint)start + i]);
+ return 0;
+}
+static int
+transfer_map_access_signed(const gs_data_source_t *psrc,
+ ulong start, uint length,
+ byte *buf, const byte **ptr)
+{
+ /* To prevent numeric errors, we need to map 0 to an integer.
+ * We can't apply a general expression, because Decode isn't accessible here.
+ * Assuming this works for UCR only.
+ * Assuming the range of UCR is always [-1, 1].
+ * Assuming BitsPerSample = 8.
+ */
+ const gx_transfer_map *map = (const gx_transfer_map *)psrc->data.str.data;
+ uint i;
+
+ *ptr = buf;
+ for (i = 0; i < length; ++i)
+ buf[i] = (byte)
+ ((frac2float(map->values[(uint)start + i]) + 1) * 127);
+ return 0;
+}
+static int
+pdf_write_transfer_map(gx_device_pdf *pdev, const gx_transfer_map *map,
+ int range0, bool check_identity,
+ const char *key, char *ids)
+{
+ gs_memory_t *mem = pdev->pdf_memory;
+ gs_function_Sd_params_t params;
+ static const float domain01[2] = { 0, 1 };
+ static const int size = transfer_map_size;
+ float range01[2], decode[2];
+ gs_function_t *pfn;
+ long id;
+ int code;
+
+ if (map == 0) {
+ *ids = 0; /* no map */
+ return 1;
+ }
+ if (check_identity) {
+ /* Check for an identity map. */
+ int i;
+
+ if (map->proc == gs_identity_transfer)
+ i = transfer_map_size;
+ else
+ for (i = 0; i < transfer_map_size; ++i) {
+ fixed d = map->values[i] - bits2frac(i, log2_transfer_map_size);
+ if (any_abs(d) > fixed_epsilon) /* ignore small noise */
+ break;
+ }
+ if (i == transfer_map_size) {
+ strcpy(ids, key);
+ strcat(ids, "/Identity");
+ return 1;
+ }
+ }
+ params.m = 1;
+ params.Domain = domain01;
+ params.n = 1;
+ range01[0] = (float)range0, range01[1] = 1.0;
+ params.Range = range01;
+ params.Order = 1;
+ params.DataSource.access =
+ (range0 < 0 ? transfer_map_access_signed : transfer_map_access);
+ params.DataSource.data.str.data = (const byte *)map; /* bogus */
+ /* DataSource */
+ params.BitsPerSample = 8; /* could be 16 */
+ params.Encode = 0;
+ if (range01[0] < 0 && range01[1] > 0) {
+ /* This works for UCR only.
+ * Map 0 to an integer.
+ * Rather the range of UCR is always [-1, 1],
+ * we prefer a general expression.
+ */
+ int r0 = (int)( -range01[0] * ((1 << params.BitsPerSample) - 1)
+ / (range01[1] - range01[0]) ); /* Round down. */
+ float r1 = r0 * range01[1] / -range01[0]; /* r0 + r1 <= (1 << params.BitsPerSample) - 1 */
+
+ decode[0] = range01[0];
+ decode[1] = range01[0] + (range01[1] - range01[0]) * ((1 << params.BitsPerSample) - 1)
+ / (r0 + r1);
+ params.Decode = decode;
+ } else
+ params.Decode = 0;
+ params.Size = &size;
+ code = gs_function_Sd_init(&pfn, &params, mem);
+ if (code < 0)
+ return code;
+ code = pdf_write_function(pdev, pfn, &id);
+ gs_function_free(pfn, false, mem);
+ if (code < 0)
+ return code;
+ gs_sprintf(ids, "%s%s%ld 0 R", key, (key[0] && key[0] != ' ' ? " " : ""), id);
+ return 0;
+}
+static int
+pdf_write_transfer(gx_device_pdf *pdev, const gx_transfer_map *map,
+ const char *key, char *ids)
+{
+ return pdf_write_transfer_map(pdev, map, 0, true, key, ids);
+}
+
+/* ------ Halftones ------ */
+
+/*
+ * Recognize the predefined PDF halftone functions. Note that because the
+ * corresponding PostScript functions use single-precision floats, the
+ * functions used for testing must do the same in order to get identical
+ * results. Currently we only do this for a few of the functions.
+ */
+#define HT_FUNC(name, expr)\
+ static double name(double xd, double yd) {\
+ float x = (float)xd, y = (float)yd;\
+ return d2f(expr);\
+ }
+
+/*
+ * In most versions of gcc (e.g., 2.7.2.3, 2.95.4), return (float)xxx
+ * doesn't actually do the coercion. Force this here. Note that if we
+ * use 'inline', it doesn't work.
+ */
+static float
+d2f(double d)
+{
+ float f = (float)d;
+ return f;
+}
+static double
+ht_Round(double xf, double yf)
+{
+ float x = (float)xf, y = (float)yf;
+ float xabs = fabs(x), yabs = fabs(y);
+
+ if (d2f(xabs + yabs) <= 1)
+ return d2f(1 - d2f(d2f(x * x) + d2f(y * y)));
+ xabs -= 1, yabs -= 1;
+ return d2f(d2f(d2f(xabs * xabs) + d2f(yabs * yabs)) - 1);
+}
+static double
+ht_Diamond(double xf, double yf)
+{
+ float x = (float)xf, y = (float)yf;
+ float xabs = fabs(x), yabs = fabs(y);
+
+ if (d2f(xabs + yabs) <= 0.75)
+ return d2f(1 - d2f(d2f(x * x) + d2f(y * y)));
+ if (d2f(xabs + yabs) <= d2f(1.23))
+ return d2f(1 - d2f(d2f(d2f(0.85) * xabs) + yabs));
+ xabs -= 1, yabs -= 1;
+ return d2f(d2f(d2f(xabs * xabs) + d2f(yabs * yabs)) - 1);
+}
+static double
+ht_Ellipse(double xf, double yf)
+{
+ float x = (float)xf, y = (float)yf;
+ float xabs = fabs(x), yabs = fabs(y);
+ /*
+ * The PDF Reference, 2nd edition, incorrectly specifies the
+ * computation w = 4 * |x| + 3 * |y| - 3. The PostScript code in the
+ * same book correctly implements w = 3 * |x| + 4 * |y| - 3.
+ */
+ float w = (float)(d2f(d2f(3 * xabs) + d2f(4 * yabs)) - 3);
+
+ if (w < 0) {
+ yabs /= 0.75;
+ return d2f(1 - d2f((d2f(x * x) + d2f(yabs * yabs)) / 4));
+ }
+ if (w > 1) {
+ xabs = 1 - xabs, yabs = d2f(1 - yabs) / 0.75;
+ return d2f(d2f((d2f(xabs * xabs) + d2f(yabs * yabs)) / 4) - 1);
+ }
+ return d2f(0.5 - w);
+}
+/*
+ * Most of these are recognized properly even without d2f. We've only
+ * added d2f where it apparently makes a difference.
+ */
+static float
+d2fsin_d(double x) {
+ return d2f(gs_sin_degrees(d2f(x)));
+}
+static float
+d2fcos_d(double x) {
+ return d2f(gs_cos_degrees(d2f(x)));
+}
+HT_FUNC(ht_EllipseA, 1 - (x * x + 0.9 * y * y))
+HT_FUNC(ht_InvertedEllipseA, x * x + 0.9 * y * y - 1)
+HT_FUNC(ht_EllipseB, 1 - sqrt(x * x + 0.625 * y * y))
+HT_FUNC(ht_EllipseC, 1 - (0.9 * x * x + y * y))
+HT_FUNC(ht_InvertedEllipseC, 0.9 * x * x + y * y - 1)
+HT_FUNC(ht_Line, -fabs((x - x) + y)) /* quiet compiler (unused variable x) */
+HT_FUNC(ht_LineX, (y - y) + x) /* quiet compiler (unused variable y) */
+HT_FUNC(ht_LineY, (x - x) + y) /* quiet compiler (unused variable x) */
+HT_FUNC(ht_Square, -max(fabs(x), fabs(y)))
+HT_FUNC(ht_Cross, -min(fabs(x), fabs(y)))
+HT_FUNC(ht_Rhomboid, (0.9 * fabs(x) + fabs(y)) / 2)
+HT_FUNC(ht_DoubleDot, (d2fsin_d(x * 360) + d2fsin_d(y * 360)) / 2)
+HT_FUNC(ht_InvertedDoubleDot, -(d2fsin_d(x * 360) + d2fsin_d(y * 360)) / 2)
+HT_FUNC(ht_SimpleDot, 1 - d2f(d2f(x * x) + d2f(y * y)))
+HT_FUNC(ht_InvertedSimpleDot, d2f(d2f(x * x) + d2f(y * y)) - 1)
+HT_FUNC(ht_CosineDot, (d2fcos_d(x * 180) + d2fcos_d(y * 180)) / 2)
+HT_FUNC(ht_Double, (d2fsin_d(x * 180) + d2fsin_d(y * 360)) / 2)
+HT_FUNC(ht_InvertedDouble, -(d2fsin_d(x * 180) + d2fsin_d(y * 360)) / 2)
+typedef struct ht_function_s {
+ const char *fname;
+ double (*proc)(double, double);
+} ht_function_t;
+static const ht_function_t ht_functions[] = {
+ {"Round", ht_Round},
+ {"Diamond", ht_Diamond},
+ {"Ellipse", ht_Ellipse},
+ {"EllipseA", ht_EllipseA},
+ {"InvertedEllipseA", ht_InvertedEllipseA},
+ {"EllipseB", ht_EllipseB},
+ {"EllipseC", ht_EllipseC},
+ {"InvertedEllipseC", ht_InvertedEllipseC},
+ {"Line", ht_Line},
+ {"LineX", ht_LineX},
+ {"LineY", ht_LineY},
+ {"Square", ht_Square},
+ {"Cross", ht_Cross},
+ {"Rhomboid", ht_Rhomboid},
+ {"DoubleDot", ht_DoubleDot},
+ {"InvertedDoubleDot", ht_InvertedDoubleDot},
+ {"SimpleDot", ht_SimpleDot},
+ {"InvertedSimpleDot", ht_InvertedSimpleDot},
+ {"CosineDot", ht_CosineDot},
+ {"Double", ht_Double},
+ {"InvertedDouble", ht_InvertedDouble}
+};
+
+/* Write each kind of halftone. */
+static int
+pdf_write_spot_function(gx_device_pdf *pdev, const gx_ht_order *porder,
+ long *pid)
+{
+ /****** DOESN'T HANDLE STRIP HALFTONES ******/
+ int w = porder->width, h = porder->height;
+ uint num_bits = porder->num_bits;
+ gs_function_Sd_params_t params;
+ static const float domain_spot[4] = { -1, 1, -1, 1 };
+ static const float range_spot[4] = { -1, 1 };
+ int size[2];
+ gs_memory_t *mem = pdev->pdf_memory;
+ /*
+ * Even though the values are logically ushort, we must always store
+ * them in big-endian order, so we access them as bytes.
+ */
+ byte *values;
+ gs_function_t *pfn;
+ uint i;
+ int code = 0;
+
+ params.array_size = 0;
+ params.m = 2;
+ params.Domain = domain_spot;
+ params.n = 1;
+ params.Range = range_spot;
+ params.Order = 0; /* default */
+ /*
+ * We could use 8, 16, or 32 bits per sample to save space, but for
+ * simplicity, we always use 16.
+ */
+ if (num_bits > 0x10000)
+ /* rangecheck is a 'special case' in gdev_pdf_fill_path, if this error is encountered
+ * then it 'falls back' to a different method assuming its handling transparency in an
+ * old PDF output version. But if we fail to write the halftone, we want to abort
+ * so use limitcheck instead.
+ */
+ return_error(gs_error_limitcheck);
+ params.BitsPerSample = 16;
+ params.Encode = 0;
+ /*
+ * The default Decode array maps the actual data values [1 .. w*h] to a
+ * sub-interval of the Range, but that's OK, since all that matters is
+ * the relative values, not the absolute values.
+ */
+ params.Decode = 0;
+ size[0] = w;
+ size[1] = h;
+ params.Size = size;
+ /* Create the (temporary) threshold array. */
+ values = gs_alloc_byte_array(mem, num_bits, 2, "pdf_write_spot_function");
+ if (values == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < num_bits; ++i) {
+ gs_int_point pt;
+ int value;
+
+ if ((code = porder->procs->bit_index(porder, i, &pt)) < 0)
+ break;
+ value = pt.y * w + pt.x;
+ /* Always store the values in big-endian order. */
+ values[i * 2] = (byte)(value >> 8);
+ values[i * 2 + 1] = (byte)value;
+ }
+ data_source_init_bytes(&params.DataSource, (const byte *)values,
+ sizeof(*values) * num_bits);
+ if (code >= 0 &&
+ /* Warning from COverity that params.array_size is uninitialised. Correct */
+ /* but immeidiately after copying the data Sd_init sets the copied value */
+ /* to zero, so it is not actually used uninitialised. */
+ (code = gs_function_Sd_init(&pfn, &params, mem)) >= 0
+ ) {
+ code = pdf_write_function(pdev, pfn, pid);
+ gs_function_free(pfn, false, mem);
+ }
+ gs_free_object(mem, values, "pdf_write_spot_function");
+ return code;
+}
+
+/* if (memcmp(order.levels, porder->levels, order.num_levels * sizeof(*order.levels))) */
+static int
+compare_gx_ht_order_levels(const gx_ht_order *order1, const gx_ht_order *order2) {
+ int i;
+ for (i=0; i<order1->num_levels; i++) {
+ if (order1->levels[i] != order2->levels[i])
+ return(1);
+ }
+ return(0);
+}
+
+static int
+pdf_write_spot_halftone(gx_device_pdf *pdev, const gs_spot_halftone *psht,
+ const gx_ht_order *porder, long *pid)
+{
+ char trs[17 + MAX_FN_CHARS + 1];
+ int code;
+ long spot_id;
+ stream *s;
+ int i = countof(ht_functions);
+ gs_memory_t *mem = pdev->pdf_memory;
+
+ if (pdev->CompatibilityLevel <= 1.7) {
+ code = pdf_write_transfer(pdev, porder->transfer, "/TransferFunction",
+ trs);
+ if (code < 0)
+ return code;
+ }
+ /*
+ * See if we can recognize the spot function, by comparing its sampled
+ * values against those in the order.
+ */
+ { gs_screen_enum senum;
+ gx_ht_order order;
+ int code;
+
+ order = *porder;
+ code = gs_screen_order_alloc(&order, mem);
+ if (code < 0)
+ goto notrec;
+ for (i = 0; i < countof(ht_functions); ++i) {
+ double (*spot_proc)(double, double) = ht_functions[i].proc;
+ gs_point pt;
+
+ gs_screen_enum_init_memory(&senum, &order, NULL, &psht->screen,
+ mem);
+ while ((code = gs_screen_currentpoint(&senum, &pt)) == 0 &&
+ gs_screen_next(&senum, spot_proc(pt.x, pt.y)) >= 0)
+ DO_NOTHING;
+ if (code < 0)
+ continue;
+ /* Compare the bits and levels arrays. */
+ if (compare_gx_ht_order_levels(&order,porder))
+ continue;
+ if (memcmp(order.bit_data, porder->bit_data,
+ order.num_bits * porder->procs->bit_data_elt_size))
+ continue;
+ /* We have a match. */
+ break;
+ }
+ gx_ht_order_release(&order, mem, false);
+ }
+ notrec:
+ if (i == countof(ht_functions)) {
+ /* Create and write a Function for the spot function. */
+ code = pdf_write_spot_function(pdev, porder, &spot_id);
+ if (code < 0)
+ return code;
+ }
+ *pid = pdf_begin_separate(pdev, resourceHalftone);
+ s = pdev->strm;
+ /* Use the original, requested frequency and angle. */
+ pprintg2(s, "<</Type/Halftone/HalftoneType 1/Frequency %g/Angle %g",
+ psht->screen.frequency, psht->screen.angle);
+ if (i < countof(ht_functions))
+ pprints1(s, "/SpotFunction/%s", ht_functions[i].fname);
+ else
+ pprintld1(s, "/SpotFunction %ld 0 R", spot_id);
+ if (pdev->CompatibilityLevel <= 1.7)
+ stream_puts(s, trs);
+ if (psht->accurate_screens)
+ stream_puts(s, "/AccurateScreens true");
+ stream_puts(s, ">>\n");
+ return pdf_end_separate(pdev, resourceHalftone);
+}
+static int
+pdf_write_screen_halftone(gx_device_pdf *pdev, const gs_screen_halftone *psht,
+ const gx_ht_order *porder, long *pid)
+{
+ gs_spot_halftone spot;
+
+ spot.screen = *psht;
+ spot.accurate_screens = false;
+ spot.transfer = 0;
+ spot.transfer_closure.proc = 0;
+ return pdf_write_spot_halftone(pdev, &spot, porder, pid);
+}
+static int
+pdf_write_colorscreen_halftone(gx_device_pdf *pdev,
+ const gs_colorscreen_halftone *pcsht,
+ const gx_device_halftone *pdht, long *pid)
+{
+ int i;
+ stream *s;
+ long ht_ids[4];
+
+ for (i = 0; i < pdht->num_comp ; ++i) {
+ int code = pdf_write_screen_halftone(pdev, &pcsht->screens.indexed[i],
+ &pdht->components[i].corder,
+ &ht_ids[i]);
+ if (code < 0)
+ return code;
+ }
+ *pid = pdf_begin_separate(pdev, resourceHalftone);
+ s = pdev->strm;
+ /* Use Black, Gray as the Default unless we are in RGB colormodel */
+ /* (num_comp < 4) in which case we use Green (arbitrarily) */
+ pprintld1(s, "<</Type/Halftone/HalftoneType 5/Default %ld 0 R\n",
+ pdht->num_comp > 3 ? ht_ids[3] : ht_ids[1]);
+ pprintld2(s, "/Red %ld 0 R/Cyan %ld 0 R", ht_ids[0], ht_ids[0]);
+ pprintld2(s, "/Green %ld 0 R/Magenta %ld 0 R", ht_ids[1], ht_ids[1]);
+ pprintld2(s, "/Blue %ld 0 R/Yellow %ld 0 R", ht_ids[2], ht_ids[2]);
+ if (pdht->num_comp > 3)
+ pprintld2(s, "/Gray %ld 0 R/Black %ld 0 R", ht_ids[3], ht_ids[3]);
+ stream_puts(s, ">>\n");
+ return pdf_end_separate(pdev, resourceHalftone);
+}
+
+#define CHECK(expr)\
+ BEGIN if ((code = (expr)) < 0) return code; END
+
+static int
+pdf_write_threshold_halftone(gx_device_pdf *pdev,
+ const gs_threshold_halftone *ptht,
+ const gx_ht_order *porder, long *pid)
+{
+ char trs[17 + MAX_FN_CHARS + 1];
+ pdf_data_writer_t writer;
+ int code;
+
+ memset(trs, 0x00, 17 + MAX_FN_CHARS + 1);
+ if (pdev->CompatibilityLevel <= 1.7) {
+ code = pdf_write_transfer(pdev, porder->transfer, "",
+ trs);
+
+ if (code < 0)
+ return code;
+ }
+ CHECK(pdf_begin_data(pdev, &writer));
+ *pid = writer.pres->object->id;
+ CHECK(cos_dict_put_c_strings((cos_dict_t *)writer.pres->object,
+ "/Type", "/Halftone"));
+ CHECK(cos_dict_put_c_strings((cos_dict_t *)writer.pres->object,
+ "/HalftoneType", "6"));
+ CHECK(cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Width", ptht->width));
+ CHECK(cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Height", ptht->height));
+ if (pdev->CompatibilityLevel <= 1.7 && trs[0] != 0)
+ CHECK(cos_dict_put_c_strings((cos_dict_t *)writer.pres->object,
+ "/TransferFunction", trs));
+ stream_write(writer.binary.strm, ptht->thresholds.data, ptht->thresholds.size);
+ return pdf_end_data(&writer);
+}
+static int
+pdf_write_threshold2_halftone(gx_device_pdf *pdev,
+ const gs_threshold2_halftone *ptht,
+ const gx_ht_order *porder, long *pid)
+{
+ char trs[17 + MAX_FN_CHARS + 1];
+ stream *s;
+ pdf_data_writer_t writer;
+ int code;
+
+ memset(trs, 0x00, 17 + MAX_FN_CHARS + 1);
+ if (pdev->CompatibilityLevel <= 1.7) {
+ code = pdf_write_transfer(pdev, porder->transfer, "",
+ trs);
+
+ if (code < 0)
+ return code;
+ }
+ CHECK(pdf_begin_data(pdev, &writer));
+ *pid = writer.pres->object->id;
+ CHECK(cos_dict_put_c_strings((cos_dict_t *)writer.pres->object,
+ "/Type", "/Halftone"));
+ CHECK(cos_dict_put_c_strings((cos_dict_t *)writer.pres->object,
+ "/HalftoneType", "16"));
+ CHECK(cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Width", ptht->width));
+ CHECK(cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Height", ptht->height));
+ if (ptht->width2 && ptht->height2) {
+ CHECK(cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Width2", ptht->width2));
+ CHECK(cos_dict_put_c_key_int((cos_dict_t *)writer.pres->object,
+ "/Height2", ptht->height2));
+ }
+ if (pdev->CompatibilityLevel <= 1.7 && trs[0] != 0)
+ CHECK(cos_dict_put_c_strings((cos_dict_t *)writer.pres->object,
+ "/TransferFunction", trs));
+ s = writer.binary.strm;
+ if (ptht->bytes_per_sample == 2)
+ stream_write(s, ptht->thresholds.data, ptht->thresholds.size);
+ else {
+ /* Expand 1-byte to 2-byte samples. */
+ int i;
+
+ for (i = 0; i < ptht->thresholds.size; ++i) {
+ byte b = ptht->thresholds.data[i];
+
+ stream_putc(s, b);
+ stream_putc(s, b);
+ }
+ }
+ return pdf_end_data(&writer);
+}
+static int
+pdf_get_halftone_component_index(const gs_multiple_halftone *pmht,
+ const gx_device_halftone *pdht,
+ int dht_index)
+{
+ int j;
+
+ for (j = 0; j < pmht->num_comp; j++)
+ if (pmht->components[j].comp_number == dht_index)
+ break;
+ if (j == pmht->num_comp) {
+ /* Look for Default. */
+ for (j = 0; j < pmht->num_comp; j++)
+ if (pmht->components[j].comp_number == GX_DEVICE_COLOR_MAX_COMPONENTS)
+ break;
+ if (j == pmht->num_comp)
+ return_error(gs_error_undefined);
+ }
+ return j;
+}
+static int
+pdf_write_multiple_halftone(gx_device_pdf *pdev,
+ const gs_multiple_halftone *pmht,
+ const gx_device_halftone *pdht, long *pid)
+{
+ stream *s;
+ int i, code, last_comp = 0;
+ gs_memory_t *mem = pdev->pdf_memory;
+ long *ids;
+ bool done_Default = false;
+
+ ids = (long *)gs_alloc_byte_array(mem, pmht->num_comp, sizeof(long),
+ "pdf_write_multiple_halftone");
+ if (ids == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < pdht->num_comp; ++i) {
+ const gs_halftone_component *phtc;
+ const gx_ht_order *porder;
+
+ code = pdf_get_halftone_component_index(pmht, pdht, i);
+ if (code < 0)
+ return code;
+ if (pmht->components[code].comp_number == GX_DEVICE_COLOR_MAX_COMPONENTS) {
+ if (done_Default)
+ continue;
+ done_Default = true;
+ }
+ phtc = &pmht->components[code];
+ porder = (pdht->components == 0 ? &pdht->order :
+ &pdht->components[i].corder);
+ switch (phtc->type) {
+ case ht_type_spot:
+ code = pdf_write_spot_halftone(pdev, &phtc->params.spot,
+ porder, &ids[i]);
+ break;
+ case ht_type_threshold:
+ code = pdf_write_threshold_halftone(pdev, &phtc->params.threshold,
+ porder, &ids[i]);
+ break;
+ case ht_type_threshold2:
+ code = pdf_write_threshold2_halftone(pdev,
+ &phtc->params.threshold2,
+ porder, &ids[i]);
+ break;
+ default:
+ code = gs_note_error(gs_error_rangecheck);
+ }
+ if (code < 0) {
+ gs_free_object(mem, ids, "pdf_write_multiple_halftone");
+ return code;
+ }
+ }
+ *pid = pdf_begin_separate(pdev, resourceHalftone);
+ s = pdev->strm;
+ stream_puts(s, "<</Type/Halftone/HalftoneType 5\n");
+ done_Default = false;
+ for (i = 0; i < pdht->num_comp; ++i) {
+ const gs_halftone_component *phtc;
+ byte *str;
+ uint len;
+ cos_value_t value;
+
+ code = pdf_get_halftone_component_index(pmht, pdht, i);
+ if (code < 0)
+ return code;
+ if (pmht->components[code].comp_number == GX_DEVICE_COLOR_MAX_COMPONENTS) {
+ if (done_Default)
+ continue;
+ done_Default = true;
+ }
+ phtc = &pmht->components[code];
+ if ((code = pmht->get_colorname_string(pdev->memory, phtc->cname, &str, &len)) < 0 ||
+ (code = pdf_string_to_cos_name(pdev, str, len, &value)) < 0)
+ return code;
+ cos_value_write(&value, pdev);
+ gs_free_string(mem, value.contents.chars.data,
+ value.contents.chars.size,
+ "pdf_write_multiple_halftone");
+ pprintld1(s, " %ld 0 R\n", ids[i]);
+ last_comp = i;
+ }
+ if (!done_Default) {
+ /*
+ * BOGUS: Type 5 halftones must contain Default component.
+ * Perhaps we have no way to obtain it,
+ * because pdht contains ProcessColorModel components only.
+ * We copy the last component as Default one.
+ */
+ pprintld1(s, " /Default %ld 0 R\n", ids[last_comp]);
+ }
+ stream_puts(s, ">>\n");
+ gs_free_object(mem, ids, "pdf_write_multiple_halftone");
+ return pdf_end_separate(pdev, resourceHalftone);
+}
+
+/*
+ * Update the halftone. This is a separate procedure only for
+ * readability.
+ */
+static int
+pdf_update_halftone(gx_device_pdf *pdev, const gs_gstate *pgs,
+ char *hts)
+{
+ const gs_halftone *pht = pgs->halftone;
+ const gx_device_halftone *pdht = pgs->dev_ht;
+ int code;
+ long id;
+
+ switch (pht->type) {
+ case ht_type_screen:
+ code = pdf_write_screen_halftone(pdev, &pht->params.screen,
+ &pdht->components[0].corder, &id);
+ break;
+ case ht_type_colorscreen:
+ code = pdf_write_colorscreen_halftone(pdev, &pht->params.colorscreen,
+ pdht, &id);
+ break;
+ case ht_type_spot:
+ code = pdf_write_spot_halftone(pdev, &pht->params.spot,
+ &pdht->components[0].corder, &id);
+ break;
+ case ht_type_threshold:
+ code = pdf_write_threshold_halftone(pdev, &pht->params.threshold,
+ &pdht->components[0].corder, &id);
+ break;
+ case ht_type_threshold2:
+ code = pdf_write_threshold2_halftone(pdev, &pht->params.threshold2,
+ &pdht->components[0].corder, &id);
+ break;
+ case ht_type_multiple:
+ case ht_type_multiple_colorscreen:
+ code = pdf_write_multiple_halftone(pdev, &pht->params.multiple,
+ pdht, &id);
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ if (code < 0)
+ return code;
+ gs_sprintf(hts, "%ld 0 R", id);
+ pdev->halftone_id = pgs->dev_ht->id;
+ return code;
+}
+
+/* ------ Graphics state updating ------ */
+
+static inline cos_dict_t *
+resource_dict(pdf_resource_t *pres)
+{
+ return (cos_dict_t *)pres->object;
+}
+
+/* Open an ExtGState. */
+static int
+pdf_open_gstate(gx_device_pdf *pdev, pdf_resource_t **ppres)
+{
+ int code;
+
+ if (*ppres)
+ return 0;
+ /*
+ * We write gs command only in stream context.
+ * If we are clipped, and the clip path is about to change,
+ * the old clipping must be undone before writing gs.
+ */
+ if (pdev->context != PDF_IN_STREAM) {
+ /* We apparently use gs_error_interrupt as a request to change context. */
+ return_error(gs_error_interrupt);
+ }
+ code = pdf_alloc_resource(pdev, resourceExtGState, gs_no_id, ppres, -1L);
+ if (code < 0)
+ return code;
+ cos_become((*ppres)->object, cos_type_dict);
+ code = cos_dict_put_c_key_string(resource_dict(*ppres), "/Type", (const byte *)"/ExtGState", 10);
+ if (code < 0)
+ return code;
+ return 0;
+}
+
+/* Finish writing an ExtGState. */
+int
+pdf_end_gstate(gx_device_pdf *pdev, pdf_resource_t *pres)
+{
+ if (pres) {
+ int code = pdf_substitute_resource(pdev, &pres, resourceExtGState, NULL, true);
+
+ if (code < 0)
+ return code;
+ pres->where_used |= pdev->used_mask;
+ code = pdf_open_page(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ code = pdf_add_resource(pdev, pdev->substream_Resources, "/ExtGState", pres);
+ if (code < 0)
+ return code;
+ pprintld1(pdev->strm, "/R%ld gs\n", pdf_resource_id(pres));
+ pres->where_used |= pdev->used_mask;
+ }
+ return 0;
+}
+
+/*
+ * Update the transfer functions(s). This is a separate procedure only
+ * for readability.
+ */
+static int
+pdf_update_transfer(gx_device_pdf *pdev, const gs_gstate *pgs,
+ char *trs)
+{
+ int i, pi = -1;
+ bool multiple = false, update = false;
+ gs_id transfer_ids[4];
+ int code = 0;
+ const gx_transfer_map *tm[4];
+
+ tm[0] = pgs->set_transfer.red;
+ tm[1] = pgs->set_transfer.green;
+ tm[2] = pgs->set_transfer.blue;
+ tm[3] = pgs->set_transfer.gray;
+ for (i = 0; i < 4; ++i)
+ if (tm[i] != NULL) {
+ transfer_ids[i] = tm[i]->id;
+ if (pdev->transfer_ids[i] != tm[i]->id)
+ update = true;
+ if (pi != -1 && transfer_ids[i] != transfer_ids[pi])
+ multiple = true;
+ pi = i;
+ } else
+ transfer_ids[i] = -1;
+ if (update) {
+ int mask;
+
+ if (!multiple) {
+ code = pdf_write_transfer(pdev, tm[pi], "", trs);
+ if (code < 0)
+ return code;
+ mask = code == 0;
+ } else {
+ strcpy(trs, "[");
+ mask = 0;
+ for (i = 0; i < 4; ++i)
+ if (tm[i] != NULL) {
+ code = pdf_write_transfer_map(pdev,
+ tm[i],
+ 0, true, " ", trs + strlen(trs));
+ if (code < 0)
+ return code;
+ mask |= (code == 0) << i;
+ }
+ strcat(trs, "]");
+ }
+ memcpy(pdev->transfer_ids, transfer_ids, sizeof(pdev->transfer_ids));
+ pdev->transfer_not_identity = mask;
+ }
+ return code;
+}
+
+/*
+ * Update the current alpha if necessary. Note that because Ghostscript
+ * stores separate opacity and shape alpha, a rangecheck will occur if
+ * both are different from the current setting.
+ */
+static int
+pdf_update_alpha(gx_device_pdf *pdev, const gs_gstate *pgs,
+ pdf_resource_t **ppres)
+{
+ bool ais;
+ double alpha;
+ int code;
+
+ if (pdev->state.soft_mask_id != pgs->soft_mask_id) {
+ char buf[20];
+
+ if (pgs->soft_mask_id == 0) {
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ code = pdf_restore_viewer_state(pdev, pdev->strm);
+ if (code < 0)
+ return code;
+ }
+ else{
+ gs_sprintf(buf, "%ld 0 R", pgs->soft_mask_id);
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_string(resource_dict(*ppres),
+ "/SMask", (byte *)buf, strlen(buf));
+ if (code < 0)
+ return code;
+ code = pdf_save_viewer_state(pdev, pdev->strm);
+ if (code < 0)
+ return code;
+ }
+ pdev->state.soft_mask_id = pgs->soft_mask_id;
+ }
+ if (pdev->state.opacity.alpha != pgs->opacity.alpha) {
+ if (pdev->state.shape.alpha != pgs->shape.alpha) {
+ /* We had previously set one of opacity or shape, but we didn't
+ * ever need to write the graphcis state out, leaving us with a
+ * dangling alpha. We should honour the current state. One of
+ * opacity or alpha will be the default (1.0), so use the other.
+ */
+ pdev->state.opacity.alpha = pgs->opacity.alpha;
+ pdev->state.shape.alpha = pgs->shape.alpha;
+ if (pgs->opacity.alpha != 1.0) {
+ ais = false;
+ alpha = pdev->state.opacity.alpha;
+ }
+ else {
+ ais = true;
+ alpha = pdev->state.shape.alpha;
+ }
+ } else {
+ ais = false;
+ alpha = pdev->state.opacity.alpha = pgs->opacity.alpha;
+ }
+ } else if (pdev->state.shape.alpha != pgs->shape.alpha) {
+ ais = true;
+ alpha = pdev->state.shape.alpha = pgs->shape.alpha;
+ } else
+ return 0;
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_bool(resource_dict(*ppres), "/AIS", ais);
+ if (code < 0)
+ return code;
+ /* we never do the 'both' operations (b, B, b*, B*) so we set both */
+ /* CA and ca the same so that we stay in sync with state.*.alpha */
+ code = cos_dict_put_c_key_real(resource_dict(*ppres), "/CA", alpha);
+ if (code < 0)
+ return code;
+ return cos_dict_put_c_key_real(resource_dict(*ppres), "/ca", alpha);
+}
+
+/*
+ * Update the graphics subset common to all high-level drawing operations.
+ */
+int
+pdf_prepare_drawing(gx_device_pdf *pdev, const gs_gstate *pgs,
+ pdf_resource_t **ppres)
+{
+ int code = 0;
+ int bottom;
+
+ if (pdev->CompatibilityLevel >= 1.4) {
+ if (pdev->state.blend_mode != pgs->blend_mode) {
+ static const char *const bm_names[] = { GS_BLEND_MODE_NAMES };
+ char buf[20];
+
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ buf[0] = '/';
+ strncpy(buf + 1, bm_names[pgs->blend_mode], sizeof(buf) - 2);
+ code = cos_dict_put_string_copy(resource_dict(*ppres), "/BM", buf);
+ if (code < 0)
+ return code;
+ pdev->state.blend_mode = pgs->blend_mode;
+ }
+ code = pdf_update_alpha(pdev, pgs, ppres);
+ if (code < 0)
+ return code;
+ } else {
+ /*
+ * If the graphics state calls for any transparency functions,
+ * we can't represent them, so return a rangecheck.
+ */
+ if (pgs->opacity.alpha != 1 ||
+ pgs->shape.alpha != 1)
+ return_error(gs_error_rangecheck);
+ }
+ /*
+ * We originally thought the remaining items were only needed for
+ * fill and stroke, but in fact they are needed for images as well.
+ */
+ /*
+ * Update halftone, transfer function, black generation, undercolor
+ * removal, halftone phase, overprint mode, smoothness, blend mode, text
+ * knockout.
+ */
+ bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
+ /* When ResourcesBeforeUsage != 0, one sbstack element
+ appears from the page contents stream. */
+ if (pdev->sbstack_depth == bottom) {
+ gs_int_point phase, dev_phase;
+ char hts[5 + MAX_FN_CHARS + 1],
+ trs[5 + MAX_FN_CHARS * 4 + 6 + 1],
+ bgs[5 + MAX_FN_CHARS + 1],
+ ucrs[6 + MAX_FN_CHARS + 1];
+
+ hts[0] = trs[0] = bgs[0] = ucrs[0] = 0;
+ if (pdev->params.PreserveHalftoneInfo &&
+ pdev->halftone_id != pgs->dev_ht->id &&
+ !pdev->PDFX
+ ) {
+ code = pdf_update_halftone(pdev, pgs, hts);
+ if (code < 0)
+ return code;
+ }
+ if (pdev->params.TransferFunctionInfo != tfi_Remove &&
+ !pdev->PDFX && pdev->PDFA == 0
+ ) {
+ code = pdf_update_transfer(pdev, pgs, trs);
+ if (code < 0)
+ return code;
+ }
+ if (pdev->params.UCRandBGInfo == ucrbg_Preserve) {
+ if (pgs->black_generation && pdev->black_generation_id != pgs->black_generation->id) {
+ code = pdf_write_transfer_map(pdev, pgs->black_generation,
+ 0, false, "", bgs);
+ if (code < 0)
+ return code;
+ pdev->black_generation_id = pgs->black_generation->id;
+ }
+ if (pgs->undercolor_removal && pdev->undercolor_removal_id != pgs->undercolor_removal->id) {
+ code = pdf_write_transfer_map(pdev, pgs->undercolor_removal,
+ -1, false, "", ucrs);
+ if (code < 0)
+ return code;
+ pdev->undercolor_removal_id = pgs->undercolor_removal->id;
+ }
+ }
+ if (hts[0] || trs[0] || bgs[0] || ucrs[0]) {
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ }
+ if (hts[0]) {
+ code = cos_dict_put_string_copy(resource_dict(*ppres), "/HT", hts);
+ if (code < 0)
+ return code;
+ }
+ if (pdev->CompatibilityLevel <= 1.7 && trs[0] && pdev->params.TransferFunctionInfo == tfi_Preserve) {
+ code = cos_dict_put_string_copy(resource_dict(*ppres), "/TR", trs);
+ if (code < 0)
+ return code;
+ }
+ if (bgs[0]) {
+ code = cos_dict_put_string_copy(resource_dict(*ppres), "/BG", bgs);
+ if (code < 0)
+ return code;
+ }
+ if (ucrs[0]) {
+ code = cos_dict_put_string_copy(resource_dict(*ppres), "/UCR", ucrs);
+ if (code < 0)
+ return code;
+ }
+ if (!pdev->PDFX) {
+ gs_currentscreenphase(pgs, &phase, 0);
+ gs_currentscreenphase(&pdev->state, &dev_phase, 0);
+ if (dev_phase.x != phase.x || dev_phase.y != phase.y) {
+ char buf[sizeof(int) * 3 + 5];
+
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ gs_sprintf(buf, "[%d %d]", phase.x, phase.y);
+ code = cos_dict_put_string_copy(resource_dict(*ppres), "/HTP", buf);
+ if (code < 0)
+ return code;
+ gx_gstate_setscreenphase(&pdev->state, phase.x, phase.y,
+ gs_color_select_all);
+ }
+ }
+ }
+ if (pdev->CompatibilityLevel >= 1.3 && pdev->sbstack_depth == bottom) {
+ if (pdev->overprint_mode != pdev->params.OPM) {
+ if (pdev->params.OPM == 1 && pdev->PDFA == 2) {
+ switch (pdev->PDFACompatibilityPolicy) {
+ case 0:
+ emprintf(pdev->memory,
+ "Setting Overprint Mode to 1\n not permitted in PDF/A-2, reverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ case 1:
+ emprintf(pdev->memory,
+ "Setting Overprint Mode to 1\n not permitted in PDF/A-2, overprint mode not set\n\n");
+ pdev->params.OPM = 0;
+ break;
+ case 2:
+ emprintf(pdev->memory,
+ "Setting Overprint Mode to 1\n not permitted in PDF/A-2, aborting conversion\n");
+ return_error(gs_error_undefined);
+ break;
+ default:
+ emprintf(pdev->memory,
+ "Setting Overprint Mode to 1\n not permitted in PDF/A-2, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
+ pdev->AbortPDFAX = true;
+ pdev->PDFA = 0;
+ break;
+ }
+ }
+ if (pdev->overprint_mode != pdev->params.OPM) {
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_int(resource_dict(*ppres), "/OPM", pdev->params.OPM);
+ if (code < 0)
+ return code;
+ pdev->overprint_mode = pdev->params.OPM;
+ }
+ }
+ if (pdev->state.smoothness != pgs->smoothness) {
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_real(resource_dict(*ppres), "/SM", pgs->smoothness);
+ if (code < 0)
+ return code;
+ pdev->state.smoothness = pgs->smoothness;
+ }
+ if (pdev->CompatibilityLevel >= 1.4) {
+ if (pdev->state.text_knockout != pgs->text_knockout) {
+ code = pdf_open_gstate(pdev, ppres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_bool(resource_dict(*ppres), "/TK", pgs->text_knockout);
+ if (code < 0)
+ return code;
+ pdev->state.text_knockout = pgs->text_knockout;
+ }
+ }
+ }
+ return code;
+}
+
+/* Update the graphics state for filling. */
+int
+pdf_try_prepare_fill(gx_device_pdf *pdev, const gs_gstate *pgs)
+{
+ pdf_resource_t *pres = 0;
+ int code = pdf_prepare_drawing(pdev, pgs, &pres);
+
+ if (code < 0)
+ return code;
+ /* Update overprint. */
+ if (pdev->params.PreserveOverprintSettings &&
+ (pdev->fill_overprint != pgs->overprint ||
+ pdev->font3) && !pdev->skip_colors
+ ) {
+ code = pdf_open_gstate(pdev, &pres);
+ if (code < 0)
+ return code;
+ /* PDF 1.2 only has a single overprint setting. */
+ if (pdev->CompatibilityLevel < 1.3) {
+ code = cos_dict_put_c_key_bool(resource_dict(pres), "/OP", pgs->overprint);
+ if (code < 0)
+ return code;
+ pdev->stroke_overprint = pgs->overprint;
+ } else {
+ code = cos_dict_put_c_key_bool(resource_dict(pres), "/op", pgs->overprint);
+ if (code < 0)
+ return code;
+ }
+ pdev->fill_overprint = pgs->overprint;
+ }
+ return pdf_end_gstate(pdev, pres);
+}
+int
+pdf_prepare_fill(gx_device_pdf *pdev, const gs_gstate *pgs)
+{
+ int code;
+
+ if (pdev->context != PDF_IN_STREAM) {
+ code = pdf_try_prepare_fill(pdev, pgs);
+ if (code != gs_error_interrupt) /* See pdf_open_gstate */
+ return code;
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ }
+ return pdf_try_prepare_fill(pdev, pgs);
+}
+
+/* Update the graphics state for stroking. */
+static int
+pdf_try_prepare_stroke(gx_device_pdf *pdev, const gs_gstate *pgs)
+{
+ pdf_resource_t *pres = 0;
+ int code = pdf_prepare_drawing(pdev, pgs, &pres);
+
+ if (code < 0)
+ return code;
+ /* Update overprint, stroke adjustment. */
+ if (pdev->params.PreserveOverprintSettings &&
+ pdev->stroke_overprint != pgs->overprint &&
+ !pdev->skip_colors
+ ) {
+ code = pdf_open_gstate(pdev, &pres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_bool(resource_dict(pres), "/OP", pgs->overprint);
+ if (code < 0)
+ return code;
+ pdev->stroke_overprint = pgs->overprint;
+ if (pdev->CompatibilityLevel < 1.3) {
+ /* PDF 1.2 only has a single overprint setting. */
+ pdev->fill_overprint = pgs->overprint;
+ } else {
+ /* According to PDF>=1.3 spec, OP also sets op,
+ if there is no /op in same garphic state object.
+ We don't write /op, so monitor the viewer's state here : */
+ pdev->fill_overprint = pgs->overprint;
+ }
+ }
+ if (pdev->state.stroke_adjust != pgs->stroke_adjust) {
+ code = pdf_open_gstate(pdev, &pres);
+ if (code < 0)
+ return code;
+ code = cos_dict_put_c_key_bool(resource_dict(pres), "/SA", pgs->stroke_adjust);
+ if (code < 0)
+ return code;
+ pdev->state.stroke_adjust = pgs->stroke_adjust;
+ }
+ return pdf_end_gstate(pdev, pres);
+}
+int
+pdf_prepare_stroke(gx_device_pdf *pdev, const gs_gstate *pgs)
+{
+ int code;
+
+ if (pdev->context != PDF_IN_STREAM) {
+ code = pdf_try_prepare_stroke(pdev, pgs);
+ if (code != gs_error_interrupt) /* See pdf_open_gstate */
+ return code;
+ code = pdf_open_contents(pdev, PDF_IN_STREAM);
+ if (code < 0)
+ return code;
+ }
+ return pdf_try_prepare_stroke(pdev, pgs);
+}
+
+/* Update the graphics state for an image other than an ImageType 1 mask. */
+int
+pdf_prepare_image(gx_device_pdf *pdev, const gs_gstate *pgs)
+{
+ /*
+ * As it turns out, this requires updating the same parameters as for
+ * filling.
+ */
+ return pdf_prepare_fill(pdev, pgs);
+}
+
+/* Update the graphics state for an ImageType 1 mask. */
+int
+pdf_prepare_imagemask(gx_device_pdf *pdev, const gs_gstate *pgs,
+ const gx_drawing_color *pdcolor)
+{
+ int code = pdf_prepare_image(pdev, pgs);
+
+ if (code < 0)
+ return code;
+ return pdf_set_drawing_color(pdev, pgs, pdcolor, &pdev->saved_fill_color,
+ &pdev->fill_used_process_color,
+ &psdf_set_fill_color_commands);
+}