summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <Robin.Watts@artifex.com>2021-08-18 17:00:52 +0100
committerRobin Watts <Robin.Watts@artifex.com>2021-09-06 10:44:43 +0100
commitc02339fb12925a71d8a707987e86a1c46d519000 (patch)
tree8899fa9f0c0da1972cf96cbe82dcb86ef4314a84
parentf2bb042229e8a27a2c78f7f4ec883e3d313192e5 (diff)
downloadghostpdl-c02339fb12925a71d8a707987e86a1c46d519000.tar.gz
Nup: Set PageUsesTransparency even when not present.
Several commits ago, I modified the Nup device so that whenever PageUsesTransparency was specified, we forced it to be true. This avoided PDF pages flushing the nest as we flip/flop between pages with and without transparency. Unfortunately, with parameters that were themselves 'collections' (like arrays), this would both leak a bit and corrupt them due to not copying them properly. Here we steal a bit of code from gsparamx.c to properly recursively copy such things, and hack it set PageUsesTransparency as part of the copy process. We also extend that to ensure that it is ALWAYS set (whether it is specified or not), to ensure that going from (say) a PS job to a PDF one does not cause the nest to be unnecessarily flushed. Also, because of the way params report errors, we need to copy the reported errors back from the copied param list to the original one. This requires extensions to the param list handling functions. Because put_params is not always called between the Nup device being instantiated and the first page started, we need to call put_params ourselves. To do that write our own install procedure and get gdevkrnlsclass.c to call that rather than doing the subclass itself. Also, incorporate a SEGV fix from Ray. The devn_put_params can close the device, but if the device is a child of the Nup device, the Nup device won't have a chance to flush the nest. Also, just in case, when we switch a printer device from clist mode (as we do when we tear down a device), clear the 'pmemdev->base' that is assumed to be NULL or valid in non-clist mode.
-rw-r--r--base/gdevdevn.c17
-rw-r--r--base/gdevepo.c2
-rw-r--r--base/gdevkrnlsclass.c2
-rw-r--r--base/gdevnup.c207
-rw-r--r--base/gdevnup.h3
-rw-r--r--base/gdevprn.c1
-rw-r--r--base/gscparam.c22
-rw-r--r--base/gsparam.h9
-rw-r--r--base/gsparamx.c2
-rw-r--r--psi/iparam.c3
10 files changed, 229 insertions, 39 deletions
diff --git a/base/gdevdevn.c b/base/gdevdevn.c
index d8ea9f0ce..f67912768 100644
--- a/base/gdevdevn.c
+++ b/base/gdevdevn.c
@@ -845,8 +845,19 @@ devn_printer_put_params(gx_device * pdev, gs_param_list * plist,
!devn_params_equal(pdevn_params, &saved_devn_params) ||
(pequiv_colors != NULL &&
compare_equivalent_cmyk_color_params(pequiv_colors, &saved_equiv_colors))) {
- gs_closedevice(pdev);
- /* Reset the sparable and linear shift, masks, bits. */
+ gx_device *parent_dev = pdev;
+ gx_device_color_info resave_info = pdev->color_info;
+
+ while (parent_dev->parent != NULL)
+ parent_dev = parent_dev->parent;
+
+ /* Temporarily restore the old color_info, so the close happens with
+ * the old version. In particular this allows Nup to flush properly. */
+ pdev->color_info = save_info;
+ gs_closedevice(parent_dev);
+ /* Then put the shiny new color_info back in. */
+ pdev->color_info = resave_info;
+ /* Reset the separable and linear shift, masks, bits. */
set_linear_color_bits_mask_shift(pdev);
}
/*
@@ -1841,7 +1852,7 @@ devn_pcx_write_page(gx_device_printer * pdev, gp_file * infile, int linesize, gp
/* If needed, convert to rgb */
if (convert_to_rgb) {
int i;
- byte *row_in = line;
+ byte *row_in = line;
/* Transform the data. */
row = rgb_buff; /* adjust to converted output buffer */
diff --git a/base/gdevepo.c b/base/gdevepo.c
index 9d6af757f..eb325d9b0 100644
--- a/base/gdevepo.c
+++ b/base/gdevepo.c
@@ -317,7 +317,7 @@ epo_handle_erase_page(gx_device *dev)
DPRINTF1(dev->memory, "Do fillpage, Uninstall erasepage, device %s\n", dev->dname);
/* Just do a fill_rectangle (using saved color) */
- if (dev->child && data->queued) {
+ if (dev->child && dev->child->is_open && data->queued) {
code = dev_proc(dev->child, fill_rectangle)(dev->child,
0, 0,
dev->child->width,
diff --git a/base/gdevkrnlsclass.c b/base/gdevkrnlsclass.c
index d615a1005..8ae05fb2c 100644
--- a/base/gdevkrnlsclass.c
+++ b/base/gdevkrnlsclass.c
@@ -39,7 +39,7 @@ int install_internal_subclass_devices(gx_device **ppdev, int *devices_loaded)
#else
if (!dev->NupHandlerPushed && dev->NupControl != 0) {
#endif
- code = gx_device_subclass(dev, (gx_device *)&gs_nup_device, sizeof(Nup_device_subclass_data));
+ code = gx_device_nup_device_install(dev);
if (code < 0)
return code;
diff --git a/base/gdevnup.c b/base/gdevnup.c
index b8e220adf..d37cd7571 100644
--- a/base/gdevnup.c
+++ b/base/gdevnup.c
@@ -305,6 +305,138 @@ err: param_signal_error(plist, param_name, ecode);
return ecode;
}
+/* Horrible hacked version of param_list_copy from gsparamx.c.
+ * Copy one parameter list to another, recursively if necessary,
+ * rewriting PageUsesTransparency to be true if it occurs. */
+static int
+copy_and_modify_sub(gs_param_list *plto, gs_param_list *plfrom, int *present)
+{
+ gs_param_enumerator_t key_enum;
+ gs_param_key_t key;
+ bool copy_persists;
+ int code;
+
+ if (present)
+ *present = 0;
+ if (plfrom == NULL)
+ return 0;
+
+ /* If plfrom and plto use different allocators, we must copy
+ * aggregate values even if they are "persistent". */
+ copy_persists = plto->memory == plfrom->memory;
+
+ param_init_enumerator(&key_enum);
+ while ((code = param_get_next_key(plfrom, &key_enum, &key)) == 0) {
+ char string_key[256]; /* big enough for any reasonable key */
+ gs_param_typed_value value;
+ gs_param_collection_type_t coll_type;
+ gs_param_typed_value copy;
+
+ if (key.size > sizeof(string_key) - 1) {
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ memcpy(string_key, key.data, key.size);
+ string_key[key.size] = 0;
+ if ((code = param_read_typed(plfrom, string_key, &value)) != 0) {
+ code = (code > 0 ? gs_note_error(gs_error_unknownerror) : code);
+ break;
+ }
+ gs_param_list_set_persistent_keys(plto, key.persistent);
+ switch (value.type) {
+ case gs_param_type_dict:
+ coll_type = gs_param_collection_dict_any;
+ goto cc;
+ case gs_param_type_dict_int_keys:
+ coll_type = gs_param_collection_dict_int_keys;
+ goto cc;
+ case gs_param_type_array:
+ coll_type = gs_param_collection_array;
+ cc:
+ copy.value.d.size = value.value.d.size;
+ if (copy.value.d.size == 0)
+ break;
+ if ((code = param_begin_write_collection(plto, string_key,
+ &copy.value.d,
+ coll_type)) < 0 ||
+ (code = copy_and_modify_sub(copy.value.d.list,
+ value.value.d.list,
+ NULL)) < 0 ||
+ (code = param_end_write_collection(plto, string_key,
+ &copy.value.d)) < 0)
+ break;
+ code = param_end_read_collection(plfrom, string_key,
+ &value.value.d);
+ break;
+ case gs_param_type_bool:
+ if (strcmp(string_key, "PageUsesTransparency") == 0 && present != NULL)
+ {
+ value.value.b = 1;
+ *present = 1;
+ }
+ goto ca;
+ case gs_param_type_string:
+ value.value.s.persistent &= copy_persists; goto ca;
+ case gs_param_type_name:
+ value.value.n.persistent &= copy_persists; goto ca;
+ case gs_param_type_int_array:
+ value.value.ia.persistent &= copy_persists; goto ca;
+ case gs_param_type_float_array:
+ value.value.fa.persistent &= copy_persists; goto ca;
+ case gs_param_type_string_array:
+ value.value.sa.persistent &= copy_persists;
+ /* fall through */
+ ca:
+ default:
+ code = param_write_typed(plto, string_key, &value);
+ }
+ if (code < 0)
+ break;
+ }
+ return code;
+}
+
+static int
+param_list_copy_and_modify(gs_param_list *plto, gs_param_list *plfrom)
+{
+ int found_put;
+ int code = copy_and_modify_sub(plto, plfrom, &found_put);
+
+ if (code >= 0 && !found_put) {
+ gs_param_typed_value value;
+ value.type = gs_param_type_bool;
+ value.value.b = 1;
+ code = param_write_typed(plto, "PageUsesTransparency", &value);
+ }
+
+ return code;
+}
+
+static int
+promote_errors(gs_param_list * plist_orig, gs_param_list * plist)
+{
+ gs_param_enumerator_t key_enum;
+ gs_param_key_t key;
+ int code;
+ int error;
+
+ param_init_enumerator(&key_enum);
+ while ((code = param_get_next_key(plist_orig, &key_enum, &key)) == 0) {
+ char string_key[256]; /* big enough for any reasonable key */
+
+ if (key.size > sizeof(string_key) - 1) {
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ memcpy(string_key, key.data, key.size);
+ string_key[key.size] = 0;
+ error = param_read_signalled_error(plist, string_key);
+ param_signal_error(plist_orig, string_key, error);
+ }
+
+ return code;
+}
+
static int
nup_put_params(gx_device *dev, gs_param_list * plist_orig)
{
@@ -317,8 +449,6 @@ nup_put_params(gx_device *dev, gs_param_list * plist_orig)
gx_device *next_dev;
gs_c_param_list *plist_c;
gs_param_list *plist;
- gs_param_enumerator_t penum;
- gs_param_key_t key;
#if 0000
gs_param_list_dump(plist_orig);
@@ -329,34 +459,12 @@ gs_param_list_dump(plist_orig);
if (plist == NULL)
return_error(gs_error_VMerror);
gs_c_param_list_write(plist_c, dev->memory->non_gc_memory);
+ gs_param_list_set_persistent_keys((gs_param_list *)plist_c, false);
- param_init_enumerator(&penum);
-
- while ((code = param_get_next_key(plist_orig, &penum, &key)) == 0) {
- char *string_key;
- gs_param_typed_value value;
-
- string_key = (char *)gs_alloc_bytes(dev->memory->non_gc_memory, key.size+1, "nup_put_params");
- if (string_key == NULL) {
- code = gs_note_error(gs_error_VMerror);
- goto fail;
- }
- memcpy(string_key, key.data, key.size);
- string_key[key.size] = 0;
- if ((code = param_read_typed(plist_orig, string_key, &value)) != 0) {
- if (code > 0)
- code = gs_note_error(gs_error_unknownerror);
- break;
- }
-
- /* Rewrite any occurrences of PageUsesTransparency to be true. */
- if (strcmp(string_key, "PageUsesTransparency") == 0)
- value.value.b = 1;
-
- code = param_write_typed(plist, string_key, &value);
- if (code < 0)
- break;
- }
+ /* Bulk copy the whole list. Can't enumerate and copy without it
+ * becoming an absolute nightmare due to the stupid way we handle
+ * 'collection' objects on writing. */
+ code = param_list_copy_and_modify((gs_param_list *)plist_c, plist_orig);
if (code < 0)
goto fail;
@@ -471,8 +579,16 @@ gs_param_list_dump(plist_orig);
/* now that we've intercepted PageSize and/or MediaSize, pass the rest along */
code = default_subclass_put_params(dev, plist);
+ /* Now promote errors from the copied list to the original list. */
+ {
+ int ecode = promote_errors(plist_orig, plist);
+ if (code == 0)
+ code = ecode;
+ }
+
fail:
gs_c_param_list_release(plist_c);
+ gs_free_object(dev->memory->non_gc_memory, plist_c, "nup_put_params");
return code;
}
@@ -577,3 +693,36 @@ nup_dev_spec_op(gx_device *dev, int dev_spec_op, void *data, int size)
}
return default_subclass_dev_spec_op(dev, dev_spec_op, data, size);
}
+
+int gx_device_nup_device_install(gx_device *dev)
+{
+ gs_param_typed_value value;
+ gs_c_param_list *plist_c;
+ int code;
+
+ code = gx_device_subclass(dev, (gx_device *)&gs_nup_device, sizeof(Nup_device_subclass_data));
+ if (code < 0)
+ return code;
+
+ /* Ensure that PageUsesTransparency is set. */
+ plist_c = gs_c_param_list_alloc(dev->memory->non_gc_memory, "nup_open_device");
+ if (plist_c == NULL)
+ return_error(gs_error_VMerror);
+ gs_c_param_list_write(plist_c, dev->memory->non_gc_memory);
+ gs_param_list_set_persistent_keys((gs_param_list *)plist_c, false);
+
+ value.type = gs_param_type_bool;
+ value.value.b = 1;
+ code = param_write_typed((gs_param_list *)plist_c, "PageUsesTransparency", &value);
+ if (code >= 0) {
+ gs_c_param_list_read(plist_c);
+
+ code = default_subclass_put_params(dev, (gs_param_list *)plist_c);
+ if (code >= 0)
+ code = default_subclass_open_device(dev->child);
+ }
+ gs_c_param_list_release(plist_c);
+ gs_free_object(dev->memory->non_gc_memory, plist_c, "nup_open_device");
+
+ return code;
+}
diff --git a/base/gdevnup.h b/base/gdevnup.h
index a8b34c4bd..51cf24eb3 100644
--- a/base/gdevnup.h
+++ b/base/gdevnup.h
@@ -23,8 +23,7 @@
typedef struct gx_device_s gx_device_nup;
-/* Initialize device. */
-void gx_device_nup_device_init(gx_device_nup * dev);
+int gx_device_nup_device_install(gx_device *dev);
typedef struct {
subclass_common;
diff --git a/base/gdevprn.c b/base/gdevprn.c
index 4bc1fec35..0cfb453fc 100644
--- a/base/gdevprn.c
+++ b/base/gdevprn.c
@@ -229,6 +229,7 @@ gdev_prn_tear_down(gx_device *pdev, byte **the_memory)
*the_memory = ppdev->buf;
ppdev->buf = 0;
ppdev->buffer_space = 0;
+ pmemdev->base = 0; /* in case finalize tries to free this */
was_command_list = true;
prn_finish_bg_print(ppdev);
diff --git a/base/gscparam.c b/base/gscparam.c
index 1b4522a8f..b39344172 100644
--- a/base/gscparam.c
+++ b/base/gscparam.c
@@ -53,6 +53,7 @@ struct gs_c_param_s {
gs_c_param_value value;
gs_param_type type;
void *alternate_typed_data;
+ int error;
};
/* GC descriptor and procedures */
@@ -261,6 +262,7 @@ c_param_add(gs_c_param_list * plist, gs_param_name pkey)
}
pparam->key.size = len;
pparam->alternate_typed_data = 0;
+ pparam->error = 0;
return pparam;
}
@@ -450,6 +452,7 @@ static param_proc_xmit_typed(c_param_read_typed);
static param_proc_next_key(c_param_get_next_key);
static param_proc_get_policy(c_param_read_get_policy);
static param_proc_signal_error(c_param_read_signal_error);
+static param_proc_read_signalled_error(c_param_read_signalled_error);
static param_proc_commit(c_param_read_commit);
static const gs_param_list_procs c_read_procs =
{
@@ -461,7 +464,8 @@ static const gs_param_list_procs c_read_procs =
NULL, /* requested, N/A */
c_param_read_get_policy,
c_param_read_signal_error,
- c_param_read_commit
+ c_param_read_commit,
+ c_param_read_signalled_error
};
/* Switch a list from writing to reading. */
@@ -596,10 +600,24 @@ c_param_read_get_policy(gs_param_list * plist, gs_param_name pkey)
static int
c_param_read_signal_error(gs_param_list * plist, gs_param_name pkey, int code)
{
- return code;
+ gs_c_param_list *const cplist = (gs_c_param_list *)plist;
+ gs_c_param *pparam = c_param_find(cplist, pkey, false);
+
+ if (pparam)
+ pparam->error = code;
+
+ return 0;
}
static int
c_param_read_commit(gs_param_list * plist)
{
return 0;
}
+static int
+c_param_read_signalled_error(gs_param_list * plist, gs_param_name pkey)
+{
+ gs_c_param_list *const cplist = (gs_c_param_list *)plist;
+ gs_c_param *pparam = c_param_find(cplist, pkey, false);
+
+ return (pparam ? pparam->error : 0);
+}
diff --git a/base/gsparam.h b/base/gsparam.h
index 43cc92d82..b5303cf08 100644
--- a/base/gsparam.h
+++ b/base/gsparam.h
@@ -374,6 +374,15 @@ typedef struct gs_param_list_procs_s {
#define param_commit(plist)\
(*(plist)->procs->commit)(plist)
+ /*
+ * Read the value of a previously signalled error. (Only used when reading.)
+ */
+#define param_proc_read_signalled_error(proc)\
+ int proc(gs_param_list *, gs_param_name)
+ param_proc_read_signalled_error((*read_signalled_error));
+#define param_read_signalled_error(plist, pkey)\
+ (*(plist)->procs->read_signalled_error)(plist, pkey)
+
} gs_param_list_procs;
/* Transmit typed parameters. */
diff --git a/base/gsparamx.c b/base/gsparamx.c
index 2893253a4..8ca19a5bb 100644
--- a/base/gsparamx.c
+++ b/base/gsparamx.c
@@ -156,6 +156,8 @@ param_list_copy(gs_param_list *plto, gs_param_list *plfrom)
coll_type = gs_param_collection_array;
cc:
copy.value.d.size = value.value.d.size;
+ /* FIXME: RJW: I suspect that this will go wrong, if size == 0.
+ * We should probably spot size == 0 and break. */
if ((code = param_begin_write_collection(plto, string_key,
&copy.value.d,
coll_type)) < 0 ||
diff --git a/psi/iparam.c b/psi/iparam.c
index 269b6661d..ea6d36b0d 100644
--- a/psi/iparam.c
+++ b/psi/iparam.c
@@ -526,7 +526,8 @@ static const gs_param_list_procs ref_read_procs =
NULL, /* requested */
ref_param_read_get_policy,
ref_param_read_signal_error,
- ref_param_read_commit
+ ref_param_read_commit,
+ NULL
};
static int ref_param_read(iparam_list *, gs_param_name,
iparam_loc *, int);