summaryrefslogtreecommitdiff
path: root/gpdl
diff options
context:
space:
mode:
authorRobin Watts <Robin.Watts@artifex.com>2019-11-20 19:02:47 +0000
committerRobin Watts <Robin.Watts@artifex.com>2019-11-21 20:21:31 +0000
commit23ea32bf2bd205c44c9a7979b3a187199e47da59 (patch)
tree631c02830cd08df3794e2494812394113dc1a66a /gpdl
parent0c30c4cda3c2487ce52f79a952e3d0febef1d8bd (diff)
downloadghostpdl-23ea32bf2bd205c44c9a7979b3a187199e47da59.tar.gz
gpdl: Add 'jbig2' language implementation.
Diffstat (limited to 'gpdl')
-rw-r--r--gpdl/gpdl.mak12
-rw-r--r--gpdl/jbig2top.c814
2 files changed, 825 insertions, 1 deletions
diff --git a/gpdl/gpdl.mak b/gpdl/gpdl.mak
index 7c674b73f..9497810aa 100644
--- a/gpdl/gpdl.mak
+++ b/gpdl/gpdl.mak
@@ -38,7 +38,11 @@ GPDL_PWG_TOP_OBJ=$(GPDLOBJ)/$(GPDL_PWG_TOP_OBJ_FILE)
GPDL_TIFF_TOP_OBJ_FILE=tifftop.$(OBJ)
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_PSI_TOP_OBJS=\
+ $(GPDL_JBIG2_TOP_OBJ)\
$(GPDL_TIFF_TOP_OBJ)\
$(GPDL_PWG_TOP_OBJ)\
$(GPDL_JPG_TOP_OBJ)\
@@ -53,7 +57,8 @@ LANG_CFLAGS=\
$(ENABLE_URF)\
$(D_)JPG_INCLUDED$(_D)\
$(D_)PWG_INCLUDED$(_D)\
- $(ENABLE_TIFF)
+ $(ENABLE_TIFF)\
+ $(D_)JBIG2_INCLUDED$(_D)
GPDLCC=$(CC_) $(LANG_CFLAGS) $(I_)$(PSSRCDIR)$(_I) $(I_)$(PLSRCDIR)$(_I) $(I_)$(GLSRCDIR)$(_I) $(I_)$(DEVSRCDIR)$(_I) $(I_)$(GLGENDIR)$(_I) $(C_)
@@ -98,3 +103,8 @@ $(GPDL_TIFF_TOP_OBJ): $(GPDLSRC)tifftop.c $(AK)\
$(gxdevice_h) $(gserrors_h) $(gsstate_h) $(strimpl_h) $(gscoord_h)\
$(pltop_h)
$(GPDLCC) $(II)$(TI_)$(_I) $(GPDLSRC)tifftop.c $(GPDLO_)$(GPDL_TIFF_TOP_OBJ_FILE)
+
+$(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)
diff --git a/gpdl/jbig2top.c b/gpdl/jbig2top.c
new file mode 100644
index 000000000..4f872215f
--- /dev/null
+++ b/gpdl/jbig2top.c
@@ -0,0 +1,814 @@
+/* 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.
+*/
+
+/* jbig2top.c */
+/* Top-level API implementation of "JBIG2" 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_LDF_JB2
+#include <ldf_jb2.h>
+#else
+#include "jbig2.h"
+#endif
+
+/* Forward decls */
+
+/************************************************************/
+/******** Language wrapper implementation (see pltop.h) *****/
+/************************************************************/
+
+typedef enum
+{
+ ii_state_identifying = 0,
+ ii_state_jbig2,
+ ii_state_jbig2_start,
+ ii_state_jbig2_decode,
+ ii_state_flush
+} ii_state;
+
+/*
+ * JBig2 interpreter instance
+ */
+typedef struct jbig2_interp_instance_s {
+ gs_memory_t *memory;
+ gs_memory_t *cmemory;
+ gx_device *dev;
+ gx_device *nulldev;
+
+ gs_color_space *gray;
+
+ /* JBig2 parser state machine */
+ ii_state state;
+
+ gs_image_t image;
+ gs_image_enum *penum;
+ gs_gstate *pgs;
+
+#ifdef USE_LDF_JB2
+ JB2_Handle_Document doc;
+ int page;
+ size_t buffer_full;
+ size_t buffer_max;
+ byte *jbig2_buffer;
+ size_t file_pos;
+#else
+ Jbig2Ctx *jbig_ctx;
+ struct _Jbig2Allocator allocator;
+#endif
+
+
+ byte *samples;
+
+} jbig2_interp_instance_t;
+
+static int
+jbig2_detect_language(const char *s, int len)
+{
+ const byte *hdr = (const byte *)s;
+ if (len >= 8) {
+ if (hdr[0] == 0x97 &&
+ hdr[1] == 'J' &&
+ hdr[2] == 'B' &&
+ hdr[3] == '2' &&
+ hdr[4] == 0x0d &&
+ hdr[5] == 0x0a &&
+ hdr[6] == 0x1a &&
+ hdr[7] == 0x0a)
+ return 100;
+ }
+
+ return 0;
+}
+
+static const pl_interp_characteristics_t jbig2_characteristics = {
+ "JBIG2",
+ jbig2_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 */
+jbig2_impl_characteristics(const pl_interp_implementation_t *impl) /* implementation of interpreter to alloc */
+{
+ return &jbig2_characteristics;
+}
+
+static void
+jbig2_deallocate(jbig2_interp_instance_t *jbig2)
+{
+ if (jbig2 == NULL)
+ return;
+
+ rc_decrement_cs(jbig2->gray, "jbig2_deallocate");
+
+ if (jbig2->pgs != NULL)
+ gs_gstate_free_chain(jbig2->pgs);
+ gs_free_object(jbig2->memory, jbig2, "jbig2_impl_allocate_interp_instance");
+}
+
+/* Deallocate a interpreter instance */
+static int
+jbig2_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
+{
+ jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)impl->interp_client_data;
+
+ jbig2_deallocate(jbig2);
+ impl->interp_client_data = NULL;
+
+ return 0;
+}
+
+/* Do per-instance interpreter allocation/init. */
+static int
+jbig2_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem)
+{
+ int code;
+ jbig2_interp_instance_t *jbig2
+ = (jbig2_interp_instance_t *)gs_alloc_bytes(mem,
+ sizeof(jbig2_interp_instance_t),
+ "jbig2_impl_allocate_interp_instance");
+ if (!jbig2)
+ return_error(gs_error_VMerror);
+ memset(jbig2, 0, sizeof(*jbig2));
+
+ jbig2->memory = mem;
+ jbig2->pgs = gs_gstate_alloc(mem);
+ if (jbig2->pgs == NULL)
+ goto failVM;
+
+ /* Push one save level onto the stack to assuage the memory handling */
+ code = gs_gsave(jbig2->pgs);
+ if (code < 0)
+ goto fail;
+
+ code = gsicc_init_iccmanager(jbig2->pgs);
+ if (code < 0)
+ goto fail;
+
+ jbig2->gray = gs_cspace_new_ICC(mem, jbig2->pgs, 1);
+
+ impl->interp_client_data = jbig2;
+
+ return 0;
+
+failVM:
+ code = gs_note_error(gs_error_VMerror);
+fail:
+ (void)jbig2_deallocate(jbig2);
+ return code;
+}
+
+/*
+ * Get the allocator with which to allocate a device
+ */
+static gs_memory_t *
+jbig2_impl_get_device_memory(pl_interp_implementation_t *impl)
+{
+ jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)impl->interp_client_data;
+
+ return jbig2->dev ? jbig2->dev->memory : NULL;
+}
+
+/* Prepare interp instance for the next "job" */
+static int
+jbig2_impl_init_job(pl_interp_implementation_t *impl,
+ gx_device *device)
+{
+ jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)impl->interp_client_data;
+
+ jbig2->dev = device;
+ jbig2->state = ii_state_identifying;
+
+ return 0;
+}
+
+/* Do any setup for parser per-cursor */
+static int /* ret 0 or +ve if ok, else -ve error code */
+jbig2_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(jbig2_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;
+}
+
+#ifdef USE_LDF_JB2
+static void * JB2_Callback my_lur_alloc(unsigned long size,
+ void *pParam)
+{
+ jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)pParam;
+
+ return gs_alloc_bytes(jbig2->memory, size, "my_lur_alloc");
+}
+
+static JB2_Error JB2_Callback my_lur_free(void *ptr,
+ void *pParam)
+{
+ jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)pParam;
+
+ gs_free_object(jbig2->memory, ptr, "my_lur_free");
+
+ return cJB2_Error_OK;
+}
+
+static void JB2_Callback my_lur_message(const char *message,
+ JB2_Message_Level level,
+ void *messageParam)
+{
+ /* Just swallow messages for now */
+}
+
+static JB2_Size_T JB2_Callback my_lur_read(unsigned char *buffer,
+ JB2_Size_T pos,
+ JB2_Size_T size,
+ void *readParam)
+{
+ jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)readParam;
+ size_t avail;
+
+ if (pos < 0)
+ return 0;
+ if (pos > jbig2->buffer_full)
+ pos = jbig2->buffer_full;
+ avail = jbig2->buffer_full - pos;
+ if (avail > size)
+ avail = size;
+
+ memcpy(buffer, &jbig2->jbig2_buffer[pos], avail);
+
+ return (JB2_Size_T)avail;
+}
+
+static JB2_Error JB2_Callback my_output(unsigned char *buffer,
+ unsigned long rownum,
+ unsigned long width,
+ unsigned long bps,
+ void *param)
+{
+ jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)param;
+ int code;
+ unsigned int used;
+
+ if (bps != 1)
+ return cJB2_Error_Failure_Output;
+
+ code = gs_image_next(jbig2->penum,
+ buffer,
+ (width+7)>>3,
+ &used);
+
+ return (code < 0) ? cJB2_Error_Failure_Output : 0;
+
+}
+
+#else
+static void my_errors(void *data, const char *msg, Jbig2Severity severity, int32_t seg_idx)
+{
+ /* Do nothing */
+}
+
+static void *my_alloc(Jbig2Allocator *allocator, size_t size)
+{
+ jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)(((char *)allocator)-offsetof(jbig2_interp_instance_t, allocator));
+
+ return gs_alloc_bytes(jbig2->memory, size, "jbig2(my_alloc)");
+}
+
+static void my_free(Jbig2Allocator *allocator, void *p)
+{
+ jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)(((char *)allocator)-offsetof(jbig2_interp_instance_t, allocator));
+
+ gs_free_object(jbig2->memory, p, "jbig2(my_free)");
+}
+
+static void *my_realloc(Jbig2Allocator *allocator, void *p, size_t size)
+{
+ jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)(((char *)allocator)-offsetof(jbig2_interp_instance_t, allocator));
+
+ return gs_resize_object(jbig2->memory, p, size, "jbig2(my_realloc)");
+}
+#endif
+
+static int
+do_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr, int eof)
+{
+ jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)impl->interp_client_data;
+ int code = 0;
+ ii_state ostate = (ii_state)-1;
+ int bytes_left = 0;
+
+ while (jbig2->state != ostate || pr->limit - pr->ptr != bytes_left)
+ {
+ ostate = jbig2->state;
+ bytes_left = pr->limit - pr->ptr;
+ switch(jbig2->state)
+ {
+ case ii_state_identifying:
+ {
+ const byte *hdr;
+ /* Try and get us 8 bytes */
+ code = ensure_bytes(jbig2, pr, 8);
+ if (code < 0)
+ return code;
+ hdr = pr->ptr+1;
+ if (hdr[0] == 0x97 &&
+ hdr[1] == 'J' &&
+ hdr[2] == 'B' &&
+ hdr[3] == '2' &&
+ hdr[4] == 0x0d &&
+ hdr[5] == 0x0a &&
+ hdr[6] == 0x1a &&
+ hdr[7] == 0x0a) {
+ jbig2->state = ii_state_jbig2;
+ break;
+ }
+ jbig2->state = ii_state_flush;
+ break;
+ }
+ case ii_state_jbig2:
+ {
+ /* 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 */
+ jbig2->state = ii_state_jbig2_start;
+ break;
+ }
+ if (bytes == 0 && eof) {
+ /* No bytes until UEL, and we are at eof */
+ jbig2->state = ii_state_jbig2_start;
+ break;
+ }
+
+#ifdef USE_LDF_JB2
+ /* For luratech, we need to gather all the data into a buffer.
+ * Once we've got it all, we drop to the next state and pass
+ * it in/decode it all at once. */
+ if (jbig2->buffer_full + bytes > jbig2->buffer_max) {
+ /* Need to expand our buffer */
+ size_t proposed = jbig2->buffer_full*2;
+ if (proposed == 0)
+ proposed = 32768;
+ while (proposed < jbig2->buffer_full + bytes)
+ proposed *= 2;
+
+ if (jbig2->jbig2_buffer == NULL) {
+ jbig2->jbig2_buffer = gs_alloc_bytes(jbig2->memory, proposed, "jbig2_buffer");
+ if (jbig2->jbig2_buffer == NULL) {
+ jbig2->state = ii_state_flush;
+ break;
+ }
+ } else {
+ void *new_buf = gs_resize_object(jbig2->memory, jbig2->jbig2_buffer, proposed, "jbig2_buffer");
+ if (new_buf == NULL) {
+ jbig2->state = ii_state_flush;
+ break;
+ }
+ jbig2->jbig2_buffer = new_buf;
+ }
+ jbig2->buffer_max = proposed;
+ }
+
+ memcpy(&jbig2->jbig2_buffer[jbig2->buffer_full], pr->ptr+1, bytes);
+ jbig2->buffer_full += bytes;
+ pr->ptr += bytes;
+#else
+ if (jbig2->jbig_ctx == NULL) {
+ jbig2->allocator.alloc = &my_alloc;
+ jbig2->allocator.free = &my_free;
+ jbig2->allocator.realloc = &my_realloc;
+ jbig2->jbig_ctx = jbig2_ctx_new(&jbig2->allocator,
+ 0, /* Options */
+ NULL, /* Global ctx */
+ &my_errors,
+ jbig2);
+ if (jbig2->jbig_ctx == NULL) {
+ jbig2->state = ii_state_flush;
+ break;
+ }
+ }
+ if (jbig2_data_in(jbig2->jbig_ctx, pr->ptr+1, bytes)) {
+ jbig2->state = ii_state_flush;
+ break;
+ }
+ pr->ptr += bytes;
+#endif
+ break;
+ }
+ case ii_state_jbig2_start:
+ /* This state exists so we can change back to it after
+ * a successful decode. It avoids the enclosing loop
+ * exiting after the first image of a jbig2 due to the
+ * state not having changed. We could avoid this by using
+ * a while loop in the "decode" state below, but that would
+ * make breaking harder. */
+ jbig2->state = ii_state_jbig2_decode;
+ break;
+ case ii_state_jbig2_decode:
+ {
+ float xext, yext, xoffset, yoffset, scale;
+ unsigned long y, w, h;
+ unsigned int used;
+#ifdef USE_LDF_JB2
+ JB2_Error error;
+
+ if (jbig2->doc == NULL) {
+ error = JB2_Document_Start(&jbig2->doc,
+ my_lur_alloc, jbig2->memory,
+ my_lur_free, jbig2->memory,
+ my_lur_read, jbig2,
+ my_lur_message, jbig2);
+#if defined(JB2_LICENSE_NUM_1) && defined(JB2_LICENSE_NUM_2)
+ if (error == cJB2_Error_OK) {
+ /* set the license keys if appropriate */
+ error = JB2_Document_Set_License(jbig2->doc,
+ JB2_LICENSE_NUM_1,
+ JB2_LICENSE_NUM_2);
+ }
+#endif
+ if (error != cJB2_Error_OK) {
+ jbig2->state = ii_state_flush;
+ break;
+ }
+ }
+ /* decode relevent image parameters */
+ error = JB2_Document_Set_Page(jbig2->doc, jbig2->page++);
+ if (error == cJB2_Error_Invalid_Index) {
+ /* Normal exit! */
+ jbig2->state = ii_state_flush;
+ break;
+ }
+ if (error == cJB2_Error_OK)
+ error = JB2_Document_Get_Property(jbig2->doc,
+ cJB2_Prop_Page_Width,
+ &w);
+ if (error == cJB2_Error_OK)
+ error = JB2_Document_Get_Property(jbig2->doc,
+ cJB2_Prop_Page_Height,
+ &h);
+ if (error != cJB2_Error_OK) {
+ jbig2->state = ii_state_flush;
+ break;
+ }
+#else
+ Jbig2Image *img = jbig2_page_out(jbig2->jbig_ctx);
+ if (img == NULL) {
+ jbig2->state = ii_state_flush;
+ break;
+ }
+ w = img->width;
+ h = img->height;
+#endif
+
+ /* Scale to fit, if too large. */
+ scale = 1.0f;
+ if (w * jbig2->dev->HWResolution[0] > jbig2->dev->width * 200)
+ scale = ((float)jbig2->dev->width * 200) / (w * jbig2->dev->HWResolution[0]);
+ if (scale * h * jbig2->dev->HWResolution[1] > jbig2->dev->height * 200)
+ scale = ((float)jbig2->dev->height * 200) / (h * jbig2->dev->HWResolution[1]);
+
+ jbig2->nulldev = gs_currentdevice(jbig2->pgs);
+ rc_increment(jbig2->nulldev);
+ code = gs_setdevice_no_erase(jbig2->pgs, jbig2->dev);
+ if (code < 0)
+ goto fail_during_decode;
+
+ code = gs_erasepage(jbig2->pgs);
+ if (code < 0)
+ goto fail_during_decode;
+
+ jbig2->penum = gs_image_enum_alloc(jbig2->memory, "jbig2_impl_process(penum)");
+ if (jbig2->penum == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail_during_decode;
+ }
+
+ /* Centre - Extents and offsets are all calculated in points (1/72 of an inch) */
+ xext = (((float)w) * 72 * scale / 200);
+ xoffset = (jbig2->dev->width * 72 / jbig2->dev->HWResolution[0] - xext)/2;
+ yext = (((float)h) * 72 * scale / 200);
+ yoffset = (jbig2->dev->height * 72 / jbig2->dev->HWResolution[1] - yext)/2;
+
+ gs_initmatrix(jbig2->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(jbig2->pgs, 0.0, jbig2->dev->height * 72 / jbig2->dev->HWResolution[1]);
+ if (code >= 0)
+ code = gs_translate(jbig2->pgs, xoffset, -yoffset);
+ if (code >= 0)
+ code = gs_scale(jbig2->pgs, scale, -scale);
+ if (code < 0)
+ goto fail_during_decode;
+
+ memset(&jbig2->image, 0, sizeof(jbig2->image));
+ gs_image_t_init(&jbig2->image, jbig2->gray);
+ jbig2->image.BitsPerComponent = 1;
+ jbig2->image.Width = w;
+ jbig2->image.Height = h;
+
+ jbig2->image.ImageMatrix.xx = 200.0f/72.0f;
+ jbig2->image.ImageMatrix.yy = 200.0f/72.0f;
+ jbig2->image.Decode[0] = 1.0f;
+ jbig2->image.Decode[1] = 0.0f;
+
+ code = gs_image_init(jbig2->penum,
+ &jbig2->image,
+ false,
+ false,
+ jbig2->pgs);
+ if (code < 0)
+ goto fail_during_decode;
+
+#ifdef USE_LDF_JB2
+ {
+ JB2_Scaling_Factor scale;
+ JB2_Rect rect;
+ scale.ulScaleUpFactor = 1;
+ scale.ulScaleDownFactor = 1;
+ rect.ulLeft = 0;
+ rect.ulRight = w;
+ rect.ulTop = 0;
+ rect.ulBottom = h;
+ error = JB2_Document_Decompress_Page(jbig2->doc,
+ scale,
+ rect,
+ my_output,
+ jbig2);
+ if (error != cJB2_Error_OK) {
+ code = (error == cJB2_Error_Failure_Alloc) ?
+gs_error_VMerror : gs_error_unknownerror;
+ goto fail_during_decode;
+ }
+ }
+#else
+ for (y = 0; y < img->height; y++) {
+ code = gs_image_next(jbig2->penum,
+ &img->data[y*img->stride],
+ (img->width+7)>>3,
+ &used);
+ if (code < 0)
+ goto fail_during_decode;
+ }
+ jbig2_release_page(jbig2->jbig_ctx, img);
+#endif
+ code = gs_image_cleanup_and_free_enum(jbig2->penum, jbig2->pgs);
+ jbig2->penum = NULL;
+ if (code < 0) {
+ jbig2->state = ii_state_flush;
+ break;
+ }
+ (void)pl_finish_page(jbig2->memory->gs_lib_ctx->top_of_system,
+ jbig2->pgs, 1, true);
+ jbig2->state = ii_state_jbig2_start;
+ break;
+fail_during_decode:
+#ifdef USE_LDF_JB2
+#else
+ jbig2_release_page(jbig2->jbig_ctx, img);
+ jbig2->state = ii_state_flush;
+#endif
+ break;
+ }
+ default:
+ case ii_state_flush:
+
+#ifdef USE_LDF_JB2
+ if (jbig2->doc) {
+ JB2_Document_End(&jbig2->doc);
+ jbig2->doc = NULL;
+ }
+
+ if (jbig2->jbig2_buffer) {
+ gs_free_object(jbig2->memory, jbig2->jbig2_buffer, "jbig2_impl_process(jbig2_buffer)");
+ jbig2->jbig2_buffer = NULL;
+ }
+#else
+ if (jbig2->jbig_ctx) {
+ jbig2_ctx_free(jbig2->jbig_ctx);
+ jbig2->jbig_ctx = NULL;
+ }
+#endif
+
+ if (jbig2->penum) {
+ (void)gs_image_cleanup_and_free_enum(jbig2->penum, jbig2->pgs);
+ jbig2->penum = 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
+jbig2_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr) {
+ return do_impl_process(impl, pr, 0);
+}
+
+static int
+jbig2_impl_process_end(pl_interp_implementation_t * impl)
+{
+ return 0;
+}
+
+/* Not implemented */
+static int
+jbig2_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
+jbig2_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
+jbig2_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
+jbig2_impl_dnit_job(pl_interp_implementation_t *impl)
+{
+ jbig2_interp_instance_t *jbig2 = (jbig2_interp_instance_t *)impl->interp_client_data;
+
+ if (jbig2->nulldev) {
+ int code = gs_setdevice(jbig2->pgs, jbig2->nulldev);
+ jbig2->dev = NULL;
+ rc_decrement(jbig2->nulldev, "jbig2_impl_dnit_job(nulldevice)");
+ jbig2->nulldev = NULL;
+ return code;
+ }
+ return 0;
+}
+
+/* Parser implementation descriptor */
+const pl_interp_implementation_t jbig2_implementation = {
+ jbig2_impl_characteristics,
+ jbig2_impl_allocate_interp_instance,
+ jbig2_impl_get_device_memory,
+ NULL, /* jbig2_impl_set_param */
+ NULL, /* jbig2_impl_add_path */
+ NULL, /* jbig2_impl_post_args_init */
+ jbig2_impl_init_job,
+ NULL, /* jbig2_impl_run_prefix_commands */
+ NULL, /* jbig2_impl_process_file */
+ jbig2_impl_process_begin,
+ jbig2_impl_process,
+ jbig2_impl_process_end,
+ jbig2_impl_flush_to_eoj,
+ jbig2_impl_process_eof,
+ jbig2_impl_report_errors,
+ jbig2_impl_dnit_job,
+ jbig2_impl_deallocate_interp_instance,
+ NULL
+};