summaryrefslogtreecommitdiff
path: root/demos
diff options
context:
space:
mode:
authorSam James <sam@gentoo.org>2022-03-29 10:27:10 +0100
committerSam James <sam@gentoo.org>2022-04-17 12:53:05 +0100
commit085bde903b9e684c3c1160e4df912bea9a660997 (patch)
treec4f5e6e9f2422e869ca5bc0b944520d451001282 /demos
parentImport Ghostscript 9.55 (diff)
downloadghostscript-gpl-patches-085bde903b9e684c3c1160e4df912bea9a660997.tar.gz
ghostscript-gpl-patches-085bde903b9e684c3c1160e4df912bea9a660997.tar.bz2
ghostscript-gpl-patches-085bde903b9e684c3c1160e4df912bea9a660997.zip
Import Ghostscript 9.56.0ghostscript-9.56
Signed-off-by: Sam James <sam@gentoo.org>
Diffstat (limited to 'demos')
-rw-r--r--demos/MATLAB/ghostpdl.m85
-rw-r--r--demos/MATLAB/gs_displaydevice.c514
-rw-r--r--demos/c/Makefile14
-rw-r--r--demos/c/ReadMe.txt40
-rw-r--r--demos/c/multi_test.c191
-rw-r--r--demos/c/multi_test.vcxproj239
-rw-r--r--demos/c/multi_test.vcxproj.filters14
-rw-r--r--demos/java/README.txt9
-rw-r--r--demos/java/gsjava/build_darwin.sh2
-rw-r--r--demos/java/gsjava/build_linux.sh2
-rw-r--r--demos/java/gsjava/build_win32.bat4
-rw-r--r--demos/java/gsjava/src/com/artifex/gsjava/GSAPI.java10
-rw-r--r--demos/java/gsjava/src/com/artifex/gsjava/GSInstance.java22
-rw-r--r--demos/java/gsviewer/README.txt10
-rwxr-xr-x[-rw-r--r--]demos/java/gsviewer/build_darwin.sh16
-rwxr-xr-x[-rw-r--r--]demos/java/gsviewer/build_linux.sh16
-rwxr-xr-x[-rw-r--r--]demos/java/gsviewer/build_win32.bat0
-rwxr-xr-x[-rw-r--r--]demos/java/gsviewer/start_darwin.sh0
-rwxr-xr-x[-rw-r--r--]demos/java/gsviewer/start_linux.sh0
-rw-r--r--demos/java/jni/gs_jni/build_darwin.sh11
-rw-r--r--demos/java/jni/gs_jni/build_linux.sh21
-rw-r--r--demos/java/jni/gs_jni/callbacks.cpp337
-rw-r--r--demos/java/jni/gs_jni/callbacks.h12
-rw-r--r--demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.cpp235
-rw-r--r--demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.h2
-rw-r--r--demos/java/jni/gs_jni/com_artifex_gsjava_util_NativePointer.cpp20
-rw-r--r--demos/java/jni/gs_jni/com_artifex_gsjava_util_NativePointer.h2
-rw-r--r--demos/java/jni/gs_jni/gs_jni.vcxproj8
-rw-r--r--demos/java/jni/gs_jni/gs_jni.vcxproj.filters9
-rw-r--r--demos/java/jni/gs_jni/install_linux.sh15
-rw-r--r--demos/java/jni/gs_jni/instance_data.cpp64
-rw-r--r--demos/java/jni/gs_jni/instance_data.h35
-rw-r--r--demos/java/jni/gs_jni/jni_util.cpp6
-rw-r--r--demos/java/jni/gs_jni/jni_util.h4
-rw-r--r--demos/java/jni/gs_jni/settings.h5
-rw-r--r--demos/java/mtdemo/Main.java51
-rw-r--r--demos/java/mtdemo/README.txt53
-rw-r--r--demos/java/mtdemo/Worker.java127
-rwxr-xr-xdemos/java/mtdemo/build_darwin.sh28
-rwxr-xr-xdemos/java/mtdemo/build_linux.sh28
-rwxr-xr-xdemos/java/mtdemo/build_win32.bat15
-rwxr-xr-xdemos/java/mtdemo/runmtd_darwin.sh5
-rwxr-xr-xdemos/java/mtdemo/runmtd_linux.sh3
-rwxr-xr-xdemos/java/mtdemo/runmtd_win32.bat3
-rwxr-xr-xdemos/python/examples.py7
-rwxr-xr-xdemos/python/gsapi.py24
46 files changed, 2079 insertions, 239 deletions
diff --git a/demos/MATLAB/ghostpdl.m b/demos/MATLAB/ghostpdl.m
new file mode 100644
index 00000000..833cf1fd
--- /dev/null
+++ b/demos/MATLAB/ghostpdl.m
@@ -0,0 +1,85 @@
+% Copyright (C) 2001-2022 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.
+%
+%
+% Paths in this example are relative to the MATLAB
+% folder in the ghostscript project. MATLAB only
+% runs on Windows 64 bit machines. Interface is
+% limited to a subset of the API methods due to
+% the fact that the MATLAB API does not allow
+% function pointers. We want to use the display
+% device callbacks to get the image data created
+% from Ghostscript. To do this, we have to wrap the
+% callback set up and associated functions into a
+% MEX-file that will serve as an interface between MATLAB
+% and the Ghostscript DLL. We use the C MEX API (as opposed to
+% the C++ MEX API). The mex file is gs_displaydevice.c
+
+% The creation of the mex file for the display device handling only
+% needs to be done once. Note the use of -R2018a as we are using
+% type-safe data access in the mex file. This also is doing a debug version -g.
+mex 'gs_displaydevice.c' -R2018a -g '../../debugbin/gpdldll64.lib'
+
+% You have to load the library to get the mex file to find the library and
+% to make direct calls to gpdldll64 directly from MATLAB
+if not(libisloaded('gpdldll64'))
+ [nf,warn] = loadlibrary('../../debugbin/gpdldll64.dll','../../pcl/pl/plapi.h')
+end
+
+% Show us the various methods in the DLL
+% libfunctions('gpdldll64')
+
+% Use planar format for MATLAB. See gdevdsp.h for what these bits mean.
+PlanarGray = 0x800802;
+PlanarRGB = 0x800804;
+PlanarCMYK = 0x800808;
+PlanarSpots = 0x880800;
+
+% Let try the display device and return the image
+page_number = 1;
+resolution = 200;
+input_file = '../../examples/tiger.eps';
+tiger_image_rgb = gs_displaydevice(input_file, PlanarRGB, page_number, resolution);
+figure(1);
+imshow(tiger_image_rgb);
+title('RGB rendering');
+
+tiger_image_gray = gs_displaydevice(input_file, PlanarGray, page_number, resolution);
+figure(2);
+imshow(tiger_image_gray);
+title('Gray rendering');
+
+% MATLAB will not display CMYK or NColor Images. We have to show
+% the separations for this case
+tiger_image_cmyk = gs_displaydevice(input_file, PlanarCMYK, page_number, resolution);
+for k=1:4
+ eval(sprintf('figure(2+k);'));
+ imshow(tiger_image_cmyk(:,:,k));
+ switch k
+ case 1
+ title('Cyan Separation');
+ case 2
+ title('Magenta Separation');
+ case 3
+ title('Yellow Separation');
+ case 4
+ title('Black Separation');
+ end
+end
+
+% At this stage, you can push the image data through MATLAB's ocr if you
+% have the Computer Vision Toolbox and want to do some sort of text
+% analysis. You can also use direct calls into gpdldll64. See the
+% C-API demo for examples on what can be done to render to file output or
+% save as PDF, PS, etc. \ No newline at end of file
diff --git a/demos/MATLAB/gs_displaydevice.c b/demos/MATLAB/gs_displaydevice.c
new file mode 100644
index 00000000..cb12c983
--- /dev/null
+++ b/demos/MATLAB/gs_displaydevice.c
@@ -0,0 +1,514 @@
+/* Copyright (C) 2001-2022 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.
+*/
+
+#include "mex.h"
+#include <memory.h>
+#include <assert.h>
+
+#ifndef GHOSTPDL
+#define GHOSTPDL 1
+#endif
+
+#if GHOSTPDL
+#include "../../pcl/pl/plapi.h" /* GSAPI - gpdf version */
+#else
+#include "psi/iapi.h" /* GSAPI - ghostscript version */
+#endif
+#include "../../devices/gdevdsp.h"
+/*--------------------------------------------------------------------*/
+/* Much of this set-up is stolen from the C-API demo code */
+
+#ifdef HIDE_POINTERS
+void *hide_pointer(void *p)
+{
+ return (p == NULL) ? NULL : (void *)1;
+}
+
+#define PTR(p) hide_pointer(p)
+
+#else
+
+#define PTR(p) p
+
+#endif
+
+#define PlanarGray 0x800802
+#define PlanarRGB 0x800804
+#define PlanarCMYK 0x800808
+#define PlanarSpots 0x880800
+#define INSTANCE_HANDLE ((void *)1234)
+#define SANITY_CHECK_VALUE 0x12345678
+
+#define SANITY_CHECK(ts) assert(ts->sanity_check_value == SANITY_CHECK_VALUE)
+
+/* All the state for a given test is contained within the following
+ * structure. */
+typedef struct {
+ /* This value should always be set to SANITY_CHECK_VALUE. It
+ * allows us to check we have a valid (or at least plausible)
+ * teststate_t pointer by checking its value. */
+ int sanity_check_value;
+
+ int w;
+ int h;
+ int r;
+ int pr;
+ int format;
+
+ int n;
+ void *mem;
+
+ mxArray **mex_image_data;
+
+} teststate_t;
+
+
+static int
+open(void *handle, void *device)
+{
+ teststate_t *ts = (teststate_t *)handle;
+
+ SANITY_CHECK(ts);
+ mexPrintf("open from C-Mex function\n");
+
+ return 0;
+}
+
+static int
+preclose(void *handle, void *device)
+{
+ teststate_t *ts = (teststate_t *)handle;
+
+ SANITY_CHECK(ts);
+ mexPrintf("preclose from C-Mex function\n");
+
+ return 0;
+}
+
+static int
+close(void *handle, void *device)
+{
+ teststate_t *ts = (teststate_t *)handle;
+
+ SANITY_CHECK(ts);
+ mexPrintf("close from C-Mex function\n");
+
+ return 0;
+}
+
+static int
+presize(void *handle, void *device,
+ int width, int height, int raster, unsigned int format)
+{
+ teststate_t *ts = (teststate_t *)handle;
+
+ SANITY_CHECK(ts);
+ mexPrintf("presize: w=%d h=%d r=%d f=%x\n",
+ width, height, raster, format);
+
+ ts->w = width;
+ ts->h = height;
+ ts->r = raster;
+ ts->format = format;
+
+ if (ts->format & DISPLAY_COLORS_GRAY)
+ ts->n = 1;
+ if (ts->format & DISPLAY_COLORS_RGB)
+ ts->n = 3;
+ if (ts->format & DISPLAY_COLORS_CMYK)
+ ts->n = 4;
+ if (ts->format & DISPLAY_COLORS_SEPARATION)
+ ts->n = 0;
+ if ((ts->format & DISPLAY_DEPTH_MASK) != DISPLAY_DEPTH_8)
+ return -1; /* Haven't written code for that! */
+
+ return 0;
+}
+
+static int
+size(void *handle, void *device, int width, int height,
+ int raster, unsigned int format, unsigned char *pimage)
+{
+ teststate_t *ts = (teststate_t *)handle;
+
+ SANITY_CHECK(ts);
+ mexPrintf("size: w=%d h=%d r=%d f=%x m=%p\n",
+ width, height, raster, format, PTR(pimage));
+
+ ts->w = width;
+ ts->h = height;
+ ts->r = raster;
+ ts->format = format;
+ ts->mem = pimage;
+
+ if (ts->format & DISPLAY_PLANAR)
+ ts->pr = ts->r * height;
+ /* When running with spots, n is not known yet. */
+ if (ts->n != 0 && ts->format & DISPLAY_PLANAR_INTERLEAVED)
+ ts->pr = ts->r / ts->n;
+
+ return 0;
+}
+
+static int
+sync(void *handle, void *device)
+{
+ teststate_t *ts = (teststate_t *)handle;
+
+ SANITY_CHECK(ts);
+
+ mexPrintf("sync\n");
+
+ return 0;
+}
+
+static int
+page(void *handle, void *device, int copies, int flush)
+{
+ teststate_t *ts = (teststate_t *)handle;
+ mwSize dims[3];
+ int num_planes;
+ int i,j,k;
+ unsigned char *des_ptr, *src_ptr, *matlab_ptr, *row_ptr;
+ size_t matlab_planar_raster;
+
+ SANITY_CHECK(ts);
+
+ mexPrintf("page: c=%d f=%d\n", copies, flush);
+
+ /* Transfer to the mex output variables at this time */
+ switch (ts->format) {
+ case PlanarGray:
+ num_planes = 1;
+ break;
+ case PlanarRGB:
+ num_planes = 3;
+ break;
+ case PlanarCMYK:
+ num_planes = 4;
+ break;
+ case PlanarSpots:
+ num_planes = 4;
+ break;
+ }
+
+ /* Matlab uses a column ordered layout for its storage of
+ arrays. So we need to do a little effort here. */
+ /* Allocate MATLAB outputs */
+ dims[0] = ts->h;
+ dims[1] = ts->w;
+ dims[2] = num_planes;
+ *(ts->mex_image_data) = mxCreateNumericArray(3, dims, mxUINT8_CLASS, mxREAL);
+
+ /* Grab image pointer */
+ matlab_ptr = (unsigned char*) mxGetUint8s(*(ts->mex_image_data));
+ matlab_planar_raster = ts->w * ts->h;
+
+ /* Copy to MATLAB memory */
+ for (k = 0; k < num_planes; k++) {
+ des_ptr = matlab_ptr + k * matlab_planar_raster;
+ src_ptr = (unsigned char*) ts->mem + k * ts->pr;
+ row_ptr = src_ptr;
+ for (i = 0; i < ts->h; i++) {
+ for (j = 0; j < ts->w; j++) {
+ des_ptr[i + j * ts->h] = *row_ptr++;
+ }
+ row_ptr = src_ptr + i * ts->r;
+ }
+ }
+ return 0;
+}
+
+static int
+update(void *handle, void *device, int x, int y, int w, int h)
+{
+ teststate_t *ts = (teststate_t *)handle;
+
+ SANITY_CHECK(ts);
+ /* This print statement just makes too much noise :) */
+ /* mexPrintf("update: x=%d y=%d w=%d h=%d\n", x, y, w, h); */
+
+ return 0;
+}
+
+static void *
+memalloc(void *handle, void *device, size_t size)
+{
+ void *ret = NULL;
+ teststate_t *ts = (teststate_t *)handle;
+
+ SANITY_CHECK(ts);
+
+ ret = malloc(size);
+
+ return ret;
+}
+
+static int
+memfree(void *handle, void *device, void *mem)
+{
+ teststate_t *ts = (teststate_t *)handle;
+
+ SANITY_CHECK(ts);
+ mexPrintf("memfree: %p\n", PTR(mem));
+
+ free(mem);
+
+ return 0;
+}
+
+static int
+separation(void *handle, void *device,
+ int component, const char *component_name,
+ unsigned short c, unsigned short m,
+ unsigned short y, unsigned short k)
+{
+ teststate_t *ts = (teststate_t *)handle;
+
+ SANITY_CHECK(ts);
+
+ mexPrintf("separation: %d %s (%x,%x,%x,%x)\n",
+ component, component_name ? component_name : "<NULL>",
+ c, m, y, k);
+ ts->n++;
+
+ /* Update the plane_raster as n has changed. */
+ if (ts->format & DISPLAY_PLANAR_INTERLEAVED)
+ ts->pr = ts->r / ts->n;
+
+ return 0;
+}
+
+static int
+adjust_band_height(void *handle, void *device, int bandheight)
+{
+ teststate_t *ts = (teststate_t *)handle;
+
+ SANITY_CHECK(ts);
+ mexPrintf("adjust_band_height: Unsupported!");
+
+ return 0;
+}
+
+static int
+rectangle_request(void *handle, void *device,
+ void **memory, int *ox, int *oy,
+ int *raster, int *plane_raster,
+ int *x, int *y, int *w, int *h)
+{
+ teststate_t *ts = (teststate_t *)handle;
+
+ SANITY_CHECK(ts);
+
+ mexPrintf("rectangle_request: Unsupported!");
+ return 0;
+}
+
+/*--------------------------------------------------------------------*/
+/* All those callback functions live in a display_callback structure
+ * that we return to the main code. This can be done using the modern
+ * "callout" method, or by using the legacy (deprecated) direct
+ * registration method. We strongly prefer the callout method as it
+ * avoids the need to pass a pointer using -sDisplayHandle. */
+static display_callback callbacks =
+{
+ sizeof(callbacks),
+ DISPLAY_VERSION_MAJOR,
+ DISPLAY_VERSION_MINOR,
+ open,
+ preclose,
+ close,
+ presize,
+ size,
+ sync,
+ page,
+ update,
+ memalloc,
+ memfree,
+ separation,
+ adjust_band_height,
+ rectangle_request
+};
+
+/*--------------------------------------------------------------------*/
+/* This is our callout handler. It handles callouts from devices within
+ * Ghostscript. It only handles a single callout, from the display
+ * device, to return the callback handler and callback handle. */
+static int
+callout(void *instance,
+ void *callout_handle,
+ const char *device_name,
+ int id,
+ int size,
+ void *data)
+{
+ teststate_t *ts = (teststate_t *)callout_handle;
+
+ SANITY_CHECK(ts);
+
+ /* We are only interested in callouts from the display device. */
+ if (strcmp(device_name, "display"))
+ return -1;
+
+ if (id == DISPLAY_CALLOUT_GET_CALLBACK)
+ {
+ /* Fill in the supplied block with the details of our callback
+ * handler, and the handle to use. In this instance, the handle
+ * is the pointer to our test structure. */
+ gs_display_get_callback_t *cb = (gs_display_get_callback_t *)data;
+ cb->callback = &callbacks;
+ cb->caller_handle = ts;
+ return 0;
+ }
+ return -1;
+}
+
+/* mexFunction is the gateway routine for the MEX-file. Like the main
+ function in a C project */
+void
+mexFunction( int nlhs, mxArray *plhs[],
+ int nrhs, const mxArray *prhs[] )
+{
+ /* Set up GS to use the display device. Register the callback
+ which will callback to a MATLAB function, whose task it is
+ to deal with the rendered image data */
+ /* Make the teststate a blank slate for us to work with. */
+ teststate_t teststate = { SANITY_CHECK_VALUE };
+
+ /* Construct the argc/argv to pass to ghostscript. */
+ int argc = 0;
+ char *argv[10];
+ char format_arg[64];
+ char first_page_arg[64];
+ char last_page_arg[64];
+ char resolution_arg[64];
+ int code = 0;
+ void *instance = NULL;
+ int format, page_number;
+ size_t m, n;
+ mxDouble *value;
+ int result;
+ char *file_name = NULL;
+ int len;
+ int resolution;
+
+ /* Error checking */
+ if (nrhs != 4) {
+ mexPrintf("Incorrect number of input args");
+ return;
+ }
+
+ if (mxGetClassID(prhs[0]) != mxCHAR_CLASS || mxIsComplex(prhs[0]) ||
+ mxGetM(prhs[0]) != 1) {
+ mexErrMsgTxt("First input must be file name");
+ return;
+ }
+ len = mxGetN(prhs[0]);
+ file_name = (char *)malloc(len + 1);
+ if (file_name == NULL) {
+ mexErrMsgTxt("Filename allocation failed");
+ return;
+ }
+ code = mxGetString(prhs[0], file_name, len + 1);
+ if (code != 0) {
+ mexErrMsgTxt("Mex string copy failed");
+ return;
+ }
+
+ if (mxGetClassID(prhs[1]) != mxUINT32_CLASS || mxIsComplex(prhs[1]) ||
+ mxGetN(prhs[1]) * mxGetM(prhs[1]) != 1) {
+ mexErrMsgTxt("Second input must be data format");
+ return;
+ }
+ format = mxGetScalar(prhs[1]);
+
+ if (!mxIsDouble(prhs[2]) || mxIsComplex(prhs[2]) ||
+ mxGetN(prhs[2]) * mxGetM(prhs[2]) != 1) {
+ mexErrMsgTxt("Third input must be page number");
+ return;
+ }
+ page_number = mxGetScalar(prhs[2]);
+
+ if (!mxIsDouble(prhs[3]) || mxIsComplex(prhs[3]) ||
+ mxGetN(prhs[3]) * mxGetM(prhs[3]) != 1) {
+ mexErrMsgTxt("Fourth input must be resolution");
+ return;
+ }
+ resolution = mxGetScalar(prhs[3]);
+
+ if (!(format == PlanarGray || format == PlanarRGB ||
+ format == PlanarCMYK || format == PlanarSpots)) {
+ mexPrintf("Data format must be planar");
+ return;
+ }
+
+ if (nlhs != 1) {
+ mexPrintf("Incorrect number of output args");
+ return;
+ }
+
+ /* Set up command */
+ argv[argc++] = "gs";
+ argv[argc++] = "-sDEVICE=display"; /* Using display device to get callback*/
+ argv[argc++] = "-dNOPAUSE";
+ argv[argc++] = first_page_arg;
+ argv[argc++] = last_page_arg;
+ argv[argc++] = format_arg;
+ argv[argc++] = resolution_arg;
+
+ sprintf(first_page_arg, "-dFirstPage=%d", page_number);
+ sprintf(last_page_arg, "-dLastPage=%d", page_number);
+ sprintf(format_arg, "-dDisplayFormat=16#%x", format);
+ sprintf(resolution_arg, "-r%d", resolution);
+
+ argv[argc++] = file_name;
+
+ /* Create a GS instance. */
+ code = gsapi_new_instance(&instance, INSTANCE_HANDLE);
+ if (code < 0) {
+ mexPrintf("Error %d in gsapi_new_instance\n", code);
+ return;
+ }
+
+ /* Assign lhs variables to teststate members */
+ teststate.mex_image_data = &plhs[0];
+
+ /* Register our callout handler. This will pass the display
+ * device the callback structure and handle when requested. */
+ code = gsapi_register_callout(instance, callout, &teststate);
+ if (code < 0) {
+ mexPrintf("Error %d in gsapi_register_callout\n", code);
+ goto fail;
+ }
+
+ code = gsapi_init_with_args(instance, argc, argv);
+ if (code < 0) {
+ mexPrintf("Error %d in gsapi_init_with_args\n", code);
+ goto fail;
+ }
+
+ /* Close the interpreter down (important, or we will leak!) */
+ code = gsapi_exit(instance);
+ if (code < 0) {
+ mexPrintf("Error %d in gsapi_exit\n", code);
+ }
+
+fail:
+ /* Delete the gs instance. */
+ gsapi_delete_instance(instance);
+
+ if (file_name != NULL)
+ free(file_name);
+}
diff --git a/demos/c/Makefile b/demos/c/Makefile
index 63dc4da5..5f053410 100644
--- a/demos/c/Makefile
+++ b/demos/c/Makefile
@@ -10,3 +10,17 @@ run_api_test: api_test
post_api_test:
md5sum apitest*
rm apitest*
+
+multi_test: multi_test.c
+ (cd ../.. && ./autogen.sh)
+ (cd ../.. && make so XCFLAGS="-DCLUSTER")
+ pwd
+ gcc -fPIC -I../.. multi_test.c -DGHOSTPDL=1 -lgpdl -lpthread -L../../sobin -o multi_test
+
+run_multi_test: multi_test
+ LD_LIBRARY_PATH=../../sobin ./multi_test
+
+post_multi_test:
+ md5sum multitest*
+ rm multitest*
+ rm multi_out*
diff --git a/demos/c/ReadMe.txt b/demos/c/ReadMe.txt
index 16c4d9de..336a31fb 100644
--- a/demos/c/ReadMe.txt
+++ b/demos/c/ReadMe.txt
@@ -29,3 +29,43 @@ tested there. Some fiddling to load the DLL may be required.
Building with GHOSTPDL=0 will allow the Ghostscript DLL to be
tested. The VS2019 project will need to be edited to use the
appropriate .lib file too.
+
+On unix, the file can be built using:
+
+ make api_test
+
+run using:
+
+ make run_api_test
+
+and cleaned up using:
+
+ make post_api_test
+
+
+ multi_test
+ ~~~~~~~~~~
+
+This is a simple VS2019 project that loads the gpdl dll and drives
+multiple instances of it from the gsapi functions.
+
+It is intended as a simple demonstration of how to use multiple
+instances of the ghostscript library at once; in this case many
+ghostscript instances run in parallel within a single process
+to convert several different documents at a time.
+
+Building with GHOSTPDL=0 will allow the Ghostscript DLL to be
+tested. The VS2019 project will need to be edited to use the
+appropriate .lib file too.
+
+On unix, the file can be built using:
+
+ make multi_test
+
+run using:
+
+ make run_multi_test
+
+and cleaned up using:
+
+ make post_multi_test
diff --git a/demos/c/multi_test.c b/demos/c/multi_test.c
new file mode 100644
index 00000000..f62d3e6e
--- /dev/null
+++ b/demos/c/multi_test.c
@@ -0,0 +1,191 @@
+/* Copyright (C) 2018-2022 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.
+*/
+
+/* Simple example file to demonstrate multi-instance use of
+ * the ghostscript/GPDL library. */
+
+#ifdef _WIN32
+/* Stop windows builds complaining about sprintf being insecure. */
+#define _CRT_SECURE_NO_WARNINGS
+#include <windows.h>
+#define WINDOWS
+#else
+#include <pthread.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <assert.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+/* This can work either with the Ghostscript or GhostPDL
+ * library. The code is the same for both. */
+#if GHOSTPDL
+#include "pcl/pl/plapi.h" /* GSAPI - gpdf version */
+#else
+#include "psi/iapi.h" /* GSAPI - ghostscript version */
+#endif
+
+/* We hold details of each threads working in a thread_data
+ * structure. */
+typedef struct
+{
+ /* What worker number are we ? */
+ int thread_num;
+ /* What input file? should this worker use? */
+ char *in_file;
+ /* Somewhere to store the thread id */
+#ifdef WINDOWS
+ HANDLE thread;
+#else
+ pthread_t thread;
+#endif
+ /* exit code for the thread */
+ int code;
+} thread_data;
+
+/* The function to perform the work of the thread.
+ * Starts a gs instance, runs a file, shuts it down.
+ */
+static
+#ifdef WINDOWS
+DWORD WINAPI
+#else
+void *
+#endif
+worker(void *td_)
+{
+ thread_data *td = (thread_data *)td_;
+ int code;
+ void *instance = NULL;
+ char out[32];
+
+ /* Construct the argc/argv to pass to ghostscript. */
+ int argc = 0;
+ char *argv[10];
+
+ sprintf(out, "multi_out_%d_", td->thread_num);
+ strcat(out, "%d.png");
+ argv[argc++] = "gpdl";
+ argv[argc++] = "-sDEVICE=png16m";
+ argv[argc++] = "-o";
+ argv[argc++] = out;
+ argv[argc++] = "-r100";
+ argv[argc++] = td->in_file;
+
+ /* Create a GS instance. */
+ code = gsapi_new_instance(&instance, NULL);
+ if (code < 0) {
+ printf("Error %d in gsapi_new_instance\n", code);
+ goto failearly;
+ }
+
+ /* Run our test. */
+ code = gsapi_init_with_args(instance, argc, argv);
+ if (code < 0) {
+ printf("Error %d in gsapi_init_with_args\n", code);
+ goto fail;
+ }
+
+ /* Close the interpreter down (important, or we will leak!) */
+ code = gsapi_exit(instance);
+ if (code < 0) {
+ printf("Error %d in gsapi_exit\n", code);
+ goto fail;
+ }
+
+fail:
+ /* Delete the gs instance. */
+ gsapi_delete_instance(instance);
+
+failearly:
+ td->code = code;
+
+#ifdef WINDOWS
+ return 0;
+#else
+ return NULL;
+#endif
+}
+
+/* A list of input files to run. */
+char *in_files[] =
+{
+ "../../examples/tiger.eps",
+ "../../examples/golfer.eps",
+ "../../examples/escher.ps",
+ "../../examples/snowflak.ps"
+#ifdef GHOSTPDL
+ , "../../pcl/examples/grashopp.pcl"
+ , "../../pcl/examples/owl.pcl"
+ , "../../pcl/examples/tiger.xps"
+#endif
+};
+
+#define NUM_INPUTS (sizeof(in_files)/sizeof(*in_files))
+#define NUM_WORKERS (10)
+
+int main(int argc, char *argv[])
+{
+ int failed = 0;
+#ifndef WINDOWS
+ int code;
+#endif
+ int i;
+ thread_data td[NUM_WORKERS];
+
+ /* Start NUM_WORKERS threads */
+ for (i = 0; i < NUM_WORKERS; i++)
+ {
+ td[i].in_file = in_files[i % NUM_INPUTS];
+ td[i].thread_num = i;
+
+#ifdef WINDOWS
+ td[i].thread = CreateThread(NULL, 0, worker, &td[i], 0, NULL);
+#else
+ code = pthread_create(&td[i].thread, NULL, worker, &td[i]);
+ if (code != 0) {
+ fprintf(stderr, "Thread %d creation failed\n", i);
+ exit(1);
+ }
+#endif
+ }
+
+ /* Wait for them all to finish */
+ for (i = 0; i < NUM_WORKERS; i++)
+ {
+ void *status = NULL;
+
+#ifdef WINDOWS
+ WaitForSingleObject(td[i].thread, INFINITE);
+#else
+ code = pthread_join(td[i].thread, &status);
+ if (code != 0) {
+ fprintf(stderr, "Thread join %d failed\n", i);
+ exit(1);
+ }
+#endif
+ /* All the threads should return with 0 */
+ if (td[i].code != 0)
+ failed = 1;
+ fprintf(stderr, "Thread %d finished with %d\n", i, td[i].code);
+ }
+
+ return failed;
+}
diff --git a/demos/c/multi_test.vcxproj b/demos/c/multi_test.vcxproj
new file mode 100644
index 00000000..ae208c5d
--- /dev/null
+++ b/demos/c/multi_test.vcxproj
@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Memento|Win32">
+ <Configuration>Memento</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Memento|x64">
+ <Configuration>Memento</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>16.0</VCProjectVersion>
+ <Keyword>Win32Proj</Keyword>
+ <ProjectGuid>{6f11d63a-3533-47e5-8aa8-973d804443a0}</ProjectGuid>
+ <RootNamespace>multi_test</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Memento|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Memento|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Memento|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Memento|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(ProjectDir)..\..\debugbin\</OutDir>
+ <TargetName>$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Memento|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(ProjectDir)..\..\membin\</OutDir>
+ <TargetName>$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(ProjectDir)..\..\bin\</OutDir>
+ <TargetName>$(ProjectName)</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <TargetName>$(ProjectName)64</TargetName>
+ <OutDir>$(ProjectDir)..\..\debugbin\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Memento|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(ProjectDir)..\..\membin\</OutDir>
+ <TargetName>$(ProjectName)64</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(ProjectDir)..\..\bin\</OutDir>
+ <TargetName>$(ProjectName)64</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>GHOSTPDL;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>gpdldll32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>..\..\debugbin</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Memento|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>GHOSTPDL;MEMENTO;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>gpdldll32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>..\..\membin</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>GHOSTPDL;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>gpdldll32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>..\..\bin</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>gpdldll64.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>..\..\debugbin</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Memento|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>MEMENTO;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\..</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>gpdldll64.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>..\..\membin</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>..\..\bin</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="multi_test.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/demos/c/multi_test.vcxproj.filters b/demos/c/multi_test.vcxproj.filters
new file mode 100644
index 00000000..0e2de063
--- /dev/null
+++ b/demos/c/multi_test.vcxproj.filters
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="multi_test.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
diff --git a/demos/java/README.txt b/demos/java/README.txt
index 4781bb09..3e94a920 100644
--- a/demos/java/README.txt
+++ b/demos/java/README.txt
@@ -6,3 +6,12 @@ variable. This exposes command line programs to build Java
applications.
On Linux and Mac, OpenJDK 8 is used.
+
+What's in this directory:
+
+./gsjava The Java project which provides the bindings to Ghostscript.
+./gstest An old project used for testing gsjava
+./gsviewer A demo Java PDF viewer made using the Java bindings
+./jni Contains the C++ project which backs the Java bindings to Ghostscript
+./mtdemo A demo project used to demonstrate how multithreading can be used with
+ the Java bindings \ No newline at end of file
diff --git a/demos/java/gsjava/build_darwin.sh b/demos/java/gsjava/build_darwin.sh
index 212dfb36..460e9ee0 100644
--- a/demos/java/gsjava/build_darwin.sh
+++ b/demos/java/gsjava/build_darwin.sh
@@ -2,8 +2,6 @@
mkdir -p bin
-echo "Compiling gs_jni C++ source..."
-
cd "../jni/gs_jni"
bash build_darwin.sh
diff --git a/demos/java/gsjava/build_linux.sh b/demos/java/gsjava/build_linux.sh
index 2866c07e..290540ae 100644
--- a/demos/java/gsjava/build_linux.sh
+++ b/demos/java/gsjava/build_linux.sh
@@ -2,8 +2,6 @@
mkdir -p bin
-echo "Compiling gs_jni C++ source..."
-
cd "../jni/gs_jni"
bash build_linux.sh
diff --git a/demos/java/gsjava/build_win32.bat b/demos/java/gsjava/build_win32.bat
index 88757811..85b81abc 100644
--- a/demos/java/gsjava/build_win32.bat
+++ b/demos/java/gsjava/build_win32.bat
@@ -2,7 +2,7 @@
if not exist bin mkdir bin
-echo Compiling Java source...
+echo Compiling gsjava Java source...
javac -sourcepath src\ -d bin^
src\com\artifex\gsjava\GSAPI.java^
src\com\artifex\gsjava\GSInstance.java^
@@ -53,7 +53,6 @@ jar -cf ..\gsjava.jar^
com\artifex\gsjava\GSAPI.class^
com\artifex\gsjava\GSAPI$Revision.class^
com\artifex\gsjava\GSInstance.class^
- com\artifex\gsjava\GSInstance$1.class^
com\artifex\gsjava\GSInstance$GSParam.class^
com\artifex\gsjava\GSInstance$ParamIterator.class^
^
@@ -74,7 +73,6 @@ jar -cf ..\gsjava.jar^
^
com\artifex\gsjava\devices\BMPDevice.class^
com\artifex\gsjava\devices\Device.class^
- com\artifex\gsjava\devices\Device$1.class^
com\artifex\gsjava\devices\Device$StdIO.class^
com\artifex\gsjava\devices\DeviceInUseException.class^
com\artifex\gsjava\devices\DeviceNotSupportedException.class^
diff --git a/demos/java/gsjava/src/com/artifex/gsjava/GSAPI.java b/demos/java/gsjava/src/com/artifex/gsjava/GSAPI.java
index 652d4356..6ab49bb2 100644
--- a/demos/java/gsjava/src/com/artifex/gsjava/GSAPI.java
+++ b/demos/java/gsjava/src/com/artifex/gsjava/GSAPI.java
@@ -59,7 +59,7 @@ public class GSAPI {
public static final long GS_NULL = 0L;
/**
- * Error codes
+ * Level 1 error codes
*/
public static final int GS_ERROR_OK = 0,
GS_ERROR_UNKNOWNERROR = -1,
@@ -89,7 +89,7 @@ public class GSAPI {
GS_ERROR_VMERROR = -25;
/**
- * Error codes
+ * Level 2 error codes
*/
public static final int GS_ERROR_CONFIGURATION_ERROR = -26,
GS_ERROR_UNDEFINEDRESOURCE = -27,
@@ -97,6 +97,12 @@ public class GSAPI {
GS_ERROR_INVALIDCONTEXT = -29,
GS_ERROR_INVALID = -30;
+ /**
+ * Psuedo-errors used internally
+ */
+ public static final int GS_ERROR_HIT_DETECTED = -59,
+ GS_ERROR_FATAL = -100;
+
public static final int GS_COLORS_NATIVE = (1 << 0),
GS_COLORS_GRAY = (1 << 1),
GS_COLORS_RGB = (1 << 2),
diff --git a/demos/java/gsjava/src/com/artifex/gsjava/GSInstance.java b/demos/java/gsjava/src/com/artifex/gsjava/GSInstance.java
index e5e713c7..ff3dbafa 100644
--- a/demos/java/gsjava/src/com/artifex/gsjava/GSInstance.java
+++ b/demos/java/gsjava/src/com/artifex/gsjava/GSInstance.java
@@ -75,13 +75,22 @@ public class GSInstance implements Iterable<GSInstance.GSParam<?>> {
}
}
- private static volatile boolean instanceExists = false;
+ private static boolean ALLOW_MULTITHREADING = false;
+ private static volatile int INSTANCES = 0;
+
+ public static void setAllowMultithreading(boolean state) {
+ ALLOW_MULTITHREADING = state;
+ }
+
+ public static int getInstanceCount() {
+ return INSTANCES;
+ }
private long instance;
private long callerHandle;
public GSInstance(long callerHandle) throws IllegalStateException {
- if (instanceExists)
+ if (!ALLOW_MULTITHREADING && INSTANCES > 0)
throw new IllegalStateException("An instance already exists");
Reference<Long> ref = new Reference<>();
int ret = gsapi_new_instance(ref, callerHandle);
@@ -89,7 +98,7 @@ public class GSInstance implements Iterable<GSInstance.GSParam<?>> {
throw new IllegalStateException("Failed to create new instance: " + ret);
this.instance = ref.getValue();
this.callerHandle = callerHandle;
- instanceExists = true;
+ INSTANCES++;
}
public GSInstance() throws IllegalStateException {
@@ -97,10 +106,11 @@ public class GSInstance implements Iterable<GSInstance.GSParam<?>> {
}
public void delete_instance() {
- if (instance != GS_NULL)
+ if (instance != GS_NULL) {
gsapi_delete_instance(instance);
- instance = GS_NULL;
- instanceExists = false;
+ instance = GS_NULL;
+ INSTANCES--;
+ }
}
public int set_stdio(IStdInFunction stdin, IStdOutFunction stdout, IStdErrFunction stderr) {
diff --git a/demos/java/gsviewer/README.txt b/demos/java/gsviewer/README.txt
index 2808fed9..1b4776fe 100644
--- a/demos/java/gsviewer/README.txt
+++ b/demos/java/gsviewer/README.txt
@@ -48,10 +48,10 @@ Building:
-= WINDOWS =-
-Ensure both gs_jni.dll and gpdldll64.dll. Then, run the build_win32.bat script.
-This will automatically build and copy gsjava.jar to this directory. To run,
-open gsviewer.jar either through File Explorer or in the command line through
-the following command:
+Ensure both gs_jni.dll and gpdldll64.dll are built. Then, run the
+build_win32.bat script. This will automatically build and copy gsjava.jar to
+this directory. To run, open gsviewer.jar either through File Explorer or in
+the command line through the following command:
java -jar gsviewer.jar
@@ -67,4 +67,4 @@ directories. gsviewer.jar will be outputted in this directory.
Ensure Ghostscript has been built.
Run the build_darwin.sh script. This will automatically build
gs_jni.dylib, gsjava.jar, and copy the files to the needed
-directories. gsviewer.jar will be outputed in this directory.
+directories. gsviewer.jar will be outputed in this directory. \ No newline at end of file
diff --git a/demos/java/gsviewer/build_darwin.sh b/demos/java/gsviewer/build_darwin.sh
index 6c9d96c7..ff9ec5b9 100644..100755
--- a/demos/java/gsviewer/build_darwin.sh
+++ b/demos/java/gsviewer/build_darwin.sh
@@ -34,4 +34,18 @@ javac -sourcepath src/ -d bin/ \
cd bin
echo "Packing gsviewer JAR file..."
-jar cfm "../gsviewer.jar" "../Manifest.md" "com/" \ No newline at end of file
+jar cfm "../gsviewer.jar" "../Manifest.md" "com/"
+
+
+echo "Copy gs_jni.dylib"
+cp "../jni/gs_jni/gs_jni.dylib" "gs_jni.dylib"
+
+echo "Create libgpdl.dylib link"
+cp "../../../sobin/libgpdl.dylib" "libgpdl.dylib"
+
+cd ../../../sobin
+
+echo "Copy libgpdl.dylib target"
+cp $(readlink "libgpdl.dylib") "../demos/java/gsviewer"
+
+cd ../demos/java/gsviewer \ No newline at end of file
diff --git a/demos/java/gsviewer/build_linux.sh b/demos/java/gsviewer/build_linux.sh
index 67b3e9cf..3cd70616 100644..100755
--- a/demos/java/gsviewer/build_linux.sh
+++ b/demos/java/gsviewer/build_linux.sh
@@ -34,4 +34,18 @@ javac -sourcepath src/ -d bin/ \
cd bin
echo "Packing gsviewer JAR file..."
-jar cfm "../gsviewer.jar" "../Manifest.md" "com/" \ No newline at end of file
+jar cfm "../gsviewer.jar" "../Manifest.md" "com/"
+
+
+echo "Copy gs_jni.so"
+cp "../jni/gs_jni/gs_jni.so" "gs_jni.so"
+
+echo "Create libgpdl.so link"
+cp "../../../sobin/libgpdl.so" "libgpdl.so"
+
+cd ../../../sobin
+
+echo "Copy libgpdl.so target"
+cp $(readlink "libgpdl.so") "../demos/java/gsviewer"
+
+cd ../demos/java/gsviewer \ No newline at end of file
diff --git a/demos/java/gsviewer/build_win32.bat b/demos/java/gsviewer/build_win32.bat
index 0e279e9f..0e279e9f 100644..100755
--- a/demos/java/gsviewer/build_win32.bat
+++ b/demos/java/gsviewer/build_win32.bat
diff --git a/demos/java/gsviewer/start_darwin.sh b/demos/java/gsviewer/start_darwin.sh
index 9e4004bd..9e4004bd 100644..100755
--- a/demos/java/gsviewer/start_darwin.sh
+++ b/demos/java/gsviewer/start_darwin.sh
diff --git a/demos/java/gsviewer/start_linux.sh b/demos/java/gsviewer/start_linux.sh
index 4ed9f514..4ed9f514 100644..100755
--- a/demos/java/gsviewer/start_linux.sh
+++ b/demos/java/gsviewer/start_linux.sh
diff --git a/demos/java/jni/gs_jni/build_darwin.sh b/demos/java/jni/gs_jni/build_darwin.sh
index 81b10c01..18e12cae 100644
--- a/demos/java/jni/gs_jni/build_darwin.sh
+++ b/demos/java/jni/gs_jni/build_darwin.sh
@@ -46,6 +46,16 @@ g++ -c -Wall -O3 -fPIC \
"jni_util.cpp" \
-o "obin/jni_util.o"
+echo "Compile instance_data.cpp"
+g++ -c -Wall -O3 -fPIC \
+ -std=c++14 \
+ -I./include \
+ -I./include/darwin \
+ -I./../../../../psi \
+ -I./../../../../devices \
+ "instance_data.cpp" \
+ -o "obin/instance_data.o"
+
echo "Link"
g++ -dynamiclib -fPIC \
-Wl \
@@ -54,4 +64,5 @@ g++ -dynamiclib -fPIC \
"obin/com_artifex_gsjava_GSAPI.o" \
"obin/com_artifex_gsjava_util_NativePointer.o" \
"obin/jni_util.o" \
+ "obin/instance_data.o" \
"../../../../sobin/libgpdl.dylib" \ No newline at end of file
diff --git a/demos/java/jni/gs_jni/build_linux.sh b/demos/java/jni/gs_jni/build_linux.sh
index 974dfba3..f66a4883 100644
--- a/demos/java/jni/gs_jni/build_linux.sh
+++ b/demos/java/jni/gs_jni/build_linux.sh
@@ -7,7 +7,7 @@ mkdir -p obin
echo "Compiling gs_jni C++ source..."
echo "Compile callbacks.cpp"
-g++ -c -Wall -O3 \
+g++ -c -Wall -O3 -fPIC \
-std=c++14 \
-I./include \
-I./include/linux \
@@ -17,7 +17,7 @@ g++ -c -Wall -O3 \
-o "obin/callbacks.o"
echo "Compile com_artifex_gsjava_GSAPI.cpp"
-g++ -c -Wall -O3 \
+g++ -c -Wall -O3 -fPIC \
-std=c++14 \
-I./include \
-I./include/linux \
@@ -27,7 +27,7 @@ g++ -c -Wall -O3 \
-o "obin/com_artifex_gsjava_GSAPI.o"
echo "Compile com_artifex_gsjava_util_NativePointer.cpp"
-g++ -c -Wall -O3 \
+g++ -c -Wall -O3 -fPIC \
-std=c++14 \
-I./include \
-I./include/linux \
@@ -37,7 +37,7 @@ g++ -c -Wall -O3 \
-o "obin/com_artifex_gsjava_util_NativePointer.o"
echo "Compile jni_util.cpp"
-g++ -c -Wall -O3\
+g++ -c -Wall -O3 -fPIC \
-std=c++14 \
-I./include \
-I./include/linux \
@@ -46,6 +46,16 @@ g++ -c -Wall -O3\
"jni_util.cpp" \
-o "obin/jni_util.o"
+echo "Compile instance_data.cpp"
+g++ -c -Wall -O3 -fPIC \
+ -std=c++14 \
+ -I./include \
+ -I./include/linux \
+ -I./../../../../psi \
+ -I./../../../../devices \
+ "instance_data.cpp" \
+ -o "obin/instance_data.o"
+
echo "Link"
g++ -shared \
-Wl,-soname,gs_jni.so \
@@ -54,4 +64,5 @@ g++ -shared \
"obin/com_artifex_gsjava_GSAPI.o" \
"obin/com_artifex_gsjava_util_NativePointer.o" \
"obin/jni_util.o" \
- "../../../../sobin/libgpdl.so" \ No newline at end of file
+ "obin/instance_data.o" \
+ "../../../../sobin/libgpdl.so"
diff --git a/demos/java/jni/gs_jni/callbacks.cpp b/demos/java/jni/gs_jni/callbacks.cpp
index 7826b328..2d0da54e 100644
--- a/demos/java/jni/gs_jni/callbacks.cpp
+++ b/demos/java/jni/gs_jni/callbacks.cpp
@@ -1,8 +1,11 @@
#include "callbacks.h"
#include "jni_util.h"
+#include "instance_data.h"
#include <string.h>
+#include <unordered_map>
+#include <assert.h>
#define STDIN_SIG "(J[BI)I"
#define STDOUT_SIG "(J[BI)I"
@@ -31,134 +34,150 @@ Lcom/artifex/gsjava/IntReference;)I"
using namespace util;
-static JNIEnv *g_env = NULL;
-
-static jobject g_stdIn = NULL;
-static jobject g_stdOut = NULL;
-static jobject g_stdErr = NULL;
-
-static jobject g_poll = NULL;
-
-static jobject g_displayCallback = NULL;
-
-static jobject g_callout = NULL;
-
-void callbacks::setJNIEnv(JNIEnv *env)
+void callbacks::setJNIEnv(GSInstanceData *idata, JNIEnv *env)
{
- g_env = env;
+ idata->env = env;
}
-void callbacks::setIOCallbacks(jobject stdIn, jobject stdOut, jobject stdErr)
+void callbacks::setIOCallbacks(GSInstanceData *idata, jobject stdIn, jobject stdOut, jobject stdErr)
{
- if (g_env)
+ if (idata->env)
{
- if (g_stdIn)
- g_env->DeleteGlobalRef(g_stdIn);
+ if (idata->stdIn)
+ idata->env->DeleteGlobalRef(idata->stdIn);
- if (g_stdOut)
- g_env->DeleteGlobalRef(g_stdOut);
+ if (idata->stdOut)
+ idata->env->DeleteGlobalRef(idata->stdOut);
- if (g_stdErr)
- g_env->DeleteGlobalRef(g_stdErr);
+ if (idata->stdErr)
+ idata->env->DeleteGlobalRef(idata->stdErr);
- g_stdIn = g_env->NewGlobalRef(stdIn);
- g_stdOut = g_env->NewGlobalRef(stdOut);
- g_stdErr = g_env->NewGlobalRef(stdErr);
+ idata->stdIn = idata->env->NewGlobalRef(stdIn);
+ idata->stdOut = idata->env->NewGlobalRef(stdOut);
+ idata->stdErr = idata->env->NewGlobalRef(stdErr);
}
}
int callbacks::stdInFunction(void *callerHandle, char *buf, int len)
{
- int code = 0;
- if (g_env && g_stdIn)
- {
- jbyteArray byteArray = g_env->NewByteArray(len);
- g_env->SetByteArrayRegion(byteArray, 0, len, (jbyte *)buf);
- code = callIntMethod(g_env, g_stdIn, "onStdIn", STDIN_SIG, (jlong)callerHandle, byteArray, (jint)len);
- }
- return code;
+ GSInstanceData *gsdata = (GSInstanceData *)callerHandle;
+ assert(gsdata);
+
+ if (!gsdata->env || !gsdata->stdErr) return 0;
+
+ jbyteArray byteArray = gsdata->env->NewByteArray(len);
+ gsdata->env->SetByteArrayRegion(byteArray, 0, len, (jbyte *)buf);
+
+ jint result = callIntMethod(gsdata->env, gsdata->stdIn, "onStdIn", STDIN_SIG, (jlong)gsdata->stdioHandle, byteArray, (jint)len);
+ if (gsdata->env->ExceptionCheck()) return 0;
+
+ jboolean isCopy = JNI_FALSE;
+ jbyte *arr = gsdata->env->GetByteArrayElements(byteArray, &isCopy);
+
+ jsize copySize = result < len ? result : len;
+ memcpy(buf, arr, copySize);
+
+ return copySize;
}
int callbacks::stdOutFunction(void *callerHandle, const char *str, int len)
{
- int code = 0;
- if (g_env && g_stdOut)
- {
- jbyteArray byteArray = g_env->NewByteArray(len);
- g_env->SetByteArrayRegion(byteArray, 0, len, (const jbyte *)str);
- code = callIntMethod(g_env, g_stdOut, "onStdOut", STDOUT_SIG, (jlong)callerHandle, byteArray, (jint)len);
- }
- return code;
+ GSInstanceData *gsdata = (GSInstanceData *)callerHandle;
+ assert(gsdata);
+
+ if (!gsdata->env || !gsdata->stdOut) return len;
+
+ jbyteArray byteArray = gsdata->env->NewByteArray(len);
+ gsdata->env->SetByteArrayRegion(byteArray, 0, len, (const jbyte *)str);
+
+ jint result = callIntMethod(gsdata->env, gsdata->stdOut, "onStdOut", STDOUT_SIG, (jlong)gsdata->stdioHandle, byteArray, (jint)len);
+ if (gsdata->env->ExceptionCheck()) return 0;
+
+ return result;
}
int callbacks::stdErrFunction(void *callerHandle, const char *str, int len)
{
- int code = 0;
- if (g_env && g_stdErr)
- {
- jbyteArray byteArray = g_env->NewByteArray(len);
- g_env->SetByteArrayRegion(byteArray, 0, len, (const jbyte *)str);
- code = callIntMethod(g_env, g_stdErr, "onStdErr", STDERR_SIG, (jlong)callerHandle, byteArray, (jint)len);
- }
- return code;
+ GSInstanceData *gsdata = (GSInstanceData *)callerHandle;
+ assert(gsdata);
+
+ if (!gsdata->env || !gsdata->stdErr) return len;
+
+ jbyteArray byteArray = gsdata->env->NewByteArray(len);
+ gsdata->env->SetByteArrayRegion(byteArray, 0, len, (const jbyte *)str);
+
+ jint result = callIntMethod(gsdata->env, gsdata->stdErr, "onStdErr", STDERR_SIG, (jlong)gsdata->stdioHandle, byteArray, (jint)len);
+ if (gsdata->env->ExceptionCheck()) return 0;
+
+ return result;
}
-void callbacks::setPollCallback(jobject poll)
+void callbacks::setPollCallback(GSInstanceData *idata, jobject poll)
{
- if (g_env)
+ if (idata->env)
{
- if (g_poll)
- g_env->DeleteGlobalRef(g_poll);
+ if (idata->poll)
+ idata->env->DeleteGlobalRef(idata->poll);
- g_poll = g_env->NewGlobalRef(poll);
+ idata->poll = idata->env->NewGlobalRef(poll);
}
}
int callbacks::pollFunction(void *callerHandle)
{
int code = 0;
- if (g_env && g_poll)
+
+ GSInstanceData *gsdata = (GSInstanceData *)callerHandle;
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->poll)
{
- code = callIntMethod(g_env, g_poll, "onPoll", POLL_SIG, (jlong)callerHandle);
+ code = callIntMethod(gsdata->env, gsdata->poll, "onPoll", POLL_SIG, (jlong)gsdata->callerHandle);
}
return code;
}
-void callbacks::setDisplayCallback(jobject displayCallback)
+void callbacks::setDisplayCallback(GSInstanceData *idata, jobject displayCallback)
{
- if (g_env)
+ if (idata->env)
{
- if (g_displayCallback)
+ if (idata->displayCallback)
{
- g_env->DeleteGlobalRef(g_displayCallback);
- g_displayCallback = NULL;
+ idata->env->DeleteGlobalRef(idata->displayCallback);
+ idata->displayCallback = NULL;
}
- g_displayCallback = g_env->NewGlobalRef(displayCallback);
+ idata->displayCallback = idata->env->NewGlobalRef(displayCallback);
//g_displayCallback = displayCallback;
}
}
-void callbacks::setCalloutCallback(jobject callout)
+void callbacks::setCalloutCallback(GSInstanceData *idata, jobject callout)
{
- if (g_env)
+ if (idata->env)
{
- if (g_callout)
- g_env->DeleteGlobalRef(g_callout);
+ if (idata->callout)
+ idata->env->DeleteGlobalRef(idata->callout);
- g_callout = g_env->NewGlobalRef(callout);
+ idata->callout = idata->env->NewGlobalRef(callout);
}
}
int callbacks::calloutFunction(void *instance, void *handle, const char *deviceName, int id, int size, void *data)
{
int code = 0;
- if (g_env && g_callout)
+
+ GSInstanceData *gsdata = findDataFromInstance(instance);
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->callout)
{
- jsize len = strlen(deviceName);
- jbyteArray array = g_env->NewByteArray(len);
- g_env->SetByteArrayRegion(array, 0, len, (const jbyte *)deviceName);
- code = callIntMethod(g_env, g_callout, "onCallout", "(JJ[BIIJ)I", (jlong)instance, (jlong)handle, array, id, size, (jlong)data);
+ jsize len = (jsize)strlen(deviceName);
+ jbyteArray array = gsdata->env->NewByteArray(len);
+ gsdata->env->SetByteArrayRegion(array, 0, len, (const jbyte *)deviceName);
+
+ // TODO: gsdata->callerHandle is not consistent with the specification for a callout
+ code = callIntMethod(gsdata->env, gsdata->callout, "onCallout", "(JJ[BIIJ)I", (jlong)instance, (jlong)gsdata->callerHandle, array, id, size, (jlong)data);
}
return code;
}
@@ -166,13 +185,17 @@ int callbacks::calloutFunction(void *instance, void *handle, const char *deviceN
int callbacks::display::displayOpenFunction(void *handle, void *device)
{
int code = 0;
- if (g_env && g_displayCallback)
+
+ GSInstanceData *gsdata = (GSInstanceData *)handle;
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->displayCallback)
{
- jclass clazz = g_env->GetObjectClass(g_displayCallback);
- const char *name = getClassName(g_env, clazz);
+ jclass clazz = gsdata->env->GetObjectClass(gsdata->displayCallback);
+ const char *name = getClassName(gsdata->env, clazz);
freeClassName(name);
- code = callIntMethod(g_env, g_displayCallback, "onDisplayOpen", DISPLAY_OPEN_SIG, (jlong)handle, (jlong)device);
- CHECK_AND_RETURN(g_env);
+ code = callIntMethod(gsdata->env, gsdata->displayCallback, "onDisplayOpen", DISPLAY_OPEN_SIG, (jlong)gsdata->displayHandle, (jlong)device);
+ CHECK_AND_RETURN(gsdata->env);
}
return code;
}
@@ -180,10 +203,14 @@ int callbacks::display::displayOpenFunction(void *handle, void *device)
int callbacks::display::displayPrecloseFunction(void *handle, void *device)
{
int code = 0;
- if (g_env && g_displayCallback)
+
+ GSInstanceData *gsdata = (GSInstanceData *)handle;
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->displayCallback)
{
- code = callIntMethod(g_env, g_displayCallback, "onDisplayPreclose", DISPLAY_PRECLOSE_SIG, (jlong)handle, (jlong)device);
- CHECK_AND_RETURN(g_env);
+ code = callIntMethod(gsdata->env, gsdata->displayCallback, "onDisplayPreclose", DISPLAY_PRECLOSE_SIG, (jlong)gsdata->displayHandle, (jlong)device);
+ CHECK_AND_RETURN(gsdata->env);
}
return code;
}
@@ -191,10 +218,14 @@ int callbacks::display::displayPrecloseFunction(void *handle, void *device)
int callbacks::display::displayCloseFunction(void *handle, void *device)
{
int code = 0;
- if (g_env && g_displayCallback)
+
+ GSInstanceData *gsdata = (GSInstanceData *)handle;
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->displayCallback)
{
- code = callIntMethod(g_env, g_displayCallback, "onDisplayClose", DISPLAY_CLOSE_SIG, (jlong)handle, (jlong)device);
- CHECK_AND_RETURN(g_env);
+ code = callIntMethod(gsdata->env, gsdata->displayCallback, "onDisplayClose", DISPLAY_CLOSE_SIG, (jlong)gsdata->displayHandle, (jlong)device);
+ CHECK_AND_RETURN(gsdata->env);
}
return code;
}
@@ -202,11 +233,15 @@ int callbacks::display::displayCloseFunction(void *handle, void *device)
int callbacks::display::displayPresizeFunction(void *handle, void *device, int width, int height, int raster, unsigned int format)
{
int code = 0;
- if (g_env && g_displayCallback)
+
+ GSInstanceData *gsdata = (GSInstanceData *)handle;
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->displayCallback)
{
- code = callIntMethod(g_env, g_displayCallback, "onDisplayPresize", DISPLAY_PRESIZE_SIG, (jlong)handle,
+ code = callIntMethod(gsdata->env, gsdata->displayCallback, "onDisplayPresize", DISPLAY_PRESIZE_SIG, (jlong)gsdata->displayHandle,
(jlong)device, width, height, raster, (jint)format);
- CHECK_AND_RETURN(g_env);
+ CHECK_AND_RETURN(gsdata->env);
}
return code;
}
@@ -215,57 +250,61 @@ int callbacks::display::displaySizeFunction(void *handle, void *device, int widt
unsigned int format, unsigned char *pimage)
{
int code = 0;
- if (g_env && g_displayCallback)
+
+ GSInstanceData *gsdata = (GSInstanceData *)handle;
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->displayCallback)
{
jsize len = height * raster;
- jbyteArray byteArray = g_env->NewByteArray(len);
- g_env->SetByteArrayRegion(byteArray, 0, len, (signed char *)pimage);
+ jbyteArray byteArray = gsdata->env->NewByteArray(len);
+ gsdata->env->SetByteArrayRegion(byteArray, 0, len, (signed char *)pimage);
static const char *const bytePointerClassName = "com/artifex/gsjava/util/BytePointer";
static const char *const nativePointerClassName = "com/artifex/gsjava/util/NativePointer";
- jclass bytePointerClass = g_env->FindClass(bytePointerClassName);
+ jclass bytePointerClass = gsdata->env->FindClass(bytePointerClassName);
if (bytePointerClass == NULL)
{
- throwNoClassDefError(g_env, bytePointerClassName);
+ throwNoClassDefError(gsdata->env, bytePointerClassName);
return -21;
}
- jclass nativePointerClass = g_env->FindClass(nativePointerClassName);
+ jclass nativePointerClass = gsdata->env->FindClass(nativePointerClassName);
if (nativePointerClass == NULL)
{
- throwNoClassDefError(g_env, nativePointerClassName);
+ throwNoClassDefError(gsdata->env, nativePointerClassName);
return -21;
}
- jmethodID constructor = g_env->GetMethodID(bytePointerClass, "<init>", "()V");
+ jmethodID constructor = gsdata->env->GetMethodID(bytePointerClass, "<init>", "()V");
if (constructor == NULL)
{
- throwNoSuchMethodError(g_env, "com.artifex.gsjava.util.BytePointer.<init>()V");
+ throwNoSuchMethodError(gsdata->env, "com.artifex.gsjava.util.BytePointer.<init>()V");
return -21;
}
- jobject bytePointer = g_env->NewObject(bytePointerClass, constructor);
+ jobject bytePointer = gsdata->env->NewObject(bytePointerClass, constructor);
- jfieldID dataPtrID = g_env->GetFieldID(nativePointerClass, "address", "J");
+ jfieldID dataPtrID = gsdata->env->GetFieldID(nativePointerClass, "address", "J");
if (dataPtrID == NULL)
{
- throwNoSuchFieldError(g_env, "address");
+ throwNoSuchFieldError(gsdata->env, "address");
return -21;
}
- jfieldID lengthID = g_env->GetFieldID(bytePointerClass, "length", "J");
+ jfieldID lengthID = gsdata->env->GetFieldID(bytePointerClass, "length", "J");
if (lengthID == NULL)
{
- throwNoSuchFieldError(g_env, "length");
+ throwNoSuchFieldError(gsdata->env, "length");
return -21;
}
- g_env->SetLongField(bytePointer, dataPtrID, (jlong)pimage);
- g_env->SetLongField(bytePointer, lengthID, len);
+ gsdata->env->SetLongField(bytePointer, dataPtrID, (jlong)pimage);
+ gsdata->env->SetLongField(bytePointer, lengthID, len);
- code = callIntMethod(g_env, g_displayCallback, "onDisplaySize", DISPLAY_SIZE_SIG, (jlong)handle,
+ code = callIntMethod(gsdata->env, gsdata->displayCallback, "onDisplaySize", DISPLAY_SIZE_SIG, (jlong)gsdata->displayHandle,
(jlong)device, width, height, raster, (jint)format, bytePointer);
- CHECK_AND_RETURN(g_env);
+ CHECK_AND_RETURN(gsdata->env);
}
return code;
}
@@ -273,10 +312,14 @@ int callbacks::display::displaySizeFunction(void *handle, void *device, int widt
int callbacks::display::displaySyncFunction(void *handle, void *device)
{
int code = 0;
- if (g_env && g_displayCallback)
+
+ GSInstanceData *gsdata = (GSInstanceData *)handle;
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->displayCallback)
{
- code = callIntMethod(g_env, g_displayCallback, "onDisplaySync", DISPLAY_SYNC_SIG, (jlong)handle, (jlong)device);
- CHECK_AND_RETURN(g_env);
+ code = callIntMethod(gsdata->env, gsdata->displayCallback, "onDisplaySync", DISPLAY_SYNC_SIG, (jlong)gsdata->displayHandle, (jlong)device);
+ CHECK_AND_RETURN(gsdata->env);
}
return code;
}
@@ -284,11 +327,15 @@ int callbacks::display::displaySyncFunction(void *handle, void *device)
int callbacks::display::displayPageFunction(void *handle, void *device, int copies, int flush)
{
int code = 0;
- if (g_env && g_displayCallback)
+
+ GSInstanceData *gsdata = (GSInstanceData *)handle;
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->displayCallback)
{
- code = callIntMethod(g_env, g_displayCallback, "onDisplayPage", DISPLAY_PAGE_SIG, (jlong)handle,
+ code = callIntMethod(gsdata->env, gsdata->displayCallback, "onDisplayPage", DISPLAY_PAGE_SIG, (jlong)gsdata->displayHandle,
(jlong)device, copies, flush);
- CHECK_AND_RETURN(g_env);
+ CHECK_AND_RETURN(gsdata->env);
}
return code;
}
@@ -296,11 +343,15 @@ int callbacks::display::displayPageFunction(void *handle, void *device, int copi
int callbacks::display::displayUpdateFunction(void *handle, void *device, int x, int y, int w, int h)
{
int code = 0;
- if (g_env && g_displayCallback)
+
+ GSInstanceData *gsdata = (GSInstanceData *)handle;
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->displayCallback)
{
- code = callIntMethod(g_env, g_displayCallback, "onDisplayUpdate", DISPLAY_UPDATE_SIG, (jlong)handle,
+ code = callIntMethod(gsdata->env, gsdata->displayCallback, "onDisplayUpdate", DISPLAY_UPDATE_SIG, (jlong)gsdata->displayHandle,
(jlong)device, x, y, w, h);
- CHECK_AND_RETURN(g_env);
+ CHECK_AND_RETURN(gsdata->env);
}
return code;
}
@@ -309,14 +360,18 @@ int callbacks::display::displaySeparationFunction(void *handle, void *device, in
unsigned short c, unsigned short m, unsigned short y, unsigned short k)
{
int code = 0;
- if (g_env && g_displayCallback)
+
+ GSInstanceData *gsdata = (GSInstanceData *)handle;
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->displayCallback)
{
- jsize len = strlen(componentName);
- jbyteArray byteArray = g_env->NewByteArray(len);
- g_env->SetByteArrayRegion(byteArray, 0, len, (const jbyte *)componentName);
- code = callIntMethod(g_env, g_displayCallback, "onDisplaySeparation", DISPLAY_SEPARATION_SIG, (jlong)handle,
+ jsize len = (jsize)strlen(componentName);
+ jbyteArray byteArray = gsdata->env->NewByteArray(len);
+ gsdata->env->SetByteArrayRegion(byteArray, 0, len, (const jbyte *)componentName);
+ code = callIntMethod(gsdata->env, gsdata->displayCallback, "onDisplaySeparation", DISPLAY_SEPARATION_SIG, (jlong)gsdata->displayHandle,
(jlong)device, component, byteArray, c, m, y, k);
- CHECK_AND_RETURN(g_env);
+ CHECK_AND_RETURN(gsdata->env);
}
return code;
}
@@ -324,11 +379,15 @@ int callbacks::display::displaySeparationFunction(void *handle, void *device, in
int callbacks::display::displayAdjustBandHeightFunction(void *handle, void *device, int bandHeight)
{
int code = 0;
- if (g_env && g_displayCallback)
+
+ GSInstanceData *gsdata = (GSInstanceData *)handle;
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->displayCallback)
{
- code = callIntMethod(g_env, g_displayCallback, "onDisplayAdjustBandHeght", DISPLAY_ADJUST_BAND_HEIGHT_SIG,
- (jlong)handle, (jlong)device, bandHeight);
- CHECK_AND_RETURN(g_env);
+ code = callIntMethod(gsdata->env, gsdata->displayCallback, "onDisplayAdjustBandHeght", DISPLAY_ADJUST_BAND_HEIGHT_SIG,
+ (jlong)gsdata->displayHandle, (jlong)device, bandHeight);
+ CHECK_AND_RETURN(gsdata->env);
}
return code;
}
@@ -337,20 +396,24 @@ int callbacks::display::displayRectangleRequestFunction(void *handle, void *devi
int *raster, int *plane_raster, int *x, int *y, int *w, int *h)
{
int code = 0;
- if (g_env && g_displayCallback)
+
+ GSInstanceData *gsdata = (GSInstanceData *)handle;
+ assert(gsdata);
+
+ if (gsdata->env && gsdata->displayCallback)
{
- Reference memoryRef = Reference(g_env, toWrapperType(g_env, (jlong)*memory));
- Reference oxRef = Reference(g_env, toWrapperType(g_env, (jint)*ox));
- Reference oyRef = Reference(g_env, toWrapperType(g_env, (jint)*oy));
- Reference rasterRef = Reference(g_env, toWrapperType(g_env, (jint)*raster));
- Reference planeRasterRef = Reference(g_env, toWrapperType(g_env, (jint)*plane_raster));
- Reference xRef = Reference(g_env, toWrapperType(g_env, (jint)*x));
- Reference yRef = Reference(g_env, toWrapperType(g_env, (jint)*y));
- Reference wRef = Reference(g_env, toWrapperType(g_env, (jint)*w));
- Reference hRef = Reference(g_env, toWrapperType(g_env, (jint)*h));
-
- code = callIntMethod(g_env, g_displayCallback, "onDisplayRectangleRequest", DISPLAY_RECTANGLE_REQUEST,
- (jlong)handle,
+ Reference memoryRef = Reference(gsdata->env, toWrapperType(gsdata->env, (jlong)*memory));
+ Reference oxRef = Reference(gsdata->env, toWrapperType(gsdata->env, (jint)*ox));
+ Reference oyRef = Reference(gsdata->env, toWrapperType(gsdata->env, (jint)*oy));
+ Reference rasterRef = Reference(gsdata->env, toWrapperType(gsdata->env, (jint)*raster));
+ Reference planeRasterRef = Reference(gsdata->env, toWrapperType(gsdata->env, (jint)*plane_raster));
+ Reference xRef = Reference(gsdata->env, toWrapperType(gsdata->env, (jint)*x));
+ Reference yRef = Reference(gsdata->env, toWrapperType(gsdata->env, (jint)*y));
+ Reference wRef = Reference(gsdata->env, toWrapperType(gsdata->env, (jint)*w));
+ Reference hRef = Reference(gsdata->env, toWrapperType(gsdata->env, (jint)*h));
+
+ code = callIntMethod(gsdata->env, gsdata->displayCallback, "onDisplayRectangleRequest", DISPLAY_RECTANGLE_REQUEST,
+ (jlong)gsdata->displayHandle,
(jlong)device,
memoryRef.object(),
oxRef.object(),
@@ -373,7 +436,7 @@ int callbacks::display::displayRectangleRequestFunction(void *handle, void *devi
*w = wRef.intValue();
*h = hRef.intValue();
- CHECK_AND_RETURN(g_env);
+ CHECK_AND_RETURN(gsdata->env);
}
return code;
}
diff --git a/demos/java/jni/gs_jni/callbacks.h b/demos/java/jni/gs_jni/callbacks.h
index 55508f2b..e8a23f93 100644
--- a/demos/java/jni/gs_jni/callbacks.h
+++ b/demos/java/jni/gs_jni/callbacks.h
@@ -2,6 +2,8 @@
#include <jni.h>
+#include "instance_data.h"
+
namespace callbacks
{
/*!
@@ -10,17 +12,17 @@ namespace callbacks
@param env A JNIEnv.
*/
- void setJNIEnv(JNIEnv *env);
+ void setJNIEnv(GSInstanceData *idata, JNIEnv *env);
- void setIOCallbacks(jobject stdIn, jobject stdOut, jobject stdErr);
+ void setIOCallbacks(GSInstanceData *idata, jobject stdIn, jobject stdOut, jobject stdErr);
int stdInFunction(void *callerHandle, char *buf, int len);
int stdOutFunction(void *callerHandle, const char *str, int len);
int stdErrFunction(void *callerHandle, const char *str, int len);
- void setPollCallback(jobject poll);
+ void setPollCallback(GSInstanceData *idata, jobject poll);
int pollFunction(void *callerHandle);
- void setDisplayCallback(jobject displayCallback);
+ void setDisplayCallback(GSInstanceData *idata, jobject displayCallback);
namespace display
{
@@ -49,6 +51,6 @@ namespace callbacks
int *x, int *y, int *w, int *h);
}
- void setCalloutCallback(jobject calout);
+ void setCalloutCallback(GSInstanceData *idata, jobject callout);
int calloutFunction(void *instance, void *handle, const char *deviceName, int id, int size, void *data);
} \ No newline at end of file
diff --git a/demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.cpp b/demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.cpp
index b28334e9..500f540a 100644
--- a/demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.cpp
+++ b/demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.cpp
@@ -4,14 +4,19 @@
#include <gdevdsp.h>
#include <string.h>
#include <memory>
+#include <assert.h>
+#include <string>
#include "jni_util.h"
#include "callbacks.h"
+#include "instance_data.h"
using namespace util;
static void *getAsPointer(JNIEnv *env, jobject object, gs_set_param_type type, bool *success);
+static void storeDispalyHandle(GSInstanceData *idata);
+
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1revision
(JNIEnv *env, jclass, jobject revision, jint len)
{
@@ -35,54 +40,82 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1new_1instance
if (instance == NULL)
return throwNullPointerException(env, "LongReference object is NULL");
- void *gsInstance;
- int code = gsapi_new_instance(&gsInstance, (void *)callerHandle);
+ GSInstanceData *idata = new GSInstanceData();
+ idata->callerHandle = (void *)callerHandle;
+
+ void *gsInstance = NULL;
+ int code = gsapi_new_instance(&gsInstance, idata);
if (code == 0)
Reference::setValueField(env, instance, toWrapperType(env, (jlong)gsInstance));
+
+ idata->instance = gsInstance;
+
+ putInstanceData(idata);
+
return code;
}
JNIEXPORT void JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1delete_1instance
(JNIEnv *env, jclass, jlong instance)
{
- callbacks::setJNIEnv(env);
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
+ callbacks::setJNIEnv(idata, env);
gsapi_delete_instance((void *)instance);
+
+ deleteDataFromInstance((void *)instance);
}
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1stdio_1with_1handle
(JNIEnv *env, jclass, jlong instance, jobject stdIn, jobject stdOut, jobject stdErr, jlong callerHandle)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
+ idata->stdioHandle = (void *)callerHandle;
+
int code = gsapi_set_stdio_with_handle((void *)instance, callbacks::stdInFunction,
- callbacks::stdOutFunction, callbacks::stdErrFunction, (void *)callerHandle);
+ callbacks::stdOutFunction, callbacks::stdErrFunction, idata);
if (code == 0)
{
- callbacks::setJNIEnv(env);
- callbacks::setIOCallbacks(stdIn, stdOut, stdErr);
+ callbacks::setJNIEnv(idata, env);
+ callbacks::setIOCallbacks(idata, stdIn, stdOut, stdErr);
}
+
return code;
}
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1stdio
(JNIEnv *env, jclass, jlong instance, jobject stdIn, jobject stdOut, jobject stdErr)
{
- int code = gsapi_set_stdio((void *)instance, callbacks::stdInFunction,
- callbacks::stdOutFunction, callbacks::stdErrFunction);
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
+ idata->stdioHandle = NULL;
+
+ int code = gsapi_set_stdio_with_handle((void *)instance, callbacks::stdInFunction,
+ callbacks::stdOutFunction, callbacks::stdErrFunction, idata);
if (code == 0)
{
- callbacks::setJNIEnv(env);
- callbacks::setIOCallbacks(stdIn, stdOut, stdErr);
+ callbacks::setJNIEnv(idata, env);
+ callbacks::setIOCallbacks(idata, stdIn, stdOut, stdErr);
}
+
return code;
}
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1poll_1with_1handle
(JNIEnv *env, jclass, jlong instance, jobject poll, jlong callerHandle)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
int code = gsapi_set_poll_with_handle((void *)instance, callbacks::pollFunction, (void *)callerHandle);
if (code == 0)
{
- callbacks::setJNIEnv(env);
- callbacks::setPollCallback(poll);
+ callbacks::setJNIEnv(idata, env);
+ callbacks::setPollCallback(idata, poll);
}
return code;
}
@@ -90,11 +123,14 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1poll_1with_1han
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1poll
(JNIEnv *env, jclass, jlong instance, jobject poll)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
int code = gsapi_set_poll((void *)instance, callbacks::pollFunction);
if (code == 0)
{
- callbacks::setJNIEnv(env);
- callbacks::setPollCallback(poll);
+ callbacks::setJNIEnv(idata, env);
+ callbacks::setPollCallback(idata, poll);
}
return code;
}
@@ -102,6 +138,12 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1poll
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1display_1callback
(JNIEnv *env, jclass, jlong instance, jobject displayCallback)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
+ if (idata->hasinit && idata->displayCallback)
+ storeDispalyHandle(idata);
+
display_callback *cb = new display_callback;
cb->size = sizeof(display_callback);
cb->version_major = DISPLAY_VERSION_MAJOR;
@@ -124,8 +166,8 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1display_1callba
int code = gsapi_set_display_callback((void *)instance, cb);
if (code == 0)
{
- callbacks::setJNIEnv(env);
- callbacks::setDisplayCallback(displayCallback);
+ callbacks::setJNIEnv(idata, env);
+ callbacks::setDisplayCallback(idata, displayCallback);
}
return code;
}
@@ -133,11 +175,14 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1display_1callba
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1register_1callout
(JNIEnv *env, jclass, jlong instance, jobject callout, jlong calloutHandle)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
int code = gsapi_register_callout((void *)instance, callbacks::calloutFunction, (void *)calloutHandle);
if (code == 0)
{
- callbacks::setJNIEnv(env);
- callbacks::setCalloutCallback(callout);
+ callbacks::setJNIEnv(idata, env);
+ callbacks::setCalloutCallback(idata, callout);
}
return code;
}
@@ -157,10 +202,14 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1arg_1encoding
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1default_1device_1list
(JNIEnv *env, jclass, jlong instance, jbyteArray list, jint listlen)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
if (list == NULL)
return throwNullPointerException(env, "list");
+
jboolean isCopy = false;
- callbacks::setJNIEnv(env);
+ callbacks::setJNIEnv(idata, env);
int code = gsapi_set_default_device_list((void *)instance,
(const char *)env->GetByteArrayElements(list, &isCopy), listlen);
return code;
@@ -190,20 +239,34 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1get_1default_1device
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1init_1with_1args
(JNIEnv *env, jclass, jlong instance, jint argc, jobjectArray argv)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
if (argv == NULL)
return throwNullPointerException(env, "argv");
+
char **cargv = jbyteArray2DToCharArray(env, argv);
- callbacks::setJNIEnv(env);
+
+ callbacks::setJNIEnv(idata, env);
int code = gsapi_init_with_args((void *)instance, argc, cargv);
delete2DByteArray(argc, cargv);
+
+ if (code == 0)
+ {
+ idata->hasinit = true;
+ storeDispalyHandle(idata);
+ }
return code;
}
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string_1begin
(JNIEnv *env, jclass, jlong instance, jint userErrors, jobject pExitCode)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
int exitCode;
- callbacks::setJNIEnv(env);
+ callbacks::setJNIEnv(idata, env);
int code = gsapi_run_string_begin((void *)instance, userErrors, &exitCode);
if (pExitCode)
Reference::setValueField(env, pExitCode, toWrapperType(env, (jint)exitCode));
@@ -213,12 +276,16 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string_1begin
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string_1continue
(JNIEnv *env, jclass, jlong instance, jbyteArray str, jint length, jint userErrors, jobject pExitCode)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
if (str == NULL)
return throwNullPointerException(env, "str");
+
jboolean copy = false;
int exitCode;
const char *cstring = (const char *)env->GetByteArrayElements(str, &copy);
- callbacks::setJNIEnv(env);
+ callbacks::setJNIEnv(idata, env);
int code = gsapi_run_string_continue((void *)instance, cstring, length, userErrors, &exitCode);
if (pExitCode)
Reference::setValueField(env, pExitCode, toWrapperType(env, (jint)exitCode));
@@ -228,8 +295,11 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string_1continu
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string_1end
(JNIEnv *env, jclass, jlong instance, jint userErrors, jobject pExitCode)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
int exitCode;
- callbacks::setJNIEnv(env);
+ callbacks::setJNIEnv(idata, env);
int code = gsapi_run_string_end((void *)instance, userErrors, &exitCode);
if (pExitCode)
Reference::setValueField(env, pExitCode, toWrapperType(env, (jint)exitCode));
@@ -239,12 +309,16 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string_1end
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string_1with_1length
(JNIEnv *env, jclass, jlong instance, jbyteArray str, jint length, jint userErrors, jobject pExitCode)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
if (str == NULL)
return throwNullPointerException(env, "str");
+
jboolean copy = false;
int exitCode;
const char *cstring = (const char *)env->GetByteArrayElements(str, &copy);
- callbacks::setJNIEnv(env);
+ callbacks::setJNIEnv(idata, env);
int code = gsapi_run_string_with_length((void *)instance, cstring, length, userErrors, &exitCode);
if (pExitCode)
Reference::setValueField(env, pExitCode, toWrapperType(env, (jint)exitCode));
@@ -254,12 +328,16 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string_1with_1l
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string
(JNIEnv *env, jclass, jlong instance, jbyteArray str, jint userErrors, jobject pExitCode)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
if (str == NULL)
return throwNullPointerException(env, "str");
+
jboolean copy = false;
int exitCode;
const char *cstring = (const char *)env->GetByteArrayElements(str, &copy);
- callbacks::setJNIEnv(env);
+ callbacks::setJNIEnv(idata, env);
int code = gsapi_run_string((void *)instance, cstring, userErrors, &exitCode);
if (pExitCode)
Reference::setValueField(env, pExitCode, toWrapperType(env, (jint)exitCode));
@@ -269,12 +347,16 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1file
(JNIEnv *env, jclass, jlong instance, jbyteArray fileName, jint userErrors, jobject pExitCode)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
if (fileName == NULL)
return throwNullPointerException(env, "fileName");
+
jboolean copy = false;
int exitCode;
const char *cstring = (const char *)env->GetByteArrayElements(fileName, &copy);
- callbacks::setJNIEnv(env);
+ callbacks::setJNIEnv(idata, env);
int code = gsapi_run_file((void *)instance, cstring, userErrors, &exitCode);
if (pExitCode)
Reference::setValueField(env, pExitCode, toWrapperType(env, (jint)exitCode));
@@ -290,11 +372,11 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1exit
JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1param
(JNIEnv *env, jclass, jlong instance, jbyteArray param, jobject value, jint paramType)
{
+ GSInstanceData *idata = findDataFromInstance((void *)instance);
+ assert(idata);
+
if (!param)
- {
- throwNullPointerException(env, "param");
- return -1;
- }
+ return throwNullPointerException(env, "param");
gs_set_param_type type = (gs_set_param_type)paramType;
bool paramSuccess;
@@ -308,7 +390,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1param
jboolean copy = false;
const char *cstring = (const char *)env->GetByteArrayElements(param, &copy);
- callbacks::setJNIEnv(env);
+ callbacks::setJNIEnv(idata, env);
int code = gsapi_set_param((void *)instance, cstring, data, type);
free(data);
@@ -355,7 +437,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1get_1param_1once
jbyteArray arr = NULL;
const char *str = NULL;
- int len = 0;
+ jsize len = 0;
switch (stripped)
{
case gs_spt_null:
@@ -382,7 +464,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1get_1param_1once
case gs_spt_string:
case gs_spt_parsed:
str = (const char *)data;
- len = strlen(str) + 1;
+ len = (jsize)strlen(str) + 1;
arr = env->NewByteArray(len);
env->SetByteArrayRegion(arr, 0, len, (const jbyte *)str);
ref.set(arr);
@@ -426,7 +508,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1enumerate_1params
{
iterRef.set((jlong)citer);
- jsize len = strlen(ckey) + 1;
+ jsize len = (jsize)strlen(ckey) + 1;
jbyteArray arr = env->NewByteArray(len);
env->SetByteArrayRegion(arr, 0, len, (const jbyte *)ckey);
keyRef.set(arr);
@@ -507,23 +589,53 @@ void *getAsPointer(JNIEnv *env, jobject object, gs_set_param_type type, bool *su
break;
case gs_spt_bool:
result = malloc(sizeof(int));
+ if (!result)
+ {
+ throwAllocationError(env, "getAsPointer");
+ return NULL;
+ }
+
*((int *)result) = (bool)toBoolean(env, object);
break;
case gs_spt_int:
result = malloc(sizeof(int));
+ if (!result)
+ {
+ throwAllocationError(env, "getAsPointer");
+ return NULL;
+ }
+
*((int *)result) = (int)toInt(env, object);
break;
case gs_spt_float:
result = malloc(sizeof(float));
+ if (!result)
+ {
+ throwAllocationError(env, "getAsPointer");
+ return NULL;
+ }
+
*((float *)result) = (float)toFloat(env, object);
break;
case gs_spt_long:
case gs_spt_i64:
result = malloc(sizeof(long long));
+ if (!result)
+ {
+ throwAllocationError(env, "getAsPointer");
+ return NULL;
+ }
+
*((long long *)result) = (long long)toLong(env, object);
break;
case gs_spt_size_t:
result = malloc(sizeof(size_t));
+ if (!result)
+ {
+ throwAllocationError(env, "getAsPointer");
+ return NULL;
+ }
+
*((size_t *)result) = (size_t)toLong(env, object);
break;
case gs_spt_name:
@@ -533,6 +645,12 @@ void *getAsPointer(JNIEnv *env, jobject object, gs_set_param_type type, bool *su
cstring = (const char *)env->GetByteArrayElements(arr, &copy);
len = env->GetArrayLength(arr);
result = malloc(sizeof(char) * len);
+ if (!result)
+ {
+ throwAllocationError(env, "getAsPointer");
+ return NULL;
+ }
+
//((char *)result)[len - 1] = 0;
memcpy(result, cstring, len);
break;
@@ -541,6 +659,7 @@ void *getAsPointer(JNIEnv *env, jobject object, gs_set_param_type type, bool *su
*success = false;
break;
}
+
if (env->ExceptionCheck())
{
if (result)
@@ -550,3 +669,51 @@ void *getAsPointer(JNIEnv *env, jobject object, gs_set_param_type type, bool *su
}
return result;
}
+
+void storeDispalyHandle(GSInstanceData *idata)
+{
+ static const char PARAM_NAME[] = "DisplayHandle";
+
+ assert(idata);
+ assert(idata->instance);
+
+ char *param = NULL;
+ int bytes = gsapi_get_param(idata->instance, PARAM_NAME, NULL, gs_spt_string);
+ if (bytes == com_artifex_gsjava_GSAPI_GS_ERROR_UNDEFINED)
+ idata->displayCallback = NULL;
+ else
+ {
+ // Parse the DisplayHandle string again
+
+ param = new char[bytes];
+ gsapi_get_param(idata->instance, PARAM_NAME, param, gs_spt_string);
+
+ char *toparse = param;
+
+ int radix = 10; // default base 10
+
+ // If there is a # character, we need to change the radix
+ char *rend = strchr(param, '#');
+ if (rend)
+ {
+ *rend = 0;
+ radix = atoi(param);
+ toparse = rend + 1;
+ }
+
+ char *end;
+ long long val = std::strtoll(toparse, &end, radix);
+
+ idata->displayHandle = (void *)val;
+
+ delete[] param;
+ }
+
+ char buf[20]; // 16#[16 hex digits][null terminator]
+#if defined(_WIN32)
+ sprintf_s(buf, "16#%llx", (long long)idata);
+#else
+ snprintf(buf, sizeof(buf), "16#%llx", (long long)idata);
+#endif
+ gsapi_set_param(idata->instance, PARAM_NAME, buf, gs_spt_string);
+}
diff --git a/demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.h b/demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.h
index 68c61e58..fd7e303a 100644
--- a/demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.h
+++ b/demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.h
@@ -2,6 +2,8 @@
#include <jni.h>
/* Header for class com_artifex_gsjava_GSAPI */
+#include "settings.h"
+
#ifndef _Included_com_artifex_gsjava_GSAPI
#define _Included_com_artifex_gsjava_GSAPI
#ifdef __cplusplus
diff --git a/demos/java/jni/gs_jni/com_artifex_gsjava_util_NativePointer.cpp b/demos/java/jni/gs_jni/com_artifex_gsjava_util_NativePointer.cpp
index 04c435cb..0589c6b3 100644
--- a/demos/java/jni/gs_jni/com_artifex_gsjava_util_NativePointer.cpp
+++ b/demos/java/jni/gs_jni/com_artifex_gsjava_util_NativePointer.cpp
@@ -33,8 +33,8 @@ JNIEXPORT void JNICALL Java_com_artifex_gsjava_util_NativePointer_freeNative
JNIEXPORT jbyteArray JNICALL Java_com_artifex_gsjava_util_NativePointer_byteArrayNative
(JNIEnv *env, jclass, jlong address, jlong len)
{
- jbyteArray array = env->NewByteArray(len);
- env->SetByteArrayRegion(array, 0, len, (const jbyte *)address);
+ jbyteArray array = env->NewByteArray((jsize)len);
+ env->SetByteArrayRegion(array, 0, (jsize)len, (const jbyte *)address);
return array;
}
@@ -53,8 +53,8 @@ JNIEXPORT void JNICALL Java_com_artifex_gsjava_util_NativePointer_setByteNative
JNIEXPORT jcharArray JNICALL Java_com_artifex_gsjava_util_NativePointer_charArrayNative
(JNIEnv *env, jclass, jlong address, jlong len)
{
- jcharArray array = env->NewCharArray(len);
- env->SetCharArrayRegion(array, 0, len, (const jchar *)address);
+ jcharArray array = env->NewCharArray((jsize)len);
+ env->SetCharArrayRegion(array, 0, (jsize)len, (const jchar *)address);
return array;
}
@@ -73,8 +73,8 @@ JNIEXPORT void JNICALL Java_com_artifex_gsjava_util_NativePointer_setCharNative
JNIEXPORT jshortArray JNICALL Java_com_artifex_gsjava_util_NativePointer_shortArrayNative
(JNIEnv *env, jclass, jlong address, jlong len)
{
- jshortArray array = env->NewShortArray(len);
- env->SetShortArrayRegion(array, 0, len, (const jshort *)address);
+ jshortArray array = env->NewShortArray((jsize)len);
+ env->SetShortArrayRegion(array, 0, (jsize)len, (const jshort *)address);
return array;
}
@@ -93,8 +93,8 @@ JNIEXPORT void JNICALL Java_com_artifex_gsjava_util_NativePointer_setShortNative
JNIEXPORT jintArray JNICALL Java_com_artifex_gsjava_util_NativePointer_intArrayNative
(JNIEnv *env, jclass, jlong address, jlong len)
{
- jintArray array = env->NewIntArray(len);
- env->SetIntArrayRegion(array, 0, len, (const jint *)address);
+ jintArray array = env->NewIntArray((jsize)len);
+ env->SetIntArrayRegion(array, 0, (jsize)len, (const jint *)address);
return array;
}
@@ -113,8 +113,8 @@ JNIEXPORT void JNICALL Java_com_artifex_gsjava_util_NativePointer_setIntNative
JNIEXPORT jlongArray JNICALL Java_com_artifex_gsjava_util_NativePointer_longArrayNative
(JNIEnv *env, jclass, jlong address, jlong len)
{
- jlongArray array = env->NewLongArray(len);
- env->SetLongArrayRegion(array, 0, len, (const jlong *)address);
+ jlongArray array = env->NewLongArray((jsize)len);
+ env->SetLongArrayRegion(array, 0, (jsize)len, (const jlong *)address);
return array;
}
diff --git a/demos/java/jni/gs_jni/com_artifex_gsjava_util_NativePointer.h b/demos/java/jni/gs_jni/com_artifex_gsjava_util_NativePointer.h
index 8dd6a30b..6c053df8 100644
--- a/demos/java/jni/gs_jni/com_artifex_gsjava_util_NativePointer.h
+++ b/demos/java/jni/gs_jni/com_artifex_gsjava_util_NativePointer.h
@@ -2,6 +2,8 @@
#include <jni.h>
/* Header for class com_artifex_gsjava_util_NativePointer */
+#include "settings.h"
+
#ifndef _Included_com_artifex_gsjava_util_NativePointer
#define _Included_com_artifex_gsjava_util_NativePointer
#ifdef __cplusplus
diff --git a/demos/java/jni/gs_jni/gs_jni.vcxproj b/demos/java/jni/gs_jni/gs_jni.vcxproj
index 5febb0d2..56f03d0a 100644
--- a/demos/java/jni/gs_jni/gs_jni.vcxproj
+++ b/demos/java/jni/gs_jni/gs_jni.vcxproj
@@ -98,7 +98,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
- <AdditionalLibraryDirectories>$(SolutionDir)..\..\..\..\debugbin;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>$(SolutionDir)..\..\..\..\bin;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -120,6 +120,7 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
+ <AdditionalLibraryDirectories>$(SolutionDir)..\..\..\..\bin;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -138,7 +139,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>gpdldll64.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <AdditionalLibraryDirectories>$(SolutionDir)..\..\..\..\debugbin;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>$(SolutionDir)..\..\..\..\bin;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -169,13 +170,16 @@
<ClInclude Include="com_artifex_gsjava_GSAPI.h" />
<ClInclude Include="com_artifex_gsjava_util_NativePointer.h" />
<ClInclude Include="framework.h" />
+ <ClInclude Include="instance_data.h" />
<ClInclude Include="jni_util.h" />
+ <ClInclude Include="settings.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="callbacks.cpp" />
<ClCompile Include="com_artifex_gsjava_GSAPI.cpp" />
<ClCompile Include="com_artifex_gsjava_util_NativePointer.cpp" />
<ClCompile Include="dllmain.cpp" />
+ <ClCompile Include="instance_data.cpp" />
<ClCompile Include="jni_util.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/demos/java/jni/gs_jni/gs_jni.vcxproj.filters b/demos/java/jni/gs_jni/gs_jni.vcxproj.filters
index 14063f1b..ce1781b7 100644
--- a/demos/java/jni/gs_jni/gs_jni.vcxproj.filters
+++ b/demos/java/jni/gs_jni/gs_jni.vcxproj.filters
@@ -30,6 +30,12 @@
<ClInclude Include="com_artifex_gsjava_util_NativePointer.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="instance_data.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="settings.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
@@ -47,5 +53,8 @@
<ClCompile Include="com_artifex_gsjava_util_NativePointer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="instance_data.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
</Project> \ No newline at end of file
diff --git a/demos/java/jni/gs_jni/install_linux.sh b/demos/java/jni/gs_jni/install_linux.sh
index 1626c212..0f4b1147 100644
--- a/demos/java/jni/gs_jni/install_linux.sh
+++ b/demos/java/jni/gs_jni/install_linux.sh
@@ -1,7 +1,14 @@
#!bin/bash
-echo "Copy libgpdl.so -> /usr/lib/libgpdl.so"
-sudo cp -L "../../../../sobin/libgpdl.so" "/usr/lib/libgpdl.so"
+echo "Copy libgpdl.so"
+cp -L "../jni/gs_jni/gs_jni.so" "gs_jni.so"
-echo "Copy gs_jni.so -> /usr/lib/gs_jni.so"
-sudo cp "gs_jni.so" "/usr/lib/gs_jni.so" \ No newline at end of file
+echo "Create libpgdl.so link"
+cp "../../../sobin/lbgpdl.so" "libgpdl.so"
+
+cd ../../../sobin
+
+echo "Copy libgpdl.so target"
+cp $(readlink "libgpdl.so") "../demos/java/gsviewer"
+
+cd ../demos/java.gsviewer
diff --git a/demos/java/jni/gs_jni/instance_data.cpp b/demos/java/jni/gs_jni/instance_data.cpp
new file mode 100644
index 00000000..a65cdf5e
--- /dev/null
+++ b/demos/java/jni/gs_jni/instance_data.cpp
@@ -0,0 +1,64 @@
+#include "instance_data.h"
+
+#include <unordered_map>
+#include <assert.h>
+#include <mutex>
+
+#if !defined(GSJNI_NO_MT)
+
+static std::unordered_map<void *, GSInstanceData *> g_inst2Data; // instance -> data
+static std::mutex g_mtx;
+
+#else
+
+static GSInstanceData g_global; // Global instance for faster multithreading
+
+#endif
+
+GSInstanceData *putInstanceData(GSInstanceData *data)
+{
+#if !defined(GSJNI_NO_MT)
+ g_mtx.lock();
+
+ assert(g_inst2Data.find(data->instance) == g_inst2Data.end());
+
+ g_inst2Data[data->instance] = data;
+
+ g_mtx.unlock();
+
+ return data;
+
+#else
+ delete data;
+ return &g_global;
+#endif
+}
+
+GSInstanceData *findDataFromInstance(void *instance)
+{
+#if !defined(GSJNI_NO_MT)
+ g_mtx.lock();
+
+ auto it = g_inst2Data.find(instance);
+ GSInstanceData *result = it == g_inst2Data.end() ? NULL : it->second;
+
+ g_mtx.unlock();
+
+ return result;
+#else
+ return &g_global;
+#endif
+}
+
+void deleteDataFromInstance(void *instance)
+{
+#if !defined(GSJNI_NO_MT)
+ g_mtx.lock();
+
+ auto i2dit = g_inst2Data.find(instance);
+ if (i2dit != g_inst2Data.end())
+ g_inst2Data.erase(i2dit);
+
+ g_mtx.unlock();
+#endif
+} \ No newline at end of file
diff --git a/demos/java/jni/gs_jni/instance_data.h b/demos/java/jni/gs_jni/instance_data.h
new file mode 100644
index 00000000..2b2afe2e
--- /dev/null
+++ b/demos/java/jni/gs_jni/instance_data.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <jni.h>
+
+#include "settings.h"
+
+/*
+This class stores data about a Ghostscript instance.
+*/
+class GSInstanceData
+{
+public:
+ void *instance = NULL; // The Ghostscript instance
+ void *callerHandle = NULL; // The caller handle passed to gsapi_new_instance
+ void *stdioHandle = NULL; // The caller handle passed to gsapi_set_stdio_with_handle
+ void *displayHandle = NULL; // The handle passed to display callbacks
+
+ JNIEnv *env = NULL; // The JNIEnv which should be used for JNI API calls
+
+ jobject stdIn = NULL; // The user IStdInFunction class for stdin input
+ jobject stdOut = NULL; // The user IStdOutFunction class for stdout output
+ jobject stdErr = NULL; // The user IStdErrFunction class for stderr output
+
+ jobject poll = NULL; // The user IPollFunction class
+
+ jobject displayCallback = NULL; // The user DisplayCallback class
+
+ jobject callout = NULL; // The user ICalloutFunction class
+
+ bool hasinit = false; // Whether the user has called init_with_args
+};
+
+GSInstanceData *putInstanceData(GSInstanceData *data);
+GSInstanceData *findDataFromInstance(void *instance);
+void deleteDataFromInstance(void *instance); \ No newline at end of file
diff --git a/demos/java/jni/gs_jni/jni_util.cpp b/demos/java/jni/gs_jni/jni_util.cpp
index 74852a95..cd43c534 100644
--- a/demos/java/jni/gs_jni/jni_util.cpp
+++ b/demos/java/jni/gs_jni/jni_util.cpp
@@ -90,7 +90,7 @@ char **util::jbyteArray2DToCharArray(JNIEnv *env, jobjectArray array)
{
jbyteArray byteArrayObject = (jbyteArray)env->GetObjectArrayElement(array, i);
char *elem = (char *)env->GetByteArrayElements(byteArrayObject, &copy);
- jsize slen = strlen(elem);
+ jsize slen = (jsize)strlen(elem);
char *nstring = new char[slen + 1LL];
nstring[slen] = 0;
memcpy(nstring, elem, slen);
@@ -145,14 +145,14 @@ jint util::getIntField(JNIEnv *env, jobject object, const char *field)
return env->GetIntField(object, fieldID);
}
-int util::callIntMethod(JNIEnv *env, jobject object, const char *method, const char *sig, ...)
+jint util::callIntMethod(JNIEnv *env, jobject object, const char *method, const char *sig, ...)
{
jmethodID methodID = getMethodID(env, object, method, sig);
if (methodID == NULL)
return 0;
va_list args;
- int result;
+ jint result;
va_start(args, sig);
result = env->CallIntMethodV(object, methodID, args);
va_end(args);
diff --git a/demos/java/jni/gs_jni/jni_util.h b/demos/java/jni/gs_jni/jni_util.h
index 6439712e..2ea20fd4 100644
--- a/demos/java/jni/gs_jni/jni_util.h
+++ b/demos/java/jni/gs_jni/jni_util.h
@@ -2,6 +2,8 @@
#include <jni.h>
+#include "settings.h"
+
#define REFERENCE_VALUE_FILED_NAME "value"
namespace util
@@ -141,7 +143,7 @@ namespace util
@param sig The method's signature.
@param ... The varargs representing the object's arguments in their respective order.
*/
- int callIntMethod(JNIEnv *env, jobject object, const char *name, const char *sig, ...);
+ jint callIntMethod(JNIEnv *env, jobject object, const char *name, const char *sig, ...);
void setObjectField(JNIEnv *env, jobject object, const char *field, jobject value);
diff --git a/demos/java/jni/gs_jni/settings.h b/demos/java/jni/gs_jni/settings.h
new file mode 100644
index 00000000..9bd7490a
--- /dev/null
+++ b/demos/java/jni/gs_jni/settings.h
@@ -0,0 +1,5 @@
+#pragma once
+
+// Use to disable multithreading support, ghostpdl must be compiled with multithreading
+// enabled as well
+//#define GSJNI_NO_MT \ No newline at end of file
diff --git a/demos/java/mtdemo/Main.java b/demos/java/mtdemo/Main.java
new file mode 100644
index 00000000..82e63619
--- /dev/null
+++ b/demos/java/mtdemo/Main.java
@@ -0,0 +1,51 @@
+import com.artifex.gsjava.GSInstance;
+
+import java.io.File;
+
+import java.util.Scanner;
+
+public class Main {
+
+ // The file we will read from
+ public static final String INFILE = "../../../examples/tiger.eps";
+
+ // The output directory
+ public static final String OUTDIR = "pdfout";
+
+ public static void main(String[] args) {
+ // For multithreading, call this before any GSInstance objects
+ // are created.
+ GSInstance.setAllowMultithreading(true);
+
+ // Parse first command line argument as thread count
+ int workerCount = 10;
+ if (args.length > 0) {
+ try {
+ workerCount = Integer.parseInt(args[0]);
+ } catch (NumberFormatException e) { }
+ }
+
+ // Create output directory if it doesn't exist
+ File outdirFile = new File(OUTDIR);
+ if (outdirFile.exists()) {
+ if (outdirFile.isFile())
+ System.err.println("Output directory exists as a file!");
+ } else {
+ outdirFile.mkdirs();
+ }
+
+
+ Worker[] workers = new Worker[workerCount];
+
+ // Create each worker
+ for (int i = 0; i < workers.length; i++) {
+ workers[i] = new Worker(INFILE, OUTDIR + "/out" + i + ".pdf");
+ }
+
+ // Dispatch each worker
+ System.out.println("Starting workers...");
+ for (int i = 0; i < workers.length; i++) {
+ workers[i].start();
+ }
+ }
+}
diff --git a/demos/java/mtdemo/README.txt b/demos/java/mtdemo/README.txt
new file mode 100644
index 00000000..0dca6335
--- /dev/null
+++ b/demos/java/mtdemo/README.txt
@@ -0,0 +1,53 @@
+This directory contains files which demo multithreading in Ghostscript
+using the Java Language Bindings. The application here simply reads in
+the same EPS file and outputs it to multiple PDF files on different
+threads.
+
+What is here?
+
+* Main.java - Class containing Java main method
+* Worker.java - Class which handles concurrently running Ghostscript
+* build_darwin.sh - Builds the Java program on Darwin systems
+* build_linux.sh - Builds the Java program on Linux systems
+* build_win32.bat - Builds the Java program on Windows
+* runmtd_darwin.sh - Runs the Java program on Darwin systems
+* runmtd_linux.sh - Runs the Java program on Linux systems
+* runmtd_win32.bat - Starts the Java program for Windows
+
+Build/run instructions:
+
+-= WINDOWS =-
+
+1. Ensure the following libraries have been built and are in
+ this directory:
+ * gpdldll64.dll
+ * gs_jni.dll
+
+2. Run build_win32.bat to build.
+
+3. Run runmtd_win32.bat to start the application.
+
+
+-= LINUX =-
+
+1. Ensure the following libraries have been built and are in
+ this directory:
+ * libgpdl.so (this would have been built as a link to another file, so
+ it should be copied into this directory and renamed to libgpdl.so)
+ gs_jni.so
+
+2. If using OpenJDK 8, the property "assistive_technologies" may
+ need to be modified for the Java code to build. It can be modified by
+ editing the "accessibility.properties" file. This is located at:
+
+ /etc/java-8-openjdk/accessibility.properties
+
+3. Run build_linux.sh to build and copy libraries.
+
+5. Run runmtd_linux.sh to start the application.
+
+
+-= DARWIN =-
+
+Same as Linux, except with .dylib extensions on all shared objects and
+"_darwin.sh" suffixes instead of "_linux.sh". \ No newline at end of file
diff --git a/demos/java/mtdemo/Worker.java b/demos/java/mtdemo/Worker.java
new file mode 100644
index 00000000..53393cba
--- /dev/null
+++ b/demos/java/mtdemo/Worker.java
@@ -0,0 +1,127 @@
+import java.io.File;
+
+import com.artifex.gsjava.GSInstance;
+import com.artifex.gsjava.callbacks.DisplayCallback;
+import com.artifex.gsjava.util.*;
+import com.artifex.gsjava.callbacks.*;
+
+import java.io.IOException;
+
+import java.util.Arrays;
+
+import static com.artifex.gsjava.GSAPI.*;
+
+/**
+ * Handles running a separate instance of Ghostscript.
+ */
+public class Worker implements Runnable {
+
+ // Whether to use Java-implemented standard input/output
+ public static final boolean USE_CUSTOM_STDIO = true;
+
+ // Whether stdout is enabled (effective only if USE_CUSTOM_STDIO = true)
+ public static final boolean USE_STDOUT = false;
+
+ private static int ID = 0; // Next thread ID
+
+ private final File inPS, outPDF; // Input and output files
+ private final Thread thread; // The thread running this worker
+
+ /**
+ * Create a worker thread to convert a postscript file
+ * to a pdf file.
+ *
+ * @param inPS The postscript file.
+ * @param outPDF The output PDF file.
+ */
+ public Worker(String inPS, String outPDF) {
+ this.inPS = new File(inPS);
+ this.outPDF = new File(outPDF);
+ this.thread = new Thread(this);
+ this.thread.setName("Worker-" + ID++);
+ }
+
+ /**
+ * Starts this worker
+ */
+ public void start() {
+ System.out.println("Start: " + thread.getName());
+ thread.start();
+ }
+
+ @Override
+ public void run() {
+ System.out.println("Started worker: " + thread.getName());
+
+ GSInstance gsInstance = null;
+ try {
+ gsInstance = createGSInstance();
+
+ // If we want to use the IO functions StdIO, set them
+ if (USE_CUSTOM_STDIO) {
+ StdIO io = new StdIO();
+ gsInstance.set_stdio(io, USE_STDOUT ? io : null, io);
+ }
+
+ // Create the output file if it doesn't exist
+ if (!outPDF.exists())
+ outPDF.createNewFile();
+
+ // Ghostscript arguments to use with init_with_args
+ String[] args = {
+ "gs",
+ "-sDEVICE=pdfwrite",
+ "-o", outPDF.getPath(),
+ inPS.getPath()
+ };
+
+ // init_with_args will perform all the conversions
+ int code = gsInstance.init_with_args(args);
+ if (code != GS_ERROR_OK)
+ throw new IllegalStateException("Failed to init with args (code = " + code + ")");
+
+ System.out.println("Worker " + thread.getName() + " completed.");
+ } catch (Exception e) {
+ System.err.println("Worker " + thread.getName() + " threw exception: " + e);
+ } finally {
+ if (gsInstance != null)
+ gsInstance.delete_instance();
+ }
+ }
+
+ // Creates a new Ghostscript inistance
+ private GSInstance createGSInstance() {
+ GSInstance gsInstance = new GSInstance();
+
+ int code;
+ code = gsInstance.set_arg_encoding(1);
+ if (code != GS_ERROR_OK) {
+ gsInstance.delete_instance();
+ throw new IllegalStateException("Failed to set arg encoding (code = " + code + ")");
+ }
+
+ return gsInstance;
+ }
+
+ // Handles standard input/output
+ private static class StdIO implements IStdOutFunction, IStdErrFunction, IStdInFunction {
+
+ public int onStdOut(long callerHandle, byte[] str, int len) {
+ System.out.println(new String(str));
+ return len;
+ }
+
+ public int onStdErr(long callerHandle, byte[] str, int len) {
+ System.err.println(new String(str));
+ return len;
+ }
+
+ public int onStdIn(long callerHandle, byte[] buf, int len) {
+ try {
+ return System.in.read(buf, 0, len);
+ } catch (IOException e) {
+ return 0;
+ }
+ }
+ }
+}
diff --git a/demos/java/mtdemo/build_darwin.sh b/demos/java/mtdemo/build_darwin.sh
new file mode 100755
index 00000000..6080bed0
--- /dev/null
+++ b/demos/java/mtdemo/build_darwin.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Build mtdemo"
+
+cd ../gsjava
+
+bash build_darwin.sh
+
+cd ../mtdemo
+
+cp ../mtdemo/gsjava.jar gsjava.jar
+
+echo "Compiling mtdemo Java source..."
+javac -classpath "../gsjava/bin:." "Main.java" "Worker.java"
+echo "Done."
+
+echo "Copy gs_jni.dylib"
+cp "../jni/gs_jni/gs_jni.dylib" "gs_jni.dylib"
+
+echo "Create libgpdl.dylib link"
+cp "../../../sobin/libgpdl.dylib" "libgpdl.dylib"
+
+cd ../../../sobin
+
+echo "Copy libgpdl.dylib target"
+cp $(readlink "libgpdl.dylib") "../demos/java/mtdemo"
+
+cd ../demos/java/mtdemo \ No newline at end of file
diff --git a/demos/java/mtdemo/build_linux.sh b/demos/java/mtdemo/build_linux.sh
new file mode 100755
index 00000000..698b4583
--- /dev/null
+++ b/demos/java/mtdemo/build_linux.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Build mtdemo"
+
+cd ../gsjava
+
+bash build_linux.sh
+
+cd ../mtdemo
+
+cp ../gsjava/gsjava.jar gsjava.jar
+
+echo "Compiling mtdemo Java source..."
+javac -classpath "../gsjava/bin:." "Main.java" "Worker.java"
+echo "Done."
+
+echo "Copy gs_jni.so"
+cp "../jni/gs_jni/gs_jni.so" "gs_jni.so"
+
+echo "Create libgpdl.so link"
+cp "../../../sobin/libgpdl.so" "libgpdl.so"
+
+cd ../../../sobin
+
+echo "Copy libgpdl.so target"
+cp $(readlink "libgpdl.so") "../demos/java/mtdemo"
+
+cd ../demos/java/mtdemo \ No newline at end of file
diff --git a/demos/java/mtdemo/build_win32.bat b/demos/java/mtdemo/build_win32.bat
new file mode 100755
index 00000000..1074fab8
--- /dev/null
+++ b/demos/java/mtdemo/build_win32.bat
@@ -0,0 +1,15 @@
+@echo off
+
+echo Build mtdemo
+
+cd "..\gsjava"
+
+call build_win32
+
+cd "..\mtdemo"
+
+copy "..\gsjava\gsjava.jar" ".\gsjava.jar"
+
+echo Compiling mtdemo Java source...
+javac -cp ../gsjava/bin;. Main.java Worker.java
+echo Done. \ No newline at end of file
diff --git a/demos/java/mtdemo/runmtd_darwin.sh b/demos/java/mtdemo/runmtd_darwin.sh
new file mode 100755
index 00000000..4e3b2114
--- /dev/null
+++ b/demos/java/mtdemo/runmtd_darwin.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+export LD_PRELOAD=./libgpdl.dylib
+
+java -cp "gsjava.jar:." Main "$ARG1"
diff --git a/demos/java/mtdemo/runmtd_linux.sh b/demos/java/mtdemo/runmtd_linux.sh
new file mode 100755
index 00000000..72acf6b6
--- /dev/null
+++ b/demos/java/mtdemo/runmtd_linux.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+java -cp "gsjava.jar:." Main "$ARG1"
diff --git a/demos/java/mtdemo/runmtd_win32.bat b/demos/java/mtdemo/runmtd_win32.bat
new file mode 100755
index 00000000..f23ce990
--- /dev/null
+++ b/demos/java/mtdemo/runmtd_win32.bat
@@ -0,0 +1,3 @@
+@echo off
+
+java -cp gsjava.jar;. Main %1 \ No newline at end of file
diff --git a/demos/python/examples.py b/demos/python/examples.py
index d5c8b6ad..864bed96 100755
--- a/demos/python/examples.py
+++ b/demos/python/examples.py
@@ -127,7 +127,6 @@ def trans_ps():
# Run string to feed chunks
def run_string():
- f = None
size = 1024;
in_filename = '%s/examples/tiger.eps' % ghostpdl_root
out_filename = 'tiger_byte_fed.png'
@@ -139,19 +138,21 @@ def run_string():
gsapi.gsapi_set_arg_encoding(instance, gsapi.GS_ARG_ENCODING_UTF8)
gsapi.gsapi_init_with_args(instance, params)
- exitcode = gsapi.gsapi_run_string_begin(instance, 0)
+ gsapi.gsapi_run_string_begin(instance, 0)
with open(in_filename,"rb") as f:
while True:
data = f.read(size)
if not data:
break
- exitcode = gsapi.gsapi_run_string_continue(instance, data, 0)
+ gsapi.gsapi_run_string_continue(instance, data, 0)
exitcode = gsapi.gsapi_run_string_end(instance, 0)
end_gpdl(instance)
+ return exitcode
+
# Examples
print('***********Text extraction***********');
diff --git a/demos/python/gsapi.py b/demos/python/gsapi.py
index 6dfeb95b..4d405a5e 100755
--- a/demos/python/gsapi.py
+++ b/demos/python/gsapi.py
@@ -343,6 +343,7 @@ def gsapi_set_arg_encoding(instance, encoding):
GS_ARG_ENCODING_UTF8,
GS_ARG_ENCODING_UTF16LE,
)
+ global _encoding
e = _libgs.gsapi_set_arg_encoding(instance, encoding)
if e < 0:
raise GSError(e)
@@ -377,7 +378,7 @@ def gsapi_init_with_args(instance, args):
raise GSError(e)
-def gsapi_run_string_begin(instance, user_errors):
+def gsapi_run_string_begin(instance, user_errors=0):
'''
Returns <exit_code>.
'''
@@ -388,7 +389,7 @@ def gsapi_run_string_begin(instance, user_errors):
return pexit_code.value
-def gsapi_run_string_continue(instance, str_, user_errors):
+def gsapi_run_string_continue(instance, str_, user_errors=0):
'''
<str_> should be either a python string or a bytes object. If the former,
it is converted into a bytes object using utf-8 encoding.
@@ -416,7 +417,7 @@ def gsapi_run_string_continue(instance, str_, user_errors):
return pexit_code.value
-def gsapi_run_string_end(instance, user_errors):
+def gsapi_run_string_end(instance, user_errors=0):
'''
Returns <exit_code>.
'''
@@ -431,17 +432,17 @@ def gsapi_run_string_end(instance, user_errors):
return pexit_code.value
-def gsapi_run_string_with_length(instance, str_, length, user_errors):
+def gsapi_run_string_with_length(instance, str_, length, user_errors=0):
'''
<str_> should be either a python string or a bytes object. If the former,
it is converted into a bytes object using utf-8 encoding.
Returns <exit_code>.
'''
- return gsapi_run_string(instance, str_[:length], user_errors)
+ return gsapi_run_string(instance, str_[:length], user_errors=0)
-def gsapi_run_string(instance, str_, user_errors):
+def gsapi_run_string(instance, str_, user_errors=0):
'''
<str_> should be either a python string or a bytes object. If the former,
it is converted into a bytes object using utf-8 encoding.
@@ -465,7 +466,7 @@ def gsapi_run_string(instance, str_, user_errors):
return pexit_code.value
-def gsapi_run_file(instance, filename, user_errors):
+def gsapi_run_file(instance, filename, user_errors=0):
'''
Returns <exit_code>.
'''
@@ -558,8 +559,7 @@ def gsapi_set_param(instance, param, value, type_=None):
if type_ is None:
# Choose a gs_spt_* that matches the Python type of <value>.
- if 0: pass
- elif value is None:
+ if value is None:
type_ = gs_spt_null
elif isinstance(value, bool):
type_ = gs_spt_bool
@@ -589,8 +589,7 @@ def gsapi_set_param(instance, param, value, type_=None):
else:
# Bool/int/float.
type2 = None
- if 0: pass
- elif type_ == gs_spt_bool:
+ if type_ == gs_spt_bool:
type2 = ctypes.c_int
elif type_ == gs_spt_int:
type2 = ctypes.c_int
@@ -977,7 +976,7 @@ if 0:
# gsapi_add_fs()
# gsapi_remove_fs()
#
- class gsapi_fs_t(ctypes.Structure):
+ class gsapi_fs_t(ctypes.Structure): # lgtm [py/unreachable-statement]
_fields_ = [
('open_file',
ctypes.CFUNCTYPE(ctypes.c_int,
@@ -1104,7 +1103,6 @@ if __name__ == '__main__':
pass
else:
assert 0
- if 0: assert gsapi_get_param(instance, "foo") is None
# Enumerate all params and print name/value.
print('gsapi_enumerate_params():')