diff options
author | Robin Watts <Robin.Watts@artifex.com> | 2019-11-20 19:02:47 +0000 |
---|---|---|
committer | Robin Watts <Robin.Watts@artifex.com> | 2019-11-21 20:21:31 +0000 |
commit | 23ea32bf2bd205c44c9a7979b3a187199e47da59 (patch) | |
tree | 631c02830cd08df3794e2494812394113dc1a66a /gpdl | |
parent | 0c30c4cda3c2487ce52f79a952e3d0febef1d8bd (diff) | |
download | ghostpdl-23ea32bf2bd205c44c9a7979b3a187199e47da59.tar.gz |
gpdl: Add 'jbig2' language implementation.
Diffstat (limited to 'gpdl')
-rw-r--r-- | gpdl/gpdl.mak | 12 | ||||
-rw-r--r-- | gpdl/jbig2top.c | 814 |
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 +}; |