[gs-commits] ghostpdl branch, pdfi, updated. jbig2dec-0.14-2551-gad96b0e
[email protected] (Ken Sharp) Wed, 20 Nov 2019 10:05:24 +0000 (UTC)
| Newsgroups | gmane.comp.printing.ghostscript.cvs |
|---|---|
| Message-ID | <[email protected]> |
The ghostpdl branch, pdfi has been updated
via ad96b0e2ee0ee50530ceb217ae57894791d11052 (commit)
via c09fbcd400e41d621659fd9323031b157f6937a8 (commit)
via c5eb242fa82585aa21371919ca4a7a2d4d4effc3 (commit)
via 69bdf79e10370ed16a17a346690fd630272f7346 (commit)
via b5956ac8be8c923ad9e9fd3045724a5579cf4dce (commit)
via 0329372164611fdff22f8c02201ff1fbbf94aae7 (commit)
via cd8747382f47393efd0f0013700a8294c0ded15c (commit)
via e1b7dbc95945ef2afa38faeebfda56830f2762ab (commit)
via e93430d762400247cdc4550be403a1f7c291112d (commit)
via bb2c2fbbc96b1f2de8e454ecb2adf3e58ddb52c3 (commit)
via a4c44cc7579ab80a1d49d721fad48e1ed9bbe976 (commit)
via f804ebfa526bd36a808319225488fe9de416f350 (commit)
via 7ff87e872888da0f5cfd392b3f8b90fa7ad0e27c (commit)
via 7e50bda19813d11555ff1ab4f7cb5c3e55ff0653 (commit)
via 05cfc3afb00404ddfd3c1eac635f369491995e5b (commit)
via 81e179af142575eab21633b2023e752e1e244c9a (commit)
via d57aff5bbdb206f00e4b0cb3b324616be10d358e (commit)
via 3aa36ebc3df83d339695fff58a73332d5304c47d (commit)
via 4a1a4e8ad165e678d3affab483460ec8b926d5dd (commit)
via 1ee350a81d5799785ab53b3eece6e853cc4c607b (commit)
via 884690a576e16043e674efb9d047a00760ac8443 (commit)
via 9763a006f7c566e92f60e799fb2270c09807e9c8 (commit)
via dd84fd3a36d0ef809ed697ed12f1bb04655a0388 (commit)
via a631e0d40337da58ba596a9499b71c1d7888c9d5 (commit)
via 6fbd278d11e9ea567ea2dce0c7aa02e706d9a9e2 (commit)
via e05d0e17c75adab181413c8bf5bf6760c76683e4 (commit)
via da03855bf9ca18eab05d4ac870d73f457758a77f (commit)
via 0aa9d4f8237f7a0b459fc567e76ab8f0c4c2eebd (commit)
via ea274a722040b1186374a96d61715ae96d58c36b (commit)
via 3a913d9dc801508b19a8d5605e785852e1bfb082 (commit)
via 8bf3152fe37368b49dcd23535a407c632d7157d2 (commit)
via 5a4c29eeea1f0b63cc0b2fff3a1ca0f5b2d143b6 (commit)
via 7fc7881b00a43fe1017e593846a0baf28e8837db (commit)
via 161adc70e0d6625618c5a6afcb5e04161acc2e2c (commit)
via b8de03bdb8696517bff0259e827ece4f5746663c (commit)
via 58bee0ad2c67888a52fa02e31de3120673525fda (commit)
via 372e4f29d3fcbfeaa83a5ee8cc530cbb948edab7 (commit)
via 194c53ed02d657159a7be894b6d4b54ce1f435d4 (commit)
via 83c6e520ee6eddeaa1e548e519eb1b42941e3153 (commit)
via 76c088b2b5468c1551c4636dc9d20f5b1ea5da36 (commit)
via b51e2be557128054b0f1e8f6eb4c3b7de506cca2 (commit)
via dd95eb235f00a600f8b8b99b8929e597283fa96c (commit)
via 95706a3ced4f1cfa50b2ea48f0095ed9423bafe5 (commit)
via 5f24a32f0cc4513dfb9825900d561b3a6771b87c (commit)
via de9dc99614f86e4aaa0a447766a58447e76ca8c1 (commit)
via 0ea433a1a172298eda11203d698ce6fe69be58d2 (commit)
via 51aff033bdc8744adfd86ce8b35ef00f23621de2 (commit)
via 33f4f0c0ba5449fd535061e785090bc57849ae51 (commit)
via 98a8b291b135d49ffca23d9a289786971cf33a12 (commit)
via 91b104936ec55b3f1f22673d7e286ff67e4e2475 (commit)
via af558a708b40a78eef9c0bc5b2ae229d771a3537 (commit)
via f63270230f329c6e89375af18d2bf2930421174a (commit)
via 97277fcf0f70d482481ac329ce473e5687e7a935 (commit)
via 6690c60c3e1be875134150fa4909bff0de7d583f (commit)
via 9e1d2f76cabf84576424af0c38dceef5881c2b21 (commit)
via e6d0fcd87934f01d4fb3c3029558fea134fbd086 (commit)
via d11a43b64a2e623aebdfa34f8ee992887dc5b2b2 (commit)
via 24ec06a27df63297796a379c95ee5d4b39040410 (commit)
via 592fb032180aac0fc22a7c860ef116b38da8ca71 (commit)
via 7da60037b788bec4217f6ee7017e238f541714ae (commit)
via 658e14023fa84232c569ecc5528b77fd35bcf290 (commit)
via 11f8439f49a3704bb4506e02382a0e697e5edf8e (commit)
via a1747b9267b4aac28e256820c5cea37af299af86 (commit)
via 37a322fb93c5cd1e96a4cad2ffccdc397f7c0ea5 (commit)
via aad7bcab6a3aedc4e2bfecd7b1e6bf893c299d0e (commit)
via 2de09275898086e903a19812fd78e73381f7e71e (commit)
via c198ac5dcb26fadc53074525cabb413e5395cdba (commit)
via 7174b69e6c0027277fe1a9ece341c3380fe7979d (commit)
via ba5b47dc979ca1590f280f482d432705e9b535bf (commit)
via 4a3401e5e4f5828c67d7c65f77a3bb0106753649 (commit)
via f44249d69a36fa7233504bdb137af9ff5008ec37 (commit)
via 2029235dc8997314c1925e5786212a6c7331f0cf (commit)
via 5605c6c6a9677013560a6a3c947f4b100eae4e09 (commit)
via 95151ae3c3c63100bc7ae958f59b8096e7a0f211 (commit)
via 0f7653fd4f1d1c5bf36719efcd8464eafcd46faf (commit)
via 6882df85d37819d849476cf2b4822a90e64d9b24 (commit)
via b7fac758d046c82d65fa69dd2c4bc0f1cf9712bc (commit)
via 328d27b3cccc89c4ff60529106489a9a635f0f72 (commit)
via 68aeff88e95ccfd1af430fe180597d624ad9f47e (commit)
via 713645137a2fc483e5509bfd0f92d7311faeb614 (commit)
via 6c3af6e12e4448b519d2b48c2047c0c83b3cf082 (commit)
via 66878fb30f37b06a532fdce39991f31642cdb09b (commit)
via 76db3df8090127482960b742f15831e59816a05e (commit)
via 9caccf9f09e06324f07822cc55e1737d881c6eb3 (commit)
via 018003614ad80910ca2c009eb1d523e8015756bc (commit)
from 70d25d8ffdd55c6c57e50541e68772b4bc341196 (commit)
----------------------------------------------------------------------
commit ad96b0e2ee0ee50530ceb217ae57894791d11052
Author: Ken Sharp <[email protected]>
Date: Wed Nov 20 09:39:18 2019 +0000
squash into prior
diff --git a/gpdl/jpgtop.c b/gpdl/jpgtop.c
new file mode 100644
index 0000000..c2b46ed
--- /dev/null
+++ b/gpdl/jpgtop.c
@@ -0,0 +1,869 @@
+/* Copyright (C) 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.
+*/
+
+/* jpgtop.c */
+/* Top-level API implementation of "JPG" Language Interface */
+
+#include "pltop.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gsstate.h"
+#include "strimpl.h"
+#include "gscoord.h"
+#include "jpeglib.h"
+#include "setjmp_.h"
+#include "sjpeg.h"
+
+/* Forward decls */
+
+/************************************************************/
+/******** Language wrapper implementation (see pltop.h) *****/
+/************************************************************/
+
+typedef enum
+{
+ ii_state_identifying = 0,
+ ii_state_jpeg,
+ ii_state_jpeg_header,
+ ii_state_jpeg_start,
+ ii_state_jpeg_rows,
+ ii_state_jpeg_finish,
+ ii_state_flush
+} ii_state;
+
+typedef struct {
+ struct jpeg_error_mgr pub;
+ jmp_buf setjmp_buffer;
+} jpg_error_mgr;
+
+/*
+ * Jpg interpreter instance
+ */
+typedef struct jpg_interp_instance_s {
+ gs_memory_t *memory;
+ gs_memory_t *cmemory;
+ gx_device *dev;
+ gx_device *nulldev;
+
+ gs_color_space *gray;
+ gs_color_space *rgb;
+ gs_color_space *cmyk;
+
+ /* Jpg parser state machine */
+ ii_state state;
+
+ int pages;
+
+ uint8_t bpp;
+ uint8_t cs;
+ uint32_t width;
+ uint32_t height;
+ uint32_t xresolution;
+ uint32_t yresolution;
+
+ uint32_t num_comps;
+ uint32_t byte_width;
+ uint32_t y;
+
+ uint32_t bytes_available_on_entry;
+
+ gs_image_t image;
+ gs_image_enum *penum;
+ gs_gstate *pgs;
+
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_source_mgr jsrc;
+ size_t bytes_to_skip;
+ jpg_error_mgr jerr;
+
+ byte *samples;
+
+} jpg_interp_instance_t;
+
+static int
+jpg_detect_language(const char *s, int len)
+{
+ const byte *hdr = (const byte *)s;
+ if (len >= 11) {
+ if (hdr[0] == 0xFF && hdr[1] == 0xd8 && hdr[2] == 0xff && hdr[3] == 0xe0 &&
+ strncmp("JFIF", s+6, 5) == 0)
+ return 100;
+ }
+ /* FIXME: Other formats go here */
+
+ return 0;
+}
+
+static const pl_interp_characteristics_t jpg_characteristics = {
+ "JPG",
+ jpg_detect_language,
+ "Artifex",
+ "0.01",
+ "11 Nov 2019",
+ 1 /* minimum input size */
+};
+
+/* GS's fakakta jpeg integration insists on putting a
+ * memory structure pointer in the decompress structs client data.
+ * This is no good to find our instance from. We therefore find
+ * it by offsetting back from the address of the cinfo. This is
+ * a nasty bit of casting, so wrap it in a macro. */
+#define JPG_FROM_CINFO(CINFO) \
+ (jpg_interp_instance_t *)(((char *)(CINFO))-offsetof(jpg_interp_instance_t,cinfo))
+
+
+/* Get implementation's characteristics */
+static const pl_interp_characteristics_t * /* always returns a descriptor */
+jpg_impl_characteristics(const pl_interp_implementation_t *impl) /* implementation of interpreter to alloc */
+{
+ return &jpg_characteristics;
+}
+
+static void
+jpg_deallocate(jpg_interp_instance_t *jpg)
+{
+ if (jpg == NULL)
+ return;
+
+ rc_decrement_cs(jpg->gray, "jpg_deallocate");
+ rc_decrement_cs(jpg->rgb, "jpg_deallocate");
+ rc_decrement_cs(jpg->cmyk, "jpg_deallocate");
+
+ if (jpg->pgs != NULL)
+ gs_gstate_free_chain(jpg->pgs);
+ gs_free_object(jpg->memory, jpg, "jpg_impl_allocate_interp_instance");
+}
+
+/* Deallocate a interpreter instance */
+static int
+jpg_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
+{
+ jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
+
+ jpg_deallocate(jpg);
+ impl->interp_client_data = NULL;
+
+ return 0;
+}
+
+/* Do per-instance interpreter allocation/init. */
+static int
+jpg_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem)
+{
+ int code;
+ jpg_interp_instance_t *jpg
+ = (jpg_interp_instance_t *)gs_alloc_bytes(mem,
+ sizeof(jpg_interp_instance_t),
+ "jpg_impl_allocate_interp_instance");
+ if (!jpg)
+ return_error(gs_error_VMerror);
+ memset(jpg, 0, sizeof(*jpg));
+
+ jpg->memory = mem;
+ jpg->pgs = gs_gstate_alloc(mem);
+ if (jpg->pgs == NULL)
+ goto failVM;
+
+ /* Push one save level onto the stack to assuage the memory handling */
+ code = gs_gsave(jpg->pgs);
+ if (code < 0)
+ goto fail;
+
+ code = gsicc_init_iccmanager(jpg->pgs);
+ if (code < 0)
+ goto fail;
+
+ jpg->gray = gs_cspace_new_ICC(mem, jpg->pgs, 1);
+ jpg->rgb = gs_cspace_new_ICC(mem, jpg->pgs, 3);
+ jpg->cmyk = gs_cspace_new_ICC(mem, jpg->pgs, 4);
+
+ impl->interp_client_data = jpg;
+
+ return 0;
+
+failVM:
+ code = gs_note_error(gs_error_VMerror);
+fail:
+ (void)jpg_deallocate(jpg);
+ return code;
+}
+
+/*
+ * Get the allocator with which to allocate a device
+ */
+static gs_memory_t *
+jpg_impl_get_device_memory(pl_interp_implementation_t *impl)
+{
+ jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
+
+ return jpg->dev ? jpg->dev->memory : NULL;
+}
+
+#if 0 /* UNUSED */
+static int
+jpg_impl_set_param(pl_interp_implementation_t *impl,
+ pl_set_param_type type,
+ const char *param,
+ const void *val)
+{
+ jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
+
+ /* No params set here */
+ return 0;
+}
+
+static int
+jpg_impl_add_path(pl_interp_implementation_t *impl,
+ const char *path)
+{
+ jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
+
+ /* No paths to add */
+ return 0;
+}
+
+static int
+jpg_impl_post_args_init(pl_interp_implementation_t *impl)
+{
+ jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
+
+ /* No post args processing */
+ return 0;
+}
+#endif
+
+/* Prepare interp instance for the next "job" */
+static int
+jpg_impl_init_job(pl_interp_implementation_t *impl,
+ gx_device *device)
+{
+ jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
+
+ jpg->dev = device;
+ jpg->state = ii_state_identifying;
+
+ return 0;
+}
+
+#if 0 /* UNUSED */
+static int
+jpg_impl_run_prefix_commands(pl_interp_implementation_t *impl,
+ const char *prefix)
+{
+ jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
+
+ return 0;
+}
+
+static int
+jpg_impl_process_file(pl_interp_implementation_t *impl, const char *filename)
+{
+ jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
+
+ return 0;
+}
+#endif
+
+/* Do any setup for parser per-cursor */
+static int /* ret 0 or +ve if ok, else -ve error code */
+jpg_impl_process_begin(pl_interp_implementation_t * impl)
+{
+ jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
+
+ return 0;
+}
+
+/* Ensure we have 'required' bytes to read, and further ensure
+ * that we have no UEL's within those bytes. */
+static int
+ensure_bytes(jpg_interp_instance_t *jpg, stream_cursor_read *pr, int required)
+{
+ int n;
+ const uint8_t *p = pr->ptr+1;
+ const uint8_t *q;
+ int avail;
+
+ /* Find out how many bytes we need to check */
+ n = pr->limit - pr->ptr;
+ if (n > required)
+ n = required;
+
+ /* Make sure there are no UELs in that block */
+ q = p + n;
+ while (p != q) {
+ while (p != q && *p != '\033')
+ p++;
+ if (p == q)
+ break;
+ avail = pr->limit - pr->ptr;
+ if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
+ /* At least a partial match to a UEL */
+ return avail < 9 ? gs_error_NeedInput : gs_error_InterpreterExit;
+ }
+ p++;
+ }
+
+ /* If we have enough bytes, great, if not, get some more */
+ return (n < required) ? gs_error_NeedInput : 0;
+}
+
+static int
+flush_to_uel(stream_cursor_read *pr)
+{
+ const uint8_t *p = pr->ptr+1;
+ const uint8_t *q = pr->limit+1;
+ int avail;
+
+ while (p != q) {
+ while (p != q && *p != '\033')
+ p++;
+ if (p == q)
+ break;
+ avail = pr->limit - pr->ptr;
+ if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
+ /* At least a partial match to a UEL. Bin everything to
+ * the start of the match. */
+ pr->ptr = p-1;
+ if (avail == 9) /* Complete match. Exit! */
+ return gs_error_InterpreterExit;
+ /* Partial match. Get more data. */
+ return gs_error_NeedInput;
+ }
+ p++;
+ }
+
+ pr->ptr = pr->limit;
+
+ return 0;
+}
+
+static int
+bytes_until_uel(const stream_cursor_read *pr)
+{
+ const uint8_t *p = pr->ptr+1;
+ const uint8_t *q = pr->limit+1;
+ int avail;
+
+ while (p != q) {
+ while (p != q && *p != '\033')
+ p++;
+ if (p == q)
+ break;
+ avail = pr->limit - pr->ptr;
+ if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
+ /* At least a partial match to a UEL. Everything up to
+ * the start of the match is up for grabs. */
+ return p - (pr->ptr+1);
+ }
+ p++;
+ }
+
+ return pr->limit - pr->ptr;
+}
+
+static int
+get32be(stream_cursor_read *pr)
+{
+ int v = pr->ptr[1] << 24;
+ v |= pr->ptr[2] << 16;
+ v |= pr->ptr[3] << 8;
+ v |= pr->ptr[4];
+ pr->ptr += 4;
+
+ return v;
+}
+
+static int
+get8(stream_cursor_read *pr)
+{
+ return *++(pr->ptr);
+}
+
+static void
+jpg_jpeg_init_source(j_decompress_ptr cinfo)
+{
+ /* We've already inited the source */
+}
+
+static boolean
+jpg_fill_input_buffer(j_decompress_ptr cinfo)
+{
+ return FALSE; /* We've filled the buffer already */
+}
+
+static void
+jpg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
+{
+ jpg_interp_instance_t *jpg = JPG_FROM_CINFO(cinfo);
+ size_t n = cinfo->src->bytes_in_buffer;
+
+ if (n > num_bytes)
+ n = num_bytes;
+ jpg->jsrc.next_input_byte += num_bytes;
+ jpg->jsrc.bytes_in_buffer -= n;
+}
+
+static boolean
+jpg_resync_to_restart(j_decompress_ptr cinfo, int desired)
+{
+ return FALSE;
+}
+
+static void
+jpg_term_source(j_decompress_ptr cinfo)
+{
+}
+
+static void
+jpg_error_exit(j_common_ptr cinfo)
+{
+ jpg_error_mgr *jerr = (jpg_error_mgr *)cinfo->err;
+
+ longjmp(jerr->setjmp_buffer, 1);
+}
+
+static int
+fill_jpeg_source(jpg_interp_instance_t *jpg, stream_cursor_read * pr)
+{
+ size_t n = pr->limit - pr->ptr;
+ size_t skip = jpg->bytes_to_skip;
+
+ /* Skip any bytes we are supposed to be skipping. */
+ if (skip > 0) {
+ if (skip > n)
+ skip = n;
+ pr->ptr += skip;
+ n -= skip;
+ jpg->bytes_to_skip -= skip;
+ if (jpg->bytes_to_skip != 0)
+ return 1; /* Still more to skip */
+ }
+
+ /* Set up for the call into the jpeg lib */
+ jpg->jsrc.next_input_byte = pr->ptr+1;
+ jpg->jsrc.bytes_in_buffer = pr->limit - pr->ptr;
+ jpg->bytes_available_on_entry = jpg->jsrc.bytes_in_buffer;
+
+ return 0;
+}
+
+static int
+consume_jpeg_data(jpg_interp_instance_t *jpg, stream_cursor_read *pr)
+{
+ size_t bytes_read = jpg->jsrc.next_input_byte - (pr->ptr+1);
+ size_t n = pr->limit - pr->ptr;
+
+ if (n > bytes_read)
+ n = bytes_read;
+ pr->ptr += n;
+ bytes_read -= n;
+
+ /* We need to skip the next bytes_read bytes */
+ jpg->bytes_to_skip = bytes_read;
+
+ return (pr->limit - pr->ptr == jpg->bytes_available_on_entry);
+}
+
+static int
+jpg_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr)
+{
+ jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
+ int code = 0;
+ int need_more_data;
+
+ do
+ {
+ need_more_data = 0;
+ switch(jpg->state)
+ {
+ case ii_state_identifying:
+ {
+ const byte *hdr;
+ /* Try and get us 11 bytes */
+ code = ensure_bytes(jpg, pr, 11);
+ if (code < 0)
+ return code;
+ hdr = pr->ptr+1;
+ if (hdr[0] == 0xFF && hdr[1] == 0xd8 && hdr[2] == 0xff && hdr[3] == 0xe0 &&
+ memcmp("JFIF", hdr+6, 5) == 0)
+ {
+ jpg->state = ii_state_jpeg;
+ break;
+ }
+ jpg->state = ii_state_flush;
+ break;
+ }
+ case ii_state_jpeg:
+ code = gs_jpeg_mem_init(jpg->memory, (j_common_ptr)&jpg->cinfo);
+ if (code < 0) {
+ jpg->state = ii_state_flush;
+ break;
+ }
+
+ jpg->cinfo.err = jpeg_std_error(&jpg->jerr.pub);
+ jpg->jerr.pub.error_exit = jpg_error_exit;
+ if (setjmp(jpg->jerr.setjmp_buffer)) {
+ jpg->state = ii_state_flush;
+ break;
+ }
+
+ jpeg_create_decompress(&jpg->cinfo);
+
+ jpg->cinfo.src = &jpg->jsrc;
+ jpg->jsrc.init_source = jpg_jpeg_init_source;
+ jpg->jsrc.fill_input_buffer = jpg_fill_input_buffer;
+ jpg->jsrc.skip_input_data = jpg_skip_input_data;
+ jpg->jsrc.resync_to_restart = jpg_resync_to_restart;
+ jpg->jsrc.term_source = jpg_term_source;
+
+ jpg->state = ii_state_jpeg_header;
+ break;
+ case ii_state_jpeg_header:
+ {
+ int ok;
+ gs_color_space *cs;
+ float scale, xext, yext, xoffset, yoffset;
+
+ if (fill_jpeg_source(jpg, pr)) {
+ need_more_data = 1;
+ break; /* No bytes left after skipping */
+ }
+ if (setjmp(jpg->jerr.setjmp_buffer)) {
+ jpeg_destroy_decompress(&jpg->cinfo);
+ jpg->state = ii_state_flush;
+ break;
+ }
+ ok = jpeg_read_header(&jpg->cinfo, TRUE);
+ need_more_data = consume_jpeg_data(jpg, pr);
+ if (ok == JPEG_SUSPENDED)
+ break;
+ need_more_data = 0;
+
+ jpg->width = jpg->cinfo.image_width;
+ jpg->height = jpg->cinfo.image_height;
+ jpg->num_comps = jpg->cinfo.num_components;
+ jpg->bpp = 8 * jpg->num_comps;
+ switch(jpg->num_comps) {
+ default:
+ case 1:
+ cs = jpg->gray;
+ jpg->cinfo.out_color_space = JCS_GRAYSCALE;
+ break;
+ case 3:
+ cs = jpg->rgb;
+ jpg->cinfo.out_color_space = JCS_RGB;
+ break;
+ case 4:
+ cs = jpg->cmyk;
+ jpg->cinfo.out_color_space = JCS_CMYK;
+ break;
+ }
+
+ /* Find us some X and Y resolutions */
+ jpg->xresolution = jpg->cinfo.X_density;
+ jpg->yresolution = jpg->cinfo.Y_density;
+ if (jpg->xresolution == 0)
+ jpg->xresolution = jpg->yresolution;
+ if (jpg->yresolution == 0)
+ jpg->yresolution = jpg->xresolution;
+ if (jpg->xresolution == 0)
+ jpg->xresolution = 72;
+ if (jpg->yresolution == 0)
+ jpg->yresolution = 72;
+
+ /* Scale to fit, if too large. */
+ scale = 1.0f;
+ if (jpg->width * jpg->dev->HWResolution[0] > jpg->dev->width * jpg->xresolution)
+ scale = ((float)jpg->dev->width * jpg->xresolution) / (jpg->width * jpg->dev->HWResolution[0]);
+ if (scale * jpg->height * jpg->dev->HWResolution[1] > jpg->dev->height * jpg->yresolution)
+ scale = ((float)jpg->dev->height * jpg->yresolution) / (jpg->height * jpg->dev->HWResolution[1]);
+
+ /* Centre - Extents and offsets are all calculated in points (1/72 of an inch) */
+ xext = ((float)jpg->width * 72 * scale / jpg->xresolution);
+ xoffset = (jpg->dev->width * 72 / jpg->dev->HWResolution[0] - xext)/2;
+ yext = ((float)jpg->height * 72 * scale / jpg->yresolution);
+ yoffset = (jpg->dev->height * 72 / jpg->dev->HWResolution[1] - yext)/2;
+
+ /* Now we've got the data from the image header, we can
+ * make the gs image instance */
+ jpg->byte_width = (jpg->bpp>>3)*jpg->width;
+
+ jpg->nulldev = gs_currentdevice(jpg->pgs);
+ rc_increment(jpg->nulldev);
+ code = gs_setdevice_no_erase(jpg->pgs, jpg->dev);
+ if (code < 0) {
+ jpg->state = ii_state_flush;
+ break;
+ }
+ gs_initmatrix(jpg->pgs);
+
+ /* By default the ctm is set to:
+ * xres/72 0
+ * 0 -yres/72
+ * 0 dev->height * yres/72
+ * i.e. it moves the origin from being top right to being bottom left.
+ * We want to move it back, as without this, the image will be displayed
+ * upside down.
+ */
+ code = gs_translate(jpg->pgs, 0.0, jpg->dev->height * 72 / jpg->dev->HWResolution[1]);
+ if (code >= 0)
+ code = gs_translate(jpg->pgs, xoffset, -yoffset);
+ if (code >= 0)
+ code = gs_scale(jpg->pgs, scale, -scale);
+ /* At this point, the ctm is set to:
+ * xres/72 0
+ * 0 yres/72
+ * 0 0
+ */
+ if (code >= 0)
+ code = gs_erasepage(jpg->pgs);
+ if (code < 0) {
+ jpg->state = ii_state_flush;
+ break;
+ }
+
+ jpg->samples = gs_alloc_bytes(jpg->memory, jpg->byte_width, "jpg_impl_process(samples)");
+ if (jpg->samples == NULL) {
+ jpg->state = ii_state_flush;
+ break;
+ }
+
+ memset(&jpg->image, 0, sizeof(jpg->image));
+ gs_image_t_init(&jpg->image, cs);
+ jpg->image.BitsPerComponent = jpg->bpp/jpg->num_comps;
+ jpg->image.Width = jpg->width;
+ jpg->image.Height = jpg->height;
+
+ jpg->image.ImageMatrix.xx = jpg->xresolution / 72.0f;
+ jpg->image.ImageMatrix.yy = jpg->yresolution / 72.0f;
+
+ jpg->penum = gs_image_enum_alloc(jpg->memory, "jpg_impl_process(penum)");
+ if (jpg->penum == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ jpg->state = ii_state_flush;
+ return code;
+ }
+
+ code = gs_image_init(jpg->penum,
+ &jpg->image,
+ false,
+ false,
+ jpg->pgs);
+ if (code < 0) {
+ jpg->state = ii_state_flush;
+ return code;
+ }
+
+ jpg->state = ii_state_jpeg_start;
+ break;
+ }
+ case ii_state_jpeg_start:
+ {
+ int ok;
+ if (fill_jpeg_source(jpg, pr)) {
+ need_more_data = 1;
+ break; /* No bytes left after skipping */
+ }
+ if (setjmp(jpg->jerr.setjmp_buffer)) {
+ jpeg_destroy_decompress(&jpg->cinfo);
+ jpg->state = ii_state_flush;
+ break;
+ }
+ ok = jpeg_start_decompress(&jpg->cinfo);
+ (void)consume_jpeg_data(jpg, pr);
+ if (ok == FALSE)
+ break;
+ jpg->state = ii_state_jpeg_rows;
+ break;
+ }
+ case ii_state_jpeg_rows:
+ {
+ int rows_decoded;
+ unsigned int used;
+
+ if (fill_jpeg_source(jpg, pr)) {
+ need_more_data = 1;
+ break; /* No bytes left after skipping */
+ }
+ if (setjmp(jpg->jerr.setjmp_buffer)) {
+ jpeg_destroy_decompress(&jpg->cinfo);
+ jpg->state = ii_state_flush;
+ break;
+ }
+ rows_decoded = jpeg_read_scanlines(&jpg->cinfo, &jpg->samples, 1);
+ need_more_data = consume_jpeg_data(jpg, pr);
+ if (rows_decoded == 0)
+ break; /* Not enough data for a scanline yet */
+ need_more_data = 0;
+
+ code = gs_image_next(jpg->penum, jpg->samples, jpg->byte_width, &used);
+ if (code < 0) {
+ jpg->state = ii_state_flush;
+ break;
+ }
+ jpg->y++;
+ if (jpg->y == jpg->height) {
+ code = gs_image_cleanup_and_free_enum(jpg->penum, jpg->pgs);
+ jpg->penum = NULL;
+ if (code < 0) {
+ jpg->state = ii_state_flush;
+ break;
+ }
+ code = pl_finish_page(jpg->memory->gs_lib_ctx->top_of_system,
+ jpg->pgs, 1, true);
+ if (code < 0) {
+ jpg->state = ii_state_flush;
+ break;
+ }
+ jpg->state = ii_state_jpeg_finish;
+ }
+ break;
+ }
+ case ii_state_jpeg_finish:
+ {
+ int ok;
+
+ if (fill_jpeg_source(jpg, pr)) {
+ need_more_data = 1;
+ break; /* No bytes left after skipping */
+ }
+ if (setjmp(jpg->jerr.setjmp_buffer)) {
+ jpeg_destroy_decompress(&jpg->cinfo);
+ jpg->state = ii_state_flush;
+ break;
+ }
+ ok = jpeg_finish_decompress(&jpg->cinfo);
+ need_more_data = consume_jpeg_data(jpg, pr);
+ if (ok == FALSE)
+ break;
+ need_more_data = 0;
+ jpg->state = ii_state_flush;
+ break;
+ }
+ default:
+ case ii_state_flush:
+ if (setjmp(jpg->jerr.setjmp_buffer))
+ break;
+ jpeg_destroy_decompress(&jpg->cinfo);
+
+ gs_jpeg_mem_term((j_common_ptr)&jpg->cinfo);
+
+ if (jpg->penum) {
+ (void)gs_image_cleanup_and_free_enum(jpg->penum, jpg->pgs);
+ jpg->penum = NULL;
+ }
+
+ gs_free_object(jpg->memory, jpg->samples, "jpg_impl_process(samples)");
+ jpg->samples = NULL;
+ /* We want to bin any data we get up to, but not including
+ * a UEL. */
+ return flush_to_uel(pr);
+ }
+ } while (!need_more_data);
+
+ return code;
+}
+
+static int
+jpg_impl_process_end(pl_interp_implementation_t * impl)
+{
+ jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
+ int code = 0;
+
+ /* FIXME: */
+ if (code == gs_error_InterpreterExit || code == gs_error_NeedInput)
+ code = 0;
+
+ return code;
+}
+
+/* Not implemented */
+static int
+jpg_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor)
+{
+ const byte *p = cursor->ptr;
+ const byte *rlimit = cursor->limit;
+
+ /* Skip to, but leave UEL in buffer for PJL to find later */
+ for (; p < rlimit; ++p)
+ if (p[1] == '\033') {
+ uint avail = rlimit - p;
+
+ if (memcmp(p + 1, "\033%-12345X", min(avail, 9)))
+ continue;
+ if (avail < 9)
+ break;
+ cursor->ptr = p;
+ return 1; /* found eoj */
+ }
+ cursor->ptr = p;
+ return 0; /* need more */
+}
+
+/* Parser action for end-of-file */
+static int
+jpg_impl_process_eof(pl_interp_implementation_t *impl)
+{
+ return 0;
+}
+
+/* Report any errors after running a job */
+static int
+jpg_impl_report_errors(pl_interp_implementation_t *impl, /* interp instance to wrap up job in */
+ int code, /* prev termination status */
+ long file_position, /* file position of error, -1 if unknown */
+ bool force_to_cout /* force errors to cout */
+)
+{
+ return 0;
+}
+
+/* Wrap up interp instance after a "job" */
+static int
+jpg_impl_dnit_job(pl_interp_implementation_t *impl)
+{
+ jpg_interp_instance_t *jpg = (jpg_interp_instance_t *)impl->interp_client_data;
+
+ if (jpg->nulldev) {
+ int code = gs_setdevice(jpg->pgs, jpg->nulldev);
+ jpg->dev = NULL;
+ rc_decrement(jpg->nulldev, "jpg_impl_dnit_job(nulldevice)");
+ jpg->nulldev = NULL;
+ return code;
+ }
+ return 0;
+}
+
+/* Parser implementation descriptor */
+const pl_interp_implementation_t jpg_implementation = {
+ jpg_impl_characteristics,
+ jpg_impl_allocate_interp_instance,
+ jpg_impl_get_device_memory,
+ NULL, /* jpg_impl_set_param */
+ NULL, /* jpg_impl_add_path */
+ NULL, /* jpg_impl_post_args_init */
+ jpg_impl_init_job,
+ NULL, /* jpg_impl_run_prefix_commands */
+ NULL, /* jpg_impl_process_file */
+ jpg_impl_process_begin,
+ jpg_impl_process,
+ jpg_impl_process_end,
+ jpg_impl_flush_to_eoj,
+ jpg_impl_process_eof,
+ jpg_impl_report_errors,
+ jpg_impl_dnit_job,
+ jpg_impl_deallocate_interp_instance,
+ NULL
+};
diff --git a/gpdl/psitop.c b/gpdl/psitop.c
new file mode 100644
index 0000000..f7ca110
--- /dev/null
+++ b/gpdl/psitop.c
@@ -0,0 +1,610 @@
+/* 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.
+*/
+
+/* psitop.c */
+/* Top-level API implementation of PS Language Interface */
+
+#include "stdio_.h"
+#include "ghost.h"
+#include "imain.h"
+#include "imainarg.h"
+#include "iapi.h"
+#include "psapi.h"
+#include "string_.h"
+#include "gdebug.h"
+#include "gp.h"
+#include "gserrors.h"
+#include "gstypes.h"
+#include "gsmemory.h"
+#include "gsmalloc.h"
+#include "gsstate.h" /* must precede gsdevice.h */
+#include "gxdevice.h" /* must precede gsdevice.h */
+#include "gsdevice.h"
+#include "icstate.h" /* for i_ctx_t */
+#include "iminst.h"
+#include "gsstruct.h" /* for gxalloc.h */
+#include "gspaint.h"
+#include "gxalloc.h"
+#include "gxstate.h"
+#include "plparse.h"
+#include "pltop.h"
+#include "plmain.h"
+#include "gzstate.h"
+#include "gsicc_manage.h"
+
+/* Forward decls */
+
+/************************************************************/
+/******** Language wrapper implementation (see pltop.h) *****/
+/************************************************************/
+
+/* Import operator procedures */
+extern int zflush(i_ctx_t *);
+
+/*
+ * PS interpreter instance: derived from pl_interp_implementation_t
+ */
+typedef struct ps_interp_instance_s {
+ gs_memory_t *memory;
+ uint bytes_fed;
+ gs_lib_ctx_t *psapi_instance;
+} ps_interp_instance_t;
+
+static int
+check_token(int token_type, const char *s, const char *e, int *score)
+{
+ /* Empty tokens are always OK */
+ if (s == e)
+ return 0;
+
+ switch (token_type) {
+ case 'a':
+ /* Angle bracket - mainly to catch << */
+ break;
+ case 'n':
+ /* Name */
+ /* FIXME: Check it's a valid name */
+ break;
+ case 'd':
+ /* Dictionary */
+ break;
+ case 'i':
+ /* int - ok by construction. */
+ return 0;
+ case 'f':
+ /* float - ok by construction. */
+ return 0;
+ }
+
+#define TOKEN_CHECK(n) else if (e-s == strlen(n) && memcmp(s, n, e-s) == 0) { score[0] += e-s; score[1]++; }
+
+ if (0) {}
+ TOKEN_CHECK("dup")
+ TOKEN_CHECK("exch")
+ TOKEN_CHECK("grestore")
+ TOKEN_CHECK("gsave")
+ TOKEN_CHECK("idiv")
+ TOKEN_CHECK("lineto")
+ TOKEN_CHECK("mod")
+ TOKEN_CHECK("mul")
+ TOKEN_CHECK("moveto")
+ TOKEN_CHECK("setflat")
+ TOKEN_CHECK("setlinecap")
+ TOKEN_CHECK("setlinejoin")
+ TOKEN_CHECK("showpage")
+ TOKEN_CHECK("stroke")
+ TOKEN_CHECK("translate")
+ TOKEN_CHECK("systemdict")
+
+ if (score[0] > 1024 && score[2] >= 3)
+ return 1;
+ if (score[0] < -1024)
+ return 1;
+
+ return 0;
+}
+
+static int
+score_comment(const char *s, const char *e, int *score)
+{
+#define COMMENT_CHECK(n) else if (e-s >= strlen(n) && memcmp(s, n, strlen(n)) == 0) { score[0] += 100; score[1]++; }
+
+ if (0) {}
+ COMMENT_CHECK("!PS")
+ COMMENT_CHECK("%Title:")
+ COMMENT_CHECK("%Version:")
+ COMMENT_CHECK("%Creator:")
+ COMMENT_CHECK("%CreationDate:")
+ COMMENT_CHECK("%Document")
+ COMMENT_CHECK("%BoundingBox:")
+ COMMENT_CHECK("%HiResBoundingBox:")
+ COMMENT_CHECK("%Pages:")
+ COMMENT_CHECK("%+ procset")
+ COMMENT_CHECK("%End")
+ COMMENT_CHECK("%Begin")
+ COMMENT_CHECK("%Copyright")
+ else {
+ score[0]++; score[1]++;
+ }
+
+ if (score[0] > 1024 && score[1] >= 3)
+ return 1;
+
+ return 0;
+}
+
+static int
+ps_detect_language(const char *s, int len)
+{
+ /* For postscript, we look for %! */
+ if (len >= 2) {
+ if (s[0] != '%' || s[1] != '!') {
+ /* Not what we were looking for */
+ } else if (len >= 12 && memcmp(s+2, "Postscript", 10) == 0) {
+ return 100;
+ } else if (len >= 4 && memcmp(s+2, "PS", 2) == 0) {
+ return 100;
+ } else if (len >= 3 && s[2] == '/') {
+ /* Looks like a shell script. Don't want that. */
+ return 0;
+ } else {
+ /* If it begins %!, then it's PROBABLY postscript */
+ return 80;
+ }
+ }
+ /* For PDF, we allow for leading crap, then a postscript version marker */
+ {
+ const char *t = s;
+ int left = len-22;
+ /* Search within just the first 4K, plus a bit for the marker length. */
+ if (left > 4096+22)
+ left = 4096+22;
+ while (left > 22) {
+ if (memcmp(t, "%PDF-", 5) == 0 &&
+ t[5] >= '1' && t[5] <= '9' &&
+ t[6] == '.' &&
+ t[7] >= '0' && t[7] <= '9') {
+ return 100;
+ }
+ if (memcmp(t, "%!PS-Adobe-", 11) == 0 &&
+ t[11] >= '0' && t[11] <= '9' &&
+ t[12] == '.' &&
+ t[13] >= '0' && t[13] <= '9' &&
+ memcmp(t+14, " PDF-", 5) == 0 &&
+ t[19] >= '0' && t[19] <= '9' &&
+ t[20] == '.' &&
+ t[21] >= '0' && t[21] <= '9') {
+ return 100;
+ }
+ t++;
+ left--;
+ }
+ }
+
+ /* Now we do some seriously hairy stuff */
+ {
+ const char *t = s;
+ const char *token = t;
+ int left = len;
+ int token_type = 0;
+ int score[2] = { 0, 0 };
+
+ while (left--) {
+ if (*t == '%') {
+ if (check_token(token_type, token, t, score))
+ break;
+ /* Skip to end of line */
+ left--;
+ token = ++t;
+ while (left && *t != '\r' && *t != '\n') {
+ left--; t++;
+ }
+ if (score_comment(token, t, score))
+ break;
+ /* Skip any combination of newlines */
+ while (left && (*t == '\r' || *t == '\n')) {
+ left--; t++;
+ }
+ token_type = 0;
+ continue;
+ } else if (*t == 27) {
+ /* Give up if we meet an ESC. It could be a UEL. */
+ break;
+ } else if (*t <= 32 || *t > 127) {
+ if (check_token(token_type, token, t, score))
+ break;
+ if (*t != 9 && *t != 10 && *t != 12 && *t != 13 && *t != 32)
+ score[0]--;
+ token = t+1;
+ token_type = 0;
+ } else if (*t == '/') {
+ if (check_token(token_type, token, t, score))
+ break;
+ token = t+1;
+ token_type = 'n';
+ } else if (*t == '[' || *t == ']' || *t == '{' || *t == '}') {
+ if (check_token(token_type, token, t, score))
+ break;
+ token = t+1;
+ token_type = 0;
+ } else if (*t == '<') {
+ if (token_type == 'a') {
+ /* << */
+ token = t+1;
+ token_type = 'd';
+ } else if (token_type == 'd') {
+ /* <<< ???!? */
+ score[0] -= 10;
+ } else {
+ if (check_token(token_type, token, t, score))
+ break;
+ token = t+1;
+ token_type = 'a';
+ }
+ } else if (*t == '>') {
+ if (check_token(token_type, token, t, score))
+ break;
+ token = t+1;
+ token_type = 0;
+ } else if (*t >= '0' && *t <= '9') {
+ if (token_type == 'i') {
+ /* Keep going */
+ } else if (token_type == 'f') {
+ /* Keep going */
+ } else {
+ if (check_token(token_type, token, t, score))
+ break;
+ token = t;
+ token_type = 'i';
+ }
+ } else if (*t == '.') {
+ if (token_type == 'f') {
+ /* seems unlikely */
+ score[0]--;
+ break;
+ } else if (token_type == 'i') {
+ token = t;
+ token_type = 'f';
+ } else {
+ if (check_token(token_type, token, t, score))
+ break;
+ token = t;
+ token_type = 'f';
+ }
+ } else {
+ /* Assume anything else goes into the token */
+ }
+ t++;
+ }
+ if (score[0] < 0 || score[1] < 3)
+ return 0; /* Unlikely to be PS */
+ else if (score[0] > 0)
+ return 75; /* Could be PS */
+ }
+
+ return 0;
+}
+
+/* Get implementation's characteristics */
+static const pl_interp_characteristics_t * /* always returns a descriptor */
+ps_impl_characteristics(const pl_interp_implementation_t *impl) /* implementation of interpreter to alloc */
+{
+ /* version and build date are not currently used */
+#define PSVERSION NULL
+#define PSBUILDDATE NULL
+ static const pl_interp_characteristics_t ps_characteristics = {
+ "POSTSCRIPT",
+ ps_detect_language,
+ "Artifex",
+ PSVERSION,
+ PSBUILDDATE,
+ 1 /* minimum input size to PostScript */
+ };
+#undef PSVERSION
+#undef PSBUILDDATE
+
+ return &ps_characteristics;
+}
+
+/* Do per-instance interpreter allocation/init. */
+static int
+ps_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem)
+{
+ ps_interp_instance_t *psi
+ = (ps_interp_instance_t *)gs_alloc_bytes( mem,
+ sizeof(ps_interp_instance_t),
+ "ps_impl_allocate_interp_instance");
+
+ int code;
+#define GS_MAX_NUM_ARGS 10
+ const char *gsargs[GS_MAX_NUM_ARGS] = {0};
+ int nargs = 0;
+
+ if (!psi)
+ return gs_error_VMerror;
+
+ gsargs[nargs++] = "gpdl";
+ /* We start gs with the nullpage device, and replace the device with the
+ * set_device call from the language independent code.
+ */
+ gsargs[nargs++] = "-dNODISPLAY";
+ /* As we're "printer targetted, use a jobserver */
+ gsargs[nargs++] = "-dJOBSERVER";
+
+ psi->memory = mem;
+ psi->bytes_fed = 0;
+ psi->psapi_instance = gs_lib_ctx_get_interp_instance(mem);
+ code = psapi_new_instance(&psi->psapi_instance, NULL);
+ if (code < 0)
+ gs_free_object(mem, psi, "ps_impl_allocate_interp_instance");
+
+ impl->interp_client_data = psi;
+
+ /* Tell gs not to ignore a UEL, but do an interpreter exit */
+ psapi_act_on_uel(psi->psapi_instance);
+
+ code = psapi_init_with_args01(psi->psapi_instance, nargs, (char **)gsargs);
+ if (code < 0) {
+ psapi_delete_instance(psi->psapi_instance);
+ gs_free_object(mem, psi, "ps_impl_allocate_interp_instance");
+ }
+ return code;
+}
+
+/*
+ * Get the allocator with which to allocate a device
+ */
+static gs_memory_t *
+ps_impl_get_device_memory(pl_interp_implementation_t *impl)
+{
+ ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
+
+ return psapi_get_device_memory(psi->psapi_instance);
+}
+
+static int
+ps_impl_set_param(pl_interp_implementation_t *impl,
+ pl_set_param_type type,
+ const char *param,
+ const void *val)
+{
+ ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
+
+ return psapi_set_param(psi->psapi_instance, type, param, val);
+}
+
+static int
+ps_impl_add_path(pl_interp_implementation_t *impl,
+ const char *path)
+{
+ ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
+
+ return psapi_add_path(psi->psapi_instance, path);
+}
+
+static int
+ps_impl_post_args_init(pl_interp_implementation_t *impl)
+{
+ ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
+ const float *resolutions;
+ const long *page_sizes;
+
+ pl_main_get_forced_geometry(psi->memory, &resolutions, &page_sizes);
+ psapi_force_geometry(psi->psapi_instance, resolutions, page_sizes);
+
+ return psapi_init_with_args2(psi->psapi_instance);
+}
+
+/* Prepare interp instance for the next "job" */
+static int
+ps_impl_init_job(pl_interp_implementation_t *impl,
+ gx_device *device)
+{
+ ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
+ int exit_code;
+ int code = 0;
+
+ /* Any error after here *must* reset the device to null */
+ if (code >= 0)
+ code = psapi_set_device(psi->psapi_instance, device);
+
+ if (code >= 0)
+ code = psapi_run_string(psi->psapi_instance, "erasepage", 0, &exit_code);
+
+ if (code < 0) {
+ int code1 = psapi_set_device(psi->psapi_instance, NULL);
+ (void)code1;
+ }
+
+ return code;
+}
+
+static int
+ps_impl_run_prefix_commands(pl_interp_implementation_t *impl,
+ const char *prefix)
+{
+ ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
+ int exit_code;
+ int code = 0;
+
+ if (prefix == NULL)
+ return 0;
+
+ /* Any error after here *must* reset the device to null */
+ code = psapi_run_string(psi->psapi_instance, prefix, 0, &exit_code);
+
+ if (code < 0) {
+ int code1 = psapi_set_device(psi->psapi_instance, NULL);
+ (void)code1;
+ }
+
+ return code;
+}
+
+static int
+ps_impl_process_file(pl_interp_implementation_t *impl, const char *filename)
+{
+ ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
+ int exit_code;
+
+ return psapi_run_file(psi->psapi_instance, filename, 0, &exit_code);
+}
+
+/* Do any setup for parser per-cursor */
+static int /* ret 0 or +ve if ok, else -ve error code */
+ps_impl_process_begin(pl_interp_implementation_t * impl)
+{
+ ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
+ int exit_code;
+
+ psi->bytes_fed = 0;
+ return psapi_run_string_begin(psi->psapi_instance, 0, &exit_code);
+}
+
+/* TODO: in some fashion have gs pass back how far into the input buffer it
+ * had read, so we don't need to explicitly search the buffer for the UEL
+ */
+static int
+ps_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr)
+{
+ ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
+ const unsigned int len = pr->limit - pr->ptr;
+ int code, exit_code = 0;
+
+ code = psapi_run_string_continue(psi->psapi_instance, (const char *)pr->ptr + 1, len, 0, &exit_code);
+ if (exit_code == gs_error_InterpreterExit) {
+ int64_t offset;
+
+ offset = psapi_get_uel_offset(psi->psapi_instance) - psi->bytes_fed;
+ pr->ptr += offset;
+ psi->bytes_fed += offset + 1;
+
+#ifdef SEND_CTRLD_AFTER_UEL
+ {
+ const char eot[1] = {4};
+ code = psapi_run_string_continue(psi->psapi_instance, eot, 1, 0, &exit_code);
+ (void)code; /* Ignoring code here */
+ }
+#endif
+ return gs_error_InterpreterExit;
+ }
+ else {
+ pr->ptr = pr->limit;
+ psi->bytes_fed += len;
+ }
+ return code;
+}
+
+static int /* ret 0 or +ve if ok, else -ve error code */
+ps_impl_process_end(pl_interp_implementation_t * impl)
+{
+ ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
+ int exit_code, code;
+
+ code = psapi_run_string_end(psi->psapi_instance, 0, &exit_code);
+
+ if (exit_code == gs_error_InterpreterExit || code == gs_error_NeedInput)
+ code = 0;
+
+ return code;
+}
+
+/* Not implemented */
+static int
+ps_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor)
+{
+ const byte *p = cursor->ptr;
+ const byte *rlimit = cursor->limit;
+
+ /* Skip to, but leave UEL in buffer for PJL to find later */
+ for (; p < rlimit; ++p)
+ if (p[1] == '\033') {
+ uint avail = rlimit - p;
+
+ if (memcmp(p + 1, "\033%-12345X", min(avail, 9)))
+ continue;
+ if (avail < 9)
+ break;
+ cursor->ptr = p;
+ return 1; /* found eoj */
+ }
+ cursor->ptr = p;
+ return 0; /* need more */
+}
+
+/* Parser action for end-of-file */
+static int
+ps_impl_process_eof(pl_interp_implementation_t *impl)
+{
+ return 0;
+}
+
+/* Report any errors after running a job */
+static int
+ps_impl_report_errors(pl_interp_implementation_t *impl, /* interp instance to wrap up job in */
+ int code, /* prev termination status */
+ long file_position, /* file position of error, -1 if unknown */
+ bool force_to_cout /* force errors to cout */
+)
+{
+ return 0;
+}
+
+/* Wrap up interp instance after a "job" */
+static int
+ps_impl_dnit_job(pl_interp_implementation_t *impl)
+{
+ ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
+
+ return psapi_set_device(psi->psapi_instance, NULL);
+}
+
+/* Deallocate a interpreter instance */
+static int
+ps_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
+{
+ ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
+ int code;
+
+ if (psi == NULL)
+ return 0;
+ code = psapi_exit(psi->psapi_instance);
+ psapi_delete_instance(psi->psapi_instance);
+ gs_free_object(psi->memory, psi, "ps_impl_allocate_interp_instance");
+ impl->interp_client_data = NULL;
+ return code;
+}
+
+/* Parser implementation descriptor */
+const pl_interp_implementation_t ps_implementation = {
+ ps_impl_characteristics,
+ ps_impl_allocate_interp_instance,
+ ps_impl_get_device_memory,
+ ps_impl_set_param,
+ ps_impl_add_path,
+ ps_impl_post_args_init,
+ ps_impl_init_job,
+ ps_impl_run_prefix_commands,
+ ps_impl_process_file,
+ ps_impl_process_begin,
+ ps_impl_process,
+ ps_impl_process_end,
+ ps_impl_flush_to_eoj,
+ ps_impl_process_eof,
+ ps_impl_report_errors,
+ ps_impl_dnit_job,
+ ps_impl_deallocate_interp_instance,
+ NULL
+};
diff --git a/gpdl/pwgtop.c b/gpdl/pwgtop.c
new file mode 100644
index 0000000..1083508
--- /dev/null
+++ b/gpdl/pwgtop.c
@@ -0,0 +1,731 @@
+/* Copyright (C) 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.
+*/
+
+/* pwgtop.c */
+/* Top-level API implementation of "PWG" Language Interface */
+
+#include "pltop.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gsstate.h"
+#include "spwgx.h"
+#include "strimpl.h"
+#include "gscoord.h"
+
+/* Forward decls */
+
+/************************************************************/
+/******** Language wrapper implementation (see pltop.h) *****/
+/************************************************************/
+
+typedef enum
+{
+ ii_state_identifying = 0,
+ ii_state_pwg,
+ ii_state_pwg_header,
+ ii_state_pwg_data,
+ ii_state_flush
+} ii_state;
+
+/*
+ * PWG interpreter instance
+ */
+typedef struct pwg_interp_instance_s {
+ gs_memory_t *memory;
+ gx_device *dev;
+ gx_device *nulldev;
+
+ gs_color_space *gray;
+ gs_color_space *rgb;
+ gs_color_space *cmyk;
+
+ /* PWG parser state machine */
+ ii_state state;
+
+ int pages;
+
+ uint32_t bpc;
+ uint32_t bpp;
+ uint32_t bpl;
+ uint32_t colororder;
+ uint32_t cs;
+ uint32_t duplexMode;
+ uint32_t printQuality;
+ uint32_t mediaType;
+ uint32_t copies;
+ uint32_t width;
+ uint32_t height;
+ uint32_t xresolution;
+ uint32_t yresolution;
+
+ uint32_t num_comps;
+ uint32_t byte_width;
+ uint32_t x;
+ uint32_t y;
+
+ gs_image_t image;
+ gs_image_enum *penum;
+ gs_gstate *pgs;
+
+ stream_PWGD_state pwgd_state;
+ byte stream_buffer[2048];
+
+} pwg_interp_instance_t;
+
+static int
+pwg_detect_language(const char *s, int len)
+{
+ /* For postscript, we look for %! */
+ if (len >= 4) {
+ if (memcmp(s, "RaS2", 4) == 0)
+ return 100;
+ }
+
+ return 0;
+}
+
+static const pl_interp_characteristics_t pwg_characteristics = {
+ "PWG",
+ pwg_detect_language,
+ "Artifex",
+ "0.01",
+ "13 Nov 2019",
+ 1 /* minimum input size */
+};
+
+/* Get implementation's characteristics */
+static const pl_interp_characteristics_t * /* always returns a descriptor */
+pwg_impl_characteristics(const pl_interp_implementation_t *impl) /* implementation of interpreter to alloc */
+{
+ return &pwg_characteristics;
+}
+
+static void
+pwg_deallocate(pwg_interp_instance_t *pwg)
+{
+ if (pwg == NULL)
+ return;
+
+ rc_decrement_cs(pwg->gray, "pwg_deallocate");
+ rc_decrement_cs(pwg->rgb, "pwg_deallocate");
+ rc_decrement_cs(pwg->cmyk, "pwg_deallocate");
+
+ if (pwg->pgs != NULL)
+ gs_gstate_free_chain(pwg->pgs);
+ gs_free_object(pwg->memory, pwg, "pwg_impl_allocate_interp_instance");
+}
+
+/* Deallocate a interpreter instance */
+static int
+pwg_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
+{
+ pwg_interp_instance_t *pwg = (pwg_interp_instance_t *)impl->interp_client_data;
+
+ pwg_deallocate(pwg);
+ impl->interp_client_data = NULL;
+
+ return 0;
+}
+
+/* Do per-instance interpreter allocation/init. */
+static int
+pwg_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem)
+{
+ int code;
+ pwg_interp_instance_t *pwg
+ = (pwg_interp_instance_t *)gs_alloc_bytes(mem,
+ sizeof(pwg_interp_instance_t),
+ "pwg_impl_allocate_interp_instance");
+ if (!pwg)
+ return_error(gs_error_VMerror);
+ memset(pwg, 0, sizeof(*pwg));
+
+ pwg->memory = mem;
+ pwg->pgs = gs_gstate_alloc(mem);
+ if (pwg->pgs == NULL)
+ goto failVM;
+
+ /* Push one save level onto the stack to assuage the memory handling */
+ code = gs_gsave(pwg->pgs);
+ if (code < 0)
+ goto fail;
+
+ code = gsicc_init_iccmanager(pwg->pgs);
+ if (code < 0)
+ goto fail;
+
+ pwg->gray = gs_cspace_new_ICC(mem, pwg->pgs, 1);
+ pwg->rgb = gs_cspace_new_ICC(mem, pwg->pgs, 3);
+ pwg->cmyk = gs_cspace_new_ICC(mem, pwg->pgs, 4);
+
+ impl->interp_client_data = pwg;
+
+ return 0;
+
+failVM:
+ code = gs_note_error(gs_error_VMerror);
+fail:
+ (void)pwg_deallocate(pwg);
+ return code;
+}
+
+/*
+ * Get the allocator with which to allocate a device
+ */
+static gs_memory_t *
+pwg_impl_get_device_memory(pl_interp_implementation_t *impl)
+{
+ pwg_interp_instance_t *pwg = (pwg_interp_instance_t *)impl->interp_client_data;
+
+ return pwg->dev ? pwg->dev->memory : NULL;
+}
+
+#if 0 /* UNUSED */
+static int
+pwg_impl_set_param(pl_interp_implementation_t *impl,
+ pl_set_param_type type,
+ const char *param,
+ const void *val)
+{
+ pwg_interp_instance_t *pwg = (pwg_interp_instance_t *)impl->interp_client_data;
+
+ /* No params set here */
+ return 0;
+}
+
+static int
+pwg_impl_add_path(pl_interp_implementation_t *impl,
+ const char *path)
+{
+ pwg_interp_instance_t *pwg = (pwg_interp_instance_t *)impl->interp_client_data;
+
+ /* No paths to add */
+ return 0;
+}
+
+static int
+pwg_impl_post_args_init(pl_interp_implementation_t *impl)
+{
+ pwg_interp_instance_t *pwg = (pwg_interp_instance_t *)impl->interp_client_data;
+
+ /* No post args processing */
+ return 0;
+}
+#endif
+
+/* Prepare interp instance for the next "job" */
+static int
+pwg_impl_init_job(pl_interp_implementation_t *impl,
+ gx_device *device)
+{
+ pwg_interp_instance_t *pwg = (pwg_interp_instance_t *)impl->interp_client_data;
+
+ pwg->dev = device;
+ pwg->state = ii_state_identifying;
+
+ return 0;
+}
+
+#if 0 /* UNUSED */
+static int
+pwg_impl_run_prefix_commands(pl_interp_implementation_t *impl,
+ const char *prefix)
+{
+ pwg_interp_instance_t *pwg = (pwg_interp_instance_t *)impl->interp_client_data;
+
+ return 0;
+}
+
+static int
+pwg_impl_process_file(pl_interp_implementation_t *impl, const char *filename)
+{
+ pwg_interp_instance_t *pwg = (pwg_interp_instance_t *)impl->interp_client_data;
+
+ return 0;
+}
+#endif
+
+/* Do any setup for parser per-cursor */
+static int /* ret 0 or +ve if ok, else -ve error code */
+pwg_impl_process_begin(pl_interp_implementation_t * impl)
+{
+ pwg_interp_instance_t *pwg = (pwg_interp_instance_t *)impl->interp_client_data;
+
+ return 0;
+}
+
+/* Ensure we have 'required' bytes to read, and further ensure
+ * that we have no UEL's within those bytes. */
+static int
+ensure_bytes(pwg_interp_instance_t *pwg, stream_cursor_read *pr, int required)
+{
+ int n;
+ const uint8_t *p = pr->ptr+1;
+ const uint8_t *q;
+ int avail;
+
+ /* Find out how many bytes we need to check */
+ n = pr->limit - pr->ptr;
+ if (n > required)
+ n = required;
+
+ /* Make sure there are no UELs in that block */
+ q = p + n;
+ while (p != q) {
+ while (p != q && *p != '\033')
+ p++;
+ if (p == q)
+ break;
+ avail = pr->limit - pr->ptr;
+ if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
+ /* At least a partial match to a UEL */
+ return avail < 9 ? gs_error_NeedInput : gs_error_InterpreterExit;
+ }
+ p++;
+ }
+
+ /* If we have enough bytes, great, if not, get some more */
+ return (n < required) ? gs_error_NeedInput : 0;
+}
+
+static int
+flush_to_uel(stream_cursor_read *pr)
+{
+ const uint8_t *p = pr->ptr+1;
+ const uint8_t *q = pr->limit+1;
+ int avail;
+
+ while (p != q) {
+ while (p != q && *p != '\033')
+ p++;
+ if (p == q)
+ break;
+ avail = pr->limit - pr->ptr;
+ if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
+ /* At least a partial match to a UEL. Bin everything to
+ * the start of the match. */
+ pr->ptr = p-1;
+ if (avail == 9) /* Complete match. Exit! */
+ return gs_error_InterpreterExit;
+ /* Partial match. Get more data. */
+ return gs_error_NeedInput;
+ }
+ p++;
+ }
+
+ pr->ptr = pr->limit;
+
+ return 0;
+}
+
+static int
+bytes_until_uel(const stream_cursor_read *pr)
+{
+ const uint8_t *p = pr->ptr+1;
+ const uint8_t *q = pr->limit+1;
+ int avail;
+
+ while (p != q) {
+ while (p != q && *p != '\033')
+ p++;
+ if (p == q)
+ break;
+ avail = pr->limit - pr->ptr;
+ if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
+ /* At least a partial match to a UEL. Everything up to
+ * the start of the match is up for grabs. */
+ return p - (pr->ptr+1);
+ }
+ p++;
+ }
+
+ return pr->limit - pr->ptr;
+}
+
+static int
+get32be(stream_cursor_read *pr)
+{
+ int v = pr->ptr[1] << 24;
+ v |= pr->ptr[2] << 16;
+ v |= pr->ptr[3] << 8;
+ v |= pr->ptr[4];
+ pr->ptr += 4;
+
+ return v;
+}
+
+static int
+get8(stream_cursor_read *pr)
+{
+ return *++(pr->ptr);
+}
+
+static int
+pwg_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr)
+{
+ pwg_interp_instance_t *pwg = (pwg_interp_instance_t *)impl->interp_client_data;
+ int code = 0;
+ gs_color_space *cs;
+
+ /* Loop while we have bytes to read */
+ while (pr->limit > pr->ptr)
+ {
+ switch(pwg->state)
+ {
+ case ii_state_identifying:
+ /* Try and get us 4 bytes for "RaS2" */
+ code = ensure_bytes(pwg, pr, 4);
+ if (code < 0)
+ return code;
+ if (memcmp("RaS2", (const char *)pr->ptr+1, 4) == 0)
+ {
+ pr->ptr += 4;
+ pwg->state = ii_state_pwg_header;
+ break;
+ }
+ pwg->state = ii_state_flush;
+ break;
+ case ii_state_pwg_header:
+ /* Try and get us 1796 bytes for the page header */
+ code = ensure_bytes(pwg, pr, 1796);
+ if (code < 0)
+ return code;
+ pr->ptr += 64; /* CString PwgRaster */
+ pr->ptr += 64; /* CString MediaColor */
+ pr->ptr += 64; /* CString MediaType */
+ pr->ptr += 64; /* CString PrintContentOptimize */
+ pr->ptr += 1-(256-267); /* Reserved */
+ (void)get32be(pr); /* WhenEnum CutMedia */
+ pwg->duplexMode = !!get32be(pr); /* Boolean Duplex */
+ pwg->xresolution = get32be(pr);
+ pwg->yresolution = get32be(pr);
+ pr->ptr += 1-(284-299); /* Reserved */
+ (void)!!get32be(pr); /* Boolean InsertSheet */
+ (void)get32be(pr); /* WhenEnum Jog */
+ (void)get32be(pr); /* EdgeEnum LeadingEdge FeedDirection */
+ pr->ptr += 1-(312-323); /* Reserved */
+ (void)get32be(pr); /* MediaPosition */
+ (void)get32be(pr); /* MediaWeightMetric */
+ pr->ptr += 1-(332-339); /* Reserved */
+ pwg->copies = get32be(pr); /* NumCopies */
+ if (pwg->copies == 0)
+ pwg->copies = 1;
+ (void)get32be(pr); /* OrientationEnum Orientation OrientationRequested */
+ pr->ptr += 1-(348-351); /* Reserved */
+ (void)get32be(pr); /* UnsignedInt PageSize */
+ (void)get32be(pr); /* UnsignedInt PageSize */
+ pr->ptr += 1-(360-367); /* Reserved */
+ (void)!!get32be(pr); /* Boolean Tumble */
+ pwg->width = get32be(pr); /* Width */
+ pwg->height = get32be(pr); /* Height */
+ pr->ptr += 1-(380-383); /* Reserved */
+ pwg->bpc = get32be(pr); /* Bits Per Color */
+ pwg->bpp = get32be(pr); /* Bits Per Pixel */
+ pwg->bpl = get32be(pr); /* Bytes Per Line */
+ pwg->colororder = get32be(pr); /* ColorOrderEnum ColorOrder */
+ pwg->cs = get32be(pr); /* ColorSpaceEnum ColorSpace */
+ pr->ptr += 1-(404-419); /* Reserved */
+ (void)get32be(pr); /* NumColors */
+ pr->ptr += 1-(424-451); /* Reserved */
+ (void)get32be(pr); /* TotalPageCount */
+ (void)get32be(pr); /* CrossFeedTransform */
+ (void)get32be(pr); /* FeedTransform */
+ (void)get32be(pr); /* ImageBoxLeft */
+ (void)get32be(pr); /* ImageBoxTop */
+ (void)get32be(pr); /* ImageBoxRight */
+ (void)get32be(pr); /* ImageBoxBottom */
+ (void)get32be(pr); /* SrgbColor AlternatePrimary */
+ pwg->printQuality = get32be(pr); /* PrintQuality */
+ pr->ptr += 1-(488-507); /* Reserved */
+ (void)get32be(pr); /* VendorIdentifier */
+ (void)get32be(pr); /* VendorLength */
+ pr->ptr += 1-(516-1603); /* VendorData */
+ pr->ptr += 1-(1604-1667); /* Reserved */
+ pr->ptr += 1-(1668-1731); /* RenderingIntent */
+ pr->ptr += 1-(1732-1795); /* PageSizeName */
+
+ /* Decode the header */
+ if (pwg->colororder != 0)
+ goto bad_header; /* 0 = chunky = only defined one so far */
+
+ switch(pwg->cs) {
+ case 3: /* DeviceBlack */
+ case 18: /* Sgray */
+ case 48: /* Device1 (Device color, 1 colorant) */
+ pwg->num_comps = 1;
+ cs = pwg->gray;
+ break;
+ case 1: /* DeviceRGB */
+ case 19: /* sRGB */
+ case 20: /* AdobeRGB */
+ pwg->num_comps = 3;
+ cs = pwg->rgb;
+ break;
+ case 6: /* DeviceCMYK */
+ pwg->num_comps = 4;
+ cs = pwg->cmyk;
+ break;
+ default:
+ goto bad_header;
+ }
+ if (pwg->bpc != 8)
+ goto bad_header;
+ if (pwg->bpp != 8*pwg->num_comps && pwg->bpp != 16*pwg->num_comps) {
+bad_header:
+ pwg->state = ii_state_flush;
+ break;
+ }
+ pwg->byte_width = (pwg->bpp>>3)*pwg->width;
+ if (pwg->bpl != pwg->byte_width)
+ goto bad_header;
+
+ pwg->nulldev = gs_currentdevice(pwg->pgs);
+ rc_increment(pwg->nulldev);
+ code = gs_setdevice_no_erase(pwg->pgs, pwg->dev);
+ if (code < 0)
+ goto early_flush;
+ gs_initmatrix(pwg->pgs);
+
+ /* By default the ctm is set to:
+ * xres/72 0
+ * 0 -yres/72
+ * 0 dev->height * yres/72
+ * i.e. it moves the origin from being top right to being bottom left.
+ * We want to move it back, as without this, the image will be displayed
+ * upside down.
+ */
+ code = gs_translate(pwg->pgs, 0.0, -pwg->dev->height * 72 / pwg->dev->HWResolution[1]);
+ if (code >= 0)
+ code = gs_scale(pwg->pgs, 1, -1);
+ /* At this point, the ctm is set to:
+ * xres/72 0
+ * 0 yres/72
+ * 0 0
+ */
+ if (code >= 0)
+ code = gs_erasepage(pwg->pgs);
+ if (code < 0)
+ goto early_flush;
+
+ memset(&pwg->image, 0, sizeof(pwg->image));
+ gs_image_t_init(&pwg->image, cs);
+ pwg->image.BitsPerComponent = pwg->bpp/pwg->num_comps;
+ pwg->image.Width = pwg->width;
+ pwg->image.Height = pwg->height;
+
+ pwg->image.ImageMatrix.xx = pwg->xresolution / 72.0f;
+ pwg->image.ImageMatrix.yy = pwg->yresolution / 72.0f;
+
+ pwg->penum = gs_image_enum_alloc(pwg->memory, "pwg_impl_process(penum)");
+ if (pwg->penum == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto early_flush;
+ }
+
+ code = gs_image_init(pwg->penum,
+ &pwg->image,
+ false,
+ false,
+ pwg->pgs);
+ if (code < 0)
+ goto early_flush;
+
+ s_init_state((stream_state *)&pwg->pwgd_state, &s_PWGD_template, pwg->memory);
+ if (s_PWGD_template.set_defaults)
+ s_PWGD_template.set_defaults((stream_state *)&pwg->pwgd_state);
+
+ pwg->pwgd_state.width = pwg->width;
+ pwg->pwgd_state.bpp = pwg->bpp;
+
+ code = (s_PWGD_template.init)((stream_state *)&pwg->pwgd_state);
+ if (code < 0)
+ goto early_flush;
+ pwg->x = pwg->y = 0;
+ pwg->state = ii_state_pwg_data;
+ break;
+ case ii_state_pwg_data:
+ {
+ int n = bytes_until_uel(pr);
+ stream_cursor_read local_r;
+ stream_cursor_write local_w;
+ int status;
+ unsigned int used;
+ int decoded_any = 0;
+
+ local_r.ptr = pr->ptr;
+ local_r.limit = local_r.ptr + n;
+
+ do {
+ local_w.ptr = pwg->stream_buffer-1;
+ local_w.limit = local_w.ptr + sizeof(pwg->stream_buffer);
+
+ status = (s_PWGD_template.process)((stream_state *)&pwg->pwgd_state,
+ &local_r, &local_w,
+ true);
+ /* status = 0 => need data
+ * 1 => need output space */
+ /* Copy the updated pointer back */
+ pr->ptr = local_r.ptr;
+ if (local_w.ptr + 1 == pwg->stream_buffer)
+ break; /* Failed to decode any data */
+ decoded_any = 1;
+
+ code = gs_image_next(pwg->penum, pwg->stream_buffer, local_w.ptr + 1 - pwg->stream_buffer, &used);
+ if (code < 0)
+ goto flush;
+
+ pwg->x += used;
+ while (pwg->x >= pwg->byte_width) {
+ pwg->x -= pwg->byte_width;
+ pwg->y++;
+ }
+ /* Loop while we haven't had all the lines, the decompression
+ * didn't ask for more data, and the decompression didn't give
+ * us more data. */
+ } while (pwg->y < pwg->height);
+
+ if (pwg->y == pwg->height) {
+ code = gs_image_cleanup_and_free_enum(pwg->penum, pwg->pgs);
+ pwg->penum = NULL;
+ if (code < 0)
+ goto flush;
+ code = pl_finish_page(pwg->memory->gs_lib_ctx->top_of_system,
+ pwg->pgs, pwg->copies, true);
+ if (code < 0)
+ goto flush;
+ if (pwg->pages > 0) {
+ pwg->pages--;
+ /* If we've reached the expected end, we should probably flush to UEL */
+ if (pwg->pages == 0)
+ pwg->state = ii_state_flush;
+ }
+ if (pwg->pwgd_state.templat->release)
+ pwg->pwgd_state.templat->release((stream_state *)&pwg->pwgd_state);
+ pwg->state = ii_state_pwg_header;
+ } else if (decoded_any == 0) {
+ /* Failed to make progress. Just give up to avoid livelocking. */
+ goto flush;
+ }
+ break;
+ }
+ default:
+ case ii_state_flush:
+ if (0) {
+flush:
+ if (pwg->pwgd_state.templat->release)
+ pwg->pwgd_state.templat->release((stream_state *)&pwg->pwgd_state);
+early_flush:
+ pwg->state = ii_state_flush;
+ }
+ /* We want to bin any data we get up to, but not including
+ * a UEL. */
+ return flush_to_uel(pr);
+ }
+ }
+
+ return code;
+}
+
+static int
+pwg_impl_process_end(pl_interp_implementation_t * impl)
+{
+ pwg_interp_instance_t *pwg = (pwg_interp_instance_t *)impl->interp_client_data;
+ int code = 0;
+
+ /* FIXME: */
+ if (code == gs_error_InterpreterExit || code == gs_error_NeedInput)
+ code = 0;
+
+ return code;
+}
+
+/* Not implemented */
+static int
+pwg_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor)
+{
+ const byte *p = cursor->ptr;
+ const byte *rlimit = cursor->limit;
+
+ /* Skip to, but leave UEL in buffer for PJL to find later */
+ for (; p < rlimit; ++p)
+ if (p[1] == '\033') {
+ uint avail = rlimit - p;
+
+ if (memcmp(p + 1, "\033%-12345X", min(avail, 9)))
+ continue;
+ if (avail < 9)
+ break;
+ cursor->ptr = p;
+ return 1; /* found eoj */
+ }
+ cursor->ptr = p;
+ return 0; /* need more */
+}
+
+/* Parser action for end-of-file */
+static int
+pwg_impl_process_eof(pl_interp_implementation_t *impl)
+{
+ return 0;
+}
+
+/* Report any errors after running a job */
+static int
+pwg_impl_report_errors(pl_interp_implementation_t *impl, /* interp instance to wrap up job in */
+ int code, /* prev termination status */
+ long file_position, /* file position of error, -1 if unknown */
+ bool force_to_cout /* force errors to cout */
+)
+{
+ return 0;
+}
+
+/* Wrap up interp instance after a "job" */
+static int
+pwg_impl_dnit_job(pl_interp_implementation_t *impl)
+{
+ pwg_interp_instance_t *pwg = (pwg_interp_instance_t *)impl->interp_client_data;
+
+ if (pwg->nulldev) {
+ int code = gs_setdevice(pwg->pgs, pwg->nulldev);
+ pwg->dev = NULL;
+ rc_decrement(pwg->nulldev, "pwg_impl_dnit_job(nulldevice)");
+ pwg->nulldev = NULL;
+ return code;
+ }
+ return 0;
+}
+
+/* Parser implementation descriptor */
+const pl_interp_implementation_t pwg_implementation = {
+ pwg_impl_characteristics,
+ pwg_impl_allocate_interp_instance,
+ pwg_impl_get_device_memory,
+ NULL, /* pwg_impl_set_param */
+ NULL, /* pwg_impl_add_path */
+ NULL, /* pwg_impl_post_args_init */
+ pwg_impl_init_job,
+ NULL, /* pwg_impl_run_prefix_commands */
+ NULL, /* pwg_impl_process_file */
+ pwg_impl_process_begin,
+ pwg_impl_process,
+ pwg_impl_process_end,
+ pwg_impl_flush_to_eoj,
+ pwg_impl_process_eof,
+ pwg_impl_report_errors,
+ pwg_impl_dnit_job,
+ pwg_impl_deallocate_interp_instance,
+ NULL
+};
diff --git a/gpdl/tifftop.c b/gpdl/tifftop.c
new file mode 100644
index 0000000..2f55e1b
--- /dev/null
+++ b/gpdl/tifftop.c
@@ -0,0 +1,829 @@
+/* Copyright (C) 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.
+*/
+
+/* tifftop.c */
+/* Top-level API implementation of "TIF" Language Interface */
+
+#include "pltop.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gsstate.h"
+#include "strimpl.h"
+#include "gscoord.h"
+#include "tiffio.h"
+
+/* Forward decls */
+
+/************************************************************/
+/******** Language wrapper implementation (see pltop.h) *****/
+/************************************************************/
+
+typedef enum
+{
+ ii_state_identifying = 0,
+ ii_state_tiff,
+ ii_state_tiff_header,
+ ii_state_tiff_decode,
+ ii_state_flush
+} ii_state;
+
+/*
+ * Tiff interpreter instance
+ */
+typedef struct tiff_interp_instance_s {
+ gs_memory_t *memory;
+ gs_memory_t *cmemory;
+ gx_device *dev;
+ gx_device *nulldev;
+
+ gs_color_space *gray;
+ gs_color_space *rgb;
+ gs_color_space *cmyk;
+
+ /* Tiff parser state machine */
+ ii_state state;
+
+ int pages;
+
+ uint32_t bpp;
+ uint32_t bpc;
+ uint32_t cs;
+ uint32_t width;
+ uint32_t height;
+ uint32_t xresolution;
+ uint32_t yresolution;
+ uint32_t tile_height;
+ uint32_t tile_width;
+
+ uint32_t num_comps;
+ uint32_t byte_width;
+ uint32_t y;
+
+ uint32_t bytes_available_on_entry;
+
+ gs_image_t image;
+ gs_image_enum *penum;
+ gs_gstate *pgs;
+
+ size_t buffer_full;
+ size_t buffer_max;
+ byte *tiff_buffer;
+ size_t file_pos;
+ TIFF *handle;
+
+ byte *samples;
+
+} tiff_interp_instance_t;
+
+static int
+tiff_detect_language(const char *s, int len)
+{
+ const byte *hdr = (const byte *)s;
+ if (len >= 4) {
+ if (hdr[0] == 'I' && hdr[1] == 'I' && hdr[2] == 42 && hdr[3] == 0)
+ return 100; /* Intel (LSB) order */
+ if (hdr[0] == 'M' && hdr[1] == 'M' && hdr[2] == 0 && hdr[3] == 42)
+ return 100; /* Motorola (MSB) order */
+ }
+
+ return 0;
+}
+
+static const pl_interp_characteristics_t tiff_characteristics = {
+ "TIFF",
+ tiff_detect_language,
+ "Artifex",
+ "0.01",
+ "18 Nov 2019",
+ 1 /* minimum input size */
+};
+
+/* Get implementation's characteristics */
+static const pl_interp_characteristics_t * /* always returns a descriptor */
+tiff_impl_characteristics(const pl_interp_implementation_t *impl) /* implementation of interpreter to alloc */
+{
+ return &tiff_characteristics;
+}
+
+static void
+tiff_deallocate(tiff_interp_instance_t *tiff)
+{
+ if (tiff == NULL)
+ return;
+
+ rc_decrement_cs(tiff->gray, "tiff_deallocate");
+ rc_decrement_cs(tiff->rgb, "tiff_deallocate");
+ rc_decrement_cs(tiff->cmyk, "tiff_deallocate");
+
+ if (tiff->pgs != NULL)
+ gs_gstate_free_chain(tiff->pgs);
+ gs_free_object(tiff->memory, tiff, "tiff_impl_allocate_interp_instance");
+}
+
+/* Deallocate a interpreter instance */
+static int
+tiff_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)impl->interp_client_data;
+
+ tiff_deallocate(tiff);
+ impl->interp_client_data = NULL;
+
+ return 0;
+}
+
+/* Do per-instance interpreter allocation/init. */
+static int
+tiff_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem)
+{
+ int code;
+ tiff_interp_instance_t *tiff
+ = (tiff_interp_instance_t *)gs_alloc_bytes(mem,
+ sizeof(tiff_interp_instance_t),
+ "tiff_impl_allocate_interp_instance");
+ if (!tiff)
+ return_error(gs_error_VMerror);
+ memset(tiff, 0, sizeof(*tiff));
+
+ tiff->memory = mem;
+ tiff->pgs = gs_gstate_alloc(mem);
+ if (tiff->pgs == NULL)
+ goto failVM;
+
+ /* Push one save level onto the stack to assuage the memory handling */
+ code = gs_gsave(tiff->pgs);
+ if (code < 0)
+ goto fail;
+
+ code = gsicc_init_iccmanager(tiff->pgs);
+ if (code < 0)
+ goto fail;
+
+ tiff->gray = gs_cspace_new_ICC(mem, tiff->pgs, 1);
+ tiff->rgb = gs_cspace_new_ICC(mem, tiff->pgs, 3);
+ tiff->cmyk = gs_cspace_new_ICC(mem, tiff->pgs, 4);
+
+ impl->interp_client_data = tiff;
+
+ return 0;
+
+failVM:
+ code = gs_note_error(gs_error_VMerror);
+fail:
+ (void)tiff_deallocate(tiff);
+ return code;
+}
+
+/*
+ * Get the allocator with which to allocate a device
+ */
+static gs_memory_t *
+tiff_impl_get_device_memory(pl_interp_implementation_t *impl)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)impl->interp_client_data;
+
+ return tiff->dev ? tiff->dev->memory : NULL;
+}
+
+#if 0 /* UNUSED */
+static int
+tiff_impl_set_param(pl_interp_implementation_t *impl,
+ pl_set_param_type type,
+ const char *param,
+ const void *val)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)impl->interp_client_data;
+
+ /* No params set here */
+ return 0;
+}
+
+static int
+tiff_impl_add_path(pl_interp_implementation_t *impl,
+ const char *path)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)impl->interp_client_data;
+
+ /* No paths to add */
+ return 0;
+}
+
+static int
+tiff_impl_post_args_init(pl_interp_implementation_t *impl)
+{
+ tiff_interp_instance_t *tiff = (jpg_interp_instance_t *)impl->interp_client_data;
+
+ /* No post args processing */
+ return 0;
+}
+#endif
+
+/* Prepare interp instance for the next "job" */
+static int
+tiff_impl_init_job(pl_interp_implementation_t *impl,
+ gx_device *device)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)impl->interp_client_data;
+
+ tiff->dev = device;
+ tiff->state = ii_state_identifying;
+
+ return 0;
+}
+
+#if 0 /* UNUSED */
+static int
+tiff_impl_run_prefix_commands(pl_interp_implementation_t *impl,
+ const char *prefix)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)impl->interp_client_data;
+
+ return 0;
+}
+
+static int
+tiff_impl_process_file(pl_interp_implementation_t *impl, const char *filename)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)impl->interp_client_data;
+
+ return 0;
+}
+#endif
+
+/* Do any setup for parser per-cursor */
+static int /* ret 0 or +ve if ok, else -ve error code */
+tiff_impl_process_begin(pl_interp_implementation_t * impl)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)impl->interp_client_data;
+
+ return 0;
+}
+
+/* Ensure we have 'required' bytes to read, and further ensure
+ * that we have no UEL's within those bytes. */
+static int
+ensure_bytes(tiff_interp_instance_t *jpg, stream_cursor_read *pr, int required)
+{
+ int n;
+ const uint8_t *p = pr->ptr+1;
+ const uint8_t *q;
+ int avail;
+
+ /* Find out how many bytes we need to check */
+ n = pr->limit - pr->ptr;
+ if (n > required)
+ n = required;
+
+ /* Make sure there are no UELs in that block */
+ q = p + n;
+ while (p != q) {
+ while (p != q && *p != '\033')
+ p++;
+ if (p == q)
+ break;
+ avail = pr->limit - pr->ptr;
+ if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
+ /* At least a partial match to a UEL */
+ return avail < 9 ? gs_error_NeedInput : gs_error_InterpreterExit;
+ }
+ p++;
+ }
+
+ /* If we have enough bytes, great, if not, get some more */
+ return (n < required) ? gs_error_NeedInput : 0;
+}
+
+static int
+flush_to_uel(stream_cursor_read *pr)
+{
+ const uint8_t *p = pr->ptr+1;
+ const uint8_t *q = pr->limit+1;
+ int avail;
+
+ while (p != q) {
+ while (p != q && *p != '\033')
+ p++;
+ if (p == q)
+ break;
+ avail = pr->limit - pr->ptr;
+ if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
+ /* At least a partial match to a UEL. Bin everything to
+ * the start of the match. */
+ pr->ptr = p-1;
+ if (avail == 9) /* Complete match. Exit! */
+ return gs_error_InterpreterExit;
+ /* Partial match. Get more data. */
+ return gs_error_NeedInput;
+ }
+ p++;
+ }
+
+ pr->ptr = pr->limit;
+
+ return 0;
+}
+
+static int
+bytes_until_uel(const stream_cursor_read *pr)
+{
+ const uint8_t *p = pr->ptr+1;
+ const uint8_t *q = pr->limit+1;
+ int avail;
+
+ while (p != q) {
+ while (p != q && *p != '\033')
+ p++;
+ if (p == q)
+ break;
+ avail = pr->limit - pr->ptr;
+ if (memcmp(p, "\033%-12345X", min(avail, 9)) == 0) {
+ /* At least a partial match to a UEL. Everything up to
+ * the start of the match is up for grabs. */
+ return p - (pr->ptr+1);
+ }
+ p++;
+ }
+
+ return pr->limit - pr->ptr;
+}
+
+static int
+get32be(stream_cursor_read *pr)
+{
+ int v = pr->ptr[1] << 24;
+ v |= pr->ptr[2] << 16;
+ v |= pr->ptr[3] << 8;
+ v |= pr->ptr[4];
+ pr->ptr += 4;
+
+ return v;
+}
+
+static int
+get8(stream_cursor_read *pr)
+{
+ return *++(pr->ptr);
+}
+
+static tmsize_t tifsReadProc(thandle_t tiff_,
+ void *buf,
+ tmsize_t size)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)tiff_;
+ tmsize_t available = tiff->buffer_full - tiff->file_pos;
+ if (available > size)
+ available = size;
+
+ memcpy(buf, &tiff->tiff_buffer[tiff->file_pos], available);
+ tiff->file_pos += available;
+
+ return available;
+}
+
+static tmsize_t tifsWriteProc(thandle_t tiff_,
+ void *buf,
+ tmsize_t size)
+{
+ return 0;
+}
+
+static toff_t tifsSeekProc(thandle_t tiff_, toff_t offset, int whence)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)tiff_;
+ size_t pos = tiff->file_pos;
+
+ if (whence == 1) { /* SEEK_CURR */
+ offset += pos;
+ } else if (whence == 2) { /* SEEK_END */
+ offset += tiff->buffer_full;
+ }
+ /* else assume SEEK_SET */
+
+ /* Clamp */
+ if (offset < 0)
+ offset = 0;
+ else if (offset > tiff->buffer_full)
+ offset = tiff->buffer_full;
+
+ tiff->file_pos = offset;
+
+ return offset;
+}
+
+static tifsCloseProc(thandle_t tiff_)
+{
+ return 0;
+}
+
+static toff_t tifsSizeProc(thandle_t tiff_)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)tiff_;
+
+ return tiff->buffer_full;
+}
+
+static int
+do_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr, int eof)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)impl->interp_client_data;
+ int code = 0;
+ ii_state ostate = (ii_state)-1;
+ int bytes_left = 0;
+
+ while (tiff->state != ostate || pr->limit - pr->ptr != bytes_left)
+ {
+ ostate = tiff->state;
+ bytes_left = pr->limit - pr->ptr;
+ switch(tiff->state)
+ {
+ case ii_state_identifying:
+ {
+ const byte *hdr;
+ /* Try and get us 4 bytes */
+ code = ensure_bytes(tiff, pr, 4);
+ if (code < 0)
+ return code;
+ hdr = pr->ptr+1;
+ if (hdr[0] == 'I' && hdr[1] == 'I' && hdr[2] == 42 && hdr[3] == 0) {
+ tiff->state = ii_state_tiff;
+ break;
+ }
+ if (hdr[0] == 'M' && hdr[1] == 'M' && hdr[2] == 0 && hdr[3] == 42) {
+ tiff->state = ii_state_tiff;
+ break;
+ }
+ tiff->state = ii_state_flush;
+ break;
+ }
+ case ii_state_tiff:
+ {
+ /* Gather data into a buffer */
+ int bytes = bytes_until_uel(pr);
+
+ if (bytes == 0 && pr->limit - pr->ptr > 9) {
+ /* No bytes until UEL, and there is space for a UEL in the buffer */
+ tiff->state = ii_state_tiff_decode;
+ tiff->file_pos = 0;
+ break;
+ }
+ if (bytes == 0 && eof) {
+ /* No bytes until UEL, and we are at eof */
+ tiff->state = ii_state_tiff_decode;
+ tiff->file_pos = 0;
+ break;
+ }
+
+ if (tiff->buffer_full + bytes > tiff->buffer_max) {
+ /* Need to expand our buffer */
+ size_t proposed = tiff->buffer_full*2;
+ if (proposed == 0)
+ proposed = 32768;
+ while (proposed < tiff->buffer_full + bytes)
+ proposed *= 2;
+
+ if (tiff->tiff_buffer == NULL) {
+ tiff->tiff_buffer = gs_alloc_bytes(tiff->memory, proposed, "tiff_buffer");
+ if (tiff->tiff_buffer == NULL) {
+ tiff->state = ii_state_flush;
+ break;
+ }
+ } else {
+ void *new_buf = gs_resize_object(tiff->memory, tiff->tiff_buffer, proposed, "tiff_buffer");
+ if (new_buf == NULL) {
+ tiff->state = ii_state_flush;
+ break;
+ }
+ tiff->tiff_buffer = new_buf;
+ }
+ tiff->buffer_max = proposed;
+ }
+
+ memcpy(&tiff->tiff_buffer[tiff->buffer_full], pr->ptr+1, bytes);
+ tiff->buffer_full += bytes;
+ pr->ptr += bytes;
+ break;
+ }
+ case ii_state_tiff_decode:
+ {
+ int tx, ty;
+ short planar;
+ float f, scale;
+ gs_color_space *cs;
+ unsigned int used;
+
+ tiff->handle = TIFFClientOpen("dummy", "rm",
+ (thandle_t)tiff,
+ tifsReadProc,
+ tifsWriteProc,
+ tifsSeekProc,
+ tifsCloseProc,
+ tifsSizeProc,
+ NULL,
+ NULL);
+ if (tiff->handle == NULL) {
+ tiff->state = ii_state_flush;
+ break;
+ }
+
+ TIFFGetField(tiff->handle, TIFFTAG_IMAGEWIDTH, &tiff->width);
+ TIFFGetField(tiff->handle, TIFFTAG_IMAGELENGTH, &tiff->height);
+ TIFFGetField(tiff->handle, TIFFTAG_TILEWIDTH, &tiff->tile_width);
+ TIFFGetField(tiff->handle, TIFFTAG_TILELENGTH, &tiff->tile_height);
+ TIFFGetField(tiff->handle, TIFFTAG_BITSPERSAMPLE, &tiff->bpc);
+ TIFFGetField(tiff->handle, TIFFTAG_SAMPLESPERPIXEL, &tiff->num_comps);
+ TIFFGetField(tiff->handle, TIFFTAG_PLANARCONFIG, &planar);
+ TIFFGetField(tiff->handle, TIFFTAG_XRESOLUTION, &f);
+ tiff->xresolution = (uint32_t)(f+0.5);
+ TIFFGetField(tiff->handle, TIFFTAG_YRESOLUTION, &f);
+ tiff->yresolution = (uint32_t)(f+0.5);
+
+ if (TIFFIsTiled(tiff->handle)) {
+ int x, y;
+ tiff->samples = gs_alloc_bytes(tiff->memory, TIFFTileSize(tiff->handle), "tiff_tile");
+ } else {
+ tiff->tile_width = tiff->width;
+ tiff->tile_height = tiff->height;
+ tiff->samples = gs_alloc_bytes(tiff->memory, TIFFScanlineSize(tiff->handle), "tiff_scan");
+ }
+ if (tiff->samples == NULL) {
+ TIFFClose(tiff->handle);
+ tiff->state = ii_state_flush;
+ break;
+ }
+
+ /* FIXME: Need to allow for LAB, YUV etc too */
+ tiff->bpp = tiff->bpc * tiff->num_comps;
+ switch(tiff->num_comps) {
+ default:
+ case 1:
+ cs = tiff->gray;
+ break;
+ case 3:
+ cs = tiff->rgb;
+ break;
+ case 4:
+ cs = tiff->cmyk;
+ break;
+ }
+
+ /* Scale to fit, if too large. */
+ scale = 1.0f;
+ if (tiff->width * tiff->dev->HWResolution[0] > tiff->dev->width * tiff->xresolution)
+ scale = ((float)tiff->dev->width * tiff->xresolution) / (tiff->width * tiff->dev->HWResolution[0]);
+ if (scale * tiff->height * tiff->dev->HWResolution[1] > tiff->dev->height * tiff->yresolution)
+ scale = ((float)tiff->dev->height * tiff->yresolution) / (tiff->height * tiff->dev->HWResolution[1]);
+
+ tiff->nulldev = gs_currentdevice(tiff->pgs);
+ rc_increment(tiff->nulldev);
+ code = gs_setdevice_no_erase(tiff->pgs, tiff->dev);
+ if (code < 0) {
+ tiff->state = ii_state_flush;
+ break;
+ }
+
+ if (TIFFIsTiled(tiff->handle) || planar == PLANARCONFIG_CONTIG) {
+ tiff->byte_width = ((tiff->bpc * tiff->num_comps * tiff->tile_width + 7)>>3);
+ } else {
+ tiff->byte_width = ((tiff->bpc * tiff->tile_width + 7)>>3) * tiff->num_comps;
+ }
+ code = gs_erasepage(tiff->pgs);
+ if (code < 0) {
+ tiff->state = ii_state_flush;
+ return code;
+ }
+ for (ty = 0; ty < tiff->height; ty += tiff->tile_height)
+ for (tx = 0; tx < tiff->width; tx += tiff->tile_width) {
+ int y, s;
+ byte *row;
+ float xext, xoffset, yext, yoffset;
+
+ tiff->penum = gs_image_enum_alloc(tiff->memory, "tiff_impl_process(penum)");
+ if (tiff->penum == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ tiff->state = ii_state_flush;
+ return code;
+ }
+
+ /* Centre - Extents and offsets are all calculated in points (1/72 of an inch) */
+ xext = (((float)tiff->width - tx * 2) * 72 * scale / tiff->xresolution);
+ xoffset = (tiff->dev->width * 72 / tiff->dev->HWResolution[0] - xext)/2;
+ yext = (((float)tiff->height - ty * 2) * 72 * scale / tiff->yresolution);
+ yoffset = (tiff->dev->height * 72 / tiff->dev->HWResolution[1] - yext)/2;
+
+ gs_initmatrix(tiff->pgs);
+
+ /* By default the ctm is set to:
+ * xres/72 0
+ * 0 -yres/72
+ * 0 dev->height * yres/72
+ * i.e. it moves the origin from being top right to being bottom left.
+ * We want to move it back, as without this, the image will be displayed
+ * upside down.
+ */
+ code = gs_translate(tiff->pgs, 0.0, tiff->dev->height * 72 / tiff->dev->HWResolution[1]);
+ if (code >= 0)
+ code = gs_translate(tiff->pgs, xoffset, -yoffset);
+ if (code >= 0)
+ code = gs_scale(tiff->pgs, scale, -scale);
+ if (code < 0) {
+ tiff->state = ii_state_flush;
+ break;
+ }
+
+ memset(&tiff->image, 0, sizeof(tiff->image));
+ gs_image_t_init(&tiff->image, cs);
+ tiff->image.BitsPerComponent = tiff->bpp/tiff->num_comps;
+ tiff->image.Width = tiff->tile_width;
+ tiff->image.Height = tiff->tile_height;
+
+ tiff->image.ImageMatrix.xx = tiff->xresolution / 72.0f;
+ tiff->image.ImageMatrix.yy = tiff->yresolution / 72.0f;
+
+ code = gs_image_init(tiff->penum,
+ &tiff->image,
+ false,
+ false,
+ tiff->pgs);
+ if (code < 0) {
+ tiff->state = ii_state_flush;
+ return code;
+ }
+
+ if (TIFFIsTiled(tiff->handle)) {
+ TIFFReadTile(tiff->handle, tiff->samples, tx, ty, 0, 0);
+ }
+
+ for (y = 0; y < tiff->tile_height; y++) {
+ if (TIFFIsTiled(tiff->handle)) {
+ row = tiff->samples + tiff->byte_width * y;
+ } else if (planar == PLANARCONFIG_CONTIG) {
+ row = tiff->samples;
+ TIFFReadScanline(tiff->handle, tiff->samples, ty+y, 0);
+ } else {
+ int span = tiff->byte_width / tiff->num_comps; /* FIXME: Avoid division */
+ row = tiff->samples;
+ for (s = 0; s < tiff->num_comps; s++)
+ TIFFReadScanline(tiff->handle, tiff->samples + span*s, ty+y, s);
+ }
+
+ code = gs_image_next(tiff->penum, row, tiff->byte_width, &used);
+ if (code < 0) {
+ tiff->state = ii_state_flush;
+ break;
+ }
+ }
+ code = gs_image_cleanup_and_free_enum(tiff->penum, tiff->pgs);
+ tiff->penum = NULL;
+ if (code < 0) {
+ tiff->state = ii_state_flush;
+ break;
+ }
+ }
+ tiff->state = ii_state_flush;
+ (void)pl_finish_page(tiff->memory->gs_lib_ctx->top_of_system,
+ tiff->pgs, 1, true);
+ break;
+ }
+ default:
+ case ii_state_flush:
+
+ if (tiff->handle) {
+ TIFFClose(tiff->handle);
+ tiff->handle = NULL;
+ }
+
+ if (tiff->penum) {
+ (void)gs_image_cleanup_and_free_enum(tiff->penum, tiff->pgs);
+ tiff->penum = NULL;
+ }
+
+ if (tiff->samples) {
+ gs_free_object(tiff->memory, tiff->samples, "tiff_impl_process(samples)");
+ tiff->samples = NULL;
+ }
+
+ if (tiff->tiff_buffer) {
+ gs_free_object(tiff->memory, tiff->tiff_buffer, "tiff_impl_process(tiff_buffer)");
+ tiff->tiff_buffer = NULL;
+ }
+ /* We want to bin any data we get up to, but not including
+ * a UEL. */
+ return flush_to_uel(pr);
+ }
+ }
+
+ return code;
+}
+
+static int
+tiff_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr) {
+ return do_impl_process(impl, pr, 0);
+}
+
+static int
+tiff_impl_process_end(pl_interp_implementation_t * impl)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)impl->interp_client_data;
+ int code = 0;
+
+ /* FIXME: */
+ if (code == gs_error_InterpreterExit || code == gs_error_NeedInput)
+ code = 0;
+
+ return code;
+}
+
+/* Not implemented */
+static int
+tiff_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor)
+{
+ const byte *p = cursor->ptr;
+ const byte *rlimit = cursor->limit;
+
+ /* Skip to, but leave UEL in buffer for PJL to find later */
+ for (; p < rlimit; ++p)
+ if (p[1] == '\033') {
+ uint avail = rlimit - p;
+
+ if (memcmp(p + 1, "\033%-12345X", min(avail, 9)))
+ continue;
+ if (avail < 9)
+ break;
+ cursor->ptr = p;
+ return 1; /* found eoj */
+ }
+ cursor->ptr = p;
+ return 0; /* need more */
+}
+
+/* Parser action for end-of-file */
+static int
+tiff_impl_process_eof(pl_interp_implementation_t *impl)
+{
+ stream_cursor_read r;
+
+ r.ptr = NULL;
+ r.limit = NULL;
+ return do_impl_process(impl, &r, 1);
+}
+
+/* Report any errors after running a job */
+static int
+tiff_impl_report_errors(pl_interp_implementation_t *impl, /* interp instance to wrap up job in */
+ int code, /* prev termination status */
+ long file_position, /* file position of error, -1 if unknown */
+ bool force_to_cout /* force errors to cout */
+)
+{
+ return 0;
+}
+
+/* Wrap up interp instance after a "job" */
+static int
+tiff_impl_dnit_job(pl_interp_implementation_t *impl)
+{
+ tiff_interp_instance_t *tiff = (tiff_interp_instance_t *)impl->interp_client_data;
+
+ if (tiff->nulldev) {
+ int code = gs_setdevice(tiff->pgs, tiff->nulldev);
+ tiff->dev = NULL;
+ rc_decrement(tiff->nulldev, "tiff_impl_dnit_job(nulldevice)");
+ tiff->nulldev = NULL;
+ return code;
+ }
+ return 0;
+}
+
+/* Parser implementation descriptor */
+const pl_interp_implementation_t tiff_implementation = {
+ tiff_impl_characteristics,
+ tiff_impl_allocate_interp_instance,
+ tiff_impl_get_device_memory,
+ NULL, /* tiff_impl_set_param */
+ NULL, /* tiff_impl_add_path */
+ NULL, /* tiff_impl_post_args_init */
+ tiff_impl_init_job,
+ NULL, /* tiff_impl_run_prefix_commands */
+ NULL, /* tiff_impl_process_file */
+ tiff_impl_process_begin,
+ tiff_impl_process,
+ tiff_impl_process_end,
+ tiff_impl_flush_to_eoj,
+ tiff_impl_process_eof,
+ tiff_impl_report_errors,
+ tiff_impl_dnit_job,
+ tiff_impl_deallocate_interp_instance,
+ NULL
+};
----------------------------------------------------------------------
commit c09fbcd400e41d621659fd9323031b157f6937a8
Merge: 70d25d8 c5eb242
Author: Ken Sharp <[email protected]>
Date: Wed Nov 20 09:05:35 2019 +0000
Merge branch 'master' into pdfi
diff --cc pcl/pcl/pcfrgrnd.c
index 8463f33,31968a3..01a8f14
--- a/pcl/pcl/pcfrgrnd.c
+++ b/pcl/pcl/pcfrgrnd.c
@@@ -82,7 -82,11 +82,11 @@@ build_foreground(pcl_state_t * pcs
int num_entries;
bool is_default = false;
int code = 0;
-
+
+ if (!pindexed) {
+ return_error(gs_error_invalidaccess);
+ }
+
/*
* Check for a request for the default foreground. Since there are only
* three fixed palettes, it is sufficient to check that the palette provided
diff --cc pcl/pxl/pxpthr.c
index 1b52a57,a0a1527..f103fc3
--- a/pcl/pxl/pxpthr.c
+++ b/pcl/pxl/pxpthr.c
@@@ -85,10 -85,15 +85,15 @@@ pxPassthrough_pcl_state_nonpage_excepti
{
/* xl cursor -> pcl cursor position */
gs_point xlcp, pclcp, dp;
+ int code;
-
+
/* make the pcl ctm active, after resets the hpgl/2 ctm is
active. */
- pcl_set_graphics_state(global_pcs);
+ code = pcl_set_graphics_state(global_pcs);
+ if (code < 0) {
+ return code;
+ }
+
/* xl current point -> device point -> pcl current
point. If anything fails we assume the current
point is not valid and use the cap from the pcl
Summary of changes:
Makefile.in | 5 +-
base/gdevdflt.c | 4 +-
base/gdevdrop.c | 9 +-
base/gdevp14.c | 12 +-
base/gp.h | 2 +-
base/gp_dosfs.c | 2 +-
base/gp_ntfs.c | 2 +-
base/gp_os2fs.c | 2 +-
base/gp_unifn.c | 4 +-
base/gp_unifs.c | 4 +
base/gp_vms.c | 2 +-
base/gsicc.c | 3 +
base/gxclimag.c | 4 +-
base/gxpcmap.c | 10 +-
base/gxpcolor.h | 2 +-
configure.ac | 8 +-
contrib/gdevlx32.c | 13 +
gpdl/gpdl.mak | 50 ++-
gpdl/{image/imagetop.c => jpgtop.c} | 494 ++++++++++-----------
gpdl/{psi => }/psitop.c | 0
gpdl/{pwg => }/pwgtop.c | 21 +-
gpdl/tifftop.c | 829 ++++++++++++++++++++++++++++++++++++
jbig2dec/jbig2.c | 5 +-
pcl/pcl/pcfrgrnd.c | 7 +-
pcl/pcl/pctext.c | 78 ++--
pcl/pcl/pgdraw.c | 2 +-
pcl/pl/plchar.c | 4 +-
pcl/pl/plimpl.c | 11 +-
pcl/pl/plmain.c | 1 -
pcl/pxl/pxpthr.c | 32 +-
pcl/pxl/pxpthr.h | 2 +-
psi/msvc.mak | 1 +
psi/zpcolor.c | 2 +-
windows/ghostpdl.vcproj | 16 +-
34 files changed, 1284 insertions(+), 359 deletions(-)
copy gpdl/{image/imagetop.c => jpgtop.c} (51%)
rename gpdl/{psi => }/psitop.c (100%)
copy gpdl/{pwg => }/pwgtop.c (96%)
create mode 100644 gpdl/tifftop.c