summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <Robin.Watts@artifex.com>2019-11-22 19:43:46 +0000
committerRobin Watts <Robin.Watts@artifex.com>2019-11-25 16:13:02 +0000
commit79e11b98dc078fcd92cda588ab6157a4aa14d0f0 (patch)
tree84c1aa51a4d89ea34f0c02f2d782e25ae8939529
parent113792dd347ac7b9436b9517aca91e1153fb230d (diff)
downloadghostpdl-79e11b98dc078fcd92cda588ab6157a4aa14d0f0.tar.gz
Add Jpeg2000 "language" interpreter for gpdl.
-rw-r--r--gpdl/gpdl.mak12
-rw-r--r--gpdl/jp2ktop.c737
-rw-r--r--pcl/pl/plimpl.c5
-rw-r--r--windows/ghostpdl.vcproj4
4 files changed, 757 insertions, 1 deletions
diff --git a/gpdl/gpdl.mak b/gpdl/gpdl.mak
index 9b1f33d98..d99b172b5 100644
--- a/gpdl/gpdl.mak
+++ b/gpdl/gpdl.mak
@@ -41,7 +41,11 @@ GPDL_TIFF_TOP_OBJ=$(GPDLOBJ)/$(GPDL_TIFF_TOP_OBJ_FILE)
GPDL_JBIG2_TOP_OBJ_FILE=jbig2top.$(OBJ)
GPDL_JBIG2_TOP_OBJ=$(GPDLOBJ)/$(GPDL_JBIG2_TOP_OBJ_FILE)
+GPDL_JP2K_TOP_OBJ_FILE=jp2ktop.$(OBJ)
+GPDL_JP2K_TOP_OBJ=$(GPDLOBJ)/$(GPDL_JP2K_TOP_OBJ_FILE)
+
GPDL_PSI_TOP_OBJS=\
+ $(GPDL_JP2K_TOP_OBJ)\
$(GPDL_JBIG2_TOP_OBJ)\
$(GPDL_TIFF_TOP_OBJ)\
$(GPDL_PWG_TOP_OBJ)\
@@ -58,7 +62,8 @@ LANG_CFLAGS=\
$(D_)JPG_INCLUDED$(_D)\
$(D_)PWG_INCLUDED$(_D)\
$(ENABLE_TIFF)\
- $(D_)JBIG2_INCLUDED$(_D)
+ $(D_)JBIG2_INCLUDED$(_D)\
+ $(D_)JP2K_INCLUDED$(_D)
GPDLCC=$(CC_) $(LANG_CFLAGS) $(I_)$(PSSRCDIR)$(_I) $(I_)$(PLSRCDIR)$(_I) $(I_)$(GLSRCDIR)$(_I) $(I_)$(DEVSRCDIR)$(_I) $(I_)$(GLGENDIR)$(_I) $(C_)
@@ -109,3 +114,8 @@ $(GPDL_JBIG2_TOP_OBJ): $(GPDLSRC)jbig2top.c $(AK)\
$(gxdevice_h) $(gserrors_h) $(gsstate_h) $(strimpl_h) $(gscoord_h)\
$(pltop_h) $(gsicc_manage_h) $(gspaint_h) $(plmain_h)
$(GPDLCC) $(I_)$(LDF_JB2I_) $(JBIG2_CFLAGS) $(II)$(JB2I_)$(_I) $(GPDLSRC)jbig2top.c $(GPDLO_)$(GPDL_JBIG2_TOP_OBJ_FILE)
+
+$(GPDL_JP2K_TOP_OBJ): $(GPDLSRC)jp2ktop.c $(AK)\
+ $(gxdevice_h) $(gserrors_h) $(gsstate_h) $(strimpl_h) $(gscoord_h)\
+ $(pltop_h) $(gsicc_manage_h) $(gspaint_h) $(plmain_h)
+ $(GPDLCC) $(I_)$(JPX_OPENJPEG_I_)$(D).. $(I_)$(JPX_OPENJPEG_I_) $(II)$(GLI_)$(_I) $(JPXCF_) $(I_)$(LWF_JPXI_) $(GPDLSRC)jp2ktop.c $(GPDLO_)$(GPDL_JP2K_TOP_OBJ_FILE)
diff --git a/gpdl/jp2ktop.c b/gpdl/jp2ktop.c
new file mode 100644
index 000000000..9c0b398d8
--- /dev/null
+++ b/gpdl/jp2ktop.c
@@ -0,0 +1,737 @@
+/* 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.
+*/
+
+/* jp2ktop.c */
+/* Top-level API implementation of "JP2K" Language Interface */
+
+#include "pltop.h"
+#include "gserrors.h"
+#include "gxdevice.h"
+#include "gsstate.h"
+#include "strimpl.h"
+#include "gscoord.h"
+#include "gsicc_manage.h"
+#include "gspaint.h"
+#include "plmain.h"
+#ifdef USE_LWF_JP2
+#include "sjpx_luratech.h"
+#else
+#include "sjpx_openjpeg.h"
+#endif
+#include "stream.h"
+
+/* Forward decls */
+
+/************************************************************/
+/******** Language wrapper implementation (see pltop.h) *****/
+/************************************************************/
+
+typedef enum
+{
+ ii_state_identifying = 0,
+ ii_state_jp2k,
+ ii_state_jp2k_header,
+ ii_state_jp2k_data,
+ ii_state_flush
+} ii_state;
+
+/*
+ * JP2K interpreter instance
+ */
+typedef struct jp2k_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 bpp;
+ uint32_t width;
+ uint32_t height;
+ uint32_t xresolution;
+ uint32_t yresolution;
+ uint32_t copies;
+
+ 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_jpxd_state jp2k_state;
+ byte stream_buffer[2048];
+
+} jp2k_interp_instance_t;
+
+static int
+jp2k_detect_language(const char *s_, int len)
+{
+ const unsigned char *s = (const unsigned char *)s_;
+ if (len >= 12) {
+ if (s[0] == 0x00 &&
+ s[1] == 0x00 &&
+ s[2] == 0x00 &&
+ s[3] == 0x0C &&
+ s[4] == 0x6a &&
+ s[5] == 0x50 &&
+ s[6] == 0x1a &&
+ s[7] == 0x1a &&
+ s[8] == 0x0d &&
+ s[9] == 0x0a &&
+ s[10] == 0x87 &&
+ s[11] == 0x0a)
+ return 100; /* JP2 file */
+ }
+ if (len >= 4) {
+ if (s[0] == 0xff &&
+ s[1] == 0x4f &&
+ s[2] == 0xff &&
+ s[3] == 0x51)
+ return 100; /* J2K stream */
+ }
+
+ return 0;
+}
+
+static const pl_interp_characteristics_t jp2k_characteristics = {
+ "JP2K",
+ jp2k_detect_language,
+ "Artifex",
+ "0.01",
+ "22 Nov 2019",
+ 1 /* minimum input size */
+};
+
+/* Get implementation's characteristics */
+static const pl_interp_characteristics_t * /* always returns a descriptor */
+jp2k_impl_characteristics(const pl_interp_implementation_t *impl) /* implementation of interpreter to alloc */
+{
+ return &jp2k_characteristics;
+}
+
+static void
+jp2k_deallocate(jp2k_interp_instance_t *jp2k)
+{
+ if (jp2k == NULL)
+ return;
+
+ rc_decrement_cs(jp2k->gray, "jp2k_deallocate");
+ rc_decrement_cs(jp2k->rgb, "jp2k_deallocate");
+ rc_decrement_cs(jp2k->cmyk, "jp2k_deallocate");
+
+ if (jp2k->pgs != NULL)
+ gs_gstate_free_chain(jp2k->pgs);
+ gs_free_object(jp2k->memory, jp2k, "jp2k_impl_allocate_interp_instance");
+}
+
+/* Deallocate a interpreter instance */
+static int
+jp2k_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
+{
+ jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
+
+ jp2k_deallocate(jp2k);
+ impl->interp_client_data = NULL;
+
+ return 0;
+}
+
+/* Do per-instance interpreter allocation/init. */
+static int
+jp2k_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem)
+{
+ int code;
+ jp2k_interp_instance_t *jp2k
+ = (jp2k_interp_instance_t *)gs_alloc_bytes(mem,
+ sizeof(jp2k_interp_instance_t),
+ "jp2k_impl_allocate_interp_instance");
+ if (!jp2k)
+ return_error(gs_error_VMerror);
+ memset(jp2k, 0, sizeof(*jp2k));
+
+ jp2k->memory = mem;
+ jp2k->pgs = gs_gstate_alloc(mem);
+ if (jp2k->pgs == NULL)
+ goto failVM;
+
+ /* Push one save level onto the stack to assuage the memory handling */
+ code = gs_gsave(jp2k->pgs);
+ if (code < 0)
+ goto fail;
+
+ code = gsicc_init_iccmanager(jp2k->pgs);
+ if (code < 0)
+ goto fail;
+
+ jp2k->gray = gs_cspace_new_ICC(mem, jp2k->pgs, 1);
+ jp2k->rgb = gs_cspace_new_ICC(mem, jp2k->pgs, 3);
+ jp2k->cmyk = gs_cspace_new_ICC(mem, jp2k->pgs, 4);
+
+ impl->interp_client_data = jp2k;
+
+ return 0;
+
+failVM:
+ code = gs_note_error(gs_error_VMerror);
+fail:
+ (void)jp2k_deallocate(jp2k);
+ return code;
+}
+
+/*
+ * Get the allocator with which to allocate a device
+ */
+static gs_memory_t *
+jp2k_impl_get_device_memory(pl_interp_implementation_t *impl)
+{
+ jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
+
+ return jp2k->dev ? jp2k->dev->memory : NULL;
+}
+
+#if 0 /* UNUSED */
+static int
+jp2k_impl_set_param(pl_interp_implementation_t *impl,
+ pl_set_param_type type,
+ const char *param,
+ const void *val)
+{
+ jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
+
+ /* No params set here */
+ return 0;
+}
+
+static int
+jp2k_impl_add_path(pl_interp_implementation_t *impl,
+ const char *path)
+{
+ jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
+
+ /* No paths to add */
+ return 0;
+}
+
+static int
+jp2k_impl_post_args_init(pl_interp_implementation_t *impl)
+{
+ jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
+
+ /* No post args processing */
+ return 0;
+}
+#endif
+
+/* Prepare interp instance for the next "job" */
+static int
+jp2k_impl_init_job(pl_interp_implementation_t *impl,
+ gx_device *device)
+{
+ jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
+
+ jp2k->dev = device;
+ jp2k->state = ii_state_identifying;
+
+ return 0;
+}
+
+#if 0 /* UNUSED */
+static int
+jp2k_impl_run_prefix_commands(pl_interp_implementation_t *impl,
+ const char *prefix)
+{
+ jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
+
+ return 0;
+}
+
+static int
+jp2k_impl_process_file(pl_interp_implementation_t *impl, const char *filename)
+{
+ jp2k_interp_instance_t *jp2k = (jp2k_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 */
+jp2k_impl_process_begin(pl_interp_implementation_t * impl)
+{
+ 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(jp2k_interp_instance_t *jp2k, 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
+do_process(jp2k_interp_instance_t *jp2k, stream_cursor_read * pr, bool eof)
+{
+ int code = 0;
+ gs_color_space *cs;
+ ii_state ostate = jp2k->state;
+ size_t bytes_in;
+ int advanced;
+
+ /* Loop until we stop 'advancing'. */
+ do
+ {
+ advanced = 0;
+ bytes_in = pr->limit - pr->ptr;
+ switch(jp2k->state)
+ {
+ case ii_state_identifying:
+ {
+ const byte *s = pr->ptr+1;
+
+ code = ensure_bytes(jp2k, pr, 4);
+ if (code < 0)
+ return code;
+ if (s[0] == 0xff &&
+ s[1] == 0x4f &&
+ s[2] == 0xff &&
+ s[3] == 0x51) {
+ jp2k->state = ii_state_jp2k_header;
+ break;
+ }
+
+ code = ensure_bytes(jp2k, pr, 12);
+ if (code < 0)
+ return code;
+ if (s[0] == 0x00 &&
+ s[1] == 0x00 &&
+ s[2] == 0x00 &&
+ s[3] == 0x0C &&
+ s[4] == 0x6a &&
+ s[5] == 0x50 &&
+ s[6] == 0x1a &&
+ s[7] == 0x1a &&
+ s[8] == 0x0d &&
+ s[9] == 0x0a &&
+ s[10] == 0x87 &&
+ s[11] == 0x0a) {
+ jp2k->state = ii_state_jp2k_header;
+ break;
+ }
+ jp2k->state = ii_state_flush;
+ break;
+ }
+ case ii_state_jp2k_header:
+ s_init_state((stream_state *)&jp2k->jp2k_state, &s_jpxd_template, jp2k->memory);
+ if (s_jpxd_template.set_defaults)
+ s_jpxd_template.set_defaults((stream_state *)&jp2k->jp2k_state);
+
+ code = (s_jpxd_template.init)((stream_state *)&jp2k->jp2k_state);
+ if (code < 0)
+ goto early_flush;
+ jp2k->state = ii_state_jp2k_data;
+ break;
+ case ii_state_jp2k_data:
+ {
+ int n = bytes_until_uel(pr);
+ stream_cursor_read local_r;
+ stream_cursor_write local_w;
+ int status;
+ unsigned int used;
+
+ local_r.ptr = pr->ptr;
+ local_r.limit = local_r.ptr + n;
+
+ do {
+ local_w.ptr = jp2k->stream_buffer-1;
+ local_w.limit = local_w.ptr + sizeof(jp2k->stream_buffer);
+
+ status = (s_jpxd_template.process)((stream_state *)&jp2k->jp2k_state,
+ &local_r, &local_w,
+ eof);
+ /* status = 0 => need data
+ * 1 => need output space
+ * but we don't actually use this currently. */
+ (void)status;
+ /* Copy the updated pointer back */
+ pr->ptr = local_r.ptr;
+ if (local_w.ptr + 1 == jp2k->stream_buffer)
+ break; /* Failed to decode any data */
+
+ if (jp2k->width == 0) {
+ float scale, xoffset, yoffset, xext, yext;
+
+ /* First data we decoded! */
+ jp2k->width = jp2k->jp2k_state.width;
+ if (jp2k->width == 0) {
+ jp2k->state = ii_state_flush;
+ break;
+ }
+ jp2k->height = jp2k->jp2k_state.height;
+#ifdef USE_LWF_JP2
+ jp2k->bpp = jp2k->jp2k_state.bpc * jp2k->jp2k_state.ncomp;
+#else
+ jp2k->bpp = jp2k->jp2k_state.bpp;
+#endif
+ jp2k->xresolution = 72;
+ jp2k->yresolution = 72;
+ jp2k->copies = 1;
+ /* I would have thought we should use jp2k->jp2k_state.out_numcomps,
+ * but this is wrong! */
+ jp2k->num_comps = jp2k->bpp/8;
+ jp2k->byte_width = (jp2k->width * jp2k->bpp + 7)>>3;
+
+#if 0
+ switch (jp2k->jp2k_state.colorspace) {
+ default:
+ goto early_flush;
+ case gs_jpx_cs_gray:
+ cs = jp2k->gray;
+ break;
+ case gs_jpx_cs_rgb:
+ cs = jp2k->rgb;
+ break;
+ case gs_jpx_cs_cmyk:
+ cs = jp2k->cmyk;
+ break;
+ }
+#else
+ switch (jp2k->num_comps) {
+ case 1:
+ cs = jp2k->gray;
+ break;
+ case 3:
+ cs = jp2k->rgb;
+ break;
+ case 4:
+ cs = jp2k->cmyk;
+ break;
+ default:
+ goto early_flush;
+ }
+#endif
+
+ /* Scale to fit, if too large. */
+ scale = 1.0f;
+ if (jp2k->width * jp2k->dev->HWResolution[0] > jp2k->dev->width * jp2k->xresolution)
+ scale = ((float)jp2k->dev->width * jp2k->xresolution) / (jp2k->width * jp2k->dev->HWResolution[0]);
+ if (scale * jp2k->height * jp2k->dev->HWResolution[1] > jp2k->dev->height * jp2k->yresolution)
+ scale = ((float)jp2k->dev->height * jp2k->yresolution) / (jp2k->height * jp2k->dev->HWResolution[1]);
+
+ jp2k->nulldev = gs_currentdevice(jp2k->pgs);
+ rc_increment(jp2k->nulldev);
+ code = gs_setdevice_no_erase(jp2k->pgs, jp2k->dev);
+ if (code < 0)
+ goto early_flush;
+ gs_initmatrix(jp2k->pgs);
+
+ /* Centre - Extents and offsets are all calculated in points (1/72 of an inch) */
+ xext = (((float)jp2k->width) * 72 * scale / jp2k->xresolution);
+ xoffset = (jp2k->dev->width * 72 / jp2k->dev->HWResolution[0] - xext)/2;
+ yext = (((float)jp2k->height) * 72 * scale / jp2k->yresolution);
+ yoffset = (jp2k->dev->height * 72 / jp2k->dev->HWResolution[1] - yext)/2;
+
+ /* 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(jp2k->pgs, 0.0, jp2k->dev->height * 72 / jp2k->dev->HWResolution[1]);
+ if (code >= 0)
+ code = gs_translate(jp2k->pgs, xoffset, -yoffset);
+ if (code >= 0)
+ code = gs_scale(jp2k->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(jp2k->pgs);
+ if (code < 0)
+ goto early_flush;
+
+ memset(&jp2k->image, 0, sizeof(jp2k->image));
+ gs_image_t_init(&jp2k->image, cs);
+ jp2k->image.BitsPerComponent = jp2k->bpp/jp2k->num_comps;
+ jp2k->image.Width = jp2k->width;
+ jp2k->image.Height = jp2k->height;
+
+ jp2k->image.ImageMatrix.xx = jp2k->xresolution / 72.0f;
+ jp2k->image.ImageMatrix.yy = jp2k->yresolution / 72.0f;
+
+ jp2k->penum = gs_image_enum_alloc(jp2k->memory, "jp2k_impl_process(penum)");
+ if (jp2k->penum == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto early_flush;
+ }
+
+ code = gs_image_init(jp2k->penum,
+ &jp2k->image,
+ false,
+ false,
+ jp2k->pgs);
+ if (code < 0)
+ goto early_flush;
+ }
+
+ code = gs_image_next(jp2k->penum, jp2k->stream_buffer, local_w.ptr + 1 - jp2k->stream_buffer, &used);
+ if (code < 0)
+ goto flush;
+
+ jp2k->x += used;
+ while (jp2k->x >= jp2k->byte_width) {
+ jp2k->x -= jp2k->byte_width;
+ jp2k->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 (jp2k->y < jp2k->height);
+
+ if (jp2k->penum && jp2k->y == jp2k->height) {
+ code = gs_image_cleanup_and_free_enum(jp2k->penum, jp2k->pgs);
+ jp2k->penum = NULL;
+ if (code < 0)
+ goto flush;
+ code = pl_finish_page(jp2k->memory->gs_lib_ctx->top_of_system,
+ jp2k->pgs, jp2k->copies, true);
+ if (code < 0)
+ goto flush;
+ if (jp2k->pages > 0) {
+ jp2k->pages--;
+ /* If we've reached the expected end, we should probably flush to UEL */
+ if (jp2k->pages == 0)
+ jp2k->state = ii_state_flush;
+ }
+ if (jp2k->jp2k_state.templat->release)
+ jp2k->jp2k_state.templat->release((stream_state *)&jp2k->jp2k_state);
+ jp2k->state = ii_state_jp2k_header;
+ }
+ break;
+ }
+ default:
+ case ii_state_flush:
+ if (0) {
+flush:
+ if (jp2k->jp2k_state.templat->release)
+ jp2k->jp2k_state.templat->release((stream_state *)&jp2k->jp2k_state);
+early_flush:
+ jp2k->state = ii_state_flush;
+ }
+ /* We want to bin any data we get up to, but not including
+ * a UEL. */
+ return flush_to_uel(pr);
+ }
+ advanced |= (ostate != jp2k->state);
+ ostate = jp2k->state;
+ advanced |= (bytes_in != pr->limit - pr->ptr);
+ bytes_in = pr->limit - pr->ptr;
+ } while (advanced);
+
+ return code;
+}
+
+static int
+jp2k_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr)
+{
+ jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
+
+ return do_process(jp2k, pr, 0);
+}
+
+static int
+jp2k_impl_process_end(pl_interp_implementation_t * impl)
+{
+ return 0;
+}
+
+/* Not implemented */
+static int
+jp2k_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
+jp2k_impl_process_eof(pl_interp_implementation_t *impl)
+{
+ jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
+ stream_cursor_read cursor;
+
+ cursor.ptr = NULL;
+ cursor.limit = NULL;
+
+ return do_process(jp2k, &cursor, 1);
+}
+
+/* Report any errors after running a job */
+static int
+jp2k_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
+jp2k_impl_dnit_job(pl_interp_implementation_t *impl)
+{
+ jp2k_interp_instance_t *jp2k = (jp2k_interp_instance_t *)impl->interp_client_data;
+
+ if (jp2k->nulldev) {
+ int code = gs_setdevice(jp2k->pgs, jp2k->nulldev);
+ jp2k->dev = NULL;
+ rc_decrement(jp2k->nulldev, "jp2k_impl_dnit_job(nulldevice)");
+ jp2k->nulldev = NULL;
+ return code;
+ }
+ return 0;
+}
+
+/* Parser implementation descriptor */
+const pl_interp_implementation_t jp2k_implementation = {
+ jp2k_impl_characteristics,
+ jp2k_impl_allocate_interp_instance,
+ jp2k_impl_get_device_memory,
+ NULL, /* jp2k_impl_set_param */
+ NULL, /* jp2k_impl_add_path */
+ NULL, /* jp2k_impl_post_args_init */
+ jp2k_impl_init_job,
+ NULL, /* jp2k_impl_run_prefix_commands */
+ NULL, /* jp2k_impl_process_file */
+ jp2k_impl_process_begin,
+ jp2k_impl_process,
+ jp2k_impl_process_end,
+ jp2k_impl_flush_to_eoj,
+ jp2k_impl_process_eof,
+ jp2k_impl_report_errors,
+ jp2k_impl_dnit_job,
+ jp2k_impl_deallocate_interp_instance,
+ NULL
+};
diff --git a/pcl/pl/plimpl.c b/pcl/pl/plimpl.c
index ff2dba4b5..71d806c93 100644
--- a/pcl/pl/plimpl.c
+++ b/pcl/pl/plimpl.c
@@ -45,6 +45,8 @@ extern pl_interp_implementation_t tiff_implementation;
extern pl_interp_implementation_t jbig2_implementation;
+extern pl_interp_implementation_t jp2k_implementation;
+
/* Zero-terminated list of pointers to implementations */
pl_interp_implementation_t *pdl_implementations[] = {
&pjl_implementation,
@@ -73,6 +75,9 @@ pl_interp_implementation_t *pdl_implementations[] = {
#ifdef JBIG2_INCLUDED
&jbig2_implementation,
#endif
+#ifdef JP2K_INCLUDED
+ &jp2k_implementation,
+#endif
0
};
diff --git a/windows/ghostpdl.vcproj b/windows/ghostpdl.vcproj
index 92694aeed..3e884e004 100644
--- a/windows/ghostpdl.vcproj
+++ b/windows/ghostpdl.vcproj
@@ -310,6 +310,10 @@
>
</File>
<File
+ RelativePath="..\gpdl\jp2ktop.c"
+ >
+ </File>
+ <File
RelativePath="..\gpdl\jpgtop.c"
>
</File>