summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'base/gdevnup.c')
-rw-r--r--base/gdevnup.c423
1 files changed, 282 insertions, 141 deletions
diff --git a/base/gdevnup.c b/base/gdevnup.c
index e1d79d1c..b9f4f356 100644
--- a/base/gdevnup.c
+++ b/base/gdevnup.c
@@ -86,92 +86,30 @@ static dev_proc_dev_spec_op(nup_dev_spec_op);
#define MAX_COORD (max_int_in_fixed - 1000)
#define MAX_RESOLUTION 4000
+static void
+nup_initialize_device_procs(gx_device *dev)
+{
+ default_subclass_initialize_device_procs(dev);
+
+ set_dev_proc(dev, get_initial_matrix, nup_get_initial_matrix);
+ set_dev_proc(dev, output_page, nup_output_page);
+ set_dev_proc(dev, close_device, nup_close_device);
+ set_dev_proc(dev, put_params, nup_put_params); /* to catch PageSize changes */
+ set_dev_proc(dev, fillpage, nup_fillpage);
+ set_dev_proc(dev, dev_spec_op, nup_dev_spec_op);
+}
+
const
gx_device_nup gs_nup_device =
{
/*
* Define the device as 8-bit gray scale to avoid computing halftones.
*/
- std_device_dci_type_body(gx_device_nup, 0, "N-up", &st_nup_device,
+ std_device_dci_type_body_sc(gx_device_nup, nup_initialize_device_procs,
+ "N-up", &st_nup_device,
MAX_COORD, MAX_COORD,
MAX_RESOLUTION, MAX_RESOLUTION,
- 1, 8, 255, 0, 256, 1),
- {default_subclass_open_device,
- nup_get_initial_matrix,
- default_subclass_sync_output, /* sync_output */
- nup_output_page,
- nup_close_device,
- default_subclass_map_rgb_color,
- default_subclass_map_color_rgb,
- default_subclass_fill_rectangle,
- default_subclass_tile_rectangle, /* tile_rectangle */
- default_subclass_copy_mono,
- default_subclass_copy_color,
- default_subclass_draw_line, /* draw_line */
- default_subclass_get_bits, /* get_bits */
- default_subclass_get_params,
- nup_put_params, /* to catch PageSize changes */
- default_subclass_map_cmyk_color,
- default_subclass_get_xfont_procs, /* get_xfont_procs */
- default_subclass_get_xfont_device, /* get_xfont_device */
- default_subclass_map_rgb_alpha_color,
- default_subclass_get_page_device,
- default_subclass_get_alpha_bits, /* get_alpha_bits */
- default_subclass_copy_alpha,
- default_subclass_get_band, /* get_band */
- default_subclass_copy_rop, /* copy_rop */
- default_subclass_fill_path,
- default_subclass_stroke_path,
- default_subclass_fill_mask,
- default_subclass_fill_trapezoid,
- default_subclass_fill_parallelogram,
- default_subclass_fill_triangle,
- default_subclass_draw_thin_line,
- default_subclass_begin_image,
- default_subclass_image_data, /* image_data */
- default_subclass_end_image, /* end_image */
- default_subclass_strip_tile_rectangle,
- default_subclass_strip_copy_rop,
- default_subclass_get_clipping_box, /* get_clipping_box */
- default_subclass_begin_typed_image,
- default_subclass_get_bits_rectangle, /* get_bits_rectangle */
- default_subclass_map_color_rgb_alpha,
- default_subclass_create_compositor,
- default_subclass_get_hardware_params, /* get_hardware_params */
- default_subclass_text_begin,
- default_subclass_finish_copydevice, /* finish_copydevice */
- default_subclass_begin_transparency_group, /* begin_transparency_group */
- default_subclass_end_transparency_group, /* end_transparency_group */
- default_subclass_begin_transparency_mask, /* begin_transparency_mask */
- default_subclass_end_transparency_mask, /* end_transparency_mask */
- default_subclass_discard_transparency_layer, /* discard_transparency_layer */
- default_subclass_get_color_mapping_procs, /* get_color_mapping_procs */
- default_subclass_get_color_comp_index, /* get_color_comp_index */
- default_subclass_encode_color, /* encode_color */
- default_subclass_decode_color, /* decode_color */
- default_subclass_pattern_manage, /* pattern_manage */
- default_subclass_fill_rectangle_hl_color, /* fill_rectangle_hl_color */
- default_subclass_include_color_space, /* include_color_space */
- default_subclass_fill_linear_color_scanline, /* fill_linear_color_scanline */
- default_subclass_fill_linear_color_trapezoid, /* fill_linear_color_trapezoid */
- default_subclass_fill_linear_color_triangle, /* fill_linear_color_triangle */
- default_subclass_update_spot_equivalent_colors, /* update_spot_equivalent_colors */
- default_subclass_ret_devn_params, /* ret_devn_params */
- nup_fillpage, /* fillpage */
- default_subclass_push_transparency_state, /* push_transparency_state */
- default_subclass_pop_transparency_state, /* pop_transparency_state */
- default_subclass_put_image, /* put_image */
- nup_dev_spec_op, /* for GetParam of PdfmarkCapable */
- default_subclass_copy_planes, /* copy_planes */
- default_subclass_get_profile, /* get_profile */
- default_subclass_set_graphics_type_tag, /* set_graphics_type_tag */
- default_subclass_strip_copy_rop2,
- default_subclass_strip_tile_rect_devn,
- default_subclass_copy_alpha_hl_color,
- default_subclass_process_page,
- default_subclass_transform_pixel_region,
- default_subclass_fill_stroke_path,
- }
+ 1, 8, 255, 0, 256, 1, NULL, NULL, NULL)
};
#undef MAX_COORD
@@ -291,7 +229,7 @@ nup_set_children_MediaSize(gx_device *dev, float PageW, float PageH)
}
static int
-nup_flush_nest_to_output(gx_device *dev, Nup_device_subclass_data *pNup_data, bool flush)
+nup_flush_nest_to_output(gx_device *dev, Nup_device_subclass_data *pNup_data)
{
int code = 0;
@@ -315,7 +253,7 @@ nup_close_device(gx_device *dev)
return code;
if (pNup_data->PageCount > 0)
- acode = nup_flush_nest_to_output(dev, pNup_data, true);
+ acode = nup_flush_nest_to_output(dev, pNup_data);
/* Reset the Nup control data */
/* NB: the data will be freed from non_gc_memory by the finalize function */
@@ -327,34 +265,6 @@ nup_close_device(gx_device *dev)
return min(code, acode);
}
- /*
- * Template:
- * BEGIN_ARRAY_PARAM(param_read_xxx_array, "pname", pxxa, size, pxxe) {
- * ... check value if desired ...
- * if (success)
- * break;
- * ... set ecode ...
- * } END_ARRAY_PARAM(pxxa, pxxe);
- */
-
-#define BEGIN_ARRAY_PARAM(pread, pname, pa, psize, e)\
- BEGIN\
- switch (code = pread(plist, (param_name = pname), &(pa))) {\
- case 0:\
- if ((pa).size != psize) {\
- ecode = gs_note_error(gs_error_rangecheck);\
- (pa).data = 0; /* mark as not filled */\
- } else
-#define END_ARRAY_PARAM(pa, e)\
- goto e;\
- default:\
- ecode = code;\
-e: param_signal_error(plist, param_name, ecode);\
- case 1:\
- (pa).data = 0; /* mark as not filled */\
- }\
- END
-
/* Read .MediaSize or, if supported as a synonym, PageSize. */
static int
param_MediaSize(gs_param_list * plist, gs_param_name pname,
@@ -364,26 +274,171 @@ param_MediaSize(gs_param_list * plist, gs_param_name pname,
int ecode = 0;
int code;
- BEGIN_ARRAY_PARAM(param_read_float_array, pname, *pa, 2, mse) {
- float width_new = pa->data[0] * res[0] / 72;
- float height_new = pa->data[1] * res[1] / 72;
-
- if (width_new < 0 || height_new < 0)
- ecode = gs_note_error(gs_error_rangecheck);
+ switch (code = param_read_float_array(plist, (param_name = pname), pa)) {
+ case 0:
+ if (pa->size != 2) {
+ ecode = gs_note_error(gs_error_rangecheck);
+ pa->data = 0; /* mark as not filled */
+ } else {
+ float width_new = pa->data[0] * res[0] / 72;
+ float height_new = pa->data[1] * res[1] / 72;
+
+ if (width_new < 0 || height_new < 0)
+ ecode = gs_note_error(gs_error_rangecheck);
#define max_coord (max_fixed / fixed_1)
#if max_coord < max_int
- else if (width_new > (long)max_coord || height_new > (long)max_coord)
- ecode = gs_note_error(gs_error_limitcheck);
+ else if (width_new > (long)max_coord || height_new > (long)max_coord)
+ ecode = gs_note_error(gs_error_limitcheck);
#endif
#undef max_coord
- else
- break;
- } END_ARRAY_PARAM(*pa, mse);
+ else
+ break;
+ }
+ goto err;
+ default:
+ ecode = code;
+err: param_signal_error(plist, param_name, ecode);
+ /* fall through */
+ case 1:
+ pa->data = 0; /* mark as not filled */
+ }
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
-nup_put_params(gx_device *dev, gs_param_list * plist)
+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)
{
int code, ecode = 0;
gs_param_float_array msa;
@@ -392,11 +447,29 @@ nup_put_params(gx_device *dev, gs_param_list * plist)
gs_param_string nuplist;
Nup_device_subclass_data* pNup_data = dev->subclass_data;
gx_device *next_dev;
+ gs_c_param_list *plist_c;
+ gs_param_list *plist;
#if 0000
-gs_param_list_dump(plist);
+gs_param_list_dump(plist_orig);
#endif
+ plist_c = gs_c_param_list_alloc(dev->memory->non_gc_memory, "nup_put_params");
+ plist = (gs_param_list *)plist_c;
+ 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);
+
+ /* 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;
+
+ gs_c_param_list_read(plist_c);
+
code = param_read_string(plist, "NupControl", &nuplist);
if (code < 0)
ecode = code;
@@ -406,7 +479,7 @@ gs_param_list_dump(plist);
(strncmp(dev->NupControl->nupcontrol_str, (const char *)nuplist.data, nuplist.size) != 0))) {
/* If we have accumulated a nest when the NupControl changes, flush the nest */
if (pNup_data->PagesPerNest > 1 && pNup_data->PageCount > 0)
- code = nup_flush_nest_to_output(dev, pNup_data, true);
+ code = nup_flush_nest_to_output(dev, pNup_data);
if (code < 0)
ecode = code;
/* There was a NupControl, but this one is different -- no longer use the old one */
@@ -416,15 +489,18 @@ gs_param_list_dump(plist);
if (dev->NupControl == NULL && nuplist.size > 0) {
dev->NupControl = (gdev_nupcontrol *)gs_alloc_bytes(dev->memory->non_gc_memory,
sizeof(gdev_nupcontrol), "structure to hold nupcontrol_str");
- if (dev->NupControl == NULL)
- return gs_note_error(gs_error_VMerror);
+ if (dev->NupControl == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
dev->NupControl->nupcontrol_str = (void *)gs_alloc_bytes(dev->memory->non_gc_memory,
nuplist.size + 1, "nupcontrol string");
if (dev->NupControl->nupcontrol_str == NULL){
gs_free(dev->memory->non_gc_memory, dev->NupControl, 1, sizeof(gdev_nupcontrol),
"free structure to hold nupcontrol string");
dev->NupControl = 0;
- return gs_note_error(gs_error_VMerror);
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
}
memset(dev->NupControl->nupcontrol_str, 0x00, nuplist.size + 1);
memcpy(dev->NupControl->nupcontrol_str, nuplist.data, nuplist.size);
@@ -446,17 +522,21 @@ gs_param_list_dump(plist);
rc_increment(next_dev->NupControl);
next_dev = next_dev->parent;
}
- if (ecode < 0)
- return ecode;
+ if (ecode < 0) {
+ code = ecode;
+ goto fail;
+ }
}
code = ParseNupControl(dev, pNup_data); /* update the nesting params */
if (code < 0)
- return code;
+ goto fail;
/* If nesting is now off, just pass params on to children devices */
- if (pNup_data->PagesPerNest == 1)
- return default_subclass_put_params(dev, plist);
+ if (pNup_data->PagesPerNest == 1) {
+ code = default_subclass_put_params(dev, plist);
+ goto fail; /* Not actually failing! */
+ }
/* .MediaSize takes precedence over PageSize, so we read PageSize first. */
code = param_MediaSize(plist, "PageSize", res, &msa);
@@ -468,34 +548,54 @@ gs_param_list_dump(plist);
code = param_MediaSize(plist, ".MediaSize", res, &msa);
if (code < 0)
ecode = code;
- else if (msa.data == 0)
+ else if (msa.data == NULL)
msa.data = data;
- if (ecode < 0)
- return ecode;
+ if (ecode < 0) {
+ code = ecode;
+ goto fail;
+ }
/* If there was PageSize or .MediaSize, update the NestedPage size */
- if (msa.data != 0) {
+ if (msa.data != NULL) {
Nup_device_subclass_data *pNup_data = dev->subclass_data;
+ /* Calculate the page sizes as ints to allow for tiny changes
+ * of width that don't actually make a difference. */
+ int w1 = (int)(pNup_data->NestedPageW * dev->HWResolution[0] / 72.0f + 0.5f);
+ int w2 = (int)(msa.data[0] * dev->HWResolution[0] / 72.0f + 0.5f);
+ int h1 = (int)(pNup_data->NestedPageH * dev->HWResolution[1] / 72.0f + 0.5f);
+ int h2 = (int)(msa.data[1] * dev->HWResolution[1] / 72.0f + 0.5f);
/* FIXME: Handle changing size (if previous value was non-zero) */
- if (msa.data[0] != pNup_data->NestedPageW || msa.data[1] != pNup_data->NestedPageH) {
+ if (w1 != w2 || h1 != h2) {
/* If needed, flush previous nest before changing */
if (pNup_data->PageCount > 0 && pNup_data->PagesPerNest > 1) {
- code = nup_flush_nest_to_output(dev, pNup_data, true);
+ code = nup_flush_nest_to_output(dev, pNup_data);
if (code < 0)
- return code;
+ goto fail;
}
pNup_data->NestedPageW = msa.data[0];
pNup_data->NestedPageH = msa.data[1];
/* And update the Nup parameters based on the updated PageSize */
code = ParseNupControl(dev, pNup_data);
if (code < 0)
- return code;
+ goto fail;
}
}
/* 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;
}
@@ -519,11 +619,19 @@ nup_output_page(gx_device *dev, int num_copies, int flush)
}
/* FIXME: Handle num_copies > 1 */
+
+ /* pNup_data holds the number of 'sub pages' we have produced,
+ * so update that. dev->PageCount holds the number of 'actual'
+ * pages we've output, so only increment that if we really
+ * do an output. */
pNup_data->PageCount++;
- dev->PageCount++;
dev->ShowpageCount = dev->child->ShowpageCount;
- if (pNup_data->PageCount >= pNup_data->PagesPerNest)
- code = nup_flush_nest_to_output(dev, pNup_data, flush);
+ if (pNup_data->PageCount >= pNup_data->PagesPerNest) {
+ code = nup_flush_nest_to_output(dev, pNup_data);
+ /* Increment this afterwards, in case the child accesses
+ * the value to fill in a %d in the filename. */
+ dev->PageCount++;
+ }
return code;
}
@@ -591,3 +699,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;
+}