summaryrefslogtreecommitdiff
path: root/psi
diff options
context:
space:
mode:
authorChris Liddell <chris.liddell@artifex.com>2013-07-23 16:24:19 +0100
committerChris Liddell <chris.liddell@artifex.com>2015-07-20 18:21:17 +0100
commit6948650efd3fb9e2a70b8cf16aca57e9d0b7eb0a (patch)
tree5c2a1c671c1d4521f8a770d1e69e3d4342718030 /psi
parent7fd9e0be26e67c36f87733bc89ea07dc26d9f839 (diff)
downloadghostpdl-6948650efd3fb9e2a70b8cf16aca57e9d0b7eb0a.tar.gz
Commit of build_consolidation branch
Squashed into one commit (see branch for details of the evolution of the branch). This brings gpcl6 and gxps into the Ghostscript build system, and a shared set of graphics library object files for all the interpreters. Also, brings the same configuration options to the pcl and xps products as we have for Ghostscript.
Diffstat (limited to 'psi')
-rw-r--r--psi/apitest.c141
-rw-r--r--psi/bfont.h87
-rw-r--r--psi/btoken.h44
-rw-r--r--psi/dmmain.c998
-rw-r--r--psi/dmmain.r2538
-rw-r--r--psi/dpmain.c1069
-rw-r--r--psi/dscparse.c4483
-rw-r--r--psi/dscparse.h555
-rw-r--r--psi/dstack.h300
-rw-r--r--psi/dw32c.def4
-rw-r--r--psi/dw64c.def4
-rw-r--r--psi/dwdll.c231
-rw-r--r--psi/dwdll.h55
-rw-r--r--psi/dwimg.c1685
-rw-r--r--psi/dwimg.h98
-rw-r--r--psi/dwmain.c564
-rw-r--r--psi/dwmain.rc33
-rw-r--r--psi/dwmain32.def3
-rw-r--r--psi/dwmain64.def3
-rw-r--r--psi/dwmainc.c715
-rw-r--r--psi/dwnodll.c51
-rw-r--r--psi/dwreg.c105
-rw-r--r--psi/dwreg.h25
-rw-r--r--psi/dwres.h26
-rw-r--r--psi/dwsetup.def4
-rw-r--r--psi/dwsetup_x64.manifest15
-rw-r--r--psi/dwsetup_x86.manifest15
-rw-r--r--psi/dwtext.c1352
-rw-r--r--psi/dwtext.h160
-rw-r--r--psi/dwtrace.c369
-rw-r--r--psi/dwtrace.h26
-rw-r--r--psi/dwuninst.def4
-rw-r--r--psi/dwuninst_x64.manifest15
-rw-r--r--psi/dwuninst_x86.manifest15
-rw-r--r--psi/dxmain.c1244
-rw-r--r--psi/dxmainc.c113
-rw-r--r--psi/estack.h170
-rw-r--r--psi/files.h152
-rw-r--r--psi/ghost.h25
-rw-r--r--psi/gs.c151
-rw-r--r--psi/gsdll.c205
-rw-r--r--psi/gsdll2.def21
-rw-r--r--psi/gsdll2.rc18
-rw-r--r--psi/gsdll32.def39
-rw-r--r--psi/gsdll32.rc50
-rw-r--r--psi/gsdll32metro.def39
-rw-r--r--psi/gsdll32w.lnk11
-rw-r--r--psi/gsdll64.def39
-rw-r--r--psi/gsdll64metro.def39
-rw-r--r--psi/gsdllARM32metro.def39
-rw-r--r--psi/gserver.c317
-rw-r--r--psi/gsos2.def3
-rw-r--r--psi/gsos2.icx233
-rw-r--r--psi/gsos2.rc18
-rw-r--r--psi/ialloc.c380
-rw-r--r--psi/ialloc.h125
-rw-r--r--psi/iapi.c573
-rw-r--r--psi/iapi.h374
-rw-r--r--psi/iastate.h27
-rw-r--r--psi/iastruct.h25
-rw-r--r--psi/ibnum.c259
-rw-r--r--psi/ibnum.h80
-rw-r--r--psi/ichar.h74
-rw-r--r--psi/ichar1.h60
-rw-r--r--psi/icharout.h71
-rw-r--r--psi/icid.h46
-rw-r--r--psi/icie.h96
-rw-r--r--psi/icolor.h52
-rw-r--r--psi/iconf.c115
-rw-r--r--psi/iconf.h36
-rw-r--r--psi/icontext.c350
-rw-r--r--psi/icontext.h52
-rw-r--r--psi/icremap.h42
-rw-r--r--psi/icsmap.h43
-rw-r--r--psi/icstate.h91
-rw-r--r--psi/iddict.h45
-rw-r--r--psi/iddstack.h37
-rw-r--r--psi/idebug.c318
-rw-r--r--psi/idebug.h42
-rw-r--r--psi/idict.c904
-rw-r--r--psi/idict.h272
-rw-r--r--psi/idictdef.h88
-rw-r--r--psi/idicttpl.h63
-rw-r--r--psi/idisp.c107
-rw-r--r--psi/idisp.h29
-rw-r--r--psi/idosave.h60
-rw-r--r--psi/idparam.c471
-rw-r--r--psi/idparam.h132
-rw-r--r--psi/idsdata.h83
-rw-r--r--psi/idstack.c267
-rw-r--r--psi/idstack.h62
-rw-r--r--psi/ierrors.h69
-rw-r--r--psi/iesdata.h52
-rw-r--r--psi/iestack.h38
-rw-r--r--psi/ifapi.h30
-rw-r--r--psi/ifcid.h30
-rw-r--r--psi/ifilter.h92
-rw-r--r--psi/ifilter2.h28
-rw-r--r--psi/ifont.h119
-rw-r--r--psi/ifont1.h68
-rw-r--r--psi/ifont2.h33
-rw-r--r--psi/ifont42.h56
-rw-r--r--psi/ifrpred.h27
-rw-r--r--psi/ifunc.h81
-rw-r--r--psi/ifwpred.h27
-rw-r--r--psi/igc.c1392
-rw-r--r--psi/igc.h99
-rw-r--r--psi/igcref.c768
-rw-r--r--psi/igcstr.c446
-rw-r--r--psi/igcstr.h35
-rw-r--r--psi/igstate.h209
-rw-r--r--psi/iht.h28
-rw-r--r--psi/iimage.h53
-rw-r--r--psi/iimage2.h34
-rw-r--r--psi/iinit.c543
-rw-r--r--psi/iinit.h47
-rw-r--r--psi/ilevel.h28
-rw-r--r--psi/ilocate.c645
-rw-r--r--psi/imain.c1063
-rw-r--r--psi/imain.h254
-rw-r--r--psi/imainarg.c1265
-rw-r--r--psi/imainarg.h55
-rw-r--r--psi/imemory.h99
-rw-r--r--psi/iminst.h94
-rw-r--r--psi/iname.c661
-rw-r--r--psi/iname.h88
-rw-r--r--psi/inamedef.h171
-rw-r--r--psi/inameidx.h84
-rw-r--r--psi/inames.h111
-rw-r--r--psi/inamestr.h91
-rw-r--r--psi/inobtokn.c28
-rw-r--r--psi/inouparm.c25
-rw-r--r--psi/int.mak2033
-rw-r--r--psi/interp.c1944
-rw-r--r--psi/interp.h96
-rw-r--r--psi/iosdata.h37
-rw-r--r--psi/iostack.h29
-rw-r--r--psi/ipacked.h133
-rw-r--r--psi/iparam.c1137
-rw-r--r--psi/iparam.h111
-rw-r--r--psi/iparray.h35
-rw-r--r--psi/ipcolor.h39
-rw-r--r--psi/iplugin.c99
-rw-r--r--psi/iplugin.h71
-rw-r--r--psi/ireclaim.c189
-rw-r--r--psi/iref.h622
-rw-r--r--psi/isave.c1422
-rw-r--r--psi/isave.h125
-rw-r--r--psi/iscan.c1253
-rw-r--r--psi/iscan.h207
-rw-r--r--psi/iscanbin.c941
-rw-r--r--psi/iscanbin.h33
-rw-r--r--psi/iscannum.c379
-rw-r--r--psi/iscannum.h30
-rw-r--r--psi/isdata.h99
-rw-r--r--psi/isstate.h38
-rw-r--r--psi/istack.c642
-rw-r--r--psi/istack.h194
-rw-r--r--psi/istkparm.h41
-rw-r--r--psi/istream.h36
-rw-r--r--psi/istruct.h89
-rw-r--r--psi/itoken.h51
-rw-r--r--psi/iutil.c994
-rw-r--r--psi/iutil.h153
-rw-r--r--psi/iutil2.c147
-rw-r--r--psi/iutil2.h47
-rw-r--r--psi/ivmem2.h26
-rw-r--r--psi/ivmspace.h102
-rw-r--r--psi/main.h107
-rw-r--r--psi/mkfilelt.cpp368
-rw-r--r--psi/msvc.mak1788
-rw-r--r--psi/msvc32.mak25
-rw-r--r--psi/msvc64.mak23
-rw-r--r--psi/nsisinst.nsi268
-rw-r--r--psi/oparc.h31
-rw-r--r--psi/opcheck.h78
-rw-r--r--psi/opdef.h148
-rw-r--r--psi/oper.h106
-rw-r--r--psi/opextern.h153
-rw-r--r--psi/os2.mak670
-rw-r--r--psi/ostack.h86
-rw-r--r--psi/psi.mak65
-rw-r--r--psi/psitop.c677
-rw-r--r--psi/psromfs.mak438
-rw-r--r--psi/sfilter1.c338
-rw-r--r--psi/store.h265
-rw-r--r--psi/winint.mak217
-rw-r--r--psi/zalg.c208
-rw-r--r--psi/zarith.c457
-rw-r--r--psi/zarray.c136
-rw-r--r--psi/zbfont.c935
-rw-r--r--psi/zbseq.c150
-rw-r--r--psi/zcfont.c172
-rw-r--r--psi/zchar.c967
-rw-r--r--psi/zchar1.c1305
-rw-r--r--psi/zchar2.c40
-rw-r--r--psi/zchar32.c210
-rw-r--r--psi/zchar42.c297
-rw-r--r--psi/zchar42.h25
-rw-r--r--psi/zcharout.c423
-rw-r--r--psi/zcharx.c189
-rw-r--r--psi/zcid.c245
-rw-r--r--psi/zcidtest.c144
-rw-r--r--psi/zcie.c963
-rw-r--r--psi/zcie.h28
-rw-r--r--psi/zcolor.c6474
-rw-r--r--psi/zcolor.h99
-rw-r--r--psi/zcolor1.c170
-rw-r--r--psi/zcolor2.c52
-rw-r--r--psi/zcolor3.c112
-rw-r--r--psi/zcontext.c1330
-rw-r--r--psi/zcontrol.c1086
-rw-r--r--psi/zcrd.c447
-rw-r--r--psi/zcsdevn.c46
-rw-r--r--psi/zcsindex.c66
-rw-r--r--psi/zcspixel.c25
-rw-r--r--psi/zcssepr.c111
-rw-r--r--psi/zdevcal.c70
-rw-r--r--psi/zdevice.c644
-rw-r--r--psi/zdevice2.c379
-rw-r--r--psi/zdfilter.c50
-rw-r--r--psi/zdict.c559
-rw-r--r--psi/zdosio.c107
-rw-r--r--psi/zdouble.c551
-rw-r--r--psi/zdpnext.c482
-rw-r--r--psi/zdps.c277
-rw-r--r--psi/zdps1.c488
-rw-r--r--psi/zdscpars.c517
-rw-r--r--psi/zfaes.c72
-rw-r--r--psi/zfapi.c2453
-rw-r--r--psi/zfarc4.c93
-rw-r--r--psi/zfbcp.c90
-rw-r--r--psi/zfcid.c92
-rw-r--r--psi/zfcid0.c581
-rw-r--r--psi/zfcid1.c585
-rw-r--r--psi/zfcmap.c488
-rw-r--r--psi/zfdctd.c115
-rw-r--r--psi/zfdcte.c143
-rw-r--r--psi/zfdecode.c362
-rw-r--r--psi/zfile.c1200
-rw-r--r--psi/zfile.h25
-rw-r--r--psi/zfile1.c128
-rw-r--r--psi/zfileio.c986
-rw-r--r--psi/zfilter.c462
-rw-r--r--psi/zfilter2.c153
-rw-r--r--psi/zfilterx.c174
-rw-r--r--psi/zfimscale.c77
-rw-r--r--psi/zfjbig2.c140
-rw-r--r--psi/zfjpx.c149
-rw-r--r--psi/zfmd5.c43
-rw-r--r--psi/zfont.c653
-rw-r--r--psi/zfont0.c357
-rw-r--r--psi/zfont1.c353
-rw-r--r--psi/zfont2.c2841
-rw-r--r--psi/zfont32.c92
-rw-r--r--psi/zfont42.c420
-rw-r--r--psi/zfontenum.c133
-rw-r--r--psi/zform.c205
-rw-r--r--psi/zfproc.c387
-rw-r--r--psi/zfrsd.c474
-rw-r--r--psi/zfrsd.h39
-rw-r--r--psi/zfsample.c718
-rw-r--r--psi/zfsha2.c43
-rw-r--r--psi/zfunc.c417
-rw-r--r--psi/zfunc.h27
-rw-r--r--psi/zfunc0.c94
-rw-r--r--psi/zfunc3.c141
-rw-r--r--psi/zfunc4.c599
-rw-r--r--psi/zfzlib.c103
-rw-r--r--psi/zgeneric.c621
-rw-r--r--psi/zgstate.c605
-rw-r--r--psi/zhsb.c61
-rw-r--r--psi/zht.c278
-rw-r--r--psi/zht1.c151
-rw-r--r--psi/zht2.c554
-rw-r--r--psi/zht2.h33
-rw-r--r--psi/zicc.c575
-rw-r--r--psi/zicc.h26
-rw-r--r--psi/zimage.c655
-rw-r--r--psi/zimage2.c41
-rw-r--r--psi/zimage3.c137
-rw-r--r--psi/ziodev.c301
-rw-r--r--psi/ziodev2.c127
-rw-r--r--psi/ziodevs.c273
-rw-r--r--psi/ziodevsc.c321
-rw-r--r--psi/zmath.c275
-rw-r--r--psi/zmatrix.c420
-rw-r--r--psi/zmedia2.c518
-rw-r--r--psi/zmisc.c528
-rw-r--r--psi/zmisc1.c176
-rw-r--r--psi/zmisc2.c287
-rw-r--r--psi/zmisc3.c126
-rw-r--r--psi/zncdummy.c62
-rw-r--r--psi/zpacked.c256
-rw-r--r--psi/zpaint.c84
-rw-r--r--psi/zpath.c176
-rw-r--r--psi/zpath1.c300
-rw-r--r--psi/zpcolor.c378
-rw-r--r--psi/zpdf_r6.c203
-rw-r--r--psi/zpdfops.c205
-rw-r--r--psi/zrelbit.c374
-rw-r--r--psi/zrop.c112
-rw-r--r--psi/zshade.c700
-rw-r--r--psi/zstack.c316
-rw-r--r--psi/zstring.c215
-rw-r--r--psi/zsysvm.c184
-rw-r--r--psi/ztoken.c385
-rw-r--r--psi/ztrans.c537
-rw-r--r--psi/ztrap.c64
-rw-r--r--psi/ztype.c528
-rw-r--r--psi/zupath.c892
-rw-r--r--psi/zusparam.c924
-rw-r--r--psi/zutf8.c82
-rw-r--r--psi/zvmem.c439
-rw-r--r--psi/zvmem2.c139
-rw-r--r--psi/zwinutf8.c75
316 files changed, 105030 insertions, 742 deletions
diff --git a/psi/apitest.c b/psi/apitest.c
new file mode 100644
index 000000000..192d73f19
--- /dev/null
+++ b/psi/apitest.c
@@ -0,0 +1,141 @@
+#include <pthread.h>
+#include "ierrors.h"
+#include "iapi.h"
+#include <gp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NUM_THREADS 10
+
+static int my_argc;
+static char **my_argv;
+static int my_argv_file;
+
+typedef struct my_stdio_s
+{
+ FILE *stdout;
+ FILE *stderr;
+} my_stdio;
+
+/* stdio functions */
+static int GSDLLCALL
+my_stdin(void *instance, char *buf, int len)
+{
+ return 0; /* We don't support stdin */
+}
+
+static int GSDLLCALL
+my_stdout(void *instance, const char *str, int len)
+{
+ my_stdio *stdio = (my_stdio *)instance;
+
+ fwrite(str, 1, len, stdio->stdout);
+ fflush(stdio->stdout);
+ return len;
+}
+
+static int GSDLLCALL
+my_stderr(void *instance, const char *str, int len)
+{
+ my_stdio *stdio = (my_stdio *)instance;
+
+ fwrite(str, 1, len, stdio->stderr);
+ fflush(stdio->stderr);
+ return len;
+}
+
+static void *gs_main(void *arg)
+{
+ int threadnum = (int)(void *)arg;
+ int code, code1;
+ char text[256];
+ void *minst;
+ char **gsargv;
+ int gsargc;
+ int i, pos, len;
+ my_stdio stdio;
+
+ snprintf(text, sizeof(text), "stdout.%d", threadnum);
+ stdio.stdout = gp_fopen(text, "w");
+ snprintf(text, sizeof(text), "stderr.%d", threadnum);
+ stdio.stderr = gp_fopen(text, "w");
+
+ gsargv = malloc(sizeof(*gsargv)*my_argc);
+ if (!gsargv)
+ {
+ fprintf(stdio.stderr, "Failed to allocate arg space in thread %d\n", threadnum);
+ return (void *)-1;
+ }
+
+ for (i=0; i < my_argc; i++)
+ gsargv[i] = my_argv[i];
+ gsargv[my_argv_file] = text;
+ gsargc = my_argc;
+
+ strncpy(text, my_argv[my_argv_file], sizeof(text));
+ text[sizeof(text)-1]=0;
+ pos = strlen(text);
+ len = sizeof(text)+1-pos;
+ snprintf(text+pos, len, "%d", threadnum);
+
+ code = gsapi_new_instance(&minst, &stdio);
+ if (code < 0)
+ {
+ fprintf(stdio.stderr, "gsapi_new_instance failure in thread %d\n", threadnum);
+ return (void *)-1;
+ }
+
+ gsapi_set_stdio(minst, my_stdin, my_stdout, my_stderr);
+
+ code = gsapi_init_with_args(minst, gsargc, gsargv);
+ code1 = gsapi_exit(minst);
+ if ((code == 0) || (code == gs_error_Quit))
+ code = code1;
+
+ gsapi_delete_instance(minst);
+
+ if ((code == 0) || (code == gs_error_Quit))
+ return (void *)0;
+
+ return (void *)1;
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+ pthread_t thread[NUM_THREADS];
+
+ my_argc = argc;
+ my_argv = argv;
+
+ for (i=0; i < argc; i++)
+ if (!strcmp(argv[i], "-o"))
+ break;
+
+ if (i >= argc-1)
+ {
+ fprintf(stderr, "Expected a -o argument to rewrite!\n");
+ exit(EXIT_FAILURE);
+ }
+ my_argv_file = i+1;
+
+ for (i=0; i < NUM_THREADS; i++)
+ {
+ if (pthread_create(&thread[i], NULL, gs_main, (void *)i) != 0)
+ {
+ fprintf(stderr, "Thread %d creation failed\n", i);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ for (i=0; i < NUM_THREADS; i++)
+ {
+ if (pthread_join(thread[i], NULL) != 0)
+ {
+ fprintf(stderr, "Thread %d join failed\n", i);
+ exit(EXIT_FAILURE);
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/psi/bfont.h b/psi/bfont.h
new file mode 100644
index 000000000..00c3b8258
--- /dev/null
+++ b/psi/bfont.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter internal routines and data needed for building fonts */
+/* Requires gxfont.h */
+
+#ifndef bfont_INCLUDED
+# define bfont_INCLUDED
+
+#include "ifont.h"
+
+/* In zfont.c */
+int add_FID(i_ctx_t *i_ctx_p, ref *pfdict, gs_font *pfont,
+ gs_ref_memory_t *imem);
+
+font_proc_make_font(zdefault_make_font);
+font_proc_make_font(zbase_make_font);
+
+/* The global font directory */
+#define ifont_dir (gs_lib_ctx_get_interp_instance(imemory)->font_dir)
+
+/* Structure for passing BuildChar and BuildGlyph procedures. */
+typedef struct build_proc_refs_s {
+ ref BuildChar;
+ ref BuildGlyph;
+} build_proc_refs;
+
+/* Options for collecting parameters from a font dictionary. */
+/* The comment indicates where the option is tested. */
+typedef enum {
+ bf_options_none = 0,
+ bf_Encoding_optional = 1, /* build_gs_font */
+ bf_UniqueID_ignored = 4, /* build_gs_simple_font */
+ bf_CharStrings_optional = 8, /* build_gs_primitive_font */
+ bf_notdef_required = 16, /* build_gs_primitive_font */
+ bf_has_font_file = 32 /* build_gs_TrueType_font ( only for gs_type42_font_init) */
+} build_font_options_t;
+
+/* In zbfont.c */
+int build_proc_name_refs(const gs_memory_t *mem,
+ build_proc_refs * pbuild,
+ const char *bcstr, const char *bgstr);
+int build_gs_font_procs(os_ptr, build_proc_refs *);
+#define BUILD_BASE_FONT_PROC(proc)\
+ int proc(i_ctx_t *, os_ptr, gs_font_base **, font_type,\
+ gs_memory_type_ptr_t, const build_proc_refs *,\
+ build_font_options_t)
+typedef BUILD_BASE_FONT_PROC((*build_base_font_proc_t));
+BUILD_BASE_FONT_PROC(build_gs_primitive_font);
+int build_gs_FDArray_font(i_ctx_t *, /*const*/ ref *, gs_font_base **,
+ font_type, gs_memory_type_ptr_t,
+ const build_proc_refs *);
+int build_gs_outline_font(i_ctx_t *, os_ptr, gs_font_base **, font_type,
+ gs_memory_type_ptr_t, const build_proc_refs *,
+ build_font_options_t, build_base_font_proc_t);
+BUILD_BASE_FONT_PROC(build_gs_simple_font);
+void init_gs_simple_font(gs_font_base *pfont, const double bbox[4],
+ const gs_uid *puid);
+void lookup_gs_simple_font_encoding(gs_font_base *);
+int build_gs_font(i_ctx_t *, os_ptr, gs_font **, font_type,
+ gs_memory_type_ptr_t, const build_proc_refs *,
+ build_font_options_t);
+int build_gs_sub_font(i_ctx_t *, const ref *, gs_font **,
+ font_type, gs_memory_type_ptr_t,
+ const build_proc_refs *, const ref *, ref *);
+int define_gs_font(i_ctx_t *, gs_font *);
+void get_font_name(const gs_memory_t *mem, ref *pfname, const ref *op);
+void copy_font_name(gs_font_name * pfstr, const ref * pfname);
+gs_glyph zfont_encode_char(gs_font *pfont, gs_char chr, gs_glyph_space_t ignored);
+gs_char gs_font_map_glyph_to_unicode(gs_font *font, gs_glyph glyph, int ch);
+const ref *zfont_get_to_unicode_map(gs_font_dir *dir);
+void get_GlyphNames2Unicode(i_ctx_t *i_ctx_p, gs_font *pfont, ref *pdref);
+
+#endif /* bfont_INCLUDED */
diff --git a/psi/btoken.h b/psi/btoken.h
new file mode 100644
index 000000000..4a0fc0747
--- /dev/null
+++ b/psi/btoken.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definitions for Level 2 binary tokens */
+
+#ifndef btoken_INCLUDED
+# define btoken_INCLUDED
+
+/*
+ * Define accessors for pointers to the system and user name tables
+ * (arrays). Note that these refer implicitly to i_ctx_p. Note also
+ * that these pointers may be NULL: clients must check this.
+ */
+#define system_names_p (gs_imemory.space_global->names_array)
+#define user_names_p (gs_imemory.space_local->names_array)
+
+/* Create a system or user name table (in the stable memory of mem). */
+int create_names_array(ref **ppnames, gs_memory_t *mem,
+ client_name_t cname); /* in zbseq.c */
+
+/* Convert an object to its representation in a binary object sequence. */
+int encode_binary_token(i_ctx_t *i_ctx_p, const ref *obj, ps_int *ref_offset,
+ ps_int *char_offset, byte *str); /* in iscanbin.c */
+
+/* Define the current binary object format for operators. */
+/* This is a ref so that it can be managed properly by save/restore. */
+#define ref_binary_object_format_container i_ctx_p
+#define ref_binary_object_format\
+ (ref_binary_object_format_container->binary_object_format)
+
+#endif /* btoken_INCLUDED */
diff --git a/psi/dmmain.c b/psi/dmmain.c
new file mode 100644
index 000000000..23c1da669
--- /dev/null
+++ b/psi/dmmain.c
@@ -0,0 +1,998 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Ghostscript shlib example wrapper for Macintosh (Classic/Carbon) contributed
+ by Nigel Hathaway. Uses the Metrowerks CodeWarrior SIOUX command-line library.
+ */
+
+#if __ide_target("Ghostscript PPC (Debug)") || __ide_target("Ghostscript PPC (Release)")
+#define TARGET_API_MAC_CARBON 0
+#define TARGET_API_MAC_OS8 1
+#define ACCESSOR_CALLS_ARE_FUNCTIONS 1
+#endif
+
+#include <Carbon.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <console.h>
+#include <SIOUX.h>
+#include <SIOUXGlobals.h>
+#include <SIOUXMenus.h>
+
+#include "gscdefs.h"
+#define GSREVISION gs_revision
+#include "ierrors.h"
+#include "iapi.h"
+
+#if DEBUG
+#include "vdtrace.h"
+#endif
+
+#include "gdevdsp.h"
+
+#define kScrollBarWidth 15
+#define MAX_ARGS 25
+
+Boolean gRunningOnX = false;
+Boolean gDone;
+ControlActionUPP gActionFunctionScrollUPP;
+
+const char start_string[] = "systemdict /start get exec\n";
+void *instance;
+
+const unsigned int display_format = DISPLAY_COLORS_RGB | DISPLAY_UNUSED_FIRST |
+ DISPLAY_DEPTH_8 | DISPLAY_BIGENDIAN |
+ DISPLAY_TOPFIRST;
+typedef struct IMAGE_S IMAGE;
+struct IMAGE_S {
+ void *handle;
+ void *device;
+ WindowRef windowRef;
+ ControlRef scrollbarVertRef;
+ ControlRef scrollbarHorizRef;
+ PixMapHandle pixmapHdl;
+ UInt64 update_time;
+ int update_interval;
+ IMAGE *next;
+};
+
+IMAGE *first_image;
+
+static IMAGE *image_find(void *handle, void *device);
+
+static int GSDLLCALL gsdll_stdin(void *instance, char *buf, int len);
+static int GSDLLCALL gsdll_stdout(void *instance, const char *str, int len);
+static int GSDLLCALL gsdll_stderr(void *instance, const char *str, int len);
+static int GSDLLCALL gsdll_poll(void *handle);
+
+static int display_open(void *handle, void *device);
+static int display_preclose(void *handle, void *device);
+static int display_close(void *handle, void *device);
+static int display_presize(void *handle, void *device, int width, int height,
+ int raster, unsigned int format);
+static int display_size(void *handle, void *device, int width, int height,
+ int raster, unsigned int format, unsigned char *pimage);
+static int display_sync(void *handle, void *device);
+static int display_page(void *handle, void *device, int copies, int flush);
+static int display_update(void *handle, void *device,
+ int x, int y, int w, int h);
+
+static size_t get_input(void *ptr, size_t size);
+
+static void window_create (IMAGE *img);
+static void window_invalidate (WindowRef windowRef);
+static void window_adjust_scrollbars (WindowRef windowRef);
+
+void main (void);
+OSErr quitAppEventHandler (AppleEvent *,AppleEvent *,SInt32);
+void doEvents (EventRecord *);
+void doMouseDown (EventRecord *);
+void doUpdate (EventRecord *);
+void doUpdateWindow (EventRecord *);
+void doOSEvent (EventRecord *);
+void doInContent (EventRecord *,WindowRef);
+pascal void actionFunctionScroll (ControlRef,ControlPartCode);
+
+/*********************************************************************/
+/* stdio functions */
+static int GSDLLCALL
+gsdll_stdin(void *instance, char *buf, int len)
+{
+ if (isatty(fileno(stdin)))
+ return get_input(buf, len);
+ else
+ return fread(buf, 1, len, stdin);
+}
+
+static int GSDLLCALL
+gsdll_stdout(void *instance, const char *str, int len)
+{
+ int n = fwrite(str, 1, len, stdout);
+ fflush(stdout);
+ return n;
+}
+
+static int GSDLLCALL
+gsdll_stderr(void *instance, const char *str, int len)
+{
+ return gsdll_stdout(instance, str, len);
+}
+
+/* Poll the caller for cooperative multitasking. */
+/* If this function is NULL, polling is not needed */
+static int GSDLLCALL gsdll_poll(void *handle)
+{
+ EventRecord eventStructure;
+
+ while (WaitNextEvent(everyEvent, &eventStructure, 0, NULL))
+ doEvents(&eventStructure);
+
+ return (gDone ? gs_error_Fatal : 0);
+}
+/*********************************************************************/
+
+/* new dll display device */
+
+/* New device has been opened */
+/* This is the first event from this device. */
+static int display_open(void *handle, void *device)
+{
+ IMAGE *img = (IMAGE *)malloc(sizeof(IMAGE));
+ if (img == NULL)
+ return -1;
+ memset(img, 0, sizeof(IMAGE));
+
+ /* add to list */
+ if (first_image)
+ img->next = first_image;
+ first_image = img;
+
+ /* remember device and handle */
+ img->handle = handle;
+ img->device = device;
+
+ /* create window */
+ window_create(img);
+
+ gsdll_poll(handle);
+ return 0;
+}
+
+/* Device is about to be closed. */
+/* Device will not be closed until this function returns. */
+static int display_preclose(void *handle, void *device)
+{
+ /* do nothing - no thread synchonisation needed */
+ return 0;
+}
+
+/* Device has been closed. */
+/* This is the last event from this device. */
+static int display_close(void *handle, void *device)
+{
+ IMAGE *img = image_find(handle, device);
+ if (img == NULL)
+ return -1;
+
+ gsdll_poll(handle);
+
+ /* remove from list */
+ if (img == first_image)
+ first_image = img->next;
+ else
+ {
+ IMAGE *tmp;
+ for (tmp = first_image; tmp!=0; tmp=tmp->next)
+ {
+ if (img == tmp->next)
+ tmp->next = img->next;
+ }
+ }
+
+ DisposePixMap(img->pixmapHdl); // need to go in doCloseWindow()
+ DisposeWindow(img->windowRef);
+
+ free(img);
+
+ return 0;
+}
+
+/* Device is about to be resized. */
+/* Resize will only occur if this function returns 0. */
+static int display_presize(void *handle, void *device, int width, int height,
+ int raster, unsigned int format)
+{
+ /* Check for correct format (32-bit RGB), fatal error if not */
+ if (format != display_format)
+ {
+ printf("DisplayFormat has been set to an incompatible value.\n");
+ fflush(stdout);
+ return gs_error_rangecheck;
+ }
+
+ return 0;
+}
+
+/* Device has been resized. */
+/* New pointer to raster returned in pimage */
+static int display_size(void *handle, void *device, int width, int height,
+ int raster, unsigned int format, unsigned char *pimage)
+{
+ PixMapPtr pixmap;
+ IMAGE *img = image_find(handle, device);
+ if (img == NULL)
+ return -1;
+
+ /* Check that image is within allowable bounds */
+ if (raster > 0x3fff)
+ {
+ printf("QuickDraw can't cope with an image this big.\n");
+ fflush(stdout);
+ if (img->pixmapHdl)
+ {
+ DisposePixMap(img->pixmapHdl);
+ img->pixmapHdl = NULL;
+ }
+ return gs_error_rangecheck;
+ }
+
+ /* Create the PixMap */
+ if (!img->pixmapHdl)
+ img->pixmapHdl = NewPixMap();
+
+ pixmap = *(img->pixmapHdl);
+ pixmap->baseAddr = (char*)pimage;
+ pixmap->rowBytes = (((SInt16)raster) & 0x3fff) | 0x8000;
+ pixmap->bounds.right = width;
+ pixmap->bounds.bottom = height;
+ pixmap->packType = 0;
+ pixmap->packSize = 0;
+ pixmap->pixelType = RGBDirect;
+ pixmap->pixelSize = 32;
+ pixmap->cmpCount = 3;
+ pixmap->cmpSize = 8;
+
+ /* Update the display window */
+ window_adjust_scrollbars(img->windowRef);
+ window_invalidate(img->windowRef);
+ return gsdll_poll(handle);
+}
+
+/* flushpage */
+static int display_sync(void *handle, void *device)
+{
+ IMAGE *img = image_find(handle, device);
+ if (img == NULL)
+ return -1;
+
+ window_invalidate(img->windowRef);
+ gsdll_poll(handle);
+
+ return 0;
+}
+
+/* showpage */
+/* If you want to pause on showpage, then don't return immediately */
+static int display_page(void *handle, void *device, int copies, int flush)
+{
+ return display_sync(handle, device);
+}
+
+/* Poll the caller for cooperative multitasking. */
+/* If this function is NULL, polling is not needed */
+static int display_update(void *handle, void *device,
+ int x, int y, int w, int h)
+{
+ UInt64 t1;
+ UInt64 t2;
+ int delta;
+ IMAGE *img = image_find(handle, device);
+ if (img == NULL)
+ return -1;
+
+ Microseconds((UnsignedWide*)&t1);
+ delta = (t1 - img->update_time) / 1000000L;
+ if (img->update_interval < 1)
+ img->update_interval = 1; /* seconds */
+ if (delta < 0)
+ img->update_time = t1;
+ else if (delta > img->update_interval)
+ {
+ /* redraw window */
+ window_invalidate(img->windowRef);
+
+ /* Make sure the update interval is at least 10 times
+ * what it takes to paint the window
+ */
+ Microseconds((UnsignedWide*)&t2);
+ delta = (t2 - t1) / 1000;
+ if (delta < 0)
+ delta += 60000; /* delta = time to redraw */
+ if (delta > img->update_interval * 100)
+ img->update_interval = delta/100;
+ img->update_time = t2;
+ }
+
+ return gsdll_poll(handle);
+}
+
+display_callback display = {
+ sizeof(display_callback),
+ DISPLAY_VERSION_MAJOR,
+ DISPLAY_VERSION_MINOR,
+ display_open,
+ display_preclose,
+ display_close,
+ display_presize,
+ display_size,
+ display_sync,
+ display_page,
+ display_update,
+ NULL, /* memalloc */
+ NULL, /* memfree */
+ NULL /* display_separation */
+};
+
+static IMAGE * image_find(void *handle, void *device)
+{
+ IMAGE *img;
+ for (img = first_image; img!=0; img=img->next) {
+ if ((img->handle == handle) && (img->device == device))
+ return img;
+ }
+ return NULL;
+}
+
+/*********************************************************************/
+
+static char *stdin_buf = NULL;
+static size_t stdin_bufpos = 0;
+static size_t stdin_bufsize = 0;
+
+/* This function is a fudge which allows the SIOUX window to be waiting for
+ input and not be modal at the same time. (Why didn't MetroWerks think of that?)
+ It is based on the SIOUX function ReadCharsFromConsole(), and contains an
+ event loop which allows other windows to be active.
+ It collects characters up to when the user presses ENTER, stores the complete
+ buffer and gives as much to the calling function as it wants until it runs
+ out, at which point it gets another line (or set of lines if pasting from the
+ clipboard) from the user.
+*/
+static size_t get_input(void *ptr, size_t size)
+{
+ EventRecord eventStructure;
+ long charswaiting, old_charswaiting = 0;
+ char *text;
+
+#if SIOUX_USE_WASTE
+ Handle textHandle;
+#endif
+
+ /* If needing more input, set edit start position */
+ if (!stdin_buf)
+#if SIOUX_USE_WASTE
+ SIOUXselstart = WEGetTextLength(SIOUXTextWindow->edit);
+#else
+ SIOUXselstart = (*SIOUXTextWindow->edit)->teLength;
+#endif
+
+ /* Wait until user presses exit (or quits) */
+ while(!gDone && !stdin_buf)
+ {
+#if SIOUX_USE_WASTE
+ charswaiting = WEGetTextLength(SIOUXTextWindow->edit) - SIOUXselstart;
+#else
+ if ((*SIOUXTextWindow->edit)->teLength > 0)
+ charswaiting = (*SIOUXTextWindow->edit)->teLength - SIOUXselstart;
+ else
+ charswaiting = ((unsigned short) (*SIOUXTextWindow->edit)->teLength) - SIOUXselstart;
+#endif
+
+ /* If something has happened, see if we need to do anything */
+ if (charswaiting != old_charswaiting)
+ {
+#if SIOUX_USE_WASTE
+ textHandle = WEGetText(SIOUXTextWindow->edit);
+ HLock(textHandle);
+ text = *textHandle + SIOUXselstart;
+#else
+ text = (*(*SIOUXTextWindow->edit)->hText) + SIOUXselstart;
+#endif
+ /* If user has pressed enter, gather up the buffer ready for returning */
+ if (text[charswaiting-1] == '\r')
+ {
+ stdin_buf = malloc(charswaiting);
+ if (!stdin_buf)
+ return -1;
+ stdin_bufsize = charswaiting;
+ memcpy(stdin_buf, text, stdin_bufsize);
+ SIOUXselstart += charswaiting;
+
+ text = stdin_buf;
+ while (text = memchr(text, '\r', charswaiting - (text - stdin_buf)))
+ *text = '\n';
+ }
+#if SIOUX_USE_WASTE
+ HUnlock(textHandle);
+#endif
+ old_charswaiting = charswaiting;
+
+ if (stdin_buf)
+ break;
+ }
+
+ /* Wait for next event and process it */
+ SIOUXState = SCANFING;
+
+ if(WaitNextEvent(everyEvent, &eventStructure, SIOUXSettings.sleep ,NULL))
+ doEvents(&eventStructure);
+ else
+ SIOUXHandleOneEvent(&eventStructure);
+
+ SIOUXState = IDLE;
+ }
+
+ /* If data has been entered, return as much as has been requested */
+ if (stdin_buf && !gDone)
+ {
+ if (size >= stdin_bufsize - stdin_bufpos)
+ {
+ size = stdin_bufsize - stdin_bufpos;
+ memcpy (ptr, stdin_buf + stdin_bufpos, size);
+ free(stdin_buf);
+ stdin_buf = NULL;
+ stdin_bufpos = 0;
+ stdin_bufsize = 0;
+ }
+ else
+ {
+ memcpy (ptr, stdin_buf + stdin_bufpos, size);
+ stdin_bufpos += size;
+ }
+ return size;
+ }
+ else if (stdin_buf)
+ {
+ free(stdin_buf);
+ stdin_buf = NULL;
+ stdin_bufpos = 0;
+ stdin_bufsize = 0;
+ }
+
+ return 0;
+}
+
+/*********************************************************************/
+
+static void window_create(IMAGE *img)
+{
+ WindowRef windowRef;
+ Str255 windowTitle = "\pGhostscript Image";
+ Rect windowRect = {20,4,580,420};//, portRect;
+ Rect scrollbarRect = {0,0,0,0};
+
+#if TARGET_API_MAC_CARBON
+ GetAvailableWindowPositioningBounds(GetMainDevice(),&windowRect);
+#endif
+
+ /* Create a new suitablty positioned window */
+ windowRect.top = windowRect.top * 2 + 2;
+ windowRect.bottom -= 10;
+ windowRect.left += 4;
+ windowRect.right = ((windowRect.bottom - windowRect.top) * 3) / 4 + windowRect.left;
+
+ if(!(windowRef = NewCWindow(NULL, &windowRect, windowTitle, true,
+ zoomDocProc, (WindowRef) -1, false, 0)))
+ ExitToShell();
+
+ img->windowRef = windowRef;
+
+ SetWRefCon(img->windowRef, (SInt32)img);
+
+ /* Create the window's scrollbars */
+#if TARGET_API_MAC_CARBON
+ if(gRunningOnX)
+ ChangeWindowAttributes(windowRef,kWindowLiveResizeAttribute,0);
+
+ CreateScrollBarControl(windowRef, &scrollbarRect, 0, 0, 0, 0,
+ true, gActionFunctionScrollUPP, &(img->scrollbarVertRef));
+
+ CreateScrollBarControl(windowRef, &scrollbarRect, 0, 0, 0, 0,
+ true, gActionFunctionScrollUPP, &(img->scrollbarHorizRef));
+#else
+ img->scrollbarVertRef = NewControl(windowRef,&scrollbarRect,"\p",false,0,0,0,scrollBarProc,0);
+ img->scrollbarHorizRef = NewControl(windowRef,&scrollbarRect,"\p",false,0,0,0,scrollBarProc,0);
+#endif
+
+ window_adjust_scrollbars(windowRef);
+}
+
+static void window_invalidate(WindowRef windowRef)
+{
+ Rect portRect;
+
+ GetWindowPortBounds(windowRef, &portRect);
+ InvalWindowRect(windowRef, &portRect);
+}
+
+static void window_adjust_scrollbars(WindowRef windowRef)
+{
+ IMAGE *img;
+ Rect portRect;
+
+ img = (IMAGE*)GetWRefCon(windowRef);
+ GetWindowPortBounds(windowRef,&portRect);
+
+ /* Move the crollbars to the edges of the window */
+ HideControl(img->scrollbarVertRef);
+ HideControl(img->scrollbarHorizRef);
+
+ MoveControl(img->scrollbarVertRef,portRect.right - kScrollBarWidth,
+ portRect.top - 1);
+ MoveControl(img->scrollbarHorizRef,portRect.left - 1,
+ portRect.bottom - kScrollBarWidth);
+
+ SizeControl(img->scrollbarVertRef,kScrollBarWidth + 1,
+ portRect.bottom - portRect.top - kScrollBarWidth + 1);
+ SizeControl(img->scrollbarHorizRef, portRect.right - portRect.left - kScrollBarWidth + 1,
+ kScrollBarWidth + 1);
+
+ /* Adjust the scroll position showing */
+ if (img->pixmapHdl)
+ {
+ PixMap *pixmap = *(img->pixmapHdl);
+ int visibleHeight = portRect.bottom - portRect.top - kScrollBarWidth;
+ int visibleWidth = portRect.right - portRect.left - kScrollBarWidth;
+
+ if (pixmap->bounds.bottom > visibleHeight)
+ {
+ SetControl32BitMaximum(img->scrollbarVertRef,
+ pixmap->bounds.bottom - visibleHeight);
+ SetControlViewSize(img->scrollbarVertRef,visibleHeight);
+ }
+ else
+ SetControlMaximum(img->scrollbarVertRef, 0);
+
+ if (pixmap->bounds.right > visibleWidth)
+ {
+ SetControl32BitMaximum(img->scrollbarHorizRef,
+ pixmap->bounds.right - visibleWidth);
+ SetControlViewSize(img->scrollbarHorizRef, visibleWidth);
+ }
+ else
+ SetControlMaximum(img->scrollbarHorizRef, 0);
+ }
+
+ ShowControl(img->scrollbarVertRef);
+ ShowControl(img->scrollbarHorizRef);
+}
+
+/*********************************************************************/
+void main(void)
+{
+ int code;
+ int exit_code;
+ int argc;
+ char **argv;
+ char dformat[64], ddevice[32];
+ SInt32 response;
+
+ /* Initialize operating environment */
+#if TARGET_API_MAC_CARBON
+ MoreMasterPointers(224);
+#else
+ MoreMasters();
+#endif
+ InitCursor();
+ FlushEvents(everyEvent,0);
+
+ if (AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,
+ NewAEEventHandlerUPP((AEEventHandlerProcPtr) quitAppEventHandler),
+ 0L,false) != noErr)
+ ExitToShell();
+
+ gActionFunctionScrollUPP = NewControlActionUPP(&actionFunctionScroll);
+
+ Gestalt(gestaltMenuMgrAttr,&response);
+ if(response & gestaltMenuMgrAquaLayoutMask)
+ gRunningOnX = true;
+
+ /* Initialize SIOUX */
+ SIOUXSettings.initializeTB = false;
+ SIOUXSettings.standalone = false;
+ SIOUXSettings.asktosaveonclose = false;
+ SIOUXSettings.sleep = GetCaretTime();
+ SIOUXSettings.userwindowtitle = "\pGhostscript";
+
+ /* Get arguments from user */
+ argc = ccommand(&argv);
+
+ /* Show command line window */
+ if (InstallConsole(0))
+ ExitToShell();
+
+ /* Part of fudge to make SIOUX accept characters without becoming modal */
+ SelectWindow(SIOUXTextWindow->window);
+ PostEvent(keyDown, 0x4c00); // Enter
+ ReadCharsFromConsole(dformat, 0x7FFF);
+ clrscr();
+
+ /* Add in the display format as the first command line argument */
+ if (argc >= MAX_ARGS - 1)
+ {
+ printf("Too many command line arguments\n");
+ return;
+ }
+
+ memmove(&argv[3], &argv[1], (argc-1) * sizeof(char**));
+ argc += 2;
+ argv[1] = ddevice;
+ argv[2] = dformat;
+
+ gs_sprintf(ddevice, "-sDEVICE=display");
+ gs_sprintf(dformat, "-dDisplayFormat=%d", display_format);
+
+ /* Run Ghostscript */
+ if (gsapi_new_instance(&instance, NULL) < 0)
+ {
+ printf("Can't create Ghostscript instance\n");
+ return;
+ }
+
+#ifdef DEBUG
+ visual_tracer_init();
+ set_visual_tracer(&visual_tracer);
+#endif
+
+ gsapi_set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr);
+ gsapi_set_poll(instance, gsdll_poll);
+ gsapi_set_display_callback(instance, &display);
+
+ code = gsapi_init_with_args(instance, argc, argv);
+ if (code == 0)
+ code = gsapi_run_string(instance, start_string, 0, &exit_code);
+ else
+ {
+ printf("Failed to initialize. Error %d.\n", code);
+ fflush(stdout);
+ }
+ code = gsapi_exit(instance);
+ if (code != 0)
+ {
+ printf("Failed to terminate. Error %d.\n", code);
+ fflush(stdout);
+ }
+
+ gsapi_delete_instance(instance);
+
+#ifdef DEBUG
+ visual_tracer_close();
+#endif
+
+ /* Ghostscript has finished - let user see output before quitting */
+ WriteCharsToConsole("\r[Finished - hit any key to quit]", 33);
+ fflush(stdout);
+
+ /* Process events until a key is hit or user quits from menu */
+ while(!gDone)
+ {
+ EventRecord eventStructure;
+
+ if(WaitNextEvent(everyEvent,&eventStructure,SIOUXSettings.sleep,NULL))
+ {
+ if (eventStructure.what == keyDown)
+ gDone = true;
+
+ doEvents(&eventStructure);
+ }
+ else
+ SIOUXHandleOneEvent(&eventStructure);
+ }
+}
+
+/*********************************************************************/
+
+void doEvents(EventRecord *eventStrucPtr)
+{
+ WindowRef windowRef;
+
+ if (eventStrucPtr->what == mouseDown &&
+ FindWindow(eventStrucPtr->where,&windowRef) == inMenuBar)
+ SelectWindow(SIOUXTextWindow->window);
+
+ SIOUXSettings.standalone = true;
+ if (SIOUXHandleOneEvent(eventStrucPtr))
+ {
+ if (SIOUXQuitting)
+ gDone = true;
+ SIOUXSettings.standalone = false;
+ return;
+ }
+ SIOUXSettings.standalone = false;
+
+ switch(eventStrucPtr->what)
+ {
+ case kHighLevelEvent:
+ AEProcessAppleEvent(eventStrucPtr);
+ break;
+
+ case mouseDown:
+ doMouseDown(eventStrucPtr);
+ break;
+
+ case keyDown:
+ case autoKey:
+ break;
+
+ case updateEvt:
+ doUpdate(eventStrucPtr);
+ break;
+
+ case activateEvt:
+ DrawGrowIcon(windowRef);
+ break;
+
+ case osEvt:
+ doOSEvent(eventStrucPtr);
+ break;
+ }
+}
+
+void doMouseDown(EventRecord *eventStrucPtr)
+{
+ WindowRef windowRef;
+ WindowPartCode partCode, zoomPart;
+ BitMap screenBits;
+ Rect constraintRect, mainScreenRect;
+ Point standardStateHeightAndWidth;
+ long newSize;
+
+ partCode = FindWindow(eventStrucPtr->where,&windowRef);
+
+ switch(partCode)
+ {
+ case inMenuBar:
+ break;
+
+ case inContent:
+ if(windowRef != FrontWindow())
+ SelectWindow(windowRef);
+ else
+ doInContent(eventStrucPtr,windowRef);
+ break;
+
+ case inDrag:
+ DragWindow(windowRef,eventStrucPtr->where,NULL);
+ break;
+
+ case inGoAway:
+ break;
+
+ case inGrow:
+ constraintRect.top = 75;
+ constraintRect.left = 250;
+ constraintRect.bottom = constraintRect.right = 32767;
+ newSize = GrowWindow(windowRef,eventStrucPtr->where,&constraintRect);
+ if (newSize != 0)
+ SizeWindow(windowRef,LoWord(newSize),HiWord(newSize),true);
+ window_adjust_scrollbars(windowRef);
+ window_invalidate(windowRef);
+ break;
+
+ case inZoomIn:
+ case inZoomOut:
+ mainScreenRect = GetQDGlobalsScreenBits(&screenBits)->bounds;
+ standardStateHeightAndWidth.v = mainScreenRect.bottom;
+ standardStateHeightAndWidth.h = mainScreenRect.right;
+
+ if(IsWindowInStandardState(windowRef,&standardStateHeightAndWidth,NULL))
+ zoomPart = inZoomIn;
+ else
+ zoomPart = inZoomOut;
+
+ if(TrackBox(windowRef,eventStrucPtr->where,partCode))
+ {
+ ZoomWindowIdeal(windowRef,zoomPart,&standardStateHeightAndWidth);
+ window_adjust_scrollbars(windowRef);
+ }
+ break;
+ }
+}
+
+void doUpdate(EventRecord *eventStrucPtr)
+{
+ WindowRef windowRef;
+
+ windowRef = (WindowRef) eventStrucPtr->message;
+
+ window_adjust_scrollbars(windowRef);
+
+ BeginUpdate(windowRef);
+
+ SetPortWindowPort(windowRef);
+ doUpdateWindow(eventStrucPtr);
+
+ EndUpdate(windowRef);
+}
+
+void doUpdateWindow(EventRecord *eventStrucPtr)
+{
+ IMAGE *img;
+ WindowRef windowRef;
+ Rect srcRect, destRect, fillRect;
+ PixMapHandle srcPixmapHdl, destPixmapHdl;
+ RGBColor grayColour = { 0xC000,0xC000,0xC000 };
+ SInt32 hScroll, vScroll;
+
+ windowRef = (WindowRef) eventStrucPtr->message;
+ img = (IMAGE*)GetWRefCon(windowRef);
+ srcPixmapHdl = img->pixmapHdl;
+ destPixmapHdl = GetPortPixMap(GetWindowPort(windowRef));
+ hScroll = GetControl32BitValue(img->scrollbarHorizRef);
+ vScroll = GetControl32BitValue(img->scrollbarVertRef);
+
+ if (srcPixmapHdl)
+ {
+ PixMap *pixmap = *srcPixmapHdl;
+ PixPatHandle hdlPixPat = NewPixPat();
+ MakeRGBPat(hdlPixPat, &grayColour);
+
+ GetWindowPortBounds(windowRef,&destRect);
+ destRect.right -= kScrollBarWidth;
+ destRect.bottom -= kScrollBarWidth;
+
+ if (destRect.right > pixmap->bounds.right)
+ {
+ fillRect.top = destRect.top;
+ fillRect.bottom = destRect.bottom;
+ fillRect.left = pixmap->bounds.right;
+ fillRect.right = destRect.right;
+ FillCRect(&fillRect, hdlPixPat);
+ destRect.right = pixmap->bounds.right;
+ }
+ if (destRect.bottom > pixmap->bounds.bottom)
+ {
+ fillRect.top = pixmap->bounds.bottom;
+ fillRect.bottom = destRect.bottom;
+ fillRect.left = destRect.left;
+ fillRect.right = destRect.right;
+ FillCRect(&fillRect, hdlPixPat);
+ destRect.bottom = pixmap->bounds.bottom;
+ }
+ DisposePixPat(hdlPixPat);
+
+ srcRect = destRect;
+ srcRect.left += hScroll;
+ srcRect.right += hScroll;
+ srcRect.top += vScroll;
+ srcRect.bottom += vScroll;
+
+ CopyBits((BitMap*)*srcPixmapHdl, (BitMap*)*destPixmapHdl,
+ &srcRect, &destRect, srcCopy, NULL);
+ }
+
+ DrawGrowIcon(windowRef);
+}
+
+void doOSEvent(EventRecord *eventStrucPtr)
+{
+ switch((eventStrucPtr->message >> 24) & 0x000000FF)
+ {
+ case suspendResumeMessage:
+ if((eventStrucPtr->message & resumeFlag) == 1)
+ SetThemeCursor(kThemeArrowCursor);
+ break;
+ }
+}
+
+void doInContent(EventRecord *eventStrucPtr,WindowRef windowRef)
+{
+ ControlPartCode controlPartCode;
+ ControlRef controlRef;
+
+ SetPortWindowPort(windowRef);
+ GlobalToLocal(&eventStrucPtr->where);
+
+ if(controlRef = FindControlUnderMouse(eventStrucPtr->where,windowRef,&controlPartCode))
+ {
+#if TARGET_API_MAC_CARBON
+ TrackControl(controlRef,eventStrucPtr->where,(ControlActionUPP) -1);
+#else
+ if (controlPartCode == kControlIndicatorPart)
+ TrackControl(controlRef,eventStrucPtr->where,NULL);
+ else
+ TrackControl(controlRef,eventStrucPtr->where,gActionFunctionScrollUPP);
+#endif
+
+ window_invalidate(windowRef);
+ }
+}
+
+pascal void actionFunctionScroll(ControlRef controlRef,ControlPartCode controlPartCode)
+{
+ SInt32 scrollDistance, controlValue, oldControlValue, controlMax;
+
+ if(controlPartCode != kControlNoPart)
+ {
+ if(controlPartCode != kControlIndicatorPart)
+ {
+ switch(controlPartCode)
+ {
+ case kControlUpButtonPart:
+ case kControlDownButtonPart:
+ scrollDistance = 10;
+ break;
+
+ case kControlPageUpPart:
+ case kControlPageDownPart:
+ scrollDistance = 100;
+ break;
+
+ default:
+ scrollDistance = 0;
+ break;
+ }
+
+ if (scrollDistance)
+ {
+ if((controlPartCode == kControlDownButtonPart) ||
+ (controlPartCode == kControlPageDownPart))
+ scrollDistance = -scrollDistance;
+
+ controlValue = GetControl32BitValue(controlRef);
+
+ if(((controlValue == GetControl32BitMaximum(controlRef)) && scrollDistance < 0) ||
+ ((controlValue == GetControl32BitMinimum(controlRef)) && scrollDistance > 0))
+ return;
+
+ oldControlValue = controlValue;
+ controlMax = GetControl32BitMaximum(controlRef);
+ controlValue = oldControlValue - scrollDistance;
+
+ if(controlValue < 0)
+ controlValue = 0;
+ else if(controlValue > controlMax)
+ controlValue = controlMax;
+
+ SetControl32BitValue(controlRef,controlValue);
+ }
+ }
+ }
+}
+
+OSErr quitAppEventHandler(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
+{
+ OSErr osError;
+ DescType returnedType;
+ Size actualSize;
+
+ osError = AEGetAttributePtr(appEvent,keyMissedKeywordAttr,typeWildCard,&returnedType,NULL,0,
+ &actualSize);
+
+ if(osError == errAEDescNotFound)
+ {
+ gDone = true;
+ osError = noErr;
+ }
+ else if(osError == noErr)
+ osError = errAEParamMissed;
+
+ return osError;
+}
+
+/*********************************************************************/
diff --git a/psi/dmmain.r b/psi/dmmain.r
new file mode 100644
index 000000000..007cf4f08
--- /dev/null
+++ b/psi/dmmain.r
@@ -0,0 +1,2538 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Resource data (Rez format) for the MacOS Ghostscript shlib example wrapper
+ contributed by Nigel Hathaway. See dmmain.c for rest of this code.
+ */
+
+#include "Types.r"
+
+resource 'BNDL' (128) {
+ 'MCGS',
+ 0,
+ { /* array TypeArray: 2 elements */
+ /* [1] */
+ 'ICN#',
+ { /* array IDArray: 1 elements */
+ /* [1] */
+ 0, 128
+ },
+ /* [2] */
+ 'FREF',
+ { /* array IDArray: 2 elements */
+ /* [1] */
+ 0, 128,
+ /* [2] */
+ 1, 129
+ }
+ }
+};
+
+resource 'FREF' (128) {
+ 'APPL',
+ 0,
+ ""
+};
+
+resource 'FREF' (129) {
+ '****',
+ 1,
+ ""
+};
+
+data 'MWBB' (1008) {
+ $"0001 0000" /* .... */
+};
+
+data 'MPSR' (1005) {
+ $"0009 4D6F 6E61 636F 0000 0000 0000 0000" /* .ÆMonaco........ */
+ $"0000 0000 0000 0000 0000 0000 0000 0000" /* ................ */
+ $"0000 0003 0004 0030 0004 01CA 027B 0030" /* .......0...Ê.{.0 */
+ $"0004 01CA 027B BA5E B2EF 0000 0000 0000" /* ...Ê.{º^²ï...... */
+ $"0000 0000 0000 0100" /* ........ */
+};
+
+resource 'ICN#' (128, "Application Icon") {
+ { /* array: 2 elements */
+ /* [1] */
+ $"0000 0000 0000 0800 0000 0A00 000B F200"
+ $"007F 4200 00FC 0700 01F0 1F80 03E0 1F80"
+ $"03E5 3FC0 03E0 1FC0 03E0 0FC0 03F0 0780"
+ $"01F8 8300 00FC 4600 003C 0800 00F4 0000"
+ $"03E0 0000 07E0 0000 07FF 6000 07FF FF00"
+ $"07FF FFC0 01FF FFE0 007F FFF0 0000 17F0"
+ $"0780 01F8 0F04 00F0 0E08 03E0 0F07 FFD0"
+ $"0780 D820 03F0 0380 007F FC00 0005 40",
+ /* [2] */
+ $"0000 1800 0000 1E00 0000 1E00 001F FF00"
+ $"00FF FF00 01FF FF00 03FF FF80 03FF FFC0"
+ $"07FF FFC0 07FF FFC0 07FF FFC0 03FF FFC0"
+ $"03FF FF80 01FF FF00 007F FE00 03FF F000"
+ $"07FF F800 07FF FE00 0FFF FF80 0FFF FFE0"
+ $"07FF FFF0 03FF FFF8 01FF FFF8 027F FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFF8 1FFF FFF8"
+ $"1FFF FFF0 07FF FFC0 01FF FF00 007F FC"
+ }
+};
+
+resource 'ics#' (128, "Application Icon") {
+ { /* array: 2 elements */
+ /* [1] */
+ $"0000 00A0 0F10 1C78 1878 1C18 0E20 0600"
+ $"1800 3FD0 1FF8 057C 300C 3358 18A8 0340",
+ /* [2] */
+ $"0070 07F0 1FF0 1FF8 3FF8 3FF8 1FF8 1FF0"
+ $"3FF0 3FFC 3FFE 1FFE 7FFE 7FFE 7FFC 1FF0"
+ }
+};
+
+resource 'icl8' (128, "Application Icon") {
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 F600 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 5656 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 00F6 FCF8 F900 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00F6 56F9 FCAC"
+ $"FCFC ACFE F600 FFF6 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 F5FA FDFF FFFF FFFF"
+ $"FD81 F800 00F7 FFF7 0000 0000 0000 0000"
+ $"0000 0000 0000 00F6 E0FF FFFF FFFD F92B"
+ $"0000 0000 F6AC FFFA 0000 0000 0000 0000"
+ $"0000 0000 0000 2BFF EAFF FFFE F700 0000"
+ $"0000 0056 FFFF FFFF 8100 0000 0000 0000"
+ $"0000 0000 0000 FCFF FFFF FF56 0000 00F5"
+ $"F500 FAFF FFFF FFFF FFF8 0000 0000 0000"
+ $"0000 0000 00F6 FFFF FFFF FE00 0079 0055"
+ $"F600 FBFF FFFF FFFF FFFD 0000 0000 0000"
+ $"0000 0000 002B FFFF FFFF FC00 007A F6F5"
+ $"4F00 00AC FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 00F6 FFFF FFFF FFF5 0000 F500"
+ $"0000 00F5 ACFF FFFF FFFC 0000 0000 0000"
+ $"0000 0000 0000 FCFF FFFF FF81 0000 0024"
+ $"F600 0000 F5FD FFFF FF2B 0000 0000 0000"
+ $"0000 0000 0000 F5FF FFFF FFFF 8100 0000"
+ $"7A7A 0000 00FA FFFF F800 0000 0000 0000"
+ $"0000 0000 0000 00F6 FDFF FFFF FFFB 0000"
+ $"F579 0000 00FB F4F8 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0056 FFFF FFFF F800"
+ $"0000 00F6 8181 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00F8 81FE FFFF FDFA F500"
+ $"0000 2BF7 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 00F5 FCFF FFFF FBF6 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 00AC FFFF FFFF FC2B 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 2BFF FFFF FFFF FFFF E0FC FC81"
+ $"FAF9 FAF7 2B00 0000 0000 0000 0000 0000"
+ $"0000 0000 F6FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF ACF9 F600 0000 0000 0000"
+ $"0000 0000 00FC FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FF81 F600 0000 0000"
+ $"0000 0000 0000 FAFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FE2B 0000 0000"
+ $"0000 0000 0000 0000 F8FB FCFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFD 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 00F5 F7F7"
+ $"2BF7 F8FA FAFD FFFF FFFF FFFF F900 0000"
+ $"0000 0000 F7AC FFFF FCF6 0000 0000 0000"
+ $"0000 0000 0000 F5FE FFFF FFFF F900 0000"
+ $"0000 00F6 FFFF FFFB 0000 0000 2B81 F700"
+ $"0000 0000 0000 00F9 FFFF FFFF 0000 0000"
+ $"0000 002B FFFF FF2B 0000 0000 FAFB 0000"
+ $"0000 0000 00F5 F8FF FFFF FEF6 F500 0000"
+ $"0000 00F5 FEFF FFFC 0000 0000 F5FC FDFC"
+ $"ACFC ACFC FDFF FFFF FF81 F556 F500 0000"
+ $"0000 0000 F8FF FFFF FBF5 0000 0000 F6FA"
+ $"81AC FCFC FCFA 562B F5F8 FCF5 0000 0000"
+ $"0000 0000 002B FCFF FFFE FCF9 F82B 0000"
+ $"0000 0000 2BF7 FAFC FD56 0000 0000 0000"
+ $"0000 0000 0000 00F7 FAFE FFFF FFFF FFFF"
+ $"FFFF FFFF FFAC 812B 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 F52B F9F9 FAF9"
+ $"FAF8 F72B F6"
+};
+
+resource 'icl4' (128, "Application Icon") {
+ $"0000 0000 0000 0000 0000 C000 0000 0000"
+ $"0000 0000 0000 0000 0000 CD00 0000 0000"
+ $"0000 0000 0000 0000 000C ECD0 0000 0000"
+ $"0000 0000 000C DDEE EEEE C0FC 0000 0000"
+ $"0000 0000 0DFF FFFF AEC0 0CFC 0000 0000"
+ $"0000 000C FFFF FEDC 0000 CEFD 0000 0000"
+ $"0000 00CA FFFA C000 000D AFFF D000 0000"
+ $"0000 00EF FFFD 000C 00DF FFFF FD00 0000"
+ $"0000 0CFF FFF0 0D0C C0EF FFFF FE00 0000"
+ $"0000 0CFF FFE0 07C0 C00E FFFF FF00 0000"
+ $"0000 00FF FFF0 0000 0000 EFFF FE00 0000"
+ $"0000 00EF FFFE 0000 C000 CAFF FC00 0000"
+ $"0000 000F FFFF D000 7D00 0DFF C000 0000"
+ $"0000 000C EFFF FE00 0D00 0EFD 0000 0000"
+ $"0000 0000 0DFF FFD0 000C DD00 0000 0000"
+ $"0000 000C EEFF AD00 00CC 0000 0000 0000"
+ $"0000 00EF FFE0 0000 0000 0000 0000 0000"
+ $"0000 0EFF FFEC 0000 0000 0000 0000 0000"
+ $"0000 CFFF FFFF FEED DDDC C000 0000 0000"
+ $"0000 CFFF FFFF FFFF FFFF FFED C000 0000"
+ $"0000 0EFF FFFF FFFF FFFF FFFF FDC0 0000"
+ $"0000 00DF FFFF FFFF FFFF FFFF FFAC 0000"
+ $"0000 0000 CEEF FFFF FFFF FFFF FFFE 0000"
+ $"0000 0000 0000 00CC CCDD DAFF FFFF D000"
+ $"0000 CEFF EC00 0000 0000 000F FFFF D000"
+ $"000C FFFE 0000 CEC0 0000 000D FFFF 0000"
+ $"000C FFFC 0000 DD00 0000 00DF FFF0 C000"
+ $"0000 FFFE 0000 0EFE EEEE FFFF FD0D 0000"
+ $"0000 CFFF E000 00CD DEEE EDDC 0DE0 0000"
+ $"0000 0CEF FFED CC00 0000 CCDD FC00 0000"
+ $"0000 000C DAFF FFFF FFFF FEEC 0000 0000"
+ $"0000 0000 000C DDDD DDCC"
+};
+
+resource 'ics8' (128, "Application Icon") {
+ $"0000 0000 0000 0000 0000 2B00 0000 0000"
+ $"0000 0000 0000 2B56 F8F9 F856 0000 0000"
+ $"0000 0000 FBFF FFFB 56F5 F7FC 0000 0000"
+ $"0000 00FC FFFD F500 00F9 FFFF F900 0000"
+ $"0000 F5FF FF56 2B2A F6FB FFFF FF00 0000"
+ $"0000 00FE FFFB 00F5 0000 FBFF FC00 0000"
+ $"0000 00F8 FFFF FA00 7900 F7FD F500 0000"
+ $"0000 00F5 F9FF FDF6 00F6 F800 0000 0000"
+ $"0000 2BEA FF56 0000 0000 0000 0000 0000"
+ $"0000 81FF FFFF FEFD FCFC FAF8 0000 0000"
+ $"0000 F6FE FFFF FFFF FFFF FFFF FEF7 0000"
+ $"0000 0000 2B56 FA81 81FC FDFF FFFF F600"
+ $"0000 ACFE 2B00 2BF5 0000 00F8 FFFF F500"
+ $"00F5 FFFC 0000 FA56 56F8 F9FD FE56 F500"
+ $"0000 56E0 FBF7 F6F6 F856 56F9 56F6 0000"
+ $"0000 0000 F881 FCAC FC81 FA2B"
+};
+
+resource 'ics4' (128, "Application Icon") {
+ $"0000 0000 00C0 0000 0000 00CD CDCD 0000"
+ $"0000 EFFD D0CE 0000 000E FA00 0DFF D000"
+ $"000F FDCC 0EFF F000 000F FE00 00EF E000"
+ $"000C FFD0 D0CE 0000 0000 DFF0 0CC0 0000"
+ $"00CF FD00 0000 0000 00DF FFFA EEDD 0000"
+ $"00CA FFFF FFFF FC00 0000 CDDE DEEF FFC0"
+ $"00EF C0C0 000C FF00 00FE 00DD CDDF AD00"
+ $"00DF DCC0 DCDD D000 0000 DDEE EDDC"
+};
+
+resource 'icm8' (128, "Application Icon") {
+ $"0000 0000 0000 0000 0000 F800 0000 0000"
+ $"0000 0000 2BFA FBAC 8156 F681 0000 0000"
+ $"0000 00FA FFFD F7F5 0056 FDFF F800 0000"
+ $"0000 F5FF FF56 2B2A F556 FFFF FF00 0000"
+ $"0000 00FB FFFE F600 4F00 56FF F800 0000"
+ $"0000 00F5 81EA FEF5 00F6 F8F6 0000 0000"
+ $"0000 F8FF FFFA 2BF6 F600 0000 0000 0000"
+ $"0000 56FF FFFF FFFF FFFF E0FC F9F5 0000"
+ $"0000 00F6 F981 FBFC FCAC FDFF FFFD F500"
+ $"0000 FDFD F600 F700 0000 0056 FFFE F500"
+ $"0000 ACFE 2B00 F7F9 8181 FBFB 81F7 0000"
+ $"0000 00F7 FAFB FBFA FAF9 56F8 F6"
+};
+
+resource 'icm4' (128, "Application Icon") {
+ $"0000 0000 00C0 0000 0000 CDEE ECCE 0000"
+ $"000D FAC0 0DEF C000 000F FD0C 0DFF F000"
+ $"000E FFC0 C0DF C000 0000 DFEC 00CC 0000"
+ $"00CF FDC0 C000 0000 00DF FFFF FFFE D000"
+ $"000C DDEE EEFF FF00 00AE C0C0 000D FEC0"
+ $"00FE C0CD DEDE DC00 000C DDED DDDC C0"
+};
+
+resource 'icm#' (128, "Application Icon") {
+ { /* array: 2 elements */
+ /* [1] */
+ $"0000 0350 1C30 1C78 1C10 0620 1900 3FF8"
+ $"0B7C 300C 31B0 0720",
+ /* [2] */
+ $"0070 0FF0 1FF8 3FF8 3FF8 1FF0 3FF8 3FFC"
+ $"1FFE 7FFE 7FFE 3FF8"
+ }
+};
+
+resource 'icns' (128, "Application Icon") {
+ { /* array elementArray: 20 elements */
+ /* [1] */
+ 'ICN#',
+ $"0000 0000 0000 0800 0000 0A00 000B F200"
+ $"007F 4200 00FC 0700 01F0 1F80 03E0 1F80"
+ $"03E5 3FC0 03E0 1FC0 03E0 0FC0 03F0 0780"
+ $"01F8 8300 00FC 4600 003C 0800 00F4 0000"
+ $"03E0 0000 07E0 0000 07FF 6000 07FF FF00"
+ $"07FF FFC0 01FF FFE0 007F FFF0 0000 17F0"
+ $"0780 01F8 0F04 00F0 0E08 03E0 0F07 FFD0"
+ $"0780 D820 03F0 0380 007F FC00 0005 4000"
+ $"0000 1800 0000 1E00 0000 1E00 001F FF00"
+ $"00FF FF00 01FF FF00 03FF FF80 03FF FFC0"
+ $"07FF FFC0 07FF FFC0 07FF FFC0 03FF FFC0"
+ $"03FF FF80 01FF FF00 007F FE00 03FF F000"
+ $"07FF F800 07FF FE00 0FFF FF80 0FFF FFE0"
+ $"07FF FFF0 03FF FFF8 01FF FFF8 027F FFFC"
+ $"1FFF FFFC 1FFF FFFC 1FFF FFF8 1FFF FFF8"
+ $"1FFF FFF0 07FF FFC0 01FF FF00 007F FC00",
+ /* [2] */
+ 'icl4',
+ $"0000 0000 0000 0000 0000 C000 0000 0000"
+ $"0000 0000 0000 0000 0000 CD00 0000 0000"
+ $"0000 0000 0000 0000 000C ECD0 0000 0000"
+ $"0000 0000 000C DDEE EEEE C0FC 0000 0000"
+ $"0000 0000 0DFF FFFF AEC0 0CFC 0000 0000"
+ $"0000 000C FFFF FEDC 0000 CEFD 0000 0000"
+ $"0000 00CA FFFA C000 000D AFFF D000 0000"
+ $"0000 00EF FFFD 000C 00DF FFFF FD00 0000"
+ $"0000 0CFF FFF0 0D0C C0EF FFFF FE00 0000"
+ $"0000 0CFF FFE0 07C0 C00E FFFF FF00 0000"
+ $"0000 00FF FFF0 0000 0000 EFFF FE00 0000"
+ $"0000 00EF FFFE 0000 C000 CAFF FC00 0000"
+ $"0000 000F FFFF D000 7D00 0DFF C000 0000"
+ $"0000 000C EFFF FE00 0D00 0EFD 0000 0000"
+ $"0000 0000 0DFF FFD0 000C DD00 0000 0000"
+ $"0000 000C EEFF AD00 00CC 0000 0000 0000"
+ $"0000 00EF FFE0 0000 0000 0000 0000 0000"
+ $"0000 0EFF FFEC 0000 0000 0000 0000 0000"
+ $"0000 CFFF FFFF FEED DDDC C000 0000 0000"
+ $"0000 CFFF FFFF FFFF FFFF FFED C000 0000"
+ $"0000 0EFF FFFF FFFF FFFF FFFF FDC0 0000"
+ $"0000 00DF FFFF FFFF FFFF FFFF FFAC 0000"
+ $"0000 0000 CEEF FFFF FFFF FFFF FFFE 0000"
+ $"0000 0000 0000 00CC CCDD DAFF FFFF D000"
+ $"0000 CEFF EC00 0000 0000 000F FFFF D000"
+ $"000C FFFE 0000 CEC0 0000 000D FFFF 0000"
+ $"000C FFFC 0000 DD00 0000 00DF FFF0 C000"
+ $"0000 FFFE 0000 0EFE EEEE FFFF FD0D 0000"
+ $"0000 CFFF E000 00CD DEEE EDDC 0DE0 0000"
+ $"0000 0CEF FFED CC00 0000 CCDD FC00 0000"
+ $"0000 000C DAFF FFFF FFFF FEEC 0000 0000"
+ $"0000 0000 000C DDDD DDCC 0000 0000 0000",
+ /* [3] */
+ 'icl8',
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 F600 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 5656 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 00F6 FCF8 F900 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00F6 56F9 FCAC"
+ $"FCFC ACFE F600 FFF6 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 F5FA FDFF FFFF FFFF"
+ $"FD81 F800 00F7 FFF7 0000 0000 0000 0000"
+ $"0000 0000 0000 00F6 E0FF FFFF FFFD F92B"
+ $"0000 0000 F6AC FFFA 0000 0000 0000 0000"
+ $"0000 0000 0000 2BFF EAFF FFFE F700 0000"
+ $"0000 0056 FFFF FFFF 8100 0000 0000 0000"
+ $"0000 0000 0000 FCFF FFFF FF56 0000 00F5"
+ $"F500 FAFF FFFF FFFF FFF8 0000 0000 0000"
+ $"0000 0000 00F6 FFFF FFFF FE00 0079 0055"
+ $"F600 FBFF FFFF FFFF FFFD 0000 0000 0000"
+ $"0000 0000 002B FFFF FFFF FC00 007A F6F5"
+ $"4F00 00AC FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 00F6 FFFF FFFF FFF5 0000 F500"
+ $"0000 00F5 ACFF FFFF FFFC 0000 0000 0000"
+ $"0000 0000 0000 FCFF FFFF FF81 0000 0024"
+ $"F600 0000 F5FD FFFF FF2B 0000 0000 0000"
+ $"0000 0000 0000 F5FF FFFF FFFF 8100 0000"
+ $"7A7A 0000 00FA FFFF F800 0000 0000 0000"
+ $"0000 0000 0000 00F6 FDFF FFFF FFFB 0000"
+ $"F579 0000 00FB F4F8 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0056 FFFF FFFF F800"
+ $"0000 00F6 8181 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00F8 81FE FFFF FDFA F500"
+ $"0000 2BF7 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 00F5 FCFF FFFF FBF6 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 00AC FFFF FFFF FC2B 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 2BFF FFFF FFFF FFFF E0FC FC81"
+ $"FAF9 FAF7 2B00 0000 0000 0000 0000 0000"
+ $"0000 0000 F6FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF ACF9 F600 0000 0000 0000"
+ $"0000 0000 00FC FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FF81 F600 0000 0000"
+ $"0000 0000 0000 FAFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FE2B 0000 0000"
+ $"0000 0000 0000 0000 F8FB FCFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFD 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 00F5 F7F7"
+ $"2BF7 F8FA FAFD FFFF FFFF FFFF F900 0000"
+ $"0000 0000 F7AC FFFF FCF6 0000 0000 0000"
+ $"0000 0000 0000 F5FE FFFF FFFF F900 0000"
+ $"0000 00F6 FFFF FFFB 0000 0000 2B81 F700"
+ $"0000 0000 0000 00F9 FFFF FFFF 0000 0000"
+ $"0000 002B FFFF FF2B 0000 0000 FAFB 0000"
+ $"0000 0000 00F5 F8FF FFFF FEF6 F500 0000"
+ $"0000 00F5 FEFF FFFC 0000 0000 F5FC FDFC"
+ $"ACFC ACFC FDFF FFFF FF81 F556 F500 0000"
+ $"0000 0000 F8FF FFFF FBF5 0000 0000 F6FA"
+ $"81AC FCFC FCFA 562B F5F8 FCF5 0000 0000"
+ $"0000 0000 002B FCFF FFFE FCF9 F82B 0000"
+ $"0000 0000 2BF7 FAFC FD56 0000 0000 0000"
+ $"0000 0000 0000 00F7 FAFE FFFF FFFF FFFF"
+ $"FFFF FFFF FFAC 812B 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 F52B F9F9 FAF9"
+ $"FAF8 F72B F600 0000 0000 0000 0000 0000",
+ /* [4] */
+ 'il32',
+ $"91FF 00DF 9CFF 01A0 909A FF03 D350 AF80"
+ $"91FF 02CF A080 8140 0538 24D5 FF00 DF8D"
+ $"FF02 EF80 2081 0008 0420 60A2 FFFF BF00"
+ $"BF8C FF01 CF10 8100 022C 86BF 81FF 03DF"
+ $"3000 728B FF01 CF10 8000 0110 B083 FF01"
+ $"9410 8000 0066 8AFF 0040 8100 00A2 80FF"
+ $"03E5 E3FF 7C83 0000 A088 FF00 DF81 0008"
+ $"10FF FF82 FF91 C7FF 6083 0000 3088 FF00"
+ $"BF81 0009 40FF FF66 E3D5 BAFF F330 8300"
+ $"88FF 00DF 8200 03EF FFF1 E381 FF01 EF30"
+ $"8100 0044 89FF 004C 8100 006C 80FF 01F1"
+ $"C780 FF05 EF20 0000 04C5 89FF 01DF 1081"
+ $"0000 6C80 FF01 7458 80FF 0380 0000 A08B"
+ $"FF01 D120 8100 0460 FFFF E382 80FF 0254"
+ $"10A2 8EFF 0192 0280 0000 A081 FF03 DB64"
+ $"60EF 8DFF 07AF 6412 0204 2080 EF80 FF01"
+ $"CFBB 8EFF 01EF 4480 0001 50CF 96FF 0040"
+ $"8100 0150 CF95 FF00 CF84 0003 1040 4268"
+ $"8080 01BF D18C FF01 CF02 8D00 0238 92DF"
+ $"89FF 0044 8F00 0204 62DF 88FF 0070 9000"
+ $"0110 CF89 FF02 A060 408D 0001 22EF 8DFF"
+ $"00DF 81BF 04A0 8070 2002 8200 0090 84FF"
+ $"05B4 3600 0046 DB89 FF01 DF20 8100 0090"
+ $"83FF 00DF 8000 0058 81FF 02CF 68BF 85FF"
+ $"0080 8000 0110 EF83 FF00 BF80 0000 BF81"
+ $"FF01 705A 84FF 07EF A410 0000 10CF EF83"
+ $"FF04 EF10 0000 5281 FF02 EF42 2082 4000"
+ $"2681 0003 68E7 96EF 84FF 05A2 0200 0052"
+ $"EF81 FF02 D380 6080 4007 4480 90BF EFA4"
+ $"54EF 86FF 08C3 4000 0010 5080 AFCF 83FF"
+ $"05CF AF80 4C24 A28A FF02 BF70 2086 0004"
+ $"0400 3260 C38F FF02 EFBF 9081 8003 A0BF"
+ $"C7DF 88FF 91FF 00DE 9BFF 02FE A090 9AFF"
+ $"03D3 50AF 8091 FF02 CFA0 8081 4005 3824"
+ $"D4FF 00DF 8DFF 02EF 8020 8100 0804 2060"
+ $"A2FF FFBF 00BF 8CFF 01CF 1081 0002 2C85"
+ $"BF81 FF03 DF30 0072 8BFF 01CF 1080 0001"
+ $"10B0 83FF 0194 1080 0000 668A FF00 4081"
+ $"0000 A280 FF03 ECEB FF7C 8300 00A0 88FF"
+ $"00DF 8100 0810 FFFF A6FF B1D7 FF60 8300"
+ $"002F 88FF 00BF 8100 0940 FFFF 92EB E1CE"
+ $"FFF2 3083 0088 FF00 DE82 0003 EFFF F5EB"
+ $"81FF 01EF 3081 0000 4489 FF00 4C81 0000"
+ $"6B80 FF01 F5D7 80FF 05EF 2000 0004 C589"
+ $"FF01 DF10 8100 006B 80FF 019C 8780 FF03"
+ $"8000 00A0 8BFF 01D1 2081 0004 60FF FFEB"
+ $"A680 FF02 5410 A28E FF01 9202 8000 00A0"
+ $"81FF 03DB 6360 EF8D FF07 AF64 1202 0420"
+ $"80EF 80FF 01CF BB8E FF01 EF44 8000 0150"
+ $"CF96 FF00 3F81 0001 50CF 95FF 00CF 8400"
+ $"0310 4042 6880 8001 BFD1 8CFF 01CF 028D"
+ $"0002 3892 DF89 FF00 448F 0002 0462 DF88"
+ $"FF00 7090 0001 10CF 89FF 02A0 6040 8D00"
+ $"0122 EF8D FF00 DF81 BF04 9F80 7020 0282"
+ $"0000 9084 FF05 B336 0000 46DB 89FF 01DF"
+ $"2081 0000 9083 FF00 DF80 0000 5881 FF04"
+ $"CF68 BFFE FE83 FF00 8080 0001 10EF 83FF"
+ $"00BF 8000 00BF 81FF 0170 5A84 FF07 EFA4"
+ $"1000 0010 CFEE 83FF 04EF 1000 0052 81FF"
+ $"08EF 4220 4040 3F3F 4026 8100 0368 E795"
+ $"EF84 FF05 A102 0000 52EF 81FF 0DD3 8060"
+ $"403F 3F43 8090 BFEF A354 EF86 FF08 C340"
+ $"0000 1050 80AF CF82 FF06 FECF AF7F 4C24"
+ $"A28A FF02 BF70 2086 0004 0400 3260 C38F"
+ $"FF02 EFBF 9081 8003 A0BF C7DF 88FF 90FF"
+ $"01FE DC9B FF03 FCA0 90FE 99FF 03D3 50AF"
+ $"8091 FF02 CFA0 7F81 4005 3C24 D8FF 00DF"
+ $"8DFF 02ED 8020 8100 0804 1E60 A4FF FFBF"
+ $"00BF 8CFF 01CF 0F81 0003 2C80 BEFD 80FF"
+ $"03DF 3000 748B FF01 CF10 8000 0110 B082"
+ $"FF02 FE94 1080 0000 6C8A FF00 4081 0000"
+ $"A480 FF03 F0EF FF7C 8300 00A0 88FF 00DE"
+ $"8100 0810 FFFF B8FF C2DF FF5F 8300 002E"
+ $"88FF 00BF 8100 0940 FFFF A7ED E7D8 FFF0"
+ $"2F83 0088 FF00 DC82 0003 EFFF F6EE 81FF"
+ $"01EE 3081 0000 4489 FF00 4C81 000E 6AFD"
+ $"FFFF F6DE FDFF FFEF 2000 0008 C589 FF01"
+ $"DE10 8100 056A FFFF FEAF 9E80 FF03 8000"
+ $"00A0 8BFF 01D2 2081 0004 60FF FFEF B880"
+ $"FF02 5410 A48E FF01 9404 8000 00A0 81FF"
+ $"03DB 645F EF8C FF08 FDAF 6414 0404 1F80"
+ $"EE80 FF01 CEBB 8EFF 01EF 4680 0002 50CE"
+ $"FE95 FF00 3E81 0001 4ECE 95FF 00CD 8400"
+ $"0310 4044 6880 8001 BFD2 8CFF 01CF 048D"
+ $"0002 3894 DF89 FF00 478F 0002 0464 DF88"
+ $"FF00 6F90 0001 10CF 88FF 03FE A060 408D"
+ $"0001 23ED 86FF 00FE 83FF 00DF 81BF 049C"
+ $"8070 2004 8200 0090 83FF 07FE B23C 0000"
+ $"48DA FD88 FF01 DF20 8100 0090 83FF 00DF"
+ $"8000 0057 81FF 04CF 70BF FCFC 82FF 01FE"
+ $"8080 0001 10ED 83FF 00BF 8000 00BF 81FF"
+ $"0270 58FD 83FF 07EF A410 0000 10CF EC83"
+ $"FF04 EF10 0000 5481 FF08 ED44 2040 403D"
+ $"3D40 2881 0003 68E7 9AEF 83FF 06FE A304"
+ $"0000 53EF 81FF 0DD2 8060 3E3C 3C45 8090"
+ $"BEEE A357 EF86 FF14 C240 0000 1050 80AF"
+ $"CFFF FDFF FFFE FCCF AF7A 4C24 A48A FF02"
+ $"BE70 1E86 0004 0800 3460 C78E FF03 FDEF"
+ $"BF90 8180 04A0 BFCA DFFD 87FF",
+ /* [5] */
+ 'l8mk',
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0005 FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0005 FFFF 0500 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 00FF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00FF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FF00 0000 0000 0000"
+ $"0000 0000 0000 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 0000 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 0000 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FF00 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 05FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FF00 0000 0000 0000"
+ $"0000 0000 FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FF00 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 0000 0000 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0005 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0500 00FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF 0000"
+ $"0000 0005 FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0005 FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0005 FFFF FFFF FFFF"
+ $"FFFF FFFF FF05 0000 0000 0000 0000 0000",
+ /* [6] */
+ 'ich#',
+ $"0000 0000 0000 0000 0001 0000 0000 0002"
+ $"8000 0000 0002 4000 0000 000C 4000 0000"
+ $"1FFC 6000 0001 FFE0 6000 0007 FF00 E000"
+ $"000F F801 F000 003F E003 F800 003F 800F"
+ $"FC00 007F 800F FC00 007F 091F FE00 007F"
+ $"010F FE00 00FF 0887 FE00 007F 8003 FE00"
+ $"007F 8001 FC00 003F C000 FC00 003F E0C0"
+ $"F000 001F F020 7000 000F F840 C000 0001"
+ $"F801 8000 0003 FC0E 0000 000F F000 0000"
+ $"007F 0000 0000 007E 0000 0000 00FF 8000"
+ $"0000 01FF F690 0000 01FF FFFF 0000 01FF"
+ $"FFFF F000 00FF FFFF FC00 007F FFFF FF00"
+ $"003F FFFF FF80 0007 FFFF FFC0 0000 5FFF"
+ $"FFC0 0000 0001 FFE0 01FC 0000 1FE0 03F0"
+ $"0000 1FC0 03E0 1800 1FC0 03E0 3000 1F80"
+ $"07E0 1801 FF00 03F0 0FFF FC40 01F8 02FF"
+ $"4100 00FC 0000 0E00 007F C000 F800 000F"
+ $"FFFF C000 0001 FFFE 0000 0000 0480 0000"
+ $"0000 0006 0000 0000 0007 4000 0000 0007"
+ $"8000 0000 0007 C000 0000 100F E000 0000"
+ $"7FFF E000 0003 FFFF E000 000F FFFF E000"
+ $"001F FFFF F000 003F FFFF F800 007F FFFF"
+ $"FC00 007F FFFF FE00 00FF FFFF FE00 00FF"
+ $"FFFF FE00 00FF FFFF FE00 00FF FFFF FE00"
+ $"00FF FFFF FE00 007F FFFF FC00 007F FFFF"
+ $"F800 001F FFFF F000 001F FFFF F000 0007"
+ $"FFFF C000 0003 FFFE 0000 007F FFFC 0000"
+ $"007F FFFE 0000 00FF FFFF 8000 01FF FFFF"
+ $"C000 03FF FFFF F800 01FF FFFF FC00 01FF"
+ $"FFFF FF00 01FF FFFF FF80 00FF FFFF FFC0"
+ $"007F FFFF FFE0 001F FFFF FFF0 0000 FFFF"
+ $"FFF0 0043 FFFF FFF0 03FF FFFF FFF0 07FF"
+ $"FFFF FFF0 07FF FFFF FFF0 07FF FFFF FFE0"
+ $"07FF FFFF FFE0 03FF FFFF FFC0 07FF FFFF"
+ $"FF80 01FF FFFF FF00 01FF FFFF FE00 001F"
+ $"FFFF F000 0007 FFFF 8000 0000 3FE4 0000",
+ /* [7] */
+ 'ich4',
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00CE 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 00DC"
+ $"D000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00F0 DD00 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 DAE0"
+ $"CFC0 0000 0000 0000 0000 0000 0000 0000"
+ $"0CDE EFFF FFFF FE00 0FA0 0000 0000 0000"
+ $"0000 0000 0000 00DE FFFF FFFF FFED C000"
+ $"CFF0 0000 0000 0000 0000 0000 0000 CFFF"
+ $"FFFF FFFF C000 0000 EFF0 0000 0000 0000"
+ $"0000 0000 000C FFFF FFFF ECC0 0000 000D"
+ $"FFFD 0000 0000 0000 0000 0000 00DF FFFF"
+ $"FFD0 0000 0000 0CFF FFFF E000 0000 0000"
+ $"0000 0000 0CFF FFFF FD00 0000 0000 EFFF"
+ $"FFFF FE00 0000 0000 0000 0000 0AFF FFFF"
+ $"F000 0000 C00D FFFF FFFF FFC0 0000 0000"
+ $"0000 0000 CFFF FFFF D000 D00D C00D FFFF"
+ $"FFFF FFE0 0000 0000 0000 0000 CFFF FFFF"
+ $"0000 9007 C00C FFFF FFFF FFF0 0000 0000"
+ $"0000 0000 CFFF FFFF 0000 7C00 C000 CAFF"
+ $"FFFF FFF0 0000 0000 0000 0000 CFFF FFFF"
+ $"D000 0C00 0000 00FF FFFF FFE0 0000 0000"
+ $"0000 0000 0FFF FFFF F000 0000 0000 000F"
+ $"FFFF FFC0 0000 0000 0000 0000 0DFF FFFF"
+ $"FD00 0000 CC00 000C FFFF FE00 0000 0000"
+ $"0000 0000 00AF FFFF FFD0 0000 7DD0 0000"
+ $"EFFF E000 0000 0000 0000 0000 000E FFFF"
+ $"FFFE 0000 0690 0000 DFFE 0000 0000 0000"
+ $"0000 0000 000C EFFF FFFF E000 0CC0 000C"
+ $"FFDC 0000 0000 0000 0000 0000 0000 0CDF"
+ $"FFFF FD00 0000 00DF E000 0000 0000 0000"
+ $"0000 0000 0000 00EF FFFF FD00 0000 DED0"
+ $"0000 0000 0000 0000 0000 0000 00CD EFFF"
+ $"FFAE C000 000C C000 0000 0000 0000 0000"
+ $"0000 0000 0DFF FFFE DC00 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 DFFF FFFD"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 000C FFFF FFFE DC00 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 000E FFFF FFFF"
+ $"FFFE EDED CCDC C000 0000 0000 0000 0000"
+ $"0000 000F FFFF FFFF FFFF FFFF FFFF FFFD"
+ $"DC00 0000 0000 0000 0000 000E FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFED C000 0000 0000"
+ $"0000 000C FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FED0 0000 0000 0000 0000 CFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFA 0000 0000"
+ $"0000 0000 0CAF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF EC00 0000 0000 0000 0000 DEAF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FE00 0000"
+ $"0000 0000 0000 0000 CDDD EFFF FFFF FFFF"
+ $"FFFF FFFF FFC0 0000 0000 0000 0000 0000"
+ $"0000 0000 000C CCDD FFFF FFFF FFE0 0000"
+ $"0000 00CD EFFF FDC0 0000 0000 0000 0000"
+ $"00DF FFFF FFE0 0000 0000 00EF FFFF C000"
+ $"0000 0000 0000 0000 000A FFFF FFC0 0000"
+ $"0000 0CFF FFFC 0000 000F ECC0 0000 0000"
+ $"000E FFFF FF00 0000 0000 0CFF FFF0 0000"
+ $"00DF D000 0000 0000 0CDF FFFF A000 0000"
+ $"0000 0DFF FFFC 0000 00CF AC00 0000 00CD"
+ $"EAFF FFFE 0CC0 0000 0000 00AF FFFE 0000"
+ $"0000 EFFF FFFF FFFF FFFF FED0 CD00 0000"
+ $"0000 00CF FFFF E000 0000 0CDE EFFF FFFE"
+ $"EDDC 00CE D000 0000 0000 000C FFFF FFC0"
+ $"0000 0000 0000 0000 000C DFFC 0000 0000"
+ $"0000 0000 0DFF FFFF EEDC C000 0000 00CC"
+ $"DEEA FD00 0000 0000 0000 0000 000C EFFF"
+ $"FFFF FFFF FFFF FFFF FFDC 0000 0000 0000"
+ $"0000 0000 0000 00CD FFFF FFFF FFFF FEFD"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"00CC CCDC CD00 0000 0000 0000 0000 0000",
+ /* [8] */
+ 'ich8',
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 F6AC"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 81F6"
+ $"FA00 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 00F5 FF00"
+ $"5681 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 FAAC 8100"
+ $"2BFF 2B00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"00F8 F9FB FBFF FFFF FFFF FFFE FFAC F500"
+ $"00FF FD00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 F9AC"
+ $"FFFF FFFF FFFF FFFF FFFF ACFA F600 0000"
+ $"2BFF FF00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 F8FF FFFF"
+ $"FFFF FFFF FFFF FFFF F800 0000 0000 0000"
+ $"FBFF FF00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00F8 FFFF FFFF"
+ $"FFFF FFFF FBF7 F600 0000 0000 0000 F5FA"
+ $"FFFF FFFA 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 F9FF FFFF FFFF"
+ $"FFFF FA00 0000 0000 0000 0000 00F8 FEFF"
+ $"FFFF FFFF 8100 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00F8 FFFF FFFF FFFF"
+ $"FFF9 0000 0000 0000 0000 0000 FCFF FFFF"
+ $"FFFF FFFF FFFC 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00AC FFFF FFFF FFFF"
+ $"FFF6 0000 0000 00F6 F600 00F9 FFFF FFFF"
+ $"FFFF FFFF FFFF 2B00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 F5FF FFFF FFFF FFFF"
+ $"F900 0000 7A00 007A 4F00 00FA FFFF FFFF"
+ $"FFFF FFFF FFFF FB00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 F8FF FFFF FFFF FFFF"
+ $"0000 0000 C800 0079 2B00 00F6 FEFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 F8FF FFFF FFFF FFFF"
+ $"0000 0000 A5F6 00F5 5500 0000 F6FE FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 2BFF FFFF FFFF FFFF"
+ $"FA00 0000 F52A 0000 0000 0000 00F5 FEFF"
+ $"FFFF FFFF FFFF FC00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00FF FFFF FFFF FFFF"
+ $"FF00 0000 0000 0000 0000 0000 0000 F6FF"
+ $"FFFF FFFF FFFF F800 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00F9 FFFF FFFF FFFF"
+ $"FF81 0000 0000 00F5 4FF5 0000 0000 00F6"
+ $"FFFF FFFF FFFC 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 FDFF FFFF FFFF"
+ $"FFFF 8100 0000 0000 55C9 5500 0000 0000"
+ $"FBFF FFFF FB00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00AC FFFF FFFF"
+ $"FFFF FFAC 0000 0000 25A4 C900 0000 0000"
+ $"FBFF FFFD 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00F5 ACFF FFFF"
+ $"FFFF FFFF FB00 0000 0055 2B00 0000 00F6"
+ $"FEFF 81F5 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 00F6 FAFF"
+ $"FFFF FFFF FFF9 0000 0000 0000 0000 F9FF"
+ $"ACF5 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 FBFE"
+ $"FFFF FFFF FFF9 0000 0000 0000 FAFC F900"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 2B56 ACFF FFFF"
+ $"FFFF FEFB 2B00 0000 0000 00F6 F800 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00FA FFFF FFFF FFFD"
+ $"F92B 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 FAFF FFFF FFFF FFF8"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00F6 FFFF FFFF FFFF FFFE"
+ $"F92B 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FD FFFF FFFF FFFF FFFF"
+ $"FFFF FFFB FBFB FB56 F8F8 F8F8 F600 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFF9"
+ $"FAF6 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00AC FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF AC81 2B00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00F6 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFAC 5600 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 F8FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFD 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00F8 FDFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FDF5 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 56FB FEFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFAC 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"2BFA F9FA FDFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF F800 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00F5 F8F8 F8FB"
+ $"FDFF FFFF FFFF FFFF FFFF FB00 0000 0000"
+ $"0000 0000 0000 F5FA FCFF FFFF FFFA F500"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"00F5 FAFF FFFF FFFF FFFF FB00 0000 0000"
+ $"0000 0000 0000 ACFF FFFF FFFF F600 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 00FD FFFF FFFF FFFF F800 0000 0000"
+ $"0000 0000 002B FFFF FFFF FF56 0000 0000"
+ $"0000 F5FD FCF8 F600 0000 0000 0000 0000"
+ $"0000 00FB FFFF FFFF FFFE 0000 0000 0000"
+ $"0000 0000 00F8 FFFF FFFF FF00 0000 0000"
+ $"0000 F9FF 5600 0000 0000 0000 0000 0000"
+ $"00F6 F9FF FFFF FFFF FDF5 0000 0000 0000"
+ $"0000 0000 00F8 FFFF FFFF FFF6 0000 0000"
+ $"0000 F6FF FFF8 0000 0000 0000 0000 F6F9"
+ $"FBFF FFFF FFFF FFFC 00F6 F800 0000 0000"
+ $"0000 0000 0000 FEFF FFFF FFFB 0000 0000"
+ $"0000 00F6 FCFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFD 5600 F6FA 0000 0000 0000"
+ $"0000 0000 0000 F7FF FFFF FFFF FBF6 0000"
+ $"0000 0000 002B FAFB ACFF FFFF FFFF FFAC"
+ $"FBFB F8F8 0000 F6AC FA00 0000 0000 0000"
+ $"0000 0000 0000 002B FFFF FFFF FFFF F800"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 00F6 F9FF FFF8 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 F581 FFFF FFFF FFFF"
+ $"FBFB F9F8 2B00 0000 0000 0000 0000 2BF8"
+ $"F9FB FBFF FDF9 F500 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00F9 FBFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFE 812B 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 F8FA"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFD FDF9"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 2BF8 F8F8 F8F8 F8F8 F500 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000",
+ /* [9] */
+ 'ih32',
+ $"CBFF 01D5 40AB FF02 71D5 71A9 FF04 E300"
+ $"FF8E 71A7 FF06 8035 60FF D500 C09B FF03"
+ $"AA8E 5555 8300 070E 0B39 E6FF FF00 2B98"
+ $"FF01 8E39 8700 0239 75D5 80FF 02C6 0000"
+ $"96FF 00AA 8700 010B AA84 FF02 5500 0095"
+ $"FF00 AA85 0002 4EB5 D584 FF01 E380 8000"
+ $"0075 93FF 0080 8400 0071 87FF 01AA 1C82"
+ $"0000 6091 FF00 AA84 0000 8087 FF00 5085"
+ $"0000 4090 FF00 3983 0001 06E3 82FF 04D0"
+ $"CDFF FF87 8700 00C6 8EFF 00E3 8400 0080"
+ $"80FF 076A FFFF 6DB5 FFFF 7187 0000 558E"
+ $"FF00 AA84 0081 FF08 20FF FF6A B5FF FFD5"
+ $"1087 008E FF00 AA84 0081 FF04 39CD FFE6"
+ $"9C80 FF01 D51C 8600 8EFF 00C6 8400 0080"
+ $"80FF 01E6 CE84 FF01 E31C 8400 0040 8FFF"
+ $"000B 8400 8AFF 00D5 8400 00AA 8FFF 007F"
+ $"8400 006A 82FF 02E6 B5DA 82FF 00D5 8200"
+ $"0045 91FF 002B 8400 006A 82FF 0283 2083"
+ $"82FF 0055 8000 0055 93FF 002B 8400 0040"
+ $"81FF 02DA 2020 82FF 0355 0000 2B94 FF01"
+ $"E639 8400 0055 81FF 0190 B581 FF04 D523"
+ $"005B E396 FF01 D571 8300 008E 85FF 0391"
+ $"0639 E399 FF01 600B 8200 0080 83FF 0280"
+ $"4080 98FF 02D5 8E40 8100 0307 1C55 C683"
+ $"FF01 E3AA 99FF 0079 8200 021C 80C6 A3FF"
+ $"0080 8300 00AA A4FF 00D5 8400 021C 80C6"
+ $"A2FF 002B 8800 8055 015B 9C81 AA00 D597"
+ $"FF94 0003 0580 80D5 92FF 0030 9600 0307"
+ $"3575 C68F FF01 DA04 9900 0135 918E FF00"
+ $"AA9B 0000 2B8E FF01 AA2B 9A00 012B E38F"
+ $"FF02 8E55 1C97 0000 3C93 FF00 C080 8000"
+ $"2B92 0000 AA9D FF00 E380 AA02 551C 0485"
+ $"0000 5588 FF02 E380 4380 0002 0B6B EA8F"
+ $"FF01 E380 8400 0055 88FF 0035 8200 00D5"
+ $"93FF 002B 8300 00AA 87FF 00C6 8200 009C"
+ $"83FF 04E3 2B47 AAD5 89FF 0055 8200 001C"
+ $"88FF 00AA 8200 84FF 028E 0098 89FF 01E3"
+ $"8082 0001 2BE3 88FF 00AA 8200 00DA 83FF"
+ $"03D5 000B AA85 FF03 DF88 550B 8200 0340"
+ $"FFD5 AA88 FF00 1C81 0000 5584 FF01 D53C"
+ $"8D00 042B 9CFF DA78 89FF 00AE 8200 0155"
+ $"DA84 FF03 CD80 5539 8300 0935 5555 AAAA"
+ $"FFFF CD40 808B FF01 C80B 8200 00AA 91FF"
+ $"04DF 8000 05AA 8DFF 01E3 7183 0004 5555"
+ $"8EAA C686 FF08 C6AA 8E55 550E 2380 E391"
+ $"FF01 8E55 9000 0304 1C60 C697 FF01 AA80"
+ $"8A00 0220 2B80 9FFF 00C6 84AA 00E3 92FF"
+ $"CAFF 04FE D340 FFFE A9FF 0271 D571 A9FF"
+ $"04E3 00FF 8E71 9EFF 00FE 85FF 0680 355F"
+ $"FFD5 00C0 9BFF 03AA 8E55 5583 0007 0E0B"
+ $"39E5 FFFF 002B 98FF 018E 3987 0002 3975"
+ $"D580 FF02 C600 0096 FF00 A887 0002 0BA9"
+ $"FD83 FF02 5500 0095 FF00 AA85 0002 4EB3"
+ $"D484 FF01 E380 8000 0075 93FF 0080 8400"
+ $"0071 87FF 01AA 1C82 0000 6091 FF00 AA84"
+ $"0000 8087 FF00 4F85 0000 4090 FF00 3983"
+ $"0001 06E3 82FF 04DC DCFF FF87 8700 00C6"
+ $"8EFF 00E2 8400 0080 80FF 0795 FFFF 97CA"
+ $"FFFF 7187 0000 548E FF00 AA84 0081 FF08"
+ $"60FE FF95 CAFF FFD3 1087 008E FF00 AA84"
+ $"0081 FF09 72DC FFED B8FF FFFE D41C 8600"
+ $"8EFF 00C6 8400 0080 80FF 01ED DB84 FF01"
+ $"E31C 8400 0040 8EFF 01FE 0B84 008A FF00"
+ $"D584 0000 A98F FF00 7F84 0001 69FE 81FF"
+ $"03ED C9E5 FE81 FF00 D582 0000 4591 FF00"
+ $"2B84 0000 6982 FF02 A660 A682 FF00 5580"
+ $"0000 5593 FF00 2B84 0000 4081 FF02 E560"
+ $"6082 FF03 5500 002B 94FF 01E6 3984 0000"
+ $"5581 FF01 B0CA 81FF 04D5 2300 5BE3 96FF"
+ $"01D5 7183 0000 8E85 FF03 8F06 38E3 99FF"
+ $"0160 0B82 0000 8083 FF02 8040 8097 FF03"
+ $"FED4 8E40 8100 0307 1C55 C683 FF01 E2AA"
+ $"99FF 0077 8200 021C 7FC6 A3FF 0080 8300"
+ $"00AA A4FF 00D3 8400 021C 7FC6 A2FF 002A"
+ $"8800 8055 015B 9C81 AA02 D5FF FE95 FF94"
+ $"0003 0580 80D5 92FF 0030 9600 0307 3575"
+ $"C68F FF01 D904 9900 0135 918E FF00 AA9B"
+ $"0000 2B8E FF01 AA2B 9A00 012B E38F FF02"
+ $"8E55 1C97 0000 3C93 FF00 C080 8000 2B92"
+ $"0000 A99D FF00 E180 AA02 551C 0485 0000"
+ $"5588 FF02 E280 4380 0003 0B6B E9FE 8EFF"
+ $"01E3 8084 0000 5587 FF01 FE35 8200 00D5"
+ $"87FF 01FE FE87 FF00 2B83 0000 AA87 FF00"
+ $"C682 0000 9C83 FF06 E32B 47AA D5FE FE87"
+ $"FF00 5582 0001 1CFE 87FF 00AA 8200 84FF"
+ $"028E 0098 89FF 01E3 8082 0002 2BE3 FE87"
+ $"FF00 AA82 0000 DA83 FF03 D500 0BAA 81FF"
+ $"07FD FDFF FFDF 8855 0B82 0003 40FF D5AA"
+ $"88FF 001C 8100 0055 83FF 02FE D53C 8D00"
+ $"042B 9CFF D978 89FF 00AD 8200 0155 D984"
+ $"FF03 CD7F 5539 8300 0935 5555 AAAA FFFF"
+ $"CD40 808B FF01 C70B 8200 00AA 88FF 00FE"
+ $"81FD 08FF FFFE FEDF 8000 05AA 8DFF 01E3"
+ $"7183 0007 5555 8EAA C6FF FFFE 81FF 0AFE"
+ $"FEC6 AA8E 5455 0E23 80E3 91FF 018D 5590"
+ $"0003 041C 60C6 96FF 02FD A980 8A00 0320"
+ $"2B7F FE9E FF00 C684 AA03 E3FF FFFE 8FFF"
+ $"9AFF 01FD FDAB FF04 FAD0 40FF FDA8 FF03"
+ $"FD71 D571 A9FF 04E3 00FF 8E71 9EFF 00FC"
+ $"85FF 0680 3568 FFD5 00C0 9BFF 03AA 8E55"
+ $"5583 0007 150B 39E5 FFFF 002B 98FF 018E"
+ $"3987 0002 3978 D580 FF02 C600 0096 FF00"
+ $"A387 0002 0BA5 F883 FF02 5500 0095 FF00"
+ $"AA85 0003 4EAA D2FD 83FF 01E3 8080 0000"
+ $"7893 FF00 8084 0000 7187 FF01 AA1C 8200"
+ $"006A 91FF 00AA 8400 0080 87FF 004C 8500"
+ $"0040 90FF 0039 8300 010B E382 FF04 E4E3"
+ $"FFFF 8787 0000 C68E FF00 DF84 0000 8080"
+ $"FF07 AAFF FFAE D5FF FF70 8700 0052 8EFF"
+ $"00AA 8400 81FF 0880 FAFF AAD5 FFFF D010"
+ $"8700 8EFF 00AA 8400 80FF 0AFD 8EE3 FFEF"
+ $"C6FF FFFC D21C 8600 8EFF 00C3 8400 0080"
+ $"80FF 01EF E183 FF02 FDE3 1C84 0000 408E"
+ $"FF01 FA0B 8400 8AFF 00D5 8400 00A8 8FFF"
+ $"007F 8400 0167 FC81 FF03 EFD2 EAFC 81FF"
+ $"00D5 8200 0053 90FF 01FD 2B84 0000 6781"
+ $"FF03 FDB7 80B5 82FF 0055 8000 0055 93FF"
+ $"002B 8400 0040 81FF 02EA 8080 82FF 0355"
+ $"0000 2B94 FF01 E839 8400 0055 81FF 01C0"
+ $"D581 FF04 D523 0060 E396 FF01 D571 8300"
+ $"008E 85FF 038E 0B37 E399 FF01 6B15 8200"
+ $"0080 83FF 0280 4080 97FF 03FC D28E 4081"
+ $"0004 071C 53C6 FD82 FF02 E1AA FD98 FF00"
+ $"7C82 0004 1C7D C6FF FDA1 FF00 8083 0000"
+ $"AAA4 FF00 D084 0002 1B7D C5A1 FF01 FD28"
+ $"8800 8055 0160 9C81 AA02 D5FF FC95 FF94"
+ $"0003 0B80 80D5 92FF 0035 9600 0307 3578"
+ $"C68F FF01 DD07 9900 0135 958E FF00 AA9B"
+ $"0000 2B8E FF01 A82B 9A00 012B E38E FF03"
+ $"FD8E 551C 9700 013E FD92 FF00 C080 8000"
+ $"2B92 0000 A88B FF00 FD8E FF00 DC80 AA02"
+ $"551C 0785 0000 5588 FF02 E180 4E80 0003"
+ $"0E6B E6FC 8EFF 01E3 8084 0000 5587 FF01"
+ $"FD33 8200 00D5 87FF 01FD FD87 FF00 2B83"
+ $"0000 AA87 FF00 C682 0000 9A83 FF06 E32B"
+ $"55AA D5FC FC85 FF02 FDFF 5582 0001 1CFC"
+ $"87FF 00AA 8200 84FF 038E 0095 FD88 FF01"
+ $"E380 8200 022B E3FA 87FF 00AA 8200 00DF"
+ $"83FF 03D5 000B A881 FF07 F8F8 FFFF DF90"
+ $"550B 8200 0340 FFD5 AA88 FF00 1C81 0000"
+ $"5583 FF02 FCD5 408D 0004 2B9C FFDD 7E88"
+ $"FF01 FDAF 8200 0155 DD84 FF03 CD7D 5539"
+ $"8300 0940 5555 A8AA FDFD CD47 808B FF01"
+ $"CC0B 8200 00AA 88FF 00FA 81F8 08FF FFFA"
+ $"FCDF 8000 0BA8 8CFF 02FD E371 8300 0755"
+ $"558E AAC6 FFFF FC81 FF0A FAFC C6AA 8E52"
+ $"530E 2380 E391 FF01 8C55 9000 0307 1C6B"
+ $"C696 FF02 F8A5 808A 0003 352B 7CFD 9EFF"
+ $"00C6 84AA 03E3 FFFF FC8F FF",
+ /* [10] */
+ 'h8mk',
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0005 0500"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0005 FFFF"
+ $"0005 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0005 FFFF"
+ $"FF00 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 00FF FFFF"
+ $"FFFF 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0005 0000 0000 0000 0000 FFFF FFFF"
+ $"FFFF FF00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FF00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FF00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FF00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00FF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 05FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0005 FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00FF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00DB FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 00FF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FF00"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0005 FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF05 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FF00"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FF00 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 05FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 00DB FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FF00 0000 0000 0000"
+ $"0000 0000 0000 0000 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 0000 0000 00FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FF00 0000 0000"
+ $"0000 0000 0000 0000 0000 0005 FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 0000 0000 0000 0005 0000 0000 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 0000 0000 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 0000 0005 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FF00 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FF00 0000 0000"
+ $"0000 0000 0000 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 0005 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FF00 0000 0000 0000"
+ $"0000 0000 0000 00DB FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0005 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00FF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0005 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"0500 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 FFFF FFFF FFFF FFFF FF00 0005 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000",
+ /* [11] */
+ 'ics#',
+ $"0000 00A0 0F10 1C78 1878 1C18 0E20 0600"
+ $"1800 3FD0 1FF8 057C 300C 3358 18A8 0340"
+ $"0070 07F0 1FF0 1FF8 3FF8 3FF8 1FF8 1FF0"
+ $"3FF0 3FFC 3FFE 1FFE 7FFE 7FFE 7FFC 1FF0",
+ /* [12] */
+ 'ics4',
+ $"0000 0000 00C0 0000 0000 00CD CDCD 0000"
+ $"0000 EFFD D0CE 0000 000E FA00 0DFF D000"
+ $"000F FDCC 0EFF F000 000F FE00 00EF E000"
+ $"000C FFD0 D0CE 0000 0000 DFF0 0CC0 0000"
+ $"00CF FD00 0000 0000 00DF FFFA EEDD 0000"
+ $"00CA FFFF FFFF FC00 0000 CDDE DEEF FFC0"
+ $"00EF C0C0 000C FF00 00FE 00DD CDDF AD00"
+ $"00DF DCC0 DCDD D000 0000 DDEE EDDC 0000",
+ /* [13] */
+ 'ics8',
+ $"0000 0000 0000 0000 0000 2B00 0000 0000"
+ $"0000 0000 0000 2B56 F8F9 F856 0000 0000"
+ $"0000 0000 FBFF FFFB 56F5 F7FC 0000 0000"
+ $"0000 00FC FFFD F500 00F9 FFFF F900 0000"
+ $"0000 F5FF FF56 2B2A F6FB FFFF FF00 0000"
+ $"0000 00FE FFFB 00F5 0000 FBFF FC00 0000"
+ $"0000 00F8 FFFF FA00 7900 F7FD F500 0000"
+ $"0000 00F5 F9FF FDF6 00F6 F800 0000 0000"
+ $"0000 2BEA FF56 0000 0000 0000 0000 0000"
+ $"0000 81FF FFFF FEFD FCFC FAF8 0000 0000"
+ $"0000 F6FE FFFF FFFF FFFF FFFF FEF7 0000"
+ $"0000 0000 2B56 FA81 81FC FDFF FFFF F600"
+ $"0000 ACFE 2B00 2BF5 0000 00F8 FFFF F500"
+ $"00F5 FFFC 0000 FA56 56F8 F9FD FE56 F500"
+ $"0000 56E0 FBF7 F6F6 F856 56F9 56F6 0000"
+ $"0000 0000 F881 FCAC FC81 FA2B 0000 0000",
+ /* [14] */
+ 'is32',
+ $"87FF 00C3 87FF 06F3 C79F 9F8C B598 84FF"
+ $"08F3 6008 0B53 9FE8 B34C 84FF 0948 002C"
+ $"EBF8 F884 0400 8182 FF0A E700 0093 B9D2"
+ $"E061 0000 0C82 FF0A F713 0057 FCF5 F1FB"
+ $"5000 4383 FF09 B008 0073 FF8C FFB4 2DE7"
+ $"83FF 08EB 8202 28E3 FFD9 B0FB 83FF 03CB"
+ $"1100 9089 FF00 6880 0006 142B 4050 75B2"
+ $"F782 FF01 D01C 8500 011A AF83 FF13 BF90"
+ $"7860 6048 2401 0009 DFFF FFF7 3B16 C8FF"
+ $"CDEF 80FF 139F 0004 DFFF FFEB 0444 FFFF"
+ $"7F97 9F9F 852D 1A97 F780 FF0B 9A10 54B3"
+ $"DFD4 A79F 9187 96D0 82FF 08EF A36C 4440"
+ $"4863 84C8 81FF 87FF 00C3 87FF 06F3 C79F"
+ $"9F8C B598 84FF 08F3 6008 0B52 9FE8 B34C"
+ $"84FF 0948 002C EBFA FA84 0400 8182 FF0A"
+ $"E700 0093 CDDF E960 0000 0C82 FF0A F713"
+ $"0057 FCF8 F5FB 5000 4383 FF09 B008 0072"
+ $"FFAD FFB4 2DE7 83FF 08EB 8202 28E3 FFD9"
+ $"B0FB 83FF 03CB 1100 8F89 FF00 6880 0006"
+ $"142B 4050 74B2 F782 FF01 D01C 8500 011A"
+ $"AF83 FF13 BF90 7860 6048 2401 0008 DFFF"
+ $"FFF7 3A16 C8FF CDEF 80FF 139F 0004 DFFF"
+ $"FFEB 0444 FFFF 7F97 9F9F 852D 1A97 F780"
+ $"FF0B 9910 54B3 DFD4 A79F 9086 96D0 82FF"
+ $"08EF A36C 4440 4863 84C8 81FF 86FF 01FE"
+ $"C287 FF06 F3C7 9F9F 8DB6 9884 FF08 F35F"
+ $"080B 519F E8B3 4D84 FF09 4800 2DEB FBFB"
+ $"8304 0083 82FF 0AE7 0000 93D7 E5ED 5F00"
+ $"000C 82FF 0AF6 1300 56FC F9F6 FB50 0044"
+ $"83FF 09B0 0800 72FF BDFF B42D E783 FF08"
+ $"EA83 0328 E3FF D9B0 FB83 FF03 CB12 008F"
+ $"89FF 0068 8000 0614 2B40 5075 B2F7 82FF"
+ $"01D1 1C85 0001 1AAF 83FF 2ABF 9078 6060"
+ $"4724 0100 09DF FFFF F73C 16C8 FFCF EEFE"
+ $"FFFF 9F00 04DF FFFF EB04 45FF FF7E 979F"
+ $"9F86 2D1A 98F6 80FF 0B9A 1055 B3DF D3A7"
+ $"9D91 8596 D182 FF08 EFA3 6C44 4048 6584"
+ $"C981 FF",
+ /* [15] */
+ 's8mk',
+ $"0000 0000 0000 0000 0005 FF05 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF 0000 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 FFFF FFFF FFFF FFFF FFFF FFFF 0000"
+ $"0000 FFFF FFFF FFFF FFFF FFFF FFFF FF00"
+ $"0000 0005 FFFF FFFF FFFF FFFF FFFF FF00"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FF00"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FF00"
+ $"0005 FFFF FFFF FFFF FFFF FFFF FFFF 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF 0000 0000",
+ /* [16] */
+ 'icm#',
+ $"0000 0350 1C30 1C78 1C10 0620 1900 3FF8"
+ $"0B7C 300C 31B0 0720 0070 0FF0 1FF8 3FF8"
+ $"3FF8 1FF0 3FF8 3FFC 1FFE 7FFE 7FFE 3FF8",
+ /* [17] */
+ 'icm4',
+ $"0000 0000 00C0 0000 0000 CDEE ECCE 0000"
+ $"000D FAC0 0DEF C000 000F FD0C 0DFF F000"
+ $"000E FFC0 C0DF C000 0000 DFEC 00CC 0000"
+ $"00CF FDC0 C000 0000 00DF FFFF FFFE D000"
+ $"000C DDEE EEFF FF00 00AE C0C0 000D FEC0"
+ $"00FE C0CD DEDE DC00 000C DDED DDDC C000",
+ /* [18] */
+ 'icm8',
+ $"0000 0000 0000 0000 0000 F800 0000 0000"
+ $"0000 0000 2BFA FBAC 8156 F681 0000 0000"
+ $"0000 00FA FFFD F7F5 0056 FDFF F800 0000"
+ $"0000 F5FF FF56 2B2A F556 FFFF FF00 0000"
+ $"0000 00FB FFFE F600 4F00 56FF F800 0000"
+ $"0000 00F5 81EA FEF5 00F6 F8F6 0000 0000"
+ $"0000 F8FF FFFA 2BF6 F600 0000 0000 0000"
+ $"0000 56FF FFFF FFFF FFFF E0FC F9F5 0000"
+ $"0000 00F6 F981 FBFC FCAC FDFF FFFD F500"
+ $"0000 FDFD F600 F700 0000 0056 FFFE F500"
+ $"0000 ACFE 2B00 F7F9 8181 FBFB 81F7 0000"
+ $"0000 00F7 FAFB FBFA FAF9 56F8 F600 0000",
+ /* [19] */
+ 'it32',
+ $"0000 0000 FFFF FFFF FFFF C9FF 0100 00FC"
+ $"FF01 0000 F8FF 0000 80FF 0100 00F7 FF01"
+ $"0000 80FF 0100 00F6 FF01 0000 81FF 0100"
+ $"00F5 FF80 0080 FF80 00F4 FF80 0081 FF80"
+ $"00F2 FF81 0081 FF80 00F1 FF00 4080 0082"
+ $"FF81 00ED FF82 0001 2020 83FF 8100 D9FF"
+ $"8D00 0240 2020 8200 0020 84FF 8100 D3FF"
+ $"9B00 85FF 8200 CDFF 9C00 0140 0087 FF82"
+ $"00CA FF9F 0089 FF82 00C7 FF9E 0001 2000"
+ $"8AFF 8300 C5FF 9E00 8EFF 8300 C3FF 9800"
+ $"0040 8000 91FF 8400 C1FF 9C00 93FF 8400"
+ $"C0FF 9700 0160 0096 FF85 00BF FF93 0002"
+ $"4040 0099 FF88 00BC FF94 0000 4099 FF89"
+ $"0001 2000 BAFF 9300 9BFF 8C00 0020 B8FF"
+ $"9300 9AFF 9000 B6FF 9200 9BFF 9000 0120"
+ $"20B4 FF92 009A FF00 4094 00B3 FF91 009A"
+ $"FF97 00B1 FF92 0099 FF00 4097 00B1 FF91"
+ $"0090 FF00 2085 FF01 4040 9800 AFFF 9100"
+ $"0020 8EFF 023A 2020 85FF 9A00 AFFF 9100"
+ $"88FF 0020 83FF 8020 85FF 9B00 AEFF 9100"
+ $"87FF 0120 2083 FF80 2085 FF9B 00AD FF92"
+ $"0086 FF80 2083 FF02 203A 2086 FF9A 00AD"
+ $"FF91 0087 FF80 2083 FF80 2086 FF9B 00AC"
+ $"FF91 0087 FF80 2083 FF80 2087 FF00 4099"
+ $"00AC FF91 0087 FF80 2084 FF80 2087 FF99"
+ $"00AC FF91 0087 FF81 2084 FF01 2020 88FF"
+ $"9800 ACFF 9100 88FF 8020 92FF 9700 ACFF"
+ $"9200 88FF 8020 92FF 9600 ACFF 9200 A1FF"
+ $"9200 0140 00AE FF91 00A2 FF93 00AE FF92"
+ $"00A2 FF91 00AF FF00 4091 00A3 FF90 00B0"
+ $"FF92 00A3 FF8D 0000 60B1 FF01 4040 9000"
+ $"0040 A3FF 8B00 0120 00B2 FF91 0001 4040"
+ $"8CFF 8220 8EFF 8B00 0020 B4FF 9300 8DFF"
+ $"8220 8DFF 8A00 B5FF 9300 0040 8CFF 8320"
+ $"8CFF 8900 B7FF 9200 0140 408C FF82 208C"
+ $"FF88 00B9 FF94 008B FF83 208B FF87 00BB"
+ $"FF94 008B FF82 208B FF86 00BD FF00 2093"
+ $"008A FF81 208C FF85 00BF FF94 008A FF01"
+ $"2020 8CFF 0040 8300 0020 C2FF 9300 99FF"
+ $"8400 C6FF 9200 97FF 8300 CAFF 9000 96FF"
+ $"8300 CDFF 8F00 93FF 0500 2000 2000 00CE"
+ $"FF01 2020 8E00 90FF 8040 8000 CFFF 0300"
+ $"0020 208D 008F FF01 0000 8040 CFFF 0040"
+ $"9100 90FF 8000 CFFF 9300 E4FF 8E00 0040"
+ $"8100 E6FF 9100 E7FF 0120 208E 00E9 FF8F"
+ $"00EA FF8F 00EA FF90 00E9 FF92 00E8 FF94"
+ $"00E5 FF98 00E2 FF9C 00DD FFA8 00D2 FFA5"
+ $"0004 2000 0040 4089 00C4 FFBC 0000 20BD"
+ $"FFC3 00B7 FFC4 0001 4040 B4FF C800 0020"
+ $"B2FF 0020 CA00 AFFF 0020 CB00 0040 AEFF"
+ $"CD00 0020 ACFF 0020 CF00 ABFF D100 ABFF"
+ $"D000 ABFF D000 ABFF D000 ACFF CF00 AFFF"
+ $"CB00 0020 B0FF CA00 B4FF C700 B9FF C200"
+ $"C2FF B800 D6FF A500 DEFF 9C00 E1FF 0200"
+ $"0020 9600 A2FF 0020 8900 0140 40B1 FF95"
+ $"009F FF03 0000 2020 8900 0040 B4FF 9300"
+ $"9DFF 8D00 0060 B7FF 9200 9CFF 0040 8C00"
+ $"B9FF 9100 9CFF 8D00 BBFF 9000 9CFF 8C00"
+ $"92FF 0200 2020 8200 9FFF 8F00 9CFF 8B00"
+ $"0020 92FF 0400 0020 2000 A3FF 8F00 9CFF"
+ $"8B00 0060 91FF 8100 A5FF 8E00 9DFF 8B00"
+ $"92FF 8100 A4FF 8E00 9EFF 8B00 91FF 8100"
+ $"0060 A3FF 8E00 9FFF 8B00 92FF 8200 A0FF"
+ $"8F00 A0FF 8B00 92FF 8200 0040 9CFF 0100"
+ $"408F 0082 FF00 009B FF8B 0000 2092 FF84"
+ $"0095 FF01 4020 9100 0140 4082 FF01 0000"
+ $"9CFF 8B00 93FF AE00 0240 4000 83FF 0100"
+ $"009D FF8C 0093 FF01 0020 AC00 84FF 0120"
+ $"009F FF8C 0094 FFAA 0085 FF01 2020 A0FF"
+ $"0020 8C00 95FF 0100 40A2 0087 FF80 00A2"
+ $"FF8D 0097 FF94 0000 2084 008A FF03 0000"
+ $"2000 A4FF 8D00 0020 9BFF 8E00 0020 8FFF"
+ $"0400 4000 0020 A6FF 0020 8D00 BBFF 8400"
+ $"A8FF 0040 8E00 B5FF 0040 8300 0120 00AC"
+ $"FF91 00AF FF02 0040 4083 00B0 FF95 00A3"
+ $"FF8A 00B4 FF99 0097 FF8C 0001 4000 B8FF"
+ $"BC00 0220 0000 BDFF BA00 0020 C3FF B000"
+ $"0020 8000 C9FF A400 0120 2083 00D3 FF9F"
+ $"0004 2020 4000 00DC FF92 00FF FFFF FFB3"
+ $"FFFF FFCA FF00 FBFD FF00 FBFC FF03 FBFB"
+ $"0000 F6FF 01FB FB81 FF01 0000 80FF 00FB"
+ $"F4FF 0000 80FF 0100 00F7 FF01 0000 80FF"
+ $"0100 00F5 FF02 FB00 0081 FF01 0000 F5FF"
+ $"8000 80FF 8000 F4FF 8000 81FF 8000 F2FF"
+ $"8100 81FF 8000 F1FF 0040 8000 82FF 8100"
+ $"D6FF 00FB 93FF 8200 0220 20FB 82FF 8100"
+ $"D9FF 8D00 0240 2020 8200 0220 FFFB 82FF"
+ $"8100 D3FF 9B00 01FB FB83 FF82 00CD FF9C"
+ $"0001 4000 87FF 8200 CAFF 9F00 89FF 8200"
+ $"C7FF 9E00 0120 008A FF83 00C5 FF9E 008E"
+ $"FF83 00C0 FF02 FBFB FF98 0000 4080 0001"
+ $"FBFB 8FFF 8400 C0FF 00FB 9C00 03FF FFFB"
+ $"FB8F FF84 00C0 FF97 0003 6000 FBFB 94FF"
+ $"8500 BFFF 9300 0440 4000 FBFB 97FF 8800"
+ $"BCFF 9400 0440 FFFF FBFB 95FF 8900 0120"
+ $"00BA FF93 009B FF8C 0000 20B8 FF93 009A"
+ $"FF90 00B6 FF92 009B FF90 0001 2020 B4FF"
+ $"9200 99FF 01FB 4094 00B3 FF91 009A FF97"
+ $"00B1 FF92 008D FF00 FB88 FF00 4097 00B1"
+ $"FF91 008E FF02 FBFF 6085 FF01 4040 9800"
+ $"AFFF 9100 0020 8EFF 026E 6060 85FF 9A00"
+ $"ADFF 01FB FF91 0088 FF00 6083 FF80 6085"
+ $"FF9B 0000 FBAB FF01 FBFF 9100 87FF 0160"
+ $"6083 FF80 6085 FF9B 0000 FBAC FF92 0086"
+ $"FF80 6083 FF02 606E 6084 FF01 FBFF 9A00"
+ $"ADFF 9100 87FF 8060 83FF 8060 86FF 9B00"
+ $"ACFF 9100 87FF 8060 01FB FB81 FF80 6085"
+ $"FF02 FBFB 4099 00AC FF91 0087 FF80 6084"
+ $"FF80 6084 FF02 FBFB FF99 00AC FF91 0086"
+ $"FF00 FB81 6082 FF03 FBFF 6060 88FF 9800"
+ $"ACFF 9100 88FF 8060 90FF 01FB FF97 00AC"
+ $"FF92 0088 FF80 608F FF02 FBFF FF96 00AC"
+ $"FF92 0088 FF03 FBFF FFFB 92FF 9200 0140"
+ $"00AC FF01 FBFB 9100 A2FF 9300 ACFF 01FB"
+ $"FB92 00A2 FF91 00AF FF00 4091 00A3 FF90"
+ $"0000 FBAF FF92 0001 FBFB 8CFF 80FB 8FFF"
+ $"8D00 0060 B1FF 0140 4090 0002 40FF FB91"
+ $"FF00 FB8C FF8B 0001 2000 B2FF 9100 0240"
+ $"40FB 8BFF 8260 01FF FB8C FF8B 0001 20FB"
+ $"B1FF 01FB FF93 0001 FBFB 89FF 01FB FF82"
+ $"6001 FBFB 8BFF 8A00 B5FF 9300 0040 8CFF"
+ $"8360 8CFF 8900 B7FF 9200 0140 408B FF00"
+ $"FB82 608C FF88 00B9 FF94 008B FF83 608B"
+ $"FF87 00BB FF94 008B FF82 608B FF86 00BD"
+ $"FF00 2093 008A FF81 608C FF85 00BE FF00"
+ $"FB94 008A FF01 6060 8CFF 0040 8300 0020"
+ $"C2FF 9300 99FF 8400 C6FF 9200 94FF 02FB"
+ $"FBFF 8300 CAFF 9000 94FF 01FB FB83 00CD"
+ $"FF8F 0093 FF06 0020 0020 0000 FBCD FF01"
+ $"2020 8E00 90FF 8040 8000 CFFF 0300 0020"
+ $"208D 008F FF01 0000 8040 CFFF 0040 9100"
+ $"80FF 00FB 8AFF 01FB FF80 0001 FFFB CAFF"
+ $"02FB FFFF 9300 E1FF 02FB FBFF 8E00 0040"
+ $"8100 01FF FBE2 FF01 FBFB 9100 81FF 00FB"
+ $"E2FF 0120 208E 0000 FBE8 FF8F 00EA FF8F"
+ $"00EA FF90 00E8 FF00 FB92 0001 FBFB E5FF"
+ $"00FB 9400 E5FF 9800 00FB DFFF 01FB FB9C"
+ $"00DD FFA8 008F FF01 FBFB BEFF A500 0420"
+ $"0000 4040 8900 C4FF BC00 0020 BDFF C300"
+ $"B7FF C400 0140 40B4 FFC8 0000 20B2 FF00"
+ $"20CA 00AF FF00 20CB 0000 40AE FFCD 0000"
+ $"20AB FF01 FB20 CF00 ABFF D100 ABFF D000"
+ $"ABFF D000 ABFF D000 A8FF 00FB 80FF CF00"
+ $"AFFF CB00 0220 FBFB AEFF CA00 ABFF 00FB"
+ $"85FF C700 B9FF C200 00FB C1FF B800 D4FF"
+ $"01FB FBA5 00D3 FF01 FBFB 86FF 9C00 A3FF"
+ $"00FB BAFF 0200 0020 9600 A2FF 0020 8900"
+ $"0440 40FF FBFB AEFF 9500 9FFF 0300 0020"
+ $"2089 0004 40FF FFFB FBB0 FF93 009C FF00"
+ $"FB8D 0000 60B7 FF92 009A FF02 FBFB 408C"
+ $"00B9 FF91 009C FF8D 009C FF01 FBFB 9AFF"
+ $"9000 9CFF 8C00 92FF 0200 2020 8200 80FF"
+ $"01FB FB9A FF8F 009C FF8B 0002 20FF FB90"
+ $"FF04 0000 2020 0084 FF01 FBFB 9AFF 8F00"
+ $"00FB 9BFF 8B00 0060 91FF 8100 A1FF 00FB"
+ $"80FF 8E00 80FF 00FB 99FF 8B00 92FF 8100"
+ $"A4FF 8E00 81FF 00FB 99FF 8B00 91FF 8100"
+ $"0060 A3FF 8E00 81FF 01FB FB99 FF8B 0092"
+ $"FF82 0080 FF00 FB9C FF8F 00A0 FF8B 0092"
+ $"FF82 0003 40FF FFFB 99FF 0100 408F 0082"
+ $"FF00 009B FF8B 0000 2092 FF84 0088 FF83"
+ $"FB84 FF01 4020 9100 0140 4082 FF01 0000"
+ $"9CFF 8B00 90FF 02FB FBFF AE00 0240 4000"
+ $"81FF 03FB FB00 009D FF8C 0093 FF01 0020"
+ $"AC00 84FF 0120 009F FF8C 0094 FFAA 0085"
+ $"FF01 2020 9EFF 02FB FB20 8C00 02FF FFFB"
+ $"92FF 0100 40A2 0002 FFFF FB84 FF80 00A2"
+ $"FF8D 0096 FF00 FB94 0000 2084 0001 FFFB"
+ $"84FF 00FB 80FF 0300 0020 00A4 FF8D 0000"
+ $"209B FF8E 0000 208F FF04 0040 0000 20A6"
+ $"FF00 208D 009E FF8A FB8D FF84 0000 FBA6"
+ $"FF01 FB40 8E00 AFFF 80FB 80FF 0040 8300"
+ $"0120 00A9 FF02 FBFF FF91 00AB FF80 FB03"
+ $"FF00 4040 8300 B0FF 9500 89FF 01FB FB89"
+ $"FF81 FB85 FF8A 00B4 FF99 0097 FF8C 0001"
+ $"4000 B8FF BC00 0220 0000 BDFF BA00 0020"
+ $"BFFF 00FB 80FF B000 0020 8000 C7FF 01FB"
+ $"FBA4 0001 2020 8300 CCFF 01FB FB82 FF9F"
+ $"0008 2020 4000 00FF FFFB FBD8 FF92 0084"
+ $"FF01 FBFB FFFF FFFF AAFF FFFF CAFF 00F0"
+ $"FDFF 00F0 FCFF 03F0 F000 00F6 FF01 F0F0"
+ $"81FF 0100 0080 FF00 F0F4 FF00 0080 FF01"
+ $"0000 F7FF 0100 0080 FF01 0000 F5FF 02F0"
+ $"0000 81FF 0100 00F5 FF80 0080 FF80 00F4"
+ $"FF80 0081 FF80 00F2 FF81 0081 FF80 00F1"
+ $"FF00 4080 0082 FF81 00D6 FF00 F093 FF82"
+ $"0002 4040 F082 FF81 00D9 FF8D 0080 4082"
+ $"0002 40FF F082 FF81 00D3 FF9B 0001 F0F0"
+ $"83FF 8200 CDFF 9C00 0140 0087 FF82 00CA"
+ $"FF9F 0089 FF82 00C7 FF9E 0001 4000 8AFF"
+ $"8300 C5FF 9E00 8EFF 8300 C0FF 02F0 F0FF"
+ $"9800 0040 8000 01F0 F08F FF84 00C0 FF00"
+ $"F09C 0003 FFFF F0F0 8FFF 8400 C0FF 9700"
+ $"0340 00F0 F094 FF85 00BF FF93 0004 4040"
+ $"00F0 F097 FF88 00BC FF94 0004 40FF FFF0"
+ $"F095 FF89 0001 4000 BAFF 9300 9BFF 8C00"
+ $"0040 B8FF 9300 9AFF 9000 B6FF 9200 9BFF"
+ $"9000 0140 40B4 FF92 0099 FF01 F040 9400"
+ $"B3FF 9100 9AFF 9700 B1FF 9200 8DFF 00F0"
+ $"88FF 0040 9700 B1FF 9100 8EFF 02F0 FF80"
+ $"85FF 0140 4098 00AF FF91 0000 408E FF02"
+ $"A580 8085 FF9A 00AD FF01 F0FF 9100 88FF"
+ $"0080 83FF 8080 85FF 9B00 00F0 ABFF 01F0"
+ $"FF91 0087 FF01 8080 83FF 8080 85FF 9B00"
+ $"00F0 ACFF 9200 86FF 8080 83FF 0280 A580"
+ $"84FF 01F0 FF9A 00AD FF91 0087 FF80 8083"
+ $"FF80 8086 FF9B 00AC FF91 0087 FF80 8001"
+ $"F0F0 81FF 8080 85FF 02F0 F040 9900 ACFF"
+ $"9100 87FF 8080 84FF 8080 84FF 02F0 F0FF"
+ $"9900 ACFF 9100 86FF 00F0 8180 82FF 03F0"
+ $"FF80 8088 FF98 00AC FF91 0088 FF80 8090"
+ $"FF01 F0FF 9700 ACFF 9200 88FF 8080 8FFF"
+ $"02F0 FFFF 9600 ACFF 9200 88FF 03F0 FFFF"
+ $"F092 FF92 0001 4000 ACFF 01F0 F091 00A2"
+ $"FF93 00AC FF01 F0F0 9200 A2FF 9100 AFFF"
+ $"0040 9100 A3FF 9000 00F0 AFFF 9200 01F0"
+ $"F08C FF80 F08F FF8D 0000 80B1 FF01 4040"
+ $"9000 0240 FFF0 91FF 00F0 8CFF 8B00 0140"
+ $"00B2 FF91 0002 4040 F08B FF82 8001 FFF0"
+ $"8CFF 8B00 0140 F0B1 FF01 F0FF 9300 01F0"
+ $"F089 FF01 F0FF 8280 01F0 F08B FF8A 00B5"
+ $"FF93 0000 408C FF83 808C FF89 00B7 FF92"
+ $"0001 4040 8BFF 00F0 8280 8CFF 8800 B9FF"
+ $"9400 8BFF 8380 8BFF 8700 BBFF 9400 8BFF"
+ $"8280 8BFF 8600 BDFF 0040 9300 8AFF 8180"
+ $"8CFF 8500 BEFF 00F0 9400 8AFF 0180 808C"
+ $"FF00 4083 0000 40C2 FF93 0099 FF84 00C6"
+ $"FF92 0094 FF02 F0F0 FF83 00CA FF90 0094"
+ $"FF01 F0F0 8300 CDFF 8F00 93FF 0600 4000"
+ $"4000 00F0 CDFF 0140 408E 0090 FF80 4080"
+ $"00CF FF03 0000 4040 8D00 8FFF 0100 0080"
+ $"40CF FF00 4091 0080 FF00 F08A FF01 F0FF"
+ $"8000 01FF F0CA FF02 F0FF FF93 00E1 FF02"
+ $"F0F0 FF8E 0000 4081 0001 FFF0 E2FF 01F0"
+ $"F091 0081 FF00 F0E2 FF01 4040 8E00 00F0"
+ $"E8FF 8F00 EAFF 8F00 EAFF 9000 E8FF 00F0"
+ $"9200 01F0 F0E5 FF00 F094 00E5 FF98 0000"
+ $"F0DF FF01 F0F0 9C00 DDFF A800 8FFF 01F0"
+ $"F0BE FFA5 0004 4000 0040 4089 00C4 FFBC"
+ $"0000 40BD FFC3 00B7 FFC4 0001 4040 B4FF"
+ $"C800 0040 B2FF 0040 CA00 AFFF 0040 CB00"
+ $"0040 AEFF CD00 0040 ABFF 01F0 40CF 00AB"
+ $"FFD1 00AB FFD0 00AB FFD0 00AB FFD0 00A8"
+ $"FF00 F080 FFCF 00AF FFCB 0002 40F0 F0AE"
+ $"FFCA 00AB FF00 F085 FFC7 00B9 FFC2 0000"
+ $"F0C1 FFB8 00D4 FF01 F0F0 A500 D3FF 01F0"
+ $"F086 FF9C 00A3 FF00 F0BA FF02 0000 4096"
+ $"00A2 FF00 4089 0004 4040 FFF0 F0AE FF95"
+ $"009F FF03 0000 4040 8900 0440 FFFF F0F0"
+ $"B0FF 9300 9CFF 00F0 8D00 0080 B7FF 9200"
+ $"9AFF 02F0 F040 8C00 B9FF 9100 9CFF 8D00"
+ $"9CFF 01F0 F09A FF90 009C FF8C 0092 FF02"
+ $"0040 4082 0080 FF01 F0F0 9AFF 8F00 9CFF"
+ $"8B00 0240 FFF0 90FF 0400 0040 4000 84FF"
+ $"01F0 F09A FF8F 0000 F09B FF8B 0000 4091"
+ $"FF81 00A1 FF00 F080 FF8E 0080 FF00 F099"
+ $"FF8B 0092 FF81 00A4 FF8E 0081 FF00 F099"
+ $"FF8B 0091 FF81 0000 40A3 FF8E 0081 FF01"
+ $"F0F0 99FF 8B00 92FF 8200 80FF 00F0 9CFF"
+ $"8F00 A0FF 8B00 92FF 8200 0340 FFFF F099"
+ $"FF01 0040 8F00 82FF 0000 9BFF 8B00 0040"
+ $"92FF 8400 88FF 83F0 84FF 0140 4091 0001"
+ $"4040 82FF 0100 009C FF8B 0090 FF02 F0F0"
+ $"FFAE 0002 4040 0081 FF03 F0F0 0000 9DFF"
+ $"8C00 93FF 0100 40AC 0084 FF01 4000 9FFF"
+ $"8C00 94FF AA00 85FF 0140 409E FF02 F0F0"
+ $"408C 0002 FFFF F092 FF01 0040 A200 02FF"
+ $"FFF0 84FF 8000 A2FF 8D00 96FF 00F0 9400"
+ $"0040 8400 01FF F084 FF00 F080 FF03 0000"
+ $"4000 A4FF 8D00 0040 9BFF 8E00 0040 8FFF"
+ $"0400 4000 0040 A6FF 0040 8D00 9EFF 8AF0"
+ $"8DFF 8400 00F0 A6FF 01F0 408E 00AF FF80"
+ $"F080 FF00 4083 0001 4000 A9FF 02F0 FFFF"
+ $"9100 ABFF 80F0 03FF 0040 4083 00B0 FF95"
+ $"0089 FF01 F0F0 89FF 81F0 85FF 8A00 B4FF"
+ $"9900 97FF 8C00 0140 00B8 FFBC 0002 4000"
+ $"00BD FFBA 0000 40BF FF00 F080 FFB0 0000"
+ $"4080 00C7 FF01 F0F0 A400 0140 4083 00CC"
+ $"FF01 F0F0 82FF 9F00 8040 0500 00FF FFF0"
+ $"F0D8 FF92 0084 FF01 F0F0 FFFF FFFF AAFF",
+ /* [20] */
+ 't8mk',
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0005"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0500 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0505 FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0005 0500"
+ $"00FF FFFF FF00 0000 0500 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"FFFF FFFF FFFF 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0005"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"FFFF FFFF FFFF FFFF FF00 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"FFFF FFFF FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 00FF"
+ $"FFFF FFFF FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 BFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FF00 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0500 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00FF FFFF FFFF"
+ $"DBFF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF BFDB DBFF FFFF FFFF"
+ $"DBFF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 00FF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF BFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0505 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF BFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"05FF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 00FF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FF00"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF DBFF"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 00FF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"DB00 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00FF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFDB DB00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0500 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF05 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0500 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF05 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFBF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0505 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0505 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 BFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF 0500 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 9B00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00BF BFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFDB FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFDB 0500 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0005 00FF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FF00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00FF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFBF BFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FF00 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 00FF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FF00"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 00DB"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0005"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFBF FFFF FFFF FFFF DB00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FF00 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FF00 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFDB FFDB FFFF 0500 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00DB DBFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF BFBF"
+ $"FFFF FF00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 00FF FFDB DBFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFBF BFBF"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 BFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0500"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0500 00FF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0005 0500 FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0005 05FF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00DB DBFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 05FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 05FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FF00 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FF00 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0505 FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FF00"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FF00 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FF00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFBF FFFF FFFF"
+ $"FFFF FFFF FFFF FF00 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 DBFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 DBFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FF00 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 05DB FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FF00"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FF00 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0500 0000 FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FF00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0005 0000"
+ $"0000 0000 0000 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 00FF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 00FF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0500 0000 0000 0000"
+ $"0000 0000 0000 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 00DB FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF BFBF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 FFFF DBDB FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF BFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0005 FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0005"
+ $"05BF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 00FF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 00FF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 00FF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FF00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 00FF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FF00 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 00FF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 00FF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 00FF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FF00 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFBF BFFF FFFF FFFF FFFF FFFF"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFDB FF00"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFDB 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0005"
+ $"05DB FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FF00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF DBFF 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFBF FFFF DB00 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 DBFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF 0500 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 05BF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFBF"
+ $"FFFF FFFF FFFF DBFF 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0500 00FF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFBF"
+ $"FFFF FFFF FFFF 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 00FF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 00FF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"BFFF 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 00FF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF DBFF FF00"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 00FF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF DB00 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0005"
+ $"0000 00FF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF DBFF FFFF 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0505 FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFDB DBFF"
+ $"FFFF FFFF FF00 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0505 0000 0000 00FF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF FFFF FFFF FFFF FFDB DBBF"
+ $"FFFF 0000 0505 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"00FF FFFF FFFF FFFF FFFF FFFF FFFF FFFF"
+ $"FFFF FFFF FFFF 0000 0000 0000 0005 0500"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ $"0000 0000 0000 0000 0000 0000 0000 0000"
+ }
+};
diff --git a/psi/dpmain.c b/psi/dpmain.c
new file mode 100644
index 000000000..6b683f6e8
--- /dev/null
+++ b/psi/dpmain.c
@@ -0,0 +1,1069 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Ghostscript DLL loader for OS/2 */
+/* For WINDOWCOMPAT (console mode) application */
+
+/* Russell Lang 1996-06-05 */
+
+/* Updated 2001-03-10 by rjl
+ * New DLL interface, uses display device.
+ * Uses same interface to gspmdrv.c as os2pm device.
+ */
+
+#define INCL_DOS
+#define INCL_DOSERRORS
+#define INCL_WIN /* to get bits/pixel of display */
+#define INCL_GPI /* to get bits/pixel of display */
+#include <os2.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <io.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/select.h>
+#include "gscdefs.h"
+#define GS_REVISION gs_revision
+#include "ierrors.h"
+#include "iapi.h"
+#include "gdevdsp.h"
+
+#define MAXSTR 256
+#define BITMAPINFO2_SIZE 40
+const char *szDllName = "GSDLL2.DLL";
+char start_string[] = "systemdict /start get exec\n";
+int debug = TRUE /* FALSE */;
+
+#define MIN_COMMIT 4096 /* memory is committed in these size chunks */
+#define ID_NAME "GSPMDRV_%u_%u"
+#define SHARED_NAME "\\SHAREMEM\\%s"
+#define SYNC_NAME "\\SEM32\\SYNC_%s"
+#define MUTEX_NAME "\\SEM32\\MUTEX_%s"
+
+LONG display_planes;
+LONG display_bitcount;
+LONG display_hasPalMan;
+ULONG os_version;
+
+/* main structure with info about the GS DLL */
+typedef struct tagGSDLL {
+ HMODULE hmodule; /* DLL module handle */
+ PFN_gsapi_revision revision;
+ PFN_gsapi_new_instance new_instance;
+ PFN_gsapi_delete_instance delete_instance;
+ PFN_gsapi_set_stdio set_stdio;
+ PFN_gsapi_set_poll set_poll;
+ PFN_gsapi_set_display_callback set_display_callback;
+ PFN_gsapi_init_with_args init_with_args;
+ PFN_gsapi_run_string run_string;
+ PFN_gsapi_exit exit;
+} GSDLL;
+
+GSDLL gsdll;
+void *instance;
+TID tid;
+
+void
+gs_addmess(char *str)
+{
+ fputs(str, stdout);
+ fflush(stdout);
+}
+
+/*********************************************************************/
+/* load and unload the Ghostscript DLL */
+
+/* free GS DLL */
+/* This should only be called when gsdll_execute has returned */
+/* TRUE means no error */
+BOOL
+gs_free_dll(void)
+{
+ char buf[MAXSTR];
+ APIRET rc;
+
+ if (gsdll.hmodule == (HMODULE) NULL)
+ return TRUE;
+ rc = DosFreeModule(gsdll.hmodule);
+ if (rc) {
+ gs_sprintf(buf, "DosFreeModule returns %d\n", rc);
+ gs_addmess(buf);
+ gs_sprintf(buf, "Unloaded GSDLL\n\n");
+ gs_addmess(buf);
+ }
+ return !rc;
+}
+
+void
+gs_load_dll_cleanup(void)
+{
+ char buf[MAXSTR];
+
+ gs_free_dll();
+}
+
+/* load GS DLL if not already loaded */
+/* return TRUE if OK */
+BOOL
+gs_load_dll(void)
+{
+ char buf[MAXSTR + 40];
+ APIRET rc;
+ char *p;
+ int i;
+ const char *dllname;
+ PTIB pptib;
+ PPIB pppib;
+ char szExePath[MAXSTR];
+ char fullname[1024];
+ const char *shortname;
+ gsapi_revision_t rv;
+
+ if ((rc = DosGetInfoBlocks(&pptib, &pppib)) != 0) {
+ fprintf(stdout, "Couldn't get pid, rc = \n", rc);
+ return FALSE;
+ }
+ /* get path to EXE */
+ if ((rc = DosQueryModuleName(pppib->pib_hmte, sizeof(szExePath),
+ szExePath)) != 0) {
+ fprintf(stdout, "Couldn't get module name, rc = %d\n", rc);
+ return FALSE;
+ }
+ if ((p = strrchr(szExePath, '\\')) != (char *)NULL) {
+ p++;
+ *p = '\0';
+ }
+ dllname = szDllName;
+#ifdef DEBUG
+ if (debug) {
+ gs_sprintf(buf, "Trying to load %s\n", dllname);
+ gs_addmess(buf);
+ }
+#endif
+ memset(buf, 0, sizeof(buf));
+ rc = DosLoadModule(buf, sizeof(buf), dllname, &gsdll.hmodule);
+ if (rc) {
+ /* failed */
+ /* try again, with path of EXE */
+ if ((shortname = strrchr((char *)szDllName, '\\'))
+ == (const char *)NULL)
+ shortname = szDllName;
+ strcpy(fullname, szExePath);
+ if ((p = strrchr(fullname, '\\')) != (char *)NULL)
+ p++;
+ else
+ p = fullname;
+ *p = '\0';
+ strcat(fullname, shortname);
+ dllname = fullname;
+#ifdef DEBUG
+ if (debug) {
+ gs_sprintf(buf, "Trying to load %s\n", dllname);
+ gs_addmess(buf);
+ }
+#endif
+ rc = DosLoadModule(buf, sizeof(buf), dllname, &gsdll.hmodule);
+ if (rc) {
+ /* failed again */
+ /* try once more, this time on system search path */
+ dllname = shortname;
+#ifdef DEBUG
+ if (debug) {
+ gs_sprintf(buf, "Trying to load %s\n", dllname);
+ gs_addmess(buf);
+ }
+#endif
+ rc = DosLoadModule(buf, sizeof(buf), dllname, &gsdll.hmodule);
+ }
+ }
+ if (rc == 0) {
+#ifdef DEBUG
+ if (debug)
+ gs_addmess("Loaded Ghostscript DLL\n");
+#endif
+ if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_REVISION",
+ (PFN *) (&gsdll.revision))) != 0) {
+ gs_sprintf(buf, "Can't find GSAPI_REVISION, rc = %d\n", rc);
+ gs_addmess(buf);
+ gs_load_dll_cleanup();
+ return FALSE;
+ }
+ /* check DLL version */
+ if (gsdll.revision(&rv, sizeof(rv)) != 0) {
+ gs_sprintf(buf, "Unable to identify Ghostscript DLL revision - it must be newer than needed.\n");
+ gs_addmess(buf);
+ gs_load_dll_cleanup();
+ return FALSE;
+ }
+
+ if (rv.revision != GS_REVISION) {
+ gs_sprintf(buf, "Wrong version of DLL found.\n Found version %ld\n Need version %ld\n", rv.revision, (long)GS_REVISION);
+ gs_addmess(buf);
+ gs_load_dll_cleanup();
+ return FALSE;
+ }
+
+ if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_NEW_INSTANCE",
+ (PFN *) (&gsdll.new_instance))) != 0) {
+ gs_sprintf(buf, "Can't find GSAPI_NEW_INSTANCE, rc = %d\n", rc);
+ gs_addmess(buf);
+ gs_load_dll_cleanup();
+ return FALSE;
+ }
+ if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_DELETE_INSTANCE",
+ (PFN *) (&gsdll.delete_instance))) != 0) {
+ gs_sprintf(buf, "Can't find GSAPI_DELETE_INSTANCE, rc = %d\n", rc);
+ gs_addmess(buf);
+ gs_load_dll_cleanup();
+ return FALSE;
+ }
+ if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_SET_STDIO",
+ (PFN *) (&gsdll.set_stdio))) != 0) {
+ gs_sprintf(buf, "Can't find GSAPI_SET_STDIO, rc = %d\n", rc);
+ gs_addmess(buf);
+ gs_load_dll_cleanup();
+ return FALSE;
+ }
+ if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_SET_DISPLAY_CALLBACK",
+ (PFN *) (&gsdll.set_display_callback))) != 0) {
+ gs_sprintf(buf, "Can't find GSAPI_SET_DISPLAY_CALLBACK, rc = %d\n", rc);
+ gs_addmess(buf);
+ gs_load_dll_cleanup();
+ return FALSE;
+ }
+ if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_SET_POLL",
+ (PFN *) (&gsdll.set_poll))) != 0) {
+ gs_sprintf(buf, "Can't find GSAPI_SET_POLL, rc = %d\n", rc);
+ gs_addmess(buf);
+ gs_load_dll_cleanup();
+ return FALSE;
+ }
+ if ((rc = DosQueryProcAddr(gsdll.hmodule, 0,
+ "GSAPI_INIT_WITH_ARGS",
+ (PFN *) (&gsdll.init_with_args))) != 0) {
+ gs_sprintf(buf, "Can't find GSAPI_INIT_WITH_ARGS, rc = %d\n", rc);
+ gs_addmess(buf);
+ gs_load_dll_cleanup();
+ return FALSE;
+ }
+ if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_RUN_STRING",
+ (PFN *) (&gsdll.run_string))) != 0) {
+ gs_sprintf(buf, "Can't find GSAPI_RUN_STRING, rc = %d\n", rc);
+ gs_addmess(buf);
+ gs_load_dll_cleanup();
+ return FALSE;
+ }
+ if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_EXIT",
+ (PFN *) (&gsdll.exit))) != 0) {
+ gs_sprintf(buf, "Can't find GSAPI_EXIT, rc = %d\n", rc);
+ gs_addmess(buf);
+ gs_load_dll_cleanup();
+ return FALSE;
+ }
+ } else {
+ gs_sprintf(buf, "Can't load Ghostscript DLL %s \nDosLoadModule rc = %d\n",
+ szDllName, rc);
+ gs_addmess(buf);
+ gs_load_dll_cleanup();
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*********************************************************************/
+/* stdio functions */
+
+static int
+gsdll_stdin(void *instance, char *buf, int len)
+{
+ return read(fileno(stdin), buf, len);
+}
+
+static int
+gsdll_stdout(void *instance, const char *str, int len)
+{
+ fwrite(str, 1, len, stdout);
+ fflush(stdout);
+ return len;
+}
+
+static int
+gsdll_stderr(void *instance, const char *str, int len)
+{
+ fwrite(str, 1, len, stderr);
+ fflush(stderr);
+ return len;
+}
+
+/*********************************************************************/
+/* display device */
+
+/*
+#define DISPLAY_DEBUG
+*/
+
+typedef struct IMAGE_S IMAGE;
+struct IMAGE_S {
+ void *handle;
+ void *device;
+ PID pid; /* PID of our window (CMD.EXE) */
+ HEV sync_event; /* tell gspmdrv to redraw window */
+ HMTX bmp_mutex; /* protects access to bitmap */
+ HQUEUE term_queue; /* notification that gspmdrv has finished */
+ ULONG session_id; /* id of gspmdrv */
+ PID process_id; /* of gspmdrv */
+
+ int width;
+ int height;
+ int raster;
+ int format;
+
+ BOOL format_known;
+
+ unsigned char *bitmap;
+ ULONG committed;
+ IMAGE *next;
+};
+
+IMAGE *first_image = NULL;
+static IMAGE *image_find(void *handle, void *device);
+
+static IMAGE *
+image_find(void *handle, void *device)
+{
+ IMAGE *img;
+ for (img = first_image; img!=0; img=img->next) {
+ if ((img->handle == handle) && (img->device == device))
+ return img;
+ }
+ return NULL;
+}
+
+/* start gspmdrv.exe */
+static int run_gspmdrv(IMAGE *img)
+{
+ int ccode;
+ PCHAR pdrvname = "gspmdrv.exe";
+ CHAR error_message[256];
+ CHAR term_queue_name[128];
+ CHAR id[128];
+ CHAR arg[1024];
+ STARTDATA sdata;
+ APIRET rc;
+ PTIB pptib;
+ PPIB pppib;
+ CHAR progname[256];
+ PCHAR tail;
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stdout, "run_gspmdrv: starting\n");
+#endif
+ gs_sprintf(id, ID_NAME, img->pid, (ULONG)img->device);
+
+ /* Create termination queue - used to find out when gspmdrv terminates */
+ gs_sprintf(term_queue_name, "\\QUEUES\\TERMQ_%s", id);
+ if (DosCreateQueue(&(img->term_queue), QUE_FIFO, term_queue_name)) {
+ fprintf(stdout, "run_gspmdrv: failed to create termination queue\n");
+ return gs_error_limitcheck;
+ }
+ /* get full path to gsos2.exe and hence path to gspmdrv.exe */
+ if ((rc = DosGetInfoBlocks(&pptib, &pppib)) != 0) {
+ fprintf(stdout, "run_gspmdrv: Couldn't get module handle, rc = %d\n",
+ rc);
+ return gs_error_limitcheck;
+ }
+ if ((rc = DosQueryModuleName(pppib->pib_hmte, sizeof(progname) - 1,
+ progname)) != 0) {
+ fprintf(stdout, "run_gspmdrv: Couldn't get module name, rc = %d\n",
+ rc);
+ return gs_error_limitcheck;
+ }
+ if ((tail = strrchr(progname, '\\')) != (PCHAR) NULL) {
+ tail++;
+ *tail = '\0';
+ } else
+ tail = progname;
+ strcat(progname, pdrvname);
+
+ /* Open the PM driver session gspmdrv.exe */
+ /* arguments are: */
+ /* (1) -d (display) option */
+ /* (2) id string */
+ gs_sprintf(arg, "-d %s", id);
+
+ /* because gspmdrv.exe is a different EXE type to gs.exe,
+ * we must use start session not DosExecPgm() */
+ sdata.Length = sizeof(sdata);
+ sdata.Related = SSF_RELATED_CHILD; /* to be a child */
+ sdata.FgBg = SSF_FGBG_BACK; /* start in background */
+ sdata.TraceOpt = 0;
+ sdata.PgmTitle = "Ghostscript PM driver session";
+ sdata.PgmName = progname;
+ sdata.PgmInputs = arg;
+ sdata.TermQ = term_queue_name;
+ sdata.Environment = pppib->pib_pchenv; /* use Parent's environment */
+ sdata.InheritOpt = 0; /* Can't inherit from parent because */
+ /* different sesison type */
+ sdata.SessionType = SSF_TYPE_DEFAULT; /* default is PM */
+ sdata.IconFile = NULL;
+ sdata.PgmHandle = 0;
+ sdata.PgmControl = 0;
+ sdata.InitXPos = 0;
+ sdata.InitYPos = 0;
+ sdata.InitXSize = 0;
+ sdata.InitYSize = 0;
+ sdata.ObjectBuffer = error_message;
+ sdata.ObjectBuffLen = sizeof(error_message);
+
+ rc = DosStartSession(&sdata, &img->session_id, &img->process_id);
+ if (rc == ERROR_FILE_NOT_FOUND) {
+ sdata.PgmName = pdrvname;
+ rc = DosStartSession(&sdata, &img->session_id, &img->process_id);
+ }
+ if (rc) {
+ fprintf(stdout, "run_gspmdrv: failed to run %s, rc = %d\n",
+ sdata.PgmName, rc);
+ fprintf(stdout, "run_gspmdrv: error_message: %s\n", error_message);
+ return gs_error_limitcheck;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stdout, "run_gspmdrv: returning\n");
+#endif
+ return 0;
+}
+
+void
+image_color(unsigned int format, int index,
+ unsigned char *r, unsigned char *g, unsigned char *b)
+{
+ switch (format & DISPLAY_COLORS_MASK) {
+ case DISPLAY_COLORS_NATIVE:
+ switch (format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ *r = *g = *b = (index ? 0 : 255);
+ break;
+ case DISPLAY_DEPTH_4:
+ if (index == 7)
+ *r = *g = *b = 170;
+ else if (index == 8)
+ *r = *g = *b = 85;
+ else {
+ int one = index & 8 ? 255 : 128;
+ *r = (index & 4 ? one : 0);
+ *g = (index & 2 ? one : 0);
+ *b = (index & 1 ? one : 0);
+ }
+ break;
+ case DISPLAY_DEPTH_8:
+ /* palette of 96 colors */
+ /* 0->63 = 00RRGGBB, 64->95 = 010YYYYY */
+ if (index < 64) {
+ int one = 255 / 3;
+ *r = ((index & 0x30) >> 4) * one;
+ *g = ((index & 0x0c) >> 2) * one;
+ *b = (index & 0x03) * one;
+ }
+ else {
+ int val = index & 0x1f;
+ *r = *g = *b = (val << 3) + (val >> 2);
+ }
+ break;
+ }
+ break;
+ case DISPLAY_COLORS_GRAY:
+ switch (format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ *r = *g = *b = (index ? 255 : 0);
+ break;
+ case DISPLAY_DEPTH_4:
+ *r = *g = *b = (unsigned char)((index<<4) + index);
+ break;
+ case DISPLAY_DEPTH_8:
+ *r = *g = *b = (unsigned char)index;
+ break;
+ }
+ break;
+ }
+}
+
+static int image_palette_size(int format)
+{
+ int palsize = 0;
+ switch (format & DISPLAY_COLORS_MASK) {
+ case DISPLAY_COLORS_NATIVE:
+ switch (format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ palsize = 2;
+ break;
+ case DISPLAY_DEPTH_4:
+ palsize = 16;
+ break;
+ case DISPLAY_DEPTH_8:
+ palsize = 96;
+ break;
+ }
+ break;
+ case DISPLAY_COLORS_GRAY:
+ switch (format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ palsize = 2;
+ break;
+ case DISPLAY_DEPTH_4:
+ palsize = 16;
+ break;
+ case DISPLAY_DEPTH_8:
+ palsize = 256;
+ break;
+ }
+ break;
+ }
+ return palsize;
+}
+
+/* New device has been opened */
+/* Tell user to use another device */
+int display_open(void *handle, void *device)
+{
+ APIRET rc;
+ IMAGE *img;
+ PTIB pptib;
+ PPIB pppib;
+ CHAR id[128];
+ CHAR name[128];
+ PBITMAPINFO2 bmi;
+
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('o', stdout);
+ fprintf(stdout, "display_open(0x%x, 0x%x)\n", handle, device);
+#endif
+
+ if (first_image) {
+ /* gsos2.exe is a console application, and displays using
+ * gspmdrv.exe which is a PM application. To start
+ * gspmdrv.exe, DosStartSession is used with SSF_RELATED_CHILD.
+ * A process can have only one child session marked SSF_RELATED_CHILD.
+ * When we call DosStopSession for the second session, it will
+ * close, but it will not write to the termination queue.
+ * When we wait for the session to end by reading the
+ * termination queue, we wait forever.
+ * For this reason, multiple image windows are disabled
+ * for OS/2.
+ * To get around this, we would need to replace the current
+ * method of one gspmdrv.exe session per window, to having
+ * a new PM application which can display multiple windows
+ * within a single session.
+ */
+ return gs_error_limitcheck;
+ }
+
+ img = (IMAGE *)malloc(sizeof(IMAGE));
+ if (img == NULL)
+ return gs_error_limitcheck;
+ memset(img, 0, sizeof(IMAGE));
+
+ /* add to list */
+ img->next = first_image;
+ first_image = img;
+
+ /* remember device and handle */
+ img->handle = handle;
+ img->device = device;
+
+ /* Derive ID from process ID */
+ if (DosGetInfoBlocks(&pptib, &pppib)) {
+ fprintf(stdout, "\ndisplay_open: Couldn't get pid\n");
+ return gs_error_limitcheck;
+ }
+ img->pid = pppib->pib_ulppid; /* use parent (CMD.EXE) pid */
+ gs_sprintf(id, ID_NAME, img->pid, (ULONG) img->device);
+
+ /* Create update event semaphore */
+ gs_sprintf(name, SYNC_NAME, id);
+ if (DosCreateEventSem(name, &(img->sync_event), 0, FALSE)) {
+ fprintf(stdout, "display_open: failed to create event semaphore %s\n", name);
+ return gs_error_limitcheck;
+ }
+ /* Create mutex - used for preventing gspmdrv from accessing */
+ /* bitmap while we are changing the bitmap size. Initially unowned. */
+ gs_sprintf(name, MUTEX_NAME, id);
+ if (DosCreateMutexSem(name, &(img->bmp_mutex), 0, FALSE)) {
+ DosCloseEventSem(img->sync_event);
+ fprintf(stdout, "display_open: failed to create mutex semaphore %s\n", name);
+ return gs_error_limitcheck;
+ }
+
+ /* Shared memory is common to all processes so we don't want to
+ * allocate too much.
+ */
+ gs_sprintf(name, SHARED_NAME, id);
+ if (DosAllocSharedMem((PPVOID) & img->bitmap, name,
+ 13 * 1024 * 1024, PAG_READ | PAG_WRITE)) {
+ fprintf(stdout, "display_open: failed allocating shared BMP memory %s\n", name);
+ return gs_error_limitcheck;
+ }
+
+ /* commit one page so there is enough storage for a */
+ /* bitmap header and palette */
+ if (DosSetMem(img->bitmap, MIN_COMMIT, PAG_COMMIT | PAG_DEFAULT)) {
+ DosFreeMem(img->bitmap);
+ fprintf(stdout, "display: failed committing BMP memory\n");
+ return gs_error_limitcheck;
+ }
+ img->committed = MIN_COMMIT;
+
+ /* write a zero pixel BMP */
+ bmi = (PBITMAPINFO2) img->bitmap;
+ bmi->cbFix = BITMAPINFO2_SIZE; /* OS/2 2.0 and Windows 3.0 compatible */
+ bmi->cx = 0;
+ bmi->cy = 0;
+ bmi->cPlanes = 1;
+ bmi->cBitCount = 24;
+ bmi->ulCompression = BCA_UNCOMP;
+ bmi->cbImage = 0;
+ bmi->cxResolution = 0;
+ bmi->cyResolution = 0;
+ bmi->cclrUsed = 0;
+ bmi->cclrImportant = 0;
+
+ /* delay start of gspmdrv until size is known */
+
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('O', stdout);
+#endif
+ return 0;
+}
+
+int display_preclose(void *handle, void *device)
+{
+ IMAGE *img;
+ REQUESTDATA Request;
+ ULONG DataLength;
+ PVOID DataAddress;
+ PULONG QueueEntry;
+ BYTE ElemPriority;
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('l', stdout);
+ fprintf(stdout, "display_preclose(0x%x, 0x%x)\n", handle, device);
+#endif
+ img = image_find(handle, device);
+ if (img) {
+ if (img->session_id) {
+ /* Close gspmdrv driver */
+ DosStopSession(STOP_SESSION_SPECIFIED, img->session_id);
+ Request.pid = img->pid;
+ Request.ulData = 0;
+ /* wait for termination queue, queue is then closed */
+ /* by session manager */
+ DosReadQueue(img->term_queue, &Request, &DataLength,
+ &DataAddress, 0, DCWW_WAIT, &ElemPriority, (HEV) NULL);
+ /* queue needs to be closed by us */
+ DosCloseQueue(img->term_queue);
+ }
+ img->session_id = 0;
+ img->term_queue = 0;
+
+ DosCloseEventSem(img->sync_event);
+ DosCloseMutexSem(img->bmp_mutex);
+ }
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('L', stdout);
+#endif
+ return 0;
+}
+
+int display_close(void *handle, void *device)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('c', stdout);
+ fprintf(stdout, "display_close(0x%x, 0x%x)\n", handle, device);
+#endif
+ img = image_find(handle, device);
+ if (img) {
+ /* gspmdrv was closed by display_preclose */
+ /* release memory */
+ DosFreeMem(img->bitmap);
+ img->bitmap = (unsigned char *)NULL;
+ img->committed = 0;
+ }
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('C', stdout);
+#endif
+ return 0;
+}
+
+int display_presize(void *handle, void *device, int width, int height,
+ int raster, unsigned int format)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('r', stdout);
+ fprintf(stdout, "display_presize(0x%x 0x%x, %d, %d, %d, %d)\n",
+ handle, device, width, height, raster, format);
+#endif
+ img = image_find(handle, device);
+ if (img) {
+ int color = format & DISPLAY_COLORS_MASK;
+ int depth = format & DISPLAY_DEPTH_MASK;
+ int alpha = format & DISPLAY_ALPHA_MASK;
+ img->format_known = FALSE;
+ if ( ((color == DISPLAY_COLORS_NATIVE) ||
+ (color == DISPLAY_COLORS_GRAY))
+ &&
+ ((depth == DISPLAY_DEPTH_1) ||
+ (depth == DISPLAY_DEPTH_4) ||
+ (depth == DISPLAY_DEPTH_8)) )
+ img->format_known = TRUE;
+ if ((color == DISPLAY_COLORS_RGB) && (depth == DISPLAY_DEPTH_8) &&
+ (alpha == DISPLAY_ALPHA_NONE))
+ img->format_known = TRUE;
+ if (!img->format_known) {
+ fprintf(stdout, "display_presize: format %d = 0x%x is unsupported\n", format, format);
+ return gs_error_limitcheck;
+ }
+ /* grab mutex to stop other thread using bitmap */
+ DosRequestMutexSem(img->bmp_mutex, 120000);
+ /* remember parameters so we can figure out where to allocate bitmap */
+ img->width = width;
+ img->height = height;
+ img->raster = raster;
+ img->format = format;
+ }
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('R', stdout);
+#endif
+ return 0;
+}
+
+int display_size(void *handle, void *device, int width, int height,
+ int raster, unsigned int format, unsigned char *pimage)
+{
+ IMAGE *img;
+ PBITMAPINFO2 bmi;
+ int nColors;
+ int i;
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('z', stdout);
+ fprintf(stdout, "display_size(0x%x 0x%x, %d, %d, %d, %d, %d, 0x%x)\n",
+ handle, device, width, height, raster, format, pimage);
+#endif
+ img = image_find(handle, device);
+ if (img) {
+ if (!img->format_known)
+ return gs_error_limitcheck;
+
+ img->width = width;
+ img->height = height;
+ img->raster = raster;
+ img->format = format;
+ /* write BMP header including palette */
+ bmi = (PBITMAPINFO2) img->bitmap;
+ bmi->cbFix = BITMAPINFO2_SIZE;
+ bmi->cx = img->width;
+ bmi->cy = img->height;
+ bmi->cPlanes = 1;
+ bmi->cBitCount = 24;
+ bmi->ulCompression = BCA_UNCOMP;
+ bmi->cbImage = 0;
+ bmi->cxResolution = 0;
+ bmi->cyResolution = 0;
+ bmi->cclrUsed = bmi->cclrImportant = image_palette_size(format);
+
+ switch (img->format & DISPLAY_DEPTH_MASK) {
+ default:
+ case DISPLAY_DEPTH_1:
+ bmi->cBitCount = 1;
+ break;
+ case DISPLAY_DEPTH_4:
+ bmi->cBitCount = 4;
+ break;
+ case DISPLAY_DEPTH_8:
+ if ((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_NATIVE)
+ bmi->cBitCount = 8;
+ else if ((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_GRAY)
+ bmi->cBitCount = 8;
+ else
+ bmi->cBitCount = 24;
+ break;
+ }
+
+ /* add palette if needed */
+ nColors = bmi->cclrUsed;
+ if (nColors) {
+ unsigned char *p;
+ p = img->bitmap + BITMAPINFO2_SIZE;
+ for (i = 0; i < nColors; i++) {
+ image_color(img->format, i, p+2, p+1, p);
+ *(p+3) = 0;
+ p += 4;
+ }
+ }
+
+ /* release mutex to allow other thread to use bitmap */
+ DosReleaseMutexSem(img->bmp_mutex);
+ }
+#ifdef DISPLAY_DEBUG
+ if (debug) {
+ fprintf(stdout, "\nBMP dump\n");
+ fprintf(stdout, " bitmap=%lx\n", img->bitmap);
+ fprintf(stdout, " committed=%lx\n", img->committed);
+ fprintf(stdout, " cx=%d\n", bmi->cx);
+ fprintf(stdout, " cy=%d\n", bmi->cy);
+ fprintf(stdout, " cPlanes=%d\n", bmi->cPlanes);
+ fprintf(stdout, " cBitCount=%d\n", bmi->cBitCount);
+ fprintf(stdout, " ulCompression=%d\n", bmi->ulCompression);
+ fprintf(stdout, " cbImage=%d\n", bmi->cbImage);
+ fprintf(stdout, " cxResolution=%d\n", bmi->cxResolution);
+ fprintf(stdout, " cyResolution=%d\n", bmi->cyResolution);
+ fprintf(stdout, " cclrUsed=%d\n", bmi->cclrUsed);
+ fprintf(stdout, " cclrImportant=%d\n", bmi->cclrImportant);
+ }
+ if (debug)
+ fputc('Z', stdout);
+#endif
+ return 0;
+}
+
+int display_sync(void *handle, void *device)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('s', stdout);
+ fprintf(stdout, "display_sync(0x%x, 0x%x)\n", handle, device);
+#endif
+ img = image_find(handle, device);
+ if (img) {
+ if (!img->format_known)
+ return gs_error_limitcheck;
+ /* delay starting gspmdrv until display_size has been called */
+ if (!img->session_id && (img->width != 0) && (img->height != 0))
+ run_gspmdrv(img);
+ DosPostEventSem(img->sync_event);
+ }
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('S', stdout);
+#endif
+ return 0;
+}
+
+int display_page(void *handle, void *device, int copies, int flush)
+{
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('p', stdout);
+ fprintf(stdout, "display_page(0x%x, 0x%x, copies=%d, flush=%d)\n",
+ handle, device, copies, flush);
+#endif
+ display_sync(handle, device);
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('P', stdout);
+#endif
+ return 0;
+}
+
+void *display_memalloc(void *handle, void *device, unsigned long size)
+{
+ IMAGE *img;
+ unsigned long needed;
+ unsigned long header;
+ APIRET rc;
+ void *mem = NULL;
+
+#ifdef DISPLAY_DEBUG
+ if (debug)
+ fputc('m', stdout);
+ fprintf(stdout, "display_memalloc(0x%x 0x%x %d)\n",
+ handle, device, size);
+#endif
+ img = image_find(handle, device);
+ if (img) {
+ /* we don't actually allocate memory here, we only commit
+ * preallocated shared memory.
+ * First work out size of header + palette.
+ * We allocate space for the header and tell Ghostscript
+ * that the memory starts just after the header.
+ * We rely on the Ghostscript memory device placing the
+ * raster at the start of this memory and having a
+ * raster length the same as the length of a BMP row.
+ */
+ header = BITMAPINFO2_SIZE + image_palette_size(img->format) * 4;
+
+ /* Work out if we need to commit more */
+ needed = (size + header + MIN_COMMIT - 1) & (~(MIN_COMMIT - 1));
+ if (needed > img->committed) {
+ /* commit more memory */
+ if (rc = DosSetMem(img->bitmap + img->committed,
+ needed - img->committed,
+ PAG_COMMIT | PAG_DEFAULT)) {
+ fprintf(stdout, "No memory in display_memalloc rc = %d\n", rc);
+ return NULL;
+ }
+ img->committed = needed;
+ }
+ mem = img->bitmap + header;
+ }
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, " returning 0x%x\n", (int)mem);
+ if (debug)
+ fputc('M', stdout);
+#endif
+ return mem;
+}
+
+int display_memfree(void *handle, void *device, void *mem)
+{
+ /* we can't uncommit shared memory, so do nothing */
+ /* memory will be released when device is closed */
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, "display_memfree(0x%x, 0x%x, 0x%x)\n",
+ handle, device, mem);
+#endif
+}
+
+int display_update(void *handle, void *device,
+ int x, int y, int w, int h)
+{
+ /* unneeded - we are running image window in a separate process */
+ return 0;
+}
+
+display_callback display = {
+ sizeof(display_callback),
+ DISPLAY_VERSION_MAJOR,
+ DISPLAY_VERSION_MINOR,
+ display_open,
+ display_preclose,
+ display_close,
+ display_presize,
+ display_size,
+ display_sync,
+ display_page,
+ display_update,
+ display_memalloc,
+ display_memfree
+};
+
+/*********************************************************************/
+
+int
+main(int argc, char *argv[])
+{
+ int code, code1;
+ int exit_code;
+ int exit_status;
+ int nargc;
+ char **nargv;
+ char dformat[64];
+ ULONG version[3];
+ void *instance;
+
+ if (DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_REVISION,
+ &version, sizeof(version)))
+ os_version = 201000; /* a guess */
+ else
+ os_version = version[0] * 10000 + version[1] * 100 + version[2];
+
+ if (!gs_load_dll()) {
+ fprintf(stdout, "Can't load %s\n", szDllName);
+ return -1;
+ }
+
+ /* insert -dDisplayFormat=XXXXX as first argument */
+ { int format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_1 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ int depth;
+ HPS ps = WinGetPS(HWND_DESKTOP);
+ HDC hdc = GpiQueryDevice(ps);
+ DevQueryCaps(hdc, CAPS_COLOR_PLANES, 1, &display_planes);
+ DevQueryCaps(hdc, CAPS_COLOR_BITCOUNT, 1, &display_bitcount);
+ DevQueryCaps(hdc, CAPS_ADDITIONAL_GRAPHICS, 1, &display_hasPalMan);
+ display_hasPalMan &= CAPS_PALETTE_MANAGER;
+ depth = display_planes * display_bitcount;
+ if ((depth <= 8) && !display_hasPalMan)
+ depth = 24; /* disaster: limited colours and no palette */
+ WinReleasePS(ps);
+
+ if (depth > 8)
+ format = DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ else if (depth >= 8)
+ format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ else if (depth >= 4)
+ format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_4 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ gs_sprintf(dformat, "-dDisplayFormat=%d", format);
+ }
+ nargc = argc + 1;
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stdout, "%s\n", dformat);
+#endif
+ nargc = argc + 1;
+ nargv = (char **)malloc((nargc + 1) * sizeof(char *));
+ nargv[0] = argv[0];
+ nargv[1] = dformat;
+ memcpy(&nargv[2], &argv[1], argc * sizeof(char *));
+
+ if ( (code = gsdll.new_instance(&instance, NULL)) == 0) {
+ gsdll.set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr);
+ gsdll.set_display_callback(instance, &display);
+
+ code = gsdll.init_with_args(instance, nargc, nargv);
+ if (code == 0)
+ code = gsdll.run_string(instance, start_string, 0, &exit_code);
+ code1 = gsdll.exit(instance);
+ if (code == 0 || (code == gs_error_Quit && code1 != 0))
+ code = code1;
+
+ gsdll.delete_instance(instance);
+ }
+
+ gs_free_dll();
+
+ free(nargv);
+
+ exit_status = 0;
+ switch (code) {
+ case 0:
+ case gs_error_Info:
+ case gs_error_Quit:
+ break;
+ case gs_error_Fatal:
+ exit_status = 1;
+ break;
+ default:
+ exit_status = 255;
+ }
+
+ return exit_status;
+}
diff --git a/psi/dscparse.c b/psi/dscparse.c
new file mode 100644
index 000000000..6888466c7
--- /dev/null
+++ b/psi/dscparse.c
@@ -0,0 +1,4483 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/*
+ * This is a DSC parser, based on the DSC 3.0 spec,
+ * with a few DSC 2.1 additions for page size.
+ *
+ * Current limitations:
+ * %%+ may be used after any comment in the comment or trailer,
+ * but is currently only supported by
+ * %%DocumentMedia
+ *
+ * DSC 2.1 additions (discontinued in DSC 3.0):
+ * %%DocumentPaperColors:
+ * %%DocumentPaperForms:
+ * %%DocumentPaperSizes:
+ * %%DocumentPaperWeights:
+ * %%PaperColor: (ignored)
+ * %%PaperForm: (ignored)
+ * %%PaperSize:
+ * %%PaperWeight: (ignored)
+ *
+ * Other additions for defaults or page section
+ % %%ViewingOrientation: xx xy yx yy
+*/
+
+#include <stdio_.h> /* for sprintf(), not file I/O */
+#include <stdlib.h>
+#include <string_.h>
+#include <ctype.h>
+
+#define MAXSTR 256
+
+#include "dscparse.h"
+
+/* Macros for comparing string literals
+ * For maximum speed, the length of the second macro argument is
+ * computed at compile time.
+ * THE SECOND MACRO ARGUMENT MUST BE A STRING LITERAL.
+ */
+#define COMPARE(p,str) (strncmp((const char *)(p), (str), sizeof(str)-1)==0)
+#define IS_DSC(line, str) (COMPARE((line), (str)))
+
+/* Macros for comparing the first one or two characters */
+#define IS_WHITE(ch) (((ch)==' ') || ((ch)=='\t'))
+#define IS_EOL(ch) (((ch)=='\r') || ((ch)=='\n'))
+#define IS_WHITE_OR_EOL(ch) (IS_WHITE(ch) || IS_EOL(ch))
+#define IS_BLANK(str) (IS_EOL(str[0]))
+#define NOT_DSC_LINE(str) (((str)[0]!='%') || ((str)[1]!='%'))
+
+/* Macros for document offset to start and end of line */
+#define DSC_START(dsc) ((dsc)->data_offset + (dsc)->data_index - (dsc)->line_length)
+#define DSC_END(dsc) ((dsc)->data_offset + (dsc)->data_index)
+
+/* dsc_scan_SECTION() functions return one of
+ * CDSC_ERROR, CDSC_OK, CDSC_NOTDSC
+ * or one of the following
+ */
+/* The line should be passed on to the next section parser. */
+#define CDSC_PROPAGATE 10
+
+/* If document is DOS EPS and we haven't read 30 bytes, ask for more. */
+#define CDSC_NEEDMORE 11
+
+/* local prototypes */
+static void * dsc_memalloc(CDSC *dsc, size_t size);
+static void dsc_memfree(CDSC*dsc, void *ptr);
+static CDSC * dsc_init2(CDSC *dsc);
+static void dsc_reset(CDSC *dsc);
+static void dsc_section_join(DSC_OFFSET begin, DSC_OFFSET *pend, DSC_OFFSET **pplast);
+static int dsc_read_line(CDSC *dsc);
+static int dsc_read_doseps(CDSC *dsc);
+static int dsc_read_macbin(CDSC *dsc);
+static int dsc_read_applesingle(CDSC *dsc);
+static char * dsc_alloc_string(CDSC *dsc, const char *str, int len);
+static char * dsc_add_line(CDSC *dsc, const char *line, unsigned int len);
+static char * dsc_copy_string(char *str, unsigned int slen,
+ char *line, unsigned int len, unsigned int *offset);
+static GSDWORD dsc_get_dword(const unsigned char *buf);
+static GSWORD dsc_get_word(const unsigned char *buf);
+static GSDWORD dsc_get_bigendian_dword(const unsigned char *buf);
+static GSWORD dsc_get_bigendian_word(const unsigned char *buf);
+static int dsc_get_int(const char *line, unsigned int len, unsigned int *offset);
+static float dsc_get_real(const char *line, unsigned int len,
+ unsigned int *offset);
+static void dsc_unknown(CDSC *dsc);
+static GSBOOL dsc_is_section(char *line);
+static int dsc_parse_pages(CDSC *dsc);
+static int dsc_parse_bounding_box(CDSC *dsc, CDSCBBOX** pbbox, int offset);
+static int dsc_parse_float_bounding_box(CDSC *dsc, CDSCFBBOX** pfbbox, int offset);
+static int dsc_parse_orientation(CDSC *dsc, unsigned int *porientation,
+ int offset);
+static int dsc_parse_order(CDSC *dsc);
+static int dsc_parse_media(CDSC *dsc, const CDSCMEDIA **page_media);
+static int dsc_parse_document_media(CDSC *dsc);
+static int dsc_parse_viewing_orientation(CDSC *dsc, CDSCCTM **pctm);
+static int dsc_parse_page(CDSC *dsc);
+static void dsc_save_line(CDSC *dsc);
+static int dsc_scan_type(CDSC *dsc);
+static int dsc_scan_comments(CDSC *dsc);
+static int dsc_scan_preview(CDSC *dsc);
+static int dsc_scan_defaults(CDSC *dsc);
+static int dsc_scan_prolog(CDSC *dsc);
+static int dsc_scan_setup(CDSC *dsc);
+static int dsc_scan_page(CDSC *dsc);
+static int dsc_scan_trailer(CDSC *dsc);
+static int dsc_error(CDSC *dsc, unsigned int explanation,
+ char *line, unsigned int line_len);
+static int dsc_dcs2_fixup(CDSC *dsc);
+static int dsc_parse_platefile(CDSC *dsc);
+static int dsc_parse_dcs1plate(CDSC *dsc);
+static CDSCCOLOUR * dsc_find_colour(CDSC *dsc, const char *colourname);
+static int dsc_parse_process_colours(CDSC *dsc);
+static int dsc_parse_custom_colours(CDSC *dsc);
+static int dsc_parse_cmyk_custom_colour(CDSC *dsc);
+static int dsc_parse_rgb_custom_colour(CDSC *dsc);
+
+/* DSC error reporting */
+static const int dsc_severity[] = {
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_BBOX */
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_EARLY_TRAILER */
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_EARLY_EOF */
+ CDSC_ERROR_ERROR, /* CDSC_MESSAGE_PAGE_IN_TRAILER */
+ CDSC_ERROR_ERROR, /* CDSC_MESSAGE_PAGE_ORDINAL */
+ CDSC_ERROR_ERROR, /* CDSC_MESSAGE_PAGES_WRONG */
+ CDSC_ERROR_ERROR, /* CDSC_MESSAGE_EPS_NO_BBOX */
+ CDSC_ERROR_ERROR, /* CDSC_MESSAGE_EPS_PAGES */
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_NO_MEDIA */
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_ATEND */
+ CDSC_ERROR_INFORM, /* CDSC_MESSAGE_DUP_COMMENT */
+ CDSC_ERROR_INFORM, /* CDSC_MESSAGE_DUP_TRAILER */
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_BEGIN_END */
+ CDSC_ERROR_INFORM, /* CDSC_MESSAGE_BAD_SECTION */
+ CDSC_ERROR_INFORM, /* CDSC_MESSAGE_LONG_LINE */
+ CDSC_ERROR_WARN, /* CDSC_MESSAGE_INCORRECT_USAGE */
+ 0
+};
+
+#define DSC_MAX_ERROR ((sizeof(dsc_severity) / sizeof(int))-2)
+
+const CDSCMEDIA dsc_known_media[CDSC_KNOWN_MEDIA] = {
+ /* These sizes taken from Ghostscript gs_statd.ps */
+ {"11x17", 792, 1224, 0, NULL, NULL},
+ {"A3", 842, 1190, 0, NULL, NULL},
+ {"A4", 595, 842, 0, NULL, NULL},
+ {"A5", 421, 595, 0, NULL, NULL},
+ {"B4", 709, 1002, 0, NULL, NULL}, /* ISO, but not Adobe standard */
+ {"B5", 501, 709, 0, NULL, NULL}, /* ISO, but not Adobe standard */
+ {"Ledger", 1224, 792, 0, NULL, NULL},
+ {"Legal", 612, 1008, 0, NULL, NULL},
+ {"Letter", 612, 792, 0, NULL, NULL},
+ {"Note", 612, 792, 0, NULL, NULL},
+ {NULL, 0, 0, 0, NULL, NULL}
+};
+
+/* parser state */
+enum CDSC_SCAN_SECTION {
+ scan_none = 0,
+ scan_comments = 1,
+ scan_pre_preview = 2,
+ scan_preview = 3,
+ scan_pre_defaults = 4,
+ scan_defaults = 5,
+ scan_pre_prolog = 6,
+ scan_prolog = 7,
+ scan_pre_setup = 8,
+ scan_setup = 9,
+ scan_pre_pages = 10,
+ scan_pages = 11,
+ scan_pre_trailer = 12,
+ scan_trailer = 13,
+ scan_eof = 14
+};
+
+static const char * const dsc_scan_section_name[15] = {
+ "Type", "Comments",
+ "pre-Preview", "Preview",
+ "pre-Defaults", "Defaults",
+ "pre-Prolog", "Prolog",
+ "pre-Setup", "Setup",
+ "pre-Page", "Page",
+ "pre-Trailer", "Trailer",
+ "EOF"
+};
+
+/******************************************************************/
+/* Public functions */
+/******************************************************************/
+
+/* constructor */
+CDSC *
+dsc_init(void *caller_data)
+{
+ CDSC *dsc = (CDSC *)malloc(sizeof(CDSC));
+ if (dsc == NULL)
+ return NULL;
+ memset(dsc, 0, sizeof(CDSC));
+ dsc->caller_data = caller_data;
+ dsc->ref_count = 0;
+ dsc_ref(dsc);
+
+ return dsc_init2(dsc);
+}
+
+/* constructor, with caller supplied memalloc */
+CDSC *
+dsc_init_with_alloc(
+ void *caller_data,
+ void *(*memalloc)(size_t size, void *closure_data),
+ void (*memfree)(void *ptr, void *closure_data),
+ void *closure_data)
+{
+ CDSC *dsc = (CDSC *)memalloc(sizeof(CDSC), closure_data);
+ if (dsc == NULL)
+ return NULL;
+ memset(dsc, 0, sizeof(CDSC));
+ dsc->caller_data = caller_data;
+
+ dsc->memalloc = memalloc;
+ dsc->memfree = memfree;
+ dsc->mem_closure_data = closure_data;
+ dsc->ref_count = 0;
+ dsc_ref(dsc);
+
+ return dsc_init2(dsc);
+}
+
+/* destructor */
+void
+dsc_free(CDSC *dsc)
+{
+ if (dsc == NULL)
+ return;
+ dsc_reset(dsc);
+ dsc_memfree(dsc, dsc);
+}
+
+CDSC *
+dsc_new(void *caller_data)
+{
+ return dsc_init(caller_data);
+}
+
+int
+dsc_ref(CDSC *dsc)
+{
+ return ++(dsc->ref_count);
+}
+
+int
+dsc_unref(CDSC *dsc)
+{
+ if (dsc->ref_count <= 0)
+ return -1;
+ dsc->ref_count--;
+ if (dsc->ref_count == 0) {
+ dsc_free(dsc);
+ return 0;
+ }
+ return dsc->ref_count;
+}
+
+/* Tell DSC parser how long document will be, to allow ignoring
+ * of early %%Trailer and %%EOF. This is optional.
+ */
+void
+dsc_set_length(CDSC *dsc, DSC_OFFSET len)
+{
+ dsc->file_length = len;
+}
+
+/* Process a buffer containing DSC comments and PostScript */
+/* Return value is < 0 for error, >=0 for OK.
+ * CDSC_ERROR
+ * CDSC_OK
+ * CDSC_NOTDSC (DSC will be ignored)
+ * other values indicate the last DSC comment read
+ */
+int
+dsc_scan_data(CDSC *dsc, const char *data, int length)
+{
+ int bytes_read;
+ int code = 0;
+
+ if (dsc == NULL)
+ return CDSC_ERROR;
+
+ if (dsc->id == CDSC_NOTDSC)
+ return CDSC_NOTDSC;
+ dsc->id = CDSC_OK;
+ if (dsc->eof)
+ return CDSC_OK; /* ignore */
+
+ if (length == 0) {
+ /* EOF, so process what remains */
+ dsc->eof = TRUE;
+ }
+
+ do {
+ if (dsc->id == CDSC_NOTDSC)
+ break;
+
+ if (length != 0) {
+ /* move existing data if needed */
+ if (dsc->data_length > CDSC_DATA_LENGTH/2) {
+ memmove(dsc->data, dsc->data + dsc->data_index,
+ dsc->data_length - dsc->data_index);
+ dsc->data_offset += dsc->data_index;
+ dsc->data_length -= dsc->data_index;
+ dsc->data_index = 0;
+ }
+ /* append to buffer */
+ bytes_read = min(length, (int)(CDSC_DATA_LENGTH - dsc->data_length));
+ memcpy(dsc->data + dsc->data_length, data, bytes_read);
+ dsc->data_length += bytes_read;
+ data += bytes_read;
+ length -= bytes_read;
+ }
+ if (dsc->scan_section == scan_none) {
+ code = dsc_scan_type(dsc);
+ if (code == CDSC_NEEDMORE) {
+ /* need more characters before we can identify type */
+ code = CDSC_OK;
+ break;
+ }
+ dsc->id = code;
+ }
+
+ if (code == CDSC_NOTDSC) {
+ dsc->id = CDSC_NOTDSC;
+ break;
+ }
+
+ while ((code = dsc_read_line(dsc)) > 0) {
+ if (dsc->id == CDSC_NOTDSC)
+ break;
+ if (dsc->file_length &&
+ (dsc->data_offset + dsc->data_index > dsc->file_length)) {
+ /* have read past end of where we need to parse. */
+ return CDSC_OK; /* ignore */
+ }
+ if (dsc->doseps_end &&
+ (dsc->data_offset + dsc->data_index > dsc->doseps_end)) {
+ /* have read past end of DOS EPS or Mac Binary
+ * PostScript section
+ */
+ return CDSC_OK; /* ignore */
+ }
+ if (dsc->eof)
+ return CDSC_OK;
+ if (dsc->skip_document)
+ continue; /* embedded document */
+ if (dsc->skip_lines)
+ continue; /* embedded lines */
+ if (IS_DSC(dsc->line, "%%BeginData:"))
+ continue;
+ if (IS_DSC(dsc->line, "%%BeginBinary:"))
+ continue;
+ if (IS_DSC(dsc->line, "%%EndDocument"))
+ continue;
+ if (IS_DSC(dsc->line, "%%EndData"))
+ continue;
+ if (IS_DSC(dsc->line, "%%EndBinary"))
+ continue;
+
+ do {
+ switch (dsc->scan_section) {
+ case scan_comments:
+ code = dsc_scan_comments(dsc);
+ break;
+ case scan_pre_preview:
+ case scan_preview:
+ code = dsc_scan_preview(dsc);
+ break;
+ case scan_pre_defaults:
+ case scan_defaults:
+ code = dsc_scan_defaults(dsc);
+ break;
+ case scan_pre_prolog:
+ case scan_prolog:
+ code = dsc_scan_prolog(dsc);
+ break;
+ case scan_pre_setup:
+ case scan_setup:
+ code = dsc_scan_setup(dsc);
+ break;
+ case scan_pre_pages:
+ case scan_pages:
+ code = dsc_scan_page(dsc);
+ break;
+ case scan_pre_trailer:
+ case scan_trailer:
+ code = dsc_scan_trailer(dsc);
+ break;
+ case scan_eof:
+ code = CDSC_OK;
+ break;
+ default:
+ /* invalid state */
+ code = CDSC_ERROR;
+ }
+ /* repeat if line is start of next section */
+ } while (code == CDSC_PROPAGATE);
+
+ /* if DOS EPS header not complete, ask for more */
+ if (code == CDSC_NEEDMORE) {
+ code = CDSC_OK;
+ break;
+ }
+ if (code == CDSC_NOTDSC) {
+ dsc->id = CDSC_NOTDSC;
+ break;
+ }
+ }
+ } while (length != 0);
+
+ return (code < 0) ? code : dsc->id;
+}
+
+/* Tidy up from incorrect DSC comments */
+int
+dsc_fixup(CDSC *dsc)
+{
+ unsigned int i;
+ char buf[32];
+ DSC_OFFSET *last;
+
+ if (dsc->id == CDSC_NOTDSC)
+ return 0;
+
+ /* flush last partial line */
+ dsc_scan_data(dsc, NULL, 0);
+
+ /* Fix DSC error: EOF before end of %%BeginData */
+ if (dsc->eof &&
+ (dsc->skip_lines || dsc->skip_bytes || dsc->skip_document)) {
+ switch (dsc->scan_section) {
+ case scan_comments:
+ dsc->endcomments = DSC_END(dsc);
+ break;
+ case scan_preview:
+ dsc->endpreview = DSC_END(dsc);
+ break;
+ case scan_defaults:
+ dsc->enddefaults = DSC_END(dsc);
+ break;
+ case scan_prolog:
+ dsc->endprolog = DSC_END(dsc);
+ break;
+ case scan_setup:
+ dsc->endsetup = DSC_END(dsc);
+ break;
+ case scan_pages:
+ if (dsc->page_count)
+ dsc->page[dsc->page_count-1].end = DSC_END(dsc);
+ break;
+ case scan_trailer:
+ case scan_eof:
+ dsc->endtrailer = DSC_END(dsc);
+ break;
+ }
+ }
+
+ /* Fix DSC error: code between %%EndSetup and %%Page */
+ if (dsc->page_count && (dsc->page[0].begin != dsc->endsetup)
+ && (dsc->endsetup != dsc->beginsetup)) {
+ dsc->endsetup = dsc->page[0].begin;
+ dsc_debug_print(dsc, "Warning: code included between setup and first page\n");
+ }
+
+ /* Last page contained a false trailer, */
+ /* so extend last page to start of trailer */
+ if (dsc->page_count && (dsc->begintrailer != 0) &&
+ (dsc->page[dsc->page_count-1].end != dsc->begintrailer)) {
+ dsc_debug_print(dsc, "Ignoring earlier misplaced trailer\n");
+ dsc_debug_print(dsc, "and extending last page to start of trailer\n");
+ dsc->page[dsc->page_count-1].end = dsc->begintrailer;
+ }
+
+ /*
+ * Join up all sections.
+ * There might be extra code between them, or we might have
+ * missed including the \n which followed \r.
+ */
+ last = &dsc->endcomments;
+ dsc_section_join(dsc->beginpreview, &dsc->endpreview, &last);
+ dsc_section_join(dsc->begindefaults, &dsc->enddefaults, &last);
+ dsc_section_join(dsc->beginprolog, &dsc->endprolog, &last);
+ dsc_section_join(dsc->beginsetup, &dsc->endsetup, &last);
+ for (i=0; i<dsc->page_count; i++)
+ dsc_section_join(dsc->page[i].begin, &dsc->page[i].end, &last);
+ if (dsc->begintrailer)
+ *last = dsc->begintrailer;
+
+ if ((dsc->page_pages == 0) && (dsc->page_count == 1)) {
+ /* don't flag an error if %%Pages absent but one %%Page found */
+ /* adjust incorrect page count */
+ dsc->page_pages = dsc->page_count;
+ }
+
+ /* Warnings and Errors that we can now identify */
+ if ((dsc->page_count != dsc->page_pages)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_PAGES_WRONG, NULL, 0);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* adjust incorrect page count */
+ dsc->page_pages = dsc->page_count;
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ break;;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ if (dsc->epsf && (dsc->bbox == (CDSCBBOX *)NULL)) {
+ /* EPS files MUST include a BoundingBox */
+ int rc = dsc_error(dsc, CDSC_MESSAGE_EPS_NO_BBOX, NULL, 0);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* Assume that it is EPS */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* Is NOT an EPS file */
+ dsc->epsf = FALSE;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ if (dsc->epsf && ((dsc->page_count > 1) || (dsc->page_pages > 1))) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_EPS_PAGES, NULL, 0);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* Is an EPS file */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* Is NOT an EPS file */
+ dsc->epsf = FALSE;
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ /* convert single file DSC 2.0 into multiple pages */
+ dsc_dcs2_fixup(dsc);
+
+ if ((dsc->media_count == 1) && (dsc->page_media == NULL)) {
+ /* if one only media was specified, and default page media */
+ /* was not specified, assume that default is the only media. */
+ dsc->page_media = dsc->media[0];
+ }
+
+ if ((dsc->media_count != 0) && (dsc->page_media == NULL)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_NO_MEDIA, NULL, 0);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* default media is first listed */
+ dsc->page_media = dsc->media[0];
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* No default media */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ /* make sure all pages have a label */
+ for (i=0; i<dsc->page_count; i++) {
+ if (strlen(dsc->page[i].label) == 0) {
+ gs_sprintf(buf, "%d", i+1);
+ if ((dsc->page[i].label = dsc_alloc_string(dsc, buf, (int)strlen(buf)))
+ == (char *)NULL)
+ return CDSC_ERROR; /* no memory */
+ }
+ }
+ return CDSC_OK;
+}
+
+/* Install a function to be used for displaying messages about
+ * DSC errors and warnings, and to request advice from user.
+ * Installing an error function is optional.
+ */
+void
+dsc_set_error_function(CDSC *dsc,
+ int (*fn)(void *caller_data, CDSC *dsc,
+ unsigned int explanation, const char *line, unsigned int line_len))
+{
+ dsc->dsc_error_fn = fn;
+}
+
+/* Install a function for printing debug messages */
+/* This is optional */
+void
+dsc_set_debug_function(CDSC *dsc,
+ void (*debug_fn)(void *caller_data, const char *str))
+{
+ dsc->debug_print_fn = debug_fn;
+}
+
+/* Doesn't need to be public for PostScript documents */
+/* Made public so GSview can add pages when processing PDF files */
+int
+dsc_add_page(CDSC *dsc, int ordinal, char *label)
+{
+ dsc->page[dsc->page_count].ordinal = ordinal;
+ dsc->page[dsc->page_count].label =
+ dsc_alloc_string(dsc, label, (int)strlen(label)+1);
+ dsc->page[dsc->page_count].begin = 0;
+ dsc->page[dsc->page_count].end = 0;
+ dsc->page[dsc->page_count].orientation = CDSC_ORIENT_UNKNOWN;
+ dsc->page[dsc->page_count].media = NULL;
+ dsc->page[dsc->page_count].bbox = NULL;
+ dsc->page[dsc->page_count].viewing_orientation = NULL;
+ dsc->page[dsc->page_count].crop_box = NULL;
+
+ dsc->page_count++;
+ if (dsc->page_count >= dsc->page_chunk_length) {
+ CDSCPAGE *new_page = (CDSCPAGE *)dsc_memalloc(dsc,
+ (CDSC_PAGE_CHUNK+dsc->page_count) * sizeof(CDSCPAGE));
+ if (new_page == NULL)
+ return CDSC_ERROR; /* out of memory */
+ memcpy(new_page, dsc->page,
+ dsc->page_count * sizeof(CDSCPAGE));
+ dsc_memfree(dsc, dsc->page);
+ dsc->page= new_page;
+ dsc->page_chunk_length = CDSC_PAGE_CHUNK+dsc->page_count;
+ }
+ return CDSC_OK;
+}
+
+/* Doesn't need to be public for PostScript documents */
+/* Made public so GSview can store PDF MediaBox */
+int
+dsc_add_media(CDSC *dsc, CDSCMEDIA *media)
+{
+ CDSCMEDIA **newmedia_array;
+ CDSCMEDIA *newmedia;
+
+ /* extend media array */
+ newmedia_array = (CDSCMEDIA **)dsc_memalloc(dsc,
+ (dsc->media_count + 1) * sizeof(CDSCMEDIA *));
+ if (newmedia_array == NULL)
+ return CDSC_ERROR; /* out of memory */
+ if (dsc->media != NULL) {
+ memcpy(newmedia_array, dsc->media,
+ dsc->media_count * sizeof(CDSCMEDIA *));
+ dsc_memfree(dsc, dsc->media);
+ }
+ dsc->media = newmedia_array;
+
+ /* allocate new media */
+ newmedia = dsc->media[dsc->media_count] =
+ (CDSCMEDIA *)dsc_memalloc(dsc, sizeof(CDSCMEDIA));
+ if (newmedia == NULL)
+ return CDSC_ERROR; /* out of memory */
+ newmedia->name = NULL;
+ newmedia->width = 595.0;
+ newmedia->height = 842.0;
+ newmedia->weight = 80.0;
+ newmedia->colour = NULL;
+ newmedia->type = NULL;
+ newmedia->mediabox = NULL;
+
+ dsc->media_count++;
+
+ if (media->name) {
+ newmedia->name = dsc_alloc_string(dsc, media->name,
+ (int)strlen(media->name));
+ if (newmedia->name == NULL)
+ return CDSC_ERROR; /* no memory */
+ }
+ newmedia->width = media->width;
+ newmedia->height = media->height;
+ newmedia->weight = media->weight;
+ if (media->colour) {
+ newmedia->colour = dsc_alloc_string(dsc, media->colour,
+ (int)strlen(media->colour));
+ if (newmedia->colour == NULL)
+ return CDSC_ERROR; /* no memory */
+ }
+ if (media->type) {
+ newmedia->type = dsc_alloc_string(dsc, media->type,
+ (int)strlen(media->type));
+ if (newmedia->type == NULL)
+ return CDSC_ERROR; /* no memory */
+ }
+ newmedia->mediabox = NULL;
+
+ if (media->mediabox) {
+ newmedia->mediabox = (CDSCBBOX *)dsc_memalloc(dsc, sizeof(CDSCBBOX));
+ if (newmedia->mediabox == NULL)
+ return CDSC_ERROR; /* no memory */
+ *newmedia->mediabox = *media->mediabox;
+ }
+ return CDSC_OK;
+}
+
+/* Doesn't need to be public for PostScript documents */
+/* Made public so GSview can store PDF CropBox */
+int
+dsc_set_page_bbox(CDSC *dsc, unsigned int page_number,
+ int llx, int lly, int urx, int ury)
+{
+ CDSCBBOX *bbox;
+ if (page_number >= dsc->page_count)
+ return CDSC_ERROR;
+ bbox = dsc->page[page_number].bbox;
+ if (bbox == NULL)
+ dsc->page[page_number].bbox = bbox =
+ (CDSCBBOX *)dsc_memalloc(dsc, sizeof(CDSCBBOX));
+ if (bbox == NULL)
+ return CDSC_ERROR;
+ bbox->llx = llx;
+ bbox->lly = lly;
+ bbox->urx = urx;
+ bbox->ury = ury;
+ return CDSC_OK;
+}
+
+/******************************************************************/
+/* Private functions below here. */
+/******************************************************************/
+
+static void *
+dsc_memalloc(CDSC *dsc, size_t size)
+{
+ if (dsc->memalloc)
+ return dsc->memalloc(size, dsc->mem_closure_data);
+ return malloc(size);
+}
+
+static void
+dsc_memfree(CDSC*dsc, void *ptr)
+{
+ if (dsc->memfree)
+ dsc->memfree(ptr, dsc->mem_closure_data);
+ else
+ free(ptr);
+}
+
+/* private constructor */
+static CDSC *
+dsc_init2(CDSC *dsc)
+{
+ dsc_reset(dsc);
+
+ dsc->string_head = (CDSCSTRING *)dsc_memalloc(dsc, sizeof(CDSCSTRING));
+ if (dsc->string_head == NULL) {
+ dsc_free(dsc);
+ return NULL; /* no memory */
+ }
+ dsc->string = dsc->string_head;
+ dsc->string->next = NULL;
+ dsc->string->data = (char *)dsc_memalloc(dsc, CDSC_STRING_CHUNK);
+ if (dsc->string->data == NULL) {
+ dsc_free(dsc);
+ return NULL; /* no memory */
+ }
+ dsc->string->index = 0;
+ dsc->string->length = CDSC_STRING_CHUNK;
+
+ dsc->page = (CDSCPAGE *)dsc_memalloc(dsc, CDSC_PAGE_CHUNK * sizeof(CDSCPAGE));
+ if (dsc->page == NULL) {
+ dsc_free(dsc);
+ return NULL; /* no memory */
+ }
+ dsc->page_chunk_length = CDSC_PAGE_CHUNK;
+ dsc->page_count = 0;
+
+ dsc->line = NULL;
+ dsc->data_length = 0;
+ dsc->data_index = dsc->data_length;
+
+ return dsc;
+}
+
+static void
+dsc_reset(CDSC *dsc)
+{
+ unsigned int i;
+ /* Clear public members */
+ dsc->dsc = FALSE;
+ dsc->ctrld = FALSE;
+ dsc->pjl = FALSE;
+ dsc->epsf = FALSE;
+ dsc->pdf = FALSE;
+ dsc->epsf = FALSE;
+ dsc->preview = CDSC_NOPREVIEW;
+ dsc->dsc_version = NULL; /* stored in dsc->string */
+ dsc->language_level = 0;
+ dsc->document_data = CDSC_DATA_UNKNOWN;
+ dsc->begincomments = 0;
+ dsc->endcomments = 0;
+ dsc->beginpreview = 0;
+ dsc->endpreview = 0;
+ dsc->begindefaults = 0;
+ dsc->enddefaults = 0;
+ dsc->beginprolog = 0;
+ dsc->endprolog = 0;
+ dsc->beginsetup = 0;
+ dsc->endsetup = 0;
+ dsc->begintrailer = 0;
+ dsc->endtrailer = 0;
+
+ for (i=0; i<dsc->page_count; i++) {
+ /* page media is pointer to an element of media or dsc_known_media */
+ /* do not free it. */
+
+ if (dsc->page[i].bbox)
+ dsc_memfree(dsc, dsc->page[i].bbox);
+ if (dsc->page[i].viewing_orientation)
+ dsc_memfree(dsc, dsc->page[i].viewing_orientation);
+ if (dsc->page[i].crop_box)
+ dsc_memfree(dsc, dsc->page[i].crop_box);
+ }
+ if (dsc->page)
+ dsc_memfree(dsc, dsc->page);
+ dsc->page = NULL;
+
+ dsc->page_count = 0;
+ dsc->page_pages = 0;
+ dsc->page_order = CDSC_ORDER_UNKNOWN;
+ dsc->page_orientation = CDSC_ORIENT_UNKNOWN;
+ if (dsc->viewing_orientation)
+ dsc_memfree(dsc, dsc->viewing_orientation);
+ dsc->viewing_orientation = NULL;
+
+ if (dsc->media) {
+ for (i=0; i<dsc->media_count; i++) {
+ if (dsc->media[i]) {
+ if (dsc->media[i]->mediabox)
+ dsc_memfree(dsc, dsc->media[i]->mediabox);
+ dsc_memfree(dsc, dsc->media[i]);
+ }
+ }
+ dsc_memfree(dsc, dsc->media);
+ }
+ dsc->media_count = 0;
+ dsc->media = NULL;
+
+ /* page_media is pointer to an element of media or dsc_known_media */
+ /* do not free it. */
+ dsc->page_media = NULL;
+
+ if (dsc->bbox)
+ dsc_memfree(dsc, dsc->bbox);
+ dsc->bbox = NULL;
+ if (dsc->page_bbox)
+ dsc_memfree(dsc, dsc->page_bbox);
+ dsc->page_bbox = NULL;
+ if (dsc->doseps)
+ dsc_memfree(dsc, dsc->doseps);
+ dsc->doseps = NULL;
+
+ dsc->dsc_title = NULL;
+ dsc->dsc_creator = NULL;
+ dsc->dsc_date = NULL;
+ dsc->dsc_for = NULL;
+
+ dsc->max_error = DSC_MAX_ERROR;
+ dsc->severity = dsc_severity;
+
+ /* Clear private members */
+ /* Don't touch dsc->caller_data */
+ dsc->id = CDSC_OK;
+ dsc->scan_section = scan_none;
+ dsc->doseps_end = 0;
+ dsc->page_chunk_length = 0;
+ dsc->file_length = 0;
+ dsc->skip_document = 0;
+ dsc->skip_bytes = 0;
+ dsc->skip_lines = 0;
+ dsc->skip_pjl = 0;
+ dsc->begin_font_count = 0;
+ dsc->begin_feature_count = 0;
+ dsc->begin_resource_count = 0;
+ dsc->begin_procset_count = 0;
+
+ dsc->data_length = 0;
+ dsc->data_index = 0;
+ dsc->data_offset = 0;
+
+ dsc->eof = 0;
+
+ dsc->line = 0;
+ dsc->line_length = 0;
+ dsc->eol = 0;
+ dsc->last_cr = FALSE;
+ dsc->line_count = 1;
+ dsc->long_line = FALSE;
+ memset(dsc->last_line, 0, sizeof(dsc->last_line));
+
+ dsc->string = dsc->string_head;
+ while (dsc->string != (CDSCSTRING *)NULL) {
+ if (dsc->string->data)
+ dsc_memfree(dsc, dsc->string->data);
+ dsc->string_head = dsc->string;
+ dsc->string = dsc->string->next;
+ dsc_memfree(dsc, dsc->string_head);
+ }
+ dsc->string_head = NULL;
+ dsc->string = NULL;
+
+ /* don't touch caller functions */
+
+ /* public data */
+ if (dsc->hires_bbox)
+ dsc_memfree(dsc, dsc->hires_bbox);
+ dsc->hires_bbox = NULL;
+ if (dsc->crop_box)
+ dsc_memfree(dsc, dsc->crop_box);
+ dsc->crop_box = NULL;
+
+ if (dsc->dcs2) {
+ CDCS2 *this_dcs, *next_dcs;
+ this_dcs = dsc->dcs2;
+ while (this_dcs) {
+ next_dcs = this_dcs->next;
+ /* strings have already been freed */
+ dsc_memfree(dsc, this_dcs);
+ this_dcs = next_dcs;
+ }
+ dsc->dcs2 = NULL;
+ }
+ if (dsc->colours) {
+ CDSCCOLOUR *this_colour, *next_colour;
+ this_colour = dsc->colours;
+ while (this_colour) {
+ next_colour = this_colour->next;
+ /* strings have already been freed */
+ dsc_memfree(dsc, this_colour);
+ this_colour = next_colour;
+ }
+ dsc->colours = NULL;
+ }
+
+ if (dsc->macbin)
+ dsc_memfree(dsc, dsc->macbin);
+ dsc->macbin = NULL;
+}
+
+/*
+* Join up all sections.
+* There might be extra code between them, or we might have
+* missed including the \n which followed \r.
+* begin is the start of this section
+* pend is a pointer to the end of this section
+* pplast is a pointer to a pointer of the end of the previous section
+*/
+static void
+dsc_section_join(DSC_OFFSET begin, DSC_OFFSET *pend, DSC_OFFSET **pplast)
+{
+ if (begin)
+ **pplast = begin;
+ if (*pend > begin)
+ *pplast = pend;
+}
+
+/* return value is 0 if no line available, or length of line */
+static int
+dsc_read_line(CDSC *dsc)
+{
+ char *p, *last;
+ dsc->line = NULL;
+
+ if (dsc->eof) {
+ /* return all that remains, even if line incomplete */
+ dsc->line = dsc->data + dsc->data_index;
+ dsc->line_length = dsc->data_length - dsc->data_index;
+ dsc->data_index = dsc->data_length;
+ return dsc->line_length;
+ }
+
+ if (dsc->file_length &&
+ (dsc->data_offset + dsc->data_index >= dsc->file_length)) {
+ /* Have read past where we need to parse. */
+ /* Ignore all that remains. */
+ dsc->line = dsc->data + dsc->data_index;
+ dsc->line_length = dsc->data_length - dsc->data_index;
+ dsc->data_index = dsc->data_length;
+ return dsc->line_length;
+
+ }
+ if (dsc->doseps_end &&
+ (dsc->data_offset + dsc->data_index >= dsc->doseps_end)) {
+ /* Have read past end of DOS EPS PostScript section. */
+ /* Ignore all that remains. */
+ dsc->line = dsc->data + dsc->data_index;
+ dsc->line_length = dsc->data_length - dsc->data_index;
+ dsc->data_index = dsc->data_length;
+ return dsc->line_length;
+ }
+
+ /* ignore embedded bytes */
+ if (dsc->skip_bytes) {
+ int cnt = min(dsc->skip_bytes,
+ (int)(dsc->data_length - dsc->data_index));
+ dsc->skip_bytes -= cnt;
+ dsc->data_index += cnt;
+ if (dsc->skip_bytes != 0)
+ return 0;
+ }
+
+ do {
+ dsc->line = dsc->data + dsc->data_index;
+ last = dsc->data + dsc->data_length;
+ if (dsc->data_index == dsc->data_length) {
+ dsc->line_length = 0;
+ return 0;
+ }
+ if (dsc->eol) {
+ /* if previous line was complete, increment line count */
+ dsc->line_count++;
+ if (dsc->skip_lines)
+ dsc->skip_lines--;
+ }
+
+ /* skip over \n which followed \r */
+ if (dsc->last_cr && dsc->line[0] == '\n') {
+ dsc->data_index++;
+ dsc->line++;
+ }
+ dsc->last_cr = FALSE;
+
+ /* look for EOL */
+ dsc->eol = FALSE;
+ for (p = dsc->line; p < last; p++) {
+ if (*p == '\r') {
+ p++;
+ if ((p<last) && (*p == '\n'))
+ p++; /* include line feed also */
+ else
+ dsc->last_cr = TRUE; /* we might need to skip \n */
+ dsc->eol = TRUE; /* dsc->line is a complete line */
+ break;
+ }
+ if (*p == '\n') {
+ p++;
+ dsc->eol = TRUE; /* dsc->line is a complete line */
+ break;
+ }
+ if (*p == '\032') { /* MS-DOS Ctrl+Z */
+ dsc->eol = TRUE;
+ }
+ }
+ if (dsc->eol == FALSE) {
+ /* we haven't got a complete line yet */
+ if (dsc->data_length - dsc->data_index < sizeof(dsc->data)/2) {
+ /* buffer is less than half full, ask for some more */
+ dsc->line_length = 0;
+ return 0;
+ }
+ }
+ dsc->data_index += dsc->line_length = (int)(p - dsc->line);
+ } while (dsc->skip_lines && dsc->line_length);
+
+ if (dsc->line_length == 0)
+ return 0;
+
+ if ((dsc->line[0]=='%') && (dsc->line[1]=='%')) {
+ /* handle recursive %%BeginDocument */
+ if ((dsc->skip_document) && dsc->line_length &&
+ COMPARE(dsc->line, "%%EndDocument")) {
+ dsc->skip_document--;
+ }
+
+ /* handle embedded lines or binary data */
+ if (COMPARE(dsc->line, "%%BeginData:")) {
+ /* %%BeginData: <numberof>[ <type> [ <bytesorlines> ] ]
+ * <numberof> ::= <uint> (Lines or physical bytes)
+ * <type> ::= Hex | Binary | ASCII (Type of data)
+ * <bytesorlines> ::= Bytes | Lines (Read in bytes or lines)
+ */
+ char begindata[MAXSTR+1], *bdatalast = NULL;
+ int cnt;
+ const char *numberof, *bytesorlines;
+ cnt = dsc->line_length;
+ if (dsc->line_length > sizeof(begindata)-1)
+ cnt = sizeof(begindata)-1;
+ memcpy(begindata, dsc->line, cnt);
+ begindata[cnt] = '\0';
+ numberof = gs_strtok(begindata+12, " \r\n", &bdatalast);
+ gs_strtok(NULL, " \r\n", &bdatalast); /* dump type */
+ bytesorlines = gs_strtok(NULL, " \r\n", &bdatalast);
+ if (bytesorlines == NULL)
+ bytesorlines = "Bytes";
+
+ if ( (numberof == NULL) || (bytesorlines == NULL) ) {
+ /* invalid usage of %%BeginData */
+ /* ignore that we ever saw it */
+ int rc = dsc_error(dsc, CDSC_MESSAGE_INCORRECT_USAGE,
+ dsc->line, dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return 0;
+ }
+ }
+ else {
+ cnt = atoi(numberof);
+ if (cnt) {
+ if (bytesorlines && (dsc_stricmp(bytesorlines, "Lines")==0)) {
+ /* skip cnt lines */
+ if (dsc->skip_lines == 0) {
+ /* we are not already skipping lines */
+ dsc->skip_lines = cnt+1;
+ }
+ }
+ else {
+ /* byte count doesn't includes \n or \r\n */
+ /* or \r of %%BeginData: */
+ /* skip cnt bytes */
+ if (dsc->skip_bytes == 0) {
+ /* we are not already skipping lines */
+ dsc->skip_bytes = cnt;
+ }
+
+ }
+ }
+ }
+ }
+ else if (COMPARE(dsc->line, "%%BeginBinary:")) {
+ /* byte count doesn't includes \n or \r\n or \r of %%BeginBinary:*/
+ int cnt = dsc_get_int(dsc->line + 14,
+ dsc->line_length - 14, NULL);
+ if (dsc->skip_bytes == 0) {
+ /* we are not already skipping lines */
+ dsc->skip_bytes = cnt;
+ }
+ }
+ }
+
+ if ((dsc->line[0]=='%') && (dsc->line[1]=='%') &&
+ COMPARE(dsc->line, "%%BeginDocument:") ) {
+ /* Skip over embedded document, recursively */
+ dsc->skip_document++;
+ }
+
+ if (!dsc->long_line && (dsc->line_length > DSC_LINE_LENGTH)) {
+ dsc_error(dsc, CDSC_MESSAGE_LONG_LINE, dsc->line, dsc->line_length);
+ dsc->long_line = TRUE;
+ }
+
+ return dsc->line_length;
+}
+
+/* Save last DSC line, for use with %%+ */
+static void
+dsc_save_line(CDSC *dsc)
+{
+ int len = min(sizeof(dsc->last_line), dsc->line_length);
+ memcpy(dsc->last_line, dsc->line, len);
+}
+
+/* display unknown DSC line */
+static void
+dsc_unknown(CDSC *dsc)
+{
+ if (dsc->debug_print_fn) {
+ char line[DSC_LINE_LENGTH];
+ unsigned int length = min(DSC_LINE_LENGTH-1, dsc->line_length);
+ gs_sprintf(line, "Unknown in %s section at line %d:\n ",
+ dsc_scan_section_name[dsc->scan_section], dsc->line_count);
+ dsc_debug_print(dsc, line);
+ strncpy(line, dsc->line, length);
+ line[length] = '\0';
+ dsc_debug_print(dsc, line);
+ dsc_debug_print(dsc, "\n");
+ }
+}
+
+static GSBOOL
+dsc_is_section(char *line)
+{
+ if ( !((line[0]=='%') && (line[1]=='%')) )
+ return FALSE;
+ if (IS_DSC(line, "%%BeginPreview"))
+ return TRUE;
+ if (IS_DSC(line, "%%BeginDefaults"))
+ return TRUE;
+ if (IS_DSC(line, "%%BeginProlog"))
+ return TRUE;
+ if (IS_DSC(line, "%%BeginSetup"))
+ return TRUE;
+ if (IS_DSC(line, "%%Page:"))
+ return TRUE;
+ if (IS_DSC(line, "%%Trailer"))
+ return TRUE;
+ if (IS_DSC(line, "%%EOF"))
+ return TRUE;
+ return FALSE;
+}
+
+/* Get little-endian DWORD, used for DOS EPS files */
+static GSDWORD
+dsc_get_dword(const unsigned char *buf)
+{
+ GSDWORD dw;
+ dw = (GSDWORD)buf[0];
+ dw += ((GSDWORD)buf[1])<<8;
+ dw += ((GSDWORD)buf[2])<<16;
+ dw += ((GSDWORD)buf[3])<<24;
+ return dw;
+}
+
+static GSWORD
+dsc_get_word(const unsigned char *buf)
+{
+ GSWORD w;
+ w = (GSWORD)buf[0];
+ w |= (GSWORD)(buf[1]<<8);
+ return w;
+}
+
+/* Get big-endian DWORD, used for Mac Binary files */
+static GSDWORD
+dsc_get_bigendian_dword(const unsigned char *buf)
+{
+ GSDWORD dw;
+ dw = (GSDWORD)buf[3];
+ dw += ((GSDWORD)buf[2])<<8;
+ dw += ((GSDWORD)buf[1])<<16;
+ dw += ((GSDWORD)buf[0])<<24;
+ return dw;
+}
+
+static GSWORD
+dsc_get_bigendian_word(const unsigned char *buf)
+{
+ GSWORD w;
+ w = (GSWORD)buf[1];
+ w |= (GSWORD)(buf[0]<<8);
+ return w;
+}
+
+static int
+dsc_read_doseps(CDSC *dsc)
+{
+ unsigned char *line = (unsigned char *)dsc->line;
+ if ((dsc->doseps = (CDSCDOSEPS *)dsc_memalloc(dsc, sizeof(CDSCDOSEPS))) == NULL)
+ return CDSC_ERROR; /* no memory */
+
+ dsc->doseps->ps_begin = dsc_get_dword(line+4);
+ dsc->doseps->ps_length = dsc_get_dword(line+8);
+ dsc->doseps->wmf_begin = dsc_get_dword(line+12);
+ dsc->doseps->wmf_length = dsc_get_dword(line+16);
+ dsc->doseps->tiff_begin = dsc_get_dword(line+20);
+ dsc->doseps->tiff_length = dsc_get_dword(line+24);
+ dsc->doseps->checksum = dsc_get_word(line+28);
+
+ if (dsc->file_length &&
+ (dsc->doseps->ps_begin + dsc->doseps->ps_length > dsc->file_length)) {
+ /* Error in DOS EPS header.
+ * Some files have been seen with a fixed large value as
+ * the length of the PostScript section.
+ * Correct for these erroneous files.
+ */
+ dsc->doseps->ps_length =
+ (GSDWORD)(dsc->file_length - dsc->doseps->ps_begin);
+ }
+
+ dsc->doseps_end = dsc->doseps->ps_begin + dsc->doseps->ps_length;
+
+ /* move data_index backwards to byte after doseps header */
+ dsc->data_index -= dsc->line_length - 30;
+ /* we haven't read a line of PostScript code yet */
+ dsc->line_count = 0;
+ /* skip from current position to start of PostScript section */
+ dsc->skip_bytes = dsc->doseps->ps_begin - 30;
+
+ if (dsc->doseps->tiff_begin)
+ dsc->preview = CDSC_TIFF;
+ if (dsc->doseps->wmf_begin)
+ dsc->preview = CDSC_WMF;
+
+ return CDSC_OK;
+}
+
+static int
+dsc_read_macbin(CDSC *dsc)
+{
+ unsigned char *line = (unsigned char *)dsc->line;
+ if ((dsc->macbin =
+ (CDSCMACBIN *)dsc_memalloc(dsc, sizeof(CDSCMACBIN))) == NULL)
+ return CDSC_ERROR; /* no memory */
+
+ dsc->macbin->data_begin = 128;
+ dsc->macbin->data_length = dsc_get_bigendian_dword(line+83);
+ dsc->macbin->resource_begin =
+ (dsc->macbin->data_begin + dsc->macbin->data_length + 127 ) & ~127;
+ dsc->macbin->resource_length = dsc_get_bigendian_dword(line+87);
+
+ if (dsc->file_length &&
+ (((dsc->macbin->resource_begin + dsc->macbin->resource_length
+ + 127) & ~127) > dsc->file_length)) {
+ return CDSC_ERROR;
+ }
+
+ dsc->doseps_end = dsc->macbin->data_begin + dsc->macbin->data_length;
+
+ /* move data_index to byte after Mac Binary header */
+ dsc->data_index -= dsc->line_length - 128;
+ /* we haven't read a line of PostScript code yet */
+ dsc->line_count = 0;
+
+ dsc->preview = CDSC_PICT;
+
+ return CDSC_OK;
+}
+
+static int
+dsc_read_applesingle(CDSC *dsc)
+{
+ GSDWORD EntryID;
+ GSDWORD Offset;
+ GSDWORD Length;
+ GSWORD entries;
+ int index;
+ int header;
+ int i;
+
+ unsigned char *line = (unsigned char *)dsc->line;
+ if ((dsc->macbin =
+ (CDSCMACBIN *)dsc_memalloc(dsc, sizeof(CDSCMACBIN))) == NULL)
+ return CDSC_ERROR; /* no memory */
+ entries = dsc_get_bigendian_word(line+24);
+ for (i=0; i<(int)entries; i++) {
+ index = 26 + i * 12;
+ EntryID = dsc_get_bigendian_dword(line+index);
+ Offset = dsc_get_bigendian_dword(line+index+4);
+ Length = dsc_get_bigendian_dword(line+index+8);
+ if (EntryID == 1) {
+ /* data fork */
+ dsc->macbin->data_begin = Offset;
+ dsc->macbin->data_length = Length;
+ }
+ else if (EntryID == 2) {
+ /* resource fork */
+ dsc->macbin->resource_begin = Offset;
+ dsc->macbin->resource_length = Length;
+ }
+ }
+
+ if (dsc->file_length &&
+ (dsc->macbin->resource_begin + dsc->macbin->resource_length
+ > dsc->file_length)) {
+ return CDSC_ERROR;
+ }
+ if (dsc->file_length &&
+ (dsc->macbin->data_begin + dsc->macbin->data_length
+ > dsc->file_length)) {
+ return CDSC_ERROR;
+ }
+
+ dsc->doseps_end = dsc->macbin->data_begin + dsc->macbin->data_length;
+
+ header = 26 + entries * 12;
+ /* move data_index to byte after AppleSingle/AppleDouble header */
+ dsc->data_index -= dsc->line_length - header;
+ /* we haven't read a line of PostScript code yet */
+ dsc->line_count = 0;
+ /* skip from current position to start of PostScript section */
+ dsc->skip_bytes = dsc->macbin->data_begin - header;
+
+ dsc->preview = CDSC_PICT;
+
+ return CDSC_OK;
+}
+
+static int
+dsc_parse_pages(CDSC *dsc)
+{
+ int ip, io;
+ unsigned int i;
+ char *p;
+ int n;
+ if ((dsc->page_pages != 0) && (dsc->scan_section == scan_comments)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((dsc->page_pages != 0) && (dsc->scan_section == scan_trailer)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_TRAILER, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ break; /* use duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ n = IS_DSC(dsc->line, "%%+") ? 3 : 8;
+ while (IS_WHITE(dsc->line[n]))
+ n++;
+ p = dsc->line + n;
+ if (COMPARE(p, "atend")) {
+ if (dsc->scan_section != scan_comments)
+ dsc_unknown(dsc);
+ else {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_ATEND,
+ dsc->line, dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* assume (atend) */
+ /* we should mark it as deferred */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore it */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ }
+ else if (COMPARE(p, "(atend)")) {
+ if (dsc->scan_section != scan_comments)
+ dsc_unknown(dsc);
+ /* do nothing */
+ /* we should mark it as deferred */
+ }
+ else {
+ ip = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ n+=i;
+ dsc->page_pages = ip;
+ io = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ /* DSC 2 uses extra integer to indicate page order */
+ /* DSC 3 uses %%PageOrder: */
+ if (dsc->page_order == CDSC_ORDER_UNKNOWN)
+ switch (io) {
+ case -1:
+ dsc->page_order = CDSC_DESCEND;
+ break;
+ case 0:
+ dsc->page_order = CDSC_SPECIAL;
+ break;
+ case 1:
+ dsc->page_order = CDSC_ASCEND;
+ break;
+ }
+ }
+ }
+ else {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_INCORRECT_USAGE, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore it */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ }
+ return CDSC_OK;
+}
+
+static int
+dsc_parse_bounding_box(CDSC *dsc, CDSCBBOX** pbbox, int offset)
+{
+ unsigned int i, n;
+ int llx, lly, urx, ury;
+ float fllx, flly, furx, fury;
+ char *p;
+ /* Process first %%BoundingBox: in comments, and last in trailer */
+ if ((*pbbox != NULL) && (dsc->scan_section == scan_comments)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((*pbbox != NULL) && (dsc->scan_section == scan_pages)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((*pbbox != NULL) && (dsc->scan_section == scan_trailer)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_TRAILER, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ break; /* use duplicate comments in trailer */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if (*pbbox != NULL) {
+ dsc_memfree(dsc, *pbbox);
+ *pbbox = NULL;
+ }
+
+ /* should only process first %%BoundingBox: */
+
+ while (IS_WHITE(dsc->line[offset]))
+ offset++;
+ p = dsc->line + offset;
+
+ if (COMPARE(p, "atend")) {
+ if (dsc->scan_section == scan_trailer)
+ dsc_unknown(dsc);
+ else {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_ATEND, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* assume (atend) */
+ /* we should mark it as deferred */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore it */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ }
+ else if (COMPARE(p, "(atend)")) {
+ if (dsc->scan_section == scan_trailer)
+ dsc_unknown(dsc);
+ /* do nothing */
+ /* we should mark it as deferred */
+ }
+ else {
+ /* llx = */ lly = urx = ury = 0;
+ n = offset;
+ llx = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ lly = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ urx = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ ury = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ *pbbox = (CDSCBBOX *)dsc_memalloc(dsc, sizeof(CDSCBBOX));
+ if (*pbbox == NULL)
+ return CDSC_ERROR; /* no memory */
+ (*pbbox)->llx = llx;
+ (*pbbox)->lly = lly;
+ (*pbbox)->urx = urx;
+ (*pbbox)->ury = ury;
+ }
+ else {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_BBOX, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* fllx = */ flly = furx = fury = 0.0;
+ n = offset;
+ n += i;
+ fllx = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ flly = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ furx = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ fury = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ *pbbox = (CDSCBBOX *)dsc_memalloc(dsc, sizeof(CDSCBBOX));
+ if (*pbbox == NULL)
+ return CDSC_ERROR; /* no memory */
+ (*pbbox)->llx = (int)fllx;
+ (*pbbox)->lly = (int)flly;
+ (*pbbox)->urx = (int)(furx+0.999);
+ (*pbbox)->ury = (int)(fury+0.999);
+ }
+ return CDSC_OK;
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ }
+ return CDSC_OK;
+}
+
+static int
+dsc_parse_float_bounding_box(CDSC *dsc, CDSCFBBOX** pbbox, int offset)
+{
+ unsigned int i, n;
+ float fllx, flly, furx, fury;
+ char *p;
+ /* Process first %%HiResBoundingBox: or %%CropBox: in comments,
+ * and last in trailer.
+ */
+ if ((*pbbox != NULL) && (dsc->scan_section == scan_comments)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((*pbbox != NULL) && (dsc->scan_section == scan_pages)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((*pbbox != NULL) && (dsc->scan_section == scan_trailer)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_TRAILER, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ break; /* use duplicate comments in trailer */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if (*pbbox != NULL) {
+ dsc_memfree(dsc, *pbbox);
+ *pbbox = NULL;
+ }
+
+ /* should only process first %%BoundingBox: */
+
+ while (IS_WHITE(dsc->line[offset]))
+ offset++;
+ p = dsc->line + offset;
+
+ if (COMPARE(p, "atend")) {
+ if (dsc->scan_section == scan_trailer)
+ dsc_unknown(dsc);
+ else {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_ATEND, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* assume (atend) */
+ /* we should mark it as deferred */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore it */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ }
+ else if (COMPARE(p, "(atend)")) {
+ if (dsc->scan_section == scan_trailer)
+ dsc_unknown(dsc);
+ /* do nothing */
+ /* we should mark it as deferred */
+ }
+ else {
+ /* fllx = */ flly = furx = fury = 0.0;
+ n = offset;
+ fllx = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ flly = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ furx = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ fury = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ *pbbox = (CDSCFBBOX *)dsc_memalloc(dsc, sizeof(CDSCFBBOX));
+ if (*pbbox == NULL)
+ return CDSC_ERROR; /* no memory */
+ (*pbbox)->fllx = fllx;
+ (*pbbox)->flly = flly;
+ (*pbbox)->furx = furx;
+ (*pbbox)->fury = fury;
+ }
+ }
+ return CDSC_OK;
+}
+
+static int
+dsc_parse_orientation(CDSC *dsc, unsigned int *porientation, int offset)
+{
+ char *p;
+ if ((dsc->page_orientation != CDSC_ORIENT_UNKNOWN) &&
+ (dsc->scan_section == scan_comments)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((dsc->page_orientation != CDSC_ORIENT_UNKNOWN) &&
+ (dsc->scan_section == scan_trailer)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_TRAILER, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ break; /* use duplicate comments in header; */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ p = dsc->line + offset;
+ while (IS_WHITE(*p))
+ p++;
+ if (COMPARE(p, "atend")) {
+ if (dsc->scan_section == scan_trailer)
+ dsc_unknown(dsc);
+ else {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_ATEND,
+ dsc->line, dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* assume (atend) */
+ /* we should mark it as deferred */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore it */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ }
+ else if (COMPARE(p, "(atend)")) {
+ if (dsc->scan_section == scan_trailer)
+ dsc_unknown(dsc);
+ /* do nothing */
+ /* we should mark it as deferred */
+ }
+ else if (COMPARE(p, "Portrait")) {
+ *porientation = CDSC_PORTRAIT;
+ }
+ else if (COMPARE(p, "Landscape")) {
+ *porientation = CDSC_LANDSCAPE;
+ }
+ else {
+ dsc_unknown(dsc);
+ }
+ return CDSC_OK;
+}
+
+static int
+dsc_parse_order(CDSC *dsc)
+{
+ char *p;
+ if ((dsc->page_order != CDSC_ORDER_UNKNOWN) &&
+ (dsc->scan_section == scan_comments)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_COMMENT, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ return CDSC_OK; /* ignore duplicate comments in header */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ if ((dsc->page_order != CDSC_ORDER_UNKNOWN) &&
+ (dsc->scan_section == scan_trailer)) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_DUP_TRAILER, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ case CDSC_RESPONSE_CANCEL:
+ break; /* use duplicate comments in trailer */
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ p = dsc->line + (IS_DSC(dsc->line, "%%+") ? 3 : 13);
+ while (IS_WHITE(*p))
+ p++;
+ if (COMPARE(p, "atend")) {
+ if (dsc->scan_section == scan_trailer)
+ dsc_unknown(dsc);
+ else {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_ATEND, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* assume (atend) */
+ /* we should mark it as deferred */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore it */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ }
+ else if (COMPARE(p, "(atend)")) {
+ if (dsc->scan_section == scan_trailer)
+ dsc_unknown(dsc);
+ /* do nothing */
+ /* we should mark it as deferred */
+ }
+ else if (COMPARE(p, "Ascend")) {
+ dsc->page_order = CDSC_ASCEND;
+ }
+ else if (COMPARE(p, "Descend")) {
+ dsc->page_order = CDSC_DESCEND;
+ }
+ else if (COMPARE(p, "Special")) {
+ dsc->page_order = CDSC_SPECIAL;
+ }
+ else {
+ dsc_unknown(dsc);
+ }
+ return CDSC_OK;
+}
+
+static int
+dsc_parse_media(CDSC *dsc, const CDSCMEDIA **page_media)
+{
+ char media_name[MAXSTR];
+ int n = IS_DSC(dsc->line, "%%+") ? 3 : 12; /* %%PageMedia: */
+ unsigned int i;
+
+ if (dsc_copy_string(media_name, sizeof(media_name)-1,
+ dsc->line+n, dsc->line_length-n, NULL)) {
+ for (i=0; i<dsc->media_count; i++) {
+ if (dsc->media[i]->name &&
+ (dsc_stricmp(media_name, dsc->media[i]->name) == 0)) {
+ *page_media = dsc->media[i];
+ return CDSC_OK;
+ }
+ }
+ }
+ dsc_unknown(dsc);
+
+ return CDSC_OK;
+}
+
+static int
+dsc_parse_document_media(CDSC *dsc)
+{
+ unsigned int i, n;
+ CDSCMEDIA lmedia;
+ GSBOOL blank_line;
+
+ if (IS_DSC(dsc->line, "%%DocumentMedia:"))
+ n = 16;
+ else if (IS_DSC(dsc->line, "%%+"))
+ n = 3;
+ else
+ return CDSC_ERROR; /* error */
+
+ /* check for blank remainder of line */
+ blank_line = TRUE;
+ for (i=n; i<dsc->line_length; i++) {
+ if (!IS_WHITE_OR_EOL(dsc->line[i])) {
+ blank_line = FALSE;
+ break;
+ }
+ }
+
+ if (!blank_line) {
+ char name[MAXSTR];
+ char colour[MAXSTR];
+ char type[MAXSTR];
+ lmedia.name = lmedia.colour = lmedia.type = (char *)NULL;
+ lmedia.width = lmedia.height = lmedia.weight = 0;
+ lmedia.mediabox = (CDSCBBOX *)NULL;
+ lmedia.name = dsc_copy_string(name, sizeof(name),
+ dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i)
+ lmedia.width = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i)
+ lmedia.height = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i)
+ lmedia.weight = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i)
+ lmedia.colour = dsc_copy_string(colour, sizeof(colour),
+ dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i)
+ lmedia.type = dsc_copy_string(type, sizeof(type),
+ dsc->line+n, dsc->line_length-n, &i);
+
+ if (i==0)
+ dsc_unknown(dsc); /* we didn't get all fields */
+ else {
+ if (dsc_add_media(dsc, &lmedia))
+ return CDSC_ERROR; /* out of memory */
+ }
+ }
+ return CDSC_OK;
+}
+
+/* viewing orientation is believed to be the first four elements of
+ * a CTM matrix
+ */
+static int
+dsc_parse_viewing_orientation(CDSC *dsc, CDSCCTM **pctm)
+{
+ CDSCCTM ctm;
+ unsigned int i, n;
+
+ if (*pctm != NULL) {
+ dsc_memfree(dsc, *pctm);
+ *pctm = NULL;
+ }
+
+ n = IS_DSC(dsc->line, "%%+") ? 3 : 21; /* %%ViewingOrientation: */
+ while (IS_WHITE(dsc->line[n]))
+ n++;
+
+ /* ctm.xx = */ ctm.xy = ctm.yx = ctm.yy = 0.0;
+ ctm.xx = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ ctm.xy = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ ctm.yx = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ ctm.yy = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ if (i==0) {
+ dsc_unknown(dsc); /* we didn't get all fields */
+ }
+ else {
+ *pctm = (CDSCCTM *)dsc_memalloc(dsc, sizeof(CDSCCTM));
+ if (*pctm == NULL)
+ return CDSC_ERROR; /* no memory */
+ **pctm = ctm;
+ }
+ return CDSC_OK;
+}
+
+/* This is called before dsc_read_line(), since we may
+ * need to skip a binary header which contains a new line
+ * character
+ */
+static int
+dsc_scan_type(CDSC *dsc)
+{
+ unsigned char *p;
+ unsigned char *line = (unsigned char *)(dsc->data + dsc->data_index);
+ int length = dsc->data_length - dsc->data_index;
+
+ /* Types that should be known:
+ * DSC
+ * EPSF
+ * PJL + any of above
+ * ^D + any of above
+ * DOS EPS
+ * PDF
+ * non-DSC
+ */
+
+ /* First process any non PostScript headers */
+ /* At this stage we do not have a complete line */
+
+ if (length == 0)
+ return CDSC_NEEDMORE;
+
+ /* If we have already found a DOS EPS header, */
+ /* ignore all until the PostScript section */
+ if (dsc->skip_bytes) {
+ int cnt = min(dsc->skip_bytes,
+ (int)(dsc->data_length - dsc->data_index));
+ dsc->skip_bytes -= cnt;
+ dsc->data_index += cnt;
+ length -= cnt;
+ line += cnt;
+ if (dsc->skip_bytes != 0)
+ return CDSC_NEEDMORE;
+ }
+
+ if (dsc->skip_pjl) {
+ /* skip until first PostScript comment */
+ while (length >= 2) {
+ while (length && !IS_EOL(line[0])) {
+ /* skip until EOL character */
+ line++;
+ dsc->data_index++;
+ length--;
+ }
+ while ((length >= 2) && IS_EOL(line[0]) && IS_EOL(line[1])) {
+ /* skip until EOL followed by non-EOL */
+ line++;
+ dsc->data_index++;
+ length--;
+ }
+ if (length < 2)
+ return CDSC_NEEDMORE;
+
+ if (IS_EOL(line[0]) && line[1]=='%') {
+ line++;
+ dsc->data_index++;
+ length--;
+ dsc->skip_pjl = FALSE;
+ break;
+ }
+ else {
+ line++;
+ dsc->data_index++;
+ length--;
+ }
+ }
+ if (dsc->skip_pjl)
+ return CDSC_NEEDMORE;
+ }
+
+ if (length == 0)
+ return CDSC_NEEDMORE;
+
+ if (line[0] == '\004') {
+ line++;
+ dsc->data_index++;
+ length--;
+ dsc->ctrld = TRUE;
+ }
+
+ if (line[0] == '\033') {
+ /* possibly PJL */
+ if (length < 9)
+ return CDSC_NEEDMORE;
+ if (COMPARE(line, "\033%-12345X")) {
+ dsc->skip_pjl = TRUE; /* skip until first PostScript comment */
+ dsc->pjl = TRUE;
+ dsc->data_index += 9;
+ return dsc_scan_type(dsc);
+ }
+ }
+
+ if ((line[0]==0x0) && (length < 2))
+ return CDSC_NEEDMORE; /* Could be Mac Binary EPSF */
+ if ((line[0]==0x0) && (line[1] >= 1) && (line[1] <= 63) && (length < 128))
+ return CDSC_NEEDMORE; /* Could be Mac Binary EPSF */
+ if ((line[0]==0x0) && (line[1] == 0x5) && (length < 4))
+ return CDSC_NEEDMORE; /* Could be Mac AppleSingle/AppleDouble */
+ if ((line[0]==0xc5) && (length < 4))
+ return CDSC_NEEDMORE; /* Could be DOS EPS */
+
+ if ((line[0]==0xc5) && (line[1]==0xd0) &&
+ (line[2]==0xd3) && (line[3]==0xc6) ) {
+ /* id is "EPSF" with bit 7 set */
+ /* read DOS EPS header, then ignore all bytes until the PS section */
+ if (length < 30)
+ return CDSC_NEEDMORE;
+ dsc->line = (char *)line;
+ if (dsc_read_doseps(dsc))
+ return CDSC_ERROR;
+ }
+ else if ((line[0]==0x0) && (line[1]==0x05) &&
+ (line[2]==0x16) && ((line[3]==0x0) || (line[3] == 0x07))) {
+ /* Mac AppleSingle or AppleDouble */
+ GSDWORD version;
+ GSWORD entries;
+ if (length < 26)
+ return CDSC_NEEDMORE;
+ version = dsc_get_bigendian_dword(line+4);
+ entries = dsc_get_bigendian_word(line+24);
+ if ((version == 0x00010000) || (version == 0x00020000)) {
+ if (length < (int)(26 + entries * 12))
+ return CDSC_NEEDMORE;
+ dsc->line = (char *)line;
+ if (dsc_read_applesingle(dsc))
+ return CDSC_ERROR;
+ }
+ }
+ else if ((line[0]==0x0) &&
+ (line[1] >= 1) && (line[1] <= 63) &&
+ (line[74]==0x0) &&
+ (line[65]=='E') && (line[66]=='P') &&
+ (line[67]=='S') && (line[68]=='F')) {
+ /* Mac Binary EPSF */
+ dsc->line = (char *)line;
+ if (dsc_read_macbin(dsc))
+ return CDSC_ERROR;
+ }
+ else {
+ if (length < 2)
+ return CDSC_NEEDMORE;
+ if ((line[0] == '%') && (line[1] == 'P')) {
+ if (length < 5)
+ return CDSC_NEEDMORE;
+ if (COMPARE(line, "%PDF-")) {
+ dsc->pdf = TRUE;
+ dsc->scan_section = scan_comments;
+ return CDSC_OK;
+ }
+ }
+ }
+
+ /* Finally process PostScript headers */
+
+ if (dsc_read_line(dsc) <= 0)
+ return CDSC_NEEDMORE;
+
+ dsc->dsc_version = dsc_add_line(dsc, dsc->line, dsc->line_length);
+ if (COMPARE(dsc->line, "%!PS-Adobe")) {
+ dsc->dsc = TRUE;
+ dsc->begincomments = DSC_START(dsc);
+ if (dsc->dsc_version == NULL)
+ return CDSC_ERROR; /* no memory */
+ p = (unsigned char *)dsc->line + 14;
+ while (IS_WHITE(*p))
+ p++;
+ if (COMPARE(p, "EPSF-"))
+ dsc->epsf = TRUE;
+ dsc->scan_section = scan_comments;
+ return CDSC_PSADOBE;
+ }
+ if (COMPARE(dsc->line, "%!")) {
+ dsc->scan_section = scan_comments;
+ return CDSC_NOTDSC;
+ }
+
+ dsc->scan_section = scan_comments;
+ return CDSC_NOTDSC; /* unrecognised */
+}
+
+static int
+dsc_scan_comments(CDSC *dsc)
+{
+ /* Comments section ends at */
+ /* %%EndComments */
+ /* another section */
+ /* line that does not start with %% */
+ /* Save a few important lines */
+
+ char *line = dsc->line;
+ GSBOOL continued = FALSE;
+ dsc->id = CDSC_OK;
+ if (IS_DSC(line, "%%EndComments")) {
+ dsc->id = CDSC_ENDCOMMENTS;
+ dsc->endcomments = DSC_END(dsc);
+ dsc->scan_section = scan_pre_preview;
+ return CDSC_OK;
+ }
+ else if (IS_DSC(line, "%%BeginComments")) {
+ /* ignore because we are in this section */
+ dsc->id = CDSC_BEGINCOMMENTS;
+ }
+ else if (dsc_is_section(line)) {
+ dsc->endcomments = DSC_START(dsc);
+ dsc->scan_section = scan_pre_preview;
+ return CDSC_PROPAGATE;
+ }
+ else if (line[0] == '%' && IS_WHITE_OR_EOL(line[1])) {
+ dsc->endcomments = DSC_START(dsc);
+ dsc->scan_section = scan_pre_preview;
+ return CDSC_PROPAGATE;
+ }
+ else if (line[0] != '%') {
+ dsc->id = CDSC_OK;
+ dsc->endcomments = DSC_START(dsc);
+ dsc->scan_section = scan_pre_preview;
+ return CDSC_PROPAGATE;
+ }
+ else if (IS_DSC(line, "%%Begin")) {
+ dsc->endcomments = DSC_START(dsc);
+ dsc->scan_section = scan_pre_preview;
+ return CDSC_PROPAGATE;
+ }
+
+ /* Handle continuation lines.
+ * To simply processing, we assume that continuation lines
+ * will only occur if repeat parameters are allowed and that
+ * a complete set of these parameters appears on each line.
+ * This is more restrictive than the DSC specification, but
+ * is valid for the DSC comments understood by this parser
+ * for all documents that we have seen.
+ */
+ if (IS_DSC(line, "%%+")) {
+ line = dsc->last_line;
+ continued = TRUE;
+ }
+ else
+ dsc_save_line(dsc);
+
+ if (IS_DSC(line, "%%Pages:")) {
+ dsc->id = CDSC_PAGES;
+ if (dsc_parse_pages(dsc) != 0)
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%Creator:")) {
+ unsigned int n = continued ? 3 : 10;
+ dsc->id = CDSC_CREATOR;
+ dsc->dsc_creator = dsc_add_line(dsc, dsc->line + n, dsc->line_length - n);
+ if (dsc->dsc_creator==NULL)
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%CreationDate:")) {
+ unsigned int n = continued ? 3 : 15;
+ dsc->id = CDSC_CREATIONDATE;
+ dsc->dsc_date = dsc_add_line(dsc, dsc->line + n, dsc->line_length - n);
+ if (dsc->dsc_date==NULL)
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%Title:")) {
+ unsigned int n = continued ? 3 : 8;
+ dsc->id = CDSC_TITLE;
+ dsc->dsc_title = dsc_add_line(dsc, dsc->line + n, dsc->line_length - n);
+ if (dsc->dsc_title==NULL)
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%For:")) {
+ unsigned int n = continued ? 3 : 6;
+ dsc->id = CDSC_FOR;
+ dsc->dsc_for = dsc_add_line(dsc, dsc->line + n, dsc->line_length - n);
+ if (dsc->dsc_for==NULL)
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%LanguageLevel:")) {
+ unsigned int n = continued ? 3 : 16;
+ unsigned int i;
+ int ll;
+ dsc->id = CDSC_LANGUAGELEVEL;
+ ll = dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ if ( (ll==1) || (ll==2) || (ll==3) )
+ dsc->language_level = ll;
+ else {
+ dsc_unknown(dsc);
+ }
+ }
+ else
+ dsc_unknown(dsc);
+ }
+ else if (IS_DSC(line, "%%BoundingBox:")) {
+ dsc->id = CDSC_BOUNDINGBOX;
+ if (dsc_parse_bounding_box(dsc, &(dsc->bbox), continued ? 3 : 14))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%HiResBoundingBox:")) {
+ dsc->id = CDSC_HIRESBOUNDINGBOX;
+ if (dsc_parse_float_bounding_box(dsc, &(dsc->hires_bbox),
+ continued ? 3 : 19))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%CropBox:")) {
+ dsc->id = CDSC_CROPBOX;
+ if (dsc_parse_float_bounding_box(dsc, &(dsc->crop_box),
+ continued ? 3 : 10))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%Orientation:")) {
+ dsc->id = CDSC_ORIENTATION;
+ if (dsc_parse_orientation(dsc, &(dsc->page_orientation),
+ continued ? 3 : 14))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%PageOrder:")) {
+ dsc->id = CDSC_PAGEORDER;
+ if (dsc_parse_order(dsc))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%DocumentMedia:")) {
+ dsc->id = CDSC_DOCUMENTMEDIA;
+ if (dsc_parse_document_media(dsc))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%DocumentPaperSizes:")) {
+ /* DSC 2.1 */
+ unsigned int n = continued ? 3 : 21;
+ unsigned int count = 0;
+ unsigned int i = 1;
+ char name[MAXSTR];
+ char *p;
+ dsc->id = CDSC_DOCUMENTPAPERSIZES;
+ while (i && (dsc->line[n]!='\r') && (dsc->line[n]!='\n')) {
+ p = dsc_copy_string(name, sizeof(name)-1,
+ dsc->line+n, dsc->line_length-n, &i);
+ if (i && p) {
+ const CDSCMEDIA *m = dsc_known_media;
+ if (count >= dsc->media_count) {
+ /* set some default values */
+ CDSCMEDIA lmedia;
+ lmedia.name = p;
+ lmedia.width = 595.0;
+ lmedia.height = 842.0;
+ lmedia.weight = 80.0;
+ lmedia.colour = NULL;
+ lmedia.type = NULL;
+ lmedia.mediabox = NULL;
+ if (dsc_add_media(dsc, &lmedia))
+ return CDSC_ERROR;
+ }
+ else
+ dsc->media[count]->name =
+ dsc_alloc_string(dsc, p, (int)strlen(p));
+ /* find in list of known media */
+ while (m && m->name) {
+ if (dsc_stricmp(p, m->name)==0) {
+ dsc->media[count]->width = m->width;
+ dsc->media[count]->height = m->height;
+ break;
+ }
+ m++;
+ }
+ }
+ n+=i;
+ count++;
+ }
+ }
+ else if (IS_DSC(line, "%%DocumentPaperForms:")) {
+ /* DSC 2.1 */
+ unsigned int n = continued ? 3 : 21;
+ unsigned int count = 0;
+ unsigned int i = 1;
+ char type[MAXSTR];
+ char *p;
+ dsc->id = CDSC_DOCUMENTPAPERFORMS;
+ while (i && (dsc->line[n]!='\r') && (dsc->line[n]!='\n')) {
+ p = dsc_copy_string(type, sizeof(type)-1,
+ dsc->line+n, dsc->line_length-n, &i);
+ if (i && p) {
+ if (count >= dsc->media_count) {
+ /* set some default values */
+ CDSCMEDIA lmedia;
+ lmedia.name = NULL;
+ lmedia.width = 595.0;
+ lmedia.height = 842.0;
+ lmedia.weight = 80.0;
+ lmedia.colour = NULL;
+ lmedia.type = p;
+ lmedia.mediabox = NULL;
+ if (dsc_add_media(dsc, &lmedia))
+ return CDSC_ERROR;
+ }
+ else
+ dsc->media[count]->type =
+ dsc_alloc_string(dsc, p, (int)strlen(p));
+ }
+ n+=i;
+ count++;
+ }
+ }
+ else if (IS_DSC(line, "%%DocumentPaperColors:")) {
+ /* DSC 2.1 */
+ unsigned int n = continued ? 3 : 22;
+ unsigned int count = 0;
+ unsigned int i = 1;
+ char colour[MAXSTR];
+ char *p;
+ dsc->id = CDSC_DOCUMENTPAPERCOLORS;
+ while (i && (dsc->line[n]!='\r') && (dsc->line[n]!='\n')) {
+ p = dsc_copy_string(colour, sizeof(colour)-1,
+ dsc->line+n, dsc->line_length-n, &i);
+ if (i && p) {
+ if (count >= dsc->media_count) {
+ /* set some default values */
+ CDSCMEDIA lmedia;
+ lmedia.name = NULL;
+ lmedia.width = 595.0;
+ lmedia.height = 842.0;
+ lmedia.weight = 80.0;
+ lmedia.colour = p;
+ lmedia.type = NULL;
+ lmedia.mediabox = NULL;
+ if (dsc_add_media(dsc, &lmedia))
+ return CDSC_ERROR;
+ }
+ else
+ dsc->media[count]->colour =
+ dsc_alloc_string(dsc, p, (int)strlen(p));
+ }
+ n+=i;
+ count++;
+ }
+ }
+ else if (IS_DSC(line, "%%DocumentPaperWeights:")) {
+ /* DSC 2.1 */
+ unsigned int n = continued ? 3 : 23;
+ unsigned int count = 0;
+ unsigned int i = 1;
+ float w;
+ dsc->id = CDSC_DOCUMENTPAPERWEIGHTS;
+ while (i && (dsc->line[n]!='\r') && (dsc->line[n]!='\n')) {
+ w = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ if (i) {
+ if (count >= dsc->media_count) {
+ /* set some default values */
+ CDSCMEDIA lmedia;
+ lmedia.name = NULL;
+ lmedia.width = 595.0;
+ lmedia.height = 842.0;
+ lmedia.weight = w;
+ lmedia.colour = NULL;
+ lmedia.type = NULL;
+ lmedia.mediabox = NULL;
+ if (dsc_add_media(dsc, &lmedia))
+ return CDSC_ERROR;
+ }
+ else
+ dsc->media[count]->weight = w;
+ }
+ n+=i;
+ count++;
+ }
+ }
+ else if (IS_DSC(line, "%%DocumentData:")) {
+ unsigned int n = continued ? 3 : 15;
+ char *p = dsc->line + n;
+ while (IS_WHITE(*p))
+ p++;
+ dsc->id = CDSC_DOCUMENTDATA;
+ if (COMPARE(p, "Clean7Bit"))
+ dsc->document_data = CDSC_CLEAN7BIT;
+ else if (COMPARE(p, "Clean8Bit"))
+ dsc->document_data = CDSC_CLEAN8BIT;
+ else if (COMPARE(p, "Binary"))
+ dsc->document_data = CDSC_BINARY;
+ else
+ dsc_unknown(dsc);
+ }
+ else if (IS_DSC(line, "%%Requirements:")) {
+ dsc->id = CDSC_REQUIREMENTS;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%DocumentNeededFonts:")) {
+ dsc->id = CDSC_DOCUMENTNEEDEDFONTS;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%DocumentSuppliedFonts:")) {
+ dsc->id = CDSC_DOCUMENTSUPPLIEDFONTS;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PlateFile:")) {
+ dsc->id = CDSC_PLATEFILE;
+ if (dsc_parse_platefile(dsc) != CDSC_OK)
+ dsc->id = CDSC_UNKNOWNDSC;
+ }
+ else if (IS_DSC(line, "%%CyanPlate:") ||
+ IS_DSC(line, "%%MagentaPlate:") ||
+ IS_DSC(line, "%%YellowPlate:") ||
+ IS_DSC(line, "%%BlackPlate:")) {
+ dsc->id = CDSC_PLATEFILE;
+ if (dsc_parse_dcs1plate(dsc) != CDSC_OK)
+ dsc->id = CDSC_UNKNOWNDSC;
+ }
+ else if (IS_DSC(line, "%%DocumentProcessColors:")) {
+ dsc->id = CDSC_DOCUMENTPROCESSCOLORS;
+ if (dsc_parse_process_colours(dsc) != CDSC_OK)
+ dsc->id = CDSC_UNKNOWNDSC;
+ }
+ else if (IS_DSC(line, "%%DocumentCustomColors:")) {
+ dsc->id = CDSC_DOCUMENTCUSTOMCOLORS;
+ if (dsc_parse_custom_colours(dsc) != CDSC_OK)
+ dsc->id = CDSC_UNKNOWNDSC;
+ }
+ else if (IS_DSC(line, "%%CMYKCustomColor:")) {
+ dsc->id = CDSC_CMYKCUSTOMCOLOR;
+ if (dsc_parse_cmyk_custom_colour(dsc) != CDSC_OK)
+ dsc->id = CDSC_UNKNOWNDSC;
+ }
+ else if (IS_DSC(line, "%%RGBCustomColor:")) {
+ dsc->id = CDSC_RGBCUSTOMCOLOR;
+ if (dsc_parse_rgb_custom_colour(dsc) != CDSC_OK)
+ dsc->id = CDSC_UNKNOWNDSC;
+ }
+ else if (dsc->line[0] == '%' && IS_WHITE_OR_EOL(dsc->line[1])) {
+ dsc->id = CDSC_OK;
+ /* ignore */
+ }
+ else {
+ dsc->id = CDSC_UNKNOWNDSC;
+ dsc_unknown(dsc);
+ }
+
+ dsc->endcomments = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+static int
+dsc_scan_preview(CDSC *dsc)
+{
+ /* Preview section ends at */
+ /* %%EndPreview */
+ /* another section */
+ /* Preview section must start with %%BeginPreview */
+ char *line = dsc->line;
+ dsc->id = CDSC_OK;
+
+ if (dsc->scan_section == scan_pre_preview) {
+ if (IS_BLANK(line))
+ return CDSC_OK; /* ignore blank lines before preview */
+ else if (IS_DSC(line, "%%BeginPreview")) {
+ dsc->id = CDSC_BEGINPREVIEW;
+ dsc->beginpreview = DSC_START(dsc);
+ dsc->endpreview = DSC_END(dsc);
+ dsc->scan_section = scan_preview;
+ /* Don't mark the preview as EPSI if a DOS EPS header is present */
+ if (dsc->preview == CDSC_NOPREVIEW)
+ dsc->preview = CDSC_EPSI;
+ return CDSC_OK;
+ }
+ else {
+ dsc->scan_section = scan_pre_defaults;
+ return CDSC_PROPAGATE;
+ }
+ }
+
+ if (IS_DSC(line, "%%BeginPreview")) {
+ /* ignore because we are in this section */
+ }
+ else if (dsc_is_section(line)) {
+ dsc->endpreview = DSC_START(dsc);
+ dsc->scan_section = scan_pre_defaults;
+ return CDSC_PROPAGATE;
+ }
+ else if (IS_DSC(line, "%%EndPreview")) {
+ dsc->id = CDSC_ENDPREVIEW;
+ dsc->endpreview = DSC_END(dsc);
+ dsc->scan_section = scan_pre_defaults;
+ return CDSC_OK;
+ }
+ else if (line[0] == '%' && line[1] != '%') {
+ /* Ordinary comments are OK */
+ }
+ else {
+ dsc->id = CDSC_UNKNOWNDSC;
+ /* DSC comments should not occur in preview */
+ dsc_unknown(dsc);
+ }
+
+ dsc->endpreview = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+static int
+dsc_scan_defaults(CDSC *dsc)
+{
+ /* Defaults section ends at */
+ /* %%EndDefaults */
+ /* another section */
+ /* Defaults section must start with %%BeginDefaults */
+ char *line = dsc->line;
+ dsc->id = CDSC_OK;
+
+ if (dsc->scan_section == scan_pre_defaults) {
+ if (IS_BLANK(line))
+ return CDSC_OK; /* ignore blank lines before defaults */
+ else if (IS_DSC(line, "%%BeginDefaults")) {
+ dsc->id = CDSC_BEGINDEFAULTS;
+ dsc->begindefaults = DSC_START(dsc);
+ dsc->enddefaults = DSC_END(dsc);
+ dsc->scan_section = scan_defaults;
+ return CDSC_OK;
+ }
+ else {
+ dsc->scan_section = scan_pre_prolog;
+ return CDSC_PROPAGATE;
+ }
+ }
+
+ if (NOT_DSC_LINE(line)) {
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%BeginPreview")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginDefaults")) {
+ /* ignore because we are in this section */
+ }
+ else if (dsc_is_section(line)) {
+ dsc->enddefaults = DSC_START(dsc);
+ dsc->scan_section = scan_pre_prolog;
+ return CDSC_PROPAGATE;
+ }
+ else if (IS_DSC(line, "%%EndDefaults")) {
+ dsc->id = CDSC_ENDDEFAULTS;
+ dsc->enddefaults = DSC_END(dsc);
+ dsc->scan_section = scan_pre_prolog;
+ return CDSC_OK;
+ }
+ else if (IS_DSC(line, "%%PageMedia:")) {
+ dsc->id = CDSC_PAGEMEDIA;
+ dsc_parse_media(dsc, &dsc->page_media);
+ }
+ else if (IS_DSC(line, "%%PageOrientation:")) {
+ dsc->id = CDSC_PAGEORIENTATION;
+ /* This can override %%Orientation: */
+ if (dsc_parse_orientation(dsc, &(dsc->page_orientation), 18))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%PageBoundingBox:")) {
+ dsc->id = CDSC_PAGEBOUNDINGBOX;
+ if (dsc_parse_bounding_box(dsc, &(dsc->page_bbox), 18))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%ViewingOrientation:")) {
+ dsc->id = CDSC_VIEWINGORIENTATION;
+ if (dsc_parse_viewing_orientation(dsc, &dsc->viewing_orientation))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%PageCropBox:")) {
+ dsc->id = CDSC_PAGECROPBOX;
+ if (dsc_parse_float_bounding_box(dsc, &dsc->crop_box, 14))
+ return CDSC_ERROR;
+ }
+ else {
+ dsc->id = CDSC_UNKNOWNDSC;
+ /* All other DSC comments are unknown, but not an error */
+ dsc_unknown(dsc);
+ }
+ dsc->enddefaults = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+/* CDSC_RESPONSE_OK and CDSC_RESPONSE_CANCEL mean ignore the
+ * mismatch (default) */
+static int
+dsc_check_match_prompt(CDSC *dsc, const char *str, int count)
+{
+ if (count != 0) {
+ char buf[MAXSTR+MAXSTR];
+ if (dsc->line_length < (unsigned int)(sizeof(buf)/2-1)) {
+ strncpy(buf, dsc->line, dsc->line_length);
+ buf[dsc->line_length] = '\0';
+ }
+ gs_sprintf(buf+strlen(buf), "\n%%%%Begin%.40s: / %%%%End%.40s\n", str, str);
+ return dsc_error(dsc, CDSC_MESSAGE_BEGIN_END, buf, (int)strlen(buf));
+ }
+ return CDSC_RESPONSE_CANCEL;
+}
+
+static int
+dsc_check_match_type(CDSC *dsc, const char *str, int count)
+{
+ if (dsc_check_match_prompt(dsc, str, count) == CDSC_RESPONSE_IGNORE_ALL)
+ return CDSC_NOTDSC;
+ return CDSC_OK;
+}
+
+/* complain if Begin/End blocks didn't match */
+/* return non-zero if we should ignore all DSC */
+static int
+dsc_check_match(CDSC *dsc)
+{
+ int rc = 0;
+ const char *font = "Font";
+ const char *feature = "Feature";
+ const char *resource = "Resource";
+ const char *procset = "ProcSet";
+
+ if (!rc)
+ rc = dsc_check_match_type(dsc, font, dsc->begin_font_count);
+ if (!rc)
+ rc = dsc_check_match_type(dsc, feature, dsc->begin_feature_count);
+ if (!rc)
+ rc = dsc_check_match_type(dsc, resource, dsc->begin_resource_count);
+ if (!rc)
+ rc = dsc_check_match_type(dsc, procset, dsc->begin_procset_count);
+
+ dsc->begin_font_count = 0;
+ dsc->begin_feature_count = 0;
+ dsc->begin_resource_count = 0;
+ dsc->begin_procset_count = 0;
+ return rc;
+}
+
+static int
+dsc_scan_prolog(CDSC *dsc)
+{
+ /* Prolog section ends at */
+ /* %%EndProlog */
+ /* another section */
+ /* Prolog section may start with %%BeginProlog or non-dsc line */
+ char *line = dsc->line;
+ dsc->id = CDSC_OK;
+
+ if (dsc->scan_section == scan_pre_prolog) {
+ if (dsc_is_section(line) && (!IS_DSC(line, "%%BeginProlog"))) {
+ dsc->scan_section = scan_pre_setup;
+ return CDSC_PROPAGATE;
+ }
+ dsc->id = CDSC_BEGINPROLOG;
+ dsc->beginprolog = DSC_START(dsc);
+ dsc->endprolog = DSC_END(dsc);
+ dsc->scan_section = scan_prolog;
+ if (IS_DSC(line, "%%BeginProlog"))
+ return CDSC_OK;
+ }
+
+ if (NOT_DSC_LINE(line)) {
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%BeginPreview")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginDefaults")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginProlog")) {
+ /* ignore because we are in this section */
+ }
+ else if (dsc_is_section(line)) {
+ dsc->endprolog = DSC_START(dsc);
+ dsc->scan_section = scan_pre_setup;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_PROPAGATE;
+ }
+ else if (IS_DSC(line, "%%EndProlog")) {
+ dsc->id = CDSC_ENDPROLOG;
+ dsc->endprolog = DSC_END(dsc);
+ dsc->scan_section = scan_pre_setup;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_OK;
+ }
+ else if (IS_DSC(line, "%%BeginFont:")) {
+ dsc->id = CDSC_BEGINFONT;
+ /* ignore Begin/EndFont, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_font_count++;
+ }
+ else if (IS_DSC(line, "%%EndFont")) {
+ dsc->id = CDSC_ENDFONT;
+ dsc->begin_font_count--;
+ }
+ else if (IS_DSC(line, "%%BeginFeature:")) {
+ dsc->id = CDSC_BEGINFEATURE;
+ /* ignore Begin/EndFeature, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_feature_count++;
+ }
+ else if (IS_DSC(line, "%%EndFeature")) {
+ dsc->id = CDSC_ENDFEATURE;
+ dsc->begin_feature_count--;
+ }
+ else if (IS_DSC(line, "%%BeginResource:")) {
+ dsc->id = CDSC_BEGINRESOURCE;
+ /* ignore Begin/EndResource, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_resource_count++;
+ }
+ else if (IS_DSC(line, "%%EndResource")) {
+ dsc->id = CDSC_ENDRESOURCE;
+ dsc->begin_resource_count--;
+ }
+ else if (IS_DSC(line, "%%BeginProcSet:")) {
+ dsc->id = CDSC_BEGINPROCSET;
+ /* ignore Begin/EndProcSet, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_procset_count++;
+ }
+ else if (IS_DSC(line, "%%EndProcSet")) {
+ dsc->id = CDSC_ENDPROCSET;
+ dsc->begin_procset_count--;
+ }
+ else {
+ /* All other DSC comments are unknown, but not an error */
+ dsc->id = CDSC_UNKNOWNDSC;
+ dsc_unknown(dsc);
+ }
+
+ dsc->endprolog = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+static int
+dsc_scan_setup(CDSC *dsc)
+{
+ /* Setup section ends at */
+ /* %%EndSetup */
+ /* another section */
+ /* Setup section must start with %%BeginSetup */
+
+ char *line = dsc->line;
+ dsc->id = CDSC_OK;
+
+ if (dsc->scan_section == scan_pre_setup) {
+ if (IS_BLANK(line))
+ return CDSC_OK; /* ignore blank lines before setup */
+ else if (IS_DSC(line, "%%BeginSetup")) {
+ dsc->id = CDSC_BEGINSETUP;
+ dsc->beginsetup = DSC_START(dsc);
+ dsc->endsetup = DSC_END(dsc);
+ dsc->scan_section = scan_setup;
+ return CDSC_OK;
+ }
+ else {
+ dsc->scan_section = scan_pre_pages;
+ return CDSC_PROPAGATE;
+ }
+ }
+
+ if (NOT_DSC_LINE(line)) {
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%BeginPreview")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginDefaults")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginProlog")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginSetup")) {
+ /* ignore because we are in this section */
+ }
+ else if (dsc_is_section(line)) {
+ dsc->endsetup = DSC_START(dsc);
+ dsc->scan_section = scan_pre_pages;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_PROPAGATE;
+ }
+ else if (IS_DSC(line, "%%EndSetup")) {
+ dsc->id = CDSC_ENDSETUP;
+ dsc->endsetup = DSC_END(dsc);
+ dsc->scan_section = scan_pre_pages;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_OK;
+ }
+ else if (IS_DSC(line, "%%BeginFeature:")) {
+ dsc->id = CDSC_BEGINFEATURE;
+ /* ignore Begin/EndFeature, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_feature_count++;
+ }
+ else if (IS_DSC(line, "%%EndFeature")) {
+ dsc->id = CDSC_ENDFEATURE;
+ dsc->begin_feature_count--;
+ }
+ else if (IS_DSC(line, "%%Feature:")) {
+ dsc->id = CDSC_FEATURE;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%BeginResource:")) {
+ dsc->id = CDSC_BEGINRESOURCE;
+ /* ignore Begin/EndResource, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_resource_count++;
+ }
+ else if (IS_DSC(line, "%%EndResource")) {
+ dsc->id = CDSC_ENDRESOURCE;
+ dsc->begin_resource_count--;
+ }
+ else if (IS_DSC(line, "%%PaperColor:")) {
+ dsc->id = CDSC_PAPERCOLOR;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PaperForm:")) {
+ dsc->id = CDSC_PAPERFORM;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PaperWeight:")) {
+ dsc->id = CDSC_PAPERWEIGHT;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PaperSize:")) {
+ /* DSC 2.1 */
+ GSBOOL found_media = FALSE;
+ int i;
+ int n = 12;
+ char buf[MAXSTR];
+ buf[0] = '\0';
+ dsc->id = CDSC_PAPERSIZE;
+ dsc_copy_string(buf, sizeof(buf)-1, dsc->line+n, dsc->line_length-n,
+ NULL);
+ for (i=0; i<(int)dsc->media_count; i++) {
+ if (dsc->media[i] && dsc->media[i]->name &&
+ (dsc_stricmp(buf, dsc->media[i]->name)==0)) {
+ dsc->page_media = dsc->media[i];
+ found_media = TRUE;
+ break;
+ }
+ }
+ if (!found_media) {
+ /* It didn't match %%DocumentPaperSizes: */
+ /* Try our known media */
+ const CDSCMEDIA *m = dsc_known_media;
+ while (m->name) {
+ if (dsc_stricmp(buf, m->name)==0) {
+ dsc->page_media = m;
+ break;
+ }
+ m++;
+ }
+ if (m->name == NULL)
+ dsc_unknown(dsc);
+ }
+ }
+ else {
+ /* All other DSC comments are unknown, but not an error */
+ dsc->id = CDSC_UNKNOWNDSC;
+ dsc_unknown(dsc);
+ }
+
+ dsc->endsetup = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+static int
+dsc_scan_page(CDSC *dsc)
+{
+ /* Page section ends at */
+ /* %%Page */
+ /* %%Trailer */
+ /* %%EOF */
+ char *line = dsc->line;
+ dsc->id = CDSC_OK;
+
+ if (dsc->scan_section == scan_pre_pages) {
+ if (IS_DSC(line, "%%Page:")) {
+ dsc->scan_section = scan_pages;
+ /* fall through */
+ }
+ else {
+ /* %%Page: didn't follow %%EndSetup
+ * Keep reading until reach %%Page or %%Trailer
+ * and add it to previous section.
+ */
+ DSC_OFFSET *last;
+ if (dsc->endsetup != 0)
+ last = &dsc->endsetup;
+ else if (dsc->endprolog != 0)
+ last = &dsc->endprolog;
+ else if (dsc->enddefaults != 0)
+ last = &dsc->enddefaults;
+ else if (dsc->endpreview != 0)
+ last = &dsc->endpreview;
+ else if (dsc->endcomments != 0)
+ last = &dsc->endcomments;
+ else
+ last = &dsc->begincomments;
+ *last = DSC_START(dsc);
+ if (IS_DSC(line, "%%Trailer") || IS_DSC(line, "%%EOF")) {
+ dsc->scan_section = scan_pre_trailer;
+ return CDSC_PROPAGATE;
+ }
+ *last = DSC_END(dsc);
+ return CDSC_OK;
+ }
+ }
+
+ if (NOT_DSC_LINE(line)) {
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%Page:")) {
+ int code;
+ dsc->id = CDSC_PAGE;
+ if (dsc->page_count) {
+ dsc->page[dsc->page_count-1].end = DSC_START(dsc);
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ }
+
+ if ( (code = dsc_parse_page(dsc)) != CDSC_OK)
+ return code;
+ if (dsc->page_count == 0)
+ dsc->scan_section = scan_pre_pages;
+ }
+ else if (IS_DSC(line, "%%BeginPreview")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginDefaults")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginProlog")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (IS_DSC(line, "%%BeginSetup")) {
+ /* ignore because we have already processed this section */
+ }
+ else if (dsc_is_section(line)) {
+ if (IS_DSC(line, "%%Trailer")) {
+ if (dsc->page_count)
+ dsc->page[dsc->page_count-1].end = DSC_START(dsc);
+ if (dsc->file_length) {
+ if ((!dsc->doseps_end &&
+ ((DSC_END(dsc) + 32768) < dsc->file_length)) ||
+ ((dsc->doseps_end) &&
+ ((DSC_END(dsc) + 32768) < dsc->doseps_end))) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_EARLY_TRAILER,
+ dsc->line, dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* ignore early trailer */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* this is the trailer */
+ dsc->scan_section = scan_pre_trailer;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_PROPAGATE;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ else {
+ dsc->scan_section = scan_pre_trailer;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_PROPAGATE;
+ }
+ }
+ else {
+ dsc->scan_section = scan_pre_trailer;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_PROPAGATE;
+ }
+ }
+ else if (IS_DSC(line, "%%EOF")) {
+ if (dsc->page_count)
+ dsc->page[dsc->page_count-1].end = DSC_START(dsc);
+ if (dsc->file_length) {
+ if ((!dsc->doseps_end &&
+ ((DSC_END(dsc) + 100) < dsc->file_length)) ||
+ ((dsc->doseps_end) &&
+ ((DSC_END(dsc) + 100) < dsc->doseps_end))) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_EARLY_EOF,
+ dsc->line, dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* %%EOF is wrong, ignore it */
+ break;
+ case CDSC_RESPONSE_CANCEL:
+ /* %%EOF is correct */
+ dsc->scan_section = scan_eof;
+ dsc->eof = TRUE;
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_PROPAGATE;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ }
+ else {
+ /* ignore it */
+ if (dsc_check_match(dsc))
+ return CDSC_NOTDSC;
+ return CDSC_OK;
+ }
+ }
+ else {
+ /* Section comment, probably from a badly */
+ /* encapsulated EPS file. */
+ int rc = dsc_error(dsc, CDSC_MESSAGE_BAD_SECTION,
+ dsc->line, dsc->line_length);
+ if (rc == CDSC_RESPONSE_IGNORE_ALL)
+ return CDSC_NOTDSC;
+ }
+ }
+ else if (IS_DSC(line, "%%PageTrailer")) {
+ dsc->id = CDSC_PAGETRAILER;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%BeginPageSetup")) {
+ dsc->id = CDSC_BEGINPAGESETUP;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%EndPageSetup")) {
+ dsc->id = CDSC_ENDPAGESETUP;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PageMedia:")) {
+ dsc->id = CDSC_PAGEMEDIA;
+ if (dsc->page_count)
+ dsc_parse_media(dsc, &(dsc->page[dsc->page_count-1].media));
+ }
+ else if (IS_DSC(line, "%%PaperColor:")) {
+ dsc->id = CDSC_PAPERCOLOR;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PaperForm:")) {
+ dsc->id = CDSC_PAPERFORM;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PaperWeight:")) {
+ dsc->id = CDSC_PAPERWEIGHT;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%PaperSize:")) {
+ /* DSC 2.1 */
+ GSBOOL found_media = FALSE;
+ int i;
+ int n = 12;
+ char buf[MAXSTR];
+ buf[0] = '\0';
+ dsc_copy_string(buf, sizeof(buf)-1, dsc->line+n,
+ dsc->line_length-n, NULL);
+ for (i=0; i<(int)dsc->media_count; i++) {
+ if (dsc->media[i] && dsc->media[i]->name &&
+ (dsc_stricmp(buf, dsc->media[i]->name)==0)) {
+ if (dsc->page_count)
+ dsc->page[dsc->page_count-1].media = dsc->media[i];
+ found_media = TRUE;
+ break;
+ }
+ }
+ if (!found_media) {
+ /* It didn't match %%DocumentPaperSizes: */
+ /* Try our known media */
+ const CDSCMEDIA *m = dsc_known_media;
+ while (m->name) {
+ if (dsc_stricmp(buf, m->name)==0) {
+ if (dsc->page_count)
+ dsc->page[dsc->page_count-1].media = m;
+ break;
+ }
+ m++;
+ }
+ if (m->name == NULL)
+ dsc_unknown(dsc);
+ }
+ }
+ else if (IS_DSC(line, "%%PageOrientation:")) {
+ if (dsc->page_count) {
+ dsc->id = CDSC_PAGEORIENTATION;
+ if (dsc_parse_orientation(dsc,
+ &(dsc->page[dsc->page_count-1].orientation) ,18))
+ return CDSC_NOTDSC;
+ }
+ }
+ else if (IS_DSC(line, "%%PageBoundingBox:")) {
+ if (dsc->page_count) {
+ dsc->id = CDSC_PAGEBOUNDINGBOX;
+ if (dsc_parse_bounding_box(dsc,
+ &dsc->page[dsc->page_count-1].bbox, 18))
+ return CDSC_NOTDSC;
+ }
+ }
+ else if (IS_DSC(line, "%%ViewingOrientation:")) {
+ if (dsc->page_count) {
+ dsc->id = CDSC_VIEWINGORIENTATION;
+ if (dsc_parse_viewing_orientation(dsc,
+ &dsc->page[dsc->page_count-1].viewing_orientation))
+ return CDSC_ERROR;
+ }
+ }
+ else if (IS_DSC(line, "%%PageCropBox:")) {
+ if (dsc->page_count) {
+ dsc->id = CDSC_PAGECROPBOX;
+ if (dsc_parse_float_bounding_box(dsc,
+ &(dsc->page[dsc->page_count-1].crop_box), 14))
+ return CDSC_ERROR;
+ }
+ }
+ else if (IS_DSC(line, "%%BeginFont:")) {
+ dsc->id = CDSC_BEGINFONT;
+ /* ignore Begin/EndFont, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_font_count++;
+ }
+ else if (IS_DSC(line, "%%EndFont")) {
+ dsc->id = CDSC_BEGINFONT;
+ dsc->begin_font_count--;
+ }
+ else if (IS_DSC(line, "%%BeginFeature:")) {
+ dsc->id = CDSC_BEGINFEATURE;
+ /* ignore Begin/EndFeature, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_feature_count++;
+ }
+ else if (IS_DSC(line, "%%EndFeature")) {
+ dsc->id = CDSC_ENDFEATURE;
+ dsc->begin_feature_count--;
+ }
+ else if (IS_DSC(line, "%%BeginResource:")) {
+ dsc->id = CDSC_BEGINRESOURCE;
+ /* ignore Begin/EndResource, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_resource_count++;
+ }
+ else if (IS_DSC(line, "%%EndResource")) {
+ dsc->id = CDSC_ENDRESOURCE;
+ dsc->begin_resource_count--;
+ }
+ else if (IS_DSC(line, "%%BeginProcSet:")) {
+ dsc->id = CDSC_BEGINPROCSET;
+ /* ignore Begin/EndProcSet, apart form making sure */
+ /* that they are matched. */
+ dsc->begin_procset_count++;
+ }
+ else if (IS_DSC(line, "%%EndProcSet")) {
+ dsc->id = CDSC_ENDPROCSET;
+ dsc->begin_procset_count--;
+ }
+ else if (IS_DSC(line, "%%IncludeFont:")) {
+ dsc->id = CDSC_INCLUDEFONT;
+ /* ignore */
+ }
+ else {
+ /* All other DSC comments are unknown, but not an error */
+ dsc->id = CDSC_UNKNOWNDSC;
+ dsc_unknown(dsc);
+ }
+
+ if (dsc->page_count)
+ dsc->page[dsc->page_count-1].end = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+/* Valid Trailer comments are
+ * %%Trailer
+ * %%EOF
+ * or the following deferred with (atend)
+ * %%BoundingBox:
+ * %%DocumentCustomColors:
+ * %%DocumentFiles:
+ * %%DocumentFonts:
+ * %%DocumentNeededFiles:
+ * %%DocumentNeededFonts:
+ * %%DocumentNeededProcSets:
+ * %%DocumentNeededResources:
+ * %%DocumentProcSets:
+ * %%DocumentProcessColors:
+ * %%DocumentSuppliedFiles:
+ * %%DocumentSuppliedFonts:
+ * %%DocumentSuppliedProcSets:
+ * %%DocumentSuppliedResources:
+ * %%Orientation:
+ * %%Pages:
+ * %%PageOrder:
+ *
+ * Our supported subset is
+ * %%Trailer
+ * %%EOF
+ * %%BoundingBox:
+ * %%CropBox:
+ * %%HiResBoundingBox:
+ * %%DocumentCustomColors:
+ * %%DocumentProcessColors:
+ * %%Orientation:
+ * %%Pages:
+ * %%PageOrder:
+ * In addition to these, we support
+ * %%DocumentMedia:
+ *
+ * A %%PageTrailer can have the following:
+ * %%PageBoundingBox:
+ * %%PageCustomColors:
+ * %%PageFiles:
+ * %%PageFonts:
+ * %%PageOrientation:
+ * %%PageProcessColors:
+ * %%PageResources:
+ */
+
+static int
+dsc_scan_trailer(CDSC *dsc)
+{
+ /* Trailer section start at */
+ /* %%Trailer */
+ /* and ends at */
+ /* %%EOF */
+ char *line = dsc->line;
+ GSBOOL continued = FALSE;
+
+ if (dsc->endtrailer && IS_DSC(line, "%!PS-Adobe")) {
+ unsigned char *p = (unsigned char *)dsc->line;
+
+ dsc->endtrailer = 0;
+ dsc->dsc_version = dsc_add_line(dsc, dsc->line, dsc->line_length);
+ if (COMPARE(p, "%!PS-Adobe")) {
+ dsc->dsc = TRUE;
+ dsc->begincomments = DSC_START(dsc);
+ if (dsc->dsc_version == NULL)
+ return CDSC_ERROR; /* no memory */
+ p = (unsigned char *)dsc->line + 14;
+ while (IS_WHITE(*p))
+ p++;
+ if (COMPARE(p, "EPSF-"))
+ dsc->epsf = TRUE;
+ dsc->scan_section = scan_comments;
+ return CDSC_PSADOBE;
+ }
+ }
+
+ dsc->id = CDSC_OK;
+
+ if (dsc->scan_section == scan_pre_trailer) {
+ if (IS_DSC(line, "%%Trailer")) {
+ dsc->id = CDSC_TRAILER;
+ dsc->begintrailer = DSC_START(dsc);
+ dsc->endtrailer = DSC_END(dsc);
+ dsc->scan_section = scan_trailer;
+ return CDSC_OK;
+ }
+ else if (IS_DSC(line, "%%EOF")) {
+ dsc->id = CDSC_EOF;
+ dsc->begintrailer = DSC_START(dsc);
+ dsc->endtrailer = DSC_END(dsc);
+ dsc->scan_section = scan_trailer;
+ /* Continue, in case we found %%EOF in an embedded document */
+ return CDSC_OK;
+ }
+ else {
+ /* %%Page: didn't follow %%EndSetup
+ * Keep reading until reach %%Page or %%Trailer
+ * and add it to setup section
+ */
+ /* append to previous section */
+ if (dsc->beginsetup)
+ dsc->endsetup = DSC_END(dsc);
+ else if (dsc->beginprolog)
+ dsc->endprolog = DSC_END(dsc);
+ else {
+ /* horribly confused */
+ }
+ return CDSC_OK;
+ }
+ }
+
+ /* Handle continuation lines.
+ * See comment above about our restrictive processing of
+ * continuation lines
+ */
+ if (IS_DSC(line, "%%+")) {
+ line = dsc->last_line;
+ continued = TRUE;
+ }
+ else
+ dsc_save_line(dsc);
+
+ if (NOT_DSC_LINE(line)) {
+ /* ignore */
+ }
+ else if (IS_DSC(dsc->line, "%%EOF")) {
+ /* Keep scanning, in case we have a false trailer */
+ dsc->id = CDSC_EOF;
+ }
+ else if (IS_DSC(dsc->line, "%%Trailer")) {
+ /* Cope with no pages with code after setup and before trailer. */
+ /* Last trailer is the correct one. */
+ dsc->id = CDSC_TRAILER;
+ dsc->begintrailer = DSC_START(dsc);
+ }
+ else if (IS_DSC(line, "%%Pages:")) {
+ dsc->id = CDSC_PAGES;
+ if (dsc_parse_pages(dsc) != 0)
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%BoundingBox:")) {
+ dsc->id = CDSC_BOUNDINGBOX;
+ if (dsc_parse_bounding_box(dsc, &(dsc->bbox), continued ? 3 : 14))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%HiResBoundingBox:")) {
+ dsc->id = CDSC_HIRESBOUNDINGBOX;
+ if (dsc_parse_float_bounding_box(dsc, &(dsc->hires_bbox),
+ continued ? 3 : 19))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%CropBox:")) {
+ dsc->id = CDSC_CROPBOX;
+ if (dsc_parse_float_bounding_box(dsc, &(dsc->crop_box),
+ continued ? 3 : 10))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%Orientation:")) {
+ dsc->id = CDSC_ORIENTATION;
+ if (dsc_parse_orientation(dsc, &(dsc->page_orientation), continued ? 3 : 14))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%PageOrder:")) {
+ dsc->id = CDSC_PAGEORDER;
+ if (dsc_parse_order(dsc))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(line, "%%DocumentMedia:")) {
+ dsc->id = CDSC_DOCUMENTMEDIA;
+ if (dsc_parse_document_media(dsc))
+ return CDSC_ERROR;
+ }
+ else if (IS_DSC(dsc->line, "%%Page:")) {
+ /* This should not occur in the trailer, but we might see
+ * this if a document has been incorrectly embedded.
+ */
+ int rc = dsc_error(dsc, CDSC_MESSAGE_PAGE_IN_TRAILER,
+ dsc->line, dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* Assume that we are really in the previous */
+ /* page, not the trailer */
+ dsc->scan_section = scan_pre_pages;
+ if (dsc->page_count)
+ dsc->page[dsc->page_count-1].end = DSC_START(dsc);
+ return CDSC_PROPAGATE; /* try again */
+ case CDSC_RESPONSE_CANCEL:
+ /* ignore pages in trailer */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+ else if (IS_DSC(line, "%%DocumentNeededFonts:")) {
+ dsc->id = CDSC_DOCUMENTNEEDEDFONTS;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%DocumentSuppliedFonts:")) {
+ dsc->id = CDSC_DOCUMENTSUPPLIEDFONTS;
+ /* ignore */
+ }
+ else if (IS_DSC(line, "%%DocumentProcessColors:")) {
+ dsc->id = CDSC_DOCUMENTPROCESSCOLORS;
+ if (dsc_parse_process_colours(dsc) != CDSC_OK)
+ dsc->id = CDSC_UNKNOWNDSC;
+ }
+ else if (IS_DSC(line, "%%DocumentCustomColors:")) {
+ dsc->id = CDSC_DOCUMENTCUSTOMCOLORS;
+ if (dsc_parse_custom_colours(dsc) != CDSC_OK)
+ dsc->id = CDSC_UNKNOWNDSC;
+ }
+ else {
+ /* All other DSC comments are unknown, but not an error */
+ dsc->id = CDSC_UNKNOWNDSC;
+ dsc_unknown(dsc);
+ }
+
+ dsc->endtrailer = DSC_END(dsc);
+ return CDSC_OK;
+}
+
+static char *
+dsc_alloc_string(CDSC *dsc, const char *str, int len)
+{
+ char *p;
+ if (dsc->string_head == NULL) {
+ dsc->string_head = (CDSCSTRING *)dsc_memalloc(dsc, sizeof(CDSCSTRING));
+ if (dsc->string_head == NULL)
+ return NULL; /* no memory */
+ dsc->string = dsc->string_head;
+ dsc->string->next = NULL;
+ dsc->string->data = (char *)dsc_memalloc(dsc, CDSC_STRING_CHUNK);
+ if (dsc->string->data == NULL) {
+ dsc_reset(dsc);
+ return NULL; /* no memory */
+ }
+ dsc->string->index = 0;
+ dsc->string->length = CDSC_STRING_CHUNK;
+ }
+ if ( dsc->string->index + len + 1 > dsc->string->length) {
+ /* allocate another string block */
+ CDSCSTRING *newstring = (CDSCSTRING *)dsc_memalloc(dsc, sizeof(CDSCSTRING));
+ if (newstring == NULL) {
+ dsc_debug_print(dsc, "Out of memory\n");
+ return NULL;
+ }
+ newstring->next = NULL;
+ newstring->length = 0;
+ newstring->index = 0;
+ newstring->data = (char *)dsc_memalloc(dsc, CDSC_STRING_CHUNK);
+ if (newstring->data == NULL) {
+ dsc_memfree(dsc, newstring);
+ dsc_debug_print(dsc, "Out of memory\n");
+ return NULL; /* no memory */
+ }
+ newstring->length = CDSC_STRING_CHUNK;
+ dsc->string->next = newstring;
+ dsc->string = newstring;
+ }
+ if ( dsc->string->index + len + 1 > dsc->string->length)
+ return NULL; /* failed */
+ p = dsc->string->data + dsc->string->index;
+ memcpy(p, str, len);
+ *(p+len) = '\0';
+ dsc->string->index += len + 1;
+ return p;
+}
+
+/* store line, ignoring leading spaces */
+static char *
+dsc_add_line(CDSC *dsc, const char *line, unsigned int len)
+{
+ char *newline;
+ unsigned int i;
+ while (len && (IS_WHITE(*line))) {
+ len--;
+ line++;
+ }
+ newline = dsc_alloc_string(dsc, line, len);
+ if (newline == NULL)
+ return NULL;
+
+ for (i=0; i<len; i++) {
+ if (newline[i] == '\r') {
+ newline[i]='\0';
+ break;
+ }
+ if (newline[i] == '\n') {
+ newline[i]='\0';
+ break;
+ }
+ }
+ return newline;
+}
+
+/* Copy string on line to new allocated string str */
+/* String is always null terminated */
+/* String is no longer than len */
+/* Return pointer to string */
+/* Store number of used characters from line */
+/* Don't copy enclosing () */
+static char *
+dsc_copy_string(char *str, unsigned int slen, char *line,
+ unsigned int len, unsigned int *offset)
+{
+ int quoted = FALSE;
+ int instring=0;
+ unsigned int newlength = 0;
+ unsigned int i = 0;
+ unsigned char ch;
+ if (len > slen)
+ len = slen-1;
+ while ( (i<len) && IS_WHITE(line[i]))
+ i++; /* skip leading spaces */
+ if ((i < len) && (line[i]=='(')) {
+ quoted = TRUE;
+ instring++;
+ i++; /* don't copy outside () */
+ }
+ while (i < len) {
+ str[newlength] = ch = line[i];
+ i++;
+ if (quoted) {
+ if (ch == '(')
+ instring++;
+ if (ch == ')')
+ instring--;
+ if (instring==0)
+ break;
+ }
+ else if (ch == ' ')
+ break;
+
+ if (ch == '\r')
+ break;
+ if (ch == '\n')
+ break;
+ else if ( (ch == '\\') && (i+1 < len) ) {
+ ch = line[i];
+ if ((ch >= '0') && (ch <= '9')) {
+ /* octal coded character */
+ int j = 3;
+ ch = 0;
+ while (j && (i < len) && line[i]>='0' && line[i]<='7') {
+ ch = (unsigned char)((ch<<3) + (line[i]-'0'));
+ i++;
+ j--;
+ }
+ str[newlength] = ch;
+ }
+ else if (ch == '(') {
+ str[newlength] = ch;
+ i++;
+ }
+ else if (ch == ')') {
+ str[newlength] = ch;
+ i++;
+ }
+ else if (ch == 'b') {
+ str[newlength] = '\b';
+ i++;
+ }
+ else if (ch == 'f') {
+ str[newlength] = '\b';
+ i++;
+ }
+ else if (ch == 'n') {
+ str[newlength] = '\n';
+ i++;
+ }
+ else if (ch == 'r') {
+ str[newlength] = '\r';
+ i++;
+ }
+ else if (ch == 't') {
+ str[newlength] = '\t';
+ i++;
+ }
+ else if (ch == '\\') {
+ str[newlength] = '\\';
+ i++;
+ }
+ }
+ newlength++;
+ }
+ str[newlength] = '\0';
+ if (offset != (unsigned int *)NULL)
+ *offset = i;
+ return str;
+}
+
+static int
+dsc_get_int(const char *line, unsigned int len, unsigned int *offset)
+{
+ char newline[MAXSTR];
+ int newlength = 0;
+ unsigned int i = 0;
+ unsigned char ch;
+
+ len = min(len, sizeof(newline)-1);
+ while ((i<len) && IS_WHITE(line[i]))
+ i++; /* skip leading spaces */
+ while (i < len) {
+ newline[newlength] = ch = line[i];
+ if (!(isdigit(ch) || (ch=='-') || (ch=='+')))
+ break; /* not part of an integer number */
+ i++;
+ newlength++;
+ }
+ while ((i<len) && IS_WHITE(line[i]))
+ i++; /* skip trailing spaces */
+ newline[newlength] = '\0';
+ if (offset != (unsigned int *)NULL)
+ *offset = i;
+ return atoi(newline);
+}
+
+static float
+dsc_get_real(const char *line, unsigned int len, unsigned int *offset)
+{
+ char newline[MAXSTR];
+ int newlength = 0;
+ unsigned int i = 0;
+ unsigned char ch;
+
+ len = min(len, sizeof(newline)-1);
+ while ((i<len) && IS_WHITE(line[i]))
+ i++; /* skip leading spaces */
+ while (i < len) {
+ newline[newlength] = ch = line[i];
+ if (!(isdigit(ch) || (ch=='.') || (ch=='-') || (ch=='+')
+ || (ch=='e') || (ch=='E')))
+ break; /* not part of a real number */
+ i++;
+ newlength++;
+ }
+ while ((i<len) && IS_WHITE(line[i]))
+ i++; /* skip trailing spaces */
+
+ newline[newlength] = '\0';
+
+ if (offset != (unsigned int *)NULL)
+ *offset = i;
+ return (float)atof(newline);
+}
+
+int
+dsc_stricmp(const char *s, const char *t)
+{
+ while (toupper((unsigned char)*s) == toupper((unsigned char)*t)) {
+ if (*s == '\0')
+ return 0;
+ s++;
+ t++;
+ }
+ return (toupper((unsigned char)*s) - toupper((unsigned char)*t));
+}
+
+static int
+dsc_parse_page(CDSC *dsc)
+{
+ char *p;
+ unsigned int i;
+ char page_label[MAXSTR];
+ char *pl;
+ int page_ordinal;
+ int page_number;
+
+ p = dsc->line + 7;
+ pl = dsc_copy_string(page_label, sizeof(page_label), p, dsc->line_length-7, &i);
+ if (pl == NULL)
+ return CDSC_ERROR;
+ p += i;
+ if (dsc->line_length - 7 - i == 0) {
+ /* Ordinal missing, or parentheses not matched in label */
+ /* Try to find ordinal at end of line */
+ while (i > 0) {
+ if (!IS_WHITE_OR_EOL(p[-1]))
+ break;
+ p--;
+ i--;
+ }
+ while (i > 0) {
+ if (!isdigit((int)p[-1]))
+ break;
+ p--;
+ i--;
+ }
+ }
+ page_ordinal = dsc_get_int(p, dsc->line_length - 7 - i, NULL);
+
+ if ( (page_ordinal == 0) || (strlen(page_label) == 0) ||
+ (dsc->page_count &&
+ (page_ordinal != dsc->page[dsc->page_count-1].ordinal+1)) ) {
+ int rc = dsc_error(dsc, CDSC_MESSAGE_PAGE_ORDINAL, dsc->line,
+ dsc->line_length);
+ switch (rc) {
+ case CDSC_RESPONSE_OK:
+ /* ignore this page */
+ return CDSC_OK;
+ case CDSC_RESPONSE_CANCEL:
+ /* accept the page */
+ break;
+ case CDSC_RESPONSE_IGNORE_ALL:
+ return CDSC_NOTDSC;
+ }
+ }
+
+ page_number = dsc->page_count;
+ dsc_add_page(dsc, page_ordinal, page_label);
+ dsc->page[page_number].begin = DSC_START(dsc);
+ dsc->page[page_number].end = DSC_START(dsc);
+
+ if (dsc->page[page_number].label == NULL)
+ return CDSC_ERROR; /* no memory */
+
+ return CDSC_OK;
+}
+
+/* DSC error reporting */
+
+void
+dsc_debug_print(CDSC *dsc, const char *str)
+{
+ if (dsc->debug_print_fn)
+ dsc->debug_print_fn(dsc->caller_data, str);
+}
+
+/* Display a message about a problem with the DSC comments.
+ *
+ * explanation = an index to to a multiline explanation in dsc_message[]
+ * line = pointer to the offending DSC line (if any)
+ * return code =
+ * CDSC_RESPONSE_OK DSC was wrong, make a guess about what
+ * was really meant.
+ * CDSC_RESPONSE_CANCEL Assume DSC was correct, ignore if it
+ * is misplaced.
+ * CDSC_RESPONSE_IGNORE_ALL Ignore all DSC.
+ */
+/* Silent operation. Don't display errors. */
+static int
+dsc_error(CDSC *dsc, unsigned int explanation,
+ char *line, unsigned int line_len)
+{
+ /* if error function provided, use it */
+ if (dsc->dsc_error_fn)
+ return dsc->dsc_error_fn(dsc->caller_data, dsc,
+ explanation, line, line_len);
+
+ /* treat DSC as being correct */
+ return CDSC_RESPONSE_CANCEL;
+}
+
+/* Fixup if DCS 2.0 was used */
+static int
+dsc_dcs2_fixup(CDSC *dsc)
+{
+ char composite[] = "Composite";
+ /* If DCS 2.0 single file format found, expose the separations
+ * as multiple pages. Treat the initial EPS file as a single
+ * page without comments, prolog or trailer.
+ */
+ if (dsc->dcs2) {
+ int code = CDSC_OK;
+ int page_number;
+ DSC_OFFSET *pbegin;
+ DSC_OFFSET *pend;
+ DSC_OFFSET end;
+ CDCS2 *pdcs = dsc->dcs2;
+ /* Now treat the initial EPS file as a single page without
+ * headers or trailer, so page extraction will fetch the
+ * the correct separation. */
+ if (dsc->page_count == 0)
+ code = dsc_add_page(dsc, 1, composite);
+ else if (dsc->page_count == 1)
+ dsc->page[0].label =
+ dsc_alloc_string(dsc, composite, (int)strlen(composite)+1);
+ if (code != CDSC_OK)
+ return code;
+ page_number = dsc->page_count - 1;
+ pbegin = &dsc->page[page_number].begin;
+ pend = &dsc->page[page_number].end;
+ if (*pbegin == *pend) {
+ /* no page, so force it to conform to the following sections */
+ *pbegin = 999999999;
+ *pend = 0;
+ }
+
+ if (dsc->begincomments != dsc->endcomments) {
+ *pbegin = min(dsc->begincomments, *pbegin);
+ dsc->begincomments = 0;
+ *pend = max(dsc->endcomments, *pend);
+ dsc->endcomments = 0;
+ }
+
+ if (dsc->beginpreview != dsc->endpreview) {
+ *pbegin = min(dsc->beginpreview, *pbegin);
+ dsc->beginpreview = 0;
+ *pend = max(dsc->endpreview, *pend);
+ dsc->endpreview = 0;
+ }
+
+ if (dsc->begindefaults != dsc->enddefaults) {
+ *pbegin = min(dsc->begindefaults, *pbegin);
+ dsc->begindefaults = 0;
+ *pend = max(dsc->enddefaults, *pend);
+ dsc->enddefaults = 0;
+ }
+
+ if (dsc->beginprolog != dsc->endprolog) {
+ *pbegin = min(dsc->beginprolog, *pbegin);
+ dsc->beginprolog = 0;
+ *pend = max(dsc->endprolog, *pend);
+ dsc->endprolog = 0;
+ }
+
+ if (dsc->beginsetup != dsc->endsetup) {
+ *pbegin = min(dsc->beginsetup, *pbegin);
+ dsc->beginsetup = 0;
+ *pend = max(dsc->endsetup, *pend);
+ dsc->endsetup = 0;
+ }
+
+ if (dsc->begintrailer != dsc->endtrailer) {
+ *pbegin = min(dsc->begintrailer, *pbegin);
+ dsc->begintrailer = 0;
+ *pend = max(dsc->endtrailer, *pend);
+ dsc->endtrailer = 0;
+ }
+
+ if (*pbegin == 999999999)
+ *pbegin = *pend;
+ end = 0; /* end of composite is start of first separation */
+
+ while (pdcs) {
+ page_number = dsc->page_count;
+ if ((pdcs->begin) && (pdcs->colourname != NULL)) {
+ /* Single file DCS 2.0 */
+ code = dsc_add_page(dsc, page_number+1, pdcs->colourname);
+ if (code)
+ return code;
+ dsc->page[page_number].begin = pdcs->begin;
+ dsc->page[page_number].end = pdcs->end;
+ if (end != 0)
+ end = min(end, pdcs->begin);
+ else
+ end = pdcs->begin; /* first separation */
+ }
+ else {
+ /* Multiple file DCS 2.0 */
+ if ((pdcs->location != NULL) &&
+ (pdcs->filetype != NULL) &&
+ (pdcs->colourname != NULL) &&
+ (dsc_stricmp(pdcs->location, "Local") == 0) &&
+ ((dsc_stricmp(pdcs->filetype, "EPS") == 0) ||
+ (dsc_stricmp(pdcs->filetype, "EPSF") == 0))) {
+ code = dsc_add_page(dsc, page_number+1, pdcs->colourname);
+ if (code)
+ return code;
+ dsc->page[page_number].begin = 0;
+ dsc->page[page_number].end = 0;
+ }
+ }
+ pdcs = pdcs->next;
+ }
+ /* end of composite is start of first separation */
+ if (end != 0)
+ *pend = end;
+ /* According to the DCS2 specification, the size of the composite
+ * section can be determined by the smallest #offset.
+ * Some incorrect DCS2 files don't put the separations inside
+ * the DOS EPS PostScript section, and have a TIFF separation
+ * between the composite and the first separation. This
+ * contravenes the DCS2 specification. If we see one of these
+ * files, bring the end of the composite back to the end of
+ * the DOS EPS PostScript section.
+ */
+ if (dsc->doseps_end && (*pend > dsc->doseps_end))
+ *pend = dsc->doseps_end;
+ }
+ return 0;
+}
+
+static int
+dsc_parse_platefile(CDSC *dsc)
+{
+ unsigned int i, n;
+ CDCS2 dcs2;
+ CDCS2 *pdcs2;
+ char colourname[MAXSTR];
+ char filetype[MAXSTR];
+ char location[MAXSTR];
+ char *filename = NULL;
+ int filename_length = 0;
+ GSBOOL blank_line;
+ GSBOOL single = FALSE;
+ if (IS_DSC(dsc->line, "%%PlateFile:"))
+ n = 12;
+ else if (IS_DSC(dsc->line, "%%+"))
+ n = 3;
+ else
+ return CDSC_ERROR; /* error */
+
+ memset(&dcs2, 0, sizeof(dcs2));
+ memset(&colourname, 0, sizeof(colourname));
+ memset(&filetype, 0, sizeof(filetype));
+ memset(&location, 0, sizeof(location));
+ memset(&filename, 0, sizeof(filename));
+
+ /* check for blank remainder of line */
+ blank_line = TRUE;
+ for (i=n; i<dsc->line_length; i++) {
+ if (!IS_WHITE_OR_EOL(dsc->line[i])) {
+ blank_line = FALSE;
+ break;
+ }
+ }
+
+ if (!blank_line) {
+ dsc_copy_string(colourname, sizeof(colourname),
+ dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i)
+ dsc_copy_string(filetype, sizeof(filetype),
+ dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ while (IS_WHITE_OR_EOL(dsc->line[n]))
+ n++;
+ if (dsc->line[n] == '#') {
+ /* single file DCS 2.0 */
+ single = TRUE;
+ n++;
+ if (i)
+ dcs2.begin= dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i)
+ dcs2.end= dcs2.begin +
+ dsc_get_int(dsc->line+n, dsc->line_length-n, &i);
+ }
+ else {
+ /* multiple file DCS 2.0 */
+ if (i)
+ dsc_copy_string(location, sizeof(location),
+ dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i) {
+ filename = dsc->line+n;
+ filename_length = dsc->line_length-n;
+ }
+ }
+ if (i==0)
+ dsc_unknown(dsc); /* we didn't get all fields */
+ else {
+ /* Allocate strings */
+ if (strlen(colourname))
+ dcs2.colourname = dsc_alloc_string(dsc,
+ colourname, (int)strlen(colourname));
+ if (strlen(filetype))
+ dcs2.filetype = dsc_alloc_string(dsc,
+ filetype, (int)strlen(filetype));
+ if (strlen(location))
+ dcs2.location = dsc_alloc_string(dsc,
+ location, (int)strlen(location));
+ if (filename)
+ dcs2.filename = dsc_add_line(dsc, filename, filename_length);
+
+ /* Prevent parser from reading separations */
+ if (single)
+ dsc->file_length = min(dsc->file_length, dcs2.begin);
+ /* Allocate it */
+ pdcs2 = (CDCS2 *)dsc_memalloc(dsc, sizeof(CDCS2));
+ if (pdcs2 == NULL)
+ return CDSC_ERROR; /* out of memory */
+ memcpy(pdcs2, &dcs2, sizeof(CDCS2));
+ /* Then add to list of separations */
+ if (dsc->dcs2 == NULL)
+ dsc->dcs2 = pdcs2;
+ else {
+ CDCS2 *this_dcs2 = dsc->dcs2;
+ while (this_dcs2->next)
+ this_dcs2 = this_dcs2->next;
+ this_dcs2->next = pdcs2;
+ }
+ }
+ }
+ return CDSC_OK;
+}
+
+/* Parse a DCS 1.0 plate comment, storing like a multi file DSC 2.0 */
+static int
+dsc_parse_dcs1plate(CDSC *dsc)
+{
+ unsigned int i, n = 0;
+ CDCS2 dcs2;
+ CDCS2 *pdcs2;
+ const char *colourname;
+ char filename[MAXSTR];
+ GSBOOL blank_line;
+ GSBOOL continued = FALSE;
+ char *line = dsc->line;
+
+ memset(&dcs2, 0, sizeof(dcs2));
+ memset(&filename, 0, sizeof(filename));
+
+ if (IS_DSC(line, "%%+")) {
+ n = 3;
+ line = dsc->last_line;
+ continued = TRUE;
+ }
+
+ if (IS_DSC(line, "%%CyanPlate:")) {
+ colourname = "Cyan";
+ if (!continued)
+ n = 12;
+ }
+ else if (IS_DSC(line, "%%MagentaPlate:")) {
+ colourname = "Magenta";
+ if (!continued)
+ n = 15;
+ }
+ else if (IS_DSC(line, "%%YellowPlate:")) {
+ colourname = "Yellow";
+ if (!continued)
+ n = 14;
+ }
+ else if (IS_DSC(line, "%%BlackPlate:")) {
+ colourname = "Black";
+ if (!continued)
+ n = 13;
+ }
+ else
+ return CDSC_ERROR; /* error */
+
+ /* check for blank remainder of line */
+ blank_line = TRUE;
+ for (i=n; i<dsc->line_length; i++) {
+ if (!IS_WHITE_OR_EOL(dsc->line[i])) {
+ blank_line = FALSE;
+ break;
+ }
+ }
+
+ if (!blank_line) {
+ dsc_copy_string(filename, sizeof(filename),
+ dsc->line+n, dsc->line_length-n, &i);
+ if (i==0)
+ dsc_unknown(dsc); /* we didn't get all fields */
+ else {
+ /* Allocate strings */
+ dcs2.colourname = dsc_alloc_string(dsc,
+ colourname, (int)strlen(colourname));
+ dcs2.filetype = dsc_alloc_string(dsc, "EPS", 3);
+ dcs2.location = dsc_alloc_string(dsc, "Local", 5);
+ if (strlen(filename))
+ dcs2.filename = dsc_alloc_string(dsc,
+ filename, (int)strlen(filename));
+ /* Allocate it */
+ pdcs2 = (CDCS2 *)dsc_memalloc(dsc, sizeof(CDCS2));
+ if (pdcs2 == NULL)
+ return CDSC_ERROR; /* out of memory */
+ memcpy(pdcs2, &dcs2, sizeof(CDCS2));
+ /* Then add to list of separations */
+ if (dsc->dcs2 == NULL)
+ dsc->dcs2 = pdcs2;
+ else {
+ CDCS2 *this_dcs2 = dsc->dcs2;
+ while (this_dcs2->next)
+ this_dcs2 = this_dcs2->next;
+ this_dcs2->next = pdcs2;
+ }
+ }
+ }
+ return CDSC_OK;
+}
+
+/* Find the filename which corresponds to this separation.
+ * Used with multiple file DCS 2.0.
+ * Returns NULL if there is no filename, or not DCS 2.0,
+ * or single file DCS 2.0.
+ * Caller will need to obtain the filesize from the file.
+ */
+const char *
+dsc_find_platefile(CDSC *dsc, int page)
+{
+ CDCS2 *pdcs = dsc->dcs2;
+ int i = 1;
+ while (pdcs) {
+ if (pdcs->begin != pdcs->end)
+ return NULL; /* Single file DCS 2.0 */
+ if (pdcs->location && pdcs->filetype && pdcs->colourname
+ && (dsc_stricmp(pdcs->location, "Local") == 0)
+ && ((dsc_stricmp(pdcs->filetype, "EPS") == 0) ||
+ (dsc_stricmp(pdcs->filetype, "EPSF") == 0))) {
+ if (i == page)
+ return pdcs->filename;
+ i++;
+ }
+ pdcs = pdcs->next;
+ }
+ return NULL;
+}
+
+static CDSCCOLOUR *
+dsc_find_colour(CDSC *dsc, const char *colourname)
+{
+ CDSCCOLOUR *colour = dsc->colours;
+ while (colour) {
+ if (colour->name && (dsc_stricmp(colour->name, colourname)==0))
+ return colour;
+ colour = colour->next;
+ }
+ return 0;
+}
+
+static int
+dsc_parse_process_colours(CDSC *dsc)
+{
+ unsigned int i, n;
+ CDSCCOLOUR *pcolour;
+ char colourname[MAXSTR];
+ GSBOOL blank_line;
+ if (IS_DSC(dsc->line, "%%DocumentProcessColors:"))
+ n = 24;
+ else if (IS_DSC(dsc->line, "%%+"))
+ n = 3;
+ else
+ return CDSC_ERROR; /* error */
+
+ memset(&colourname, 0, sizeof(colourname));
+
+ /* check for blank remainder of line */
+ blank_line = TRUE;
+ for (i=n; i<dsc->line_length; i++) {
+ if (!IS_WHITE_OR_EOL(dsc->line[i])) {
+ blank_line = FALSE;
+ break;
+ }
+ }
+ while (IS_WHITE(dsc->line[n]))
+ n++;
+ if (COMPARE(dsc->line+n, "(atend)")) {
+ if (dsc->scan_section == scan_comments)
+ blank_line = TRUE;
+ else {
+ dsc_unknown(dsc);
+ return CDSC_NOTDSC;
+ }
+ }
+
+ if (!blank_line) {
+ do {
+ dsc_copy_string(colourname, sizeof(colourname),
+ dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i && strlen(colourname)) {
+ if ((pcolour = dsc_find_colour(dsc, colourname)) == NULL) {
+ pcolour = (CDSCCOLOUR *)
+ dsc_memalloc(dsc, sizeof(CDSCCOLOUR));
+ if (pcolour == NULL)
+ return CDSC_ERROR; /* out of memory */
+ memset(pcolour, 0, sizeof(CDSCCOLOUR));
+ pcolour->custom = CDSC_CUSTOM_COLOUR_UNKNOWN;
+ pcolour->name = dsc_alloc_string(dsc,
+ colourname, (int)strlen(colourname));
+ if (dsc->colours == NULL)
+ dsc->colours = pcolour;
+ else {
+ CDSCCOLOUR *this_colour = dsc->colours;
+ while (this_colour->next)
+ this_colour = this_colour->next;
+ this_colour->next = pcolour;
+ }
+ }
+ pcolour->type = CDSC_COLOUR_PROCESS;
+ if (dsc_stricmp(colourname, "Cyan")==0) {
+ pcolour->custom = CDSC_CUSTOM_COLOUR_CMYK;
+ pcolour->cyan = 1.0;
+ pcolour->magenta = pcolour->yellow = pcolour->black = 0.0;
+ }
+ else if (dsc_stricmp(colourname, "Magenta")==0) {
+ pcolour->custom = CDSC_CUSTOM_COLOUR_CMYK;
+ pcolour->magenta = 1.0;
+ pcolour->cyan = pcolour->yellow = pcolour->black = 0.0;
+ }
+ else if (dsc_stricmp(colourname, "Yellow")==0) {
+ pcolour->custom = CDSC_CUSTOM_COLOUR_CMYK;
+ pcolour->yellow = 1.0;
+ pcolour->cyan = pcolour->magenta = pcolour->black = 0.0;
+ }
+ else if (dsc_stricmp(colourname, "Black")==0) {
+ pcolour->custom = CDSC_CUSTOM_COLOUR_CMYK;
+ pcolour->black = 1.0;
+ pcolour->cyan = pcolour->magenta = pcolour->yellow = 0.0;
+ }
+ else if (dsc_stricmp(colourname, "Red")==0) {
+ pcolour->custom = CDSC_CUSTOM_COLOUR_RGB;
+ pcolour->red = 1.0;
+ pcolour->green = pcolour->blue = 0.0;
+ }
+ else if (dsc_stricmp(colourname, "Green")==0) {
+ pcolour->custom = CDSC_CUSTOM_COLOUR_RGB;
+ pcolour->green = 1.0;
+ pcolour->red = pcolour->blue = 0.0;
+ }
+ else if (dsc_stricmp(colourname, "Blue")==0) {
+ pcolour->custom = CDSC_CUSTOM_COLOUR_RGB;
+ pcolour->blue = 1.0;
+ pcolour->red = pcolour->green = 0.0;
+ }
+ }
+ } while (i != 0);
+ }
+ return CDSC_OK;
+}
+
+static int
+dsc_parse_custom_colours(CDSC *dsc)
+{
+ unsigned int i, n;
+ CDSCCOLOUR *pcolour;
+ char colourname[MAXSTR];
+ GSBOOL blank_line;
+ if (IS_DSC(dsc->line, "%%DocumentCustomColors:"))
+ n = 23;
+ else if (IS_DSC(dsc->line, "%%+"))
+ n = 3;
+ else
+ return CDSC_ERROR; /* error */
+
+ memset(&colourname, 0, sizeof(colourname));
+
+ /* check for blank remainder of line */
+ blank_line = TRUE;
+ for (i=n; i<dsc->line_length; i++) {
+ if (!IS_WHITE_OR_EOL(dsc->line[i])) {
+ blank_line = FALSE;
+ break;
+ }
+ }
+ while (IS_WHITE(dsc->line[n]))
+ n++;
+ if (COMPARE(dsc->line+n, "(atend)")) {
+ if (dsc->scan_section == scan_comments)
+ blank_line = TRUE;
+ else {
+ dsc_unknown(dsc);
+ return CDSC_NOTDSC;
+ }
+ }
+
+ if (!blank_line) {
+ do {
+ dsc_copy_string(colourname, sizeof(colourname),
+ dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i && strlen(colourname)) {
+ if ((pcolour = dsc_find_colour(dsc, colourname)) == NULL) {
+ pcolour = (CDSCCOLOUR *)
+ dsc_memalloc(dsc, sizeof(CDSCCOLOUR));
+ if (pcolour == NULL)
+ return CDSC_ERROR; /* out of memory */
+ memset(pcolour, 0, sizeof(CDSCCOLOUR));
+ pcolour->name = dsc_alloc_string(dsc,
+ colourname, (int)strlen(colourname));
+ pcolour->custom = CDSC_CUSTOM_COLOUR_UNKNOWN;
+ if (dsc->colours == NULL)
+ dsc->colours = pcolour;
+ else {
+ CDSCCOLOUR *this_colour = dsc->colours;
+ while (this_colour->next)
+ this_colour = this_colour->next;
+ this_colour->next = pcolour;
+ }
+ }
+ pcolour->type = CDSC_COLOUR_CUSTOM;
+ }
+ } while (i != 0);
+ }
+ return CDSC_OK;
+}
+
+static int
+dsc_parse_cmyk_custom_colour(CDSC *dsc)
+{
+ unsigned int i, n;
+ CDSCCOLOUR *pcolour;
+ char colourname[MAXSTR];
+ float cyan, magenta, yellow, black;
+ GSBOOL blank_line;
+ if (IS_DSC(dsc->line, "%%CMYKCustomColor:"))
+ n = 18;
+ else if (IS_DSC(dsc->line, "%%+"))
+ n = 3;
+ else
+ return CDSC_ERROR; /* error */
+
+ memset(&colourname, 0, sizeof(colourname));
+
+ /* check for blank remainder of line */
+
+ do {
+ blank_line = TRUE;
+ for (i=n; i<dsc->line_length; i++) {
+ if (!IS_WHITE_OR_EOL(dsc->line[i])) {
+ blank_line = FALSE;
+ break;
+ }
+ }
+ if (blank_line)
+ break;
+ else {
+ cyan = magenta = yellow = black = 0.0;
+ cyan = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ magenta = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ yellow = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ black = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ dsc_copy_string(colourname, sizeof(colourname),
+ dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i && strlen(colourname)) {
+ if ((pcolour = dsc_find_colour(dsc, colourname)) == NULL) {
+ pcolour = (CDSCCOLOUR *)
+ dsc_memalloc(dsc, sizeof(CDSCCOLOUR));
+ if (pcolour == NULL)
+ return CDSC_ERROR; /* out of memory */
+ memset(pcolour, 0, sizeof(CDSCCOLOUR));
+ pcolour->name = dsc_alloc_string(dsc,
+ colourname, (int)strlen(colourname));
+ pcolour->type = CDSC_COLOUR_UNKNOWN;
+ if (dsc->colours == NULL)
+ dsc->colours = pcolour;
+ else {
+ CDSCCOLOUR *this_colour = dsc->colours;
+ while (this_colour->next)
+ this_colour = this_colour->next;
+ this_colour->next = pcolour;
+ }
+ }
+ pcolour->custom = CDSC_CUSTOM_COLOUR_CMYK;
+ pcolour->cyan = cyan;
+ pcolour->magenta = magenta;
+ pcolour->yellow = yellow;
+ pcolour->black = black;
+ }
+ }
+ } while (i != 0);
+ return CDSC_OK;
+}
+
+static int
+dsc_parse_rgb_custom_colour(CDSC *dsc)
+{
+ unsigned int i, n;
+ CDSCCOLOUR *pcolour;
+ char colourname[MAXSTR];
+ float red, green, blue;
+ GSBOOL blank_line;
+ if (IS_DSC(dsc->line, "%%RGBCustomColor:"))
+ n = 17;
+ else if (IS_DSC(dsc->line, "%%+"))
+ n = 3;
+ else
+ return CDSC_ERROR; /* error */
+
+ memset(&colourname, 0, sizeof(colourname));
+
+ /* check for blank remainder of line */
+
+ do {
+ blank_line = TRUE;
+ for (i=n; i<dsc->line_length; i++) {
+ if (!IS_WHITE_OR_EOL(dsc->line[i])) {
+ blank_line = FALSE;
+ break;
+ }
+ }
+ if (blank_line)
+ break;
+ else {
+ red = green = blue = 0.0;
+ red = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ green = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ blue = dsc_get_real(dsc->line+n, dsc->line_length-n, &i);
+ n += i;
+ if (i)
+ dsc_copy_string(colourname, sizeof(colourname),
+ dsc->line+n, dsc->line_length-n, &i);
+ n+=i;
+ if (i && strlen(colourname)) {
+ if ((pcolour = dsc_find_colour(dsc, colourname)) == NULL) {
+ pcolour = (CDSCCOLOUR *)
+ dsc_memalloc(dsc, sizeof(CDSCCOLOUR));
+ if (pcolour == NULL)
+ return CDSC_ERROR; /* out of memory */
+ memset(pcolour, 0, sizeof(CDSCCOLOUR));
+ pcolour->name = dsc_alloc_string(dsc,
+ colourname, (int)strlen(colourname));
+ pcolour->type = CDSC_COLOUR_UNKNOWN;
+ if (dsc->colours == NULL)
+ dsc->colours = pcolour;
+ else {
+ CDSCCOLOUR *this_colour = dsc->colours;
+ while (this_colour->next)
+ this_colour = this_colour->next;
+ this_colour->next = pcolour;
+ }
+ }
+ pcolour->custom = CDSC_CUSTOM_COLOUR_RGB;
+ pcolour->red = red;
+ pcolour->green = green;
+ pcolour->blue = blue;
+ }
+ }
+ } while (i != 0);
+ return CDSC_OK;
+}
diff --git a/psi/dscparse.h b/psi/dscparse.h
new file mode 100644
index 000000000..7646ef7d5
--- /dev/null
+++ b/psi/dscparse.h
@@ -0,0 +1,555 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface for the DSC parser. */
+
+#ifndef dscparse_INCLUDED
+# define dscparse_INCLUDED
+
+/* Some local types that may need modification */
+typedef int GSBOOL;
+typedef unsigned long GSDWORD; /* must be at least 32 bits */
+typedef unsigned int GSWORD; /* must be at least 16 bits */
+
+#ifndef FALSE
+# define FALSE ((GSBOOL)0)
+# define TRUE ((GSBOOL)(!FALSE))
+#endif
+
+/* DSC_OFFSET is an unsigned integer which holds the offset
+ * from the start of a file to a particular DSC comment,
+ * or the length of a file.
+ * Normally it is "unsigned long" which is commonly 32 bits.
+ * Change it if you need to handle larger files.
+ */
+#ifndef DSC_OFFSET
+# define DSC_OFFSET unsigned long
+#endif
+#ifndef DSC_OFFSET_FORMAT
+# define DSC_OFFSET_FORMAT "lu" /* for printf */
+#endif
+
+#ifndef min
+# define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef max
+# define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+/* maximum legal length of lines in a DSC compliant file */
+#define DSC_LINE_LENGTH 255
+
+/* memory for strings is allocated in chunks of this length */
+#define CDSC_STRING_CHUNK 4096
+
+/* page array is allocated in chunks of this many pages */
+#define CDSC_PAGE_CHUNK 128
+
+/* buffer length for storing lines passed to dsc_scan_data() */
+/* must be at least 2 * DSC_LINE_LENGTH */
+/* We choose 8192 as twice the length passed to us by GSview */
+#define CDSC_DATA_LENGTH 8192
+
+/* Return codes from dsc_scan_data()
+ * < 0 = error
+ * >=0 = OK
+ *
+ * -1 = error, usually insufficient memory.
+ * 0-9 = normal
+ * 10-99 = internal codes, should not be seen.
+ * 100-999 = identifier of last DSC comment processed.
+ */
+
+typedef enum CDSC_RETURN_CODE_e {
+ CDSC_ERROR = -1, /* Fatal error, usually insufficient memory */
+
+ CDSC_OK = 0, /* OK, no DSC comment found */
+ CDSC_NOTDSC = 1, /* Not DSC, or DSC is being ignored */
+
+/* Any section */
+ CDSC_UNKNOWNDSC = 100, /* DSC comment not recognised */
+
+/* Header section */
+ CDSC_PSADOBE = 200, /* %!PS-Adobe- */
+ CDSC_BEGINCOMMENTS = 201, /* %%BeginComments */
+ CDSC_ENDCOMMENTS = 202, /* %%EndComments */
+ CDSC_PAGES = 203, /* %%Pages: */
+ CDSC_CREATOR = 204, /* %%Creator: */
+ CDSC_CREATIONDATE = 205, /* %%CreationDate: */
+ CDSC_TITLE = 206, /* %%Title: */
+ CDSC_FOR = 207, /* %%For: */
+ CDSC_LANGUAGELEVEL = 208, /* %%LanguageLevel: */
+ CDSC_BOUNDINGBOX = 209, /* %%BoundingBox: */
+ CDSC_ORIENTATION = 210, /* %%Orientation: */
+ CDSC_PAGEORDER = 211, /* %%PageOrder: */
+ CDSC_DOCUMENTMEDIA = 212, /* %%DocumentMedia: */
+ CDSC_DOCUMENTPAPERSIZES = 213, /* %%DocumentPaperSizes: */
+ CDSC_DOCUMENTPAPERFORMS = 214, /* %%DocumentPaperForms: */
+ CDSC_DOCUMENTPAPERCOLORS = 215, /* %%DocumentPaperColors: */
+ CDSC_DOCUMENTPAPERWEIGHTS = 216, /* %%DocumentPaperWeights: */
+ CDSC_DOCUMENTDATA = 217, /* %%DocumentData: */
+ CDSC_REQUIREMENTS = 218, /* IGNORED %%Requirements: */
+ CDSC_DOCUMENTNEEDEDFONTS = 219, /* IGNORED %%DocumentNeededFonts: */
+ CDSC_DOCUMENTSUPPLIEDFONTS = 220, /* IGNORED %%DocumentSuppliedFonts: */
+ CDSC_HIRESBOUNDINGBOX = 221, /* %%HiResBoundingBox: */
+ CDSC_CROPBOX = 222, /* %%CropBox: */
+ CDSC_PLATEFILE = 223, /* %%PlateFile: (DCS 2.0) */
+ CDSC_DOCUMENTPROCESSCOLORS = 224, /* %%DocumentProcessColors: */
+ CDSC_DOCUMENTCUSTOMCOLORS = 225, /* %%DocumentCustomColors: */
+ CDSC_CMYKCUSTOMCOLOR = 226, /* %%CMYKCustomColor: */
+ CDSC_RGBCUSTOMCOLOR = 227, /* %%RGBCustomColor: */
+
+/* Preview section */
+ CDSC_BEGINPREVIEW = 301, /* %%BeginPreview */
+ CDSC_ENDPREVIEW = 302, /* %%EndPreview */
+
+/* Defaults section */
+ CDSC_BEGINDEFAULTS = 401, /* %%BeginDefaults */
+ CDSC_ENDDEFAULTS = 402, /* %%EndDefaults */
+/* also %%PageMedia, %%PageOrientation, %%PageBoundingBox */
+
+/* Prolog section */
+ CDSC_BEGINPROLOG = 501, /* %%BeginProlog */
+ CDSC_ENDPROLOG = 502, /* %%EndProlog */
+ CDSC_BEGINFONT = 503, /* IGNORED %%BeginFont */
+ CDSC_ENDFONT = 504, /* IGNORED %%EndFont */
+ CDSC_BEGINFEATURE = 505, /* IGNORED %%BeginFeature */
+ CDSC_ENDFEATURE = 506, /* IGNORED %%EndFeature */
+ CDSC_BEGINRESOURCE = 507, /* IGNORED %%BeginResource */
+ CDSC_ENDRESOURCE = 508, /* IGNORED %%EndResource */
+ CDSC_BEGINPROCSET = 509, /* IGNORED %%BeginProcSet */
+ CDSC_ENDPROCSET = 510, /* IGNORED %%EndProcSet */
+
+/* Setup section */
+ CDSC_BEGINSETUP = 601, /* %%BeginSetup */
+ CDSC_ENDSETUP = 602, /* %%EndSetup */
+ CDSC_FEATURE = 603, /* IGNORED %%Feature: */
+ CDSC_PAPERCOLOR = 604, /* IGNORED %%PaperColor: */
+ CDSC_PAPERFORM = 605, /* IGNORED %%PaperForm: */
+ CDSC_PAPERWEIGHT = 606, /* IGNORED %%PaperWeight: */
+ CDSC_PAPERSIZE = 607, /* %%PaperSize: */
+/* also %%Begin/EndFeature, %%Begin/EndResource */
+
+/* Page section */
+ CDSC_PAGE = 700, /* %%Page: */
+ CDSC_PAGETRAILER = 701, /* IGNORED %%PageTrailer */
+ CDSC_BEGINPAGESETUP = 702, /* IGNORED %%BeginPageSetup */
+ CDSC_ENDPAGESETUP = 703, /* IGNORED %%EndPageSetup */
+ CDSC_PAGEMEDIA = 704, /* %%PageMedia: */
+/* also %%PaperColor, %%PaperForm, %%PaperWeight, %%PaperSize */
+ CDSC_PAGEORIENTATION = 705, /* %%PageOrientation: */
+ CDSC_PAGEBOUNDINGBOX = 706, /* %%PageBoundingBox: */
+/* also %%Begin/EndFont, %%Begin/EndFeature */
+/* also %%Begin/EndResource, %%Begin/EndProcSet */
+ CDSC_INCLUDEFONT = 707, /* IGNORED %%IncludeFont: */
+ CDSC_VIEWINGORIENTATION = 708, /* %%ViewingOrientation: */
+ CDSC_PAGECROPBOX = 709, /* %%PageCropBox: */
+
+/* Trailer section */
+ CDSC_TRAILER = 800, /* %%Trailer */
+/* also %%Pages, %%BoundingBox, %%Orientation, %%PageOrder, %%DocumentMedia */
+/* %%Page is recognised as an error */
+/* also %%DocumentNeededFonts, %%DocumentSuppliedFonts */
+
+/* End of File */
+ CDSC_EOF = 900 /* %%EOF */
+} CDSC_RETURN_CODE;
+
+/* stored in dsc->preview */
+typedef enum CDSC_PREVIEW_TYPE_e {
+ CDSC_NOPREVIEW = 0,
+ CDSC_EPSI = 1,
+ CDSC_TIFF = 2,
+ CDSC_WMF = 3,
+ CDSC_PICT = 4
+} CDSC_PREVIEW_TYPE;
+
+/* stored in dsc->page_order */
+typedef enum CDSC_PAGE_ORDER_e {
+ CDSC_ORDER_UNKNOWN = 0,
+ CDSC_ASCEND = 1,
+ CDSC_DESCEND = 2,
+ CDSC_SPECIAL = 3
+} CDSC_PAGE_ORDER;
+
+/* stored in dsc->page_orientation and dsc->page[pagenum-1].orientation */
+typedef enum CDSC_ORIENTATION_ENUM_e {
+ CDSC_ORIENT_UNKNOWN = 0,
+ CDSC_PORTRAIT = 1,
+ CDSC_LANDSCAPE = 2,
+ CDSC_UPSIDEDOWN = 3,
+ CDSC_SEASCAPE = 4
+} CDSC_ORIENTATION_ENUM;
+
+/* stored in dsc->document_data */
+typedef enum CDSC_DOCUMENT_DATA_e {
+ CDSC_DATA_UNKNOWN = 0,
+ CDSC_CLEAN7BIT = 1,
+ CDSC_CLEAN8BIT = 2,
+ CDSC_BINARY = 3
+} CDSC_DOCUMENT_DATA ;
+
+typedef struct CDSCBBOX_S {
+ int llx;
+ int lly;
+ int urx;
+ int ury;
+} CDSCBBOX;
+
+typedef struct CDSCFBBOX_S {
+ float fllx;
+ float flly;
+ float furx;
+ float fury;
+} CDSCFBBOX;
+
+typedef struct CDSCMEDIA_S {
+ const char *name;
+ float width; /* PostScript points */
+ float height;
+ float weight; /* GSM */
+ const char *colour;
+ const char *type;
+ CDSCBBOX *mediabox; /* Used by GSview for PDF MediaBox */
+} CDSCMEDIA;
+
+#define CDSC_KNOWN_MEDIA 11
+extern const CDSCMEDIA dsc_known_media[CDSC_KNOWN_MEDIA];
+
+typedef struct CDSCCTM_S { /* used for %%ViewingOrientation */
+ float xx;
+ float xy;
+ float yx;
+ float yy;
+ /* float ty; */
+ /* float ty; */
+} CDSCCTM;
+
+typedef struct CDSCPAGE_S {
+ int ordinal;
+ const char *label;
+ DSC_OFFSET begin;
+ DSC_OFFSET end;
+ unsigned int orientation;
+ const CDSCMEDIA *media;
+ CDSCBBOX *bbox; /* PageBoundingBox, also used by GSview for PDF CropBox */
+ CDSCCTM *viewing_orientation;
+ /* Added 2001-10-19 */
+ CDSCFBBOX *crop_box; /* CropBox */
+} CDSCPAGE;
+
+/* binary DOS EPS header */
+typedef struct CDSCDOSEPS_S {
+ GSDWORD ps_begin;
+ GSDWORD ps_length;
+ GSDWORD wmf_begin;
+ GSDWORD wmf_length;
+ GSDWORD tiff_begin;
+ GSDWORD tiff_length;
+ GSWORD checksum;
+} CDSCDOSEPS;
+
+/* macbinary header */
+typedef struct CDSCMACBIN_S {
+ GSDWORD data_begin; /* EPS */
+ GSDWORD data_length;
+ GSDWORD resource_begin; /* PICT */
+ GSDWORD resource_length;
+} CDSCMACBIN;
+
+/* rather than allocated every string with malloc, we allocate
+ * chunks of 4k and place the (usually) short strings in these
+ * chunks.
+ */
+typedef struct CDSCSTRING_S CDSCSTRING;
+struct CDSCSTRING_S {
+ unsigned int index;
+ unsigned int length;
+ char *data;
+ CDSCSTRING *next;
+};
+
+/* Desktop Color Separations - DCS 2.0 */
+typedef struct CDCS2_S CDCS2;
+struct CDCS2_S {
+ char *colourname;
+ char *filetype; /* Usually EPS */
+ /* For multiple file DCS, location and filename will be set */
+ char *location; /* Local or NULL */
+ char *filename;
+ /* For single file DCS, begin will be not equals to end */
+ DSC_OFFSET begin;
+ DSC_OFFSET end;
+ /* We maintain the separations as a linked list */
+ CDCS2 *next;
+};
+
+typedef enum CDSC_COLOUR_TYPE_e {
+ CDSC_COLOUR_UNKNOWN=0,
+ CDSC_COLOUR_PROCESS=1, /* %%DocumentProcessColors: */
+ CDSC_COLOUR_CUSTOM=2 /* %%DocumentCustomColors: */
+} CDSC_COLOUR_TYPE;
+
+typedef enum CDSC_CUSTOM_COLOUR_e {
+ CDSC_CUSTOM_COLOUR_UNKNOWN=0,
+ CDSC_CUSTOM_COLOUR_RGB=1, /* %%RGBCustomColor: */
+ CDSC_CUSTOM_COLOUR_CMYK=2 /* %%CMYKCustomColor: */
+} CDSC_CUSTOM_COLOUR;
+
+typedef struct CDSCCOLOUR_S CDSCCOLOUR;
+struct CDSCCOLOUR_S {
+ char *name;
+ CDSC_COLOUR_TYPE type;
+ CDSC_CUSTOM_COLOUR custom;
+ /* If custom is CDSC_CUSTOM_COLOUR_RGB, the next three are correct */
+ float red;
+ float green;
+ float blue;
+ /* If colourtype is CDSC_CUSTOM_COLOUR_CMYK, the next four are correct */
+ float cyan;
+ float magenta;
+ float yellow;
+ float black;
+ /* Next colour */
+ CDSCCOLOUR *next;
+};
+
+/* DSC error reporting */
+
+typedef enum CDSC_MESSAGE_ERROR_e {
+ CDSC_MESSAGE_BBOX = 0,
+ CDSC_MESSAGE_EARLY_TRAILER = 1,
+ CDSC_MESSAGE_EARLY_EOF = 2,
+ CDSC_MESSAGE_PAGE_IN_TRAILER = 3,
+ CDSC_MESSAGE_PAGE_ORDINAL = 4,
+ CDSC_MESSAGE_PAGES_WRONG = 5,
+ CDSC_MESSAGE_EPS_NO_BBOX = 6,
+ CDSC_MESSAGE_EPS_PAGES = 7,
+ CDSC_MESSAGE_NO_MEDIA = 8,
+ CDSC_MESSAGE_ATEND = 9,
+ CDSC_MESSAGE_DUP_COMMENT = 10,
+ CDSC_MESSAGE_DUP_TRAILER = 11,
+ CDSC_MESSAGE_BEGIN_END = 12,
+ CDSC_MESSAGE_BAD_SECTION = 13,
+ CDSC_MESSAGE_LONG_LINE = 14,
+ CDSC_MESSAGE_INCORRECT_USAGE = 15
+} CDSC_MESSAGE_ERROR;
+
+/* severity */
+typedef enum CDSC_MESSAGE_SEVERITY_e {
+ CDSC_ERROR_INFORM = 0, /* Not an error */
+ CDSC_ERROR_WARN = 1, /* Not a DSC error itself, */
+ CDSC_ERROR_ERROR = 2 /* DSC error */
+} CDSC_MESSAGE_SEVERITY;
+
+/* response */
+typedef enum CDSC_RESPONSE_e {
+ CDSC_RESPONSE_OK = 0,
+ CDSC_RESPONSE_CANCEL = 1,
+ CDSC_RESPONSE_IGNORE_ALL = 2
+} CDSC_RESPONSE;
+
+extern const char * const dsc_message[];
+
+#ifndef CDSC_TYPEDEF
+#define CDSC_TYPEDEF
+typedef struct CDSC_s CDSC;
+#endif
+
+struct CDSC_s {
+char dummy[1024];
+ /* public data */
+ GSBOOL dsc; /* TRUE if DSC comments found */
+ GSBOOL ctrld; /* TRUE if has CTRLD at start of stream */
+ GSBOOL pjl; /* TRUE if has HP PJL at start of stream */
+ GSBOOL epsf; /* TRUE if EPSF */
+ GSBOOL pdf; /* TRUE if Portable Document Format */
+ unsigned int preview; /* enum CDSC_PREVIEW_TYPE */
+ char *dsc_version; /* first line of file */
+ unsigned int language_level;
+ unsigned int document_data; /* Clean7Bit, Clean8Bit, Binary */
+ /* enum CDSC_DOCUMENT_DATA */
+ /* DSC sections */
+ DSC_OFFSET begincomments;
+ DSC_OFFSET endcomments;
+ DSC_OFFSET beginpreview;
+ DSC_OFFSET endpreview;
+ DSC_OFFSET begindefaults;
+ DSC_OFFSET enddefaults;
+ DSC_OFFSET beginprolog;
+ DSC_OFFSET endprolog;
+ DSC_OFFSET beginsetup;
+ DSC_OFFSET endsetup;
+ DSC_OFFSET begintrailer;
+ DSC_OFFSET endtrailer;
+ CDSCPAGE *page;
+ unsigned int page_count; /* number of %%Page: pages in document */
+ unsigned int page_pages; /* number of pages in document from %%Pages: */
+ unsigned int page_order; /* enum CDSC_PAGE_ORDER */
+ unsigned int page_orientation; /* the default page orientation */
+ /* enum CDSC_ORIENTATION */
+ CDSCCTM *viewing_orientation;
+ unsigned int media_count; /* number of media items */
+ CDSCMEDIA **media; /* the array of media */
+ const CDSCMEDIA *page_media;/* the default page media */
+ CDSCBBOX *bbox; /* the document bounding box */
+ CDSCBBOX *page_bbox; /* the default page bounding box */
+ CDSCDOSEPS *doseps; /* DOS binary header */
+ char *dsc_title;
+ char *dsc_creator;
+ char *dsc_date;
+ char *dsc_for;
+
+ unsigned int max_error; /* highest error number that will be reported */
+ const int *severity; /* array of severity values, one per error */
+
+ /* private data */
+ void *caller_data; /* pointer to be provided when calling */
+ /* error and debug callbacks */
+ int id; /* last DSC comment found */
+ int scan_section; /* section currently being scanned */
+ /* enum CDSC_SECTION */
+
+ DSC_OFFSET doseps_end; /* ps_begin+ps_length, otherwise 0 */
+ unsigned int page_chunk_length; /* number of pages allocated */
+ DSC_OFFSET file_length; /* length of document */
+ /* If provided we try to recognise %%Trailer and %%EOF */
+ /* incorrectly embedded inside document. */
+ /* We will not parse DSC comments beyond this point. */
+ /* Can be left set to default value of 0 */
+ int skip_document; /* recursion level of %%BeginDocument: */
+ int skip_bytes; /* #bytes to ignore from BeginData: */
+ /* or DOSEPS preview section */
+ int skip_lines; /* #lines to ignore from BeginData: */
+ GSBOOL skip_pjl; /* TRUE if skip PJL until first PS comment */
+ int begin_font_count; /* recursion level of %%BeginFont */
+ int begin_feature_count; /* recursion level of %%BeginFeature */
+ int begin_resource_count; /* recursion level of %%BeginResource */
+ int begin_procset_count; /* recursion level of %%BeginProcSet */
+
+ /* buffer for input */
+ char data[CDSC_DATA_LENGTH];/* start of buffer */
+ unsigned int data_length; /* length of data in buffer */
+ unsigned int data_index; /* offset to next char in buffer */
+ DSC_OFFSET data_offset; /* offset from start of document */
+ /* to byte in data[0] */
+ GSBOOL eof; /* TRUE if there is no more data */
+
+ /* information about DSC line */
+ char *line; /* pointer to last read DSC line */
+ /* not null terminated */
+ unsigned int line_length; /* number of characters in line */
+ GSBOOL eol; /* TRUE if dsc_line contains EOL */
+ GSBOOL last_cr; /* TRUE if last line ended in \r */
+ /* check next time for \n */
+ unsigned int line_count; /* line number */
+ GSBOOL long_line; /* TRUE if found a line longer than 255 characters */
+ char last_line[256]; /* previous DSC line, used for %%+ */
+
+ /* more efficient string storage (for short strings) than malloc */
+ CDSCSTRING *string_head; /* linked list head */
+ CDSCSTRING *string; /* current list item */
+
+ /* memory allocation routines */
+ void *(*memalloc)(size_t size, void *closure_data);
+ void (*memfree)(void *ptr, void *closure_data);
+ void *mem_closure_data;
+
+ /* function for printing debug messages */
+ void (*debug_print_fn)(void *caller_data, const char *str);
+
+ /* function for reporting errors in DSC comments */
+ int (*dsc_error_fn)(void *caller_data, CDSC *dsc,
+ unsigned int explanation, const char *line, unsigned int line_len);
+
+ /* public data */
+ /* Added 2001-10-01 */
+ CDSCFBBOX *hires_bbox; /* the hires document bounding box */
+ CDSCFBBOX *crop_box; /* the size of the trimmed page */
+ CDCS2 *dcs2; /* Desktop Color Separations 2.0 */
+ CDSCCOLOUR *colours; /* Process and custom colours */
+
+ /* private data */
+ /* Added 2002-03-30 */
+ int ref_count;
+
+ /* public data */
+ /* Added 2003-07-15 */
+ CDSCMACBIN *macbin; /* Mac Binary header */
+};
+
+/* Public functions */
+
+/* Create and initialise DSC parser */
+CDSC *dsc_init(void *caller_data);
+
+CDSC *dsc_init_with_alloc(
+ void *caller_data,
+ void *(*memalloc)(size_t size, void *closure_data),
+ void (*memfree)(void *ptr, void *closure_data),
+ void *closure_data);
+
+/* Free the DSC parser */
+void dsc_free(CDSC *dsc);
+
+/* Reference counting for dsc structure */
+/* dsc_new is the same as dsc_init */
+/* dsc_ref is called by dsc_new */
+/* If dsc_unref decrements to 0, dsc_free will be called */
+CDSC *dsc_new(void *caller_data);
+int dsc_ref(CDSC *dsc);
+int dsc_unref(CDSC *dsc);
+
+/* Tell DSC parser how long document will be, to allow ignoring
+ * of early %%Trailer and %%EOF. This is optional.
+ */
+void dsc_set_length(CDSC *dsc, DSC_OFFSET len);
+
+/* Process a buffer containing DSC comments and PostScript */
+int dsc_scan_data(CDSC *dsc, const char *data, int len);
+
+/* All data has been processed, fixup any DSC errors */
+int dsc_fixup(CDSC *dsc);
+
+/* Install error query function */
+void dsc_set_error_function(CDSC *dsc,
+ int (*dsc_error_fn)(void *caller_data, CDSC *dsc,
+ unsigned int explanation, const char *line, unsigned int line_len));
+
+/* Install print function for debug messages */
+void dsc_set_debug_function(CDSC *dsc,
+ void (*debug_fn)(void *caller_data, const char *str));
+
+/* Print a message to debug output, if provided */
+void dsc_debug_print(CDSC *dsc, const char *str);
+
+/* Given a page number, find the filename for multi-file DCS 2.0 */
+const char * dsc_find_platefile(CDSC *dsc, int page);
+
+/* Compare two strings, case insensitive */
+int dsc_stricmp(const char *s, const char *t);
+
+/* should be internal only functions, but made available to
+ * GSview for handling PDF
+ */
+int dsc_add_page(CDSC *dsc, int ordinal, char *label);
+int dsc_add_media(CDSC *dsc, CDSCMEDIA *media);
+int dsc_set_page_bbox(CDSC *dsc, unsigned int page_number,
+ int llx, int lly, int urx, int ury);
+
+/* in dscutil.c */
+void dsc_display(CDSC *dsc, void (*dfn)(void *ptr, const char *str));
+#endif /* dscparse_INCLUDED */
diff --git a/psi/dstack.h b/psi/dstack.h
new file mode 100644
index 000000000..e27eb242e
--- /dev/null
+++ b/psi/dstack.h
@@ -0,0 +1,300 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definitions for the interpreter's dictionary stack */
+
+#ifndef dstack_INCLUDED
+# define dstack_INCLUDED
+
+#include "idstack.h"
+#include "icstate.h" /* for access to dict_stack */
+
+/* Define the dictionary stack instance for operators. */
+#define idict_stack (i_ctx_p->dict_stack)
+#define d_stack (idict_stack.stack)
+
+/* Define the interpreter-specific versions of the generic dstack API. */
+#define min_dstack_size (idict_stack.min_size)
+#define dstack_userdict_index (idict_stack.userdict_index)
+#define dsspace (idict_stack.def_space)
+#define dtop_can_store(pvalue) ((int)r_space(pvalue) <= dsspace)
+#define dtop_keys (idict_stack.top_keys)
+#define dtop_npairs (idict_stack.top_npairs)
+#define dtop_values (idict_stack.top_values)
+#define dict_set_top() dstack_set_top(&idict_stack);
+#define dict_is_permanent_on_dstack(pdict)\
+ dstack_dict_is_permanent(&idict_stack, pdict)
+#define dicts_gc_cleanup() dstack_gc_cleanup(&idict_stack)
+#define systemdict (&idict_stack.system_dict)
+
+/* Define the dictionary stack pointers. */
+#define dsbot (d_stack.bot)
+#define dsp (d_stack.p)
+#define dstop (d_stack.top)
+
+/* Macro to ensure enough room on the dictionary stack */
+#define check_dstack(n)\
+ if ( dstop - dsp < (n) )\
+ { int ds_code_ = ref_stack_extend(&d_stack, n);\
+ if ( ds_code_ < 0 ) return ds_code_;\
+ }
+
+/*
+ * The dictionary stack is implemented as a linked list of blocks;
+ * operators that access the entire d-stack must take this into account.
+ * These are:
+ * countdictstack dictstack
+ * In addition, name lookup requires searching the entire stack, not just
+ * the top block, and the underflow check for the dictionary stack
+ * (`end' operator) is not just a check for underflowing the top block.
+ */
+
+/* Name lookup */
+#define dict_find_name_by_index(nidx)\
+ dstack_find_name_by_index(&idict_stack, nidx)
+#define dict_find_name(pnref) dict_find_name_by_index(name_index(imemory, pnref))
+#define dict_find_name_by_index_inline(nidx, htemp)\
+ dstack_find_name_by_index_inline(&idict_stack, nidx, htemp)
+#define if_dict_find_name_by_index_top(nidx, htemp, pvslot)\
+ if_dstack_find_name_by_index_top(&idict_stack, nidx, htemp, pvslot)
+
+/*
+Notes on dictionary lookup performance
+======================================
+
+We mark heavily used operations with a * below; moderately heavily used
+operations with a +.
+
+The following operations look up keys on the dictionary stack:
+ *(interpreter name lookup)
+ load
+ where
+
+The following operations change the contents of dictionaries:
+ *def, +put
+ undef
+ restore
+ (grow)
+We implement store in PostScript, and copy as a series of puts. Many
+other operators also do puts (e.g., ScaleMatrix in makefont,
+Implementation in makepattern, ...). Note that put can do an implicit
+.setmaxlength (if it has to grow the dictionary).
+
+The following operations change the dictionary stack:
+ +begin, +end
+ +?(context switch)
+ readonly (on a dictionary that is on the stack)
+ noaccess (on a dictionary that is on the stack)
+We implement cleardictstack as a series of ends.
+
+Current design
+==============
+
+Each name N has a pointer N.V that has one of 3 states:
+ - This name has no definitions.
+ - This name has exactly one definition, in systemdict or userdict.
+ In this case, N.V points to the value slot.
+ - This name has some other status.
+
+We cache some pointers to the top dictionary on the stack if it is a
+readable dictionary with packed keys, which allows us to do fast,
+single-probe lookups in this dictionary. We also cache a value that
+allows us to do a fast check for stores into the top dictionary
+(writability + space check).
+
+Improved design
+===============
+
+Data structures
+---------------
+
+With each dictionary stack (or equivalently with each context), we
+associate:
+
+ * A name lookup cache, C. Each entry C[i] in the cache consists of:
+
+ A key, K (a name index).
+
+ A dictionary stack level (depth), L. C[i] is valid iff the
+ current dictionary stack depth, |dstack|, is equal to L.
+ (L is always less than or equal to |dstack|.)
+
+ A value pointer, V, which points to some value slot of some
+ dictionary on the stack.
+
+ * A lookup cache restoration stack, R. Each entry R[j] on this stack
+ consists of:
+
+ An index i in C.
+
+ The previous (K,D,V) of C[i].
+
+C needs to be large enough to satisfy the overwhelming majority of name
+lookups with only 1-3 probes. In a single-context system, C can be large
+(perhaps 8K entries = 80K bytes, which is enough to accommodate every name
+in a typical run with no reprobes). In a multiple-context system, one can
+choose a variety of different strategies for managing C, such as:
+
+ A single cache that is cleared on every context switch;
+
+ A small cache (e.g., .5K entries) for each context;
+
+ A cache that starts out small and grows adaptively if the hit rate
+ is too low.
+
+R needs to be able to grow dynamically; in the worst case, it may have |C|
+entries per level of the dictionary stack. We assume that R will always be
+able to grow as needed (i.e., inability to allocate space for R is a
+VMerror, like inability to allocate space for the undo-save information for
+'save').
+
+With each entry E[j] on the dictionary stack, we associate:
+
+ * A value U that gives the depth of R at the time that E[j] was pushed
+ on the stack. E[j].U = 0 for 0 <= j < the initial depth of the dictionary
+ stack (normally 3).
+
+With each dictionary D we associate:
+
+ * A counter S that gives the total number of occurrences of D on all
+ dictionary stacks. If this counter overflows, it is pinned at its maximum
+ value.
+
+In order to be effective, D.S needs to be able to count up to a small
+multiple of the total number of contexts: 16 bits should be plenty.
+
+As at present, we also maintain a pair of pointers that bracket the value
+region of the top dictionary on the stack, for fast checking in def. If the
+top dictionary is readonly or noaccess, the pointers designate an empty
+area. We call this the "def region cache".
+
+Now we describe the implementation of each of the above operations.
+
+(name lookup)
+-------------
+
+To look up a name with index N, compute a hash index 0 <= i < |C|. There
+are three cases:
+
+ 1. C[i].K == N and C[i].L == |dstack|. Nothing more is needed:
+ C[i].V points to the N's value.
+
+ 2. C[i].K == N but C[i].L < |dstack|. Look up N in the top |dstack|
+ - L entries on the dictionary stack; push i and C[i] onto R; set
+ C[i].V to point to the value if found, and in any case set C[i].L =
+ |dstack|.
+
+ 3. C[i].K != N. Reprobe some small number of times.
+
+If all reprobes fail, look up N on the (full) dictionary stack. Pick an
+index i (one of the probed entries) in C to replace. If C[i].L != |dstack|,
+push i and C[i] onto R. Then replace C[i] with K = N, L = |dstack|, and V
+pointing to N's value.
+
+load
+----
+
+Proceed as for name lookup. However, it might be worth not making the new
+cache entry in case 3, since names looked up by load will rarely be looked
+up again.
+
+where
+-----
+
+Just do a full lookup, ignoring C.
+
+def
+---
+
+As at present: start by doing one or two fast probes in the def region
+cache; if they succeed, just store the new value; otherwise, do a normal
+dictionary lookup and access check. If a new dictionary entry is created
+and the key is a name, check all possible probe slots of the name in C; if
+the name is present, update its entry in C as for a lookup. Then if D.S >
+1, scan as for 'grow' below.
+
+put
+---
+
+If the key is a name, the dictionary entry is new, and D.S != 0, scan as for
+'grow' below.
+
+undef
+-----
+
+If the key is a name and D.S != 0, scan as for 'grow' below. It might be
+worth checking for D.S == 1 and D = the top dictionary on the stack as a
+special case, which only requires removing the name from C, similar to
+'def'.
+
+restore
+-------
+
+The undo-save machinery must be enhanced so that grow, def/put, and undef
+operations can be recognized as such. TBD.
+
+(grow)
+------
+
+If D.S == 0, do nothing special. Otherwise, scan C, R, and the dictionary
+stack (first for the current context, and then for other contexts if needed)
+until D.S occurrences of D have been processed. (If D is in local VM, it is
+only necessary to scan contexts that share local VM with the current one; if
+D is in global VM, it is necessary to scan contexts that share global VM
+with the current one.) Entries in C whose V pointed within D's old values
+array are updated; entries in R whose V pointed within the old values array
+are replaced with empty entries.
+
+begin
+-----
+
+After pushing the new dictionary, set dstack[|dstack| - 1].U = |R|. Reset
+the def region cache.
+
+end
+---
+
+Before popping the top entry, for dstack[|dstack| - 1].U <= j < |R|, restore
+C[R[j].i] from R[j].(K,L,V), popping R. Reset the def region cache.
+
+(context switch)
+----------------
+
+Reset the def region cache.
+
+readonly
+--------
+
+If the dictionary is the top one on the stack, reset the def region cache.
+
+noaccess
+--------
+
+If D.S != 0, scan as for 'grow' above, removing every C and R entry whose V
+points into D. Also reset the def region cache if the dictionary is the top
+one on the stack.
+
+(garbage collection)
+--------------------
+
+The garbage collector must mark names referenced from C and R. Dictionaries
+referenced from C and R are also referenced from the dictionary stack, so
+they do not need to be marked specially; however, pointers to those
+dictionaries' values arrays from C and R need to be relocated.
+
+ */
+
+#endif /* dstack_INCLUDED */
diff --git a/psi/dw32c.def b/psi/dw32c.def
new file mode 100644
index 000000000..6a3262807
--- /dev/null
+++ b/psi/dw32c.def
@@ -0,0 +1,4 @@
+; 'Ghostscript Interpreter'
+NAME GSWIN32C
+HEAPSIZE 256
+STACKSIZE 131072
diff --git a/psi/dw64c.def b/psi/dw64c.def
new file mode 100644
index 000000000..9932c0bec
--- /dev/null
+++ b/psi/dw64c.def
@@ -0,0 +1,4 @@
+; 'Ghostscript Interpreter'
+NAME GSWIN64C
+HEAPSIZE 256
+STACKSIZE 131072
diff --git a/psi/dwdll.c b/psi/dwdll.c
new file mode 100644
index 000000000..b8d64c26d
--- /dev/null
+++ b/psi/dwdll.c
@@ -0,0 +1,231 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/* dwdll.c */
+
+#define STRICT
+#include <windows.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "stdpre.h"
+#include "gpgetenv.h"
+#include "gscdefs.h"
+#define GSREVISION gs_revision
+
+#define GSDLLEXPORT
+#define GSDLLAPI CALLBACK
+#define GSDLLCALL
+
+#include "dwdll.h"
+
+#ifdef METRO
+#ifdef _WIN64
+static const char name[] = "gsdll64metro.dll";
+#else
+static const char name[] = "gsdll32metro.dll";
+#endif
+#else
+#ifdef _WIN64
+static const char name[] = "gsdll64.dll";
+#else
+static const char name[] = "gsdll32.dll";
+#endif
+#endif
+
+int load_dll(GSDLL *gsdll, char *last_error, int len)
+{
+char fullname[1024];
+char *p;
+int length;
+gsapi_revision_t rv;
+
+ /* Don't load if already loaded */
+ if (gsdll->hmodule)
+ return 0;
+
+ /* First try to load DLL from the same directory as EXE */
+ GetModuleFileName(GetModuleHandle(NULL), fullname, sizeof(fullname));
+ if ((p = strrchr(fullname,'\\')) != (char *)NULL)
+ p++;
+ else
+ p = fullname;
+ *p = '\0';
+ strcat(fullname, name);
+ gsdll->hmodule = LoadLibrary(fullname);
+
+ /* Next try to load DLL with name in registry or environment variable */
+ if (gsdll->hmodule < (HINSTANCE)HINSTANCE_ERROR) {
+ length = sizeof(fullname);
+ if (gp_getenv("GS_DLL", fullname, &length) == 0)
+ gsdll->hmodule = LoadLibrary(fullname);
+ }
+
+ /* Finally try the system search path */
+ if (gsdll->hmodule < (HINSTANCE)HINSTANCE_ERROR)
+ gsdll->hmodule = LoadLibrary(name);
+
+ if (gsdll->hmodule < (HINSTANCE)HINSTANCE_ERROR) {
+ /* Failed */
+ DWORD err = GetLastError();
+ sprintf(fullname, "Can't load DLL, LoadLibrary error code %ld", err);
+ strncpy(last_error, fullname, len-1);
+ gsdll->hmodule = (HINSTANCE)0;
+ return 1;
+ }
+
+ /* DLL is now loaded */
+ /* Get pointers to functions */
+ gsdll->revision = (PFN_gsapi_revision) GetProcAddress(gsdll->hmodule,
+ "gsapi_revision");
+ if (gsdll->revision == NULL) {
+ strncpy(last_error, "Can't find gsapi_revision\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+ /* check DLL version */
+ if (gsdll->revision(&rv, sizeof(rv)) != 0) {
+ sprintf(fullname, "Unable to identify Ghostscript DLL revision - it must be newer than needed.\n");
+ strncpy(last_error, fullname, len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+ if (rv.revision != GSREVISION) {
+ sprintf(fullname, "Wrong version of DLL found.\n Found version %ld\n Need version %ld\n", rv.revision, GSREVISION);
+ strncpy(last_error, fullname, len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ /* continue loading other functions */
+ gsdll->new_instance = (PFN_gsapi_new_instance) GetProcAddress(gsdll->hmodule,
+ "gsapi_new_instance");
+ if (gsdll->new_instance == NULL) {
+ strncpy(last_error, "Can't find gsapi_new_instance\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ gsdll->delete_instance = (PFN_gsapi_delete_instance) GetProcAddress(gsdll->hmodule,
+ "gsapi_delete_instance");
+ if (gsdll->delete_instance == NULL) {
+ strncpy(last_error, "Can't find gsapi_delete_instance\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ gsdll->set_stdio = (PFN_gsapi_set_stdio) GetProcAddress(gsdll->hmodule,
+ "gsapi_set_stdio");
+ if (gsdll->set_stdio == NULL) {
+ strncpy(last_error, "Can't find gsapi_set_stdio\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ gsdll->set_poll = (PFN_gsapi_set_poll) GetProcAddress(gsdll->hmodule,
+ "gsapi_set_poll");
+ if (gsdll->set_poll == NULL) {
+ strncpy(last_error, "Can't find gsapi_set_poll\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ gsdll->set_display_callback = (PFN_gsapi_set_display_callback)
+ GetProcAddress(gsdll->hmodule, "gsapi_set_display_callback");
+ if (gsdll->set_display_callback == NULL) {
+ strncpy(last_error, "Can't find gsapi_set_display_callback\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ gsdll->get_default_device_list = (PFN_gsapi_get_default_device_list)
+ GetProcAddress(gsdll->hmodule, "gsapi_get_default_device_list");
+ if (gsdll->get_default_device_list == NULL) {
+ strncpy(last_error, "Can't find gsapi_get_default_device_list\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ gsdll->set_default_device_list = (PFN_gsapi_set_default_device_list)
+ GetProcAddress(gsdll->hmodule, "gsapi_set_default_device_list");
+ if (gsdll->set_default_device_list == NULL) {
+ strncpy(last_error, "Can't find gsapi_set_default_device_list\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ gsdll->init_with_args = (PFN_gsapi_init_with_args)
+ GetProcAddress(gsdll->hmodule, "gsapi_init_with_args");
+ if (gsdll->init_with_args == NULL) {
+ strncpy(last_error, "Can't find gsapi_init_with_args\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ gsdll->set_arg_encoding = (PFN_gsapi_set_arg_encoding)
+ GetProcAddress(gsdll->hmodule, "gsapi_set_arg_encoding");
+ if (gsdll->set_arg_encoding == NULL) {
+ strncpy(last_error, "Can't find gsapi_set_arg_encoding\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ gsdll->run_string = (PFN_gsapi_run_string) GetProcAddress(gsdll->hmodule,
+ "gsapi_run_string");
+ if (gsdll->run_string == NULL) {
+ strncpy(last_error, "Can't find gsapi_run_string\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ gsdll->exit = (PFN_gsapi_exit) GetProcAddress(gsdll->hmodule,
+ "gsapi_exit");
+ if (gsdll->exit == NULL) {
+ strncpy(last_error, "Can't find gsapi_exit\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ gsdll->set_visual_tracer = (PFN_gsapi_set_visual_tracer)
+ GetProcAddress(gsdll->hmodule, "gsapi_set_visual_tracer");
+ if (gsdll->set_visual_tracer == NULL) {
+ strncpy(last_error, "Can't find gsapi_set_visual_tracer\n", len-1);
+ unload_dll(gsdll);
+ return 1;
+ }
+
+ return 0;
+}
+
+void unload_dll(GSDLL *gsdll)
+{
+ /* Set functions to NULL to prevent use */
+ gsdll->revision = NULL;
+ gsdll->new_instance = NULL;
+ gsdll->delete_instance = NULL;
+ gsdll->init_with_args = NULL;
+ gsdll->run_string = NULL;
+ gsdll->exit = NULL;
+ gsdll->set_stdio = NULL;
+ gsdll->set_poll = NULL;
+ gsdll->set_display_callback = NULL;
+ gsdll->set_visual_tracer = NULL;
+
+ if (gsdll->hmodule != (HINSTANCE)NULL)
+ FreeLibrary(gsdll->hmodule);
+ gsdll->hmodule = NULL;
+}
diff --git a/psi/dwdll.h b/psi/dwdll.h
new file mode 100644
index 000000000..7516e3bfa
--- /dev/null
+++ b/psi/dwdll.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/* gsdll structure for MS-Windows */
+
+#ifndef dwdll_INCLUDED
+# define dwdll_INCLUDED
+
+#ifndef __PROTOTYPES__
+#define __PROTOTYPES__
+#endif
+
+#include "iapi.h"
+
+typedef struct GSDLL_S {
+ HINSTANCE hmodule; /* DLL module handle */
+ PFN_gsapi_revision revision;
+ PFN_gsapi_new_instance new_instance;
+ PFN_gsapi_delete_instance delete_instance;
+ PFN_gsapi_set_stdio set_stdio;
+ PFN_gsapi_set_poll set_poll;
+ PFN_gsapi_set_display_callback set_display_callback;
+ PFN_gsapi_init_with_args init_with_args;
+ PFN_gsapi_run_string run_string;
+ PFN_gsapi_exit exit;
+ PFN_gsapi_set_visual_tracer set_visual_tracer;
+ PFN_gsapi_set_arg_encoding set_arg_encoding;
+ PFN_gsapi_set_default_device_list set_default_device_list;
+ PFN_gsapi_get_default_device_list get_default_device_list;
+} GSDLL;
+
+/* Load the Ghostscript DLL.
+ * Return 0 on success.
+ * Return non-zero on error and store error message
+ * to last_error of length len
+ */
+int load_dll(GSDLL *gsdll, char *last_error, int len);
+
+void unload_dll(GSDLL *gsdll);
+
+#endif /* dwdll_INCLUDED */
diff --git a/psi/dwimg.c b/psi/dwimg.c
new file mode 100644
index 000000000..d71bf23ca
--- /dev/null
+++ b/psi/dwimg.c
@@ -0,0 +1,1685 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/* display device image window for Windows */
+
+/* This code supports both single threaded and multithreaded operation */
+/* For multithread, access is shared as follows:
+ * Each image has a Mutex img->hmutex, used for protected access to
+ * the img->image and its dimensions.
+ * Main thread can access
+ * image_find()
+ * image_new()
+ * image_delete()
+ * image_size()
+ * Main thread must acquire mutex on display_presize() and release
+ * in display_size() after image_size() is called.
+ * Main thread must acquire mutex on display_preclose().
+ *
+ * Second thread must not access image_find, image_new, image_delete
+ * or image_size. It must grab mutex before accessing img->image.
+ */
+
+#define STRICT
+#include <windows.h>
+
+
+/* prevent gp.h redefining sprintf */
+#define sprintf sprintf
+
+#include "stdio_.h"
+
+#include "dwres.h"
+#include "dwimg.h"
+#include "dwreg.h"
+#include "gdevdsp.h"
+
+static const char szImgName2[] = "Ghostscript Image";
+static const char szTrcName2[] = "Ghostscript Graphical Trace";
+
+/* Forward references */
+LRESULT CALLBACK WndImg2Proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+static void register_class(void);
+static void draw(IMAGE *img, HDC hdc, int dx, int dy, int wx, int wy,
+ int sx, int sy);
+static HGLOBAL copy_dib(IMAGE *img);
+static HPALETTE create_palette(IMAGE *img);
+static void create_window(IMAGE *img);
+
+#define M_COPY_CLIP 1
+#define M_DEVICEN_GRAY 2 /* show single separation as gray */
+#define M_SEPARATION 3 /* 3 to 3+IMG_DEVICEN_MAX-1 */
+
+#define DISPLAY_ERROR (-1) /* return this to Ghostscript on error */
+
+/* Define min and max, but make sure to use the identical definition */
+/* to the one that all the compilers seem to have.... */
+#ifndef min
+# define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef max
+# define max(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+/* GUI thread only */
+void image_color(unsigned int format, int index,
+ unsigned char *r, unsigned char *g, unsigned char *b);
+void image_convert_line(IMAGE *img, unsigned char *dest, unsigned char *source);
+void image_16BGR555_to_24BGR(int width, unsigned char *dest,
+ unsigned char *source);
+void image_16BGR565_to_24BGR(int width, unsigned char *dest,
+ unsigned char *source);
+void image_16RGB555_to_24BGR(int width, unsigned char *dest,
+ unsigned char *source);
+void image_16RGB565_to_24BGR(int width, unsigned char *dest,
+ unsigned char *source);
+void image_4CMYK_to_24BGR(int width, unsigned char *dest,
+ unsigned char *source, IMAGE_DEVICEN *devicen, int devicen_gray);
+void image_32CMYK_to_24BGR(int width, unsigned char *dest,
+ unsigned char *source, IMAGE_DEVICEN *devicen, int devicen_gray);
+void image_devicen_to_24BGR(int width, unsigned char *dest,
+ unsigned char *source, IMAGE_DEVICEN *devicen, int devicen_gray);
+
+/****************************************************************/
+/* These functions are only accessed by the main thread */
+
+IMAGE *first_image = NULL;
+static HWND img_hwndtext = (HWND)0;
+
+void
+image_textwindow(HWND hwnd)
+{
+ /* Save the handle to the text window in a global variable
+ * (if running as a GUI) so we can redirect keyboard input
+ * to the text window.
+ * If set to 0, then assume we are using console mode.
+ */
+ img_hwndtext = hwnd;
+}
+
+/* image_find must only be accessed by main thread */
+/* valid for main thread */
+IMAGE *
+image_find(void *handle, void *device)
+{
+ IMAGE *img;
+ for (img = first_image; img!=0; img=img->next) {
+ if ((img->handle == handle) && (img->device == device))
+ return img;
+ }
+ return NULL;
+}
+
+/* image_find must only be accessed by main thread */
+/* valid for main thread */
+IMAGE *
+image_new(void *handle, void *device)
+{
+ IMAGE *img = (IMAGE *)malloc(sizeof(IMAGE));
+ if (img) {
+ memset(img, 0, sizeof(IMAGE));
+ /* remember device and handle */
+ img->handle = handle;
+ img->device = device;
+ img->hwndtext = img_hwndtext;
+
+ img->update_tick = 100; /* milliseconds */
+ img->update_interval = 1; /* 1 tick */
+ img->update_count = 0;
+
+ img->hmutex = INVALID_HANDLE_VALUE;
+
+ /* add to list */
+ img->next = first_image;
+ first_image = img;
+ }
+ return img;
+}
+
+/* remove image from linked list */
+/* valid for main thread */
+void
+image_delete(IMAGE *img)
+{
+ /* remove from list */
+ if (img == first_image) {
+ first_image = img->next;
+ }
+ else {
+ IMAGE *tmp;
+ for (tmp = first_image; tmp!=0; tmp=tmp->next) {
+ if (img == tmp->next)
+ tmp->next = img->next;
+ }
+ }
+ /* Note: img is freed by image_close, not image_delete */
+}
+
+/* resize image */
+/* valid for main thread */
+int
+image_size(IMAGE *img, int new_width, int new_height, int new_raster,
+ unsigned int new_format, void *pimage)
+{
+ int i;
+ img->raster = new_raster;
+ img->format = new_format;
+ img->image = (unsigned char *)pimage;
+
+ /* create a BMP header for the bitmap */
+ img->bmih.biSize = sizeof(BITMAPINFOHEADER);
+ img->bmih.biWidth = new_width;
+ img->bmih.biHeight = new_height;
+ img->bmih.biPlanes = 1;
+
+ /* Reset separations */
+ for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
+ img->devicen[i].used = 0;
+ img->devicen[i].visible = 1;
+ memset(img->devicen[i].name, 0, sizeof(img->devicen[i].name));
+ img->devicen[i].cyan = 0;
+ img->devicen[i].magenta = 0;
+ img->devicen[i].yellow = 0;
+ img->devicen[i].black = 0;
+ }
+
+ switch (img->format & DISPLAY_COLORS_MASK) {
+ case DISPLAY_COLORS_NATIVE:
+ switch (img->format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ img->bmih.biBitCount = 1;
+ img->bmih.biClrUsed = 2;
+ img->bmih.biClrImportant = 2;
+ break;
+ case DISPLAY_DEPTH_4:
+ /* Fixed color palette */
+ img->bmih.biBitCount = 4;
+ img->bmih.biClrUsed = 16;
+ img->bmih.biClrImportant = 16;
+ break;
+ case DISPLAY_DEPTH_8:
+ /* Fixed color palette */
+ img->bmih.biBitCount = 8;
+ img->bmih.biClrUsed = 96;
+ img->bmih.biClrImportant = 96;
+ break;
+ case DISPLAY_DEPTH_16:
+ /* RGB bitfields */
+ /* Bit fields */
+ if ((img->format & DISPLAY_ENDIAN_MASK)
+ == DISPLAY_BIGENDIAN) {
+ /* convert to 24BGR */
+ img->bmih.biBitCount = 24;
+ img->bmih.biClrUsed = 0;
+ img->bmih.biClrImportant = 0;
+ }
+ else {
+ img->bmih.biBitCount = 16;
+ img->bmih.biClrUsed = 0;
+ img->bmih.biClrImportant = 0;
+ }
+ break;
+ default:
+ return DISPLAY_ERROR;
+ }
+ break;
+ case DISPLAY_COLORS_GRAY:
+ switch (img->format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ img->bmih.biBitCount = 1;
+ img->bmih.biClrUsed = 2;
+ img->bmih.biClrImportant = 2;
+ break;
+ case DISPLAY_DEPTH_4:
+ /* Fixed gray palette */
+ img->bmih.biBitCount = 4;
+ img->bmih.biClrUsed = 16;
+ img->bmih.biClrImportant = 16;
+ break;
+ case DISPLAY_DEPTH_8:
+ /* Fixed gray palette */
+ img->bmih.biBitCount = 8;
+ img->bmih.biClrUsed = 256;
+ img->bmih.biClrImportant = 256;
+ break;
+ default:
+ return DISPLAY_ERROR;
+ }
+ break;
+ case DISPLAY_COLORS_RGB:
+ if ((img->format & DISPLAY_DEPTH_MASK) != DISPLAY_DEPTH_8)
+ return DISPLAY_ERROR;
+ if (((img->format & DISPLAY_ALPHA_MASK) == DISPLAY_UNUSED_LAST) &&
+ ((img->format & DISPLAY_ENDIAN_MASK) == DISPLAY_LITTLEENDIAN)) {
+ /* use bitfields to display this */
+ img->bmih.biBitCount = 32;
+ img->bmih.biClrUsed = 0;
+ img->bmih.biClrImportant = 0;
+ }
+ else {
+ /* either native BGR, or we need to convert it */
+ img->bmih.biBitCount = 24;
+ img->bmih.biClrUsed = 0;
+ img->bmih.biClrImportant = 0;
+ }
+ break;
+ case DISPLAY_COLORS_CMYK:
+ switch (img->format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ case DISPLAY_DEPTH_8:
+ /* we can convert these formats */
+ break;
+ default:
+ return DISPLAY_ERROR;
+ }
+ /* we can't display this natively */
+ /* we will convert it just before displaying */
+ img->bmih.biBitCount = 24;
+ img->bmih.biClrUsed = 0;
+ img->bmih.biClrImportant = 0;
+ img->devicen[0].used = 1;
+ img->devicen[0].cyan = 65535;
+ /* We already know about the CMYK components */
+ strncpy(img->devicen[0].name, "Cyan",
+ sizeof(img->devicen[0].name));
+ img->devicen[1].used = 1;
+ img->devicen[1].magenta = 65535;
+ strncpy(img->devicen[1].name, "Magenta",
+ sizeof(img->devicen[1].name));
+ img->devicen[2].used = 1;
+ img->devicen[2].yellow = 65535;
+ strncpy(img->devicen[2].name, "Yellow",
+ sizeof(img->devicen[2].name));
+ img->devicen[3].used = 1;
+ img->devicen[3].black = 65535;
+ strncpy(img->devicen[3].name, "Black",
+ sizeof(img->devicen[3].name));
+ break;
+ case DISPLAY_COLORS_SEPARATION:
+ /* we can't display this natively */
+ /* we will convert it just before displaying */
+ img->bmih.biBitCount = 24;
+ img->bmih.biClrUsed = 0;
+ img->bmih.biClrImportant = 0;
+ break;
+ }
+
+ img->bmih.biCompression = 0;
+ img->bmih.biSizeImage = 0;
+ img->bmih.biXPelsPerMeter = 0;
+ img->bmih.biYPelsPerMeter = 0;
+ img->bytewidth = ((img->bmih.biWidth * img->bmih.biBitCount + 31 ) & ~31) >> 3;
+
+ if (img->palette)
+ DeleteObject(img->palette);
+ img->palette = create_palette(img);
+
+ return 0;
+}
+
+int
+image_separation(IMAGE *img,
+ int comp_num, const char *name,
+ unsigned short c, unsigned short m,
+ unsigned short y, unsigned short k)
+{
+ if ((comp_num < 0) || (comp_num > IMAGE_DEVICEN_MAX))
+ return DISPLAY_ERROR;
+ img->devicen[comp_num].used = 1;
+ strncpy(img->devicen[comp_num].name, name,
+ sizeof(img->devicen[comp_num].name)-1);
+ img->devicen[comp_num].cyan = c;
+ img->devicen[comp_num].magenta = m;
+ img->devicen[comp_num].yellow = y;
+ img->devicen[comp_num].black = k;
+ return 0;
+}
+
+/****************************************************************/
+/* These functions are only accessed by the GUI thread */
+
+/* open window for device and add to list */
+void
+image_open(IMAGE *img)
+{
+ /* register class */
+ register_class();
+
+ /* open window */
+ create_window(img);
+}
+
+/* close window and remove from list */
+void
+image_close(IMAGE *img)
+{
+ DestroyWindow(img->hwnd);
+ img->hwnd = NULL;
+
+ if (img->palette)
+ DeleteObject(img->palette);
+ img->palette = NULL;
+
+ if (img->hBrush)
+ DeleteObject(img->hBrush);
+ img->hBrush = NULL;
+
+ free(img);
+}
+
+void
+register_class(void)
+{
+ WNDCLASS wndclass;
+ HINSTANCE hInstance = GetModuleHandle(NULL);
+
+ /* register the window class for graphics */
+ wndclass.style = CS_HREDRAW | CS_VREDRAW;
+ wndclass.lpfnWndProc = WndImg2Proc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = sizeof(LONG);
+ wndclass.hInstance = hInstance;
+ wndclass.hIcon = LoadIcon(hInstance,(LPSTR)MAKEINTRESOURCE(GSIMAGE_ICON));
+ wndclass.hCursor = LoadCursor((HINSTANCE)NULL, IDC_ARROW);
+ wndclass.hbrBackground = NULL; /* we will paint background */
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = szImgName2;
+ RegisterClass(&wndclass);
+}
+
+void image_separations(IMAGE *img)
+{
+ char buf[64];
+ int i;
+ int exist;
+ int num_visible = 0;
+ HMENU sysmenu = GetSystemMenu(img->hwnd, FALSE);
+ if (((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_CMYK) ||
+ ((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_SEPARATION)) {
+ /* Add menus if needed */
+ for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
+ exist = 0;
+ if (img->devicen[i].menu)
+ exist = GetMenuString(sysmenu, M_SEPARATION+i,
+ buf, sizeof(buf)-1, MF_BYCOMMAND) != 0;
+ if (exist && (strcmp(img->devicen[i].name, buf) != 0)) {
+ /* remove it because name changed */
+ RemoveMenu(sysmenu, M_SEPARATION+i, MF_BYCOMMAND);
+ img->devicen[i].menu = 0;
+ }
+ if (img->devicen[i].name[0] && !img->devicen[i].menu) {
+ AppendMenu(sysmenu, MF_STRING | MF_CHECKED,
+ M_SEPARATION+i, img->devicen[i].name);
+ img->devicen[i].menu = 1;
+ }
+ if (img->devicen[i].used && img->devicen[i].visible)
+ num_visible++;
+ }
+ EnableMenuItem(sysmenu, M_DEVICEN_GRAY,
+ MF_BYCOMMAND | ((num_visible <= 1) ? MF_ENABLED : MF_GRAYED));
+ }
+ else {
+ for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
+ if (img->devicen[i].menu) {
+ RemoveMenu(sysmenu, M_SEPARATION+i, MF_BYCOMMAND);
+ img->devicen[i].menu = 0;
+ }
+ }
+ EnableMenuItem(sysmenu, M_DEVICEN_GRAY, MF_BYCOMMAND | MF_GRAYED);
+ }
+}
+
+void sep_menu(IMAGE *img, int component)
+{
+ int i;
+ int num_visible = 0;
+ img->devicen[component].visible = !img->devicen[component].visible;
+ CheckMenuItem(GetSystemMenu(img->hwnd, FALSE),
+ M_SEPARATION+component,
+ (img->devicen[component].visible ? MF_CHECKED : MF_UNCHECKED));
+ for (i=0; i<IMAGE_DEVICEN_MAX; i++)
+ if (img->devicen[i].used && img->devicen[i].visible)
+ num_visible++;
+ EnableMenuItem(GetSystemMenu(img->hwnd, FALSE), M_DEVICEN_GRAY,
+ MF_BYCOMMAND | ((num_visible <= 1) ? MF_ENABLED : MF_GRAYED));
+ InvalidateRect(img->hwnd, NULL, 0);
+ UpdateWindow(img->hwnd);
+}
+
+static void
+create_window(IMAGE *img)
+{
+ HMENU sysmenu;
+ LOGBRUSH lb;
+ char winposbuf[256];
+ char window_title[256];
+ int len = sizeof(winposbuf);
+
+ /* create background brush */
+ lb.lbStyle = BS_SOLID;
+ lb.lbHatch = 0;
+ lb.lbColor = GetSysColor(COLOR_WINDOW);
+ if (lb.lbColor = RGB(255,255,255)) /* Don't allow white */
+ lb.lbColor = GetSysColor(COLOR_MENU);
+ if (lb.lbColor = RGB(255,255,255)) /* Don't allow white */
+ lb.lbColor = GetSysColor(COLOR_APPWORKSPACE);
+ if (lb.lbColor = RGB(255,255,255)) /* Don't allow white */
+ lb.lbColor = RGB(192,192,192);
+ img->hBrush = CreateBrushIndirect(&lb);
+
+ img->cxClient = img->cyClient = 0;
+ img->nVscrollPos = img->nVscrollMax = 0;
+ img->nHscrollPos = img->nHscrollMax = 0;
+ img->x = img->y = img->cx = img->cy = CW_USEDEFAULT;
+ if (win_get_reg_value((img->device != NULL ? "Image" : "Tracer"), winposbuf, &len) == 0) {
+ int x, y, cx, cy;
+
+ if (sscanf(winposbuf, "%d %d %d %d", &x, &y, &cx, &cy) == 4) {
+ img->x = x;
+ img->y = y;
+ img->cx = cx;
+ img->cy = cy;
+ }
+ }
+ strcpy(window_title, (img->device != NULL ? (LPSTR)szImgName2 : (LPSTR)szTrcName2));
+ { /*
+ * This section is for debug purpose only.
+ * It allows to replace window title so that user can identify window
+ * when multiple instances of the application run in same time.
+ * Create gs\bin\gswin32.ini or gs\bin\gswin32c.ini and
+ * (for 64-bit: gs\bin\gswin64.ini or gs\bin\gswin64c.ini)
+ * put an identifier to there like this :
+ *
+ * [Window]
+ * Title=Current Revision
+ *
+ * It is useful to compare images generated with different revisions.
+ */
+ char ini_path[MAX_PATH];
+ DWORD ini_path_length;
+
+ ini_path_length = GetModuleFileName(NULL, ini_path, sizeof(ini_path));
+ if (ini_path_length > 0) {
+ int i = ini_path_length - 1;
+ for (; i>=0; i--)
+ if(ini_path[i] == '.')
+ break;
+ if (i < sizeof(ini_path) - 4) {
+ strcpy(ini_path + i, ".ini");
+ GetPrivateProfileString("Window", "Title",
+ (img->device != NULL ? (LPSTR)szImgName2 : (LPSTR)szTrcName2),
+ window_title, sizeof(window_title), ini_path);
+ }
+ }
+ }
+ /* create window */
+ img->hwnd = CreateWindow(szImgName2, window_title,
+ WS_OVERLAPPEDWINDOW,
+ img->x, img->y, img->cx, img->cy,
+ NULL, NULL, GetModuleHandle(NULL), (void *)img);
+ if (img->device == NULL && img->x != CW_USEDEFAULT &&
+ img->y != CW_USEDEFAULT &&
+ img->cx != CW_USEDEFAULT &&
+ img->cy != CW_USEDEFAULT)
+ MoveWindow(img->hwnd, img->x, img->y, img->cx, img->cy, FALSE);
+ ShowWindow(img->hwnd, (img->device != NULL ? SW_SHOWMINNOACTIVE : SW_SHOW));
+
+ /* modify the menu to have the new items we want */
+ sysmenu = GetSystemMenu(img->hwnd, 0); /* get the sysmenu */
+ AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(sysmenu, MF_STRING, M_COPY_CLIP, "Copy to Clip&board");
+ AppendMenu(sysmenu, MF_STRING, M_DEVICEN_GRAY, "Show as Gray");
+ AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
+
+ image_separations(img);
+}
+
+void
+image_poll(IMAGE *img)
+{
+ if ((img->bmih.biWidth == 0) || (img->bmih.biHeight == 0))
+ return;
+ img->pending_update = 1;
+ if (img->update_timer == 0) {
+ img->update_timer = 1;
+ img->update_count = 0;
+ SetTimer(img->hwnd, img->update_timer, img->update_tick, NULL);
+ }
+}
+
+/* Redraw the window, making sure that periodic updates don't take too long. */
+void
+image_update_now(IMAGE *img)
+{
+ SYSTEMTIME t1;
+ SYSTEMTIME t2;
+ int delta;
+ if ( !IsWindow(img->hwnd) ) /* some clod closed the window */
+ create_window(img);
+
+ if ( !IsIconic(img->hwnd) ) { /* redraw window */
+ GetSystemTime(&t1);
+ InvalidateRect(img->hwnd, NULL, 1);
+ UpdateWindow(img->hwnd);
+ GetSystemTime(&t2);
+ /* Make sure the update interval is at least 10 times
+ * what it takes to paint the window
+ */
+ delta = (t2.wSecond - t1.wSecond)*1000 +
+ (t2.wMilliseconds - t1.wMilliseconds);
+ if (delta < 0)
+ delta += 60000;
+ delta = 10 * delta / img->update_tick + 1;
+ if (delta > img->update_interval)
+ img->update_interval = delta;
+ else if ((delta >= 2) &&
+ (delta < img->update_interval / 4))
+ img->update_interval = delta/2;
+ }
+ img->update_count = 0;
+}
+
+void
+image_sync(IMAGE *img)
+{
+ if (img->update_timer) {
+ /* stop timer when nothing is happening */
+ KillTimer(img->hwnd, img->update_timer);
+ img->update_timer = 0;
+ }
+ img->pending_sync = 0;
+ image_update_now(img);
+ image_separations(img);
+ img->pending_update = 0;
+}
+
+void
+image_page(IMAGE *img)
+{
+ if (IsIconic(img->hwnd)) /* useless as an Icon so fix it */
+ ShowWindow(img->hwnd, SW_SHOWNORMAL);
+ BringWindowToTop(img->hwnd);
+
+ image_sync(img);
+}
+
+/* GUI thread */
+void
+image_updatesize(IMAGE *img)
+{
+ RECT rect;
+ int nSizeType;
+ image_separations(img);
+ /* update scroll bars */
+ if (!IsIconic(img->hwnd)) {
+ if (IsZoomed(img->hwnd))
+ nSizeType = SIZE_MAXIMIZED;
+ else
+ nSizeType = SIZE_RESTORED;
+ GetClientRect(img->hwnd, &rect);
+ SendMessage(img->hwnd, WM_SIZE, nSizeType,
+ MAKELONG(rect.right, rect.bottom));
+ }
+}
+
+void
+image_color(unsigned int format, int index,
+ unsigned char *r, unsigned char *g, unsigned char *b)
+{
+ switch (format & DISPLAY_COLORS_MASK) {
+ case DISPLAY_COLORS_NATIVE:
+ switch (format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ *r = *g = *b = (index ? 0 : 255);
+ break;
+ case DISPLAY_DEPTH_4:
+ if (index == 7)
+ *r = *g = *b = 170;
+ else if (index == 8)
+ *r = *g = *b = 85;
+ else {
+ int one = index & 8 ? 255 : 128;
+ *r = (index & 4 ? one : 0);
+ *g = (index & 2 ? one : 0);
+ *b = (index & 1 ? one : 0);
+ }
+ break;
+ case DISPLAY_DEPTH_8:
+ /* palette of 96 colors */
+ /* 0->63 = 00RRGGBB, 64->95 = 010YYYYY */
+ if (index < 64) {
+ int one = 255 / 3;
+ *r = ((index & 0x30) >> 4) * one;
+ *g = ((index & 0x0c) >> 2) * one;
+ *b = (index & 0x03) * one;
+ }
+ else {
+ int val = index & 0x1f;
+ *r = *g = *b = (val << 3) + (val >> 2);
+ }
+ break;
+ }
+ break;
+ case DISPLAY_COLORS_GRAY:
+ switch (format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ *r = *g = *b = (index ? 255 : 0);
+ break;
+ case DISPLAY_DEPTH_4:
+ *r = *g = *b = (unsigned char)((index<<4) + index);
+ break;
+ case DISPLAY_DEPTH_8:
+ *r = *g = *b = (unsigned char)index;
+ break;
+ }
+ break;
+ }
+}
+
+/* convert one line of 16BGR555 to 24BGR */
+/* byte0=GGGBBBBB byte1=0RRRRRGG */
+void
+image_16BGR555_to_24BGR(int width, unsigned char *dest, unsigned char *source)
+{
+ int i;
+ WORD w;
+ unsigned char value;
+ for (i=0; i<width; i++) {
+ w = source[0] + (source[1] << 8);
+ value = w & 0x1f; /* blue */
+ *dest++ = (value << 3) + (value >> 2);
+ value = (w >> 5) & 0x1f; /* green */
+ *dest++ = (value << 3) + (value >> 2);
+ value = (w >> 10) & 0x1f; /* red */
+ *dest++ = (value << 3) + (value >> 2);
+ source += 2;
+ }
+}
+
+/* convert one line of 16BGR565 to 24BGR */
+/* byte0=GGGBBBBB byte1=RRRRRGGG */
+void
+image_16BGR565_to_24BGR(int width, unsigned char *dest, unsigned char *source)
+{
+ int i;
+ WORD w;
+ unsigned char value;
+ for (i=0; i<width; i++) {
+ w = source[0] + (source[1] << 8);
+ value = w & 0x1f; /* blue */
+ *dest++ = (value << 3) + (value >> 2);
+ value = (w >> 5) & 0x3f; /* green */
+ *dest++ = (value << 2) + (value >> 4);
+ value = (w >> 11) & 0x1f; /* red */
+ *dest++ = (value << 3) + (value >> 2);
+ source += 2;
+ }
+}
+
+/* convert one line of 16RGB555 to 24BGR */
+/* byte0=0RRRRRGG byte1=GGGBBBBB */
+void
+image_16RGB555_to_24BGR(int width, unsigned char *dest, unsigned char *source)
+{
+ int i;
+ WORD w;
+ unsigned char value;
+ for (i=0; i<width; i++) {
+ w = (source[0] << 8) + source[1];
+ value = w & 0x1f; /* blue */
+ *dest++ = (value << 3) + (value >> 2);
+ value = (w >> 5) & 0x1f; /* green */
+ *dest++ = (value << 3) + (value >> 2);
+ value = (w >> 10) & 0x1f; /* red */
+ *dest++ = (value << 3) + (value >> 2);
+ source += 2;
+ }
+}
+
+/* convert one line of 16RGB565 to 24BGR */
+/* byte0=RRRRRGGG byte1=GGGBBBBB */
+void
+image_16RGB565_to_24BGR(int width, unsigned char *dest, unsigned char *source)
+{
+ int i;
+ WORD w;
+ unsigned char value;
+ for (i=0; i<width; i++) {
+ w = (source[0] << 8) + source[1];
+ value = w & 0x1f; /* blue */
+ *dest++ = (value << 3) + (value >> 2);
+ value = (w >> 5) & 0x3f; /* green */
+ *dest++ = (value << 2) + (value >> 4);
+ value = (w >> 11) & 0x1f; /* red */
+ *dest++ = (value << 3) + (value >> 2);
+ source += 2;
+ }
+}
+
+void
+image_4CMYK_to_24BGR(int width, unsigned char *dest, unsigned char *source,
+ IMAGE_DEVICEN *devicen, int devicen_gray)
+{
+ int i;
+ int cyan, magenta, yellow, black;
+ int vc = devicen[0].visible;
+ int vm = devicen[1].visible;
+ int vy = devicen[2].visible;
+ int vk = devicen[3].visible;
+ int vall = vc && vm && vy && vk;
+ int show_gray = (vc + vm + vy + vk == 1) && devicen_gray;
+ int value;
+ for (i=0; i<width; i++) {
+ value = source[i/2];
+ if (i & 0)
+ value >>= 4;
+ cyan = ((value >> 3) & 1) * 255;
+ magenta = ((value >> 2) & 1) * 255;
+ yellow = ((value >> 1) & 1) * 255;
+ black = (value & 1) * 255;
+ if (!vall) {
+ if (!vc)
+ cyan = 0;
+ if (!vm)
+ magenta = 0;
+ if (!vy)
+ yellow = 0;
+ if (!vk)
+ black = 0;
+ if (show_gray) {
+ black += cyan + magenta + yellow;
+ cyan = magenta = yellow = 0;
+ }
+ }
+ *dest++ = (255 - yellow) * (255 - black)/255; /* blue */
+ *dest++ = (255 - magenta) * (255 - black)/255; /* green */
+ *dest++ = (255 - cyan) * (255 - black)/255; /* red */
+ }
+}
+
+/* convert one line of 32CMYK to 24BGR */
+void
+image_32CMYK_to_24BGR(int width, unsigned char *dest, unsigned char *source,
+ IMAGE_DEVICEN *devicen, int devicen_gray)
+{
+ int i;
+ int cyan, magenta, yellow, black;
+ int vc = devicen[0].visible;
+ int vm = devicen[1].visible;
+ int vy = devicen[2].visible;
+ int vk = devicen[3].visible;
+ int vall = vc && vm && vy && vk;
+ int show_gray = (vc + vm + vy + vk == 1) && devicen_gray;
+ for (i=0; i<width; i++) {
+ cyan = source[0];
+ magenta = source[1];
+ yellow = source[2];
+ black = source[3];
+ if (!vall) {
+ if (!vc)
+ cyan = 0;
+ if (!vm)
+ magenta = 0;
+ if (!vy)
+ yellow = 0;
+ if (!vk)
+ black = 0;
+ if (show_gray) {
+ black += cyan + magenta + yellow;
+ cyan = magenta = yellow = 0;
+ }
+ }
+ *dest++ = (255 - yellow) * (255 - black)/255; /* blue */
+ *dest++ = (255 - magenta) * (255 - black)/255; /* green */
+ *dest++ = (255 - cyan) * (255 - black)/255; /* red */
+ source += 4;
+ }
+}
+
+void
+image_devicen_to_24BGR(int width, unsigned char *dest, unsigned char *source,
+ IMAGE_DEVICEN *devicen, int devicen_gray)
+{
+ int i, j;
+ int cyan, magenta, yellow, black;
+ int num_comp = 0;
+ int value;
+ int num_visible = 0;
+ int show_gray = 0;
+ for (j=0; j<IMAGE_DEVICEN_MAX; j++) {
+ if (devicen[j].used) {
+ num_comp = j+1;
+ if (devicen[j].visible)
+ num_visible++;
+ }
+ }
+ if ((num_visible == 1) && devicen_gray)
+ show_gray = 1;
+
+ for (i=0; i<width; i++) {
+ cyan = magenta = yellow = black = 0;
+ for (j=0; j<num_comp; j++) {
+ if (devicen[j].visible && devicen[j].used) {
+ value = source[j];
+ if (show_gray)
+ black += value;
+ else {
+ cyan += value * devicen[j].cyan / 65535;
+ magenta += value * devicen[j].magenta / 65535;
+ yellow += value * devicen[j].yellow / 65535;
+ black += value * devicen[j].black / 65535;
+ }
+ }
+ }
+ if (cyan > 255)
+ cyan = 255;
+ if (magenta > 255)
+ magenta = 255;
+ if (yellow > 255)
+ yellow = 255;
+ if (black > 255)
+ black = 255;
+ *dest++ = (255 - yellow) * (255 - black)/255; /* blue */
+ *dest++ = (255 - magenta) * (255 - black)/255; /* green */
+ *dest++ = (255 - cyan) * (255 - black)/255; /* red */
+ source += 8;
+ }
+}
+
+void
+image_convert_line(IMAGE *img, unsigned char *dest, unsigned char *source)
+{
+ unsigned char *d = dest;
+ unsigned char *s = source;
+ int width = img->bmih.biWidth;
+ unsigned int alpha = img->format & DISPLAY_ALPHA_MASK;
+ BOOL bigendian = (img->format & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN;
+ int i;
+
+ switch (img->format & DISPLAY_COLORS_MASK) {
+ case DISPLAY_COLORS_NATIVE:
+ if ((img->format & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_16) {
+ if (bigendian) {
+ if ((img->format & DISPLAY_555_MASK)
+ == DISPLAY_NATIVE_555)
+ image_16RGB555_to_24BGR(img->bmih.biWidth,
+ dest, source);
+ else
+ image_16RGB565_to_24BGR(img->bmih.biWidth,
+ dest, source);
+ }
+ else {
+ if ((img->format & DISPLAY_555_MASK)
+ == DISPLAY_NATIVE_555) {
+ image_16BGR555_to_24BGR(img->bmih.biWidth,
+ dest, source);
+ }
+ else
+ image_16BGR565_to_24BGR(img->bmih.biWidth,
+ dest, source);
+ }
+ }
+ break;
+ case DISPLAY_COLORS_RGB:
+ if ((img->format & DISPLAY_DEPTH_MASK) != DISPLAY_DEPTH_8)
+ return;
+ for (i=0; i<width; i++) {
+ if ((alpha == DISPLAY_ALPHA_FIRST) ||
+ (alpha == DISPLAY_UNUSED_FIRST))
+ s++;
+ if (bigendian) {
+ *d++ = s[2];
+ *d++ = s[1];
+ *d++ = s[0];
+ s+=3;
+ }
+ else {
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+ }
+ if ((alpha == DISPLAY_ALPHA_LAST) ||
+ (alpha == DISPLAY_UNUSED_LAST))
+ s++;
+ }
+/*
+printf("rgb, width=%d alpha=%d d=0x%x s=0x%x\n", width, alpha, (int)d, (int)s);
+printf(" d=0x%x s=0x%x\n", (int)d, (int)s);
+*/
+ break;
+ case DISPLAY_COLORS_CMYK:
+ if ((img->format & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8)
+ image_32CMYK_to_24BGR(width, dest, source,
+ img->devicen, img->devicen_gray);
+ else if ((img->format & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_1) {
+ image_4CMYK_to_24BGR(width, dest, source,
+ img->devicen, img->devicen_gray);
+ }
+ else
+ return;
+ break;
+ case DISPLAY_COLORS_SEPARATION:
+ if ((img->format & DISPLAY_DEPTH_MASK) != DISPLAY_DEPTH_8)
+ return;
+ image_devicen_to_24BGR(width, dest, source,
+ img->devicen, img->devicen_gray);
+ break;
+ }
+}
+
+/* This makes a copy of the bitmap in global memory, suitable for clipboard */
+/* Do not put 16 or 32-bit per pixels on the clipboard because */
+/* ClipBook Viewer (NT4) can't display them */
+static HGLOBAL
+copy_dib(IMAGE *img)
+{
+ int bitsperpixel;
+ int bytewidth;
+ int bitmapsize;
+ int palcount;
+ HGLOBAL hglobal;
+ BYTE *pBits;
+ BYTE *pLine;
+ BYTE *pDIB;
+ BITMAPINFOHEADER *pbmih;
+ RGBQUAD *pColors;
+ int i;
+ BOOL directcopy = FALSE;
+
+ /* Allocates memory for the clipboard bitmap */
+ if (img->bmih.biBitCount <= 1)
+ bitsperpixel = 1;
+ else if (img->bmih.biBitCount <= 4)
+ bitsperpixel = 4;
+ else if (img->bmih.biBitCount <= 8)
+ bitsperpixel = 8;
+ else
+ bitsperpixel = 24;
+ bytewidth = ((img->bmih.biWidth * bitsperpixel + 31 ) & ~31) >> 3;
+ bitmapsize = bytewidth * img->bmih.biHeight;
+ if (bitsperpixel > 8)
+ palcount = 0; /* 24-bit BGR */
+ else
+ palcount = img->bmih.biClrUsed;
+
+ hglobal = GlobalAlloc(GHND | GMEM_SHARE, sizeof(BITMAPINFOHEADER)
+ + sizeof(RGBQUAD) * palcount + bitmapsize);
+ if (hglobal == (HGLOBAL) NULL)
+ return (HGLOBAL) NULL;
+ pDIB = (BYTE *) GlobalLock(hglobal);
+ if (pDIB == (BYTE *) NULL)
+ return (HGLOBAL) NULL;
+
+ /* initialize the clipboard bitmap */
+ pbmih = (BITMAPINFOHEADER *) (pDIB);
+ pColors = (RGBQUAD *) (pDIB + sizeof(BITMAPINFOHEADER));
+ pBits = (BYTE *) (pDIB + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * palcount);
+ pbmih->biSize = sizeof(BITMAPINFOHEADER);
+ pbmih->biWidth = img->bmih.biWidth;
+ pbmih->biHeight = img->bmih.biHeight;
+ pbmih->biPlanes = 1;
+ pbmih->biBitCount = bitsperpixel;
+ pbmih->biCompression = 0;
+ pbmih->biSizeImage = 0; /* default */
+ pbmih->biXPelsPerMeter = 0;
+ pbmih->biYPelsPerMeter = 0;
+ pbmih->biClrUsed = palcount;
+ pbmih->biClrImportant = palcount;
+
+ for (i = 0; i < palcount; i++) {
+ image_color(img->format, i, &pColors[i].rgbRed,
+ &pColors[i].rgbGreen, &pColors[i].rgbBlue);
+ pColors[i].rgbReserved = 0;
+ }
+
+ /* find out if the format needs to be converted */
+ switch (img->format & DISPLAY_COLORS_MASK) {
+ case DISPLAY_COLORS_NATIVE:
+ switch (img->format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ case DISPLAY_DEPTH_4:
+ case DISPLAY_DEPTH_8:
+ directcopy = TRUE;
+ }
+ break;
+ case DISPLAY_COLORS_GRAY:
+ switch (img->format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ case DISPLAY_DEPTH_4:
+ case DISPLAY_DEPTH_8:
+ directcopy = TRUE;
+ }
+ break;
+ case DISPLAY_COLORS_RGB:
+ if (((img->format & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8) &&
+ ((img->format & DISPLAY_ALPHA_MASK) == DISPLAY_ALPHA_NONE) &&
+ ((img->format & DISPLAY_ENDIAN_MASK) == DISPLAY_LITTLEENDIAN))
+ directcopy = TRUE;
+ }
+
+ pLine = pBits;
+ if (directcopy) {
+ for (i = 0; i < img->bmih.biHeight; i++) {
+ memcpy(pLine, img->image + i * img->raster, bytewidth);
+ pLine += bytewidth;
+ }
+ }
+ else {
+ /* we need to convert the format to 24BGR */
+ for (i = 0; i < img->bmih.biHeight; i++) {
+ image_convert_line(img, pLine, img->image + i * img->raster);
+ pLine += bytewidth;
+ }
+ }
+
+ GlobalUnlock(hglobal);
+
+ return hglobal;
+}
+
+static HPALETTE
+create_palette(IMAGE *img)
+{
+ int i;
+ int nColors;
+ HPALETTE palette = NULL;
+
+ nColors = img->bmih.biClrUsed;
+ if (nColors) {
+ LPLOGPALETTE logpalette;
+ logpalette = (LPLOGPALETTE) malloc(sizeof(LOGPALETTE) +
+ nColors * sizeof(PALETTEENTRY));
+ if (logpalette == (LPLOGPALETTE) NULL)
+ return (HPALETTE)0;
+ logpalette->palVersion = 0x300;
+ logpalette->palNumEntries = img->bmih.biClrUsed;
+ for (i = 0; i < nColors; i++) {
+ logpalette->palPalEntry[i].peFlags = 0;
+ image_color(img->format, i,
+ &logpalette->palPalEntry[i].peRed,
+ &logpalette->palPalEntry[i].peGreen,
+ &logpalette->palPalEntry[i].peBlue);
+ }
+ palette = CreatePalette(logpalette);
+ free(logpalette);
+ }
+ return palette;
+}
+
+static void UpdateScrollBarX(IMAGE *img)
+{
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
+ si.nPage = img->cxClient;
+ si.nMin = 0;
+ si.nMax = img->bmih.biWidth-1;
+ si.nPos = img->nHscrollPos;
+ si.nTrackPos = 0;
+ SetScrollInfo(img->hwnd, SB_HORZ, &si, TRUE);
+}
+
+static void UpdateScrollBarY(IMAGE *img)
+{
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
+ si.nPage = img->cyClient;
+ si.nMin = 0;
+ si.nMax = img->bmih.biHeight-1;
+ si.nPos = img->nVscrollPos;
+ si.nTrackPos = 0;
+ SetScrollInfo(img->hwnd, SB_VERT, &si, TRUE);
+}
+
+/* image window */
+/* All accesses to img->image or dimensions must be protected by mutex */
+LRESULT CALLBACK
+WndImg2Proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ HDC hdc;
+ PAINTSTRUCT ps;
+ RECT rect;
+ int nVscrollInc, nHscrollInc;
+ IMAGE *img;
+
+ if (message == WM_CREATE) {
+ /* Object is stored in window extra data.
+ * Nothing must try to use the object before WM_CREATE
+ * initializes it here.
+ */
+ img = (IMAGE *)(((CREATESTRUCT *)lParam)->lpCreateParams);
+ SetWindowLong(hwnd, 0, (LONG)img);
+ }
+ img = (IMAGE *)GetWindowLong(hwnd, 0);
+
+ switch(message) {
+ case WM_SYSCOMMAND:
+ /* copy to clipboard */
+ if (LOWORD(wParam) == M_COPY_CLIP) {
+ HGLOBAL hglobal;
+ HPALETTE hpalette;
+ if (img->hmutex != INVALID_HANDLE_VALUE)
+ WaitForSingleObject(img->hmutex, 120000);
+ hglobal = copy_dib(img);
+ if (hglobal == (HGLOBAL)NULL) {
+ if (img->hmutex != INVALID_HANDLE_VALUE)
+ ReleaseMutex(img->hmutex);
+ MessageBox(hwnd, "Not enough memory to Copy to Clipboard",
+ szImgName2, MB_OK | MB_ICONEXCLAMATION);
+ return 0;
+ }
+ OpenClipboard(hwnd);
+ EmptyClipboard();
+ SetClipboardData(CF_DIB, hglobal);
+ hpalette = create_palette(img);
+ if (hpalette)
+ SetClipboardData(CF_PALETTE, hpalette);
+ CloseClipboard();
+ if (img->hmutex != INVALID_HANDLE_VALUE)
+ ReleaseMutex(img->hmutex);
+ return 0;
+ }
+ else if ((LOWORD(wParam) >= M_SEPARATION) &&
+ (LOWORD(wParam) < M_SEPARATION+IMAGE_DEVICEN_MAX)) {
+ sep_menu(img, LOWORD(wParam) - M_SEPARATION);
+ }
+ else if (LOWORD(wParam) == M_DEVICEN_GRAY) {
+ img->devicen_gray = !img->devicen_gray;
+ CheckMenuItem(GetSystemMenu(img->hwnd, FALSE), M_DEVICEN_GRAY,
+ (img->devicen_gray ? MF_CHECKED : MF_UNCHECKED));
+ InvalidateRect(img->hwnd, NULL, 0);
+ UpdateWindow(img->hwnd);
+ }
+ break;
+ case WM_CREATE:
+ /* enable drag-drop */
+ DragAcceptFiles(hwnd, TRUE);
+ break;
+ case WM_MOVE:
+ if (!IsIconic(hwnd) && !IsZoomed(hwnd)) {
+ GetWindowRect(hwnd, &rect);
+ img->x = rect.left;
+ img->y = rect.top;
+ }
+ break;
+ case WM_SIZE:
+ if (wParam == SIZE_MINIMIZED)
+ return(0);
+
+ /* remember current window size */
+ if (wParam != SIZE_MAXIMIZED) {
+ GetWindowRect(hwnd, &rect);
+ img->cx = rect.right - rect.left;
+ img->cy = rect.bottom - rect.top;
+ img->x = rect.left;
+ img->y = rect.top;
+ }
+
+ if (img->hmutex != INVALID_HANDLE_VALUE)
+ WaitForSingleObject(img->hmutex, 120000);
+ img->cyClient = HIWORD(lParam);
+ img->cxClient = LOWORD(lParam);
+
+ img->cyAdjust = min(img->bmih.biHeight, img->cyClient) - img->cyClient;
+ img->cyClient += img->cyAdjust;
+
+ img->nVscrollMax = max(0, img->bmih.biHeight - img->cyClient);
+ img->nVscrollPos = min(img->nVscrollPos, img->nVscrollMax);
+
+ UpdateScrollBarY(img);
+
+ img->cxAdjust = min(img->bmih.biWidth, img->cxClient) - img->cxClient;
+ img->cxClient += img->cxAdjust;
+
+ img->nHscrollMax = max(0, img->bmih.biWidth - img->cxClient);
+ img->nHscrollPos = min(img->nHscrollPos, img->nHscrollMax);
+
+ UpdateScrollBarX(img);
+
+ if ((wParam==SIZENORMAL)
+ && (img->cxAdjust!=0 || img->cyAdjust!=0)) {
+ GetWindowRect(GetParent(hwnd),&rect);
+ MoveWindow(GetParent(hwnd),rect.left,rect.top,
+ rect.right-rect.left+img->cxAdjust,
+ rect.bottom-rect.top+img->cyAdjust, TRUE);
+ img->cxAdjust = img->cyAdjust = 0;
+ }
+ if (img->hmutex != INVALID_HANDLE_VALUE)
+ ReleaseMutex(img->hmutex);
+ return(0);
+ case WM_VSCROLL:
+ switch(LOWORD(wParam)) {
+ case SB_TOP:
+ nVscrollInc = -img->nVscrollPos;
+ break;
+ case SB_BOTTOM:
+ nVscrollInc = img->nVscrollMax - img->nVscrollPos;
+ break;
+ case SB_LINEUP:
+ nVscrollInc = -img->cyClient/16;
+ break;
+ case SB_LINEDOWN:
+ nVscrollInc = img->cyClient/16;
+ break;
+ case SB_PAGEUP:
+ nVscrollInc = min(-1,-img->cyClient);
+ break;
+ case SB_PAGEDOWN:
+ nVscrollInc = max(1,img->cyClient);
+ break;
+ case SB_THUMBTRACK:
+ case SB_THUMBPOSITION:
+ nVscrollInc = HIWORD(wParam) - img->nVscrollPos;
+ break;
+ default:
+ nVscrollInc = 0;
+ }
+ if ((nVscrollInc = max(-img->nVscrollPos,
+ min(nVscrollInc, img->nVscrollMax - img->nVscrollPos)))!=0) {
+ img->nVscrollPos += nVscrollInc;
+ ScrollWindow(hwnd,0,-nVscrollInc,NULL,NULL);
+ UpdateScrollBarY(img);
+ UpdateWindow(hwnd);
+ }
+ return(0);
+ case WM_HSCROLL:
+ switch(LOWORD(wParam)) {
+ case SB_LINEUP:
+ nHscrollInc = -img->cxClient/16;
+ break;
+ case SB_LINEDOWN:
+ nHscrollInc = img->cyClient/16;
+ break;
+ case SB_PAGEUP:
+ nHscrollInc = min(-1,-img->cxClient);
+ break;
+ case SB_PAGEDOWN:
+ nHscrollInc = max(1,img->cxClient);
+ break;
+ case SB_THUMBTRACK:
+ case SB_THUMBPOSITION:
+ nHscrollInc = HIWORD(wParam) - img->nHscrollPos;
+ break;
+ default:
+ nHscrollInc = 0;
+ }
+ if ((nHscrollInc = max(-img->nHscrollPos,
+ min(nHscrollInc, img->nHscrollMax - img->nHscrollPos)))!=0) {
+ img->nHscrollPos += nHscrollInc;
+ ScrollWindow(hwnd,-nHscrollInc,0,NULL,NULL);
+ UpdateScrollBarX(img);
+ UpdateWindow(hwnd);
+ }
+ return(0);
+ case WM_KEYDOWN:
+ switch(LOWORD(wParam)) {
+ case VK_HOME:
+ SendMessage(hwnd,WM_VSCROLL,SB_TOP,0L);
+ break;
+ case VK_END:
+ SendMessage(hwnd,WM_VSCROLL,SB_BOTTOM,0L);
+ break;
+ case VK_PRIOR:
+ SendMessage(hwnd,WM_VSCROLL,SB_PAGEUP,0L);
+ break;
+ case VK_NEXT:
+ SendMessage(hwnd,WM_VSCROLL,SB_PAGEDOWN,0L);
+ break;
+ case VK_UP:
+ SendMessage(hwnd,WM_VSCROLL,SB_LINEUP,0L);
+ break;
+ case VK_DOWN:
+ SendMessage(hwnd,WM_VSCROLL,SB_LINEDOWN,0L);
+ break;
+ case VK_LEFT:
+ SendMessage(hwnd,WM_HSCROLL,SB_PAGEUP,0L);
+ break;
+ case VK_RIGHT:
+ SendMessage(hwnd,WM_HSCROLL,SB_PAGEDOWN,0L);
+ break;
+ case VK_RETURN:
+ if (img->hwndtext)
+ BringWindowToTop(img->hwndtext);
+ break;
+ }
+ return(0);
+ case WM_CHAR:
+ /* send on all characters to text window */
+ if (img->hwndtext)
+ SendMessage(img->hwndtext, message, wParam, lParam);
+ else {
+ /* assume we have a console */
+ INPUT_RECORD ir;
+ HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
+ DWORD dwWritten = 0;
+ DWORD cks = 0;
+ ir.EventType = KEY_EVENT;
+ ir.Event.KeyEvent.bKeyDown = TRUE;
+ ir.Event.KeyEvent.wRepeatCount = lParam & 0xffff;
+ ir.Event.KeyEvent.wVirtualKeyCode = VkKeyScan((TCHAR)wParam) & 0xff;
+ ir.Event.KeyEvent.wVirtualScanCode =
+ (lParam >> 16) & 0xff;
+ ir.Event.KeyEvent.uChar.AsciiChar = wParam;
+ if (GetKeyState(VK_CAPITAL))
+ cks |= CAPSLOCK_ON;
+ /* ENHANCED_KEY unimplemented */
+ if (GetKeyState(VK_LMENU))
+ cks |= LEFT_ALT_PRESSED;
+ if (GetKeyState(VK_LCONTROL))
+ cks |= LEFT_CTRL_PRESSED;
+ if (GetKeyState(VK_NUMLOCK))
+ cks |= NUMLOCK_ON;
+ if (GetKeyState(VK_RMENU))
+ cks |= RIGHT_ALT_PRESSED;
+ if (GetKeyState(VK_RCONTROL))
+ cks |= RIGHT_CTRL_PRESSED;
+ if (GetKeyState(VK_SCROLL))
+ cks |= SCROLLLOCK_ON;
+ if (GetKeyState(VK_SHIFT))
+ cks |= SHIFT_PRESSED;
+ ir.Event.KeyEvent.dwControlKeyState = cks;
+ if (ir.Event.KeyEvent.uChar.AsciiChar == 3)
+ GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0L);
+ else if (hStdin != INVALID_HANDLE_VALUE)
+ WriteConsoleInput(hStdin, &ir, 1, &dwWritten);
+ }
+ return 0;
+ case WM_TIMER:
+ img->update_count++;
+ if (img->update_count >= img->update_interval)
+ image_update_now(img);
+ return 0;
+ case WM_PAINT:
+ {
+ int sx,sy,wx,wy,dx,dy;
+ RECT fillrect;
+ hdc = BeginPaint(hwnd, &ps);
+ if (img->hmutex != INVALID_HANDLE_VALUE)
+ WaitForSingleObject(img->hmutex, 120000);
+ SetMapMode(hdc, MM_TEXT);
+ SetBkMode(hdc,OPAQUE);
+ rect = ps.rcPaint;
+ dx = rect.left; /* destination */
+ dy = rect.top;
+ wx = rect.right-rect.left; /* width */
+ wy = rect.bottom-rect.top;
+ sx = rect.left; /* source */
+ sy = rect.top;
+ sx += img->nHscrollPos; /* scrollbars */
+ sy += img->nVscrollPos;
+ if (sx+wx > img->bmih.biWidth)
+ wx = img->bmih.biWidth - sx;
+ if (sy+wy > img->bmih.biHeight)
+ wy = img->bmih.biHeight - sy;
+
+ draw(img, hdc, dx, dy, wx, wy, sx, sy);
+
+ /* fill areas around page */
+ if (rect.right > img->bmih.biWidth) {
+ fillrect.top = rect.top;
+ fillrect.left = img->bmih.biWidth;
+ fillrect.bottom = rect.bottom;
+ fillrect.right = rect.right;
+ FillRect(hdc, &fillrect, img->hBrush);
+ }
+ if (rect.bottom > img->bmih.biHeight) {
+ fillrect.top = img->bmih.biHeight;
+ fillrect.left = rect.left;
+ fillrect.bottom = rect.bottom;
+ fillrect.right = rect.right;
+ FillRect(hdc, &fillrect, img->hBrush);
+ }
+
+ if (img->hmutex != INVALID_HANDLE_VALUE)
+ ReleaseMutex(img->hmutex);
+ EndPaint(hwnd, &ps);
+ return 0;
+ }
+ case WM_DROPFILES:
+ if (img->hwndtext)
+ SendMessage(img->hwndtext, message, wParam, lParam);
+ else {
+ char *szFile;
+ int i, cFiles;
+ unsigned int Len, error;
+ const char *p;
+ const char *szDragPre = "\r(";
+ const char *szDragPost = ") run\r";
+ HDROP hdrop = (HDROP)wParam;
+ cFiles = DragQueryFile(hdrop, (UINT)(-1), (LPSTR)NULL, 0);
+ for (i=0; i<cFiles; i++) {
+ Len = DragQueryFile(hdrop, i, NULL, 0);
+ szFile = malloc(Len+1);
+ if (szFile != 0) {
+ error = DragQueryFile(hdrop, i, szFile, Len+1);
+ if (error != 0) {
+ for (p=szDragPre; *p; p++)
+ SendMessage(hwnd,WM_CHAR,*p,1L);
+ for (p=szFile; *p; p++) {
+ if (*p == '\\')
+ SendMessage(hwnd,WM_CHAR,'/',1L);
+ else
+ SendMessage(hwnd,WM_CHAR,*p,1L);
+ }
+ for (p=szDragPost; *p; p++)
+ SendMessage(hwnd,WM_CHAR,*p,1L);
+ }
+ free(szFile);
+ }
+ }
+ DragFinish(hdrop);
+ }
+ break;
+ case WM_DESTROY:
+ { /* Save the text window size */
+ char winposbuf[64];
+ sprintf(winposbuf, "%d %d %d %d", img->x, img->y,
+ img->cx, img->cy);
+ win_set_reg_value((img->device != NULL ? "Image" : "Tracer"), winposbuf);
+ }
+ DragAcceptFiles(hwnd, FALSE);
+ break;
+
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+/* Repaint a section of the window. */
+static void
+draw(IMAGE *img, HDC hdc, int dx, int dy, int wx, int wy,
+ int sx, int sy)
+{
+ HPALETTE oldpalette;
+ struct bmi_s {
+ BITMAPINFOHEADER h;
+ unsigned short pal_index[256];
+ } bmi;
+ int i;
+ UINT which_colors;
+ unsigned char *line = NULL;
+ long ny;
+ unsigned char *bits;
+ BOOL directcopy = FALSE;
+
+ if (img->device == NULL)
+ return;
+
+ memset(&bmi.h, 0, sizeof(bmi.h));
+
+ bmi.h.biSize = sizeof(bmi.h);
+ bmi.h.biWidth = img->bmih.biWidth;
+ bmi.h.biHeight = wy;
+ bmi.h.biPlanes = 1;
+ bmi.h.biBitCount = img->bmih.biBitCount;
+ bmi.h.biCompression = 0;
+ bmi.h.biSizeImage = 0; /* default */
+ bmi.h.biXPelsPerMeter = 0; /* default */
+ bmi.h.biYPelsPerMeter = 0; /* default */
+ bmi.h.biClrUsed = img->bmih.biClrUsed;
+ bmi.h.biClrImportant = img->bmih.biClrImportant;
+
+ if (img->bmih.biClrUsed) {
+ /* palette colors */
+ for (i = 0; i < img->bmih.biClrUsed; i++)
+ bmi.pal_index[i] = i;
+ which_colors = DIB_PAL_COLORS;
+ }
+ else if (bmi.h.biBitCount == 16) {
+ DWORD* bmi_colors = (DWORD*)(&bmi.pal_index[0]);
+ bmi.h.biCompression = BI_BITFIELDS;
+ which_colors = DIB_RGB_COLORS;
+ if ((img->format & DISPLAY_555_MASK) == DISPLAY_NATIVE_555) {
+ /* 5-5-5 RGB mode */
+ bmi_colors[0] = 0x7c00;
+ bmi_colors[1] = 0x03e0;
+ bmi_colors[2] = 0x001f;
+ }
+ else {
+ /* 5-6-5 RGB mode */
+ bmi_colors[0] = 0xf800;
+ bmi_colors[1] = 0x07e0;
+ bmi_colors[2] = 0x001f;
+ }
+ }
+ else if (bmi.h.biBitCount == 32) {
+ unsigned int alpha = img->format & DISPLAY_ALPHA_MASK;
+ DWORD* bmi_colors = (DWORD*)(&bmi.pal_index[0]);
+ bmi.h.biCompression = BI_BITFIELDS;
+ which_colors = DIB_RGB_COLORS;
+ if ((img->format & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN) {
+ if ((alpha == DISPLAY_ALPHA_FIRST) ||
+ (alpha == DISPLAY_UNUSED_FIRST)) {
+ /* Mac mode */
+ bmi_colors[0] = 0x0000ff00;
+ bmi_colors[1] = 0x00ff0000;
+ bmi_colors[2] = 0xff000000;
+ }
+ else {
+ bmi_colors[0] = 0x000000ff;
+ bmi_colors[1] = 0x0000ff00;
+ bmi_colors[2] = 0x00ff0000;
+ }
+ }
+ else {
+ if ((alpha == DISPLAY_ALPHA_FIRST) ||
+ (alpha == DISPLAY_UNUSED_FIRST)) {
+ /* ignore alpha */
+ bmi_colors[0] = 0xff000000;
+ bmi_colors[1] = 0x00ff0000;
+ bmi_colors[2] = 0x0000ff00;
+ }
+ else {
+ /* Windows mode */
+ /* ignore alpha */
+ bmi_colors[0] = 0x00ff0000;
+ bmi_colors[1] = 0x0000ff00;
+ bmi_colors[2] = 0x000000ff;
+ }
+ }
+ } else {
+ bmi.h.biClrUsed = 0;
+ bmi.h.biClrImportant = 0;
+ which_colors = DIB_RGB_COLORS;
+ }
+
+ if (img->raster <= 0)
+ return;
+ if (img->bytewidth <= 0)
+ return;
+
+ /* Determine if the format is native and we can do a direct copy */
+ switch (img->format & DISPLAY_COLORS_MASK) {
+ case DISPLAY_COLORS_NATIVE:
+ switch (img->format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ case DISPLAY_DEPTH_4:
+ case DISPLAY_DEPTH_8:
+ directcopy = TRUE;
+ break;
+ case DISPLAY_DEPTH_16:
+ if ((img->format & DISPLAY_ENDIAN_MASK)
+ == DISPLAY_LITTLEENDIAN)
+ directcopy = TRUE;
+ break;
+ }
+ break;
+ case DISPLAY_COLORS_GRAY:
+ switch (img->format & DISPLAY_DEPTH_MASK) {
+ case DISPLAY_DEPTH_1:
+ case DISPLAY_DEPTH_4:
+ case DISPLAY_DEPTH_8:
+ directcopy = TRUE;
+ }
+ break;
+ case DISPLAY_COLORS_RGB:
+ if (((img->format & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8) &&
+ ((img->format & DISPLAY_ENDIAN_MASK) == DISPLAY_LITTLEENDIAN) &&
+ ((img->format & DISPLAY_ALPHA_MASK) == DISPLAY_ALPHA_NONE))
+ directcopy = TRUE; /* BGR24 */
+ if (((img->format & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8) &&
+ ((img->format & DISPLAY_ENDIAN_MASK) == DISPLAY_LITTLEENDIAN) &&
+ ((img->format & DISPLAY_ALPHA_MASK) == DISPLAY_UNUSED_LAST))
+ directcopy = TRUE; /* 32-bit */
+ break;
+ }
+
+ if (which_colors == DIB_PAL_COLORS) {
+ oldpalette = SelectPalette(hdc, img->palette, FALSE);
+ RealizePalette(hdc);
+ }
+
+ /*
+ * Windows apparently limits the size of a single transfer
+ * to 2 Mb, which can be exceeded on 24-bit displays.
+ */
+ ny = 2000000 / img->raster;
+
+ if (img->raster != img->bytewidth) /* not 32-bit architecture */
+ ny = 1;
+
+ /* If color format not native, convert it line by line */
+ /* This is slow, but these formats aren't normally used */
+ if (!directcopy) {
+ ny = 1;
+ line = (unsigned char *)malloc(img->bytewidth);
+ if (line == NULL)
+ return;
+ }
+
+ for (; wy; dy += ny, wy -= ny, sy += ny) {
+ ny = min(ny, wy);
+ if (directcopy) {
+ bits = img->image + img->raster * (img->bmih.biHeight - (sy + ny));
+ }
+ else {
+ image_convert_line(img, line,
+ img->image + img->raster * (img->bmih.biHeight - (sy + ny)));
+ bits = line;
+ }
+ SetDIBitsToDevice(hdc, dx, dy, wx, ny, sx, 0, 0, ny, bits,
+ (BITMAPINFO *) & bmi, which_colors);
+ }
+
+ if (which_colors == DIB_PAL_COLORS)
+ SelectPalette(hdc, oldpalette, FALSE);
+
+ if (line)
+ free(line);
+}
diff --git a/psi/dwimg.h b/psi/dwimg.h
new file mode 100644
index 000000000..4d22e5ee0
--- /dev/null
+++ b/psi/dwimg.h
@@ -0,0 +1,98 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+#ifndef dwimg_INCLUDED
+# define dwimg_INCLUDED
+
+/* Windows Image Window structure */
+
+typedef struct IMAGE_DEVICEN_S IMAGE_DEVICEN;
+struct IMAGE_DEVICEN_S {
+ int used; /* non-zero if in use */
+ int visible; /* show on window */
+ char name[64];
+ int cyan;
+ int magenta;
+ int yellow;
+ int black;
+ int menu; /* non-zero if menu item added to system menu */
+};
+#define IMAGE_DEVICEN_MAX 8
+
+typedef struct IMAGE_S IMAGE;
+struct IMAGE_S {
+ void *handle;
+ void *device;
+ HWND hwnd;
+ HBRUSH hBrush; /* background */
+ int raster;
+ unsigned int format;
+ unsigned char *image;
+ BITMAPINFOHEADER bmih;
+ HPALETTE palette;
+ int bytewidth;
+ int devicen_gray; /* true if a single separation should be shown gray */
+ IMAGE_DEVICEN devicen[IMAGE_DEVICEN_MAX];
+
+ /* periodic redrawing */
+ UINT update_timer; /* identifier */
+ int update_tick; /* timer duration in milliseconds */
+ int update_count; /* Number of WM_TIMER messages received */
+ int update_interval; /* Number of WM_TIMER until refresh */
+ int pending_update; /* We have asked for periodic updates */
+ int pending_sync; /* We have asked for a SYNC */
+
+ /* Window scrolling stuff */
+ int cxClient, cyClient;
+ int cxAdjust, cyAdjust;
+ int nVscrollPos, nVscrollMax;
+ int nHscrollPos, nHscrollMax;
+
+ /* thread synchronisation */
+ HANDLE hmutex;
+
+ IMAGE *next;
+
+ HWND hwndtext; /* handle to text window */
+
+ int x, y, cx, cy; /* window position */
+};
+
+extern IMAGE *first_image;
+
+/* Main thread only */
+IMAGE *image_find(void *handle, void *device);
+IMAGE *image_new(void *handle, void *device);
+void image_delete(IMAGE *img);
+int image_size(IMAGE *img, int new_width, int new_height, int new_raster,
+ unsigned int new_format, void *pimage);
+int image_separation(IMAGE *img, int comp_num, const char *name,
+ unsigned short c, unsigned short m, unsigned short y, unsigned short k);
+
+/* GUI thread only */
+void image_open(IMAGE *img);
+void image_close(IMAGE *img);
+void image_sync(IMAGE *img);
+void image_page(IMAGE *img);
+void image_presize(IMAGE *img, int new_width, int new_height, int new_raster,
+ unsigned int new_format);
+void image_poll(IMAGE *img);
+void image_updatesize(IMAGE *img);
+
+/* To be called during initialization after the text window has been created */
+void image_textwindow(HWND hwnd);
+
+#endif /* dwimg_INCLUDED */
diff --git a/psi/dwmain.c b/psi/dwmain.c
new file mode 100644
index 000000000..7c8b1c790
--- /dev/null
+++ b/psi/dwmain.c
@@ -0,0 +1,564 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Ghostscript DLL loader for Windows */
+
+#include "windows_.h"
+#include <shellapi.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "gscdefs.h"
+#define GSREVISION gs_revision
+#include "ierrors.h"
+#include "iapi.h"
+#include "vdtrace.h"
+
+#include "dwres.h"
+#include "dwdll.h"
+#include "dwtext.h"
+#include "dwimg.h"
+#include "dwtrace.h"
+#include "dwreg.h"
+#include "gdevdsp.h"
+
+/* public handles */
+HINSTANCE ghInstance;
+
+/* redirected stdio */
+TW *tw;
+
+static const LPSTR szAppName = "Ghostscript";
+
+#ifdef _WIN64
+const LPSTR szIniName = "gswin64.ini";
+const char *szDllName = "gsdll64.dll";
+#else
+const LPSTR szIniName = "gswin32.ini";
+const char *szDllName = "gsdll32.dll";
+#endif
+const LPSTR szIniSection = "Text";
+
+GSDLL gsdll;
+void *instance;
+HWND hwndtext;
+
+char start_string[] = "systemdict /start get exec\n";
+
+static int poll(void)
+{
+ MSG msg;
+ while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ /* If text window closing then abort Ghostscript */
+ if (tw->quitnow)
+ return gs_error_Fatal;
+ return 0;
+}
+
+/*********************************************************************/
+/* stdio functions */
+static int GSDLLCALL
+gsdll_stdin(void *instance, char *buf, int len)
+{
+ return text_read_line(tw, buf, len);
+}
+
+static int GSDLLCALL
+gsdll_stdout(void *instance, const char *str, int len)
+{
+ text_write_buf(tw, str, len);
+ return len;
+}
+
+static int GSDLLCALL
+gsdll_stderr(void *instance, const char *str, int len)
+{
+ text_write_buf(tw, str, len);
+ return len;
+}
+
+/* Poll the caller for cooperative multitasking. */
+/* If this function is NULL, polling is not needed */
+static int GSDLLCALL gsdll_poll(void *handle)
+{
+ return poll();
+}
+/*********************************************************************/
+
+/* new dll display device */
+/*
+#define DISPLAY_DEBUG
+ */
+
+/* New device has been opened */
+/* This is the first event from this device. */
+static int display_open(void *handle, void *device)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ char buf[256];
+ sprintf(buf, "display_open(0x%x, 0x%x)\n", handle, device);
+ text_puts(tw, buf);
+#endif
+ img = image_new(handle, device); /* create and add to list */
+ if (img)
+ image_open(img);
+ return 0;
+}
+
+/* Device is about to be closed. */
+/* Device will not be closed until this function returns. */
+static int display_preclose(void *handle, void *device)
+{
+#ifdef DISPLAY_DEBUG
+ char buf[256];
+ sprintf(buf, "display_preclose(0x%x, 0x$x)\n", handle, device);
+ text_puts(tw, buf);
+#endif
+ /* do nothing - no thread synchonisation needed */
+ return 0;
+}
+
+/* Device has been closed. */
+/* This is the last event from this device. */
+static int display_close(void *handle, void *device)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ char buf[256];
+ sprintf(buf, "display_close(0x%x, 0x$x)\n", handle, device);
+ text_puts(tw, buf);
+#endif
+ img = image_find(handle, device);
+ if (img) {
+ image_delete(img); /* remove from list but don't free */
+ image_close(img);
+ }
+ return 0;
+}
+
+/* Device is about to be resized. */
+/* Resize will only occur if this function returns 0. */
+static int display_presize(void *handle, void *device, int width, int height,
+ int raster, unsigned int format)
+{
+#ifdef DISPLAY_DEBUG
+ char buf[256];
+ sprintf(buf, "display_presize(0x%x, 0x%x, width=%d height=%d raster=%d\n\
+ format=%d)\n",
+ handle, device, width, height, raster, format);
+ text_puts(tw, buf);
+#endif
+ return 0;
+}
+
+/* Device has been resized. */
+/* New pointer to raster returned in pimage */
+static int display_size(void *handle, void *device, int width, int height,
+ int raster, unsigned int format, unsigned char *pimage)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ char buf[256];
+ sprintf(buf, "display_size(0x%x, 0x%x, width=%d height=%d raster=%d\n\
+ format=%d image=0x%x)\n",
+ handle, device, width, height, raster, format, pimage);
+ text_puts(tw, buf);
+#endif
+ img = image_find(handle, device);
+ image_size(img, width, height, raster, format, pimage);
+ image_updatesize(img);
+ return 0;
+}
+
+/* flushpage */
+static int display_sync(void *handle, void *device)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ char buf[256];
+ sprintf(buf, "display_sync(0x%x, 0x%x)\n", handle, device);
+ text_puts(tw, buf);
+#endif
+ img = image_find(handle, device);
+ image_sync(img);
+ return 0;
+}
+
+/* showpage */
+/* If you want to pause on showpage, then don't return immediately */
+static int display_page(void *handle, void *device, int copies, int flush)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ char buf[256];
+ sprintf(buf, "display_page(0x%x, 0x%x, copies=%d flush=%d)\n",
+ handle, device, copies, flush);
+ text_puts(tw, buf);
+#endif
+ img = image_find(handle, device);
+ image_page(img);
+ return 0;
+}
+
+/* Poll the caller for cooperative multitasking. */
+/* If this function is NULL, polling is not needed */
+static int display_update(void *handle, void *device,
+ int x, int y, int w, int h)
+{
+ IMAGE *img;
+ img = image_find(handle, device);
+ image_poll(img); /* redraw the window periodically */
+ return poll();
+}
+
+int display_separation(void *handle, void *device,
+ int comp_num, const char *name,
+ unsigned short c, unsigned short m,
+ unsigned short y, unsigned short k)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, "display_separation(0x%x, 0x%x, %d '%s' %d,%d,%d,%d)\n",
+ handle, device, comp_num, name, (int)c, (int)m, (int)y, (int)k);
+#endif
+ img = image_find(handle, device);
+ if (img)
+ image_separation(img, comp_num, name, c, m, y, k);
+ return 0;
+}
+
+display_callback display = {
+ sizeof(display_callback),
+ DISPLAY_VERSION_MAJOR,
+ DISPLAY_VERSION_MINOR,
+ display_open,
+ display_preclose,
+ display_close,
+ display_presize,
+ display_size,
+ display_sync,
+ display_page,
+ display_update,
+ NULL, /* memalloc */
+ NULL, /* memfree */
+ display_separation
+};
+
+/*********************************************************************/
+
+/* program really starts at WinMain */
+int new_main(int argc, char *argv[])
+{
+ int code, code1;
+ int exit_status;
+ int exit_code;
+ int nargc;
+ char **nargv;
+ char dformat[64];
+ char ddpi[64];
+ char buf[256];
+
+ memset(buf, 0, sizeof(buf));
+ if (load_dll(&gsdll, buf, sizeof(buf))) {
+ text_puts(tw, "Can't load Ghostscript DLL\n");
+ text_puts(tw, buf);
+ text_puts(tw, "\n");
+ return 1;
+ }
+
+ if (gsdll.new_instance(&instance, NULL) < 0) {
+ text_puts(tw, "Can't create Ghostscript instance\n");
+ return 1;
+ }
+
+#ifdef DEBUG
+ visual_tracer_init();
+ gsdll.set_visual_tracer(&visual_tracer);
+#endif
+
+ gsdll.set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr);
+ gsdll.set_poll(instance, gsdll_poll);
+ gsdll.set_display_callback(instance, &display);
+
+ /* insert display device defaults as first arguments */
+ { int format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_1 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ HDC hdc = GetDC(NULL); /* get hdc for desktop */
+ int depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
+ sprintf(ddpi, "-dDisplayResolution=%d", GetDeviceCaps(hdc, LOGPIXELSY));
+ ReleaseDC(NULL, hdc);
+ if (depth == 32)
+ format = DISPLAY_COLORS_RGB | DISPLAY_UNUSED_LAST |
+ DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ else if (depth == 16)
+ format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_16 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST |
+ DISPLAY_NATIVE_555;
+ else if (depth > 8)
+ format = DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ else if (depth >= 8)
+ format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ else if (depth >= 4)
+ format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_4 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ sprintf(dformat, "-dDisplayFormat=%d", format);
+ }
+ nargc = argc + 2;
+ nargv = (char **)malloc((nargc + 1) * sizeof(char *));
+ nargv[0] = argv[0];
+ nargv[1] = dformat;
+ nargv[2] = ddpi;
+ memcpy(&nargv[3], &argv[1], argc * sizeof(char *));
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+ __try {
+#endif
+ code = gsdll.init_with_args(instance, nargc, nargv);
+ if (code == 0)
+ code = gsdll.run_string(instance, start_string, 0, &exit_code);
+ code1 = gsdll.exit(instance);
+ if (code == 0 || (code == gs_error_Quit && code1 != 0))
+ code = code1;
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+ } __except(exception_code() == EXCEPTION_STACK_OVERFLOW) {
+ code = gs_error_Fatal;
+ text_puts(tw, "*** C stack overflow. Quiting...\n");
+ }
+#endif
+
+ gsdll.delete_instance(instance);
+
+#ifdef DEBUG
+ visual_tracer_close();
+#endif
+
+ unload_dll(&gsdll);
+
+ free(nargv);
+
+ exit_status = 0;
+ switch (code) {
+ case 0:
+ case gs_error_Quit:
+ break;
+ case gs_error_Fatal:
+ exit_status = 1;
+ break;
+ case gs_error_Info:
+ default:
+ exit_status = 255;
+ }
+
+ return exit_status;
+}
+
+void
+set_font(void)
+{
+ int fontsize;
+ char fontname[256];
+ char buf[32];
+
+ /* read ini file */
+ GetPrivateProfileString(szIniSection, "FontName", "Courier New", fontname, sizeof(fontname), szIniName);
+ fontsize = GetPrivateProfileInt(szIniSection, "FontSize", 10, szIniName);
+
+ /* set font */
+ text_font(tw, fontname, fontsize);
+
+ /* write ini file */
+ WritePrivateProfileString(szIniSection, "FontName", fontname, szIniName);
+ sprintf(buf, "%d", fontsize);
+ WritePrivateProfileString(szIniSection, "FontSize", buf, szIniName);
+}
+
+typedef BOOL (SetProcessDPIAwareFn)(void);
+
+static void
+avoid_windows_scale(void)
+{
+ /* Fetch the function address and only call it if it is there; this keeps
+ * compatability with Windows < 8.1 */
+ HMODULE hUser32 = LoadLibrary(TEXT("user32.dll"));
+ SetProcessDPIAwareFn *ptr;
+
+ ptr = (SetProcessDPIAwareFn *)GetProcAddress(hUser32, "SetProcessDPIAware");
+ if (ptr != NULL)
+ ptr();
+ FreeLibrary(hUser32);
+}
+
+int PASCAL
+WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int cmdShow)
+{
+ int dll_exit_status;
+#define MAXCMDTOKENS 128
+ /* BC++ 4.5 will give us _argc and _argv[], but they will be */
+ /* incorrect if there is a space in the program name. */
+ /* Provide our own parsing code to create argc and argv[]. */
+ int argc;
+ LPSTR argv[MAXCMDTOKENS];
+ LPSTR p;
+#ifndef GS_NO_UTF8
+ LPSTR pstart;
+#endif
+ char command[256];
+ char *args;
+ char *d, *e;
+ char winposbuf[256];
+ int len = sizeof(winposbuf);
+ int x, y, cx, cy;
+
+ /* Mark us as being 'system dpi aware' to avoid horrid scaling */
+ avoid_windows_scale();
+
+ /* copy the hInstance into a variable so it can be used */
+ ghInstance = hInstance;
+
+ if (hPrevInstance) {
+ MessageBox((HWND)NULL,"Can't run twice", szAppName,
+ MB_ICONHAND | MB_OK);
+ return FALSE;
+ }
+
+ /* If called with "gswin32c.exe arg1 arg2"
+ * lpszCmdLine returns:
+ * "arg1 arg2" if called with CreateProcess(NULL, command, ...)
+ * "arg2" if called with CreateProcess(command, args, ...)
+ * GetCommandLine() returns
+ * ""gswin32c.exe" arg1 arg2"
+ * if called with CreateProcess(NULL, command, ...)
+ * " arg1 arg2"
+ * if called with CreateProcess(command, args, ...)
+ * Consequently we must use GetCommandLine()
+ */
+#ifdef GS_NO_UTF8
+ p = GetCommandLine();
+#else
+ {
+ wchar_t *uni = GetCommandLineW();
+ pstart = p = malloc(wchar_to_utf8(NULL, uni));
+ if (p != NULL)
+ wchar_to_utf8(p, uni);
+ }
+#endif
+
+ argc = 0;
+ args = (char *)malloc(lstrlen(p)+1);
+ if (args == (char *)NULL) {
+ fprintf(stdout, "Insufficient memory in WinMain()\n");
+ return 1;
+ }
+
+ /* Parse command line handling quotes. */
+ d = args;
+ while (*p) {
+ /* for each argument */
+
+ if (argc >= MAXCMDTOKENS - 1)
+ break;
+
+ e = d;
+ while ((*p) && (*p != ' ')) {
+ if (*p == '\042') {
+ /* Remove quotes, skipping over embedded spaces. */
+ /* Doesn't handle embedded quotes. */
+ p++;
+ while ((*p) && (*p != '\042'))
+ *d++ =*p++;
+ }
+ else
+ *d++ = *p;
+ if (*p)
+ p++;
+ }
+ *d++ = '\0';
+ argv[argc++] = e;
+
+ while ((*p) && (*p == ' '))
+ p++; /* Skip over trailing spaces */
+ }
+ argv[argc] = NULL;
+
+#ifndef GS_NO_UTF8
+ free(pstart);
+#endif
+
+ if (strlen(argv[0]) == 0) {
+ GetModuleFileName(hInstance, command, sizeof(command)-1);
+ argv[0] = command;
+ }
+
+ tw = text_new();
+ if (tw == NULL) {
+ MessageBox((HWND)NULL, "Can't create text window",
+ szAppName, MB_OK | MB_ICONSTOP);
+ return 1;
+ }
+
+ /* start up the text window */
+ if (!hPrevInstance) {
+ HICON hicon = LoadIcon(hInstance, (LPSTR)MAKEINTRESOURCE(GSTEXT_ICON));
+ text_register_class(tw, hicon);
+ }
+ set_font();
+ text_size(tw, 80, 80);
+ text_drag(tw, "(", ") run\r");
+ if (win_get_reg_value("Text", winposbuf, &len) == 0) {
+ if (sscanf(winposbuf, "%d %d %d %d", &x, &y, &cx, &cy) == 4)
+ text_setpos(tw, x, y, cx, cy);
+ }
+
+ /* create the text window */
+ if (text_create(tw, szAppName, cmdShow))
+ exit(1);
+
+ hwndtext = text_get_handle(tw);
+ image_textwindow(hwndtext);
+
+ dll_exit_status = new_main(argc, argv);
+
+ if (dll_exit_status && !tw->quitnow) {
+ /* display error message in text window */
+ MSG msg;
+ text_puts(tw, "\nClose this window with the close button on the title bar or the system menu.\n");
+ if (IsIconic(text_get_handle(tw)))
+ ShowWindow(text_get_handle(tw), SW_SHOWNORMAL);
+ BringWindowToTop(text_get_handle(tw)); /* make text window visible */
+ FlashWindow(text_get_handle(tw), TRUE);
+ /* Wait until error message is read */
+ while (!tw->quitnow && GetMessage(&msg, (HWND)NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ /* Save the text window size */
+ if (text_getpos(tw, &x, &y, &cx, &cy) == 0) {
+ sprintf(winposbuf, "%d %d %d %d", x, y, cx, cy);
+ win_set_reg_value("Text", winposbuf);
+ }
+
+ text_destroy(tw);
+ tw = NULL;
+
+ return dll_exit_status;
+}
diff --git a/psi/dwmain.rc b/psi/dwmain.rc
new file mode 100644
index 000000000..e75c0154a
--- /dev/null
+++ b/psi/dwmain.rc
@@ -0,0 +1,33 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+#include <windows.h>
+#include "dwres.h"
+
+#ifndef gstext_ico
+#define gstext_ico gswin.ico
+#endif
+#ifndef gsgraph_ico
+#define gsgraph_ico gswin.ico
+#endif
+
+GSTEXT_ICON ICON gstext_ico
+GSIMAGE_ICON ICON gsgraph_ico
+
+#ifndef DS_3DLOOK
+#define DS_3DLOOK 0x0004L /* for Windows 95 look */
+#endif
diff --git a/psi/dwmain32.def b/psi/dwmain32.def
new file mode 100644
index 000000000..3972c8cf3
--- /dev/null
+++ b/psi/dwmain32.def
@@ -0,0 +1,3 @@
+NAME GSWIN32
+HEAPSIZE 256
+STACKSIZE 131072
diff --git a/psi/dwmain64.def b/psi/dwmain64.def
new file mode 100644
index 000000000..1b9341a9c
--- /dev/null
+++ b/psi/dwmain64.def
@@ -0,0 +1,3 @@
+NAME GSWIN64
+HEAPSIZE 256
+STACKSIZE 131072
diff --git a/psi/dwmainc.c b/psi/dwmainc.c
new file mode 100644
index 000000000..f71d8643a
--- /dev/null
+++ b/psi/dwmainc.c
@@ -0,0 +1,715 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* prevent gp.h redefining sprintf */
+#define sprintf sprintf
+
+/* dwmainc.c */
+
+#include "windows_.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <io.h>
+#include <fcntl.h>
+#include <process.h>
+#include "ierrors.h"
+#include "iapi.h"
+#include "vdtrace.h"
+#include "gdevdsp.h"
+#include "dwdll.h"
+#include "dwimg.h"
+#include "dwtrace.h"
+
+/* Patch by Rod Webster (rodw) */
+/* Added conditional below to allow Borland Compilation (Tested on 5.5) */
+/* It would be better to place this code in an include file but no dwmainc.h exists */
+#ifdef __BORLANDC__
+#define _isatty isatty
+#define _setmode setmode
+#endif
+
+GSDLL gsdll;
+void *instance;
+#ifndef METRO
+BOOL quitnow = FALSE;
+HANDLE hthread;
+DWORD thread_id;
+HWND hwndforeground; /* our best guess for our console window handle */
+#endif
+
+char start_string[] = "systemdict /start get exec\n";
+
+/*********************************************************************/
+/* stdio functions */
+
+static int GSDLLCALL
+gsdll_stdin(void *instance, char *buf, int len)
+{
+ return _read(fileno(stdin), buf, len);
+}
+
+static int GSDLLCALL
+gsdll_stdout(void *instance, const char *str, int len)
+{
+ fwrite(str, 1, len, stdout);
+ fflush(stdout);
+ return len;
+}
+
+static int GSDLLCALL
+gsdll_stderr(void *instance, const char *str, int len)
+{
+ fwrite(str, 1, len, stderr);
+ fflush(stderr);
+ return len;
+}
+
+#ifndef GS_NO_UTF8
+/* stdio functions - versions that translate to/from utf-8 */
+static int GSDLLCALL
+gsdll_stdin_utf8(void *instance, char *buf, int len)
+{
+ static WCHAR thiswchar = 0; /* wide character to convert to multiple bytes */
+ static int nmore = 0; /* number of additional encoding bytes to generate */
+ UINT consolecp = 0;
+ int nret = 0; /* number of bytes returned to caller */
+ int i;
+
+ /* protect against caller passing invalid len */
+ while (len > 0) {
+ while (len && nmore) {
+ nmore--;
+ *buf++ = 0x80 | ((thiswchar >> (6 * nmore)) & 0x3F), nret++;
+ len--;
+ }
+ while (len) {
+ if (0 >= _read(fileno(stdin), buf, 1))
+ return nret;
+ nret++, buf++, len--;
+ if (buf[-1] == '\n')
+ /* return at end of line (note: no traslation needed) */
+ return nret;
+ else if ((unsigned char)buf[-1] <= 0x7F)
+ /* no translation needed for 7-bit ASCII codes */
+ continue;
+ else {
+ /* extended character, may be double */
+ BYTE dbcsstr[2];
+
+ dbcsstr[0] = buf[-1];
+ if (!consolecp)
+ consolecp = GetConsoleCP();
+ thiswchar = L'?'; /* initialize in case the conversion below fails */
+ if (IsDBCSLeadByteEx(consolecp, dbcsstr[0])) {
+ /* double-byte character code, fetch the trail byte */
+ _read(fileno(stdin), &dbcsstr[1], 1);
+ MultiByteToWideChar(consolecp, 0, dbcsstr, 2, &thiswchar, 1);
+ }
+ else {
+ MultiByteToWideChar(consolecp, 0, dbcsstr, 1, &thiswchar, 1);
+ }
+ /* convert thiswchar to utf-8 */
+ if (thiswchar <= 0x007F) { /* encoded as single byte */
+ buf[-1] = (char)thiswchar;
+ } else if (thiswchar <= 0x07FF) { /* encoded as 2 bytes */
+ buf[-1] = 0xC0 | ((thiswchar >> 6) & 0x1F);
+ nmore = 1;
+ break;
+ } else if (thiswchar <= 0xFFFF) { /* encoded as 3 bytes */
+ buf[-1] = 0xE0 | ((thiswchar >> 12) & 0xF);
+ nmore = 2;
+ break;
+ } else
+ /* note: codes outside the BMP not handled */
+ buf[-1] = '?';
+ }
+ }
+ }
+ return nret;
+}
+
+static void
+gsdll_utf8write(FILE *stdwr, const char *str, int len, WCHAR *thiswchar, int *nmore)
+{
+ UINT consolecp = 0;
+
+ /* protect against caller passing invalid len */
+ while (len > 0) {
+ const char *str0;
+
+ /* write ASCII chars without translation */
+ for (str0 = str; len && !(*str & 0x80); str++, len--);
+ if (str > str0) {
+ if (*nmore) {
+ /* output previous, incomplete utf-8 sequence as ASCII "?" */
+ fwrite("?", 1, 1, stdwr);
+ *nmore = 0, *thiswchar = 0;
+ }
+ fwrite(str0, 1, str - str0, stdwr);
+ }
+ /* accumulate lead/trail bytes into *thiswchar */
+ for (; len; str++, len--) {
+ switch (*str & 0xC0) {
+ case 0x80: /* trail byte */
+ if (*nmore) {
+ (*nmore)--;
+ *thiswchar |= (WCHAR)(unsigned char)(*str & 0x3F) << (6 * *nmore);
+ }
+ else {
+ /* lead byte missing; output unexpected trail byte as ASCII "?" */
+ *nmore = 0;
+ *thiswchar = L'?';
+ }
+ break;
+ case 0xC0: /* lead byte */
+ if (*nmore)
+ /* output previous, incomplete utf-8 sequence as ASCII "?" */
+ fwrite("?", 1, 1, stdwr);
+ if (!(*str & 0x20))
+ *nmore = 1; /* 2-byte encoding */
+ else if (!(*str & 0x10))
+ *nmore = 2; /* 3-byte encoding */
+ else if (!(*str & 0x08))
+ *nmore = 3; /* 4-byte encoding */
+ else
+ *nmore = 0; /* restricted (> 4) or invalid encodings */
+ if (*nmore)
+ *thiswchar = (WCHAR)(unsigned char)(*str & (0x3F >> *nmore)) << (6 * *nmore);
+ else {
+ /* output invalid encoding as ASCII "?" */
+ *thiswchar = L'?';
+ }
+ break;
+ default: /* cannot happen because *str has MSB set */
+ break;
+ }
+ /* output wide character if finished */
+ if (!*nmore) {
+ char mbstr[8];
+ int n_mbstr;
+
+ if (!consolecp)
+ consolecp = GetConsoleOutputCP();
+ n_mbstr = WideCharToMultiByte(consolecp, 0, thiswchar, 1, mbstr, sizeof mbstr, NULL, NULL);
+ if (n_mbstr <= 0)
+ fwrite("?", 1, 1, stdwr);
+ else
+ fwrite(mbstr, 1, n_mbstr, stdwr);
+ *thiswchar = 0; /* cleanup */
+ str++, len--;
+ break;
+ }
+ }
+ }
+ fflush(stdwr);
+}
+
+static int GSDLLCALL
+gsdll_stdout_utf8(void *instance, const char *utf8str, int bytelen)
+{
+ static WCHAR thiswchar = 0; /* accumulates the bits from multiple encoding bytes */
+ static int nmore = 0; /* expected number of additional encoding bytes */
+
+ gsdll_utf8write(stdout, utf8str, bytelen, &thiswchar, &nmore);
+ return bytelen;
+}
+
+static int GSDLLCALL
+gsdll_stderr_utf8(void *instance, const char *utf8str, int bytelen)
+{
+ static WCHAR thiswchar = 0; /* accumulates the bits from multiple encoding bytes */
+ static int nmore = 0; /* expected number of additional encoding bytes */
+
+ gsdll_utf8write(stderr, utf8str, bytelen, &thiswchar, &nmore);
+ return bytelen;
+}
+#endif
+
+/*********************************************************************/
+/* dll device */
+
+#ifndef METRO
+/* We must run windows from another thread, since main thread */
+/* is running Ghostscript and blocks on stdin. */
+
+/* We notify second thread of events using PostThreadMessage()
+ * with the following messages. Apparently Japanese Windows sends
+ * WM_USER+1 with lParam == 0 and crashes. So we use WM_USER+101.
+ * Fix from Akira Kakuto
+ */
+#define DISPLAY_OPEN WM_USER+101
+#define DISPLAY_CLOSE WM_USER+102
+#define DISPLAY_SIZE WM_USER+103
+#define DISPLAY_SYNC WM_USER+104
+#define DISPLAY_PAGE WM_USER+105
+#define DISPLAY_UPDATE WM_USER+106
+
+/*
+#define DISPLAY_DEBUG
+*/
+
+/* The second thread is the message loop */
+static void winthread(void *arg)
+{
+ MSG msg;
+ thread_id = GetCurrentThreadId();
+ hthread = GetCurrentThread();
+
+ while (!quitnow && GetMessage(&msg, (HWND)NULL, 0, 0)) {
+ switch (msg.message) {
+ case DISPLAY_OPEN:
+ image_open((IMAGE *)msg.lParam);
+ break;
+ case DISPLAY_CLOSE:
+ {
+ IMAGE *img = (IMAGE *)msg.lParam;
+ HANDLE hmutex = img->hmutex;
+ image_close(img);
+ CloseHandle(hmutex);
+ }
+ break;
+ case DISPLAY_SIZE:
+ image_updatesize((IMAGE *)msg.lParam);
+ break;
+ case DISPLAY_SYNC:
+ image_sync((IMAGE *)msg.lParam);
+ break;
+ case DISPLAY_PAGE:
+ image_page((IMAGE *)msg.lParam);
+ break;
+ case DISPLAY_UPDATE:
+ image_poll((IMAGE *)msg.lParam);
+ break;
+ default:
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+}
+
+/* New device has been opened */
+/* Tell user to use another device */
+int display_open(void *handle, void *device)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, "display_open(0x%x, 0x%x)\n", handle, device);
+#endif
+ img = image_new(handle, device); /* create and add to list */
+ if (img) {
+ img->hmutex = CreateMutex(NULL, FALSE, NULL);
+ PostThreadMessage(thread_id, DISPLAY_OPEN, 0, (LPARAM)img);
+ }
+ return 0;
+}
+
+int display_preclose(void *handle, void *device)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, "display_preclose(0x%x, 0x%x)\n", handle, device);
+#endif
+ img = image_find(handle, device);
+ if (img) {
+ /* grab mutex to stop other thread using bitmap */
+ WaitForSingleObject(img->hmutex, 120000);
+ }
+ return 0;
+}
+
+int display_close(void *handle, void *device)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, "display_close(0x%x, 0x%x)\n", handle, device);
+#endif
+ img = image_find(handle, device);
+ if (img) {
+ /* This is a hack to pass focus from image window to console */
+ if (GetForegroundWindow() == img->hwnd)
+ SetForegroundWindow(hwndforeground);
+
+ image_delete(img); /* remove from list, but don't free */
+ PostThreadMessage(thread_id, DISPLAY_CLOSE, 0, (LPARAM)img);
+ }
+ return 0;
+}
+
+int display_presize(void *handle, void *device, int width, int height,
+ int raster, unsigned int format)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, "display_presize(0x%x 0x%x, %d, %d, %d, %d, %ld)\n",
+ handle, device, width, height, raster, format);
+#endif
+ img = image_find(handle, device);
+ if (img) {
+ /* grab mutex to stop other thread using bitmap */
+ WaitForSingleObject(img->hmutex, 120000);
+ }
+ return 0;
+}
+
+int display_size(void *handle, void *device, int width, int height,
+ int raster, unsigned int format, unsigned char *pimage)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, "display_size(0x%x 0x%x, %d, %d, %d, %d, %ld, 0x%x)\n",
+ handle, device, width, height, raster, format, pimage);
+#endif
+ img = image_find(handle, device);
+ if (img) {
+ image_size(img, width, height, raster, format, pimage);
+ /* release mutex to allow other thread to use bitmap */
+ ReleaseMutex(img->hmutex);
+ PostThreadMessage(thread_id, DISPLAY_SIZE, 0, (LPARAM)img);
+ }
+ return 0;
+}
+
+int display_sync(void *handle, void *device)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, "display_sync(0x%x, 0x%x)\n", handle, device);
+#endif
+ img = image_find(handle, device);
+ if (img && !img->pending_sync) {
+ img->pending_sync = 1;
+ PostThreadMessage(thread_id, DISPLAY_SYNC, 0, (LPARAM)img);
+ }
+ return 0;
+}
+
+int display_page(void *handle, void *device, int copies, int flush)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, "display_page(0x%x, 0x%x, copies=%d, flush=%d)\n",
+ handle, device, copies, flush);
+#endif
+ img = image_find(handle, device);
+ if (img)
+ PostThreadMessage(thread_id, DISPLAY_PAGE, 0, (LPARAM)img);
+ return 0;
+}
+
+int display_update(void *handle, void *device,
+ int x, int y, int w, int h)
+{
+ IMAGE *img;
+ img = image_find(handle, device);
+ if (img && !img->pending_update && !img->pending_sync) {
+ img->pending_update = 1;
+ PostThreadMessage(thread_id, DISPLAY_UPDATE, 0, (LPARAM)img);
+ }
+ return 0;
+}
+
+/*
+#define DISPLAY_DEBUG_USE_ALLOC
+*/
+#ifdef DISPLAY_DEBUG_USE_ALLOC
+/* This code isn't used, but shows how to use this function */
+void *display_memalloc(void *handle, void *device, unsigned long size)
+{
+ void *mem;
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, "display_memalloc(0x%x 0x%x %d)\n",
+ handle, device, size);
+#endif
+ mem = malloc(size);
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, " returning 0x%x\n", (int)mem);
+#endif
+ return mem;
+}
+
+int display_memfree(void *handle, void *device, void *mem)
+{
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, "display_memfree(0x%x, 0x%x, 0x%x)\n",
+ handle, device, mem);
+#endif
+ free(mem);
+ return 0;
+}
+#endif
+
+int display_separation(void *handle, void *device,
+ int comp_num, const char *name,
+ unsigned short c, unsigned short m,
+ unsigned short y, unsigned short k)
+{
+ IMAGE *img;
+#ifdef DISPLAY_DEBUG
+ fprintf(stdout, "display_separation(0x%x, 0x%x, %d '%s' %d,%d,%d,%d)\n",
+ handle, device, comp_num, name, (int)c, (int)m, (int)y, (int)k);
+#endif
+ img = image_find(handle, device);
+ if (img)
+ image_separation(img, comp_num, name, c, m, y, k);
+ return 0;
+}
+
+display_callback display = {
+ sizeof(display_callback),
+ DISPLAY_VERSION_MAJOR,
+ DISPLAY_VERSION_MINOR,
+ display_open,
+ display_preclose,
+ display_close,
+ display_presize,
+ display_size,
+ display_sync,
+ display_page,
+ display_update,
+#ifdef DISPLAY_DEBUG_USE_ALLOC
+ display_memalloc, /* memalloc */
+ display_memfree, /* memfree */
+#else
+ NULL, /* memalloc */
+ NULL, /* memfree */
+#endif
+ display_separation
+};
+#endif /* !METRO */
+
+/*********************************************************************/
+
+typedef BOOL (SetProcessDPIAwareFn)(void);
+
+static void
+avoid_windows_scale(void)
+{
+ /* Fetch the function address and only call it if it is there; this keeps
+ * compatability with Windows < 8.1 */
+ HMODULE hUser32 = LoadLibrary(TEXT("user32.dll"));
+ SetProcessDPIAwareFn *ptr;
+
+ ptr = (SetProcessDPIAwareFn *)GetProcAddress(hUser32, "SetProcessDPIAware");
+ if (ptr != NULL)
+ ptr();
+ FreeLibrary(hUser32);
+}
+
+#ifdef GS_NO_UTF8
+int main(int argc, char *argv[])
+#else
+static int main_utf8(int argc, char *argv[])
+#endif
+{
+ int code, code1;
+ int exit_code;
+ int exit_status;
+ int nargc;
+ char **nargv;
+ char buf[256];
+ char dformat[64];
+ char ddpi[64];
+
+ /* Mark us as being 'system dpi aware' to avoid horrid scaling */
+ avoid_windows_scale();
+
+ if (!_isatty(fileno(stdin)))
+ _setmode(fileno(stdin), _O_BINARY);
+ _setmode(fileno(stdout), _O_BINARY);
+ _setmode(fileno(stderr), _O_BINARY);
+
+#ifndef METRO
+ hwndforeground = GetForegroundWindow(); /* assume this is ours */
+#endif
+ memset(buf, 0, sizeof(buf));
+ if (load_dll(&gsdll, buf, sizeof(buf))) {
+ fprintf(stderr, "Can't load Ghostscript DLL\n");
+ fprintf(stderr, "%s\n", buf);
+ return 1;
+ }
+
+ if (gsdll.new_instance(&instance, NULL) < 0) {
+ fprintf(stderr, "Can't create Ghostscript instance\n");
+ return 1;
+ }
+
+#ifndef METRO
+#ifdef DEBUG
+ visual_tracer_init();
+ gsdll.set_visual_tracer(&visual_tracer);
+#endif
+
+ if (_beginthread(winthread, 65535, NULL) == -1) {
+ fprintf(stderr, "GUI thread creation failed\n");
+ }
+ else {
+ int n = 30;
+ /* wait for thread to start */
+ Sleep(0);
+ while (n && (hthread == INVALID_HANDLE_VALUE)) {
+ n--;
+ Sleep(100);
+ }
+ while (n && (PostThreadMessage(thread_id, WM_USER, 0, 0) == 0)) {
+ n--;
+ Sleep(100);
+ }
+ if (n == 0)
+ fprintf(stderr, "Can't post message to GUI thread\n");
+ }
+#endif
+
+#ifdef GS_NO_UTF8
+ gsdll.set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr);
+#else
+ gsdll.set_stdio(instance,
+ _isatty(fileno(stdin)) ? gsdll_stdin_utf8 : gsdll_stdin,
+ _isatty(fileno(stdout)) ? gsdll_stdout_utf8 : gsdll_stdout,
+ _isatty(fileno(stderr)) ? gsdll_stderr_utf8 : gsdll_stderr);
+#endif
+#ifdef METRO
+ /* For metro, no insertion of display device args */
+ nargc = argc;
+ nargv = argv;
+ {
+#else
+ gsdll.set_display_callback(instance, &display);
+
+ { int format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_1 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ HDC hdc = GetDC(NULL); /* get hdc for desktop */
+ int depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
+ sprintf(ddpi, "-dDisplayResolution=%d", GetDeviceCaps(hdc, LOGPIXELSY));
+ ReleaseDC(NULL, hdc);
+ if (depth == 32)
+ format = DISPLAY_COLORS_RGB | DISPLAY_UNUSED_LAST |
+ DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ else if (depth == 16)
+ format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_16 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST |
+ DISPLAY_NATIVE_555;
+ else if (depth > 8)
+ format = DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ else if (depth >= 8)
+ format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ else if (depth >= 4)
+ format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
+ DISPLAY_DEPTH_4 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
+ sprintf(dformat, "-dDisplayFormat=%d", format);
+ }
+ nargc = argc + 2;
+ nargv = (char **)malloc((nargc + 1) * sizeof(char *));
+ if (nargv == NULL) {
+ fprintf(stderr, "Malloc failure!\n");
+ } else {
+ nargv[0] = argv[0];
+ nargv[1] = dformat;
+ nargv[2] = ddpi;
+ memcpy(&nargv[3], &argv[1], argc * sizeof(char *));
+#endif
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+ __try {
+#endif
+#ifndef GS_NO_UTF8
+ code = gsdll.set_arg_encoding(instance, GS_ARG_ENCODING_UTF8);
+ if (code == 0)
+#endif
+ code = gsdll.init_with_args(instance, nargc, nargv);
+ if (code == 0)
+ code = gsdll.run_string(instance, start_string, 0, &exit_code);
+ code1 = gsdll.exit(instance);
+ if (code == 0 || (code == gs_error_Quit && code1 != 0))
+ code = code1;
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+ } __except(exception_code() == EXCEPTION_STACK_OVERFLOW) {
+ code = gs_error_Fatal;
+ fprintf(stderr, "*** C stack overflow. Quiting...\n");
+ }
+#endif
+
+ gsdll.delete_instance(instance);
+
+#ifndef METRO
+#ifdef DEBUG
+ visual_tracer_close();
+#endif
+#endif
+
+ unload_dll(&gsdll);
+
+#ifndef METRO
+ free(nargv);
+#endif
+ }
+#ifndef METRO
+ /* close other thread */
+ quitnow = TRUE;
+ PostThreadMessage(thread_id, WM_QUIT, 0, (LPARAM)0);
+ Sleep(0);
+#endif
+
+ exit_status = 0;
+ switch (code) {
+ case 0:
+ case gs_error_Info:
+ case gs_error_Quit:
+ break;
+ case gs_error_Fatal:
+ exit_status = 1;
+ break;
+ default:
+ exit_status = 255;
+ }
+
+ return exit_status;
+}
+
+#ifndef GS_NO_UTF8
+int wmain(int argc, wchar_t *argv[], wchar_t *envp[]) {
+ /* Duplicate args as utf8 */
+ char **nargv;
+ int i, code;
+
+ nargv = calloc(argc, sizeof(nargv[0]));
+ if (nargv == NULL)
+ goto err;
+ for (i=0; i < argc; i++) {
+ nargv[i] = malloc(wchar_to_utf8(NULL, argv[i]));
+ if (nargv[i] == NULL)
+ goto err;
+ (void)wchar_to_utf8(nargv[i], argv[i]);
+ }
+ code = main_utf8(argc, nargv);
+
+ if (0) {
+err:
+ fprintf(stderr,
+ "Ghostscript failed to initialise due to malloc failure\n");
+ code = -1;
+ }
+
+ if (nargv) {
+ for (i=0; i<argc; i++) {
+ free(nargv[i]);
+ }
+ free(nargv);
+ }
+
+ return code;
+}
+#endif
diff --git a/psi/dwnodll.c b/psi/dwnodll.c
new file mode 100644
index 000000000..87e5fa9c0
--- /dev/null
+++ b/psi/dwnodll.c
@@ -0,0 +1,51 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* dwnodll.c */
+
+#define STRICT
+#include <windows.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "stdpre.h"
+#include "gpgetenv.h"
+#include "gscdefs.h"
+#define GSREVISION gs_revision
+
+#include "dwdll.h"
+
+/* We are static linking, so just store the function addresses */
+int load_dll(GSDLL *gsdll, char *last_error, int len)
+{
+ gsdll->new_instance = &gsapi_new_instance;
+ gsdll->delete_instance = &gsapi_delete_instance;
+ gsdll->set_stdio = &gsapi_set_stdio;
+ gsdll->set_poll = &gsapi_set_poll;
+ gsdll->set_display_callback = &gsapi_set_display_callback;
+ gsdll->init_with_args = &gsapi_init_with_args;
+ gsdll->set_arg_encoding = &gsapi_set_arg_encoding;
+ gsdll->run_string = &gsapi_run_string;
+ gsdll->exit = &gsapi_exit;
+ gsdll->set_visual_tracer = &gsapi_set_visual_tracer;
+ gsdll->set_default_device_list = &gsapi_set_default_device_list;
+ gsdll->get_default_device_list = &gsapi_get_default_device_list;
+ return 0;
+}
+
+void unload_dll(GSDLL *gsdll)
+{
+}
diff --git a/psi/dwreg.c b/psi/dwreg.c
new file mode 100644
index 000000000..d8bbbea89
--- /dev/null
+++ b/psi/dwreg.c
@@ -0,0 +1,105 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* MS Windows registry values */
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h> /* for getenv */
+#include <string.h>
+#include "gscdefs.h" /* for gs_productfamily and gs_revision */
+
+/* We store registry named values under the key
+ * "Software\\GPL Ghostscript"
+ * where "GPL Ghostscript" is actually gs_productfamily.
+ * Either HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER will be used.
+ */
+int
+win_registry_key(char *buf, int len)
+{
+ const char *software = "Software";
+ if (strlen(software) + 1 + strlen(gs_productfamily) >= len)
+ return -1;
+
+ strcpy(buf, software);
+ strcat(buf, "\\");
+ strcat(buf, gs_productfamily);
+ return 0;
+}
+
+/*
+ * Get a named registry value from HKCU.
+ * name, ptr, plen and return values are the same as in gp_getenv();
+ */
+int
+win_get_reg_value(const char *name, char *ptr, int *plen)
+{
+ HKEY hkey;
+ DWORD cbData, keytype;
+ BYTE b;
+ LONG rc;
+ BYTE *bptr = (BYTE *)ptr;
+ char key[256];
+
+ win_registry_key(key, sizeof(key));
+ if (RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_READ, &hkey)
+ == ERROR_SUCCESS) {
+ keytype = REG_SZ;
+ cbData = *plen;
+ if (bptr == (char *)NULL)
+ bptr = &b; /* Registry API won't return ERROR_MORE_DATA */
+ /* if ptr is NULL */
+ rc = RegQueryValueEx(hkey, (char *)name, 0, &keytype, bptr, &cbData);
+ RegCloseKey(hkey);
+ if (rc == ERROR_SUCCESS) {
+ *plen = cbData;
+ return 0; /* found environment variable and copied it */
+ } else if (rc == ERROR_MORE_DATA) {
+ /* buffer wasn't large enough */
+ *plen = cbData;
+ return -1;
+ }
+ }
+ return 1; /* not found */
+}
+
+/*
+ * Set a named registry value under HKCU.
+ * name = name of named value
+ * str = value of named value
+ * Returns 0 on success.
+ */
+int
+win_set_reg_value(const char *name, const char *value)
+{
+ HKEY hkey;
+ LONG rc;
+ char key[256];
+ DWORD dwDisposition;
+
+ win_registry_key(key, sizeof(key));
+ rc = RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_WRITE, &hkey);
+ if (rc != ERROR_SUCCESS)
+ rc = RegCreateKeyEx(HKEY_CURRENT_USER, key, 0, "", 0,
+ KEY_ALL_ACCESS, NULL, &hkey, &dwDisposition);
+ if (rc == ERROR_SUCCESS) {
+ rc = RegSetValueEx(hkey, name, 0, REG_SZ,
+ (CONST BYTE *)value, strlen(value)+1);
+ RegCloseKey(hkey);
+ }
+
+ return rc == ERROR_SUCCESS ? 0 : -1;
+}
diff --git a/psi/dwreg.h b/psi/dwreg.h
new file mode 100644
index 000000000..60959403f
--- /dev/null
+++ b/psi/dwreg.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+#ifndef dwreg_INCLUDED
+# define dwreg_INCLUDED
+
+/* Get and set named registry values for Ghostscript application. */
+int win_get_reg_value(const char *name, char *ptr, int *plen);
+int win_set_reg_value(const char *name, const char *value);
+
+#endif /* dwreg_INCLUDED */
diff --git a/psi/dwres.h b/psi/dwres.h
new file mode 100644
index 000000000..d25bada66
--- /dev/null
+++ b/psi/dwres.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+#ifndef dwres_INCLUDED
+# define dwres_INCLUDED
+
+/* Icon index definitions - needed by resources */
+
+#define GSTEXT_ICON 50
+#define GSIMAGE_ICON 51
+
+#endif /* dwmain_INCLUDED */
diff --git a/psi/dwsetup.def b/psi/dwsetup.def
new file mode 100644
index 000000000..28fe216d5
--- /dev/null
+++ b/psi/dwsetup.def
@@ -0,0 +1,4 @@
+; 'Ghostscript Setup'
+NAME SETUPGS
+HEAPSIZE 4096
+STACKSIZE 32768
diff --git a/psi/dwsetup_x64.manifest b/psi/dwsetup_x64.manifest
new file mode 100644
index 000000000..ef75baa67
--- /dev/null
+++ b/psi/dwsetup_x64.manifest
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity version="1.0.0.0"
+ processorArchitecture="AMD64"
+ name="setupgs"
+ type="win32" />
+ <description>Ghostscript installer</description>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/psi/dwsetup_x86.manifest b/psi/dwsetup_x86.manifest
new file mode 100644
index 000000000..420fada59
--- /dev/null
+++ b/psi/dwsetup_x86.manifest
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity version="1.0.0.0"
+ processorArchitecture="X86"
+ name="setupgs"
+ type="win32" />
+ <description>Ghostscript installer</description>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/psi/dwtext.c b/psi/dwtext.c
new file mode 100644
index 000000000..9eeada7cf
--- /dev/null
+++ b/psi/dwtext.c
@@ -0,0 +1,1352 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/* Microsoft Windows text window for Ghostscript. */
+
+#include <stdlib.h>
+#include <string.h> /* use only far items */
+#include <wchar.h>
+#include <ctype.h>
+
+#define STRICT
+#include <windowsx.h>
+#include "dwtext.h"
+#include <commdlg.h>
+#include <shellapi.h>
+
+#ifdef GS_NO_UTF8
+#define CHARSIZE 1
+#else
+#define CHARSIZE 2
+#ifndef WM_UNICHAR
+#define WM_UNICHAR 0x109
+#endif
+#endif
+
+/* Define min and max, but make sure to use the identical definition */
+/* to the one that all the compilers seem to have.... */
+#ifndef min
+# define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef max
+# define max(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef EOF
+#define EOF (-1)
+#endif
+
+/* sysmenu */
+#define M_COPY_CLIP 1
+#define M_PASTE_CLIP 2
+
+LRESULT CALLBACK WndTextProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+static void text_error(char *message);
+static void text_new_line(TW *tw);
+static void text_update_text(TW *tw, int count);
+static void text_drag_drop(TW *tw, HDROP hdrop);
+static void text_copy_to_clipboard(TW *tw);
+static void text_paste_from_clipboard(TW *tw);
+
+#ifdef GS_NO_UTF8
+static const char* TextWinClassName = "rjlTextWinClass";
+#else
+static const wchar_t* TextWinClassName = L"rjlTextWinClass";
+#endif
+static const POINT TextWinMinSize = {16, 4};
+
+#if !defined(GS_NO_UTF8) && defined(_MSC_VER) && _MSC_VER < 1400
+/*
+ * 'wmemset()' is documented for both Visual Studio 6.0 and .NET 2003, but
+ * a bug in the shipped "wctype.h" makes it available only for C++ programs.
+ * As this is an inline function, it's not found in the libs.
+ */
+static wchar_t *wmemset(wchar_t *buf, wchar_t w, int count) {
+ while (count > 0)
+ buf[--count] = w;
+ return buf;
+}
+#endif
+
+static void
+text_error(char *message)
+{
+ MessageBoxA((HWND)NULL,message,(LPSTR)NULL, MB_ICONHAND | MB_SYSTEMMODAL);
+}
+
+static void
+UpdateScrollBarX(TW *tw)
+{
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
+ si.nMin = 0;
+ si.nTrackPos = 0;
+ si.nPage = tw->ClientSize.x;
+ si.nMax = tw->CharSize.x*tw->ScreenSize.x-1;
+ si.nPos = tw->ScrollPos.x;
+ SetScrollInfo(tw->hwnd, SB_HORZ, &si, TRUE);
+}
+
+static void
+UpdateScrollBarY(TW *tw)
+{
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
+ si.nMin = 0;
+ si.nTrackPos = 0;
+ si.nPage = tw->ClientSize.y;
+ si.nMax = tw->CharSize.y*tw->ScreenSize.y-1;
+ si.nPos = tw->ScrollPos.y;
+ SetScrollInfo(tw->hwnd, SB_VERT, &si, TRUE);
+}
+
+/* Bring Cursor into text window */
+void
+text_to_cursor(TW *tw)
+{
+int nXinc=0;
+int nYinc=0;
+int cxCursor;
+int cyCursor;
+ cyCursor = tw->CursorPos.y * tw->CharSize.y;
+ if ( (cyCursor + tw->CharSize.y > tw->ScrollPos.y + tw->ClientSize.y)
+/* || (cyCursor < tw->ScrollPos.y) ) { */
+/* change to scroll to end of window instead of just making visible */
+/* so that ALL of error message can be seen */
+ || (cyCursor < tw->ScrollPos.y+tw->ClientSize.y) ) {
+ nYinc = max(0, cyCursor + tw->CharSize.y
+ - tw->ClientSize.y) - tw->ScrollPos.y;
+ nYinc = min(nYinc, tw->ScrollMax.y - tw->ScrollPos.y);
+ }
+ cxCursor = tw->CursorPos.x * tw->CharSize.x;
+ if ( (cxCursor + tw->CharSize.x > tw->ScrollPos.x + tw->ClientSize.x)
+ || (cxCursor < tw->ScrollPos.x) ) {
+ nXinc = max(0, cxCursor + tw->CharSize.x
+ - tw->ClientSize.x/2) - tw->ScrollPos.x;
+ nXinc = min(nXinc, tw->ScrollMax.x - tw->ScrollPos.x);
+ }
+ if (nYinc || nXinc) {
+ tw->ScrollPos.y += nYinc;
+ tw->ScrollPos.x += nXinc;
+ ScrollWindow(tw->hwnd,-nXinc,-nYinc,NULL,NULL);
+ UpdateScrollBarX(tw);
+ UpdateScrollBarY(tw);
+ UpdateWindow(tw->hwnd);
+ }
+}
+
+static void
+text_new_line(TW *tw)
+{
+ tw->CursorPos.x = 0;
+ tw->CursorPos.y++;
+ if (tw->CursorPos.y >= tw->ScreenSize.y) {
+ int i = tw->ScreenSize.x * (tw->ScreenSize.y - 1);
+ memmove(tw->ScreenBuffer, tw->ScreenBuffer+tw->ScreenSize.x,
+ i * CHARSIZE);
+#ifdef GS_NO_UTF8
+ memset(tw->ScreenBuffer + i, ' ', tw->ScreenSize.x);
+#else
+ wmemset(tw->ScreenBuffer + i, ' ', tw->ScreenSize.x);
+#endif
+ tw->CursorPos.y--;
+ ScrollWindow(tw->hwnd,0,-tw->CharSize.y,NULL,NULL);
+ UpdateWindow(tw->hwnd);
+ }
+ if (tw->CursorFlag)
+ text_to_cursor(tw);
+
+/* TextMessage(); */
+}
+
+/* Update count characters in window at cursor position */
+/* Updates cursor position */
+static void
+text_update_text(TW *tw, int count)
+{
+HDC hdc;
+int xpos, ypos;
+ xpos = tw->CursorPos.x*tw->CharSize.x - tw->ScrollPos.x;
+ ypos = tw->CursorPos.y*tw->CharSize.y - tw->ScrollPos.y;
+ hdc = GetDC(tw->hwnd);
+ SelectFont(hdc, tw->hfont);
+#ifdef GS_NO_UTF8
+ TextOut(hdc,xpos,ypos,
+ (LPSTR)(tw->ScreenBuffer + tw->CursorPos.y*tw->ScreenSize.x
+ + tw->CursorPos.x), count);
+#else
+ TextOutW(hdc,xpos,ypos,
+ (LPCWSTR)(tw->ScreenBuffer +
+ tw->CursorPos.y*tw->ScreenSize.x +
+ tw->CursorPos.x),
+ count);
+#endif
+ (void)ReleaseDC(tw->hwnd,hdc);
+ tw->CursorPos.x += count;
+ if (tw->CursorPos.x >= tw->ScreenSize.x)
+ text_new_line(tw);
+}
+
+void
+text_size(TW *tw, int width, int height)
+{
+ tw->ScreenSize.x = max(width, TextWinMinSize.x);
+ tw->ScreenSize.y = max(height, TextWinMinSize.y);
+}
+
+void
+text_font(TW *tw, const char *name, int size)
+{
+ /* make a new font */
+ LOGFONTA lf;
+ TEXTMETRIC tm;
+ LPSTR p;
+ HDC hdc;
+
+ /* reject inappropriate arguments */
+ if (name == NULL)
+ return;
+ if (size < 4)
+ return;
+
+ /* set new name and size */
+ free(tw->fontname);
+ tw->fontname = (char *)malloc(strlen(name)+1);
+ if (tw->fontname == NULL)
+ return;
+ strcpy(tw->fontname, name);
+ tw->fontsize = size;
+
+ /* if window not open, hwnd == 0 == HWND_DESKTOP */
+ hdc = GetDC(tw->hwnd);
+ memset(&lf, 0, sizeof(LOGFONTA));
+ strncpy(lf.lfFaceName,tw->fontname,LF_FACESIZE);
+ lf.lfHeight = -MulDiv(tw->fontsize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ lf.lfPitchAndFamily = FIXED_PITCH;
+ lf.lfCharSet = DEFAULT_CHARSET;
+ if ( (p = strstr(tw->fontname," Italic")) != (LPSTR)NULL ) {
+ lf.lfFaceName[ (unsigned int)(p-tw->fontname) ] = '\0';
+ lf.lfItalic = TRUE;
+ }
+ if ( (p = strstr(tw->fontname," Bold")) != (LPSTR)NULL ) {
+ lf.lfFaceName[ (unsigned int)(p-tw->fontname) ] = '\0';
+ lf.lfWeight = FW_BOLD;
+ }
+ if (tw->hfont)
+ DeleteFont(tw->hfont);
+
+ tw->hfont = CreateFontIndirectA((LOGFONTA FAR *)&lf);
+
+ /* get text size */
+ SelectFont(hdc, tw->hfont);
+ GetTextMetrics(hdc,(TEXTMETRIC FAR *)&tm);
+ tw->CharSize.y = tm.tmHeight;
+ tw->CharSize.x = tm.tmAveCharWidth;
+ tw->CharAscent = tm.tmAscent;
+ if (tw->bFocus)
+ CreateCaret(tw->hwnd, 0, tw->CharSize.x, 2+tw->CaretHeight);
+ ReleaseDC(tw->hwnd, hdc);
+
+ /* redraw window if necessary */
+ if (tw->hwnd != HWND_DESKTOP) {
+ /* INCOMPLETE */
+ }
+}
+
+/* Set drag strings */
+void
+text_drag(TW *tw, const char *pre, const char *post)
+{
+ /* remove old strings */
+ free((char *)tw->DragPre);
+ tw->DragPre = NULL;
+ free((char *)tw->DragPost);
+ tw->DragPost = NULL;
+
+ /* add new strings */
+ tw->DragPre = malloc(strlen(pre)+1);
+ if (tw->DragPre)
+ strcpy(tw->DragPre, pre);
+ tw->DragPost = malloc(strlen(post)+1);
+ if (tw->DragPost)
+ strcpy(tw->DragPost, post);
+}
+
+/* Set the window position and size */
+void
+text_setpos(TW *tw, int x, int y, int cx, int cy)
+{
+ tw->x = x;
+ tw->y = y;
+ tw->cx = cx;
+ tw->cy = cy;
+}
+
+/* Get the window position and size */
+int text_getpos(TW *tw, int *px, int *py, int *pcx, int *pcy)
+{
+ *px = tw->x;
+ *py = tw->y;
+ *pcx = tw->cx;
+ *pcy = tw->cy;
+ return 0;
+}
+
+/* Allocate new text window */
+TW *
+text_new(void)
+{
+ TW *tw;
+ tw = (TW *)malloc(sizeof(TW));
+ if (tw == NULL)
+ return NULL;
+ /* make sure everything is null */
+ memset(tw, 0, sizeof(TW));
+
+ /* set some defaults */
+ text_font(tw, "Courier New", 10);
+ text_size(tw, 80, 24);
+ tw->KeyBufSize = 2048;
+ tw->CursorFlag = 1; /* scroll to cursor after \n or \r */
+ tw->hwnd = HWND_DESKTOP;
+
+ tw->line_end = 0;
+ tw->line_start = 0;
+ tw->line_complete = FALSE;
+ tw->line_eof = FALSE;
+
+ tw->x = CW_USEDEFAULT;
+ tw->y = CW_USEDEFAULT;
+ tw->cx = CW_USEDEFAULT;
+ tw->cy = CW_USEDEFAULT;
+#ifndef GS_NO_UTF8
+ tw->utf8shift = 0;
+#endif
+ return tw;
+}
+
+/* Destroy window and deallocate text window structure */
+void
+text_destroy(TW *tw)
+{
+ if (tw->hwnd)
+ DestroyWindow(tw->hwnd);
+ tw->hwnd = HWND_DESKTOP;
+
+ if (tw->hfont)
+ DeleteFont(tw->hfont);
+ tw->hfont = NULL;
+
+ free((char *)tw->KeyBuf);
+ tw->KeyBuf = NULL;
+
+ free((char *)tw->ScreenBuffer);
+ tw->ScreenBuffer = NULL;
+
+ free((char *)tw->DragPre);
+ tw->DragPre = NULL;
+
+ free((char *)tw->DragPost);
+ tw->DragPost = NULL;
+
+ free((char *)tw->fontname);
+ tw->fontname = NULL;
+
+#ifndef GS_NO_UTF8
+ free((char *)tw->TitleW);
+ tw->TitleW = NULL;
+#endif
+}
+
+/* register the window class */
+int
+text_register_class(TW *tw, HICON hicon)
+{
+#ifdef GS_NO_UTF8
+ WNDCLASS wndclass;
+#else
+ WNDCLASSW wndclass;
+#endif
+ HINSTANCE hInstance = GetModuleHandle(NULL);
+ tw->hIcon = hicon;
+
+ /* register window class */
+ wndclass.style = CS_HREDRAW | CS_VREDRAW;
+ wndclass.lpfnWndProc = WndTextProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = sizeof(void *);
+ wndclass.hInstance = hInstance;
+ wndclass.hIcon = tw->hIcon ? tw->hIcon : LoadIcon(NULL, IDI_APPLICATION);
+ wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndclass.hbrBackground = GetStockBrush(WHITE_BRUSH);
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = TextWinClassName;
+#ifdef GS_NO_UTF8
+ return RegisterClass(&wndclass);
+#else
+ return RegisterClassW(&wndclass);
+#endif
+}
+
+/* Show the window */
+int text_create(TW *tw, const char *app_name, int show_cmd)
+{
+ HMENU sysmenu;
+ HINSTANCE hInstance = GetModuleHandle(NULL);
+#ifndef GS_NO_UTF8
+ wchar_t *app_nameW, *d;
+ const char *s;
+
+ app_nameW = malloc(strlen(app_name)*2+2);
+ if (app_nameW == NULL) {
+ text_error("Out of memory");
+ return 1;
+ }
+ d = app_nameW;
+ s = app_name;
+ while ((*d++ = (wchar_t)(unsigned char)(*s++)) != 0);
+ tw->TitleW = app_nameW;
+#endif
+
+ tw->Title = app_name;
+ tw->nCmdShow = show_cmd;
+ tw->quitnow = FALSE;
+
+ /* make sure we have some sensible defaults */
+ if (tw->KeyBufSize < 256)
+ tw->KeyBufSize = 256;
+
+ tw->CursorPos.x = tw->CursorPos.y = 0;
+ tw->bFocus = FALSE;
+ tw->bGetCh = FALSE;
+ tw->CaretHeight = 0;
+
+ /* allocate buffers */
+ tw->KeyBufIn = tw->KeyBufOut = tw->KeyBuf = malloc(tw->KeyBufSize);
+ if (tw->KeyBuf == NULL) {
+ text_error("Out of memory");
+ return 1;
+ }
+ tw->ScreenBuffer = malloc(tw->ScreenSize.x * tw->ScreenSize.y * CHARSIZE);
+ if (tw->ScreenBuffer == NULL) {
+ text_error("Out of memory");
+ return 1;
+ }
+#ifdef GS_NO_UTF8
+ memset(tw->ScreenBuffer, ' ', tw->ScreenSize.x * tw->ScreenSize.y);
+#else
+ wmemset(tw->ScreenBuffer, ' ', tw->ScreenSize.x * tw->ScreenSize.y);
+#endif
+
+#ifdef GS_NO_UTF8
+ tw->hwnd = CreateWindow(TextWinClassName, tw->Title,
+ WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
+ tw->x, tw->y, tw->cx, tw->cy,
+ NULL, NULL, hInstance, tw);
+#else
+ tw->hwnd = CreateWindowW(TextWinClassName, app_nameW,
+ WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
+ tw->x, tw->y, tw->cx, tw->cy,
+ NULL, NULL, hInstance, tw);
+#endif
+
+ if (tw->hwnd == NULL) {
+ MessageBoxA((HWND)NULL,"Couldn't open text window",(LPSTR)NULL, MB_ICONHAND | MB_SYSTEMMODAL);
+ return 1;
+ }
+
+ ShowWindow(tw->hwnd, tw->nCmdShow);
+ sysmenu = GetSystemMenu(tw->hwnd,0); /* get the sysmenu */
+ AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
+ AppendMenuA(sysmenu, MF_STRING, M_COPY_CLIP, "Copy to Clip&board");
+ AppendMenuA(sysmenu, MF_STRING, M_PASTE_CLIP, "&Paste");
+
+ return 0;
+}
+
+int
+text_putch(TW *tw, int ch)
+{
+int pos;
+int n;
+#ifndef GS_NO_UTF8
+int shift = tw->utf8shift;
+ tw->utf8shift=0;
+#endif
+ if (tw->quitnow)
+ return ch; /* don't write error message as we shut down */
+ switch(ch) {
+ case '\r':
+ tw->CursorPos.x = 0;
+ if (tw->CursorFlag)
+ text_to_cursor(tw);
+ break;
+ case '\n':
+ text_new_line(tw);
+ break;
+ case 7:
+ MessageBeep(-1);
+ if (tw->CursorFlag)
+ text_to_cursor(tw);
+ break;
+ case '\t':
+ {
+ for (n = 8 - (tw->CursorPos.x % 8); n>0; n-- )
+ text_putch(tw, ' ');
+ }
+ break;
+ case 0x08:
+ case 0x7f:
+ tw->CursorPos.x--;
+ if (tw->CursorPos.x < 0) {
+ tw->CursorPos.x = tw->ScreenSize.x - 1;
+ tw->CursorPos.y--;
+ }
+ if (tw->CursorPos.y < 0)
+ tw->CursorPos.y = 0;
+ break;
+ default:
+ pos = tw->CursorPos.y*tw->ScreenSize.x + tw->CursorPos.x;
+#ifndef GS_NO_UTF8
+ /* Are we continuing a unicode char? */
+ if ((ch & 0xC0) == 0x80) {
+ tw->ScreenBuffer[pos] |= (ch & 0x3F)<<shift;
+ if (shift > 0)
+ tw->utf8shift = shift-6;
+ else
+ text_update_text(tw, 1); /* Only update when complete */
+ } else if (ch >= 0xe0) { /* 2 more to come */
+ tw->ScreenBuffer[pos] = (ch & 0x0f)<<12;
+ tw->utf8shift = 6;
+ } else if (ch >= 0xC0) { /* 1 more to come */
+ tw->ScreenBuffer[pos] = (ch & 0x01f)<<6;
+ tw->utf8shift = 0;
+ }
+ else
+#endif
+ {
+ tw->ScreenBuffer[pos] = ch;
+ text_update_text(tw, 1);
+ }
+ }
+ return ch;
+}
+
+void
+text_write_buf(TW *tw, const char *str, int cnt)
+{
+#ifdef GS_NO_UTF8
+BYTE *p;
+#else
+wchar_t *p;
+#endif
+int count, limit;
+int n;
+ if (tw->quitnow)
+ return; /* don't write error message as we shut down */
+ while (cnt>0) {
+ p = tw->ScreenBuffer + tw->CursorPos.y*tw->ScreenSize.x + tw->CursorPos.x;
+ limit = tw->ScreenSize.x - tw->CursorPos.x;
+ for (count=0; (count < limit) && (cnt>0) &&
+ (
+#ifdef GS_NO_UTF8
+ isprint((unsigned char)(*str))
+#else
+ ((*str >= 32) && (*str <= 0x7F))
+#endif
+ || *str=='\t'); count++) {
+ if (*str=='\t') {
+ for (n = 8 - ((tw->CursorPos.x+count) % 8); (count < limit) & (n>0); n--, count++ )
+ *p++ = ' ';
+ str++;
+ count--;
+ }
+ else {
+ *p++ = *str++;
+ }
+ cnt--;
+ }
+ if (count>0) {
+ text_update_text(tw, count);
+ }
+ if (cnt > 0) {
+ if (*str=='\n') {
+ text_new_line(tw);
+ str++;
+ cnt--;
+ }
+ else if (!
+#ifdef GS_NO_UTF8
+ isprint((unsigned char)(*str))
+#else
+ ((*str >= 32) && (*str <= 0x7f))
+#endif
+ && *str!='\t') {
+ text_putch(tw, *(const unsigned char *)str++);
+ cnt--;
+ }
+ }
+ }
+}
+
+/* Put string to window */
+void
+text_puts(TW *tw, const char *str)
+{
+ text_write_buf(tw, str, strlen(str));
+}
+
+/* TRUE if key hit, FALSE if no key */
+int
+text_kbhit(TW *tw)
+{
+ return (tw->KeyBufIn != tw->KeyBufOut);
+}
+
+/* get character from keyboard, no echo */
+/* need to add extended codes */
+int
+text_getch(TW *tw)
+{
+ MSG msg;
+ int ch;
+ text_to_cursor(tw);
+ tw->bGetCh = TRUE;
+ if (tw->bFocus) {
+ SetCaretPos(tw->CursorPos.x*tw->CharSize.x - tw->ScrollPos.x,
+ tw->CursorPos.y*tw->CharSize.y + tw->CharAscent
+ - tw->CaretHeight - tw->ScrollPos.y);
+ ShowCaret(tw->hwnd);
+ }
+
+#ifdef GS_NO_UTF8
+ while (PeekMessage(&msg, (HWND)NULL, 0, 0, PM_NOREMOVE)) {
+ if (GetMessage(&msg, (HWND)NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+#else
+ while (PeekMessageW(&msg, (HWND)NULL, 0, 0, PM_NOREMOVE)) {
+ if (GetMessageW(&msg, (HWND)NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ }
+#endif
+
+ if (tw->quitnow)
+ return EOF; /* window closed */
+
+ while (!text_kbhit(tw)) {
+ if (!tw->quitnow) {
+#ifdef GS_NO_UTF8
+ if (GetMessage(&msg, (HWND)NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+#else
+ if (GetMessageW(&msg, (HWND)NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+#endif
+ }
+ else
+ return EOF; /* window closed */
+ }
+
+ ch = *tw->KeyBufOut++;
+ if (ch=='\r')
+ ch = '\n';
+ if (tw->KeyBufOut - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufOut = tw->KeyBuf; /* wrap around */
+ if (tw->bFocus)
+ HideCaret(tw->hwnd);
+ tw->bGetCh = FALSE;
+ return ch;
+}
+
+/* Read line from keyboard using buffered input
+ * Return at most 'len' characters
+ * Does NOT add null terminating character
+ * This is NOT the same as fgets()
+ * Do not mix this with calls to text_getch()
+ */
+int
+text_read_line(TW *tw, char *line, int len)
+{
+int ch;
+ if (tw->line_eof)
+ return 0;
+
+ while (!tw->line_complete) {
+ /* we have not yet collected a full line */
+ ch = text_getch(tw);
+ switch(ch) {
+ case EOF:
+ case 26: /* ^Z == EOF */
+ tw->line_eof = TRUE;
+ tw->line_complete = TRUE;
+ break;
+ case '\b': /* ^H */
+ case 0x7f: /* DEL */
+ if (tw->line_end) {
+ text_putch(tw, '\b');
+ text_putch(tw, ' ');
+ text_putch(tw, '\b');
+#ifndef GS_NO_UTF8
+ while ((tw->line_end) &&
+ ((tw->line_buf[tw->line_end-1] & 0xC0) == 0x80)) {
+ /* It's a UTF-8 continuation char. */
+ --(tw->line_end);
+ }
+ if (tw->line_end == 0)
+ break;
+#endif
+ --(tw->line_end);
+ }
+ break;
+ case 21: /* ^U */
+ while (tw->line_end) {
+ text_putch(tw, '\b');
+ text_putch(tw, ' ');
+ text_putch(tw, '\b');
+ --(tw->line_end);
+ }
+ break;
+ case '\r':
+ case '\n':
+ tw->line_complete = TRUE;
+ /* fall through */
+ default:
+ tw->line_buf[tw->line_end++] = ch;
+ text_putch(tw, ch);
+ break;
+ }
+ if (tw->line_end >= sizeof(tw->line_buf))
+ tw->line_complete = TRUE;
+ }
+
+ if (tw->quitnow)
+ return -1;
+
+ if (tw->line_complete) {
+ /* We either filled the buffer or got CR, LF or EOF */
+ int count = min(len, tw->line_end - tw->line_start);
+ memcpy(line, tw->line_buf + tw->line_start, count);
+ tw->line_start += count;
+ if (tw->line_start == tw->line_end) {
+ tw->line_start = tw->line_end = 0;
+ tw->line_complete = FALSE;
+ }
+ return count;
+ }
+
+ return 0;
+}
+
+/* Read a string from the keyboard, of up to len characters */
+/* (not including trailing NULL) */
+int
+text_gets(TW *tw, char *line, int len)
+{
+LPSTR dest = line;
+LPSTR limit = dest + len; /* don't leave room for '\0' */
+int ch;
+ do {
+ if (dest >= limit)
+ break;
+ ch = text_getch(tw);
+ switch(ch) {
+ case 26: /* ^Z == EOF */
+ return 0;
+ case '\b': /* ^H */
+ case 0x7f: /* DEL */
+ if (dest > line) {
+ text_putch(tw, '\b');
+ text_putch(tw, ' ');
+ text_putch(tw, '\b');
+#ifndef GS_NO_UTF8
+ while ((dest > line) &&
+ ((dest[-1] & 0xC0) == 0x80)) {
+ /* It's a UTF-8 continuation char. */
+ --(dest);
+ }
+ if (dest == line)
+ break;
+#endif
+ --dest;
+ }
+ break;
+ case 21: /* ^U */
+ while (dest > line) {
+ text_putch(tw, '\b');
+ text_putch(tw, ' ');
+ text_putch(tw, '\b');
+ --dest;
+ }
+ break;
+ default:
+ *dest++ = ch;
+ text_putch(tw, ch);
+ break;
+ }
+ } while (ch != '\n');
+
+ *dest = '\0';
+ return (dest-line);
+}
+
+/* Windows 3.1 drag-drop feature */
+void
+text_drag_drop(TW *tw, HDROP hdrop)
+{
+ TCHAR *szFile;
+ int i, cFiles;
+ unsigned int Len, error;
+ const char *p;
+ const TCHAR *t;
+ if ( (tw->DragPre==NULL) || (tw->DragPost==NULL) )
+ return;
+
+ cFiles = DragQueryFile(hdrop, (UINT)(-1), (LPTSTR)NULL, 0);
+ for (i=0; i<cFiles; i++) {
+ Len = DragQueryFile(hdrop, i, NULL, 0);
+ szFile = (TCHAR *)malloc((Len+1)*sizeof(TCHAR));
+ if (szFile != 0) {
+ error = DragQueryFile(hdrop, i, szFile, Len+1);
+ if (error != 0) {
+ for (p=tw->DragPre; *p; p++)
+ SendMessage(tw->hwnd,WM_CHAR,*p,1L);
+ for (t=szFile; *t; t++) {
+ if (*t == '\\')
+ SendMessage(tw->hwnd,WM_CHAR,'/',1L);
+ else
+ SendMessage(tw->hwnd,WM_CHAR,*t,1L);
+ }
+ for (p=tw->DragPost; *p; p++)
+ SendMessage(tw->hwnd,WM_CHAR,*p,1L);
+ }
+ free(szFile);
+ }
+ }
+ DragFinish(hdrop);
+}
+
+void
+text_copy_to_clipboard(TW *tw)
+{
+ int size, count;
+ HGLOBAL hGMem;
+#ifdef GS_NO_UTF8
+ LPSTR cbuf, cp;
+ HDC hdc;
+ TEXTMETRIC tm;
+#else
+ LPWSTR cbuf, cp;
+#endif
+ UINT type;
+ int i;
+
+ size = tw->ScreenSize.y * (tw->ScreenSize.x + 2) + 1;
+ size *= CHARSIZE;
+ hGMem = GlobalAlloc(GHND | GMEM_SHARE, (DWORD)size);
+#ifdef GS_NO_UTF8
+ cbuf = cp = (LPSTR)GlobalLock(hGMem);
+#else
+ cbuf = cp = (LPWSTR)GlobalLock(hGMem);
+#endif
+ if (cp == NULL)
+ return;
+
+ for (i=0; i<tw->ScreenSize.y; i++) {
+ count = tw->ScreenSize.x;
+ memcpy(cp, tw->ScreenBuffer + tw->ScreenSize.x*i, count*CHARSIZE);
+ /* remove trailing spaces */
+ for (count=count-1; count>=0; count--) {
+ if (cp[count]!=' ')
+ break;
+ cp[count] = '\0';
+ }
+ cp[++count] = '\r';
+ cp[++count] = '\n';
+ cp[++count] = '\0';
+ cp += count;
+ }
+ /* Now remove completely empty trailing lines */
+ while (cp >= cbuf+4) {
+ if ((cp[-3] != '\n') || (cp[-4] != '\r'))
+ break;
+ cp -= 2;
+ *cp = '\0';
+ }
+#ifdef GS_NO_UTF8
+ size = strlen(cbuf) + 1;
+#else
+ size = CHARSIZE*(wcslen(cbuf) + 1);
+#endif
+ GlobalUnlock(hGMem);
+ hGMem = GlobalReAlloc(hGMem, (DWORD)size, GHND | GMEM_SHARE);
+#ifdef GS_NO_UTF8
+ /* find out what type to put into clipboard */
+ hdc = GetDC(tw->hwnd);
+ SelectFont(hdc, tw->hfont);
+ GetTextMetrics(hdc,(TEXTMETRIC FAR *)&tm);
+ if (tm.tmCharSet == OEM_CHARSET)
+ type = CF_OEMTEXT;
+ else
+ type = CF_TEXT;
+ ReleaseDC(tw->hwnd, hdc);
+#else
+ type = CF_UNICODETEXT;
+#endif
+ /* give buffer to clipboard */
+ OpenClipboard(tw->hwnd);
+ EmptyClipboard();
+ SetClipboardData(type, hGMem);
+ CloseClipboard();
+}
+
+void
+text_paste_from_clipboard(TW *tw)
+{
+ HGLOBAL hClipMemory;
+#ifdef GS_NO_UTF8
+ BYTE *p;
+#else
+ wchar_t *p;
+#endif
+ long count;
+ OpenClipboard(tw->hwnd);
+#ifdef GS_NO_UTF8
+ if (IsClipboardFormatAvailable(CF_TEXT)) {
+ hClipMemory = GetClipboardData(CF_TEXT);
+#else
+ if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
+ hClipMemory = GetClipboardData(CF_UNICODETEXT);
+#endif
+ p = GlobalLock(hClipMemory);
+ while (*p) {
+ /* transfer to keyboard circular buffer */
+ count = tw->KeyBufIn - tw->KeyBufOut;
+ if (count < 0)
+ count += tw->KeyBufSize;
+#ifndef GS_NO_UTF8
+ /* The clipboard contains unicode, but we put it into the key
+ * buffer as if it was typing utf8 */
+ if (*p >= 0x800) {
+ if (count < tw->KeyBufSize-3) {
+ *tw->KeyBufIn++ = 0xE0 | (*p>>12);
+ if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufIn = tw->KeyBuf; /* wrap around */
+ *tw->KeyBufIn++ = 0x80 | ((*p>>6) & 0x3F);
+ if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufIn = tw->KeyBuf; /* wrap around */
+ *tw->KeyBufIn++ = 0x80 | (*p & 0x3f);
+ if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufIn = tw->KeyBuf; /* wrap around */
+ }
+ } else if (*p >= 0x80) {
+ if (count < tw->KeyBufSize-2) {
+ *tw->KeyBufIn++ = 0xC0 | (*p>>6);
+ if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufIn = tw->KeyBuf; /* wrap around */
+ *tw->KeyBufIn++ = 0x80 | (*p & 0x3f);
+ if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufIn = tw->KeyBuf; /* wrap around */
+ }
+ } else
+#endif
+ if (count < tw->KeyBufSize-1) {
+ *tw->KeyBufIn++ = *p;
+ if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufIn = tw->KeyBuf; /* wrap around */
+ }
+ p++;
+ }
+ GlobalUnlock(hClipMemory);
+ }
+ CloseClipboard();
+}
+
+/* text window */
+LRESULT CALLBACK
+WndTextProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ HDC hdc;
+ PAINTSTRUCT ps;
+ RECT rect;
+ int nYinc, nXinc;
+ TW *tw;
+ if (message == WM_CREATE) {
+ /* Object is stored in window extra data.
+ * Nothing must try to use the object before WM_CREATE
+ * initializes it here.
+ */
+ tw = (TW *)(((CREATESTRUCT FAR *)lParam)->lpCreateParams);
+ SetWindowLong(hwnd, 0, (LONG)tw);
+ }
+ tw = (TW *)GetWindowLong(hwnd, 0);
+
+ switch(message) {
+ case WM_SYSCOMMAND:
+ switch(LOWORD(wParam)) {
+ case M_COPY_CLIP:
+ text_copy_to_clipboard(tw);
+ return 0;
+ case M_PASTE_CLIP:
+ text_paste_from_clipboard(tw);
+ return 0;
+ }
+ break;
+ case WM_SETFOCUS:
+ tw->bFocus = TRUE;
+ CreateCaret(hwnd, 0, tw->CharSize.x, 2+tw->CaretHeight);
+ SetCaretPos(tw->CursorPos.x*tw->CharSize.x - tw->ScrollPos.x,
+ tw->CursorPos.y*tw->CharSize.y + tw->CharAscent
+ - tw->CaretHeight - tw->ScrollPos.y);
+ if (tw->bGetCh)
+ ShowCaret(hwnd);
+ break;
+ case WM_KILLFOCUS:
+ DestroyCaret();
+ tw->bFocus = FALSE;
+ break;
+ case WM_MOVE:
+ if (!IsIconic(hwnd) && !IsZoomed(hwnd)) {
+ GetWindowRect(hwnd, &rect);
+ tw->x = rect.left;
+ tw->y = rect.top;
+ }
+ break;
+ case WM_SIZE:
+ if (wParam == SIZE_MINIMIZED)
+ return(0);
+
+ /* remember current window size */
+ if (wParam != SIZE_MAXIMIZED) {
+ GetWindowRect(hwnd, &rect);
+ tw->cx = rect.right - rect.left;
+ tw->cy = rect.bottom - rect.top;
+ tw->x = rect.left;
+ tw->y = rect.top;
+ }
+
+ tw->ClientSize.y = HIWORD(lParam);
+ tw->ClientSize.x = LOWORD(lParam);
+
+ tw->ScrollMax.y = max(0, tw->CharSize.y*tw->ScreenSize.y - tw->ClientSize.y);
+ tw->ScrollPos.y = min(tw->ScrollPos.y, tw->ScrollMax.y);
+
+ UpdateScrollBarY(tw);
+
+ tw->ScrollMax.x = max(0, tw->CharSize.x*tw->ScreenSize.x - tw->ClientSize.x);
+ tw->ScrollPos.x = min(tw->ScrollPos.x, tw->ScrollMax.x);
+
+ UpdateScrollBarX(tw);
+
+ if (tw->bFocus && tw->bGetCh) {
+ SetCaretPos(tw->CursorPos.x*tw->CharSize.x - tw->ScrollPos.x,
+ tw->CursorPos.y*tw->CharSize.y + tw->CharAscent
+ - tw->CaretHeight - tw->ScrollPos.y);
+ ShowCaret(hwnd);
+ }
+ return(0);
+ case WM_VSCROLL:
+ switch(LOWORD(wParam)) {
+ case SB_TOP:
+ nYinc = -tw->ScrollPos.y;
+ break;
+ case SB_BOTTOM:
+ nYinc = tw->ScrollMax.y - tw->ScrollPos.y;
+ break;
+ case SB_LINEUP:
+ nYinc = -tw->CharSize.y;
+ break;
+ case SB_LINEDOWN:
+ nYinc = tw->CharSize.y;
+ break;
+ case SB_PAGEUP:
+ nYinc = min(-1,-tw->ClientSize.y);
+ break;
+ case SB_PAGEDOWN:
+ nYinc = max(1,tw->ClientSize.y);
+ break;
+ case SB_THUMBPOSITION:
+ nYinc = HIWORD(wParam) - tw->ScrollPos.y;
+ break;
+ default:
+ nYinc = 0;
+ }
+ if ( (nYinc = max(-tw->ScrollPos.y,
+ min(nYinc, tw->ScrollMax.y - tw->ScrollPos.y)))
+ != 0 ) {
+ tw->ScrollPos.y += nYinc;
+ ScrollWindow(hwnd,0,-nYinc,NULL,NULL);
+ UpdateScrollBarY(tw);
+ UpdateWindow(hwnd);
+ }
+ return(0);
+ case WM_HSCROLL:
+ switch(LOWORD(wParam)) {
+ case SB_LINEUP:
+ nXinc = -tw->CharSize.x;
+ break;
+ case SB_LINEDOWN:
+ nXinc = tw->CharSize.x;
+ break;
+ case SB_PAGEUP:
+ nXinc = min(-1,-tw->ClientSize.x);
+ break;
+ case SB_PAGEDOWN:
+ nXinc = max(1,tw->ClientSize.x);
+ break;
+ case SB_THUMBPOSITION:
+ nXinc = HIWORD(wParam) - tw->ScrollPos.x;
+ break;
+ default:
+ nXinc = 0;
+ }
+ if ( (nXinc = max(-tw->ScrollPos.x,
+ min(nXinc, tw->ScrollMax.x - tw->ScrollPos.x)))
+ != 0 ) {
+ tw->ScrollPos.x += nXinc;
+ ScrollWindow(hwnd,-nXinc,0,NULL,NULL);
+ UpdateScrollBarX(tw);
+ UpdateWindow(hwnd);
+ }
+ return(0);
+ case WM_KEYDOWN:
+ switch(wParam) {
+ case VK_HOME:
+ SendMessage(hwnd, WM_VSCROLL, SB_TOP, (LPARAM)0);
+ break;
+ case VK_END:
+ SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, (LPARAM)0);
+ break;
+ case VK_PRIOR:
+ SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, (LPARAM)0);
+ break;
+ case VK_NEXT:
+ SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, (LPARAM)0);
+ break;
+ case VK_UP:
+ SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, (LPARAM)0);
+ break;
+ case VK_DOWN:
+ SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, (LPARAM)0);
+ break;
+ case VK_LEFT:
+ SendMessage(hwnd, WM_HSCROLL, SB_LINEUP, (LPARAM)0);
+ break;
+ case VK_RIGHT:
+ SendMessage(hwnd, WM_HSCROLL, SB_LINEDOWN, (LPARAM)0);
+ break;
+ }
+ break;
+#ifdef WM_UNICHAR
+ case WM_UNICHAR: /* Belt and braces */
+#endif
+ case WM_CHAR:
+ { /* We get unicode in, but we put it into the buffer as if the
+ * user was typing UTF8.
+ */
+ long count = tw->KeyBufIn - tw->KeyBufOut;
+ if (count < 0) count += tw->KeyBufSize;
+#ifndef GS_NO_UTF8
+ if (wParam >= 0x800) {
+ if (count >= tw->KeyBufSize-3)
+ return 0; /* Silently drop the chars! */
+ *tw->KeyBufIn++ = 0xE0 | (wParam>>12);
+ if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufIn = tw->KeyBuf; /* wrap around */
+ *tw->KeyBufIn++ = 0x80 | ((wParam>>6) & 0x3F);
+ if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufIn = tw->KeyBuf; /* wrap around */
+ *tw->KeyBufIn++ = 0x80 | (wParam & 0x3f);
+ if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufIn = tw->KeyBuf; /* wrap around */
+ } else if (wParam >= 0x80) {
+ if (count >= tw->KeyBufSize-2)
+ return 0; /* Silently drop the chars! */
+ *tw->KeyBufIn++ = 0xC0 | (wParam>>6);
+ if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufIn = tw->KeyBuf; /* wrap around */
+ *tw->KeyBufIn++ = 0x80 | (wParam & 0x3f);
+ if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufIn = tw->KeyBuf; /* wrap around */
+ } else
+#endif
+ {
+ if (count >= tw->KeyBufSize-1)
+ return 0; /* Silently drop the char! */
+ *tw->KeyBufIn++ = wParam;
+ if (tw->KeyBufIn - tw->KeyBuf >= tw->KeyBufSize)
+ tw->KeyBufIn = tw->KeyBuf; /* wrap around */
+ }
+ }
+ return(0);
+ case WM_PAINT:
+ {
+ POINT source, width, dest;
+ hdc = BeginPaint(hwnd, &ps);
+ SelectFont(hdc, tw->hfont);
+ SetMapMode(hdc, MM_TEXT);
+ SetBkMode(hdc,OPAQUE);
+ GetClientRect(hwnd, &rect);
+ source.x = (rect.left + tw->ScrollPos.x) / tw->CharSize.x; /* source */
+ source.y = (rect.top + tw->ScrollPos.y) / tw->CharSize.y;
+ dest.x = source.x * tw->CharSize.x - tw->ScrollPos.x; /* destination */
+ dest.y = source.y * tw->CharSize.y - tw->ScrollPos.y;
+ width.x = ((rect.right + tw->ScrollPos.x + tw->CharSize.x - 1) / tw->CharSize.x) - source.x; /* width */
+ width.y = ((rect.bottom + tw->ScrollPos.y + tw->CharSize.y - 1) / tw->CharSize.y) - source.y;
+ if (source.x < 0)
+ source.x = 0;
+ if (source.y < 0)
+ source.y = 0;
+ if (source.x+width.x > tw->ScreenSize.x)
+ width.x = tw->ScreenSize.x - source.x;
+ if (source.y+width.y > tw->ScreenSize.y)
+ width.y = tw->ScreenSize.y - source.y;
+ /* for each line */
+ while (width.y>0) {
+#ifdef GS_NO_UTF8
+ TextOut(hdc,dest.x,dest.y,
+ (LPSTR)(tw->ScreenBuffer + source.y*tw->ScreenSize.x + source.x),
+ width.x);
+#else
+ TextOutW(hdc,dest.x,dest.y,
+ (LPCWSTR)(tw->ScreenBuffer + source.y*tw->ScreenSize.x + source.x),
+ width.x);
+#endif
+ dest.y += tw->CharSize.y;
+ source.y++;
+ width.y--;
+ }
+ EndPaint(hwnd, &ps);
+ return 0;
+ }
+ case WM_DROPFILES:
+ text_drag_drop(tw, (HDROP)wParam);
+ break;
+ case WM_CREATE:
+ {
+ RECT crect, wrect;
+ int cx, cy;
+
+ tw->hwnd = hwnd;
+
+ /* make window no larger than screen buffer */
+ GetWindowRect(hwnd, &wrect);
+ GetClientRect(hwnd, &crect);
+ cx = min(tw->CharSize.x*tw->ScreenSize.x, crect.right);
+ cy = min(tw->CharSize.y*tw->ScreenSize.y, crect.bottom);
+ MoveWindow(hwnd, wrect.left, wrect.top,
+ wrect.right-wrect.left + (cx - crect.right),
+ wrect.bottom-wrect.top + (cy - crect.bottom),
+ TRUE);
+
+ if ( (tw->DragPre!=(LPSTR)NULL) && (tw->DragPost!=(LPSTR)NULL) )
+ DragAcceptFiles(hwnd, TRUE);
+ }
+ break;
+ case WM_CLOSE:
+ /* Tell user that we heard them */
+ if (!tw->quitnow) {
+ TCHAR title[256];
+ int count = GetWindowText(hwnd, title,
+ sizeof(title)/sizeof(TCHAR)-11);
+#ifdef GS_NO_UTF8
+ lstrcpyA(title+count, " - closing");
+#else
+ lstrcpyW(title+count, L" - closing");
+#endif
+ SetWindowText(hwnd, title);
+ }
+ tw->quitnow = TRUE;
+ /* wait until Ghostscript exits before destroying window */
+ return 0;
+ case WM_DESTROY:
+ DragAcceptFiles(hwnd, FALSE);
+ if (tw->hfont)
+ DeleteFont(tw->hfont);
+ tw->hfont = (HFONT)0;
+ tw->quitnow = TRUE;
+ PostQuitMessage(0);
+ break;
+ }
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+HWND text_get_handle(TW *tw)
+{
+ return tw->hwnd;
+}
+
+#ifdef NOTUSED
+/* test program */
+
+int PASCAL
+WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
+{
+ TW *tw;
+
+ /* make a test window */
+ tw = text_new();
+
+ if (!hPrevInstance) {
+ HICON hicon = LoadIcon(NULL, IDI_APPLICATION);
+ text_register_class(tw, hicon);
+ }
+ text_font(tw, "Courier New", 10);
+ text_size(tw, 80, 80);
+ text_drag(tw, "(", ") run\r");
+
+ /* show the text window */
+ if (!text_create(tw, "Application Name", nCmdShow)) {
+ /* do the real work here */
+ /* TESTING */
+ int ch;
+ int len;
+ char line[256];
+ while ( (len = text_read_line(tw, line, 256-1)) != 0 ) {
+ text_write_buf(tw, line, len);
+ }
+/*
+ while ( text_gets(tw, line, 256-1) ) {
+ text_puts(tw, line);
+ }
+*/
+/*
+ while ( (ch = text_getch(tw, )) != 4 )
+ text_putch(tw, ch);
+*/
+ }
+ else {
+
+ }
+
+ /* clean up */
+ text_destroy(tw);
+
+ /* end program */
+ return 0;
+}
+#endif
diff --git a/psi/dwtext.h b/psi/dwtext.h
new file mode 100644
index 000000000..c16499583
--- /dev/null
+++ b/psi/dwtext.h
@@ -0,0 +1,160 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Text Window class */
+
+#ifndef dwtext_INCLUDED
+# define dwtext_INCLUDED
+
+#ifdef _WINDOWS
+#define _Windows
+#endif
+
+#ifndef GS_NO_UTF8
+#define UNICODE
+#endif
+
+#include "windows_.h"
+
+#undef UNICODE
+
+typedef struct TEXTWINDOW_S {
+ const char *Title; /* required */
+#ifndef GS_NO_UTF8
+ wchar_t *TitleW; /* required */
+#endif
+ HICON hIcon; /* optional */
+#ifdef GS_NO_UTF8
+ BYTE *ScreenBuffer;
+#else
+ wchar_t *ScreenBuffer;
+#endif
+ POINT ScreenSize; /* optional */
+ char *DragPre; /* optional */
+ char *DragPost; /* optional */
+ int nCmdShow; /* optional */
+
+ HWND hwnd;
+
+ BYTE *KeyBuf;
+ BYTE *KeyBufIn;
+ BYTE *KeyBufOut;
+ unsigned int KeyBufSize;
+ BOOL quitnow;
+
+ char line_buf[256];
+ int line_end;
+ int line_start;
+ BOOL line_complete;
+ BOOL line_eof;
+
+ BOOL bFocus;
+ BOOL bGetCh;
+
+ char *fontname; /* font name */
+ int fontsize; /* font size in pts */
+
+ HFONT hfont;
+ int CharAscent;
+
+ int CaretHeight;
+ int CursorFlag;
+ POINT CursorPos;
+ POINT ClientSize;
+ POINT CharSize;
+ POINT ScrollPos;
+ POINT ScrollMax;
+
+ int x, y, cx, cy; /* window position */
+#ifndef GS_NO_UTF8
+ int utf8shift;
+#endif
+} TW;
+
+/* Create new TW structure */
+TW *text_new(void);
+
+/* Destroy window and TW structure */
+void text_destroy(TW *tw);
+
+/* test if a key has been pressed
+ * return TRUE if key hit
+ * return FALSE if no key
+ */
+int text_kbhit(TW *tw);
+
+/* Get a character from the keyboard, waiting if necessary */
+int getch(void);
+
+/* Get a line from the keyboard
+ * store line in buf, with no more than len characters
+ * including null character
+ * return number of characters read
+ */
+int text_gets(TW *tw, char *buf, int len);
+
+/* Get a line from the keyboard
+ * store line in buf, with no more than len characters
+ * line is not null terminated
+ * return number of characters read
+ */
+int text_read_line(TW *tw, char *buf, int len);
+
+/* Put a character to the window */
+int text_putch(TW *tw, int ch);
+
+/* Write cnt character from buf to the window */
+void text_write_buf(TW *tw, const char *buf, int cnt);
+
+/* Put a string to the window */
+void text_puts(TW *tw, const char *str);
+
+/* Scroll window to make cursor visible */
+void text_to_cursor(TW *tw);
+
+/* register window class */
+int text_register_class(TW *tw, HICON hicon);
+
+/* Create and show window with given name and min/max/normal state */
+/* return 0 on success, non-zero on error */
+int text_create(TW *tw, const char *title, int cmdShow);
+
+/* Set window font and size */
+/* a must choose monospaced */
+void text_font(TW *tw, const char *fontname, int fontsize);
+
+/* Set screen size in characters */
+void text_size(TW *tw, int width, int height);
+
+/* Set and get the window position and size */
+void text_setpos(TW *tw, int x, int y, int cx, int cy);
+int text_getpos(TW *tw, int *px, int *py, int *pcx, int *pcy);
+
+/* Set pre drag and post drag strings
+ * If a file is dropped on the window, the following will
+ * be poked into the keyboard buffer:
+ * the pre_drag string
+ * the file name
+ * the post_drag string
+ */
+void text_drag(TW *tw, const char *pre_drag, const char *post_drag);
+
+/* provide access to window handle */
+HWND text_get_handle(TW *tw);
+
+/* ================================== */
+
+#endif /* dwtext_INCLUDED */
diff --git a/psi/dwtrace.c b/psi/dwtrace.c
new file mode 100644
index 000000000..acb5b1391
--- /dev/null
+++ b/psi/dwtrace.c
@@ -0,0 +1,369 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Graphical trace server for Windows */
+
+/* This module use Win32-specific API.
+ For 16-bit Windows many of functions compile to stubs.
+*/
+
+/* fixme : Restoring image on WM_PAINT is NOT implemented yet.
+*/
+
+#define STRICT
+#include <windows.h>
+#include <math.h>
+#include "dwimg.h"
+
+static COLORREF WindowsColor(unsigned long c)
+{ /* This body uses a Windows specific macro RGB, which is being
+ redefined in GS include files. Please include them after
+ this definition.
+ */
+ return RGB(c >> 16, (c >> 8) & 255, c & 255);
+}
+
+#include "gscdefs.h"
+#include "stdpre.h"
+#include "gsdll.h"
+#include "vdtrace.h"
+#include "dwtrace.h"
+
+struct vd_trace_host_s {
+ bool inited;
+ IMAGE * tw;
+ HDC hdc;
+ int count_GetDC;
+ int window_height;
+ int line_width;
+ COLORREF color;
+ HPEN pen, pen0;
+ HBRUSH brush, brush0;
+ double bx, by;
+};
+
+static struct vd_trace_host_s host = {false, NULL, NULL};
+vd_trace_interface visual_tracer = { &host, 1, 1, 0, 0, 0, 0 };
+static const char *vdtrace_ini = "gs_vdtrace.ini";
+
+static void get_window()
+{ if (!host.inited) {
+ host.tw = image_new(NULL, NULL); /* create and add to list */
+ if (host.tw) {
+ image_open(host.tw);
+ UpdateWindow(host.tw->hwnd);
+ }
+ host.hdc = NULL;
+ host.count_GetDC = 0;
+ host.window_height = 100;
+ host.line_width = 1;
+ host.color = 0;
+ host.inited = true;
+ }
+}
+
+static inline int ScaleX(struct vd_trace_host_s *h, double x)
+{ return (int)(x + 0.5);
+}
+
+static inline int ScaleY(struct vd_trace_host_s *h, double y)
+{ return h->window_height - (int)(y + 0.5);
+}
+
+#define SX(x) ScaleX(I->host,x)
+#define SY(y) ScaleY(I->host,y)
+
+static inline void delete_pen_brush(vd_trace_interface *I)
+{ SelectObject(I->host->hdc, I->host->pen0);
+ SelectObject(I->host->hdc, I->host->brush0);
+ if(I->host->pen != NULL)
+ DeleteObject(I->host->pen);
+ I->host->pen = NULL;
+ if(I->host->brush != NULL)
+ DeleteObject(I->host->brush);
+ I->host->brush = NULL;
+}
+
+static inline void new_pen_brush(vd_trace_interface *I)
+{ delete_pen_brush(I);
+ I->host->pen = CreatePen(PS_SOLID, I->host->line_width, WindowsColor(I->host->color));
+ I->host->brush = CreateSolidBrush(WindowsColor(I->host->color));
+ SelectObject(I->host->hdc, I->host->pen);
+ SelectObject(I->host->hdc, I->host->brush);
+}
+
+static double dw_gt_get_size_x(vd_trace_interface *I)
+{ RECT r;
+ get_window();
+ if (host.tw == NULL)
+ return(100);
+ GetClientRect(I->host->tw->hwnd,&r);
+ return r.right - r.left;
+}
+
+static double dw_gt_get_size_y(vd_trace_interface *I)
+{ RECT r;
+ get_window();
+ if (host.tw == NULL)
+ return(100);
+ GetClientRect(I->host->tw->hwnd,&r);
+ return r.bottom - r.top;
+}
+
+static void dw_gt_get_dc(vd_trace_interface *I, vd_trace_interface **I1)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ if (I->host->hdc == NULL) {
+ RECT r;
+ I->host->hdc = GetDC(I->host->tw->hwnd);
+ SetMapMode(I->host->hdc,MM_TEXT);
+ GetClientRect(I->host->tw->hwnd, &r);
+ I->host->window_height = r.bottom;
+ SetBkMode(I->host->hdc,TRANSPARENT);
+ I->host->pen0 = (HPEN)SelectObject(I->host->hdc, GetStockObject(BLACK_PEN));
+ I->host->brush0 = (HBRUSH)SelectObject(I->host->hdc, GetStockObject(BLACK_BRUSH));
+ I->host->color = 0;
+ I->host->count_GetDC = 1;
+ *I1 = I;
+ } else
+ ++I->host->count_GetDC;
+}
+
+static void dw_gt_release_dc(vd_trace_interface *I, vd_trace_interface **I1)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ --I->host->count_GetDC;
+ if(I->host->count_GetDC == 0) {
+ ReleaseDC(I->host->tw->hwnd, I->host->hdc);
+ I->host->hdc = NULL;
+ delete_pen_brush(I);
+ *I1 = NULL;
+ } else if(I->host->count_GetDC < 0) {
+ /* safety : */
+ I->host->count_GetDC = 0;
+ *I1 = NULL;
+ }
+}
+
+static void dw_gt_erase(vd_trace_interface *I, unsigned long rgbcolor)
+{ HWND hwnd;
+ RECT r;
+ HBRUSH hbr;
+ get_window();
+ if (host.tw == NULL)
+ return;
+ hwnd = I->host->tw->hwnd;
+ GetClientRect(hwnd, &r);
+ hbr = CreateSolidBrush(rgbcolor);
+ FillRect(I->host->hdc, &r, hbr);
+ DeleteObject(hbr);
+}
+
+static void dw_gt_beg_path(vd_trace_interface *I)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ BeginPath(I->host->hdc);
+}
+
+static void dw_gt_end_path(vd_trace_interface *I)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ EndPath(I->host->hdc);
+}
+
+static void dw_gt_moveto(vd_trace_interface *I, double x, double y)
+{ POINT p;
+ get_window();
+ if (host.tw == NULL)
+ return;
+#ifdef __WIN32__
+ MoveToEx(I->host->hdc, SX(x), SY(y), &p);
+#else
+ MoveTo(I->host->hdc, SX(x), SY(y));
+#endif
+ I->host->bx = x; I->host->by = y;
+}
+
+static void dw_gt_lineto(vd_trace_interface *I, double x, double y)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ LineTo(I->host->hdc, SX(x), SY(y));
+}
+
+static void dw_gt_curveto(vd_trace_interface *I, double x0, double y0, double x1, double y1, double x2, double y2)
+{ POINT p[3];
+ get_window();
+ if (host.tw == NULL)
+ return;
+ p[0].x = SX(x0), p[0].y = SY(y0);
+ p[1].x = SX(x1), p[1].y = SY(y1);
+ p[2].x = SX(x2), p[2].y = SY(y2);
+ PolyBezierTo(I->host->hdc, p, 3);
+}
+
+static void dw_gt_closepath(vd_trace_interface *I)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ LineTo(I->host->hdc, SX(I->host->bx), SY(I->host->by));
+ CloseFigure(I->host->hdc);
+}
+
+static void dw_gt_circle(vd_trace_interface *I, double x, double y, int r)
+{ HBRUSH h;
+ get_window();
+ if (host.tw == NULL)
+ return;
+ h = (HBRUSH)SelectObject(I->host->hdc, GetStockObject(NULL_BRUSH));
+ Ellipse(I->host->hdc, SX(x)-r, SY(y)-r, SX(x)+r, SY(y)+r);
+ SelectObject(I->host->hdc, h);
+}
+
+static void dw_gt_round(vd_trace_interface *I, double x, double y, int r)
+{ HPEN h;
+ get_window();
+ if (host.tw == NULL)
+ return;
+ h = (HPEN)SelectObject(I->host->hdc, GetStockObject(NULL_PEN));
+ Ellipse(I->host->hdc, SX(x)-r, SY(y)-r, SX(x)+r, SY(y)+r);
+ SelectObject(I->host->hdc, h);
+}
+
+static void dw_gt_pixel(vd_trace_interface *I, double x, double y, unsigned long rgbcolor)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ SetPixel(I->host->hdc, SX(x), SY(y), rgbcolor);
+}
+
+static void dw_gt_fill(vd_trace_interface *I)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ FillPath(I->host->hdc);
+}
+
+static void dw_gt_stroke(vd_trace_interface *I)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ StrokePath(I->host->hdc);
+}
+
+static void dw_gt_setcolor(vd_trace_interface *I, unsigned long rgbcolor)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ if (I->host->color != rgbcolor) {
+ I->host->color = rgbcolor;
+ new_pen_brush(I);
+ SetTextColor(I->host->hdc, rgbcolor);
+ }
+}
+
+static void dw_gt_setlinewidth(vd_trace_interface *I, unsigned int width)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ if (I->host->line_width != width) {
+ I->host->line_width = width;
+ new_pen_brush(I);
+ }
+}
+
+static void dw_gt_text(vd_trace_interface *I, double x, double y, char *ASCIIZ)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ TextOut(I->host->hdc, SX(x), SY(y), ASCIIZ, strlen(ASCIIZ));
+}
+
+static void dw_gt_wait(vd_trace_interface *I)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ /* fixme : not implemented yet. */
+}
+
+static void dw_gt_set_scale(vd_trace_interface *I)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ I->scale_x *= GetPrivateProfileInt("VDTRACE", "ScaleX", 1000, vdtrace_ini) / 1000.0;
+ I->scale_y *= GetPrivateProfileInt("VDTRACE", "ScaleY", 1000, vdtrace_ini) / 1000.0;
+}
+
+static void dw_gt_set_shift(vd_trace_interface *I)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ I->shift_x += (int)GetPrivateProfileInt("VDTRACE", "ShiftX", 0, vdtrace_ini);
+ I->shift_y += (int)GetPrivateProfileInt("VDTRACE", "ShiftY", 0, vdtrace_ini);
+}
+
+static void dw_gt_set_origin(vd_trace_interface *I)
+{ get_window();
+ if (host.tw == NULL)
+ return;
+ I->orig_x += (int)GetPrivateProfileInt("VDTRACE", "OrigX", 0, vdtrace_ini);
+ I->orig_y += (int)GetPrivateProfileInt("VDTRACE", "OrigY", 0, vdtrace_ini);
+}
+
+#ifdef __WIN32__
+# define SET_CALLBACK(I,a) I.a = dw_gt_##a
+#else
+# define SET_CALLBACK(I,a) I.a = 0
+#endif
+
+vd_trace_interface *visual_tracer_init(void)
+{ SET_CALLBACK(visual_tracer, get_dc);
+ SET_CALLBACK(visual_tracer, release_dc);
+ SET_CALLBACK(visual_tracer, erase);
+ SET_CALLBACK(visual_tracer, get_size_x);
+ SET_CALLBACK(visual_tracer, get_size_y);
+ SET_CALLBACK(visual_tracer, erase);
+ SET_CALLBACK(visual_tracer, beg_path);
+ SET_CALLBACK(visual_tracer, end_path);
+ SET_CALLBACK(visual_tracer, moveto);
+ SET_CALLBACK(visual_tracer, lineto);
+ SET_CALLBACK(visual_tracer, curveto); /* optional */
+ SET_CALLBACK(visual_tracer, closepath);
+ SET_CALLBACK(visual_tracer, circle);
+ SET_CALLBACK(visual_tracer, round);
+ SET_CALLBACK(visual_tracer, pixel);
+ SET_CALLBACK(visual_tracer, fill);
+ SET_CALLBACK(visual_tracer, stroke);
+ SET_CALLBACK(visual_tracer, setcolor);
+ SET_CALLBACK(visual_tracer, setlinewidth);
+ SET_CALLBACK(visual_tracer, text);
+ SET_CALLBACK(visual_tracer, wait);
+ SET_CALLBACK(visual_tracer, set_scale);
+ SET_CALLBACK(visual_tracer, set_shift);
+ SET_CALLBACK(visual_tracer, set_origin);
+ return &visual_tracer;
+}
+
+void visual_tracer_close(void)
+{ if (host.tw != NULL) {
+ image_delete(host.tw);
+ image_close(host.tw);
+ }
+}
diff --git a/psi/dwtrace.h b/psi/dwtrace.h
new file mode 100644
index 000000000..19a56b445
--- /dev/null
+++ b/psi/dwtrace.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* The interface of Graphical trace server for Windows */
+
+#ifndef dwtrace_INCLUDED
+# define dwtrace_INCLUDED
+
+extern struct vd_trace_interface_s visual_tracer;
+struct vd_trace_interface_s *visual_tracer_init(void);
+void visual_tracer_close(void);
+
+#endif /* dwtrace_INCLUDED */
diff --git a/psi/dwuninst.def b/psi/dwuninst.def
new file mode 100644
index 000000000..5a4002e27
--- /dev/null
+++ b/psi/dwuninst.def
@@ -0,0 +1,4 @@
+; 'Ghostscript uninstall'
+NAME UNINSTGS
+HEAPSIZE 4096
+STACKSIZE 32768
diff --git a/psi/dwuninst_x64.manifest b/psi/dwuninst_x64.manifest
new file mode 100644
index 000000000..12ac7b8ed
--- /dev/null
+++ b/psi/dwuninst_x64.manifest
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity version="1.0.0.0"
+ processorArchitecture="AMD64"
+ name="uninstgs"
+ type="win32" />
+ <description>Ghostscript uninstaller</description>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/psi/dwuninst_x86.manifest b/psi/dwuninst_x86.manifest
new file mode 100644
index 000000000..865ad09fd
--- /dev/null
+++ b/psi/dwuninst_x86.manifest
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity version="1.0.0.0"
+ processorArchitecture="X86"
+ name="uninstgs"
+ type="win32" />
+ <description>Ghostscript uninstaller</description>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/psi/dxmain.c b/psi/dxmain.c
new file mode 100644
index 000000000..6cb03a98c
--- /dev/null
+++ b/psi/dxmain.c
@@ -0,0 +1,1244 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* dxmain.c */
+/*
+ * Ghostscript frontend which provides a graphical window
+ * using Gtk+. Load time linking to libgs.so
+ * Compile using
+ * gcc `gtk-config --cflags` -o gs dxmain.c -lgs `gtk-config --libs`
+ *
+ * The ghostscript library needs to be compiled with
+ * gcc -fPIC -g -c -Wall file.c
+ * gcc -shared -Wl,-soname,libgs.so.6 -o libgs.so.6.60 file.o -lc
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <gtk/gtk.h>
+#define __PROTOTYPES__
+#include "ierrors.h"
+#include "iapi.h"
+#include "gdevdsp.h"
+
+const char start_string[] = "systemdict /start get exec\n";
+
+static gboolean read_stdin_handler(GIOChannel *channel, GIOCondition condition,
+ gpointer data);
+static int gsdll_stdin(void *instance, char *buf, int len);
+static int gsdll_stdout(void *instance, const char *str, int len);
+static int gsdll_stdout(void *instance, const char *str, int len);
+static int display_open(void *handle, void *device);
+static int display_preclose(void *handle, void *device);
+static int display_close(void *handle, void *device);
+static int display_presize(void *handle, void *device, int width, int height,
+ int raster, unsigned int format);
+static int display_size(void *handle, void *device, int width, int height,
+ int raster, unsigned int format, unsigned char *pimage);
+static int display_sync(void *handle, void *device);
+static int display_page(void *handle, void *device, int copies, int flush);
+static int display_update(void *handle, void *device, int x, int y,
+ int w, int h);
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*********************************************************************/
+/* stdio functions */
+
+struct stdin_buf {
+ char *buf;
+ int len; /* length of buffer */
+ int count; /* number of characters returned */
+};
+
+/* handler for reading non-blocking stdin */
+static gboolean
+read_stdin_handler(GIOChannel *channel, GIOCondition condition, gpointer data)
+{
+ struct stdin_buf *input = (struct stdin_buf *)data;
+ GError *error = NULL;
+ gsize c;
+
+ if (condition & (G_IO_PRI)) {
+ g_print("input exception");
+ input->count = 0; /* EOF */
+ }
+ else if (condition & (G_IO_IN)) {
+ g_io_channel_read_chars(channel, input->buf, input->len, &c, &error);
+ input->count = (int)c;
+ if (error) {
+ g_print("%s\n", error->message);
+ g_error_free(error);
+ }
+ }
+ else {
+ g_print("input condition unknown");
+ input->count = 0; /* EOF */
+ }
+ return TRUE;
+}
+
+/* callback for reading stdin */
+static int
+gsdll_stdin(void *instance, char *buf, int len)
+{
+ struct stdin_buf input;
+ gint input_tag;
+ GIOChannel *channel;
+ GError *error = NULL;
+
+ input.len = len;
+ input.buf = buf;
+ input.count = -1;
+
+ channel = g_io_channel_unix_new(fileno(stdin));
+ g_io_channel_set_encoding(channel, NULL, &error);
+ g_io_channel_set_buffered(channel, FALSE);
+ if (error) {
+ g_print("%s\n", error->message);
+ g_error_free(error);
+ }
+ input_tag = g_io_add_watch(channel,
+ (G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP),
+ read_stdin_handler, &input);
+ while (input.count < 0)
+ gtk_main_iteration_do(TRUE);
+ g_source_remove(input_tag);
+ g_io_channel_unref(channel);
+
+ return input.count;
+}
+
+static int
+gsdll_stdout(void *instance, const char *str, int len)
+{
+ gtk_main_iteration_do(FALSE);
+ fwrite(str, 1, len, stdout);
+ fflush(stdout);
+ return len;
+}
+
+static int
+gsdll_stderr(void *instance, const char *str, int len)
+{
+ gtk_main_iteration_do(FALSE);
+ fwrite(str, 1, len, stderr);
+ fflush(stderr);
+ return len;
+}
+
+
+/*********************************************************************/
+/* dll display device */
+
+typedef struct IMAGE_DEVICEN_S IMAGE_DEVICEN;
+struct IMAGE_DEVICEN_S {
+ int used; /* non-zero if in use */
+ int visible; /* show on window */
+ char name[64];
+ int cyan;
+ int magenta;
+ int yellow;
+ int black;
+ int menu; /* non-zero if menu item added to system menu */
+};
+#define IMAGE_DEVICEN_MAX 8
+
+typedef struct IMAGE_S IMAGE;
+struct IMAGE_S {
+ void *handle;
+ void *device;
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *cmyk_bar;
+ GtkWidget *separation[IMAGE_DEVICEN_MAX];
+ GtkWidget *show_as_gray;
+ GtkWidget *scroll;
+ GtkWidget *darea;
+ guchar *buf;
+ gint width;
+ gint height;
+ gint rowstride;
+ unsigned int format;
+ int devicen_gray; /* true if a single separation should be shown gray */
+ IMAGE_DEVICEN devicen[IMAGE_DEVICEN_MAX];
+ guchar *rgbbuf; /* used when we need to convert raster format */
+ IMAGE *next;
+};
+
+IMAGE *first_image;
+static IMAGE *image_find(void *handle, void *device);
+static void window_destroy(GtkWidget *w, gpointer data);
+static void window_create(IMAGE *img);
+static void window_resize(IMAGE *img);
+#if !GTK_CHECK_VERSION(3, 0, 0)
+static gboolean window_draw(GtkWidget *widget, GdkEventExpose *event, gpointer user_data);
+#else
+static gboolean window_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data);
+#endif
+
+static IMAGE *
+image_find(void *handle, void *device)
+{
+ IMAGE *img;
+ for (img = first_image; img!=0; img=img->next) {
+ if ((img->handle == handle) && (img->device == device))
+ return img;
+ }
+ return NULL;
+}
+
+static gboolean
+#if !GTK_CHECK_VERSION(3, 0, 0)
+window_draw(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
+#else
+window_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
+#endif
+{
+ IMAGE *img = (IMAGE *)user_data;
+ if (img && img->window && img->buf) {
+ GdkPixbuf *pixbuf = NULL;
+ int color = img->format & DISPLAY_COLORS_MASK;
+ int depth = img->format & DISPLAY_DEPTH_MASK;
+#if !GTK_CHECK_VERSION(3, 0, 0)
+ cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget));
+ gdk_cairo_region(cr, event->region);
+ cairo_clip(cr);
+#endif
+ gdk_cairo_set_source_color(cr, &gtk_widget_get_style(widget)->bg[GTK_STATE_NORMAL]);
+ cairo_paint(cr);
+ switch (color) {
+ case DISPLAY_COLORS_NATIVE:
+ if (depth == DISPLAY_DEPTH_8 && img->rgbbuf)
+ pixbuf = gdk_pixbuf_new_from_data(img->rgbbuf,
+ GDK_COLORSPACE_RGB, FALSE, 8,
+ img->width, img->height, img->width*3,
+ NULL, NULL);
+ else if ((depth == DISPLAY_DEPTH_16) && img->rgbbuf)
+ pixbuf = gdk_pixbuf_new_from_data(img->rgbbuf,
+ GDK_COLORSPACE_RGB, FALSE, 8,
+ img->width, img->height, img->width*3,
+ NULL, NULL);
+ break;
+ case DISPLAY_COLORS_GRAY:
+ if (depth == DISPLAY_DEPTH_8 && img->rgbbuf)
+ pixbuf = gdk_pixbuf_new_from_data(img->rgbbuf,
+ GDK_COLORSPACE_RGB, FALSE, 8,
+ img->width, img->height, img->width*3,
+ NULL, NULL);
+ break;
+ case DISPLAY_COLORS_RGB:
+ if (depth == DISPLAY_DEPTH_8) {
+ if (img->rgbbuf)
+ pixbuf = gdk_pixbuf_new_from_data(img->rgbbuf,
+ GDK_COLORSPACE_RGB, FALSE, 8,
+ img->width, img->height, img->width*3,
+ NULL, NULL);
+ else
+ pixbuf = gdk_pixbuf_new_from_data(img->buf,
+ GDK_COLORSPACE_RGB, FALSE, 8,
+ img->width, img->height, img->rowstride,
+ NULL, NULL);
+ }
+ break;
+ case DISPLAY_COLORS_CMYK:
+ if (((depth == DISPLAY_DEPTH_1) ||
+ (depth == DISPLAY_DEPTH_8)) && img->rgbbuf)
+ pixbuf = gdk_pixbuf_new_from_data(img->rgbbuf,
+ GDK_COLORSPACE_RGB, FALSE, 8,
+ img->width, img->height, img->width*3,
+ NULL, NULL);
+ break;
+ case DISPLAY_COLORS_SEPARATION:
+ if ((depth == DISPLAY_DEPTH_8) && img->rgbbuf)
+ pixbuf = gdk_pixbuf_new_from_data(img->rgbbuf,
+ GDK_COLORSPACE_RGB, FALSE, 8,
+ img->width, img->height, img->width*3,
+ NULL, NULL);
+ break;
+ }
+ if (pixbuf) gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
+ cairo_paint(cr);
+#if !GTK_CHECK_VERSION(3, 0, 0)
+ cairo_destroy(cr);
+#endif
+ if (pixbuf) g_object_unref(pixbuf);
+ }
+ return TRUE;
+}
+
+static void window_destroy(GtkWidget *w, gpointer data)
+{
+ IMAGE *img = (IMAGE *)data;
+ img->window = NULL;
+ img->scroll = NULL;
+ img->darea = NULL;
+}
+
+static void window_create(IMAGE *img)
+{
+ /* Create a gtk window */
+ img->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(img->window), "gs");
+#if !GTK_CHECK_VERSION(3, 0, 0)
+ img->vbox = gtk_vbox_new(FALSE, 0);
+#else
+ img->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_set_homogeneous(img->vbox, FALSE);
+#endif
+ gtk_container_add(GTK_CONTAINER(img->window), img->vbox);
+ gtk_widget_show(img->vbox);
+
+ img->darea = gtk_drawing_area_new();
+ gtk_widget_show(img->darea);
+ img->scroll = gtk_scrolled_window_new(NULL, NULL);
+ gtk_widget_show(img->scroll);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(img->scroll),
+ GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(img->scroll),
+ img->darea);
+ gtk_box_pack_start(GTK_BOX(img->vbox), img->scroll, TRUE, TRUE, 0);
+#if !GTK_CHECK_VERSION(3, 0, 0)
+ g_signal_connect(G_OBJECT (img->darea), "expose-event",
+#else
+ g_signal_connect(G_OBJECT (img->darea), "draw",
+#endif
+ G_CALLBACK (window_draw), img);
+ g_signal_connect(G_OBJECT (img->window), "destroy",
+ G_CALLBACK (window_destroy), img);
+ g_signal_connect(G_OBJECT (img->window), "delete-event",
+ G_CALLBACK (gtk_widget_hide_on_delete), NULL);
+ /* do not show img->window until we know the image size */
+}
+
+static void window_resize(IMAGE *img)
+{
+ gboolean visible;
+ gtk_widget_set_size_request(GTK_WIDGET (img->darea),
+ img->width, img->height);
+
+#if !GTK_CHECK_VERSION(3, 0, 0)
+ visible = (GTK_WIDGET_FLAGS(img->window) & GTK_VISIBLE);
+#else
+ visible = gtk_widget_get_visible(img->window);
+#endif
+
+ if (!visible) {
+ /* We haven't yet shown the window, so set a default size
+ * which is smaller than the desktop to allow room for
+ * desktop toolbars, and if possible a little larger than
+ * the image to allow room for the scroll bars.
+ * We don't know the width of the scroll bars, so just guess. */
+ gtk_window_set_default_size(GTK_WINDOW(img->window),
+ min(gdk_screen_width()-96, img->width+24),
+ min(gdk_screen_height()-96, img->height+24));
+ }
+}
+
+static void window_separation(IMAGE *img, int sep)
+{
+ img->devicen[sep].visible = !img->devicen[sep].visible;
+ display_sync(img->handle, img->device);
+}
+
+static void signal_sep0(GtkWidget *w, gpointer data)
+{
+ window_separation((IMAGE *)data, 0);
+}
+
+static void signal_sep1(GtkWidget *w, gpointer data)
+{
+ window_separation((IMAGE *)data, 1);
+}
+
+static void signal_sep2(GtkWidget *w, gpointer data)
+{
+ window_separation((IMAGE *)data, 2);
+}
+
+static void signal_sep3(GtkWidget *w, gpointer data)
+{
+ window_separation((IMAGE *)data, 3);
+}
+
+static void signal_sep4(GtkWidget *w, gpointer data)
+{
+ window_separation((IMAGE *)data, 4);
+}
+
+static void signal_sep5(GtkWidget *w, gpointer data)
+{
+ window_separation((IMAGE *)data, 5);
+}
+
+static void signal_sep6(GtkWidget *w, gpointer data)
+{
+ window_separation((IMAGE *)data, 6);
+}
+
+static void signal_sep7(GtkWidget *w, gpointer data)
+{
+ window_separation((IMAGE *)data, 7);
+}
+
+GCallback signal_separation[IMAGE_DEVICEN_MAX] = {
+ (GCallback)signal_sep0,
+ (GCallback)signal_sep1,
+ (GCallback)signal_sep2,
+ (GCallback)signal_sep3,
+ (GCallback)signal_sep4,
+ (GCallback)signal_sep5,
+ (GCallback)signal_sep6,
+ (GCallback)signal_sep7
+};
+
+static GtkWidget *
+window_add_button(IMAGE *img, const char *label, GCallback fn)
+{
+ GtkWidget *w;
+ w = gtk_check_button_new_with_label(label);
+ gtk_box_pack_start(GTK_BOX(img->cmyk_bar), w, FALSE, FALSE, 5);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
+ g_signal_connect(G_OBJECT(w), "clicked", fn, img);
+ gtk_widget_show(w);
+ return w;
+}
+
+static void signal_show_as_gray(GtkWidget *w, gpointer data)
+{
+ IMAGE *img= (IMAGE *)data;
+ img->devicen_gray= !img->devicen_gray;
+ display_sync(img->handle, img->device);
+}
+
+/* New device has been opened */
+static int display_open(void *handle, void *device)
+{
+
+ IMAGE *img = (IMAGE *)malloc(sizeof(IMAGE));
+ if (img == NULL)
+ return -1;
+ memset(img, 0, sizeof(IMAGE));
+
+ /* add to list */
+ if (first_image)
+ img->next = first_image;
+ first_image = img;
+
+ /* remember device and handle */
+ img->handle = handle;
+ img->device = device;
+
+ /* create window */
+ window_create(img);
+
+ gtk_main_iteration_do(FALSE);
+ return 0;
+}
+
+static int display_preclose(void *handle, void *device)
+{
+ IMAGE *img = image_find(handle, device);
+ if (img == NULL)
+ return -1;
+
+ gtk_main_iteration_do(FALSE);
+
+ img->buf = NULL;
+ img->width = 0;
+ img->height = 0;
+ img->rowstride = 0;
+ img->format = 0;
+
+ gtk_widget_destroy(img->window);
+ img->window = NULL;
+ img->scroll = NULL;
+ img->darea = NULL;
+ if (img->rgbbuf)
+ free(img->rgbbuf);
+ img->rgbbuf = NULL;
+
+ gtk_main_iteration_do(FALSE);
+
+ return 0;
+}
+
+static int display_close(void *handle, void *device)
+{
+ IMAGE *img = image_find(handle, device);
+ if (img == NULL)
+ return -1;
+
+ /* remove from list */
+ if (img == first_image) {
+ first_image = img->next;
+ }
+ else {
+ IMAGE *tmp;
+ for (tmp = first_image; tmp!=0; tmp=tmp->next) {
+ if (img == tmp->next)
+ tmp->next = img->next;
+ }
+ }
+
+ return 0;
+}
+
+static int display_presize(void *handle, void *device, int width, int height,
+ int raster, unsigned int format)
+{
+ /* Assume everything is OK.
+ * It would be better to return gs_error_rangecheck if we can't
+ * support the format.
+ */
+ return 0;
+}
+
+static int display_size(void *handle, void *device, int width, int height,
+ int raster, unsigned int format, unsigned char *pimage)
+{
+ IMAGE *img = image_find(handle, device);
+ int color;
+ int depth;
+ int i;
+ gboolean visible;
+
+ if (img == NULL)
+ return -1;
+
+ if (img->rgbbuf)
+ free(img->rgbbuf);
+ img->rgbbuf = NULL;
+
+ img->width = width;
+ img->height = height;
+ img->rowstride = raster;
+ img->buf = pimage;
+ img->format = format;
+
+ /* Reset separations */
+ for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
+ img->devicen[i].used = 0;
+ img->devicen[i].visible = 1;
+ memset(img->devicen[i].name, 0, sizeof(img->devicen[i].name));
+ img->devicen[i].cyan = 0;
+ img->devicen[i].magenta = 0;
+ img->devicen[i].yellow = 0;
+ img->devicen[i].black = 0;
+ }
+
+ color = img->format & DISPLAY_COLORS_MASK;
+ depth = img->format & DISPLAY_DEPTH_MASK;
+ switch (color) {
+ case DISPLAY_COLORS_NATIVE:
+ if (depth == DISPLAY_DEPTH_8) {
+ img->rgbbuf = (guchar *)malloc(width * height * 3);
+ if (img->rgbbuf == NULL)
+ return -1;
+ break;
+ }
+ else if (depth == DISPLAY_DEPTH_16) {
+ /* need to convert to 24RGB */
+ img->rgbbuf = (guchar *)malloc(width * height * 3);
+ if (img->rgbbuf == NULL)
+ return -1;
+ }
+ else
+ return gs_error_rangecheck; /* not supported */
+ case DISPLAY_COLORS_GRAY:
+ if (depth == DISPLAY_DEPTH_8) {
+ img->rgbbuf = (guchar *)malloc(width * height * 3);
+ if (img->rgbbuf == NULL)
+ return -1;
+ break;
+ }
+ else
+ return -1; /* not supported */
+ case DISPLAY_COLORS_RGB:
+ if (depth == DISPLAY_DEPTH_8) {
+ if (((img->format & DISPLAY_ALPHA_MASK) == DISPLAY_ALPHA_NONE)
+ && ((img->format & DISPLAY_ENDIAN_MASK)
+ == DISPLAY_BIGENDIAN))
+ break;
+ else {
+ /* need to convert to 24RGB */
+ img->rgbbuf = (guchar *)malloc(width * height * 3);
+ if (img->rgbbuf == NULL)
+ return -1;
+ }
+ }
+ else
+ return -1; /* not supported */
+ break;
+ case DISPLAY_COLORS_CMYK:
+ if ((depth == DISPLAY_DEPTH_1) || (depth == DISPLAY_DEPTH_8)) {
+ /* need to convert to 24RGB */
+ img->rgbbuf = (guchar *)malloc(width * height * 3);
+ if (img->rgbbuf == NULL)
+ return -1;
+ /* We already know about the CMYK components */
+ img->devicen[0].used = 1;
+ img->devicen[0].cyan = 65535;
+ strncpy(img->devicen[0].name, "Cyan",
+ sizeof(img->devicen[0].name));
+ img->devicen[1].used = 1;
+ img->devicen[1].magenta = 65535;
+ strncpy(img->devicen[1].name, "Magenta",
+ sizeof(img->devicen[1].name));
+ img->devicen[2].used = 1;
+ img->devicen[2].yellow = 65535;
+ strncpy(img->devicen[2].name, "Yellow",
+ sizeof(img->devicen[2].name));
+ img->devicen[3].used = 1;
+ img->devicen[3].black = 65535;
+ strncpy(img->devicen[3].name, "Black",
+ sizeof(img->devicen[3].name));
+ }
+ else
+ return -1; /* not supported */
+ break;
+ case DISPLAY_COLORS_SEPARATION:
+ /* we can't display this natively */
+ /* we will convert it just before displaying */
+ if (depth != DISPLAY_DEPTH_8)
+ return -1; /* not supported */
+ img->rgbbuf = (guchar *)malloc(width * height * 3);
+ if (img->rgbbuf == NULL)
+ return -1;
+ break;
+ }
+
+ if ((color == DISPLAY_COLORS_CMYK) ||
+ (color == DISPLAY_COLORS_SEPARATION)) {
+ if (!GTK_IS_WIDGET(img->cmyk_bar)) {
+ /* add bar to select separation */
+#if !GTK_CHECK_VERSION(3, 0, 0)
+ img->cmyk_bar = gtk_hbox_new(FALSE, 0);
+#else
+ img->cmyk_bar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_homogeneous(img->cmyk_bar, FALSE);
+#endif
+ gtk_box_pack_start(GTK_BOX(img->vbox), img->cmyk_bar,
+ FALSE, FALSE, 0);
+ for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
+ img->separation[i] =
+ window_add_button(img, img->devicen[i].name,
+ signal_separation[i]);
+ }
+ img->show_as_gray = gtk_check_button_new_with_label("Show as Gray");
+ gtk_box_pack_end(GTK_BOX(img->cmyk_bar), img->show_as_gray,
+ FALSE, FALSE, 5);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(img->show_as_gray),
+ FALSE);
+ g_signal_connect(G_OBJECT(img->show_as_gray), "clicked",
+ G_CALLBACK(signal_show_as_gray), img);
+ gtk_widget_show(img->show_as_gray);
+ }
+ gtk_widget_show(img->cmyk_bar);
+ }
+ else {
+ if (GTK_IS_WIDGET(img->cmyk_bar))
+ gtk_widget_hide(img->cmyk_bar);
+ }
+
+ window_resize(img);
+
+#if !GTK_CHECK_VERSION(3, 0, 0)
+ visible = (GTK_WIDGET_FLAGS(img->window) & GTK_VISIBLE);
+#else
+ visible = gtk_widget_get_visible(img->window);
+#endif
+
+ if (!visible) gtk_widget_show_all(img->window);
+
+ gtk_main_iteration_do(FALSE);
+ return 0;
+}
+
+static int display_sync(void *handle, void *device)
+{
+ IMAGE *img = image_find(handle, device);
+ int color;
+ int depth;
+ int endian;
+ int native555;
+ int alpha;
+ gboolean visible;
+
+ if (img == NULL)
+ return -1;
+
+ color = img->format & DISPLAY_COLORS_MASK;
+ depth = img->format & DISPLAY_DEPTH_MASK;
+ endian = img->format & DISPLAY_ENDIAN_MASK;
+ native555 = img->format & DISPLAY_555_MASK;
+ alpha = img->format & DISPLAY_ALPHA_MASK;
+
+ if ((color == DISPLAY_COLORS_CMYK) ||
+ (color == DISPLAY_COLORS_SEPARATION)) {
+ /* check if separations have changed */
+ int i;
+ const gchar *str;
+ for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
+ str = gtk_label_get_text(
+ GTK_LABEL(gtk_bin_get_child(GTK_BIN(img->separation[i]))));
+ if (!img->devicen[i].used)
+ gtk_widget_hide(img->separation[i]);
+ else if (strcmp(img->devicen[i].name, str) != 0) {
+ /* text has changed, update it */
+ gtk_label_set_text(
+ GTK_LABEL(gtk_bin_get_child(GTK_BIN(img->separation[i]))),
+ img->devicen[i].name);
+ gtk_widget_show(img->separation[i]);
+ }
+ }
+ }
+
+ /* some formats need to be converted for use by GdkRgb */
+ switch (color) {
+ case DISPLAY_COLORS_NATIVE:
+ if (depth == DISPLAY_DEPTH_16) {
+ if (endian == DISPLAY_LITTLEENDIAN) {
+ if (native555 == DISPLAY_NATIVE_555) {
+ /* BGR555 */
+ int x, y;
+ unsigned short w;
+ unsigned char value;
+ unsigned char *s, *d;
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ w = s[0] + (s[1] << 8);
+ value = (w >> 10) & 0x1f; /* red */
+ *d++ = (value << 3) + (value >> 2);
+ value = (w >> 5) & 0x1f; /* green */
+ *d++ = (value << 3) + (value >> 2);
+ value = w & 0x1f; /* blue */
+ *d++ = (value << 3) + (value >> 2);
+ s += 2;
+ }
+ }
+ }
+ else {
+ /* BGR565 */
+ int x, y;
+ unsigned short w;
+ unsigned char value;
+ unsigned char *s, *d;
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ w = s[0] + (s[1] << 8);
+ value = (w >> 11) & 0x1f; /* red */
+ *d++ = (value << 3) + (value >> 2);
+ value = (w >> 5) & 0x3f; /* green */
+ *d++ = (value << 2) + (value >> 4);
+ value = w & 0x1f; /* blue */
+ *d++ = (value << 3) + (value >> 2);
+ s += 2;
+ }
+ }
+ }
+ }
+ else {
+ if (native555 == DISPLAY_NATIVE_555) {
+ /* RGB555 */
+ int x, y;
+ unsigned short w;
+ unsigned char value;
+ unsigned char *s, *d;
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ w = s[1] + (s[0] << 8);
+ value = (w >> 10) & 0x1f; /* red */
+ *d++ = (value << 3) + (value >> 2);
+ value = (w >> 5) & 0x1f; /* green */
+ *d++ = (value << 3) + (value >> 2);
+ value = w & 0x1f; /* blue */
+ *d++ = (value << 3) + (value >> 2);
+ s += 2;
+ }
+ }
+ }
+ else {
+ /* RGB565 */
+ int x, y;
+ unsigned short w;
+ unsigned char value;
+ unsigned char *s, *d;
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ w = s[1] + (s[0] << 8);
+ value = (w >> 11) & 0x1f; /* red */
+ *d++ = (value << 3) + (value >> 2);
+ value = (w >> 5) & 0x3f; /* green */
+ *d++ = (value << 2) + (value >> 4);
+ value = w & 0x1f; /* blue */
+ *d++ = (value << 3) + (value >> 2);
+ s += 2;
+ }
+ }
+ }
+ }
+ }
+ if (depth == DISPLAY_DEPTH_8) {
+ /* palette of 96 colors */
+ guchar color[96][3];
+ int i;
+ int one = 255 / 3;
+ int x, y;
+ unsigned char *s, *d;
+
+ for (i=0; i<96; i++) {
+ /* 0->63 = 00RRGGBB, 64->95 = 010YYYYY */
+ if (i < 64) {
+ color[i][0] =
+ ((i & 0x30) >> 4) * one; /* r */
+ color[i][1] =
+ ((i & 0x0c) >> 2) * one; /* g */
+ color[i][2] =
+ (i & 0x03) * one; /* b */
+ }
+ else {
+ int val = i & 0x1f;
+ val = (val << 3) + (val >> 2);
+ color[i][0] = color[i][1] = color[i][2] = val;
+ }
+ }
+
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ *d++ = color[*s][0]; /* r */
+ *d++ = color[*s][1]; /* g */
+ *d++ = color[*s][2]; /* b */
+ s++;
+ }
+ }
+ }
+ break;
+ case DISPLAY_COLORS_GRAY:
+ if (depth == DISPLAY_DEPTH_8) {
+ int x, y;
+ unsigned char *s, *d;
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ *d++ = *s; /* r */
+ *d++ = *s; /* g */
+ *d++ = *s; /* b */
+ s++;
+ }
+ }
+ }
+ break;
+ case DISPLAY_COLORS_RGB:
+ if ( (depth == DISPLAY_DEPTH_8) &&
+ ((alpha == DISPLAY_ALPHA_FIRST) ||
+ (alpha == DISPLAY_UNUSED_FIRST)) &&
+ (endian == DISPLAY_BIGENDIAN) ) {
+ /* Mac format */
+ int x, y;
+ unsigned char *s, *d;
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ s++; /* x = filler */
+ *d++ = *s++; /* r */
+ *d++ = *s++; /* g */
+ *d++ = *s++; /* b */
+ }
+ }
+ }
+ else if ( (depth == DISPLAY_DEPTH_8) &&
+ (endian == DISPLAY_LITTLEENDIAN) ) {
+ if ((alpha == DISPLAY_UNUSED_LAST) ||
+ (alpha == DISPLAY_ALPHA_LAST)) {
+ /* Windows format + alpha = BGRx */
+ int x, y;
+ unsigned char *s, *d;
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ *d++ = s[2]; /* r */
+ *d++ = s[1]; /* g */
+ *d++ = s[0]; /* b */
+ s += 4;
+ }
+ }
+ }
+ else if ((alpha == DISPLAY_UNUSED_FIRST) ||
+ (alpha == DISPLAY_ALPHA_FIRST)) {
+ /* xBGR */
+ int x, y;
+ unsigned char *s, *d;
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ *d++ = s[3]; /* r */
+ *d++ = s[2]; /* g */
+ *d++ = s[1]; /* b */
+ s += 4;
+ }
+ }
+ }
+ else {
+ /* Windows BGR24 */
+ int x, y;
+ unsigned char *s, *d;
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ *d++ = s[2]; /* r */
+ *d++ = s[1]; /* g */
+ *d++ = s[0]; /* b */
+ s += 3;
+ }
+ }
+ }
+ }
+ break;
+ case DISPLAY_COLORS_CMYK:
+ if (depth == DISPLAY_DEPTH_8) {
+ /* Separations */
+ int x, y;
+ int cyan, magenta, yellow, black;
+ unsigned char *s, *d;
+ int vc = img->devicen[0].visible;
+ int vm = img->devicen[1].visible;
+ int vy = img->devicen[2].visible;
+ int vk = img->devicen[3].visible;
+ int vall = vc && vm && vy && vk;
+ int show_gray = (vc + vm + vy + vk == 1) && img->devicen_gray;
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ cyan = *s++;
+ magenta = *s++;
+ yellow = *s++;
+ black = *s++;
+ if (!vall) {
+ if (!vc)
+ cyan = 0;
+ if (!vm)
+ magenta = 0;
+ if (!vy)
+ yellow = 0;
+ if (!vk)
+ black = 0;
+ if (show_gray) {
+ black += cyan + magenta + yellow;
+ cyan = magenta = yellow = 0;
+ }
+ }
+ *d++ = (255-cyan) * (255-black) / 255; /* r */
+ *d++ = (255-magenta) * (255-black) / 255; /* g */
+ *d++ = (255-yellow) * (255-black) / 255; /* b */
+ }
+ }
+ }
+ else if (depth == DISPLAY_DEPTH_1) {
+ /* Separations */
+ int x, y;
+ int cyan, magenta, yellow, black;
+ unsigned char *s, *d;
+ int vc = img->devicen[0].visible;
+ int vm = img->devicen[1].visible;
+ int vy = img->devicen[2].visible;
+ int vk = img->devicen[3].visible;
+ int vall = vc && vm && vy && vk;
+ int show_gray = (vc + vm + vy + vk == 1) && img->devicen_gray;
+ int value;
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ value = s[x/2];
+ if (x & 0)
+ value >>= 4;
+ cyan = ((value >> 3) & 1) * 255;
+ magenta = ((value >> 2) & 1) * 255;
+ yellow = ((value >> 1) & 1) * 255;
+ black = (value & 1) * 255;
+ if (!vall) {
+ if (!vc)
+ cyan = 0;
+ if (!vm)
+ magenta = 0;
+ if (!vy)
+ yellow = 0;
+ if (!vk)
+ black = 0;
+ if (show_gray) {
+ black += cyan + magenta + yellow;
+ cyan = magenta = yellow = 0;
+ }
+ }
+ *d++ = (255-cyan) * (255-black) / 255; /* r */
+ *d++ = (255-magenta) * (255-black) / 255; /* g */
+ *d++ = (255-yellow) * (255-black) / 255; /* b */
+ }
+ }
+ }
+ break;
+ case DISPLAY_COLORS_SEPARATION:
+ if (depth == DISPLAY_DEPTH_8) {
+ int j;
+ int x, y;
+ unsigned char *s, *d;
+ int cyan, magenta, yellow, black;
+ int num_comp = 0;
+ int value;
+ int num_visible = 0;
+ int show_gray = 0;
+ IMAGE_DEVICEN *devicen = img->devicen;
+ for (j=0; j<IMAGE_DEVICEN_MAX; j++) {
+ if (img->devicen[j].used) {
+ num_comp = j+1;
+ if (img->devicen[j].visible)
+ num_visible++;
+ }
+ }
+ if ((num_visible == 1) && img->devicen_gray)
+ show_gray = 1;
+
+ for (y = 0; y<img->height; y++) {
+ s = img->buf + y * img->rowstride;
+ d = img->rgbbuf + y * img->width * 3;
+ for (x=0; x<img->width; x++) {
+ cyan = magenta = yellow = black = 0;
+ if (show_gray) {
+ for (j=0; j<num_comp; j++) {
+ devicen = &img->devicen[j];
+ if (devicen->visible && devicen->used)
+ black += s[j];
+ }
+ }
+ else {
+ for (j=0; j<num_comp; j++) {
+ devicen = &img->devicen[j];
+ if (devicen->visible && devicen->used) {
+ value = s[j];
+ cyan += value*devicen->cyan /65535;
+ magenta += value*devicen->magenta/65535;
+ yellow += value*devicen->yellow /65535;
+ black += value*devicen->black /65535;
+ }
+ }
+ }
+ if (cyan > 255)
+ cyan = 255;
+ if (magenta > 255)
+ magenta = 255;
+ if (yellow > 255)
+ yellow = 255;
+ if (black > 255)
+ black = 255;
+ *d++ = (255-cyan) * (255-black) / 255; /* r */
+ *d++ = (255-magenta) * (255-black) / 255; /* g */
+ *d++ = (255-yellow) * (255-black) / 255; /* b */
+ s += 8;
+ }
+ }
+ }
+ break;
+ }
+
+ if (!GTK_IS_WIDGET(img->window)) {
+ window_create(img);
+ window_resize(img);
+ }
+#if !GTK_CHECK_VERSION(3, 0, 0)
+ visible = (GTK_WIDGET_FLAGS(img->window) & GTK_VISIBLE);
+#else
+ visible = gtk_widget_get_visible(img->window);
+#endif
+
+ if (!visible) gtk_widget_show_all(img->window);
+
+ gtk_widget_queue_draw(img->darea);
+ gtk_main_iteration_do(FALSE);
+ return 0;
+}
+
+static int display_page(void *handle, void *device, int copies, int flush)
+{
+ display_sync(handle, device);
+ return 0;
+}
+
+static int display_update(void *handle, void *device,
+ int x, int y, int w, int h)
+{
+ /* not implemented - eventually this will be used for progressive update */
+ return 0;
+}
+
+static int
+display_separation(void *handle, void *device,
+ int comp_num, const char *name,
+ unsigned short c, unsigned short m,
+ unsigned short y, unsigned short k)
+{
+ IMAGE *img = image_find(handle, device);
+ if (img == NULL)
+ return -1;
+ if ((comp_num < 0) || (comp_num > IMAGE_DEVICEN_MAX))
+ return -1;
+ img->devicen[comp_num].used = 1;
+ strncpy(img->devicen[comp_num].name, name,
+ sizeof(img->devicen[comp_num].name)-1);
+ img->devicen[comp_num].cyan = c;
+ img->devicen[comp_num].magenta = m;
+ img->devicen[comp_num].yellow = y;
+ img->devicen[comp_num].black = k;
+ return 0;
+}
+
+/* callback structure for "display" device */
+display_callback display = {
+ sizeof(display_callback),
+ DISPLAY_VERSION_MAJOR,
+ DISPLAY_VERSION_MINOR,
+ display_open,
+ display_preclose,
+ display_close,
+ display_presize,
+ display_size,
+ display_sync,
+ display_page,
+ display_update,
+ NULL, /* memalloc */
+ NULL, /* memfree */
+ display_separation
+};
+
+static int
+write_stderr(const char *str)
+{
+ fwrite(str, 1, strlen(str), stderr);
+
+ return fflush(stderr);
+}
+
+
+/* Note the space! It makes the string merging simpler */
+#define OUR_DEFAULT_DEV_STR "display "
+
+/*********************************************************************/
+
+int main(int argc, char *argv[])
+{
+ int exit_status;
+ int code = 1, code1;
+ void *instance;
+ int nargc;
+ char **nargv;
+ char dformat[64];
+ int exit_code;
+ gboolean use_gui;
+ const char *default_devs = NULL;
+ char *our_default_devs = NULL;
+ int len;
+
+ /* Gtk initialisation */
+ setlocale(LC_ALL, "");
+ use_gui = gtk_init_check(&argc, &argv);
+
+ /* insert display device parameters as first arguments */
+ sprintf(dformat, "-dDisplayFormat=%d",
+ DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_8 |
+ DISPLAY_BIGENDIAN | DISPLAY_TOPFIRST);
+ nargc = argc + 1;
+ nargv = (char **)malloc((nargc + 1) * sizeof(char *));
+ nargv[0] = argv[0];
+ nargv[1] = dformat;
+ memcpy(&nargv[2], &argv[1], argc * sizeof(char *));
+
+ /* run Ghostscript */
+ if ((code = gsapi_new_instance(&instance, NULL)) == 0) {
+ gsapi_set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr);
+ if (use_gui) {
+ gsapi_set_display_callback(instance, &display);
+
+ code = gsapi_get_default_device_list(instance, &default_devs, &len);
+ if (code >= 0) {
+ our_default_devs = malloc(len + strlen(OUR_DEFAULT_DEV_STR) + 1);
+ if (our_default_devs) {
+ memcpy(our_default_devs, OUR_DEFAULT_DEV_STR, strlen(OUR_DEFAULT_DEV_STR));
+ memcpy(our_default_devs + strlen(OUR_DEFAULT_DEV_STR), default_devs, len);
+ our_default_devs[len + strlen(OUR_DEFAULT_DEV_STR)] = '\0';
+ code = gsapi_set_default_device_list(instance, our_default_devs, strlen(default_devs));
+ free(our_default_devs);
+ }
+ else {
+ code = -1;
+ }
+ }
+ if (code < 0) {
+ write_stderr("Could not set default devices, continuing with existing defaults\n");
+ code = 0;
+ }
+ }
+
+ if (code == 0)
+ code = gsapi_init_with_args(instance, nargc, nargv);
+
+ if (code == 0)
+ code = gsapi_run_string(instance, start_string, 0, &exit_code);
+ code1 = gsapi_exit(instance);
+ if (code == 0 || code == gs_error_Quit)
+ code = code1;
+ if (code == gs_error_Quit)
+ code = 0; /* user executed 'quit' */
+
+ gsapi_delete_instance(instance);
+ }
+
+ exit_status = 0;
+ switch (code) {
+ case 0:
+ case gs_error_Info:
+ case gs_error_Quit:
+ break;
+ case gs_error_Fatal:
+ exit_status = 1;
+ break;
+ default:
+ exit_status = 255;
+ }
+
+ return exit_status;
+}
diff --git a/psi/dxmainc.c b/psi/dxmainc.c
new file mode 100644
index 000000000..8e0da40b2
--- /dev/null
+++ b/psi/dxmainc.c
@@ -0,0 +1,113 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* dxmainc.c */
+/*
+ * Ghostscript frontend which provides a console to the Ghostscript
+ * shared library. Load time linking to libgs.so
+ * This does not support the display device. Use dxmain.c/gsx for that,
+ * or modify this program to use bare Xlib calls.
+ * Compile using
+ * gcc -o gsc dxmainc.c -lgs
+ *
+ * The ghostscript library needs to be compiled with
+ * gcc -fPIC -g -c -Wall file.c
+ * gcc -shared -Wl,-soname,libgs.so.7 -o libgs.so.7.00 file.o -lc
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#define __PROTOTYPES__
+#include "ierrors.h"
+#include "iapi.h"
+
+const char start_string[] = "systemdict /start get exec\n";
+
+static int gsdll_stdin(void *instance, char *buf, int len);
+static int gsdll_stdout(void *instance, const char *str, int len);
+static int gsdll_stdout(void *instance, const char *str, int len);
+
+/*********************************************************************/
+/* stdio functions */
+
+/* callback for reading stdin */
+/* Use async input */
+static int
+gsdll_stdin(void *instance, char *buf, int len)
+{
+ return read(fileno(stdin), buf, len);
+}
+
+static int
+gsdll_stdout(void *instance, const char *str, int len)
+{
+ fwrite(str, 1, len, stdout);
+ fflush(stdout);
+ return len;
+}
+
+static int
+gsdll_stderr(void *instance, const char *str, int len)
+{
+ fwrite(str, 1, len, stderr);
+ fflush(stderr);
+ return len;
+}
+
+/*********************************************************************/
+
+int main(int argc, char *argv[])
+{
+ int exit_status;
+ int code = 1, code1;
+ void *instance;
+ int exit_code;
+
+ /* run Ghostscript */
+ if ((code = gsapi_new_instance(&instance, NULL)) == 0) {
+ gsapi_set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr);
+ code = gsapi_init_with_args(instance, argc, argv);
+
+ if (code == 0)
+ code = gsapi_run_string(instance, start_string, 0, &exit_code);
+ code1 = gsapi_exit(instance);
+ if (code == 0 || code == gs_error_Quit)
+ code = code1;
+ if (code == gs_error_Quit)
+ code = 0; /* user executed 'quit' */
+
+ gsapi_delete_instance(instance);
+ }
+
+ exit_status = 0;
+ switch (code) {
+ case 0:
+ case gs_error_Info:
+ case gs_error_Quit:
+ break;
+ case gs_error_Fatal:
+ exit_status = 1;
+ break;
+ default:
+ exit_status = 255;
+ }
+
+ return exit_status;
+}
diff --git a/psi/estack.h b/psi/estack.h
new file mode 100644
index 000000000..6dc106520
--- /dev/null
+++ b/psi/estack.h
@@ -0,0 +1,170 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Definitions for the execution stack */
+
+#ifndef estack_INCLUDED
+# define estack_INCLUDED
+
+#include "iestack.h"
+#include "icstate.h" /* for access to exec_stack */
+
+/* Define access to the cached current_file pointer. */
+#define esfile (iexec_stack.current_file)
+#define esfile_clear_cache() estack_clear_cache(&iexec_stack)
+#define esfile_set_cache(pref) estack_set_cache(&iexec_stack, pref)
+#define esfile_check_cache() estack_check_cache(&iexec_stack)
+
+/* Define the execution stack pointers for operators. */
+#define iexec_stack (i_ctx_p->exec_stack)
+#define e_stack (iexec_stack.stack)
+
+#define esbot (e_stack.bot)
+#define esp (e_stack.p)
+#define estop (e_stack.top)
+
+/*
+ * The execution stack holds several different kinds of objects (refs)
+ * related to executing PostScript code:
+ *
+ * - Procedures being executed are held here. They always have
+ * type = t_array, t_mixedarray, or t_shortarray, with a_executable set.
+ * More specifically, the e-stack holds the as yet unexecuted tail of the
+ * procedure.
+ *
+ * - if, ifelse, etc. push arguments to be executed here. They may be
+ * any kind of object whatever. Similarly, looping operators (forall, for,
+ * etc.) push the procedure that is to be executed for each iteration.
+ *
+ * - Control operators (filenameforall, for, repeat, loop, forall,
+ * pathforall, run, stopped, ...) use continuations as described below.
+ *
+ * Note that there are many internal operators that need to use
+ * continuations -- for example, all the 'show' operators, since they may
+ * call out to BuildChar procedures.
+ */
+
+/*
+ * Because the Ghostscript architecture doesn't allow recursive calls to the
+ * interpreter, any operator that needs to call out to PostScript code (for
+ * example, the 'show' operators calling a BuildChar procedure, or setscreen
+ * sampling a spot function) must use a continuation -- an internal
+ * "operator" procedure that continues the logical thread of execution after
+ * the callout. Operators needing to use continuations push the following
+ * onto the execution stack (from bottom to top):
+ *
+ * - An e-stack mark -- an executable null that indicates the bottom of
+ * the block associated with a callout. (This should not be confused
+ * with a PostScript mark, a ref of type t_mark on the operand stack.)
+ * See make_mark_estack and push_mark_estack below. The value.opproc
+ * member of the e-stack mark contains a procedure to execute in case
+ * the e-stack is stripped back beyond this point by a 'stop' or
+ * 'exit': see pop_estack in zcontrol.c for details.
+ *
+ * - Any number of refs holding information that the continuation
+ * operator needs -- i.e., the saved logical state of the thread of
+ * execution. For example, 'for' stores the procedure, the current
+ * value, the increment, and the limit here.
+ *
+ * - The continuation procedure itself -- the pseudo-operator to be
+ * called after returns from the interpreter callout. See
+ * make_op_estack and push_op_estack below.
+ *
+ * - The PostScript procedure for the interpreter to execute.
+ *
+ * The operator then returns o_push_estack, indicating to the interpreter
+ * that the operator has pushed information on the e-stack for the
+ * interpreter to process.
+ *
+ * When the interpreter finishes executing the PostScript procedure, it pops
+ * the next item off the e-stack, which is the continuation procedure. When
+ * the continuation procedure gets control, the top of the e-stack (esp)
+ * points just below the continuation procedure slot -- i.e., to the topmost
+ * saved state item. The continuation procedure normally pops all of the
+ * saved state, and the e-stack mark, and continues execution normally,
+ * eventually returning o_pop_estack to tell the interpreter that the
+ * "operator" has popped information off the e-stack. (Loop operators do
+ * something a bit more efficient than popping the information and then
+ * pushing it again: refer to the examples in zcontrol.c for details.)
+ *
+ * Continuation procedures are called just like any other operator, so they
+ * can call each other, or be called from ordinary operator procedures, as
+ * long as the e-stack is in the right state. The most complex example of
+ * this is probably the Type 1 character rendering code in zchar1.c, where
+ * continuation procedures either call each other directly or call out to
+ * the interpreter to execute optional PostScript procedures like CDevProc.
+ */
+
+/* Macro for marking the execution stack */
+#define make_mark_estack(ep, es_idx, proc)\
+ make_tasv(ep, t_null, a_executable, es_idx, opproc, proc)
+#define push_mark_estack(es_idx, proc)\
+ (++esp, make_mark_estack(esp, es_idx, proc))
+#define r_is_estack_mark(ep)\
+ r_has_type_attrs(ep, t_null, a_executable)
+#define estack_mark_index(ep) r_size(ep)
+#define set_estack_mark_index(ep, es_idx) r_set_size(ep, es_idx)
+
+/* Macro for pushing an operator on the execution stack */
+/* to represent a continuation procedure */
+#define make_op_estack(ep, proc)\
+ make_oper(ep, 0, proc)
+#define push_op_estack(proc)\
+ (++esp, make_op_estack(esp, proc))
+
+/* Macro to ensure enough room on the execution stack */
+#define check_estack(n)\
+ if ( esp > estop - (n) )\
+ { int es_code_ = ref_stack_extend(&e_stack, n);\
+ if ( es_code_ < 0 ) return es_code_;\
+ }
+
+/* Macro to ensure enough entries on the execution stack */
+#define check_esp(n)\
+ if ( esp < esbot + ((n) - 1) )\
+ { e_stack.requested = (n); return_error(gs_error_ExecStackUnderflow); }
+
+/* Define the various kinds of execution stack marks. */
+#define es_other 0 /* internal use */
+#define es_show 1 /* show operators */
+#define es_for 2 /* iteration operators */
+#define es_stopped 3 /* stopped operator */
+
+/*
+ * Pop a given number of elements off the execution stack,
+ * executing cleanup procedures as necessary.
+ */
+void pop_estack(i_ctx_t *, uint);
+
+/*
+ * The execution stack is implemented as a linked list of blocks;
+ * operators that can push or pop an unbounded number of values, or that
+ * access the entire e-stack, must take this into account. These are:
+ * exit .stop .instopped countexecstack execstack currentfile
+ * .execn
+ * pop_estack(exit, stop, error recovery)
+ * gs_show_find(all the show operators)
+ * In addition, for e-stack entries created by control operators, we must
+ * ensure that the mark and its data are never separated. We do this
+ * by ensuring that when splitting the top block, at least N items
+ * are kept in the new top block above the bottommost retained mark,
+ * where N is the largest number of data items associated with a mark.
+ * Finally, in order to avoid specific checks for underflowing a block,
+ * we put a guard entry at the bottom of each block except the top one
+ * that contains a procedure that returns an internal "exec stack block
+ * underflow" error.
+ */
+
+#endif /* estack_INCLUDED */
diff --git a/psi/files.h b/psi/files.h
new file mode 100644
index 000000000..8c39ac165
--- /dev/null
+++ b/psi/files.h
@@ -0,0 +1,152 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Definitions for interpreter support for file objects */
+/* Requires stream.h */
+
+#ifndef files_INCLUDED
+# define files_INCLUDED
+
+/*
+ * File objects store a pointer to a stream in value.pfile.
+ * A file object is valid if its "size" matches the read_id or write_id
+ * (as appropriate) in the stream it points to. This arrangement
+ * allows us to detect closed files reliably, while allowing us to
+ * reuse closed streams for new files.
+ */
+#define fptr(pref) (pref)->value.pfile
+#define make_file(pref,a,id,s)\
+ make_tasv(pref,t_file,a,id,pfile,s)
+
+/* The stdxxx files. We have to access them through procedures, */
+/* because they might have to be opened when referenced. */
+int zget_stdin(i_ctx_t *, stream **);
+int zget_stdout(i_ctx_t *, stream **);
+int zget_stderr(i_ctx_t *, stream **);
+/* Test whether a stream is stdin. */
+bool zis_stdin(const stream *);
+
+/* Define access to the stdio refs for operators. */
+#define ref_stdio (i_ctx_p->stdio)
+#define ref_stdin ref_stdio[0]
+#define ref_stdout ref_stdio[1]
+#define ref_stderr ref_stdio[2]
+/* An invalid (closed) file. */
+#define avm_invalid_file_entry avm_foreign
+/* Make an invalid file object. */
+void make_invalid_file(i_ctx_t *,ref *);
+
+/*
+ * If a file is open for both reading and writing, its read_id, write_id,
+ * and stream procedures and modes reflect the current mode of use;
+ * an id check failure will switch it to the other mode.
+ */
+int file_switch_to_read(const ref *);
+
+#define check_read_file(ctx, svar,op)\
+ BEGIN\
+ check_read_type(*(op), t_file);\
+ check_read_known_file(ctx, svar, op, return);\
+ END
+#define check_read_known_file(ctx,svar,op,error_return)\
+ check_read_known_file_else(svar, op, error_return, svar = (ctx->invalid_file_stream))
+#define check_read_known_file_else(svar,op,error_return,invalid_action)\
+ BEGIN\
+ svar = fptr(op);\
+ if (svar->read_id != r_size(op)) {\
+ if (svar->read_id == 0 && svar->write_id == r_size(op)) {\
+ int fcode = file_switch_to_read(op);\
+\
+ if (fcode < 0)\
+ error_return(fcode);\
+ } else {\
+ invalid_action; /* closed or reopened file */\
+ }\
+ }\
+ END
+int file_switch_to_write(const ref *);
+
+#define check_write_file(svar,op)\
+ BEGIN\
+ check_write_type(*(op), t_file);\
+ check_write_known_file(svar, op, return);\
+ END
+#define check_write_known_file(svar,op,error_return)\
+ BEGIN\
+ svar = fptr(op);\
+ if ( svar->write_id != r_size(op) )\
+ { int fcode = file_switch_to_write(op);\
+ if ( fcode < 0 ) error_return(fcode);\
+ }\
+ END
+
+/* Data exported by zfile.c. */
+ /* for zfilter.c and ziodev.c */
+extern const uint file_default_buffer_size;
+
+#ifndef gs_file_path_ptr_DEFINED
+# define gs_file_path_ptr_DEFINED
+typedef struct gs_file_path_s *gs_file_path_ptr;
+#endif
+
+/* Procedures exported by zfile.c. */
+ /* for imainarg.c */
+FILE *lib_fopen(const gs_file_path_ptr pfpath, const gs_memory_t *mem, const char *);
+
+ /* for imain.c */
+int
+lib_file_open(gs_file_path_ptr, const gs_memory_t *, i_ctx_t *,
+ const char *, uint, char *, int, uint *, ref *pfile);
+
+ /* for imain.c */
+#ifndef gs_ref_memory_DEFINED
+# define gs_ref_memory_DEFINED
+typedef struct gs_ref_memory_s gs_ref_memory_t;
+#endif
+int file_read_string(const byte *, uint, ref *, gs_ref_memory_t *);
+
+ /* for os_open in ziodev.c */
+#ifdef iodev_proc_fopen /* in gxiodev.h */
+int file_open_stream(const char *, uint, const char *, uint, stream **,
+ gx_io_device *, iodev_proc_fopen_t, gs_memory_t *);
+#endif
+
+ /* for zfilter.c */
+int filter_open(const char *, uint, ref *, const stream_procs *,
+ const stream_template *, const stream_state *,
+ gs_memory_t *);
+
+ /* for zfileio.c */
+void make_stream_file(ref *, stream *, const char *);
+
+ /* for ziodev.c */
+int file_close_file(stream *);
+
+ /* for gsmain.c, interp.c */
+int file_close(ref *);
+
+ /* for zfproc.c, ziodev.c */
+stream *file_alloc_stream(gs_memory_t *, client_name_t);
+
+/* Procedures exported by zfileio.c. */
+ /* for ziodev.c */
+int zreadline_from(stream *s, gs_string *buf, gs_memory_t *bufmem,
+ uint *pcount, bool *pin_eol);
+
+/* Procedures exported by zfileio.c. */
+ /* for zfile.c */
+int zfilelineedit(i_ctx_t *i_ctx_p);
+
+#endif /* files_INCLUDED */
diff --git a/psi/ghost.h b/psi/ghost.h
new file mode 100644
index 000000000..3b0f88a4a
--- /dev/null
+++ b/psi/ghost.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Common definitions for interpreter */
+
+#ifndef ghost_INCLUDED
+# define ghost_INCLUDED
+
+#include "gx.h"
+#include "iref.h"
+
+#endif /* ghost_INCLUDED */
diff --git a/psi/gs.c b/psi/gs.c
new file mode 100644
index 000000000..dde82cd12
--- /dev/null
+++ b/psi/gs.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* 'main' program for Ghostscript */
+#include "ghost.h"
+#include "imain.h"
+#include "imainarg.h"
+#include "iapi.h"
+#include "iminst.h"
+#include "ierrors.h"
+#include "gsmalloc.h"
+#include "locale_.h"
+
+#ifdef __GNUC__
+# if (__GNUC__ == 2 && __GNUC_MINOR__ == 96)
+# define NEED_COMMIT_STACK 1
+# endif
+#endif
+
+/* Define an optional array of strings for testing. */
+/*#define RUN_STRINGS */
+#ifdef RUN_STRINGS
+static const char *run_strings[] =
+{
+ "2 vmreclaim /SAVE save def 2 vmreclaim",
+ "(saved\n) print flush",
+ "SAVE restore (restored\n) print flush 2 vmreclaim",
+ "(done\n) print flush quit",
+ 0
+};
+
+#endif
+
+#ifdef NEED_COMMIT_STACK
+/*
+ * It is well known that GCC 2.96 for x86 sometimes forgets to adjust $esp
+ * and leaves automatic variables at small distance below the stack pointer.
+ * Apparently, when the access to the automatic variable causes a page fault
+ * Linux sends a SEGV signal if the access happens below the stack pointer.
+ * Pre-loading the stack pages resolves the problem.
+ */
+static void
+commit_stack_pages( void )
+{
+ char buf[65536]; /* In most cases GS lives in 64K stack */
+ int i;
+ for ( i = 0; i < sizeof(buf) - 1; i += 1024)
+ buf[i] = 0;
+}
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ int exit_status, code;
+ gs_main_instance *minst;
+ gs_memory_t *mem;
+
+#ifdef NEED_COMMIT_STACK /* hack for bug in gcc 2.96 */
+ commit_stack_pages();
+#endif
+ exit_status = 0;
+
+ /*
+ * Call setlocale(LC_CTYPE), so that we can convert PDF passwords
+ * from the locale character set to UTF-8 if necessary. Note that
+ * we only do this when running as a standalone application -- we
+ * can't use setlocale at all if ghostscript is built as a library,
+ * because it would affect the rest of the program. Applications
+ * that use ghostscript as a library are responsible for setting
+ * the locale themselves.
+ *
+ * For now, we ignore the return value of setlocale, since there's
+ * not much we can do here if it fails. It might be nice to set
+ * a flag instead, so we could warn the user if they later enter
+ * a non-ASCII PDF password that doesn't work.
+ */
+ (void)setlocale(LC_CTYPE, "");
+ mem = gs_malloc_init();
+ minst = gs_main_alloc_instance(mem);
+ code = (minst == NULL ? gs_error_Fatal : 0);
+ if (code >= 0)
+ code = gs_main_init_with_args(minst, argc, argv);
+
+#ifdef RUN_STRINGS
+ { /* Run a list of strings (for testing). */
+ const char **pstr = run_strings;
+
+ for (; *pstr; ++pstr) {
+ int exit_code;
+ ref error_object;
+ int code;
+
+ fprintf(stdout, "{%s} =>\n", *pstr);
+ fflush(stdout);
+ code = gs_main_run_string(minst, *pstr, 0,
+ &exit_code, &error_object);
+ zflush(minst->i_ctx_p);
+ fprintf(stdout, " => code = %d\n", code);
+ fflush(stdout);
+ if (code < 0) {
+ gs_to_exit(1);
+ return 1;
+ }
+ }
+ }
+#endif
+
+ if (code >= 0)
+ code = gs_main_run_start(minst);
+
+ exit_status = 0;
+ switch (code) {
+ case 0:
+ case gs_error_Info:
+ case gs_error_Quit:
+ break;
+ case gs_error_Fatal:
+ exit_status = 1;
+ break;
+ default:
+ exit_status = 255;
+ }
+
+ if (minst)
+ gs_to_exit_with_code(minst->heap, exit_status, code);
+ gs_malloc_release(mem);
+
+ switch (exit_status) {
+ case 0:
+ exit_status = exit_OK;
+ break;
+ case 1:
+ exit_status = exit_FAILED;
+ break;
+ }
+ return exit_status;
+}
diff --git a/psi/gsdll.c b/psi/gsdll.c
new file mode 100644
index 000000000..0774172d8
--- /dev/null
+++ b/psi/gsdll.c
@@ -0,0 +1,205 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Dynamic Link Library interface for OS/2 and MS-Windows Ghostscript */
+/* front end to gs.c */
+
+/* This has been reimplemented to call the new DLL interface in iapi.h */
+
+#ifdef _Windows
+#include <windows.h>
+#endif
+#ifdef __OS2__
+#define INCL_DOS
+#define INCL_WIN
+#include <os2.h>
+#endif
+
+#include "stdpre.h"
+#include "iapi.h" /* Ghostscript interpreter public interface */
+#include "string_.h"
+#include "ierrors.h"
+#include "gscdefs.h"
+#include "gstypes.h"
+#include "iref.h"
+#include "iminst.h"
+#include "imain.h"
+
+#include "gsdll.h" /* old DLL public interface */
+
+/* MacGSView still requires that hwnd be exported
+ through the old dll interface. We do that here,
+ but expect to remove it when that client has been
+ ported to the gsapi interface. */
+#ifdef __MACOS__
+extern HWND hwndtext;
+#endif
+
+/****** SINGLE-INSTANCE HACK ******/
+/* GLOBAL WARNING */
+GSDLL_CALLBACK pgsdll_callback = NULL; /* callback for messages and stdio to caller */
+
+static gs_main_instance *pgs_minst = NULL;
+
+/****** SINGLE-INSTANCE HACK ******/
+
+/* local functions */
+static int GSDLLCALL gsdll_old_stdin(void *caller_handle, char *buf, int len);
+static int GSDLLCALL gsdll_old_stdout(void *caller_handle, const char *str, int len);
+static int GSDLLCALL gsdll_old_stderr(void *caller_handle, const char *str, int len);
+static int GSDLLCALL gsdll_old_poll(void *caller_handle);
+
+/* ---------- DLL exported functions ---------- */
+
+/* arguments are:
+ * 1. callback function for stdio and for notification of
+ * sync_output, output_page and resize events
+ * 2. window handle, used as parent. Use NULL if you have no window.
+ * 3. argc
+ * 4. argv
+ */
+int GSDLLEXPORT GSDLLAPI
+gsdll_init_with_encoding(GSDLL_CALLBACK callback, HWND hwnd, int argc, char * argv[], int encoding)
+{
+ int code;
+
+ if ((code = gsapi_new_instance(&pgs_minst, (void *)1)) < 0)
+ return -1;
+
+ gsapi_set_stdio(pgs_minst,
+ gsdll_old_stdin, gsdll_old_stdout, gsdll_old_stderr);
+ gsapi_set_poll(pgs_minst, gsdll_old_poll);
+ /* ignore hwnd */
+
+/* rest of MacGSView compatibilty hack */
+#ifdef __MACOS__
+ hwndtext=hwnd;
+#endif
+
+/****** SINGLE-INSTANCE HACK ******/
+ pgsdll_callback = callback;
+/****** SINGLE-INSTANCE HACK ******/
+
+ code = gsapi_set_arg_encoding(pgs_minst, encoding);
+ if (code >= 0)
+ code = gsapi_init_with_args(pgs_minst, argc, argv);
+ if (code == gs_error_Quit) {
+ gsapi_exit(pgs_minst);
+ return GSDLL_INIT_QUIT;
+ }
+ return code;
+}
+
+int GSDLLEXPORT GSDLLAPI
+gsdll_init(GSDLL_CALLBACK callback, HWND hwnd, int argc, char * argv[])
+{
+ return gsdll_init_with_encoding(callback, hwnd, argc, argv, GS_ARG_ENCODING_LOCAL);
+}
+
+#ifdef __WIN32__
+int GSDLLEXPORT GSDLLAPI
+gsdll_initW(GSDLL_CALLBACK callback, HWND hwnd, int argc, wchar_t * argv[])
+{
+ return gsdll_init_with_encoding(callback, hwnd, argc, argv, GS_ARG_ENCODING_UTF16LE);
+}
+int GSDLLEXPORT GSDLLAPI
+gsdll_initA(GSDLL_CALLBACK callback, HWND hwnd, int argc, char * argv[])
+{
+ return gsdll_init_with_encoding(callback, hwnd, argc, argv, GS_ARG_ENCODING_LOCAL);
+}
+#endif
+
+/* if return value < 0, then error occured and caller should call */
+/* gsdll_exit, then unload library */
+int GSDLLEXPORT GSDLLAPI
+gsdll_execute_begin(void)
+{
+ int exit_code;
+ return gsapi_run_string_begin(pgs_minst, 0, &exit_code);
+}
+
+/* if return value < 0, then error occured and caller should call */
+/* gsdll_execute_end, then gsdll_exit, then unload library */
+int GSDLLEXPORT GSDLLAPI
+gsdll_execute_cont(const char * str, int len)
+{
+ int exit_code;
+ int code = gsapi_run_string_continue(pgs_minst, str, len,
+ 0, &exit_code);
+ if (code == gs_error_NeedInput)
+ code = 0; /* this is not an error */
+ return code;
+}
+
+/* if return value < 0, then error occured and caller should call */
+/* gsdll_exit, then unload library */
+int GSDLLEXPORT GSDLLAPI
+gsdll_execute_end(void)
+{
+ int exit_code;
+ return gsapi_run_string_end(pgs_minst, 0, &exit_code);
+}
+
+int GSDLLEXPORT GSDLLAPI
+gsdll_exit(void)
+{
+ int code = gsapi_exit(pgs_minst);
+
+ gsapi_delete_instance(pgs_minst);
+ return code;
+}
+
+/* Return revision numbers and strings of Ghostscript. */
+/* Used for determining if wrong GSDLL loaded. */
+/* This may be called before any other function. */
+int GSDLLEXPORT GSDLLAPI
+gsdll_revision(const char ** product, const char ** copyright,
+ long * revision, long * revisiondate)
+{
+ if (product)
+ *product = gs_product;
+ if (copyright)
+ *copyright = gs_copyright;
+ if (revision)
+ *revision = gs_revision;
+ if (revisiondate)
+ *revisiondate = gs_revisiondate;
+ return 0;
+}
+
+static int GSDLLCALL
+gsdll_old_stdin(void *caller_handle, char *buf, int len)
+{
+ return (*pgsdll_callback)(GSDLL_STDIN, buf, len);
+}
+static int GSDLLCALL
+gsdll_old_stdout(void *caller_handle, const char *str, int len)
+{
+ return (*pgsdll_callback)(GSDLL_STDOUT, (char *)str, len);
+}
+
+static int GSDLLCALL
+gsdll_old_stderr(void *caller_handle, const char *str, int len)
+{
+ return (*pgsdll_callback)(GSDLL_STDOUT, (char *)str, len);
+}
+
+static int GSDLLCALL
+gsdll_old_poll(void *caller_handle)
+{
+ return (*pgsdll_callback)(GSDLL_POLL, NULL, 0);
+}
+
+/* end gsdll.c */
diff --git a/psi/gsdll2.def b/psi/gsdll2.def
new file mode 100644
index 000000000..b6e198ef0
--- /dev/null
+++ b/psi/gsdll2.def
@@ -0,0 +1,21 @@
+LIBRARY GSDLL2 INITINSTANCE TERMINSTANCE
+DESCRIPTION 'Ghostscript DLL'
+DATA MULTIPLE NONSHARED
+EXPORTS
+ gsapi_revision
+ gsapi_new_instance
+ gsapi_delete_instance
+ gsapi_init_with_args
+ gsapi_run_string_begin
+ gsapi_run_string_continue
+ gsapi_run_string_end
+ gsapi_run_string_with_length
+ gsapi_run_string
+ gsapi_run_file
+ gsapi_exit
+ gsapi_set_stdio
+ gsapi_set_poll
+ gsapi_set_display_callback
+ gsapi_set_arg_encoding
+ gsapi_set_default_device_list
+ gsapi_get_default_device_list
diff --git a/psi/gsdll2.rc b/psi/gsdll2.rc
new file mode 100644
index 000000000..6c2ed03a5
--- /dev/null
+++ b/psi/gsdll2.rc
@@ -0,0 +1,18 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Resources for gsdll2.dll, Ghostscript DLL for OS/2 */
+
+ICON 1 gsos2.ico
diff --git a/psi/gsdll32.def b/psi/gsdll32.def
new file mode 100644
index 000000000..e43e9e301
--- /dev/null
+++ b/psi/gsdll32.def
@@ -0,0 +1,39 @@
+; 'Ghostscript Interpreter DLL'
+LIBRARY GSDLL32
+EXPORTS
+ DllEntryPoint
+ DllMain
+ gsdll_revision @1
+ gsdll_init @2
+ gsdll_execute_begin @3
+ gsdll_execute_cont @4
+ gsdll_execute_end @5
+ gsdll_exit @6
+ gsdll_lock_device @7
+ gsdll_copy_dib @8
+ gsdll_copy_palette @9
+ gsdll_draw @10
+ gsdll_get_bitmap_row @11
+ gsdll_init_with_encoding
+ gsdll_initA
+ gsdll_initW
+ gsapi_revision
+ gsapi_new_instance
+ gsapi_delete_instance
+ gsapi_init_with_args
+ gsapi_run_string_begin
+ gsapi_run_string_continue
+ gsapi_run_string_end
+ gsapi_run_string_with_length
+ gsapi_run_string
+ gsapi_run_file
+ gsapi_run_fileA
+ gsapi_run_fileW
+ gsapi_exit
+ gsapi_set_stdio
+ gsapi_set_poll
+ gsapi_set_display_callback
+ gsapi_set_visual_tracer
+ gsapi_set_arg_encoding
+ gsapi_set_default_device_list
+ gsapi_get_default_device_list
diff --git a/psi/gsdll32.rc b/psi/gsdll32.rc
new file mode 100644
index 000000000..9546b2025
--- /dev/null
+++ b/psi/gsdll32.rc
@@ -0,0 +1,50 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+#include <windows.h>
+#include "gp_mswin.h"
+
+#ifndef gstext_ico
+#define gstext_ico gswin.ico
+#endif
+#ifndef gsgraph_ico
+#define gsgraph_ico gswin.ico
+#endif
+
+GSTEXT_ICON ICON gstext_ico
+GSIMAGE_ICON ICON gsgraph_ico
+
+#ifndef DS_3DLOOK
+#define DS_3DLOOK 0x0004L /* for Windows 95 look */
+#endif
+
+SpoolDlgBox DIALOG 32, 40, 110, 63
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_3DLOOK
+CAPTION "Select Printer Port"
+BEGIN
+ CONTROL "&OK", IDOK, "button", BS_DEFPUSHBUTTON | WS_GROUP | WS_TABSTOP | WS_CHILD, 72, 14, 32, 14
+ CONTROL "&Cancel", IDCANCEL, "button", BS_PUSHBUTTON | WS_GROUP | WS_TABSTOP | WS_CHILD, 72, 36, 32, 14
+ CONTROL "", SPOOL_PORT, "LISTBOX", LBS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_TABSTOP, 8, 8, 56, 50
+END
+
+CancelDlgBox DIALOG 32, 40, 120, 48
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE | DS_3DLOOK
+BEGIN
+ CTEXT "Printing", -1, 8, 4, 104, 8, WS_CHILD | WS_VISIBLE | WS_GROUP
+ CTEXT "", CANCEL_PCDONE, 8, 16, 104, 8, WS_CHILD | WS_VISIBLE | WS_GROUP
+ CONTROL "&Cancel", IDCANCEL, "button", BS_PUSHBUTTON | WS_GROUP | WS_TABSTOP | WS_CHILD, 44, 30, 32, 14
+END
diff --git a/psi/gsdll32metro.def b/psi/gsdll32metro.def
new file mode 100644
index 000000000..1ce1c6135
--- /dev/null
+++ b/psi/gsdll32metro.def
@@ -0,0 +1,39 @@
+; 'Ghostscript Interpreter DLL'
+LIBRARY GSDLL32METRO
+EXPORTS
+ DllEntryPoint
+ DllMain
+ gsdll_revision @1
+ gsdll_init @2
+ gsdll_execute_begin @3
+ gsdll_execute_cont @4
+ gsdll_execute_end @5
+ gsdll_exit @6
+; gsdll_lock_device @7
+; gsdll_copy_dib @8
+; gsdll_copy_palette @9
+; gsdll_draw @10
+; gsdll_get_bitmap_row @11
+ gsdll_init_with_encoding
+ gsdll_initA
+ gsdll_initW
+ gsapi_revision
+ gsapi_new_instance
+ gsapi_delete_instance
+ gsapi_init_with_args
+ gsapi_init_with_argsA
+ gsapi_init_with_argsW
+ gsapi_run_string_begin
+ gsapi_run_string_continue
+ gsapi_run_string_end
+ gsapi_run_string_with_length
+ gsapi_run_string
+ gsapi_run_file
+ gsapi_exit
+ gsapi_set_stdio
+ gsapi_set_poll
+ gsapi_set_display_callback
+ gsapi_set_visual_tracer
+ gsapi_set_arg_encoding
+ gsapi_set_default_device_list
+ gsapi_get_default_device_list
diff --git a/psi/gsdll32w.lnk b/psi/gsdll32w.lnk
new file mode 100644
index 000000000..12c60ffb6
--- /dev/null
+++ b/psi/gsdll32w.lnk
@@ -0,0 +1,11 @@
+EXPORT gsdll_revision.1
+EXPORT gsdll_init.2
+EXPORT gsdll_execute_begin.3
+EXPORT gsdll_execute_cont.4
+EXPORT gsdll_execute_end.5
+EXPORT gsdll_exit.6
+EXPORT gsdll_lock_device.7
+EXPORT gsdll_copy_dib.8
+EXPORT gsdll_copy_palette.9
+EXPORT gsdll_draw.10
+EXPORT gsdll_get_bitmap_row.11
diff --git a/psi/gsdll64.def b/psi/gsdll64.def
new file mode 100644
index 000000000..3c1c1d7d1
--- /dev/null
+++ b/psi/gsdll64.def
@@ -0,0 +1,39 @@
+; 'Ghostscript Interpreter DLL'
+LIBRARY GSDLL64
+EXPORTS
+ DllEntryPoint
+ DllMain
+ gsdll_revision @1
+ gsdll_init @2
+ gsdll_execute_begin @3
+ gsdll_execute_cont @4
+ gsdll_execute_end @5
+ gsdll_exit @6
+ gsdll_lock_device @7
+ gsdll_copy_dib @8
+ gsdll_copy_palette @9
+ gsdll_draw @10
+ gsdll_get_bitmap_row @11
+ gsdll_init_with_encoding
+ gsdll_initA
+ gsdll_initW
+ gsapi_revision
+ gsapi_new_instance
+ gsapi_delete_instance
+ gsapi_init_with_args
+ gsapi_init_with_argsA
+ gsapi_init_with_argsW
+ gsapi_run_string_begin
+ gsapi_run_string_continue
+ gsapi_run_string_end
+ gsapi_run_string_with_length
+ gsapi_run_string
+ gsapi_run_file
+ gsapi_exit
+ gsapi_set_stdio
+ gsapi_set_poll
+ gsapi_set_display_callback
+ gsapi_set_visual_tracer
+ gsapi_set_arg_encoding
+ gsapi_set_default_device_list
+ gsapi_get_default_device_list
diff --git a/psi/gsdll64metro.def b/psi/gsdll64metro.def
new file mode 100644
index 000000000..b34d546e8
--- /dev/null
+++ b/psi/gsdll64metro.def
@@ -0,0 +1,39 @@
+; 'Ghostscript Interpreter DLL'
+LIBRARY GSDLL64METRO
+EXPORTS
+ DllEntryPoint
+ DllMain
+ gsdll_revision @1
+ gsdll_init @2
+ gsdll_execute_begin @3
+ gsdll_execute_cont @4
+ gsdll_execute_end @5
+ gsdll_exit @6
+; gsdll_lock_device @7
+; gsdll_copy_dib @8
+; gsdll_copy_palette @9
+; gsdll_draw @10
+; gsdll_get_bitmap_row @11
+ gsdll_init_with_encoding
+ gsdll_initA
+ gsdll_initW
+ gsapi_revision
+ gsapi_new_instance
+ gsapi_delete_instance
+ gsapi_init_with_args
+ gsapi_init_with_argsA
+ gsapi_init_with_argsW
+ gsapi_run_string_begin
+ gsapi_run_string_continue
+ gsapi_run_string_end
+ gsapi_run_string_with_length
+ gsapi_run_string
+ gsapi_run_file
+ gsapi_exit
+ gsapi_set_stdio
+ gsapi_set_poll
+ gsapi_set_display_callback
+ gsapi_set_visual_tracer
+ gsapi_set_arg_encoding
+ gsapi_set_default_device_list
+ gsapi_get_default_device_list
diff --git a/psi/gsdllARM32metro.def b/psi/gsdllARM32metro.def
new file mode 100644
index 000000000..58aa56609
--- /dev/null
+++ b/psi/gsdllARM32metro.def
@@ -0,0 +1,39 @@
+; 'Ghostscript Interpreter DLL'
+LIBRARY GSDLLARM32METRO
+EXPORTS
+ DllEntryPoint
+ DllMain
+ gsdll_revision @1
+ gsdll_init @2
+ gsdll_execute_begin @3
+ gsdll_execute_cont @4
+ gsdll_execute_end @5
+ gsdll_exit @6
+; gsdll_lock_device @7
+; gsdll_copy_dib @8
+; gsdll_copy_palette @9
+; gsdll_draw @10
+; gsdll_get_bitmap_row @11
+ gsdll_init_with_encoding
+ gsdll_initA
+ gsdll_initW
+ gsapi_revision
+ gsapi_new_instance
+ gsapi_delete_instance
+ gsapi_init_with_args
+ gsapi_init_with_argsA
+ gsapi_init_with_argsW
+ gsapi_run_string_begin
+ gsapi_run_string_continue
+ gsapi_run_string_end
+ gsapi_run_string_with_length
+ gsapi_run_string
+ gsapi_run_file
+ gsapi_exit
+ gsapi_set_stdio
+ gsapi_set_poll
+ gsapi_set_display_callback
+ gsapi_set_visual_tracer
+ gsapi_set_arg_encoding
+ gsapi_set_default_device_list
+ gsapi_get_default_device_list
diff --git a/psi/gserver.c b/psi/gserver.c
new file mode 100644
index 000000000..2438aa516
--- /dev/null
+++ b/psi/gserver.c
@@ -0,0 +1,317 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Server front end for Ghostscript, replacing gs.c. */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "imemory.h" /* for iutil.h */
+#include "interp.h" /* for gs_interp_reset */
+#include "iutil.h" /* for obj_cvs */
+#include "main.h"
+#include "ostack.h"
+#include "store.h"
+#include "gspaint.h" /* for gs_erasepage */
+#include "gp.h"
+
+/*
+ * This file provides a very simple procedural interface to the Ghostscript
+ * PostScript/PDF language interpreter, with a rudimentary provision for
+ * saving and restoring state around "jobs".
+ * See below for the descriptions of individual procedures.
+ *
+ * All routines in this file return an integer value which is 0 if the
+ * routine completed successfully, non-0 if an error occurred.
+ */
+
+/* ------ Public interface ------ */
+
+/*
+ * Initialize Ghostscript. fno_stdin, fno_stdout, and fno_stderr are
+ * file handles that Ghostscript will use in place of stdin, stdout,
+ * and stderr respectively. This routine should be called once at the
+ * beginning of execution, and not after that.
+ *
+ * This routine establishes a "baseline" initial state for Ghostscript,
+ * which includes reading in the standard Ghostscript initialization files
+ * such as gs_init.ps and gs_fonts.ps. This baseline is always used as the
+ * starting state for gs_server_run_string and gs_server_run_files, unless
+ * modified as described below.
+ *
+ * This routine 'opens' the default driver.
+ */
+
+int gs_server_initialize(int fno_stdin, int fno_stdout, int fno_stderr,
+ const char *init_str);
+
+/*
+ * Execute a string containing PostScript code. The effects of this code
+ * do modify the baseline state for future calls of ...run_string and
+ * ...run_files. There are four cases of return values:
+ * value = 0: normal return.
+ * value = gs_error_Quit: the PostScript code executed a `quit'.
+ * value = gs_error_Fatal: the PostScript code encountered a fatal error.
+ * *exit_code_ptr holds the C exit code.
+ * other value: the PostScript code encountered a PostScript error
+ * while processing another error, or some other fatal
+ * PostScript error.
+ *
+ * errstr points to a string area of length errstr_max_len for reporting
+ * the PostScript object causing the error. In the case of an error return,
+ * the characters from errstr[0] through errstr[*errstr_len_ptr-1] will
+ * contain a representation of the error object. *errstr_len_ptr will not
+ * exceed errstr_max_len.
+ */
+
+int gs_server_run_string(const char *str, int *exit_code_ptr,
+ char *errstr, int errstr_max_len,
+ int *errstr_len_ptr);
+
+/*
+ * Run a sequence of files containing PostScript code. If permanent is 0,
+ * the files do not affect the baseline state; if permanent is 1, they do
+ * affect the baseline state, just like ...run_string. The returned value,
+ * exit code, and error string are the same as for gs_server_run_string.
+ *
+ * If permanent is 0, the output page buffer is cleared before running the
+ * first file (equivalent to `erasepage').
+ */
+
+int gs_server_run_files(const char **file_names, int permanent,
+ int *exit_code_ptr, char *errstr,
+ int errstr_max_len, int *errstr_len_ptr);
+
+/*
+ * Terminate Ghostscript. Ghostscript will release all memory and close
+ * all files it has opened, including the ones referenced by fno_stdin,
+ * fno_stdout, and fno_stderr.
+ *
+ * This routine 'closes' the default driver.
+ */
+
+int gs_server_terminate();
+
+/* ------ Example of use ------ */
+
+/* To run this example, change the 0 to 1 in the following line. */
+#if 0
+
+/*
+ * This example predefines the name fubar, prints out its value,
+ * and then renders the golfer art file supplied with Ghostscript.
+ */
+
+#include <fcntl.h>
+#include <sys/stat.h>
+int
+main(int argc, char *argv[])
+{
+ int code, exit_code;
+
+#define emax 50
+ char errstr[emax + 1];
+ int errlen;
+ static const char *fnames[] =
+ {"golfer.eps", 0};
+ FILE *cin = gp_fopen("stdin.tmp", "w+");
+ int sout = open("stdout.tmp", O_WRONLY | O_CREAT | O_TRUNC,
+ S_IREAD | S_IWRITE);
+ int serr = open("stderr.tmp", O_WRONLY | O_CREAT | O_TRUNC,
+ S_IREAD | S_IWRITE);
+
+ code = gs_server_initialize(fileno(cin), sout, serr,
+ "/fubar 42 def");
+ fprintf(stdout, "init: code %d\n", code);
+ if (code < 0)
+ goto x;
+ code = gs_server_run_string("fubar == flush", &exit_code,
+ errstr, emax, &errlen);
+ fprintf(stdout, "print: code %d\n", code);
+ if (code < 0)
+ goto x;
+ code = gs_server_run_files(fnames, 0, &exit_code,
+ errstr, emax, &errlen);
+ fprintf(stdout, "golfer: code %d\n", code);
+ if (code < 0)
+ goto x;
+ errlen = 0;
+ code = gs_server_run_string("fubar 0 div", &exit_code,
+ errstr, emax, &errlen);
+ errstr[errlen] = 0;
+ fprintf(stdout, "0 div: code %d object %s\n", code, errstr);
+ errlen = 0;
+ code = gs_server_run_string("xxx", &exit_code,
+ errstr, emax, &errlen);
+ errstr[errlen] = 0;
+ fprintf(stdout, "undef: code %d object %s\n", code, errstr);
+ x:code = gs_server_terminate();
+ fprintf(stdout, "end: code %d\n", code);
+ fflush(stdout);
+ close(serr);
+ close(sout);
+ fclose(cin);
+ return code;
+}
+
+#endif
+
+/* ------ Private definitions ------ */
+
+/* Forward references */
+static int job_begin(void);
+static int job_end(void);
+static void errstr_report(ref *, char *, int, int *);
+
+/* ------ Public routines ------ */
+
+/* Initialize Ghostscript. */
+
+int
+gs_server_initialize(int fno_stdin, int fno_stdout, int fno_stderr,
+ const char *init_str)
+{
+ int code, exit_code; /* discard exit_code for now */
+ int errstr_len; /* discard */
+ FILE *c_stdin, *c_stdout, *c_stderr;
+
+ /* Establish C-compatible files for stdout and stderr. */
+ c_stdin = fdopen(fno_stdin, "r");
+ if (c_stdin == NULL)
+ return -1;
+ c_stdout = fdopen(fno_stdout, "w");
+ if (c_stdout == NULL)
+ return -1;
+ c_stderr = fdopen(fno_stderr, "w");
+ if (c_stderr == NULL)
+ return -1;
+ /* Initialize the Ghostscript interpreter. */
+ if ((code = gs_init0(c_stdin, c_stdout, c_stderr, 0)) < 0 ||
+ (code = gs_init1()) < 0 ||
+ (code = gs_init2()) < 0
+ )
+ return code;
+ code = gs_server_run_string("/QUIET true def /NOPAUSE true def",
+ &exit_code,
+ (char *)0, 0, &errstr_len);
+ if (code < 0)
+ return code;
+ return (init_str == NULL ? 0 :
+ gs_server_run_string(init_str, &exit_code,
+ (char *)0, 0, &errstr_len));
+}
+
+/* Run a string. */
+
+int
+gs_server_run_string(const char *str, int *exit_code_ptr,
+ char *errstr, int errstr_max_len, int *errstr_len_ptr)
+{
+ ref error_object;
+ int code;
+
+ make_tasv(&error_object, t_string, 0, 0, bytes, 0);
+ code = gs_run_string(str, 0, exit_code_ptr, &error_object);
+ if (code < 0)
+ errstr_report(&error_object, errstr, errstr_max_len,
+ errstr_len_ptr);
+ return code;
+}
+
+/* Run files. */
+
+int
+gs_server_run_files(const char **file_names, int permanent,
+ int *exit_code_ptr, char *errstr, int errstr_max_len, int *errstr_len_ptr)
+{
+ int code = 0;
+ ref error_object;
+ const char **pfn;
+
+ if (!permanent)
+ job_begin();
+ make_tasv(&error_object, t_string, 0, 0, bytes, 0);
+ for (pfn = file_names; *pfn != NULL && code == 0; pfn++)
+ code = gs_run_file(*pfn, 0, exit_code_ptr, &error_object);
+ if (!permanent)
+ job_end();
+ if (code < 0)
+ errstr_report(&error_object, errstr, errstr_max_len,
+ errstr_len_ptr);
+ return code;
+}
+
+/* Terminate Ghostscript. */
+
+int
+gs_server_terminate()
+{
+ gs_finit(0, 0);
+ return 0;
+}
+
+/* ------ Private routines ------ */
+
+static ref job_save; /* 'save' object for baseline state */
+
+extern int zsave(os_ptr), zrestore(os_ptr);
+
+/* Start a 'job' by restoring the baseline state. */
+
+static int
+job_begin()
+{
+ int code;
+
+ /* Ghostscript doesn't provide erasepage as an operator. */
+ /* However, we can get the same effect by calling gs_erasepage. */
+ extern gs_state *igs;
+
+ if ((code = gs_erasepage(igs)) < 0)
+ return code;
+ code = zsave(osp);
+ if (code == 0)
+ job_save = *osp--;
+ return code;
+}
+
+/* End a 'job'. */
+
+static int
+job_end()
+{
+ gs_interp_reset();
+ *++osp = job_save;
+ return zrestore(osp);
+}
+
+/* Produce a printable representation of an error object. */
+
+static void
+errstr_report(ref * perror_object, char *errstr, int errstr_max_len,
+ int *errstr_len_ptr)
+{
+ int code = obj_cvs(perror_object, (byte *) errstr,
+ (uint) errstr_max_len, (uint *) errstr_len_ptr,
+ false);
+
+ if (code < 0) {
+ const char *ustr = "[unprintable]";
+ int len = min(strlen(ustr), errstr_max_len);
+
+ memcpy(errstr, ustr, len);
+ *errstr_len_ptr = len;
+ }
+}
diff --git a/psi/gsos2.def b/psi/gsos2.def
new file mode 100644
index 000000000..227a9fc1e
--- /dev/null
+++ b/psi/gsos2.def
@@ -0,0 +1,3 @@
+NAME GSOS2 WINDOWCOMPAT
+DESCRIPTION 'Ghostscript OS/2 EXE'
+STACKSIZE 131072
diff --git a/psi/gsos2.icx b/psi/gsos2.icx
new file mode 100644
index 000000000..8e02eb848
--- /dev/null
+++ b/psi/gsos2.icx
@@ -0,0 +1,233 @@
+4241280000002e000000000000004943
+1a000000080008003c0200000c000000
+1000200001000100000000ffffff4241
+28000000a60000000000000043491a00
+000010001000bc0200000c0000002000
+400001000100000000ffffff43491a00
+000010001000bc0300000c0000002000
+20000100040000000080000000800080
+8000000080800080008080808080cccc
+ccff000000ff00ffff000000ffff00ff
+00ffffffffff4241280000001e010000
+0000000043491a00000008000800bc05
+00000c00000010002000010001000000
+00ffffff43491a000000080008003c06
+00000c00000010001000010004000000
+00800000008000808000000080800080
+008080808080ccccccff000000ff00ff
+ff000000ffff00ff00ffffffffff4241
+280000004c0100000004000349431a00
+00000a000a00bc0600000c0000001400
+280001000100000000ffffff42412800
+0000c40100000004000343491a000000
+140014005c0700000c00000028005000
+01000100000000ffffff43491a000000
+14001400dc0900000c00000028002800
+01000400000000800000008000808000
+000080800080008080808080ccccccff
+000000ff00ffff000000ffff00ff00ff
+ffffffff424128000000000000000004
+000343491a0000000a000a00fc0c0000
+0c0000001400280001000100000000ff
+ffff43491a0000000a000a009c0d0000
+0c000000140014000100040000000080
+00000080008080000000808000800080
+80808080ccccccff000000ff00ffff00
+0000ffff00ff00ffffffffff07800011
+01c0000001f0000007f8111107380001
+0f30ff000fe0ff000de0fc000d60ff10
+0760f80001f87f000078e00000183f10
+0008c00000081f5a0000ca00c07f1f00
+f03fc000c0031f008001c00080011f00
+8000c00000001f000000c00000001f00
+8000e00080011f00c001fc00f8011f00
+ffe3f800fff31f00fff7f00000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+000000000000000000000000fc0003ff
+f000007fc000001f8000000f80000007
+800000078000000780000007c0000007
+f0000007e0000007c000000fc000003f
+c000003fc00001ffc0001fffe00007ff
+f80001fff80000fff000007fe000003f
+e000003fe000003fe000003fe000003f
+e000003ff000003ff800007ffc0000ff
+ff0000ffffffc0ffffffc1ff000000ff
+ffffffffffffff00000000000000fff0
+000000000000fffff000000000fff000
+0778ffffffff0000fff000000ff00007
+ffffffffffffffff00ff00000f0000ff
+ffff00000000000fff0ff0000f0000ff
+fff0fffffffff0000ff0f0000f0000ff
+fff0ffffffffff0000fff0000ff0000f
+ffff7fffffffff00000ff00000ffffff
+ffffffffffff0000000ff0000000fff0
+0000000000000000000ff000000ff000
+0000000000000000000ff00000ff0000
+000000000000000000ff000000f00000
+00000000000000007f00000000f00000
+00000000000007ffff00000000f00000
+07fffffffffffff00000000000ff0000
+0ffffffffff0000000000000000ff700
+00000ffffffff0000000000000000fff
+f00000fffff08ff00000000000000ff0
+00000fffffff00ff000000000000f800
+0007fff99fff0008f0000000000ff000
+000ffff99fff0000ff000000000f0000
+00fffffffff000000f000000000f0000
+00ff9fffff0000000f000000000f0000
+00ff9f9ff00000000f000000000f8000
+00ffff9ff00000000f000000000ff000
+000fffffff000000ff0000000000ff00
+00000ffffff70008ff00000000000ff0
+000000008fff800ff0000000000000ff
+8700000000fff00f0000000000000000
+ffffffffff0ff07f0000000000000000
+0000000000f0f0ff0000000000000000
+0000000000f0fff00000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+000000000000000000000000c07f0000
+f03f0000c00300008001000080010000
+80000000000000000000000000000000
+8000000080010000c0010000f8010000
+ffe30000fff30000fff7000000000fff
+f00000000000000fff0000000000000f
+ffff0000000007fffffff00000000fff
+99fff00000007fff99ff00000000ffff
+fff000000000ff9fff7000000000ff9f
+9f70000000000fff9ff000000000000f
+ffff70000000000008fff00000000000
+000ff000000000000000f00000000000
+0000f000000000000000000001f0003f
+007c003f003e003f007f003f01f7003f
+01e7007f01c700ff03fe00ff03fc00ff
+037801ff03680a5a03e80ff801fc0a5a
+007f0301000f800000038c0000018000
+000181000003000000000fffe00ff000
+f801f000f800f000e0003000c0001000
+c0001000800000008000000000000000
+00000000000000008000000080000000
+c0001000e0001000fc001000fffc3000
+fffc3000fff87000fffef00000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+000000000000000000000000ff00000f
+ff000000fc000001ff000000f8000000
+7f000000e00000003f000000c0000000
+1f000000c00000001f000000c0000000
+1f000000c00000001f000000c0000000
+1f000000c00000001f000000e0000000
+1f000000fc0000001f000000f8000000
+1f000000f00000001f000000f0000000
+7f000000f00000007f000000f0000003
+ff000000f000003fff000000f00003ff
+ff000000f00003ffff000c00f800003f
+ff000000fe00001fff000000fe000007
+ff000000fc000003ff000000f8000001
+ff000000f8000001ff000000f8000000
+ff000000f8000000ff000000f8000000
+ff000000f8000000ff000000f8000000
+ff000000f8000001ff000000fc000001
+ff000000fe000003ff000000ff000007
+ff000000ff800007ff000000ffe00007
+ff999999ffffe007ff999999fffffc0f
+ff999999fffffe0fff99999900000000
+ffffffffffffffffffff000000000000
+000000ffff700000000000007ffffff0
+0000000000000ff70000000000000000
+000000fff0000000000fff00000077ff
+fffffffffffff707ff00000000ff0000
+007fffffff70000000ffffff7ff00000
+00f700000ffffff700000000000000ff
+f7f0000000f00000ffffff0007ffffff
+80000000fff0000000f00000ffffff00
+fffffffffff000000ff0000000f70000
+0ffffff0007fffffffff000000f00000
+00ff700000fffffffffffffffff70000
+00f00000000fffffffffffffffffffff
+7000000000f00000000000fffff70000
+000000000000000000f0000000000ff7
+00000000000000000000000000f00000
+0000ff00000000000000000000000000
+0ff000000000f0000000000000000000
+00000007f00000000000f00000000000
+00000000000000fff00000000000f000
+000000000000000007ffff0000000000
+0000f0000000007fffffffffff000000
+000000000000f7000000ffffffffff00
+00000000000000000000ff000000ffff
+ffffff00000000000000000000000fff
+00000000ffffffffff00000000000000
+0000000fff7000000ffffff07ff00000
+000000000000000fff0000000fffffff
+007ff00000000000000000ff70000000
+ffff9ffff7008f000000000000000ff8
+0000000ffff99fffff0008f000000000
+00000f80000000ffff999ffff00000f0
+0000000000000f0000000fffffffffff
+7000000f0000000000000f0000007fff
+9ffffff00000000f0000000000000f00
+00007ff99ff9ff000000000f00000000
+00000f0000000ff9ff99ff000000000f
+0000000000000f0000000fffff9fff00
+0000000f0000000000000ff0000000ff
+ffffff70000000f000000000000000ff
+0000000fffffffff000008f000000000
+0000000ff0000000008ffffff000ff00
+0000000000000000ff000000000007ff
+ff00f00000000000000000000fff7700
+00000007ff00f0000000000000000000
+000fffffffffff70ff00f00000000000
+0000000000000000000ffff0ff0ff000
+000000000000000000000000000000f0
+ff0f0000000000000000000000000000
+0000000ff0ff00000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+00000000000000000000000000000000
+000000000000000000000000e00ff000
+f801f000f800f000e0003000c0001000
+c0001000800000008000000000000000
+00000000000000008000000080000000
+c0001000e0001000fc001000fffc3000
+fffc3000fff87000fffef0000000000f
+ffff000000000000000000000fffff00
+000000000000000000fffff000000000
+000000000fffffff000000000000000f
+ffff9fff000000000000000ffff99fff
+000000000000000fff999fff00000000
+000000fffffffff000000000000000ff
+ffffff0000000000000000ff9ffff000
+00000000000000ff9f9ff00000000000
+000000ffff9ff000000000000000000f
+ffffff0000000000000000000fffffff
+00000000000000000000fffff0000000
+00000000000000fff000000000000000
+0000000ff0000000000000000000000f
+f000000000000000000000ff00000000
+0000000000000000000000000a
diff --git a/psi/gsos2.rc b/psi/gsos2.rc
new file mode 100644
index 000000000..688d2da6e
--- /dev/null
+++ b/psi/gsos2.rc
@@ -0,0 +1,18 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Resources for gsos2.exe, Ghostscript for OS/2 */
+
+ICON 1 obj\gsos2.ico
diff --git a/psi/ialloc.c b/psi/ialloc.c
new file mode 100644
index 000000000..97087197a
--- /dev/null
+++ b/psi/ialloc.c
@@ -0,0 +1,380 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Memory allocator for Ghostscript interpreter */
+#include "gx.h"
+#include "memory_.h"
+#include "ierrors.h"
+#include "gsstruct.h"
+#include "iref.h" /* must precede iastate.h */
+#include "iastate.h"
+#include "igc.h" /* for gs_gc_reclaim */
+#include "ipacked.h"
+#include "iutil.h"
+#include "ivmspace.h"
+#include "store.h"
+
+/*
+ * Define global and local instances.
+ */
+public_st_gs_dual_memory();
+
+/* Initialize the allocator */
+int
+ialloc_init(gs_dual_memory_t *dmem, gs_memory_t * rmem, uint chunk_size,
+ bool level2)
+{
+ gs_ref_memory_t *ilmem = ialloc_alloc_state(rmem, chunk_size);
+ gs_ref_memory_t *ilmem_stable = ialloc_alloc_state(rmem, chunk_size);
+ gs_ref_memory_t *igmem = 0;
+ gs_ref_memory_t *igmem_stable = 0;
+ gs_ref_memory_t *ismem = ialloc_alloc_state(rmem, chunk_size);
+ int i;
+
+ if (ilmem == 0 || ilmem_stable == 0 || ismem == 0)
+ goto fail;
+ ilmem->stable_memory = (gs_memory_t *)ilmem_stable;
+ if (level2) {
+ igmem = ialloc_alloc_state(rmem, chunk_size);
+ igmem_stable = ialloc_alloc_state(rmem, chunk_size);
+ if (igmem == 0 || igmem_stable == 0)
+ goto fail;
+ igmem->stable_memory = (gs_memory_t *)igmem_stable;
+ } else
+ igmem = ilmem, igmem_stable = ilmem_stable;
+ for (i = 0; i < countof(dmem->spaces_indexed); i++)
+ dmem->spaces_indexed[i] = 0;
+ dmem->space_local = ilmem;
+ dmem->space_global = igmem;
+ dmem->space_system = ismem;
+ dmem->spaces.vm_reclaim = gs_gc_reclaim; /* real GC */
+ dmem->reclaim = 0; /* no interpreter GC yet */
+ /* Level 1 systems have only local VM. */
+ igmem->space = avm_global;
+ igmem_stable->space = avm_global;
+ ilmem->space = avm_local; /* overrides if ilmem == igmem */
+ ilmem_stable->space = avm_local; /* ditto */
+ ismem->space = avm_system;
+# if IGC_PTR_STABILITY_CHECK
+ igmem->space_id = (i_vm_global << 1) + 1;
+ igmem_stable->space_id = i_vm_global << 1;
+ ilmem->space_id = (i_vm_local << 1) + 1; /* overrides if ilmem == igmem */
+ ilmem_stable->space_id = i_vm_local << 1; /* ditto */
+ ismem->space_id = (i_vm_system << 1);
+# endif
+ ialloc_set_space(dmem, avm_global);
+ return 0;
+ fail:
+ ialloc_free_state(igmem_stable);
+ ialloc_free_state(igmem);
+ ialloc_free_state(ismem);
+ ialloc_free_state(ilmem_stable);
+ ialloc_free_state(ilmem);
+ return_error(gs_error_VMerror);
+}
+
+/* ================ Local/global VM ================ */
+
+/* Get the space attribute of an allocator */
+uint
+imemory_space(const gs_ref_memory_t * iimem)
+{
+ return iimem->space;
+}
+
+/* Select the allocation space. */
+void
+ialloc_set_space(gs_dual_memory_t * dmem, uint space)
+{
+ gs_ref_memory_t *mem = dmem->spaces_indexed[space >> r_space_shift];
+
+ dmem->current = mem;
+ dmem->current_space = mem->space;
+}
+
+/* Get the l_new attribute of a current allocator. */
+/* (A copy of the new_mask in the gs_dual_memory_t.) */
+uint
+imemory_new_mask(const gs_ref_memory_t *imem)
+{
+ return imem->new_mask;
+}
+
+/* Get the save level of an allocator. */
+int
+imemory_save_level(const gs_ref_memory_t *imem)
+{
+ return imem->save_level;
+}
+
+/* Reset the requests. */
+void
+ialloc_reset_requested(gs_dual_memory_t * dmem)
+{
+ dmem->space_system->gc_status.requested = 0;
+ dmem->space_global->gc_status.requested = 0;
+ dmem->space_local->gc_status.requested = 0;
+}
+
+/* ================ Refs ================ */
+
+#ifdef DEBUG
+static int
+ialloc_trace_space(const gs_ref_memory_t *imem)
+{
+ return imem->space + (imem->stable_memory == (const gs_memory_t *)imem);
+}
+#endif
+
+/* Register a ref root. */
+int
+gs_register_ref_root(gs_memory_t *mem, gs_gc_root_t *root,
+ void **pp, client_name_t cname)
+{
+ return gs_register_root(mem, root, ptr_ref_type, pp, cname);
+}
+
+/*
+ * As noted in iastate.h, every run of refs has an extra ref at the end
+ * to hold relocation information for the garbage collector;
+ * since sizeof(ref) % obj_align_mod == 0, we never need to
+ * allocate any additional padding space at the end of the block.
+ */
+
+/* Allocate an array of refs. */
+int
+gs_alloc_ref_array(gs_ref_memory_t * mem, ref * parr, uint attrs,
+ uint num_refs, client_name_t cname)
+{
+ ref *obj;
+ int i;
+
+ /* If we're allocating a run of refs already, */
+ /* and we aren't about to overflow the maximum run length, use it. */
+ if (mem->cc.has_refs == true && mem->cc.rtop == mem->cc.cbot &&
+ num_refs < (mem->cc.ctop - mem->cc.cbot) / sizeof(ref) &&
+ mem->cc.rtop - (byte *) mem->cc.rcur + num_refs * sizeof(ref) <
+ max_size_st_refs
+ ) {
+ ref *end;
+
+ obj = (ref *) mem->cc.rtop - 1; /* back up over last ref */
+ if_debug4m('A', (const gs_memory_t *)mem, "[a%d:+$ ]%s(%u) = 0x%lx\n",
+ ialloc_trace_space(mem), client_name_string(cname),
+ num_refs, (ulong) obj);
+ mem->cc.rcur[-1].o_size += num_refs * sizeof(ref);
+ end = (ref *) (mem->cc.rtop = mem->cc.cbot +=
+ num_refs * sizeof(ref));
+ make_mark(end - 1);
+ } else {
+ /*
+ * Allocate a new run. We have to distinguish 3 cases:
+ * - Same chunk: pcc unchanged, end == cc.cbot.
+ * - Large chunk: pcc unchanged, end != cc.cbot.
+ * - New chunk: pcc changed.
+ */
+ chunk_t *pcc = mem->pcc;
+ ref *end;
+ alloc_change_t *cp = 0;
+ int code = 0;
+
+ if ((gs_memory_t *)mem != mem->stable_memory) {
+ code = alloc_save_change_alloc(mem, "gs_alloc_ref_array", &cp);
+ if (code < 0)
+ return code;
+ }
+ obj = gs_alloc_struct_array((gs_memory_t *) mem, num_refs + 1,
+ ref, &st_refs, cname);
+ if (obj == 0)
+ return_error(gs_error_VMerror);
+ /* Set the terminating ref now. */
+ end = (ref *) obj + num_refs;
+ make_mark(end);
+ /* Set has_refs in the chunk. */
+ if (mem->pcc != pcc || mem->cc.cbot == (byte *) (end + 1)) {
+ /* Ordinary chunk. */
+ mem->cc.rcur = (obj_header_t *) obj;
+ mem->cc.rtop = (byte *) (end + 1);
+ mem->cc.has_refs = true;
+ } else {
+ /* Large chunk. */
+ /* This happens only for very large arrays, */
+ /* so it doesn't need to be cheap. */
+ chunk_locator_t cl;
+
+ cl.memory = mem;
+ cl.cp = mem->clast;
+ chunk_locate_ptr(obj, &cl);
+ cl.cp->has_refs = true;
+ }
+ if (cp) {
+ mem->changes = cp;
+ cp->where = (ref_packed *)obj;
+ }
+ }
+ for (i = 0; i < num_refs; i++) {
+ make_null(&(obj[i]));
+ }
+ make_array(parr, attrs | mem->space, num_refs, obj);
+ return 0;
+}
+
+/* Resize an array of refs. Currently this is only implemented */
+/* for shrinking, not for growing. */
+int
+gs_resize_ref_array(gs_ref_memory_t * mem, ref * parr,
+ uint new_num_refs, client_name_t cname)
+{
+ uint old_num_refs = r_size(parr);
+ uint diff;
+ ref *obj = parr->value.refs;
+
+ if (new_num_refs > old_num_refs || !r_has_type(parr, t_array))
+ return_error(gs_error_Fatal);
+ diff = old_num_refs - new_num_refs;
+ /* Check for LIFO. See gs_free_ref_array for more details. */
+ if (mem->cc.rtop == mem->cc.cbot &&
+ (byte *) (obj + (old_num_refs + 1)) == mem->cc.rtop
+ ) {
+ /* Shorten the refs object. */
+ ref *end = (ref *) (mem->cc.cbot = mem->cc.rtop -=
+ diff * sizeof(ref));
+
+ if_debug4m('A', (const gs_memory_t *)mem, "[a%d:<$ ]%s(%u) 0x%lx\n",
+ ialloc_trace_space(mem), client_name_string(cname), diff,
+ (ulong) obj);
+ mem->cc.rcur[-1].o_size -= diff * sizeof(ref);
+ make_mark(end - 1);
+ } else {
+ /* Punt. */
+ if_debug4m('A', (const gs_memory_t *)mem, "[a%d:<$#]%s(%u) 0x%lx\n",
+ ialloc_trace_space(mem), client_name_string(cname), diff,
+ (ulong) obj);
+ mem->lost.refs += diff * sizeof(ref);
+ }
+ r_set_size(parr, new_num_refs);
+ return 0;
+}
+
+/* Deallocate an array of refs. Only do this if LIFO, or if */
+/* the array occupies an entire chunk by itself. */
+void
+gs_free_ref_array(gs_ref_memory_t * mem, ref * parr, client_name_t cname)
+{
+ uint num_refs = r_size(parr);
+ ref *obj = parr->value.refs;
+
+ /*
+ * Compute the storage size of the array, and check for LIFO
+ * freeing or a separate chunk. Note that the array might be packed;
+ * for the moment, if it's anything but a t_array, punt.
+ * The +1s are for the extra ref for the GC.
+ */
+ if (!r_has_type(parr, t_array))
+ DO_NOTHING; /* don't look for special cases */
+ else if (mem->cc.rtop == mem->cc.cbot &&
+ (byte *) (obj + (num_refs + 1)) == mem->cc.rtop
+ ) {
+ if ((obj_header_t *) obj == mem->cc.rcur) {
+ /* Deallocate the entire refs object. */
+ if ((gs_memory_t *)mem != mem->stable_memory)
+ alloc_save_remove(mem, (ref_packed *)obj, "gs_free_ref_array");
+ gs_free_object((gs_memory_t *) mem, obj, cname);
+ mem->cc.rcur = 0;
+ mem->cc.rtop = 0;
+ } else {
+ /* Deallocate it at the end of the refs object. */
+ if_debug4m('A', (const gs_memory_t *)mem, "[a%d:-$ ]%s(%u) 0x%lx\n",
+ ialloc_trace_space(mem), client_name_string(cname),
+ num_refs, (ulong) obj);
+ mem->cc.rcur[-1].o_size -= num_refs * sizeof(ref);
+ mem->cc.rtop = mem->cc.cbot = (byte *) (obj + 1);
+ make_mark(obj);
+ }
+ return;
+ } else if (num_refs >= (mem->large_size / arch_sizeof_ref - 1)) {
+ /* See if this array has a chunk all to itself. */
+ /* We only make this check when freeing very large objects, */
+ /* so it doesn't need to be cheap. */
+ chunk_locator_t cl;
+
+ cl.memory = mem;
+ cl.cp = mem->clast;
+ if (chunk_locate_ptr(obj, &cl) &&
+ obj == (ref *) ((obj_header_t *) (cl.cp->cbase) + 1) &&
+ (byte *) (obj + (num_refs + 1)) == cl.cp->cend
+ ) {
+ /* Free the chunk. */
+ if_debug4m('a', (const gs_memory_t *)mem, "[a%d:-$L]%s(%u) 0x%lx\n",
+ ialloc_trace_space(mem), client_name_string(cname),
+ num_refs, (ulong) obj);
+ if ((gs_memory_t *)mem != mem->stable_memory)
+ alloc_save_remove(mem, (ref_packed *)obj, "gs_free_ref_array");
+ alloc_free_chunk(cl.cp, mem);
+ return;
+ }
+ }
+ /* Punt, but fill the array with nulls so that there won't be */
+ /* dangling references to confuse the garbage collector. */
+ if_debug4m('A', (const gs_memory_t *)mem, "[a%d:-$#]%s(%u) 0x%lx\n",
+ ialloc_trace_space(mem), client_name_string(cname), num_refs,
+ (ulong) obj);
+ {
+ uint size;
+
+ switch (r_type(parr)) {
+ case t_shortarray:
+ size = num_refs * sizeof(ref_packed);
+ break;
+ case t_mixedarray:{
+ /* We have to parse the array to compute the storage size. */
+ uint i = 0;
+ const ref_packed *p = parr->value.packed;
+
+ for (; i < num_refs; ++i)
+ p = packed_next(p);
+ size = (const byte *)p - (const byte *)parr->value.packed;
+ break;
+ }
+ case t_array:
+ size = num_refs * sizeof(ref);
+ break;
+ default:
+ lprintf3("Unknown type 0x%x in free_ref_array(%u,0x%lx)!",
+ r_type(parr), num_refs, (ulong) obj);
+ return;
+ }
+ /*
+ * If there are any leftover packed elements, we don't
+ * worry about them, since they can't be dangling references.
+ */
+ refset_null_new(obj, size / sizeof(ref), 0);
+ mem->lost.refs += size;
+ }
+}
+
+/* Allocate a string ref. */
+int
+gs_alloc_string_ref(gs_ref_memory_t * mem, ref * psref,
+ uint attrs, uint nbytes, client_name_t cname)
+{
+ byte *str = gs_alloc_string((gs_memory_t *) mem, nbytes, cname);
+
+ if (str == 0)
+ return_error(gs_error_VMerror);
+ make_string(psref, attrs | mem->space, nbytes, str);
+ return 0;
+}
diff --git a/psi/ialloc.h b/psi/ialloc.h
new file mode 100644
index 000000000..2c3e74790
--- /dev/null
+++ b/psi/ialloc.h
@@ -0,0 +1,125 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to Ghostscript interpreter memory allocator */
+
+#ifndef ialloc_INCLUDED
+# define ialloc_INCLUDED
+
+#include "imemory.h"
+
+/*
+ * Define the interpreter memory manager instance.
+ */
+#define gs_imemory (i_ctx_p->memory)
+#define idmemory (&gs_imemory)
+#define iimemory (gs_imemory.current)
+#define imemory ((gs_memory_t *)iimemory)
+#define iimemory_local (gs_imemory.space_local)
+#define imemory_local ((gs_memory_t *)iimemory_local)
+#define iimemory_global (gs_imemory.space_global)
+#define imemory_global ((gs_memory_t *)iimemory_global)
+#define iimemory_system (gs_imemory.space_system)
+#define imemory_system ((gs_memory_t *)iimemory_system)
+
+/*
+ * Aliases for invoking the standard allocator interface.
+ */
+#define ialloc_bytes(nbytes, cname)\
+ gs_alloc_bytes(imemory, nbytes, cname)
+#define ialloc_struct(typ, pstype, cname)\
+ gs_alloc_struct(imemory, typ, pstype, cname)
+#define ialloc_byte_array(nelts, esize, cname)\
+ gs_alloc_byte_array(imemory, nelts, esize, cname)
+#define ialloc_struct_array(nelts, typ, pstype, cname)\
+ gs_alloc_struct_array(imemory, nelts, typ, pstype, cname)
+#define ifree_object(data, cname)\
+ gs_free_object(imemory, data, cname)
+#define ifree_const_object(data, cname)\
+ gs_free_const_object(imemory, data, cname)
+#define ialloc_string(nbytes, cname)\
+ gs_alloc_string(imemory, nbytes, cname)
+#define iresize_string(data, oldn, newn, cname)\
+ gs_resize_string(imemory, data, oldn, newn, cname)
+#define ifree_string(data, nbytes, cname)\
+ gs_free_string(imemory, data, nbytes, cname)
+#define ifree_const_string(data, nbytes, cname)\
+ gs_free_const_string(imemory, data, nbytes, cname)
+
+/* Initialize the interpreter's allocator. */
+int ialloc_init(gs_dual_memory_t *, gs_memory_t *, uint, bool);
+
+/* ------ Internal routines ------ */
+
+/* Reset the request values that identify the cause of a GC. */
+void ialloc_reset_requested(gs_dual_memory_t *);
+
+/* Validate the contents of memory. */
+void ialloc_validate_spaces(const gs_dual_memory_t *);
+
+#define ivalidate_spaces() ialloc_validate_spaces(idmemory)
+
+void ivalidate_clean_spaces(i_ctx_t *i_ctx_p);
+
+/*
+ * Local/global VM management.
+ */
+
+/* Get the space attribute of the current allocator. */
+#define ialloc_space(dmem) ((dmem)->current_space)
+#define icurrent_space ialloc_space(idmemory)
+uint imemory_space(const gs_ref_memory_t *);
+
+/* Select the allocation space. */
+void ialloc_set_space(gs_dual_memory_t *, uint);
+
+/* Get the l_new attribute of an allocator. */
+uint imemory_new_mask(const gs_ref_memory_t *);
+
+/* Get the save level of an allocator. */
+int imemory_save_level(const gs_ref_memory_t *);
+
+/*
+ * Ref-related facilities.
+ */
+
+#ifdef r_type /* i.e., we know about refs */
+
+/* Allocate and free ref arrays. */
+#define ialloc_ref_array(paref, attrs, nrefs, cname)\
+ gs_alloc_ref_array(iimemory, paref, attrs, nrefs, cname)
+#define iresize_ref_array(paref, nrefs, cname)\
+ gs_resize_ref_array(iimemory, paref, nrefs, cname)
+#define ifree_ref_array(paref, cname)\
+ gs_free_ref_array(iimemory, paref, cname)
+
+/* Allocate a string ref. */
+#define ialloc_string_ref(psref, attrs, nbytes, cname)\
+ gs_alloc_string_ref(iimemory, psref, attrs, nbytes, cname)
+
+/* Make a ref for a newly allocated structure. */
+#define make_istruct(pref,attrs,ptr)\
+ make_struct(pref, (attrs) | icurrent_space, ptr)
+#define make_istruct_new(pref,attrs,ptr)\
+ make_struct_new(pref, (attrs) | icurrent_space, ptr)
+#define make_iastruct(pref,attrs,ptr)\
+ make_astruct(pref, (attrs) | icurrent_space, ptr)
+#define make_iastruct_new(pref,attrs,ptr)\
+ make_astruct_new(pref, (attrs) | icurrent_space, ptr)
+
+#endif /* ifdef r_type */
+
+#endif /* ialloc_INCLUDED */
diff --git a/psi/iapi.c b/psi/iapi.c
new file mode 100644
index 000000000..1378c49ac
--- /dev/null
+++ b/psi/iapi.c
@@ -0,0 +1,573 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/* Public Application Programming Interface to Ghostscript interpreter */
+
+#include "string_.h"
+#include "ierrors.h"
+#include "gscdefs.h"
+#include "gstypes.h"
+#include "gdebug.h"
+#include "iapi.h" /* Public API */
+#include "iref.h"
+#include "iminst.h"
+#include "imain.h"
+#include "imainarg.h"
+#include "gsmemory.h"
+#include "gsmalloc.h"
+#include "gslibctx.h"
+#include "gp.h"
+#include "gsargs.h"
+
+#ifndef GS_THREADSAFE
+/* Number of threads to allow per process. Unless GS_THREADSAFE is defined
+ * more than 1 is guaranteed to fail.
+ */
+static int gsapi_instance_counter = 0;
+static const int gsapi_instance_max = 1;
+#endif
+
+
+/* Return revision numbers and strings of Ghostscript. */
+/* Used for determining if wrong GSDLL loaded. */
+/* This may be called before any other function. */
+GSDLLEXPORT int GSDLLAPI
+gsapi_revision(gsapi_revision_t *pr, int rvsize)
+{
+ if (rvsize < sizeof(gsapi_revision_t))
+ return sizeof(gsapi_revision_t);
+ pr->product = gs_product;
+ pr->copyright = gs_copyright;
+ pr->revision = gs_revision;
+ pr->revisiondate = gs_revisiondate;
+ return 0;
+}
+
+#ifdef METRO
+static int GSDLLCALL metro_stdin(void *v, char *buf, int len)
+{
+ return 0;
+}
+
+static int GSDLLCALL metro_stdout(void *v, const char *str, int len)
+{
+#ifdef DEBUG
+ OutputDebugStringWRT(str, len);
+#endif
+ return len;
+}
+
+static int GSDLLCALL metro_stderr(void *v, const char *str, int len)
+{
+ return metro_stdout(v, str, len);
+}
+#endif
+
+/* Create a new instance of Ghostscript.
+ * First instance per process call with *pinstance == NULL
+ * next instance in a proces call with *pinstance == copy of valid_instance pointer
+ * *pinstance is set to a new instance pointer.
+ */
+GSDLLEXPORT int GSDLLAPI
+gsapi_new_instance(void **pinstance, void *caller_handle)
+{
+ gs_memory_t *mem = NULL;
+ gs_main_instance *minst = NULL;
+
+ if (pinstance == NULL)
+ return gs_error_Fatal;
+
+#ifndef GS_THREADSAFE
+ /* limited to 1 instance, till it works :) */
+ if ( gsapi_instance_counter >= gsapi_instance_max )
+ return gs_error_Fatal;
+ ++gsapi_instance_counter;
+#endif
+
+ if (*pinstance == NULL)
+ /* first instance in this process */
+ mem = gs_malloc_init();
+ else {
+ /* nothing different for second thread initialization
+ * seperate memory, ids, only stdio is process shared.
+ */
+ mem = gs_malloc_init();
+
+ }
+ if (mem == NULL)
+ return gs_error_Fatal;
+ minst = gs_main_alloc_instance(mem);
+ if (minst == NULL) {
+ gs_malloc_release(mem);
+ return gs_error_Fatal;
+ }
+ mem->gs_lib_ctx->top_of_system = (void*) minst;
+ mem->gs_lib_ctx->caller_handle = caller_handle;
+ mem->gs_lib_ctx->custom_color_callback = NULL;
+#ifdef METRO
+ mem->gs_lib_ctx->stdin_fn = metro_stdin;
+ mem->gs_lib_ctx->stdout_fn = metro_stdout;
+ mem->gs_lib_ctx->stderr_fn = metro_stderr;
+#else
+ mem->gs_lib_ctx->stdin_fn = NULL;
+ mem->gs_lib_ctx->stdout_fn = NULL;
+ mem->gs_lib_ctx->stderr_fn = NULL;
+#endif
+ mem->gs_lib_ctx->poll_fn = NULL;
+
+ *pinstance = (void*)(mem->gs_lib_ctx);
+ return gsapi_set_arg_encoding(*pinstance, GS_ARG_ENCODING_LOCAL);
+}
+
+/* Destroy an instance of Ghostscript */
+/* We do not support multiple instances, so make sure
+ * we use the default instance only once.
+ */
+GSDLLEXPORT void GSDLLAPI
+gsapi_delete_instance(void *lib)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if ((ctx != NULL)) {
+ gs_main_instance *minst = get_minst_from_memory(ctx->memory);
+
+ ctx->caller_handle = NULL;
+ ctx->stdin_fn = NULL;
+ ctx->stdout_fn = NULL;
+ ctx->stderr_fn = NULL;
+ ctx->poll_fn = NULL;
+ minst->display = NULL;
+
+ /* Release the memory (frees up everything) */
+ gs_malloc_release(minst->heap);
+
+#ifndef GS_THREADSAFE
+ --gsapi_instance_counter;
+#endif
+ }
+}
+
+/* Set the callback functions for stdio */
+GSDLLEXPORT int GSDLLAPI
+gsapi_set_stdio(void *lib,
+ int(GSDLLCALL *stdin_fn)(void *caller_handle, char *buf, int len),
+ int(GSDLLCALL *stdout_fn)(void *caller_handle, const char *str, int len),
+ int(GSDLLCALL *stderr_fn)(void *caller_handle, const char *str, int len))
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if (lib == NULL)
+ return gs_error_Fatal;
+ ctx->stdin_fn = stdin_fn;
+ ctx->stdout_fn = stdout_fn;
+ ctx->stderr_fn = stderr_fn;
+ return 0;
+}
+
+/* Set the callback function for polling */
+GSDLLEXPORT int GSDLLAPI
+gsapi_set_poll(void *lib,
+ int(GSDLLCALL *poll_fn)(void *caller_handle))
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if (lib == NULL)
+ return gs_error_Fatal;
+ ctx->poll_fn = poll_fn;
+ return 0;
+}
+
+/* Set the display callback structure */
+GSDLLEXPORT int GSDLLAPI
+gsapi_set_display_callback(void *lib, display_callback *callback)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if (lib == NULL)
+ return gs_error_Fatal;
+ get_minst_from_memory(ctx->memory)->display = callback;
+ /* not in a language switched build */
+ return 0;
+}
+
+/* Set/Get the default device list string */
+GSDLLEXPORT int GSDLLAPI
+gsapi_set_default_device_list(void *lib, char *list, int listlen)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if (lib == NULL)
+ return gs_error_Fatal;
+ return gs_lib_ctx_set_default_device_list(ctx->memory, list, listlen);
+}
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_get_default_device_list(void *lib, char **list, int *listlen)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if (lib == NULL)
+ return gs_error_Fatal;
+ return gs_lib_ctx_get_default_device_list(ctx->memory, list, listlen);
+}
+
+static int utf16le_get_codepoint(FILE *file, const char **astr)
+{
+ int c;
+ int rune;
+ int trail;
+
+ /* This code spots the BOM for 16bit LE and ignores it. Strictly speaking
+ * this may be wrong, as we are only supposed to ignore it at the beginning
+ * of the string, but if anyone is stupid enough to use ZWNBSP (zero width
+ * non breaking space) in the middle of their strings, then they deserve
+ * what they get. */
+ /* We spot the BOM for 16bit BE and treat that as EOF. We'd rather give
+ * up on dealing with a broken file than try to run something we know to
+ * be wrong. */
+
+ do {
+ if (file) {
+ rune = fgetc(file);
+ if (rune == EOF)
+ return EOF;
+ c = fgetc(file);
+ if (c == EOF)
+ return EOF;
+ rune += c<<8;
+ } else {
+ rune = (*astr)[0] | ((*astr)[1]<<8);
+ if (rune != 0)
+ (*astr) += 2;
+ else
+ return EOF;
+ }
+ if (rune == 0xFEFF) /* BOM - ignore it */
+ continue;
+ if (rune == 0xFFFE) /* BOM for BE - hopelessly broken */
+ return EOF;
+ if (rune < 0xD800 || rune >= 0xE000)
+ return rune;
+ if (rune >= 0xDC00) /* Found a trailing surrogate pair. Skip it */
+ continue;
+lead: /* We've just read a leading surrogate */
+ rune -= 0xD800;
+ rune <<= 10;
+ if (file) {
+ trail = fgetc(file);
+ if (trail == EOF)
+ return EOF;
+ c = fgetc(file);
+ if (c == EOF)
+ return EOF;
+ trail += c<<8;
+ } else {
+ trail = (*astr)[0] | ((*astr)[1]<<8);
+ if (trail != 0)
+ (*astr) += 2;
+ else
+ return EOF;
+ }
+ if (trail < 0xd800 || trail >= 0xE000) {
+ if (rune == 0xFEFF) /* BOM - ignore it. */
+ continue;
+ if (rune == 0xFFFE) /* BOM for BE - hopelessly broken. */
+ return EOF;
+ /* No trail surrogate was found, so skip the lead surrogate and
+ * return the rune we landed on. */
+ return trail;
+ }
+ if (trail < 0xdc00) {
+ /* We found another leading surrogate. */
+ rune = trail;
+ goto lead;
+ }
+ break;
+ } while (1);
+
+ return rune + (trail-0xDC00) + 0x10000;
+}
+
+#ifdef GS_NO_UTF8
+static int clean8bit_get_codepoint(FILE *file, const char **astr)
+{
+ return (file ? fgetc(file) : (**astr ? (int)(unsigned char)*(*astr)++ : EOF));
+}
+#endif
+
+/* Initialise the interpreter */
+GSDLLEXPORT int GSDLLAPI
+gsapi_set_arg_encoding(void *lib, int encoding)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if (lib == NULL)
+ return gs_error_Fatal;
+
+#if defined(GS_NO_UTF8)
+ if (encoding == GS_ARG_ENCODING_LOCAL) {
+ /* For GS_NO_UTF8 builds, we don't use utf8 internally, and we assume
+ * that all inputs are 8 bit clean. */
+ gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), clean8bit_get_codepoint);
+ return 0;
+ }
+#else
+ if (encoding == GS_ARG_ENCODING_LOCAL) {
+#if defined(__WIN32__) && !defined(METRO)
+ /* For windows, we need to set it up so that we convert from 'local'
+ * format (in this case whatever codepage is set) to utf8 format. At
+ * the moment, all the other OS we care about provide utf8 anyway.
+ */
+ gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), gp_local_arg_encoding_get_codepoint);
+#else
+ gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), NULL);
+#endif /* WIN32 */
+ return 0;
+ }
+ if (encoding == GS_ARG_ENCODING_UTF8) {
+ gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), NULL);
+ return 0;
+ }
+ if (encoding == GS_ARG_ENCODING_UTF16LE) {
+ gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), utf16le_get_codepoint);
+ return 0;
+ }
+#endif
+ return gs_error_Fatal;
+}
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_init_with_args(void *lib, int argc, char **argv)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if (lib == NULL)
+ return gs_error_Fatal;
+
+ return gs_main_init_with_args(get_minst_from_memory(ctx->memory), argc, argv);
+}
+
+/* The gsapi_run_* functions are like gs_main_run_* except
+ * that the error_object is omitted.
+ * An error object in minst is used instead.
+ */
+
+/* Setup up a suspendable run_string */
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_string_begin(void *lib, int user_errors,
+ int *pexit_code)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if (lib == NULL)
+ return gs_error_Fatal;
+
+ return gs_main_run_string_begin(get_minst_from_memory(ctx->memory),
+ user_errors, pexit_code,
+ &(get_minst_from_memory(ctx->memory)->error_object));
+}
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_string_continue(void *lib,
+ const char *str, uint length, int user_errors, int *pexit_code)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if (lib == NULL)
+ return gs_error_Fatal;
+
+ return gs_main_run_string_continue(get_minst_from_memory(ctx->memory),
+ str, length, user_errors, pexit_code,
+ &(get_minst_from_memory(ctx->memory)->error_object));
+}
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_string_end(void *lib,
+ int user_errors, int *pexit_code)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if (lib == NULL)
+ return gs_error_Fatal;
+
+ return gs_main_run_string_end(get_minst_from_memory(ctx->memory),
+ user_errors, pexit_code,
+ &(get_minst_from_memory(ctx->memory)->error_object));
+}
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_string_with_length(void *lib,
+ const char *str, uint length, int user_errors, int *pexit_code)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if (lib == NULL)
+ return gs_error_Fatal;
+
+ return gs_main_run_string_with_length(get_minst_from_memory(ctx->memory),
+ str, length, user_errors, pexit_code,
+ &(get_minst_from_memory(ctx->memory)->error_object));
+}
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_string(void *lib,
+ const char *str, int user_errors, int *pexit_code)
+{
+ return gsapi_run_string_with_length(lib,
+ str, (uint)strlen(str), user_errors, pexit_code);
+}
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_file(void *lib, const char *file_name,
+ int user_errors, int *pexit_code)
+{
+#ifndef GS_NO_UTF8
+ char *d, *temp;
+ const char *c = file_name;
+ char dummy[6];
+ int rune, code, len;
+#endif
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ gs_main_instance *minst;
+ if (lib == NULL)
+ return gs_error_Fatal;
+ minst = get_minst_from_memory(ctx->memory);
+
+#ifdef GS_NO_UTF8
+ return gs_main_run_file(minst, file_name, user_errors, pexit_code,
+ &(minst->error_object));
+#else
+ /* Convert the file_name to utf8 */
+ if (minst->get_codepoint) {
+ len = 1;
+ while ((rune = minst->get_codepoint(NULL, &c)) >= 0)
+ len += codepoint_to_utf8(dummy, rune);
+ temp = (char *)gs_alloc_bytes_immovable(ctx->memory, len, "gsapi_run_file");
+ if (temp == NULL)
+ return 0;
+ c = file_name;
+ d = temp;
+ while ((rune = minst->get_codepoint(NULL, &c)) >= 0)
+ d += codepoint_to_utf8(d, rune);
+ *d = 0;
+ }
+ else {
+ temp = (char *)file_name;
+ }
+ code = gs_main_run_file(minst, temp, user_errors, pexit_code,
+ &(minst->error_object));
+ if (temp != file_name)
+ gs_free_object(ctx->memory, temp, "gsapi_run_file");
+ return code;
+#endif
+}
+
+#ifdef __WIN32__
+GSDLLEXPORT int GSDLLAPI
+gsapi_init_with_argsW(void *lib, int argc, wchar_t **argv)
+{
+#ifdef GS_NO_UTF8
+ /* Cannot call the W entrypoints in a GS_NO_UTF8 build */
+ return gs_error_Fatal;
+#else
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ int code;
+ gs_arg_get_codepoint *old;
+ if (lib == NULL)
+ return gs_error_Fatal;
+
+ old = gs_main_inst_get_arg_decode(get_minst_from_memory(ctx->memory));
+ code = gsapi_set_arg_encoding(lib, GS_ARG_ENCODING_UTF16LE);
+ if (code != 0)
+ return code;
+ code = gsapi_init_with_args(lib, 2*argc, (char **)argv);
+ gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), old);
+ return code;
+#endif
+}
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_init_with_argsA(void *lib, int argc, char **argv)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ int code;
+ gs_arg_get_codepoint *old;
+ if (lib == NULL)
+ return gs_error_Fatal;
+
+ old = gs_main_inst_get_arg_decode(get_minst_from_memory(ctx->memory));
+ code = gsapi_set_arg_encoding(lib, GS_ARG_ENCODING_LOCAL);
+ if (code != 0)
+ return code;
+ code = gsapi_init_with_args(lib, 2*argc, (char **)argv);
+ gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), old);
+ return code;
+}
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_fileW(void *lib, const wchar_t *file_name,
+ int user_errors, int *pexit_code)
+{
+#ifdef GS_NO_UTF8
+ /* Cannot call the W entrypoints in a GS_NO_UTF8 build */
+ return gs_error_Fatal;
+#else
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ int code;
+ gs_arg_get_codepoint *old;
+ if (lib == NULL)
+ return gs_error_Fatal;
+
+ old = gs_main_inst_get_arg_decode(get_minst_from_memory(ctx->memory));
+ code = gsapi_set_arg_encoding(lib, GS_ARG_ENCODING_UTF16LE);
+ if (code != 0)
+ return code;
+ code = gsapi_run_file(lib, (const char *)file_name, user_errors, pexit_code);
+ gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), old);
+ return code;
+#endif
+}
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_fileA(void *lib, const char *file_name,
+ int user_errors, int *pexit_code)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ int code;
+ gs_arg_get_codepoint *old;
+ if (lib == NULL)
+ return gs_error_Fatal;
+
+ old = gs_main_inst_get_arg_decode(get_minst_from_memory(ctx->memory));
+ code = gsapi_set_arg_encoding(lib, GS_ARG_ENCODING_LOCAL);
+ if (code != 0)
+ return code;
+ code = gsapi_run_file(lib, (const char *)file_name, user_errors, pexit_code);
+ gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), old);
+ return code;
+}
+#endif
+
+/* Exit the interpreter */
+GSDLLEXPORT int GSDLLAPI
+gsapi_exit(void *lib)
+{
+ gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib;
+ if (lib == NULL)
+ return gs_error_Fatal;
+
+ gs_to_exit(ctx->memory, 0);
+ return 0;
+}
+
+/* Visual tracer : */
+struct vd_trace_interface_s;
+extern struct vd_trace_interface_s * vd_trace0;
+GSDLLEXPORT void GSDLLAPI
+gsapi_set_visual_tracer(struct vd_trace_interface_s *I)
+{ vd_trace0 = I;
+}
+
+/* end of iapi.c */
diff --git a/psi/iapi.h b/psi/iapi.h
new file mode 100644
index 000000000..ee6be52a2
--- /dev/null
+++ b/psi/iapi.h
@@ -0,0 +1,374 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/*
+ * Public API for Ghostscript interpreter
+ * for use both as DLL and for static linking.
+ *
+ * Should work for Windows, OS/2, Linux, Mac.
+ *
+ * DLL exported functions should be as similar as possible to imain.c
+ * You will need to include "ierrors.h".
+ *
+ * Current problems:
+ * 1. Ghostscript does not support multiple instances.
+ * 2. Global variables in gs_main_instance_default()
+ * and gsapi_instance_counter
+ */
+
+/* Exported functions may need different prefix
+ * GSDLLEXPORT marks functions as exported
+ * GSDLLAPI is the calling convention used on functions exported
+ * by Ghostscript
+ * GSDLLCALL is used on callback functions called by Ghostscript
+ * When you include this header file in the caller, you may
+ * need to change the definitions by defining these
+ * before including this header file.
+ * Make sure you get the calling convention correct, otherwise your
+ * program will crash either during callbacks or soon after returning
+ * due to stack corruption.
+ */
+
+#ifndef iapi_INCLUDED
+# define iapi_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(_WINDOWS_) || defined(__WINDOWS__)
+# ifndef _Windows
+# define _Windows
+# endif
+#endif
+
+#ifdef _Windows
+# ifndef GSDLLEXPORT
+/* We don't need both the "__declspec(dllexport)" declaration
+ * and the listing in the .def file - having both results in
+ * a linker warning on x64 builds (but is incorrect on x86, too)
+ */
+# if 0
+# define GSDLLEXPORT __declspec(dllexport)
+# else
+# define GSDLLEXPORT
+# endif
+# endif
+# ifndef GSDLLAPI
+# define GSDLLAPI __stdcall
+# endif
+# ifndef GSDLLCALL
+# define GSDLLCALL __stdcall
+# endif
+#endif /* _Windows */
+
+#if defined(OS2) && defined(__IBMC__)
+# ifndef GSDLLAPI
+# define GSDLLAPI _System
+# endif
+# ifndef GSDLLCALL
+# define GSDLLCALL _System
+# endif
+#endif /* OS2 && __IBMC */
+
+#ifdef __MACOS__
+# pragma export on
+#endif
+
+#ifndef GSDLLEXPORT
+# define GSDLLEXPORT
+#endif
+#ifndef GSDLLAPI
+# define GSDLLAPI
+#endif
+#ifndef GSDLLCALL
+# define GSDLLCALL
+#endif
+
+#if defined(__IBMC__)
+# define GSDLLAPIPTR * GSDLLAPI
+# define GSDLLCALLPTR * GSDLLCALL
+#else
+# define GSDLLAPIPTR GSDLLAPI *
+# define GSDLLCALLPTR GSDLLCALL *
+#endif
+
+#ifndef display_callback_DEFINED
+# define display_callback_DEFINED
+typedef struct display_callback_s display_callback;
+#endif
+
+typedef struct gsapi_revision_s {
+ const char *product;
+ const char *copyright;
+ long revision;
+ long revisiondate;
+} gsapi_revision_t;
+
+/* Get version numbers and strings.
+ * This is safe to call at any time.
+ * You should call this first to make sure that the correct version
+ * of the Ghostscript is being used.
+ * pr is a pointer to a revision structure.
+ * len is the size of this structure in bytes.
+ * Returns 0 if OK, or if len too small (additional parameters
+ * have been added to the structure) it will return the required
+ * size of the structure.
+ */
+GSDLLEXPORT int GSDLLAPI
+gsapi_revision(gsapi_revision_t *pr, int len);
+
+/*
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ * Ghostscript supports only one instance.
+ * The current implementation uses a global static instance
+ * counter to make sure that only a single instance is used.
+ * If you try to create two instances, the second attempt
+ * will return < 0 and set pinstance to NULL.
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ */
+/* Create a new instance of Ghostscript.
+ * This instance is passed to most other API functions.
+ * The caller_handle will be provided to callback functions.
+ */
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_new_instance(void **pinstance, void *caller_handle);
+
+/*
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ * Ghostscript supports only one instance.
+ * The current implementation uses a global static instance
+ * counter to make sure that only a single instance is used.
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ */
+/* Destroy an instance of Ghostscript
+ * Before you call this, Ghostscript must have finished.
+ * If Ghostscript has been initialised, you must call gsapi_exit()
+ * before gsapi_delete_instance.
+ */
+GSDLLEXPORT void GSDLLAPI
+gsapi_delete_instance(void *instance);
+
+/* Set the callback functions for stdio
+ * The stdin callback function should return the number of
+ * characters read, 0 for EOF, or -1 for error.
+ * The stdout and stderr callback functions should return
+ * the number of characters written.
+ * If a callback address is NULL, the real stdio will be used.
+ */
+GSDLLEXPORT int GSDLLAPI
+gsapi_set_stdio(void *instance,
+ int (GSDLLCALLPTR stdin_fn)(void *caller_handle, char *buf, int len),
+ int (GSDLLCALLPTR stdout_fn)(void *caller_handle, const char *str, int len),
+ int (GSDLLCALLPTR stderr_fn)(void *caller_handle, const char *str, int len));
+
+/* Set the callback function for polling.
+ * This is used for handling window events or cooperative
+ * multitasking. This function will only be called if
+ * Ghostscript was compiled with CHECK_INTERRUPTS
+ * as described in gpcheck.h.
+ * The polling function should return 0 if all is well,
+ * and negative if it wants ghostscript to abort.
+ * The polling function must be fast.
+ */
+GSDLLEXPORT int GSDLLAPI gsapi_set_poll(void *instance,
+ int (GSDLLCALLPTR poll_fn)(void *caller_handle));
+
+/* Set the display device callback structure.
+ * If the display device is used, this must be called
+ * after gsapi_new_instance() and before gsapi_init_with_args().
+ * See gdevdisp.h for more details.
+ */
+GSDLLEXPORT int GSDLLAPI gsapi_set_display_callback(
+ void *instance, display_callback *callback);
+
+/* Set the string containing the list of default device names
+ * for example "display x11alpha x11 bbox". Allows the calling
+ * application to influence which device(s) gs will try in order
+ * to select the default device
+ *
+ * *Must* be called after gsapi_new_instance() and before
+ * gsapi_init_with_args().
+ */
+GSDLLEXPORT int GSDLLAPI
+gsapi_set_default_device_list(void *lib, char *list, int listlen);
+
+/* Returns a pointer to the current default device string
+ * *Must* be called after gsapi_new_instance().
+ */
+GSDLLEXPORT int GSDLLAPI
+gsapi_get_default_device_list(void *lib, char **list, int *listlen);
+
+/* Set the encoding used for the args. By default we assume
+ * 'local' encoding. For windows this equates to whatever the current
+ * codepage is. For linux this is utf8.
+ *
+ * Use of this API (gsapi) with 'local' encodings (and hence without calling
+ * this function) is now deprecated!
+ */
+GSDLLEXPORT int GSDLLAPI gsapi_set_arg_encoding(void *instance,
+ int encoding);
+
+enum {
+ GS_ARG_ENCODING_LOCAL = 0,
+ GS_ARG_ENCODING_UTF8 = 1,
+ GS_ARG_ENCODING_UTF16LE = 2
+};
+
+/* Initialise the interpreter.
+ * This calls gs_main_init_with_args() in imainarg.c
+ * 1. If quit or EOF occur during gsapi_init_with_args(),
+ * the return value will be gs_error_Quit. This is not an error.
+ * You must call gsapi_exit() and must not call any other
+ * gsapi_XXX functions.
+ * 2. If usage info should be displayed, the return value will be gs_error_Info
+ * which is not an error. Do not call gsapi_exit().
+ * 3. Under normal conditions this returns 0. You would then
+ * call one or more gsapi_run_*() functions and then finish
+ * with gsapi_exit().
+ */
+GSDLLEXPORT int GSDLLAPI gsapi_init_with_args(void *instance,
+ int argc, char **argv);
+
+#ifdef __WIN32__
+GSDLLEXPORT int GSDLLAPI gsapi_init_with_argsA(void *instance,
+ int argc, char **argv);
+
+GSDLLEXPORT int GSDLLAPI gsapi_init_with_argsW(void *instance,
+ int argc, wchar_t **argv);
+#endif
+
+/*
+ * The gsapi_run_* functions are like gs_main_run_* except
+ * that the error_object is omitted.
+ * If these functions return <= -100, either quit or a fatal
+ * error has occured. You then call gsapi_exit() next.
+ * The only exception is gsapi_run_string_continue()
+ * which will return gs_error_NeedInput if all is well.
+ */
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_string_begin(void *instance,
+ int user_errors, int *pexit_code);
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_string_continue(void *instance,
+ const char *str, unsigned int length, int user_errors, int *pexit_code);
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_string_end(void *instance,
+ int user_errors, int *pexit_code);
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_string_with_length(void *instance,
+ const char *str, unsigned int length, int user_errors, int *pexit_code);
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_string(void *instance,
+ const char *str, int user_errors, int *pexit_code);
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_file(void *instance,
+ const char *file_name, int user_errors, int *pexit_code);
+
+#ifdef __WIN32__
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_fileA(void *instance,
+ const char *file_name, int user_errors, int *pexit_code);
+
+GSDLLEXPORT int GSDLLAPI
+gsapi_run_fileW(void *instance,
+ const wchar_t *file_name, int user_errors, int *pexit_code);
+#endif
+
+/* Exit the interpreter.
+ * This must be called on shutdown if gsapi_init_with_args()
+ * has been called, and just before gsapi_delete_instance().
+ */
+GSDLLEXPORT int GSDLLAPI
+gsapi_exit(void *instance);
+
+/* Visual Tracer */
+/* This function is only for debug purpose clients */
+struct vd_trace_interface_s;
+GSDLLEXPORT void GSDLLAPI
+gsapi_set_visual_tracer(struct vd_trace_interface_s *I);
+
+/* function prototypes */
+typedef int (GSDLLAPIPTR PFN_gsapi_revision)(
+ gsapi_revision_t *pr, int len);
+typedef int (GSDLLAPIPTR PFN_gsapi_new_instance)(
+ void **pinstance, void *caller_handle);
+typedef void (GSDLLAPIPTR PFN_gsapi_delete_instance)(
+ void *instance);
+typedef int (GSDLLAPIPTR PFN_gsapi_set_stdio)(void *instance,
+ int (GSDLLCALLPTR stdin_fn)(void *caller_handle, char *buf, int len),
+ int (GSDLLCALLPTR stdout_fn)(void *caller_handle, const char *str, int len),
+ int (GSDLLCALLPTR stderr_fn)(void *caller_handle, const char *str, int len));
+typedef int (GSDLLAPIPTR PFN_gsapi_set_poll)(void *instance,
+ int(GSDLLCALLPTR poll_fn)(void *caller_handle));
+typedef int (GSDLLAPIPTR PFN_gsapi_set_display_callback)(
+ void *instance, display_callback *callback);
+typedef int (GSDLLAPIPTR PFN_gsapi_set_default_device_list)(
+ void *lib, char *list, int listlen);
+typedef int (GSDLLAPIPTR PFN_gsapi_get_default_device_list)(
+ void *lib, char **list, int *listlen);
+typedef int (GSDLLAPIPTR PFN_gsapi_init_with_args)(
+ void *instance, int argc, char **argv);
+#ifdef __WIN32__
+typedef int (GSDLLAPIPTR PFN_gsapi_init_with_argsA)(
+ void *instance, int argc, char **argv);
+typedef int (GSDLLAPIPTR PFN_gsapi_init_with_argsW)(
+ void *instance, int argc, wchar_t **argv);
+#endif
+typedef int (GSDLLAPIPTR PFN_gsapi_set_arg_encoding)(
+ void *instance, int encoding);
+typedef int (GSDLLAPIPTR PFN_gsapi_run_string_begin)(
+ void *instance, int user_errors, int *pexit_code);
+typedef int (GSDLLAPIPTR PFN_gsapi_run_string_continue)(
+ void *instance, const char *str, unsigned int length,
+ int user_errors, int *pexit_code);
+typedef int (GSDLLAPIPTR PFN_gsapi_run_string_end)(
+ void *instance, int user_errors, int *pexit_code);
+typedef int (GSDLLAPIPTR PFN_gsapi_run_string_with_length)(
+ void *instance, const char *str, unsigned int length,
+ int user_errors, int *pexit_code);
+typedef int (GSDLLAPIPTR PFN_gsapi_run_string)(
+ void *instance, const char *str,
+ int user_errors, int *pexit_code);
+typedef int (GSDLLAPIPTR PFN_gsapi_run_file)(void *instance,
+ const char *file_name, int user_errors, int *pexit_code);
+#ifdef __WIN32__
+typedef int (GSDLLAPIPTR PFN_gsapi_run_fileA)(void *instance,
+ const char *file_name, int user_errors, int *pexit_code);
+typedef int (GSDLLAPIPTR PFN_gsapi_run_fileW)(void *instance,
+ const wchar_t *file_name, int user_errors, int *pexit_code);
+#endif
+typedef int (GSDLLAPIPTR PFN_gsapi_exit)(void *instance);
+typedef void (GSDLLAPIPTR PFN_gsapi_set_visual_tracer)
+ (struct vd_trace_interface_s *I);
+
+#ifdef __MACOS__
+#pragma export off
+#endif
+
+#ifdef __cplusplus
+} /* extern 'C' protection */
+#endif
+
+#endif /* iapi_INCLUDED */
diff --git a/psi/iastate.h b/psi/iastate.h
new file mode 100644
index 000000000..4c4bd552b
--- /dev/null
+++ b/psi/iastate.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Collected definitions for interpreter allocator state */
+/* Requires gsmemory.h, gsstruct.h */
+
+#ifndef iastate_INCLUDED
+# define iastate_INCLUDED
+
+#include "gxalloc.h"
+#include "istruct.h"
+#include "ialloc.h"
+
+#endif /* iastate_INCLUDED */
diff --git a/psi/iastruct.h b/psi/iastruct.h
new file mode 100644
index 000000000..f7bfd00aa
--- /dev/null
+++ b/psi/iastruct.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter memory manager implementation structures */
+
+#ifndef iastruct_INCLUDED
+# define iastruct_INCLUDED
+
+#include "gxobj.h"
+#include "ialloc.h"
+
+#endif /* iastruct_INCLUDED */
diff --git a/psi/ibnum.c b/psi/ibnum.c
new file mode 100644
index 000000000..638a1a3a0
--- /dev/null
+++ b/psi/ibnum.c
@@ -0,0 +1,259 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 2 encoded number reading utilities for Ghostscript */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "opcheck.h"
+#include "ierrors.h"
+#include "stream.h"
+#include "ibnum.h"
+#include "imemory.h" /* for iutil.h */
+#include "iutil.h"
+
+/* Define the number of bytes for a given format of encoded number. */
+const byte enc_num_bytes[] = {
+ enc_num_bytes_values
+};
+
+/* ------ Encoded number reading ------ */
+
+/* Set up to read from an encoded number array/string. */
+/* Return <0 for error, or a number format. */
+int
+num_array_format(const ref * op)
+{
+ int format;
+
+ switch (r_type(op)) {
+ case t_string:
+ {
+ /* Check that this is a legitimate encoded number string. */
+ const byte *bp = op->value.bytes;
+
+ if (r_size(op) < 4 || bp[0] != bt_num_array_value)
+ return_error(gs_error_typecheck);
+ format = bp[1];
+ if (!num_is_valid(format) ||
+ sdecodeshort(bp + 2, format) !=
+ (r_size(op) - 4) / encoded_number_bytes(format)
+ )
+ return_error(gs_error_rangecheck);
+ }
+ break;
+ case t_array:
+ case t_mixedarray:
+ case t_shortarray:
+ format = num_array;
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ check_read(*op);
+ return format;
+}
+
+/* Get the number of elements in an encoded number array/string. */
+uint
+num_array_size(const ref * op, int format)
+{
+ return (format == num_array ? r_size(op) :
+ (r_size(op) - 4) / encoded_number_bytes(format));
+}
+
+/* Get an encoded number from an array/string according to the given format. */
+/* Put the value in np->value.{intval,realval}. */
+/* Return t_int if integer, t_real if real, t_null if end of stream, */
+/* or an error if the format is invalid. */
+int
+num_array_get(const gs_memory_t *mem, const ref * op, int format, uint index, ref * np)
+{
+ if (format == num_array) {
+ int code = array_get(mem, op, (long)index, np);
+
+ if (code < 0)
+ return t_null;
+ switch (r_type(np)) {
+ case t_integer:
+ return t_integer;
+ case t_real:
+ return t_real;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ } else {
+ uint nbytes = encoded_number_bytes(format);
+
+ if (index >= (r_size(op) - 4) / nbytes)
+ return t_null;
+ return sdecode_number(op->value.bytes + 4 + index * nbytes,
+ format, np);
+ }
+}
+
+/* Internal routine to decode a number in a given format. */
+/* Same returns as sget_encoded_number. */
+static const double binary_scale[32] = {
+#define EXPN2(n) (0.5 / (1L << (n-1)))
+ 1.0, EXPN2(1), EXPN2(2), EXPN2(3),
+ EXPN2(4), EXPN2(5), EXPN2(6), EXPN2(7),
+ EXPN2(8), EXPN2(9), EXPN2(10), EXPN2(11),
+ EXPN2(12), EXPN2(13), EXPN2(14), EXPN2(15),
+ EXPN2(16), EXPN2(17), EXPN2(18), EXPN2(19),
+ EXPN2(20), EXPN2(21), EXPN2(22), EXPN2(23),
+ EXPN2(24), EXPN2(25), EXPN2(26), EXPN2(27),
+ EXPN2(28), EXPN2(29), EXPN2(30), EXPN2(31)
+#undef EXPN2
+};
+int
+sdecode_number(const byte * str, int format, ref * np)
+{
+ switch (format & 0x170) {
+ case num_int32:
+ case num_int32 + 16:
+ if ((format & 31) == 0) {
+ np->value.intval = sdecodeint32(str, format);
+ return t_integer;
+ } else {
+ np->value.realval =
+ (double)sdecodeint32(str, format) *
+ binary_scale[format & 31];
+ return t_real;
+ }
+ case num_int16:
+ if ((format & 15) == 0) {
+ np->value.intval = sdecodeshort(str, format);
+ return t_integer;
+ } else {
+ np->value.realval =
+ sdecodeshort(str, format) *
+ binary_scale[format & 15];
+ return t_real;
+ }
+ case num_float:
+ {
+ float fval;
+ int code = sdecode_float(str, format, &fval);
+
+ if (code < 0)
+ return code;
+ np->value.realval = fval;
+ return t_real;
+ }
+ default:
+ return_error(gs_error_syntaxerror); /* invalid format?? */
+ }
+}
+
+/* ------ Decode number ------ */
+
+/* Decode encoded numbers from a string according to format. */
+
+/* Decode a (16-bit, signed or unsigned) short. */
+uint
+sdecodeushort(const byte * p, int format)
+{
+ int a = p[0], b = p[1];
+
+ return (num_is_lsb(format) ? (b << 8) + a : (a << 8) + b);
+}
+int
+sdecodeshort(const byte * p, int format)
+{
+ int v = (int)sdecodeushort(p, format);
+
+ return (v & 0x7fff) - (v & 0x8000);
+}
+
+/* Decode a (32-bit, signed) long. */
+int
+sdecodeint32(const byte * p, int format)
+{
+ int a = p[0], b = p[1], c = p[2], d = p[3];
+ int v = (num_is_lsb(format) ?
+ ((int)d << 24) + ((int)c << 16) + (b << 8) + a :
+ ((int)a << 24) + ((int)b << 16) + (c << 8) + d);
+ return v;
+}
+
+/* Decode a 32-bit number. Return the resukt through a pointer */
+/* to work around a gcc 4.2.1 bug on PowerPC, bug 689586 */
+static void
+sdecodebits32(const byte * p, int format, bits32 *v)
+{
+ int a = p[0], b = p[1], c = p[2], d = p[3];
+ *v = (num_is_lsb(format) ?
+ ((long)d << 24) + ((long)c << 16) + (b << 8) + a :
+ ((long)a << 24) + ((long)b << 16) + (c << 8) + d);
+
+}
+
+/* Decode a float. We assume that native floats occupy 32 bits. */
+/* If the float is an IEEE NaN or Inf, return gs_error_undefinedresult. */
+int
+sdecode_float(const byte * p, int format, float *pfnum)
+{
+ bits32 lnum;
+
+ if ((format & ~(num_msb | num_lsb)) == num_float_native) {
+ /*
+ * Just read 4 bytes and interpret them as a float, ignoring
+ * any indication of byte ordering.
+ */
+ memcpy(pfnum, p, 4);
+#if !ARCH_FLOATS_ARE_IEEE
+ return 0; /* no way to check for anomalies */
+#endif
+ lnum = *(bits32 *)pfnum;
+ } else {
+ sdecodebits32(p, format, &lnum);
+
+#if !ARCH_FLOATS_ARE_IEEE
+ {
+ /* We know IEEE floats take 32 bits. */
+ /* Convert IEEE float to native float. */
+ int sign_expt = lnum >> 23;
+ int expt = sign_expt & 0xff;
+ long mant = lnum & 0x7fffff;
+ float fnum;
+
+ if (expt == 0 && mant == 0)
+ fnum = 0;
+ else if (expt == 0xff)
+ return_error(gs_error_undefinedresult); /* Inf or NaN */
+ else {
+ mant += 0x800000;
+ fnum = (float)ldexp((float)mant, expt - 127 - 23);
+ }
+ if (sign_expt & 0x100)
+ fnum = -fnum;
+ *pfnum = fnum;
+ return 0; /* checked for Infs and NaNs above */
+ }
+#else
+ *pfnum = *(float *)&lnum;
+#endif
+ }
+ /*
+ * Unfortunately, there is no portable way for testing whether a float
+ * is a NaN or Inf. Do it "by hand" if the input representation is
+ * IEEE (which is the case if control arrives here).
+ */
+ if (!(~lnum & 0x7f800000)) /* i.e. exponent all 1's */
+ return_error(gs_error_undefinedresult); /* Inf or NaN */
+ return 0;
+}
diff --git a/psi/ibnum.h b/psi/ibnum.h
new file mode 100644
index 000000000..01eb8f54a
--- /dev/null
+++ b/psi/ibnum.h
@@ -0,0 +1,80 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Encoded number definitions and support */
+/* Requires stream.h */
+
+#ifndef ibnum_INCLUDED
+# define ibnum_INCLUDED
+
+/*
+ * There is a bug in all Adobe interpreters that causes them to byte-swap
+ * native reals in binary object sequences iff the native real format is
+ * IEEE. We emulate this bug (it will be added to the PLRM errata at some
+ * point), but under a conditional so that it is clear where this is being
+ * done.
+ */
+#define BYTE_SWAP_IEEE_NATIVE_REALS 1
+
+/*
+ * Define the byte that begins an encoded number string.
+ * (This is the same as the value of bt_num_array in btoken.h.)
+ */
+#define bt_num_array_value 149
+
+/*
+ * Define the homogenous number array formats. The default for numbers is
+ * big-endian. Note that these values are defined by the PostScript
+ * Language Reference Manual: they are not arbitrary.
+ */
+#define num_int32 0 /* [0..31] */
+#define num_int16 32 /* [32..47] */
+#define num_float 48
+#define num_float_IEEE num_float
+/* Note that num_msb / num_lsb is ignored for num_float_native. */
+#define num_float_native (num_float + 1)
+#define num_msb 0
+#define num_lsb 128
+#define num_is_lsb(format) ((format) >= num_lsb)
+#define num_is_valid(format) (((format) & 127) <= 49)
+/*
+ * Special "format" for reading from an array.
+ * num_msb/lsb is not used in this case.
+ */
+#define num_array 256
+/* Define the number of bytes for a given format of encoded number. */
+extern const byte enc_num_bytes[]; /* in ibnum.c */
+
+#define enc_num_bytes_values\
+ 4, 4, 2, 4, 0, 0, 0, 0,\
+ 4, 4, 2, 4, 0, 0, 0, 0,\
+ sizeof(ref)
+#define encoded_number_bytes(format)\
+ (enc_num_bytes[(format) >> 4])
+
+/* Read from an array or encoded number string. */
+int num_array_format(const ref *); /* returns format or error */
+uint num_array_size(const ref *, int);
+int num_array_get(const gs_memory_t *mem, const ref *, int, uint, ref *);
+
+/* Decode a number from a string with appropriate byte swapping. */
+int sdecode_number(const byte *, int, ref *);
+int sdecodeshort(const byte *, int);
+uint sdecodeushort(const byte *, int);
+int sdecodeint32(const byte *, int);
+int sdecode_float(const byte *, int, float *);
+
+#endif /* ibnum_INCLUDED */
diff --git a/psi/ichar.h b/psi/ichar.h
new file mode 100644
index 000000000..cfed94f89
--- /dev/null
+++ b/psi/ichar.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Character rendering operator definitions and support procedures */
+/* Requires gstext.h */
+
+#ifndef ichar_INCLUDED
+# define ichar_INCLUDED
+
+/*
+ * All the character rendering operators use the execution stack
+ * for loop control -- see estack.h for details.
+ * The information pushed by these operators is as follows:
+ * the enumerator (t_struct, a gs_text_enum_t);
+ * a slot for the procedure for kshow or cshow (probably t_array);
+ * a slot for the saved o-stack depth for cshow or stringwidth,
+ * and for error recovery (t_integer);
+ * a slot for the saved d-stack depth ditto (t_integer);
+ * a slot for the saved gstate level ditto (t_integer);
+ * a slot for saving the font during the cshow proc (t_struct);
+ * a slot for saving the root font during the cshow proc (t_struct);
+ * the procedure to be called at the end of the enumeration
+ * (t_operator, but called directly, not by the interpreter);
+ * the usual e-stack mark (t_null).
+ */
+#define snumpush 9
+#define esenum(ep) r_ptr(ep, gs_text_enum_t)
+#define senum esenum(esp)
+#define esslot(ep) ((ep)[-1])
+#define sslot esslot(esp)
+#define esodepth(ep) ((ep)[-2])
+#define sodepth esodepth(esp)
+#define esddepth(ep) ((ep)[-3])
+#define sddepth esddepth(esp)
+#define esgslevel(ep) ((ep)[-4])
+#define sgslevel esgslevel(esp)
+#define essfont(ep) ((ep)[-5])
+#define ssfont essfont(esp)
+#define esrfont(ep) ((ep)[-6])
+#define srfont esrfont(esp)
+#define eseproc(ep) ((ep)[-7])
+#define seproc eseproc(esp)
+
+/* Procedures exported by zchar.c for zchar*.c. */
+gs_text_enum_t *op_show_find(i_ctx_t *);
+int op_show_setup(i_ctx_t *, os_ptr);
+int op_show_enum_setup(i_ctx_t *);
+int op_show_finish_setup(i_ctx_t *, gs_text_enum_t *, int, op_proc_t);
+int op_show_continue(i_ctx_t *);
+int op_show_continue_pop(i_ctx_t *, int);
+int op_show_continue_dispatch(i_ctx_t *, int, int);
+int op_show_free(i_ctx_t *, int);
+void glyph_ref(const gs_memory_t *mem, gs_glyph, ref *);
+int finish_stringwidth(i_ctx_t *);
+
+/* Exported by zchar.c for zcharout.c */
+bool zchar_show_width_only(const gs_text_enum_t *);
+int zsetcachedevice(i_ctx_t *);
+int zsetcachedevice2(i_ctx_t *);
+
+#endif /* ichar_INCLUDED */
diff --git a/psi/ichar1.h b/psi/ichar1.h
new file mode 100644
index 000000000..44d599f9c
--- /dev/null
+++ b/psi/ichar1.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Type 1 / Type 2 character rendering operator procedures */
+
+#ifndef ichar1_INCLUDED
+# define ichar1_INCLUDED
+
+#ifndef gs_font_type1_DEFINED
+# define gs_font_type1_DEFINED
+typedef struct gs_font_type1_s gs_font_type1;
+#endif
+
+/* ---------------- Public ---------------- */
+
+/* Render a Type 1 or Type 2 outline. */
+/* This is the entire implementation of the .type1/2execchar operators. */
+int charstring_execchar(i_ctx_t *i_ctx_p, int font_type_mask);
+
+/* ---------------- Internal ---------------- */
+
+/*
+ * Get a Type 1 or Type 2 glyph outline. This is the glyph_outline
+ * procedure for the font.
+ */
+font_proc_glyph_outline(zchar1_glyph_outline);
+
+/*
+ * Get a glyph outline given a CharString. The glyph_outline procedure
+ * for CIDFontType 0 fonts uses this.
+ */
+int zcharstring_outline(gs_font_type1 *pfont, int WMode, const ref *pgref,
+ const gs_glyph_data_t *pgd,
+ const gs_matrix *pmat, gx_path *ppath, double sbw[4]);
+
+int
+z1_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
+ int members, gs_glyph_info_t *info);
+
+int z1_glyph_info_generic(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
+ int members, gs_glyph_info_t *info, font_proc_glyph_info((*proc)),
+ int wmode);
+
+int z1_set_cache(i_ctx_t *i_ctx_p, gs_font_base *pbfont, ref *cnref,
+ gs_glyph glyph, op_proc_t cont, op_proc_t *exec_cont);
+
+#endif /* ichar1_INCLUDED */
diff --git a/psi/icharout.h b/psi/icharout.h
new file mode 100644
index 000000000..fac85e1f2
--- /dev/null
+++ b/psi/icharout.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to zcharout.c */
+
+#ifndef icharout_INCLUDED
+# define icharout_INCLUDED
+
+/* Execute an outline defined by a PostScript procedure. */
+int zchar_exec_char_proc(i_ctx_t *);
+
+/*
+ * Get the metrics for a character from the Metrics dictionary of a base
+ * font. If present, store the l.s.b. in psbw[0,1] and the width in
+ * psbw[2,3].
+ */
+typedef enum {
+ metricsNone = 0,
+ metricsWidthOnly = 1,
+ metricsSideBearingAndWidth = 2
+} metrics_present;
+int /*metrics_present*/
+ zchar_get_metrics(const gs_font_base * pbfont, const ref * pcnref,
+ double psbw[4]);
+
+/* Get the vertical metrics for a character from Metrics2, if present. */
+int /*metrics_present*/
+ zchar_get_metrics2(const gs_font_base * pbfont, const ref * pcnref,
+ double pwv[4]);
+/* Get CDevProc. */
+bool zchar_get_CDevProc(const gs_font_base * pbfont, ref **ppcdevproc);
+
+/*
+ * Consult Metrics2 and CDevProc, and call setcachedevice[2]. Return
+ * o_push_estack if we had to call a CDevProc, or if we are skipping the
+ * rendering process (only getting the metrics).
+ */
+int zchar_set_cache(i_ctx_t *i_ctx_p, const gs_font_base * pbfont,
+ const ref * pcnref, const double psb[2],
+ const double pwidth[2], const gs_rect * pbbox,
+ op_proc_t cont, op_proc_t *exec_cont,
+ const double Metrics2_sbw_default[4]);
+
+/*
+ * Get the CharString data corresponding to a glyph. Return typecheck
+ * if it isn't a string.
+ */
+int zchar_charstring_data(gs_font *font, const ref *pgref,
+ gs_glyph_data_t *pgd);
+
+/*
+ * Enumerate the next glyph from a directory. This is essentially a
+ * wrapper around dict_first/dict_next to implement the enumerate_glyph
+ * font procedure.
+ */
+int zchar_enumerate_glyph(const gs_memory_t *mem, const ref *prdict, int *pindex, gs_glyph *pglyph);
+
+#endif /* icharout_INCLUDED */
diff --git a/psi/icid.h b/psi/icid.h
new file mode 100644
index 000000000..0d5716918
--- /dev/null
+++ b/psi/icid.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to zcid.c, zfcid0.c */
+
+#ifndef icid_INCLUDED
+# define icid_INCLUDED
+
+#ifndef gs_cid_system_info_DEFINED
+# define gs_cid_system_info_DEFINED
+typedef struct gs_cid_system_info_s gs_cid_system_info_t;
+#endif
+
+/* Get the information from a CIDSystemInfo dictionary. */
+int cid_system_info_param(gs_cid_system_info_t *, const ref *);
+
+/* Convert a CID into TT char code or to TT glyph index, using SubstNWP. */
+/* Returns 1 if a glyph presents, 0 if not, <0 if error. */
+int cid_to_TT_charcode(const gs_memory_t *mem,
+ const ref *Decoding, const ref *TT_cmap,
+ const ref *SubstNWP,
+ uint nCID, uint *c, ref *src_type, ref *dst_type);
+
+/* Create a CIDMap from a True Type cmap, Decoding and SubstNWP. */
+int cid_fill_CIDMap(const gs_memory_t *mem, const ref *Decoding, const ref *TT_cmap, const ref *SubstNWP,
+ int GDBytes, ref *CIDMap);
+/* Create an identity CIDMap. */
+int cid_fill_Identity_CIDMap(const gs_memory_t *mem, ref *CIDMap);
+
+/* <cid9font> <cid> .type9mapcid <charstring> <font_index> */
+int ztype9mapcid(i_ctx_t *i_ctx_p);
+
+#endif /* icid_INCLUDED */
diff --git a/psi/icie.h b/psi/icie.h
new file mode 100644
index 000000000..7fcf6a043
--- /dev/null
+++ b/psi/icie.h
@@ -0,0 +1,96 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Internal definitions for interpreter CIE color handling */
+
+#ifndef icie_INCLUDED
+# define icie_INCLUDED
+
+/*
+ * All of the routines below are exported by zcie.c for zcrd.c,
+ * except for cie_cache_joint which is exported by zcrd.c for zcie.c.
+ */
+
+/* ------ Parameter acquisition ------ */
+
+/* Get a range array parameter from a dictionary. */
+/* We know that count <= 4. */
+int dict_ranges_param(const gs_memory_t *mem,
+ const ref * pdref, const char *kstr, int count,
+ gs_range * prange);
+
+/* Get 3 ranges from a dictionary. */
+int dict_range3_param(const gs_memory_t *mem,
+ const ref *pdref, const char *kstr,
+ gs_range3 *prange3);
+
+/* Get a 3x3 matrix parameter from a dictionary. */
+int dict_matrix3_param(const gs_memory_t *mem, const ref *pdref,
+ const char *kstr,
+ gs_matrix3 *pmat3);
+
+/* Get an array of procedures from a dictionary. */
+/* We know count <= countof(empty_procs). */
+int dict_proc_array_param(const gs_memory_t *mem, const ref *pdict,
+ const char *kstr,
+ uint count, ref * pparray);
+
+/* Get 3 procedures from a dictionary. */
+int dict_proc3_param(const gs_memory_t *mem, const ref *pdref,
+ const char *kstr, ref proc3[3]);
+
+/* Get WhitePoint and BlackPoint values. */
+int cie_points_param(const gs_memory_t *mem,
+ const ref * pdref, gs_cie_wb * pwb);
+
+/* Process a 3- or 4-dimensional lookup table from a dictionary. */
+/* The caller has set pclt->n and pclt->m. */
+/* ptref is known to be a readable array of size at least n+1. */
+int cie_table_param(const ref * ptable, gx_color_lookup_table * pclt,
+ const gs_memory_t * mem);
+
+/* ------ Internal routines ------ */
+
+int cie_set_finish(i_ctx_t *, gs_color_space *,
+ const ref_cie_procs *, int, int);
+
+int cie_cache_push_finish(i_ctx_t *i_ctx_p, op_proc_t finish_proc,
+ gs_ref_memory_t * imem, void *data);
+int cie_prepare_cache(i_ctx_t *i_ctx_p, const gs_range * domain,
+ const ref * proc, cie_cache_floats * pcache,
+ void *container, gs_ref_memory_t * imem,
+ client_name_t cname);
+int cie_prepare_caches_4(i_ctx_t *i_ctx_p, const gs_range * domains,
+ const ref * procs,
+ cie_cache_floats * pc0,
+ cie_cache_floats * pc1,
+ cie_cache_floats * pc2,
+ cie_cache_floats * pc3 /* may be 0 */,
+ void *container,
+ gs_ref_memory_t * imem, client_name_t cname);
+#define cie_prepare_cache3(p,d3,p3,c3,pcie,imem,cname)\
+ cie_prepare_caches_4(p, (d3)->ranges, p3,\
+ &(c3)->floats, &(c3)[1].floats, &(c3)[2].floats,\
+ NULL, pcie, imem, cname)
+#define cie_prepare_cache4(p,d4,p4,c4,pcie,imem,cname)\
+ cie_prepare_caches_4(p, (d4)->ranges, p4,\
+ &(c4)->floats, &(c4)[1].floats, &(c4)[2].floats,\
+ &(c4)[3].floats, pcie, imem, cname)
+
+int cie_cache_joint(i_ctx_t *, const ref_cie_render_procs *,
+ const gs_cie_common *, gs_state *);
+
+#endif /* icie_INCLUDED */
diff --git a/psi/icolor.h b/psi/icolor.h
new file mode 100644
index 000000000..33f64ee08
--- /dev/null
+++ b/psi/icolor.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Declarations for transfer function & similar cache remapping */
+
+#ifndef icolor_INCLUDED
+# define icolor_INCLUDED
+
+/*
+ * Define the number of stack slots needed for zcolor_remap_one.
+ * The client is responsible for doing check_e/ostack or the equivalent
+ * before calling zcolor_remap_one.
+ */
+extern const int zcolor_remap_one_ostack;
+extern const int zcolor_remap_one_estack;
+
+/*
+ * Schedule the sampling and reloading of a cache. Note that if
+ * zcolor_remap_one recognize the procedure as being of a special form, it
+ * may not schedule anything, but it still returns o_push_estack. (This is
+ * a change as of release 5.95; formerly, it returned 0 in this case.)
+ */
+int zcolor_remap_one(i_ctx_t *, const ref *, gx_transfer_map *,
+ const gs_state *, op_proc_t);
+
+/* Reload a cache with entries in [0..1] after sampling. */
+int zcolor_remap_one_finish(i_ctx_t *);
+
+/* Reload a cache with entries in [-1..1] after sampling. */
+int zcolor_remap_one_signed_finish(i_ctx_t *);
+
+/* Recompute the effective transfer functions and invalidate the current */
+/* color after cache reloading. */
+int zcolor_reset_transfer(i_ctx_t *);
+
+/* Invalidate the current color after cache reloading. */
+int zcolor_remap_color(i_ctx_t *);
+
+#endif /* icolor_INCLUDED */
diff --git a/psi/iconf.c b/psi/iconf.c
new file mode 100644
index 000000000..2ddeb8686
--- /dev/null
+++ b/psi/iconf.c
@@ -0,0 +1,115 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Configuration-dependent tables and initialization for interpreter */
+#include "stdio_.h" /* stdio for stream.h */
+#include "gstypes.h"
+#include "gsmemory.h" /* for iminst.h */
+#include "gconfigd.h"
+#include "iref.h"
+#include "ivmspace.h"
+#include "opdef.h"
+#include "ifunc.h"
+#include "iapi.h"
+#include "iminst.h"
+#include "iplugin.h"
+#include "gxiodev.h"
+
+/* Declare IODevices as extern. */
+#define io_device_(iodev) extern const gx_io_device iodev;
+#include "iconfig.h"
+#undef io_device_
+
+/* Define the default values for an interpreter instance. */
+const gs_main_instance gs_main_instance_init_values =
+{gs_main_instance_default_init_values};
+
+/* Set up the .ps file name string. */
+#define psfile_(fns,len) fns "\000"
+const byte gs_init_files[] =
+#include "iconfig.h"
+#include "gconf.h"
+;
+#undef psfile_
+const uint gs_init_files_sizeof = sizeof(gs_init_files);
+
+/* Set up the emulator name string. */
+#define emulator_(ems,len) ems "\000"
+const byte gs_emulators[] =
+#include "iconfig.h"
+#include "gconf.h"
+;
+#undef emulator_
+const uint gs_emulators_sizeof = sizeof(gs_emulators);
+
+/* Set up the function type table similarly. */
+#define function_type_(i,proc) extern build_function_proc(proc);
+#include "iconfig.h"
+#include "gconf.h"
+#undef function_type_
+#define function_type_(i,proc) {i,proc},
+const build_function_type_t build_function_type_table[] = {
+#include "iconfig.h"
+#include "gconf.h"
+
+ {0}
+};
+#undef function_type_
+const uint build_function_type_table_count =
+ countof(build_function_type_table) - 1;
+
+/* Initialize the operators. */
+ /* Declare the externs. */
+#define oper_(xx_op_defs) extern const op_def xx_op_defs[];
+oper_(interp1_op_defs) /* Interpreter operators */
+oper_(interp2_op_defs) /* ibid. */
+#include "iconfig.h"
+#undef oper_
+const op_def *const op_defs_all[] = {
+#define oper_(defs) defs,
+ oper_(interp1_op_defs) /* Interpreter operators */
+ oper_(interp2_op_defs) /* ibid. */
+#include "iconfig.h"
+#include "gconf.h"
+#undef oper_
+ 0
+};
+const uint op_def_count = (countof(op_defs_all) - 1) * OP_DEFS_MAX_SIZE;
+
+/* Set up the plugin table. */
+
+#define plugin_(proc) extern plugin_instantiation_proc(proc);
+#include "iconfig.h"
+#include "gconf.h"
+#undef plugin_
+
+extern_i_plugin_table();
+#define plugin_(proc) proc,
+const i_plugin_instantiation_proc i_plugin_table[] = {
+#include "iconfig.h"
+#include "gconf.h"
+ 0
+};
+#undef plugin_
+
+#define io_device_(iodev) &iodev,
+const gx_io_device *const i_io_device_table[] = {
+#include "iconfig.h"
+ 0
+};
+#undef io_device_
+/* We must use unsigned here, not uint. See gscdefs.h. */
+const unsigned i_io_device_table_count = countof(i_io_device_table) - 1;
diff --git a/psi/iconf.h b/psi/iconf.h
new file mode 100644
index 000000000..1980e5bdd
--- /dev/null
+++ b/psi/iconf.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Collected imports for interpreter configuration and initialization */
+
+#ifndef iconf_INCLUDED
+#define iconf_INCLUDED
+/* ------ Imported data ------ */
+
+/* Configuration information imported from iccinit[01].c */
+extern const byte gs_init_string[];
+extern const uint gs_init_string_sizeof;
+
+/* Configuration information imported from iconf.c */
+extern const byte gs_init_files[];
+extern const uint gs_init_files_sizeof;
+extern const byte gs_emulators[];
+extern const uint gs_emulators_sizeof;
+
+extern gx_io_device *i_io_device_table[];
+extern const unsigned i_io_device_table_count;
+
+#endif /* iconf_INCLUDED */
diff --git a/psi/icontext.c b/psi/icontext.c
new file mode 100644
index 000000000..0d440c069
--- /dev/null
+++ b/psi/icontext.c
@@ -0,0 +1,350 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Context state operations */
+#include "ghost.h"
+#include "gsstruct.h" /* for gxalloc.h */
+#include "gxalloc.h"
+#include "ierrors.h"
+#include "stream.h" /* for files.h */
+#include "files.h"
+#include "idict.h"
+#include "igstate.h"
+#include "icontext.h"
+#include "interp.h"
+#include "isave.h"
+#include "dstack.h"
+#include "estack.h"
+#include "store.h"
+
+/* Declare the GC descriptors for embedded objects. */
+extern_st(st_gs_dual_memory);
+extern_st(st_ref_stack);
+extern_st(st_dict_stack);
+extern_st(st_exec_stack);
+extern_st(st_op_stack);
+
+/* GC descriptors */
+static
+CLEAR_MARKS_PROC(context_state_clear_marks)
+{
+ gs_context_state_t *const pcst = vptr;
+
+ r_clear_attrs(&pcst->stdio[0], l_mark);
+ r_clear_attrs(&pcst->stdio[1], l_mark);
+ r_clear_attrs(&pcst->stdio[2], l_mark);
+ r_clear_attrs(&pcst->error_object, l_mark);
+ r_clear_attrs(&pcst->userparams, l_mark);
+ r_clear_attrs(&pcst->op_array_table_global.table, l_mark);
+ r_clear_attrs(&pcst->op_array_table_local.table, l_mark);
+}
+static
+ENUM_PTRS_WITH(context_state_enum_ptrs, gs_context_state_t *pcst) {
+ index -= 10;
+ if (index < st_gs_dual_memory_num_ptrs)
+ return ENUM_USING(st_gs_dual_memory, &pcst->memory,
+ sizeof(pcst->memory), index);
+ index -= st_gs_dual_memory_num_ptrs;
+ if (index < st_dict_stack_num_ptrs)
+ return ENUM_USING(st_dict_stack, &pcst->dict_stack,
+ sizeof(pcst->dict_stack), index);
+ index -= st_dict_stack_num_ptrs;
+ if (index < st_exec_stack_num_ptrs)
+ return ENUM_USING(st_exec_stack, &pcst->exec_stack,
+ sizeof(pcst->exec_stack), index);
+ index -= st_exec_stack_num_ptrs;
+ return ENUM_USING(st_op_stack, &pcst->op_stack,
+ sizeof(pcst->op_stack), index);
+ }
+ ENUM_PTR(0, gs_context_state_t, pgs);
+ case 1: ENUM_RETURN_REF(&pcst->stdio[0]);
+ case 2: ENUM_RETURN_REF(&pcst->stdio[1]);
+ case 3: ENUM_RETURN_REF(&pcst->stdio[2]);
+ case 4: ENUM_RETURN_REF(&pcst->error_object);
+ case 5: ENUM_RETURN_REF(&pcst->userparams);
+ ENUM_PTR(6, gs_context_state_t, op_array_table_global.nx_table);
+ ENUM_PTR(7, gs_context_state_t, op_array_table_local.nx_table);
+ case 8: ENUM_RETURN_REF(&pcst->op_array_table_global.table);
+ case 9: ENUM_RETURN_REF(&pcst->op_array_table_local.table);
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(context_state_reloc_ptrs, gs_context_state_t *pcst);
+ RELOC_PTR(gs_context_state_t, pgs);
+ RELOC_USING(st_gs_dual_memory, &pcst->memory, sizeof(pcst->memory));
+ /******* WHY DON'T WE CLEAR THE l_mark OF stdio? ******/
+ RELOC_REF_VAR(pcst->stdio[0]);
+ RELOC_REF_VAR(pcst->stdio[1]);
+ RELOC_REF_VAR(pcst->stdio[2]);
+ RELOC_REF_VAR(pcst->error_object);
+ r_clear_attrs(&pcst->error_object, l_mark);
+ RELOC_REF_VAR(pcst->userparams);
+ r_clear_attrs(&pcst->userparams, l_mark);
+ RELOC_PTR(gs_context_state_t, op_array_table_global.nx_table);
+ RELOC_PTR(gs_context_state_t, op_array_table_local.nx_table);
+ RELOC_REF_VAR(pcst->op_array_table_global.table);
+ r_clear_attrs(&pcst->op_array_table_global.table, l_mark);
+ RELOC_REF_VAR(pcst->op_array_table_local.table);
+ r_clear_attrs(&pcst->op_array_table_local.table, l_mark);
+ RELOC_USING(st_dict_stack, &pcst->dict_stack, sizeof(pcst->dict_stack));
+ RELOC_USING(st_exec_stack, &pcst->exec_stack, sizeof(pcst->exec_stack));
+ RELOC_USING(st_op_stack, &pcst->op_stack, sizeof(pcst->op_stack));
+RELOC_PTRS_END
+public_st_context_state();
+
+static int
+no_reschedule(i_ctx_t **pi_ctx_p)
+{
+ return (gs_error_invalidcontext);
+}
+
+/* Allocate the state of a context. */
+int
+context_state_alloc(gs_context_state_t ** ppcst,
+ const ref *psystem_dict,
+ const gs_dual_memory_t * dmem)
+{
+ gs_ref_memory_t *mem = dmem->space_local;
+ gs_context_state_t *pcst = *ppcst;
+ int code;
+ int i;
+
+ if (pcst == 0) {
+ pcst = gs_alloc_struct((gs_memory_t *) mem, gs_context_state_t,
+ &st_context_state, "context_state_alloc");
+ if (pcst == 0)
+ return_error(gs_error_VMerror);
+ }
+ code = gs_interp_alloc_stacks(mem, pcst);
+ if (code < 0)
+ goto x0;
+ /*
+ * We have to initialize the dictionary stack early,
+ * for far-off references to systemdict.
+ */
+ pcst->dict_stack.system_dict = *psystem_dict;
+ pcst->dict_stack.min_size = 0;
+ pcst->dict_stack.userdict_index = 0;
+ pcst->pgs = int_gstate_alloc(dmem);
+ if (pcst->pgs == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto x1;
+ }
+ pcst->memory = *dmem;
+ pcst->language_level = 1;
+ make_false(&pcst->array_packing);
+ make_int(&pcst->binary_object_format, 0);
+ pcst->nv_page_count = 0;
+ pcst->rand_state = rand_state_initial;
+ pcst->usertime_total = 0;
+ pcst->keep_usertime = false;
+ pcst->in_superexec = 0;
+ pcst->plugin_list = 0;
+ make_t(&pcst->error_object, t__invalid);
+ { /*
+ * Create an empty userparams dictionary of the right size.
+ * If we can't determine the size, pick an arbitrary one.
+ */
+ ref *puserparams;
+ uint size;
+ ref *system_dict = &pcst->dict_stack.system_dict;
+
+ if (dict_find_string(system_dict, "userparams", &puserparams) >= 0)
+ size = dict_length(puserparams);
+ else
+ size = 300;
+ code = dict_alloc(pcst->memory.space_local, size, &pcst->userparams);
+ if (code < 0)
+ goto x2;
+ /* PostScript code initializes the user parameters. */
+ }
+ pcst->scanner_options = 0;
+ pcst->LockFilePermissions = false;
+ pcst->starting_arg_file = false;
+ pcst->RenderTTNotdef = true;
+ /* Create and initialize an invalid (closed) stream. */
+ /* Initialize the stream for the sake of the GC, */
+ /* and so it can act as an empty input stream. */
+ {
+ stream *s;
+
+ s = (stream*)gs_alloc_bytes_immovable(mem->non_gc_memory->stable_memory,
+ sizeof(*s),
+ "context_state_alloc");
+ if (s == NULL) {
+ code = gs_error_VMerror;
+ goto x3;
+ }
+ pcst->invalid_file_stream = s;
+
+ s_init(s, NULL);
+ sread_string(s, NULL, 0);
+ s->next = s->prev = 0;
+ s_init_no_id(s);
+ }
+ /* The initial stdio values are bogus.... */
+ make_file(&pcst->stdio[0], a_readonly | avm_invalid_file_entry, 1,
+ pcst->invalid_file_stream);
+ make_file(&pcst->stdio[1], a_all | avm_invalid_file_entry, 1,
+ pcst->invalid_file_stream);
+ make_file(&pcst->stdio[2], a_all | avm_invalid_file_entry, 1,
+ pcst->invalid_file_stream);
+ for (i = countof(pcst->memory.spaces_indexed); --i >= 0;)
+ if (dmem->spaces_indexed[i] != 0)
+ ++(dmem->spaces_indexed[i]->num_contexts);
+ /* The number of interpreter "ticks" between calls on the time_slice_proc.
+ * Currently, the clock ticks before each operator, and at each
+ * procedure return. */
+ pcst->time_slice_ticks = 0x7fff;
+ pcst->reschedule_proc = no_reschedule;
+ pcst->time_slice_proc = no_reschedule;
+ *ppcst = pcst;
+ return 0;
+ x3:/* No need to delete dictionary here, as gc will do it for us. */
+ x2:gs_state_free(pcst->pgs);
+ x1:gs_interp_free_stacks(mem, pcst);
+ x0:if (*ppcst == 0)
+ gs_free_object((gs_memory_t *) mem, pcst, "context_state_alloc");
+ return code;
+}
+
+/* Load the interpreter state from a context. */
+int
+context_state_load(gs_context_state_t * i_ctx_p)
+{
+ gs_ref_memory_t *lmem = iimemory_local;
+ ref *system_dict = systemdict;
+ uint space = r_space(system_dict);
+ dict_stack_t *dstack = &idict_stack;
+ int code;
+
+ /*
+ * Disable save checking, and space check for systemdict, while
+ * copying dictionaries.
+ */
+ alloc_set_not_in_save(idmemory);
+ r_set_space(system_dict, avm_max);
+ /*
+ * Switch references from systemdict to local objects.
+ * userdict.localdicts holds these objects. We could optimize this by
+ * only doing it if we're changing to a different local VM relative to
+ * the same global VM, but the cost is low enough relative to other
+ * things that we don't bother.
+ */
+ {
+ ref_stack_t *rdstack = &dstack->stack;
+ const ref *puserdict =
+ ref_stack_index(rdstack, ref_stack_count(rdstack) - 1 -
+ dstack->userdict_index);
+ ref *plocaldicts;
+
+ if (dict_find_string(puserdict, "localdicts", &plocaldicts) > 0 &&
+ r_has_type(plocaldicts, t_dictionary)
+ ) {
+ dict_copy(plocaldicts, system_dict, dstack);
+ }
+ }
+ /*
+ * Set systemdict.userparams to the saved copy, and then
+ * set the actual user parameters. Note that we must disable both
+ * space checking and save checking while doing this. Also,
+ * we must do this after copying localdicts (if required), because
+ * userparams also appears in localdicts.
+ */
+ code = dict_put_string(system_dict, "userparams", &i_ctx_p->userparams,
+ dstack);
+ if (code >= 0)
+ code = set_user_params(i_ctx_p, &i_ctx_p->userparams);
+ r_set_space(system_dict, space);
+ if (lmem->save_level > 0)
+ alloc_set_in_save(idmemory);
+ estack_clear_cache(&iexec_stack);
+ dstack_set_top(&idict_stack);
+ return code;
+}
+
+/* Store the interpreter state in a context. */
+int
+context_state_store(gs_context_state_t * pcst)
+{
+ ref_stack_cleanup(&pcst->dict_stack.stack);
+ ref_stack_cleanup(&pcst->exec_stack.stack);
+ ref_stack_cleanup(&pcst->op_stack.stack);
+ /*
+ * The user parameters in systemdict.userparams are kept
+ * up to date by PostScript code, but we still need to save
+ * systemdict.userparams to get the correct l_new flag.
+ */
+ {
+ ref *puserparams;
+ /* We need i_ctx_p for access to the d_stack. */
+ i_ctx_t *i_ctx_p = pcst;
+
+ if (dict_find_string(systemdict, "userparams", &puserparams) < 0)
+ return_error(gs_error_Fatal);
+ pcst->userparams = *puserparams;
+ }
+ return 0;
+}
+
+/* Free the contents of the state of a context, always to its local VM. */
+/* Return a mask of which of its VMs, if any, we freed. */
+int
+context_state_free(gs_context_state_t * pcst)
+{
+ gs_ref_memory_t *mem = pcst->memory.space_local;
+ int freed = 0;
+ int i;
+
+ /* Invalid file stream is always in static space, so needs to be done
+ * separately. */
+ gs_free_object(mem->non_gc_memory->stable_memory, pcst->invalid_file_stream, "context_state_alloc");
+ /*
+ * If this context is the last one referencing a particular VM
+ * (local / global / system), free the entire VM space;
+ * otherwise, just free the context-related structures.
+ */
+ for (i = countof(pcst->memory.spaces_indexed); --i >= 0;) {
+ if (pcst->memory.spaces_indexed[i] != 0 &&
+ !--(pcst->memory.spaces_indexed[i]->num_contexts)
+ ) {
+/****** FREE THE ENTIRE SPACE ******/
+ freed |= 1 << i;
+ }
+ }
+ /*
+ * If we freed any spaces at all, we must have freed the local
+ * VM where the context structure and its substructures were
+ * allocated.
+ */
+ if (freed)
+ return freed;
+ {
+ gs_state *pgs = pcst->pgs;
+
+ gs_grestoreall(pgs);
+ /* Patch the saved pointer so we can do the last grestore. */
+ {
+ gs_state *saved = gs_state_saved(pgs);
+
+ gs_state_swap_saved(saved, saved);
+ }
+ gs_grestore(pgs);
+ gs_state_swap_saved(pgs, (gs_state *) 0);
+ gs_state_free(pgs);
+ }
+/****** FREE USERPARAMS ******/
+ gs_interp_free_stacks(mem, pcst);
+ return 0;
+}
diff --git a/psi/icontext.h b/psi/icontext.h
new file mode 100644
index 000000000..1521cacd5
--- /dev/null
+++ b/psi/icontext.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Externally visible context state */
+/* Requires iref.h, stdio_.h */
+
+#ifndef icontext_INCLUDED
+# define icontext_INCLUDED
+
+#include "gsstype.h" /* for extern_st */
+#include "icstate.h"
+
+/* Declare the GC descriptor for context states. */
+extern_st(st_context_state);
+
+/*
+ * Define the procedure for resetting user parameters when switching
+ * contexts. This is defined in either zusparam.c or inouparm.c.
+ */
+extern int set_user_params(i_ctx_t *i_ctx_p, const ref * paramdict);
+
+/* Allocate the state of a context, always in local VM. */
+/* If *ppcst == 0, allocate the state object as well. */
+int context_state_alloc(gs_context_state_t ** ppcst,
+ const ref *psystem_dict,
+ const gs_dual_memory_t * dmem);
+
+/* Load the state of the interpreter from a context. */
+/* The argument is not const because caches may be updated. */
+int context_state_load(gs_context_state_t *);
+
+/* Store the state of the interpreter into a context. */
+int context_state_store(gs_context_state_t *);
+
+/* Free the contents of the state of a context, always to its local VM. */
+/* Return a mask of which of its VMs, if any, we freed. */
+int context_state_free(gs_context_state_t *);
+
+#endif /* icontext_INCLUDED */
diff --git a/psi/icremap.h b/psi/icremap.h
new file mode 100644
index 000000000..58a495d39
--- /dev/null
+++ b/psi/icremap.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter color remapping structure */
+
+#ifndef icremap_INCLUDED
+# define icremap_INCLUDED
+
+#include "gsccolor.h"
+
+/*
+ * Define the structure used to communicate information back to the
+ * interpreter for color remapping. Pattern remapping doesn't use the
+ * tint values, DeviceN remapping does.
+ */
+#ifndef int_remap_color_info_DEFINED
+# define int_remap_color_info_DEFINED
+typedef struct int_remap_color_info_s int_remap_color_info_t;
+#endif
+struct int_remap_color_info_s {
+ op_proc_t proc; /* remapping procedure */
+ float tint[MAX_COMPONENTS_IN_DEVN]; /* must match limitcheck in psi/zcolor.c: validatedevicenspace */
+};
+
+#define private_st_int_remap_color_info() /* in zgstate.c */\
+ gs_private_st_simple(st_int_remap_color_info, int_remap_color_info_t,\
+ "int_remap_color_info_t")
+
+#endif /* icremap_INCLUDED */
diff --git a/psi/icsmap.h b/psi/icsmap.h
new file mode 100644
index 000000000..446b1e677
--- /dev/null
+++ b/psi/icsmap.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to shared routines for loading the cached color space maps. */
+
+#ifndef icsmap_INCLUDED
+# define icsmap_INCLUDED
+
+/*
+ * Set up to load a cached map for an Indexed or substituted Separation
+ * color space. The implementation is in zcsindex.c. When the map1
+ * procedure is called, the following structure is on the e_stack:
+ */
+#define num_csme 5
+# define csme_num_components (-4) /* t_integer */
+# define csme_map (-3) /* t_struct (bytes) */
+# define csme_proc (-2) /* -procedure- */
+# define csme_hival (-1) /* t_integer */
+# define csme_index 0 /* t_integer */
+/*
+ * Note that the underlying color space parameter is a direct space, not a
+ * base space, since the underlying space of an Indexed color space may be
+ * a Separation or DeviceN space.
+ */
+int zcs_begin_map(i_ctx_t *i_ctx_p, gs_indexed_map ** pmap,
+ const ref * pproc, int num_entries,
+ const gs_color_space * base_space,
+ op_proc_t map1);
+
+#endif /* icsmap_INCLUDED */
diff --git a/psi/icstate.h b/psi/icstate.h
new file mode 100644
index 000000000..ea09bb871
--- /dev/null
+++ b/psi/icstate.h
@@ -0,0 +1,91 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Externally visible context state */
+
+#ifndef icstate_INCLUDED
+# define icstate_INCLUDED
+
+#include "imemory.h"
+#include "iref.h"
+#include "idsdata.h"
+#include "iesdata.h"
+#include "iosdata.h"
+#include "stream.h"
+#include "opdef.h"
+
+/*
+ * Define the externally visible state of an interpreter context.
+ * If we aren't supporting Display PostScript features, there is only
+ * a single context.
+ */
+#ifndef gs_context_state_t_DEFINED
+# define gs_context_state_t_DEFINED
+typedef struct gs_context_state_s gs_context_state_t;
+#endif
+
+#ifndef gs_file_path_ptr_DEFINED
+# define gs_file_path_ptr_DEFINED
+typedef struct gs_file_path_s *gs_file_path_ptr;
+#endif
+
+struct gs_context_state_s {
+ gs_state *pgs;
+ gs_dual_memory_t memory;
+ int language_level;
+ ref array_packing; /* t_boolean */
+ ref binary_object_format; /* t_integer */
+ long nv_page_count; /* non-decreasing page counter for /PageCount */
+ /* It's updated only by currentsystemparams .*/
+ long rand_state; /* (not in Red Book) */
+ long usertime_total; /* total accumulated usertime, */
+ /* not counting current time if running */
+ bool keep_usertime; /* true if context ever executed usertime */
+ int in_superexec; /* # of levels of superexec */
+ /* View clipping is handled in the graphics state. */
+ ref error_object; /* t__invalid or error object from operator */
+ ref userparams; /* t_dictionary */
+ int scanner_options; /* derived from userparams */
+ bool LockFilePermissions; /* accessed from userparams */
+ bool starting_arg_file; /* starting a file specified in command line. */
+ bool RenderTTNotdef; /* accessed from userparams */
+ gs_file_path_ptr lib_path; /* library search list (GS_LIB) */
+ ref stdio[3]; /* t_file */
+ stream *invalid_file_stream;/* An invalid file object (stable memory) */
+ op_array_table op_array_table_global; /* Global operator table */
+ op_array_table op_array_table_local; /* Local operator table */
+ int (*time_slice_proc)(i_ctx_t **); /* Time slice procedure */
+ int time_slice_ticks; /* Ticks before next slice */
+ int (*reschedule_proc)(i_ctx_t **); /* Reschedule procedure */
+
+ /* Put the stacks at the end to minimize other offsets. */
+ dict_stack_t dict_stack;
+ exec_stack_t exec_stack;
+ op_stack_t op_stack;
+ struct i_plugin_holder_s *plugin_list;
+};
+extern const long rand_state_initial; /* in zmath.c */
+
+/*
+ * We make st_context_state public because zcontext.c must subclass it.
+ */
+/*extern_st(st_context_state); *//* in icontext.h */
+#define public_st_context_state() /* in icontext.c */\
+ gs_public_st_complex_only(st_context_state, gs_context_state_t,\
+ "gs_context_state_t", context_state_clear_marks,\
+ context_state_enum_ptrs, context_state_reloc_ptrs, 0)
+
+#endif /* icstate_INCLUDED */
diff --git a/psi/iddict.h b/psi/iddict.h
new file mode 100644
index 000000000..f00db5f1e
--- /dev/null
+++ b/psi/iddict.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Dictionary API with implicit dict_stack argument */
+
+#ifndef iddict_INCLUDED
+# define iddict_INCLUDED
+
+#include "idict.h"
+#include "icstate.h" /* for access to dict_stack */
+
+/* Define the dictionary stack instance for operators. */
+#define idict_stack (i_ctx_p->dict_stack)
+
+#define idict_put(pdref, key, pvalue)\
+ dict_put(pdref, key, pvalue, &idict_stack)
+#define idict_put_string(pdref, kstr, pvalue)\
+ dict_put_string(pdref, kstr, pvalue, &idict_stack)
+#define idict_undef(pdref, key)\
+ dict_undef(pdref, key, &idict_stack)
+#define idict_copy(dfrom, dto)\
+ dict_copy(dfrom, dto, &idict_stack)
+#define idict_copy_new(dfrom, dto)\
+ dict_copy_new(dfrom, dto, &idict_stack)
+#define idict_resize(pdref, newmax)\
+ dict_resize(pdref, newmax, &idict_stack)
+#define idict_grow(pdref)\
+ dict_grow(pdref, &idict_stack)
+#define idict_unpack(pdref)\
+ dict_unpack(pdref, &idict_stack)
+
+#endif /* iddict_INCLUDED */
diff --git a/psi/iddstack.h b/psi/iddstack.h
new file mode 100644
index 000000000..8a8a37ec9
--- /dev/null
+++ b/psi/iddstack.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Dictionary stack API subset needed by idict.h */
+
+#ifndef iddstack_INCLUDED
+# define iddstack_INCLUDED
+
+#ifndef dict_stack_DEFINED
+# define dict_stack_DEFINED
+typedef struct dict_stack_s dict_stack_t;
+#endif
+
+/*
+ * Reset the cached top values. Every routine that alters the
+ * dictionary stack (including changing the protection or size of the
+ * top dictionary on the stack) must call this.
+ */
+void dstack_set_top(dict_stack_t *);
+
+/* Check whether a dictionary is one of the permanent ones on the d-stack. */
+bool dstack_dict_is_permanent(const dict_stack_t *, const ref *);
+
+#endif /* iddstack_INCLUDED */
diff --git a/psi/idebug.c b/psi/idebug.c
new file mode 100644
index 000000000..dfa81d607
--- /dev/null
+++ b/psi/idebug.c
@@ -0,0 +1,318 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Debugging support for Ghostscript interpreter */
+/* This file must always be compiled with DEBUG set. */
+#undef DEBUG
+#define DEBUG
+#include "string_.h"
+#include "ghost.h"
+#include "gxalloc.h" /* for procs for getting struct type */
+#include "idebug.h" /* defines interface */
+#include "idict.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "istack.h"
+#include "iutil.h"
+#include "ivmspace.h"
+#include "opdef.h"
+
+/* Table of type name strings */
+static const char *const type_strings[] = {
+ REF_TYPE_DEBUG_PRINT_STRINGS
+};
+
+/* First unassigned type index */
+extern const int tx_next_index; /* in interp.c */
+
+/* Print a name. */
+void
+debug_print_name(const gs_memory_t *mem, const ref * pnref)
+{
+ ref sref;
+
+ name_string_ref(mem, pnref, &sref);
+ debug_print_string(mem, sref.value.const_bytes, r_size(&sref));
+}
+void
+debug_print_name_index(const gs_memory_t *mem, name_index_t nidx)
+{
+ ref nref;
+
+ name_index_ref(mem, nidx, &nref);
+ debug_print_name(mem, &nref);
+}
+
+/* Print a ref. */
+static void
+debug_print_full_ref(const gs_memory_t *mem, const ref * pref)
+{
+ uint size = r_size(pref);
+ ref nref;
+
+ dmprintf1(mem, "(%x)", r_type_attrs(pref));
+ switch (r_type(pref)) {
+ case t_array:
+ dmprintf2(mem, "array(%u)0x%lx", size, (ulong) pref->value.refs);
+ break;
+ case t_astruct:
+ goto strct;
+ case t_boolean:
+ dmprintf1(mem, "boolean %x", pref->value.boolval);
+ break;
+ case t_device:
+ dmprintf1(mem, "device 0x%lx", (ulong) pref->value.pdevice);
+ break;
+ case t_dictionary:
+ dmprintf3(mem, "dict(%u/%u)0x%lx",
+ dict_length(pref), dict_maxlength(pref),
+ (ulong) pref->value.pdict);
+ break;
+ case t_file:
+ dmprintf1(mem, "file 0x%lx", (ulong) pref->value.pfile);
+ break;
+ case t_fontID:
+ goto strct;
+ case t_integer:
+ dmprintf1(mem, "int %"PRIpsint, pref->value.intval);
+ break;
+ case t_mark:
+ dmprintf(mem, "mark");
+ break;
+ case t_mixedarray:
+ dmprintf2(mem, "mixed packedarray(%u)0x%lx", size,
+ (ulong) pref->value.packed);
+ break;
+ case t_name:
+ dmprintf2(mem, "name(0x%lx#%u)", (ulong) pref->value.pname,
+ name_index(mem, pref));
+ debug_print_name(mem, pref);
+ break;
+ case t_null:
+ dmprintf(mem, "null");
+ break;
+ case t_oparray:
+ dmprintf2(mem, "op_array(%u)0x%lx:", size, (ulong) pref->value.const_refs);
+ {
+ const op_array_table *opt = get_op_array(mem, size);
+
+ name_index_ref(mem, opt->nx_table[size - opt->base_index], &nref);
+ }
+ debug_print_name(mem, &nref);
+ break;
+ case t_operator:
+ dmprintf1(mem, "op(%u", size);
+ if (size > 0 && size < op_def_count) /* just in case */
+ dmprintf1(mem, ":%s", (const char *)(op_index_def(size)->oname + 1));
+ dmprintf1(mem, ")0x%lx", (ulong) pref->value.opproc);
+ break;
+ case t_real:
+ dmprintf1(mem, "real %f", pref->value.realval);
+ break;
+ case t_save:
+ dmprintf1(mem, "save %lu", pref->value.saveid);
+ break;
+ case t_shortarray:
+ dmprintf2(mem, "short packedarray(%u)0x%lx", size,
+ (ulong) pref->value.packed);
+ break;
+ case t_string:
+ dmprintf2(mem, "string(%u)0x%lx", size, (ulong) pref->value.bytes);
+ break;
+ case t_struct:
+ strct:{
+ obj_header_t *obj = (obj_header_t *) pref->value.pstruct;
+ /* HACK: We know this object was allocated with gsalloc.c. */
+ gs_memory_type_ptr_t otype =
+ gs_ref_memory_procs.object_type(NULL, obj);
+
+ dmprintf2(mem, "struct %s 0x%lx",
+ (r_is_foreign(pref) ? "-foreign-" :
+ gs_struct_type_name_string(otype)),
+ (ulong) obj);
+ }
+ break;
+ default:
+ dmprintf1(mem, "type 0x%x", r_type(pref));
+ }
+}
+static void
+debug_print_packed_ref(const gs_memory_t *mem, const ref_packed *pref)
+{
+ ushort elt = *pref & packed_value_mask;
+ ref nref;
+
+ switch (*pref >> r_packed_type_shift) {
+ case pt_executable_operator:
+ dmprintf(mem, "<op_name>");
+ op_index_ref(mem, elt, &nref);
+ debug_print_ref(mem, &nref);
+ break;
+ case pt_integer:
+ dmprintf1(mem, "<int> %d", (int)elt + packed_min_intval);
+ break;
+ case pt_literal_name:
+ dmprintf(mem, "<lit_name>");
+ goto ptn;
+ case pt_executable_name:
+ dmprintf(mem, "<exec_name>");
+ ptn: name_index_ref(mem, elt, &nref);
+ dmprintf2(mem, "(0x%lx#%u)", (ulong) nref.value.pname, elt);
+ debug_print_name(mem, &nref);
+ break;
+ default:
+ dmprintf2(mem, "<packed_%d?>0x%x", *pref >> r_packed_type_shift, elt);
+ }
+}
+void
+debug_print_ref_packed(const gs_memory_t *mem, const ref_packed *rpp)
+{
+ if (r_is_packed(rpp))
+ debug_print_packed_ref(mem, rpp);
+ else
+ debug_print_full_ref(mem, (const ref *)rpp);
+ dmflush(mem);
+}
+void
+debug_print_ref(const gs_memory_t *mem, const ref * pref)
+{
+ debug_print_ref_packed(mem, (const ref_packed *)pref);
+}
+
+/* Dump one ref. */
+static void print_ref_data(const gs_memory_t *mem, const ref *);
+void
+debug_dump_one_ref(const gs_memory_t *mem, const ref * p)
+{
+ uint attrs = r_type_attrs(p);
+ uint type = r_type(p);
+ static const ref_attr_print_mask_t apm[] = {
+ REF_ATTR_PRINT_MASKS,
+ {0, 0, 0}
+ };
+ const ref_attr_print_mask_t *ap = apm;
+
+ if (type >= tx_next_index)
+ dmprintf1(mem, "0x%02x?? ", type);
+ else if (type >= t_next_index)
+ dmprintf(mem, "opr* ");
+ else
+ dmprintf1(mem, "%s ", type_strings[type]);
+ for (; ap->mask; ++ap)
+ if ((attrs & ap->mask) == ap->value)
+ dmputc(mem, ap->print);
+ dmprintf2(mem, " 0x%04x 0x%08lx", r_size(p), *(const ulong *)&p->value);
+ print_ref_data(mem, p);
+ dmflush(mem);
+}
+static void
+print_ref_data(const gs_memory_t *mem, const ref *p)
+{
+#define BUF_SIZE 30
+ byte buf[BUF_SIZE + 1];
+ const byte *pchars;
+ uint plen;
+
+ if (obj_cvs(mem, p, buf, countof(buf) - 1, &plen, &pchars) >= 0 &&
+ pchars == buf &&
+ ((buf[plen] = 0), strcmp((char *)buf, "--nostringval--"))
+ )
+ dmprintf1(mem, " = %s", (char *)buf);
+#undef BUF_SIZE
+}
+
+/* Dump a region of memory containing refs. */
+void
+debug_dump_refs(const gs_memory_t *mem, const ref * from,
+ uint size, const char *msg)
+{
+ const ref *p = from;
+ uint count = size;
+
+ if (size && msg)
+ dmprintf2(mem, "%s at 0x%lx:\n", msg, (ulong) from);
+ while (count--) {
+ dmprintf2(mem, "0x%lx: 0x%04x ", (ulong)p, r_type_attrs(p));
+ debug_dump_one_ref(mem, p);
+ dmputc(mem, '\n');
+ p++;
+ }
+}
+
+/* Dump a stack. */
+void
+debug_dump_stack(const gs_memory_t *mem,
+ const ref_stack_t * pstack, const char *msg)
+{
+ uint i;
+ const char *m = msg;
+
+ for (i = ref_stack_count(pstack); i != 0;) {
+ const ref *p = ref_stack_index(pstack, --i);
+
+ if (m) {
+ dmprintf2(mem, "%s at 0x%lx:\n", m, (ulong) pstack);
+ m = NULL;
+ }
+ dmprintf2(mem, "0x%lx: 0x%02x ", (ulong)p, r_type(p));
+ debug_dump_one_ref(mem, p);
+ dmputc(mem, '\n');
+ }
+}
+
+/* Dump an array. */
+void
+debug_dump_array(const gs_memory_t *mem, const ref * array)
+{
+ const ref_packed *pp;
+ uint type = r_type(array);
+ uint len;
+
+ switch (type) {
+ default:
+ dmprintf2(mem, "%s at 0x%lx isn't an array.\n",
+ (type < countof(type_strings) ?
+ type_strings[type] : "????"),
+ (ulong) array);
+ return;
+ case t_oparray:
+ /* This isn't really an array, but we'd like to see */
+ /* its contents anyway. */
+ debug_dump_array(mem, array->value.const_refs);
+ return;
+ case t_array:
+ case t_mixedarray:
+ case t_shortarray:
+ ;
+ }
+
+ /* This "packed" loop works for all array-types. */
+ for (len = r_size(array), pp = array->value.packed;
+ len > 0;
+ len--, pp = packed_next(pp)) {
+ ref temp;
+
+ packed_get(mem, pp, &temp);
+ if (r_is_packed(pp)) {
+ dmprintf2(mem, "0x%lx* 0x%04x ", (ulong)pp, (uint)*pp);
+ print_ref_data(mem, &temp);
+ } else {
+ dmprintf2(mem, "0x%lx: 0x%02x ", (ulong)pp, r_type(&temp));
+ debug_dump_one_ref(mem, &temp);
+ }
+ dmputc(mem, '\n');
+ }
+}
diff --git a/psi/idebug.h b/psi/idebug.h
new file mode 100644
index 000000000..3ad4a4616
--- /dev/null
+++ b/psi/idebug.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Prototypes for debugging procedures in idebug.c */
+
+#ifndef idebug_INCLUDED
+# define idebug_INCLUDED
+
+/* Print individual values. */
+void debug_print_name(const gs_memory_t *mem, const ref *);
+void debug_print_name_index(const gs_memory_t *mem, uint /*name_index_t*/);
+void debug_print_ref(const gs_memory_t *mem, const ref *);
+void debug_print_ref_packed(const gs_memory_t *mem, const ref_packed *);
+
+/* Dump regions of memory. */
+void debug_dump_one_ref(const gs_memory_t *mem, const ref *);
+void debug_dump_refs(const gs_memory_t *mem,
+ const ref * from, uint size, const char *msg);
+void debug_dump_array(const gs_memory_t *mem, const ref * array);
+
+/* Dump a stack. Using this requires istack.h. */
+#ifndef ref_stack_DEFINED
+typedef struct ref_stack_s ref_stack_t; /* also defined in isdata.h */
+# define ref_stack_DEFINED
+#endif
+void debug_dump_stack(const gs_memory_t *mem,
+ const ref_stack_t * pstack, const char *msg);
+
+#endif /* idebug_INCLUDED */
diff --git a/psi/idict.c b/psi/idict.c
new file mode 100644
index 000000000..40ea01907
--- /dev/null
+++ b/psi/idict.c
@@ -0,0 +1,904 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Dictionary implementation */
+#include "math_.h" /* for frexp */
+#include "string_.h" /* for strlen */
+#include "ghost.h"
+#include "gxalloc.h" /* for accessing masks */
+#include "ierrors.h"
+#include "imemory.h"
+#include "idebug.h" /* for debug_print_name */
+#include "inamedef.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "isave.h" /* for value cache in names */
+#include "store.h"
+#include "idict.h" /* interface definition */
+#include "idictdef.h"
+#include "iutil.h"
+#include "ivmspace.h" /* for store check */
+/*
+#include "idicttpl.h" - Do not remove this comment.
+ "idicttpl.h" is included below.
+*/
+
+/*
+ * Dictionaries per se aren't supposed to know anything about the
+ * dictionary stack, let alone the interpreter's dictionary stack.
+ * Unfortunately, there is are two design couplings between them:
+ * dictionary stacks cache some of the elements of their top dictionary
+ * (requiring updating when that dictionary grows or is unpacked),
+ * and names may cache a pointer to their definition (requiring a
+ * check whether a dictionary appears on the dictionary stack).
+ * Therefore, we need iddstack.h here.
+ * We'd really like to fix this, but we don't see how.
+ */
+#include "iddstack.h"
+
+/*
+ * Define the size of the largest valid dictionary.
+ * This is limited by the size field of the keys and values refs,
+ * and by the enumeration interface, which requires the size to
+ * fit in an int. As it happens, max_array_size will always be
+ * smaller than max_int.
+ */
+const uint dict_max_size = max_array_size - 1;
+
+/* Define whether dictionaries are packed by default. */
+enum {
+ dict_default_pack = true
+};
+
+/*
+ * Define the check for whether we can set the 1-element cache.
+ * We only set the cache if we aren't inside a save.
+ * This way, we never have to undo setting the cache.
+ */
+#define CAN_SET_PVALUE_CACHE(pds, pdref, mem)\
+ (pds && dstack_dict_is_permanent(pds, pdref) && !ref_saving_in(mem))
+
+/* Forward references */
+static int dict_create_contents(uint size, const ref * pdref, bool pack);
+
+/* Debugging statistics - uses a static, so not threadsafe. */
+#if defined(DEBUG) && !defined(GS_THREADSAFE)
+struct stats_dict_s {
+ long lookups; /* total lookups */
+ long probe1; /* successful lookups on only 1 probe */
+ long probe2; /* successful lookups on 2 probes */
+} stats_dict;
+
+/* Wrapper for dict_find */
+int real_dict_find(const ref * pdref, const ref * key, ref ** ppvalue);
+int
+dict_find(const ref * pdref, const ref * pkey, ref ** ppvalue)
+{
+ dict *pdict = pdref->value.pdict;
+ int code = real_dict_find(pdref, pkey, ppvalue);
+
+ stats_dict.lookups++;
+ if (r_has_type(pkey, t_name) && dict_is_packed(pdict)) {
+ uint nidx = name_index(dict_mem(pdict), pkey);
+ uint hash =
+ dict_hash_mod(dict_name_index_hash(nidx), npairs(pdict)) + 1;
+
+ if (pdict->keys.value.packed[hash] ==
+ pt_tag(pt_literal_name) + nidx
+ )
+ stats_dict.probe1++;
+ else if (pdict->keys.value.packed[hash - 1] ==
+ pt_tag(pt_literal_name) + nidx
+ )
+ stats_dict.probe2++;
+ }
+ /* Do the cheap flag test before the expensive remainder test. */
+ if (gs_debug_c('d') && !(stats_dict.lookups % 1000))
+ dlprintf3("[d]lookups=%ld probe1=%ld probe2=%ld\n",
+ stats_dict.lookups, stats_dict.probe1, stats_dict.probe2);
+ return code;
+}
+#define dict_find real_dict_find
+#endif
+
+/* Round up the size of a dictionary. Return 0 if too large. */
+uint
+dict_round_size_small(uint rsize)
+{
+ return (rsize > dict_max_size ? 0 : rsize);
+}
+uint
+dict_round_size_large(uint rsize)
+{ /* Round up to a power of 2 if not huge. */
+ /* If the addition overflows, the new rsize will be zero, */
+ /* which will (correctly) be interpreted as a limitcheck. */
+ if (rsize > dict_max_non_huge)
+ return (rsize > dict_max_size ? 0 : rsize);
+ while (rsize & (rsize - 1))
+ rsize = (rsize | (rsize - 1)) + 1;
+ return (rsize <= dict_max_size ? rsize : dict_max_non_huge);
+}
+
+/* Create a dictionary using the given allocator. */
+int
+dict_alloc(gs_ref_memory_t * mem, uint size, ref * pdref)
+{
+ ref arr;
+ int code =
+ gs_alloc_ref_array(mem, &arr, a_all, sizeof(dict) / sizeof(ref),
+ "dict_alloc");
+ dict *pdict;
+ ref dref;
+
+ if (code < 0)
+ return code;
+ pdict = (dict *) arr.value.refs;
+ make_tav(&dref, t_dictionary,
+ r_space(&arr) | imemory_new_mask(mem) | a_all,
+ pdict, pdict);
+ make_struct(&pdict->memory, avm_foreign, mem);
+ code = dict_create_contents(size, &dref, dict_default_pack);
+ if (code < 0) {
+ gs_free_ref_array(mem, &arr, "dict_alloc");
+ return code;
+ }
+ *pdref = dref;
+ return 0;
+}
+/* Create unpacked keys for a dictionary. */
+/* The keys are allocated using the same allocator as the dictionary. */
+static int
+dict_create_unpacked_keys(uint asize, const ref * pdref)
+{
+ dict *pdict = pdref->value.pdict;
+ gs_ref_memory_t *mem = dict_memory(pdict);
+ int code;
+
+ code = gs_alloc_ref_array(mem, &pdict->keys, a_all, asize,
+ "dict_create_unpacked_keys");
+ if (code >= 0) {
+ uint new_mask = imemory_new_mask(mem);
+ ref *kp = pdict->keys.value.refs;
+
+ r_set_attrs(&pdict->keys, new_mask);
+ refset_null_new(kp, asize, new_mask);
+ r_set_attrs(kp, a_executable); /* wraparound entry */
+ }
+ return code;
+}
+/* Create the contents (keys and values) of a newly allocated dictionary. */
+/* Allocate in the current VM space, which is assumed to be the same as */
+/* the VM space where the dictionary is allocated. */
+static int
+dict_create_contents(uint size, const ref * pdref, bool pack)
+{
+ dict *pdict = pdref->value.pdict;
+ gs_ref_memory_t *mem = dict_memory(pdict);
+ uint new_mask = imemory_new_mask(mem);
+ uint asize = dict_round_size((size == 0 ? 1 : size));
+ int code;
+ register uint i;
+
+ if (asize == 0 || asize > max_array_size - 1) /* too large */
+ return_error(gs_error_limitcheck);
+ asize++; /* allow room for wraparound entry */
+ code = gs_alloc_ref_array(mem, &pdict->values, a_all, asize,
+ "dict_create_contents(values)");
+ if (code < 0)
+ return code;
+ r_set_attrs(&pdict->values, new_mask);
+ refset_null_new(pdict->values.value.refs, asize, new_mask);
+ if (pack) {
+ uint ksize = (asize + packed_per_ref - 1) / packed_per_ref;
+ ref arr;
+ ref_packed *pkp;
+ ref_packed *pzp;
+
+ code = gs_alloc_ref_array(mem, &arr, a_all, ksize,
+ "dict_create_contents(packed keys)");
+ if (code < 0)
+ return code;
+ pkp = (ref_packed *) arr.value.refs;
+ make_tasv(&pdict->keys, t_shortarray,
+ r_space(&arr) | a_all | new_mask,
+ asize, packed, pkp);
+ for (pzp = pkp, i = 0; i < asize || i % packed_per_ref; pzp++, i++)
+ *pzp = packed_key_empty;
+ *pkp = packed_key_deleted; /* wraparound entry */
+ } else { /* not packed */
+ int code = dict_create_unpacked_keys(asize, pdref);
+
+ if (code < 0)
+ return code;
+ }
+ make_tav(&pdict->count, t_integer, new_mask, intval, 0);
+ make_tav(&pdict->maxlength, t_integer, new_mask, intval, size);
+ return 0;
+}
+
+/*
+ * Ensure that a dictionary uses the unpacked representation for keys.
+ * We can't just use dict_resize, because the values slots mustn't move.
+ */
+int
+dict_unpack(ref * pdref, dict_stack_t *pds)
+{
+ dict *pdict = pdref->value.pdict;
+
+ if (!dict_is_packed(pdict))
+ return 0; /* nothing to do */
+ {
+ gs_ref_memory_t *mem = dict_memory(pdict);
+ uint count = nslots(pdict);
+ const ref_packed *okp = pdict->keys.value.packed;
+ ref old_keys;
+ int code;
+ ref *nkp;
+
+ old_keys = pdict->keys;
+ if (ref_must_save_in(mem, &old_keys))
+ ref_do_save_in(mem, pdref, &pdict->keys, "dict_unpack(keys)");
+ code = dict_create_unpacked_keys(count, pdref);
+ if (code < 0)
+ return code;
+ for (nkp = pdict->keys.value.refs; count--; okp++, nkp++)
+ if (r_packed_is_name(okp)) {
+ packed_get((const gs_memory_t *)mem, okp, nkp);
+ ref_mark_new_in(mem, nkp);
+ } else if (*okp == packed_key_deleted)
+ r_set_attrs(nkp, a_executable);
+ if (!ref_must_save_in(mem, &old_keys))
+ gs_free_ref_array(mem, &old_keys, "dict_unpack(old keys)");
+ if (pds)
+ dstack_set_top(pds); /* just in case */
+ }
+ return 0;
+}
+
+/*
+ * Look up a key in a dictionary. Store a pointer to the value slot
+ * where found, or to the (value) slot for inserting.
+ * See idict.h for the possible return values.
+ */
+int
+dict_find(const ref * pdref, const ref * pkey,
+ ref ** ppvalue /* result is stored here */ )
+{
+ dict *pdict = pdref->value.pdict;
+ uint size = npairs(pdict);
+ register int etype;
+ uint nidx;
+ ref_packed kpack;
+ uint hash;
+ int ktype;
+ const gs_memory_t *mem = dict_mem(pdict);
+
+ /* Compute hash. The only types we bother with are strings, */
+ /* names, and (unlikely, but worth checking for) integers. */
+ switch (r_type(pkey)) {
+ case t_name:
+ nidx = name_index(mem, pkey);
+ nh:
+ hash = dict_name_index_hash(nidx);
+ kpack = packed_name_key(nidx);
+ ktype = t_name;
+ break;
+ case t_string: /* convert to a name first */
+ {
+ ref nref;
+ int code;
+
+ if (!r_has_attr(pkey, a_read))
+ return_error(gs_error_invalidaccess);
+ code = name_ref(mem, pkey->value.bytes, r_size(pkey), &nref, 1);
+ if (code < 0)
+ return code;
+ nidx = name_index(mem, &nref);
+ }
+ goto nh;
+ case t_real:
+ /*
+ * Make sure that equal reals and integers hash the same.
+ */
+ {
+ int expt, i;
+ double mant = frexp(pkey->value.realval, &expt);
+ /*
+ * The value is mant * 2^expt, where 0.5 <= mant < 1,
+ * or else expt == mant == 0.
+ */
+
+ if (expt < sizeof(long) * 8 || pkey->value.realval == min_long)
+ i = (int)pkey->value.realval;
+ else
+ i = (int)(mant * min_long); /* MSVC 6.00.8168.0 cannot compile this */
+ hash = (uint)i * 30503; /* with -O2 as a single expression */
+ }
+ goto ih;
+ case t_integer:
+ hash = (uint)pkey->value.intval * 30503;
+ ih:
+ kpack = packed_key_impossible;
+ ktype = -1;
+ nidx = 0; /* only to pacify gcc */
+ break;
+ case t_null: /* not allowed as a key */
+ return_error(gs_error_typecheck);
+ default:
+ hash = r_btype(pkey) * 99; /* yech */
+ kpack = packed_key_impossible;
+ ktype = -1;
+ nidx = 0; /* only to pacify gcc */
+ }
+ /* Search the dictionary */
+ if (dict_is_packed(pdict)) {
+ const ref_packed *pslot = 0;
+
+# define found *ppvalue = packed_search_value_pointer; return 1
+# define deleted if (pslot == 0) pslot = kp
+# define missing goto miss
+# include "idicttpl.h"
+# undef missing
+# undef deleted
+# undef found
+ /*
+ * Double wraparound, dict is full.
+ * Note that even if there was an empty slot (pslot != 0),
+ * we must return dictfull if length = maxlength.
+ */
+ if (pslot == 0 || d_length(pdict) == d_maxlength(pdict))
+ return_error(gs_error_dictfull);
+ *ppvalue = pdict->values.value.refs + (pslot - kbot);
+ return 0;
+ miss: /* Key is missing, not double wrap. See above re dictfull. */
+ if (d_length(pdict) == d_maxlength(pdict))
+ return_error(gs_error_dictfull);
+ if (pslot == 0)
+ pslot = kp;
+ *ppvalue = pdict->values.value.refs + (pslot - kbot);
+ return 0;
+ } else {
+ ref *kbot = pdict->keys.value.refs;
+ register ref *kp;
+ ref *pslot = 0;
+ int wrap = 0;
+
+ for (kp = kbot + dict_hash_mod(hash, size) + 2;;) {
+ --kp;
+ if ((etype = r_type(kp)) == ktype) { /* Fast comparison if both keys are names */
+ if (name_index(mem, kp) == nidx) {
+ *ppvalue = pdict->values.value.refs + (kp - kbot);
+ return 1;
+ }
+ } else if (etype == t_null) { /* Empty, deleted, or wraparound. */
+ /* Figure out which. */
+ if (kp == kbot) { /* wrap */
+ if (wrap++) { /* wrapped twice */
+ if (pslot == 0)
+ return_error(gs_error_dictfull);
+ break;
+ }
+ kp += size + 1;
+ } else if (r_has_attr(kp, a_executable)) { /* Deleted entry, save the slot. */
+ if (pslot == 0)
+ pslot = kp;
+ } else /* key not found */
+ break;
+ } else {
+ if (obj_eq(mem, kp, pkey)) {
+ *ppvalue = pdict->values.value.refs + (kp - kbot);
+ return 1;
+ }
+ }
+ }
+ if (d_length(pdict) == d_maxlength(pdict))
+ return_error(gs_error_dictfull);
+ *ppvalue = pdict->values.value.refs +
+ ((pslot != 0 ? pslot : kp) - kbot);
+ return 0;
+ }
+}
+
+/*
+ * Look up a (constant) C string in a dictionary.
+ * Return 1 if found, <= 0 if not.
+ */
+int
+dict_find_string(const ref * pdref, const char *kstr, ref ** ppvalue)
+{
+ int code;
+ ref kname;
+ if ( pdref != 0 ) {
+ dict *pdict = pdref->value.pdict;
+
+ if ((code = name_ref(dict_mem(pdict),
+ (const byte *)kstr, strlen(kstr), &kname, -1)) < 0)
+ return code;
+ code = dict_find(pdref, &kname, ppvalue);
+ if (code == gs_error_dictfull)
+ return_error(gs_error_undefined);
+ return code;
+ }
+ return 0;
+}
+
+/*
+ * Enter a key-value pair in a dictionary.
+ * See idict.h for the possible return values.
+ */
+int
+dict_put(ref * pdref /* t_dictionary */ , const ref * pkey, const ref * pvalue,
+ dict_stack_t *pds)
+{
+ dict *pdict = pdref->value.pdict;
+ gs_ref_memory_t *mem = dict_memory(pdict);
+ gs_memory_t *pmem = dict_mem(pdict);
+ int rcode = 0;
+ int code;
+ ref *pvslot, kname;
+
+ /* Check the value. */
+ store_check_dest(pdref, pvalue);
+ top:if ((code = dict_find(pdref, pkey, &pvslot)) <= 0) { /* not found *//* Check for overflow */
+ uint index;
+
+ switch (code) {
+ case 0:
+ break;
+ case gs_error_dictfull:
+ if (!pmem->gs_lib_ctx->dict_auto_expand)
+ return_error(gs_error_dictfull);
+ code = dict_grow(pdref, pds);
+ if (code < 0)
+ return code;
+ goto top; /* keep things simple */
+ default: /* gs_error_typecheck */
+ return code;
+ }
+ index = pvslot - pdict->values.value.refs;
+ /* If the key is a string, convert it to a name. */
+ if (r_has_type(pkey, t_string)) {
+ int code;
+
+ if (!r_has_attr(pkey, a_read))
+ return_error(gs_error_invalidaccess);
+ code = name_from_string(pmem, pkey, &kname);
+ if (code < 0)
+ return code;
+ pkey = &kname;
+ }
+ if (dict_is_packed(pdict)) {
+ ref_packed *kp;
+
+ if (!r_has_type(pkey, t_name) ||
+ name_index(pmem, pkey) > packed_name_max_index
+ ) { /* Change to unpacked representation. */
+ int code = dict_unpack(pdref, pds);
+
+ if (code < 0)
+ return code;
+ goto top;
+ }
+ kp = pdict->keys.value.writable_packed + index;
+ if (ref_must_save_in(mem, &pdict->keys)) { /* See initial comment for why it is safe */
+ /* not to save the change if the keys */
+ /* array itself is new. */
+ ref_do_save_in(mem, &pdict->keys, kp, "dict_put(key)");
+ }
+ *kp = pt_tag(pt_literal_name) + name_index(pmem, pkey);
+ } else {
+ ref *kp = pdict->keys.value.refs + index;
+
+ if_debug2m('d', (const gs_memory_t *)mem, "[d]0x%lx: fill key at 0x%lx\n",
+ (ulong) pdict, (ulong) kp);
+ store_check_dest(pdref, pkey);
+ ref_assign_old_in(mem, &pdict->keys, kp, pkey,
+ "dict_put(key)"); /* set key of pair */
+ }
+ ref_save_in(mem, pdref, &pdict->count, "dict_put(count)");
+ pdict->count.value.intval++;
+ /* If the key is a name, update its 1-element cache. */
+ if (r_has_type(pkey, t_name)) {
+ name *pname = pkey->value.pname;
+
+ if (pname->pvalue == pv_no_defn &&
+ CAN_SET_PVALUE_CACHE(pds, pdref, mem)
+ ) { /* Set the cache. */
+ if_debug0m('d', (const gs_memory_t *)mem, "[d]set cache\n");
+ pname->pvalue = pvslot;
+ } else { /* The cache can't be used. */
+ if_debug0m('d', (const gs_memory_t *)mem, "[d]no cache\n");
+ pname->pvalue = pv_other;
+ }
+ }
+ rcode = 1;
+ }
+ if_debug8m('d', (const gs_memory_t *)mem,
+ "[d]0x%lx: put key 0x%lx 0x%lx\n value at 0x%lx: old 0x%lx 0x%lx, new 0x%lx 0x%lx\n",
+ (ulong) pdref->value.pdict,
+ ((const ulong *)pkey)[0], ((const ulong *)pkey)[1],
+ (ulong) pvslot,
+ ((const ulong *)pvslot)[0], ((const ulong *)pvslot)[1],
+ ((const ulong *)pvalue)[0], ((const ulong *)pvalue)[1]);
+ ref_assign_old_in(mem, &pdref->value.pdict->values, pvslot, pvalue,
+ "dict_put(value)");
+ return rcode;
+}
+
+/*
+ * Enter a key-value pair where the key is a (constant) C string.
+ */
+int
+dict_put_string(ref * pdref, const char *kstr, const ref * pvalue,
+ dict_stack_t *pds)
+{
+ int code;
+ ref kname;
+ dict *pdict = pdref->value.pdict;
+
+ if ((code = name_ref(dict_mem(pdict),
+ (const byte *)kstr, strlen(kstr), &kname, 0)) < 0)
+ return code;
+ return dict_put(pdref, &kname, pvalue, pds);
+}
+
+/* Remove an element from a dictionary. */
+int
+dict_undef(ref * pdref, const ref * pkey, dict_stack_t *pds)
+{
+ gs_ref_memory_t *mem;
+ ref *pvslot;
+ dict *pdict;
+ uint index;
+ int code = dict_find(pdref, pkey, &pvslot);
+
+ switch (code) {
+ case 0:
+ case gs_error_dictfull:
+ return_error(gs_error_undefined);
+ case 1:
+ break;
+ default: /* other error */
+ return code;
+ }
+ /* Remove the entry from the dictionary. */
+ pdict = pdref->value.pdict;
+ index = pvslot - pdict->values.value.refs;
+ mem = dict_memory(pdict);
+ if (dict_is_packed(pdict)) {
+ ref_packed *pkp = pdict->keys.value.writable_packed + index;
+ bool must_save = ref_must_save_in(mem, &pdict->keys);
+
+ if_debug3m('d', (const gs_memory_t *)mem,
+ "[d]0x%lx: removing key at 0%lx: 0x%x\n",
+ (ulong)pdict, (ulong)pkp, (uint)*pkp);
+ /* See the initial comment for why it is safe not to save */
+ /* the change if the keys array itself is new. */
+ if (must_save)
+ ref_do_save_in(mem, &pdict->keys, pkp, "dict_undef(key)");
+ /*
+ * Accumulating deleted entries slows down lookup.
+ * Detect the easy case where we can use an empty entry
+ * rather than a deleted one, namely, when the next entry
+ * in the probe order is empty.
+ */
+ if (pkp[-1] == packed_key_empty) {
+ /*
+ * In this case we can replace any preceding deleted keys with
+ * empty ones as well.
+ */
+ uint end = nslots(pdict);
+
+ *pkp = packed_key_empty;
+ if (must_save) {
+ while (++index < end && *++pkp == packed_key_deleted) {
+ ref_do_save_in(mem, &pdict->keys, pkp, "dict_undef(key)");
+ *pkp = packed_key_empty;
+ }
+ } else {
+ while (++index < end && *++pkp == packed_key_deleted)
+ *pkp = packed_key_empty;
+ }
+ } else
+ *pkp = packed_key_deleted;
+ } else { /* not packed */
+ ref *kp = pdict->keys.value.refs + index;
+
+ if_debug4m('d', (const gs_memory_t *)mem,
+ "[d]0x%lx: removing key at 0%lx: 0x%lx 0x%lx\n",
+ (ulong)pdict, (ulong)kp, ((ulong *)kp)[0], ((ulong *)kp)[1]);
+ make_null_old_in(mem, &pdict->keys, kp, "dict_undef(key)");
+ /*
+ * Accumulating deleted entries slows down lookup.
+ * Detect the easy case where we can use an empty entry
+ * rather than a deleted one, namely, when the next entry
+ * in the probe order is empty.
+ */
+ if (!r_has_type(kp - 1, t_null) || /* full entry */
+ r_has_attr(kp - 1, a_executable) /* deleted or wraparound */
+ )
+ r_set_attrs(kp, a_executable); /* mark as deleted */
+ }
+ ref_save_in(mem, pdref, &pdict->count, "dict_undef(count)");
+ pdict->count.value.intval--;
+ /* If the key is a name, update its 1-element cache. */
+ if (r_has_type(pkey, t_name)) {
+ name *pname = pkey->value.pname;
+
+ if (pv_valid(pname->pvalue)) {
+#ifdef DEBUG
+ /* Check the the cache is correct. */
+ if (!(pds && dstack_dict_is_permanent(pds, pdref)))
+ lprintf1("dict_undef: cached name value pointer 0x%lx is incorrect!\n",
+ (ulong) pname->pvalue);
+#endif
+ /* Clear the cache */
+ pname->pvalue = pv_no_defn;
+ }
+ }
+ make_null_old_in(mem, &pdict->values, pvslot, "dict_undef(value)");
+ return 0;
+}
+
+/* Return the number of elements in a dictionary. */
+uint
+dict_length(const ref * pdref /* t_dictionary */ )
+{
+ return d_length(pdref->value.pdict);
+}
+
+/* Return the capacity of a dictionary. */
+uint
+dict_maxlength(const ref * pdref /* t_dictionary */ )
+{
+ return d_maxlength(pdref->value.pdict);
+}
+
+/* Return the maximum index of a slot within a dictionary. */
+uint
+dict_max_index(const ref * pdref /* t_dictionary */ )
+{
+ return npairs(pdref->value.pdict) - 1;
+}
+
+/*
+ * Copy one dictionary into another.
+ * If COPY_NEW_ONLY is set, only copy entries whose keys
+ * aren't already present in the destination.
+ * If COPY_FOR_RESIZE is set, reset any valid name cache entries to
+ * pv_no_defn before doing the dict_put.
+ */
+#define COPY_NEW_ONLY 1
+#define COPY_FOR_RESIZE 2
+static int
+dict_copy_elements(const ref * pdrfrom /* t_dictionary */ ,
+ ref * pdrto /* t_dictionary */ , int options,
+ dict_stack_t *pds)
+{
+ int space = r_space(pdrto);
+ int index;
+ ref elt[2];
+ ref *pvslot;
+ int code;
+
+ if (space != avm_max) {
+ /* Do the store check before starting the copy. */
+ index = dict_first(pdrfrom);
+ while ((index = dict_next(pdrfrom, index, elt)) >= 0)
+ if (!(options & COPY_NEW_ONLY) ||
+ dict_find(pdrto, &elt[0], &pvslot) <= 0
+ ) {
+ store_check_space(space, &elt[0]);
+ store_check_space(space, &elt[1]);
+ }
+ }
+ /* Now copy the contents. */
+ index = dict_first(pdrfrom);
+ while ((index = dict_next(pdrfrom, index, elt)) >= 0) {
+ ref *pvalue = pv_no_defn;
+
+ if ((options & COPY_NEW_ONLY) &&
+ dict_find(pdrto, &elt[0], &pvslot) > 0
+ )
+ continue;
+ if ((options & COPY_FOR_RESIZE) &&
+ r_has_type(&elt[0], t_name) &&
+ (pvalue = elt[0].value.pname->pvalue, pv_valid(pvalue))
+ )
+ elt[0].value.pname->pvalue = pv_no_defn;
+ if ((code = dict_put(pdrto, &elt[0], &elt[1], pds)) < 0) {
+ /*
+ * If COPY_FOR_RESIZE is set, the dict_put isn't supposed to
+ * be able to fail, but we don't want to depend on this.
+ */
+ if (pvalue != pv_no_defn)
+ elt[0].value.pname->pvalue = pvalue;
+ return code;
+ }
+ }
+ return 0;
+}
+int
+dict_copy_entries(const ref *pdrfrom, ref *pdrto, bool new_only,
+ dict_stack_t *pds)
+{
+ return dict_copy_elements(pdrfrom, pdrto, (new_only ? COPY_NEW_ONLY : 0),
+ pds);
+}
+
+/* Resize a dictionary. */
+int
+dict_resize(ref * pdref, uint new_size, dict_stack_t *pds)
+{
+ dict *pdict = pdref->value.pdict;
+ gs_ref_memory_t *mem = dict_memory(pdict);
+ uint new_mask = imemory_new_mask(mem);
+ ushort orig_attrs = r_type_attrs(&pdict->values) & (a_all | a_executable);
+ dict dnew;
+ ref drto;
+ int code;
+
+ if (new_size < d_length(pdict)) {
+ if (!mem->gs_lib_ctx->dict_auto_expand)
+ return_error(gs_error_dictfull);
+ new_size = d_length(pdict);
+ }
+ make_tav(&drto, t_dictionary, r_space(pdref) | a_all | new_mask,
+ pdict, &dnew);
+ dnew.memory = pdict->memory;
+ if ((code = dict_create_contents(new_size, &drto, dict_is_packed(pdict))) < 0)
+ return code;
+ /*
+ * We must suppress the store check, in case we are expanding
+ * systemdict or another global dictionary that is allowed
+ * to reference local objects.
+ */
+ r_set_space(&drto, avm_local);
+ /*
+ * If we are expanding a permanent dictionary, we must make sure that
+ * dict_put doesn't think this is a second definition for any
+ * single-definition names. This in turn requires that
+ * dstack_dict_is_permanent must be true for the second ("to")
+ * argument of dict_copy_elements, which requires temporarily
+ * setting *pdref = drto.
+ */
+ if (CAN_SET_PVALUE_CACHE(pds, pdref, mem)) {
+ ref drfrom;
+
+ drfrom = *pdref;
+ *pdref = drto;
+ dict_copy_elements(&drfrom, pdref, COPY_FOR_RESIZE, pds);
+ *pdref = drfrom;
+ } else {
+ dict_copy_elements(pdref, &drto, 0, pds);
+ }
+ /* Save or free the old dictionary. */
+ if (ref_must_save_in(mem, &pdict->values))
+ ref_do_save_in(mem, pdref, &pdict->values, "dict_resize(values)");
+ else
+ gs_free_ref_array(mem, &pdict->values, "dict_resize(old values)");
+ if (ref_must_save_in(mem, &pdict->keys))
+ ref_do_save_in(mem, pdref, &pdict->keys, "dict_resize(keys)");
+ else
+ gs_free_ref_array(mem, &pdict->keys, "dict_resize(old keys)");
+ ref_assign(&pdict->keys, &dnew.keys);
+ ref_assign(&pdict->values, &dnew.values);
+ r_store_attrs(&pdict->values, a_all | a_executable, orig_attrs);
+ ref_save_in(dict_memory(pdict), pdref, &pdict->maxlength,
+ "dict_resize(maxlength)");
+ d_set_maxlength(pdict, new_size);
+ if (pds)
+ dstack_set_top(pds); /* just in case this is the top dict */
+ return 0;
+}
+
+/* Grow a dictionary for dict_put. */
+int
+dict_grow(ref * pdref, dict_stack_t *pds)
+{
+ dict *pdict = pdref->value.pdict;
+ /* We might have maxlength < npairs, if */
+ /* dict_round_size increased the size. */
+ ulong new_size = (ulong) d_maxlength(pdict);
+ /* Adobe does this */
+ if (new_size < 20)
+ new_size += 10;
+ else if (new_size < 200)
+ new_size *= 2;
+ else
+ new_size += new_size / 2;
+#if arch_sizeof_int < arch_sizeof_long
+ if (new_size > max_uint)
+ new_size = max_uint;
+#endif
+ if (new_size > npairs(pdict)) {
+ int code = dict_resize(pdref, (uint) new_size, pds);
+
+ if (code >= 0)
+ return code;
+ /* new_size was too big. */
+ if (npairs(pdict) < dict_max_size) {
+ code = dict_resize(pdref, dict_max_size, pds);
+ if (code >= 0)
+ return code;
+ }
+ if (npairs(pdict) == d_maxlength(pdict)) { /* Can't do it. */
+ return code;
+ }
+ /* We can't grow to new_size, but we can grow to npairs. */
+ new_size = npairs(pdict);
+ }
+ /* maxlength < npairs, we can grow in place */
+ ref_save_in(dict_memory(pdict), pdref, &pdict->maxlength,
+ "dict_put(maxlength)");
+ d_set_maxlength(pdict, new_size);
+ return 0;
+}
+
+/* Prepare to enumerate a dictionary. */
+int
+dict_first(const ref * pdref)
+{
+ return (int)nslots(pdref->value.pdict);
+}
+
+/* Enumerate the next element of a dictionary. */
+int
+dict_next(const ref * pdref, int index, ref * eltp /* ref eltp[2] */ )
+{
+ dict *pdict = pdref->value.pdict;
+ ref *vp = pdict->values.value.refs + index;
+
+ while (vp--, --index >= 0) {
+ array_get(dict_mem(pdict), &pdict->keys, (long)index, eltp);
+ /* Make sure this is a valid entry. */
+ if (r_has_type(eltp, t_name) ||
+ (!dict_is_packed(pdict) && !r_has_type(eltp, t_null))
+ ) {
+ eltp[1] = *vp;
+ if_debug6m('d', dict_mem(pdict), "[d]0x%lx: index %d: %lx %lx, %lx %lx\n",
+ (ulong) pdict, index,
+ ((ulong *) eltp)[0], ((ulong *) eltp)[1],
+ ((ulong *) vp)[0], ((ulong *) vp)[1]);
+ return index;
+ }
+ }
+ return -1; /* no more elements */
+}
+
+/* Return the index of a value within a dictionary. */
+int
+dict_value_index(const ref * pdref, const ref * pvalue)
+{
+ return (int)(pvalue - pdref->value.pdict->values.value.refs - 1);
+}
+
+/* Return the entry at a given index within a dictionary. */
+/* If the index designates an unoccupied entry, return gs_error_undefined. */
+int
+dict_index_entry(const ref * pdref, int index, ref * eltp /* ref eltp[2] */ )
+{
+ const dict *pdict = pdref->value.pdict;
+
+ array_get(dict_mem(pdict), &pdict->keys, (long)(index + 1), eltp);
+ if (r_has_type(eltp, t_name) ||
+ (!dict_is_packed(pdict) && !r_has_type(eltp, t_null))
+ ) {
+ eltp[1] = pdict->values.value.refs[index + 1];
+ return 0;
+ }
+ return gs_error_undefined;
+}
diff --git a/psi/idict.h b/psi/idict.h
new file mode 100644
index 000000000..ff784bd24
--- /dev/null
+++ b/psi/idict.h
@@ -0,0 +1,272 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interfaces for Ghostscript dictionary package */
+
+#ifndef idict_INCLUDED
+# define idict_INCLUDED
+
+#include "iddstack.h"
+
+/*
+ * Contrary to our usual practice, we expose the (first-level)
+ * representation of a dictionary in the interface file,
+ * because it is so important that access checking go fast.
+ * The access attributes for the dictionary are stored in
+ * the values ref.
+ */
+struct dict_s {
+ ref values; /* t_array, values */
+ ref keys; /* t_shortarray or t_array, keys */
+ ref count; /* t_integer, count of occupied entries */
+ /* (length) */
+ ref maxlength; /* t_integer, maxlength as seen by client. */
+ ref memory; /* foreign t_struct, the allocator that */
+ /* created this dictionary */
+#define dict_memory(pdict) r_ptr(&(pdict)->memory, gs_ref_memory_t)
+#define dict_mem(pdict) r_ptr(&(pdict)->memory, gs_memory_t)
+};
+
+/*
+ * Define the maximum size of a dictionary.
+ */
+extern const uint dict_max_size;
+
+/*
+ * Define whether dictionaries expand automatically when full. Note that
+ * if dict_auto_expand is true, dict_put, dict_copy, dict_resize, and
+ * dict_grow cannot return gs_error_dictfull; however, they can return gs_error_VMerror.
+ * (dict_find can return gs_error_dictfull even if dict_auto_expand is true.)
+ */
+extern bool dict_auto_expand;
+
+/*
+ * Create a dictionary.
+ */
+#ifndef gs_ref_memory_DEFINED
+# define gs_ref_memory_DEFINED
+typedef struct gs_ref_memory_s gs_ref_memory_t;
+#endif
+int dict_alloc(gs_ref_memory_t *, uint maxlength, ref * pdref);
+
+#define dict_create(maxlen, pdref)\
+ dict_alloc(iimemory, maxlen, pdref)
+
+/*
+ * Return a pointer to a ref that holds the access attributes
+ * for a dictionary.
+ */
+#define dict_access_ref(pdref) (&(pdref)->value.pdict->values)
+/*
+ * Check a dictionary for read or write permission.
+ * Note: this does NOT check the type of its operand!
+ */
+#define check_dict_read(dref) check_read(*dict_access_ref(&dref))
+#define check_dict_write(dref) check_write(*dict_access_ref(&dref))
+
+/*
+ * Look up a key in a dictionary. Store a pointer to the value slot
+ * where found, or to the (value) slot for inserting.
+ * The caller is responsible for checking that the dictionary is readable.
+ * Return 1 if found, 0 if not and there is room for a new entry,
+ * Failure returns:
+ * gs_error_typecheck if the key is null;
+ * gs_error_invalidaccess if the key is a string lacking read access;
+ * gs_error_VMerror or gs_error_limitcheck if the key is a string and the corresponding
+ * error occurs from attempting to convert it to a name;
+ * gs_error_dictfull if the dictionary is full and the key is missing.
+ */
+int dict_find(const ref * pdref, const ref * key, ref ** ppvalue);
+
+/*
+ * Look up a (constant) C string in a dictionary.
+ * Return 1 if found, <= 0 if not.
+ */
+int dict_find_string(const ref * pdref, const char *kstr, ref ** ppvalue);
+
+/*
+ * Enter a key-value pair in a dictionary.
+ * The caller is responsible for checking that the dictionary is writable.
+ * Return 1 if this was a new entry, 0 if this replaced an existing entry.
+ * Failure returns are as for dict_find, except that gs_error_dictfull doesn't
+ * occur if the dictionary is full but expandable, plus:
+ * gs_error_invalidaccess for an attempt to store a younger key or value into
+ * an older dictionary, or as described just below;
+ * gs_error_VMerror if a VMerror occurred while trying to expand the
+ * dictionary.
+ * Note that this procedure, and all procedures that may change the
+ * contents of a dictionary, take a pointer to a dictionary stack,
+ * so they can update the cached 'top' values and also update the cached
+ * value pointer in names. A NULL pointer for the dictionary stack is
+ * allowed, but in this case, if the dictionary is present on any dictionary
+ * stack, an gs_error_invalidaccess error will occur if cached values need updating.
+ * THIS ERROR CHECK IS NOT IMPLEMENTED YET.
+ */
+int dict_put(ref * pdref, const ref * key, const ref * pvalue,
+ dict_stack_t *pds);
+
+/*
+ * Enter a key-value pair where the key is a (constant) C string.
+ */
+int dict_put_string(ref * pdref, const char *kstr, const ref * pvalue,
+ dict_stack_t *pds);
+
+/*
+ * Remove a key-value pair from a dictionary.
+ * Return any of the same values as dict_put, except for 0 and gs_error_dictfull
+ * which are converted to gs_error_undefined.
+ */
+int dict_undef(ref * pdref, const ref * key, dict_stack_t *pds);
+
+/*
+ * Return the number of elements in a dictionary.
+ */
+uint dict_length(const ref * pdref);
+
+/*
+ * Return the capacity of a dictionary.
+ */
+uint dict_maxlength(const ref * pdref);
+
+/*
+ * Return the maximum index of a slot within a dictionary.
+ * Note that this may be greater than maxlength.
+ */
+uint dict_max_index(const ref * pdref);
+
+/*
+ * Copy one dictionary into another.
+ * Return 0 or gs_error_dictfull.
+ * If new_only is true, only copy entries whose keys
+ * aren't already present in the destination.
+ */
+int dict_copy_entries(const ref * dfrom, ref * dto, bool new_only,
+ dict_stack_t *pds);
+
+#define dict_copy(dfrom, dto, pds) dict_copy_entries(dfrom, dto, false, pds)
+#define dict_copy_new(dfrom, dto, pds) dict_copy_entries(dfrom, dto, true, pds)
+
+/*
+ * Grow or shrink a dictionary.
+ * Return 0, gs_error_dictfull, or gs_error_VMerror.
+ */
+int dict_resize(ref * pdref, uint newmaxlength, dict_stack_t *pds);
+
+/*
+ * Grow a dictionary in the same way as dict_put does.
+ * We export this for some special-case code in zop_def.
+ */
+int dict_grow(ref * pdref, dict_stack_t *pds);
+
+/*
+ * Ensure that a dictionary uses the unpacked representation for keys.
+ * (This is of no interest to ordinary clients.)
+ */
+int dict_unpack(ref * pdref, dict_stack_t *pds);
+
+/*
+ * Prepare to enumerate a dictionary.
+ * Return an integer suitable for the first call to dict_next.
+ */
+int dict_first(const ref * pdref);
+
+/*
+ * Enumerate the next element of a dictionary.
+ * index is initially the result of a call on dict_first.
+ * Either store a key and value at eltp[0] and eltp[1]
+ * and return an updated index, or return -1
+ * to signal that there are no more elements in the dictionary.
+ */
+int dict_next(const ref * pdref, int index, ref * eltp);
+
+/*
+ * Given a value pointer return by dict_find, return an index that
+ * identifies the entry within the dictionary. (This may, but need not,
+ * be the same as the index returned by dict_next.)
+ * The index is in the range [0..max_index-1].
+ */
+int dict_value_index(const ref * pdref, const ref * pvalue);
+
+/*
+ * Given an index in [0..max_index-1], as returned by dict_value_index,
+ * return the key and value, as returned by dict_next.
+ * If the index designates an unoccupied entry, return gs_error_undefined.
+ */
+int dict_index_entry(const ref * pdref, int index, ref * eltp);
+
+/*
+ * The following are some internal details that are used in both the
+ * implementation and some high-performance clients.
+ */
+
+/* On machines with reasonable amounts of memory, we round up dictionary
+ * sizes to the next power of 2 whenever possible, to allow us to use
+ * masking rather than division for computing the hash index.
+ * Unfortunately, if we required this, it would cut the maximum size of a
+ * dictionary in half. Therefore, on such machines, we distinguish
+ * "huge" dictionaries (dictionaries whose size is larger than the largest
+ * power of 2 less than dict_max_size) as a special case:
+ *
+ * - If the top dictionary on the stack is huge, we set the dtop
+ * parameters so that the fast inline lookup will always fail.
+ *
+ * - For general lookup, we use the slower hash_mod algorithm for
+ * huge dictionaries.
+ */
+#define dict_max_non_huge ((uint)(max_array_size / 2 + 1))
+
+/* Define the hashing function for names. */
+/* We don't have to scramble the index, because */
+/* indices are assigned in a scattered order (see name_ref in iname.c). */
+#define dict_name_index_hash(nidx) (nidx)
+
+/* Hash an arbitrary non-negative or unsigned integer into a dictionary. */
+#define dict_hash_mod_rem(hash, size) ((hash) % (size))
+#define dict_hash_mod_mask(hash, size) ((hash) & ((size) - 1))
+#define dict_hash_mod_small(hash, size) dict_hash_mod_rem(hash, size)
+#define dict_hash_mod_inline_small(hash, size) dict_hash_mod_rem(hash, size)
+#define dict_hash_mod_large(hash, size)\
+ (size > dict_max_non_huge ? dict_hash_mod_rem(hash, size) :\
+ dict_hash_mod_mask(hash, size))
+#define dict_hash_mod_inline_large(hash, size) dict_hash_mod_mask(hash, size)
+/* Round up the requested size of a dictionary. Return 0 if too big. */
+uint dict_round_size_small(uint rsize);
+uint dict_round_size_large(uint rsize);
+
+/* Choose the algorithms depending on the size of memory. */
+#if arch_small_memory
+# define dict_hash_mod(h, s) dict_hash_mod_small(h, s)
+# define dict_hash_mod_inline(h, s) dict_hash_mod_inline_small(h, s)
+# define dict_round_size(s) dict_round_size_small(s)
+#else
+# ifdef DEBUG
+# define dict_hash_mod(h, s)\
+ (gs_debug_c('.') ? dict_hash_mod_small(h, s) :\
+ dict_hash_mod_large(h, s))
+# define dict_hash_mod_inline(h, s)\
+ (gs_debug_c('.') ? dict_hash_mod_inline_small(h, s) :\
+ dict_hash_mod_inline_large(h, s))
+# define dict_round_size(s)\
+ (gs_debug_c('.') ? dict_round_size_small(s) :\
+ dict_round_size_large(s))
+# else
+# define dict_hash_mod(h, s) dict_hash_mod_large(h, s)
+# define dict_hash_mod_inline(h, s) dict_hash_mod_inline_large(h, s)
+# define dict_round_size(s) dict_round_size_large(s)
+# endif
+#endif
+
+#endif /* idict_INCLUDED */
diff --git a/psi/idictdef.h b/psi/idictdef.h
new file mode 100644
index 000000000..4f41d0714
--- /dev/null
+++ b/psi/idictdef.h
@@ -0,0 +1,88 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Internals of dictionary implementation */
+
+#ifndef idictdef_INCLUDED
+# define idictdef_INCLUDED
+
+/*
+ * A dictionary of capacity M is a structure containing the following
+ * elements (refs):
+ *
+ * keys - a t_shortarray or t_array of M+1 elements, containing
+ * the keys.
+ *
+ * values - a t_array of M+1 elements, containing the values.
+ *
+ * count - a t_integer whose value tells how many entries are
+ * occupied (N).
+ *
+ * maxlength - a t_integer whose value gives the client's view of
+ * the capacity (C). C may be less than M (see below).
+ *
+ * memory - a foreign t_struct referencing the allocator used to
+ * create this dictionary, which will also be used to expand or
+ * unpack it if necessary.
+ *
+ * C < M is possible because on large-memory systems, we usually round up M
+ * so that M is a power of 2 (see idict.h for details); this allows us to
+ * use masking rather than division for computing the initial hash probe.
+ * However, C is always the maxlength specified by the client, so clients
+ * get a consistent story.
+ *
+ * As noted above, the keys may be either in packed or unpacked form.
+ * The markers for unused and deleted entries are different in the two forms.
+ * In the packed form:
+ * unused entries contain packed_key_empty;
+ * deleted entries contain packed_key_deleted.
+ * In the unpacked form:
+ * unused entries contain a literal null;
+ * deleted entries contain an executable null.
+ *
+ * The first entry is always marked deleted, to reduce the cost of the
+ * wrap-around check.
+ *
+ * Note that if the keys slot in the dictionary is new,
+ * all the key slots are new (more recent than the last save).
+ * We use this fact to avoid saving stores into packed keys
+ * for newly created dictionaries.
+ *
+ * Note that name keys with indices above packed_name_max_index require using
+ * the unpacked form. */
+#define dict_is_packed(dct) r_has_type(&(dct)->keys, t_shortarray)
+#define packed_key_empty (pt_tag(pt_integer) + 0)
+#define packed_key_deleted (pt_tag(pt_integer) + 1)
+#define packed_key_impossible pt_tag(pt_full_ref) /* never matches */
+#define packed_name_key(nidx)\
+ ((nidx) <= packed_name_max_index ? pt_tag(pt_literal_name) + (nidx) :\
+ packed_key_impossible)
+/*
+ * Using a special mark for deleted entries causes lookup time to degrade
+ * as entries are inserted and deleted. This is not a problem, because
+ * entries are almost never deleted.
+ */
+#define d_maxlength(dct) ((uint)((dct)->maxlength.value.intval))
+#define d_set_maxlength(dct,siz) ((dct)->maxlength.value.intval = (siz))
+#define nslots(dct) r_size(&(dct)->values)
+#define npairs(dct) (nslots(dct) - 1)
+#define d_length(dct) ((uint)((dct)->count.value.intval))
+
+/* packed_search_value_pointer simplifies the access to
+ packed dictionary search template data - see idicttpl.h . */
+#define packed_search_value_pointer (pdict->values.value.refs + (kp - kbot))
+
+#endif /* idictdef_INCLUDED */
diff --git a/psi/idicttpl.h b/psi/idicttpl.h
new file mode 100644
index 000000000..16f47174c
--- /dev/null
+++ b/psi/idicttpl.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* A template for packed dictionary search method */
+
+/*
+ * Define template for searching a packed dictionary.
+ *
+ * Free variables:
+ * ref_packed kpack - holds the packed key.
+ * uint hash - holds the hash of the name.
+ * dict *pdict - points to the dictionary.
+ * uint size - holds npairs(pdict).
+ *
+ * Template parameters are :
+ * found - the found key action.
+ * deleted - the deleted key action.
+ * missing - the missed key action.
+ *
+ * Note that the template is *not* enclosed in {}, so that we can access
+ * the values of kbot and kp after leaving the template.
+ */
+
+ const ref_packed *kbot = pdict->keys.value.packed;
+ const int start = dict_hash_mod(hash, size) + 1;
+ register const ref_packed *kp = kbot + start;
+ int wrap = 0;
+
+ again:
+ for (; ; kp-- ) {
+ if_debug2('D', "[D]probe 0x%lx: 0x%x\n", (ulong)kp, *kp);
+ if ( *kp == kpack ) {
+ found;
+ } else if ( !r_packed_is_name(kp) ) {
+ /* Empty, deleted, or wraparound. Figure out which. */
+ if ( *kp == packed_key_empty )
+ missing;
+ if ( kp == kbot ) {
+ if (wrap)
+ break;
+ else {
+ wrap++;
+ kp += size; /* wrap */
+ goto again; /* skip "kp--". */
+ }
+ } else {
+ deleted;
+ }
+ }
+ }
diff --git a/psi/idisp.c b/psi/idisp.c
new file mode 100644
index 000000000..6a0157f8b
--- /dev/null
+++ b/psi/idisp.c
@@ -0,0 +1,107 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* idisp.c */
+
+/*
+ * This allows the interpreter to set the callback structure
+ * of the "display" device. This must set at the end of
+ * initialization and before the device is used.
+ * This is harmless if the 'display' device is not included.
+ * If gsapi_set_display_callback() is not called, this code
+ * won't even be used.
+ */
+
+#include "stdio_.h"
+#include "stdpre.h"
+#include "iapi.h"
+#include "ghost.h"
+#include "gp.h"
+#include "gscdefs.h"
+#include "gsmemory.h"
+#include "gstypes.h"
+#include "gsdevice.h"
+#include "iref.h"
+#include "imain.h"
+#include "iminst.h"
+#include "oper.h"
+#include "ostack.h"
+#include "gx.h"
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "idisp.h"
+#include "gdevdevn.h"
+#include "gsequivc.h"
+#include "gdevdsp.h"
+#include "gdevdsp2.h"
+
+int
+display_set_callback(gs_main_instance *minst, display_callback *callback)
+{
+ i_ctx_t *i_ctx_p;
+ bool was_open;
+ int code;
+ int exit_code = 0;
+ os_ptr op;
+ gx_device *dev;
+ gx_device_display *ddev;
+
+ /* If display device exists, copy prototype if needed and return
+ * device true
+ * If it doesn't exist, return
+ * false
+ */
+ const char getdisplay[] =
+ "devicedict /display known dup { /display finddevice exch } if";
+ code = gs_main_run_string(minst, getdisplay, 0, &exit_code,
+ &minst->error_object);
+ if (code < 0)
+ return code;
+
+ i_ctx_p = minst->i_ctx_p; /* run_string may change i_ctx_p if GC */
+ op = osp;
+ check_type(*op, t_boolean);
+ if (op->value.boolval) {
+ /* display device was included in Ghostscript so we need
+ * to set the callback structure pointer within it.
+ * If the device is already open, close it before
+ * setting callback, then reopen it.
+ */
+ check_read_type(op[-1], t_device);
+ dev = op[-1].value.pdevice;
+
+ was_open = dev->is_open;
+ if (was_open) {
+ code = gs_closedevice(dev);
+ if (code < 0)
+ return_error(code);
+ }
+
+ ddev = (gx_device_display *) dev;
+ ddev->callback = callback;
+
+ if (was_open) {
+ code = gs_opendevice(dev);
+ if (code < 0) {
+ dmprintf(dev->memory, "**** Unable to open the display device, quitting.\n");
+ return_error(code);
+ }
+ }
+ pop(1); /* device */
+ }
+ pop(1); /* boolean */
+ return 0;
+}
diff --git a/psi/idisp.h b/psi/idisp.h
new file mode 100644
index 000000000..0c6f0202a
--- /dev/null
+++ b/psi/idisp.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+#ifndef idisp_INCLUDED
+# define idisp_INCLUDED
+
+#ifndef display_callback_DEFINED
+# define display_callback_DEFINED
+typedef struct display_callback_s display_callback;
+#endif
+
+/* Called from imain.c to set the display callback in the device instance. */
+int display_set_callback(gs_main_instance *minst, display_callback *callback);
+
+#endif /* idisp_INCLUDED */
diff --git a/psi/idosave.h b/psi/idosave.h
new file mode 100644
index 000000000..cc1dd4dc4
--- /dev/null
+++ b/psi/idosave.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Supporting procedures for 'save' recording. */
+
+#ifndef idosave_INCLUDED
+# define idosave_INCLUDED
+
+/*
+ * Structure for saved change chain for save/restore. Because of the
+ * garbage collector, we need to distinguish the cases where the change
+ * is in a static object, a dynamic ref, or a dynamic struct.
+ */
+typedef struct alloc_change_s alloc_change_t;
+struct alloc_change_s {
+ alloc_change_t *next;
+ ref_packed *where;
+ ref contents;
+#define AC_OFFSET_STATIC (-2) /* static object */
+#define AC_OFFSET_REF (-1) /* dynamic ref */
+#define AC_OFFSET_ALLOCATED (-3) /* a newly allocated ref array */
+ short offset; /* if >= 0, offset within struct */
+};
+
+/*
+ * Save a change that must be undone by restore. We have to pass the
+ * pointer to the containing object to alloc_save_change for two reasons:
+ *
+ * - We need to know which VM the containing object is in, so we can
+ * know on which chain of saved changes to put the new change.
+ *
+ * - We need to know whether the object is an array of refs (which
+ * includes dictionaries) or a struct, so we can properly trace and
+ * relocate the pointer to it from the change record during garbage
+ * collection.
+ */
+
+int alloc_save_change(gs_dual_memory_t *dmem, const ref *pcont,
+ ref_packed *ptr, client_name_t cname);
+int alloc_save_change_in(gs_ref_memory_t *mem, const ref *pcont,
+ ref_packed *ptr, client_name_t cname);
+/* Remove an AC_OFFSET_ALLOCATED element. */
+void alloc_save_remove(gs_ref_memory_t *mem, ref_packed *obj, client_name_t cname);
+/* Allocate a structure for recording an allocation event. */
+int alloc_save_change_alloc(gs_ref_memory_t *mem, client_name_t cname, alloc_change_t **pcp);
+
+#endif /* idosave_INCLUDED */
diff --git a/psi/idparam.c b/psi/idparam.c
new file mode 100644
index 000000000..c16d4fb8a
--- /dev/null
+++ b/psi/idparam.c
@@ -0,0 +1,471 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Utilities for getting parameters out of dictionaries. */
+#include "memory_.h"
+#include "string_.h" /* for strlen */
+#include "ghost.h"
+#include "ierrors.h"
+#include "gsmatrix.h" /* for dict_matrix_param */
+#include "gsuid.h"
+#include "dstack.h" /* for systemdict */
+#include "idict.h"
+#include "iddict.h"
+#include "idparam.h" /* interface definition */
+#include "ilevel.h"
+#include "imemory.h" /* for iutil.h */
+#include "iname.h"
+#include "iutil.h"
+#include "oper.h" /* for check_proc */
+#include "store.h" /* for making empty proc */
+
+/* Get a Boolean parameter from a dictionary. */
+/* Return 0 if found, 1 if defaulted, <0 if wrong type. */
+int
+dict_bool_param(const ref * pdict, const char *kstr,
+ bool defaultval, bool * pvalue)
+{
+ ref *pdval;
+
+ if (pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0) {
+ *pvalue = defaultval;
+ return 1;
+ }
+ if (!r_has_type(pdval, t_boolean))
+ return_error(gs_error_typecheck);
+ *pvalue = pdval->value.boolval;
+ return 0;
+}
+
+/* Get an integer or null parameter from a dictionary. */
+/* Return 0 if found, 1 if defaulted, <0 if invalid. */
+/* If the parameter is null, return 2 without setting *pvalue. */
+/* Note that the default value may be out of range, in which case */
+/* a missing value will return gs_error_undefined rather than 1. */
+int
+dict_int_null_param(const ref * pdict, const char *kstr, int minval,
+ int maxval, int defaultval, int *pvalue)
+{
+ ref *pdval;
+ int code, ival;
+
+ if (pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0) {
+ ival = defaultval;
+ code = 1;
+ } else {
+ switch (r_type(pdval)) {
+ case t_integer:
+ if (pdval->value.intval < minval || pdval->value.intval > maxval)
+ return_error(gs_error_rangecheck);
+ ival = pdval->value.intval;
+ break;
+ case t_real:
+ /* Allow an integral real, because Fontographer */
+ /* (which violates the Adobe specs in other ways */
+ /* as well) sometimes generates output that */
+ /* needs this. */
+ if (pdval->value.realval < minval || pdval->value.realval > maxval)
+ return_error(gs_error_rangecheck);
+ ival = (long)pdval->value.realval;
+ if (ival != pdval->value.realval)
+ return_error(gs_error_rangecheck);
+ break;
+ case t_null:
+ return 2;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ code = 0;
+ }
+ if (ival < minval || ival > maxval) {
+ if (code == 1)
+ return_error(gs_error_undefined);
+ else
+ return_error(gs_error_rangecheck);
+ }
+ *pvalue = (int)ival;
+ return code;
+}
+/* Get an integer parameter from a dictionary. */
+/* Return like dict_int_null_param, but return gs_error_typecheck for null. */
+int
+dict_int_param(const ref * pdict, const char *kstr, int minval, int maxval,
+ int defaultval, int *pvalue)
+{
+ int code = dict_int_null_param(pdict, kstr, minval, maxval,
+ defaultval, pvalue);
+
+ return (code == 2 ? gs_note_error(gs_error_typecheck) : code);
+}
+
+/* Get an unsigned integer parameter from a dictionary. */
+/* Return 0 if found, 1 if defaulted, <0 if invalid. */
+/* Note that the default value may be out of range, in which case */
+/* a missing value will return gs_error_undefined rather than 1. */
+int
+dict_uint_param(const ref * pdict, const char *kstr,
+ uint minval, uint maxval, uint defaultval, uint * pvalue)
+{
+ ref *pdval;
+ int code;
+ uint ival;
+
+ if (pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0) {
+ ival = defaultval;
+ code = 1;
+ } else {
+ check_type_only(*pdval, t_integer);
+ if (pdval->value.intval != (uint) pdval->value.intval)
+ return_error(gs_error_rangecheck);
+ ival = (uint) pdval->value.intval;
+ code = 0;
+ }
+ if (ival < minval || ival > maxval) {
+ if (code == 1)
+ return_error(gs_error_undefined);
+ else
+ return_error(gs_error_rangecheck);
+ }
+ *pvalue = ival;
+ return code;
+}
+
+/* Get a float parameter from a dictionary. */
+/* Return 0 if found, 1 if defaulted, <0 if wrong type. */
+int
+dict_float_param(const ref * pdict, const char *kstr,
+ double defaultval, float *pvalue)
+{
+ ref *pdval;
+
+ if (pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0) {
+ *pvalue = defaultval;
+ return 1;
+ }
+ switch (r_type(pdval)) {
+ case t_integer:
+ *pvalue = (float)pdval->value.intval;
+ return 0;
+ case t_real:
+ *pvalue = pdval->value.realval;
+ return 0;
+ }
+ return_error(gs_error_typecheck);
+}
+
+/* Get an integer array from a dictionary. */
+/* See idparam.h for specification. */
+int
+dict_int_array_check_param(const gs_memory_t *mem, const ref * pdict,
+ const char *kstr, uint len, int *ivec, int under_error, int over_error)
+{
+ ref pa, *pdval;
+ uint size;
+ int i, code;
+
+ if (pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0)
+ return 0;
+ if (!r_is_array(pdval))
+ return_error(gs_error_typecheck);
+ size = r_size(pdval);
+ if (size > len)
+ return_error(over_error);
+ for (i = 0; i < size; i++) {
+ code = array_get(mem, pdval, i, &pa);
+ if (code < 0)
+ return code;
+ /* See dict_int_param above for why we allow reals here. */
+ switch (r_type(&pa)) {
+ case t_integer:
+ if (pa.value.intval != (int)pa.value.intval)
+ return_error(gs_error_rangecheck);
+ ivec[i] = (int)pa.value.intval;
+ break;
+ case t_real:
+ if (pa.value.realval < min_int ||
+ pa.value.realval > max_int ||
+ pa.value.realval != (int)pa.value.realval
+ )
+ return_error(gs_error_rangecheck);
+ ivec[i] = (int)pa.value.realval;
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ }
+ return (size == len || under_error >= 0 ? size :
+ gs_note_error(under_error));
+}
+int
+dict_int_array_param(const gs_memory_t *mem, const ref * pdict,
+ const char *kstr, uint maxlen, int *ivec)
+{
+ return dict_int_array_check_param(mem, pdict, kstr, maxlen, ivec,
+ 0, gs_error_limitcheck);
+}
+int
+dict_ints_param(const gs_memory_t *mem, const ref * pdict,
+ const char *kstr, uint len, int *ivec)
+{
+ return dict_int_array_check_param(mem, pdict, kstr, len, ivec,
+ gs_error_rangecheck, gs_error_rangecheck);
+}
+
+/* Get a float array from a dictionary. */
+/* If there are more than len elements, return over_error if it is < 0 */
+/* Otherwise, load len elements. If there are less than len elements, and */
+/* under_error < 0, return the error, otherwise return the count of elements */
+/* If the parameter is not in the dict, then if defaultvec is NULL, return 0; */
+/* if defaultvec is not NULL, copy it into fvec (len elements), and return len */
+int
+dict_float_array_check_param(const gs_memory_t *mem,
+ const ref * pdict, const char *kstr,
+ uint len, float *fvec, const float *defaultvec,
+ int under_error, int over_error)
+{
+ ref *pdval;
+ uint size;
+ int code;
+
+ if (pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0) {
+ if (defaultvec == NULL)
+ return 0;
+ memcpy(fvec, defaultvec, len * sizeof(float));
+ return len;
+ }
+
+ if (!r_is_array(pdval))
+ return_error(gs_error_typecheck);
+ size = r_size(pdval);
+ if (over_error < 0 && size > len)
+ return_error(over_error);
+
+ size = min(size, len); /* don't process more than we have room for */
+ code = process_float_array(mem, pdval, size, fvec);
+ return (code < 0 ? code :
+ size == len || under_error >= 0 ? size :
+ gs_note_error(under_error));
+}
+int
+dict_float_array_param(const gs_memory_t *mem,
+ const ref * pdict, const char *kstr,
+ uint maxlen, float *fvec, const float *defaultvec)
+{
+ return dict_float_array_check_param(mem ,pdict, kstr, maxlen, fvec,
+ defaultvec, 0, gs_error_limitcheck);
+}
+int
+dict_floats_param(const gs_memory_t *mem,
+ const ref * pdict, const char *kstr,
+ uint maxlen, float *fvec, const float *defaultvec)
+{
+ return dict_float_array_check_param(mem, pdict, kstr, maxlen,
+ fvec, defaultvec,
+ gs_error_rangecheck, gs_error_rangecheck);
+}
+
+/* Do dict_floats_param() and store [/key any] array in $error.errorinfo
+ * on failure. The key must be a permanently allocated C string.
+ */
+int
+dict_floats_param_errorinfo(i_ctx_t *i_ctx_p,
+ const ref * pdict, const char *kstr,
+ uint maxlen, float *fvec, const float *defaultvec)
+{
+ ref *val;
+ int code = dict_float_array_check_param(imemory, pdict, kstr, maxlen,
+ fvec, defaultvec, gs_error_rangecheck, gs_error_rangecheck);
+ if (code < 0) {
+ if (dict_find_string(pdict, kstr, &val) > 0)
+ gs_errorinfo_put_pair(i_ctx_p, kstr, strlen(kstr), val);
+ }
+ return code;
+}
+
+/*
+ * Get a procedure from a dictionary. If the key is missing,
+ * defaultval = false means substitute t__invalid;
+ * defaultval = true means substitute an empty procedure.
+ * In either case, return 1.
+ */
+int
+dict_proc_param(const ref * pdict, const char *kstr, ref * pproc,
+ bool defaultval)
+{
+ ref *pdval;
+
+ if (pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0) {
+ if (defaultval)
+ make_empty_const_array(pproc, a_readonly + a_executable);
+ else
+ make_t(pproc, t__invalid);
+ return 1;
+ }
+ check_proc(*pdval);
+ *pproc = *pdval;
+ return 0;
+}
+
+/* Get a matrix from a dictionary. */
+int
+dict_matrix_param(const gs_memory_t *mem, const ref * pdict, const char *kstr, gs_matrix * pmat)
+{
+ ref *pdval;
+
+ if (pdict == 0 || dict_find_string(pdict, kstr, &pdval) <= 0)
+ return_error(gs_error_typecheck);
+ return read_matrix(mem, pdval, pmat);
+}
+
+/* Get a UniqueID or XUID from a dictionary. */
+/* Return 0 if UniqueID, 1 if XUID, <0 if error. */
+/* If there is no uid, return default. */
+int
+dict_uid_param(const ref * pdict, gs_uid * puid, int defaultval,
+ gs_memory_t * mem, const i_ctx_t *i_ctx_p)
+{
+ ref *puniqueid;
+
+ if (pdict == 0) {
+ uid_set_invalid(puid);
+ return defaultval;
+ }
+ /* In a Level 2 environment, check for XUID first. */
+ if (level2_enabled &&
+ dict_find_string(pdict, "XUID", &puniqueid) > 0
+ ) {
+ long *xvalues;
+ uint size, i;
+
+ if (!r_has_type(puniqueid, t_array))
+ return_error(gs_error_typecheck);
+ size = r_size(puniqueid);
+ if (size == 0)
+ return_error(gs_error_rangecheck);
+ xvalues = (long *)gs_alloc_byte_array(mem, size, sizeof(long),
+ "get XUID");
+
+ if (xvalues == 0)
+ return_error(gs_error_VMerror);
+ /* Get the values from the XUID array. */
+ for (i = 0; i < size; i++) {
+ const ref *pvalue = puniqueid->value.const_refs + i;
+
+ if (!r_has_type(pvalue, t_integer)) {
+ gs_free_object(mem, xvalues, "get XUID");
+ return_error(gs_error_typecheck);
+ }
+ xvalues[i] = pvalue->value.intval;
+ }
+ uid_set_XUID(puid, xvalues, size);
+ return 1;
+ }
+ /* If no UniqueID entry, set the UID to invalid, */
+ /* because UniqueID need not be present in all fonts, */
+ /* and if it is, the legal range is 0 to 2^24-1. */
+ if (dict_find_string(pdict, "UniqueID", &puniqueid) <= 0) {
+ uid_set_invalid(puid);
+ return defaultval;
+ } else {
+ if (!r_has_type(puniqueid, t_integer))
+ return_error(gs_error_typecheck);
+ if (puniqueid->value.intval < 0 || puniqueid->value.intval > 0xffffff)
+ return_error(gs_error_rangecheck);
+ /* Apparently fonts created by Fontographer often have */
+ /* a UniqueID of 0, contrary to Adobe's specifications. */
+ /* Treat 0 as equivalent to -1 (no UniqueID). */
+ if (puniqueid->value.intval == 0) {
+ uid_set_invalid(puid);
+ return defaultval;
+ } else
+ uid_set_UniqueID(puid, puniqueid->value.intval);
+ }
+ return 0;
+}
+
+/* Check that a UID in a dictionary is equal to an existing, valid UID. */
+bool
+dict_check_uid_param(const ref * pdict, const gs_uid * puid)
+{
+ ref *puniqueid;
+
+ if (uid_is_XUID(puid)) {
+ uint size = uid_XUID_size(puid);
+ uint i;
+
+ if (dict_find_string(pdict, "XUID", &puniqueid) <= 0)
+ return false;
+ if (!r_has_type(puniqueid, t_array) ||
+ r_size(puniqueid) != size
+ )
+ return false;
+ for (i = 0; i < size; i++) {
+ const ref *pvalue = puniqueid->value.const_refs + i;
+
+ if (!r_has_type(pvalue, t_integer))
+ return false;
+ if (pvalue->value.intval != uid_XUID_values(puid)[i])
+ return false;
+ }
+ return true;
+ } else {
+ if (dict_find_string(pdict, "UniqueID", &puniqueid) <= 0)
+ return false;
+ return (r_has_type(puniqueid, t_integer) &&
+ puniqueid->value.intval == puid->id);
+ }
+}
+
+/* Create and store [/key any] array in $error.errorinfo.
+ * The key must be a permanently allocated C string.
+ * This routine is here because it is often used with parameter dictionaries.
+ */
+int
+gs_errorinfo_put_pair(i_ctx_t *i_ctx_p, const char *key, int len, const ref *any)
+{
+ int code;
+ ref pair, *aptr, key_name, *pderror;
+
+ code = name_ref(imemory_local, (const byte *)key, len, &key_name, 0);
+ if (code < 0)
+ return code;
+ code = gs_alloc_ref_array(iimemory_local, &pair, a_readonly, 2, "gs_errorinfo_put_pair");
+ if (code < 0)
+ return code;
+ aptr = pair.value.refs;
+ ref_assign_new(aptr, &key_name);
+ ref_assign_new(aptr+1, any);
+ if (dict_find_string(systemdict, "$error", &pderror) <= 0 ||
+ !r_has_type(pderror, t_dictionary) ||
+ idict_put_string(pderror, "errorinfo", &pair) < 0
+ )
+ return_error(gs_error_Fatal);
+ return 0;
+}
+
+/* Take a key's value from a given dictionary, create [/key any] array,
+ * and store it in $error.errorinfo.
+ * The key must be a permanently allocated C string.
+ */
+void
+gs_errorinfo_put_pair_from_dict(i_ctx_t *i_ctx_p, const ref *op, const char *key)
+{ ref *val, n;
+ if (dict_find_string(op, key, &val) <= 0) {
+ make_null(&n);
+ val = &n;
+ }
+ gs_errorinfo_put_pair(i_ctx_p, key, strlen(key), val);
+}
diff --git a/psi/idparam.h b/psi/idparam.h
new file mode 100644
index 000000000..499de311f
--- /dev/null
+++ b/psi/idparam.h
@@ -0,0 +1,132 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to idparam.c */
+
+#ifndef idparam_INCLUDED
+# define idparam_INCLUDED
+
+#ifndef gs_matrix_DEFINED
+# define gs_matrix_DEFINED
+typedef struct gs_matrix_s gs_matrix;
+#endif
+
+#ifndef gs_uid_DEFINED
+# define gs_uid_DEFINED
+typedef struct gs_uid_s gs_uid;
+#endif
+
+/*
+ * Unless otherwise noted, all the following routines return 0 for
+ * a valid parameter, 1 for a defaulted parameter, or <0 on error.
+ *
+ * Note that all the dictionary parameter routines take a C string,
+ * not a t_name ref *. Even though this is slower, it means that
+ * the GC doesn't have to worry about finding statically declared
+ * name refs, and we have that many fewer static variables.
+ *
+ * All these routines allow pdict == NULL, which they treat the same as
+ * pdict referring to an empty dictionary. Routines with "null" in their
+ * name return 2 if the parameter is null, without setting *pvalue.
+ */
+int dict_bool_param(const ref * pdict, const char *kstr,
+ bool defaultval, bool * pvalue);
+int dict_int_param(const ref * pdict, const char *kstr,
+ int minval, int maxval, int defaultval, int *pvalue);
+int dict_int_null_param(const ref * pdict, const char *kstr,
+ int minval, int maxval, int defaultval,
+ int *pvalue);
+int dict_uint_param(const ref * pdict, const char *kstr,
+ uint minval, uint maxval, uint defaultval,
+ uint * pvalue);
+int dict_float_param(const ref * pdict, const char *kstr,
+ double defaultval, float *pvalue);
+/*
+ * There are 3 variants of the procedures for getting array parameters.
+ * All return the element count if the parameter is present and of the
+ * correct size, 0 if the key is missing.
+ * _xxx_check_param return over_error if the array size > len,
+ * (under_error < 0 ? under_error : the element count) if the array
+ * size < len.
+ * _xxx_param return limitcheck if the array size > maxlen.
+ * Equivalent to _xxx_check_param(..., 0, limitcheck).
+ * _xxxs return rangecheck if the array size != len.
+ * Equivalent to _xxx_check_param(..., rangecheck, rangecheck).
+ * All can return other error codes (e.g., typecheck).
+ */
+int dict_int_array_check_param(const gs_memory_t *mem, const ref * pdict,
+ const char *kstr, uint len, int *ivec, int under_error, int over_error);
+int dict_int_array_param(const gs_memory_t *mem, const ref * pdict,
+ const char *kstr, uint maxlen, int *ivec);
+int dict_ints_param(const gs_memory_t *mem, const ref * pdict,
+ const char *kstr, uint len, int *ivec);
+/*
+ * For _float_array_param, if the parameter is missing and defaultvec is
+ * not NULL, copy (max)len elements from defaultvec to fvec and return
+ * (max)len.
+ */
+int dict_float_array_check_param(const gs_memory_t *mem,
+ const ref * pdict, const char *kstr,
+ uint len, float *fvec,
+ const float *defaultvec,
+ int under_error, int over_error);
+int dict_float_array_param(const gs_memory_t *mem,
+ const ref * pdict, const char *kstr,
+ uint maxlen, float *fvec,
+ const float *defaultvec);
+int dict_floats_param(const gs_memory_t *mem,
+ const ref * pdict, const char *kstr,
+ uint len, float *fvec,
+ const float *defaultvec);
+/* Do dict_floats_param() and store [/key any] array in $error.errorinfo
+ * on failure. The key must be a permanently allocated C string.
+ */
+int
+dict_floats_param_errorinfo(i_ctx_t *i_ctx_p,
+ const ref * pdict, const char *kstr,
+ uint maxlen, float *fvec, const float *defaultvec);
+
+/*
+ * For dict_proc_param,
+ * defaultval = false means substitute t__invalid;
+ * defaultval = true means substitute an empty procedure.
+ * In either case, return 1.
+ */
+int dict_proc_param(const ref * pdict, const char *kstr, ref * pproc,
+ bool defaultval);
+int dict_matrix_param(const gs_memory_t *mem,
+ const ref * pdict, const char *kstr,
+ gs_matrix * pmat);
+int dict_uid_param(const ref * pdict, gs_uid * puid, int defaultval,
+ gs_memory_t * mem, const i_ctx_t *i_ctx_p);
+
+/* Check that a UID in a dictionary is equal to an existing, valid UID. */
+bool dict_check_uid_param(const ref * pdict, const gs_uid * puid);
+
+/* Create and store [/key any] array in $error.errorinfo.
+ * The key must be a permanently allocated C string.
+ */
+int
+gs_errorinfo_put_pair(i_ctx_t *i_ctx_p, const char *key, int len, const ref *any);
+
+/* Take a key's value from a given dictionary, create [/key any] array,
+ * and store it in $error.errorinfo.
+ * The key must be a permanently allocated C string.
+ */
+void
+gs_errorinfo_put_pair_from_dict(i_ctx_t *i_ctx_p, const ref *op, const char *key);
+
+#endif /* idparam_INCLUDED */
diff --git a/psi/idsdata.h b/psi/idsdata.h
new file mode 100644
index 000000000..e18713c54
--- /dev/null
+++ b/psi/idsdata.h
@@ -0,0 +1,83 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Generic dictionary stack structure definition */
+
+#ifndef idsdata_INCLUDED
+# define idsdata_INCLUDED
+
+#include "isdata.h"
+
+/* Define the dictionary stack structure. */
+#ifndef dict_stack_DEFINED
+# define dict_stack_DEFINED
+typedef struct dict_stack_s dict_stack_t;
+#endif
+struct dict_stack_s {
+
+ ref_stack_t stack; /* the actual stack of dictionaries */
+
+/*
+ * Switching between Level 1 and Level 2 involves inserting and removing
+ * globaldict on the dictionary stack. Instead of truly inserting and
+ * removing entries, we replace globaldict by a copy of systemdict in
+ * Level 1 mode. min_dstack_size, the minimum number of entries, does not
+ * change depending on language level; the countdictstack and dictstack
+ * operators must take this into account.
+ */
+ uint min_size; /* size of stack after clearing */
+
+ int userdict_index; /* index of userdict on stack */
+
+/*
+ * Cache a value for fast checking of def operations.
+ * If the top entry on the dictionary stack is a writable dictionary,
+ * dsspace is the space of the dictionary; if it is a non-writable
+ * dictionary, dsspace = -1. Then def is legal precisely if
+ * r_space(pvalue) <= dsspace. Note that in order for this trick to work,
+ * the result of r_space must be a signed integer; some compilers treat
+ * enums as unsigned, probably in violation of the ANSI standard.
+ */
+ int def_space;
+
+/*
+ * Cache values for fast name lookup. If the top entry on the dictionary
+ * stack is a readable dictionary with packed keys, dtop_keys, dtop_npairs,
+ * and dtop_values are keys.value.packed, npairs, and values.value.refs
+ * for that dictionary; otherwise, these variables point to a dummy
+ * empty dictionary.
+ */
+ const ref_packed *top_keys;
+ uint top_npairs;
+ ref *top_values;
+
+/*
+ * Cache a copy of the bottom entry on the stack, which is never deleted.
+ */
+ ref system_dict;
+
+};
+
+/*
+ * The top-entry pointers are recomputed after garbage collection, so we
+ * don't declare them as pointers.
+ */
+#define public_st_dict_stack() /* in interp.c */\
+ gs_public_st_suffix_add0(st_dict_stack, dict_stack_t, "dict_stack_t",\
+ dict_stack_enum_ptrs, dict_stack_reloc_ptrs, st_ref_stack)
+#define st_dict_stack_num_ptrs st_ref_stack_num_ptrs
+
+#endif /* idsdata_INCLUDED */
diff --git a/psi/idstack.c b/psi/idstack.c
new file mode 100644
index 000000000..bc4f42af2
--- /dev/null
+++ b/psi/idstack.c
@@ -0,0 +1,267 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Implementation of dictionary stacks */
+#include "ghost.h"
+#include "idict.h"
+#include "idictdef.h"
+#include "idstack.h"
+#include "inamedef.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "iutil.h"
+#include "ivmspace.h"
+/*
+#include "idicttpl.h" - Do not remove this comment.
+ "idicttpl.h" is included below.
+*/
+
+/* Debugging statistics */
+#if defined(DEBUG) && !defined(GS_THREADSAFE)
+#include "idebug.h"
+#define MAX_STATS_DEPTH 6
+struct stats_dstack_s {
+ long lookups; /* total lookups */
+ long probes[2]; /* successful lookups on 1 or 2 probes */
+ long depth[MAX_STATS_DEPTH + 1]; /* stack depth of lookups requiring search */
+} stats_dstack;
+# define INCR(v) (++stats_dstack.v)
+#else
+# define INCR(v) DO_NOTHING
+#endif
+
+#if defined(DEBUG) && !defined(GS_THREADSAFE)
+/* Wrapper for dstack_find_name_by_index */
+ref *real_dstack_find_name_by_index(dict_stack_t * pds, uint nidx);
+ref *
+dstack_find_name_by_index(dict_stack_t * pds, uint nidx)
+{
+ ref *pvalue = real_dstack_find_name_by_index(pds, nidx);
+ dict *pdict = pds->stack.p->value.pdict;
+
+ INCR(lookups);
+ if (dict_is_packed(pdict)) {
+ uint hash =
+ dict_hash_mod(dict_name_index_hash(nidx), npairs(pdict)) + 1;
+
+ if (pdict->keys.value.packed[hash] ==
+ pt_tag(pt_literal_name) + nidx
+ )
+ INCR(probes[0]);
+ else if (pdict->keys.value.packed[hash - 1] ==
+ pt_tag(pt_literal_name) + nidx
+ )
+ INCR(probes[1]);
+ }
+ if (gs_debug_c('d') && !(stats_dstack.lookups % 1000))
+ dlprintf3("[d]lookups=%ld probe1=%ld probe2=%ld\n",
+ stats_dstack.lookups, stats_dstack.probes[0],
+ stats_dstack.probes[1]);
+ return pvalue;
+}
+#define dstack_find_name_by_index real_dstack_find_name_by_index
+#endif
+
+/* Check whether a dictionary is one of the permanent ones on the d-stack. */
+bool
+dstack_dict_is_permanent(const dict_stack_t * pds, const ref * pdref)
+{
+ dict *pdict = pdref->value.pdict;
+ int i;
+
+ if (pds->stack.extension_size == 0) { /* Only one block of d-stack. */
+ for (i = 0; i < pds->min_size; ++i)
+ if (pds->stack.bot[i].value.pdict == pdict)
+ return true;
+ } else { /* More than one block of d-stack. */
+ uint count = ref_stack_count(&pds->stack);
+
+ for (i = count - pds->min_size; i < count; ++i)
+ if (ref_stack_index(&pds->stack, i)->value.pdict == pdict)
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Look up a name on the dictionary stack.
+ * Return the pointer to the value if found, 0 if not.
+ */
+ref *
+dstack_find_name_by_index(dict_stack_t * pds, uint nidx)
+{
+ ds_ptr pdref = pds->stack.p;
+
+/* Since we know the hash function is the identity function, */
+/* there's no point in allocating a separate variable for it. */
+#define hash dict_name_index_hash(nidx)
+ ref_packed kpack = packed_name_key(nidx);
+
+ do {
+ dict *pdict = pdref->value.pdict;
+ uint size = npairs(pdict);
+#ifdef DEBUG
+ if (gs_debug_c('D')) {
+ const gs_memory_t *mem = dict_mem(pdict);
+ ref dnref;
+
+ name_index_ref(mem, nidx, &dnref);
+ dmlputs(mem, "[D]lookup ");
+ debug_print_name(mem, &dnref);
+ dmprintf3(mem," in 0x%lx(%u/%u)\n",
+ (ulong) pdict, dict_length(pdref),
+ dict_maxlength(pdref));
+ }
+#endif
+#define INCR_DEPTH(pdref)\
+ INCR(depth[min(MAX_STATS_DEPTH, pds->stack.p - pdref)])
+ if (dict_is_packed(pdict)) {
+# define found INCR_DEPTH(pdref); return packed_search_value_pointer
+# define deleted
+# define missing break;
+# include "idicttpl.h"
+# undef missing
+# undef deleted
+# undef found
+ } else {
+ /*
+ * The name_index macro takes mem as its first argument, but
+ * does not actually use it. The following is a little ugly,
+ * but it avoids a compiler warning.
+ */
+ /*const gs_memory_t *_mem_not_used = dict_mem(pdict);*/
+ ref *kbot = pdict->keys.value.refs;
+ register ref *kp;
+ int wrap = 0;
+
+ /* Search the dictionary */
+ for (kp = kbot + dict_hash_mod(hash, size) + 2;;) {
+ --kp;
+ if (r_has_type(kp, t_name)) {
+ if (name_index(_mem_not_used, kp) == nidx) {
+ INCR_DEPTH(pdref);
+ return pdict->values.value.refs + (kp - kbot);
+ }
+ } else if (r_has_type(kp, t_null)) { /* Empty, deleted, or wraparound. */
+ /* Figure out which. */
+ if (!r_has_attr(kp, a_executable))
+ break;
+ if (kp == kbot) { /* wrap */
+ if (wrap++)
+ break; /* 2 wraps */
+ kp += size + 1;
+ }
+ }
+ }
+ }
+#undef INCR_DEPTH
+ }
+ while (pdref-- > pds->stack.bot);
+ /* The name isn't in the top dictionary block. */
+ /* If there are other blocks, search them now (more slowly). */
+ if (!pds->stack.extension_size) /* no more blocks */
+ return (ref *) 0;
+ { /* We could use the STACK_LOOP macros, but for now, */
+ /* we'll do things the simplest way. */
+ ref key;
+ uint i = pds->stack.p + 1 - pds->stack.bot;
+ uint size = ref_stack_count(&pds->stack);
+ ref *pvalue;
+
+ dict *pdict = pds->stack.p->value.pdict;
+ const gs_memory_t *mem = dict_mem(pdict);
+
+ name_index_ref(mem, nidx, &key);
+ for (; i < size; i++) {
+ if (dict_find(ref_stack_index(&pds->stack, i),
+ &key, &pvalue) > 0
+ ) {
+ INCR(depth[min(MAX_STATS_DEPTH, i)]);
+ return pvalue;
+ }
+ }
+ }
+ return (ref *) 0;
+#undef hash
+}
+
+/* Set the cached values computed from the top entry on the dstack. */
+/* See idstack.h for details. */
+static const ref_packed no_packed_keys[2] =
+{packed_key_deleted, packed_key_empty};
+void
+dstack_set_top(dict_stack_t * pds)
+{
+ ds_ptr dsp = pds->stack.p;
+ dict *pdict = dsp->value.pdict;
+
+ if_debug3('d', "[d]dsp = 0x%lx -> 0x%lx, key array type = %d\n",
+ (ulong) dsp, (ulong) pdict, r_type(&pdict->keys));
+ if (dict_is_packed(pdict) &&
+ r_has_attr(dict_access_ref(dsp), a_read)
+ ) {
+ pds->top_keys = pdict->keys.value.packed;
+ pds->top_npairs = npairs(pdict);
+ pds->top_values = pdict->values.value.refs;
+ } else {
+ pds->top_keys = no_packed_keys;
+ pds->top_npairs = 1;
+ }
+ if (!r_has_attr(dict_access_ref(dsp), a_write))
+ pds->def_space = -1;
+ else
+ pds->def_space = r_space(dsp);
+}
+
+/* After a garbage collection, scan the permanent dictionaries and */
+/* update the cached value pointers in names. */
+void
+dstack_gc_cleanup(dict_stack_t * pds)
+{
+ uint count = ref_stack_count(&pds->stack);
+ uint dsi;
+
+ for (dsi = pds->min_size; dsi > 0; --dsi) {
+ const dict *pdict =
+ ref_stack_index(&pds->stack, count - dsi)->value.pdict;
+ uint size = nslots(pdict);
+ ref *pvalue = pdict->values.value.refs;
+ uint i;
+
+ for (i = 0; i < size; ++i, ++pvalue) {
+ ref key;
+ ref *old_pvalue;
+
+ array_get(dict_mem(pdict), &pdict->keys, (long)i, &key);
+ if (r_has_type(&key, t_name) &&
+ pv_valid(old_pvalue = key.value.pname->pvalue)
+ ) { /*
+ * The name only has a single definition,
+ * so it must be this one. Check to see if
+ * no relocation is actually needed; if so,
+ * we can skip the entire dictionary.
+ */
+ if (old_pvalue == pvalue) {
+ if_debug1('d', "[d]skipping dstack entry %d\n",
+ dsi - 1);
+ break;
+ }
+ /* Update the value pointer. */
+ key.value.pname->pvalue = pvalue;
+ }
+ }
+ }
+}
diff --git a/psi/idstack.h b/psi/idstack.h
new file mode 100644
index 000000000..c0fe131de
--- /dev/null
+++ b/psi/idstack.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Generic dictionary stack API */
+
+#ifndef idstack_INCLUDED
+# define idstack_INCLUDED
+
+#include "iddstack.h"
+#include "idsdata.h"
+#include "istack.h"
+
+/* Define the type of pointers into the dictionary stack. */
+typedef s_ptr ds_ptr;
+typedef const_s_ptr const_ds_ptr;
+
+/* Clean up a dictionary stack after a garbage collection. */
+void dstack_gc_cleanup(dict_stack_t *);
+
+/*
+ * Define a special fast entry for name lookup on a dictionary stack.
+ * The key is known to be a name; search the entire dict stack.
+ * Return the pointer to the value slot.
+ * If the name isn't found, just return 0.
+ */
+ref *dstack_find_name_by_index(dict_stack_t *, uint);
+
+/*
+ * Define an extra-fast macro for name lookup, optimized for
+ * a single-probe lookup in the top dictionary on the stack.
+ * Amazingly enough, this seems to hit over 90% of the time
+ * (aside from operators, of course, which are handled either with
+ * the special cache pointer or with 'bind').
+ */
+#define dstack_find_name_by_index_inline(pds,nidx,htemp)\
+ ((pds)->top_keys[htemp = dict_hash_mod_inline(dict_name_index_hash(nidx),\
+ (pds)->top_npairs) + 1] == pt_tag(pt_literal_name) + (nidx) ?\
+ (pds)->top_values + htemp : dstack_find_name_by_index(pds, nidx))
+/*
+ * Define a similar macro that only checks the top dictionary on the stack.
+ */
+#define if_dstack_find_name_by_index_top(pds,nidx,htemp,pvslot)\
+ if ( (((pds)->top_keys[htemp = dict_hash_mod_inline(dict_name_index_hash(nidx),\
+ (pds)->top_npairs) + 1] == pt_tag(pt_literal_name) + (nidx)) ?\
+ ((pvslot) = (pds)->top_values + (htemp), 1) :\
+ 0)\
+ )
+
+#endif /* idstack_INCLUDED */
diff --git a/psi/ierrors.h b/psi/ierrors.h
new file mode 100644
index 000000000..9480e11b0
--- /dev/null
+++ b/psi/ierrors.h
@@ -0,0 +1,69 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definition of error codes */
+
+#ifndef ierrors_INCLUDED
+# define ierrors_INCLUDED
+
+#include "gserrors.h"
+
+/*
+ * DO NOT USE THIS FILE IN THE GRAPHICS LIBRARY.
+ * THIS FILE IS PART OF THE POSTSCRIPT INTERPRETER.
+ * USE gserrors.h IN THE LIBRARY.
+ */
+
+/*
+ * A procedure that may return an error always returns
+ * a non-negative value (zero, unless otherwise noted) for success,
+ * or negative for failure.
+ * We use ints rather than an enum to avoid a lot of casting.
+ */
+
+/* Define the error name table */
+extern const char *const gs_error_names[];
+
+ /* ------ PostScript Level 1 errors ------ */
+
+#define LEVEL1_ERROR_NAMES\
+ "unknownerror", "dictfull", "dictstackoverflow", "dictstackunderflow",\
+ "execstackoverflow", "interrupt", "invalidaccess", "invalidexit",\
+ "invalidfileaccess", "invalidfont", "invalidrestore", "ioerror",\
+ "limitcheck", "nocurrentpoint", "rangecheck", "stackoverflow",\
+ "stackunderflow", "syntaxerror", "timeout", "typecheck", "undefined",\
+ "undefinedfilename", "undefinedresult", "unmatchedmark", "VMerror"
+
+ /* ------ Additional Level 2 errors (also in DPS) ------ */
+
+#define LEVEL2_ERROR_NAMES\
+ "configurationerror", "undefinedresource", "unregistered"
+
+ /* ------ Additional DPS errors ------ */
+
+#define DPS_ERROR_NAMES\
+ "invalidcontext", "invalidid"
+
+#define ERROR_NAMES\
+ LEVEL1_ERROR_NAMES, LEVEL2_ERROR_NAMES, DPS_ERROR_NAMES
+
+/*
+ * Define which error codes require re-executing the current object.
+ */
+#define GS_ERROR_IS_INTERRUPT(ecode)\
+ ((ecode) == gs_error_interrupt || (ecode) == gs_error_timeout)
+
+#endif /* ierrors_INCLUDED */
diff --git a/psi/iesdata.h b/psi/iesdata.h
new file mode 100644
index 000000000..09e577cc6
--- /dev/null
+++ b/psi/iesdata.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Generic execution stack structure definition */
+
+#ifndef iesdata_INCLUDED
+# define iesdata_INCLUDED
+
+#include "isdata.h"
+
+/* Define the execution stack structure. */
+typedef struct exec_stack_s {
+
+ ref_stack_t stack; /* the actual execution stack */
+
+/*
+ * To improve performance, we cache the currentfile pointer
+ * (i.e., `shallow-bind' it in Lisp terminology). The invariant is as
+ * follows: either esfile points to the currentfile slot on the estack
+ * (i.e., the topmost slot with an executable file), or it is 0.
+ * To maintain the invariant, it is sufficient that whenever a routine
+ * pushes or pops anything on the estack, if the object *might* be
+ * an executable file, invoke esfile_clear_cache(); alternatively,
+ * immediately after pushing an object, invoke esfile_check_cache().
+ */
+ ref *current_file;
+
+} exec_stack_t;
+
+/*
+ * current_file is cleared by garbage collection, so we don't declare it
+ * as a pointer.
+ */
+#define public_st_exec_stack() /* in interp.c */\
+ gs_public_st_suffix_add0(st_exec_stack, exec_stack_t, "exec_stack_t",\
+ exec_stack_enum_ptrs, exec_stack_reloc_ptrs, st_ref_stack)
+#define st_exec_stack_num_ptrs st_ref_stack_num_ptrs
+
+#endif /* iesdata_INCLUDED */
diff --git a/psi/iestack.h b/psi/iestack.h
new file mode 100644
index 000000000..ceaad67a6
--- /dev/null
+++ b/psi/iestack.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Generic execution stack API */
+
+#ifndef iestack_INCLUDED
+# define iestack_INCLUDED
+
+#include "iesdata.h"
+#include "istack.h"
+
+/* Define pointers into the execution stack. */
+typedef s_ptr es_ptr;
+typedef const_s_ptr const_es_ptr;
+
+/* Manage the current_file cache. */
+#define estack_clear_cache(pes) ((pes)->current_file = 0)
+#define estack_set_cache(pes,pref) ((pes)->current_file = (pref))
+#define estack_check_cache(pes)\
+ BEGIN\
+ if (r_has_type_attrs((pes)->stack.p, t_file, a_executable))\
+ estack_set_cache(pes, (pes)->stack.p);\
+ END
+
+#endif /* iestack_INCLUDED */
diff --git a/psi/ifapi.h b/psi/ifapi.h
new file mode 100644
index 000000000..104466e19
--- /dev/null
+++ b/psi/ifapi.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Font API interface */
+
+#ifndef ifapi_INCLUDED
+# define ifapi_INCLUDED
+
+#include "iplugin.h"
+#include "gstypes.h"
+#include "gsmatrix.h"
+#include "memory_.h"
+#include "gp.h"
+
+#include "gxfapi.h"
+
+#endif /* ifapi_INCLUDED */
diff --git a/psi/ifcid.h b/psi/ifcid.h
new file mode 100644
index 000000000..245d6c797
--- /dev/null
+++ b/psi/ifcid.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to zfcid.c */
+
+#ifndef ifcid_INCLUDED
+# define ifcid_INCLUDED
+
+/* Get the CIDSystemInfo of a CIDFont. */
+int cid_font_system_info_param(gs_cid_system_info_t *pcidsi,
+ const ref *prfont);
+
+/* Get the additional information for a CIDFontType 0 or 2 CIDFont. */
+int cid_font_data_param(os_ptr op, gs_font_cid_data *pdata,
+ ref *pGlyphDirectory);
+
+#endif /* ifcid_INCLUDED */
diff --git a/psi/ifilter.h b/psi/ifilter.h
new file mode 100644
index 000000000..c498afb3a
--- /dev/null
+++ b/psi/ifilter.h
@@ -0,0 +1,92 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter filter support */
+/* Requires oper.h, stream.h, strimpl.h */
+
+#ifndef ifilter_INCLUDED
+# define ifilter_INCLUDED
+
+#include "istream.h"
+#include "ivmspace.h"
+
+/*
+ * Define the utility procedures for creating filters.
+ * Note that a filter will be allocated in global VM iff the source/target
+ * and all relevant parameters (if any) are in global VM.
+ */
+int filter_read(
+ /* Operator arguments that were passed to zfxxx operator */
+ i_ctx_t *i_ctx_p,
+ /* # of parameters to pop off o-stack, */
+ /* not counting the source/target and also not counting any */
+ /* top dictionary operand (both of which will always be popped) */
+ int npop,
+ /* Template for stream */
+ const stream_template * templat,
+ /* Initialized s_xxx_state, 0 if no separate state */
+ stream_state * st,
+ /* Max of space attributes of all parameters referenced by */
+ /* the state, 0 if no such parameters */
+ uint space
+ );
+int filter_write(i_ctx_t *i_ctx_p, int npop,
+ const stream_template * templat,
+ stream_state * st, uint space);
+
+/*
+ * Define a simplified interface for streams with no parameters (except
+ * an optional dictionary) or state.
+ */
+int filter_read_simple(i_ctx_t *i_ctx_p,
+ const stream_template * templat);
+int filter_write_simple(i_ctx_t *i_ctx_p,
+ const stream_template * templat);
+
+/* Mark a filter stream as temporary. */
+/* See stream.h for the meaning of is_temp. */
+void filter_mark_temp(const ref * fop, int is_temp);
+
+/* Mark the source or target of a filter as temporary, and propagate */
+/* close_strm from the temporary stream to the filter. */
+void filter_mark_strm_temp(const ref * fop, int is_temp);
+
+/* Define a standard report_error procedure for filters, */
+/* that records the error message in $error.errorinfo. */
+stream_proc_report_error(filter_report_error);
+
+/*
+ * Define the state of a procedure-based stream.
+ * Note that procedure-based streams are defined at the Ghostscript
+ * interpreter level, unlike all other stream types which depend only
+ * on the stream package and the memory manager.
+ */
+typedef struct stream_proc_state_s {
+ stream_state_common;
+ bool eof;
+ uint index; /* current index within data */
+ ref proc;
+ ref data;
+} stream_proc_state;
+
+#define private_st_stream_proc_state() /* in zfproc.c */\
+ gs_private_st_complex_only(st_sproc_state, stream_proc_state,\
+ "procedure stream state", sproc_clear_marks, sproc_enum_ptrs, sproc_reloc_ptrs, 0)
+
+/* Test whether a stream is procedure-based. */
+bool s_is_proc(const stream *s);
+
+#endif /* ifilter_INCLUDED */
diff --git a/psi/ifilter2.h b/psi/ifilter2.h
new file mode 100644
index 000000000..31c134ccf
--- /dev/null
+++ b/psi/ifilter2.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Utilities for Level 2 filters */
+
+#ifndef ifilter2_INCLUDED
+# define ifilter2_INCLUDED
+
+/* Import setup code from zfdecode.c */
+int zcf_setup(os_ptr op, stream_CF_state * pcfs, gs_ref_memory_t *imem);
+int zlz_setup(os_ptr op, stream_LZW_state * plzs);
+int zpd_setup(os_ptr op, stream_PDiff_state * ppds);
+int zpp_setup(os_ptr op, stream_PNGP_state * ppps);
+
+#endif /* ifilter2_INCLUDED */
diff --git a/psi/ifont.h b/psi/ifont.h
new file mode 100644
index 000000000..e4f10365a
--- /dev/null
+++ b/psi/ifont.h
@@ -0,0 +1,119 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter internal font representation */
+
+#ifndef ifont_INCLUDED
+# define ifont_INCLUDED
+
+#include "gsccode.h" /* for gs_glyph, NUM_KNOWN_ENCODINGS */
+#include "gsstype.h" /* for extern_st */
+
+/* The external definition of fonts is given in the PostScript manual, */
+/* pp. 91-93. */
+
+/* The structure given below is 'client data' from the viewpoint */
+/* of the library. font-type objects (t_struct/st_font, "t_fontID") */
+/* point directly to a gs_font. */
+/* Note: From the GC point of view, it is handled as an array of refs, followed by */
+/* non-relocatable data starting with u.type42.mru_sfnts_index; this also */
+/* means all members of union _fs must start with the same number of refs */
+
+typedef struct font_data_s {
+ ref dict; /* font dictionary object */
+ ref BuildChar;
+ ref BuildGlyph;
+ ref Encoding;
+ ref CharStrings;
+ ref GlyphNames2Unicode;
+ union _fs {
+ struct _f1 {
+ ref OtherSubrs; /* from Private dictionary */
+ ref Subrs; /* from Private dictionary */
+ ref GlobalSubrs; /* from Private dictionary, */
+ /* for Type 2 charstrings */
+ } type1;
+ struct _f42 {
+ ref sfnts;
+ ref CIDMap; /* for CIDFontType 2 fonts */
+ ref GlyphDirectory;
+ /* the following are used to optimize lookups into sfnts */
+ uint mru_sfnts_index; /* index of most recently used sfnts string */
+ ulong mru_sfnts_pos; /* data bytes before sfnts string at index mru_sfnts_index */
+ } type42;
+ struct _fc0 {
+ ref GlyphDirectory;
+ ref GlyphData; /* (if preloaded) string or array of strings */
+ ref DataSource; /* (if not preloaded) reusable stream */
+ } cid0;
+ } u;
+} font_data;
+
+/*
+ * Even though the interpreter's part of the font data actually
+ * consists of refs, allocating it as refs tends to create sandbars;
+ * since it is always allocated and freed as a unit, we can treat it
+ * as an ordinary structure.
+ */
+/* st_font_data is exported for zdefault_make_font in zfont.c. */
+extern_st(st_font_data);
+#define public_st_font_data() /* in zbfont.c */\
+ static struct_proc_clear_marks(font_data_clear_marks);\
+ static struct_proc_enum_ptrs(font_data_enum_ptrs);\
+ static struct_proc_reloc_ptrs(font_data_reloc_ptrs);\
+ gs_public_st_complex_only(st_font_data, font_data, "font_data",\
+ font_data_clear_marks, font_data_enum_ptrs, font_data_reloc_ptrs, 0)
+#define pfont_data(pfont) ((font_data *)((pfont)->client_data))
+#define pfont_dict(pfont) (&pfont_data(pfont)->dict)
+
+/* ================Internal procedures shared across files ================ */
+
+/* ---------------- Exported by zchar.c ---------------- */
+
+/*
+ * Get the FontBBox from a font dictionary, if any; if none, or if invalid,
+ * return 4 zeros.
+ */
+int font_bbox_param(const gs_memory_t *mem, const ref *pfdict, double bbox[4]);
+
+/* ---------------- Exported by zfont.c ---------------- */
+
+#ifndef gs_font_DEFINED
+# define gs_font_DEFINED
+typedef struct gs_font_s gs_font;
+#endif
+
+/*
+ * Check a parameter that should be a valid font dictionary, and return
+ * the gs_font stored in its FID entry.
+ */
+int font_param(const ref * pfdict, gs_font ** ppfont);
+
+/*
+ * Mark a glyph as a PostScript name (if it isn't a CID) for the garbage
+ * collector. Return true if a mark was just added. This procedure is
+ * intended to be used as the mark_glyph procedure in the character cache.
+ */
+bool zfont_mark_glyph_name(const gs_memory_t *mem, gs_glyph glyph, void *ignore_data);
+
+/*
+ * Return information about a font, including information from the FontInfo
+ * dictionary. This procedure is intended to be used as the font_info
+ * procedure in all PostScript fonts.
+ */
+font_proc_font_info(zfont_info);
+
+#endif /* ifont_INCLUDED */
diff --git a/psi/ifont1.h b/psi/ifont1.h
new file mode 100644
index 000000000..42fff6d34
--- /dev/null
+++ b/psi/ifont1.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Type 1 font utilities shared with Type 2 */
+
+#ifndef ifont1_INCLUDED
+# define ifont1_INCLUDED
+
+/*
+ * Define the temporary structure for holding pointers to substructures of a
+ * CharString-based font. This is used for parsing Type 1, 2, and 4 fonts.
+ */
+typedef struct charstring_font_refs_s {
+ ref *Private;
+ ref no_subrs;
+ ref *OtherSubrs;
+ ref *Subrs;
+ ref *GlobalSubrs;
+} charstring_font_refs_t;
+
+/* Define the default lenIV value for a Type 1 font. */
+#define DEFAULT_LENIV_1 4
+
+/*
+ * Parse the substructures of a CharString-based font.
+ */
+int charstring_font_get_refs(const_os_ptr op, charstring_font_refs_t *pfr);
+
+/*
+ * Get the parameters of a CharString-based font or a FDArray entry for a
+ * CIDFontType 0 font. The client has filled in pdata1->interpret,
+ * subroutineNumberBias, lenIV, and (if applicable) the Type 2 elements.
+ */
+int charstring_font_params(const gs_memory_t *mem,
+ const_os_ptr op, charstring_font_refs_t *pfr,
+ gs_type1_data *pdata1);
+
+/*
+ * Fill in a newly built CharString-based font or FDArray entry.
+ */
+int charstring_font_init(gs_font_type1 *pfont,
+ const charstring_font_refs_t *pfr,
+ const gs_type1_data *pdata1);
+
+/*
+ * Finish building a CharString-based font. The client has filled in the
+ * same elements as for charstring_font_params.
+ */
+int build_charstring_font(i_ctx_t *i_ctx_p, os_ptr op,
+ build_proc_refs * pbuild, font_type ftype,
+ charstring_font_refs_t *pfr,
+ gs_type1_data *pdata1,
+ build_font_options_t options);
+
+#endif /* ifont1_INCLUDED */
diff --git a/psi/ifont2.h b/psi/ifont2.h
new file mode 100644
index 000000000..382fd2561
--- /dev/null
+++ b/psi/ifont2.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Type 2 font utilities 2 */
+
+#ifndef ifont2_INCLUDED
+# define ifont2_INCLUDED
+
+/* Default value of lenIV */
+#define DEFAULT_LENIV_2 (-1)
+
+/*
+ * Get the additional parameters for a Type 2 font (or FontType 2 FDArray
+ * entry in a CIDFontType 0 font), beyond those common to Type 1 and Type 2
+ * fonts.
+ */
+int type2_font_params(const_os_ptr op, charstring_font_refs_t *pfr,
+ gs_type1_data *pdata1);
+
+#endif /* ifont2_INCLUDED */
diff --git a/psi/ifont42.h b/psi/ifont42.h
new file mode 100644
index 000000000..5b337a394
--- /dev/null
+++ b/psi/ifont42.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Procedure for building a Type 42 or CIDFontType 2 font */
+
+#ifndef ifont42_INCLUDED
+# define ifont42_INCLUDED
+
+/* Build a type 11 (TrueType CID-keyed) or 42 (TrueType) font. */
+int build_gs_TrueType_font(i_ctx_t *, os_ptr, gs_font_type42 **, font_type,
+ gs_memory_type_ptr_t, const char *, const char *,
+ build_font_options_t);
+
+/*
+ * Check a parameter for being an array of strings. Return the parameter
+ * value even if it is of the wrong type.
+ */
+int font_string_array_param(const gs_memory_t *mem, os_ptr, const char *, ref *);
+
+/*
+ * Get a GlyphDirectory if present. Return 0 if present, 1 if absent,
+ * or an error code.
+ */
+int font_GlyphDirectory_param(os_ptr, ref *);
+
+/*
+ * Get a glyph outline from GlyphDirectory. Return an empty string if
+ * the glyph is missing or out of range.
+ */
+int font_gdir_get_outline(const gs_memory_t *mem, const ref *, long, gs_glyph_data_t *);
+
+/*
+ * Access a given byte offset and length in an array of strings.
+ * This is used for sfnts and for CIDMap. The int argument is 2 for sfnts
+ * (because of the strange behavior of odd-length strings), 1 for CIDMap.
+ * Return code : 0 - success, <0 - error,
+ * >0 - number of accessible bytes (client must cycle).
+ * - mru_index/pos are used as a hint where to start searching; NULLs for no hint.
+ */
+int string_array_access_proc(const gs_memory_t *mem, const ref *, int, ulong, uint,
+ uint *mru_index, ulong *mru_pos, const byte **);
+
+#endif /* ifont42_INCLUDED */
diff --git a/psi/ifrpred.h b/psi/ifrpred.h
new file mode 100644
index 000000000..e7cdd2397
--- /dev/null
+++ b/psi/ifrpred.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* filter_read_predictor prototype */
+
+#ifndef ifrpred_INCLUDED
+# define ifrpred_INCLUDED
+
+/* Exported by zfdecode.c for zfzlib.c */
+int filter_read_predictor(i_ctx_t *i_ctx_p, int npop,
+ const stream_template * templat,
+ stream_state * st);
+
+#endif /* ifrpred_INCLUDED */
diff --git a/psi/ifunc.h b/psi/ifunc.h
new file mode 100644
index 000000000..8e2748992
--- /dev/null
+++ b/psi/ifunc.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Internal interpreter interfaces for Functions */
+
+#ifndef ifunc_INCLUDED
+# define ifunc_INCLUDED
+
+#include "gsfunc.h"
+
+/* Define build procedures for the various function types. */
+#define build_function_proc(proc)\
+ int proc(i_ctx_t *i_ctx_p, const ref *op, const gs_function_params_t *params, int depth,\
+ gs_function_t **ppfn, gs_memory_t *mem)
+typedef build_function_proc((*build_function_proc_t));
+
+/* Define the table of build procedures, indexed by FunctionType. */
+typedef struct build_function_type_s {
+ int type;
+ build_function_proc_t proc;
+} build_function_type_t;
+extern const build_function_type_t build_function_type_table[];
+extern const uint build_function_type_table_count;
+
+/* Build a function structure from a PostScript dictionary. */
+int fn_build_function(i_ctx_t *i_ctx_p, const ref * op, gs_function_t ** ppfn,
+ gs_memory_t *mem, const float *shading_domain, const int num_inputs);
+int fn_build_sub_function(i_ctx_t *i_ctx_p, const ref * op, gs_function_t ** ppfn,
+ int depth, gs_memory_t *mem, const float *shading_domain, const int num_inputs);
+
+/* Called only from the routines in zcolor.c, in order to convert a tint-transform
+ * procedure to a function. Little more than a place-holder to avoid making a number
+ * of functions non-static
+ */
+int buildfunction(i_ctx_t * i_ctx_p, ref *arr, ref *pproc, int type);
+
+/*
+ * Collect a heap-allocated array of floats. If the key is missing, set
+ * *pparray = 0 and return 0; otherwise set *pparray and return the number
+ * of elements. Note that 0-length arrays are acceptable, so if the value
+ * returned is 0, the caller must check whether *pparray == 0.
+ */
+int fn_build_float_array(const ref * op, const char *kstr, bool required,
+ bool even, const float **pparray,
+ gs_memory_t *mem);
+
+/*
+ * Similar to fn_build_float_array() except
+ * - numeric parameter is accepted and converted to 1-element array
+ * - number of elements is not checked for even/odd
+ */
+int
+fn_build_float_array_forced(const ref * op, const char *kstr, bool required,
+ const float **pparray, gs_memory_t *mem);
+
+/*
+ * If a PostScript object is a Function procedure, return the function
+ * object, otherwise return 0.
+ */
+gs_function_t *ref_function(const ref *op);
+
+/*
+ * Operator to execute a function.
+ * <in1> ... <function_struct> %execfunction <out1> ...
+ */
+int zexecfunction(i_ctx_t *);
+
+#endif /* ifunc_INCLUDED */
diff --git a/psi/ifwpred.h b/psi/ifwpred.h
new file mode 100644
index 000000000..574d57184
--- /dev/null
+++ b/psi/ifwpred.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* filter_read_predictor prototype */
+
+#ifndef ifwpred_INCLUDED
+# define ifwpred_INCLUDED
+
+/* Exported by zfilter2.c for zfzlib.c */
+int filter_write_predictor(i_ctx_t *i_ctx_p, int npop,
+ const stream_template * templat,
+ stream_state * st);
+
+#endif /* ifwpred_INCLUDED */
diff --git a/psi/igc.c b/psi/igc.c
new file mode 100644
index 000000000..3238f3f11
--- /dev/null
+++ b/psi/igc.c
@@ -0,0 +1,1392 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Garbage collector for Ghostscript */
+#include "memory_.h"
+#include "ghost.h"
+#include "ierrors.h"
+#include "gsexit.h"
+#include "gsmdebug.h"
+#include "gsstruct.h"
+#include "iastate.h"
+#include "isave.h"
+#include "isstate.h"
+#include "idict.h"
+#include "ipacked.h"
+#include "istruct.h"
+#include "igc.h"
+#include "igcstr.h"
+#include "inamedef.h"
+#include "opdef.h" /* for marking oparray names */
+
+/* Define whether to force all garbage collections to be global. */
+static const bool I_FORCE_GLOBAL_GC = false;
+
+/* Define whether to bypass the collector entirely. */
+static const bool I_BYPASS_GC = false;
+
+/* Define an entry on the mark stack. */
+typedef struct {
+ void *ptr;
+ uint index;
+ bool is_refs;
+} ms_entry;
+
+/* Define (a segment of) the mark stack. */
+/* entries[0] has ptr = 0 to indicate the bottom of the stack. */
+/* count additional entries follow this structure. */
+typedef struct gc_mark_stack_s gc_mark_stack;
+struct gc_mark_stack_s {
+ gc_mark_stack *prev;
+ gc_mark_stack *next;
+ uint count;
+ bool on_heap; /* if true, allocated during GC */
+ ms_entry entries[1];
+};
+
+/* Define the mark stack sizing parameters. */
+#define ms_size_default 100 /* default, allocated on C stack */
+/* This should probably be defined as a parameter somewhere.... */
+#define ms_size_desired /* for additional allocation */\
+ ((max_ushort - sizeof(gc_mark_stack)) / sizeof(ms_entry) - 10)
+#define ms_size_min 50 /* min size for segment in free block */
+
+/* Forward references */
+
+static void gc_init_mark_stack(gc_mark_stack *, uint);
+static void gc_objects_clear_marks(const gs_memory_t *mem, chunk_t *);
+static void gc_unmark_names(name_table *, op_array_table *, op_array_table *);
+static int gc_trace(gs_gc_root_t *, gc_state_t *, gc_mark_stack *);
+static int gc_rescan_chunk(chunk_t *, gc_state_t *, gc_mark_stack *);
+static int gc_trace_chunk(const gs_memory_t *mem, chunk_t *, gc_state_t *, gc_mark_stack *);
+static bool gc_trace_finish(gc_state_t *);
+static void gc_clear_reloc(chunk_t *);
+static void gc_objects_set_reloc(gc_state_t * gcst, chunk_t *);
+static void gc_do_reloc(chunk_t *, gs_ref_memory_t *, gc_state_t *);
+static void gc_objects_compact(chunk_t *, gc_state_t *);
+static void gc_free_empty_chunks(gs_ref_memory_t *);
+
+/* Forward references for pointer types */
+static ptr_proc_unmark(ptr_struct_unmark);
+static ptr_proc_mark(ptr_struct_mark);
+static ptr_proc_unmark(ptr_string_unmark);
+static ptr_proc_mark(ptr_string_mark);
+static ptr_proc_unmark(ptr_name_index_unmark);
+static ptr_proc_mark(ptr_name_index_mark);
+/*ptr_proc_unmark(ptr_ref_unmark); *//* in igc.h */
+/*ptr_proc_mark(ptr_ref_mark); *//* in igc.h */
+static ptr_proc_reloc(igc_reloc_struct_ptr, void);
+
+ptr_proc_reloc(igc_reloc_ref_ptr, ref_packed); /* in igcref.c */
+refs_proc_reloc(igc_reloc_refs); /* in igcref.c */
+
+/* Define this GC's procedure vector. */
+static const gc_procs_with_refs_t igc_procs = {
+ igc_reloc_struct_ptr, igc_reloc_string, igc_reloc_const_string,
+ igc_reloc_param_string, igc_reloc_ref_ptr, igc_reloc_refs
+};
+
+/* Pointer type descriptors. */
+/* Note that the trace/mark routine has special knowledge of ptr_ref_type */
+/* and ptr_struct_type -- it assumes that no other types have embedded */
+/* pointers. Note also that the reloc procedures for string, ref and name */
+/* pointers are never called. */
+typedef ptr_proc_reloc((*ptr_proc_reloc_t), void);
+const gs_ptr_procs_t ptr_struct_procs =
+{ptr_struct_unmark, ptr_struct_mark, (ptr_proc_reloc_t) igc_reloc_struct_ptr};
+const gs_ptr_procs_t ptr_string_procs =
+{ptr_string_unmark, ptr_string_mark, NULL};
+const gs_ptr_procs_t ptr_const_string_procs =
+{ptr_string_unmark, ptr_string_mark, NULL};
+const gs_ptr_procs_t ptr_ref_procs =
+{ptr_ref_unmark, ptr_ref_mark, NULL};
+const gs_ptr_procs_t ptr_name_index_procs =
+{ptr_name_index_unmark, ptr_name_index_mark, NULL};
+
+/* ------ Main program ------ */
+
+/* Top level of garbage collector. */
+#ifdef DEBUG
+static void
+end_phase(const gs_memory_t *mem, const char *str)
+{
+ if (gs_debug_c('6')) {
+ dmlprintf1(mem, "[6]---------------- end %s ----------------\n",
+ (const char *)str);
+ dmflush(mem);
+ }
+}
+static const char *const depth_dots_string = "..........";
+static const char *
+depth_dots(const ms_entry * sp, const gc_mark_stack * pms)
+{
+ int depth = sp - pms->entries - 1;
+ const gc_mark_stack *pss = pms;
+
+ while ((pss = pss->prev) != 0)
+ depth += pss->count - 1;
+ return depth_dots_string + (depth >= 10 ? 0 : 10 - depth);
+}
+static void
+gc_validate_spaces(gs_ref_memory_t **spaces, int max_space, gc_state_t *gcst)
+{
+ int i;
+ gs_ref_memory_t *mem;
+
+ for (i = 1; i <= max_space; ++i)
+ if ((mem = spaces[i]) != 0)
+ ialloc_validate_memory(mem, gcst);
+}
+#else /* !DEBUG */
+# define end_phase(mem,str) DO_NOTHING
+#endif /* DEBUG */
+
+void
+gs_gc_reclaim(vm_spaces * pspaces, bool global)
+{
+#define nspaces ((i_vm_max + 1) * 2) /* * 2 for stable allocators */
+
+ vm_spaces spaces;
+ gs_ref_memory_t *space_memories[nspaces];
+ gs_gc_root_t space_roots[nspaces];
+ int max_trace; /* max space_ to trace */
+ int min_collect; /* min space_ to collect */
+ int min_collect_vm_space; /* min VM space to collect */
+ int ispace;
+ gs_ref_memory_t *mem;
+ chunk_t *cp;
+ gs_gc_root_t *rp;
+ gc_state_t state;
+ struct _msd {
+ gc_mark_stack stack;
+ ms_entry body[ms_size_default];
+ } ms_default;
+ gc_mark_stack *mark_stack = &ms_default.stack;
+ const gs_memory_t *cmem;
+
+ /* Optionally force global GC for debugging. */
+
+ if (I_FORCE_GLOBAL_GC)
+ global = true;
+
+ /* Determine which spaces we are tracing and collecting. */
+
+ spaces = *pspaces;
+ cmem = space_system->stable_memory;
+ space_memories[1] = space_system;
+ space_memories[2] = space_global;
+ min_collect = max_trace = 2;
+ min_collect_vm_space = i_vm_global;
+ if (space_global->stable_memory != (gs_memory_t *)space_global)
+ space_memories[++max_trace] =
+ (gs_ref_memory_t *)space_global->stable_memory;
+ if (space_global != space_local) {
+ space_memories[++max_trace] = space_local;
+ min_collect = max_trace;
+ min_collect_vm_space = i_vm_local;
+ if (space_local->stable_memory != (gs_memory_t *)space_local)
+ space_memories[++max_trace] =
+ (gs_ref_memory_t *)space_local->stable_memory;
+ }
+ if (global)
+ min_collect = min_collect_vm_space = 1;
+
+#define for_spaces(i, n)\
+ for (i = 1; i <= n; ++i)
+#define for_collected_spaces(i)\
+ for (i = min_collect; i <= max_trace; ++i)
+#define for_space_mems(i, mem)\
+ for (mem = space_memories[i]; mem != 0; mem = &mem->saved->state)
+#define for_mem_chunks(mem, cp)\
+ for (cp = (mem)->cfirst; cp != 0; cp = cp->cnext)
+#define for_space_chunks(i, mem, cp)\
+ for_space_mems(i, mem) for_mem_chunks(mem, cp)
+#define for_chunks(n, mem, cp)\
+ for_spaces(ispace, n) for_space_chunks(ispace, mem, cp)
+#define for_collected_chunks(mem, cp)\
+ for_collected_spaces(ispace) for_space_chunks(ispace, mem, cp)
+#define for_roots(n, mem, rp)\
+ for_spaces(ispace, n)\
+ for (mem = space_memories[ispace], rp = mem->roots; rp != 0; rp = rp->next)
+
+ /* Initialize the state. */
+
+ state.procs = &igc_procs;
+ state.loc.memory = space_global; /* any one will do */
+
+ state.loc.cp = 0;
+ state.spaces = spaces;
+ state.min_collect = min_collect_vm_space << r_space_shift;
+ state.relocating_untraced = false;
+ state.heap = state.loc.memory->non_gc_memory;
+ state.ntable = state.heap->gs_lib_ctx->gs_name_table;
+
+ /* Register the allocators themselves as roots, */
+ /* so we mark and relocate the change and save lists properly. */
+
+ for_spaces(ispace, max_trace)
+ gs_register_struct_root((gs_memory_t *)space_memories[ispace],
+ &space_roots[ispace],
+ (void **)&space_memories[ispace],
+ "gc_top_level");
+
+ end_phase(state.heap,"register space roots");
+
+#ifdef DEBUG
+
+ /* Pre-validate the state. This shouldn't be necessary.... */
+
+ gc_validate_spaces(space_memories, max_trace, &state);
+
+ end_phase(state.heap,"pre-validate pointers");
+
+#endif
+
+ if (I_BYPASS_GC) { /* Don't collect at all. */
+ goto no_collect;
+ }
+
+ /* Clear marks in spaces to be collected. */
+
+ for_collected_spaces(ispace)
+ for_space_chunks(ispace, mem, cp) {
+ gc_objects_clear_marks((const gs_memory_t *)mem, cp);
+ gc_strings_set_marks(cp, false);
+ }
+
+ end_phase(state.heap,"clear chunk marks");
+
+ /* Clear the marks of roots. We must do this explicitly, */
+ /* since some roots are not in any chunk. */
+
+ for_roots(max_trace, mem, rp) {
+ enum_ptr_t eptr;
+
+ eptr.ptr = *rp->p;
+ if_debug_root('6', (const gs_memory_t *)mem, "[6]unmarking root", rp);
+ (*rp->ptype->unmark)(&eptr, &state);
+ }
+
+ end_phase(state.heap,"clear root marks");
+
+ if (global) {
+ op_array_table *global_ops = get_global_op_array(cmem);
+ op_array_table *local_ops = get_local_op_array(cmem);
+ gc_unmark_names(state.ntable, global_ops, local_ops);
+ }
+
+ /* Initialize the (default) mark stack. */
+
+ gc_init_mark_stack(&ms_default.stack, ms_size_default);
+ ms_default.stack.prev = 0;
+ ms_default.stack.on_heap = false;
+
+ /* Add all large-enough free blocks to the mark stack. */
+ /* Also initialize the rescan pointers. */
+
+ {
+ gc_mark_stack *end = mark_stack;
+
+ for_chunks(max_trace, mem, cp) {
+ uint avail = cp->ctop - cp->cbot;
+
+ if (avail >= sizeof(gc_mark_stack) + sizeof(ms_entry) *
+ ms_size_min &&
+ !cp->inner_count
+ ) {
+ gc_mark_stack *pms = (gc_mark_stack *) cp->cbot;
+
+ gc_init_mark_stack(pms, (avail - sizeof(gc_mark_stack)) /
+ sizeof(ms_entry));
+ end->next = pms;
+ pms->prev = end;
+ pms->on_heap = false;
+ if_debug2m('6', (const gs_memory_t *)mem,
+ "[6]adding free 0x%lx(%u) to mark stack\n",
+ (ulong) pms, pms->count);
+ }
+ cp->rescan_bot = cp->cend;
+ cp->rescan_top = cp->cbase;
+ }
+ }
+
+ /* Mark reachable objects. */
+
+ {
+ int more = 0;
+
+ /* Mark from roots. */
+
+ for_roots(max_trace, mem, rp) {
+ if_debug_root('6', (const gs_memory_t *)mem, "[6]marking root", rp);
+ more |= gc_trace(rp, &state, mark_stack);
+ }
+
+ end_phase(state.heap,"mark");
+
+ /* If this is a local GC, mark from non-local chunks. */
+
+ if (!global)
+ for_chunks(min_collect - 1, mem, cp)
+ more |= gc_trace_chunk((const gs_memory_t *)mem, cp, &state, mark_stack);
+
+ /* Handle mark stack overflow. */
+
+ while (more < 0) { /* stack overflowed */
+ more = 0;
+ for_chunks(max_trace, mem, cp)
+ more |= gc_rescan_chunk(cp, &state, mark_stack);
+ }
+
+ end_phase(state.heap,"mark overflow");
+ }
+
+ /* Free the mark stack. */
+
+ {
+ gc_mark_stack *pms = mark_stack;
+
+ while (pms->next)
+ pms = pms->next;
+ while (pms) {
+ gc_mark_stack *prev = pms->prev;
+
+ if (pms->on_heap)
+ gs_free_object(state.heap, pms, "gc mark stack");
+ else
+ gs_alloc_fill(pms, gs_alloc_fill_free,
+ sizeof(*pms) + sizeof(ms_entry) * pms->count);
+ pms = prev;
+ }
+ }
+
+ end_phase(state.heap,"free mark stack");
+
+ if (global) {
+ gc_trace_finish(&state);
+ names_trace_finish(state.ntable, &state);
+
+ end_phase(state.heap,"finish trace");
+ }
+
+ /* Filter save change lists with removing elements,
+ which point to unmarked blocks of refs. */
+ {
+ int i;
+
+ for_collected_spaces(i) {
+ gs_ref_memory_t *mem = space_memories[i];
+
+ alloc_save__filter_changes(mem);
+ }
+ }
+ /* Clear marks and relocation in spaces that are only being traced. */
+ /* We have to clear the marks first, because we want the */
+ /* relocation to wind up as o_untraced, not o_unmarked. */
+
+ for_chunks(min_collect - 1, mem, cp)
+ gc_objects_clear_marks((const gs_memory_t *)mem, cp);
+
+ end_phase(state.heap,"post-clear marks");
+
+ for_chunks(min_collect - 1, mem, cp)
+ gc_clear_reloc(cp);
+
+ end_phase(state.heap,"clear reloc");
+
+ /* Set the relocation of roots outside any chunk to o_untraced, */
+ /* so we won't try to relocate pointers to them. */
+ /* (Currently, there aren't any.) */
+
+ /* Disable freeing in the allocators of the spaces we are */
+ /* collecting, so finalization procedures won't cause problems. */
+ {
+ int i;
+
+ for_collected_spaces(i)
+ gs_enable_free((gs_memory_t *)space_memories[i], false);
+ }
+
+ /* Compute relocation based on marks, in the spaces */
+ /* we are going to compact. Also finalize freed objects. */
+ state.cur_mem = (gs_memory_t *)mem;
+
+ for_collected_chunks(mem, cp) {
+ gc_objects_set_reloc(&state, cp);
+ gc_strings_set_reloc(cp);
+ }
+
+ /* Re-enable freeing. */
+ {
+ int i;
+
+ for_collected_spaces(i)
+ gs_enable_free((gs_memory_t *)space_memories[i], true);
+ }
+
+ end_phase(state.heap,"set reloc");
+
+ /* Relocate pointers. */
+
+ state.relocating_untraced = true;
+ for_chunks(min_collect - 1, mem, cp)
+ gc_do_reloc(cp, mem, &state);
+ state.relocating_untraced = false;
+ for_collected_chunks(mem, cp)
+ gc_do_reloc(cp, mem, &state);
+
+ end_phase(state.heap,"relocate chunks");
+
+ for_roots(max_trace, mem, rp) {
+ if_debug3m('6', (const gs_memory_t *)mem,
+ "[6]relocating root 0x%lx: 0x%lx -> 0x%lx\n",
+ (ulong) rp, (ulong) rp->p, (ulong) * rp->p);
+ if (rp->ptype == ptr_ref_type) {
+ ref *pref = (ref *) * rp->p;
+
+ igc_reloc_refs((ref_packed *) pref,
+ (ref_packed *) (pref + 1),
+ &state);
+ } else
+ *rp->p = (*rp->ptype->reloc) (*rp->p, &state);
+ if_debug3m('6', (const gs_memory_t *)mem,
+ "[6]relocated root 0x%lx: 0x%lx -> 0x%lx\n",
+ (ulong) rp, (ulong) rp->p, (ulong) * rp->p);
+ }
+
+ end_phase(state.heap,"relocate roots");
+
+ /* Compact data. We only do this for spaces we are collecting. */
+
+ for_collected_spaces(ispace) {
+ for_space_mems(ispace, mem) {
+ for_mem_chunks(mem, cp) {
+ if_debug_chunk('6', (const gs_memory_t *)mem, "[6]compacting chunk", cp);
+ gc_objects_compact(cp, &state);
+ gc_strings_compact(cp, cmem);
+ if_debug_chunk('6', (const gs_memory_t *)mem, "[6]after compaction:", cp);
+ if (mem->pcc == cp)
+ mem->cc = *cp;
+ }
+ mem->saved = mem->reloc_saved;
+ ialloc_reset_free(mem);
+ }
+ }
+
+ end_phase(state.heap,"compact");
+
+ /* Free empty chunks. */
+
+ for_collected_spaces(ispace) {
+ for_space_mems(ispace, mem) {
+ gc_free_empty_chunks(mem);
+ }
+ }
+
+ end_phase(state.heap,"free empty chunks");
+
+ /*
+ * Update previous_status to reflect any freed chunks,
+ * and set inherited to the negative of allocated,
+ * so it has no effect. We must update previous_status by
+ * working back-to-front along the save chain, using pointer reversal.
+ * (We could update inherited in any order, since it only uses
+ * information local to the individual save level.)
+ */
+
+ for_collected_spaces(ispace) { /* Reverse the pointers. */
+ alloc_save_t *curr;
+ alloc_save_t *prev = 0;
+ alloc_save_t *next;
+ gs_memory_status_t total;
+
+ for (curr = space_memories[ispace]->saved; curr != 0;
+ prev = curr, curr = next
+ ) {
+ next = curr->state.saved;
+ curr->state.saved = prev;
+ }
+ /* Now work the other way, accumulating the values. */
+ total.allocated = 0, total.used = 0;
+ for (curr = prev, prev = 0; curr != 0;
+ prev = curr, curr = next
+ ) {
+ mem = &curr->state;
+ next = mem->saved;
+ mem->saved = prev;
+ mem->previous_status = total;
+ if_debug3m('6', (const gs_memory_t *)mem,
+ "[6]0x%lx previous allocated=%lu, used=%lu\n",
+ (ulong) mem, total.allocated, total.used);
+ gs_memory_status((gs_memory_t *) mem, &total);
+ mem->gc_allocated = mem->allocated + total.allocated;
+ }
+ mem = space_memories[ispace];
+ mem->previous_status = total;
+ mem->gc_allocated = mem->allocated + total.allocated;
+ if_debug3m('6', (const gs_memory_t *)mem,
+ "[6]0x%lx previous allocated=%lu, used=%lu\n",
+ (ulong) mem, total.allocated, total.used);
+ }
+
+ end_phase(state.heap,"update stats");
+
+ no_collect:
+
+ /* Unregister the allocator roots. */
+
+ for_spaces(ispace, max_trace)
+ gs_unregister_root((gs_memory_t *)space_memories[ispace],
+ &space_roots[ispace], "gc_top_level");
+
+ end_phase(state.heap,"unregister space roots");
+
+#ifdef DEBUG
+
+ /* Validate the state. This shouldn't be necessary.... */
+
+ gc_validate_spaces(space_memories, max_trace, &state);
+
+ end_phase(state.heap,"validate pointers");
+
+#endif
+}
+
+/* ------ Debugging utilities ------ */
+
+/* Validate a pointer to an object header. */
+#ifdef DEBUG
+# define debug_check_object(pre, cp, gcst)\
+ ialloc_validate_object((pre) + 1, cp, gcst)
+#else
+# define debug_check_object(pre, cp, gcst) DO_NOTHING
+#endif
+
+/* ------ Unmarking phase ------ */
+
+/* Unmark a single struct. */
+static void
+ptr_struct_unmark(enum_ptr_t *pep, gc_state_t * ignored)
+{
+ void *const vptr = (void *)pep->ptr; /* break const */
+
+ if (vptr != 0)
+ o_set_unmarked(((obj_header_t *) vptr - 1));
+}
+
+/* Unmark a single string. */
+static void
+ptr_string_unmark(enum_ptr_t *pep, gc_state_t * gcst)
+{
+ discard(gc_string_mark(pep->ptr, pep->size, false, gcst));
+}
+
+/* Unmark a single name. */
+static void
+ptr_name_index_unmark(enum_ptr_t *pep, gc_state_t * gcst)
+{
+ /* Do nothing */
+}
+
+/* Unmark the objects in a chunk. */
+static void
+gc_objects_clear_marks(const gs_memory_t *mem, chunk_t * cp)
+{
+ if_debug_chunk('6', mem, "[6]unmarking chunk", cp);
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ struct_proc_clear_marks((*proc)) =
+ pre->o_type->clear_marks;
+#ifdef DEBUG
+ if (pre->o_type != &st_free)
+ debug_check_object(pre, cp, NULL);
+#endif
+ if_debug3m('7', (const gs_memory_t *)mem, " [7](un)marking %s(%lu) 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong) size, (ulong) pre);
+ o_set_unmarked(pre);
+ if (proc != 0)
+ (*proc) (mem, pre + 1, size, pre->o_type);
+ END_OBJECTS_SCAN
+}
+
+/* Mark 0- and 1-character names, and those referenced from the */
+/* op_array_nx_table, and unmark all the rest. */
+static void
+gc_unmark_names(name_table * nt, op_array_table *op_array_table_global,
+ op_array_table *op_array_table_local)
+{
+ uint i;
+
+ names_unmark_all(nt);
+ for (i = 0; i < op_array_table_global->count; i++) {
+ name_index_t nidx = op_array_table_global->nx_table[i];
+
+ names_mark_index(nt, nidx);
+ }
+ for (i = 0; i < op_array_table_local->count; i++) {
+ name_index_t nidx = op_array_table_local->nx_table[i];
+
+ names_mark_index(nt, nidx);
+ }
+}
+
+/* ------ Marking phase ------ */
+
+/* Initialize (a segment of) the mark stack. */
+static void
+gc_init_mark_stack(gc_mark_stack * pms, uint count)
+{
+ pms->next = 0;
+ pms->count = count;
+ pms->entries[0].ptr = 0;
+ pms->entries[0].index = 0;
+ pms->entries[0].is_refs = false;
+}
+
+/* Mark starting from all marked objects in the interval of a chunk */
+/* needing rescanning. */
+static int
+gc_rescan_chunk(chunk_t * cp, gc_state_t * pstate, gc_mark_stack * pmstack)
+{
+ byte *sbot = cp->rescan_bot;
+ byte *stop = cp->rescan_top;
+ gs_gc_root_t root;
+ void *comp;
+ int more = 0;
+ const gs_memory_t *mem = gcst_get_memory_ptr( pstate );
+
+ if (sbot > stop)
+ return 0;
+ root.p = &comp;
+ if_debug_chunk('6', mem, "[6]rescanning chunk", cp);
+ cp->rescan_bot = cp->cend;
+ cp->rescan_top = cp->cbase;
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ if ((byte *) (pre + 1) + size < sbot);
+ else if ((byte *) (pre + 1) > stop)
+ return more; /* 'break' won't work here */
+ else {
+ if_debug2m('7', mem, " [7]scanning/marking 0x%lx(%lu)\n",
+ (ulong) pre, (ulong) size);
+ if (pre->o_type == &st_refs) {
+ ref_packed *rp = (ref_packed *) (pre + 1);
+ char *end = (char *)rp + size;
+
+ root.ptype = ptr_ref_type;
+ while ((char *)rp < end) {
+ comp = rp;
+ if (r_is_packed(rp)) {
+ if (r_has_pmark(rp)) {
+ r_clear_pmark(rp);
+ more |= gc_trace(&root, pstate,
+ pmstack);
+ }
+ rp++;
+ } else {
+ ref *const pref = (ref *)rp;
+
+ if (r_has_attr(pref, l_mark)) {
+ r_clear_attrs(pref, l_mark);
+ more |= gc_trace(&root, pstate, pmstack);
+ }
+ rp += packed_per_ref;
+ }
+ }
+ } else if (!o_is_unmarked(pre)) {
+ struct_proc_clear_marks((*proc)) =
+ pre->o_type->clear_marks;
+ root.ptype = ptr_struct_type;
+ comp = pre + 1;
+ if (!o_is_untraced(pre))
+ o_set_unmarked(pre);
+ if (proc != 0)
+ (*proc) (mem, comp, size, pre->o_type);
+ more |= gc_trace(&root, pstate, pmstack);
+ }
+ }
+ END_OBJECTS_SCAN
+ return more;
+}
+
+/* Mark starting from all the objects in a chunk. */
+/* We assume that pstate->min_collect > avm_system, */
+/* so we don't have to trace names. */
+static int
+gc_trace_chunk(const gs_memory_t *mem, chunk_t * cp, gc_state_t * pstate, gc_mark_stack * pmstack)
+{
+ gs_gc_root_t root;
+ void *comp;
+ int more = 0;
+ int min_trace = pstate->min_collect;
+
+ root.p = &comp;
+ if_debug_chunk('6', mem, "[6]marking from chunk", cp);
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ {
+ if_debug2m('7', mem, " [7]scanning/marking 0x%lx(%lu)\n",
+ (ulong) pre, (ulong) size);
+ if (pre->o_type == &st_refs) {
+ ref_packed *rp = (ref_packed *) (pre + 1);
+ char *end = (char *)rp + size;
+
+ root.ptype = ptr_ref_type;
+ while ((char *)rp < end) {
+ comp = rp;
+ if (r_is_packed(rp)) { /* No packed refs need tracing. */
+ rp++;
+ } else {
+ ref *const pref = (ref *)rp;
+
+ if (r_space(pref) >= min_trace) {
+ r_clear_attrs(pref, l_mark);
+ more |= gc_trace(&root, pstate, pmstack);
+ }
+ rp += packed_per_ref;
+ }
+ }
+ } else if (!o_is_unmarked(pre)) {
+ if (!o_is_untraced(pre))
+ o_set_unmarked(pre);
+ if (pre->o_type != &st_free) {
+ struct_proc_clear_marks((*proc)) =
+ pre->o_type->clear_marks;
+
+ root.ptype = ptr_struct_type;
+ comp = pre + 1;
+ if (proc != 0)
+ (*proc) (mem, comp, size, pre->o_type);
+ more |= gc_trace(&root, pstate, pmstack);
+ }
+ }
+ }
+ END_OBJECTS_SCAN
+ return more;
+}
+
+/* Recursively mark from a (root) pointer. */
+/* Return -1 if we overflowed the mark stack, */
+/* 0 if we completed successfully without marking any new objects, */
+/* 1 if we completed and marked some new objects. */
+static int gc_extend_stack(gc_mark_stack *, gc_state_t *);
+static int
+gc_trace(gs_gc_root_t * rp, gc_state_t * pstate, gc_mark_stack * pmstack)
+{
+ int min_trace = pstate->min_collect;
+ gc_mark_stack *pms = pmstack;
+ ms_entry *sp = pms->entries + 1;
+
+ /* We stop the mark stack 1 entry early, because we store into */
+ /* the entry beyond the top. */
+ ms_entry *stop = sp + pms->count - 2;
+ int new = 0;
+ enum_ptr_t nep;
+ void *nptr;
+ name_table *nt = pstate->ntable;
+
+#define mark_name(nidx)\
+ BEGIN\
+ if (names_mark_index(nt, nidx)) {\
+ new |= 1;\
+ if_debug2m('8', gcst_get_memory_ptr(pstate), " [8]marked name 0x%lx(%u)\n",\
+ (ulong)names_index_ptr(nt, nidx), nidx);\
+ }\
+ END
+
+ nptr = *rp->p;
+ if (nptr == 0)
+ return 0;
+
+ /* Initialize the stack */
+ sp->ptr = nptr;
+ if (rp->ptype == ptr_ref_type)
+ sp->index = 1, sp->is_refs = true;
+ else {
+ sp->index = 0, sp->is_refs = false;
+ nep.ptr = nptr;
+ if ((*rp->ptype->mark) (&nep, pstate))
+ new |= 1;
+ }
+ for (;;) {
+ gs_ptr_type_t ptp;
+
+ /*
+ * The following should really be an if..else, but that
+ * would force unnecessary is_refs tests.
+ */
+ if (sp->is_refs)
+ goto do_refs;
+
+ /* ---------------- Structure ---------------- */
+
+ do_struct:
+ {
+ obj_header_t *ptr = sp->ptr;
+
+ struct_proc_enum_ptrs((*mproc));
+
+ if (ptr == 0) { /* We've reached the bottom of a stack segment. */
+ pms = pms->prev;
+ if (pms == 0)
+ break; /* all done */
+ stop = pms->entries + pms->count - 1;
+ sp = stop;
+ continue;
+ }
+ debug_check_object(ptr - 1, NULL, NULL);
+ ts:if_debug4m('7', pstate->heap, " [7]%smarking %s 0x%lx[%u]",
+ depth_dots(sp, pms),
+ struct_type_name_string(ptr[-1].o_type),
+ (ulong) ptr, sp->index);
+ mproc = ptr[-1].o_type->enum_ptrs;
+ if (mproc == gs_no_struct_enum_ptrs ||
+ (ptp = (*mproc)
+ (gcst_get_memory_ptr(pstate), ptr, pre_obj_contents_size(ptr - 1),
+ sp->index, &nep, ptr[-1].o_type, pstate)) == 0
+ ) {
+ if_debug0m('7', pstate->heap, " - done\n");
+ sp--;
+ continue;
+ }
+ /* The cast in the following statement is the one */
+ /* place we need to break 'const' to make the */
+ /* template for pointer enumeration work. */
+ nptr = (void *)nep.ptr;
+ sp->index++;
+ if_debug1m('7', pstate->heap, " = 0x%lx\n", (ulong) nptr);
+ /* Descend into nep.ptr, whose pointer type is ptp. */
+ if (ptp == ptr_struct_type) {
+ sp[1].index = 0;
+ sp[1].is_refs = false;
+ if (sp == stop)
+ goto push;
+ if (!ptr_struct_mark(&nep, pstate))
+ goto ts;
+ new |= 1;
+ (++sp)->ptr = nptr;
+ goto do_struct;
+ } else if (ptp == ptr_ref_type) {
+ sp[1].index = 1;
+ sp[1].is_refs = true;
+ if (sp == stop)
+ goto push;
+ new |= 1;
+ (++sp)->ptr = nptr;
+ goto do_refs;
+ } else { /* We assume this is some non-pointer- */
+ /* containing type. */
+ /* This was the only formulation of this call/condition/assignment that valgrind did not complain about */
+ new |= ((*ptp->mark) (&nep, pstate)) ? 1 : 0;
+ goto ts;
+ }
+ }
+
+ /* ---------------- Refs ---------------- */
+
+ do_refs:
+ {
+ ref_packed *pptr = sp->ptr;
+ ref *rptr;
+
+ tr:if (!sp->index) {
+ --sp;
+ continue;
+ }
+ --(sp->index);
+ if_debug3m('8', pstate->heap, " [8]%smarking refs 0x%lx[%u]\n",
+ depth_dots(sp, pms), (ulong) pptr, sp->index);
+ if (r_is_packed(pptr)) {
+ if (!r_has_pmark(pptr)) {
+ r_set_pmark(pptr);
+ new |= 1;
+ if (r_packed_is_name(pptr)) {
+ name_index_t nidx = packed_name_index(pptr);
+
+ mark_name(nidx);
+ }
+ }
+ ++pptr;
+ goto tr;
+ }
+ rptr = (ref *) pptr; /* * const beyond here */
+ if (r_has_attr(rptr, l_mark)) {
+ pptr = (ref_packed *)(rptr + 1);
+ goto tr;
+ }
+ r_set_attrs(rptr, l_mark);
+ new |= 1;
+ if (r_space(rptr) < min_trace) { /* Note that this always picks up all scalars. */
+ pptr = (ref_packed *) (rptr + 1);
+ goto tr;
+ }
+ sp->ptr = rptr + 1;
+ switch (r_type(rptr)) {
+ /* Struct cases */
+ case t_file:
+ nptr = rptr->value.pfile;
+ rs:sp[1].is_refs = false;
+ sp[1].index = 0;
+ if (sp == stop) {
+ ptp = ptr_struct_type;
+ break;
+ }
+ nep.ptr = nptr;
+ if (!ptr_struct_mark(&nep, pstate))
+ goto nr;
+ new |= 1;
+ (++sp)->ptr = nptr;
+ goto do_struct;
+ case t_device:
+ nptr = rptr->value.pdevice;
+ goto rs;
+ case t_fontID:
+ case t_struct:
+ case t_astruct:
+ nptr = rptr->value.pstruct;
+ goto rs;
+ /* Non-trivial non-struct cases */
+ case t_dictionary:
+ nptr = rptr->value.pdict;
+ sp[1].index = sizeof(dict) / sizeof(ref);
+ goto rrp;
+ case t_array:
+ nptr = rptr->value.refs;
+ rr:if ((sp[1].index = r_size(rptr)) == 0) { /* Set the base pointer to 0, */
+ /* so we never try to relocate it. */
+ rptr->value.refs = 0;
+ goto nr;
+ }
+ rrp:
+ rrc:sp[1].is_refs = true;
+ if (sp == stop) {
+ /*
+ * The following initialization is unnecessary:
+ * ptp will not be used if sp[1].is_refs = true.
+ * We put this here solely to get rid of bogus
+ * "possibly uninitialized variable" warnings
+ * from certain compilers.
+ */
+ ptp = ptr_ref_type;
+ break;
+ }
+ new |= 1;
+ (++sp)->ptr = nptr;
+ goto do_refs;
+ case t_mixedarray:
+ case t_shortarray:
+ nptr = rptr->value.writable_packed;
+ goto rr;
+ case t_name:
+ mark_name(names_index(nt, rptr));
+ nr:pptr = (ref_packed *) (rptr + 1);
+ goto tr;
+ case t_string:
+ if (gc_string_mark(rptr->value.bytes, r_size(rptr), true, pstate))
+ new |= 1;
+ goto nr;
+ case t_oparray:
+ nptr = rptr->value.refs; /* discard const */
+ sp[1].index = 1;
+ goto rrc;
+ default:
+ goto nr;
+ }
+ }
+
+ /* ---------------- Recursion ---------------- */
+
+ push:
+ if (sp == stop) { /* The current segment is full. */
+ int new_added = gc_extend_stack(pms, pstate);
+
+ if (new_added) {
+ new |= new_added;
+ continue;
+ }
+ pms = pms->next;
+ stop = pms->entries + pms->count - 1;
+ pms->entries[1] = sp[1];
+ sp = pms->entries;
+ }
+ /* index and is_refs are already set */
+ if (!sp[1].is_refs) {
+ nep.ptr = nptr;
+ if (!(*ptp->mark) (&nep, pstate))
+ continue;
+ new |= 1;
+ }
+ (++sp)->ptr = nptr;
+ }
+ return new;
+}
+/* Link to, attempting to allocate if necessary, */
+/* another chunk of mark stack. */
+static int
+gc_extend_stack(gc_mark_stack * pms, gc_state_t * pstate)
+{
+ if (pms->next == 0) { /* Try to allocate another segment. */
+ uint count;
+
+ for (count = ms_size_desired; count >= ms_size_min; count >>= 1) {
+ pms->next = (gc_mark_stack *)
+ gs_alloc_bytes_immovable(pstate->heap,
+ sizeof(gc_mark_stack) +
+ sizeof(ms_entry) * count,
+ "gc mark stack");
+ if (pms->next != 0)
+ break;
+ }
+ if (pms->next == 0) { /* The mark stack overflowed. */
+ ms_entry *sp = pms->entries + pms->count - 1;
+ byte *cptr = sp->ptr; /* container */
+ chunk_t *cp = gc_locate(cptr, pstate);
+ int new = 1;
+
+ if (cp == 0) { /* We were tracing outside collectible */
+ /* storage. This can't happen. */
+ lprintf1("mark stack overflowed while outside collectible space at 0x%lx!\n",
+ (ulong) cptr);
+ gs_abort(pstate->heap);
+ }
+ if (cptr < cp->rescan_bot)
+ cp->rescan_bot = cptr, new = -1;
+ if (cptr > cp->rescan_top)
+ cp->rescan_top = cptr, new = -1;
+ return new;
+ }
+ gc_init_mark_stack(pms->next, count);
+ pms->next->prev = pms;
+ pms->next->on_heap = true;
+ }
+ return 0;
+}
+
+/* Mark a struct. Return true if new mark. */
+static bool
+ptr_struct_mark(enum_ptr_t *pep, gc_state_t * ignored)
+{
+ obj_header_t *ptr = (obj_header_t *)pep->ptr;
+
+ if (ptr == 0)
+ return false;
+ ptr--; /* point to header */
+ if (!o_is_unmarked(ptr))
+ return false;
+ o_mark(ptr);
+ return true;
+}
+
+/* Mark a string. Return true if new mark. */
+static bool
+ptr_string_mark(enum_ptr_t *pep, gc_state_t * gcst)
+{
+ return gc_string_mark(pep->ptr, pep->size, true, gcst);
+}
+
+/* Mark a name. Return true if new mark. */
+static bool
+ptr_name_index_mark(enum_ptr_t *pep, gc_state_t * gcst)
+{
+ return names_mark_index(gcst->heap->gs_lib_ctx->gs_name_table, pep->size);
+}
+
+/* Finish tracing by marking names. */
+static bool
+gc_trace_finish(gc_state_t * pstate)
+{
+ name_table *nt = pstate->ntable;
+ name_index_t nidx = 0;
+ bool marked = false;
+
+ while ((nidx = names_next_valid_index(nt, nidx)) != 0) {
+ name_string_t *pnstr = names_index_string_inline(nt, nidx);
+
+ if (pnstr->mark) {
+ enum_ptr_t enst, ensst;
+
+ if (!pnstr->foreign_string &&
+ gc_string_mark(pnstr->string_bytes, pnstr->string_size,
+ true, pstate)
+ )
+ marked = true;
+ enst.ptr = names_index_sub_table(nt, nidx);
+ ensst.ptr = names_index_string_sub_table(nt, nidx);
+ marked |=
+ ptr_struct_mark(&enst, pstate) |
+ ptr_struct_mark(&ensst, pstate);
+ }
+ }
+ return marked;
+}
+
+/* ------ Relocation planning phase ------ */
+
+/* Initialize the relocation information in the chunk header. */
+static void
+gc_init_reloc(chunk_t * cp)
+{
+ chunk_head_t *chead = cp->chead;
+
+ chead->dest = cp->cbase;
+ chead->free.o_back =
+ offset_of(chunk_head_t, free) >> obj_back_shift;
+ chead->free.o_size = sizeof(obj_header_t);
+ chead->free.o_nreloc = 0;
+}
+
+/* Set marks and clear relocation for chunks that won't be compacted. */
+static void
+gc_clear_reloc(chunk_t * cp)
+{
+ byte *pfree = (byte *) & cp->chead->free;
+
+ gc_init_reloc(cp);
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ const struct_shared_procs_t *procs =
+ pre->o_type->shared;
+
+ if (procs != 0)
+ (*procs->clear_reloc) (pre, size);
+ o_set_untraced(pre);
+ pre->o_back = ((byte *) pre - pfree) >> obj_back_shift;
+ END_OBJECTS_SCAN
+ gc_strings_set_marks(cp, true);
+ gc_strings_clear_reloc(cp);
+}
+
+/* Set the relocation for the objects in a chunk. */
+/* This will never be called for a chunk with any o_untraced objects. */
+static void
+gc_objects_set_reloc(gc_state_t * gcst, chunk_t * cp)
+{
+ size_t reloc = 0;
+ chunk_head_t *chead = cp->chead;
+ byte *pfree = (byte *) & chead->free; /* most recent free object */
+
+ if_debug_chunk('6', gcst->heap, "[6]setting reloc for chunk", cp);
+ gc_init_reloc(cp);
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ struct_proc_finalize((*finalize));
+ const struct_shared_procs_t *procs =
+ pre->o_type->shared;
+
+ if ((procs == 0 ? o_is_unmarked(pre) :
+ !(*procs->set_reloc) (pre, reloc, size))
+ ) { /* Free object */
+ reloc += sizeof(obj_header_t) + obj_align_round(size);
+ if ((finalize = pre->o_type->finalize) != 0) {
+ if_debug2m('u', gcst->heap, "[u]GC finalizing %s 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong) (pre + 1));
+ (*finalize) (gcst->cur_mem, pre + 1);
+ }
+ pfree = (byte *) pre;
+ pre->o_back = (pfree - (byte *) chead) >> obj_back_shift;
+ pre->o_nreloc = reloc;
+ if_debug3m('7', gcst->heap, " [7]at 0x%lx, unmarked %lu, new reloc = %u\n",
+ (ulong) pre, (ulong) size, reloc);
+ } else { /* Useful object */
+ debug_check_object(pre, cp, gcst);
+ pre->o_back = ((byte *) pre - pfree) >> obj_back_shift;
+ }
+ END_OBJECTS_SCAN
+#ifdef DEBUG
+ if (reloc != 0) {
+ if_debug1m('6', gcst->heap, "[6]freed %u", reloc);
+ if_debug_chunk('6', gcst->heap, " in", cp);
+ }
+#endif
+}
+
+/* ------ Relocation phase ------ */
+
+/* Relocate the pointers in all the objects in a chunk. */
+static void
+gc_do_reloc(chunk_t * cp, gs_ref_memory_t * mem, gc_state_t * pstate)
+{
+ chunk_head_t *chead = cp->chead;
+
+ if_debug_chunk('6', (const gs_memory_t *)mem, "[6]relocating in chunk", cp);
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+#ifdef DEBUG
+ pstate->container = cp;
+#endif
+ /* We need to relocate the pointers in an object iff */
+ /* it is o_untraced, or it is a useful object. */
+ /* An object is free iff its back pointer points to */
+ /* the chunk_head structure. */
+ if (o_is_untraced(pre) ||
+ pre->o_back << obj_back_shift != (byte *) pre - (byte *) chead
+ ) {
+ struct_proc_reloc_ptrs((*proc)) =
+ pre->o_type->reloc_ptrs;
+
+ if_debug3m('7', (const gs_memory_t *)mem,
+ " [7]relocating ptrs in %s(%lu) 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong) size, (ulong) pre);
+ if (proc != 0)
+ (*proc) (pre + 1, size, pre->o_type, pstate);
+ }
+#ifdef DEBUG
+ pstate->container = 0;
+#endif
+ END_OBJECTS_SCAN
+}
+
+/* Print pointer relocation if debugging. */
+/* We have to provide this procedure even if DEBUG is not defined, */
+/* in case one of the other GC modules was compiled with DEBUG. */
+const void *
+print_reloc_proc(const void *obj, const char *cname, const void *robj)
+{
+ if_debug3('9', " [9]relocate %s * 0x%lx to 0x%lx\n",
+ cname, (ulong)obj, (ulong)robj);
+ return robj;
+}
+
+/* Relocate a pointer to an (aligned) object. */
+/* See gsmemory.h for why the argument is const and the result is not. */
+static void /*obj_header_t */ *
+igc_reloc_struct_ptr(const void /*obj_header_t */ *obj, gc_state_t * gcst)
+{
+ const obj_header_t *const optr = (const obj_header_t *)obj;
+ const void *robj;
+
+ if (obj == 0) {
+ discard(print_reloc(obj, "NULL", 0));
+ return 0;
+ }
+ debug_check_object(optr - 1, NULL, gcst);
+ {
+ uint back = optr[-1].o_back;
+
+ if (back == o_untraced)
+ robj = obj;
+ else {
+#ifdef DEBUG
+ /* Do some sanity checking. */
+ chunk_t *cp = gcst->container;
+
+ if (cp != 0 && cp->cbase <= (byte *)obj && (byte *)obj <cp->ctop) {
+ if (back > (cp->ctop - cp->cbase) >> obj_back_shift) {
+ lprintf2("Invalid back pointer %u at 0x%lx!\n",
+ back, (ulong) obj);
+ gs_abort(NULL);
+ }
+ } else {
+ /* Pointed to unknown chunk. Can't check it, sorry. */
+ }
+#endif
+ {
+ const obj_header_t *pfree = (const obj_header_t *)
+ ((const char *)(optr - 1) -
+ (back << obj_back_shift));
+ const chunk_head_t *chead = (const chunk_head_t *)
+ ((const char *)pfree -
+ (pfree->o_back << obj_back_shift));
+
+ robj = chead->dest +
+ ((const char *)obj - (const char *)(chead + 1) -
+ pfree->o_nreloc);
+ }
+ }
+ }
+ /* Use a severely deprecated pun to remove the const property. */
+ {
+ union { const void *r; void *w; } u;
+
+ u.r = print_reloc(obj, struct_type_name_string(optr[-1].o_type), robj);
+ return u.w;
+ }
+}
+
+/* ------ Compaction phase ------ */
+
+/* Compact the objects in a chunk. */
+/* This will never be called for a chunk with any o_untraced objects. */
+static void
+gc_objects_compact(chunk_t * cp, gc_state_t * gcst)
+{
+ chunk_head_t *chead = cp->chead;
+ obj_header_t *dpre = (obj_header_t *) chead->dest;
+ const gs_memory_t *cmem = gcst->spaces.memories.named.system->stable_memory;
+
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ /* An object is free iff its back pointer points to */
+ /* the chunk_head structure. */
+ if (pre->o_back << obj_back_shift != (byte *) pre - (byte *) chead) {
+ const struct_shared_procs_t *procs = pre->o_type->shared;
+
+ debug_check_object(pre, cp, gcst);
+ if_debug4m('7', cmem,
+ " [7]compacting %s 0x%lx(%lu) to 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong) pre, (ulong) size, (ulong) dpre);
+ if (procs == 0) {
+ if (dpre != pre)
+ memmove(dpre, pre,
+ sizeof(obj_header_t) + size);
+ } else
+ (*procs->compact) (cmem, pre, dpre, size);
+ dpre = (obj_header_t *)
+ ((byte *) dpre + obj_size_round(size));
+ }
+ END_OBJECTS_SCAN
+ if (cp->outer == 0 && chead->dest != cp->cbase)
+ dpre = (obj_header_t *) cp->cbase; /* compacted this chunk into another */
+ gs_alloc_fill(dpre, gs_alloc_fill_collected, cp->cbot - (byte *) dpre);
+ cp->cbot = (byte *) dpre;
+ cp->rcur = 0;
+ cp->rtop = 0; /* just to be sure */
+}
+
+/* ------ Cleanup ------ */
+
+/* Free empty chunks. */
+static void
+gc_free_empty_chunks(gs_ref_memory_t * mem)
+{
+ chunk_t *cp;
+ chunk_t *csucc;
+
+ /* Free the chunks in reverse order, */
+ /* to encourage LIFO behavior. */
+ for (cp = mem->clast; cp != 0; cp = csucc) { /* Make sure this isn't an inner chunk, */
+ /* or a chunk that has inner chunks. */
+ csucc = cp->cprev; /* save before freeing */
+ if (cp->cbot == cp->cbase && cp->ctop == cp->climit &&
+ cp->outer == 0 && cp->inner_count == 0
+ ) {
+ alloc_free_chunk(cp, mem);
+ if (mem->pcc == cp)
+ mem->pcc = 0;
+ }
+ }
+}
+
+const gs_memory_t * gcst_get_memory_ptr(gc_state_t *gcst)
+{
+ vm_spaces spaces = gcst->spaces;
+ const gs_memory_t *cmem = space_system->stable_memory;
+ return cmem;
+}
diff --git a/psi/igc.h b/psi/igc.h
new file mode 100644
index 000000000..a01e73796
--- /dev/null
+++ b/psi/igc.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Internal interfaces in Ghostscript GC */
+
+#ifndef igc_INCLUDED
+# define igc_INCLUDED
+
+#include "istruct.h"
+
+/* Declare the vm_reclaim procedure for the real GC. */
+extern vm_reclaim_proc(gs_gc_reclaim);
+
+/* Define the procedures shared among a "genus" of structures. */
+/* Currently there are only two genera: refs, and all other structures. */
+struct struct_shared_procs_s {
+
+ /* Clear the relocation information in an object. */
+
+#define gc_proc_clear_reloc(proc)\
+ void proc(obj_header_t *pre, uint size)
+ gc_proc_clear_reloc((*clear_reloc));
+
+ /* Compute any internal relocation for a marked object. */
+ /* Return true if the object should be kept. */
+ /* The reloc argument shouldn't be required, */
+ /* but we need it for ref objects. */
+
+#define gc_proc_set_reloc(proc)\
+ bool proc(obj_header_t *pre, uint reloc, uint size)
+ gc_proc_set_reloc((*set_reloc));
+
+ /* Compact an object. */
+
+#define gc_proc_compact(proc)\
+ void proc(const gs_memory_t *cmem, obj_header_t *pre, obj_header_t *dpre, uint size)
+ gc_proc_compact((*compact));
+
+};
+
+/* Define the structure for holding GC state. */
+/*typedef struct gc_state_s gc_state_t; *//* in gsstruct.h */
+#ifndef name_table_DEFINED
+# define name_table_DEFINED
+typedef struct name_table_s name_table;
+#endif
+struct gc_state_s {
+ const gc_procs_with_refs_t *procs; /* must be first */
+ chunk_locator_t loc;
+ vm_spaces spaces;
+ int min_collect; /* avm_space */
+ bool relocating_untraced; /* if true, we're relocating */
+ /* pointers from untraced spaces */
+ gs_memory_t *heap; /* for extending mark stack */
+ name_table *ntable; /* (implicitly referenced by names) */
+ gs_memory_t *cur_mem;
+#ifdef DEBUG
+ chunk_t *container;
+#endif
+};
+
+/* Exported by igcref.c for igc.c */
+ptr_proc_unmark(ptr_ref_unmark);
+ptr_proc_mark(ptr_ref_mark);
+/*ref_packed *gs_reloc_ref_ptr(const ref_packed *, gc_state_t *); */
+
+/* Exported by ilocate.c for igc.c */
+void ialloc_validate_memory(const gs_ref_memory_t *, gc_state_t *);
+void ialloc_validate_chunk(const chunk_t *, gc_state_t *);
+void ialloc_validate_object(const obj_header_t *, const chunk_t *,
+ gc_state_t *);
+
+/* Exported by igc.c for ilocate.c */
+const gs_memory_t * gcst_get_memory_ptr(gc_state_t *gcst);
+
+/* Macro for returning a relocated pointer */
+const void *print_reloc_proc(const void *obj, const char *cname,
+ const void *robj);
+#ifdef DEBUG
+# define print_reloc(obj, cname, nobj)\
+ (gs_debug_c('9') ? print_reloc_proc(obj, cname, nobj) : nobj)
+#else
+# define print_reloc(obj, cname, nobj) (nobj)
+#endif
+
+#endif /* igc_INCLUDED */
diff --git a/psi/igcref.c b/psi/igcref.c
new file mode 100644
index 000000000..031985fed
--- /dev/null
+++ b/psi/igcref.c
@@ -0,0 +1,768 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* ref garbage collector for Ghostscript */
+#include "memory_.h"
+#include "ghost.h"
+#include "gsexit.h"
+#include "gsstruct.h" /* for gxalloc.h included by iastate.h */
+#include "iname.h"
+#include "iastate.h"
+#include "idebug.h"
+#include "igc.h"
+#include "ipacked.h"
+#include "store.h" /* for ref_assign_inline */
+
+/* Define whether to trace every step of relocating ref pointers. */
+#if 0
+# define rputc(m,c) dmputc(m,c)
+#else
+# define rputc(m,c) DO_NOTHING
+#endif
+
+/* Forward references */
+ptr_proc_reloc(igc_reloc_ref_ptr, ref_packed);
+ptr_proc_reloc(igc_reloc_ref_ptr_nocheck, ref_packed);
+refs_proc_reloc(igc_reloc_refs);
+
+/*
+ * Define the 'structure' type descriptor for refs.
+ * This is special because it has different shared procs.
+ */
+static gc_proc_clear_reloc(refs_clear_reloc);
+static gc_proc_set_reloc(refs_set_reloc);
+static gc_proc_compact(refs_compact);
+static const struct_shared_procs_t refs_shared_procs =
+{refs_clear_reloc, refs_set_reloc, refs_compact};
+static struct_proc_clear_marks(refs_clear_marks);
+static struct_proc_reloc_ptrs(refs_do_reloc);
+const gs_memory_struct_type_t st_refs =
+{sizeof(ref), "refs", &refs_shared_procs, refs_clear_marks, 0, refs_do_reloc};
+
+/*
+ * Define the GC procedures for structs that actually contain refs.
+ * These are special because the shared refs_* procedures
+ * are never called. Instead, we unmark the individual refs in clear_marks,
+ * disregard refs_*_reloc (because we will never relocate a ptr_ref_type
+ * pointer pointing into the structure), disregard refs_compact (because
+ * compaction is never required), and remove the marks in reloc_ptrs.
+ * See also the comment about ptr_ref_type in imemory.h.
+ */
+CLEAR_MARKS_PROC(ref_struct_clear_marks)
+{
+ ref *pref = (ref *) vptr;
+ ref *end = (ref *) ((char *)vptr + size);
+
+ for (; pref < end; pref++)
+ r_clear_attrs(pref, l_mark);
+}
+ENUM_PTRS_BEGIN_PROC(ref_struct_enum_ptrs)
+{
+ if (index >= size / sizeof(ref))
+ return 0;
+ pep->ptr = (const ref *)vptr + index;
+ return ptr_ref_type;
+ ENUM_PTRS_END_PROC
+}
+RELOC_PTRS_BEGIN(ref_struct_reloc_ptrs)
+{
+ vm_spaces spaces = gcst->spaces;
+ const gs_memory_t *cmem = space_system->stable_memory;
+
+ ref *beg = vptr;
+ ref *end = (ref *) ((char *)vptr + size);
+
+ igc_reloc_refs((ref_packed *) beg, (ref_packed *) end, gcst);
+ ref_struct_clear_marks(cmem, vptr, size, pstype);
+} RELOC_PTRS_END
+
+/* ------ Unmarking phase ------ */
+
+/* Unmark a single ref. */
+void
+ptr_ref_unmark(enum_ptr_t *pep, gc_state_t * ignored)
+{
+ ref_packed *rpp = (ref_packed *)pep->ptr;
+
+ if (r_is_packed(rpp))
+ r_clear_pmark(rpp);
+ else
+ r_clear_attrs((ref *)rpp, l_mark);
+}
+
+/* Unmarking routine for ref objects. */
+static void
+refs_clear_marks(const gs_memory_t *cmem,
+ void /*obj_header_t */ *vptr, uint size,
+ const gs_memory_struct_type_t * pstype)
+{
+ ref_packed *rp = (ref_packed *) vptr;
+ ref_packed *end = (ref_packed *) ((byte *) vptr + size);
+
+ /* Since the last ref is full-size, we only need to check for */
+ /* the end of the block when we see one of those. */
+ for (;;) {
+ if (r_is_packed(rp)) {
+#ifdef DEBUG
+ if (gs_debug_c('8')) {
+ dmlprintf1(cmem, " [8]unmark packed 0x%lx ", (ulong) rp);
+ debug_print_ref(cmem, (const ref *)rp);
+ dmputs(cmem, "\n");
+ }
+#endif
+ r_clear_pmark(rp);
+ rp++;
+ } else { /* full-size ref */
+ ref *const pref = (ref *)rp;
+
+#ifdef DEBUG
+ if (gs_debug_c('8')) {
+ dmlprintf1(cmem, " [8]unmark ref 0x%lx ", (ulong) rp);
+ debug_print_ref(cmem, pref);
+ dmputs(cmem, "\n");
+ }
+#endif
+ r_clear_attrs(pref, l_mark);
+ rp += packed_per_ref;
+ if (rp >= (ref_packed *) end)
+ break;
+ }
+ }
+}
+
+/* ------ Marking phase ------ */
+
+/* Mark a ref. Return true if new mark. */
+bool
+ptr_ref_mark(enum_ptr_t *pep, gc_state_t * ignored)
+{
+ ref_packed *rpp = (void *)pep->ptr;
+
+ if (r_is_packed(rpp)) {
+ if (r_has_pmark(rpp))
+ return false;
+ r_set_pmark(rpp);
+ } else {
+ ref *const pref = (ref *)rpp;
+
+ if (r_has_attr(pref, l_mark))
+ return false;
+ r_set_attrs(pref, l_mark);
+ }
+ return true;
+}
+
+/* ------ Relocation planning phase ------ */
+
+/*
+ * We store relocation in the size field of refs that don't use it,
+ * so that we don't have to scan all the way to an unmarked object.
+ * We must avoid nulls, which sometimes have useful information
+ * in their size fields, and the types above t_next_index, which are
+ * actually operators in disguise and also use the size field.
+ */
+
+/* Clear the relocation for a ref object. */
+static void
+refs_clear_reloc(obj_header_t *hdr, uint size)
+{
+ ref_packed *rp = (ref_packed *) (hdr + 1);
+ ref_packed *end = (ref_packed *) ((byte *) rp + size);
+
+ while (rp < end) {
+ if (r_is_packed(rp))
+ rp++;
+ else {
+ /* Full-size ref. Store the relocation here if possible. */
+ ref *const pref = (ref *)rp;
+
+ if (!ref_type_uses_size_or_null(r_type(pref))) {
+ if_debug1('8', " [8]clearing reloc at 0x%lx\n", (ulong) rp);
+ r_set_size(pref, 0);
+ }
+ rp += packed_per_ref;
+ }
+ }
+}
+
+/* Set the relocation for a ref object. */
+static bool
+refs_set_reloc(obj_header_t * hdr, uint reloc, uint size)
+{
+ ref_packed *rp = (ref_packed *) (hdr + 1);
+ ref_packed *end = (ref_packed *) ((byte *) rp + size);
+ uint freed = 0;
+
+ /*
+ * We have to be careful to keep refs aligned properly.
+ * For the moment, we do this by either keeping or discarding
+ * an entire (aligned) block of align_packed_per_ref packed elements
+ * as a unit. We know that align_packed_per_ref <= packed_per_ref,
+ * and we also know that packed refs are always allocated in blocks
+ * of align_packed_per_ref, so this makes things relatively easy.
+ */
+ while (rp < end) {
+ if (r_is_packed(rp)) {
+#if align_packed_per_ref == 1
+ if (r_has_pmark(rp)) {
+ if_debug1('8',
+ " [8]packed ref 0x%lx is marked\n",
+ (ulong) rp);
+ rp++;
+ } else {
+#else
+ int i;
+
+ /*
+ * Note: align_packed_per_ref is typically
+ * 2 or 4 for 32-bit processors.
+ */
+#define all_marked (align_packed_per_ref * lp_mark)
+# if align_packed_per_ref == 2
+# if arch_sizeof_int == arch_sizeof_short * 2
+# undef all_marked
+# define all_marked ( (lp_mark << (sizeof(short) * 8)) + lp_mark )
+# define marked (*(int *)rp & all_marked)
+# else
+# define marked ((*rp & lp_mark) + (rp[1] & lp_mark))
+# endif
+# else
+# if align_packed_per_ref == 4
+# define marked ((*rp & lp_mark) + (rp[1] & lp_mark) +\
+ (rp[2] & lp_mark) + (rp[3] & lp_mark))
+# else
+ /*
+ * The value of marked is logically a uint, not an int:
+ * we declare it as int only to avoid a compiler warning
+ * message about using a non-int value in a switch statement.
+ */
+ int marked = *rp & lp_mark;
+
+ for (i = 1; i < align_packed_per_ref; i++)
+ marked += rp[i] & lp_mark;
+# endif
+# endif
+ /*
+ * Now marked is lp_mark * the number of marked
+ * packed refs in the aligned block, except for
+ * a couple of special cases above.
+ */
+ switch (marked) {
+ case all_marked:
+ if_debug2('8',
+ " [8]packed refs 0x%lx..0x%lx are marked\n",
+ (ulong) rp,
+ (ulong) (rp + (align_packed_per_ref - 1)));
+ rp += align_packed_per_ref;
+ break;
+ default:
+ /* At least one packed ref in the block */
+ /* is marked: Keep the whole block. */
+ for (i = align_packed_per_ref; i--; rp++) {
+ r_set_pmark(rp);
+ if_debug1('8',
+ " [8]packed ref 0x%lx is marked\n",
+ (ulong) rp);
+ }
+ break;
+ case 0:
+#endif
+ if_debug2('8', " [8]%d packed ref(s) at 0x%lx are unmarked\n",
+ align_packed_per_ref, (ulong) rp);
+ {
+ uint rel = reloc + freed;
+
+ /* Change this to an integer so we can */
+ /* store the relocation here. */
+ *rp = pt_tag(pt_integer) +
+ min(rel, packed_max_value);
+ }
+ rp += align_packed_per_ref;
+ freed += sizeof(ref_packed) * align_packed_per_ref;
+ }
+ } else { /* full-size ref */
+ uint rel = reloc + freed;
+
+ /* The following assignment is logically */
+ /* unnecessary; we do it only for convenience */
+ /* in debugging. */
+ ref *pref = (ref *) rp;
+
+ if (!r_has_attr(pref, l_mark)) {
+ if_debug1('8', " [8]ref 0x%lx is unmarked\n",
+ (ulong) pref);
+ /* Change this to a mark so we can */
+ /* store the relocation. */
+ r_set_type(pref, t_mark);
+ r_set_size(pref, rel);
+ freed += sizeof(ref);
+ } else {
+ if_debug1('8', " [8]ref 0x%lx is marked\n",
+ (ulong) pref);
+ /* Store the relocation here if possible. */
+ if (!ref_type_uses_size_or_null(r_type(pref))) {
+ if_debug2('8', " [8]storing reloc %u at 0x%lx\n",
+ rel, (ulong) pref);
+ r_set_size(pref, rel);
+ }
+ }
+ rp += packed_per_ref;
+ }
+ }
+ if_debug3('7', " [7]at end of refs 0x%lx, size = %u, freed = %u\n",
+ (ulong) (hdr + 1), size, freed);
+ if (freed == size)
+ return false;
+#if arch_sizeof_int > arch_sizeof_short
+ /*
+ * If the final relocation can't fit in the r_size field
+ * (which can't happen if the object shares a chunk with
+ * any other objects, so we know reloc = 0 in this case),
+ * we have to keep the entire object unless there are no
+ * references to any ref in it.
+ */
+ if (freed <= max_ushort)
+ return true;
+ /*
+ * We have to mark all surviving refs, but we also must
+ * overwrite any non-surviving refs with something that
+ * doesn't contain any pointers.
+ */
+ rp = (ref_packed *) (hdr + 1);
+ while (rp < end) {
+ if (r_is_packed(rp)) {
+ if (!r_has_pmark(rp))
+ *rp = pt_tag(pt_integer) | lp_mark;
+ ++rp;
+ } else { /* The following assignment is logically */
+ /* unnecessary; we do it only for convenience */
+ /* in debugging. */
+ ref *pref = (ref *) rp;
+
+ if (!r_has_attr(pref, l_mark)) {
+ r_set_type_attrs(pref, t_mark, l_mark);
+ r_set_size(pref, reloc);
+ } else {
+ if (!ref_type_uses_size_or_null(r_type(pref)))
+ r_set_size(pref, reloc);
+ }
+ rp += packed_per_ref;
+ }
+ }
+ /* The last ref has to remain unmarked. */
+ r_clear_attrs((ref *) rp - 1, l_mark);
+#endif
+ return true;
+}
+
+/* ------ Relocation phase ------ */
+
+/* Relocate all the pointers in a block of refs. */
+static void
+refs_do_reloc(void /*obj_header_t */ *vptr, uint size,
+ const gs_memory_struct_type_t * pstype, gc_state_t * gcst)
+{
+ igc_reloc_refs((ref_packed *) vptr,
+ (ref_packed *) ((char *)vptr + size),
+ gcst);
+}
+/* Relocate the contents of a block of refs. */
+/* If gcst->relocating_untraced is true, we are relocating pointers from an */
+/* untraced space, so relocate all refs, not just marked ones. */
+void
+igc_reloc_refs(ref_packed * from, ref_packed * to, gc_state_t * gcst)
+{
+ int min_trace = gcst->min_collect;
+ ref_packed *rp = from;
+ bool do_all = gcst->relocating_untraced;
+
+ vm_spaces spaces = gcst->spaces;
+ const gs_memory_t *cmem = space_system->stable_memory;
+
+ while (rp < to) {
+ ref *pref;
+#ifdef DEBUG
+ const void *before = 0;
+ const void *after = 0;
+# define DO_RELOC(var, stat)\
+ BEGIN before = (var); stat; after = (var); END
+# define SET_RELOC(var, expr)\
+ BEGIN before = (var); after = (var) = (expr); END
+#else
+# define DO_RELOC(var, stat) stat
+# define SET_RELOC(var, expr) var = expr
+#endif
+
+ if (r_is_packed(rp)) {
+ rp++;
+ continue;
+ }
+ /* The following assignment is logically unnecessary; */
+ /* we do it only for convenience in debugging. */
+ pref = (ref *) rp;
+ if_debug3m('8', gcst->heap, " [8]relocating %s %d ref at 0x%lx\n",
+ (r_has_attr(pref, l_mark) ? "marked" : "unmarked"),
+ r_btype(pref), (ulong) pref);
+ if ((r_has_attr(pref, l_mark) || do_all) &&
+ r_space(pref) >= min_trace
+ ) {
+ switch (r_type(pref)) {
+ /* Struct cases */
+ case t_file:
+ DO_RELOC(pref->value.pfile, RELOC_VAR(pref->value.pfile));
+ break;
+ case t_device:
+ DO_RELOC(pref->value.pdevice,
+ RELOC_VAR(pref->value.pdevice));
+ break;
+ case t_fontID:
+ case t_struct:
+ case t_astruct:
+ DO_RELOC(pref->value.pstruct,
+ RELOC_VAR(pref->value.pstruct));
+ break;
+ /* Non-trivial non-struct cases */
+ case t_dictionary:
+ rputc(gcst->heap, 'd');
+ SET_RELOC(pref->value.pdict,
+ (dict *)igc_reloc_ref_ptr((ref_packed *)pref->value.pdict, gcst));
+ break;
+ case t_array:
+ {
+ uint size = r_size(pref);
+
+ if (size != 0) { /* value.refs might be NULL */
+
+ /*
+ * If the array is large, we allocated it in its
+ * own object (at least originally -- this might
+ * be a pointer to a subarray.) In this case,
+ * we know it is the only object in its
+ * containing st_refs object, so we know that
+ * the mark containing the relocation appears
+ * just after it.
+ */
+ if (size < max_size_st_refs / sizeof(ref)) {
+ rputc(gcst->heap, 'a');
+ SET_RELOC(pref->value.refs,
+ (ref *) igc_reloc_ref_ptr(
+ (ref_packed *) pref->value.refs, gcst));
+ } else {
+ rputc(gcst->heap, 'A');
+ /*
+ * See the t_shortarray case below for why we
+ * decrement size.
+ */
+ --size;
+ SET_RELOC(pref->value.refs,
+ (ref *) igc_reloc_ref_ptr(
+ (ref_packed *) (pref->value.refs + size),
+ gcst) - size);
+ }
+ }
+ }
+ break;
+ case t_mixedarray:
+ if (r_size(pref) != 0) { /* value.refs might be NULL */
+ rputc(gcst->heap, 'm');
+ SET_RELOC(pref->value.packed,
+ igc_reloc_ref_ptr(pref->value.packed, gcst));
+ }
+ break;
+ case t_shortarray:
+ {
+ uint size = r_size(pref);
+
+ /*
+ * Since we know that igc_reloc_ref_ptr works by
+ * scanning forward, and we know that all the
+ * elements of this array itself are marked, we can
+ * save some scanning time by relocating the pointer
+ * to the end of the array rather than the
+ * beginning.
+ */
+ if (size != 0) { /* value.refs might be NULL */
+ rputc(gcst->heap, 's');
+ /*
+ * igc_reloc_ref_ptr has to be able to determine
+ * whether the pointer points into a space that
+ * isn't being collected. It does this by
+ * checking whether the referent of the pointer
+ * is marked. For this reason, we have to pass
+ * a pointer to the last real element of the
+ * array, rather than just beyond it.
+ */
+ --size;
+ SET_RELOC(pref->value.packed,
+ igc_reloc_ref_ptr(pref->value.packed + size,
+ gcst) - size);
+ }
+ }
+ break;
+ case t_name:
+ {
+ void *psub = name_ref_sub_table(cmem, pref);
+ void *rsub = RELOC_OBJ(psub); /* gcst implicit */
+
+ SET_RELOC(pref->value.pname,
+ (name *)
+ ((char *)rsub + ((char *)pref->value.pname -
+ (char *)psub)));
+ } break;
+ case t_string:
+ {
+ gs_string str;
+
+ str.data = pref->value.bytes;
+ str.size = r_size(pref);
+
+ DO_RELOC(str.data, RELOC_STRING_VAR(str));
+ pref->value.bytes = str.data;
+ }
+ break;
+ case t_oparray:
+ rputc(gcst->heap, 'o');
+ SET_RELOC(pref->value.const_refs,
+ (const ref *)igc_reloc_ref_ptr((const ref_packed *)pref->value.const_refs, gcst));
+ break;
+ default:
+ goto no_reloc; /* don't print trace message */
+ }
+ if_debug2m('8', gcst->heap, " [8]relocated 0x%lx => 0x%lx\n",
+ (ulong)before, (ulong)after);
+ }
+no_reloc:
+ rp += packed_per_ref;
+ }
+}
+
+/* Relocate a pointer to a ref. */
+/* See gsmemory.h for why the argument is const and the result is not. */
+ref_packed *
+igc_reloc_ref_ptr_nocheck(const ref_packed * prp, gc_state_t *gcst)
+{
+ /*
+ * Search forward for relocation. This algorithm is intrinsically very
+ * inefficient; we hope eventually to replace it with a better one.
+ */
+ const ref_packed *rp = prp;
+ uint dec = 0;
+#ifdef ALIGNMENT_ALIASING_BUG
+ const ref *rpref;
+# define RP_REF(rp) (rpref = (const ref *)rp, rpref)
+#else
+# define RP_REF(rp) ((const ref *)rp)
+#endif
+ for (;;) {
+
+ if (r_is_packed(rp)) {
+ /*
+ * Normally, an unmarked packed ref will be an
+ * integer whose value is the amount of relocation.
+ * However, the relocation value might have been
+ * too large to fit. If this is the case, for
+ * each such unmarked packed ref we pass over,
+ * we have to decrement the final relocation.
+ */
+ rputc(gcst->heap, (*rp & lp_mark ? '1' : '0'));
+ if (!(*rp & lp_mark)) {
+ if (*rp != pt_tag(pt_integer) + packed_max_value) {
+ /* This is a stored relocation value. */
+ rputc(gcst->heap, '\n');
+ rp = print_reloc(prp, "ref",
+ (const ref_packed *)
+ ((const char *)prp -
+ (*rp & packed_value_mask) + dec));
+ break;
+ }
+ /*
+ * We know this is the first of an aligned block
+ * of packed refs. Skip over the entire block,
+ * decrementing the final relocation.
+ */
+ dec += sizeof(ref_packed) * align_packed_per_ref;
+ rp += align_packed_per_ref;
+ } else
+ rp++;
+ continue;
+ }
+ if (!ref_type_uses_size_or_null(r_type(RP_REF(rp)))) {
+ /* reloc is in r_size */
+ rputc(gcst->heap, '\n');
+ rp = print_reloc(prp, "ref",
+ (const ref_packed *)
+ (r_size(RP_REF(rp)) == 0 ? prp :
+ (const ref_packed *)((const char *)prp -
+ r_size(RP_REF(rp)) + dec)));
+ break;
+ }
+ rputc(gcst->heap, 'u');
+ rp += packed_per_ref;
+ }
+ /* Use a severely deprecated pun to remove the const property. */
+ {
+ union { const ref_packed *r; ref_packed *w; } u;
+
+ u.r = rp;
+ return u.w;
+ }
+#undef RP_REF
+}
+ref_packed *
+igc_reloc_ref_ptr(const ref_packed * prp, gc_state_t *gcst)
+{
+ /*
+ * Search forward for relocation. This algorithm is intrinsically very
+ * inefficient; we hope eventually to replace it with a better one.
+ */
+ const ref_packed *rp = prp;
+#ifdef ALIGNMENT_ALIASING_BUG
+ const ref *rpref;
+# define RP_REF(rp) (rpref = (const ref *)rp, rpref)
+#else
+# define RP_REF(rp) ((const ref *)rp)
+#endif
+ /*
+ * Iff this pointer points into a space that wasn't traced,
+ * the referent won't be marked. In this case, we shouldn't
+ * do any relocation. Check for this first.
+ */
+ if (r_is_packed(rp)) {
+ if (!r_has_pmark(rp))
+ goto ret_rp;
+ } else {
+ if (!r_has_attr(RP_REF(rp), l_mark))
+ goto ret_rp;
+ }
+ return igc_reloc_ref_ptr_nocheck(prp, gcst);
+ret_rp:
+ /* Use a severely deprecated pun to remove the const property. */
+ {
+ union { const ref_packed *r; ref_packed *w; } u;
+
+ u.r = rp;
+ return u.w;
+ }
+}
+
+/* ------ Compaction phase ------ */
+
+/* Compact a ref object. */
+/* Remove the marks at the same time. */
+static void
+refs_compact(const gs_memory_t *mem, obj_header_t * pre, obj_header_t * dpre, uint size)
+{
+ ref_packed *dest;
+ ref_packed *src;
+ ref_packed *end;
+ uint new_size;
+
+ /* The next switch controls an optimization
+ for the loop termination condition.
+ It was useful during the development,
+ when some assumptions were temporary wrong.
+ We keep it for records. */
+
+ src = (ref_packed *) (pre + 1);
+ end = (ref_packed *) ((byte *) src + size);
+ /*
+ * We know that a block of refs always ends with a
+ * full-size ref, so we only need to check for reaching the end
+ * of the block when we see one of those.
+ */
+ if (dpre == pre) /* Loop while we don't need to copy. */
+ for (;;) {
+ if (r_is_packed(src)) {
+ if (!r_has_pmark(src))
+ break;
+ if_debug1m('8', mem, " [8]packed ref 0x%lx \"copied\"\n",
+ (ulong) src);
+ *src &= ~lp_mark;
+ src++;
+ } else { /* full-size ref */
+ ref *const pref = (ref *)src;
+
+ if (!r_has_attr(pref, l_mark))
+ break;
+ if_debug1m('8', mem, " [8]ref 0x%lx \"copied\"\n", (ulong) src);
+ r_clear_attrs(pref, l_mark);
+ src += packed_per_ref;
+ }
+ } else
+ *dpre = *pre;
+ dest = (ref_packed *) ((char *)dpre + ((char *)src - (char *)pre));
+ for (;;) {
+ if (r_is_packed(src)) {
+ if (r_has_pmark(src)) {
+ if_debug2m('8', mem, " [8]packed ref 0x%lx copied to 0x%lx\n",
+ (ulong) src, (ulong) dest);
+ *dest++ = *src & ~lp_mark;
+ }
+ src++;
+ } else { /* full-size ref */
+ if (r_has_attr((ref *) src, l_mark)) {
+ ref rtemp;
+
+ if_debug2m('8', mem, " [8]ref 0x%lx copied to 0x%lx\n",
+ (ulong) src, (ulong) dest);
+ /* We can't just use ref_assign_inline, */
+ /* because the source and destination */
+ /* might overlap! */
+ ref_assign_inline(&rtemp, (ref *) src);
+ r_clear_attrs(&rtemp, l_mark);
+ ref_assign_inline((ref *) dest, &rtemp);
+ src += packed_per_ref;
+ dest += packed_per_ref;
+ } else { /* check for end of block */
+ src += packed_per_ref;
+ if (src >= end)
+ break;
+ }
+ }
+ }
+ new_size = (byte *) dest - (byte *) (dpre + 1) + sizeof(ref);
+#ifdef DEBUG
+ /* Check that the relocation came out OK. */
+ /* NOTE: this check only works within a single chunk. */
+ if ((byte *) src - (byte *) dest != r_size((ref *) src - 1) + sizeof(ref)) {
+ mlprintf3(mem, "Reloc error for refs 0x%lx: reloc = %lu, stored = %u\n",
+ (ulong) dpre, (ulong) ((byte *) src - (byte *) dest),
+ (uint) r_size((ref *) src - 1));
+ gs_abort(mem);
+ }
+#endif
+ /* Pad to a multiple of sizeof(ref). */
+ while (new_size % sizeof(ref))
+ *dest++ = pt_tag(pt_integer),
+ new_size += sizeof(ref_packed);
+ /* We want to make the newly freed space into a free block, */
+ /* but we can only do this if we have enough room. */
+ if (size - new_size < sizeof(obj_header_t)) { /* Not enough room. Pad to original size. */
+ while (new_size < size)
+ *dest++ = pt_tag(pt_integer),
+ new_size += sizeof(ref_packed);
+ } else {
+ obj_header_t *pfree = (obj_header_t *) ((ref *) dest + 1);
+
+ pfree->o_pad = 0;
+ pfree->o_alone = 0;
+ pfree->o_size = size - new_size - sizeof(obj_header_t);
+ pfree->o_type = &st_bytes;
+ }
+ /* Re-create the final ref. */
+ r_set_type((ref *) dest, t_integer);
+ dpre->o_size = new_size;
+}
diff --git a/psi/igcstr.c b/psi/igcstr.c
new file mode 100644
index 000000000..eb7052202
--- /dev/null
+++ b/psi/igcstr.c
@@ -0,0 +1,446 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* String GC routines for Ghostscript */
+#include "memory_.h"
+#include "ghost.h"
+#include "gsmdebug.h"
+#include "gsstruct.h"
+#include "iastate.h"
+#include "igcstr.h"
+#include "igc.h"
+
+/* Forward references */
+static bool gc_mark_string(const byte *, uint, bool, const chunk_t *);
+
+/* (Un)mark the strings in a chunk. */
+void
+gc_strings_set_marks(chunk_t * cp, bool mark)
+{
+ if (cp->smark != 0) {
+ if_debug3('6', "[6]clearing string marks 0x%lx[%u] to %d\n",
+ (ulong) cp->smark, cp->smark_size, (int)mark);
+ memset(cp->smark, 0, cp->smark_size);
+ if (mark)
+ gc_mark_string(cp->sbase + HDR_ID_OFFSET, (cp->climit - cp->sbase) - HDR_ID_OFFSET, true, cp);
+ }
+}
+
+/* We mark strings a word at a time. */
+typedef string_mark_unit bword;
+
+#define bword_log2_bytes log2_sizeof_string_mark_unit
+#define bword_bytes (1 << bword_log2_bytes)
+#define bword_log2_bits (bword_log2_bytes + 3)
+#define bword_bits (1 << bword_log2_bits)
+#define bword_1s (~(bword)0)
+/* Compensate for byte order reversal if necessary. */
+#if arch_is_big_endian
+# if bword_bytes == 2
+# define bword_swap_bytes(m) m = (m << 8) | (m >> 8)
+# else /* bword_bytes == 4 */
+# define bword_swap_bytes(m)\
+ m = (m << 24) | ((m & 0xff00) << 8) | ((m >> 8) & 0xff00) | (m >> 24)
+# endif
+#else
+# define bword_swap_bytes(m) DO_NOTHING
+#endif
+
+/* (Un)mark a string in a known chunk. Return true iff any new marks. */
+static bool
+gc_mark_string(const byte * ptr, uint size, bool set, const chunk_t * cp)
+{
+ uint offset = (ptr - HDR_ID_OFFSET) - cp->sbase;
+ bword *bp = (bword *) (cp->smark + ((offset & -bword_bits) >> 3));
+ uint bn = offset & (bword_bits - 1);
+ bword m = bword_1s << bn;
+ uint left = size + HDR_ID_OFFSET;
+ bword marks = 0;
+
+ bword_swap_bytes(m);
+ if (set) {
+ if (left + bn >= bword_bits) {
+ marks |= ~*bp & m;
+ *bp |= m;
+ m = bword_1s, left -= bword_bits - bn, bp++;
+ while (left >= bword_bits) {
+ marks |= ~*bp;
+ *bp = bword_1s;
+ left -= bword_bits, bp++;
+ }
+ }
+ if (left) {
+ bword_swap_bytes(m);
+ m -= m << left;
+ bword_swap_bytes(m);
+ marks |= ~*bp & m;
+ *bp |= m;
+ }
+ } else {
+ if (left + bn >= bword_bits) {
+ *bp &= ~m;
+ m = bword_1s, left -= bword_bits - bn, bp++;
+ if (left >= bword_bits * 5) {
+ memset(bp, 0, (left & -bword_bits) >> 3);
+ bp += left >> bword_log2_bits;
+ left &= bword_bits - 1;
+ } else
+ while (left >= bword_bits) {
+ *bp = 0;
+ left -= bword_bits, bp++;
+ }
+ }
+ if (left) {
+ bword_swap_bytes(m);
+ m -= m << left;
+ bword_swap_bytes(m);
+ *bp &= ~m;
+ }
+ }
+ return marks != 0;
+}
+
+#ifdef DEBUG
+/* Print a string for debugging. We need this because there is no d---
+ * equivalent of fwrite.
+ */
+static void
+dmfwrite(const gs_memory_t *mem, const byte *ptr, uint count)
+{
+ uint i;
+ for (i = 0; i < count; ++i)
+ dmputc(mem, ptr[i]);
+}
+#endif
+
+/* Mark a string. Return true if any new marks. */
+bool
+gc_string_mark(const byte * ptr, uint size, bool set, gc_state_t * gcst)
+{
+ const chunk_t *cp;
+ bool marks;
+
+ if (size == 0)
+ return false;
+#define dmprintstr(mem)\
+ dmputc(mem, '('); dmfwrite(mem, ptr - HDR_ID_OFFSET, min(size, 20));\
+ dmputs(mem, (size <= 20 ? ")" : "...)"))
+ if (!(cp = gc_locate(ptr - HDR_ID_OFFSET, gcst))) { /* not in a chunk */
+#ifdef DEBUG
+ if (gs_debug_c('5')) {
+ dmlprintf2(gcst->heap, "[5]0x%lx[%u]", (ulong) ptr - HDR_ID_OFFSET, size);
+ dmprintstr(gcst->heap);
+ dmputs(gcst->heap, " not in a chunk\n");
+ }
+#endif
+ return false;
+ }
+ if (cp->smark == 0) /* not marking strings */
+ return false;
+#ifdef DEBUG
+ if (ptr - HDR_ID_OFFSET < cp->ctop) {
+ lprintf4("String pointer 0x%lx[%u] outside [0x%lx..0x%lx)\n",
+ (ulong) ptr - HDR_ID_OFFSET, size, (ulong) cp->ctop, (ulong) cp->climit);
+ return false;
+ } else if (ptr + size > cp->climit) { /*
+ * If this is the bottommost string in a chunk that has
+ * an inner chunk, the string's starting address is both
+ * cp->ctop of the outer chunk and cp->climit of the inner;
+ * gc_locate may incorrectly attribute the string to the
+ * inner chunk because of this. This doesn't affect
+ * marking or relocation, since the machinery for these
+ * is all associated with the outermost chunk,
+ * but it can cause the validity check to fail.
+ * Check for this case now.
+ */
+ const chunk_t *scp = cp;
+
+ while (ptr - HDR_ID_OFFSET == scp->climit && scp->outer != 0)
+ scp = scp->outer;
+ if (ptr - HDR_ID_OFFSET + size > scp->climit) {
+ lprintf4("String pointer 0x%lx[%u] outside [0x%lx..0x%lx)\n",
+ (ulong) ptr - HDR_ID_OFFSET, size,
+ (ulong) scp->ctop, (ulong) scp->climit);
+ return false;
+ }
+ }
+#endif
+ marks = gc_mark_string(ptr, size, set, cp);
+#ifdef DEBUG
+ if (gs_debug_c('5')) {
+ dmlprintf4(gcst->heap, "[5]%s%smarked 0x%lx[%u]",
+ (marks ? "" : "already "), (set ? "" : "un"),
+ (ulong) ptr - HDR_ID_OFFSET, size);
+ dmprintstr(gcst->heap);
+ dmputc(gcst->heap, '\n');
+ }
+#endif
+ return marks;
+}
+
+/* Clear the relocation for strings. */
+/* This requires setting the marks. */
+void
+gc_strings_clear_reloc(chunk_t * cp)
+{
+ if (cp->sreloc != 0) {
+ gc_strings_set_marks(cp, true);
+ if_debug1('6', "[6]clearing string reloc 0x%lx\n",
+ (ulong) cp->sreloc);
+ gc_strings_set_reloc(cp);
+ }
+}
+
+/* Count the 0-bits in a byte. */
+static const byte count_zero_bits_table[256] =
+{
+#define o4(n) n,n-1,n-1,n-2
+#define o16(n) o4(n),o4(n-1),o4(n-1),o4(n-2)
+#define o64(n) o16(n),o16(n-1),o16(n-1),o16(n-2)
+ o64(8), o64(7), o64(7), o64(6)
+};
+
+#define byte_count_zero_bits(byt)\
+ (uint)(count_zero_bits_table[byt])
+#define byte_count_one_bits(byt)\
+ (uint)(8 - count_zero_bits_table[byt])
+
+/* Set the relocation for the strings in a chunk. */
+/* The sreloc table stores the relocated offset from climit for */
+/* the beginning of each block of string_data_quantum characters. */
+void
+gc_strings_set_reloc(chunk_t * cp)
+{
+ if (cp->sreloc != 0 && cp->smark != 0) {
+ byte *bot = cp->ctop;
+ byte *top = cp->climit;
+ uint count =
+ (top - bot + (string_data_quantum - 1)) >>
+ log2_string_data_quantum;
+ string_reloc_offset *relp =
+ cp->sreloc +
+ (cp->smark_size >> (log2_string_data_quantum - 3));
+ register const byte *bitp = cp->smark + cp->smark_size;
+ register string_reloc_offset reloc = 0;
+
+ /* Skip initial unrelocated strings quickly. */
+#if string_data_quantum == bword_bits || string_data_quantum == bword_bits * 2
+ {
+ /* Work around the alignment aliasing bug. */
+ const bword *wp = (const bword *)bitp;
+
+#if string_data_quantum == bword_bits
+# define RELOC_TEST_1S(wp) (wp[-1])
+#else /* string_data_quantum == bword_bits * 2 */
+# define RELOC_TEST_1S(wp) (wp[-1] & wp[-2])
+#endif
+ while (count && RELOC_TEST_1S(wp) == bword_1s) {
+ wp -= string_data_quantum / bword_bits;
+ *--relp = reloc += string_data_quantum;
+ --count;
+ }
+#undef RELOC_TEST_1S
+ bitp = (const byte *)wp;
+ }
+#endif
+ while (count--) {
+ bitp -= string_data_quantum / 8;
+ reloc += string_data_quantum -
+ byte_count_zero_bits(bitp[0]);
+ reloc -= byte_count_zero_bits(bitp[1]);
+ reloc -= byte_count_zero_bits(bitp[2]);
+ reloc -= byte_count_zero_bits(bitp[3]);
+#if log2_string_data_quantum > 5
+ reloc -= byte_count_zero_bits(bitp[4]);
+ reloc -= byte_count_zero_bits(bitp[5]);
+ reloc -= byte_count_zero_bits(bitp[6]);
+ reloc -= byte_count_zero_bits(bitp[7]);
+#endif
+ *--relp = reloc;
+ }
+ }
+ cp->sdest = cp->climit;
+}
+
+/* Relocate a string pointer. */
+void
+igc_reloc_string(gs_string * sptr, gc_state_t * gcst)
+{
+ byte *ptr;
+ const chunk_t *cp;
+ uint offset;
+ uint reloc;
+ const byte *bitp;
+ byte byt;
+
+ if (sptr->size == 0) {
+ sptr->data = 0;
+ return;
+ }
+ ptr = sptr->data;
+ ptr -= HDR_ID_OFFSET;
+
+ if (!(cp = gc_locate(ptr, gcst))) /* not in a chunk */
+ return;
+ if (cp->sreloc == 0 || cp->smark == 0) /* not marking strings */
+ return;
+ offset = ptr - cp->sbase;
+ reloc = cp->sreloc[offset >> log2_string_data_quantum];
+ bitp = &cp->smark[offset >> 3];
+ switch (offset & (string_data_quantum - 8)) {
+#if log2_string_data_quantum > 5
+ case 56:
+ reloc -= byte_count_one_bits(bitp[-7]);
+ case 48:
+ reloc -= byte_count_one_bits(bitp[-6]);
+ case 40:
+ reloc -= byte_count_one_bits(bitp[-5]);
+ case 32:
+ reloc -= byte_count_one_bits(bitp[-4]);
+#endif
+ case 24:
+ reloc -= byte_count_one_bits(bitp[-3]);
+ case 16:
+ reloc -= byte_count_one_bits(bitp[-2]);
+ case 8:
+ reloc -= byte_count_one_bits(bitp[-1]);
+ }
+ byt = *bitp & (0xff >> (8 - (offset & 7)));
+ reloc -= byte_count_one_bits(byt);
+ if_debug2('5', "[5]relocate string 0x%lx to 0x%lx\n",
+ (ulong) ptr, (ulong) (cp->sdest - reloc));
+ sptr->data = (cp->sdest - reloc) + HDR_ID_OFFSET;
+}
+void
+igc_reloc_const_string(gs_const_string * sptr, gc_state_t * gcst)
+{ /* We assume the representation of byte * and const byte * is */
+ /* the same.... */
+ igc_reloc_string((gs_string *) sptr, gcst);
+}
+void
+igc_reloc_param_string(gs_param_string * sptr, gc_state_t * gcst)
+{
+ if (!sptr->persistent) {
+ /* We assume that gs_param_string is a subclass of gs_string. */
+ igc_reloc_string((gs_string *)sptr, gcst);
+ }
+}
+
+/* Compact the strings in a chunk. */
+void
+gc_strings_compact(chunk_t * cp, const gs_memory_t *mem)
+{
+ if (cp->smark != 0) {
+ byte *hi = cp->climit;
+ byte *lo = cp->ctop;
+ const byte *from = hi;
+ byte *to = hi;
+ const byte *bp = cp->smark + cp->smark_size;
+
+#ifdef DEBUG
+ if (gs_debug_c('4') || gs_debug_c('5')) {
+ byte *base = cp->sbase;
+ uint i = (lo - base) & -string_data_quantum;
+ uint n = ROUND_UP(hi - base, string_data_quantum);
+
+#define R 16
+ for (; i < n; i += R) {
+ uint j;
+
+ dmlprintf1(mem, "[4]0x%lx: ", (ulong) (base + i));
+ for (j = i; j < i + R; j++) {
+ byte ch = base[j];
+
+ if (ch <= 31) {
+ dmputc(mem, '^');
+ dmputc(mem, ch + 0100);
+ } else
+ dmputc(mem, ch);
+ }
+ dmputc(mem, ' ');
+ for (j = i; j < i + R; j++)
+ dmputc(mem, (cp->smark[j >> 3] & (1 << (j & 7)) ?
+ '+' : '.'));
+#undef R
+ if (!(i & (string_data_quantum - 1)))
+ dmprintf1(mem, " %u", cp->sreloc[i >> log2_string_data_quantum]);
+ dmputc(mem, '\n');
+ }
+ }
+#endif
+ /*
+ * Skip unmodified strings quickly. We know that cp->smark is
+ * aligned to a string_mark_unit.
+ */
+ {
+ /* Work around the alignment aliasing bug. */
+ const bword *wp = (const bword *)bp;
+
+ while (to > lo && wp[-1] == bword_1s)
+ to -= bword_bits, --wp;
+ bp = (const byte *)wp;
+ while (to > lo && bp[-1] == 0xff)
+ to -= 8, --bp;
+ }
+ from = to;
+
+ while (from > lo) {
+ byte b = *--bp;
+
+ from -= 8;
+ switch (b) {
+ case 0xff:
+ to -= 8;
+ /*
+ * Since we've seen a byte other than 0xff, we know
+ * to != from at this point.
+ */
+ to[7] = from[7];
+ to[6] = from[6];
+ to[5] = from[5];
+ to[4] = from[4];
+ to[3] = from[3];
+ to[2] = from[2];
+ to[1] = from[1];
+ to[0] = from[0];
+ break;
+ default:
+ if (b & 0x80)
+ *--to = from[7];
+ if (b & 0x40)
+ *--to = from[6];
+ if (b & 0x20)
+ *--to = from[5];
+ if (b & 0x10)
+ *--to = from[4];
+ if (b & 8)
+ *--to = from[3];
+ if (b & 4)
+ *--to = from[2];
+ if (b & 2)
+ *--to = from[1];
+ if (b & 1)
+ *--to = from[0];
+ /* falls through */
+ case 0:
+ ;
+ }
+ }
+ gs_alloc_fill(cp->ctop, gs_alloc_fill_collected,
+ to - cp->ctop);
+ cp->ctop = to;
+ }
+}
diff --git a/psi/igcstr.h b/psi/igcstr.h
new file mode 100644
index 000000000..4bf0fbc93
--- /dev/null
+++ b/psi/igcstr.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Internal interface to string garbage collector */
+
+#ifndef igcstr_INCLUDED
+# define igcstr_INCLUDED
+
+/* Exported by ilocate.c for igcstr.c */
+chunk_t *gc_locate(const void *, gc_state_t *);
+
+/* Exported by igcstr.c for igc.c */
+void gc_strings_set_marks(chunk_t *, bool);
+bool gc_string_mark(const byte *, uint, bool, gc_state_t *);
+void gc_strings_clear_reloc(chunk_t *);
+void gc_strings_set_reloc(chunk_t *);
+void gc_strings_compact(chunk_t *, const gs_memory_t *);
+string_proc_reloc(igc_reloc_string);
+const_string_proc_reloc(igc_reloc_const_string);
+param_string_proc_reloc(igc_reloc_param_string);
+
+#endif /* igcstr_INCLUDED */
diff --git a/psi/igstate.h b/psi/igstate.h
new file mode 100644
index 000000000..0af15608a
--- /dev/null
+++ b/psi/igstate.h
@@ -0,0 +1,209 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter graphics state definition */
+
+#ifndef igstate_INCLUDED
+# define igstate_INCLUDED
+
+#include "gsstate.h"
+#include "gxstate.h" /* for 'client data' access */
+#include "imemory.h"
+#include "istruct.h" /* for gstate obj definition */
+#include "gxcindex.h"
+
+/*
+ * From the interpreter's point of view, the graphics state is largely opaque,
+ * i.e., the interpreter is just another client of the library.
+ * The interpreter does require additional items in the graphics state;
+ * these are "client data" from the library's point of view.
+ * Most of the complexity in this added state comes from
+ * the parameters associated with the various Level 2 color spaces.
+ * Note that the added information consists entirely of refs.
+ */
+
+/*
+ * The interpreter represents graphics state objects in a slightly
+ * unnatural way, namely, by a t_astruct ref that points to an object
+ * of type st_igstate_obj, which is essentially a t_struct ref that in turn
+ * points to a real graphics state (object of type st_gs_state).
+ * We do this so that save and restore can manipulate the intermediate
+ * object and not have to worry about copying entire gs_states.
+ *
+ * Because a number of different operators must test whether an object
+ * is a gstate, we make an exception to our convention of declaring
+ * structure descriptors only in the place where the structure itself
+ * is defined (see gsstruct.h for more information on this).
+ */
+typedef struct igstate_obj_s {
+ ref gstate; /* t_struct / st_gs_state */
+} igstate_obj;
+
+extern_st(st_igstate_obj);
+#define public_st_igstate_obj() /* in zdps1.c */\
+ gs_public_st_ref_struct(st_igstate_obj, igstate_obj, "gstatetype")
+#define igstate_ptr(rp) r_ptr(&r_ptr(rp, igstate_obj)->gstate, gs_state)
+
+/* DeviceN names and tint transform */
+typedef struct ref_device_n_params_s {
+ ref layer_names, tint_transform;
+} ref_device_n_params;
+
+/* CIE transformation procedures */
+typedef struct ref_cie_procs_s {
+ union {
+ ref DEFG;
+ ref DEF;
+ } PreDecode;
+ union {
+ ref ABC;
+ ref A;
+ } Decode;
+ ref DecodeLMN;
+} ref_cie_procs;
+
+/* CIE rendering transformation procedures */
+typedef struct ref_cie_render_procs_s {
+ ref TransformPQR, EncodeLMN, EncodeABC, RenderTableT;
+} ref_cie_render_procs;
+
+/* Separation name and tint transform */
+typedef struct ref_separation_params_s {
+ ref layer_name, tint_transform;
+} ref_separation_params;
+
+/* All color space parameters. */
+/* All of these are optional. */
+/* Note that they may actually be the parameters for an underlying or */
+/* alternate space for a special space. */
+typedef struct ref_color_procs_s {
+ ref_cie_procs cie;
+ union {
+ ref_device_n_params device_n;
+ ref_separation_params separation;
+ ref index_proc;
+ } special;
+} ref_color_procs;
+typedef struct ref_colorspace_s {
+ ref array; /* color space (array), only relevant if */
+ /* the current color space has parameters */
+ /* associated with it. */
+ ref_color_procs procs; /* associated procedures/parameters, */
+ /* only relevant for DeviceN, CIE, */
+ /* Separation, Indexed/CIE, */
+ /* Indexed with procedure, or a Pattern */
+ /* with one of these. */
+} ref_colorspace;
+
+#ifndef int_remap_color_info_DEFINED
+# define int_remap_color_info_DEFINED
+typedef struct int_remap_color_info_s int_remap_color_info_t;
+#endif
+
+typedef struct int_gstate_s {
+ ref dash_pattern_array; /* (array) */
+ /* Screen_procs are only relevant if setscreen was */
+ /* executed more recently than sethalftone */
+ /* (for this graphics context). */
+ struct {
+ ref red, green, blue, gray;
+ } screen_procs, /* halftone screen procedures */
+ transfer_procs; /* transfer procedures */
+ ref black_generation; /* (procedure) */
+ ref undercolor_removal; /* (procedure) */
+ ref_colorspace colorspace[2];
+ /*
+ * Pattern is relevant only if the current color space
+ * is a pattern space.
+ */
+ ref pattern[2]; /* pattern (dictionary) */
+ struct {
+ ref dict; /* CIE color rendering dictionary */
+ ref_cie_render_procs procs; /* (see above) */
+ } colorrendering;
+ /*
+ * Use_cie_color tracks the UseCIEColor parameter of the page
+ * device. This parameter may, during initialization, be read
+ * through the .getuseciecolor operator, and set (in Level 3)
+ * via the .setuseciecolor operator.
+ *
+ * Previously, the UseCIEColor color space substitution feature
+ * was implemented in the graphic library. It is now implemented
+ * strictly in the interpreter.
+ */
+ ref use_cie_color;
+ /*
+ * Halftone is relevant only if sethalftone was executed
+ * more recently than setscreen for this graphics context.
+ * setscreen sets it to null.
+ */
+ ref halftone; /* halftone (dictionary) */
+ /*
+ * Pagedevice is only relevant if setpagedevice was executed more
+ * recently than nulldevice, setcachedevice, or setdevice with a
+ * non-page device (for this graphics context). If the current device
+ * is not a page device, pagedevice is null.
+ */
+ ref pagedevice; /* page device (dictionary|null) */
+ /*
+ * Remap_color_info is used temporarily to communicate the need for
+ * Pattern or DeviceNcolor remapping to the interpreter. See
+ * gs_error_Remap_Color in gserrors.h. The extra level of indirection through a
+ * structure is needed because the gstate passed to the PaintProc is
+ * different from the current gstate in the graphics state, and because
+ * the DeviceN color being remapped is not necessarily the current color
+ * in the graphics state (for shading or images): the structure is
+ * shared, so that the interpreter can get its hands on the remapping
+ * procedure.
+ */
+ ref remap_color_info; /* t_struct (int_remap_color_info_t) */
+ /*
+ * The opacity and shape masks are a PDF 1.4 transparency feature,
+ * not standard PostScript.
+ */
+ ref opacity_mask, shape_mask; /* dictionary|null */
+} int_gstate;
+
+#define clear_pagedevice(pigs) make_null(&(pigs)->pagedevice)
+/*
+ * Even though the interpreter's part of the graphics state actually
+ * consists of refs, allocating it as refs tends to create sandbars;
+ * since it is always allocated and freed as a unit, we can treat it
+ * as an ordinary structure.
+ */
+#define private_st_int_gstate() /* in zgstate.c */\
+ gs_private_st_ref_struct(st_int_gstate, int_gstate, "int_gstate")
+
+/* Enumerate the refs in an int_gstate. */
+/* Since all the elements of an int_gstate are refs, this is simple. */
+#define int_gstate_map_refs(p,m)\
+ { register ref *rp_ = (ref *)(p);\
+ register int i = sizeof(int_gstate) / sizeof(ref);\
+ do { m(rp_); ++rp_; } while ( --i );\
+ }
+
+/* Create the gstate for a new context. */
+/* We export this so that fork can use it. */
+gs_state *int_gstate_alloc(const gs_dual_memory_t * dmem);
+
+/* Get the int_gstate from a gs_state. */
+#define gs_int_gstate(pgs) ((int_gstate *)gs_state_client_data(pgs))
+
+/* The current instances for operators. */
+#define igs (i_ctx_p->pgs)
+#define istate gs_int_gstate(igs)
+
+#endif /* igstate_INCLUDED */
diff --git a/psi/iht.h b/psi/iht.h
new file mode 100644
index 000000000..87395388a
--- /dev/null
+++ b/psi/iht.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Procedures exported by zht.c for zht1.c and zht2.c */
+
+#ifndef iht_INCLUDED
+# define iht_INCLUDED
+
+int zscreen_params(os_ptr op, gs_screen_halftone * phs);
+
+int zscreen_enum_init(i_ctx_t *i_ctx_p, const gx_ht_order * porder,
+ gs_screen_halftone * phs, ref * pproc, int npop,
+ op_proc_t finish_proc, int space_index);
+
+#endif /* iht_INCLUDED */
diff --git a/psi/iimage.h b/psi/iimage.h
new file mode 100644
index 000000000..94c432d56
--- /dev/null
+++ b/psi/iimage.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Image operator entry points */
+/* Requires gscspace.h, gxiparam.h */
+
+#ifndef iimage_INCLUDED
+# define iimage_INCLUDED
+
+/* These procedures are exported by zimage.c for other modules. */
+
+/*
+ * Define a structure for image parameters other than those defined
+ * in the gs_*image*_t structure.
+ */
+typedef struct image_params_s {
+ bool MultipleDataSources;
+ ref DataSource[GS_IMAGE_MAX_COMPONENTS];
+ const float *pDecode;
+} image_params;
+
+/* Extract and check parameters for an image. */
+int data_image_params(const gs_memory_t *mem,
+ const ref *op, gs_data_image_t *pim,
+ image_params *pip, bool require_DataSource,
+ int num_components, int max_bits_per_component,
+ bool has_alpha, bool islab);
+int pixel_image_params(i_ctx_t *i_ctx_p, const ref *op,
+ gs_pixel_image_t *pim, image_params * pip,
+ int max_bits_per_component, bool has_alpha,
+ gs_color_space *csp);
+
+/* Exported for zimage3.c and ztrans.c */
+int zimage_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
+ const ref * sources, bool uses_color, int npop);
+
+/* Exported for zdpnext.c */
+int image1_setup(i_ctx_t * i_ctx_p, bool has_alpha);
+
+#endif /* iimage_INCLUDED */
diff --git a/psi/iimage2.h b/psi/iimage2.h
new file mode 100644
index 000000000..c0c1ba8df
--- /dev/null
+++ b/psi/iimage2.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 2 image operator support */
+/* Requires gsiparam.h */
+
+#ifndef iimage2_INCLUDED
+# define iimage2_INCLUDED
+
+/* This procedure is exported by zimage2.c for other modules. */
+
+/*
+ * Process an image that has no explicit source data. This isn't used by
+ * standard Level 2, but it's a very small procedure and is needed by
+ * both zdps.c and zdpnext.c.
+ */
+int process_non_source_image(i_ctx_t *i_ctx_p,
+ const gs_image_common_t * pim,
+ client_name_t cname);
+
+#endif /* iimage2_INCLUDED */
diff --git a/psi/iinit.c b/psi/iinit.c
new file mode 100644
index 000000000..a70f7c12f
--- /dev/null
+++ b/psi/iinit.c
@@ -0,0 +1,543 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Initialize internally known objects for Ghostscript interpreter */
+#include "string_.h"
+#include "ghost.h"
+#include "gscdefs.h"
+#include "gsexit.h"
+#include "gsstruct.h"
+#include "ierrors.h"
+#include "ialloc.h"
+#include "iddict.h"
+#include "dstack.h"
+#include "ilevel.h"
+#include "iinit.h"
+#include "iname.h"
+#include "interp.h"
+#include "ipacked.h"
+#include "iparray.h"
+#include "iutil.h"
+#include "ivmspace.h"
+#include "opdef.h"
+#include "store.h"
+#include "iconf.h"
+
+/* Implementation parameters. */
+/*
+ * Define the (initial) sizes of the various system dictionaries. We want
+ * the sizes to be prime numbers large enough to cover all the operators,
+ * plus everything in the init files, even if all the optional features are
+ * selected. Note that these sizes must be large enough to get us through
+ * initialization, since we start up in Level 1 mode where dictionaries
+ * don't expand automatically.
+ */
+/* The size of systemdict can be set in the makefile. */
+#ifndef SYSTEMDICT_SIZE
+# define SYSTEMDICT_SIZE 631
+#endif
+#ifndef SYSTEMDICT_LEVEL2_SIZE
+# define SYSTEMDICT_LEVEL2_SIZE 983
+#endif
+#ifndef SYSTEMDICT_LL3_SIZE
+# define SYSTEMDICT_LL3_SIZE 1123
+#endif
+/* The size of level2dict, if applicable, can be set in the makefile. */
+#ifndef LEVEL2DICT_SIZE
+# define LEVEL2DICT_SIZE 251
+#endif
+/* Ditto the size of ll3dict. */
+#ifndef LL3DICT_SIZE
+# define LL3DICT_SIZE 43
+#endif
+/* Ditto the size of filterdict. */
+#ifndef FILTERDICT_SIZE
+# define FILTERDICT_SIZE 43
+#endif
+/* Define an arbitrary size for the operator procedure tables. */
+#ifndef OP_ARRAY_TABLE_SIZE
+# define OP_ARRAY_TABLE_SIZE 300
+#endif
+#ifndef OP_ARRAY_TABLE_GLOBAL_SIZE
+# define OP_ARRAY_TABLE_GLOBAL_SIZE OP_ARRAY_TABLE_SIZE
+#endif
+#ifndef OP_ARRAY_TABLE_LOCAL_SIZE
+# define OP_ARRAY_TABLE_LOCAL_SIZE (OP_ARRAY_TABLE_SIZE / 2)
+#endif
+#define OP_ARRAY_TABLE_TOTAL_SIZE\
+ (OP_ARRAY_TABLE_GLOBAL_SIZE + OP_ARRAY_TABLE_LOCAL_SIZE)
+
+/* Define the list of error names. */
+const char *const gs_error_names[] =
+{
+ ERROR_NAMES
+};
+
+/* Enter a name and value into a dictionary. */
+static int
+i_initial_enter_name_in(i_ctx_t *i_ctx_p, ref *pdict, const char *nstr,
+ const ref * pref)
+{
+ int code = idict_put_string(pdict, nstr, pref);
+
+ if (code < 0)
+ lprintf4("initial_enter failed (%d), entering /%s in -dict:%u/%u-\n",
+ code, nstr, dict_length(pdict), dict_maxlength(pdict));
+ return code;
+}
+int
+i_initial_enter_name(i_ctx_t *i_ctx_p, const char *nstr, const ref * pref)
+{
+ return i_initial_enter_name_in(i_ctx_p, systemdict, nstr, pref);
+}
+
+/* Remove a name from systemdict. */
+void
+i_initial_remove_name(i_ctx_t *i_ctx_p, const char *nstr)
+{
+ ref nref;
+
+ if (name_ref(imemory, (const byte *)nstr, strlen(nstr), &nref, -1) >= 0)
+ idict_undef(systemdict, &nref);
+}
+
+/* Define the names and sizes of the initial dictionaries. */
+/* The names are used to create references in systemdict. */
+const struct {
+ const char *name;
+ uint size;
+ bool local;
+} initial_dictionaries[] = {
+#ifdef INITIAL_DICTIONARIES
+ INITIAL_DICTIONARIES
+#else
+ /* systemdict is created and named automagically */
+ {
+ "level2dict", LEVEL2DICT_SIZE, false
+ },
+ {
+ "ll3dict", LL3DICT_SIZE, false
+ },
+ {
+ "globaldict", 0, false
+ },
+ {
+ "userdict", 0, true
+ },
+ {
+ "filterdict", FILTERDICT_SIZE, false
+ },
+#endif
+};
+/* systemdict and globaldict are magically inserted at the bottom */
+const char *const initial_dstack[] =
+{
+#ifdef INITIAL_DSTACK
+ INITIAL_DSTACK
+#else
+ "userdict"
+#endif
+};
+
+#define MIN_DSTACK_SIZE (countof(initial_dstack) + 1) /* +1 for systemdict */
+
+/*
+ * Detect whether we have any Level 2 or LanguageLevel 3 operators.
+ * We export this for gs_init1 in imain.c.
+ * This is slow, but we only call it a couple of times.
+ */
+static int
+gs_op_language_level(void)
+{
+ const op_def *const *tptr;
+ int level = 1;
+
+ for (tptr = op_defs_all; *tptr != 0; ++tptr) {
+ const op_def *def;
+
+ for (def = *tptr; def->oname != 0; ++def)
+ if (op_def_is_begin_dict(def)) {
+ if (!strcmp(def->oname, "level2dict"))
+ level = max(level, 2);
+ else if (!strcmp(def->oname, "ll3dict"))
+ level = max(level, 3);
+ }
+ }
+ return level;
+}
+bool
+gs_have_level2(void)
+{
+ return (gs_op_language_level() >= 2);
+}
+
+/* Create an initial dictionary if necessary. */
+static ref *
+make_initial_dict(i_ctx_t *i_ctx_p, const char *iname, ref idicts[])
+{
+ int i;
+
+ /* systemdict was created specially. */
+ if (!strcmp(iname, "systemdict"))
+ return systemdict;
+ for (i = 0; i < countof(initial_dictionaries); i++) {
+ const char *dname = initial_dictionaries[i].name;
+ const int dsize = initial_dictionaries[i].size;
+
+ if (!strcmp(iname, dname)) {
+ ref *dref = &idicts[i];
+
+ if (r_has_type(dref, t_null)) {
+ gs_ref_memory_t *mem =
+ (initial_dictionaries[i].local ?
+ iimemory_local : iimemory_global);
+ int code = dict_alloc(mem, dsize, dref);
+
+ if (code < 0)
+ return 0; /* disaster */
+ }
+ return dref;
+ }
+ }
+
+ /*
+ * Name mentioned in some op_def, but not in initial_dictionaries.
+ * Punt.
+ */
+ return 0;
+}
+
+/* Initialize objects other than operators. In particular, */
+/* initialize the dictionaries that hold operator definitions. */
+int
+obj_init(i_ctx_t **pi_ctx_p, gs_dual_memory_t *idmem)
+{
+ int level = gs_op_language_level();
+ ref system_dict;
+ i_ctx_t *i_ctx_p;
+ int code;
+
+ /*
+ * Create systemdict. The context machinery requires that
+ * we do this before initializing the interpreter.
+ */
+ code = dict_alloc(idmem->space_global,
+ (level >= 3 ? SYSTEMDICT_LL3_SIZE :
+ level >= 2 ? SYSTEMDICT_LEVEL2_SIZE : SYSTEMDICT_SIZE),
+ &system_dict);
+ if (code < 0)
+ return code;
+
+ /* Initialize the interpreter. */
+ code = gs_interp_init(pi_ctx_p, &system_dict, idmem);
+ if (code < 0)
+ return code;
+ i_ctx_p = *pi_ctx_p;
+
+ {
+#define icount countof(initial_dictionaries)
+ ref idicts[icount];
+ int i;
+ const op_def *const *tptr;
+
+ min_dstack_size = MIN_DSTACK_SIZE;
+
+ refset_null(idicts, icount);
+
+ /* Put systemdict on the dictionary stack. */
+ if (level >= 2) {
+ dsp += 2;
+ /*
+ * For the moment, let globaldict be an alias for systemdict.
+ */
+ dsp[-1] = system_dict;
+ min_dstack_size++;
+ } else {
+ ++dsp;
+ }
+ *dsp = system_dict;
+
+ /* Create dictionaries which are to be homes for operators. */
+ for (tptr = op_defs_all; *tptr != 0; tptr++) {
+ const op_def *def;
+
+ for (def = *tptr; def->oname != 0; def++)
+ if (op_def_is_begin_dict(def)) {
+ if (make_initial_dict(i_ctx_p, def->oname, idicts) == 0)
+ return_error(gs_error_VMerror);
+ }
+ }
+
+ /* Set up the initial dstack. */
+ for (i = 0; i < countof(initial_dstack); i++) {
+ const char *dname = initial_dstack[i];
+ ref *r;
+
+ ++dsp;
+ if (!strcmp(dname, "userdict"))
+ dstack_userdict_index = dsp - dsbot;
+ r = make_initial_dict(i_ctx_p, dname, idicts);
+ if (r == NULL)
+ return_error(gs_error_VMerror);
+ ref_assign(dsp, r);
+ }
+
+ /* Enter names of referenced initial dictionaries into systemdict. */
+ initial_enter_name("systemdict", systemdict);
+ for (i = 0; i < icount; i++) {
+ ref *idict = &idicts[i];
+
+ if (!r_has_type(idict, t_null)) {
+ /*
+ * Note that we enter the dictionary in systemdict
+ * even if it is in local VM. There is a special
+ * provision in the garbage collector for this:
+ * see ivmspace.h for more information.
+ * In order to do this, we must temporarily
+ * identify systemdict as local, so that the
+ * store check in dict_put won't fail.
+ */
+ uint save_space = r_space(systemdict);
+
+ r_set_space(systemdict, avm_local);
+ code = initial_enter_name(initial_dictionaries[i].name,
+ idict);
+ r_set_space(systemdict, save_space);
+ if (code < 0)
+ return code;
+ }
+ }
+#undef icount
+ }
+
+ gs_interp_reset(i_ctx_p);
+
+ {
+#ifdef PACIFY_VALGRIND
+ ref vnull = { 0 }, vtrue = { 0 }, vfalse = { 0 };
+#else
+ ref vnull, vtrue, vfalse;
+#endif
+
+ make_null(&vnull);
+ make_true(&vtrue);
+ make_false(&vfalse);
+ if ((code = initial_enter_name("null", &vnull)) < 0 ||
+ (code = initial_enter_name("true", &vtrue)) < 0 ||
+ (code = initial_enter_name("false", &vfalse)) < 0
+ )
+ return code;
+ }
+
+ /* Create the error name table */
+ {
+ int n = countof(gs_error_names) - 1;
+ int i;
+ ref era;
+
+ code = ialloc_ref_array(&era, a_readonly, n, "ErrorNames");
+ if (code < 0)
+ return code;
+ for (i = 0; i < n; i++)
+ if ((code = name_enter_string(imemory, (const char *)gs_error_names[i],
+ era.value.refs + i)) < 0)
+ return code;
+ return initial_enter_name("ErrorNames", &era);
+ }
+}
+
+/* Run the initialization procedures of the individual operator files. */
+int
+zop_init(i_ctx_t *i_ctx_p)
+{
+ const op_def *const *tptr;
+ int code;
+
+ /* Because of a bug in Sun's SC1.0 compiler, */
+ /* we have to spell out the typedef for op_def_ptr here: */
+ const op_def *def;
+
+ for (tptr = op_defs_all; *tptr != 0; tptr++) {
+ for (def = *tptr; def->oname != 0; def++)
+ DO_NOTHING;
+ if (def->proc != 0) {
+ code = def->proc(i_ctx_p);
+ if (code < 0) {
+ lprintf2("op_init proc 0x%lx returned error %d!\n",
+ (ulong)def->proc, code);
+ return code;
+ }
+ }
+ }
+
+ /* Initialize the predefined names other than operators. */
+ /* Do this here in case op_init changed any of them. */
+ {
+ ref vcr, vpr, vpf, vre, vrd;
+
+ make_const_string(&vcr, a_readonly | avm_foreign,
+ strlen(gs_copyright), (const byte *)gs_copyright);
+ make_const_string(&vpr, a_readonly | avm_foreign,
+ strlen(gs_product), (const byte *)gs_product);
+ make_const_string(&vpf, a_readonly | avm_foreign,
+ strlen(gs_productfamily),
+ (const byte *)gs_productfamily);
+ make_int(&vre, gs_revision);
+ make_int(&vrd, gs_revisiondate);
+ if ((code = initial_enter_name("copyright", &vcr)) < 0 ||
+ (code = initial_enter_name("product", &vpr)) < 0 ||
+ (code = initial_enter_name("productfamily", &vpf)) < 0 ||
+ (code = initial_enter_name("revision", &vre)) < 0 ||
+ (code = initial_enter_name("revisiondate", &vrd)) < 0)
+ return code;
+ }
+
+ return 0;
+}
+
+/* Create an op_array table. */
+static int
+alloc_op_array_table(i_ctx_t *i_ctx_p, uint size, uint space,
+ op_array_table *opt)
+{
+ uint save_space = ialloc_space(idmemory);
+ int code;
+
+ ialloc_set_space(idmemory, space);
+ code = ialloc_ref_array(&opt->table, a_readonly, size,
+ "op_array table");
+ ialloc_set_space(idmemory, save_space);
+ if (code < 0)
+ return code;
+ refset_null(opt->table.value.refs, size);
+ opt->nx_table =
+ (ushort *) ialloc_byte_array(size, sizeof(ushort),
+ "op_array nx_table");
+ if (opt->nx_table == 0)
+ return_error(gs_error_VMerror);
+ opt->count = 0;
+ opt->attrs = space | a_executable;
+ return 0;
+}
+
+/* Initialize the operator table. */
+int
+op_init(i_ctx_t *i_ctx_p)
+{
+ const op_def *const *tptr;
+ int code;
+
+ /* Enter each operator into the appropriate dictionary. */
+
+ for (tptr = op_defs_all; *tptr != 0; tptr++) {
+ ref *pdict = systemdict;
+ const op_def *def;
+ const char *nstr;
+
+ for (def = *tptr; (nstr = def->oname) != 0; def++)
+ if (op_def_is_begin_dict(def)) {
+ ref nref;
+
+ code = name_ref(imemory, (const byte *)nstr, strlen(nstr), &nref, -1);
+ if (code < 0)
+ return code;
+ if (!dict_find(systemdict, &nref, &pdict))
+ return_error(gs_error_Fatal);
+ if (!r_has_type(pdict, t_dictionary))
+ return_error(gs_error_Fatal);
+ } else {
+ ref oper;
+ uint index_in_table = def - *tptr;
+ uint opidx = (tptr - op_defs_all) * OP_DEFS_MAX_SIZE +
+ index_in_table;
+
+ if (index_in_table >= OP_DEFS_MAX_SIZE) {
+ lprintf1("opdef overrun! %s\n", def->oname);
+ return_error(gs_error_Fatal);
+ }
+ gs_interp_make_oper(&oper, def->proc, opidx);
+ /* The first character of the name is a digit */
+ /* giving the minimum acceptable number of operands. */
+ /* Check to make sure it's within bounds. */
+ if (*nstr - '0' > gs_interp_max_op_num_args)
+ return_error(gs_error_Fatal);
+ nstr++;
+ /*
+ * Skip internal operators, and the second occurrence of
+ * operators with special indices.
+ */
+ if (*nstr != '%' && r_size(&oper) == opidx) {
+ code =
+ i_initial_enter_name_in(i_ctx_p, pdict, nstr, &oper);
+ if (code < 0)
+ return code;
+ }
+ }
+ }
+ /* Allocate the tables for `operator' procedures. */
+ /* Make one of them local so we can have local operators. */
+ if ((code = alloc_op_array_table(i_ctx_p, OP_ARRAY_TABLE_GLOBAL_SIZE,
+ avm_global,
+ &i_ctx_p->op_array_table_global) < 0))
+ return code;
+ i_ctx_p->op_array_table_global.base_index = op_def_count;
+ if ((code = alloc_op_array_table(i_ctx_p, OP_ARRAY_TABLE_LOCAL_SIZE,
+ avm_local,
+ &i_ctx_p->op_array_table_local) < 0))
+ return code;
+ i_ctx_p->op_array_table_local.base_index =
+ i_ctx_p->op_array_table_global.base_index +
+ r_size(&i_ctx_p->op_array_table_global.table);
+
+ return 0;
+}
+
+#if defined(DEBUG_TRACE_PS_OPERATORS) || defined(DEBUG)
+static const char *unknown_op_name = "unknown_op";
+
+const char *
+op_get_name_string(op_proc_t opproc)
+{
+ const op_def *const *tptr;
+
+ for (tptr = op_defs_all; *tptr != 0; tptr++) {
+ const op_def *def;
+
+ for (def = *tptr; def->oname != 0; def++)
+ if (!op_def_is_begin_dict(def)) {
+ if (def->proc == opproc)
+ return def->oname;
+ }
+ }
+ return unknown_op_name;
+}
+#endif
+
+int
+i_iodev_init(i_ctx_t *i_ctx_p)
+{
+ int i;
+ int code;
+ extern init_proc(gs_iodev_init);
+
+ code = gs_iodev_init(imemory);
+
+ for (i = 0; i < i_io_device_table_count && code >= 0; i++) {
+ code = gs_iodev_register_dev(imemory, i_io_device_table[i]);
+ }
+
+ return code;
+}
diff --git a/psi/iinit.h b/psi/iinit.h
new file mode 100644
index 000000000..95c19c10c
--- /dev/null
+++ b/psi/iinit.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* (Internal) interface to iinit.c */
+
+/* The following will allow for -Z! to trace PS operators. */
+/* This is slightly less noisy but less informative than -ZI */
+/* #define DEBUG_TRACE_PS_OPERATORS */
+
+#ifndef iinit_INCLUDED
+# define iinit_INCLUDED
+
+/*
+ * Declare initialization procedures exported by iinit.c for imain.c.
+ * These must be executed in the order they are declared below.
+ */
+int obj_init(i_ctx_t **, gs_dual_memory_t *);
+int zop_init(i_ctx_t *);
+int op_init(i_ctx_t *);
+#if defined(DEBUG_TRACE_PS_OPERATORS) || defined(DEBUG)
+const char *op_get_name_string(op_proc_t opproc);
+#endif
+
+int
+i_iodev_init(i_ctx_t *i_ctx_p);
+
+/*
+ * Test whether there are any Level 2 operators in the executable.
+ * (This is different from the language level in which the interpreter is
+ * actually running: it is only tested during initialization.)
+ */
+bool gs_have_level2(void);
+
+#endif /* iinit_INCLUDED */
diff --git a/psi/ilevel.h b/psi/ilevel.h
new file mode 100644
index 000000000..e06d373a0
--- /dev/null
+++ b/psi/ilevel.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter language level interface */
+
+#ifndef ilevel_INCLUDED
+# define ilevel_INCLUDED
+
+/* The current interpreter language level */
+#define LANGUAGE_LEVEL (i_ctx_p->language_level)
+#define LL2_ENABLED (LANGUAGE_LEVEL >= 2)
+#define LL3_ENABLED (LANGUAGE_LEVEL >= 3)
+#define level2_enabled LL2_ENABLED /* backward compatibility */
+
+#endif /* ilevel_INCLUDED */
diff --git a/psi/ilocate.c b/psi/ilocate.c
new file mode 100644
index 000000000..6898f39c2
--- /dev/null
+++ b/psi/ilocate.c
@@ -0,0 +1,645 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Object locating and validating for Ghostscript memory manager */
+#include "ghost.h"
+#include "memory_.h"
+#include "ierrors.h"
+#include "gsexit.h"
+#include "gsstruct.h"
+#include "iastate.h"
+#include "idict.h"
+#include "igc.h" /* for gc_state_t and gcst_get_memory_ptr() */
+#include "igcstr.h" /* for prototype */
+#include "iname.h"
+#include "ipacked.h"
+#include "isstate.h"
+#include "iutil.h" /* for packed_get */
+#include "ivmspace.h"
+#include "store.h"
+
+static int do_validate_chunk(const chunk_t * cp, gc_state_t * gcst);
+static int do_validate_object(const obj_header_t * ptr, const chunk_t * cp,
+ gc_state_t * gcst);
+
+
+/* ================ Locating ================ */
+
+/* Locate a pointer in the chunks of a space being collected. */
+/* This is only used for string garbage collection and for debugging. */
+chunk_t *
+gc_locate(const void *ptr, gc_state_t * gcst)
+{
+ const gs_ref_memory_t *mem;
+ const gs_ref_memory_t *other;
+
+ if (chunk_locate(ptr, &gcst->loc))
+ return gcst->loc.cp;
+ mem = gcst->loc.memory;
+
+ /*
+ * Try the stable allocator of this space, or, if the current memory
+ * is the stable one, the non-stable allocator of this space.
+ */
+
+ if ((other = (const gs_ref_memory_t *)mem->stable_memory) != mem ||
+ (other = gcst->spaces_indexed[mem->space >> r_space_shift]) != mem
+ ) {
+ gcst->loc.memory = other;
+ gcst->loc.cp = 0;
+ if (chunk_locate(ptr, &gcst->loc))
+ return gcst->loc.cp;
+ }
+
+ /*
+ * Try the other space, if there is one, including its stable allocator
+ * and all save levels. (If the original space is system space, try
+ * local space.)
+ */
+
+ if (gcst->space_local != gcst->space_global) {
+ gcst->loc.memory = other =
+ (mem->space == avm_local ? gcst->space_global : gcst->space_local);
+ gcst->loc.cp = 0;
+ if (chunk_locate(ptr, &gcst->loc))
+ return gcst->loc.cp;
+ /* Try its stable allocator. */
+ if (other->stable_memory != (const gs_memory_t *)other) {
+ gcst->loc.memory = (gs_ref_memory_t *)other->stable_memory;
+ gcst->loc.cp = 0;
+ if (chunk_locate(ptr, &gcst->loc))
+ return gcst->loc.cp;
+ gcst->loc.memory = other;
+ }
+ /* Try other save levels of this space. */
+ while (gcst->loc.memory->saved != 0) {
+ gcst->loc.memory = &gcst->loc.memory->saved->state;
+ gcst->loc.cp = 0;
+ if (chunk_locate(ptr, &gcst->loc))
+ return gcst->loc.cp;
+ }
+ }
+
+ /*
+ * Try system space. This is simpler because it isn't subject to
+ * save/restore and doesn't have a separate stable allocator.
+ */
+
+ if (mem != gcst->space_system) {
+ gcst->loc.memory = gcst->space_system;
+ gcst->loc.cp = 0;
+ if (chunk_locate(ptr, &gcst->loc))
+ return gcst->loc.cp;
+ }
+
+ /*
+ * Try other save levels of the initial space, or of global space if the
+ * original space was system space. In the latter case, try all
+ * levels, and its stable allocator.
+ */
+
+ switch (mem->space) {
+ default: /* system */
+ other = gcst->space_global;
+ if (other->stable_memory != (const gs_memory_t *)other) {
+ gcst->loc.memory = (gs_ref_memory_t *)other->stable_memory;
+ gcst->loc.cp = 0;
+ if (chunk_locate(ptr, &gcst->loc))
+ return gcst->loc.cp;
+ }
+ gcst->loc.memory = other;
+ break;
+ case avm_global:
+ gcst->loc.memory = gcst->space_global;
+ break;
+ case avm_local:
+ gcst->loc.memory = gcst->space_local;
+ break;
+ }
+ for (;;) {
+ if (gcst->loc.memory != mem) { /* don't do twice */
+ gcst->loc.cp = 0;
+ if (chunk_locate(ptr, &gcst->loc))
+ return gcst->loc.cp;
+ }
+ if (gcst->loc.memory->saved == 0)
+ break;
+ gcst->loc.memory = &gcst->loc.memory->saved->state;
+ }
+
+ /* Restore locator to a legal state and report failure. */
+
+ gcst->loc.memory = mem;
+ gcst->loc.cp = 0;
+ return 0;
+}
+
+/* ================ Debugging ================ */
+
+#ifdef DEBUG
+
+/* Define the structure for temporarily saving allocator state. */
+typedef struct alloc_temp_save_s {
+ chunk_t cc;
+ uint rsize;
+ ref rlast;
+} alloc_temp_save_t;
+/* Temporarily save the state of an allocator. */
+static void
+alloc_temp_save(alloc_temp_save_t *pats, gs_ref_memory_t *mem)
+{
+ chunk_t *pcc = mem->pcc;
+ obj_header_t *rcur = mem->cc.rcur;
+
+ if (pcc != 0) {
+ pats->cc = *pcc;
+ *pcc = mem->cc;
+ }
+ if (rcur != 0) {
+ pats->rsize = rcur[-1].o_size;
+ rcur[-1].o_size = mem->cc.rtop - (byte *) rcur;
+ /* Create the final ref, reserved for the GC. */
+ pats->rlast = ((ref *) mem->cc.rtop)[-1];
+ make_mark((ref *) mem->cc.rtop - 1);
+ }
+}
+/* Restore the temporarily saved state. */
+static void
+alloc_temp_restore(alloc_temp_save_t *pats, gs_ref_memory_t *mem)
+{
+ chunk_t *pcc = mem->pcc;
+ obj_header_t *rcur = mem->cc.rcur;
+
+ if (rcur != 0) {
+ rcur[-1].o_size = pats->rsize;
+ ((ref *) mem->cc.rtop)[-1] = pats->rlast;
+ }
+ if (pcc != 0)
+ *pcc = pats->cc;
+}
+
+/* Validate the contents of an allocator. */
+void
+ialloc_validate_spaces(const gs_dual_memory_t * dmem)
+{
+ int i;
+ gc_state_t state;
+ alloc_temp_save_t
+ save[countof(dmem->spaces_indexed)],
+ save_stable[countof(dmem->spaces_indexed)];
+ gs_ref_memory_t *mem;
+
+ state.spaces = dmem->spaces;
+ state.loc.memory = state.space_local;
+ state.loc.cp = 0;
+ state.heap = dmem->current->non_gc_memory; /* valid 'heap' needed for printing */
+
+ /* Save everything we need to reset temporarily. */
+
+ for (i = 0; i < countof(save); i++)
+ if ((mem = dmem->spaces_indexed[i]) != 0) {
+ alloc_temp_save(&save[i], mem);
+ if (mem->stable_memory != (gs_memory_t *)mem)
+ alloc_temp_save(&save_stable[i],
+ (gs_ref_memory_t *)mem->stable_memory);
+ }
+
+ /* Validate memory. */
+
+ for (i = 0; i < countof(save); i++)
+ if ((mem = dmem->spaces_indexed[i]) != 0) {
+ ialloc_validate_memory(mem, &state);
+ if (mem->stable_memory != (gs_memory_t *)mem)
+ ialloc_validate_memory((gs_ref_memory_t *)mem->stable_memory,
+ &state);
+ }
+
+ /* Undo temporary changes. */
+
+ for (i = 0; i < countof(save); i++)
+ if ((mem = dmem->spaces_indexed[i]) != 0) {
+ if (mem->stable_memory != (gs_memory_t *)mem)
+ alloc_temp_restore(&save_stable[i],
+ (gs_ref_memory_t *)mem->stable_memory);
+ alloc_temp_restore(&save[i], mem);
+ }
+}
+void
+ialloc_validate_memory(const gs_ref_memory_t * mem, gc_state_t * gcst)
+{
+ const gs_ref_memory_t *smem;
+ int level;
+
+ for (smem = mem, level = 0; smem != 0;
+ smem = &smem->saved->state, --level
+ ) {
+ const chunk_t *cp;
+ int i;
+
+ if_debug3m('6', (gs_memory_t *)mem, "[6]validating memory 0x%lx, space %d, level %d\n",
+ (ulong) mem, mem->space, level);
+ /* Validate chunks. */
+ for (cp = smem->cfirst; cp != 0; cp = cp->cnext)
+ if (do_validate_chunk(cp, gcst)) {
+ mlprintf3((gs_memory_t *)mem, "while validating memory 0x%lx, space %d, level %d\n",
+ (ulong) mem, mem->space, level);
+ gs_abort(gcst->heap);
+ }
+ /* Validate freelists. */
+ for (i = 0; i < num_freelists; ++i) {
+ uint free_size = i << log2_obj_align_mod;
+ const obj_header_t *pfree;
+
+ for (pfree = mem->freelists[i]; pfree != 0;
+ pfree = *(const obj_header_t * const *)pfree
+ ) {
+ uint size = pfree[-1].o_size;
+
+ if (pfree[-1].o_type != &st_free) {
+ mlprintf3((gs_memory_t *)mem, "Non-free object 0x%lx(%u) on freelist %i!\n",
+ (ulong) pfree, size, i);
+ break;
+ }
+ if ((i == LARGE_FREELIST_INDEX && size < max_freelist_size) ||
+ (i != LARGE_FREELIST_INDEX &&
+ (size < free_size - obj_align_mask || size > free_size))) {
+ mlprintf3((gs_memory_t *)mem, "Object 0x%lx(%u) size wrong on freelist %i!\n",
+ (ulong) pfree, size, i);
+ break;
+ }
+ }
+ }
+ };
+}
+
+/* Check the validity of an object's size. */
+static inline bool
+object_size_valid(const obj_header_t * pre, uint size, const chunk_t * cp)
+{
+ return (pre->o_alone ? (const byte *)pre == cp->cbase :
+ size <= cp->ctop - (const byte *)(pre + 1));
+}
+
+/* Validate all the objects in a chunk. */
+#if IGC_PTR_STABILITY_CHECK
+void ialloc_validate_pointer_stability(const obj_header_t * ptr_from,
+ const obj_header_t * ptr_to);
+static int ialloc_validate_ref(const ref *, gc_state_t *, const obj_header_t *pre_fr);
+static int ialloc_validate_ref_packed(const ref_packed *, gc_state_t *, const obj_header_t *pre_fr);
+#else
+static int ialloc_validate_ref(const ref *, gc_state_t *);
+static int ialloc_validate_ref_packed(const ref_packed *, gc_state_t *);
+#endif
+static int
+do_validate_chunk(const chunk_t * cp, gc_state_t * gcst)
+{
+ int ret = 0;
+
+ if_debug_chunk('6', gcst->heap, "[6]validating chunk", cp);
+ SCAN_CHUNK_OBJECTS(cp);
+ DO_ALL
+ if (pre->o_type == &st_free) {
+ if (!object_size_valid(pre, size, cp)) {
+ lprintf3("Bad free object 0x%lx(%lu), in chunk 0x%lx!\n",
+ (ulong) (pre + 1), (ulong) size, (ulong) cp);
+ return 1;
+ }
+ } else if (do_validate_object(pre + 1, cp, gcst)) {
+ dmprintf_chunk(gcst->heap, "while validating chunk", cp);
+ return 1;
+ }
+ if_debug3m('7', gcst->heap, " [7]validating %s(%lu) 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong) size, (ulong) pre);
+ if (pre->o_type == &st_refs) {
+ const ref_packed *rp = (const ref_packed *)(pre + 1);
+ const char *end = (const char *)rp + size;
+
+ while ((const char *)rp < end) {
+# if IGC_PTR_STABILITY_CHECK
+ ret = ialloc_validate_ref_packed(rp, gcst, pre);
+# else
+ ret = ialloc_validate_ref_packed(rp, gcst);
+# endif
+ if (ret) {
+ mlprintf3(gcst->heap, "while validating %s(%lu) 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong) size, (ulong) pre);
+ dmprintf_chunk(gcst->heap, "in chunk", cp);
+ return ret;
+ }
+ rp = packed_next(rp);
+ }
+ } else {
+ struct_proc_enum_ptrs((*proc)) = pre->o_type->enum_ptrs;
+ uint index = 0;
+ enum_ptr_t eptr;
+ gs_ptr_type_t ptype;
+
+ if (proc != gs_no_struct_enum_ptrs)
+ for (; (ptype = (*proc) (gcst_get_memory_ptr(gcst),
+ pre + 1, size, index, &eptr,
+ pre->o_type, gcst)) != 0; ++index) {
+ if (eptr.ptr == 0)
+ DO_NOTHING;
+ /* NB check other types ptr_string_type, etc. */
+ else if (ptype == ptr_struct_type) {
+ ret = do_validate_object(eptr.ptr, NULL, gcst);
+# if IGC_PTR_STABILITY_CHECK
+ ialloc_validate_pointer_stability(pre,
+ (const obj_header_t *)eptr.ptr - 1);
+# endif
+ } else if (ptype == ptr_ref_type)
+# if IGC_PTR_STABILITY_CHECK
+ ret = ialloc_validate_ref_packed(eptr.ptr, gcst, pre);
+# else
+ ret = ialloc_validate_ref_packed(eptr.ptr, gcst);
+# endif
+ if (ret) {
+ dmprintf_chunk(gcst->heap, "while validating chunk", cp);
+ return ret;
+ }
+ }
+ }
+ END_OBJECTS_SCAN
+ return ret;
+}
+
+void
+ialloc_validate_chunk(const chunk_t * cp, gc_state_t * gcst)
+{
+ if (do_validate_chunk(cp, gcst))
+ gs_abort(gcst->heap);
+}
+
+/* Validate a ref. */
+#if IGC_PTR_STABILITY_CHECK
+static int
+ialloc_validate_ref_packed(const ref_packed * rp, gc_state_t * gcst, const obj_header_t *pre_fr)
+{
+ const gs_memory_t *cmem = gcst->spaces.memories.named.system->stable_memory;
+
+ if (r_is_packed(rp)) {
+ ref unpacked;
+
+ packed_get(cmem, rp, &unpacked);
+ return ialloc_validate_ref(&unpacked, gcst, pre_fr);
+ } else {
+ return ialloc_validate_ref((const ref *)rp, gcst, pre_fr);
+ }
+}
+#else
+static int
+ialloc_validate_ref_packed(const ref_packed * rp, gc_state_t * gcst)
+{
+ const gs_memory_t *cmem = gcst->spaces.memories.named.system->stable_memory;
+
+ if (r_is_packed(rp)) {
+ ref unpacked;
+
+ packed_get(cmem, rp, &unpacked);
+ return ialloc_validate_ref(&unpacked, gcst);
+ } else {
+ return ialloc_validate_ref((const ref *)rp, gcst);
+ }
+}
+#endif
+static int
+ialloc_validate_ref(const ref * pref, gc_state_t * gcst
+# if IGC_PTR_STABILITY_CHECK
+ , const obj_header_t *pre_fr
+# endif
+ )
+{
+ const void *optr;
+ const ref *rptr;
+ const char *tname;
+ uint size;
+ const gs_memory_t *cmem = gcst->spaces.memories.named.system->stable_memory;
+ int ret = 0;
+
+ if (!gs_debug_c('?'))
+ return 0; /* no check */
+ if (r_space(pref) == avm_foreign)
+ return 0;
+ switch (r_type(pref)) {
+ case t_file:
+ optr = pref->value.pfile;
+ goto cks;
+ case t_device:
+ optr = pref->value.pdevice;
+ goto cks;
+ case t_fontID:
+ case t_struct:
+ case t_astruct:
+ optr = pref->value.pstruct;
+cks: if (optr != 0) {
+ ret = do_validate_object(optr, NULL, gcst);
+# if IGC_PTR_STABILITY_CHECK
+ ialloc_validate_pointer_stability(pre_fr,
+ (const obj_header_t *)optr - 1);
+# endif
+ if (ret) {
+ lprintf1("while validating 0x%"PRIx64" (fontID/struct/astruct)\n",
+ (uint64_t)pref);
+ return ret;
+ }
+ }
+ break;
+ case t_name:
+ if (name_index_ptr(cmem, name_index(cmem, pref)) != pref->value.pname) {
+ lprintf3("At 0x%lx, bad name %u, pname = 0x%lx\n",
+ (ulong) pref, (uint)name_index(cmem, pref),
+ (ulong) pref->value.pname);
+ ret = 1;
+ break;
+ } {
+ ref sref;
+
+ name_string_ref(cmem, pref, &sref);
+ if (r_space(&sref) != avm_foreign &&
+ !gc_locate(sref.value.const_bytes, gcst)
+ ) {
+ lprintf4("At 0x%lx, bad name %u, pname = 0x%lx, string 0x%lx not in any chunk\n",
+ (ulong) pref, (uint) r_size(pref),
+ (ulong) pref->value.pname,
+ (ulong) sref.value.const_bytes);
+ ret = 1;
+ }
+ }
+ break;
+ case t_string:
+ if (r_size(pref) != 0 && !gc_locate(pref->value.bytes, gcst)) {
+ lprintf3("At 0x%lx, string ptr 0x%lx[%u] not in any chunk\n",
+ (ulong) pref, (ulong) pref->value.bytes,
+ (uint) r_size(pref));
+ ret = 1;
+ }
+ break;
+ case t_array:
+ if (r_size(pref) == 0)
+ break;
+ rptr = pref->value.refs;
+ size = r_size(pref);
+ tname = "array";
+cka: if (!gc_locate(rptr, gcst)) {
+ lprintf3("At 0x%lx, %s 0x%lx not in any chunk\n",
+ (ulong) pref, tname, (ulong) rptr);
+ ret = 1;
+ break;
+ } {
+ uint i;
+
+ for (i = 0; i < size; ++i) {
+ const ref *elt = rptr + i;
+
+ if (r_is_packed(elt)) {
+ lprintf5("At 0x%lx, %s 0x%lx[%u] element %u is not a ref\n",
+ (ulong) pref, tname, (ulong) rptr, size, i);
+ ret = 1;
+ }
+ }
+ }
+ break;
+ case t_shortarray:
+ case t_mixedarray:
+ if (r_size(pref) == 0)
+ break;
+ optr = pref->value.packed;
+ if (!gc_locate(optr, gcst)) {
+ lprintf2("At 0x%lx, packed array 0x%lx not in any chunk\n",
+ (ulong) pref, (ulong) optr);
+ ret = 1;
+ }
+ break;
+ case t_dictionary:
+ {
+ const dict *pdict = pref->value.pdict;
+
+ if (!r_has_type(&pdict->values, t_array) ||
+ !r_is_array(&pdict->keys) ||
+ !r_has_type(&pdict->count, t_integer) ||
+ !r_has_type(&pdict->maxlength, t_integer)
+ ) {
+ lprintf2("At 0x%lx, invalid dict 0x%lx\n",
+ (ulong) pref, (ulong) pdict);
+ ret = 1;
+ }
+ rptr = (const ref *)pdict;
+ }
+ size = sizeof(dict) / sizeof(ref);
+ tname = "dict";
+ goto cka;
+ }
+ return ret;
+}
+
+#if IGC_PTR_STABILITY_CHECK
+/* Validate an pointer stability. */
+void
+ialloc_validate_pointer_stability(const obj_header_t * ptr_fr,
+ const obj_header_t * ptr_to)
+{
+ static const char *sn[] = {"undef", "undef", "system", "undef",
+ "global_stable", "global", "local_stable", "local"};
+
+ if (ptr_fr->d.o.space_id < ptr_to->d.o.space_id) {
+ const char *sn_fr = (ptr_fr->d.o.space_id < count_of(sn)
+ ? sn[ptr_fr->d.o.space_id] : "unknown");
+ const char *sn_to = (ptr_to->d.o.space_id < count_of(sn)
+ ? sn[ptr_to->d.o.space_id] : "unknown");
+
+ lprintf6("Reference to a less stable object 0x%lx<%s> "
+ "in the space \'%s\' from 0x%lx<%s> in the space \'%s\' !\n",
+ (ulong) ptr_to, ptr_to->d.o.t.type->sname, sn_to,
+ (ulong) ptr_fr, ptr_fr->d.o.t.type->sname, sn_fr);
+ }
+}
+#endif
+
+/* Validate an object. */
+static int
+do_validate_object(const obj_header_t * ptr, const chunk_t * cp,
+ gc_state_t * gcst)
+{
+ const obj_header_t *pre = ptr - 1;
+ ulong size = pre_obj_contents_size(pre);
+ gs_memory_type_ptr_t otype = pre->o_type;
+ const char *oname;
+
+ if (!gs_debug_c('?'))
+ return 0; /* no check */
+ if (cp == 0 && gcst != 0) {
+ gc_state_t st;
+
+ st = *gcst; /* no side effects! */
+ if (!(cp = gc_locate(pre, &st))) {
+ mlprintf1(gcst->heap, "Object 0x%lx not in any chunk!\n",
+ (ulong) ptr);
+ return 1; /*gs_abort(); */
+ }
+ }
+ if (otype == &st_free) {
+ mlprintf3(gcst->heap, "Reference to free object 0x%lx(%lu), in chunk 0x%lx!\n",
+ (ulong) ptr, (ulong) size, (ulong) cp);
+ return 1;
+ }
+ if ((cp != 0 && !object_size_valid(pre, size, cp)) ||
+ otype->ssize == 0 ||
+ size % otype->ssize != 0 ||
+ (oname = struct_type_name_string(otype),
+ *oname < 33 || *oname > 126)
+ ) {
+ mlprintf2(gcst->heap, "Bad object 0x%lx(%lu),\n",
+ (ulong) ptr, (ulong) size);
+ dmprintf2(gcst->heap, " ssize = %u, in chunk 0x%lx!\n",
+ otype->ssize, (ulong) cp);
+ return 1;
+ }
+ return 0;
+}
+
+void
+ialloc_validate_object(const obj_header_t * ptr, const chunk_t * cp,
+ gc_state_t * gcst)
+{
+ if (do_validate_object(ptr, cp, gcst))
+ gs_abort(gcst->heap);
+}
+#else /* !DEBUG */
+
+void
+ialloc_validate_spaces(const gs_dual_memory_t * dmem)
+{
+}
+
+void
+ialloc_validate_memory(const gs_ref_memory_t * mem, gc_state_t * gcst)
+{
+}
+
+void
+ialloc_validate_chunk(const chunk_t * cp, gc_state_t * gcst)
+{
+}
+
+void
+ialloc_validate_object(const obj_header_t * ptr, const chunk_t * cp,
+ gc_state_t * gcst)
+{
+}
+
+#endif /* (!)DEBUG */
diff --git a/psi/imain.c b/psi/imain.c
new file mode 100644
index 000000000..7dddfcf1d
--- /dev/null
+++ b/psi/imain.c
@@ -0,0 +1,1063 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Common support for interpreter front ends */
+
+
+#include "malloc_.h"
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "gscdefs.h" /* for gs_init_file */
+#include "gslib.h"
+#include "gsmatrix.h" /* for gxdevice.h */
+#include "gsutil.h" /* for bytes_compare */
+#include "gspaint.h" /* for gs_erasepage */
+#include "gxdevice.h"
+#include "gxdevsop.h" /* for gxdso_* enums */
+#include "gxclpage.h"
+#include "gdevprn.h"
+#include "gxalloc.h"
+#include "gxiodev.h" /* for iodev struct */
+#include "gzstate.h"
+#include "ierrors.h"
+#include "oper.h"
+#include "iconf.h" /* for gs_init_* imports */
+#include "idebug.h"
+#include "idict.h"
+#include "iname.h" /* for name_init */
+#include "dstack.h"
+#include "estack.h"
+#include "ostack.h" /* put here for files.h */
+#include "stream.h" /* for files.h */
+#include "files.h"
+#include "ialloc.h"
+#include "iinit.h"
+#include "strimpl.h" /* for sfilter.h */
+#include "sfilter.h" /* for iscan.h */
+#include "iscan.h"
+#include "main.h"
+#include "store.h"
+#include "isave.h" /* for prototypes */
+#include "interp.h"
+#include "ivmspace.h"
+#include "idisp.h" /* for setting display device callback */
+#include "iplugin.h"
+
+#ifdef PACIFY_VALGRIND
+#include "valgrind.h"
+#endif
+
+/* ------ Exported data ------ */
+
+/** using backpointers retrieve minst from any memory pointer
+ *
+ */
+gs_main_instance*
+get_minst_from_memory(const gs_memory_t *mem)
+{
+#ifdef PSI_INCLUDED
+ extern gs_main_instance *ps_impl_get_minst( const gs_memory_t *mem );
+ return ps_impl_get_minst(mem);
+#else
+ return (gs_main_instance*)mem->gs_lib_ctx->top_of_system;
+#endif
+}
+
+/** construct main instance caller needs to retain */
+gs_main_instance *
+gs_main_alloc_instance(gs_memory_t *mem)
+{
+ gs_main_instance *minst;
+ if (mem == NULL)
+ return NULL;
+
+ minst = (gs_main_instance *)gs_alloc_bytes_immovable(mem,
+ sizeof(gs_main_instance),
+ "init_main_instance");
+ if (minst == NULL)
+ return NULL;
+ memcpy(minst, &gs_main_instance_init_values, sizeof(gs_main_instance_init_values));
+ minst->heap = mem;
+
+# ifndef PSI_INCLUDED
+ mem->gs_lib_ctx->top_of_system = minst;
+ /* else top of system is pl_universe */
+# endif
+
+ return minst;
+}
+
+op_array_table *
+get_op_array(const gs_memory_t *mem, int size)
+{
+ gs_main_instance *minst = get_minst_from_memory(mem);
+ return op_index_op_array_table(minst->i_ctx_p,size);
+}
+
+op_array_table *
+get_global_op_array(const gs_memory_t *mem)
+{
+ gs_main_instance *minst = get_minst_from_memory(mem);
+ return &minst->i_ctx_p->op_array_table_global;
+}
+
+op_array_table *
+get_local_op_array(const gs_memory_t *mem)
+{
+ gs_main_instance *minst = get_minst_from_memory(mem);
+ return &minst->i_ctx_p->op_array_table_local;
+}
+
+/* ------ Forward references ------ */
+
+static int gs_run_init_file(gs_main_instance *, int *, ref *);
+void print_resource_usage(const gs_main_instance *,
+ gs_dual_memory_t *, const char *);
+
+/* ------ Initialization ------ */
+
+/* Initialization to be done before anything else. */
+int
+gs_main_init0(gs_main_instance * minst, FILE * in, FILE * out, FILE * err,
+ int max_lib_paths)
+{
+ ref *paths;
+ ref *array;
+
+ /* Do platform-dependent initialization. */
+ /* We have to do this as the very first thing, */
+ /* because it detects attempts to run 80N86 executables (N>0) */
+ /* on incompatible processors. */
+ gp_init();
+
+ /* Initialize the imager. */
+# ifndef PSI_INCLUDED
+ /* Reset debugging flags */
+#ifdef PACIFY_VALGRIND
+ VALGRIND_HG_DISABLE_CHECKING(gs_debug, 128);
+#endif
+ memset(gs_debug, 0, 128);
+ gs_log_errors = 0; /* gs_debug['#'] = 0 */
+# else
+ /* plmain settings remain in effect */
+# endif
+ gp_get_usertime(minst->base_time);
+
+ /* Initialize the file search paths. */
+ paths = (ref *) gs_alloc_byte_array(minst->heap, max_lib_paths, sizeof(ref),
+ "lib_path array");
+ if (paths == 0) {
+ gs_lib_finit(1, gs_error_VMerror, minst->heap);
+ return_error(gs_error_VMerror);
+ }
+ array = (ref *) gs_alloc_byte_array(minst->heap, max_lib_paths, sizeof(ref),
+ "lib_path array");
+ if (array == 0) {
+ gs_lib_finit(1, gs_error_VMerror, minst->heap);
+ return_error(gs_error_VMerror);
+ }
+ make_array(&minst->lib_path.container, avm_foreign, max_lib_paths,
+ array);
+ make_array(&minst->lib_path.list, avm_foreign | a_readonly, 0,
+ minst->lib_path.container.value.refs);
+ minst->lib_path.env = 0;
+ minst->lib_path.final = 0;
+ minst->lib_path.count = 0;
+ minst->user_errors = 1;
+ minst->init_done = 0;
+ return 0;
+}
+
+/* Initialization to be done before constructing any objects. */
+int
+gs_main_init1(gs_main_instance * minst)
+{
+ i_ctx_t *i_ctx_p;
+
+ if (minst->init_done < 1) {
+ gs_dual_memory_t idmem;
+ int code =
+ ialloc_init(&idmem, minst->heap,
+ minst->memory_chunk_size, gs_have_level2());
+
+ if (code < 0)
+ return code;
+ code = gs_lib_init1((gs_memory_t *)idmem.space_system);
+ if (code < 0)
+ return code;
+ alloc_save_init(&idmem);
+ {
+ gs_memory_t *mem = (gs_memory_t *)idmem.space_system;
+ name_table *nt = names_init(minst->name_table_size,
+ idmem.space_system);
+
+ if (nt == 0)
+ return_error(gs_error_VMerror);
+ mem->gs_lib_ctx->gs_name_table = nt;
+ code = gs_register_struct_root(mem, NULL,
+ (void **)&mem->gs_lib_ctx->gs_name_table,
+ "the_gs_name_table");
+ if (code < 0)
+ return code;
+ }
+ code = obj_init(&minst->i_ctx_p, &idmem); /* requires name_init */
+ if (code < 0)
+ return code;
+ code = i_plugin_init(minst->i_ctx_p);
+ if (code < 0)
+ return code;
+ i_ctx_p = minst->i_ctx_p;
+ code = i_iodev_init(minst->i_ctx_p);
+ if (code < 0)
+ return code;
+ minst->init_done = 1;
+ }
+ return 0;
+}
+
+/*
+ * Invoke the interpreter. This layer doesn't do much (previously stdio
+ * callouts were handled here instead of in the stream processing.
+ */
+static int
+gs_main_interpret(gs_main_instance *minst, ref * pref, int user_errors,
+ int *pexit_code, ref * perror_object)
+{
+ int code;
+
+ /* set interpreter pointer to lib_path */
+ minst->i_ctx_p->lib_path = &minst->lib_path;
+
+ code = gs_interpret(&minst->i_ctx_p, pref,
+ user_errors, pexit_code, perror_object);
+ return code;
+}
+
+/* gcc wants prototypes for all external functions. */
+int gs_main_init2aux(gs_main_instance * minst);
+
+static const op_array_table empty_table = { { { 0 } } };
+
+/* This is an external function to work around */
+/* a bug in gcc 4.5.1 optimizer. See bug 692684. */
+int gs_main_init2aux(gs_main_instance * minst) {
+ i_ctx_t * i_ctx_p = minst->i_ctx_p;
+
+ if (minst->init_done < 2) {
+ int code, exit_code;
+ ref error_object, ifa;
+
+ /* Set up enough so that we can safely be garbage collected */
+ i_ctx_p->op_array_table_global = empty_table;
+ i_ctx_p->op_array_table_local = empty_table;
+
+ code = zop_init(i_ctx_p);
+ if (code < 0)
+ return code;
+ code = op_init(i_ctx_p); /* requires obj_init */
+ if (code < 0)
+ return code;
+
+ /* Set up the array of additional initialization files. */
+ make_const_string(&ifa, a_readonly | avm_foreign, gs_init_files_sizeof - 2, gs_init_files);
+ code = initial_enter_name("INITFILES", &ifa);
+ if (code < 0)
+ return code;
+
+ /* Set up the array of emulator names. */
+ make_const_string(&ifa, a_readonly | avm_foreign, gs_emulators_sizeof - 2, gs_emulators);
+ code = initial_enter_name("EMULATORS", &ifa);
+ if (code < 0)
+ return code;
+
+ /* Pass the search path. */
+ code = initial_enter_name("LIBPATH", &minst->lib_path.list);
+ if (code < 0)
+ return code;
+
+ /* Execute the standard initialization file. */
+ code = gs_run_init_file(minst, &exit_code, &error_object);
+ if (code < 0)
+ return code;
+ minst->init_done = 2;
+ /* NB this is to be done with device parameters
+ * both minst->display and display_set_callback() are going away
+ */
+ if (minst->display)
+ if ((code = display_set_callback(minst, minst->display)) < 0)
+ return code;
+
+#ifndef PSI_INCLUDED
+ if ((code = gs_main_run_string(minst,
+ "JOBSERVER "
+ " { false 0 .startnewjob } "
+ " { NOOUTERSAVE not { save pop } if } "
+ "ifelse", 0, &exit_code,
+ &error_object)) < 0)
+ return code;
+#endif /* PSI_INCLUDED */
+ }
+ return 0;
+}
+
+int
+gs_main_init2(gs_main_instance * minst)
+{
+ i_ctx_t *i_ctx_p;
+ int code = gs_main_init1(minst);
+ int initial_init_level = minst->init_done;
+
+ if (code < 0)
+ return code;
+ i_ctx_p = minst->i_ctx_p;
+ code = gs_main_init2aux(minst);
+ if (code < 0)
+ return code;
+ i_ctx_p = minst->i_ctx_p; /* display_set_callback or run_string may change it */
+
+ /* Now process the initial saved-pages=... argument, if any as well as saved-pages-test */
+ if (initial_init_level < 2) {
+ gx_device *pdev = gs_currentdevice(minst->i_ctx_p->pgs); /* get the current device */
+ gx_device_printer *ppdev = (gx_device_printer *)pdev;
+
+ if (minst->saved_pages_test_mode) {
+ if ((dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_saved_pages, NULL, 0) == 0)) {
+ /* no warning or error if saved-pages-test mode is used, just disable it */
+ minst->saved_pages_test_mode = false; /* device doesn't support it */
+ } else {
+ if ((code = gx_saved_pages_param_process(ppdev, (byte *)"begin", 5)) < 0)
+ return code;
+ if (code > 0)
+ if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0)
+ return code;
+ }
+ } else if (minst->saved_pages_initial_arg != NULL) {
+ if (dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_saved_pages, NULL, 0) == 0) {
+ outprintf(minst->heap,
+ " --saved-pages not supported by the '%s' device.\n",
+ pdev->dname);
+ return gs_error_Fatal;
+ }
+ code = gx_saved_pages_param_process(ppdev, minst->saved_pages_initial_arg,
+ strlen(minst->saved_pages_initial_arg));
+ if (code > 0)
+ if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0)
+ return code;
+ }
+ }
+
+ if (gs_debug_c(':'))
+ print_resource_usage(minst, &gs_imemory, "Start");
+ gp_readline_init(&minst->readline_data, imemory_system);
+ return 0;
+}
+
+/* ------ Search paths ------ */
+
+#define LIB_PATH_EXTEND 5
+
+/* If the existing array is full, extend it */
+static int
+extend_path_list_container (gs_main_instance * minst, gs_file_path * pfp)
+{
+ uint len = r_size(&minst->lib_path.container);
+ ref *paths, *opaths = minst->lib_path.container.value.refs;
+
+ /* Add 5 entries at a time to reduce VM thrashing */
+ paths = (ref *) gs_alloc_byte_array(minst->heap, len + LIB_PATH_EXTEND, sizeof(ref),
+ "extend_path_list_container array");
+
+ if (paths == 0) {
+ return_error(gs_error_VMerror);
+ }
+ make_array(&minst->lib_path.container, avm_foreign, len + LIB_PATH_EXTEND, paths);
+ make_array(&minst->lib_path.list, avm_foreign | a_readonly, 0,
+ minst->lib_path.container.value.refs);
+
+ memcpy(paths, opaths, len * sizeof(ref));
+ r_set_size(&minst->lib_path.list, len);
+
+ gs_free_object (minst->heap, opaths, "extend_path_list_container");
+ return(0);
+}
+
+/* Internal routine to add a set of directories to a search list. */
+/* Returns 0 or an error code. */
+
+static int
+file_path_add(gs_main_instance * minst, gs_file_path * pfp, const char *dirs)
+{
+ uint len = r_size(&pfp->list);
+ const char *dpath = dirs;
+ int code;
+
+ if (dirs == 0)
+ return 0;
+ for (;;) { /* Find the end of the next directory name. */
+ const char *npath = dpath;
+
+ while (*npath != 0 && *npath != gp_file_name_list_separator)
+ npath++;
+ if (npath > dpath) {
+ if (len == r_size(&pfp->container)) {
+ code = extend_path_list_container(minst, pfp);
+ if (code < 0) {
+ emprintf(minst->heap, "\nAdding path to search paths failed.\n");
+ return(code);
+ }
+ }
+ make_const_string(&pfp->container.value.refs[len],
+ avm_foreign | a_readonly,
+ npath - dpath, (const byte *)dpath);
+ ++len;
+ }
+ if (!*npath)
+ break;
+ dpath = npath + 1;
+ }
+ r_set_size(&pfp->list, len);
+ return 0;
+}
+
+/* Add a library search path to the list. */
+int
+gs_main_add_lib_path(gs_main_instance * minst, const char *lpath)
+{
+ /* Account for the possibility that the first element */
+ /* is gp_current_directory name added by set_lib_paths. */
+ int first_is_here =
+ (r_size(&minst->lib_path.list) != 0 &&
+ minst->lib_path.container.value.refs[0].value.bytes ==
+ (const byte *)gp_current_directory_name ? 1 : 0);
+ int code;
+
+ r_set_size(&minst->lib_path.list, minst->lib_path.count +
+ first_is_here);
+ code = file_path_add(minst, &minst->lib_path, lpath);
+ minst->lib_path.count = r_size(&minst->lib_path.list) - first_is_here;
+ if (code < 0)
+ return code;
+ return gs_main_set_lib_paths(minst);
+}
+
+/* ------ Execution ------ */
+
+extern_gx_io_device_table();
+
+/* Complete the list of library search paths. */
+/* This may involve adding the %rom%Resource/Init and %rom%lib/ paths (for COMPILE_INITS) */
+/* and adding or removing the current directory as the first element (for -P and -P-). */
+int
+gs_main_set_lib_paths(gs_main_instance * minst)
+{
+ ref *paths = minst->lib_path.container.value.refs;
+ int first_is_here =
+ (r_size(&minst->lib_path.list) != 0 &&
+ paths[0].value.bytes == (const byte *)gp_current_directory_name ? 1 : 0);
+ int code = 0;
+ int count = minst->lib_path.count;
+ int i, have_rom_device = 0;
+
+ if (minst->search_here_first) {
+ if (!(first_is_here ||
+ (r_size(&minst->lib_path.list) != 0 &&
+ !bytes_compare((const byte *)gp_current_directory_name,
+ strlen(gp_current_directory_name),
+ paths[0].value.bytes,
+ r_size(&paths[0]))))
+ ) {
+ memmove(paths + 1, paths, count * sizeof(*paths));
+ make_const_string(paths, avm_foreign | a_readonly,
+ strlen(gp_current_directory_name),
+ (const byte *)gp_current_directory_name);
+ }
+ } else {
+ if (first_is_here)
+ memmove(paths, paths + 1, count * sizeof(*paths));
+ }
+ r_set_size(&minst->lib_path.list,
+ count + (minst->search_here_first ? 1 : 0));
+ if (minst->lib_path.env != 0)
+ code = file_path_add(minst, &minst->lib_path, minst->lib_path.env);
+ /* now put the %rom%lib/ device path before the gs_lib_default_path on the list */
+ for (i = 0; i < gx_io_device_table_count; i++) {
+ const gx_io_device *iodev = gx_io_device_table[i];
+ const char *dname = iodev->dname;
+
+ if (dname && strlen(dname) == 5 && !memcmp("%rom%", dname, 5)) {
+ have_rom_device = 1;
+ break;
+ }
+ }
+ if (have_rom_device && code >= 0) {
+ code = file_path_add(minst, &minst->lib_path, "%rom%Resource/Init/");
+ if (code < 0)
+ return code;
+ code = file_path_add(minst, &minst->lib_path, "%rom%lib/");
+ }
+ if (minst->lib_path.final != 0 && code >= 0)
+ code = file_path_add(minst, &minst->lib_path, minst->lib_path.final);
+ return code;
+}
+
+/* Open a file, using the search paths. */
+int
+gs_main_lib_open(gs_main_instance * minst, const char *file_name, ref * pfile)
+{
+ /* This is a separate procedure only to avoid tying up */
+ /* extra stack space while running the file. */
+ i_ctx_t *i_ctx_p = minst->i_ctx_p;
+#define maxfn 2048
+ char fn[maxfn];
+ uint len;
+
+ return lib_file_open(&minst->lib_path, imemory,
+ NULL, /* Don't check permissions here, because permlist
+ isn't ready running init files. */
+ file_name, strlen(file_name), fn, maxfn, &len, pfile);
+}
+
+/* Open and execute a file. */
+int
+gs_main_run_file(gs_main_instance * minst, const char *file_name, int user_errors, int *pexit_code, ref * perror_object)
+{
+ ref initial_file;
+ int code = gs_main_run_file_open(minst, file_name, &initial_file);
+
+ if (code < 0)
+ return code;
+ return gs_main_interpret(minst, &initial_file, user_errors,
+ pexit_code, perror_object);
+}
+int
+gs_main_run_file_open(gs_main_instance * minst, const char *file_name, ref * pfref)
+{
+ gs_main_set_lib_paths(minst);
+ if (gs_main_lib_open(minst, file_name, pfref) < 0) {
+ emprintf1(minst->heap,
+ "Can't find initialization file %s.\n",
+ file_name);
+ return_error(gs_error_Fatal);
+ }
+ r_set_attrs(pfref, a_execute + a_executable);
+ return 0;
+}
+
+/* Open and run the very first initialization file. */
+static int
+gs_run_init_file(gs_main_instance * minst, int *pexit_code, ref * perror_object)
+{
+ i_ctx_t *i_ctx_p = minst->i_ctx_p;
+ ref ifile;
+ ref first_token;
+ int code;
+ scanner_state state;
+
+ gs_main_set_lib_paths(minst);
+ code = gs_main_run_file_open(minst, gs_init_file, &ifile);
+ if (code < 0) {
+ *pexit_code = 255;
+ return code;
+ }
+ /* Check to make sure the first token is an integer */
+ /* (for the version number check.) */
+ gs_scanner_init(&state, &ifile);
+ code = gs_scan_token(i_ctx_p, &first_token, &state);
+ if (code != 0 || !r_has_type(&first_token, t_integer)) {
+ emprintf1(minst->heap,
+ "Initialization file %s does not begin with an integer.\n",
+ gs_init_file);
+ *pexit_code = 255;
+ return_error(gs_error_Fatal);
+ }
+ *++osp = first_token;
+ r_set_attrs(&ifile, a_executable);
+ return gs_main_interpret(minst, &ifile, minst->user_errors,
+ pexit_code, perror_object);
+}
+
+/* Run a string. */
+int
+gs_main_run_string(gs_main_instance * minst, const char *str, int user_errors,
+ int *pexit_code, ref * perror_object)
+{
+ return gs_main_run_string_with_length(minst, str, (uint) strlen(str),
+ user_errors,
+ pexit_code, perror_object);
+}
+int
+gs_main_run_string_with_length(gs_main_instance * minst, const char *str,
+ uint length, int user_errors, int *pexit_code, ref * perror_object)
+{
+ int code;
+
+ code = gs_main_run_string_begin(minst, user_errors,
+ pexit_code, perror_object);
+ if (code < 0)
+ return code;
+ code = gs_main_run_string_continue(minst, str, length, user_errors,
+ pexit_code, perror_object);
+ if (code != gs_error_NeedInput)
+ return code;
+ return gs_main_run_string_end(minst, user_errors,
+ pexit_code, perror_object);
+}
+
+/* Set up for a suspendable run_string. */
+int
+gs_main_run_string_begin(gs_main_instance * minst, int user_errors,
+ int *pexit_code, ref * perror_object)
+{
+ const char *setup = ".runstringbegin";
+ ref rstr;
+ int code;
+
+ gs_main_set_lib_paths(minst);
+ make_const_string(&rstr, avm_foreign | a_readonly | a_executable,
+ strlen(setup), (const byte *)setup);
+ code = gs_main_interpret(minst, &rstr, user_errors, pexit_code,
+ perror_object);
+ return (code == gs_error_NeedInput ? 0 : code == 0 ? gs_error_Fatal : code);
+}
+/* Continue running a string with the option of suspending. */
+int
+gs_main_run_string_continue(gs_main_instance * minst, const char *str,
+ uint length, int user_errors, int *pexit_code, ref * perror_object)
+{
+ ref rstr;
+
+ if (length == 0)
+ return 0; /* empty string signals EOF */
+ make_const_string(&rstr, avm_foreign | a_readonly, length,
+ (const byte *)str);
+ return gs_main_interpret(minst, &rstr, user_errors, pexit_code,
+ perror_object);
+}
+/* Signal EOF when suspended. */
+int
+gs_main_run_string_end(gs_main_instance * minst, int user_errors,
+ int *pexit_code, ref * perror_object)
+{
+ ref rstr;
+
+ make_empty_const_string(&rstr, avm_foreign | a_readonly);
+ return gs_main_interpret(minst, &rstr, user_errors, pexit_code,
+ perror_object);
+}
+
+/* ------ Operand stack access ------ */
+
+/* These are built for comfort, not for speed. */
+
+static int
+push_value(gs_main_instance *minst, ref * pvalue)
+{
+ i_ctx_t *i_ctx_p = minst->i_ctx_p;
+ int code = ref_stack_push(&o_stack, 1);
+
+ if (code < 0)
+ return code;
+ *ref_stack_index(&o_stack, 0L) = *pvalue;
+ return 0;
+}
+
+int
+gs_push_boolean(gs_main_instance * minst, bool value)
+{
+ ref vref;
+
+ make_bool(&vref, value);
+ return push_value(minst, &vref);
+}
+
+int
+gs_push_integer(gs_main_instance * minst, long value)
+{
+ ref vref;
+
+ make_int(&vref, value);
+ return push_value(minst, &vref);
+}
+
+int
+gs_push_real(gs_main_instance * minst, double value)
+{
+ ref vref;
+
+ make_real(&vref, value);
+ return push_value(minst, &vref);
+}
+
+int
+gs_push_string(gs_main_instance * minst, byte * chars, uint length,
+ bool read_only)
+{
+ ref vref;
+
+ make_string(&vref, avm_foreign | (read_only ? a_readonly : a_all),
+ length, (byte *) chars);
+ return push_value(minst, &vref);
+}
+
+static int
+pop_value(i_ctx_t *i_ctx_p, ref * pvalue)
+{
+ if (!ref_stack_count(&o_stack))
+ return_error(gs_error_stackunderflow);
+ *pvalue = *ref_stack_index(&o_stack, 0L);
+ return 0;
+}
+
+int
+gs_pop_boolean(gs_main_instance * minst, bool * result)
+{
+ i_ctx_t *i_ctx_p = minst->i_ctx_p;
+ ref vref;
+ int code = pop_value(i_ctx_p, &vref);
+
+ if (code < 0)
+ return code;
+ check_type_only(vref, t_boolean);
+ *result = vref.value.boolval;
+ ref_stack_pop(&o_stack, 1);
+ return 0;
+}
+
+int
+gs_pop_integer(gs_main_instance * minst, long *result)
+{
+ i_ctx_t *i_ctx_p = minst->i_ctx_p;
+ ref vref;
+ int code = pop_value(i_ctx_p, &vref);
+
+ if (code < 0)
+ return code;
+ check_type_only(vref, t_integer);
+ *result = vref.value.intval;
+ ref_stack_pop(&o_stack, 1);
+ return 0;
+}
+
+int
+gs_pop_real(gs_main_instance * minst, float *result)
+{
+ i_ctx_t *i_ctx_p = minst->i_ctx_p;
+ ref vref;
+ int code = pop_value(i_ctx_p, &vref);
+
+ if (code < 0)
+ return code;
+ switch (r_type(&vref)) {
+ case t_real:
+ *result = vref.value.realval;
+ break;
+ case t_integer:
+ *result = (float)(vref.value.intval);
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ ref_stack_pop(&o_stack, 1);
+ return 0;
+}
+
+int
+gs_pop_string(gs_main_instance * minst, gs_string * result)
+{
+ i_ctx_t *i_ctx_p = minst->i_ctx_p;
+ ref vref;
+ int code = pop_value(i_ctx_p, &vref);
+
+ if (code < 0)
+ return code;
+ switch (r_type(&vref)) {
+ case t_name:
+ name_string_ref(minst->heap, &vref, &vref);
+ code = 1;
+ goto rstr;
+ case t_string:
+ code = (r_has_attr(&vref, a_write) ? 0 : 1);
+ rstr:result->data = vref.value.bytes;
+ result->size = r_size(&vref);
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ ref_stack_pop(&o_stack, 1);
+ return code;
+}
+
+/* ------ Termination ------ */
+
+/* Get the names of temporary files.
+ * Each name is null terminated, and the last name is
+ * terminated by a double null.
+ * We retrieve the names of temporary files just before
+ * the interpreter finishes, and then delete the files
+ * after the interpreter has closed all files.
+ */
+static char *gs_main_tempnames(gs_main_instance *minst)
+{
+ i_ctx_t *i_ctx_p = minst->i_ctx_p;
+ ref *SAFETY;
+ ref *tempfiles;
+ ref keyval[2]; /* for key and value */
+ char *tempnames = NULL;
+ int i;
+ int idict;
+ int len = 0;
+ const byte *data = NULL;
+ uint size;
+ if (minst->init_done >= 2) {
+ if (dict_find_string(systemdict, "SAFETY", &SAFETY) <= 0 ||
+ dict_find_string(SAFETY, "tempfiles", &tempfiles) <= 0)
+ return NULL;
+ /* get lengths of temporary filenames */
+ idict = dict_first(tempfiles);
+ while ((idict = dict_next(tempfiles, idict, &keyval[0])) >= 0) {
+ if (obj_string_data(minst->heap, &keyval[0], &data, &size) >= 0)
+ len += size + 1;
+ }
+ if (len != 0)
+ tempnames = (char *)malloc(len+1);
+ if (tempnames) {
+ memset(tempnames, 0, len+1);
+ /* copy temporary filenames */
+ idict = dict_first(tempfiles);
+ i = 0;
+ while ((idict = dict_next(tempfiles, idict, &keyval[0])) >= 0) {
+ if (obj_string_data(minst->heap, &keyval[0], &data, &size) >= 0) {
+ memcpy(tempnames+i, (const char *)data, size);
+ i+= size;
+ tempnames[i++] = '\0';
+ }
+ }
+ }
+ }
+ return tempnames;
+}
+
+/* Free all resources and return. */
+int
+gs_main_finit(gs_main_instance * minst, int exit_status, int code)
+{
+ i_ctx_t *i_ctx_p = minst->i_ctx_p;
+ int exit_code;
+ ref error_object;
+ char *tempnames;
+
+ /* NB: need to free gs_name_table
+ */
+
+ /*
+ * Previous versions of this code closed the devices in the
+ * device list here. Since these devices are now prototypes,
+ * they cannot be opened, so they do not need to be closed;
+ * alloc_restore_all will close dynamically allocated devices.
+ */
+ tempnames = gs_main_tempnames(minst);
+
+#ifndef PSI_INCLUDED
+ /* We have to disable BGPrint before we call interp_reclaim() to prevent the
+ * parent rendering thread initialising for the next page, whilst we are
+ * removing objects it may want to access - for example, the I/O device table.
+ * We also have to mess with the BeginPage/EndPage procs so that we don't
+ * trigger a spurious extra page to be emitted.
+ */
+ if (minst->init_done >= 1) {
+ gs_main_run_string(minst,
+ "/systemdict .systemexec /begin .systemexec \
+ /BGPrint /GetDeviceParam .special_op \
+ {{ <</BeginPage {pop} /EndPage {pop pop //false } \
+ /BGPrint false /NumRenderingThreads 0>> setpagedevice} if} if \
+ serverdict /.jobsavelevel get 0 eq {/quit} {/stop} ifelse end \
+ .systemvar exec",
+ 0 , &exit_code, &error_object);
+ }
+#endif
+
+ /*
+ * Close the "main" device, because it may need to write out
+ * data before destruction. pdfwrite needs so.
+ */
+ if (minst->init_done >= 1) {
+ int code = 0;
+
+ if (idmemory->reclaim != 0) {
+ code = interp_reclaim(&minst->i_ctx_p, avm_global);
+
+ if (code < 0) {
+ emprintf1(minst->heap,
+ "ERROR %d reclaiming the memory while the interpreter finalization.\n",
+ code);
+ return gs_error_Fatal;
+ }
+ i_ctx_p = minst->i_ctx_p; /* interp_reclaim could change it. */
+ }
+#ifndef PSI_INCLUDED
+ if (i_ctx_p->pgs != NULL && i_ctx_p->pgs->device != NULL) {
+ gx_device *pdev = i_ctx_p->pgs->device;
+ const char * dname = pdev->dname;
+
+ /* make sure device doesn't isn't freed by .uninstalldevice */
+ rc_adjust(pdev, 1, "gs_main_finit");
+ /* deactivate the device just before we close it for the last time */
+ gs_main_run_string(minst,
+ /* we need to do the 'quit' so we don't loop for input (double quit) */
+ ".uninstallpagedevice serverdict \
+ /.jobsavelevel get 0 eq {/quit} {/stop} ifelse .systemvar exec",
+ 0 , &exit_code, &error_object);
+ code = gs_closedevice(pdev);
+ if (code < 0)
+ emprintf2(pdev->memory,
+ "ERROR %d closing %s device. See gs/psi/ierrors.h for code explanation.\n",
+ code,
+ dname);
+ rc_decrement(pdev, "gs_main_finit"); /* device might be freed */
+ if (exit_status == 0 || exit_status == gs_error_Quit)
+ exit_status = code;
+ }
+#endif
+ }
+ /* Flush stdout and stderr */
+ if (minst->init_done >= 2)
+ gs_main_run_string(minst,
+ "(%stdout) (w) file closefile (%stderr) (w) file closefile \
+ /systemdict .systemexec /begin .systemexec \
+ serverdict /.jobsavelevel get 0 eq {/quit} {/stop} ifelse .systemexec \
+ end",
+ 0 , &exit_code, &error_object);
+ gp_readline_finit(minst->readline_data);
+ i_ctx_p = minst->i_ctx_p; /* get current interp context */
+ if (gs_debug_c(':')) {
+ print_resource_usage(minst, &gs_imemory, "Final");
+ dmprintf1(minst->heap, "%% Exiting instance 0x%p\n", minst);
+ }
+ /* Do the equivalent of a restore "past the bottom". */
+ /* This will release all memory, close all open files, etc. */
+ if (minst->init_done >= 1) {
+ gs_memory_t *mem_raw = i_ctx_p->memory.current->non_gc_memory;
+ i_plugin_holder *h = i_ctx_p->plugin_list;
+ code = alloc_restore_all(idmemory);
+ if (code < 0)
+ emprintf1(mem_raw,
+ "ERROR %d while the final restore. See gs/psi/ierrors.h for code explanation.\n",
+ code);
+ i_plugin_finit(mem_raw, h);
+ }
+#ifndef PSI_INCLUDED
+ /* clean up redirected stdout */
+ if (minst->heap->gs_lib_ctx->fstdout2
+ && (minst->heap->gs_lib_ctx->fstdout2 != minst->heap->gs_lib_ctx->fstdout)
+ && (minst->heap->gs_lib_ctx->fstdout2 != minst->heap->gs_lib_ctx->fstderr)) {
+ fclose(minst->heap->gs_lib_ctx->fstdout2);
+ minst->heap->gs_lib_ctx->fstdout2 = (FILE *)NULL;
+ }
+#endif
+ minst->heap->gs_lib_ctx->stdout_is_redirected = 0;
+ minst->heap->gs_lib_ctx->stdout_to_stderr = 0;
+ /* remove any temporary files, after ghostscript has closed files */
+ if (tempnames) {
+ char *p = tempnames;
+ while (*p) {
+ unlink(p);
+ p += strlen(p) + 1;
+ }
+ free(tempnames);
+ }
+#ifndef PSI_INCLUDED
+ gs_lib_finit(exit_status, code, minst->heap);
+#endif
+ return exit_status;
+}
+int
+gs_to_exit_with_code(const gs_memory_t *mem, int exit_status, int code)
+{
+ return gs_main_finit(get_minst_from_memory(mem), exit_status, code);
+}
+int
+gs_to_exit(const gs_memory_t *mem, int exit_status)
+{
+ return gs_to_exit_with_code(mem, exit_status, 0);
+}
+void
+gs_abort(const gs_memory_t *mem)
+{
+ /* In previous versions, we tried to do a cleanup (using gs_to_exit),
+ * but more often than not, that will trip another abort and create
+ * an infinite recursion. So just abort without trying to cleanup.
+ */
+ gp_do_exit(1);
+}
+
+/* ------ Debugging ------ */
+
+/* Print resource usage statistics. */
+void
+print_resource_usage(const gs_main_instance * minst, gs_dual_memory_t * dmem,
+ const char *msg)
+{
+ ulong allocated = 0, used = 0;
+ long utime[2];
+
+ gp_get_usertime(utime);
+ {
+ int i;
+
+ for (i = 0; i < countof(dmem->spaces_indexed); ++i) {
+ gs_ref_memory_t *mem = dmem->spaces_indexed[i];
+
+ if (mem != 0 && (i == 0 || mem != dmem->spaces_indexed[i - 1])) {
+ gs_memory_status_t status;
+ gs_ref_memory_t *mem_stable =
+ (gs_ref_memory_t *)gs_memory_stable((gs_memory_t *)mem);
+
+ gs_memory_status((gs_memory_t *)mem, &status);
+ allocated += status.allocated;
+ used += status.used;
+ if (mem_stable != mem) {
+ gs_memory_status((gs_memory_t *)mem_stable, &status);
+ allocated += status.allocated;
+ used += status.used;
+ }
+ }
+ }
+ }
+ dmprintf4(minst->heap, "%% %s time = %g, memory allocated = %lu, used = %lu\n",
+ msg, utime[0] - minst->base_time[0] +
+ (utime[1] - minst->base_time[1]) / 1000000000.0,
+ allocated, used);
+}
+
+/* Dump the stacks after interpretation */
+void
+gs_main_dump_stack(gs_main_instance *minst, int code, ref * perror_object)
+{
+ i_ctx_t *i_ctx_p = minst->i_ctx_p;
+
+ zflush(i_ctx_p); /* force out buffered output */
+ dmprintf1(minst->heap, "\nUnexpected interpreter error %d.\n", code);
+ if (perror_object != 0) {
+ dmputs(minst->heap, "Error object: ");
+ debug_print_ref(minst->heap, perror_object);
+ dmputc(minst->heap, '\n');
+ }
+ debug_dump_stack(minst->heap, &o_stack, "Operand stack");
+ debug_dump_stack(minst->heap, &e_stack, "Execution stack");
+ debug_dump_stack(minst->heap, &d_stack, "Dictionary stack");
+}
diff --git a/psi/imain.h b/psi/imain.h
new file mode 100644
index 000000000..a840f7ad3
--- /dev/null
+++ b/psi/imain.h
@@ -0,0 +1,254 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to imain.c */
+/* Requires <stdio.h>, stdpre.h, gsmemory.h, gstypes.h, iref.h */
+
+#ifndef imain_INCLUDED
+# define imain_INCLUDED
+
+#include "gsexit.h" /* exported by imain.c */
+
+/*
+ * This file defines the intended API between client front ends
+ * (such as imainarg.c, the command-line-driven front end)
+ * and imain.c, which provides top-level control of the interpreter.
+ */
+
+/* ================ Types ================ */
+
+/*
+ * Currently, the interpreter has a lot of static variables, but
+ * eventually it will have none, so that clients will be able to make
+ * multiple instances of it. In anticipation of this, many of the
+ * top-level API calls take an interpreter instance (gs_main_instance *)
+ * as their first argument.
+ */
+#ifndef gs_main_instance_DEFINED
+# define gs_main_instance_DEFINED
+typedef struct gs_main_instance_s gs_main_instance;
+#endif
+
+/* ================ Exported procedures from imain.c ================ */
+
+/* get minst from memory is a hack to allow parts of the system
+ * to reach minst, slightly better than a global
+ */
+gs_main_instance* get_minst_from_memory(const gs_memory_t *mem);
+
+/* ---------------- Instance creation ---------------- */
+
+/*
+ * NB: multiple instances are not supported yet
+ *
+ * // add usage documentation
+ */
+gs_main_instance *gs_main_alloc_instance(gs_memory_t *);
+
+/* ---------------- Initialization ---------------- */
+
+/*
+ * The interpreter requires three initialization steps, called init0,
+ * init1, and init2. These steps must be done in that order, but
+ * init1 may be omitted.
+ */
+
+/*
+ * init0 records the files to be used for stdio, and initializes the
+ * graphics library, the file search paths, and other instance data.
+ */
+int gs_main_init0(gs_main_instance *minst, FILE *in, FILE *out, FILE *err,
+ int max_lib_paths);
+
+/*
+ * init1 initializes the memory manager and other internal data
+ * structures such as the name table, the token scanner tables,
+ * dictionaries such as systemdict, and the interpreter stacks.
+ */
+int gs_main_init1(gs_main_instance * minst);
+
+/*
+ * init2 finishes preparing the interpreter for use by running
+ * initialization files with PostScript procedure definitions.
+ */
+int gs_main_init2(gs_main_instance * minst);
+
+/*
+ * The runlibfile operator uses a search path, as described in
+ * Use.htm, for looking up file names. Each interpreter instance has
+ * its own search path. The following call adds a directory or set of
+ * directories to the search path; it is equivalent to the -I command
+ * line switch. It may be called any time after init0.
+ */
+int gs_main_add_lib_path(gs_main_instance * minst, const char *path);
+
+/*
+ * Under certain internal conditions, the search path may temporarily
+ * be in an inconsistent state; gs_main_set_lib_paths takes care of
+ * this. Clients should never need to call this procedure, and
+ * eventually it may be removed.
+ */
+int gs_main_set_lib_paths(gs_main_instance * minst);
+
+/*
+ * Open a PostScript file using the search path. Clients should
+ * never need to call this procedure, since gs_main_run_file opens the
+ * file itself, and eventually the procedure may be removed.
+ */
+int gs_main_lib_open(gs_main_instance * minst, const char *fname,
+ ref * pfile);
+
+/*
+ * Here we summarize the C API calls that correspond to some of the
+ * most common command line switches documented in Use.htm, to help
+ * clients who are familiar with the command line and are starting to
+ * use the API.
+ *
+ * -d/D, -s/S (for setting device parameters like OutputFile)
+ * Use the C API for device parameters documented near the
+ * end of gsparam.h.
+ *
+ * -d/D (for setting Boolean parameters like NOPAUSE)
+ * { ref vtrue;
+ * make_true(&vtrue);
+ * dict_put_string(systemdict, "NOPAUSE", &vtrue);
+ * }
+ * -I
+ * Use gs_main_add_lib_path, documented above.
+ *
+ * -A, -A-
+ * Set gs_debug['@'] = 1 or 0 respectively.
+ * -E, -E-
+ * Set gs_debug['#'] = 1 or 0 respectively.
+ * -Z..., -Z-...
+ * Set gs_debug[c] = 1 or 0 respectively for each character
+ * c in the string.
+ */
+
+/* ---------------- Execution ---------------- */
+
+/*
+ * After initializing the interpreter, clients may pass it files or
+ * strings to be interpreted. There are four ways to do this:
+ * - Pass a file name (gs_main_run_file);
+ * - Pass a C string (gs_main_run_string);
+ * - Pass a string defined by pointer and length
+ * (gs_main_run_string_with_length);
+ * - Pass strings piece-by-piece
+ * (gs_main_run_string_begin/continue/end).
+ *
+ * The value returned by the first three of these calls is
+ * 0 if the interpreter ran to completion, gs_error_Quit for a normal quit,
+ * or gs_error_Fatal for a non-zero quit or a fatal error.
+ * gs_error_Fatal stores the exit code in the third argument.
+ * The str argument of gs_main_run_string[_with_length] must be allocated
+ * in non-garbage-collectable space (e.g., by malloc or gs_malloc,
+ * or statically).
+ */
+int gs_main_run_file(gs_main_instance * minst, const char *fname,
+ int user_errors, int *pexit_code,
+ ref * perror_object);
+int gs_main_run_string(gs_main_instance * minst, const char *str,
+ int user_errors, int *pexit_code,
+ ref * perror_object);
+int gs_main_run_string_with_length(gs_main_instance * minst,
+ const char *str, uint length,
+ int user_errors, int *pexit_code,
+ ref * perror_object);
+
+/*
+ * Open the file for gs_main_run_file. This is an internal routine
+ * that is only exported for some special clients.
+ */
+int gs_main_run_file_open(gs_main_instance * minst,
+ const char *file_name, ref * pfref);
+
+/*
+ * The next 3 procedures provide for feeding input to the interpreter
+ * in arbitrary chunks, unlike run_string, which requires that each string
+ * be a properly formed PostScript program fragment. To use them:
+ * Call run_string_begin.
+ * Call run_string_continue as many times as desired,
+ * stopping if it returns anything other than gs_error_NeedInput.
+ * If run_string_continue didn't indicate an error or a quit
+ * (i.e., a return value other than gs_error_NeedInput), call run_string_end
+ * to provide an EOF indication.
+ * Note that run_string_continue takes a pointer and a length, like
+ * run_string_with_length.
+ */
+int gs_main_run_string_begin(gs_main_instance * minst, int user_errors,
+ int *pexit_code, ref * perror_object);
+int gs_main_run_string_continue(gs_main_instance * minst,
+ const char *str, uint length,
+ int user_errors, int *pexit_code,
+ ref * perror_object);
+int gs_main_run_string_end(gs_main_instance * minst, int user_errors,
+ int *pexit_code, ref * perror_object);
+
+/* ---------------- Operand stack access ---------------- */
+
+/*
+ * The following procedures are not used in normal operation;
+ * they exist only to allow clients driving the interpreter through the
+ * gs_main_run_xxx procedures to push parameters quickly and to get results
+ * back. The push procedures return 0, gs_error_stackoverflow, or gs_error_VMerror;
+ * the pop procedures return 0, gs_error_stackunderflow, or gs_error_typecheck.
+ *
+ * Procedures to push values on the operand stack:
+ */
+int gs_push_boolean(gs_main_instance * minst, bool value);
+int gs_push_integer(gs_main_instance * minst, long value);
+int gs_push_real(gs_main_instance * minst, double value);
+int gs_push_string(gs_main_instance * minst, byte * chars, uint length,
+ bool read_only);
+
+/*
+ * Procedures to pop values from the operand stack:
+ */
+int gs_pop_boolean(gs_main_instance * minst, bool * result);
+int gs_pop_integer(gs_main_instance * minst, long *result);
+int gs_pop_real(gs_main_instance * minst, float *result);
+
+/* gs_pop_string returns 1 if the string is read-only. */
+int gs_pop_string(gs_main_instance * minst, gs_string * result);
+
+/* ---------------- Debugging ---------------- */
+
+/*
+ * Print an error mesage including the error code, error object (if any),
+ * and operand and execution stacks in hex. Clients will probably
+ * never call this.
+ */
+void gs_main_dump_stack(gs_main_instance *minst, int code,
+ ref * perror_object);
+
+/* ---------------- Console output ---------------- */
+
+/* ---------------- Termination ---------------- */
+
+/*
+ * Terminate the interpreter by closing all devices and releasing all
+ * allocated memory. Currently, because of some technical problems
+ * with statically initialized data, it is not possible to reinitialize
+ * the interpreter after terminating it; we plan to fix this as soon as
+ * possible.
+ *
+ * Note that calling gs_to_exit (defined in gsexit.h) automatically calls
+ * gs_main_finit for the default instance.
+ */
+int gs_main_finit(gs_main_instance * minst, int exit_status, int code);
+
+#endif /* imain_INCLUDED */
diff --git a/psi/imainarg.c b/psi/imainarg.c
new file mode 100644
index 000000000..1abfcf8c6
--- /dev/null
+++ b/psi/imainarg.c
@@ -0,0 +1,1265 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Command line parsing and dispatching */
+
+#include "ctype_.h"
+#include "memory_.h"
+#include "string_.h"
+#include <stdlib.h> /* for qsort */
+
+#include "ghost.h"
+#include "gp.h"
+#include "gsargs.h"
+#include "gscdefs.h"
+#include "gsmalloc.h" /* for gs_malloc_limit */
+#include "gsmdebug.h"
+#include "gspaint.h" /* for gs_erasepage */
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "gsdevice.h"
+#include "gxdevsop.h" /* for gxdso_* enums */
+#include "gxclpage.h"
+#include "gdevprn.h"
+#include "stream.h"
+#include "ierrors.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "strimpl.h" /* for sfilter.h */
+#include "sfilter.h" /* for iscan.h */
+#include "ostack.h" /* must precede iscan.h */
+#include "iscan.h"
+#include "iconf.h"
+#include "imain.h"
+#include "imainarg.h"
+#include "iapi.h"
+#include "iminst.h"
+#include "iname.h"
+#include "store.h"
+#include "files.h" /* requires stream.h */
+#include "interp.h"
+#include "iutil.h"
+#include "ivmspace.h"
+#include "vdtrace.h"
+
+/* Import operator procedures */
+extern int zflush(i_ctx_t *);
+extern int zflushpage(i_ctx_t *);
+
+#ifndef GS_LIB
+# define GS_LIB "GS_LIB"
+#endif
+
+#ifndef GS_OPTIONS
+# define GS_OPTIONS "GS_OPTIONS"
+#endif
+
+/* This is now the default number of entries we initially
+ * allocate for the search path array objects. The arrays
+ * will now enlarge as required -
+ * see imain.c: file_path_add()/extend_path_list_container()
+ */
+
+#ifndef GS_MAX_LIB_DIRS
+# define GS_MAX_LIB_DIRS 25
+#endif
+
+#define MAX_BUFFERED_SIZE 1024
+
+/* Note: sscanf incorrectly defines its first argument as char * */
+/* rather than const char *. This accounts for the ugly casts below. */
+
+/* Redefine puts to use outprintf, */
+/* so it will work even without stdio. */
+#undef puts
+#define puts(mem, str) outprintf(mem, "%s\n", str)
+
+/* Forward references */
+#define runInit 1
+#define runFlush 2
+#define runBuffer 4
+static int swproc(gs_main_instance *, const char *, arg_list *);
+static int argproc(gs_main_instance *, const char *);
+static int run_buffered(gs_main_instance *, const char *);
+static int esc_strlen(const char *);
+static void esc_strcat(char *, const char *);
+static int runarg(gs_main_instance *, const char *, const char *, const char *, int);
+static int run_string(gs_main_instance *, const char *, int);
+static int run_finish(gs_main_instance *, int, int, ref *);
+static int try_stdout_redirect(gs_main_instance * minst,
+ const char *command, const char *filename);
+
+/* Forward references for help printout */
+static void print_help(gs_main_instance *);
+static void print_revision(const gs_main_instance *);
+static void print_version(const gs_main_instance *);
+static void print_usage(const gs_main_instance *);
+static void print_devices(const gs_main_instance *);
+static void print_emulators(const gs_main_instance *);
+static void print_paths(gs_main_instance *);
+static void print_help_trailer(const gs_main_instance *);
+
+/* ------ Main program ------ */
+
+/* Process the command line with a given instance. */
+static FILE *
+gs_main_arg_fopen(const char *fname, void *vminst)
+{
+ gs_main_set_lib_paths((gs_main_instance *) vminst);
+ return lib_fopen(&((gs_main_instance *)vminst)->lib_path,
+ ((gs_main_instance *)vminst)->heap, fname);
+}
+static void
+set_debug_flags(const char *arg, char *flags)
+{
+ byte value = (*arg == '-' ? (++arg, 0) : 0xff);
+
+ while (*arg)
+ flags[*arg++ & 127] = value;
+}
+
+int
+gs_main_init_with_args(gs_main_instance * minst, int argc, char *argv[])
+{
+ const char *arg;
+ arg_list args;
+ int code;
+
+ arg_init(&args, (const char **)argv, argc,
+ gs_main_arg_fopen, (void *)minst,
+ minst->get_codepoint,
+ minst->heap);
+ code = gs_main_init0(minst, 0, 0, 0, GS_MAX_LIB_DIRS);
+ if (code < 0)
+ return code;
+/* This first check is not needed on VMS since GS_LIB evaluates to the same
+ value as that returned by gs_lib_default_path. Also, since GS_LIB is
+ defined as a searchlist logical and getenv only returns the first entry
+ in the searchlist, it really doesn't make sense to search that particular
+ directory twice.
+*/
+#ifndef __VMS
+ {
+ int len = 0;
+ int code = gp_getenv(GS_LIB, (char *)0, &len);
+
+ if (code < 0) { /* key present, value doesn't fit */
+ char *path = (char *)gs_alloc_bytes(minst->heap, len, "GS_LIB");
+
+ gp_getenv(GS_LIB, path, &len); /* can't fail */
+ minst->lib_path.env = path;
+ }
+ }
+#endif /* __VMS */
+ minst->lib_path.final = gs_lib_default_path;
+ code = gs_main_set_lib_paths(minst);
+ if (code < 0)
+ return code;
+ /* Prescan the command line for --help and --version. */
+ {
+ int i;
+ bool helping = false;
+
+ for (i = 1; i < argc; ++i)
+ if (!arg_strcmp(&args, argv[i], "--")) {
+ /* A PostScript program will be interpreting all the */
+ /* remaining switches, so stop scanning. */
+ helping = false;
+ break;
+ } else if (!arg_strcmp(&args, argv[i], "--help")) {
+ print_help(minst);
+ helping = true;
+ } else if (!arg_strcmp(&args, argv[i], "--debug")) {
+ gs_debug_flags_list(minst->heap);
+ helping = true;
+ } else if (!arg_strcmp(&args, argv[i], "--version")) {
+ print_version(minst);
+ puts(minst->heap, ""); /* \n */
+ helping = true;
+ }
+ if (helping)
+ return gs_error_Info;
+ }
+ /* Execute files named in the command line, */
+ /* processing options along the way. */
+ /* Wait until the first file name (or the end */
+ /* of the line) to finish initialization. */
+ minst->run_start = true;
+
+ {
+ int len = 0;
+ int code = gp_getenv(GS_OPTIONS, (char *)0, &len);
+
+ if (code < 0) { /* key present, value doesn't fit */
+ char *opts =
+ (char *)gs_alloc_bytes(minst->heap, len, "GS_OPTIONS");
+
+ gp_getenv(GS_OPTIONS, opts, &len); /* can't fail */
+#if defined(GS_NO_UTF8)
+ if (arg_push_memory_string(&args, opts, false, minst->heap))
+ return gs_error_Fatal;
+#else
+ if (arg_push_decoded_memory_string(&args, opts, false, true, minst->heap))
+ return gs_error_Fatal;
+#endif
+ }
+ }
+ while ((arg = arg_next(&args, &code, minst->heap)) != 0) {
+ switch (*arg) {
+ case '-':
+ code = swproc(minst, arg, &args);
+ if (code < 0)
+ return code;
+ if (code > 0)
+ outprintf(minst->heap, "Unknown switch %s - ignoring\n", arg);
+ if (gs_debug[':'] && arg[1] == 'Z') {
+ int i;
+
+ dmprintf1(minst->heap, "%% Init started, instance 0x%p, with args: ", minst);
+ for (i=1; i<argc; i++)
+ dmprintf1(minst->heap, "%s ", argv[i]);
+ dmprintf(minst->heap, "\n");
+ }
+ break;
+ default:
+ /* default is to treat this as a file name to be run */
+ code = argproc(minst, arg);
+ if (code < 0)
+ return code;
+ if (minst->saved_pages_test_mode) {
+ gx_device *pdev;
+
+ /* get the current device */
+ pdev = gs_currentdevice(minst->i_ctx_p->pgs);
+ if ((code = gx_saved_pages_param_process((gx_device_printer *)pdev,
+ (byte *)"print normal flush", 18)) < 0)
+ return code;
+ if (code > 0)
+ if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0)
+ return code;
+ }
+ }
+ }
+ if (code < 0)
+ return code;
+
+ code = gs_main_init2(minst);
+ if (code < 0)
+ return code;
+
+ if (gs_debug[':']) {
+ int i;
+
+ dmprintf1(minst->heap, "%% Init done, instance 0x%p, with args: ", minst);
+ for (i=1; i<argc; i++)
+ dmprintf1(minst->heap, "%s ", argv[i]);
+ dmprintf(minst->heap, "\n");
+ }
+ if (!minst->run_start)
+ return gs_error_Quit;
+ return code ;
+}
+
+/*
+ * Specify a decoding function
+ */
+void gs_main_inst_arg_decode(gs_main_instance * minst,
+ gs_arg_get_codepoint *get_codepoint)
+{
+ minst->get_codepoint = get_codepoint;
+}
+
+gs_arg_get_codepoint *gs_main_inst_get_arg_decode(gs_main_instance * minst)
+{
+ return minst->get_codepoint;
+}
+
+/*
+ * Run the 'start' procedure (after processing the command line).
+ */
+int
+gs_main_run_start(gs_main_instance * minst)
+{
+ return run_string(minst, "systemdict /start get exec", runFlush);
+}
+
+/* Process switches. Return 0 if processed, 1 for unknown switch, */
+/* <0 if error. */
+static int
+swproc(gs_main_instance * minst, const char *arg, arg_list * pal)
+{
+ char sw = arg[1];
+ ref vtrue;
+ int code = 0;
+#undef initial_enter_name
+#define initial_enter_name(nstr, pvalue)\
+ i_initial_enter_name(minst->i_ctx_p, nstr, pvalue)
+
+ make_true(&vtrue);
+ arg += 2; /* skip - and letter */
+ switch (sw) {
+ default:
+ return 1;
+ case 0: /* read stdin as a file char-by-char */
+ /* This is a ******HACK****** for Ghostview. */
+ minst->heap->gs_lib_ctx->stdin_is_interactive = true;
+ goto run_stdin;
+ case '_': /* read stdin with normal buffering */
+ minst->heap->gs_lib_ctx->stdin_is_interactive = false;
+run_stdin:
+ minst->run_start = false; /* don't run 'start' */
+ /* Set NOPAUSE so showpage won't try to read from stdin. */
+ code = swproc(minst, "-dNOPAUSE", pal);
+ if (code)
+ return code;
+ code = gs_main_init2(minst); /* Finish initialization */
+ if (code < 0)
+ return code;
+
+ code = run_string(minst, ".runstdin", runFlush);
+ if (code < 0)
+ return code;
+ if (minst->saved_pages_test_mode) {
+ gx_device *pdev;
+
+ /* get the current device */
+ pdev = gs_currentdevice(minst->i_ctx_p->pgs);
+ if ((code = gx_saved_pages_param_process((gx_device_printer *)pdev,
+ (byte *)"print normal flush", 18)) < 0)
+ return code;
+ if (code > 0)
+ if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0)
+ return code;
+ }
+ break;
+ case '-': /* run with command line args */
+ if (strncmp(arg, "debug=", 6) == 0) {
+ code = gs_debug_flags_parse(minst->heap, arg+6);
+ if (code < 0)
+ return code;
+ break;
+ } else if (strncmp(arg, "saved-pages=", 12) == 0) {
+ gx_device *pdev;
+ gx_device_printer *ppdev;
+
+ /* If init2 not yet done, just save the argument for processing then */
+ if (minst->init_done < 2) {
+ if (minst->saved_pages_initial_arg == NULL) {
+ /* Tuck the parameters away for later when init2 is done */
+ minst->saved_pages_initial_arg = (char *)arg+12;
+ } else {
+ outprintf(minst->heap,
+ " Only one --saved-pages=... command allowed before processing input\n");
+ arg_finit(pal);
+ return gs_error_Fatal;
+ }
+ } else {
+ /* get the current device */
+ pdev = gs_currentdevice(minst->i_ctx_p->pgs);
+ if (dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_saved_pages, NULL, 0) == 0) {
+ outprintf(minst->heap,
+ " --saved-pages not supported by the '%s' device.\n",
+ pdev->dname);
+ arg_finit(pal);
+ return gs_error_Fatal;
+ }
+ ppdev = (gx_device_printer *)pdev;
+ code = gx_saved_pages_param_process(ppdev, (byte *)arg+12, strlen(arg+12));
+ if (code > 0)
+ if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0)
+ return code;
+ }
+ break;
+ /* The following code is only to allow regression testing of saved-pages */
+ } else if (strncmp(arg, "saved-pages-test", 16) == 0) {
+ minst->saved_pages_test_mode = true;
+ break;
+ }
+ /* FALLTHROUGH */
+ case '+':
+ pal->expand_ats = false;
+ /* FALLTHROUGH */
+ case '@': /* ditto with @-expansion */
+ {
+ const char *psarg = arg_next(pal, &code, minst->heap);
+
+ if (code < 0)
+ return gs_error_Fatal;
+ if (psarg == 0) {
+ outprintf(minst->heap, "Usage: gs ... -%c file.ps arg1 ... argn\n", sw);
+ arg_finit(pal);
+ return gs_error_Fatal;
+ }
+ psarg = arg_copy(psarg, minst->heap);
+ if (psarg == NULL)
+ code = gs_error_Fatal;
+ else
+ code = gs_main_init2(minst);
+ if (code >= 0)
+ code = run_string(minst, "userdict/ARGUMENTS[", 0);
+ if (code >= 0)
+ while ((arg = arg_next(pal, &code, minst->heap)) != 0) {
+ code = runarg(minst, "", arg, "", runInit);
+ if (code < 0)
+ break;
+ }
+ if (code >= 0)
+ code = runarg(minst, "]put", psarg, ".runfile", runInit | runFlush);
+ arg_free((char *)psarg, minst->heap);
+ if (code >= 0)
+ code = gs_error_Quit;
+
+ return code;
+ }
+ case 'A': /* trace allocator */
+ switch (*arg) {
+ case 0:
+ gs_alloc_debug = 1;
+ break;
+ case '-':
+ gs_alloc_debug = 0;
+ break;
+ default:
+ puts(minst->heap, "-A may only be followed by -");
+ return gs_error_Fatal;
+ }
+ break;
+ case 'B': /* set run_string buffer size */
+ if (*arg == '-')
+ minst->run_buffer_size = 0;
+ else {
+ uint bsize;
+
+ if (sscanf((const char *)arg, "%u", &bsize) != 1 ||
+ bsize <= 0 || bsize > MAX_BUFFERED_SIZE
+ ) {
+ outprintf(minst->heap,
+ "-B must be followed by - or size between 1 and %u\n",
+ MAX_BUFFERED_SIZE);
+ return gs_error_Fatal;
+ }
+ minst->run_buffer_size = bsize;
+ }
+ break;
+ case 'c': /* code follows */
+ {
+ bool ats = pal->expand_ats;
+
+ code = gs_main_init2(minst);
+ if (code < 0)
+ return code;
+ pal->expand_ats = false;
+ while ((arg = arg_next(pal, &code, minst->heap)) != 0) {
+ if (arg[0] == '@' ||
+ (arg[0] == '-' && !isdigit((unsigned char)arg[1]))
+ )
+ break;
+ code = runarg(minst, "", arg, ".runstring", 0);
+ if (code < 0)
+ return code;
+ }
+ if (code < 0)
+ return gs_error_Fatal;
+ if (arg != 0) {
+ char *p = arg_copy(arg, minst->heap);
+ if (p == NULL)
+ return gs_error_Fatal;
+ arg_push_string(pal, p, true);
+ }
+ pal->expand_ats = ats;
+ break;
+ }
+ case 'E': /* log errors */
+ switch (*arg) {
+ case 0:
+ gs_log_errors = 1;
+ break;
+ case '-':
+ gs_log_errors = 0;
+ break;
+ default:
+ puts(minst->heap, "-E may only be followed by -");
+ return gs_error_Fatal;
+ }
+ break;
+ case 'f': /* run file of arbitrary name */
+ if (*arg != 0) {
+ code = argproc(minst, arg);
+ if (code < 0)
+ return code;
+ if (minst->saved_pages_test_mode) {
+ gx_device *pdev;
+
+ /* get the current device */
+ pdev = gs_currentdevice(minst->i_ctx_p->pgs);
+ return code;
+ if ((code = gx_saved_pages_param_process((gx_device_printer *)pdev,
+ (byte *)"print normal flush", 18)) < 0)
+ return code;
+ if (code > 0)
+ if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0)
+ return code;
+ }
+ }
+ break;
+ case 'F': /* run file with buffer_size = 1 */
+ if (!*arg) {
+ puts(minst->heap, "-F requires a file name");
+ return gs_error_Fatal;
+ } {
+ uint bsize = minst->run_buffer_size;
+
+ minst->run_buffer_size = 1;
+ code = argproc(minst, arg);
+ minst->run_buffer_size = bsize;
+ if (code < 0)
+ return code;
+ if (minst->saved_pages_test_mode) {
+ gx_device *pdev;
+
+ /* get the current device */
+ pdev = gs_currentdevice(minst->i_ctx_p->pgs);
+ if ((code = gx_saved_pages_param_process((gx_device_printer *)pdev,
+ (byte *)"print normal flush", 18)) < 0)
+ return code;
+ if (code > 0)
+ if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0)
+ return code;
+ }
+ }
+ break;
+ case 'g': /* define device geometry */
+ {
+ long width, height;
+ ref value;
+
+ if ((code = gs_main_init1(minst)) < 0)
+ return code;
+ if (sscanf((const char *)arg, "%ldx%ld", &width, &height) != 2) {
+ puts(minst->heap, "-g must be followed by <width>x<height>");
+ return gs_error_Fatal;
+ }
+ make_int(&value, width);
+ initial_enter_name("DEVICEWIDTH", &value);
+ make_int(&value, height);
+ initial_enter_name("DEVICEHEIGHT", &value);
+ initial_enter_name("FIXEDMEDIA", &vtrue);
+ break;
+ }
+ case 'h': /* print help */
+ case '?': /* ditto */
+ print_help(minst);
+ return gs_error_Info; /* show usage info on exit */
+ case 'I': /* specify search path */
+ {
+ const char *path;
+
+ if (arg[0] == 0) {
+ path = arg_next(pal, &code, minst->heap);
+ if (code < 0)
+ return code;
+ } else
+ path = arg;
+ if (path == NULL)
+ return gs_error_Fatal;
+ path = arg_copy(path, minst->heap);
+ if (path == NULL)
+ return gs_error_Fatal;
+ gs_main_add_lib_path(minst, path);
+ }
+ break;
+ case 'K': /* set malloc limit */
+ {
+ long msize = 0;
+ gs_malloc_memory_t *rawheap = gs_malloc_wrapped_contents(minst->heap);
+
+ sscanf((const char *)arg, "%ld", &msize);
+ if (msize <= 0 || msize > max_long >> 10) {
+ outprintf(minst->heap, "-K<numK> must have 1 <= numK <= %ld\n",
+ max_long >> 10);
+ return gs_error_Fatal;
+ }
+ rawheap->limit = msize << 10;
+ }
+ break;
+ case 'M': /* set memory allocation increment */
+ {
+ unsigned msize = 0;
+
+ sscanf((const char *)arg, "%u", &msize);
+#if ARCH_INTS_ARE_SHORT
+ if (msize <= 0 || msize >= 64) {
+ puts(minst->heap, "-M must be between 1 and 63");
+ return gs_error_Fatal;
+ }
+#endif
+ minst->memory_chunk_size = msize << 10;
+ }
+ break;
+ case 'N': /* set size of name table */
+ {
+ unsigned nsize = 0;
+
+ sscanf((const char *)arg, "%d", &nsize);
+#if ARCH_INTS_ARE_SHORT
+ if (nsize < 2 || nsize > 64) {
+ puts(minst->heap, "-N must be between 2 and 64");
+ return gs_error_Fatal;
+ }
+#endif
+ minst->name_table_size = (ulong) nsize << 10;
+ }
+ break;
+ case 'o': /* set output file name and batch mode */
+ {
+ const char *adef;
+ char *str;
+ ref value;
+ int len;
+
+ if (arg[0] == 0) {
+ adef = arg_next(pal, &code, minst->heap);
+ if (code < 0)
+ return code;
+ } else
+ adef = arg;
+ if ((code = gs_main_init1(minst)) < 0)
+ return code;
+ len = strlen(adef);
+ str = (char *)gs_alloc_bytes(minst->heap, (uint)len, "-o");
+ if (str == NULL)
+ return gs_error_VMerror;
+ memcpy(str, adef, len);
+ make_const_string(&value, a_readonly | avm_foreign,
+ len, (const byte *)str);
+ initial_enter_name("OutputFile", &value);
+ initial_enter_name("NOPAUSE", &vtrue);
+ initial_enter_name("BATCH", &vtrue);
+ }
+ break;
+ case 'P': /* choose whether search '.' first */
+ if (!strcmp(arg, ""))
+ minst->search_here_first = true;
+ else if (!strcmp(arg, "-"))
+ minst->search_here_first = false;
+ else {
+ puts(minst->heap, "Only -P or -P- is allowed.");
+ return gs_error_Fatal;
+ }
+ break;
+ case 'q': /* quiet startup */
+ if ((code = gs_main_init1(minst)) < 0)
+ return code;
+ initial_enter_name("QUIET", &vtrue);
+ break;
+ case 'r': /* define device resolution */
+ {
+ float xres, yres;
+ ref value;
+
+ if ((code = gs_main_init1(minst)) < 0)
+ return code;
+ switch (sscanf((const char *)arg, "%fx%f", &xres, &yres)) {
+ default:
+ puts(minst->heap, "-r must be followed by <res> or <xres>x<yres>");
+ return gs_error_Fatal;
+ case 1: /* -r<res> */
+ yres = xres;
+ case 2: /* -r<xres>x<yres> */
+ make_real(&value, xres);
+ initial_enter_name("DEVICEXRESOLUTION", &value);
+ make_real(&value, yres);
+ initial_enter_name("DEVICEYRESOLUTION", &value);
+ initial_enter_name("FIXEDRESOLUTION", &vtrue);
+ }
+ break;
+ }
+ case 'D': /* define name */
+ case 'd':
+ case 'S': /* define name as string */
+ case 's':
+ {
+ char *adef = arg_copy(arg, minst->heap);
+ char *eqp;
+ bool isd = (sw == 'D' || sw == 'd');
+ ref value;
+
+ if (adef == NULL)
+ return gs_error_Fatal;
+ eqp = strchr(adef, '=');
+
+ if (eqp == NULL)
+ eqp = strchr(adef, '#');
+ /* Initialize the object memory, scanner, and */
+ /* name table now if needed. */
+ if ((code = gs_main_init1(minst)) < 0)
+ return code;
+ if (eqp == adef) {
+ puts(minst->heap, "Usage: -dNAME, -dNAME=TOKEN, -sNAME=STRING");
+ return gs_error_Fatal;
+ }
+ if (eqp == NULL) {
+ if (isd)
+ make_true(&value);
+ else
+ make_empty_string(&value, a_readonly);
+ } else {
+ int code;
+ i_ctx_t *i_ctx_p = minst->i_ctx_p;
+ uint space = icurrent_space;
+
+ *eqp++ = 0;
+ ialloc_set_space(idmemory, avm_system);
+ if (isd) {
+ int num, i;
+
+ /* Check for numbers so we can provide for suffix scalers */
+ /* Note the check for '#' is for PS "radix" numbers such as 16#ff */
+ /* and check for '.' and 'e' or 'E' which are 'real' numbers */
+ if ((strchr(eqp, '#') == NULL) && (strchr(eqp, '.') == NULL) &&
+ (strchr(eqp, 'e') == NULL) && (strchr(eqp, 'E') == NULL) &&
+ ((i = sscanf((const char *)eqp, "%d", &num)) == 1)) {
+ char suffix = eqp[strlen(eqp) - 1];
+
+ switch (suffix) {
+ case 'k':
+ case 'K':
+ num *= 1024;
+ break;
+ case 'm':
+ case 'M':
+ num *= 1024 * 1024;
+ break;
+ case 'g':
+ case 'G':
+ /* caveat emptor: more than 2g will overflow */
+ /* and really should produce a 'real', so don't do this */
+ num *= 1024 * 1024 * 1024;
+ break;
+ default:
+ break; /* not a valid suffix or last char was digit */
+ }
+ make_int(&value, num);
+ } else {
+ /* use the PS scanner to capture other valid token types */
+ stream astream;
+ scanner_state state;
+
+ s_init(&astream, NULL);
+ sread_string(&astream,
+ (const byte *)eqp, strlen(eqp));
+ gs_scanner_init_stream(&state, &astream);
+ code = gs_scan_token(minst->i_ctx_p, &value, &state);
+ if (code) {
+ outprintf(minst->heap, "Invalid value for option -d%s, -dNAME= must be followed by a valid token\n", arg);
+ return gs_error_Fatal;
+ }
+ if (r_has_type_attrs(&value, t_name,
+ a_executable)) {
+ ref nsref;
+
+ name_string_ref(minst->heap, &value, &nsref);
+#undef string_is
+#define string_is(nsref, str, len)\
+ (r_size(&(nsref)) == (len) &&\
+ !strncmp((const char *)(nsref).value.const_bytes, str, (len)))
+ if (string_is(nsref, "null", 4))
+ make_null(&value);
+ else if (string_is(nsref, "true", 4))
+ make_true(&value);
+ else if (string_is(nsref, "false", 5))
+ make_false(&value);
+ else {
+ outprintf(minst->heap, "Invalid value for option -d%s, use -sNAME= to define string constants\n", arg);
+ return gs_error_Fatal;
+ }
+ }
+ }
+ } else {
+ int len = strlen(eqp);
+ char *str =
+ (char *)gs_alloc_bytes(minst->heap,
+ (uint) len, "-s");
+
+ if (str == 0) {
+ lprintf("Out of memory!\n");
+ return gs_error_Fatal;
+ }
+ memcpy(str, eqp, len);
+ make_const_string(&value,
+ a_readonly | avm_foreign,
+ len, (const byte *)str);
+ if ((code = try_stdout_redirect(minst, adef, eqp)) < 0)
+ return code;
+ }
+ ialloc_set_space(idmemory, space);
+ }
+ /* Enter the name in systemdict. */
+ initial_enter_name(adef, &value);
+ break;
+ }
+ case 'T':
+ set_debug_flags(arg, vd_flags);
+ break;
+ case 'u': /* undefine name */
+ if (!*arg) {
+ puts(minst->heap, "-u requires a name to undefine.");
+ return gs_error_Fatal;
+ }
+ if ((code = gs_main_init1(minst)) < 0)
+ return code;
+ i_initial_remove_name(minst->i_ctx_p, arg);
+ break;
+ case 'v': /* print revision */
+ print_revision(minst);
+ return gs_error_Info;
+/*#ifdef DEBUG */
+ /*
+ * Here we provide a place for inserting debugging code that can be
+ * run in place of the normal interpreter code.
+ */
+ case 'X':
+ code = gs_main_init2(minst);
+ if (code < 0)
+ return code;
+ {
+ int xec; /* exit_code */
+ ref xeo; /* error_object */
+
+#define start_x()\
+ gs_main_run_string_begin(minst, 1, &xec, &xeo)
+#define run_x(str)\
+ gs_main_run_string_continue(minst, str, strlen(str), 1, &xec, &xeo)
+#define stop_x()\
+ gs_main_run_string_end(minst, 1, &xec, &xeo)
+ start_x();
+ run_x("\216\003abc");
+ run_x("== flush\n");
+ stop_x();
+ }
+ return gs_error_Quit;
+/*#endif */
+ case 'Z':
+ set_debug_flags(arg, gs_debug);
+ break;
+ }
+ return 0;
+}
+
+/* Define versions of strlen and strcat that encode strings in hex. */
+/* This is so we can enter escaped characters regardless of whether */
+/* the Level 1 convention of ignoring \s in strings-within-strings */
+/* is being observed (sigh). */
+static int
+esc_strlen(const char *str)
+{
+ return strlen(str) * 2 + 2;
+}
+static void
+esc_strcat(char *dest, const char *src)
+{
+ char *d = dest + strlen(dest);
+ const char *p;
+ static const char *const hex = "0123456789abcdef";
+
+ *d++ = '<';
+ for (p = src; *p; p++) {
+ byte c = (byte) * p;
+
+ *d++ = hex[c >> 4];
+ *d++ = hex[c & 0xf];
+ }
+ *d++ = '>';
+ *d = 0;
+}
+
+/* Process file names */
+static int
+argproc(gs_main_instance * minst, const char *arg)
+{
+ int code = gs_main_init1(minst); /* need i_ctx_p to proceed */
+
+ if (code < 0)
+ return code;
+ if (minst->run_buffer_size) {
+ /* Run file with run_string. */
+ return run_buffered(minst, arg);
+ } else {
+ /* Run file directly in the normal way. */
+ return runarg(minst, "", arg, ".runfile", runInit | runFlush);
+ }
+}
+static int
+run_buffered(gs_main_instance * minst, const char *arg)
+{
+ FILE *in = gp_fopen(arg, gp_fmode_rb);
+ int exit_code;
+ ref error_object;
+ int code;
+
+ if (in == 0) {
+ outprintf(minst->heap, "Unable to open %s for reading", arg);
+ return_error(gs_error_invalidfileaccess);
+ }
+ code = gs_main_init2(minst);
+ if (code < 0) {
+ fclose(in);
+ return code;
+ }
+ code = gs_main_run_string_begin(minst, minst->user_errors,
+ &exit_code, &error_object);
+ if (!code) {
+ char buf[MAX_BUFFERED_SIZE];
+ int count;
+
+ code = gs_error_NeedInput;
+ while ((count = fread(buf, 1, minst->run_buffer_size, in)) > 0) {
+ code = gs_main_run_string_continue(minst, buf, count,
+ minst->user_errors,
+ &exit_code, &error_object);
+ if (code != gs_error_NeedInput)
+ break;
+ }
+ if (code == gs_error_NeedInput) {
+ code = gs_main_run_string_end(minst, minst->user_errors,
+ &exit_code, &error_object);
+ }
+ }
+ fclose(in);
+ zflush(minst->i_ctx_p);
+ zflushpage(minst->i_ctx_p);
+ return run_finish(minst, code, exit_code, &error_object);
+}
+static int
+runarg(gs_main_instance * minst, const char *pre, const char *arg,
+ const char *post, int options)
+{
+ int len = strlen(pre) + esc_strlen(arg) + strlen(post) + 1;
+ int code;
+ char *line;
+
+ if (options & runInit) {
+ code = gs_main_init2(minst); /* Finish initialization */
+
+ if (code < 0)
+ return code;
+ }
+ line = (char *)gs_alloc_bytes(minst->heap, len, "runarg");
+ if (line == 0) {
+ lprintf("Out of memory!\n");
+ return_error(gs_error_VMerror);
+ }
+ strcpy(line, pre);
+ esc_strcat(line, arg);
+ strcat(line, post);
+ minst->i_ctx_p->starting_arg_file = true;
+ code = run_string(minst, line, options);
+ minst->i_ctx_p->starting_arg_file = false;
+ gs_free_object(minst->heap, line, "runarg");
+ return code;
+}
+static int
+run_string(gs_main_instance * minst, const char *str, int options)
+{
+ int exit_code;
+ ref error_object;
+ int code = gs_main_run_string(minst, str, minst->user_errors,
+ &exit_code, &error_object);
+
+ if ((options & runFlush) || code != 0) {
+ zflush(minst->i_ctx_p); /* flush stdout */
+ zflushpage(minst->i_ctx_p); /* force display update */
+ }
+ return run_finish(minst, code, exit_code, &error_object);
+}
+static int
+run_finish(gs_main_instance *minst, int code, int exit_code,
+ ref * perror_object)
+{
+ switch (code) {
+ case gs_error_Quit:
+ case 0:
+ break;
+ case gs_error_Fatal:
+ emprintf1(minst->heap,
+ "Unrecoverable error, exit code %d\n",
+ exit_code);
+ break;
+ default:
+ gs_main_dump_stack(minst, code, perror_object);
+ }
+ return code;
+}
+
+/* Redirect stdout to a file:
+ * -sstdout=filename
+ * -sstdout=-
+ * -sstdout=%stdout
+ * -sstdout=%stderr
+ * -sOutputFile=- is not affected.
+ * File is closed at program exit (if not stdout/err)
+ * or when -sstdout is used again.
+ */
+static int
+try_stdout_redirect(gs_main_instance * minst,
+ const char *command, const char *filename)
+{
+ if (strcmp(command, "stdout") == 0) {
+ minst->heap->gs_lib_ctx->stdout_to_stderr = 0;
+ minst->heap->gs_lib_ctx->stdout_is_redirected = 0;
+ /* If stdout already being redirected and it is not stdout
+ * or stderr, close it
+ */
+ if (minst->heap->gs_lib_ctx->fstdout2
+ && (minst->heap->gs_lib_ctx->fstdout2 != minst->heap->gs_lib_ctx->fstdout)
+ && (minst->heap->gs_lib_ctx->fstdout2 != minst->heap->gs_lib_ctx->fstderr)) {
+ fclose(minst->heap->gs_lib_ctx->fstdout2);
+ minst->heap->gs_lib_ctx->fstdout2 = (FILE *)NULL;
+ }
+ /* If stdout is being redirected, set minst->fstdout2 */
+ if ( (filename != 0) && strlen(filename) &&
+ strcmp(filename, "-") && strcmp(filename, "%stdout") ) {
+ if (strcmp(filename, "%stderr") == 0) {
+ minst->heap->gs_lib_ctx->stdout_to_stderr = 1;
+ }
+ else if ((minst->heap->gs_lib_ctx->fstdout2 =
+ gp_fopen(filename, "w")) == (FILE *)NULL)
+ return_error(gs_error_invalidfileaccess);
+ minst->heap->gs_lib_ctx->stdout_is_redirected = 1;
+ }
+ return 0;
+ }
+ return 1;
+}
+
+/* ---------------- Print information ---------------- */
+
+/*
+ * Help strings. We have to break them up into parts, because
+ * the Watcom compiler has a limit of 510 characters for a single token.
+ * For PC displays, we want to limit the strings to 24 lines.
+ */
+static const char help_usage1[] = "\
+Usage: gs [switches] [file1.ps file2.ps ...]\n\
+Most frequently used switches: (you can use # in place of =)\n\
+ -dNOPAUSE no pause after page | -q `quiet', fewer messages\n\
+ -g<width>x<height> page size in pixels | -r<res> pixels/inch resolution\n";
+static const char help_usage2[] = "\
+ -sDEVICE=<devname> select device | -dBATCH exit after last file\n\
+ -sOutputFile=<file> select output file: - for stdout, |command for pipe,\n\
+ embed %d or %ld for page #\n";
+#ifdef DEBUG
+static const char help_debug[] = "\
+ --debug=<option>[,<option>]* select debugging options\n\
+ --debug list debugging options\n";
+#endif
+static const char help_trailer[] = "\
+For more information, see %s.\n\
+Please report bugs to bugs.ghostscript.com.\n";
+static const char help_devices[] = "Available devices:";
+static const char help_default_device[] = "Default output device:";
+static const char help_emulators[] = "Input formats:";
+static const char help_paths[] = "Search path:";
+#ifdef HAVE_FONTCONFIG
+static const char help_fontconfig[] = "Ghostscript is also using fontconfig to search for font files\n";
+#else
+static const char help_fontconfig[] = "";
+#endif
+
+extern_gx_io_device_table();
+
+/* Print the standard help message. */
+static void
+print_help(gs_main_instance * minst)
+{
+ int i, have_rom_device = 0;
+
+ print_revision(minst);
+ print_usage(minst);
+ print_emulators(minst);
+ print_devices(minst);
+ print_paths(minst);
+ /* Check if we have the %rom device */
+ for (i = 0; i < gx_io_device_table_count; i++) {
+ const gx_io_device *iodev = gx_io_device_table[i];
+ const char *dname = iodev->dname;
+
+ if (dname && strlen(dname) == 5 && !memcmp("%rom%", dname, 5)) {
+ have_rom_device = 1;
+ break;
+ }
+ }
+ if (have_rom_device) {
+ outprintf(minst->heap, "Initialization files are compiled into the executable.\n");
+ }
+ print_help_trailer(minst);
+}
+
+/* Print the revision, revision date, and copyright. */
+static void
+print_revision(const gs_main_instance *minst)
+{
+ printf_program_ident(minst->heap, gs_product, gs_revision);
+ outprintf(minst->heap, " (%d-%02d-%02d)\n%s\n",
+ (int)(gs_revisiondate / 10000),
+ (int)(gs_revisiondate / 100 % 100),
+ (int)(gs_revisiondate % 100),
+ gs_copyright);
+}
+
+/* Print the version number. */
+static void
+print_version(const gs_main_instance *minst)
+{
+ printf_program_ident(minst->heap, NULL, gs_revision);
+}
+
+/* Print usage information. */
+static void
+print_usage(const gs_main_instance *minst)
+{
+ outprintf(minst->heap, "%s", help_usage1);
+ outprintf(minst->heap, "%s", help_usage2);
+#ifdef DEBUG
+ outprintf(minst->heap, "%s", help_debug);
+#endif
+}
+
+/* compare function for qsort */
+static int
+cmpstr(const void *v1, const void *v2)
+{
+ return strcmp( *(char * const *)v1, *(char * const *)v2 );
+}
+
+/* Print the list of available devices. */
+static void
+print_devices(const gs_main_instance *minst)
+{
+ outprintf(minst->heap, "%s", help_default_device);
+ outprintf(minst->heap, " %s\n", gs_devicename(gs_getdefaultdevice()));
+ outprintf(minst->heap, "%s", help_devices);
+ {
+ int i;
+ int pos = 100;
+ const gx_device *pdev;
+ const char **names;
+ size_t ndev = 0;
+
+ for (i = 0; gs_getdevice(i) != 0; i++)
+ ;
+ ndev = (size_t)i;
+ names = (const char **)gs_alloc_bytes(minst->heap, ndev * sizeof(const char*), "print_devices");
+ if (names == (const char **)NULL) { /* old-style unsorted device list */
+ for (i = 0; (pdev = gs_getdevice(i)) != 0; i++) {
+ const char *dname = gs_devicename(pdev);
+ int len = strlen(dname);
+
+ if (pos + 1 + len > 76)
+ outprintf(minst->heap, "\n "), pos = 2;
+ outprintf(minst->heap, " %s", dname);
+ pos += 1 + len;
+ }
+ }
+ else { /* new-style sorted device list */
+ for (i = 0; (pdev = gs_getdevice(i)) != 0; i++)
+ names[i] = gs_devicename(pdev);
+ qsort((void*)names, ndev, sizeof(const char*), cmpstr);
+ for (i = 0; i < ndev; i++) {
+ int len = strlen(names[i]);
+
+ if (pos + 1 + len > 76)
+ outprintf(minst->heap, "\n "), pos = 2;
+ outprintf(minst->heap, " %s", names[i]);
+ pos += 1 + len;
+ }
+ gs_free(minst->heap, (char *)names, ndev * sizeof(const char*), 1, "print_devices");
+ }
+ }
+ outprintf(minst->heap, "\n");
+}
+
+/* Print the list of language emulators. */
+static void
+print_emulators(const gs_main_instance *minst)
+{
+ outprintf(minst->heap, "%s", help_emulators);
+ {
+ const byte *s;
+
+ for (s = gs_emulators; s[0] != 0; s += strlen((const char *)s) + 1)
+ outprintf(minst->heap, " %s", s);
+ }
+ outprintf(minst->heap, "\n");
+}
+
+/* Print the search paths. */
+static void
+print_paths(gs_main_instance * minst)
+{
+ outprintf(minst->heap, "%s", help_paths);
+ gs_main_set_lib_paths(minst);
+ {
+ uint count = r_size(&minst->lib_path.list);
+ uint i;
+ int pos = 100;
+ char fsepr[3];
+
+ fsepr[0] = ' ', fsepr[1] = gp_file_name_list_separator,
+ fsepr[2] = 0;
+ for (i = 0; i < count; ++i) {
+ const ref *prdir =
+ minst->lib_path.list.value.refs + i;
+ uint len = r_size(prdir);
+ const char *sepr = (i == count - 1 ? "" : fsepr);
+
+ if (1 + pos + strlen(sepr) + len > 76)
+ outprintf(minst->heap, "\n "), pos = 2;
+ outprintf(minst->heap, " ");
+ /*
+ * This is really ugly, but it's necessary because some
+ * platforms rely on all console output being funneled through
+ * outprintf. We wish we could just do:
+ fwrite(prdir->value.bytes, 1, len, minst->fstdout);
+ */
+ {
+ const char *p = (const char *)prdir->value.bytes;
+ uint j;
+
+ for (j = len; j; j--)
+ outprintf(minst->heap, "%c", *p++);
+ }
+ outprintf(minst->heap, "%s", sepr);
+ pos += 1 + len + strlen(sepr);
+ }
+ }
+ outprintf(minst->heap, "\n");
+ outprintf(minst->heap, "%s", help_fontconfig);
+}
+
+/* Print the help trailer. */
+static void
+print_help_trailer(const gs_main_instance *minst)
+{
+ char buffer[gp_file_name_sizeof];
+ const char *use_htm = "Use.htm", *p = buffer;
+ uint blen = sizeof(buffer);
+
+ if (gp_file_name_combine(gs_doc_directory, strlen(gs_doc_directory),
+ use_htm, strlen(use_htm), false, buffer, &blen) != gp_combine_success)
+ p = use_htm;
+ outprintf(minst->heap, help_trailer, p);
+}
diff --git a/psi/imainarg.h b/psi/imainarg.h
new file mode 100644
index 000000000..4a6302f67
--- /dev/null
+++ b/psi/imainarg.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* argv/argc interface to imainarg.c */
+
+#ifndef imainarg_INCLUDED
+# define imainarg_INCLUDED
+
+/* Define an opaque type for an interpreter instance. See imain.h. */
+#ifndef gs_main_instance_DEFINED
+# define gs_main_instance_DEFINED
+typedef struct gs_main_instance_s gs_main_instance;
+#endif
+
+/*
+ * As a shortcut for very high-level clients, we define a single call
+ * that does the equivalent of command line invocation, passing argc
+ * and argv. This call includes calling init0 through init2.
+ * argv should really be const char *[], but ANSI C requires writable
+ * strings (which, however, it forbids the callee to modify!).
+ */
+int gs_main_init_with_args(gs_main_instance * minst, int argc, char *argv[]);
+
+/*
+ * Run the 'start' procedure (after processing the command line).
+ */
+int gs_main_run_start(gs_main_instance * minst);
+
+/*
+ * By default gs assumes that all args are in utf8 format. If not, the caller
+ * should call this function to specify a decoding function in advance.
+ * Allocation should be done with the standard gs allocation functions, as
+ * the returned string may be freed using the same.
+ */
+typedef int (gs_arg_get_codepoint)(FILE *, const char **);
+
+void gs_main_inst_arg_decode(gs_main_instance * minst,
+ gs_arg_get_codepoint *get_codepoint);
+
+gs_arg_get_codepoint *gs_main_inst_get_arg_decode(gs_main_instance * minst);
+
+#endif /* imainarg_INCLUDED */
diff --git a/psi/imemory.h b/psi/imemory.h
new file mode 100644
index 000000000..4fa87e69e
--- /dev/null
+++ b/psi/imemory.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Ghostscript memory allocator extensions for interpreter level */
+
+#ifndef imemory_INCLUDED
+# define imemory_INCLUDED
+
+#include "ivmspace.h"
+
+/*
+ * The interpreter level of Ghostscript defines a "subclass" extension
+ * of the allocator interface in gsmemory.h, by adding the ability to
+ * allocate and free arrays of refs, and by adding the distinction
+ * between local, global, and system allocation.
+ */
+
+#include "gsalloc.h"
+
+#ifndef gs_ref_memory_DEFINED
+# define gs_ref_memory_DEFINED
+typedef struct gs_ref_memory_s gs_ref_memory_t;
+#endif
+
+ /* Allocate a ref array. */
+
+int gs_alloc_ref_array(gs_ref_memory_t * mem, ref * paref,
+ uint attrs, uint num_refs, client_name_t cname);
+
+ /* Resize a ref array. */
+ /* Currently this is only implemented for shrinking, */
+ /* not growing. */
+
+int gs_resize_ref_array(gs_ref_memory_t * mem, ref * paref,
+ uint new_num_refs, client_name_t cname);
+
+ /* Free a ref array. */
+
+void gs_free_ref_array(gs_ref_memory_t * mem, ref * paref,
+ client_name_t cname);
+
+ /* Allocate a string ref. */
+
+int gs_alloc_string_ref(gs_ref_memory_t * mem, ref * psref,
+ uint attrs, uint nbytes, client_name_t cname);
+
+/* Register a ref root. This just calls gs_register_root. */
+/* Note that ref roots are a little peculiar: they assume that */
+/* the ref * that they point to points to a *statically* allocated ref. */
+int gs_register_ref_root(gs_memory_t *mem, gs_gc_root_t *root,
+ void **pp, client_name_t cname);
+
+/*
+ * The interpreter allocator can allocate in either local or global VM,
+ * and can switch between the two dynamically. In Level 1 configurations,
+ * global VM is the same as local; however, this is *not* currently true in
+ * a Level 2 system running in Level 1 mode. In addition, there is a third
+ * VM space, system VM, that exists in both modes and is used for objects
+ * that must not be affected by even the outermost save/restore (stack
+ * segments and names).
+ *
+ * NOTE: since the interpreter's (only) instances of gs_dual_memory_t are
+ * embedded in-line in context state structures, pointers to these
+ * instances must not be stored anywhere that might persist across a
+ * garbage collection.
+ */
+#ifndef gs_dual_memory_DEFINED
+# define gs_dual_memory_DEFINED
+typedef struct gs_dual_memory_s gs_dual_memory_t;
+#endif
+struct gs_dual_memory_s {
+ gs_ref_memory_t *current; /* = ...global or ...local */
+ vm_spaces spaces; /* system, global, local */
+ uint current_space; /* = current->space */
+ /* Garbage collection hook */
+ int (*reclaim) (gs_dual_memory_t *, int);
+ /* Masks for store checking, see isave.h. */
+ uint test_mask;
+ uint new_mask;
+};
+
+#define public_st_gs_dual_memory() /* in ialloc.c */\
+ gs_public_st_simple(st_gs_dual_memory, gs_dual_memory_t, "gs_dual_memory_t")
+#define st_gs_dual_memory_num_ptrs 0
+
+#endif /* imemory_INCLUDED */
diff --git a/psi/iminst.h b/psi/iminst.h
new file mode 100644
index 000000000..1e8012330
--- /dev/null
+++ b/psi/iminst.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definition of interpreter instance */
+/* Requires stdio_.h, gsmemory.h, iref.h, iapi.h */
+
+#ifndef iminst_INCLUDED
+# define iminst_INCLUDED
+
+#ifndef gs_main_instance_DEFINED
+# define gs_main_instance_DEFINED
+typedef struct gs_main_instance_s gs_main_instance;
+#endif
+
+/*
+ * Define the structure of a search path. Currently there is only one,
+ * but there might be more someday.
+ *
+ * container - an array large enough to hold the specified maximum
+ * number of directories. Both the array and all the strings in it are
+ * in the 'foreign' VM space.
+ * list - the initial interval of container that defines the actual
+ * search list.
+ * env - the contents of an environment variable, implicitly added
+ * at the end of the list; may be 0.
+ * final - the final set of directories specified in the makefile;
+ * may be 0.
+ * count - the number of elements in the list, excluding a possible
+ * initial '.', env, and final.
+ */
+typedef struct gs_file_path_s {
+ ref container;
+ ref list;
+ const char *env;
+ const char *final;
+ uint count;
+} gs_file_path;
+
+/*
+ * Here is where we actually define the structure of interpreter instances.
+ * Clients should not reference any of the members. Note that in order to
+ * be able to initialize this structure statically, members including
+ * unions must come last (and be initialized to 0 by default).
+ */
+struct gs_main_instance_s {
+ /* The following are set during initialization. */
+ gs_memory_t *heap; /* (C) heap allocator */
+ uint memory_chunk_size; /* 'wholesale' allocation unit */
+ ulong name_table_size;
+ uint run_buffer_size;
+ int init_done; /* highest init done so far */
+ int user_errors; /* define what to do with errors */
+ bool search_here_first; /* if true, make '.' first lib dir */
+ bool run_start; /* if true, run 'start' after */
+ /* processing command line */
+ gs_file_path lib_path; /* library search list (GS_LIB) */
+ long base_time[2]; /* starting usertime */
+ void *readline_data; /* data for gp_readline */
+ ref error_object; /* Use by gsapi_*() */
+ int (*get_codepoint)(FILE *file, const char **astr);
+ /* Get next 'unicode' codepoint (in
+ * GS_NO_UTF8 builds, these will actually
+ * just be 8 bit clean values). */
+ display_callback *display; /* callback structure for display device */
+ /* The following are updated dynamically. */
+ i_ctx_t *i_ctx_p; /* current interpreter context state */
+ char *saved_pages_initial_arg; /* used to defer processing of --saved-pages=begin... */
+ bool saved_pages_test_mode; /* for regression testing of saved-pages */
+};
+
+/*
+ * Note that any file that uses the following definition of default values
+ * must include gconfig.h, because of SEARCH_HERE_FIRST.
+ */
+#define gs_main_instance_default_init_values\
+ 0/* heap */, 20000/* chunk_size */, 0/* name_table_size */, 0/* run_buffer_size */,\
+ -1/* init_done */, 0/* user_errors */, SEARCH_HERE_FIRST/* duh */, 1/* run_start */,
+
+extern const gs_main_instance gs_main_instance_init_values;
+
+#endif /* iminst_INCLUDED */
diff --git a/psi/iname.c b/psi/iname.c
new file mode 100644
index 000000000..88cc938fe
--- /dev/null
+++ b/psi/iname.c
@@ -0,0 +1,661 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Name lookup for Ghostscript interpreter */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gsstruct.h"
+#include "gxobj.h" /* for o_set_unmarked */
+#include "ierrors.h"
+#include "inamedef.h"
+#include "imemory.h" /* for isave.h */
+#include "isave.h"
+#include "store.h"
+
+/* Public values */
+const uint name_max_string = max_name_string;
+
+/* Define the permutation table for name hashing. */
+static const byte hash_permutation[256] = {
+ NAME_HASH_PERMUTATION_DATA
+};
+
+/* Define the data for the 1-character names. */
+static const byte nt_1char_names[NT_1CHAR_SIZE] = {
+ NT_1CHAR_NAMES_DATA
+};
+
+static void gs_names_finalize(const gs_memory_t *, void *);
+
+/* Structure descriptors */
+gs_private_st_simple(st_name_sub_table, name_sub_table, "name_sub_table");
+gs_private_st_composite(st_name_string_sub_table, name_string_sub_table_t,
+ "name_string_sub_table_t",
+ name_string_sub_enum_ptrs, name_string_sub_reloc_ptrs);
+gs_private_st_composite_final(st_name_table, name_table, "name_table",
+ name_table_enum_ptrs, name_table_reloc_ptrs,gs_names_finalize);
+
+/* Forward references */
+static int name_alloc_sub(name_table *);
+static void name_free_sub(name_table *, uint, bool);
+static void name_scan_sub(name_table *, uint, bool, bool);
+
+/* Debugging printout */
+#ifdef DEBUG
+static void
+name_print(const char *msg, const name_table *nt, uint nidx, const int *pflag)
+{
+ const name_string_t *pnstr = names_index_string_inline(nt, nidx);
+ const name *pname = names_index_ptr_inline(nt, nidx);
+ const byte *str = pnstr->string_bytes;
+
+ dmlprintf1(nt->memory, "[n]%s", msg);
+ if (pflag)
+ dmprintf1(nt->memory, "(%d)", *pflag);
+ dmprintf2(nt->memory, " (0x%lx#%u)", (ulong)pname, nidx);
+ debug_print_string(nt->memory, str, pnstr->string_size);
+ dmprintf2(nt->memory, "(0x%lx,%u)\n", (ulong)str, pnstr->string_size);
+}
+# define if_debug_name(msg, nt, nidx, pflag)\
+ if ( gs_debug_c('n') ) name_print(msg, nt, nidx, pflag)
+#else
+# define if_debug_name(msg, nt, nidx, pflag) DO_NOTHING
+#endif
+
+/* Initialize a name table */
+name_table *
+names_init(ulong count, gs_ref_memory_t *imem)
+{
+ gs_memory_t *mem = (gs_memory_t *)imem;
+ name_table *nt;
+ int i;
+
+ if (count == 0)
+ count = max_name_count + 1L;
+ else if (count - 1 > max_name_count)
+ return 0;
+ nt = gs_alloc_struct(mem, name_table, &st_name_table, "name_init(nt)");
+ if (nt == 0)
+ return 0;
+ memset(nt, 0, sizeof(name_table));
+ nt->max_sub_count =
+ ((count - 1) | nt_sub_index_mask) >> nt_log2_sub_size;
+ nt->name_string_attrs = imemory_space(imem) | a_readonly;
+ nt->memory = mem;
+ /* Initialize the one-character names. */
+ /* Start by creating the necessary sub-tables. */
+ for (i = 0; i < NT_1CHAR_FIRST + NT_1CHAR_SIZE; i += nt_sub_size) {
+ int code = name_alloc_sub(nt);
+
+ if (code < 0) {
+ while (nt->sub_next > 0)
+ name_free_sub(nt, --(nt->sub_next), false);
+ gs_free_object(mem, nt, "name_init(nt)");
+ return 0;
+ }
+ }
+ for (i = -1; i < NT_1CHAR_SIZE; i++) {
+ uint ncnt = NT_1CHAR_FIRST + i;
+ uint nidx = name_count_to_index(ncnt);
+ name *pname = names_index_ptr_inline(nt, nidx);
+ name_string_t *pnstr = names_index_string_inline(nt, nidx);
+
+ if (i < 0)
+ pnstr->string_bytes = nt_1char_names,
+ pnstr->string_size = 0;
+ else
+ pnstr->string_bytes = nt_1char_names + i,
+ pnstr->string_size = 1;
+ pnstr->foreign_string = 1;
+ pnstr->mark = 1;
+ pname->pvalue = pv_no_defn;
+ }
+ nt->perm_count = NT_1CHAR_FIRST + NT_1CHAR_SIZE;
+ /* Reconstruct the free list. */
+ nt->free = 0;
+ names_trace_finish(nt, NULL);
+ return nt;
+}
+
+static void
+gs_names_finalize(const gs_memory_t *cmem, void *vptr)
+{
+ (void)vptr; /* unused */
+ cmem->gs_lib_ctx->gs_name_table = NULL;
+}
+
+/* Get the allocator for the name table. */
+gs_memory_t *
+names_memory(const name_table * nt)
+{
+ return nt->memory;
+}
+
+/* Look up or enter a name in the table. */
+/* Return 0 or an error code. */
+/* The return may overlap the characters of the string! */
+/* See iname.h for the meaning of enterflag. */
+int
+names_ref(name_table *nt, const byte *ptr, uint size, ref *pref, int enterflag)
+{
+ name *pname;
+ name_string_t *pnstr;
+ uint nidx;
+ uint *phash;
+
+ /* Compute a hash for the string. */
+ /* Make a special check for 1-character names. */
+ switch (size) {
+ case 0:
+ nidx = name_count_to_index(1);
+ pname = names_index_ptr_inline(nt, nidx);
+ goto mkn;
+ case 1:
+ if (*ptr < NT_1CHAR_SIZE) {
+ uint hash = *ptr + NT_1CHAR_FIRST;
+
+ nidx = name_count_to_index(hash);
+ pname = names_index_ptr_inline(nt, nidx);
+ goto mkn;
+ }
+ /* falls through */
+ default: {
+ uint hash;
+
+ NAME_HASH(hash, hash_permutation, ptr, size);
+ phash = nt->hash + (hash & (NT_HASH_SIZE - 1));
+ }
+ }
+
+ for (nidx = *phash; nidx != 0;
+ nidx = name_next_index(nidx, pnstr)
+ ) {
+ pnstr = names_index_string_inline(nt, nidx);
+ if (pnstr->string_size == size &&
+ !memcmp_inline(ptr, pnstr->string_bytes, size)
+ ) {
+ pname = name_index_ptr_inline(nt, nidx);
+ goto mkn;
+ }
+ }
+ /* Name was not in the table. Make a new entry. */
+ if (enterflag < 0)
+ return_error(gs_error_undefined);
+ if (size > max_name_string)
+ return_error(gs_error_limitcheck);
+ nidx = nt->free;
+ if (nidx == 0) {
+ int code = name_alloc_sub(nt);
+
+ if (code < 0)
+ return code;
+ nidx = nt->free;
+ }
+ pnstr = names_index_string_inline(nt, nidx);
+ if (enterflag == 1) {
+ byte *cptr = (byte *)gs_alloc_string(nt->memory, size,
+ "names_ref(string)");
+
+ if (cptr == 0)
+ return_error(gs_error_VMerror);
+ memcpy(cptr, ptr, size);
+ pnstr->string_bytes = cptr;
+ pnstr->foreign_string = 0;
+ } else {
+ pnstr->string_bytes = ptr;
+ pnstr->foreign_string = (enterflag == 0 ? 1 : 0);
+ }
+ pnstr->string_size = size;
+ pname = name_index_ptr_inline(nt, nidx);
+ pname->pvalue = pv_no_defn;
+ nt->free = name_next_index(nidx, pnstr);
+ set_name_next_index(nidx, pnstr, *phash);
+ *phash = nidx;
+ if_debug_name("new name", nt, nidx, &enterflag);
+ mkn:
+ make_name(pref, nidx, pname);
+ return 0;
+}
+
+/* Get the string for a name. */
+void
+names_string_ref(const name_table * nt, const ref * pnref /* t_name */ ,
+ ref * psref /* result, t_string */ )
+{
+ const name_string_t *pnstr = names_string_inline(nt, pnref);
+
+ make_const_string(psref,
+ (pnstr->foreign_string ? avm_foreign | a_readonly :
+ nt->name_string_attrs),
+ pnstr->string_size,
+ (const byte *)pnstr->string_bytes);
+}
+
+/* Convert a t_string object to a name. */
+/* Copy the executable attribute. */
+int
+names_from_string(name_table * nt, const ref * psref, ref * pnref)
+{
+ int exec = r_has_attr(psref, a_executable);
+ int code = names_ref(nt, psref->value.bytes, r_size(psref), pnref, 1);
+
+ if (code < 0)
+ return code;
+ if (exec)
+ r_set_attrs(pnref, a_executable);
+ return code;
+}
+
+/* Enter a (permanently allocated) C string as a name. */
+int
+names_enter_string(name_table * nt, const char *str, ref * pref)
+{
+ return names_ref(nt, (const byte *)str, strlen(str), pref, 0);
+}
+
+/* Invalidate the value cache for a name. */
+void
+names_invalidate_value_cache(name_table * nt, const ref * pnref)
+{
+ pnref->value.pname->pvalue = pv_other;
+}
+
+/* Convert between names and indices. */
+#undef names_index
+name_index_t
+names_index(const name_table * nt, const ref * pnref)
+{
+ return names_index_inline(nt, pnref);
+}
+void
+names_index_ref(const name_table * nt, name_index_t index, ref * pnref)
+{
+ names_index_ref_inline(nt, index, pnref);
+}
+name *
+names_index_ptr(const name_table * nt, name_index_t index)
+{
+ return names_index_ptr_inline(nt, index);
+}
+
+/* Get the index of the next valid name. */
+/* The argument is 0 or a valid index. */
+/* Return 0 if there are no more. */
+name_index_t
+names_next_valid_index(name_table * nt, name_index_t nidx)
+{
+ const name_string_sub_table_t *ssub =
+ nt->sub[nidx >> nt_log2_sub_size].strings;
+ const name_string_t *pnstr;
+
+ do {
+ ++nidx;
+ if ((nidx & nt_sub_index_mask) == 0)
+ for (;; nidx += nt_sub_size) {
+ if ((nidx >> nt_log2_sub_size) >= nt->sub_count)
+ return 0;
+ ssub = nt->sub[nidx >> nt_log2_sub_size].strings;
+ if (ssub != 0)
+ break;
+ }
+ pnstr = &ssub->strings[nidx & nt_sub_index_mask];
+ }
+ while (pnstr->string_bytes == 0);
+ return nidx;
+}
+
+/* ------ Garbage collection ------ */
+
+/* Unmark all non-permanent names before a garbage collection. */
+void
+names_unmark_all(name_table * nt)
+{
+ uint si;
+ name_string_sub_table_t *ssub;
+
+ for (si = 0; si < nt->sub_count; ++si)
+ if ((ssub = nt->sub[si].strings) != 0) {
+ uint i;
+
+ /* We can make the test much more efficient if we want.... */
+ for (i = 0; i < nt_sub_size; ++i)
+ if (name_index_to_count((si << nt_log2_sub_size) + i) >=
+ nt->perm_count)
+ ssub->strings[i].mark = 0;
+ }
+}
+
+/* Mark a name. Return true if new mark. We export this so we can mark */
+/* character names in the character cache. */
+bool
+names_mark_index(name_table * nt, name_index_t nidx)
+{
+ name_string_t *pnstr = names_index_string_inline(nt, nidx);
+
+ if (pnstr->mark)
+ return false;
+ pnstr->mark = 1;
+ return true;
+}
+
+/* Get the object (sub-table) containing a name. */
+/* The garbage collector needs this so it can relocate pointers to names. */
+void /*obj_header_t */ *
+names_ref_sub_table(name_table * nt, const ref * pnref)
+{
+ /* When this procedure is called, the pointers from the name table */
+ /* to the sub-tables may or may not have been relocated already, */
+ /* so we can't use them. Instead, we have to work backwards from */
+ /* the name pointer itself. */
+ return pnref->value.pname - (r_size(pnref) & nt_sub_index_mask);
+}
+void /*obj_header_t */ *
+names_index_sub_table(name_table * nt, name_index_t index)
+{
+ return nt->sub[index >> nt_log2_sub_size].names;
+}
+void /*obj_header_t */ *
+names_index_string_sub_table(name_table * nt, name_index_t index)
+{
+ return nt->sub[index >> nt_log2_sub_size].strings;
+}
+
+/*
+ * Clean up the name table after the trace/mark phase of a garbage
+ * collection, by removing names that aren't marked. gcst == NULL indicates
+ * we're doing this for initialization or restore rather than for a GC.
+ */
+void
+names_trace_finish(name_table * nt, gc_state_t * gcst)
+{
+ uint *phash = &nt->hash[0];
+ int i;
+
+ for (i = 0; i < NT_HASH_SIZE; phash++, i++) {
+ name_index_t prev = 0;
+ /*
+ * The following initialization is only to pacify compilers:
+ * pnprev is only referenced if prev has been set in the loop,
+ * in which case pnprev is also set.
+ */
+ name_string_t *pnprev = 0;
+ name_index_t nidx = *phash;
+
+ while (nidx != 0) {
+ name_string_t *pnstr = names_index_string_inline(nt, nidx);
+ name_index_t next = name_next_index(nidx, pnstr);
+
+ if (pnstr->mark) {
+ prev = nidx;
+ pnprev = pnstr;
+ } else {
+ if_debug_name("GC remove name", nt, nidx, NULL);
+ /* Zero out the string data for the GC. */
+ pnstr->string_bytes = 0;
+ pnstr->string_size = 0;
+ if (prev == 0)
+ *phash = next;
+ else
+ set_name_next_index(prev, pnprev, next);
+ }
+ nidx = next;
+ }
+ }
+ /* Reconstruct the free list. */
+ nt->free = 0;
+ for (i = nt->sub_count; --i >= 0;) {
+ name_sub_table *sub = nt->sub[i].names;
+
+ if (sub != 0) {
+ name_scan_sub(nt, i, true, true && (gcst != 0));
+ }
+ }
+ nt->sub_next = 0;
+}
+
+/* ------ Save/restore ------ */
+
+/* Clean up the name table before a restore. */
+/* Currently, this is never called, because the name table is allocated */
+/* in system VM. However, for a Level 1 system, we might choose to */
+/* allocate the name table in global VM; in this case, this routine */
+/* would be called before doing the global part of a top-level restore. */
+/* Currently we don't make any attempt to optimize this. */
+void
+names_restore(name_table * nt, alloc_save_t * save)
+{
+ /* We simply mark all names older than the save, */
+ /* and let names_trace_finish sort everything out. */
+ uint si;
+
+ for (si = 0; si < nt->sub_count; ++si)
+ if (nt->sub[si].strings != 0) {
+ uint i;
+
+ for (i = 0; i < nt_sub_size; ++i) {
+ name_string_t *pnstr =
+ names_index_string_inline(nt, (si << nt_log2_sub_size) + i);
+
+ if (pnstr->string_bytes == 0)
+ pnstr->mark = 0;
+ else if (pnstr->foreign_string) {
+ /* Avoid storing into a read-only name string. */
+ if (!pnstr->mark)
+ pnstr->mark = 1;
+ } else
+ pnstr->mark =
+ !alloc_is_since_save(pnstr->string_bytes, save);
+ }
+ }
+ names_trace_finish(nt, NULL);
+}
+
+/* ------ Internal procedures ------ */
+
+/* Allocate the next sub-table. */
+static int
+name_alloc_sub(name_table * nt)
+{
+ gs_memory_t *mem = nt->memory;
+ uint sub_index = nt->sub_next;
+ name_sub_table *sub;
+ name_string_sub_table_t *ssub;
+
+ for (;; ++sub_index) {
+ if (sub_index > nt->max_sub_count)
+ return_error(gs_error_limitcheck);
+ if (nt->sub[sub_index].names == 0)
+ break;
+ }
+ nt->sub_next = sub_index + 1;
+ if (nt->sub_next > nt->sub_count)
+ nt->sub_count = nt->sub_next;
+ sub = gs_alloc_struct(mem, name_sub_table, &st_name_sub_table,
+ "name_alloc_sub(sub-table)");
+ ssub = gs_alloc_struct(mem, name_string_sub_table_t,
+ &st_name_string_sub_table,
+ "name_alloc_sub(string sub-table)");
+ if (sub == 0 || ssub == 0) {
+ gs_free_object(mem, ssub, "name_alloc_sub(string sub-table)");
+ gs_free_object(mem, sub, "name_alloc_sub(sub-table)");
+ return_error(gs_error_VMerror);
+ }
+ memset(sub, 0, sizeof(name_sub_table));
+ memset(ssub, 0, sizeof(name_string_sub_table_t));
+ /* The following code is only used if EXTEND_NAMES is non-zero. */
+#if name_extension_bits > 0
+ sub->high_index = (sub_index >> (16 - nt_log2_sub_size)) << 16;
+#endif
+ nt->sub[sub_index].names = sub;
+ nt->sub[sub_index].strings = ssub;
+ /* Add the newly allocated entries to the free list. */
+ /* Note that the free list will only be properly sorted if */
+ /* it was empty initially. */
+ name_scan_sub(nt, sub_index, false, false);
+#ifdef DEBUG
+ if (gs_debug_c('n')) { /* Print the lengths of the hash chains. */
+ int i0;
+
+ for (i0 = 0; i0 < NT_HASH_SIZE; i0 += 16) {
+ int i;
+
+ dmlprintf1(mem, "[n]chain %d:", i0);
+ for (i = i0; i < i0 + 16; i++) {
+ int n = 0;
+ uint nidx;
+
+ for (nidx = nt->hash[i]; nidx != 0;
+ nidx = name_next_index(nidx,
+ names_index_string_inline(nt, nidx))
+ )
+ n++;
+ dmprintf1(mem, " %d", n);
+ }
+ dmputc(mem, '\n');
+ }
+ }
+#endif
+ return 0;
+}
+
+/* Free a sub-table. */
+static void
+name_free_sub(name_table * nt, uint sub_index, bool unmark)
+{
+ /* If the subtable is in a previous save level, gs_free_object()
+ * may not actually free the memory, in case that happens, we need
+ * to explicitly remove the gc mark if requested.
+ */
+ if (unmark) {
+ name_sub_table *sub = nt->sub[sub_index].names;
+ name_string_sub_table_t *ssub = nt->sub[sub_index].strings;
+
+ o_set_unmarked((obj_header_t *)sub - 1);
+ o_set_unmarked((obj_header_t *)ssub - 1);
+ }
+
+ gs_free_object(nt->memory, nt->sub[sub_index].strings,
+ "name_free_sub(string sub-table)");
+ gs_free_object(nt->memory, nt->sub[sub_index].names,
+ "name_free_sub(sub-table)");
+ nt->sub[sub_index].names = 0;
+ nt->sub[sub_index].strings = 0;
+}
+
+/* Scan a sub-table and add unmarked entries to the free list. */
+/* We add the entries in decreasing count order, so the free list */
+/* will stay sorted. If all entries are unmarked and free_empty is true, */
+/* free the sub-table. */
+static void
+name_scan_sub(name_table * nt, uint sub_index, bool free_empty, bool unmark)
+{
+ name_string_sub_table_t *ssub = nt->sub[sub_index].strings;
+ uint free = nt->free;
+ uint nbase = sub_index << nt_log2_sub_size;
+ uint ncnt = nbase + (nt_sub_size - 1);
+ bool keep = !free_empty;
+
+ if (ssub == 0)
+ return;
+ if (nbase == 0)
+ nbase = 1, keep = true; /* don't free name 0 */
+ for (;; --ncnt) {
+ uint nidx = name_count_to_index(ncnt);
+ name_string_t *pnstr = &ssub->strings[nidx & nt_sub_index_mask];
+
+ if (pnstr->mark)
+ keep = true;
+ else {
+ set_name_next_index(nidx, pnstr, free);
+ free = nidx;
+ }
+ if (ncnt == nbase)
+ break;
+ }
+ if (keep)
+ nt->free = free;
+ else {
+ /* No marked entries, free the sub-table. */
+ name_free_sub(nt, sub_index, unmark);
+ if (sub_index == nt->sub_count - 1) {
+ /* Back up over a final run of deleted sub-tables. */
+ do {
+ --sub_index;
+ } while (nt->sub[sub_index].names == 0);
+ nt->sub_count = sub_index + 1;
+ if (nt->sub_next > sub_index)
+ nt->sub_next = sub_index;
+ } else if (nt->sub_next == sub_index)
+ nt->sub_next--;
+ }
+}
+
+/* Garbage collector enumeration and relocation procedures. */
+static
+ENUM_PTRS_BEGIN_PROC(name_table_enum_ptrs)
+{
+ EV_CONST name_table *const nt = vptr;
+ uint i = index >> 1;
+
+ if (i >= nt->sub_count)
+ return 0;
+ if (index & 1)
+ ENUM_RETURN(nt->sub[i].strings);
+ else
+ ENUM_RETURN(nt->sub[i].names);
+}
+ENUM_PTRS_END_PROC
+static RELOC_PTRS_WITH(name_table_reloc_ptrs, name_table *nt)
+{
+ uint sub_count = nt->sub_count;
+ uint i;
+
+ /* Now we can relocate the sub-table pointers. */
+ for (i = 0; i < sub_count; i++) {
+ RELOC_VAR(nt->sub[i].names);
+ RELOC_VAR(nt->sub[i].strings);
+ }
+ /*
+ * We also need to relocate the cached value pointers.
+ * We don't do this here, but in a separate scan over the
+ * permanent dictionaries, at the very end of garbage collection.
+ */
+}
+RELOC_PTRS_END
+
+static ENUM_PTRS_BEGIN_PROC(name_string_sub_enum_ptrs)
+{
+ return 0;
+}
+ENUM_PTRS_END_PROC
+static RELOC_PTRS_BEGIN(name_string_sub_reloc_ptrs)
+{
+ name_string_t *pnstr = ((name_string_sub_table_t *)vptr)->strings;
+ uint i;
+
+ for (i = 0; i < nt_sub_size; ++pnstr, ++i) {
+ if (pnstr->string_bytes != 0 && !pnstr->foreign_string) {
+ gs_const_string nstr;
+
+ nstr.data = pnstr->string_bytes;
+ nstr.size = pnstr->string_size;
+ RELOC_CONST_STRING_VAR(nstr);
+ pnstr->string_bytes = nstr.data;
+ }
+ }
+}
+RELOC_PTRS_END
diff --git a/psi/iname.h b/psi/iname.h
new file mode 100644
index 000000000..45fae14bc
--- /dev/null
+++ b/psi/iname.h
@@ -0,0 +1,88 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter's name table interface */
+
+#ifndef iname_INCLUDED
+# define iname_INCLUDED
+
+#include "inames.h"
+
+/*
+ * This file defines those parts of the name table API that refer to the
+ * interpreter's distinguished instance. Procedures in this file begin
+ * with name_.
+ */
+
+/* ---------------- Procedural interface ---------------- */
+
+/* Get the allocator for the name table. */
+#define name_memory(mem)\
+ names_memory(mem->gs_lib_ctx->gs_name_table)
+
+/*
+ * Look up and/or enter a name in the name table.
+ * See inames.h for the values of enterflag, and the possible return values.
+ */
+#define name_ref(mem, ptr, size, pnref, enterflag)\
+ names_ref(mem->gs_lib_ctx->gs_name_table, ptr, size, pnref, enterflag)
+#define name_string_ref(mem, pnref, psref)\
+ names_string_ref(mem->gs_lib_ctx->gs_name_table, pnref, psref)
+/*
+ * name_enter_string calls name_ref with a (permanent) C string.
+ */
+#define name_enter_string(mem, str, pnref)\
+ names_enter_string(mem->gs_lib_ctx->gs_name_table, str, pnref)
+/*
+ * name_from_string essentially implements cvn.
+ * It always enters the name, and copies the executable attribute.
+ */
+#define name_from_string(mem, psref, pnref)\
+ names_from_string(mem->gs_lib_ctx->gs_name_table, psref, pnref)
+
+/* Compare two names for equality. */
+#define name_eq(pnref1, pnref2)\
+ names_eq(pnref1, pnref2)
+
+/* Invalidate the value cache for a name. */
+#define name_invalidate_value_cache(mem, pnref)\
+ names_invalidate_value_cache(mem->gs_lib_ctx->gs_name_table, pnref)
+
+/* Convert between names and indices. */
+#define name_index(mem, pnref) /* ref => index */\
+ names_index(mem->gs_lib_ctx->gs_name_table, pnref)
+#define name_index_ptr(mem, nidx) /* index => name */\
+ names_index_ptr(mem->gs_lib_ctx->gs_name_table, nidx)
+#define name_index_ref(mem, nidx, pnref) /* index => ref */\
+ names_index_ref(mem->gs_lib_ctx->gs_name_table, nidx, pnref)
+
+/* Get the index of the next valid name. */
+/* The argument is 0 or a valid index. */
+/* Return 0 if there are no more. */
+#define name_next_valid_index(mem, nidx)\
+ names_next_valid_index(mem->gs_lib_ctx->gs_name_table, nidx)
+
+/* Mark a name for the garbage collector. */
+/* Return true if this is a new mark. */
+#define name_mark_index(mem, nidx)\
+ names_mark_index(mem->gs_lib_ctx->gs_name_table, nidx)
+
+/* Get the object (sub-table) containing a name. */
+/* The garbage collector needs this so it can relocate pointers to names. */
+#define name_ref_sub_table(mem, pnref)\
+ names_ref_sub_table(mem->gs_lib_ctx->gs_name_table, pnref)
+
+#endif /* iname_INCLUDED */
diff --git a/psi/inamedef.h b/psi/inamedef.h
new file mode 100644
index 000000000..8e917ece2
--- /dev/null
+++ b/psi/inamedef.h
@@ -0,0 +1,171 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Name table definition */
+
+#ifndef inamedef_INCLUDED
+# define inamedef_INCLUDED
+
+#include "inameidx.h"
+#include "inamestr.h"
+#include "inames.h"
+#include "gsstruct.h" /* for gc_state_t */
+
+/*
+ * The name table machinery has two slightly different configurations:
+ * a faster one that limits the total number of names to 64K and allows
+ * names up to 16K in size, and a slightly slower one that limits
+ * the total to 4M and restricts names to 256 characters.
+ * The maximum number of names is 2^(16+EXTEND_NAMES)-1.
+ * By default, EXTEND_NAMES == 4, which coresponds to a slower algorithm,
+ * the total of 1M names up to 1K characters long.
+ */
+#define max_name_extension_bits 6
+#if EXTEND_NAMES > max_name_extension_bits
+# undef EXTEND_NAMES
+# define EXTEND_NAMES max_name_extension_bits
+#endif
+/*
+ * We capture the small algorithmic differences between these two
+ * configurations entirely in this header file;
+ * the implementation doesn't need any conditionals on EXTEND_NAMES.
+ */
+#define max_name_index (uint)((0x10000 << EXTEND_NAMES) - 1)
+/* As explained below, we distinguish name indices from name counts. */
+#define max_name_count max_name_index
+
+/* ---------------- Structure definitions ---------------- */
+
+/*
+ * Define the structure of a name. The pvalue member implements an
+ * important optimization to avoid lookup for operator and other global
+ * names.
+ */
+struct name_s {
+/* pvalue specifies the definition status of the name: */
+/* pvalue == pv_no_defn: no definitions */
+#define pv_no_defn ((ref *)0)
+/* pvalue == pv_other: other status */
+#define pv_other ((ref *)1)
+/* pvalue != pv_no_defn, pvalue != pv_other: pvalue is valid */
+#define pv_valid(pvalue) ((unsigned long)(pvalue) > 1)
+ ref *pvalue; /* if only defined in systemdict or */
+ /* userdict, this points to the value */
+};
+
+/*typedef struct name_s name; *//* in iref.h */
+
+/*
+ * Define the structure of a name table. Normally we would make this
+ * an opaque type, but we want to be able to in-line some of the
+ * access procedures.
+ *
+ * The name table is a two-level indexed table, consisting of
+ * sub-tables of size nt_sub_size each.
+ *
+ * First we define the name sub-table structure.
+ */
+#define nt_log2_sub_size NT_LOG2_SUB_SIZE /* in inameidx.h */
+# define nt_sub_size (1 << nt_log2_sub_size)
+# define nt_sub_index_mask (nt_sub_size - 1)
+typedef struct name_sub_table_s {
+ name names[NT_SUB_SIZE]; /* must be first */
+#if EXTEND_NAMES
+ uint high_index; /* sub-table base index & (-1 << 16) */
+#endif
+} name_sub_table;
+
+/*
+ * Now define the name table itself.
+ * This must be made visible so that the interpreter can use the
+ * inline macros defined below.
+ */
+struct name_table_s {
+ uint free; /* head of free list, which is sorted in */
+ /* increasing count (not index) order */
+ uint sub_next; /* index of next sub-table to allocate */
+ /* if not already allocated */
+ uint perm_count; /* # of permanent (read-only) strings */
+ uint sub_count; /* index of highest allocated sub-table +1 */
+ uint max_sub_count; /* max allowable value of sub_count */
+ uint name_string_attrs; /* imemory_space(memory) | a_readonly */
+ gs_memory_t *memory;
+ uint hash[NT_HASH_SIZE];
+ struct sub_ { /* both ptrs are 0 or both are non-0 */
+ name_sub_table *names;
+ name_string_sub_table_t *strings;
+ } sub[max_name_index / nt_sub_size + 1];
+};
+/*typedef struct name_table_s name_table; *//* in inames.h */
+
+/* ---------------- Procedural interface ---------------- */
+
+/*
+ * Convert between names, indices, and strings. Note that the inline
+ * versions, but not the procedure versions, take a name_table argument.
+ */
+ /* index => string */
+#define names_index_string_inline(nt, nidx)\
+ ((nt)->sub[(nidx) >> nt_log2_sub_size].strings->strings +\
+ ((nidx) & nt_sub_index_mask))
+ /* ref => string */
+#define names_string_inline(nt, pnref)\
+ names_index_string_inline(nt, names_index_inline(nt, pnref))
+ /* ref => index */
+#if EXTEND_NAMES
+# define names_index_inline(nt_ignored, pnref)\
+ ( ((const name_sub_table *)\
+ ((pnref)->value.pname - (r_size(pnref) & nt_sub_index_mask)))->high_index + r_size(pnref) )
+#else
+# define names_index_inline(nt_ignored, pnref) r_size(pnref)
+#endif
+#define names_index(nt_ignored, pnref) names_index_inline(nt_ignored, pnref)
+ /* index => name */
+#define names_index_ptr_inline(nt, nidx)\
+ ((nt)->sub[(nidx) >> nt_log2_sub_size].names->names +\
+ ((nidx) & nt_sub_index_mask))
+ /* index => ref */
+#define names_index_ref_inline(nt, nidx, pnref)\
+ make_name(pnref, nidx, names_index_ptr_inline(nt, nidx));
+/* Backward compatibility */
+#define name_index_inline(pnref) names_index_inline(ignored, pnref)
+#define name_index_ptr_inline(nt, pnref) names_index_ptr_inline(nt, pnref)
+#define name_index_ref_inline(nt, nidx, pnref)\
+ names_index_ref_inline(nt, nidx, pnref)
+ /* name => ref */
+/* We have to set the space to system so that the garbage collector */
+/* won't think names are foreign and therefore untraceable. */
+#define make_name(pnref, nidx, pnm)\
+ make_tasv(pnref, t_name, avm_system, (ushort)(nidx), pname, pnm)
+
+/* ------ Garbage collection ------ */
+
+/* Unmark all non-permanent names before a garbage collection. */
+void names_unmark_all(name_table * nt);
+
+/* Finish tracing the name table by putting free names on the free list. */
+void names_trace_finish(name_table * nt, gc_state_t * gcst);
+
+/* ------ Save/restore ------ */
+
+/* Clean up the name table before a restore. */
+#ifndef alloc_save_t_DEFINED /* also in isave.h */
+typedef struct alloc_save_s alloc_save_t;
+# define alloc_save_t_DEFINED
+#endif
+void names_restore(name_table * nt, alloc_save_t * save);
+
+#endif /* inamedef_INCLUDED */
diff --git a/psi/inameidx.h b/psi/inameidx.h
new file mode 100644
index 000000000..2fa5f9ba8
--- /dev/null
+++ b/psi/inameidx.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Name index definitions */
+
+#ifndef inameidx_INCLUDED
+# define inameidx_INCLUDED
+
+/*
+ * The name table machinery has two slightly different configurations:
+ * a faster one that limits the total number of names to 64K and allows
+ * names up to 16K in size (EXTEND_NAMES == 0), and a slightly slower
+ * one that limits the total to 64K << EXTEND_NAMES and and restricts names
+ * to 16K >> EXTEND_NAMES. Max value of EXTEND_NAMES is 6, which corresponds
+ * to 4M names of up to to 256 characters.
+ */
+#ifndef EXTEND_NAMES /* # of bits beyond 16 */
+# define EXTEND_NAMES 4
+#endif
+
+/* Define the size of a name sub-table. */
+/* With NT_LOG2_SUB_SIZE >= 10 the subtable goes into a large chunk, */
+/* which names_trace_finish() cannot handle. */
+# define NT_LOG2_SUB_SIZE (8 + (EXTEND_NAMES >= 2))
+# define NT_SUB_SIZE (1 << NT_LOG2_SUB_SIZE)
+# define NT_SUB_INDEX_MASK (NT_SUB_SIZE - 1)
+
+/*
+ * Define the first few entries of the name table. Entry 0 is left unused.
+ * The entry with count = 1 is the entry for the 0-length name.
+ * The next NT_1CHAR_SIZE entries (in count order) are 1-character names.
+ */
+#define NT_1CHAR_SIZE 128
+#define NT_1CHAR_FIRST 2
+#define NT_1CHAR_NAMES_DATA\
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,\
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,\
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,\
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,\
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,\
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,\
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127
+
+/* ---------------- Name count/index mapping ---------------- */
+
+/*
+ * We scramble the assignment order within a sub-table, so that
+ * dictionary lookup doesn't have to scramble the index.
+ * The scrambling algorithm must have three properties:
+ * - It must map 0 to 0;
+ * - It must only scramble the sub-table index;
+ * - It must be a permutation on the sub-table index.
+ * Something very simple works just fine.
+ */
+#define NAME_COUNT_TO_INDEX_FACTOR 23
+#define name_count_to_index(cnt)\
+ (((cnt) & (-NT_SUB_SIZE)) +\
+ (((cnt) * NAME_COUNT_TO_INDEX_FACTOR) & NT_SUB_INDEX_MASK))
+/*
+ * The reverse permutation requires finding a number R such that
+ * NAME_COUNT_TO_INDEX_FACTOR * R = 1 mod NT_SUB_SIZE.
+ * The value given below works for NT_SUB_SIZE any power of 2 up to 4096.
+ * This is not currently used anywhere.
+ */
+#define NAME_INDEX_TO_COUNT_FACTOR 1959
+#define name_index_to_count(nidx)\
+ (((nidx) & (-NT_SUB_SIZE)) +\
+ (((nidx) * NAME_INDEX_TO_COUNT_FACTOR) & NT_SUB_INDEX_MASK))
+
+#endif /* inameidx_INCLUDED */
diff --git a/psi/inames.h b/psi/inames.h
new file mode 100644
index 000000000..a1b5dff57
--- /dev/null
+++ b/psi/inames.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Name table interface */
+
+#ifndef inames_INCLUDED
+# define inames_INCLUDED
+
+/*
+ * This file defines those parts of the name table API that depend neither
+ * on the implementation nor on the existence of a single distinguished
+ * instance. Procedures in this file begin with names_.
+ */
+
+/* ---------------- Interface types ---------------- */
+
+#ifndef name_table_DEFINED
+# define name_table_DEFINED
+typedef struct name_table_s name_table;
+#endif
+
+typedef uint name_index_t;
+
+/* ---------------- Constant values ---------------- */
+
+extern const uint name_max_string;
+
+/* ---------------- Procedural interface ---------------- */
+
+#ifndef gs_ref_memory_DEFINED
+# define gs_ref_memory_DEFINED
+typedef struct gs_ref_memory_s gs_ref_memory_t;
+#endif
+
+/* Allocate and initialize a name table. */
+name_table *names_init(ulong size, gs_ref_memory_t *imem);
+
+/* Get the allocator for a name table. */
+gs_memory_t *names_memory(const name_table * nt);
+
+/*
+ * Look up and/or enter a name in the name table.
+ * The values of enterflag are:
+ * -1 -- don't enter (return an error) if missing;
+ * 0 -- enter if missing, don't copy the string, which was allocated
+ * statically;
+ * 1 -- enter if missing, copy the string;
+ * 2 -- enter if missing, don't copy the string, which was already
+ * allocated dynamically (using the names_memory allocator).
+ * Possible errors: VMerror, limitcheck (if string is too long or if
+ * we have assigned all possible name indices).
+ */
+int names_ref(name_table * nt, const byte * ptr, uint size, ref * pnref,
+ int enterflag);
+void names_string_ref(const name_table * nt, const ref * pnref, ref * psref);
+
+/*
+ * names_enter_string calls names_ref with a (permanent) C string.
+ */
+int names_enter_string(name_table * nt, const char *str, ref * pnref);
+
+/*
+ * names_from_string essentially implements cvn.
+ * It always enters the name, and copies the executable attribute.
+ */
+int names_from_string(name_table * nt, const ref * psref, ref * pnref);
+
+/* Compare two names for equality. */
+#define names_eq(pnref1, pnref2)\
+ ((pnref1)->value.pname == (pnref2)->value.pname)
+
+/* Invalidate the value cache for a name. */
+void names_invalidate_value_cache(name_table * nt, const ref * pnref);
+
+/* Convert between names and indices. */
+name_index_t names_index(const name_table * nt, const ref * pnref); /* ref => index */
+name *names_index_ptr(const name_table * nt, name_index_t nidx); /* index => name */
+void names_index_ref(const name_table * nt, name_index_t nidx, ref * pnref); /* index => ref */
+
+/* Get the index of the next valid name. */
+/* The argument is 0 or a valid index. */
+/* Return 0 if there are no more. */
+name_index_t names_next_valid_index(name_table * nt, name_index_t nidx);
+
+/* Mark a name for the garbage collector. */
+/* Return true if this is a new mark. */
+bool names_mark_index(name_table * nt, name_index_t nidx);
+
+/* Get the object (sub-table) containing a name. */
+/* The garbage collector needs this so it can relocate pointers to names. */
+void /*obj_header_t */ *
+ names_ref_sub_table(name_table * nt, const ref * pnref);
+void /*obj_header_t */ *
+ names_index_sub_table(name_table * nt, name_index_t nidx);
+void /*obj_header_t */ *
+ names_index_string_sub_table(name_table * nt, name_index_t nidx);
+
+#endif /* inames_INCLUDED */
diff --git a/psi/inamestr.h b/psi/inamestr.h
new file mode 100644
index 000000000..bbd5e157e
--- /dev/null
+++ b/psi/inamestr.h
@@ -0,0 +1,91 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Name table strings definition */
+
+#ifndef inamestr_INCLUDED
+# define inamestr_INCLUDED
+
+#include "inameidx.h"
+
+/*
+ * In the code below, we use the hashing method described in
+ * "Fast Hashing of Variable-Length Text Strings" by Peter K. Pearson,
+ * pp. 677-680, CACM 33(6), June 1990.
+ */
+
+/*
+ * Define a pseudo-random permutation of the integers 0..255.
+ * Pearson's article claims this permutation gave good results.
+ */
+#define NAME_HASH_PERMUTATION_DATA\
+ 1, 87, 49, 12, 176, 178, 102, 166, 121, 193, 6, 84, 249, 230, 44, 163,\
+ 14, 197, 213, 181, 161, 85, 218, 80, 64, 239, 24, 226, 236, 142, 38, 200,\
+ 110, 177, 104, 103, 141, 253, 255, 50, 77, 101, 81, 18, 45, 96, 31, 222,\
+ 25, 107, 190, 70, 86, 237, 240, 34, 72, 242, 20, 214, 244, 227, 149, 235,\
+ 97, 234, 57, 22, 60, 250, 82, 175, 208, 5, 127, 199, 111, 62, 135, 248,\
+ 174, 169, 211, 58, 66, 154, 106, 195, 245, 171, 17, 187, 182, 179, 0, 243,\
+ 132, 56, 148, 75, 128, 133, 158, 100, 130, 126, 91, 13, 153, 246, 216, 219,\
+ 119, 68, 223, 78, 83, 88, 201, 99, 122, 11, 92, 32, 136, 114, 52, 10,\
+ 138, 30, 48, 183, 156, 35, 61, 26, 143, 74, 251, 94, 129, 162, 63, 152,\
+ 170, 7, 115, 167, 241, 206, 3, 150, 55, 59, 151, 220, 90, 53, 23, 131,\
+ 125, 173, 15, 238, 79, 95, 89, 16, 105, 137, 225, 224, 217, 160, 37, 123,\
+ 118, 73, 2, 157, 46, 116, 9, 145, 134, 228, 207, 212, 202, 215, 69, 229,\
+ 27, 188, 67, 124, 168, 252, 42, 4, 29, 108, 21, 247, 19, 205, 39, 203,\
+ 233, 40, 186, 147, 198, 192, 155, 33, 164, 191, 98, 204, 165, 180, 117, 76,\
+ 140, 36, 210, 172, 41, 54, 159, 8, 185, 232, 113, 196, 231, 47, 146, 120,\
+ 51, 65, 28, 144, 254, 221, 93, 189, 194, 139, 112, 43, 71, 109, 184, 209
+
+/* Compute the hash for a name string. Assume size >= 1. */
+#define NAME_HASH(hash, hperm, ptr, size)\
+ BEGIN\
+ const byte *p = ptr;\
+ uint n = size;\
+\
+ hash = hperm[*p++];\
+ while (--n > 0)\
+ hash = (hash << 8) | hperm[(byte)hash ^ *p++];\
+ END
+
+/*
+ * Define the structure for a name string. The next_index "pointer" is used
+ * both for the chained hash table in the name_table and for the list of
+ * free names.
+ */
+typedef struct name_string_s {
+# define name_extension_bits EXTEND_NAMES
+ unsigned next_index:16 + name_extension_bits; /* next name in chain or 0 */
+ unsigned foreign_string:1; /* string is allocated statically */
+ unsigned mark:1; /* GC mark bit of index for this name */
+#define name_string_size_bits (14 - name_extension_bits)
+#define max_name_string ((1 << name_string_size_bits) - 1)
+ unsigned string_size:name_string_size_bits;
+ const byte *string_bytes;
+} name_string_t;
+#define name_next_index(nidx, pnstr)\
+ ((pnstr)->next_index)
+#define set_name_next_index(nidx, pnstr, next)\
+ ((pnstr)->next_index = (next))
+
+/* Define a sub-table of name strings. */
+typedef struct name_string_sub_table_s {
+ name_string_t strings[NT_SUB_SIZE];
+} name_string_sub_table_t;
+
+/* Define the size of the name hash table. */
+#define NT_HASH_SIZE (1024 << (EXTEND_NAMES / 2)) /* must be a power of 2 */
+
+#endif /* inamestr_INCLUDED */
diff --git a/psi/inobtokn.c b/psi/inobtokn.c
new file mode 100644
index 000000000..6c6cd3810
--- /dev/null
+++ b/psi/inobtokn.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Dummy scan_binary_token for Level 1 systems */
+#include "ghost.h"
+#include "ierrors.h"
+#include "stream.h"
+#include "iscan.h"
+#include "iscanbin.h"
+
+int
+scan_binary_token(i_ctx_t *i_ctx_p, ref *pref, scanner_state *pstate)
+{
+ return_error(gs_error_unregistered);
+}
diff --git a/psi/inouparm.c b/psi/inouparm.c
new file mode 100644
index 000000000..b17ff4b7f
--- /dev/null
+++ b/psi/inouparm.c
@@ -0,0 +1,25 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Dummy set_user_params for Level 1 systems */
+#include "ghost.h"
+#include "icontext.h" /* for set_user_params prototype */
+
+int
+set_user_params(i_ctx_t *i_ctx_p, const ref *paramdict)
+{
+ return 0;
+}
diff --git a/psi/int.mak b/psi/int.mak
new file mode 100644
index 000000000..f4f9636dd
--- /dev/null
+++ b/psi/int.mak
@@ -0,0 +1,2033 @@
+# Copyright (C) 2001-2015 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+# CA 94903, U.S.A., +1(415)492-9861, for further information.
+#
+#
+# (Platform-independent) makefile for PostScript and PDF language
+# interpreters.
+# Users of this makefile must define the following:
+# GLSRCDIR - the graphics library source directory
+# GLGENDIR - the directory for graphics library source files
+# generated during building
+# PSSRCDIR - the source directory
+# PSOBJDIR - the object code directory
+
+PSSRC=$(PSSRCDIR)$(D)
+PSLIB=$(PSLIBDIR)$(D)
+PSINIT=$(PSRESDIR)$(D)Init$(D)
+PSGEN=$(PSGENDIR)$(D)
+PSOBJ=$(PSOBJDIR)$(D)
+PSO_=$(O_)$(PSOBJ)
+PSI_=$(PSSRCDIR) $(II)$(PSGENDIR) $(II)$(GLI_)
+PSF_=
+PSCC=$(CC_) $(I_)$(PSI_)$(_I) $(PSF_)
+PSJBIG2CC=$(CC_) $(I_)$(JB2I_) $(II)$(PSI_)$(_I) $(JB2CF_) $(PSF_)
+PSJASCC=$(CC_) $(I_)$(JPXI_) $(II)$(PSI_)$(_I) $(JPXCF_) $(PSF)
+PSLDFJB2CC=$(CC_) $(I_)$(LDF_JB2I_) $(II)$(LDF_JB2I_) $(II)$(PSI_)$(_I) $(JB2CF_) $(PSF_)
+PSLWFJPXCC=$(CC_) $(I_)$(LWF_JPXI_) $(II)$(PSI_)$(_I) $(JPXCF_) $(PSF)
+PSOPJJPXCC=$(CC_) $(I_)$(JPX_OPENJPEG_I_)$(D).. $(I_)$(JPX_OPENJPEG_I_) $(II)$(PSI_)$(_I) $(JPXCF_) $(PSF)
+
+# All top-level makefiles define PSD.
+#PSD=$(PSGEN)
+
+# Define the name of this makefile.
+INT_MAK=$(PSSRC)int.mak
+
+# ======================== Interpreter support ======================== #
+
+# This is support code for all interpreters, not just PostScript and PDF.
+# It knows about the PostScript data types, but isn't supposed to
+# depend on anything outside itself.
+
+ierrors_h=$(PSSRC)ierrors.h $(gserrors_h)
+iconf_h=$(PSSRC)iconf.h
+idebug_h=$(PSSRC)idebug.h
+# Having iddstack.h at this level is unfortunate, but unavoidable.
+iddstack_h=$(PSSRC)iddstack.h
+idict_h=$(PSSRC)idict.h $(iddstack_h)
+idictdef_h=$(PSSRC)idictdef.h
+idicttpl_h=$(PSSRC)idicttpl.h
+idosave_h=$(PSSRC)idosave.h
+igcstr_h=$(PSSRC)igcstr.h
+inames_h=$(PSSRC)inames.h
+iname_h=$(PSSRC)iname.h $(inames_h)
+inameidx_h=$(PSSRC)inameidx.h
+inamestr_h=$(PSSRC)inamestr.h $(inameidx_h)
+ipacked_h=$(PSSRC)ipacked.h
+iref_h=$(PSSRC)iref.h $(stdint__h)
+isave_h=$(PSSRC)isave.h $(idosave_h)
+isstate_h=$(PSSRC)isstate.h
+istruct_h=$(PSSRC)istruct.h $(gsstruct_h)
+iutil_h=$(PSSRC)iutil.h
+ivmspace_h=$(PSSRC)ivmspace.h $(gsgc_h)
+opdef_h=$(PSSRC)opdef.h
+# Nested include files
+ghost_h=$(PSSRC)ghost.h $(gx_h) $(iref_h)
+igc_h=$(PSSRC)igc.h $(istruct_h)
+imemory_h=$(PSSRC)imemory.h $(gsalloc_h) $(ivmspace_h)
+ialloc_h=$(PSSRC)ialloc.h $(imemory_h)
+iastruct_h=$(PSSRC)iastruct.h $(gxobj_h) $(ialloc_h)
+iastate_h=$(PSSRC)iastate.h $(gxalloc_h) $(ialloc_h) $(istruct_h)
+inamedef_h=$(PSSRC)inamedef.h\
+ $(gsstruct_h) $(inameidx_h) $(inames_h) $(inamestr_h)
+store_h=$(PSSRC)store.h $(ialloc_h) $(idosave_h)
+iplugin_h=$(PSSRC)iplugin.h
+ifapi_h=$(PSSRC)ifapi.h $(iplugin_h) $(gstypes_h) $(gsmatrix_h) $(gp_h) $(memory__h)
+zht2_h=$(PSSRC)zht2.h $(gscspace_h)
+zchar42_h=$(PSSRC)zchar42.h
+zfunc_h=$(PSSRC)zfunc.h
+
+GH=$(AK) $(ghost_h)
+
+isupport1_=$(PSOBJ)ialloc.$(OBJ) $(PSOBJ)igc.$(OBJ) $(PSOBJ)igcref.$(OBJ) $(PSOBJ)igcstr.$(OBJ)
+isupport2_=$(PSOBJ)ilocate.$(OBJ) $(PSOBJ)iname.$(OBJ) $(PSOBJ)isave.$(OBJ)
+isupport_=$(isupport1_) $(isupport2_)
+$(PSD)isupport.dev : $(INT_MAK) $(ECHOGS_XE) $(isupport_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)isupport $(isupport1_)
+ $(ADDMOD) $(PSD)isupport -obj $(isupport2_)
+
+$(PSOBJ)ialloc.$(OBJ) : $(PSSRC)ialloc.c $(AK) $(memory__h) $(gx_h)\
+ $(ierrors_h) $(gsstruct_h)\
+ $(iastate_h) $(igc_h) $(ipacked_h) $(iref_h) $(iutil_h) $(ivmspace_h)\
+ $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)ialloc.$(OBJ) $(C_) $(PSSRC)ialloc.c
+
+# igc.c, igcref.c, and igcstr.c should really be in the dpsand2 list,
+# but since all the GC enumeration and relocation routines refer to them,
+# it's too hard to separate them out from the Level 1 base.
+$(PSOBJ)igc.$(OBJ) : $(PSSRC)igc.c $(GH) $(memory__h)\
+ $(ierrors_h) $(gsexit_h) $(gsmdebug_h) $(gsstruct_h)\
+ $(iastate_h) $(idict_h) $(igc_h) $(igcstr_h) $(inamedef_h)\
+ $(ipacked_h) $(isave_h) $(isstate_h) $(istruct_h) $(opdef_h) \
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)igc.$(OBJ) $(C_) $(PSSRC)igc.c
+
+$(PSOBJ)igcref.$(OBJ) : $(PSSRC)igcref.c $(GH) $(memory__h)\
+ $(gsexit_h) $(gsstruct_h)\
+ $(iastate_h) $(idebug_h) $(igc_h) $(iname_h) $(ipacked_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)igcref.$(OBJ) $(C_) $(PSSRC)igcref.c
+
+$(PSOBJ)igcstr.$(OBJ) : $(PSSRC)igcstr.c $(GH) $(memory__h)\
+ $(gsmdebug_h) $(gsstruct_h) $(iastate_h) $(igcstr_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)igcstr.$(OBJ) $(C_) $(PSSRC)igcstr.c
+
+$(PSOBJ)ilocate.$(OBJ) : $(PSSRC)ilocate.c $(GH) $(memory__h)\
+ $(ierrors_h) $(gsexit_h) $(gsstruct_h)\
+ $(iastate_h) $(idict_h) $(igc_h) $(igcstr_h) $(iname_h)\
+ $(ipacked_h) $(isstate_h) $(iutil_h) $(ivmspace_h)\
+ $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)ilocate.$(OBJ) $(C_) $(PSSRC)ilocate.c
+
+$(PSOBJ)iname.$(OBJ) : $(PSSRC)iname.c $(GH) $(memory__h) $(string__h)\
+ $(gsstruct_h) $(gxobj_h)\
+ $(ierrors_h) $(imemory_h) $(inamedef_h) $(isave_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)iname.$(OBJ) $(C_) $(PSSRC)iname.c
+
+$(PSOBJ)isave.$(OBJ) : $(PSSRC)isave.c $(GH) $(memory__h)\
+ $(ierrors_h) $(gsexit_h) $(gsstruct_h) $(gsutil_h)\
+ $(iastate_h) $(iname_h) $(inamedef_h) $(isave_h) $(isstate_h) $(ivmspace_h)\
+ $(ipacked_h) $(store_h) $(stream_h) $(igc_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)isave.$(OBJ) $(C_) $(PSSRC)isave.c
+
+### Include files
+
+idparam_h=$(PSSRC)idparam.h
+ilevel_h=$(PSSRC)ilevel.h
+interp_h=$(PSSRC)interp.h
+iparam_h=$(PSSRC)iparam.h $(gsparam_h)
+isdata_h=$(PSSRC)isdata.h
+istack_h=$(PSSRC)istack.h $(isdata_h)
+istkparm_h=$(PSSRC)istkparm.h
+iutil2_h=$(PSSRC)iutil2.h
+oparc_h=$(PSSRC)oparc.h
+opcheck_h=$(PSSRC)opcheck.h
+opextern_h=$(PSSRC)opextern.h
+# Nested include files
+idsdata_h=$(PSSRC)idsdata.h $(isdata_h)
+idstack_h=$(PSSRC)idstack.h $(iddstack_h) $(idsdata_h) $(istack_h)
+iesdata_h=$(PSSRC)iesdata.h $(isdata_h)
+iestack_h=$(PSSRC)iestack.h $(istack_h) $(iesdata_h)
+iosdata_h=$(PSSRC)iosdata.h $(isdata_h)
+iostack_h=$(PSSRC)iostack.h $(istack_h) $(iosdata_h)
+icstate_h=$(PSSRC)icstate.h $(imemory_h) $(iref_h) $(idsdata_h) $(iesdata_h) $(iosdata_h)
+iddict_h=$(PSSRC)iddict.h $(icstate_h) $(idict_h)
+dstack_h=$(PSSRC)dstack.h $(icstate_h) $(idstack_h)
+estack_h=$(PSSRC)estack.h $(icstate_h) $(iestack_h)
+ostack_h=$(PSSRC)ostack.h $(icstate_h) $(iostack_h)
+oper_h=$(PSSRC)oper.h $(ierrors_h) $(iutil_h) $(opcheck_h) $(opdef_h) $(opextern_h) $(ostack_h)
+
+$(PSOBJ)idebug.$(OBJ) : $(PSSRC)idebug.c $(GH) $(string__h)\
+ $(gxalloc_h)\
+ $(idebug_h) $(idict_h) $(iname_h) $(istack_h) $(iutil_h) $(ivmspace_h)\
+ $(opdef_h) $(ipacked_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)idebug.$(OBJ) $(C_) $(PSSRC)idebug.c
+
+$(PSOBJ)idict.$(OBJ) : $(PSSRC)idict.c $(GH) $(math__h) $(string__h)\
+ $(ierrors_h)\
+ $(gxalloc_h)\
+ $(iddstack_h) $(idebug_h) $(idict_h) $(idictdef_h) $(idicttpl_h)\
+ $(imemory_h) $(iname_h) $(inamedef_h) $(ipacked_h) $(isave_h)\
+ $(iutil_h) $(ivmspace_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)idict.$(OBJ) $(C_) $(PSSRC)idict.c
+
+$(PSOBJ)idparam.$(OBJ) : $(PSSRC)idparam.c $(GH) $(memory__h) $(string__h) $(ierrors_h)\
+ $(gsmatrix_h) $(gsuid_h) $(dstack_h)\
+ $(idict_h) $(iddict_h) $(idparam_h) $(ilevel_h) $(imemory_h) $(iname_h) $(iutil_h)\
+ $(oper_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)idparam.$(OBJ) $(C_) $(PSSRC)idparam.c
+
+$(PSOBJ)idstack.$(OBJ) : $(PSSRC)idstack.c $(GH)\
+ $(idebug_h) $(idict_h) $(idictdef_h) $(idicttpl_h) $(idstack_h) $(iname_h) $(inamedef_h)\
+ $(ipacked_h) $(iutil_h) $(ivmspace_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)idstack.$(OBJ) $(C_) $(PSSRC)idstack.c
+
+$(PSOBJ)iparam.$(OBJ) : $(PSSRC)iparam.c $(GH)\
+ $(memory__h) $(string__h) $(ierrors_h)\
+ $(ialloc_h) $(idict_h) $(iname_h) $(imemory_h) $(iparam_h) $(istack_h) $(iutil_h) $(ivmspace_h)\
+ $(opcheck_h) $(oper_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)iparam.$(OBJ) $(C_) $(PSSRC)iparam.c
+
+$(PSOBJ)istack.$(OBJ) : $(PSSRC)istack.c $(GH) $(memory__h)\
+ $(ierrors_h) $(gsstruct_h) $(gsutil_h)\
+ $(ialloc_h) $(istack_h) $(istkparm_h) $(istruct_h) $(iutil_h) $(ivmspace_h)\
+ $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)istack.$(OBJ) $(C_) $(PSSRC)istack.c
+
+$(PSOBJ)iutil.$(OBJ) : $(PSSRC)iutil.c $(GH) $(math__h) $(memory__h) $(string__h)\
+ $(gsccode_h) $(gsmatrix_h) $(gsutil_h) $(gxfont_h)\
+ $(sstring_h) $(strimpl_h)\
+ $(ierrors_h) $(idict_h) $(imemory_h) $(iutil_h) $(ivmspace_h)\
+ $(iname_h) $(ipacked_h) $(oper_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)iutil.$(OBJ) $(C_) $(PSSRC)iutil.c
+
+$(PSOBJ)iplugin.$(OBJ) : $(PSSRC)iplugin.c $(GH) $(malloc__h) $(string__h)\
+ $(gxalloc_h)\
+ $(ierrors_h) $(ialloc_h) $(icstate_h) $(iplugin_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)iplugin.$(OBJ) $(C_) $(PSSRC)iplugin.c
+
+# ======================== PostScript Level 1 ======================== #
+
+###### Include files
+
+# Binary tokens are a Level 2 feature, but we need to refer to them
+# in the scanner.
+btoken_h=$(PSSRC)btoken.h
+files_h=$(PSSRC)files.h
+fname_h=$(PSSRC)fname.h
+iapi_h=$(PSSRC)iapi.h
+ichar_h=$(PSSRC)ichar.h
+ichar1_h=$(PSSRC)ichar1.h
+icharout_h=$(PSSRC)icharout.h
+icolor_h=$(PSSRC)icolor.h
+icremap_h=$(PSSRC)icremap.h $(gsccolor_h)
+icsmap_h=$(PSSRC)icsmap.h
+idisp_h=$(PSSRC)idisp.h
+ifilter2_h=$(PSSRC)ifilter2.h
+ifont_h=$(PSSRC)ifont.h $(gsccode_h) $(gsstype_h)
+ifont1_h=$(PSSRC)ifont1.h
+ifont2_h=$(PSSRC)ifont2.h
+ifont42_h=$(PSSRC)ifont42.h
+ifrpred_h=$(PSSRC)ifrpred.h
+ifwpred_h=$(PSSRC)ifwpred.h
+iht_h=$(PSSRC)iht.h
+iimage_h=$(PSSRC)iimage.h
+iinit_h=$(PSSRC)iinit.h
+imain_h=$(PSSRC)imain.h $(gsexit_h)
+imainarg_h=$(PSSRC)imainarg.h
+iminst_h=$(PSSRC)iminst.h
+iparray_h=$(PSSRC)iparray.h
+iscanbin_h=$(PSSRC)iscanbin.h
+iscannum_h=$(PSSRC)iscannum.h
+istream_h=$(PSSRC)istream.h
+itoken_h=$(PSSRC)itoken.h
+main_h=$(PSSRC)main.h $(iapi_h) $(imain_h) $(iminst_h)
+sbwbs_h=$(PSSRC)sbwbs.h
+shcgen_h=$(PSSRC)shcgen.h
+smtf_h=$(PSSRC)smtf.h
+# Nested include files
+bfont_h=$(PSSRC)bfont.h $(ifont_h)
+icontext_h=$(PSSRC)icontext.h $(gsstype_h) $(icstate_h)
+ifilter_h=$(PSSRC)ifilter.h $(istream_h) $(ivmspace_h)
+igstate_h=$(PSSRC)igstate.h $(gsstate_h) $(gxstate_h) $(imemory_h) $(istruct_h) $(gxcindex_h)
+iscan_h=$(PSSRC)iscan.h $(sa85x_h) $(sstring_h) $(inamestr_h)
+sbhc_h=$(PSSRC)sbhc.h $(shc_h)
+zfile_h=$(PSSRC)zfile.h
+# Include files for optional features
+ibnum_h=$(PSSRC)ibnum.h
+zcolor_h=$(PSSRC)zcolor.h
+zcie_h=$(PSSRC)zcie.h
+zicc_h=$(PSSRC)zicc.h
+zfrsd_h=$(PSSRC)zfrsd.h
+
+### Initialization and scanning
+
+$(PSOBJ)iconfig.$(OBJ) : $(gconfig_h) $(PSSRC)iconf.c $(stdio__h)\
+ $(gconf_h) $(gconfigd_h) $(gsmemory_h) $(gstypes_h)\
+ $(iminst_h) $(iref_h) $(ivmspace_h) $(opdef_h) $(iplugin_h)\
+ $(MAKEDIRS)
+ $(RM_) $(PSGEN)iconfig.c
+ $(CP_) $(PSSRC)iconf.c $(PSGEN)iconfig.c
+ $(PSCC) $(PSO_)iconfig.$(OBJ) $(C_) $(PSGEN)iconfig.c
+
+$(PSOBJ)iinit.$(OBJ) : $(PSSRC)iinit.c $(GH) $(string__h)\
+ $(gscdefs_h) $(gsexit_h) $(gsstruct_h)\
+ $(dstack_h) $(ierrors_h) $(ialloc_h) $(iddict_h)\
+ $(iinit_h) $(ilevel_h) $(iname_h) $(interp_h) $(opdef_h)\
+ $(ipacked_h) $(iparray_h) $(iutil_h) $(ivmspace_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)iinit.$(OBJ) $(C_) $(PSSRC)iinit.c
+
+$(PSOBJ)iscan.$(OBJ) : $(PSSRC)iscan.c $(GH) $(memory__h)\
+ $(btoken_h) $(dstack_h) $(ierrors_h) $(files_h)\
+ $(ialloc_h) $(idict_h) $(ilevel_h) $(iname_h) $(ipacked_h) $(iparray_h)\
+ $(iscan_h) $(iscanbin_h) $(iscannum_h)\
+ $(istruct_h) $(istream_h) $(iutil_h) $(ivmspace_h)\
+ $(ostack_h) $(store_h)\
+ $(sa85d_h) $(stream_h) $(strimpl_h) $(sfilter_h) $(scanchar_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)iscan.$(OBJ) $(C_) $(PSSRC)iscan.c
+
+$(PSOBJ)iscannum.$(OBJ) : $(PSSRC)iscannum.c $(GH) $(math__h)\
+ $(ierrors_h) $(iscan_h) $(iscannum_h) $(scanchar_h) $(scommon_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)iscannum.$(OBJ) $(C_) $(PSSRC)iscannum.c
+
+### Streams
+
+$(PSOBJ)sfilter1.$(OBJ) : $(PSSRC)sfilter1.c $(AK) $(stdio__h) $(memory__h)\
+ $(sfilter_h) $(strimpl_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)sfilter1.$(OBJ) $(C_) $(PSSRC)sfilter1.c
+
+###### Operators
+
+OP=$(GH) $(oper_h)
+
+### Non-graphics operators
+
+$(PSOBJ)zarith.$(OBJ) : $(PSSRC)zarith.c $(OP) $(math__h) $(store_h)\
+ $(gsstate_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zarith.$(OBJ) $(C_) $(PSSRC)zarith.c
+
+$(PSOBJ)zarray.$(OBJ) : $(PSSRC)zarray.c $(OP) $(memory__h)\
+ $(ialloc_h) $(ipacked_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zarray.$(OBJ) $(C_) $(PSSRC)zarray.c
+
+$(PSOBJ)zcontrol.$(OBJ) : $(PSSRC)zcontrol.c $(OP) $(string__h)\
+ $(estack_h) $(files_h) $(ipacked_h) $(iutil_h) $(store_h) $(stream_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcontrol.$(OBJ) $(C_) $(PSSRC)zcontrol.c
+
+$(PSOBJ)zdict.$(OBJ) : $(PSSRC)zdict.c $(OP)\
+ $(dstack_h) $(iddict_h) $(ilevel_h) $(iname_h) $(ipacked_h) $(ivmspace_h)\
+ $(store_h) $(iscan_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zdict.$(OBJ) $(C_) $(PSSRC)zdict.c
+
+$(PSOBJ)zfile.$(OBJ) : $(PSSRC)zfile.c $(OP)\
+ $(memory__h) $(string__h) $(unistd__h) $(stat__h) $(gp_h) $(gpmisc_h)\
+ $(gscdefs_h) $(gsfname_h) $(gsstruct_h) $(gsutil_h) $(gxalloc_h) $(gxiodev_h)\
+ $(dstack_h) $(estack_h) $(files_h)\
+ $(ialloc_h) $(idict_h) $(ilevel_h) $(iname_h) $(iutil_h)\
+ $(isave_h) $(main_h) $(sfilter_h) $(stream_h) $(strimpl_h) $(store_h)\
+ $(zfile_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfile.$(OBJ) $(C_) $(PSSRC)zfile.c
+
+$(PSOBJ)zfile1.$(OBJ) : $(PSSRC)zfile1.c $(OP) $(memory__h) $(string__h)\
+ $(gp_h) $(ierrors_h) $(oper_h) $(opcheck_h) $(ialloc_h) $(opdef_h) $(store_h)\
+ $(gpmisc_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfile1.$(OBJ) $(C_) $(PSSRC)zfile1.c
+
+$(PSOBJ)zfileio.$(OBJ) : $(PSSRC)zfileio.c $(OP) $(memory__h) $(gp_h)\
+ $(estack_h) $(files_h) $(ifilter_h) $(interp_h) $(store_h)\
+ $(stream_h) $(strimpl_h)\
+ $(gsmatrix_h) $(gxdevice_h) $(gxdevmem_h) $(gsstate_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfileio.$(OBJ) $(C_) $(PSSRC)zfileio.c
+
+$(PSOBJ)zfilter.$(OBJ) : $(PSSRC)zfilter.c $(OP) $(memory__h)\
+ $(gsstruct_h)\
+ $(files_h) $(ialloc_h) $(idict_h) $(idparam_h) $(ifilter_h) $(ilevel_h)\
+ $(sfilter_h) $(srlx_h) $(sstring_h) $(stream_h) $(strimpl_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfilter.$(OBJ) $(C_) $(PSSRC)zfilter.c
+
+$(PSOBJ)zfproc.$(OBJ) : $(PSSRC)zfproc.c $(GH) $(memory__h) $(stat__h)\
+ $(oper_h)\
+ $(estack_h) $(files_h) $(gsstruct_h) $(ialloc_h) $(ifilter_h) $(istruct_h)\
+ $(store_h) $(stream_h) $(strimpl_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfproc.$(OBJ) $(C_) $(PSSRC)zfproc.c
+
+$(PSOBJ)zgeneric.$(OBJ) : $(PSSRC)zgeneric.c $(OP) $(memory__h)\
+ $(gsstruct_h)\
+ $(dstack_h) $(estack_h) $(iddict_h) $(iname_h) $(ipacked_h) $(ivmspace_h)\
+ $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zgeneric.$(OBJ) $(C_) $(PSSRC)zgeneric.c
+
+$(PSOBJ)ziodev.$(OBJ) : $(PSSRC)ziodev.c $(OP)\
+ $(memory__h) $(stdio__h) $(string__h)\
+ $(gp_h) $(gpcheck_h)\
+ $(gxiodev_h)\
+ $(files_h) $(ialloc_h) $(iscan_h) $(ivmspace_h)\
+ $(scanchar_h) $(store_h) $(stream_h) $(istream_h) $(ierrors_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)ziodev.$(OBJ) $(C_) $(PSSRC)ziodev.c
+
+$(PSOBJ)ziodevs$(STDIO_IMPLEMENTATION).$(OBJ) : $(PSSRC)ziodevs$(STDIO_IMPLEMENTATION).c $(OP) $(stdio__h)\
+ $(gpcheck_h)\
+ $(gxiodev_h)\
+ $(files_h) $(ifilter_h) $(istream_h) $(store_h) $(stream_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)ziodevs$(STDIO_IMPLEMENTATION).$(OBJ) $(C_) $(PSSRC)ziodevs$(STDIO_IMPLEMENTATION).c
+
+$(PSOBJ)zmath.$(OBJ) : $(PSSRC)zmath.c $(OP) $(math__h) $(gxfarith_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zmath.$(OBJ) $(C_) $(PSSRC)zmath.c
+
+$(PSOBJ)zalg.$(OBJ) : $(PSSRC)zalg.c $(OP) $(ghost_h) $(gserrors_h)\
+ $(oper_h) $(store_h) $(estack_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zalg.$(OBJ) $(C_) $(PSSRC)zalg.c
+
+$(PSOBJ)zmisc.$(OBJ) : $(PSSRC)zmisc.c $(OP) $(gscdefs_h) $(gp_h)\
+ $(errno__h) $(memory__h) $(string__h) $(iscan_h)\
+ $(ialloc_h) $(idict_h) $(dstack_h) $(iname_h) $(ivmspace_h) $(ipacked_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zmisc.$(OBJ) $(C_) $(PSSRC)zmisc.c
+
+$(PSOBJ)zpacked.$(OBJ) : $(PSSRC)zpacked.c $(OP)\
+ $(ialloc_h) $(idict_h) $(ivmspace_h) $(iname_h) $(ipacked_h) $(iparray_h)\
+ $(istack_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zpacked.$(OBJ) $(C_) $(PSSRC)zpacked.c
+
+$(PSOBJ)zrelbit.$(OBJ) : $(PSSRC)zrelbit.c $(OP)\
+ $(gsutil_h) $(store_h) $(idict_h) $(gsstate_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zrelbit.$(OBJ) $(C_) $(PSSRC)zrelbit.c
+
+$(PSOBJ)zstack.$(OBJ) : $(PSSRC)zstack.c $(OP) $(memory__h)\
+ $(ialloc_h) $(istack_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zstack.$(OBJ) $(C_) $(PSSRC)zstack.c
+
+$(PSOBJ)zstring.$(OBJ) : $(PSSRC)zstring.c $(OP) $(memory__h)\
+ $(gsutil_h)\
+ $(ialloc_h) $(iname_h) $(ivmspace_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zstring.$(OBJ) $(C_) $(PSSRC)zstring.c
+
+$(PSOBJ)zsysvm.$(OBJ) : $(PSSRC)zsysvm.c $(GH)\
+ $(ialloc_h) $(ivmspace_h) $(oper_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zsysvm.$(OBJ) $(C_) $(PSSRC)zsysvm.c
+
+$(PSOBJ)ztoken.$(OBJ) : $(PSSRC)ztoken.c $(OP) $(string__h) $(stat__h)\
+ $(gsstruct_h) $(gsutil_h)\
+ $(dstack_h) $(estack_h) $(files_h)\
+ $(idict_h) $(iname_h) $(iscan_h) $(itoken_h)\
+ $(sfilter_h) $(store_h) $(stream_h) $(strimpl_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)ztoken.$(OBJ) $(C_) $(PSSRC)ztoken.c
+
+$(PSOBJ)ztype.$(OBJ) : $(PSSRC)ztype.c $(OP)\
+ $(math__h) $(memory__h) $(string__h)\
+ $(gsexit_h)\
+ $(dstack_h) $(idict_h) $(imemory_h) $(iname_h)\
+ $(iscan_h) $(iutil_h) $(sfilter_h) $(store_h) $(stream_h) $(strimpl_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)ztype.$(OBJ) $(C_) $(PSSRC)ztype.c
+
+$(PSOBJ)zvmem.$(OBJ) : $(PSSRC)zvmem.c $(OP) $(stat__h)\
+ $(dstack_h) $(estack_h) $(files_h)\
+ $(ialloc_h) $(idict_h) $(igstate_h) $(isave_h) $(store_h) $(stream_h)\
+ $(gsmalloc_h) $(gsmatrix_h) $(gsstate_h) $(gsstruct_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zvmem.$(OBJ) $(C_) $(PSSRC)zvmem.c
+
+### Graphics operators
+
+$(PSOBJ)zbfont.$(OBJ) : $(PSSRC)zbfont.c $(OP) $(memory__h) $(string__h)\
+ $(gscencs_h) $(gsmatrix_h) $(gxdevice_h) $(gxfixed_h) $(gxfont_h)\
+ $(bfont_h) $(ialloc_h) $(idict_h) $(idparam_h) $(ilevel_h)\
+ $(iname_h) $(inamedef_h) $(interp_h) $(istruct_h) $(ipacked_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zbfont.$(OBJ) $(C_) $(PSSRC)zbfont.c
+
+$(PSOBJ)zchar.$(OBJ) : $(PSSRC)zchar.c $(OP)\
+ $(gsstruct_h) $(gstext_h) $(gxarith_h) $(gxfixed_h) $(gxmatrix_h)\
+ $(gxdevice_h) $(gxfont_h) $(gxfont42_h) $(gxfont0_h) $(gzstate_h)\
+ $(dstack_h) $(estack_h) $(ialloc_h) $(ichar_h) $(ichar1_h) $(idict_h) $(ifont_h)\
+ $(ilevel_h) $(iname_h) $(igstate_h) $(ipacked_h) $(store_h) $(zchar42_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zchar.$(OBJ) $(C_) $(PSSRC)zchar.c
+
+# zcharout is used for Type 1 and Type 42 fonts only.
+$(PSOBJ)zcharout.$(OBJ) : $(PSSRC)zcharout.c $(OP) $(memory__h)\
+ $(gscrypt1_h) $(gstext_h) $(gxdevice_h) $(gxfont_h) $(gxfont1_h)\
+ $(dstack_h) $(estack_h) $(ichar_h) $(icharout_h)\
+ $(idict_h) $(ifont_h) $(igstate_h) $(iname_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcharout.$(OBJ) $(C_) $(PSSRC)zcharout.c
+
+$(PSOBJ)zcolor.$(OBJ) : $(PSSRC)zcolor.c $(OP)\
+ $(memory__h) $(math__h) $(dstack_h) $(estack_h) $(ialloc_h)\
+ $(igstate_h) $(iutil_h) $(store_h) $(gscolor_h)\
+ $(gscsepr_h) $(gscdevn_h) $(gscpixel_h) $(gxfixed_h) $(gxmatrix_h)\
+ $(gzstate_h) $(gxdcolor_h) $(gxdevice_h) $(gxdevmem_h) $(gxcmap_h)\
+ $(gxcspace_h) $(gxcolor2_h) $(gxpcolor_h)\
+ $(idict_h) $(icolor_h) $(idparam_h) $(iname_h) $(iutil_h) $(icsmap_h)\
+ $(ifunc_h) $(zht2_h) $(zcolor_h) $(zcie_h) $(zicc_h) $(gscspace_h)\
+ $(zfrsd_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcolor.$(OBJ) $(C_) $(PSSRC)zcolor.c
+
+$(PSOBJ)zdevice.$(OBJ) : $(PSSRC)zdevice.c $(OP) $(string__h)\
+ $(ialloc_h) $(idict_h) $(igstate_h) $(iname_h) $(interp_h) $(iparam_h) $(ivmspace_h)\
+ $(gsmatrix_h) $(gsstate_h) $(gxdevice_h) $(gxgetbit_h) $(store_h) $(gsicc_manage_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zdevice.$(OBJ) $(C_) $(PSSRC)zdevice.c
+
+$(PSOBJ)zdfilter.$(OBJ) : $(PSSRC)zdfilter.c $(OP) $(string__h) $(ghost_h) $(oper_h)\
+ $(ialloc_h) $(idict_h) $(igstate_h) $(iname_h) $(interp_h) $(iparam_h) $(ivmspace_h)\
+ $(gsdfilt_h) $(gsmatrix_h) $(gsstate_h) $(gxdevice_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zdfilter.$(OBJ) $(C_) $(PSSRC)zdfilter.c
+
+$(PSOBJ)zfont.$(OBJ) : $(PSSRC)zfont.c $(OP)\
+ $(gsstruct_h) $(gxdevice_h) $(gxfont_h) $(gxfcache_h)\
+ $(gzstate_h)\
+ $(ialloc_h) $(iddict_h) $(igstate_h) $(iname_h) $(isave_h) $(ivmspace_h)\
+ $(bfont_h) $(store_h) $(gscencs_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfont.$(OBJ) $(C_) $(PSSRC)zfont.c
+
+$(PSOBJ)zfontenum.$(OBJ) : $(PSSRC)zfontenum.c $(OP)\
+ $(memory__h) $(gsstruct_h) $(ialloc_h) $(idict_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfontenum.$(OBJ) $(C_) $(PSSRC)zfontenum.c
+
+$(PSOBJ)zgstate.$(OBJ) : $(PSSRC)zgstate.c $(OP) $(math__h)\
+ $(gsmatrix_h)\
+ $(ialloc_h) $(icremap_h) $(idict_h) $(igstate_h) $(istruct_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zgstate.$(OBJ) $(C_) $(PSSRC)zgstate.c
+
+$(PSOBJ)zht.$(OBJ) : $(PSSRC)zht.c $(OP) $(memory__h)\
+ $(gsmatrix_h) $(gsstate_h) $(gsstruct_h) $(gxdevice_h) $(gzht_h)\
+ $(ialloc_h) $(estack_h) $(igstate_h) $(iht_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zht.$(OBJ) $(C_) $(PSSRC)zht.c
+
+$(PSOBJ)zimage.$(OBJ) : $(PSSRC)zimage.c $(OP) $(math__h) $(memory__h) $(stat__h)\
+ $(gscspace_h) $(gscssub_h) $(gsimage_h) $(gsmatrix_h) $(gsstruct_h)\
+ $(gxiparam_h)\
+ $(estack_h) $(ialloc_h) $(ifilter_h) $(igstate_h) $(iimage_h) $(ilevel_h)\
+ $(store_h) $(stream_h) $(gxcspace_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zimage.$(OBJ) $(C_) $(PSSRC)zimage.c
+
+$(PSOBJ)zmatrix.$(OBJ) : $(PSSRC)zmatrix.c $(OP)\
+ $(gsmatrix_h) $(igstate_h) $(gscoord_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zmatrix.$(OBJ) $(C_) $(PSSRC)zmatrix.c
+
+$(PSOBJ)zpaint.$(OBJ) : $(PSSRC)zpaint.c $(OP)\
+ $(gspaint_h) $(igstate_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zpaint.$(OBJ) $(C_) $(PSSRC)zpaint.c
+
+$(PSOBJ)zpath.$(OBJ) : $(PSSRC)zpath.c $(OP) $(math__h)\
+ $(gsmatrix_h) $(gspath_h) $(igstate_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zpath.$(OBJ) $(C_) $(PSSRC)zpath.c
+
+# Define the base PostScript language interpreter.
+# This is the subset of PostScript Level 1 required by our PDF reader.
+
+INT1=$(PSOBJ)iapi.$(OBJ) $(PSOBJ)icontext.$(OBJ) $(PSOBJ)idebug.$(OBJ)
+INT2=$(PSOBJ)idict.$(OBJ) $(PSOBJ)idparam.$(OBJ) $(PSOBJ)idstack.$(OBJ)
+INT3=$(PSOBJ)iinit.$(OBJ) $(PSOBJ)interp.$(OBJ)
+INT4=$(PSOBJ)iparam.$(OBJ) $(PSOBJ)ireclaim.$(OBJ) $(PSOBJ)iplugin.$(OBJ)
+INT5=$(PSOBJ)iscan.$(OBJ) $(PSOBJ)iscannum.$(OBJ) $(PSOBJ)istack.$(OBJ)
+INT6=$(PSOBJ)iutil.$(OBJ) $(GLOBJ)sa85d.$(OBJ) $(GLOBJ)scantab.$(OBJ)
+INT7=$(PSOBJ)sfilter1.$(OBJ) $(GLOBJ)sstring.$(OBJ) $(GLOBJ)stream.$(OBJ)
+Z1=$(PSOBJ)zarith.$(OBJ) $(PSOBJ)zarray.$(OBJ) $(PSOBJ)zcontrol.$(OBJ)
+Z2=$(PSOBJ)zdict.$(OBJ) $(PSOBJ)zfile.$(OBJ) $(PSOBJ)zfile1.$(OBJ) $(PSOBJ)zfileio.$(OBJ)
+Z3=$(PSOBJ)zfilter.$(OBJ) $(PSOBJ)zfproc.$(OBJ) $(PSOBJ)zgeneric.$(OBJ)
+Z4=$(PSOBJ)ziodev.$(OBJ) $(PSOBJ)ziodevs$(STDIO_IMPLEMENTATION).$(OBJ) $(PSOBJ)zmath.$(OBJ) $(PSOBJ)zalg.$(OBJ)
+Z5=$(PSOBJ)zmisc.$(OBJ) $(PSOBJ)zpacked.$(OBJ) $(PSOBJ)zrelbit.$(OBJ)
+Z6=$(PSOBJ)zstack.$(OBJ) $(PSOBJ)zstring.$(OBJ) $(PSOBJ)zsysvm.$(OBJ)
+Z7=$(PSOBJ)ztoken.$(OBJ) $(PSOBJ)ztype.$(OBJ) $(PSOBJ)zvmem.$(OBJ)
+Z8=$(PSOBJ)zbfont.$(OBJ) $(PSOBJ)zchar.$(OBJ) $(PSOBJ)zcolor.$(OBJ)
+Z9=$(PSOBJ)zdevice.$(OBJ) $(PSOBJ)zfont.$(OBJ) $(PSOBJ)zfontenum.$(OBJ) $(PSOBJ)zgstate.$(OBJ)
+Z10=$(PSOBJ)zdfilter.$(OBJ) $(PSOBJ)zht.$(OBJ) $(PSOBJ)zimage.$(OBJ) $(PSOBJ)zmatrix.$(OBJ)
+Z11=$(PSOBJ)zpaint.$(OBJ) $(PSOBJ)zpath.$(OBJ)
+Z12=$(PSOBJ)zncdummy.$(OBJ)
+Z1OPS=zarith zarray zcontrol1 zcontrol2 zcontrol3
+Z2OPS=zdict1 zdict2 zfile zfile1 zfileio1 zfileio2
+Z3_4OPS=zfilter zfproc zgeneric ziodev zmath zalg
+Z5_6OPS=zmisc zpacked zrelbit zstack zstring zsysvm
+Z7_8OPS=ztoken ztype zvmem zbfont zchar_a zchar_b zcolor zcolor_ext
+Z9OPS=zdevice zdevice_ext zfont zfontenum zgstate1 zgstate2 zgstate3
+Z10OPS=zdfilter zht zimage zmatrix zmatrix2
+Z11OPS=zpaint zpath pantone
+# We have to be a little underhanded with *config.$(OBJ) so as to avoid
+# circular definitions.
+INT_MAIN=$(PSOBJ)imain.$(OBJ) $(PSOBJ)imainarg.$(OBJ) $(GLOBJ)gsargs.$(OBJ) $(PSOBJ)idisp.$(OBJ)
+INT_OBJS=$(INT_MAIN)\
+ $(INT1) $(INT2) $(INT3) $(INT4) $(INT5) $(INT6) $(INT7)\
+ $(Z1) $(Z2) $(Z3) $(Z4) $(Z5) $(Z6) $(Z7) $(Z8) $(Z9) $(Z10) $(Z11) $(Z12)
+INT_CONFIG=$(GLOBJ)gconfig.$(OBJ) $(GLOBJ)gscdefs.$(OBJ)\
+ $(PSOBJ)iconfig.$(OBJ)
+INT_ALL=$(INT_OBJS) $(INT_CONFIG)
+# We omit libcore.dev, which should be included here, because problems
+# with the Unix linker require libcore to appear last in the link list
+# when libcore is really a library.
+# We omit $(INT_CONFIG) from the dependency list because they have special
+# dependency requirements and are added to the link list at the very end.
+# zfilter.c shouldn't include the RLE and RLD filters, but we don't want to
+# change this now.
+#
+# We add dscparse.dev here since it can be used with any PS level even
+# though we don't strictly need it unless we have the pdfwrite device.
+$(PSD)psbase.dev : $(INT_MAK) $(ECHOGS_XE) $(INT_OBJS)\
+ $(PSD)isupport.dev $(PSD)nobtoken.dev $(PSD)nousparm.dev\
+ $(GLD)rld.dev $(GLD)rle.dev $(GLD)sfile.dev $(PSD)dscparse.dev \
+ $(PSD)fapi_ps.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)psbase $(INT_MAIN)
+ $(ADDMOD) $(PSD)psbase -obj $(INT_CONFIG)
+ $(ADDMOD) $(PSD)psbase -obj $(INT1)
+ $(ADDMOD) $(PSD)psbase -obj $(INT2)
+ $(ADDMOD) $(PSD)psbase -obj $(INT3)
+ $(ADDMOD) $(PSD)psbase -obj $(INT4)
+ $(ADDMOD) $(PSD)psbase -obj $(INT5)
+ $(ADDMOD) $(PSD)psbase -obj $(INT6)
+ $(ADDMOD) $(PSD)psbase -obj $(INT7)
+ $(ADDMOD) $(PSD)psbase -obj $(Z1)
+ $(ADDMOD) $(PSD)psbase -obj $(Z2)
+ $(ADDMOD) $(PSD)psbase -obj $(Z3)
+ $(ADDMOD) $(PSD)psbase -obj $(Z4)
+ $(ADDMOD) $(PSD)psbase -obj $(Z5)
+ $(ADDMOD) $(PSD)psbase -obj $(Z6)
+ $(ADDMOD) $(PSD)psbase -obj $(Z7)
+ $(ADDMOD) $(PSD)psbase -obj $(Z8)
+ $(ADDMOD) $(PSD)psbase -obj $(Z9)
+ $(ADDMOD) $(PSD)psbase -obj $(Z10)
+ $(ADDMOD) $(PSD)psbase -obj $(Z11)
+ $(ADDMOD) $(PSD)psbase -obj $(Z12)
+ $(ADDMOD) $(PSD)psbase -oper $(Z1OPS)
+ $(ADDMOD) $(PSD)psbase -oper $(Z2OPS)
+ $(ADDMOD) $(PSD)psbase -oper $(Z3_4OPS)
+ $(ADDMOD) $(PSD)psbase -oper $(Z5_6OPS)
+ $(ADDMOD) $(PSD)psbase -oper $(Z7_8OPS)
+ $(ADDMOD) $(PSD)psbase -oper $(Z9OPS)
+ $(ADDMOD) $(PSD)psbase -oper $(Z10OPS)
+ $(ADDMOD) $(PSD)psbase -oper $(Z11OPS)
+ $(ADDMOD) $(PSD)psbase -iodev stdin stdout stderr lineedit statementedit
+ $(ADDMOD) $(PSD)psbase -include $(PSD)isupport $(PSD)nobtoken $(PSD)nousparm
+ $(ADDMOD) $(PSD)psbase -include $(GLD)rld $(GLD)rle $(GLD)sfile $(PSD)dscparse
+ $(ADDMOD) $(PSD)psbase -include $(GLD)fapi_ps
+ $(ADDMOD) $(PSD)psbase -replace $(GLD)gsiodevs
+
+# -------------------------- Feature definitions -------------------------- #
+
+# ---------------- Full Level 1 interpreter ---------------- #
+
+# We keep the old name for backward compatibility.
+$(PSD)level1.dev : $(PSD)psl1.dev $(MAKEDIRS)
+ $(CP_) $(PSD)psl1.dev $(PSD)level1.dev
+
+$(PSD)psl1.dev : $(INT_MAK) $(ECHOGS_XE)\
+ $(PSD)psbase.dev $(PSD)bcp.dev $(PSD)path1.dev $(PSD)type1.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)psl1 -include $(PSD)psbase $(PSD)bcp $(PSD)path1 $(PSD)type1
+ $(ADDMOD) $(PSD)psl1 -emulator PostScript PostScriptLevel1
+
+# -------- Level 1 color extensions (CMYK color and colorimage) -------- #
+
+$(PSD)color.dev : $(INT_MAK) $(ECHOGS_XE) $(GLD)cmyklib.dev $(GLD)colimlib.dev\
+ $(PSD)cmykread.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)color -include $(GLD)cmyklib $(GLD)colimlib $(PSD)cmykread
+
+cmykread_=$(PSOBJ)zcolor1.$(OBJ) $(PSOBJ)zht1.$(OBJ)
+$(PSD)cmykread.dev : $(INT_MAK) $(ECHOGS_XE) $(cmykread_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)cmykread $(cmykread_)
+ $(ADDMOD) $(PSD)cmykread -oper zcolor1 zht1
+
+$(PSOBJ)zcolor1.$(OBJ) : $(PSSRC)zcolor1.c $(OP)\
+ $(gscolor1_h) $(gscssub_h)\
+ $(gxcmap_h) $(gxcspace_h) $(gxdevice_h) $(gxfixed_h) $(gxmatrix_h)\
+ $(gzstate_h)\
+ $(ialloc_h) $(icolor_h) $(iimage_h) $(estack_h) $(iutil_h) $(igstate_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcolor1.$(OBJ) $(C_) $(PSSRC)zcolor1.c
+
+$(PSOBJ)zht1.$(OBJ) : $(PSSRC)zht1.c $(OP) $(memory__h)\
+ $(gsmatrix_h) $(gsstate_h) $(gsstruct_h) $(gxdevice_h) $(gzht_h)\
+ $(ialloc_h) $(estack_h) $(igstate_h) $(iht_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zht1.$(OBJ) $(C_) $(PSSRC)zht1.c
+
+# ---------------- DSC Parser ---------------- #
+
+# The basic DSC parsing facility, used both for Orientation detection
+# (to compensate for badly-written PostScript producers that don't emit
+# the necessary setpagedevice calls) and by the PDF writer.
+
+dscparse_h=$(PSSRC)dscparse.h
+
+$(PSOBJ)zdscpars.$(OBJ) : $(PSSRC)zdscpars.c $(GH) $(memory__h) $(string__h)\
+ $(dscparse_h) $(estack_h) $(ialloc_h) $(idict_h) $(iddict_h) $(iname_h)\
+ $(iparam_h) $(istack_h) $(ivmspace_h) $(oper_h) $(store_h)\
+ $(gsstruct_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zdscpars.$(OBJ) $(C_) $(PSSRC)zdscpars.c
+
+$(PSOBJ)dscparse.$(OBJ) : $(PSSRC)dscparse.c $(dscparse_h) $(stdio__h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)dscparse.$(OBJ) $(C_) $(PSSRC)dscparse.c
+
+dscparse_=$(PSOBJ)zdscpars.$(OBJ) $(PSOBJ)dscparse.$(OBJ)
+
+$(PSD)dscparse.dev : $(INT_MAK) $(ECHOGS_XE) $(dscparse_)\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)dscparse -obj $(dscparse_)
+ $(ADDMOD) $(PSD)dscparse -oper zdscpars
+
+# A feature to pass the Orientation information from the DSC comments
+# to setpagedevice.
+
+$(PSD)usedsc.dev : $(INT_MAK) $(ECHOGS_XE) $(PSD)dscparse.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)usedsc -include $(PSD)dscparse -ps gs_dscp
+
+# ---- Level 1 path miscellany (arcs, pathbbox, path enumeration) ---- #
+
+path1_=$(PSOBJ)zpath1.$(OBJ)
+$(PSD)path1.dev : $(INT_MAK) $(ECHOGS_XE) $(path1_) $(GLD)path1lib.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)path1 $(path1_)
+ $(ADDMOD) $(PSD)path1 -include $(GLD)path1lib
+ $(ADDMOD) $(PSD)path1 -oper zpath1
+
+$(PSOBJ)zpath1.$(OBJ) : $(PSSRC)zpath1.c $(OP) $(memory__h)\
+ $(ialloc_h) $(estack_h) $(gspath_h) $(gsstruct_h) $(igstate_h)\
+ $(oparc_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zpath1.$(OBJ) $(C_) $(PSSRC)zpath1.c
+
+# ================ Level-independent PostScript options ================ #
+
+# ---------------- BCP filters ---------------- #
+
+bcp_=$(GLOBJ)sbcp.$(OBJ) $(PSOBJ)zfbcp.$(OBJ)
+$(PSD)bcp.dev : $(INT_MAK) $(ECHOGS_XE) $(bcp_)\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)bcp $(bcp_)
+ $(ADDMOD) $(PSD)bcp -oper zfbcp
+
+$(PSOBJ)zfbcp.$(OBJ) : $(PSSRC)zfbcp.c $(OP) $(memory__h)\
+ $(gsstruct_h) $(ialloc_h) $(ifilter_h)\
+ $(sbcp_h) $(stream_h) $(strimpl_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfbcp.$(OBJ) $(C_) $(PSSRC)zfbcp.c
+
+# ---------------- Incremental font loading ---------------- #
+# (This only works for Type 1 fonts without eexec encryption.)
+
+$(PSD)diskfont.dev : $(INT_MAK) $(ECHOGS_XE) $(MAKEDIRS)
+ $(SETMOD) $(PSD)diskfont -ps gs_diskf
+
+# ---------------- Double-precision floats ---------------- #
+
+double_=$(PSOBJ)zdouble.$(OBJ)
+$(PSD)double.dev : $(INT_MAK) $(ECHOGS_XE) $(double_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)double $(double_)
+ $(ADDMOD) $(PSD)double -oper zdouble1 zdouble2
+
+$(PSOBJ)zdouble.$(OBJ) : $(PSSRC)zdouble.c $(OP)\
+ $(ctype__h) $(math__h) $(memory__h) $(string__h)\
+ $(gxfarith_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zdouble.$(OBJ) $(C_) $(PSSRC)zdouble.c
+
+# ---------------- EPSF files with binary headers ---------------- #
+
+$(PSD)epsf.dev : $(INT_MAK) $(ECHOGS_XE) $(MAKEDIRS)
+ $(SETMOD) $(PSD)epsf -ps gs_epsf
+
+# -------- Postscript end of the pdfwriter functionality --------- #
+
+$(PSD)gs_pdfwr.dev : $(INT_MAK) $(ECHOGS_XE) $(PSD)psl3.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)gs_pdfwr -include $(PSD)psl3
+ $(ADDMOD) $(PSD)gs_pdfwr -ps gs_pdfwr
+
+# ---------------- RasterOp ---------------- #
+# This should be a separable feature in the core also....
+
+$(PSD)rasterop.dev : $(INT_MAK) $(ECHOGS_XE) $(GLD)roplib.dev $(PSD)ropread.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)rasterop -include $(GLD)roplib $(PSD)ropread
+
+ropread_=$(PSOBJ)zrop.$(OBJ)
+$(PSD)ropread.dev : $(INT_MAK) $(ECHOGS_XE) $(ropread_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)ropread $(ropread_)
+ $(ADDMOD) $(PSD)ropread -oper zrop
+
+$(PSOBJ)zrop.$(OBJ) : $(PSSRC)zrop.c $(OP) $(memory__h)\
+ $(gsrop_h) $(gsutil_h) $(gxdevice_h)\
+ $(idict_h) $(idparam_h) $(igstate_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zrop.$(OBJ) $(C_) $(PSSRC)zrop.c
+
+# ---------------- PostScript Type 1 (and Type 4) fonts ---------------- #
+
+$(PSD)type1.dev : $(INT_MAK) $(ECHOGS_XE) $(GLD)psf1lib.dev $(PSD)psf1read.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)type1 -include $(GLD)psf1lib $(PSD)psf1read
+
+psf1read_1=$(PSOBJ)zchar1.$(OBJ) $(PSOBJ)zcharout.$(OBJ)
+psf1read_2=$(PSOBJ)zfont1.$(OBJ) $(PSOBJ)zmisc1.$(OBJ)
+psf1read_=$(psf1read_1) $(psf1read_2)
+$(PSD)psf1read.dev : $(INT_MAK) $(ECHOGS_XE) $(psf1read_) $(GLD)seexec.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)psf1read $(psf1read_1)
+ $(ADDMOD) $(PSD)psf1read -obj $(psf1read_2)
+ $(ADDMOD) $(PSD)psf1read -include $(GLD)seexec
+ $(ADDMOD) $(PSD)psf1read -oper zchar1 zfont1 zmisc1
+ $(ADDMOD) $(PSD)psf1read -ps gs_agl gs_type1
+
+$(PSOBJ)zchar1.$(OBJ) : $(PSSRC)zchar1.c $(OP) $(memory__h)\
+ $(gscencs_h) $(gspaint_h) $(gspath_h) $(gsrect_h) $(gsstruct_h)\
+ $(gxdevice_h) $(gxfixed_h) $(gxmatrix_h)\
+ $(gxfont_h) $(gxfont1_h) $(gxtype1_h) $(gxfcid_h) $(gxchar_h) $(gzstate_h)\
+ $(estack_h) $(ialloc_h) $(ichar_h) $(ichar1_h) $(icharout_h)\
+ $(idict_h) $(ifont_h) $(igstate_h) $(iname_h) $(iutil_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zchar1.$(OBJ) $(C_) $(PSSRC)zchar1.c
+
+$(PSOBJ)zfont1.$(OBJ) : $(PSSRC)zfont1.c $(OP) $(memory__h)\
+ $(gsmatrix_h) $(gxdevice_h)\
+ $(gxfixed_h) $(gxfont_h) $(gxfont1_h)\
+ $(bfont_h) $(ialloc_h) $(ichar1_h) $(icharout_h) $(idict_h) $(idparam_h)\
+ $(ifont1_h) $(iname_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfont1.$(OBJ) $(C_) $(PSSRC)zfont1.c
+
+$(PSOBJ)zmisc1.$(OBJ) : $(PSSRC)zmisc1.c $(OP) $(memory__h)\
+ $(gscrypt1_h)\
+ $(idict_h) $(idparam_h) $(ifilter_h)\
+ $(sfilter_h) $(stream_h) $(strimpl_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zmisc1.$(OBJ) $(C_) $(PSSRC)zmisc1.c
+
+# -------------- Compact Font Format and Type 2 charstrings ------------- #
+
+$(PSD)cff.dev : $(INT_MAK) $(ECHOGS_XE) $(PSD)psl2int.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)cff -include $(PSD)psl2int -ps gs_css_e gs_cff
+
+$(PSOBJ)zchar2.$(OBJ) : $(PSSRC)zchar2.c $(OP)\
+ $(gxfixed_h) $(gxmatrix_h) $(gxfont_h) $(gxfont1_h) $(gxtype1_h)\
+ $(ichar1_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zchar2.$(OBJ) $(C_) $(PSSRC)zchar2.c
+
+$(PSOBJ)zfont2.$(OBJ) : $(PSSRC)zfont2.c $(OP) $(string__h)\
+ $(gsmatrix_h) $(gxfixed_h) $(gxfont_h) $(gxfont1_h)\
+ $(bfont_h) $(idict_h) $(idparam_h) $(ifont1_h) $(ifont2_h)\
+ $(iname_h) $(iddict_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfont2.$(OBJ) $(C_) $(PSSRC)zfont2.c
+
+type2_=$(PSOBJ)zchar2.$(OBJ) $(PSOBJ)zfont2.$(OBJ)
+$(PSD)type2.dev : $(INT_MAK) $(ECHOGS_XE) $(type2_)\
+ $(PSD)type1.dev $(GLD)psf2lib.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)type2 $(type2_)
+ $(ADDMOD) $(PSD)type2 -oper zchar2 zfont2
+ $(ADDMOD) $(PSD)type2 -include $(PSD)type1 $(GLD)psf2lib
+
+# ---------------- Type 32 (downloaded bitmap) fonts ---------------- #
+
+$(PSOBJ)zchar32.$(OBJ) : $(PSSRC)zchar32.c $(OP)\
+ $(gsccode_h) $(gsmatrix_h) $(gsutil_h)\
+ $(gxfcache_h) $(gxfixed_h) $(gxfont_h)\
+ $(ifont_h) $(igstate_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zchar32.$(OBJ) $(C_) $(PSSRC)zchar32.c
+
+$(PSOBJ)zfont32.$(OBJ) : $(PSSRC)zfont32.c $(OP)\
+ $(gsccode_h) $(gsmatrix_h) $(gsutil_h) $(gxfont_h) $(gxtext_h)\
+ $(bfont_h) $(store_h) $(ichar_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfont32.$(OBJ) $(C_) $(PSSRC)zfont32.c
+
+type32_=$(PSOBJ)zchar32.$(OBJ) $(PSOBJ)zfont32.$(OBJ)
+$(PSD)type32.dev : $(INT_MAK) $(ECHOGS_XE) $(type32_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)type32 $(type32_)
+ $(ADDMOD) $(PSD)type32 -oper zchar32 zfont32
+ $(ADDMOD) $(PSD)type32 -ps gs_res gs_typ32
+
+# ---------------- TrueType and PostScript Type 42 fonts ---------------- #
+
+# Mac glyph support (has an internal dependency)
+$(PSD)macroman.dev : $(INT_MAK) $(ECHOGS_XE) $(PSINIT)gs_mro_e.ps\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)macroman -ps gs_mro_e
+
+$(PSD)macglyph.dev : $(INT_MAK) $(ECHOGS_XE) $(PSINIT)gs_mgl_e.ps\
+ $(PSD)macroman.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)macglyph -include $(PSD)macroman -ps gs_mgl_e
+
+# Native TrueType support
+$(PSD)ttfont.dev : $(INT_MAK) $(ECHOGS_XE) $(PSD)macglyph.dev $(PSD)type42.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)ttfont -include $(PSD)macglyph $(PSD)type42
+ $(ADDMOD) $(PSD)ttfont -ps gs_wan_e gs_ttf
+
+# Type 42 (embedded TrueType) support
+type42read_=$(PSOBJ)zchar42.$(OBJ) $(PSOBJ)zcharout.$(OBJ) $(PSOBJ)zfont42.$(OBJ)
+$(PSD)type42.dev : $(INT_MAK) $(ECHOGS_XE) $(type42read_) $(GLD)ttflib.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)type42 $(type42read_)
+ $(ADDMOD) $(PSD)type42 -include $(GLD)ttflib
+ $(ADDMOD) $(PSD)type42 -oper zchar42 zfont42
+ $(ADDMOD) $(PSD)type42 -ps gs_typ42
+
+$(PSOBJ)zchar42.$(OBJ) : $(PSSRC)zchar42.c $(OP)\
+ $(gsmatrix_h) $(gspaint_h) $(gspath_h)\
+ $(gxfixed_h) $(gxfont_h) $(gxfont42_h)\
+ $(gxistate_h) $(gxpath_h) $(gxtext_h) $(gzstate_h)\
+ $(dstack_h) $(estack_h) $(ichar_h) $(icharout_h)\
+ $(ifont_h) $(igstate_h) $(store_h) $(string_h) $(zchar42_h)\
+ $(idict_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zchar42.$(OBJ) $(C_) $(PSSRC)zchar42.c
+
+$(PSOBJ)zfont42.$(OBJ) : $(PSSRC)zfont42.c $(OP) $(memory__h)\
+ $(gsccode_h) $(gsmatrix_h) $(gxfont_h) $(gxfont42_h)\
+ $(bfont_h) $(icharout_h) $(idict_h) $(idparam_h) $(ifont42_h) $(iname_h)\
+ $(ichar1_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfont42.$(OBJ) $(C_) $(PSSRC)zfont42.c
+
+# ======================== Precompilation options ======================== #
+
+# ---------------- Stochastic halftone ---------------- #
+
+$(PSD)stocht.dev : $(INT_MAK) $(ECHOGS_XE) $(PSD)stocht$(COMPILE_INITS).dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)stocht -include $(PSD)stocht$(COMPILE_INITS)
+
+# If we aren't compiling, just include the PostScript code.
+# Note that the resource machinery must be loaded first.
+$(PSD)stocht0.dev : $(INT_MAK) $(ECHOGS_XE) $(MAKEDIRS)
+ $(SETMOD) $(PSD)stocht0 -ps gs_res ht_ccsto
+
+# If we are compiling, a special compilation step is needed.
+stocht1_=$(PSOBJ)ht_ccsto.$(OBJ)
+$(PSD)stocht1.dev : $(INT_MAK) $(ECHOGS_XE) $(stocht1_) $(PSD)stocht0.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)stocht1 $(stocht1_)
+ $(ADDMOD) $(PSD)stocht1 -halftone $(Q)StochasticDefault$(Q)
+ $(ADDMOD) $(PSD)stocht1 -include $(PSD)stocht0
+
+$(PSOBJ)ht_ccsto.$(OBJ) : $(PSGEN)ht_ccsto.c $(gxdhtres_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)ht_ccsto.$(OBJ) $(C_) $(PSGEN)ht_ccsto.c
+
+$(PSGEN)ht_ccsto.c : $(PSLIB)ht_ccsto.ps $(GENHT_XE) $(MAKEDIRS)
+ $(EXP)$(GENHT_XE) $(PSLIB)ht_ccsto.ps $(PSGEN)ht_ccsto.c
+
+# ================ PS LL3 features used internally in L2 ================ #
+
+# ---------------- Functions ---------------- #
+
+ifunc_h=$(PSSRC)ifunc.h $(gsfunc_h)
+
+# Generic support, and FunctionType 0.
+funcread_=$(PSOBJ)zfunc.$(OBJ) $(PSOBJ)zfunc0.$(OBJ)
+$(PSD)func.dev : $(INT_MAK) $(ECHOGS_XE) $(funcread_) $(GLD)funclib.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)func $(funcread_)
+ $(ADDMOD) $(PSD)func -oper zfunc
+ $(ADDMOD) $(PSD)func -functiontype 0
+ $(ADDMOD) $(PSD)func -include $(GLD)funclib
+
+$(PSOBJ)zfunc.$(OBJ) : $(PSSRC)zfunc.c $(OP) $(memory__h)\
+ $(gscdefs_h) $(gsfunc_h) $(gsstruct_h)\
+ $(ialloc_h) $(idict_h) $(idparam_h) $(ifunc_h) $(store_h) $(zfunc_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfunc.$(OBJ) $(C_) $(PSSRC)zfunc.c
+
+$(PSOBJ)zfunc0.$(OBJ) : $(PSSRC)zfunc0.c $(OP) $(memory__h)\
+ $(gsdsrc_h) $(gsfunc_h) $(gsfunc0_h)\
+ $(stream_h)\
+ $(files_h) $(ialloc_h) $(idict_h) $(idparam_h) $(ifunc_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfunc0.$(OBJ) $(C_) $(PSSRC)zfunc0.c
+
+# ---------------- zlib/Flate filters ---------------- #
+
+fzlib_=$(PSOBJ)zfzlib.$(OBJ)
+$(PSD)fzlib.dev : $(INT_MAK) $(ECHOGS_XE) $(fzlib_)\
+ $(GLD)szlibe.dev $(GLD)szlibd.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)fzlib -include $(GLD)szlibe $(GLD)szlibd
+ $(ADDMOD) $(PSD)fzlib -obj $(fzlib_)
+ $(ADDMOD) $(PSD)fzlib -oper zfzlib
+
+$(PSOBJ)zfzlib.$(OBJ) : $(PSSRC)zfzlib.c $(OP)\
+ $(idict_h) $(idparam_h) $(ifilter_h) $(ifrpred_h) $(ifwpred_h)\
+ $(spdiffx_h) $(spngpx_h) $(strimpl_h) $(szlibx_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfzlib.$(OBJ) $(C_) $(PSSRC)zfzlib.c
+
+# ---------------- ReusableStreamDecode filter ---------------- #
+# This is also used by the implementation of CIDFontType 0 fonts.
+
+$(PSD)frsd.dev : $(INT_MAK) $(ECHOGS_XE) $(PSD)zfrsd.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)frsd -include $(PSD)zfrsd
+ $(ADDMOD) $(PSD)frsd -ps gs_lev2 gs_res gs_frsd
+
+zfrsd_=$(PSOBJ)zfrsd.$(OBJ)
+$(PSD)zfrsd.dev : $(INT_MAK) $(ECHOGS_XE) $(zfrsd_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)zfrsd $(zfrsd_)
+ $(ADDMOD) $(PSD)zfrsd -oper zfrsd
+
+$(PSOBJ)zfrsd.$(OBJ) : $(PSSRC)zfrsd.c $(OP) $(memory__h)\
+ $(gsfname_h) $(gxiodev_h)\
+ $(sfilter_h) $(stream_h) $(strimpl_h)\
+ $(files_h) $(idict_h) $(idparam_h) $(iname_h) $(istruct_h) $(store_h)\
+ $(zfile_h) $(zfrsd_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfrsd.$(OBJ) $(C_) $(PSSRC)zfrsd.c
+
+# ======================== PostScript Level 2 ======================== #
+
+# We keep the old name for backward compatibility.
+$(PSD)level2.dev : $(PSD)psl2.dev
+ $(CP_) $(PSD)psl2.dev $(PSD)level2.dev
+
+# We -include dpsand2 first so that geninit will have access to the
+# system name table as soon as possible.
+$(PSD)psl2.dev : $(INT_MAK) $(ECHOGS_XE)\
+ $(PSD)cidfont.dev $(PSD)cie.dev $(PSD)cmapread.dev $(PSD)compfont.dev\
+ $(PSD)dct.dev $(PSD)dpsand2.dev\
+ $(PSD)filter.dev $(PSD)iodevice.dev $(PSD)pagedev.dev $(PSD)pattern.dev\
+ $(PSD)psl1.dev $(GLD)psl2lib.dev $(PSD)psl2read.dev\
+ $(PSD)sepr.dev $(PSD)type32.dev $(PSD)type42.dev\
+ $(PSD)fimscale.dev $(PSD)form.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)psl2 -include $(PSD)dpsand2
+ $(ADDMOD) $(PSD)psl2 -include $(PSD)cidfont $(PSD)cie $(PSD)cmapread $(PSD)compfont
+ $(ADDMOD) $(PSD)psl2 -include $(PSD)dct $(PSD)filter $(PSD)iodevice
+ $(ADDMOD) $(PSD)psl2 -include $(PSD)pagedev $(PSD)pattern $(PSD)psl1 $(GLD)psl2lib $(PSD)psl2read
+ $(ADDMOD) $(PSD)psl2 -include $(PSD)sepr $(PSD)type32 $(PSD)type42
+ $(ADDMOD) $(PSD)psl2 -include $(PSD)fimscale $(PSD)form
+ $(ADDMOD) $(PSD)psl2 -emulator PostScript PostScriptLevel2
+
+# Define basic Level 2 language support.
+# This is the minimum required for CMap and CIDFont support.
+
+psl2int_=$(PSOBJ)iutil2.$(OBJ) $(PSOBJ)zmisc2.$(OBJ)
+$(PSD)psl2int.dev : $(INT_MAK) $(ECHOGS_XE) $(psl2int_)\
+ $(PSD)dps2int.dev $(PSD)usparam.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)psl2int $(psl2int_)
+ $(ADDMOD) $(PSD)psl2int -include $(PSD)dps2int $(PSD)usparam
+ $(ADDMOD) $(PSD)psl2int -oper zmisc2
+ $(ADDMOD) $(PSD)psl2int -ps gs_lev2 gs_res
+
+ivmem2_h=$(PSSRC)ivmem2.h
+
+$(PSOBJ)iutil2.$(OBJ) : $(PSSRC)iutil2.c $(GH) $(memory__h) $(string__h)\
+ $(gsparam_h) $(gsutil_h)\
+ $(ierrors_h) $(idict_h) $(imemory_h) $(iutil_h) $(iutil2_h) $(opcheck_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)iutil2.$(OBJ) $(C_) $(PSSRC)iutil2.c
+
+$(PSOBJ)zmisc2.$(OBJ) : $(PSSRC)zmisc2.c $(OP) $(memory__h) $(string__h)\
+ $(iddict_h) $(idparam_h) $(iparam_h) $(dstack_h) $(estack_h)\
+ $(ilevel_h) $(iname_h) $(iutil2_h) $(ivmspace_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zmisc2.$(OBJ) $(C_) $(PSSRC)zmisc2.c
+
+# Define support for user and system parameters.
+# We make this a separate module only because it must have a default.
+
+nousparm_=$(PSOBJ)inouparm.$(OBJ)
+$(PSD)nousparm.dev : $(INT_MAK) $(ECHOGS_XE) $(nousparm_)\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)nousparm $(nousparm_)
+
+$(PSOBJ)inouparm.$(OBJ) : $(PSSRC)inouparm.c\
+ $(ghost_h) $(icontext_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)inouparm.$(OBJ) $(C_) $(PSSRC)inouparm.c
+
+usparam_=$(PSOBJ)zusparam.$(OBJ)
+$(PSD)usparam.dev : $(INT_MAK) $(ECHOGS_XE) $(usparam_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)usparam $(usparam_)
+ $(ADDMOD) $(PSD)usparam -oper zusparam -replace $(PSD)nousparm
+
+
+# Note that zusparam includes both Level 1 and Level 2 operators.
+$(PSOBJ)zusparam.$(OBJ) : $(PSSRC)zusparam.c $(OP) $(memory__h) $(string__h)\
+ $(gscdefs_h) $(gsfont_h) $(gsstruct_h) $(gsutil_h) $(gxht_h)\
+ $(ialloc_h) $(icontext_h) $(idict_h) $(idparam_h) $(iparam_h)\
+ $(iname_h) $(itoken_h) $(iutil2_h) $(ivmem2_h)\
+ $(dstack_h) $(estack_h) $(store_h) $(gsnamecl_h) $(gslibctx_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zusparam.$(OBJ) $(C_) $(PSSRC)zusparam.c
+
+# Define full Level 2 support.
+
+iimage2_h=$(PSSRC)iimage2.h
+
+psl2read_=$(PSOBJ)zcolor2.$(OBJ) $(PSOBJ)zcsindex.$(OBJ) $(PSOBJ)zht2.$(OBJ) $(PSOBJ)zimage2.$(OBJ)
+# Note that zmisc2 includes both Level 1 and Level 2 operators.
+$(PSD)psl2read.dev : $(INT_MAK) $(ECHOGS_XE) $(psl2read_)\
+ $(PSD)psl2int.dev $(PSD)dps2read.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)psl2read $(psl2read_)
+ $(ADDMOD) $(PSD)psl2read -include $(PSD)psl2int $(PSD)dps2read
+ $(ADDMOD) $(PSD)psl2read -oper zht2_l2
+
+$(PSOBJ)zcolor2.$(OBJ) : $(PSSRC)zcolor2.c $(OP) $(string__h)\
+ $(gscolor_h) $(gscssub_h) $(gsmatrix_h) $(gsstruct_h)\
+ $(gxcolor2_h) $(gxcspace_h) $(gxdcolor_h) $(gxdevice_h) $(gxdevmem_h) $(gxfixed_h) $(gxpcolor_h)\
+ $(estack_h) $(ialloc_h) $(idict_h) $(iname_h) $(idparam_h) $(igstate_h) $(istruct_h)\
+ $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcolor2.$(OBJ) $(C_) $(PSSRC)zcolor2.c
+
+$(PSOBJ)zcsindex.$(OBJ) : $(PSSRC)zcsindex.c $(OP) $(memory__h)\
+ $(gscolor_h) $(gsstruct_h) $(gxfixed_h) $(gxcolor2_h) $(gxcspace_h) $(gsmatrix_h)\
+ $(ialloc_h) $(icsmap_h) $(estack_h) $(igstate_h) $(ivmspace_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcsindex.$(OBJ) $(C_) $(PSSRC)zcsindex.c
+
+$(PSOBJ)zht2.$(OBJ) : $(PSSRC)zht2.c $(OP)\
+ $(gsstruct_h) $(gxdevice_h) $(gzht_h)\
+ $(estack_h) $(ialloc_h) $(icolor_h) $(iddict_h) $(idparam_h) $(igstate_h)\
+ $(iht_h) $(store_h) $(iname) $(zht2_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zht2.$(OBJ) $(C_) $(PSSRC)zht2.c
+
+$(PSOBJ)zimage2.$(OBJ) : $(PSSRC)zimage2.c $(OP) $(math__h) $(memory__h)\
+ $(gscolor_h) $(gscolor2_h) $(gscspace_h) $(gsimage_h) $(gsmatrix_h)\
+ $(gxfixed_h)\
+ $(idict_h) $(idparam_h) $(iimage_h) $(iimage2_h) $(ilevel_h) $(igstate_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zimage2.$(OBJ) $(C_) $(PSSRC)zimage2.c
+
+# ---------------- setpagedevice ---------------- #
+
+# NOTE: gs_pdfwr.ps is not strictly speaking an interpreter feature
+# but is logically part of the PS resources, and requires the content
+# of gs_setpd.ps to work, so we include it here (rather than in the
+# graphics library).
+
+pagedev_=$(PSOBJ)zdevice2.$(OBJ) $(PSOBJ)zmedia2.$(OBJ)
+$(PSD)pagedev.dev : $(INT_MAK) $(ECHOGS_XE) $(pagedev_)\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)pagedev $(pagedev_)
+ $(ADDMOD) $(PSD)pagedev -oper zdevice2_l2 zmedia2_l2
+ $(ADDMOD) $(PSD)pagedev -ps gs_setpd
+
+$(PSOBJ)zdevice2.$(OBJ) : $(PSSRC)zdevice2.c $(OP) $(math__h) $(memory__h)\
+ $(dstack_h) $(estack_h)\
+ $(idict_h) $(idparam_h) $(igstate_h) $(iname_h) $(iutil_h) $(store_h)\
+ $(gxdevice_h) $(gsstate_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zdevice2.$(OBJ) $(C_) $(PSSRC)zdevice2.c
+
+$(PSOBJ)zmedia2.$(OBJ) : $(PSSRC)zmedia2.c $(OP) $(math__h) $(memory__h)\
+ $(gsmatrix_h) $(idict_h) $(idparam_h) $(iname_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zmedia2.$(OBJ) $(C_) $(PSSRC)zmedia2.c
+
+# ---------------- IODevices ---------------- #
+
+iodevice_=$(PSOBJ)ziodev2.$(OBJ) $(PSOBJ)zdevcal.$(OBJ)
+$(PSD)iodevice.dev : $(INT_MAK) $(ECHOGS_XE) $(iodevice_)\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)iodevice $(iodevice_)
+ $(ADDMOD) $(PSD)iodevice -oper ziodev2_l2
+ $(ADDMOD) $(PSD)iodevice -iodev null calendar
+
+$(PSOBJ)ziodev2.$(OBJ) : $(PSSRC)ziodev2.c $(OP) $(string__h) $(gp_h)\
+ $(gxiodev_h) $(stream_h)\
+ $(dstack_h) $(files_h) $(iparam_h) $(iutil2_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)ziodev2.$(OBJ) $(C_) $(PSSRC)ziodev2.c
+
+$(PSOBJ)zdevcal.$(OBJ) : $(PSSRC)zdevcal.c $(GH) $(time__h)\
+ $(gxiodev_h) $(iparam_h) $(istack_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zdevcal.$(OBJ) $(C_) $(PSSRC)zdevcal.c
+
+# ---------------- Filters other than the ones in sfilter.c ---------------- #
+
+# Standard Level 2 decoding filters only. The PDF configuration uses this.
+fdecode_=$(GLOBJ)scantab.$(OBJ) $(GLOBJ)scfparam.$(OBJ) $(GLOBJ)sfilter2.$(OBJ) $(PSOBJ)zfdecode.$(OBJ)
+$(PSD)fdecode.dev : $(INT_MAK) $(ECHOGS_XE) $(fdecode_)\
+ $(GLD)cfd.dev $(GLD)lzwd.dev $(GLD)pdiff.dev $(GLD)pngp.dev $(GLD)rld.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)fdecode $(fdecode_)
+ $(ADDMOD) $(PSD)fdecode -include $(GLD)cfd $(GLD)lzwd $(GLD)pdiff $(GLD)pngp $(GLD)rld
+ $(ADDMOD) $(PSD)fdecode -oper zfdecode
+
+$(PSOBJ)zfdecode.$(OBJ) : $(PSSRC)zfdecode.c $(OP) $(memory__h)\
+ $(gsparam_h) $(gsstruct_h)\
+ $(ialloc_h) $(idict_h) $(idparam_h) $(ifilter_h) $(ifilter2_h) $(ifrpred_h)\
+ $(ilevel_h) $(iparam_h)\
+ $(sa85x_h) $(scf_h) $(scfx_h) $(sfilter_h) $(slzwx_h) $(spdiffx_h) $(spngpx_h)\
+ $(store_h) $(stream_h) $(strimpl_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfdecode.$(OBJ) $(C_) $(PSSRC)zfdecode.c
+
+# Complete Level 2 filter capability.
+filter_=$(PSOBJ)zfilter2.$(OBJ)
+$(PSD)filter.dev : $(INT_MAK) $(ECHOGS_XE) $(PSD)fdecode.dev $(filter_)\
+ $(GLD)cfe.dev $(GLD)lzwe.dev $(GLD)rle.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)filter -include $(PSD)fdecode
+ $(ADDMOD) $(PSD)filter -obj $(filter_)
+ $(ADDMOD) $(PSD)filter -include $(GLD)cfe $(GLD)lzwe $(GLD)rle
+ $(ADDMOD) $(PSD)filter -oper zfilter2
+
+$(PSOBJ)zfilter2.$(OBJ) : $(PSSRC)zfilter2.c $(OP) $(memory__h)\
+ $(gsstruct_h)\
+ $(ialloc_h) $(idict_h) $(idparam_h) $(ifilter_h) $(ifilter2_h) $(ifwpred_h)\
+ $(store_h)\
+ $(sfilter_h) $(scfx_h) $(slzwx_h) $(spdiffx_h) $(spngpx_h) $(strimpl_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfilter2.$(OBJ) $(C_) $(PSSRC)zfilter2.c
+
+# Extensions beyond Level 2 standard.
+xfilter_=$(GLD)smtf.$(OBJ) $(PSOBJ)zfilterx.$(OBJ)
+$(PSD)xfilter.dev : $(INT_MAK) $(ECHOGS_XE) $(xfilter_) $(GLD)pngp.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)xfilter $(xfilter_)
+ $(ADDMOD) $(PSD)xfilter -include $(GLD)pngp
+ $(ADDMOD) $(PSD)xfilter -oper zfilterx
+
+$(PSOBJ)smtf.$(OBJ) : $(PSSRC)smtf.c $(AK) $(stdio__h)\
+ $(smtf_h) $(strimpl_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)smtf.$(OBJ) $(C_) $(PSSRC)smtf.c
+
+$(PSOBJ)zfilterx.$(OBJ) : $(PSSRC)zfilterx.c $(OP) $(memory__h)\
+ $(gsstruct_h) $(ialloc_h) $(idict_h) $(idparam_h) $(ifilter_h)\
+ $(store_h) $(sfilter_h) $(sbtx_h) $(smtf_h) $(strimpl_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfilterx.$(OBJ) $(C_) $(PSSRC)zfilterx.c
+
+# MD5 digest filter
+fmd5_=$(PSOBJ)zfmd5.$(OBJ)
+$(PSD)fmd5.dev : $(INT_MAK) $(ECHOGS_XE) $(fmd5_) $(GLD)smd5.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)fmd5 $(fmd5_)
+ $(ADDMOD) $(PSD)fmd5 -include $(GLD)smd5
+ $(ADDMOD) $(PSD)fmd5 -oper zfmd5
+
+$(PSOBJ)zfmd5.$(OBJ) : $(PSSRC)zfmd5.c $(OP) $(memory__h)\
+ $(gsstruct_h) $(ialloc_h) $(ifilter_h)\
+ $(smd5_h) $(stream_h) $(strimpl_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfmd5.$(OBJ) $(C_) $(PSSRC)zfmd5.c
+
+# SHA-256 digest filter
+fsha2_=$(PSOBJ)zfsha2.$(OBJ)
+$(PSD)fsha2.dev : $(INT_MAK) $(ECHOGS_XE) $(fsha2_) $(GLD)ssha2.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)fsha2 $(fsha2_)
+ $(ADDMOD) $(PSD)fsha2 -include $(GLD)ssha2
+ $(ADDMOD) $(PSD)fsha2 -oper zfsha2
+
+$(PSOBJ)zfsha2.$(OBJ) : $(PSSRC)zfsha2.c $(OP) $(memory__h)\
+ $(ghost_h) $(oper_h) $(gsstruct_h) $(stream_h) $(strimpl_h)\
+ $(ialloc_h) $(ifilter_h) $(ssha2_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfsha2.$(OBJ) $(C_) $(PSSRC)zfsha2.c
+
+# Arcfour cipher filter
+farc4_=$(PSOBJ)zfarc4.$(OBJ)
+$(PSD)farc4.dev : $(INT_MAK) $(ECHOGS_XE) $(farc4_) $(GLD)sarc4.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)farc4 $(farc4_)
+ $(ADDMOD) $(PSD)farc4 -include $(GLD)sarc4
+ $(ADDMOD) $(PSD)farc4 -oper zfarc4
+
+$(PSOBJ)zfarc4.$(OBJ) : $(PSSRC)zfarc4.c $(OP) $(memory__h)\
+ $(gsstruct_h) $(ialloc_h) $(idict_h) $(ifilter_h)\
+ $(sarc4_h) $(stream_h) $(strimpl_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfarc4.$(OBJ) $(C_) $(PSSRC)zfarc4.c
+
+# AES cipher filter
+faes_=$(PSOBJ)zfaes.$(OBJ)
+$(PSD)faes.dev : $(INT_MAK) $(ECHOGS_XE) $(faes_) $(GLD)saes.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)faes $(faes_)
+ $(ADDMOD) $(PSD)faes -include $(GLD)saes
+ $(ADDMOD) $(PSD)faes -oper zfaes
+
+$(PSOBJ)zfaes.$(OBJ) : $(PSSRC)zfaes.c $(OP) $(memory__h)\
+ $(gsstruct_h) $(ialloc_h) $(idict_h) $(idparam_h) $(ifilter_h)\
+ $(saes_h) $(stream_h) $(strimpl_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfaes.$(OBJ) $(C_) $(PSSRC)zfaes.c
+
+# JBIG2 compression filter
+# this can be turned on and off with a FEATURE_DEV
+
+fjbig2_=$(PSOBJ)zfjbig2_$(JBIG2_LIB).$(OBJ)
+$(PSD)jbig2.dev : $(INT_MAK) $(ECHOGS_XE) $(fjbig2_) $(GLD)sjbig2.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)jbig2 $(fjbig2_)
+ $(ADDMOD) $(PSD)jbig2 -include $(GLD)sjbig2
+ $(ADDMOD) $(PSD)jbig2 -oper zfjbig2
+
+$(PSOBJ)zfjbig2_jbig2dec.$(OBJ) : $(PSSRC)zfjbig2.c $(OP) $(memory__h)\
+ $(gsstruct_h) $(gstypes_h) $(ialloc_h) $(idict_h) $(ifilter_h)\
+ $(store_h) $(stream_h) $(strimpl_h) $(sjbig2_h) $(MAKEDIRS)
+ $(PSJBIG2CC) $(PSO_)zfjbig2_jbig2dec.$(OBJ) $(C_) $(PSSRC)zfjbig2.c
+
+$(PSOBJ)zfjbig2_luratech.$(OBJ) : $(PSSRC)zfjbig2.c $(OP) $(memory__h)\
+ $(gsstruct_h) $(gstypes_h) $(ialloc_h) $(idict_h) $(ifilter_h)\
+ $(store_h) $(stream_h) $(strimpl_h) $(sjbig2_h) $(MAKEDIRS)
+ $(PSLDFJB2CC) $(PSO_)zfjbig2_luratech.$(OBJ) $(C_) $(PSSRC)zfjbig2.c
+
+# JPX (jpeg 2000) compression filter
+# this can be turned on and off with a FEATURE_DEV
+
+$(PSD)jpx.dev : $(INT_MAK) $(ECHOGS_XE) $(PSD)jpx_$(JPX_LIB).dev\
+ $(MAKEDIRS)
+ $(CP_) $(PSD)jpx_$(JPX_LIB).dev $(PSD)jpx.dev
+
+fjpx_luratech=$(PSOBJ)zfjpx_luratech.$(OBJ)
+
+$(PSOBJ)zfjpx.$(OBJ) : $(PSSRC)zfjpx.c $(OP) $(memory__h)\
+ $(gsstruct_h) $(gstypes_h) $(ialloc_h) $(idict_h) $(ifilter_h)\
+ $(store_h) $(stream_h) $(strimpl_h) $(ialloc_h) $(iname_h)\
+ $(gdebug_h) $(sjpx_h) $(MAKEDIRS)
+ $(PSJASCC) $(PSO_)zfjpx.$(OBJ) $(C_) $(PSSRC)zfjpx.c
+
+$(PSD)jpx_luratech.dev : $(INT_MAK) $(ECHOGS_XE) $(fjpx_luratech)\
+ $(GLD)sjpx.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)jpx_luratech $(fjpx_luratech)
+ $(ADDMOD) $(PSD)jpx_luratech -include $(GLD)sjpx
+ $(ADDMOD) $(PSD)jpx_luratech -include $(GLD)lwf_jp2
+ $(ADDMOD) $(PSD)jpx_luratech -oper zfjpx
+
+$(PSOBJ)zfjpx_luratech.$(OBJ) : $(PSSRC)zfjpx.c $(OP) $(memory__h)\
+ $(gsstruct_h) $(gstypes_h) $(ialloc_h) $(idict_h) $(ifilter_h)\
+ $(store_h) $(stream_h) $(strimpl_h) $(sjpx_luratech_h)\
+ $(MAKEDIRS)
+ $(PSLWFJPXCC) $(PSO_)zfjpx_luratech.$(OBJ) \
+ $(C_) $(PSSRC)zfjpx.c
+
+fjpx_openjpeg=$(PSOBJ)zfjpx_openjpeg.$(OBJ)
+
+$(PSD)jpx_openjpeg.dev : $(INT_MAK) $(ECHOGS_XE) $(fjpx_openjpeg)\
+ $(GLD)sjpx.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)jpx_openjpeg $(fjpx_openjpeg)
+ $(ADDMOD) $(PSD)jpx_openjpeg -include $(GLD)sjpx
+ $(ADDMOD) $(PSD)jpx_openjpeg -include $(GLD)openjpeg
+ $(ADDMOD) $(PSD)jpx_openjpeg -oper zfjpx
+
+$(PSOBJ)zfjpx_openjpeg.$(OBJ) : $(PSSRC)zfjpx.c $(OP) $(memory__h)\
+ $(gsstruct_h) $(gstypes_h) $(ialloc_h) $(idict_h) $(ifilter_h)\
+ $(store_h) $(stream_h) $(strimpl_h) $(sjpx_openjpeg_h)\
+ $(MAKEDIRS)
+ $(PSOPJJPXCC) $(PSO_)zfjpx_openjpeg.$(OBJ) \
+ $(C_) $(PSSRC)zfjpx.c
+
+
+# imagemask scaling filter
+fimscale_=$(PSOBJ)zfimscale.$(OBJ)
+$(PSD)fimscale.dev : $(INT_MAK) $(ECHOGS_XE) $(fimscale_)\
+ $(GLD)simscale.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)fimscale $(fimscale_)
+ $(ADDMOD) $(PSD)fimscale -include $(GLD)simscale
+ $(ADDMOD) $(PSD)fimscale -oper zfimscale
+
+$(PSOBJ)zfimscale.$(OBJ) : $(PSSRC)zfimscale.c $(OP) $(memory__h)\
+ $(gsstruct_h) $(ialloc_h) $(idict_h) $(ifilter_h)\
+ $(simscale_h) $(stream_h) $(strimpl_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfimscale.$(OBJ) $(C_) $(PSSRC)zfimscale.c
+
+# ---------------- Binary tokens ---------------- #
+
+nobtoken_=$(PSOBJ)inobtokn.$(OBJ)
+$(PSD)nobtoken.dev : $(INT_MAK) $(ECHOGS_XE) $(nobtoken_)\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)nobtoken $(nobtoken_)
+
+$(PSOBJ)inobtokn.$(OBJ) : $(PSSRC)inobtokn.c $(GH)\
+ $(stream_h) $(ierrors_h) $(iscan_h) $(iscanbin_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)inobtokn.$(OBJ) $(C_) $(PSSRC)inobtokn.c
+
+btoken_=$(PSOBJ)iscanbin.$(OBJ) $(PSOBJ)zbseq.$(OBJ)
+$(PSD)btoken.dev : $(INT_MAK) $(ECHOGS_XE) $(btoken_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)btoken $(btoken_)
+ $(ADDMOD) $(PSD)btoken -oper zbseq_l2 -replace $(PSD)nobtoken
+ $(ADDMOD) $(PSD)btoken -ps gs_btokn
+
+$(PSOBJ)iscanbin.$(OBJ) : $(PSSRC)iscanbin.c $(GH)\
+ $(math__h) $(memory__h) $(ierrors_h)\
+ $(gsutil_h) $(gxalloc_h) $(ialloc_h) $(ibnum_h) $(iddict_h) $(iname_h)\
+ $(iscan_h) $(iscanbin_h) $(iutil_h) $(ivmspace_h)\
+ $(btoken_h) $(dstack_h) $(ostack_h)\
+ $(sfilter_h) $(store_h) $(stream_h) $(strimpl_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)iscanbin.$(OBJ) $(C_) $(PSSRC)iscanbin.c
+
+$(PSOBJ)zbseq.$(OBJ) : $(PSSRC)zbseq.c $(OP) $(memory__h)\
+ $(gxalloc_h)\
+ $(btoken_h) $(ialloc_h) $(istruct_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zbseq.$(OBJ) $(C_) $(PSSRC)zbseq.c
+
+# ---------------- User paths & insideness testing ---------------- #
+
+upath_=$(PSOBJ)zupath.$(OBJ) $(PSOBJ)ibnum.$(OBJ) $(GLOBJ)gdevhit.$(OBJ)
+$(PSD)upath.dev : $(INT_MAK) $(ECHOGS_XE) $(upath_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)upath $(upath_)
+ $(ADDMOD) $(PSD)upath -oper zupath_l2
+
+$(PSOBJ)zupath.$(OBJ) : $(PSSRC)zupath.c $(OP)\
+ $(dstack_h) $(oparc_h) $(store_h)\
+ $(ibnum_h) $(idict_h) $(igstate_h) $(iname_h) $(iutil_h) $(stream_h)\
+ $(gscoord_h) $(gsmatrix_h) $(gspaint_h) $(gspath_h) $(gsstate_h)\
+ $(gxfixed_h) $(gxdevice_h) $(gzpath_h) $(gzstate_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zupath.$(OBJ) $(C_) $(PSSRC)zupath.c
+
+# -------- Additions common to Display PostScript and Level 2 -------- #
+
+$(PSD)dpsand2.dev : $(INT_MAK) $(ECHOGS_XE)\
+ $(PSD)btoken.dev $(PSD)color.dev $(PSD)upath.dev $(GLD)dps2lib.dev $(PSD)dps2read.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)dpsand2 -include $(PSD)btoken $(PSD)color $(PSD)upath $(GLD)dps2lib $(PSD)dps2read
+
+dps2int_=$(PSOBJ)zvmem2.$(OBJ) $(PSOBJ)zdps1.$(OBJ)
+# Note that zvmem2 includes both Level 1 and Level 2 operators.
+$(PSD)dps2int.dev : $(INT_MAK) $(ECHOGS_XE) $(dps2int_)\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)dps2int $(dps2int_)
+ $(ADDMOD) $(PSD)dps2int -oper zvmem2 zdps1_l2
+ $(ADDMOD) $(PSD)dps2int -ps gs_dps1
+
+dps2read_=$(PSOBJ)ibnum.$(OBJ) $(PSOBJ)zcharx.$(OBJ)
+$(PSD)dps2read.dev : $(INT_MAK) $(ECHOGS_XE) $(dps2read_) $(PSD)dps2int.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)dps2read $(dps2read_)
+ $(ADDMOD) $(PSD)dps2read -include $(PSD)dps2int
+ $(ADDMOD) $(PSD)dps2read -oper ireclaim_l2 zcharx
+ $(ADDMOD) $(PSD)dps2read -ps gs_dps2
+
+$(PSOBJ)ibnum.$(OBJ) : $(PSSRC)ibnum.c $(GH) $(math__h) $(memory__h)\
+ $(ierrors_h) $(stream_h) $(ibnum_h) $(imemory_h) $(iutil_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)ibnum.$(OBJ) $(C_) $(PSSRC)ibnum.c
+
+$(PSOBJ)zcharx.$(OBJ) : $(PSSRC)zcharx.c $(OP)\
+ $(gsmatrix_h) $(gstext_h) $(gxfixed_h) $(gxfont_h) $(gxtext_h)\
+ $(ialloc_h) $(ibnum_h) $(ichar_h) $(iname_h) $(igstate_h) $(memory__h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcharx.$(OBJ) $(C_) $(PSSRC)zcharx.c
+
+$(PSOBJ)zdps1.$(OBJ) : $(PSSRC)zdps1.c $(OP)\
+ $(gsmatrix_h) $(gspath_h) $(gspath2_h) $(gsstate_h)\
+ $(ialloc_h) $(ivmspace_h) $(igstate_h) $(store_h) $(stream_h) $(ibnum_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zdps1.$(OBJ) $(C_) $(PSSRC)zdps1.c
+
+$(PSOBJ)zvmem2.$(OBJ) : $(PSSRC)zvmem2.c $(OP)\
+ $(estack_h) $(ialloc_h) $(ivmspace_h) $(store_h) $(ivmem2_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zvmem2.$(OBJ) $(C_) $(PSSRC)zvmem2.c
+
+# -------- Composite (PostScript Type 0) font support -------- #
+
+$(PSD)compfont.dev : $(INT_MAK) $(ECHOGS_XE)\
+ $(GLD)psf0lib.dev $(PSD)psf0read.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)compfont -include $(GLD)psf0lib $(PSD)psf0read
+
+# We always include cmapread because zfont0.c refers to it,
+# and it's not worth the trouble to exclude.
+psf0read_=$(PSOBJ)zcfont.$(OBJ) $(PSOBJ)zfont0.$(OBJ)
+$(PSD)psf0read.dev : $(INT_MAK) $(ECHOGS_XE) $(psf0read_)\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)psf0read $(psf0read_)
+ $(ADDMOD) $(PSD)psf0read -oper zcfont zfont0
+ $(ADDMOD) $(PSD)psf0read -include $(PSD)cmapread
+
+$(PSOBJ)zcfont.$(OBJ) : $(PSSRC)zcfont.c $(OP)\
+ $(gsmatrix_h)\
+ $(gxfixed_h) $(gxfont_h) $(gxtext_h)\
+ $(ichar_h) $(estack_h) $(ifont_h) $(igstate_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcfont.$(OBJ) $(C_) $(PSSRC)zcfont.c
+
+$(PSOBJ)zfont0.$(OBJ) : $(PSSRC)zfont0.c $(OP)\
+ $(gsstruct_h)\
+ $(gxdevice_h) $(gxfcmap_h) $(gxfixed_h) $(gxfont_h) $(gxfont0_h) $(gxmatrix_h)\
+ $(gzstate_h)\
+ $(bfont_h) $(ialloc_h) $(iddict_h) $(idparam_h) $(igstate_h) $(iname_h)\
+ $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfont0.$(OBJ) $(C_) $(PSSRC)zfont0.c
+
+# ---------------- CMap and CIDFont support ---------------- #
+# Note that this requires at least minimal Level 2 support,
+# because it requires findresource.
+
+icid_h=$(PSSRC)icid.h
+ifcid_h=$(PSSRC)ifcid.h
+
+cmapread_=$(PSOBJ)zcid.$(OBJ) $(PSOBJ)zfcmap.$(OBJ)
+$(PSD)cmapread.dev : $(INT_MAK) $(ECHOGS_XE) $(cmapread_)\
+ $(GLD)cmaplib.dev $(PSD)psl2int.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)cmapread $(cmapread_)
+ $(ADDMOD) $(PSD)cmapread -include $(GLD)cmaplib $(PSD)psl2int
+ $(ADDMOD) $(PSD)cmapread -oper zfcmap
+ $(ADDMOD) $(PSD)cmapread -ps gs_cmap
+
+$(PSOBJ)zfcmap.$(OBJ) : $(PSSRC)zfcmap.c $(OP) $(memory__h)\
+ $(gsmatrix_h) $(gsstruct_h) $(gsutil_h)\
+ $(gxfcmap1_h) $(gxfont_h)\
+ $(ialloc_h) $(icid_h) $(iddict_h) $(idparam_h) $(ifont_h) $(iname_h)\
+ $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfcmap.$(OBJ) $(C_) $(PSSRC)zfcmap.c
+
+cidread_=$(PSOBJ)zcid.$(OBJ) $(PSOBJ)zfcid.$(OBJ) $(PSOBJ)zfcid0.$(OBJ) $(PSOBJ)zfcid1.$(OBJ)
+$(PSD)cidfont.dev : $(INT_MAK) $(ECHOGS_XE) $(cidread_)\
+ $(PSD)psf1read.dev $(PSD)psl2int.dev $(PSD)type2.dev $(PSD)type42.dev\
+ $(PSD)zfrsd.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)cidfont $(cidread_)
+ $(ADDMOD) $(PSD)cidfont -include $(PSD)psf1read $(PSD)psl2int
+ $(ADDMOD) $(PSD)cidfont -include $(PSD)type2 $(PSD)type42 $(PSD)zfrsd
+ $(ADDMOD) $(PSD)cidfont -oper zfcid0 zfcid1
+ $(ADDMOD) $(PSD)cidfont -ps gs_cidfn gs_cidcm gs_fntem gs_cidtt gs_cidfm
+
+$(PSOBJ)zcid.$(OBJ) : $(PSSRC)zcid.c $(OP)\
+ $(gxcid_h) $(ierrors_h) $(icid_h) $(idict_h) $(idparam_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcid.$(OBJ) $(C_) $(PSSRC)zcid.c
+
+$(PSOBJ)zfcid.$(OBJ) : $(PSSRC)zfcid.c $(OP)\
+ $(gsmatrix_h) $(gxfcid_h)\
+ $(bfont_h) $(icid_h) $(idict_h) $(idparam_h) $(ifcid_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfcid.$(OBJ) $(C_) $(PSSRC)zfcid.c
+
+$(PSOBJ)zfcid0.$(OBJ) : $(PSSRC)zfcid0.c $(OP) $(memory__h)\
+ $(gsccode_h) $(gsmatrix_h) $(gsstruct_h)\
+ $(gxalloc_h) $(gxfcid_h) $(gxfont1_h)\
+ $(stream_h)\
+ $(bfont_h) $(files_h) $(ichar_h) $(ichar1_h) $(icid_h) $(idict_h) $(idparam_h)\
+ $(ifcid_h) $(ifont1_h) $(ifont2_h) $(ifont42_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfcid0.$(OBJ) $(C_) $(PSSRC)zfcid0.c
+
+$(PSOBJ)zfcid1.$(OBJ) : $(PSSRC)zfcid1.c $(OP) $(memory__h)\
+ $(gsccode_h) $(gsmatrix_h) $(gsstruct_h) $(gsgcache_h) $(gsutil_h)\
+ $(gxfcid_h) $(gxfcache_h)\
+ $(bfont_h) $(icid_h) $(ichar1_h) $(idict_h) $(idparam_h)\
+ $(ifcid_h) $(ifont42_h) $(store_h) $(stream_h) $(files_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfcid1.$(OBJ) $(C_) $(PSSRC)zfcid1.c
+
+# Testing only (CIDFont and CMap)
+
+cidtest_=$(PSOBJ)zcidtest.$(OBJ) $(GLOBJ)gsfont0c.$(OBJ)
+$(PSD)cidtest.dev : $(INT_MAK) $(ECHOGS_XE) $(cidtest_)\
+ $(PSD)cidfont.dev $(PSD)cmapread.dev $(GLD)psf.dev $(GLD)psf0lib.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)cidtest $(cidtest_)
+ $(ADDMOD) $(PSD)cidtest -oper zcidtest
+ $(ADDMOD) $(PSD)cidtest -include $(PSD)cidfont $(PSD)cmapread
+ $(ADDMOD) $(PSD)cidtest -include $(GLD)psf $(GLD)psf0lib
+
+$(PSOBJ)zcidtest.$(OBJ) : $(PSSRC)zcidtest.c $(string__h) $(OP)\
+ $(gdevpsf_h) $(gxfont_h) $(gxfont0c_h)\
+ $(spprint_h) $(stream_h)\
+ $(files_h) $(idict_h) $(ifont_h) $(igstate_h) $(iname_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcidtest.$(OBJ) $(C_) $(PSSRC)zcidtest.c
+
+# ---------------- CIE color ---------------- #
+
+cieread_=$(PSOBJ)zcie.$(OBJ) $(PSOBJ)zcrd.$(OBJ)
+$(PSD)cie.dev : $(INT_MAK) $(ECHOGS_XE) $(cieread_) $(GLD)cielib.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)cie $(cieread_)
+ $(ADDMOD) $(PSD)cie -oper zcrd_l2
+ $(ADDMOD) $(PSD)cie -include $(GLD)cielib
+
+icie_h=$(PSSRC)icie.h
+
+$(PSOBJ)zcie.$(OBJ) : $(PSSRC)zcie.c $(OP) $(math__h) $(memory__h)\
+ $(gscolor2_h) $(gscie_h) $(gsstruct_h) $(gxcspace_h)\
+ $(ialloc_h) $(icie_h) $(idict_h) $(idparam_h) $(estack_h)\
+ $(isave_h) $(igstate_h) $(ivmspace_h) $(store_h)\
+ $(zcie_h) $(gsicc_create_h) $(gsicc_manage_h) $(gsicc_profilecache_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcie.$(OBJ) $(C_) $(PSSRC)zcie.c
+
+$(PSOBJ)zcrd.$(OBJ) : $(PSSRC)zcrd.c $(OP) $(math__h)\
+ $(gscrd_h) $(gscrdp_h) $(gscspace_h) $(gscolor2_h) $(gsstruct_h)\
+ $(estack_h) $(ialloc_h) $(icie_h) $(idict_h) $(idparam_h) $(igstate_h)\
+ $(iparam_h) $(ivmspace_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcrd.$(OBJ) $(C_) $(PSSRC)zcrd.c
+
+# ---------------- Pattern color ---------------- #
+
+ipcolor_h=$(PSSRC)ipcolor.h
+
+$(PSD)pattern.dev : $(INT_MAK) $(ECHOGS_XE) $(GLD)patlib.dev\
+ $(PSD)patread.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)pattern -include $(GLD)patlib $(PSD)patread
+
+patread_=$(PSOBJ)zpcolor.$(OBJ)
+$(PSD)patread.dev : $(INT_MAK) $(ECHOGS_XE) $(patread_)\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)patread $(patread_)
+ $(ADDMOD) $(PSD)patread -oper zpcolor_l2
+
+$(PSOBJ)zpcolor.$(OBJ) : $(PSSRC)zpcolor.c $(OP)\
+ $(gscolor_h) $(gsmatrix_h) $(gsstruct_h) $(gscoord_h)\
+ $(gxcolor2_h) $(gxcspace_h) $(gxdcolor_h) $(gxdevice_h) $(gxdevmem_h)\
+ $(gxfixed_h) $(gxpcolor_h) $(gxpath_h)\
+ $(estack_h)\
+ $(ialloc_h) $(icremap_h) $(idict_h) $(idparam_h) $(igstate_h)\
+ $(ipcolor_h) $(istruct_h)\
+ $(store_h) $(gzstate_h) $(memory__h) $(gdevp14_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zpcolor.$(OBJ) $(C_) $(PSSRC)zpcolor.c
+
+# ---------------- Separation color ---------------- #
+
+seprread_=$(PSOBJ)zcssepr.$(OBJ) $(PSOBJ)zfsample.$(OBJ)
+$(PSD)sepr.dev : $(INT_MAK) $(ECHOGS_XE) $(seprread_)\
+ $(PSD)func4.dev $(GLD)seprlib.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)sepr $(seprread_)
+ $(ADDMOD) $(PSD)sepr -oper zcssepr_l2
+ $(ADDMOD) $(PSD)sepr -oper zfsample
+ $(ADDMOD) $(PSD)sepr -include $(PSD)func4 $(GLD)seprlib
+
+$(PSOBJ)zcssepr.$(OBJ) : $(PSSRC)zcssepr.c $(OP) $(memory__h)\
+ $(gscolor_h) $(gscsepr_h) $(gsmatrix_h) $(gsstruct_h)\
+ $(gxcolor2_h) $(gxcspace_h) $(gxfixed_h) $(zht2_h)\
+ $(estack_h) $(ialloc_h) $(icsmap_h) $(ifunc_h) $(igstate_h)\
+ $(iname_h) $(ivmspace_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcssepr.$(OBJ) $(C_) $(PSSRC)zcssepr.c
+
+$(PSOBJ)zfsample.$(OBJ) : $(PSSRC)zfsample.c $(OP) $(memory__h)\
+ $(gxcspace_h)\
+ $(estack_h) $(ialloc_h) $(idict_h) $(idparam_h) $(ifunc_h) $(ostack_h)\
+ $(store_h) $(gsfunc0_h) $(gscdevn_h) $(zfunc_h) $(zcolor_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfsample.$(OBJ) $(C_) $(PSSRC)zfsample.c
+
+# ---------------- DCT filters ---------------- #
+# The definitions for jpeg*.dev are in jpeg.mak.
+
+$(PSD)dct.dev : $(INT_MAK) $(ECHOGS_XE) $(PSD)dcte.dev $(PSD)dctd.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)dct -include $(PSD)dcte $(PSD)dctd
+
+# Encoding (compression)
+
+dcte_=$(PSOBJ)zfdcte.$(OBJ)
+$(PSD)dcte.dev : $(INT_MAK) $(ECHOGS_XE) $(GLD)sdcte.dev $(GLD)sdeparam.dev\
+ $(dcte_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)dcte -include $(GLD)sdcte $(GLD)sdeparam
+ $(ADDMOD) $(PSD)dcte -obj $(dcte_)
+ $(ADDMOD) $(PSD)dcte -oper zfdcte
+
+$(PSOBJ)zfdcte.$(OBJ) : $(PSSRC)zfdcte.c $(OP)\
+ $(memory__h) $(stdio__h) $(jpeglib__h) $(gsmemory_h)\
+ $(sdct_h) $(sjpeg_h) $(stream_h) $(strimpl_h)\
+ $(files_h) $(ialloc_h) $(idict_h) $(idparam_h) $(ifilter_h)\
+ $(iparam_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfdcte.$(OBJ) $(C_) $(PSSRC)zfdcte.c
+
+# Decoding (decompression)
+
+dctd_=$(PSOBJ)zfdctd.$(OBJ)
+$(PSD)dctd.dev : $(INT_MAK) $(ECHOGS_XE) $(GLD)sdctd.dev $(GLD)sddparam.dev\
+ $(dctd_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)dctd -include $(GLD)sdctd $(GLD)sddparam
+ $(ADDMOD) $(PSD)dctd -obj $(dctd_)
+ $(ADDMOD) $(PSD)dctd -oper zfdctd
+
+$(PSOBJ)zfdctd.$(OBJ) : $(PSSRC)zfdctd.c $(OP)\
+ $(memory__h) $(stdio__h) $(jpeglib__h) $(gsmemory_h)\
+ $(ialloc_h) $(ifilter_h) $(iparam_h) $(sdct_h) $(sjpeg_h)\
+ $(strimpl_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfdctd.$(OBJ) $(C_) $(PSSRC)zfdctd.c
+
+# ================ Display PostScript ================ #
+
+dps_=$(PSOBJ)zdps.$(OBJ) $(PSOBJ)zcontext.$(OBJ)
+$(PSD)dps.dev : $(INT_MAK) $(ECHOGS_XE) $(GLD)dpslib.dev $(PSD)psl2.dev\
+ $(dps_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)dps -include $(GLD)dpslib $(PSD)psl2
+ $(ADDMOD) $(PSD)dps -obj $(dps_)
+ $(ADDMOD) $(PSD)dps -oper zcontext1 zcontext2 zdps
+ $(ADDMOD) $(PSD)dps -ps gs_dps
+
+$(PSOBJ)zdps.$(OBJ) : $(PSSRC)zdps.c $(OP)\
+ $(gsdps_h) $(gsimage_h) $(gsiparm2_h) $(gsstate_h)\
+ $(gxalloc_h) $(gxfixed_h) $(gxpath_h)\
+ $(btoken_h)\
+ $(idparam_h) $(iddict_h) $(igstate_h) $(iimage2_h) $(iname_h)\
+ $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zdps.$(OBJ) $(C_) $(PSSRC)zdps.c
+
+$(PSOBJ)zcontext.$(OBJ) : $(PSSRC)zcontext.c $(OP) $(gp_h) $(memory__h)\
+ $(gsexit_h) $(gsgc_h) $(gsstruct_h) $(gsutil_h) $(gxalloc_h) $(gxstate_h)\
+ $(icontext_h) $(idict_h) $(igstate_h) $(interp_h) $(isave_h) $(istruct_h)\
+ $(dstack_h) $(estack_h) $(files_h) $(ostack_h) $(store_h) $(stream_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcontext.$(OBJ) $(C_) $(PSSRC)zcontext.c
+
+# ---------------- NeXT Display PostScript ---------------- #
+
+dpsnext_=$(PSOBJ)zdpnext.$(OBJ)
+$(PSD)dpsnext.dev : $(INT_MAK) $(ECHOGS_XE) $(dpsnext_)\
+ $(PSD)dps.dev $(GLD)dpnxtlib.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)dpsnext -include $(PSD)dps $(GLD)dpnxtlib
+ $(ADDMOD) $(PSD)dpsnext -obj $(dpsnext_)
+ $(ADDMOD) $(PSD)dpsnext -oper zdpnext
+ $(ADDMOD) $(PSD)dpsnext -ps gs_dpnxt
+
+$(PSOBJ)zdpnext.$(OBJ) : $(PSSRC)zdpnext.c $(math__h) $(OP)\
+ $(gscoord_h) $(gscspace_h) $(gsdpnext_h)\
+ $(gsiparam_h) $(gsiparm2_h) $(gsmatrix_h) $(gspath2_h)\
+ $(gxcvalue_h) $(gxdevice_h) $(gxsample_h)\
+ $(ialloc_h) $(igstate_h) $(iimage_h) $(iimage2_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zdpnext.$(OBJ) $(C_) $(PSSRC)zdpnext.c
+
+# ==================== PostScript LanguageLevel 3 ===================== #
+
+# ---------------- DevicePixel color space ---------------- #
+
+cspixint_=$(PSOBJ)zcspixel.$(OBJ)
+$(PSD)cspixel.dev : $(INT_MAK) $(ECHOGS_XE) $(cspixint_) $(GLD)cspixlib.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)cspixel $(cspixint_)
+ $(ADDMOD) $(PSD)cspixel -include $(GLD)cspixlib
+
+$(PSOBJ)zcspixel.$(OBJ) : $(PSSRC)zcspixel.c $(OP)\
+ $(gscolor2_h) $(gscpixel_h) $(gscspace_h) $(gsmatrix_h)\
+ $(igstate_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcspixel.$(OBJ) $(C_) $(PSSRC)zcspixel.c
+
+# ---------------- Rest of LanguageLevel 3 ---------------- #
+
+$(PSD)psl3.dev : $(INT_MAK) $(ECHOGS_XE)\
+ $(PSD)psl2.dev $(PSD)cspixel.dev $(PSD)frsd.dev $(PSD)func.dev\
+ $(GLD)psl3lib.dev $(PSD)psl3read.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)psl3 -include $(PSD)psl2 $(PSD)cspixel $(PSD)frsd $(PSD)func
+ $(ADDMOD) $(PSD)psl3 -include $(GLD)psl3lib $(PSD)psl3read
+ $(ADDMOD) $(PSD)psl3 -emulator PostScript PostScriptLevel2 PostScriptLevel3
+
+$(PSOBJ)zfunc3.$(OBJ) : $(PSSRC)zfunc3.c $(memory__h) $(OP)\
+ $(gsfunc3_h) $(gsstruct_h)\
+ $(files_h) $(ialloc_h) $(idict_h) $(idparam_h) $(ifunc_h)\
+ $(store_h) $(stream_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfunc3.$(OBJ) $(C_) $(PSSRC)zfunc3.c
+
+# FunctionType 4 functions are not a PostScript feature, but they
+# are used in the implementation of Separation and DeviceN color spaces.
+
+func4read_=$(PSOBJ)zfunc4.$(OBJ)
+$(PSD)func4.dev : $(INT_MAK) $(ECHOGS_XE) $(func4read_)\
+ $(PSD)func.dev $(GLD)func4lib.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)func4 $(func4read_)
+ $(ADDMOD) $(PSD)func4 -functiontype 4
+ $(ADDMOD) $(PSD)func4 -include $(PSD)func $(GLD)func4lib
+
+# Note: opextern.h is included from oper.h and is a dependency of oper.h
+$(PSOBJ)zfunc4.$(OBJ) : $(PSSRC)zfunc4.c $(memory__h) $(string__h)\
+ $(OP) $(opextern_h)\
+ $(gsfunc_h) $(gsfunc4_h) $(gsutil_h)\
+ $(idict_h) $(ifunc_h) $(iname_h) $(ialloc_h)\
+ $(dstack_h) $(gzstate_h) $(gxdevcli_h) $(string__h) $(zfunc_h)\
+ $(zcolor_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfunc4.$(OBJ) $(C_) $(PSSRC)zfunc4.c
+
+$(PSOBJ)zimage3.$(OBJ) : $(PSSRC)zimage3.c $(OP) $(memory__h)\
+ $(gscolor2_h) $(gsiparm3_h) $(gsiparm4_h) $(gscspace_h) $(gxiparam_h)\
+ $(idparam_h) $(idict_h) $(igstate_h) $(iimage_h) $(iimage2_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zimage3.$(OBJ) $(C_) $(PSSRC)zimage3.c
+
+$(PSOBJ)zmisc3.$(OBJ) : $(PSSRC)zmisc3.c $(GH)\
+ $(gsclipsr_h) $(gscolor2_h) $(gscspace_h) $(gscssub_h) $(gsmatrix_h)\
+ $(igstate_h) $(oper_h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zmisc3.$(OBJ) $(C_) $(PSSRC)zmisc3.c
+
+$(PSOBJ)zcolor3.$(OBJ) : $(PSSRC)zcolor3.c $(GH)\
+ $(oper_h) $(igstate_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zcolor3.$(OBJ) $(C_) $(PSSRC)zcolor3.c
+
+$(PSOBJ)zshade.$(OBJ) : $(PSSRC)zshade.c $(memory__h) $(OP)\
+ $(gscolor2_h) $(gscolor3_h) $(gscspace_h) $(gsfunc3_h)\
+ $(gsptype2_h) $(gsshade_h) $(gsstruct_h) $(gsuid_h) $(gscie_h)\
+ $(stream_h)\
+ $(files_h)\
+ $(ialloc_h) $(idict_h) $(idparam_h) $(ifunc_h) $(igstate_h) $(ipcolor_h)\
+ $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zshade.$(OBJ) $(C_) $(PSSRC)zshade.c
+
+psl3read_1=$(PSOBJ)zfunc3.$(OBJ) $(PSOBJ)zfsample.$(OBJ)
+psl3read_2=$(PSOBJ)zimage3.$(OBJ) $(PSOBJ)zmisc3.$(OBJ) $(PSOBJ)zcolor3.$(OBJ)\
+ $(PSOBJ)zshade.$(OBJ)
+psl3read_=$(psl3read_1) $(psl3read_2)
+
+# Note: we need the ReusableStreamDecode filter for shadings.
+$(PSD)psl3read.dev : $(INT_MAK) $(ECHOGS_XE) $(psl3read_)\
+ $(PSD)frsd.dev $(PSD)fzlib.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)psl3read $(psl3read_1)
+ $(ADDMOD) $(PSD)psl3read $(psl3read_2)
+ $(ADDMOD) $(PSD)psl3read -oper zfsample
+ $(ADDMOD) $(PSD)psl3read -oper zimage3 zmisc3 zcolor3_l3 zshade
+ $(ADDMOD) $(PSD)psl3read -functiontype 2 3
+ $(ADDMOD) $(PSD)psl3read -ps gs_ll3
+ $(ADDMOD) $(PSD)psl3read -include $(PSD)frsd $(PSD)fzlib
+
+# ---------------- Trapping ---------------- #
+
+trapread_=$(PSOBJ)ztrap.$(OBJ)
+$(PSD)trapread.dev : $(INT_MAK) $(ECHOGS_XE) $(trapread_)\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)trapread $(trapread_)
+ $(ADDMOD) $(PSD)trapread -oper ztrap
+ $(ADDMOD) $(PSD)trapread -ps gs_trap
+
+$(PSOBJ)ztrap.$(OBJ) : $(PSSRC)ztrap.c $(OP)\
+ $(gstrap_h)\
+ $(ialloc_h) $(iparam_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)ztrap.$(OBJ) $(C_) $(PSSRC)ztrap.c
+
+$(PSD)trapping.dev : $(INT_MAK) $(ECHOGS_XE) $(GLD)traplib.dev $(PSD)trapread.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)trapping -include $(GLD)traplib $(PSD)trapread
+
+# ---------------- Transparency ---------------- #
+
+transread_=$(PSOBJ)ztrans.$(OBJ)
+$(PSD)transpar.dev : $(INT_MAK) $(ECHOGS_XE)\
+ $(PSD)psl2read.dev $(GLD)translib.dev $(transread_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)transpar $(transread_)
+ $(ADDMOD) $(PSD)transpar -oper ztrans1 ztrans2
+ $(ADDMOD) $(PSD)transpar -include $(PSD)psl2read $(GLD)translib
+
+$(PSOBJ)ztrans.$(OBJ) : $(PSSRC)ztrans.c $(OP) $(memory__h) $(string__h)\
+ $(ghost_h) $(oper_h) $(gscspace_h) $(gscolor2_h) $(gsipar3x_h) $(gstrans_h)\
+ $(gxiparam_h) $(gxcspace_h)\
+ $(idict_h) $(idparam_h) $(ifunc_h) $(igstate_h) $(iimage_h) $(iname_h)\
+ $(store_h) $(gsdflt_h) $(gdevdevn_h) $(gxblend_h) $(gdevp14_h)\
+ $(gsicc_cms_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)ztrans.$(OBJ) $(C_) $(PSSRC)ztrans.c
+
+# ---------------- ICCBased color spaces ---------------- #
+
+iccread_=$(PSOBJ)zicc.$(OBJ)
+$(PSD)icc.dev : $(INT_MAK) $(ECHOGS_XE) $(PSD)cie.dev $(iccread_) \
+ $(GLD)sicclib.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)icc $(iccread_)
+ $(ADDMOD) $(PSD)icc -oper zicc_ll3
+ $(ADDMOD) $(PSD)icc -ps gs_icc
+ $(ADDMOD) $(PSD)icc -include $(GLD)sicclib $(PSD)cie
+
+$(PSOBJ)zicc.$(OBJ) : $(PSSRC)zicc.c $(OP) $(math__h) $(memory__h)\
+ $(gsstruct_h) $(gxcspace_h) $(stream_h) $(files_h) $(gscolor2_h)\
+ $(gsicc_h) $(estack_h) $(idict_h) $(idparam_h) $(igstate_h)\
+ $(icie_h) $(ialloc_h) $(zicc_h) $(gsicc_manage_h) $(GX) $(gxistate_h)\
+ $(gsicc_create_h) $(gsicc_profilecache_h) $(gxdevice_h)\
+ $(gsicc_cache_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zicc.$(OBJ) $(C_) $(PSSRC)zicc.c
+
+# ---------------- Support for %disk IODevices ---------------- #
+
+# Note that we go ahead and create 7 %disk devices. The internal
+# overhead of any unused %disk structures is minimal.
+# We could have more, but the DynaLab font installer has problems
+# with more than 7 disk devices.
+diskn_=$(GLOBJ)gsiodisk.$(OBJ)
+$(GLD)diskn.dev : $(LIB_MAK) $(ECHOGS_XE) $(diskn_) $(MAKEDIRS)
+ $(SETMOD) $(GLD)diskn $(diskn_)
+ $(ADDMOD) $(GLD)diskn -iodev disk0 disk1 disk2 disk3 disk4 disk5 disk6
+ $(ADDMOD) $(GLD)diskn -ps gs_diskn
+
+# ------------------ Support high level Forms ------------------ #
+form_=$(GLOBJ)zform.$(OBJ)
+$(GLD)form.dev : $(LIB_MAK) $(ECHOGS_XE) $(form_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)form $(form_)
+ $(ADDMOD) $(PSD)form -oper zform
+
+$(PSOBJ)zform.$(OBJ) : $(PSSRC)zform.c $(OP) $(ghost_h) $(oper_h)\
+ $(gxdevice_h) $(ialloc_h) $(idict_h) $(idparam_h) $(igstate_h)\
+ $(gxdevsop_h) $(gscoord_h) $(gsform1_h) $(gspath_h) $(gxpath_h)\
+ $(gzstate_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zform.$(OBJ) $(C_) $(PSSRC)zform.c
+
+# ================================ PDF ================================ #
+
+# We need nearly all of the PostScript LanguageLevel 3 interpreter for PDF,
+# but not all of it: we could do without the arc operators (Level 1),
+# the Encode filters (Level 2), and some LL3 features (clipsave/cliprestore,
+# UseCIEColor, IdiomSets). However, we've decided it isn't worth the
+# trouble to do the fine-grain factoring to enable this, since code size
+# is not at a premium for PDF interpreters.
+
+# Because of the way the PDF encodings are defined, they must get loaded
+# before we install the Level 2 resource machinery.
+# On the other hand, the PDF .ps files must get loaded after
+# level2dict is defined.
+$(PSD)pdf.dev : $(INT_MAK) $(ECHOGS_XE)\
+ $(GLD)dps2lib.dev $(PSD)dps2read.dev\
+ $(PSD)pdffonts.dev $(PSD)psl3.dev $(PSD)pdfread.dev $(PSD)cff.dev\
+ $(PSD)fmd5.dev $(PSD)fsha2.dev $(PSD)farc4.dev $(PSD)faes.dev\
+ $(PSD)ttfont.dev $(PSD)type2.dev $(PSD)icc.dev $(PSD)pdfops.dev\
+ $(PSD)pdf_r6.dev $(MAKEDIRS)
+ $(SETMOD) $(PSD)pdf -include $(PSD)psbase $(GLD)dps2lib
+ $(ADDMOD) $(PSD)pdf -include $(PSD)dps2read $(PSD)pdffonts $(PSD)psl3
+ $(ADDMOD) $(PSD)pdf -include $(GLD)psl2lib $(PSD)pdfread $(PSD)cff
+ $(ADDMOD) $(PSD)pdf -include $(PSD)fmd5 $(PSD)fsha2
+ $(ADDMOD) $(PSD)pdf -include $(PSD)farc4 $(PSD)faes.dev
+ $(ADDMOD) $(PSD)pdf -include $(PSD)ttfont $(PSD)type2
+ $(ADDMOD) $(PSD)pdf -include $(PSD)icc $(PSD)pdfops
+ $(ADDMOD) $(PSD)pdf -include $(PSD)pdf_r6
+ $(ADDMOD) $(PSD)pdf -functiontype 4
+ $(ADDMOD) $(PSD)pdf -emulator PDF
+
+# Reader only
+
+$(PSD)pdffonts.dev : $(INT_MAK) $(ECHOGS_XE) $(MAKEDIRS)
+ $(SETMOD) $(PSD)pdffonts -ps gs_mex_e gs_mro_e gs_pdf_e gs_wan_e
+
+$(PSD)pdfread.dev : $(INT_MAK) $(ECHOGS_XE) \
+ $(PSD)frsd.dev $(PSD)func4.dev $(PSD)fzlib.dev $(PSD)transpar.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)pdfread -include $(PSD)frsd $(PSD)func4 $(PSD)fzlib
+ $(ADDMOD) $(PSD)pdfread -include $(PSD)transpar
+ $(ADDMOD) $(PSD)pdfread -ps pdf_ops gs_l2img
+ $(ADDMOD) $(PSD)pdfread -ps pdf_rbld
+ $(ADDMOD) $(PSD)pdfread -ps pdf_base pdf_draw pdf_font pdf_main pdf_sec
+
+# Optional Illustrator CS2/CS3 layer info extractor
+
+$(PSD)cslayer.dev : $(INT_MAK) $(ECHOGS_XE) $(PSD)pdfread.dev\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)cslayer -ps pdf_cslayer
+
+# ---------------- PS Support for Font API ---------------- #
+$(PSD)fapi_ps.dev : $(LIB_MAK) $(ECHOGS_XE) $(PSOBJ)zfapi.$(OBJ)\
+ $(MAKEDIRS)
+ $(SETMOD) $(PSD)fapi_ps $(PSOBJ)zfapi.$(OBJ)
+ $(ADDMOD) $(PSD)fapi_ps -oper zfapi
+ $(ADDMOD) $(PSD)fapi_ps -ps gs_fntem gs_fapi
+
+$(PSOBJ)zfapi.$(OBJ) : $(PSSRC)zfapi.c $(OP) $(math__h) $(memory__h) $(string__h)\
+ $(stat__h)\
+ $(gp_h) $(gscoord_h) $(gscrypt1_h) $(gsfont_h) $(gspaint_h) $(gspath_h)\
+ $(gxchar_h) $(gxchrout_h) $(gximask_h) $(gxdevice_h) $(gxfcache_h) $(gxfcid_h)\
+ $(gxfont_h) $(gxfont1_h) $(gxpath_h) $(gzstate_h) \
+ $(bfont_h) $(dstack_h) $(files_h) \
+ $(ichar_h) $(idict_h) $(iddict_h) $(idparam_h) $(iname_h) $(ifont_h)\
+ $(icid_h) $(igstate_h) $(icharout_h) $(ifapi_h) $(iplugin_h) \
+ $(oper_h) $(store_h) $(stream_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zfapi.$(OBJ) $(C_) $(PSSRC)zfapi.c
+
+# ---------------- Custom color dummy callback ---------------- #
+
+$(PSOBJ)zncdummy.$(OBJ) : $(PSSRC)zncdummy.c $(OP) $(GX) $(math_h)\
+ $(memory__h) $(gscdefs_h) $(gsnamecl_h) $(malloc__h) $(gsncdummy_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)zncdummy.$(OBJ) $(C_) $(PSSRC)zncdummy.c
+
+# ---------------- Custom operators for PDF interpreter ---------------- #
+
+zpdfops_=$(PSOBJ)zpdfops.$(OBJ)
+$(PSD)pdfops.dev : $(INT_MAK) $(ECHOGS_XE) $(zpdfops_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)pdfops $(zpdfops_)
+ $(ADDMOD) $(PSD)pdfops -oper zpdfops
+
+$(PSOBJ)zpdfops.$(OBJ) : $(PSSRC)zpdfops.c $(OP) $(MAKEFILE)\
+ $(igstate_h) $(istack_h) $(iutil_h) $(gspath_h) $(math__h) $(ialloc_h)\
+ $(string__h) $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zpdfops.$(OBJ) $(C_) $(PSSRC)zpdfops.c
+
+zutf8_=$(PSOBJ)zutf8.$(OBJ)
+$(PSD)utf8.dev : $(INT_MAK) $(ECHOGS_XE) $(zutf8_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)utf8 $(zutf8_)
+ $(ADDMOD) $(PSD)utf8 -oper zutf8
+
+$(PSOBJ)zutf8.$(OBJ) : $(PSSRC)zutf8.c $(OP)\
+ $(ghost_h) $(oper_h) $(iutil_h) $(ialloc_h) $(malloc__h) $(string__h)\
+ $(store_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zutf8.$(OBJ) $(C_) $(PSSRC)zutf8.c
+
+zpdf_r6_=$(PSOBJ)zpdf_r6.$(OBJ)
+$(PSD)pdf_r6.dev : $(INT_MAK) $(ECHOGS_XE) $(zpdf_r6_) $(MAKEDIRS)
+ $(SETMOD) $(PSD)pdf_r6 $(zpdf_r6_)
+ $(ADDMOD) $(PSD)pdf_r6 -oper zpdf_r6
+
+$(PSOBJ)zpdf_r6.$(OBJ) : $(PSSRC)zpdf_r6.c $(OP) $(MAKEFILE) $(MAKEDIRS)
+ $(PSCC) $(PSO_)zpdf_r6.$(OBJ) $(C_) $(PSSRC)zpdf_r6.c
+
+# ================ Dependencies for auxiliary programs ================ #
+
+# ============================= Main program ============================== #
+
+$(PSOBJ)gs.$(OBJ) : $(PSSRC)gs.c $(GH)\
+ $(ierrors_h) $(iapi_h) $(imain_h) $(imainarg_h) $(iminst_h) $(gsmalloc_h)\
+ $(locale__h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)gs.$(OBJ) $(C_) $(PSSRC)gs.c
+
+$(PSOBJ)apitest.$(OBJ) : $(PSSRC)apitest.c $(GH)\
+ $(ierrors_h) $(iapi_h) $(imain_h) $(imainarg_h) $(iminst_h) $(gsmalloc_h)\
+ $(locale__h) $(gp_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)apitest.$(OBJ) $(C_) $(PSSRC)apitest.c
+
+$(PSOBJ)iapi.$(OBJ) : $(PSSRC)iapi.c $(AK)\
+ $(string__h) $(ierrors_h) $(gscdefs_h) $(gstypes_h) $(iapi_h)\
+ $(iref_h) $(imain_h) $(imainarg_h) $(iminst_h) $(gslibctx_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)iapi.$(OBJ) $(C_) $(PSSRC)iapi.c
+
+$(PSOBJ)icontext.$(OBJ) : $(PSSRC)icontext.c $(GH)\
+ $(gsstruct_h) $(gxalloc_h)\
+ $(dstack_h) $(ierrors_h) $(estack_h) $(files_h)\
+ $(icontext_h) $(idict_h) $(igstate_h) $(interp_h) $(isave_h) $(store_h)\
+ $(stream_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)icontext.$(OBJ) $(C_) $(PSSRC)icontext.c
+
+gdevdsp_h=$(DEVSRCDIR)$(D)gdevdsp.h
+gdevdsp2_h=$(DEVSRCDIR)$(D)gdevdsp2.h
+
+$(PSOBJ)idisp.$(OBJ) : $(PSSRC)idisp.c $(OP) $(stdio__h) $(gp_h)\
+ $(stdpre_h) $(gscdefs_h) $(gsdevice_h) $(gsmemory_h) $(gstypes_h)\
+ $(iapi_h) $(iref_h)\
+ $(imain_h) $(iminst_h) $(idisp_h) $(ostack_h)\
+ $(gx_h) $(gxdevice_h) $(gxdevmem_h) $(gdevdsp_h) $(gdevdsp2_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(I_)$(DEVSRCDIR) $(PSO_)idisp.$(OBJ) $(C_) $(PSSRC)idisp.c
+
+$(PSOBJ)imainarg.$(OBJ) : $(PSSRC)imainarg.c $(GH)\
+ $(ctype__h) $(memory__h) $(string__h)\
+ $(gp_h)\
+ $(gsargs_h) $(gscdefs_h) $(gsdevice_h) $(gsmalloc_h) $(gsmdebug_h)\
+ $(gspaint_h) $(gxclpage_h) $(gdevprn_h) $(gxdevice_h) $(gxdevmem_h)\
+ $(ierrors_h) $(estack_h) $(files_h)\
+ $(iapi_h) $(ialloc_h) $(iconf_h) $(imain_h) $(imainarg_h) $(iminst_h)\
+ $(iname_h) $(interp_h) $(iscan_h) $(iutil_h) $(ivmspace_h)\
+ $(ostack_h) $(sfilter_h) $(store_h) $(stream_h) $(strimpl_h) \
+ $(vdtrace_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)imainarg.$(OBJ) $(C_) $(PSSRC)imainarg.c
+
+$(PSOBJ)imain.$(OBJ) : $(PSSRC)imain.c $(GH) $(memory__h) $(string__h)\
+ $(gp_h) $(gscdefs_h) $(gslib_h) $(gsmatrix_h) $(gsutil_h)\
+ $(gspaint_h) $(gxclpage_h) $(gxalloc_h) $(gxdevice_h) $(gzstate_h)\
+ $(dstack_h) $(ierrors_h) $(estack_h) $(files_h)\
+ $(ialloc_h) $(iconf_h) $(idebug_h) $(idict_h) $(idisp_h) $(iinit_h)\
+ $(iname_h) $(interp_h) $(iplugin_h) $(isave_h) $(iscan_h) $(ivmspace_h)\
+ $(iinit_h) $(main_h) $(oper_h) $(ostack_h)\
+ $(sfilter_h) $(store_h) $(stream_h) $(strimpl_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)imain.$(OBJ) $(C_) $(PSSRC)imain.c
+
+#****** $(CCINT) interp.c
+$(PSOBJ)interp.$(OBJ) : $(PSSRC)interp.c $(GH) $(memory__h) $(string__h)\
+ $(gsstruct_h) $(idebug_h)\
+ $(dstack_h) $(ierrors_h) $(estack_h) $(files_h)\
+ $(ialloc_h) $(iastruct_h) $(icontext_h) $(icremap_h) $(iddict_h) $(igstate_h)\
+ $(iname_h) $(inamedef_h) $(interp_h) $(ipacked_h)\
+ $(isave_h) $(iscan_h) $(istack_h) $(itoken_h) $(iutil_h) $(ivmspace_h)\
+ $(oper_h) $(ostack_h) $(sfilter_h) $(store_h) $(stream_h) $(strimpl_h)\
+ $(gpcheck_h) $(MAKEDIRS)
+ $(PSCC) $(PSO_)interp.$(OBJ) $(C_) $(PSSRC)interp.c
+
+$(PSOBJ)ireclaim.$(OBJ) : $(PSSRC)ireclaim.c $(GH)\
+ $(gsstruct_h)\
+ $(iastate_h) $(icontext_h) $(interp_h) $(isave_h) $(isstate_h)\
+ $(dstack_h) $(ierrors_h) $(estack_h) $(opdef_h) $(ostack_h) $(store_h)\
+ $(MAKEDIRS)
+ $(PSCC) $(PSO_)ireclaim.$(OBJ) $(C_) $(PSSRC)ireclaim.c
diff --git a/psi/interp.c b/psi/interp.c
new file mode 100644
index 000000000..64f336022
--- /dev/null
+++ b/psi/interp.c
@@ -0,0 +1,1944 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Ghostscript language interpreter */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gsstruct.h" /* for iastruct.h */
+#include "gserrors.h" /* for gpcheck.h */
+#include "stream.h"
+#include "ierrors.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "iastruct.h"
+#include "icontext.h"
+#include "icremap.h"
+#include "idebug.h"
+#include "igstate.h" /* for handling gs_error_Remap_Color */
+#include "inamedef.h"
+#include "iname.h" /* for the_name_table */
+#include "interp.h"
+#include "ipacked.h"
+#include "ostack.h" /* must precede iscan.h */
+#include "strimpl.h" /* for sfilter.h */
+#include "sfilter.h" /* for iscan.h */
+#include "iscan.h"
+#include "iddict.h"
+#include "isave.h"
+#include "istack.h"
+#include "itoken.h"
+#include "iutil.h" /* for array_get */
+#include "ivmspace.h"
+#include "iinit.h"
+#include "dstack.h"
+#include "files.h" /* for file_check_read */
+#include "oper.h"
+#include "store.h"
+#include "gpcheck.h"
+
+/*
+ * We may or may not optimize the handling of the special fast operators
+ * in packed arrays. If we do this, they run much faster when packed, but
+ * slightly slower when not packed.
+ */
+#define PACKED_SPECIAL_OPS 1
+
+/*
+ * Pseudo-operators (procedures of type t_oparray) record
+ * the operand and dictionary stack pointers, and restore them if an error
+ * occurs during the execution of the procedure and if the procedure hasn't
+ * (net) decreased the depth of the stack. While this obviously doesn't
+ * do all the work of restoring the state if a pseudo-operator gets an
+ * error, it's a big help. The only downside is that pseudo-operators run
+ * a little slower.
+ */
+
+/* GC descriptors for stacks */
+extern_st(st_ref_stack);
+public_st_dict_stack();
+public_st_exec_stack();
+public_st_op_stack();
+
+/*
+ * Apply an operator. When debugging, we route all operator calls
+ * through a procedure.
+ */
+#if defined(DEBUG_TRACE_PS_OPERATORS) || defined(DEBUG)
+#define call_operator(proc, p) (*call_operator_fn)(proc, p)
+static int
+do_call_operator(op_proc_t op_proc, i_ctx_t *i_ctx_p)
+{
+ int code;
+ code = op_proc(i_ctx_p);
+ if (gs_debug_c(gs_debug_flag_validate_chunks))
+ ivalidate_clean_spaces(i_ctx_p);
+ return code; /* A good place for a conditional breakpoint. */
+}
+static int
+do_call_operator_verbose(op_proc_t op_proc, i_ctx_t *i_ctx_p)
+{
+ int code;
+
+#ifndef SHOW_STACK_DEPTHS
+ if_debug1m('!', imemory, "[!]operator %s\n", op_get_name_string(op_proc));
+#else
+ if_debug3m('!', imemory, "[!][es=%d os=%d]operator %s\n",
+ esp-i_ctx_p->exec_stack.stack.bot,
+ osp-i_ctx_p->op_stack.stack.bot,
+ op_get_name_string(op_proc));
+#endif
+ code = do_call_operator(op_proc, i_ctx_p);
+#if defined(SHOW_STACK_DEPTHS)
+ if_debug2m('!', imemory, "[!][es=%d os=%d]\n",
+ esp-i_ctx_p->exec_stack.stack.bot,
+ osp-i_ctx_p->op_stack.stack.bot);
+#endif
+ if (gs_debug_c(gs_debug_flag_validate_chunks))
+ ivalidate_clean_spaces(i_ctx_p);
+ return code; /* A good place for a conditional breakpoint. */
+}
+#else
+# define call_operator(proc, p) ((*(proc))(p))
+#endif
+
+/* Define debugging statistics (not threadsafe as uses globals) */
+#if defined(DEBUG) && !defined(GS_THREADSAFE)
+struct stats_interp_s {
+ long top;
+ long lit, lit_array, exec_array, exec_operator, exec_name;
+ long x_add, x_def, x_dup, x_exch, x_if, x_ifelse,
+ x_index, x_pop, x_roll, x_sub;
+ long find_name, name_lit, name_proc, name_oparray, name_operator;
+ long p_full, p_exec_operator, p_exec_oparray, p_exec_non_x_operator,
+ p_integer, p_lit_name, p_exec_name;
+ long p_find_name, p_name_lit, p_name_proc;
+} stats_interp;
+# define INCR(v) (++(stats_interp.v))
+#else
+# define INCR(v) DO_NOTHING
+#endif
+
+/* Forward references */
+static int estack_underflow(i_ctx_t *);
+static int interp(i_ctx_t **, const ref *, ref *);
+static int interp_exit(i_ctx_t *);
+static void set_gc_signal(i_ctx_t *, int *, int);
+static int copy_stack(i_ctx_t *, const ref_stack_t *, int skip, ref *);
+static int oparray_pop(i_ctx_t *);
+static int oparray_cleanup(i_ctx_t *);
+static int zerrorexec(i_ctx_t *);
+static int zfinderrorobject(i_ctx_t *);
+static int errorexec_find(i_ctx_t *, ref *);
+static int errorexec_pop(i_ctx_t *);
+static int errorexec_cleanup(i_ctx_t *);
+static int zsetstackprotect(i_ctx_t *);
+static int zcurrentstackprotect(i_ctx_t *);
+
+/* Stack sizes */
+
+/* The maximum stack sizes may all be set in the makefile. */
+
+/*
+ * Define the initial maximum size of the operand stack (MaxOpStack
+ * user parameter).
+ */
+#ifndef MAX_OSTACK
+# define MAX_OSTACK 800
+#endif
+/*
+ * The minimum block size for extending the operand stack is the larger of:
+ * - the maximum number of parameters to an operator
+ * (currently setcolorscreen, with 12 parameters);
+ * - the maximum number of values pushed by an operator
+ * (currently setcolortransfer, which calls zcolor_remap_one 4 times
+ * and therefore pushes 16 values).
+ */
+#define MIN_BLOCK_OSTACK 16
+const int gs_interp_max_op_num_args = MIN_BLOCK_OSTACK; /* for iinit.c */
+
+/*
+ * Define the initial maximum size of the execution stack (MaxExecStack
+ * user parameter).
+ */
+#ifndef MAX_ESTACK
+# define MAX_ESTACK 5000
+#endif
+/*
+ * The minimum block size for extending the execution stack is the largest
+ * size of a contiguous block surrounding an e-stack mark. (At least,
+ * that's what the minimum value would be if we supported multi-block
+ * estacks, which we currently don't.) Currently, the largest such block is
+ * the one created for text processing, which is 8 (snumpush) slots.
+ */
+#define MIN_BLOCK_ESTACK 8
+/*
+ * If we get an e-stack overflow, we need to cut it back far enough to
+ * have some headroom for executing the error procedure.
+ */
+#define ES_HEADROOM 20
+
+/*
+ * Define the initial maximum size of the dictionary stack (MaxDictStack
+ * user parameter). Again, this is also currently the block size for
+ * extending the d-stack.
+ */
+#ifndef MAX_DSTACK
+# define MAX_DSTACK 20
+#endif
+/*
+ * The minimum block size for extending the dictionary stack is the number
+ * of permanent entries on the dictionary stack, currently 3.
+ */
+#define MIN_BLOCK_DSTACK 3
+
+/* See estack.h for a description of the execution stack. */
+
+/* The logic for managing icount and iref below assumes that */
+/* there are no control operators which pop and then push */
+/* information on the execution stack. */
+
+/* Stacks */
+extern_st(st_ref_stack);
+#define OS_GUARD_UNDER 10
+#define OS_GUARD_OVER 10
+#define OS_REFS_SIZE(body_size)\
+ (stack_block_refs + OS_GUARD_UNDER + (body_size) + OS_GUARD_OVER)
+
+#define ES_GUARD_UNDER 1
+#define ES_GUARD_OVER 10
+#define ES_REFS_SIZE(body_size)\
+ (stack_block_refs + ES_GUARD_UNDER + (body_size) + ES_GUARD_OVER)
+
+#define DS_REFS_SIZE(body_size)\
+ (stack_block_refs + (body_size))
+
+/* Extended types. The interpreter may replace the type of operators */
+/* in procedures with these, to speed up the interpretation loop. */
+/****** NOTE: If you add or change entries in this list, */
+/****** you must change the three dispatches in the interpreter loop. */
+/* The operator procedures are declared in opextern.h. */
+#define tx_op t_next_index
+typedef enum {
+ tx_op_add = tx_op,
+ tx_op_def,
+ tx_op_dup,
+ tx_op_exch,
+ tx_op_if,
+ tx_op_ifelse,
+ tx_op_index,
+ tx_op_pop,
+ tx_op_roll,
+ tx_op_sub,
+ tx_next_op
+} special_op_types;
+
+#define num_special_ops ((int)tx_next_op - tx_op)
+const int gs_interp_num_special_ops = num_special_ops; /* for iinit.c */
+const int tx_next_index = tx_next_op;
+
+/*
+ * NOTE: if the size of either table below ever exceeds 15 real entries, it
+ * will have to be split.
+ */
+/* Define the extended-type operators per the list above. */
+const op_def interp1_op_defs[] = {
+ /*
+ * The very first entry, which corresponds to operator index 0,
+ * must not contain an actual operator.
+ */
+ op_def_begin_dict("systemdict"),
+ {"2add", zadd},
+ {"2def", zdef},
+ {"1dup", zdup},
+ {"2exch", zexch},
+ {"2if", zif},
+ {"3ifelse", zifelse},
+ {"1index", zindex},
+ {"1pop", zpop},
+ {"2roll", zroll},
+ {"2sub", zsub},
+ op_def_end(0)
+};
+/* Define the internal interpreter operators. */
+const op_def interp2_op_defs[] = {
+ {"0.currentstackprotect", zcurrentstackprotect},
+ {"1.setstackprotect", zsetstackprotect},
+ {"2.errorexec", zerrorexec},
+ {"0.finderrorobject", zfinderrorobject},
+ {"0%interp_exit", interp_exit},
+ {"0%oparray_pop", oparray_pop},
+ {"0%errorexec_pop", errorexec_pop},
+ op_def_end(0)
+};
+
+#define make_null_proc(pref)\
+ make_empty_const_array(pref, a_executable + a_readonly)
+
+/* Initialize the interpreter. */
+int
+gs_interp_init(i_ctx_t **pi_ctx_p, const ref *psystem_dict,
+ gs_dual_memory_t *dmem)
+{
+ /* Create and initialize a context state. */
+ gs_context_state_t *pcst = 0;
+ int code = context_state_alloc(&pcst, psystem_dict, dmem);
+
+ if (code >= 0)
+ code = context_state_load(pcst);
+ if (code < 0)
+ lprintf1("Fatal error %d in gs_interp_init!", code);
+ *pi_ctx_p = pcst;
+ return code;
+}
+/*
+ * Create initial stacks for the interpreter.
+ * We export this for creating new contexts.
+ */
+int
+gs_interp_alloc_stacks(gs_ref_memory_t *mem, gs_context_state_t * pcst)
+{
+ int code;
+ gs_ref_memory_t *smem =
+ (gs_ref_memory_t *)gs_memory_stable((gs_memory_t *)mem);
+ ref stk;
+
+#define REFS_SIZE_OSTACK OS_REFS_SIZE(MAX_OSTACK)
+#define REFS_SIZE_ESTACK ES_REFS_SIZE(MAX_ESTACK)
+#define REFS_SIZE_DSTACK DS_REFS_SIZE(MAX_DSTACK)
+ code = gs_alloc_ref_array(smem, &stk, 0,
+ REFS_SIZE_OSTACK + REFS_SIZE_ESTACK +
+ REFS_SIZE_DSTACK, "gs_interp_alloc_stacks");
+ if (code < 0)
+ return code;
+
+ {
+ ref_stack_t *pos = &pcst->op_stack.stack;
+
+ r_set_size(&stk, REFS_SIZE_OSTACK);
+ code = ref_stack_init(pos, &stk, OS_GUARD_UNDER, OS_GUARD_OVER, NULL,
+ smem, NULL);
+ if (code < 0)
+ return code;
+ ref_stack_set_error_codes(pos, gs_error_stackunderflow, gs_error_stackoverflow);
+ ref_stack_set_max_count(pos, MAX_OSTACK);
+ stk.value.refs += REFS_SIZE_OSTACK;
+ }
+
+ {
+ ref_stack_t *pes = &pcst->exec_stack.stack;
+ ref euop;
+
+ r_set_size(&stk, REFS_SIZE_ESTACK);
+ make_oper(&euop, 0, estack_underflow);
+ code = ref_stack_init(pes, &stk, ES_GUARD_UNDER, ES_GUARD_OVER, &euop,
+ smem, NULL);
+ if (code < 0)
+ return code;
+ ref_stack_set_error_codes(pes, gs_error_ExecStackUnderflow,
+ gs_error_execstackoverflow);
+ /**************** E-STACK EXPANSION IS NYI. ****************/
+ ref_stack_allow_expansion(pes, false);
+ ref_stack_set_max_count(pes, MAX_ESTACK);
+ stk.value.refs += REFS_SIZE_ESTACK;
+ }
+
+ {
+ ref_stack_t *pds = &pcst->dict_stack.stack;
+
+ r_set_size(&stk, REFS_SIZE_DSTACK);
+ code = ref_stack_init(pds, &stk, 0, 0, NULL, smem, NULL);
+ if (code < 0)
+ return code;
+ ref_stack_set_error_codes(pds, gs_error_dictstackunderflow,
+ gs_error_dictstackoverflow);
+ ref_stack_set_max_count(pds, MAX_DSTACK);
+ }
+
+#undef REFS_SIZE_OSTACK
+#undef REFS_SIZE_ESTACK
+#undef REFS_SIZE_DSTACK
+ return 0;
+}
+/*
+ * Free the stacks when destroying a context. This is the inverse of
+ * create_stacks.
+ */
+void
+gs_interp_free_stacks(gs_ref_memory_t * smem, gs_context_state_t * pcst)
+{
+ /* Free the stacks in inverse order of allocation. */
+ ref_stack_release(&pcst->dict_stack.stack);
+ ref_stack_release(&pcst->exec_stack.stack);
+ ref_stack_release(&pcst->op_stack.stack);
+}
+void
+gs_interp_reset(i_ctx_t *i_ctx_p)
+{ /* Reset the stacks. */
+ ref_stack_clear(&o_stack);
+ ref_stack_clear(&e_stack);
+ esp++;
+ make_oper(esp, 0, interp_exit);
+ ref_stack_pop_to(&d_stack, min_dstack_size);
+ dict_set_top();
+}
+/* Report an e-stack block underflow. The bottom guard slots of */
+/* e-stack blocks contain a pointer to this procedure. */
+static int
+estack_underflow(i_ctx_t *i_ctx_p)
+{
+ return gs_error_ExecStackUnderflow;
+}
+
+/*
+ * Create an operator during initialization.
+ * If operator is hard-coded into the interpreter,
+ * assign it a special type and index.
+ */
+void
+gs_interp_make_oper(ref * opref, op_proc_t proc, int idx)
+{
+ int i;
+
+ for (i = num_special_ops; i > 0 && proc != interp1_op_defs[i].proc; --i)
+ DO_NOTHING;
+ if (i > 0)
+ make_tasv(opref, tx_op + (i - 1), a_executable, i, opproc, proc);
+ else
+ make_tasv(opref, t_operator, a_executable, idx, opproc, proc);
+}
+
+/*
+ * Call the garbage collector, updating the context pointer properly.
+ */
+int
+interp_reclaim(i_ctx_t **pi_ctx_p, int space)
+{
+ i_ctx_t *i_ctx_p = *pi_ctx_p;
+ gs_gc_root_t ctx_root;
+ int code;
+
+#ifdef DEBUG
+ if (gs_debug_c(gs_debug_flag_gc_disable))
+ return 0;
+#endif
+
+ gs_register_struct_root(imemory_system, &ctx_root,
+ (void **)pi_ctx_p, "interp_reclaim(pi_ctx_p)");
+ code = (*idmemory->reclaim)(idmemory, space);
+ i_ctx_p = *pi_ctx_p; /* may have moved */
+ gs_unregister_root(imemory_system, &ctx_root, "interp_reclaim(pi_ctx_p)");
+ return code;
+}
+
+/*
+ * Invoke the interpreter. If execution completes normally, return 0.
+ * If an error occurs, the action depends on user_errors as follows:
+ * user_errors < 0: always return an error code.
+ * user_errors >= 0: let the PostScript machinery handle all errors.
+ * (This will eventually result in a fatal error if no 'stopped'
+ * is active.)
+ * In case of a quit or a fatal error, also store the exit code.
+ * Set *perror_object to null or the error object.
+ */
+static int gs_call_interp(i_ctx_t **, ref *, int, int *, ref *);
+int
+gs_interpret(i_ctx_t **pi_ctx_p, ref * pref, int user_errors, int *pexit_code,
+ ref * perror_object)
+{
+ i_ctx_t *i_ctx_p = *pi_ctx_p;
+ gs_gc_root_t error_root;
+ int code;
+
+ gs_register_ref_root(imemory_system, &error_root,
+ (void **)&perror_object, "gs_interpret");
+ code = gs_call_interp(pi_ctx_p, pref, user_errors, pexit_code,
+ perror_object);
+ i_ctx_p = *pi_ctx_p;
+ gs_unregister_root(imemory_system, &error_root, "gs_interpret");
+ /* Avoid a dangling reference to a stack-allocated GC signal. */
+ set_gc_signal(i_ctx_p, NULL, 0);
+ return code;
+}
+static int
+gs_call_interp(i_ctx_t **pi_ctx_p, ref * pref, int user_errors,
+ int *pexit_code, ref * perror_object)
+{
+ ref *epref = pref;
+ ref doref;
+ ref *perrordict;
+ ref error_name;
+ int code, ccode;
+ ref saref;
+ int gc_signal = 0;
+ i_ctx_t *i_ctx_p = *pi_ctx_p;
+
+ *pexit_code = 0;
+ ialloc_reset_requested(idmemory);
+again:
+ /* Avoid a dangling error object that might get traced by a future GC. */
+ make_null(perror_object);
+ o_stack.requested = e_stack.requested = d_stack.requested = 0;
+ while (gc_signal) { /* Some routine below triggered a GC. */
+ gs_gc_root_t epref_root;
+
+ gc_signal = 0;
+ /* Make sure that doref will get relocated properly if */
+ /* a garbage collection happens with epref == &doref. */
+ gs_register_ref_root(imemory_system, &epref_root,
+ (void **)&epref, "gs_call_interp(epref)");
+ code = interp_reclaim(pi_ctx_p, -1);
+ i_ctx_p = *pi_ctx_p;
+ gs_unregister_root(imemory_system, &epref_root,
+ "gs_call_interp(epref)");
+ if (code < 0)
+ return code;
+ }
+ code = interp(pi_ctx_p, epref, perror_object);
+ i_ctx_p = *pi_ctx_p;
+ if (!r_has_type(&i_ctx_p->error_object, t__invalid)) {
+ *perror_object = i_ctx_p->error_object;
+ make_t(&i_ctx_p->error_object, t__invalid);
+ }
+ /* Prevent a dangling reference to the GC signal in ticks_left */
+ /* in the frame of interp, but be prepared to do a GC if */
+ /* an allocation in this routine asks for it. */
+ set_gc_signal(i_ctx_p, &gc_signal, 1);
+ if (esp < esbot) /* popped guard entry */
+ esp = esbot;
+ switch (code) {
+ case gs_error_Fatal:
+ *pexit_code = 255;
+ return code;
+ case gs_error_Quit:
+ *perror_object = osp[-1];
+ *pexit_code = code = osp->value.intval;
+ osp -= 2;
+ return
+ (code == 0 ? gs_error_Quit :
+ code < 0 && code > -100 ? code : gs_error_Fatal);
+ case gs_error_InterpreterExit:
+ return 0;
+ case gs_error_ExecStackUnderflow:
+/****** WRONG -- must keep mark blocks intact ******/
+ ref_stack_pop_block(&e_stack);
+ doref = *perror_object;
+ epref = &doref;
+ goto again;
+ case gs_error_VMreclaim:
+ /* Do the GC and continue. */
+ code = interp_reclaim(pi_ctx_p,
+ (osp->value.intval == 2 ?
+ avm_global : avm_local));
+ i_ctx_p = *pi_ctx_p;
+ /****** What if code < 0? ******/
+ make_oper(&doref, 0, zpop);
+ epref = &doref;
+ goto again;
+ case gs_error_NeedInput:
+ case gs_error_interrupt:
+ return code;
+ }
+ /* Adjust osp in case of operand stack underflow */
+ if (osp < osbot - 1)
+ osp = osbot - 1;
+ /* We have to handle stack over/underflow specially, because */
+ /* we might be able to recover by adding or removing a block. */
+ switch (code) {
+ case gs_error_dictstackoverflow:
+ /* We don't have to handle this specially: */
+ /* The only places that could generate it */
+ /* use check_dstack, which does a ref_stack_extend, */
+ /* so if` we get this error, it's a real one. */
+ if (osp >= ostop) {
+ if ((ccode = ref_stack_extend(&o_stack, 1)) < 0)
+ return ccode;
+ }
+ /* Skip system dictionaries for CET 20-02-02 */
+ ccode = copy_stack(i_ctx_p, &d_stack, min_dstack_size, &saref);
+ if (ccode < 0)
+ return ccode;
+ ref_stack_pop_to(&d_stack, min_dstack_size);
+ dict_set_top();
+ *++osp = saref;
+ break;
+ case gs_error_dictstackunderflow:
+ if (ref_stack_pop_block(&d_stack) >= 0) {
+ dict_set_top();
+ doref = *perror_object;
+ epref = &doref;
+ goto again;
+ }
+ break;
+ case gs_error_execstackoverflow:
+ /* We don't have to handle this specially: */
+ /* The only places that could generate it */
+ /* use check_estack, which does a ref_stack_extend, */
+ /* so if we get this error, it's a real one. */
+ if (osp >= ostop) {
+ if ((ccode = ref_stack_extend(&o_stack, 1)) < 0)
+ return ccode;
+ }
+ ccode = copy_stack(i_ctx_p, &e_stack, 0, &saref);
+ if (ccode < 0)
+ return ccode;
+ {
+ uint count = ref_stack_count(&e_stack);
+ uint limit = ref_stack_max_count(&e_stack) - ES_HEADROOM;
+
+ if (count > limit) {
+ /*
+ * If there is an e-stack mark within MIN_BLOCK_ESTACK of
+ * the new top, cut the stack back to remove the mark.
+ */
+ int skip = count - limit;
+ int i;
+
+ for (i = skip; i < skip + MIN_BLOCK_ESTACK; ++i) {
+ const ref *ep = ref_stack_index(&e_stack, i);
+
+ if (r_has_type_attrs(ep, t_null, a_executable)) {
+ skip = i + 1;
+ break;
+ }
+ }
+ pop_estack(i_ctx_p, skip);
+ }
+ }
+ *++osp = saref;
+ break;
+ case gs_error_stackoverflow:
+ if (ref_stack_extend(&o_stack, o_stack.requested) >= 0) { /* We can't just re-execute the object, because */
+ /* it might be a procedure being pushed as a */
+ /* literal. We check for this case specially. */
+ doref = *perror_object;
+ if (r_is_proc(&doref)) {
+ *++osp = doref;
+ make_null_proc(&doref);
+ }
+ epref = &doref;
+ goto again;
+ }
+ ccode = copy_stack(i_ctx_p, &o_stack, 0, &saref);
+ if (ccode < 0)
+ return ccode;
+ ref_stack_clear(&o_stack);
+ *++osp = saref;
+ break;
+ case gs_error_stackunderflow:
+ if (ref_stack_pop_block(&o_stack) >= 0) {
+ doref = *perror_object;
+ epref = &doref;
+ goto again;
+ }
+ break;
+ }
+ if (user_errors < 0)
+ return code;
+ if (gs_errorname(i_ctx_p, code, &error_name) < 0)
+ return code; /* out-of-range error code! */
+ /*
+ * For greater Adobe compatibility, only the standard PostScript errors
+ * are defined in errordict; the rest are in gserrordict.
+ */
+ if (dict_find_string(systemdict, "errordict", &perrordict) <= 0 ||
+ (dict_find(perrordict, &error_name, &epref) <= 0 &&
+ (dict_find_string(systemdict, "gserrordict", &perrordict) <= 0 ||
+ dict_find(perrordict, &error_name, &epref) <= 0))
+ )
+ return code; /* error name not in errordict??? */
+ doref = *epref;
+ epref = &doref;
+ /* Push the error object on the operand stack if appropriate. */
+ if (!GS_ERROR_IS_INTERRUPT(code)) {
+ /* Replace the error object if within an oparray or .errorexec. */
+ *++osp = *perror_object;
+ errorexec_find(i_ctx_p, osp);
+ }
+ goto again;
+}
+static int
+interp_exit(i_ctx_t *i_ctx_p)
+{
+ return gs_error_InterpreterExit;
+}
+
+/* Set the GC signal for all VMs. */
+static void
+set_gc_signal(i_ctx_t *i_ctx_p, int *psignal, int value)
+{
+ gs_memory_gc_status_t stat;
+ int i;
+
+ for (i = 0; i < countof(idmemory->spaces_indexed); i++) {
+ gs_ref_memory_t *mem = idmemory->spaces_indexed[i];
+ gs_ref_memory_t *mem_stable;
+
+ if (mem == 0)
+ continue;
+ for (;; mem = mem_stable) {
+ mem_stable = (gs_ref_memory_t *)
+ gs_memory_stable((gs_memory_t *)mem);
+ gs_memory_gc_status(mem, &stat);
+ stat.psignal = psignal;
+ stat.signal_value = value;
+ gs_memory_set_gc_status(mem, &stat);
+ if (mem_stable == mem)
+ break;
+ }
+ }
+}
+
+/* Copy top elements of an overflowed stack into a (local) array. */
+/* Adobe copies only 500 top elements, we copy up to 65535 top elements */
+/* for better debugging, PLRM compliance, and backward compatibility. */
+static int
+copy_stack(i_ctx_t *i_ctx_p, const ref_stack_t * pstack, int skip, ref * arr)
+{
+ uint size = ref_stack_count(pstack) - skip;
+ uint save_space = ialloc_space(idmemory);
+ int code;
+
+ if (size > 65535)
+ size = 65535;
+ ialloc_set_space(idmemory, avm_local);
+ code = ialloc_ref_array(arr, a_all, size, "copy_stack");
+ if (code >= 0)
+ code = ref_stack_store(pstack, arr, size, 0, 1, true, idmemory,
+ "copy_stack");
+ ialloc_set_space(idmemory, save_space);
+ return code;
+}
+
+/* Get the name corresponding to an error number. */
+int
+gs_errorname(i_ctx_t *i_ctx_p, int code, ref * perror_name)
+{
+ ref *perrordict, *pErrorNames;
+
+ if (dict_find_string(systemdict, "errordict", &perrordict) <= 0 ||
+ dict_find_string(systemdict, "ErrorNames", &pErrorNames) <= 0
+ )
+ return_error(gs_error_undefined); /* errordict or ErrorNames not found?! */
+ return array_get(imemory, pErrorNames, (long)(-code - 1), perror_name);
+}
+
+/* Store an error string in $error.errorinfo. */
+/* This routine is here because of the proximity to the error handler. */
+int
+gs_errorinfo_put_string(i_ctx_t *i_ctx_p, const char *str)
+{
+ ref rstr;
+ ref *pderror;
+ int code = string_to_ref(str, &rstr, iimemory, "gs_errorinfo_put_string");
+
+ if (code < 0)
+ return code;
+ if (dict_find_string(systemdict, "$error", &pderror) <= 0 ||
+ !r_has_type(pderror, t_dictionary) ||
+ idict_put_string(pderror, "errorinfo", &rstr) < 0
+ )
+ return_error(gs_error_Fatal);
+ return 0;
+}
+
+/* Main interpreter. */
+/* If execution terminates normally, return gs_error_InterpreterExit. */
+/* If an error occurs, leave the current object in *perror_object */
+/* and return a (negative) error code. */
+static int
+interp(i_ctx_t **pi_ctx_p /* context for execution, updated if resched */,
+ const ref * pref /* object to interpret */,
+ ref * perror_object)
+{
+ i_ctx_t *i_ctx_p = *pi_ctx_p;
+ /*
+ * Note that iref may actually be either a ref * or a ref_packed *.
+ * Certain DEC compilers assume that a ref * is ref-aligned even if it
+ * is cast to a short *, and generate code on this assumption, leading
+ * to "unaligned access" errors. For this reason, we declare
+ * iref_packed, and use a macro to cast it to the more aligned type
+ * where necessary (which is almost everywhere it is used). This may
+ * lead to compiler warnings about "cast increases alignment
+ * requirements", but this is less harmful than expensive traps at run
+ * time.
+ */
+ register const ref_packed *iref_packed = (const ref_packed *)pref;
+ /*
+ * To make matters worse, some versions of gcc/egcs have a bug that
+ * leads them to assume that if iref_packed is EVER cast to a ref *,
+ * it is ALWAYS ref-aligned. We detect this in stdpre.h and provide
+ * the following workaround:
+ */
+#ifdef ALIGNMENT_ALIASING_BUG
+ const ref *iref_temp;
+# define IREF (iref_temp = (const ref *)iref_packed, iref_temp)
+#else
+# define IREF ((const ref *)iref_packed)
+#endif
+#define SET_IREF(rp) (iref_packed = (const ref_packed *)(rp))
+ register int icount = 0; /* # of consecutive tokens at iref */
+ register os_ptr iosp = osp; /* private copy of osp */
+ register es_ptr iesp = esp; /* private copy of esp */
+ int code;
+ ref token; /* token read from file or string, */
+ /* must be declared in this scope */
+ register const ref *pvalue = 0;
+ uint opindex; /* needed for oparrays */
+ os_ptr whichp;
+
+ /*
+ * We have to make the error information into a struct;
+ * otherwise, the Watcom compiler will assign it to registers
+ * strictly on the basis of textual frequency.
+ * We also have to use ref_assign_inline everywhere, and
+ * avoid direct assignments of refs, so that esi and edi
+ * will remain available on Intel processors.
+ */
+ struct interp_error_s {
+ int code;
+ int line;
+ const ref *obj;
+ ref full;
+ } ierror;
+
+ /*
+ * Get a pointer to the name table so that we can use the
+ * inline version of name_index_ref.
+ */
+ const name_table *const int_nt = imemory->gs_lib_ctx->gs_name_table;
+
+#define set_error(ecode)\
+ { ierror.code = ecode; ierror.line = __LINE__; }
+#define return_with_error(ecode, objp)\
+ { set_error(ecode); ierror.obj = objp; goto rwe; }
+#define return_with_error_iref(ecode)\
+ { set_error(ecode); goto rwei; }
+#define return_with_code_iref()\
+ { ierror.line = __LINE__; goto rweci; }
+#define return_with_stackoverflow(objp)\
+ { o_stack.requested = 1; return_with_error(gs_error_stackoverflow, objp); }
+#define return_with_stackoverflow_iref()\
+ { o_stack.requested = 1; return_with_error_iref(gs_error_stackoverflow); }
+/*
+ * If control reaches the special operators (x_add, etc.) as a result of
+ * interpreting an executable name, iref points to the name, not the
+ * operator, so the name rather than the operator becomes the error object,
+ * which is wrong. We detect and handle this case explicitly when an error
+ * occurs, so as not to slow down the non-error case.
+ */
+#define return_with_error_tx_op(err_code)\
+ { if (r_has_type(IREF, t_name)) {\
+ return_with_error(err_code, pvalue);\
+ } else {\
+ return_with_error_iref(err_code);\
+ }\
+ }
+
+ int ticks_left = i_ctx_p->time_slice_ticks;
+
+#if defined(DEBUG_TRACE_PS_OPERATORS) || defined(DEBUG)
+ int (*call_operator_fn)(op_proc_t, i_ctx_t *) = do_call_operator;
+
+ if (gs_debug_c('!'))
+ call_operator_fn = do_call_operator_verbose;
+#endif
+
+ /*
+ * If we exceed the VMThreshold, set ticks_left to -100
+ * to alert the interpreter that we need to garbage collect.
+ */
+ set_gc_signal(i_ctx_p, &ticks_left, -100);
+
+ esfile_clear_cache();
+ /*
+ * From here on, if icount > 0, iref and icount correspond
+ * to the top entry on the execution stack: icount is the count
+ * of sequential entries remaining AFTER the current one.
+ */
+#define IREF_NEXT(ip)\
+ ((const ref_packed *)((const ref *)(ip) + 1))
+#define IREF_NEXT_EITHER(ip)\
+ ( r_is_packed(ip) ? (ip) + 1 : IREF_NEXT(ip) )
+#define store_state(ep)\
+ ( icount > 0 ? (ep->value.const_refs = IREF + 1, r_set_size(ep, icount)) : 0 )
+#define store_state_short(ep)\
+ ( icount > 0 ? (ep->value.packed = iref_packed + 1, r_set_size(ep, icount)) : 0 )
+#define store_state_either(ep)\
+ ( icount > 0 ? (ep->value.packed = IREF_NEXT_EITHER(iref_packed), r_set_size(ep, icount)) : 0 )
+#define next()\
+ if ( --icount > 0 ) { iref_packed = IREF_NEXT(iref_packed); goto top; } else goto out
+#define next_short()\
+ if ( --icount <= 0 ) { if ( icount < 0 ) goto up; iesp--; }\
+ ++iref_packed; goto top
+#define next_either()\
+ if ( --icount <= 0 ) { if ( icount < 0 ) goto up; iesp--; }\
+ iref_packed = IREF_NEXT_EITHER(iref_packed); goto top
+
+#if !PACKED_SPECIAL_OPS
+# undef next_either
+# define next_either() next()
+# undef store_state_either
+# define store_state_either(ep) store_state(ep)
+#endif
+
+ /* We want to recognize executable arrays here, */
+ /* so we push the argument on the estack and enter */
+ /* the loop at the bottom. */
+ if (iesp >= estop)
+ return_with_error(gs_error_execstackoverflow, pref);
+ ++iesp;
+ ref_assign_inline(iesp, pref);
+ goto bot;
+ top:
+ /*
+ * This is the top of the interpreter loop.
+ * iref points to the ref being interpreted.
+ * Note that this might be an element of a packed array,
+ * not a real ref: we carefully arranged the first 16 bits of
+ * a ref and of a packed array element so they could be distinguished
+ * from each other. (See ghost.h and packed.h for more detail.)
+ */
+ INCR(top);
+#ifdef DEBUG
+ /* Do a little validation on the top o-stack entry. */
+ if (iosp >= osbot &&
+ (r_type(iosp) == t__invalid || r_type(iosp) >= tx_next_op)
+ ) {
+ mlprintf(imemory, "Invalid value on o-stack!\n");
+ return_with_error_iref(gs_error_Fatal);
+ }
+ if (gs_debug['I'] ||
+ (gs_debug['i'] &&
+ (r_is_packed(iref_packed) ?
+ r_packed_is_name(iref_packed) :
+ r_has_type(IREF, t_name)))
+ ) {
+ os_ptr save_osp = osp; /* avoid side-effects */
+ es_ptr save_esp = esp;
+
+ osp = iosp;
+ esp = iesp;
+ dmlprintf5(imemory, "d%u,e%u<%u>0x%lx(%d): ",
+ ref_stack_count(&d_stack), ref_stack_count(&e_stack),
+ ref_stack_count(&o_stack), (ulong)IREF, icount);
+ debug_print_ref(imemory, IREF);
+ if (iosp >= osbot) {
+ dmputs(imemory, " // ");
+ debug_print_ref(imemory, iosp);
+ }
+ dmputc(imemory, '\n');
+ osp = save_osp;
+ esp = save_esp;
+ dmflush(imemory);
+ }
+#endif
+/* Objects that have attributes (arrays, dictionaries, files, and strings) */
+/* use lit and exec; other objects use plain and plain_exec. */
+#define lit(t) type_xe_value(t, a_execute)
+#define exec(t) type_xe_value(t, a_execute + a_executable)
+#define nox(t) type_xe_value(t, 0)
+#define nox_exec(t) type_xe_value(t, a_executable)
+#define plain(t) type_xe_value(t, 0)
+#define plain_exec(t) type_xe_value(t, a_executable)
+ /*
+ * We have to populate enough cases of the switch statement to force
+ * some compilers to use a dispatch rather than a testing loop.
+ * What a nuisance!
+ */
+ switch (r_type_xe(iref_packed)) {
+ /* Access errors. */
+#define cases_invalid()\
+ case plain(t__invalid): case plain_exec(t__invalid)
+ cases_invalid():
+ return_with_error_iref(gs_error_Fatal);
+#define cases_nox()\
+ case nox_exec(t_array): case nox_exec(t_dictionary):\
+ case nox_exec(t_file): case nox_exec(t_string):\
+ case nox_exec(t_mixedarray): case nox_exec(t_shortarray)
+ cases_nox():
+ return_with_error_iref(gs_error_invalidaccess);
+ /*
+ * Literal objects. We have to enumerate all the types.
+ * In fact, we have to include some extra plain_exec entries
+ * just to populate the switch. We break them up into groups
+ * to avoid overflowing some preprocessors.
+ */
+#define cases_lit_1()\
+ case lit(t_array): case nox(t_array):\
+ case plain(t_boolean): case plain_exec(t_boolean):\
+ case lit(t_dictionary): case nox(t_dictionary)
+#define cases_lit_2()\
+ case lit(t_file): case nox(t_file):\
+ case plain(t_fontID): case plain_exec(t_fontID):\
+ case plain(t_integer): case plain_exec(t_integer):\
+ case plain(t_mark): case plain_exec(t_mark)
+#define cases_lit_3()\
+ case plain(t_name):\
+ case plain(t_null):\
+ case plain(t_oparray):\
+ case plain(t_operator)
+#define cases_lit_4()\
+ case plain(t_real): case plain_exec(t_real):\
+ case plain(t_save): case plain_exec(t_save):\
+ case lit(t_string): case nox(t_string)
+#define cases_lit_5()\
+ case lit(t_mixedarray): case nox(t_mixedarray):\
+ case lit(t_shortarray): case nox(t_shortarray):\
+ case plain(t_device): case plain_exec(t_device):\
+ case plain(t_struct): case plain_exec(t_struct):\
+ case plain(t_astruct): case plain_exec(t_astruct)
+ /* Executable arrays are treated as literals in direct execution. */
+#define cases_lit_array()\
+ case exec(t_array): case exec(t_mixedarray): case exec(t_shortarray)
+ cases_lit_1():
+ cases_lit_2():
+ cases_lit_3():
+ cases_lit_4():
+ cases_lit_5():
+ INCR(lit);
+ break;
+ cases_lit_array():
+ INCR(lit_array);
+ break;
+ /* Special operators. */
+ case plain_exec(tx_op_add):
+x_add: INCR(x_add);
+ osp = iosp; /* sync o_stack */
+ if ((code = zop_add(i_ctx_p)) < 0)
+ return_with_error_tx_op(code);
+ iosp--;
+ next_either();
+ case plain_exec(tx_op_def):
+x_def: INCR(x_def);
+ osp = iosp; /* sync o_stack */
+ if ((code = zop_def(i_ctx_p)) < 0)
+ return_with_error_tx_op(code);
+ iosp -= 2;
+ next_either();
+ case plain_exec(tx_op_dup):
+x_dup: INCR(x_dup);
+ if (iosp < osbot)
+ return_with_error_tx_op(gs_error_stackunderflow);
+ if (iosp >= ostop) {
+ o_stack.requested = 1;
+ return_with_error_tx_op(gs_error_stackoverflow);
+ }
+ iosp++;
+ ref_assign_inline(iosp, iosp - 1);
+ next_either();
+ case plain_exec(tx_op_exch):
+x_exch: INCR(x_exch);
+ if (iosp <= osbot)
+ return_with_error_tx_op(gs_error_stackunderflow);
+ ref_assign_inline(&token, iosp);
+ ref_assign_inline(iosp, iosp - 1);
+ ref_assign_inline(iosp - 1, &token);
+ next_either();
+ case plain_exec(tx_op_if):
+x_if: INCR(x_if);
+ if (!r_is_proc(iosp))
+ return_with_error_tx_op(check_proc_failed(iosp));
+ if (!r_has_type(iosp - 1, t_boolean))
+ return_with_error_tx_op((iosp <= osbot ?
+ gs_error_stackunderflow : gs_error_typecheck));
+ if (!iosp[-1].value.boolval) {
+ iosp -= 2;
+ next_either();
+ }
+ if (iesp >= estop)
+ return_with_error_tx_op(gs_error_execstackoverflow);
+ store_state_either(iesp);
+ whichp = iosp;
+ iosp -= 2;
+ goto ifup;
+ case plain_exec(tx_op_ifelse):
+x_ifelse: INCR(x_ifelse);
+ if (!r_is_proc(iosp))
+ return_with_error_tx_op(check_proc_failed(iosp));
+ if (!r_is_proc(iosp - 1))
+ return_with_error_tx_op(check_proc_failed(iosp - 1));
+ if (!r_has_type(iosp - 2, t_boolean))
+ return_with_error_tx_op((iosp < osbot + 2 ?
+ gs_error_stackunderflow : gs_error_typecheck));
+ if (iesp >= estop)
+ return_with_error_tx_op(gs_error_execstackoverflow);
+ store_state_either(iesp);
+ whichp = (iosp[-2].value.boolval ? iosp - 1 : iosp);
+ iosp -= 3;
+ /* Open code "up" for the array case(s) */
+ ifup:if ((icount = r_size(whichp) - 1) <= 0) {
+ if (icount < 0)
+ goto up; /* 0-element proc */
+ SET_IREF(whichp->value.refs); /* 1-element proc */
+ if (--ticks_left > 0)
+ goto top;
+ }
+ ++iesp;
+ /* Do a ref_assign, but also set iref. */
+ iesp->tas = whichp->tas;
+ SET_IREF(iesp->value.refs = whichp->value.refs);
+ if (--ticks_left > 0)
+ goto top;
+ goto slice;
+ case plain_exec(tx_op_index):
+x_index: INCR(x_index);
+ osp = iosp; /* zindex references o_stack */
+ if ((code = zindex(i_ctx_p)) < 0)
+ return_with_error_tx_op(code);
+ next_either();
+ case plain_exec(tx_op_pop):
+x_pop: INCR(x_pop);
+ if (iosp < osbot)
+ return_with_error_tx_op(gs_error_stackunderflow);
+ iosp--;
+ next_either();
+ case plain_exec(tx_op_roll):
+x_roll: INCR(x_roll);
+ osp = iosp; /* zroll references o_stack */
+ if ((code = zroll(i_ctx_p)) < 0)
+ return_with_error_tx_op(code);
+ iosp -= 2;
+ next_either();
+ case plain_exec(tx_op_sub):
+x_sub: INCR(x_sub);
+ osp = iosp; /* sync o_stack */
+ if ((code = zop_sub(i_ctx_p)) < 0)
+ return_with_error_tx_op(code);
+ iosp--;
+ next_either();
+ /* Executable types. */
+ case plain_exec(t_null):
+ goto bot;
+ case plain_exec(t_oparray):
+ /* Replace with the definition and go again. */
+ INCR(exec_array);
+ opindex = op_index(IREF);
+ pvalue = IREF->value.const_refs;
+ opst: /* Prepare to call a t_oparray procedure in *pvalue. */
+ store_state(iesp);
+ oppr: /* Record the stack depths in case of failure. */
+ if (iesp >= estop - 4)
+ return_with_error_iref(gs_error_execstackoverflow);
+ iesp += 5;
+ osp = iosp; /* ref_stack_count_inline needs this */
+ make_mark_estack(iesp - 4, es_other, oparray_cleanup);
+ make_int(iesp - 3, opindex); /* for .errorexec effect */
+ make_int(iesp - 2, ref_stack_count_inline(&o_stack));
+ make_int(iesp - 1, ref_stack_count_inline(&d_stack));
+ make_op_estack(iesp, oparray_pop);
+ goto pr;
+ prst: /* Prepare to call the procedure (array) in *pvalue. */
+ store_state(iesp);
+ pr: /* Call the array in *pvalue. State has been stored. */
+ if ((icount = r_size(pvalue) - 1) <= 0) {
+ if (icount < 0)
+ goto up; /* 0-element proc */
+ SET_IREF(pvalue->value.refs); /* 1-element proc */
+ if (--ticks_left > 0)
+ goto top;
+ }
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ ++iesp;
+ /* Do a ref_assign, but also set iref. */
+ iesp->tas = pvalue->tas;
+ SET_IREF(iesp->value.refs = pvalue->value.refs);
+ if (--ticks_left > 0)
+ goto top;
+ goto slice;
+ case plain_exec(t_operator):
+ INCR(exec_operator);
+ if (--ticks_left <= 0) { /* The following doesn't work, */
+ /* and I can't figure out why. */
+/****** goto sst; ******/
+ }
+ esp = iesp; /* save for operator */
+ osp = iosp; /* ditto */
+ /* Operator routines take osp as an argument. */
+ /* This is just a convenience, since they adjust */
+ /* osp themselves to reflect the results. */
+ /* Operators that (net) push information on the */
+ /* operand stack must check for overflow: */
+ /* this normally happens automatically through */
+ /* the push macro (in oper.h). */
+ /* Operators that do not typecheck their operands, */
+ /* or take a variable number of arguments, */
+ /* must check explicitly for stack underflow. */
+ /* (See oper.h for more detail.) */
+ /* Note that each case must set iosp = osp: */
+ /* this is so we can switch on code without having to */
+ /* store it and reload it (for dumb compilers). */
+ switch (code = call_operator(real_opproc(IREF), i_ctx_p)) {
+ case 0: /* normal case */
+ case 1: /* alternative success case */
+ iosp = osp;
+ next();
+ case o_push_estack: /* store the state and go to up */
+ store_state(iesp);
+ opush:iosp = osp;
+ iesp = esp;
+ if (--ticks_left > 0)
+ goto up;
+ goto slice;
+ case o_pop_estack: /* just go to up */
+ opop:iosp = osp;
+ if (esp == iesp)
+ goto bot;
+ iesp = esp;
+ goto up;
+ case o_reschedule:
+ store_state(iesp);
+ goto res;
+ case gs_error_Remap_Color:
+oe_remap: store_state(iesp);
+remap: if (iesp + 2 >= estop) {
+ esp = iesp;
+ code = ref_stack_extend(&e_stack, 2);
+ if (code < 0)
+ return_with_error_iref(code);
+ iesp = esp;
+ }
+ packed_get(imemory, iref_packed, iesp + 1);
+ make_oper(iesp + 2, 0,
+ r_ptr(&istate->remap_color_info,
+ int_remap_color_info_t)->proc);
+ iesp += 2;
+ goto up;
+ }
+ iosp = osp;
+ iesp = esp;
+ return_with_code_iref();
+ case plain_exec(t_name):
+ INCR(exec_name);
+ pvalue = IREF->value.pname->pvalue;
+ if (!pv_valid(pvalue)) {
+ uint nidx = names_index(int_nt, IREF);
+ uint htemp;
+
+ INCR(find_name);
+ if ((pvalue = dict_find_name_by_index_inline(nidx, htemp)) == 0)
+ return_with_error_iref(gs_error_undefined);
+ }
+ /* Dispatch on the type of the value. */
+ /* Again, we have to over-populate the switch. */
+ switch (r_type_xe(pvalue)) {
+ cases_invalid():
+ return_with_error_iref(gs_error_Fatal);
+ cases_nox(): /* access errors */
+ return_with_error_iref(gs_error_invalidaccess);
+ cases_lit_1():
+ cases_lit_2():
+ cases_lit_3():
+ cases_lit_4():
+ cases_lit_5():
+ INCR(name_lit);
+ /* Just push the value */
+ if (iosp >= ostop)
+ return_with_stackoverflow(pvalue);
+ ++iosp;
+ ref_assign_inline(iosp, pvalue);
+ next();
+ case exec(t_array):
+ case exec(t_mixedarray):
+ case exec(t_shortarray):
+ INCR(name_proc);
+ /* This is an executable procedure, execute it. */
+ goto prst;
+ case plain_exec(tx_op_add):
+ goto x_add;
+ case plain_exec(tx_op_def):
+ goto x_def;
+ case plain_exec(tx_op_dup):
+ goto x_dup;
+ case plain_exec(tx_op_exch):
+ goto x_exch;
+ case plain_exec(tx_op_if):
+ goto x_if;
+ case plain_exec(tx_op_ifelse):
+ goto x_ifelse;
+ case plain_exec(tx_op_index):
+ goto x_index;
+ case plain_exec(tx_op_pop):
+ goto x_pop;
+ case plain_exec(tx_op_roll):
+ goto x_roll;
+ case plain_exec(tx_op_sub):
+ goto x_sub;
+ case plain_exec(t_null):
+ goto bot;
+ case plain_exec(t_oparray):
+ INCR(name_oparray);
+ opindex = op_index(pvalue);
+ pvalue = (const ref *)pvalue->value.const_refs;
+ goto opst;
+ case plain_exec(t_operator):
+ INCR(name_operator);
+ { /* Shortcut for operators. */
+ /* See above for the logic. */
+ if (--ticks_left <= 0) { /* The following doesn't work, */
+ /* and I can't figure out why. */
+/****** goto sst; ******/
+ }
+ esp = iesp;
+ osp = iosp;
+ switch (code = call_operator(real_opproc(pvalue),
+ i_ctx_p)
+ ) {
+ case 0: /* normal case */
+ case 1: /* alternative success case */
+ iosp = osp;
+ next();
+ case o_push_estack:
+ store_state(iesp);
+ goto opush;
+ case o_pop_estack:
+ goto opop;
+ case o_reschedule:
+ store_state(iesp);
+ goto res;
+ case gs_error_Remap_Color:
+ goto oe_remap;
+ }
+ iosp = osp;
+ iesp = esp;
+ return_with_error(code, pvalue);
+ }
+ case plain_exec(t_name):
+ case exec(t_file):
+ case exec(t_string):
+ default:
+ /* Not a procedure, reinterpret it. */
+ store_state(iesp);
+ icount = 0;
+ SET_IREF(pvalue);
+ goto top;
+ }
+ case exec(t_file):
+ { /* Executable file. Read the next token and interpret it. */
+ stream *s;
+ scanner_state sstate;
+
+ check_read_known_file(i_ctx_p, s, IREF, return_with_error_iref);
+ rt:
+ if (iosp >= ostop) /* check early */
+ return_with_stackoverflow_iref();
+ osp = iosp; /* gs_scan_token uses ostack */
+ gs_scanner_init_options(&sstate, IREF, i_ctx_p->scanner_options);
+ again:
+ code = gs_scan_token(i_ctx_p, &token, &sstate);
+ iosp = osp; /* ditto */
+ switch (code) {
+ case 0: /* read a token */
+ /* It's worth checking for literals, which make up */
+ /* the majority of input tokens, before storing the */
+ /* state on the e-stack. Note that because of //, */
+ /* the token may have *any* type and attributes. */
+ /* Note also that executable arrays aren't executed */
+ /* at the top level -- they're treated as literals. */
+ if (!r_has_attr(&token, a_executable) ||
+ r_is_array(&token)
+ ) { /* If gs_scan_token used the o-stack, */
+ /* we know we can do a push now; if not, */
+ /* the pre-check is still valid. */
+ iosp++;
+ ref_assign_inline(iosp, &token);
+ goto rt;
+ }
+ store_state(iesp);
+ /* Push the file on the e-stack */
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ esfile_set_cache(++iesp);
+ ref_assign_inline(iesp, IREF);
+ SET_IREF(&token);
+ icount = 0;
+ goto top;
+ case gs_error_undefined: /* //name undefined */
+ gs_scanner_error_object(i_ctx_p, &sstate, &token);
+ return_with_error(code, &token);
+ case scan_EOF: /* end of file */
+ esfile_clear_cache();
+ goto bot;
+ case scan_BOS:
+ /* Binary object sequences */
+ /* ARE executed at the top level. */
+ store_state(iesp);
+ /* Push the file on the e-stack */
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ esfile_set_cache(++iesp);
+ ref_assign_inline(iesp, IREF);
+ pvalue = &token;
+ goto pr;
+ case scan_Refill:
+ store_state(iesp);
+ /* iref may point into the exec stack; */
+ /* save its referent now. */
+ ref_assign_inline(&token, IREF);
+ /* Push the file on the e-stack */
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ ++iesp;
+ ref_assign_inline(iesp, &token);
+ esp = iesp;
+ osp = iosp;
+ code = gs_scan_handle_refill(i_ctx_p, &sstate, true,
+ ztokenexec_continue);
+ scan_cont:
+ iosp = osp;
+ iesp = esp;
+ switch (code) {
+ case 0:
+ iesp--; /* don't push the file */
+ goto again; /* stacks are unchanged */
+ case o_push_estack:
+ esfile_clear_cache();
+ if (--ticks_left > 0)
+ goto up;
+ goto slice;
+ }
+ /* must be an error */
+ iesp--; /* don't push the file */
+ return_with_code_iref();
+ case scan_Comment:
+ case scan_DSC_Comment: {
+ /* See scan_Refill above for comments. */
+ ref file_token;
+
+ store_state(iesp);
+ ref_assign_inline(&file_token, IREF);
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ ++iesp;
+ ref_assign_inline(iesp, &file_token);
+ esp = iesp;
+ osp = iosp;
+ code = ztoken_handle_comment(i_ctx_p,
+ &sstate, &token,
+ code, true, true,
+ ztokenexec_continue);
+ }
+ goto scan_cont;
+ default: /* error */
+ ref_assign_inline(&token, IREF);
+ gs_scanner_error_object(i_ctx_p, &sstate, &token);
+ return_with_error(code, &token);
+ }
+ }
+ case exec(t_string):
+ { /* Executable string. Read a token and interpret it. */
+ stream ss;
+ scanner_state sstate;
+
+ s_init(&ss, NULL);
+ sread_string(&ss, IREF->value.bytes, r_size(IREF));
+ gs_scanner_init_stream_options(&sstate, &ss, SCAN_FROM_STRING);
+ osp = iosp; /* gs_scan_token uses ostack */
+ code = gs_scan_token(i_ctx_p, &token, &sstate);
+ iosp = osp; /* ditto */
+ switch (code) {
+ case 0: /* read a token */
+ case scan_BOS: /* binary object sequence */
+ store_state(iesp);
+ /* If the updated string isn't empty, push it back */
+ /* on the e-stack. */
+ {
+ uint size = sbufavailable(&ss);
+
+ if (size) {
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ ++iesp;
+ iesp->tas.type_attrs = IREF->tas.type_attrs;
+ iesp->value.const_bytes = sbufptr(&ss);
+ r_set_size(iesp, size);
+ }
+ }
+ if (code == 0) {
+ SET_IREF(&token);
+ icount = 0;
+ goto top;
+ }
+ /* Handle BOS specially */
+ pvalue = &token;
+ goto pr;
+ case scan_EOF: /* end of string */
+ goto bot;
+ case scan_Refill: /* error */
+ code = gs_note_error(gs_error_syntaxerror);
+ default: /* error */
+ ref_assign_inline(&token, IREF);
+ gs_scanner_error_object(i_ctx_p, &sstate, &token);
+ return_with_error(code, &token);
+ }
+ }
+ /* Handle packed arrays here by re-dispatching. */
+ /* This also picks up some anomalous cases of non-packed arrays. */
+ default:
+ {
+ uint index;
+
+ switch (*iref_packed >> r_packed_type_shift) {
+ case pt_full_ref:
+ case pt_full_ref + 1:
+ INCR(p_full);
+ if (iosp >= ostop)
+ return_with_stackoverflow_iref();
+ /* We know this can't be an executable object */
+ /* requiring special handling, so we just push it. */
+ ++iosp;
+ /* We know that refs are properly aligned: */
+ /* see packed.h for details. */
+ ref_assign_inline(iosp, IREF);
+ next();
+ case pt_executable_operator:
+ index = *iref_packed & packed_value_mask;
+ if (--ticks_left <= 0) { /* The following doesn't work, */
+ /* and I can't figure out why. */
+/****** goto sst_short; ******/
+ }
+ if (!op_index_is_operator(index)) {
+ INCR(p_exec_oparray);
+ store_state_short(iesp);
+ opindex = index;
+ /* Call the operator procedure. */
+ index -= op_def_count;
+ pvalue = (const ref *)
+ (index < r_size(&i_ctx_p->op_array_table_global.table) ?
+ i_ctx_p->op_array_table_global.table.value.const_refs +
+ index :
+ i_ctx_p->op_array_table_local.table.value.const_refs +
+ (index - r_size(&i_ctx_p->op_array_table_global.table)));
+ goto oppr;
+ }
+ INCR(p_exec_operator);
+ /* See the main plain_exec(t_operator) case */
+ /* for details of what happens here. */
+#if PACKED_SPECIAL_OPS
+ /*
+ * We arranged in iinit.c that the special ops
+ * have operator indices starting at 1.
+ *
+ * The (int) cast in the next line is required
+ * because some compilers don't allow arithmetic
+ * involving two different enumerated types.
+ */
+# define case_xop(xop) case xop - (int)tx_op + 1
+ switch (index) {
+ case_xop(tx_op_add):goto x_add;
+ case_xop(tx_op_def):goto x_def;
+ case_xop(tx_op_dup):goto x_dup;
+ case_xop(tx_op_exch):goto x_exch;
+ case_xop(tx_op_if):goto x_if;
+ case_xop(tx_op_ifelse):goto x_ifelse;
+ case_xop(tx_op_index):goto x_index;
+ case_xop(tx_op_pop):goto x_pop;
+ case_xop(tx_op_roll):goto x_roll;
+ case_xop(tx_op_sub):goto x_sub;
+ case 0: /* for dumb compilers */
+ default:
+ ;
+ }
+# undef case_xop
+#endif
+ INCR(p_exec_non_x_operator);
+ esp = iesp;
+ osp = iosp;
+ switch (code = call_operator(op_index_proc(index), i_ctx_p)) {
+ case 0:
+ case 1:
+ iosp = osp;
+ next_short();
+ case o_push_estack:
+ store_state_short(iesp);
+ goto opush;
+ case o_pop_estack:
+ iosp = osp;
+ if (esp == iesp) {
+ next_short();
+ }
+ iesp = esp;
+ goto up;
+ case o_reschedule:
+ store_state_short(iesp);
+ goto res;
+ case gs_error_Remap_Color:
+ store_state_short(iesp);
+ goto remap;
+ }
+ iosp = osp;
+ iesp = esp;
+ return_with_code_iref();
+ case pt_integer:
+ INCR(p_integer);
+ if (iosp >= ostop)
+ return_with_stackoverflow_iref();
+ ++iosp;
+ make_int(iosp,
+ ((int)*iref_packed & packed_int_mask) +
+ packed_min_intval);
+ next_short();
+ case pt_literal_name:
+ INCR(p_lit_name);
+ {
+ uint nidx = *iref_packed & packed_value_mask;
+
+ if (iosp >= ostop)
+ return_with_stackoverflow_iref();
+ ++iosp;
+ name_index_ref_inline(int_nt, nidx, iosp);
+ next_short();
+ }
+ case pt_executable_name:
+ INCR(p_exec_name);
+ {
+ uint nidx = *iref_packed & packed_value_mask;
+
+ pvalue = name_index_ptr_inline(int_nt, nidx)->pvalue;
+ if (!pv_valid(pvalue)) {
+ uint htemp;
+
+ INCR(p_find_name);
+ if ((pvalue = dict_find_name_by_index_inline(nidx, htemp)) == 0) {
+ names_index_ref(int_nt, nidx, &token);
+ return_with_error(gs_error_undefined, &token);
+ }
+ }
+ if (r_has_masked_attrs(pvalue, a_execute, a_execute + a_executable)) { /* Literal, push it. */
+ INCR(p_name_lit);
+ if (iosp >= ostop)
+ return_with_stackoverflow_iref();
+ ++iosp;
+ ref_assign_inline(iosp, pvalue);
+ next_short();
+ }
+ if (r_is_proc(pvalue)) { /* This is an executable procedure, */
+ /* execute it. */
+ INCR(p_name_proc);
+ store_state_short(iesp);
+ goto pr;
+ }
+ /* Not a literal or procedure, reinterpret it. */
+ store_state_short(iesp);
+ icount = 0;
+ SET_IREF(pvalue);
+ goto top;
+ }
+ /* default can't happen here */
+ }
+ }
+ }
+ /* Literal type, just push it. */
+ if (iosp >= ostop)
+ return_with_stackoverflow_iref();
+ ++iosp;
+ ref_assign_inline(iosp, IREF);
+ bot:next();
+ out: /* At most 1 more token in the current procedure. */
+ /* (We already decremented icount.) */
+ if (!icount) {
+ /* Pop the execution stack for tail recursion. */
+ iesp--;
+ iref_packed = IREF_NEXT(iref_packed);
+ goto top;
+ }
+ up:if (--ticks_left < 0)
+ goto slice;
+ /* See if there is anything left on the execution stack. */
+ if (!r_is_proc(iesp)) {
+ SET_IREF(iesp--);
+ icount = 0;
+ goto top;
+ }
+ SET_IREF(iesp->value.refs); /* next element of array */
+ icount = r_size(iesp) - 1;
+ if (icount <= 0) { /* <= 1 more elements */
+ iesp--; /* pop, or tail recursion */
+ if (icount < 0)
+ goto up;
+ }
+ goto top;
+res:
+ /* Some operator has asked for context rescheduling. */
+ /* We've done a store_state. */
+ *pi_ctx_p = i_ctx_p;
+ code = (*i_ctx_p->reschedule_proc)(pi_ctx_p);
+ i_ctx_p = *pi_ctx_p;
+ sched: /* We've just called a scheduling procedure. */
+ /* The interpreter state is in memory; iref is not current. */
+ if (code < 0) {
+ set_error(code);
+ /*
+ * We need a real object to return as the error object.
+ * (It only has to last long enough to store in
+ * *perror_object.)
+ */
+ make_null_proc(&ierror.full);
+ SET_IREF(ierror.obj = &ierror.full);
+ goto error_exit;
+ }
+ /* Reload state information from memory. */
+ iosp = osp;
+ iesp = esp;
+ goto up;
+#if 0 /****** ****** ***** */
+ sst: /* Time-slice, but push the current object first. */
+ store_state(iesp);
+ if (iesp >= estop)
+ return_with_error_iref(gs_error_execstackoverflow);
+ iesp++;
+ ref_assign_inline(iesp, iref);
+#endif /****** ****** ***** */
+ slice: /* It's time to time-slice or garbage collect. */
+ /* iref is not live, so we don't need to do a store_state. */
+ osp = iosp;
+ esp = iesp;
+ /* If ticks_left <= -100, we need to GC now. */
+ if (ticks_left <= -100) { /* We need to garbage collect now. */
+ *pi_ctx_p = i_ctx_p;
+ code = interp_reclaim(pi_ctx_p, -1);
+ i_ctx_p = *pi_ctx_p;
+ } else if (i_ctx_p->time_slice_proc != NULL) {
+ *pi_ctx_p = i_ctx_p;
+ code = (*i_ctx_p->time_slice_proc)(pi_ctx_p);
+ i_ctx_p = *pi_ctx_p;
+ } else
+ code = 0;
+ ticks_left = i_ctx_p->time_slice_ticks;
+ set_code_on_interrupt(imemory, &code);
+ goto sched;
+
+ /* Error exits. */
+
+ rweci:
+ ierror.code = code;
+ rwei:
+ ierror.obj = IREF;
+ rwe:
+ if (!r_is_packed(iref_packed))
+ store_state(iesp);
+ else {
+ /*
+ * We need a real object to return as the error object.
+ * (It only has to last long enough to store in *perror_object.)
+ */
+ packed_get(imemory, (const ref_packed *)ierror.obj, &ierror.full);
+ store_state_short(iesp);
+ if (IREF == ierror.obj)
+ SET_IREF(&ierror.full);
+ ierror.obj = &ierror.full;
+ }
+ error_exit:
+ if (GS_ERROR_IS_INTERRUPT(ierror.code)) { /* We must push the current object being interpreted */
+ /* back on the e-stack so it will be re-executed. */
+ /* Currently, this is always an executable operator, */
+ /* but it might be something else someday if we check */
+ /* for interrupts in the interpreter loop itself. */
+ if (iesp >= estop)
+ code = gs_error_execstackoverflow;
+ else {
+ iesp++;
+ ref_assign_inline(iesp, IREF);
+ }
+ }
+ esp = iesp;
+ osp = iosp;
+ ref_assign_inline(perror_object, ierror.obj);
+#ifdef DEBUG
+ if (ierror.code == gs_error_InterpreterExit) {
+ /* Do not call gs_log_error to reduce the noise. */
+ return gs_error_InterpreterExit;
+ }
+#endif
+ return gs_log_error(ierror.code, __FILE__, ierror.line);
+}
+
+/* Pop the bookkeeping information for a normal exit from a t_oparray. */
+static int
+oparray_pop(i_ctx_t *i_ctx_p)
+{
+ esp -= 4;
+ return o_pop_estack;
+}
+
+/* Restore the stack pointers after an error inside a t_oparray procedure. */
+/* This procedure is called only from pop_estack. */
+static int
+oparray_cleanup(i_ctx_t *i_ctx_p)
+{ /* esp points just below the cleanup procedure. */
+ es_ptr ep = esp;
+ uint ocount_old = (uint) ep[3].value.intval;
+ uint dcount_old = (uint) ep[4].value.intval;
+ uint ocount = ref_stack_count(&o_stack);
+ uint dcount = ref_stack_count(&d_stack);
+
+ if (ocount > ocount_old)
+ ref_stack_pop(&o_stack, ocount - ocount_old);
+ if (dcount > dcount_old) {
+ ref_stack_pop(&d_stack, dcount - dcount_old);
+ dict_set_top();
+ }
+ return 0;
+}
+
+/* Don't restore the stack pointers. */
+static int
+oparray_no_cleanup(i_ctx_t *i_ctx_p)
+{
+ return 0;
+}
+
+/* Find the innermost oparray. */
+static ref *
+oparray_find(i_ctx_t *i_ctx_p)
+{
+ long i;
+ ref *ep;
+
+ for (i = 0; (ep = ref_stack_index(&e_stack, i)) != 0; ++i) {
+ if (r_is_estack_mark(ep) &&
+ (ep->value.opproc == oparray_cleanup ||
+ ep->value.opproc == oparray_no_cleanup)
+ )
+ return ep;
+ }
+ return 0;
+}
+
+/* <errorobj> <obj> .errorexec ... */
+/* Execute an object, substituting errorobj for the 'command' if an error */
+/* occurs during the execution. Cf .execfile (in zfile.c). */
+static int
+zerrorexec(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_op(2);
+ check_estack(4); /* mark/cleanup, errobj, pop, obj */
+ push_mark_estack(es_other, errorexec_cleanup);
+ *++esp = op[-1];
+ push_op_estack(errorexec_pop);
+ code = zexec(i_ctx_p);
+ if (code >= 0)
+ pop(1);
+ else
+ esp -= 3; /* undo our additions to estack */
+ return code;
+}
+
+/* - .finderrorobject <errorobj> true */
+/* - .finderrorobject false */
+/* If we are within an .errorexec or oparray, return the error object */
+/* and true, otherwise return false. */
+static int
+zfinderrorobject(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref errobj;
+
+ if (errorexec_find(i_ctx_p, &errobj)) {
+ push(2);
+ op[-1] = errobj;
+ make_true(op);
+ } else {
+ push(1);
+ make_false(op);
+ }
+ return 0;
+}
+
+/*
+ * Find the innermost .errorexec or oparray. If there is an oparray, or a
+ * .errorexec with errobj != null, store it in *perror_object and return 1,
+ * otherwise return 0;
+ */
+static int
+errorexec_find(i_ctx_t *i_ctx_p, ref *perror_object)
+{
+ long i;
+ const ref *ep;
+
+ for (i = 0; (ep = ref_stack_index(&e_stack, i)) != 0; ++i) {
+ if (r_is_estack_mark(ep)) {
+ if (ep->value.opproc == oparray_cleanup) {
+ /* See oppr: above. */
+ uint opindex = (uint)ep[1].value.intval;
+ if (opindex == 0) /* internal operator, ignore */
+ continue;
+ op_index_ref(imemory, opindex, perror_object);
+ return 1;
+ }
+ if (ep->value.opproc == oparray_no_cleanup)
+ return 0; /* protection disabled */
+ if (ep->value.opproc == errorexec_cleanup) {
+ if (r_has_type(ep + 1, t_null))
+ return 0;
+ *perror_object = ep[1]; /* see .errorexec above */
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Pop the bookkeeping information on a normal exit from .errorexec. */
+static int
+errorexec_pop(i_ctx_t *i_ctx_p)
+{
+ esp -= 2;
+ return o_pop_estack;
+}
+
+/* Clean up when unwinding the stack on an error. (No action needed.) */
+static int
+errorexec_cleanup(i_ctx_t *i_ctx_p)
+{
+ return 0;
+}
+
+/* <bool> .setstackprotect - */
+/* Set whether to protect the stack for the innermost oparray. */
+static int
+zsetstackprotect(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *ep = oparray_find(i_ctx_p);
+
+ check_type(*op, t_boolean);
+ if (ep == 0)
+ return_error(gs_error_rangecheck);
+ ep->value.opproc =
+ (op->value.boolval ? oparray_cleanup : oparray_no_cleanup);
+ pop(1);
+ return 0;
+}
+
+/* - .currentstackprotect <bool> */
+/* Return the stack protection status. */
+static int
+zcurrentstackprotect(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *ep = oparray_find(i_ctx_p);
+
+ if (ep == 0)
+ return_error(gs_error_rangecheck);
+ push(1);
+ make_bool(op, ep->value.opproc == oparray_cleanup);
+ return 0;
+}
diff --git a/psi/interp.h b/psi/interp.h
new file mode 100644
index 000000000..9a829aa47
--- /dev/null
+++ b/psi/interp.h
@@ -0,0 +1,96 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Internal interfaces to interp.c and iinit.c */
+
+#ifndef interp_INCLUDED
+# define interp_INCLUDED
+
+/* ------ iinit.c ------ */
+
+/* Enter a name and value into systemdict. */
+int i_initial_enter_name(i_ctx_t *, const char *, const ref *);
+#define initial_enter_name(nstr, pvalue)\
+ i_initial_enter_name(i_ctx_p, nstr, pvalue)
+
+/* Remove a name from systemdict. */
+void i_initial_remove_name(i_ctx_t *, const char *);
+#define initial_remove_name(nstr)\
+ i_initial_remove_name(i_ctx_p, nstr)
+
+/* ------ interp.c ------ */
+
+/*
+ * Maximum number of arguments (and results) for an operator,
+ * determined by operand stack block size.
+ */
+extern const int gs_interp_max_op_num_args;
+
+/*
+ * Number of slots to reserve at the start of op_def_table for
+ * operators which are hard-coded into the interpreter loop.
+ */
+extern const int gs_interp_num_special_ops;
+
+/*
+ * Create an operator during initialization.
+ * If operator is hard-coded into the interpreter,
+ * assign it a special type and index.
+ */
+void gs_interp_make_oper(ref * opref, op_proc_t, int index);
+
+/*
+ * Call the garbage collector, updating the context pointer properly.
+ */
+int interp_reclaim(i_ctx_t **pi_ctx_p, int space);
+
+/* Get the name corresponding to an error number. */
+int gs_errorname(i_ctx_t *, int, ref *);
+
+/* Put a string in $error /errorinfo. */
+int gs_errorinfo_put_string(i_ctx_t *, const char *);
+
+/* Initialize the interpreter. */
+int gs_interp_init(i_ctx_t **pi_ctx_p, const ref *psystem_dict,
+ gs_dual_memory_t *dmem);
+
+#ifndef gs_context_state_t_DEFINED
+# define gs_context_state_t_DEFINED
+typedef struct gs_context_state_s gs_context_state_t;
+#endif
+
+/*
+ * Create initial stacks for the interpreter.
+ * We export this for creating new contexts.
+ */
+int gs_interp_alloc_stacks(gs_ref_memory_t * smem,
+ gs_context_state_t * pcst);
+
+/*
+ * Free the stacks when destroying a context. This is the inverse of
+ * create_stacks.
+ */
+void gs_interp_free_stacks(gs_ref_memory_t * smem,
+ gs_context_state_t * pcst);
+
+/* Reset the interpreter. */
+void gs_interp_reset(i_ctx_t *i_ctx_p);
+
+/* Define the top-level interface to the interpreter. */
+int gs_interpret(i_ctx_t **pi_ctx_p, ref * pref, int user_errors,
+ int *pexit_code, ref * perror_object);
+
+#endif /* interp_INCLUDED */
diff --git a/psi/iosdata.h b/psi/iosdata.h
new file mode 100644
index 000000000..56102974d
--- /dev/null
+++ b/psi/iosdata.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Generic operand stack API */
+
+#ifndef iosdata_INCLUDED
+# define iosdata_INCLUDED
+
+#include "isdata.h"
+
+/* Define the operand stack structure. */
+/* Currently this is just a generic ref stack. */
+typedef struct op_stack_s {
+
+ ref_stack_t stack; /* the actual operand stack */
+
+} op_stack_t;
+
+#define public_st_op_stack() /* in interp.c */\
+ gs_public_st_suffix_add0(st_op_stack, op_stack_t, "op_stack_t",\
+ op_stack_enum_ptrs, op_stack_reloc_ptrs, st_ref_stack)
+#define st_op_stack_num_ptrs st_ref_stack_num_ptrs
+
+#endif /* iosdata_INCLUDED */
diff --git a/psi/iostack.h b/psi/iostack.h
new file mode 100644
index 000000000..e37079c04
--- /dev/null
+++ b/psi/iostack.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Generic operand stack API */
+
+#ifndef iostack_INCLUDED
+# define iostack_INCLUDED
+
+#include "iosdata.h"
+#include "istack.h"
+
+/* Define pointers into the operand stack. */
+typedef s_ptr os_ptr;
+typedef const_s_ptr const_os_ptr;
+
+#endif /* iostack_INCLUDED */
diff --git a/psi/ipacked.h b/psi/ipacked.h
new file mode 100644
index 000000000..9c0066913
--- /dev/null
+++ b/psi/ipacked.h
@@ -0,0 +1,133 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Packed array format for Ghostscript */
+
+#ifndef ipacked_INCLUDED
+# define ipacked_INCLUDED
+
+/*
+
+ In a packed array, an element may either be a 2-byte ref_packed or a
+ full-size ref (8 or 16 bytes). We carefully arrange the first two bytes,
+ which are either an entire ref_packed or the type_attrs member of a ref,
+ so that we can distinguish the 2 forms. The encoding:
+
+ 00tttttt exrwsfnm full-size ref
+ 010mjjjj jjjjjjjj executable operator (so bind can work)
+ 011mvvvv vvvvvvvv integer (biased by packed_min_intval)
+ 100m---- -------- (not used)
+ 101m---- -------- (not used)
+ 110miiii iiiiiiii literal name
+ 111miiii iiiiiiii executable name
+
+ The m bit is the mark bit for the garbage collector.
+
+ ****** Note for the future: We could get packed tokens into the first-level
+ ****** interpreter dispatch by changing to the following representation:
+
+ 000ttttt exrwsfnm full-size ref
+ m0100jjj jjjjjjjj executable operator (so bind can work)
+ m0101vvv vvvvvvvv integer (biased by packed_min_intval)
+ m011iiii iiiiiiii literal name
+ m100iiii iiiiiiii executable name
+ m101---- -------- (not used)
+ m11----- -------- (not used)
+
+ ****** We aren't going to do this for a while.
+
+ The jjj index of executable operators is either the index of the operator
+ in the op_def_table, if the index is less than op_def_count, or the index
+ of the definition in the op_array_table (subtracting op_def_count first).
+
+ The iii index of names is the one that the name machinery already
+ maintains. A name whose index is larger than will fit in the packed
+ representation must be represented as a full-size ref.
+
+ There are two packed array types, t_mixedarray and t_shortarray. A
+ t_mixedarray can have a mix of packed and full-size elements; a
+ t_shortarray has all packed elements. The 'size' of a packed array is the
+ number of elements, not the number of bytes it occupies.
+
+ Packed array elements can be distinguished from full-size elements, so we
+ allow the interpreter to simply execute all the different kinds of arrays
+ directly. However, if we really allowed free mixing of packed and
+ full-size elements, this could lead to unaligned placement of full-size
+ refs; some machines can't handle unaligned accesses of this kind. To
+ guarantee that full-size elements in mixed arrays are always properly
+ aligned, if a full-size ref must be aligned at an address which is 0 mod
+ N, we convert up to N/2-1 preceding packed elements into full-size
+ elements, when creating the array, so that the alignment is preserved.
+ The only code this actually affects is in make_packed_array and in the
+ code for compacting refs in the garbage collector.
+
+ Note that code in zpacked.c and interp.c knows more about the
+ representation of packed elements than the definitions in this file would
+ imply. Read the code carefully if you change the representation.
+
+ */
+
+#define r_packed_type_shift 13
+#define r_packed_value_bits 12
+typedef enum {
+ pt_full_ref = 0,
+#define pt_min_packed 2
+ pt_executable_operator = 2,
+ pt_integer = 3,
+ pt_unused1 = 4,
+ pt_unused2 = 5,
+#define pt_min_name 6
+ pt_literal_name = 6,
+#define pt_min_exec_name 7
+ pt_executable_name = 7
+} packed_type;
+
+#define packed_per_ref (sizeof(ref) / sizeof(ref_packed))
+#define align_packed_per_ref\
+ (ARCH_ALIGN_REF_MOD / ARCH_ALIGN_SHORT_MOD)
+#define pt_tag(pt) ((ref_packed)(pt) << r_packed_type_shift)
+#define packed_value_mask ((1 << r_packed_value_bits) - 1)
+#define packed_max_value packed_value_mask
+#define r_is_packed(rp) (*(const ref_packed *)(rp) >= pt_tag(pt_min_packed))
+/* Names */
+#define r_packed_is_name(prp) (*(prp) >= pt_tag(pt_min_name))
+#define r_packed_is_exec_name(prp) (*(prp) >= pt_tag(pt_min_exec_name))
+#define packed_name_max_index packed_max_value
+#define packed_name_index(prp) (*(prp) & packed_value_mask)
+/* Integers */
+#define packed_min_intval (-(1 << (r_packed_value_bits - 1)))
+#define packed_max_intval ((1 << (r_packed_value_bits - 1)) - 1)
+#define packed_int_mask packed_value_mask
+
+/* Packed ref marking */
+#define lp_mark_shift 12
+#define lp_mark (1 << lp_mark_shift)
+#define r_has_pmark(rp) (*(rp) & lp_mark)
+#define r_set_pmark(rp) (*(rp) |= lp_mark)
+#define r_clear_pmark(rp) (*(rp) &= ~lp_mark)
+#define r_store_pmark(rp,pm) (*(rp) = (*(rp) & ~lp_mark) | (pm))
+
+/* Advance to the next element in a packed array. */
+#define packed_next(prp)\
+ (r_is_packed(prp) ? prp + 1 : prp + packed_per_ref)
+
+/* Define the current array packing flag (setpacking/currentpacking) */
+/* for operators. */
+/* This is a ref so that it can be managed properly by save/restore. */
+#define ref_array_packing_container i_ctx_p
+#define ref_array_packing (ref_array_packing_container->array_packing)
+
+#endif /* ipacked_INCLUDED */
diff --git a/psi/iparam.c b/psi/iparam.c
new file mode 100644
index 000000000..4e63b6d25
--- /dev/null
+++ b/psi/iparam.c
@@ -0,0 +1,1137 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter implementations of parameter dictionaries */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "ierrors.h"
+#include "oper.h" /* for check_type */
+#include "opcheck.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "imemory.h" /* for iutil.h */
+#include "iname.h"
+#include "istack.h"
+#include "iparam.h"
+#include "iutil.h" /* for num_params */
+#include "ivmspace.h"
+#include "store.h"
+#include "gsstruct.h" /* for st_bytes */
+
+/* ================ Utilities ================ */
+
+/* Convert a key to a ref. */
+static int
+ref_param_key(const iparam_list * plist, gs_param_name pkey, ref * pkref)
+{
+ if (plist->int_keys) {
+ long key;
+
+ if (sscanf(pkey, "%ld", &key) != 1)
+ return_error(gs_error_rangecheck);
+ make_int(pkref, key);
+ return 0;
+ } else
+ return name_ref(plist->memory, (const byte *)pkey, strlen(pkey), pkref, 0);
+}
+
+/* Fill in a gs_param_key_t from a name or int ref. */
+static int
+ref_to_key(const ref * pref, gs_param_key_t * key, iparam_list *plist)
+{
+ if (r_has_type(pref, t_name)) {
+ ref nref;
+
+ name_string_ref(plist->memory, pref, &nref);
+ key->data = nref.value.const_bytes;
+ key->size = r_size(&nref);
+ key->persistent = false; /* names may be freed */
+ } else if (r_has_type(pref, t_integer)) {
+ char istr[sizeof(long) * 8 / 3 + 2];
+ int len;
+ byte *buf;
+
+ gs_sprintf(istr, "%"PRIpsint, pref->value.intval);
+ len = strlen(istr);
+ /* GC will take care of freeing this: */
+ buf = gs_alloc_string(plist->memory, len, "ref_to_key");
+ if (!buf)
+ return_error(gs_error_VMerror);
+ key->data = buf;
+ key->size = len;
+ key->persistent = true;
+ } else
+ return_error(gs_error_typecheck);
+ return 0;
+}
+
+/* ================ Writing parameters to refs ================ */
+
+/* Forward references */
+static int array_new_indexed_plist_write(dict_param_list *plist,
+ ref *parray, const ref *pwanted,
+ gs_ref_memory_t *imem);
+
+/* ---------------- Generic writing procedures ---------------- */
+
+static param_proc_begin_xmit_collection(ref_param_begin_write_collection);
+static param_proc_end_xmit_collection(ref_param_end_write_collection);
+static param_proc_xmit_typed(ref_param_write_typed);
+static param_proc_next_key(ref_param_get_next_key);
+static param_proc_requested(ref_param_requested);
+static const gs_param_list_procs ref_write_procs =
+{
+ ref_param_write_typed,
+ ref_param_begin_write_collection,
+ ref_param_end_write_collection,
+ ref_param_get_next_key,
+ NULL, /* request */
+ ref_param_requested
+};
+static int ref_array_param_requested(const iparam_list *, gs_param_name,
+ ref *, uint, client_name_t);
+static int ref_param_write(iparam_list *, gs_param_name, const ref *);
+static int ref_param_write_string_value(ref *, const gs_param_string *,
+ gs_ref_memory_t *);
+static int ref_param_write_name_value(const gs_memory_t *mem, ref *, const gs_param_string *);
+static int
+ref_param_make_int(ref *pe, const void *pvalue, uint i, gs_ref_memory_t *imem)
+{
+ make_tav(pe, t_integer, imemory_new_mask(imem), intval,
+ ((const gs_param_int_array *)pvalue)->data[i]);
+ return 0;
+}
+static int
+ref_param_make_float(ref *pe, const void *pvalue, uint i, gs_ref_memory_t *imem)
+{
+ make_tav(pe, t_real, imemory_new_mask(imem), realval,
+ ((const gs_param_float_array *)pvalue)->data[i]);
+ return 0;
+}
+static int
+ref_param_make_string(ref *pe, const void *pvalue, uint i, gs_ref_memory_t *imem)
+{
+ return ref_param_write_string_value(pe,
+ &((const gs_param_string_array *)pvalue)->data[i],
+ imem);
+}
+static int
+ref_param_make_name(ref * pe, const void *pvalue, uint i, gs_ref_memory_t *imem)
+{
+ return ref_param_write_name_value((const gs_memory_t *)imem, pe,
+ &((const gs_param_string_array *)pvalue)->data[i]);
+}
+static int
+ref_param_write_typed_array(gs_param_list * plist, gs_param_name pkey,
+ void *pvalue, uint count,
+ int (*make)(ref *, const void *, uint,
+ gs_ref_memory_t *))
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ ref value;
+ uint i;
+ ref *pe;
+ int code;
+
+ if ((code = ref_array_param_requested(iplist, pkey, &value, count,
+ "ref_param_write_typed_array")) <= 0)
+ return code;
+ for (i = 0, pe = value.value.refs; i < count; ++i, ++pe)
+ if ((code = (*make) (pe, pvalue, i, iplist->ref_memory)) < 0)
+ return code;
+ return ref_param_write(iplist, pkey, &value);
+}
+static int
+ref_param_begin_write_collection(gs_param_list * plist, gs_param_name pkey,
+ gs_param_dict * pvalue,
+ gs_param_collection_type_t coll_type)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ gs_ref_memory_t *imem = iplist->ref_memory;
+ dict_param_list *dlist = (dict_param_list *)
+ gs_alloc_bytes(plist->memory, size_of(dict_param_list),
+ "ref_param_begin_write_collection");
+ int code;
+
+ if (dlist == 0)
+ return_error(gs_error_VMerror);
+ if (coll_type != gs_param_collection_array) {
+ ref dref;
+
+ code = dict_alloc(imem, pvalue->size, &dref);
+ if (code >= 0) {
+ code = dict_param_list_write(dlist, &dref, NULL, imem);
+ dlist->int_keys = coll_type == gs_param_collection_dict_int_keys;
+ }
+ } else {
+ ref aref;
+
+ code = gs_alloc_ref_array(imem, &aref, a_all, pvalue->size,
+ "ref_param_begin_write_collection");
+ if (code >= 0)
+ code = array_new_indexed_plist_write(dlist, &aref, NULL, imem);
+ }
+ if (code < 0)
+ gs_free_object(plist->memory, dlist, "ref_param_begin_write_collection");
+ else
+ pvalue->list = (gs_param_list *) dlist;
+ return code;
+}
+static int
+ref_param_end_write_collection(gs_param_list * plist, gs_param_name pkey,
+ gs_param_dict * pvalue)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ int code = ref_param_write(iplist, pkey,
+ &((dict_param_list *) pvalue->list)->dict);
+
+ gs_free_object(plist->memory, pvalue->list, "ref_param_end_write_collection");
+ pvalue->list = 0;
+ return code;
+}
+static int
+ref_param_write_typed(gs_param_list * plist, gs_param_name pkey,
+ gs_param_typed_value * pvalue)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ ref value;
+ int code = 0;
+
+ switch (pvalue->type) {
+ case gs_param_type_null:
+ make_null(&value);
+ break;
+ case gs_param_type_bool:
+ make_bool(&value, pvalue->value.b);
+ break;
+ case gs_param_type_int:
+ make_int(&value, pvalue->value.i);
+ break;
+ case gs_param_type_long:
+ make_int(&value, pvalue->value.l);
+ break;
+ case gs_param_type_float:
+ make_real(&value, pvalue->value.f);
+ break;
+ case gs_param_type_string:
+ if (!ref_param_requested(plist, pkey))
+ return 0;
+ code = ref_param_write_string_value(&value, &pvalue->value.s,
+ iplist->ref_memory);
+ break;
+ case gs_param_type_name:
+ if (!ref_param_requested(plist, pkey))
+ return 0;
+ code = ref_param_write_name_value(iplist->memory, &value, &pvalue->value.n);
+ break;
+ case gs_param_type_int_array:
+ return ref_param_write_typed_array(plist, pkey, &pvalue->value.ia,
+ pvalue->value.ia.size,
+ ref_param_make_int);
+ case gs_param_type_float_array:
+ return ref_param_write_typed_array(plist, pkey, &pvalue->value.fa,
+ pvalue->value.fa.size,
+ ref_param_make_float);
+ case gs_param_type_string_array:
+ return ref_param_write_typed_array(plist, pkey, &pvalue->value.sa,
+ pvalue->value.sa.size,
+ ref_param_make_string);
+ case gs_param_type_name_array:
+ return ref_param_write_typed_array(plist, pkey, &pvalue->value.na,
+ pvalue->value.na.size,
+ ref_param_make_name);
+ case gs_param_type_dict:
+ case gs_param_type_dict_int_keys:
+ case gs_param_type_array:
+ return ref_param_begin_write_collection(plist, pkey,
+ &pvalue->value.d,
+ (gs_param_collection_type_t)(pvalue->type - gs_param_type_dict));
+ default:
+ return_error(gs_error_typecheck);
+ }
+ if (code < 0)
+ return code;
+ return ref_param_write(iplist, pkey, &value);
+}
+
+/* Check whether a given parameter was requested. */
+static int
+ref_param_requested(const gs_param_list * plist, gs_param_name pkey)
+{
+ const iparam_list *const ciplist = (const iparam_list *)plist;
+ ref kref;
+ ref *ignore_value;
+
+ if (!r_has_type(&ciplist->u.w.wanted, t_dictionary))
+ return -1;
+ if (ref_param_key(ciplist, pkey, &kref) < 0)
+ return -1; /* catch it later */
+ return (dict_find(&ciplist->u.w.wanted, &kref, &ignore_value) > 0);
+}
+
+/* Check whether an array parameter is wanted, and allocate it if so. */
+/* Return <0 on error, 0 if not wanted, 1 if wanted. */
+static int
+ref_array_param_requested(const iparam_list *iplist, gs_param_name pkey,
+ ref *pvalue, uint size, client_name_t cname)
+{
+ int code;
+
+ if (!ref_param_requested((const gs_param_list *)iplist, pkey))
+ return 0;
+ code = gs_alloc_ref_array(iplist->ref_memory, pvalue, a_all, size, cname);
+ return (code < 0 ? code : 1);
+}
+
+/* ---------------- Internal routines ---------------- */
+
+/* Prepare to write a string value. */
+static int
+ref_param_write_string_value(ref * pref, const gs_param_string * pvalue,
+ gs_ref_memory_t *imem)
+{
+ const byte *pdata = pvalue->data;
+ uint n = pvalue->size;
+
+ if (pvalue->persistent)
+ make_const_string(pref, a_readonly | avm_foreign, n, pdata);
+ else {
+ byte *pstr = gs_alloc_string((gs_memory_t *)imem, n,
+ "ref_param_write_string");
+
+ if (pstr == 0)
+ return_error(gs_error_VMerror);
+ memcpy(pstr, pdata, n);
+ make_string(pref, a_readonly | imemory_space(imem), n, pstr);
+ }
+ return 0;
+}
+
+/* Prepare to write a name value. */
+static int
+ref_param_write_name_value(const gs_memory_t *mem, ref * pref, const gs_param_string * pvalue)
+{
+ return name_ref(mem, pvalue->data, pvalue->size, pref,
+ (pvalue->persistent ? 0 : 1));
+}
+
+/* Generic routine for writing a ref parameter. */
+static int
+ref_param_write(iparam_list * plist, gs_param_name pkey, const ref * pvalue)
+{
+ ref kref;
+ int code;
+
+ if (!ref_param_requested((gs_param_list *) plist, pkey))
+ return 0;
+ code = ref_param_key(plist, pkey, &kref);
+ if (code < 0)
+ return code;
+ return (*plist->u.w.write) (plist, &kref, pvalue);
+}
+
+/* ---------------- Implementations ---------------- */
+
+/* Initialize for writing parameters. */
+static void
+ref_param_write_init(iparam_list * plist, const ref * pwanted,
+ gs_ref_memory_t *imem)
+{
+ gs_param_list_init((gs_param_list *)plist, &ref_write_procs,
+ (gs_memory_t *)imem);
+ plist->ref_memory = imem;
+ if (pwanted == 0)
+ make_null(&plist->u.w.wanted);
+ else
+ plist->u.w.wanted = *pwanted;
+ plist->results = 0;
+ plist->int_keys = false;
+}
+
+/* Implementation for getting parameters to a stack. */
+static int
+stack_param_write(iparam_list * plist, const ref * pkey, const ref * pvalue)
+{
+ stack_param_list *const splist = (stack_param_list *) plist;
+ ref_stack_t *pstack = splist->pstack;
+ s_ptr p = pstack->p;
+
+ if (pstack->top - p < 2) {
+ int code = ref_stack_push(pstack, 2);
+
+ if (code < 0)
+ return code;
+ *ref_stack_index(pstack, 1) = *pkey;
+ p = pstack->p;
+ } else {
+ pstack->p = p += 2;
+ p[-1] = *pkey;
+ }
+ *p = *pvalue;
+ splist->count++;
+ return 0;
+}
+
+/* Implementation for enumerating parameters on a stack */
+static int /* ret 0 ok, 1 if EOF, or -ve err */
+stack_param_enumerate(iparam_list * plist, gs_param_enumerator_t * penum,
+ gs_param_key_t * key, ref_type * type)
+{
+ int code;
+ stack_param_list *const splist = (stack_param_list *) plist;
+ int index = penum->intval;
+ ref *stack_element;
+
+ do {
+ stack_element =
+ ref_stack_index(splist->pstack, index + 1 + splist->skip);
+ if (!stack_element)
+ return 1;
+ } while (index += 2, !r_has_type(stack_element, t_name));
+ *type = r_type(stack_element);
+ code = ref_to_key(stack_element, key, plist);
+ penum->intval = index;
+ return code;
+}
+
+int
+stack_param_list_write(stack_param_list * plist, ref_stack_t * pstack,
+ const ref * pwanted, gs_ref_memory_t *imem)
+{
+ plist->u.w.write = stack_param_write;
+ ref_param_write_init((iparam_list *) plist, pwanted, imem);
+ plist->enumerate = stack_param_enumerate;
+ plist->pstack = pstack;
+ plist->skip = 0;
+ plist->count = 0;
+ return 0;
+}
+
+/* Implementation for getting parameters to a dictionary. */
+static int
+dict_param_write(iparam_list * plist, const ref * pkey, const ref * pvalue)
+{
+ int code =
+ dict_put(&((dict_param_list *) plist)->dict, pkey, pvalue, NULL);
+
+ return min(code, 0);
+}
+
+/* Implementation for enumerating parameters in a dictionary */
+static int /* ret 0 ok, 1 if EOF, or -ve err */
+dict_param_enumerate(iparam_list * plist, gs_param_enumerator_t * penum,
+ gs_param_key_t * key, ref_type * type)
+{
+ ref elt[2];
+ int code;
+ dict_param_list *const pdlist = (dict_param_list *) plist;
+ int index =
+ (penum->intval != 0 ? penum->intval : dict_first(&pdlist->dict));
+
+ index = dict_next(&pdlist->dict, index, elt);
+ if (index < 0)
+ return 1;
+ *type = r_type(&elt[1]);
+ code = ref_to_key(&elt[0], key, plist);
+ penum->intval = index;
+ return code;
+}
+
+int
+dict_param_list_write(dict_param_list *plist, ref *pdict, const ref *pwanted,
+ gs_ref_memory_t *imem)
+{
+ check_dict_write(*pdict);
+ plist->u.w.write = dict_param_write;
+ plist->enumerate = dict_param_enumerate;
+ ref_param_write_init((iparam_list *) plist, pwanted, imem);
+ plist->dict = *pdict;
+ return 0;
+}
+
+/* Implementation for getting parameters to an indexed array. */
+/* Note that this is now internal, since it only handles "new" arrays. */
+static int
+array_new_indexed_param_write(iparam_list * iplist, const ref * pkey,
+ const ref * pvalue)
+{
+ const ref *const arr = &((dict_param_list *)iplist)->dict;
+ ref *eltp;
+
+ if (!r_has_type(pkey, t_integer))
+ return_error(gs_error_typecheck);
+ check_int_ltu(*pkey, r_size(arr));
+ store_check_dest(arr, pvalue);
+ eltp = arr->value.refs + pkey->value.intval;
+ /* ref_assign_new(eltp, pvalue); */
+ ref_assign(eltp, pvalue);
+ r_set_attrs(eltp, imemory_new_mask(iplist->ref_memory));
+ return 0;
+}
+static int
+array_new_indexed_plist_write(dict_param_list * plist, ref * parray,
+ const ref * pwanted, gs_ref_memory_t *imem)
+{
+ check_array(*parray);
+ check_write(*parray);
+ plist->u.w.write = array_new_indexed_param_write;
+ ref_param_write_init((iparam_list *) plist, pwanted, imem);
+ plist->dict = *parray;
+ plist->int_keys = true;
+ return 0;
+}
+
+/* ================ Reading refs to parameters ================ */
+
+/* ---------------- Generic reading procedures ---------------- */
+
+static param_proc_begin_xmit_collection(ref_param_begin_read_collection);
+static param_proc_end_xmit_collection(ref_param_end_read_collection);
+static param_proc_xmit_typed(ref_param_read_typed);
+
+/*static param_proc_next_key(ref_param_get_next_key); already dec'ld above */
+static param_proc_get_policy(ref_param_read_get_policy);
+static param_proc_signal_error(ref_param_read_signal_error);
+static param_proc_commit(ref_param_read_commit);
+static const gs_param_list_procs ref_read_procs =
+{
+ ref_param_read_typed,
+ ref_param_begin_read_collection,
+ ref_param_end_read_collection,
+ ref_param_get_next_key,
+ NULL, /* request */
+ NULL, /* requested */
+ ref_param_read_get_policy,
+ ref_param_read_signal_error,
+ ref_param_read_commit
+};
+static int ref_param_read(iparam_list *, gs_param_name,
+ iparam_loc *, int);
+static int ref_param_read_string_value(gs_memory_t *mem,
+ const iparam_loc *,
+ gs_param_string *);
+static int ref_param_read_array(iparam_list *, gs_param_name,
+ iparam_loc *);
+
+#define iparam_note_error(loc, code)\
+ gs_note_error(*(loc).presult = code)
+#define iparam_check_type(loc, typ)\
+ if ( !r_has_type((loc).pvalue, typ) )\
+ return iparam_note_error(loc, gs_error_typecheck)
+#define iparam_check_read(loc)\
+ if ( !r_has_attr((loc).pvalue, a_read) )\
+ return iparam_note_error(loc, gs_error_invalidaccess)
+
+static int
+ref_param_read_int_array(gs_param_list * plist, gs_param_name pkey,
+ gs_param_int_array * pvalue)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ iparam_loc loc;
+ int code = ref_param_read_array(iplist, pkey, &loc);
+ int *piv;
+ uint size;
+ long i;
+
+ if (code != 0)
+ return code;
+ size = r_size(loc.pvalue);
+ piv = (int *)gs_alloc_byte_array(plist->memory, size, sizeof(int),
+ "ref_param_read_int_array");
+
+ if (piv == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < size; i++) {
+ ref elt;
+
+ array_get(plist->memory, loc.pvalue, i, &elt);
+ if (!r_has_type(&elt, t_integer)) {
+ code = gs_note_error(gs_error_typecheck);
+ break;
+ }
+ piv[i] = (int)elt.value.intval;
+ }
+ if (code < 0) {
+ gs_free_object(plist->memory, piv, "ref_param_read_int_array");
+ return (*loc.presult = code);
+ }
+ pvalue->data = piv;
+ pvalue->size = size;
+ pvalue->persistent = true;
+ return 0;
+}
+static int
+ref_param_read_float_array(gs_param_list * plist, gs_param_name pkey,
+ gs_param_float_array * pvalue)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ iparam_loc loc;
+ ref aref, elt;
+ int code = ref_param_read_array(iplist, pkey, &loc);
+ float *pfv;
+ uint size;
+ long i;
+
+ if (code != 0)
+ return code;
+ size = r_size(loc.pvalue);
+ pfv = (float *)gs_alloc_byte_array(plist->memory, size, sizeof(float),
+ "ref_param_read_float_array");
+
+ if (pfv == 0)
+ return_error(gs_error_VMerror);
+ aref = *loc.pvalue;
+ loc.pvalue = &elt;
+ for (i = 0; code >= 0 && i < size; i++) {
+ array_get(plist->memory, &aref, i, &elt);
+ code = float_param(&elt, pfv + i);
+ }
+ if (code < 0) {
+ gs_free_object(plist->memory, pfv, "ref_read_float_array_param");
+ return (*loc.presult = code);
+ }
+ pvalue->data = pfv;
+ pvalue->size = size;
+ pvalue->persistent = true;
+ return 0;
+}
+static int
+ref_param_read_string_array(gs_param_list * plist, gs_param_name pkey,
+ gs_param_string_array * pvalue)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ iparam_loc loc;
+ ref aref;
+ int code = ref_param_read_array(iplist, pkey, &loc);
+ gs_param_string *psv;
+ uint size;
+ long i;
+
+ if (code != 0)
+ return code;
+ size = r_size(loc.pvalue);
+ psv = (gs_param_string *)
+ gs_alloc_byte_array(plist->memory, size, sizeof(gs_param_string),
+ "ref_param_read_string_array");
+ if (psv == 0)
+ return_error(gs_error_VMerror);
+ aref = *loc.pvalue;
+ if (r_has_type(&aref, t_array)) {
+ for (i = 0; code >= 0 && i < size; i++) {
+ loc.pvalue = aref.value.refs + i;
+ code = ref_param_read_string_value(plist->memory, &loc, psv + i);
+ }
+ } else {
+ ref elt;
+
+ loc.pvalue = &elt;
+ for (i = 0; code >= 0 && i < size; i++) {
+ array_get(plist->memory, &aref, i, &elt);
+ code = ref_param_read_string_value(plist->memory, &loc, psv + i);
+ }
+ }
+ if (code < 0) {
+ gs_free_object(plist->memory, psv, "ref_param_read_string_array");
+ return (*loc.presult = code);
+ }
+ pvalue->data = psv;
+ pvalue->size = size;
+ pvalue->persistent = true;
+ return 0;
+}
+static int
+ref_param_begin_read_collection(gs_param_list * plist, gs_param_name pkey,
+ gs_param_dict * pvalue,
+ gs_param_collection_type_t coll_type)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ iparam_loc loc;
+ bool int_keys = coll_type != 0;
+ int code = ref_param_read(iplist, pkey, &loc, -1);
+ dict_param_list *dlist;
+
+ if (code != 0)
+ return code;
+ dlist = (dict_param_list *)
+ gs_alloc_bytes(plist->memory, size_of(dict_param_list),
+ "ref_param_begin_read_collection");
+ if (dlist == 0)
+ return_error(gs_error_VMerror);
+ if (r_has_type(loc.pvalue, t_dictionary)) {
+ code = dict_param_list_read(dlist, loc.pvalue, NULL, false,
+ iplist->ref_memory);
+ dlist->int_keys = int_keys;
+ if (code >= 0)
+ pvalue->size = dict_length(loc.pvalue);
+ } else if (int_keys && r_is_array(loc.pvalue)) {
+ code = array_indexed_param_list_read(dlist, loc.pvalue, NULL, false,
+ iplist->ref_memory);
+ if (code >= 0)
+ pvalue->size = r_size(loc.pvalue);
+ } else
+ code = gs_note_error(gs_error_typecheck);
+ if (code < 0) {
+ gs_free_object(plist->memory, dlist, "ref_param_begin_write_collection");
+ return iparam_note_error(loc, code);
+ }
+ pvalue->list = (gs_param_list *) dlist;
+ return 0;
+}
+static int
+ref_param_end_read_collection(gs_param_list * plist, gs_param_name pkey,
+ gs_param_dict * pvalue)
+{
+ iparam_list_release((dict_param_list *) pvalue->list);
+ gs_free_object(plist->memory, pvalue->list,
+ "ref_param_end_read_collection");
+ return 0;
+}
+static int
+ref_param_read_typed(gs_param_list * plist, gs_param_name pkey,
+ gs_param_typed_value * pvalue)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ iparam_loc loc;
+ ref elt;
+ int code = ref_param_read(iplist, pkey, &loc, -1);
+
+ if (code != 0)
+ return code;
+ switch (r_type(loc.pvalue)) {
+ case t_array:
+ case t_mixedarray:
+ case t_shortarray:
+ iparam_check_read(loc);
+ if (r_size(loc.pvalue) <= 0) {
+ /* 0-length array; can't get type info */
+ pvalue->type = gs_param_type_array;
+ pvalue->value.d.list = 0;
+ pvalue->value.d.size = 0;
+ return 0;
+ }
+ /*
+ * We have to guess at the array type. First we guess based
+ * on the type of the first element of the array. If that
+ * fails, we try again with more general types.
+ */
+ array_get(plist->memory, loc.pvalue, 0, &elt);
+ switch (r_type(&elt)) {
+ case t_integer:
+ pvalue->type = gs_param_type_int_array;
+ code = ref_param_read_int_array(plist, pkey,
+ &pvalue->value.ia);
+ if (code != gs_error_typecheck)
+ return code;
+ /* This might be a float array. Fall through. */
+ *loc.presult = 0; /* reset error */
+ case t_real:
+ pvalue->type = gs_param_type_float_array;
+ return ref_param_read_float_array(plist, pkey,
+ &pvalue->value.fa);
+ case t_string:
+ pvalue->type = gs_param_type_string_array;
+ return ref_param_read_string_array(plist, pkey,
+ &pvalue->value.sa);
+ case t_name:
+ pvalue->type = gs_param_type_name_array;
+ return ref_param_read_string_array(plist, pkey,
+ &pvalue->value.na);
+ default:
+ break;
+ }
+ return gs_note_error(gs_error_typecheck);
+ case t_boolean:
+ pvalue->type = gs_param_type_bool;
+ pvalue->value.b = loc.pvalue->value.boolval;
+ return 0;
+ case t_dictionary:
+ code = ref_param_begin_read_collection(plist, pkey,
+ &pvalue->value.d, gs_param_collection_dict_any);
+ if (code < 0)
+ return code;
+ pvalue->type = gs_param_type_dict;
+
+ /* fixup new dict's type & int_keys field if contents have int keys */
+ {
+ gs_param_enumerator_t enumr;
+ gs_param_key_t key;
+ ref_type keytype;
+
+ param_init_enumerator(&enumr);
+ if (!(*((iparam_list *) plist)->enumerate)
+ ((iparam_list *) pvalue->value.d.list, &enumr, &key, &keytype)
+ && keytype == t_integer) {
+ ((dict_param_list *) pvalue->value.d.list)->int_keys = 1;
+ pvalue->type = gs_param_type_dict_int_keys;
+ }
+ }
+ return 0;
+ case t_integer:
+ pvalue->type = gs_param_type_long;
+ pvalue->value.l = loc.pvalue->value.intval;
+ return 0;
+ case t_name:
+ pvalue->type = gs_param_type_name;
+ return ref_param_read_string_value(plist->memory, &loc, &pvalue->value.n);
+ case t_null:
+ pvalue->type = gs_param_type_null;
+ return 0;
+ case t_real:
+ pvalue->value.f = loc.pvalue->value.realval;
+ pvalue->type = gs_param_type_float;
+ return 0;
+ case t_string:
+ case t_astruct:
+ pvalue->type = gs_param_type_string;
+ return ref_param_read_string_value(plist->memory, &loc, &pvalue->value.s);
+ default:
+ break;
+ }
+ return gs_note_error(gs_error_typecheck);
+}
+
+static int
+ref_param_read_get_policy(gs_param_list * plist, gs_param_name pkey)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ ref *pvalue;
+
+ if (!(r_has_type(&iplist->u.r.policies, t_dictionary) &&
+ dict_find_string(&iplist->u.r.policies, pkey, &pvalue) > 0 &&
+ r_has_type(pvalue, t_integer))
+ )
+ return gs_param_policy_ignore;
+ return (int)pvalue->value.intval;
+}
+static int
+ref_param_read_signal_error(gs_param_list * plist, gs_param_name pkey, int code)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ iparam_loc loc;
+
+ ref_param_read(iplist, pkey, &loc, -1); /* can't fail */
+ *loc.presult = code;
+ switch (ref_param_read_get_policy(plist, pkey)) {
+ case gs_param_policy_ignore:
+ return 0;
+ case gs_param_policy_consult_user:
+ return_error(gs_error_configurationerror);
+ default:
+ return code;
+ }
+}
+static int
+ref_param_read_commit(gs_param_list * plist)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ int i;
+ int ecode = 0;
+
+ if (!iplist->u.r.require_all)
+ return 0;
+ /* Check to make sure that all parameters were actually read. */
+ for (i = 0; i < iplist->count; ++i)
+ if (iplist->results[i] == 0)
+ iplist->results[i] = ecode = gs_note_error(gs_error_undefined);
+ return ecode;
+}
+static int
+ref_param_get_next_key(gs_param_list * plist, gs_param_enumerator_t * penum,
+ gs_param_key_t * key)
+{
+ ref_type keytype; /* result not needed here */
+ iparam_list *const pilist = (iparam_list *) plist;
+
+ return (*pilist->enumerate) (pilist, penum, key, &keytype);
+}
+
+/* ---------------- Internal routines ---------------- */
+
+/* Read a string value. */
+static int
+ref_param_read_string_value(gs_memory_t *mem, const iparam_loc * ploc, gs_param_string * pvalue)
+{
+ const ref *pref = ploc->pvalue;
+
+ switch (r_type(pref)) {
+ case t_name: {
+ ref nref;
+
+ name_string_ref(mem, pref, &nref);
+ pvalue->data = nref.value.const_bytes;
+ pvalue->size = r_size(&nref);
+ pvalue->persistent = true;
+ }
+ break;
+ case t_string:
+ iparam_check_read(*ploc);
+ pvalue->data = pref->value.const_bytes;
+ pvalue->size = r_size(pref);
+ pvalue->persistent = false;
+ break;
+ case t_astruct:
+ /* Note: technically, instead of the "mem" argument, we
+ should be using the plists's ref_memory. However, in a
+ simple call to .putdeviceparams, they are identical. */
+ iparam_check_read(*ploc);
+ if (gs_object_type(mem, pref->value.pstruct) != &st_bytes)
+ return iparam_note_error(*ploc, gs_error_typecheck);
+ pvalue->data = r_ptr(pref, byte);
+ pvalue->size = gs_object_size(mem, pref->value.pstruct);
+ pvalue->persistent = false;
+ break;
+ default:
+ return iparam_note_error(*ploc, gs_error_typecheck);
+ }
+ return 0;
+}
+
+/* Read an array (or packed array) parameter. */
+static int
+ref_param_read_array(iparam_list * plist, gs_param_name pkey, iparam_loc * ploc)
+{
+ int code = ref_param_read(plist, pkey, ploc, -1);
+
+ if (code != 0)
+ return code;
+ if (!r_is_array(ploc->pvalue))
+ return iparam_note_error(*ploc, gs_error_typecheck);
+ iparam_check_read(*ploc);
+ return 0;
+}
+
+/* Generic routine for reading a ref parameter. */
+static int
+ref_param_read(iparam_list * plist, gs_param_name pkey, iparam_loc * ploc,
+ int type)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ ref kref;
+ int code = ref_param_key(plist, pkey, &kref);
+
+ if (code < 0)
+ return code;
+ code = (*plist->u.r.read) (iplist, &kref, ploc);
+ if (code != 0)
+ return code;
+ if (type >= 0)
+ iparam_check_type(*ploc, type);
+ return 0;
+}
+
+/* ---------------- Implementations ---------------- */
+
+/* Implementation for putting parameters from an empty collection. */
+static int
+empty_param_read(iparam_list * plist, const ref * pkey, iparam_loc * ploc)
+{
+ return 1;
+}
+
+/* Initialize for reading parameters. */
+static int
+ref_param_read_init(iparam_list * plist, uint count, const ref * ppolicies,
+ bool require_all, gs_ref_memory_t *imem)
+{
+ gs_param_list_init((gs_param_list *)plist, &ref_read_procs,
+ (gs_memory_t *)imem);
+ plist->ref_memory = imem;
+ if (ppolicies == 0)
+ make_null(&plist->u.r.policies);
+ else
+ plist->u.r.policies = *ppolicies;
+ plist->u.r.require_all = require_all;
+ plist->count = count;
+ plist->results = (int *)
+ gs_alloc_byte_array(plist->memory, count, sizeof(int),
+ "ref_param_read_init");
+
+ if (plist->results == 0)
+ return_error(gs_error_VMerror);
+ memset(plist->results, 0, count * sizeof(int));
+
+ plist->int_keys = false;
+ return 0;
+}
+
+/* Implementation for putting parameters from an indexed array. */
+static int
+array_indexed_param_read(iparam_list * plist, const ref * pkey, iparam_loc * ploc)
+{
+ ref *const arr = &((dict_param_list *) plist)->dict;
+
+ check_type(*pkey, t_integer);
+ if (pkey->value.intval < 0 || pkey->value.intval >= r_size(arr))
+ return 1;
+ ploc->pvalue = arr->value.refs + pkey->value.intval;
+ ploc->presult = &plist->results[pkey->value.intval];
+ *ploc->presult = 1;
+ return 0;
+}
+int
+array_indexed_param_list_read(dict_param_list * plist, const ref * parray,
+ const ref * ppolicies, bool require_all,
+ gs_ref_memory_t *ref_memory)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ int code;
+
+ check_read_type(*parray, t_array);
+ plist->u.r.read = array_indexed_param_read;
+ plist->dict = *parray;
+ code = ref_param_read_init(iplist, r_size(parray), ppolicies,
+ require_all, ref_memory);
+ plist->int_keys = true;
+ return code;
+}
+
+/* Implementation for putting parameters from an array. */
+static int
+array_param_read(iparam_list * plist, const ref * pkey, iparam_loc * ploc)
+{
+ ref *bot = ((array_param_list *) plist)->bot;
+ ref *ptr = bot;
+ ref *top = ((array_param_list *) plist)->top;
+
+ for (; ptr < top; ptr += 2) {
+ if (r_has_type(ptr, t_name) && name_eq(ptr, pkey)) {
+ ploc->pvalue = ptr + 1;
+ ploc->presult = &plist->results[ptr - bot];
+ *ploc->presult = 1;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Implementation for enumerating parameters in an array */
+static int /* ret 0 ok, 1 if EOF, or -ve err */
+array_param_enumerate(iparam_list * plist, gs_param_enumerator_t * penum,
+ gs_param_key_t * key, ref_type * type)
+{
+ int index = penum->intval;
+ ref *bot = ((array_param_list *) plist)->bot;
+ ref *ptr = bot + index;
+ ref *top = ((array_param_list *) plist)->top;
+
+ for (; ptr < top; ptr += 2) {
+ index += 2;
+
+ if (r_has_type(ptr, t_name)) {
+ int code = ref_to_key(ptr, key, plist);
+
+ *type = r_type(ptr);
+ penum->intval = index;
+ return code;
+ }
+ }
+ return 1;
+}
+
+int
+array_param_list_read(array_param_list * plist, ref * bot, uint count,
+ const ref * ppolicies, bool require_all,
+ gs_ref_memory_t *imem)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+
+ if (count & 1)
+ return_error(gs_error_rangecheck);
+ plist->u.r.read = array_param_read;
+ plist->enumerate = array_param_enumerate;
+ plist->bot = bot;
+ plist->top = bot + count;
+ return ref_param_read_init(iplist, count, ppolicies, require_all, imem);
+}
+
+/* Implementation for putting parameters from a stack. */
+static int
+stack_param_read(iparam_list * plist, const ref * pkey, iparam_loc * ploc)
+{
+ stack_param_list *const splist = (stack_param_list *) plist;
+ ref_stack_t *pstack = splist->pstack;
+
+ /* This implementation is slow, but it probably doesn't matter. */
+ uint index = splist->skip + 1;
+ uint count = splist->count;
+
+ for (; count; count--, index += 2) {
+ const ref *p = ref_stack_index(pstack, index);
+
+ if (r_has_type(p, t_name) && name_eq(p, pkey)) {
+ ploc->pvalue = ref_stack_index(pstack, index - 1);
+ ploc->presult = &plist->results[count - 1];
+ *ploc->presult = 1;
+ return 0;
+ }
+ }
+ return 1;
+}
+int
+stack_param_list_read(stack_param_list * plist, ref_stack_t * pstack,
+ uint skip, const ref * ppolicies, bool require_all,
+ gs_ref_memory_t *imem)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ uint count = ref_stack_counttomark(pstack);
+
+ if (count == 0)
+ return_error(gs_error_unmatchedmark);
+ count -= skip + 1;
+ if (count & 1)
+ return_error(gs_error_rangecheck);
+ plist->u.r.read = stack_param_read;
+ plist->enumerate = stack_param_enumerate;
+ plist->pstack = pstack;
+ plist->skip = skip;
+ return ref_param_read_init(iplist, count >> 1, ppolicies, require_all, imem);
+}
+
+/* Implementation for putting parameters from a dictionary. */
+static int
+dict_param_read(iparam_list * plist, const ref * pkey, iparam_loc * ploc)
+{
+ ref const *spdict = &((dict_param_list *) plist)->dict;
+ int code = dict_find(spdict, pkey, &ploc->pvalue);
+
+ if (code != 1)
+ return 1;
+ ploc->presult =
+ &plist->results[dict_value_index(spdict, ploc->pvalue)];
+ *ploc->presult = 1;
+ return 0;
+}
+int
+dict_param_list_read(dict_param_list * plist, const ref * pdict,
+ const ref * ppolicies, bool require_all,
+ gs_ref_memory_t *imem)
+{
+ iparam_list *const iplist = (iparam_list *) plist;
+ uint count;
+
+ if (pdict == 0) {
+ plist->u.r.read = empty_param_read;
+ count = 0;
+ } else {
+ check_dict_read(*pdict);
+ plist->u.r.read = dict_param_read;
+ plist->dict = *pdict;
+ count = dict_max_index(pdict) + 1;
+ }
+ plist->enumerate = dict_param_enumerate;
+ return ref_param_read_init(iplist, count, ppolicies, require_all, imem);
+}
diff --git a/psi/iparam.h b/psi/iparam.h
new file mode 100644
index 000000000..7ec332983
--- /dev/null
+++ b/psi/iparam.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definitions and interface for interpreter parameter list implementations */
+/* Requires ialloc.h, istack.h */
+
+#ifndef iparam_INCLUDED
+# define iparam_INCLUDED
+
+#include "gsparam.h"
+
+/*
+ * This file defines the interface to iparam.c, which provides
+ * several implementations of the parameter dictionary interface
+ * defined in gsparam.h:
+ * - an implementation using dictionary objects;
+ * - an implementation using name/value pairs in an array;
+ * - an implementation using name/value pairs on a stack.
+ *
+ * When reading ('putting'), these implementations keep track of
+ * which parameters have been referenced and which have caused errors.
+ * The results array contains 0 for a parameter that has not been accessed,
+ * 1 for a parameter accessed without error, or <0 for an error.
+ */
+
+typedef struct iparam_loc_s {
+ ref *pvalue; /* (actually const) */
+ int *presult;
+} iparam_loc;
+
+#define iparam_list_common\
+ gs_param_list_common;\
+ gs_ref_memory_t *ref_memory; /* a properly typed copy of memory */\
+ union {\
+ struct { /* reading */\
+ int (*read)(iparam_list *, const ref *, iparam_loc *);\
+ ref policies; /* policy dictionary or null */\
+ bool require_all; /* if true, require all params to be known */\
+ } r;\
+ struct { /* writing */\
+ int (*write)(iparam_list *, const ref *, const ref *);\
+ ref wanted; /* desired keys or null */\
+ } w;\
+ } u;\
+ int (*enumerate)(iparam_list *, gs_param_enumerator_t *, gs_param_key_t *, ref_type *);\
+ int *results; /* (only used when reading, 0 when writing) */\
+ uint count; /* # of key/value pairs */\
+ bool int_keys /* if true, keys are integers */
+typedef struct iparam_list_s iparam_list;
+struct iparam_list_s {
+ iparam_list_common;
+};
+
+typedef struct dict_param_list_s {
+ iparam_list_common;
+ ref dict; /* dictionary or array */
+} dict_param_list;
+typedef struct array_param_list_s {
+ iparam_list_common;
+ ref *bot;
+ ref *top;
+} array_param_list;
+
+/* For stack lists, the bottom of the list is just above a mark. */
+typedef struct stack_param_list_s {
+ iparam_list_common;
+ ref_stack_t *pstack;
+ uint skip; /* # of top items to skip (reading only) */
+} stack_param_list;
+
+/* Procedural interface */
+/*
+ * For dict_param_list_read (only), the second parameter may be NULL,
+ * equivalent to an empty dictionary.
+ * The 3rd (const ref *) parameter is the policies dictionary when reading,
+ * or the key selection dictionary when writing; it may be NULL in either case.
+ * If the bool parameter is true, if there are any unqueried parameters,
+ * the commit procedure will return an gs_error_undefined error.
+ */
+int dict_param_list_read(dict_param_list *, const ref * /*t_dictionary */ ,
+ const ref *, bool, gs_ref_memory_t *);
+int dict_param_list_write(dict_param_list *, ref * /*t_dictionary */ ,
+ const ref *, gs_ref_memory_t *);
+int array_indexed_param_list_read(dict_param_list *, const ref * /*t_*array */ ,
+ const ref *, bool, gs_ref_memory_t *);
+int array_indexed_param_list_write(dict_param_list *, ref * /*t_*array */ ,
+ const ref *, gs_ref_memory_t *);
+int array_param_list_read(array_param_list *, ref *, uint,
+ const ref *, bool, gs_ref_memory_t *);
+int stack_param_list_read(stack_param_list *, ref_stack_t *, uint,
+ const ref *, bool, gs_ref_memory_t *);
+int stack_param_list_write(stack_param_list *, ref_stack_t *,
+ const ref *, gs_ref_memory_t *);
+
+#define iparam_list_release(plist)\
+ gs_free_object((plist)->memory, (plist)->results, "iparam_list_release")
+
+#endif /* iparam_INCLUDED */
diff --git a/psi/iparray.h b/psi/iparray.h
new file mode 100644
index 000000000..0f7d268f5
--- /dev/null
+++ b/psi/iparray.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Packed array constructor for Ghostscript */
+/* Requires ipacked.h, istack.h */
+
+#ifndef iparray_INCLUDED
+# define iparray_INCLUDED
+
+/*
+ * The only reason to put this in a separate header is that it requires
+ * both ipacked.h and istack.h; putting it in either one would make it
+ * depend on the other one. There must be a better way....
+ */
+
+/* Procedures implemented in zpacked.c */
+
+/* Make a packed array from the top N elements of a stack. */
+int make_packed_array(ref *, ref_stack_t *, uint, gs_dual_memory_t *,
+ client_name_t);
+
+#endif /* iparray_INCLUDED */
diff --git a/psi/ipcolor.h b/psi/ipcolor.h
new file mode 100644
index 000000000..da2ac0ed6
--- /dev/null
+++ b/psi/ipcolor.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter definitions for Pattern color */
+
+#ifndef ipcolor_INCLUDED
+# define ipcolor_INCLUDED
+
+/*
+ * Define the structure for remembering the pattern dictionary.
+ * This is the "client data" in the template.
+ * See zgstate.c (int_gstate) or zfont2.c (font_data) for information
+ * as to why we define this as a structure rather than a ref array.
+ */
+typedef struct int_pattern_s {
+ ref dict;
+} int_pattern;
+
+#define private_st_int_pattern() /* in zpcolor.c */\
+ gs_private_st_ref_struct(st_int_pattern, int_pattern, "int_pattern")
+
+/* Create an interpreter pattern structure. */
+int int_pattern_alloc(int_pattern **ppdata, const ref *op,
+ gs_memory_t *mem);
+
+#endif /* ipcolor_INCLUDED */
diff --git a/psi/iplugin.c b/psi/iplugin.c
new file mode 100644
index 000000000..bc3c209a6
--- /dev/null
+++ b/psi/iplugin.c
@@ -0,0 +1,99 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Plugin manager */
+
+#include "malloc_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gxalloc.h"
+#include "ierrors.h"
+#include "ialloc.h"
+#include "iplugin.h"
+#include "icstate.h"
+
+/* Plugin list is being build in raw memory pool,
+ because it is irrelevant to PS virtual memory.
+ At least it must live during alloc_restore_all, allowing
+ plugins to handle the finalization of objects they manage.
+*/
+
+extern_i_plugin_table();
+
+static void *i_plugin_mem_alloc(i_plugin_client_memory *mem, unsigned int nbytes, const char *cname)
+{ gs_memory_t *mem_raw = mem->client_data;
+ return mem_raw->procs.alloc_bytes_immovable(mem_raw, nbytes, cname);
+}
+
+static void i_plugin_mem_free(i_plugin_client_memory *mem, void *data, const char *cname)
+{ gs_memory_t *mem_raw = mem->client_data;
+ mem_raw->procs.free_object(mem_raw, data, cname);
+}
+
+void i_plugin_make_memory(i_plugin_client_memory *mem, gs_memory_t *mem_raw)
+{ mem->client_data = mem_raw;
+ mem->alloc = i_plugin_mem_alloc;
+ mem->free = i_plugin_mem_free;
+}
+
+int i_plugin_init(i_ctx_t *i_ctx_p)
+{ gs_memory_t *mem_raw = i_ctx_p->memory.current->non_gc_memory;
+ const i_plugin_instantiation_proc *p = i_plugin_table;
+ i_plugin_holder *h;
+ int code;
+ i_plugin_client_memory client_mem;
+ i_plugin_make_memory(&client_mem, mem_raw);
+ for (; *p != 0; p++) {
+ i_plugin_instance *instance = 0;
+ code = (*p)(&client_mem, &instance);
+ if (code != 0)
+ return code;
+ h = (i_plugin_holder *)gs_alloc_bytes_immovable(mem_raw, sizeof(i_plugin_holder), "plugin_holder");
+ if (h == 0)
+ return_error(gs_error_Fatal);
+ h->I = instance;
+ h->next = i_ctx_p->plugin_list;
+ i_ctx_p->plugin_list = h;
+ }
+ return 0;
+}
+
+void i_plugin_finit(gs_memory_t *mem_raw, i_plugin_holder *list)
+{ i_plugin_client_memory client_mem;
+ i_plugin_make_memory(&client_mem, mem_raw);
+ while (list != 0) {
+ i_plugin_holder *h = list;
+ list = h->next;
+ h->I->d->finit(h->I, &client_mem);
+ gs_free_object(mem_raw, h, "plugin_holder");
+ }
+}
+
+i_plugin_holder * i_plugin_get_list(i_ctx_t *i_ctx_p)
+{ return i_ctx_p->plugin_list;
+}
+
+i_plugin_instance *i_plugin_find(i_ctx_t *i_ctx_p, const char *type, const char *subtype)
+{ i_plugin_holder *h = i_ctx_p->plugin_list;
+ for (; h != 0; h = h->next) {
+ i_plugin_instance *I = h->I;
+ if (!strcmp(I->d->type, type) && !strcmp(I->d->subtype, subtype))
+ return I;
+ }
+ return NULL;
+}
+
+/* todo: define plugin enumerator by 'type' */
diff --git a/psi/iplugin.h b/psi/iplugin.h
new file mode 100644
index 000000000..c192ebbe6
--- /dev/null
+++ b/psi/iplugin.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Plugin manager */
+
+#ifndef iplugin_INCLUDED
+#define iplugin_INCLUDED
+
+#ifndef i_ctx_t_DEFINED
+#define i_ctx_t_DEFINED
+typedef struct gs_context_state_s i_ctx_t;
+#endif
+
+#ifndef gs_memory_DEFINED
+#define gs_memory_DEFINED
+typedef struct gs_memory_s gs_memory_t;
+#endif
+
+typedef struct i_plugin_holder_s i_plugin_holder;
+typedef struct i_plugin_instance_s i_plugin_instance;
+typedef struct i_plugin_descriptor_s i_plugin_descriptor;
+typedef struct i_plugin_client_memory_s i_plugin_client_memory;
+
+struct i_plugin_descriptor_s { /* RTTI for plugins */
+ const char *type; /* Plugin type, such as "FAPI" */
+ const char *subtype; /* Plugin type, such as "UFST" */
+ void (*finit)(i_plugin_instance *instance, i_plugin_client_memory *mem); /* Destructor & deallocator for the instance. */
+};
+
+struct i_plugin_instance_s { /* Base class for various plugins */
+ const i_plugin_descriptor *d;
+};
+
+struct i_plugin_holder_s { /* Forms list of plugins for plugin manager */
+ i_plugin_holder *next;
+ i_plugin_instance *I;
+};
+
+struct i_plugin_client_memory_s { /* must be copying */
+ void *client_data;
+ void *(*alloc)(i_plugin_client_memory *mem, unsigned int size, const char *id);
+ void (*free)(i_plugin_client_memory *mem, void *data, const char *cname);
+};
+
+#define plugin_instantiation_proc(proc)\
+ int proc(i_plugin_client_memory *client_mem, i_plugin_instance **instance)
+
+#define extern_i_plugin_table()\
+ typedef plugin_instantiation_proc((*i_plugin_instantiation_proc));\
+ extern const i_plugin_instantiation_proc i_plugin_table[]
+
+void i_plugin_make_memory(i_plugin_client_memory *mem, gs_memory_t *mem_raw);
+int i_plugin_init(i_ctx_t *);
+void i_plugin_finit(gs_memory_t *mem, i_plugin_holder *list);
+i_plugin_instance *i_plugin_find(i_ctx_t *i_ctx_p, const char *type, const char *subtype);
+i_plugin_holder * i_plugin_get_list(i_ctx_t *i_ctx_p);
+
+#endif /* iplugin_INCLUDED */
diff --git a/psi/ireclaim.c b/psi/ireclaim.c
new file mode 100644
index 000000000..d72801636
--- /dev/null
+++ b/psi/ireclaim.c
@@ -0,0 +1,189 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter's interface to garbage collector */
+#include "ghost.h"
+#include "ierrors.h"
+#include "gsstruct.h"
+#include "iastate.h"
+#include "icontext.h"
+#include "interp.h"
+#include "isave.h" /* for isstate.h */
+#include "isstate.h" /* for mem->saved->state */
+#include "dstack.h" /* for dsbot, dsp, dict_set_top */
+#include "estack.h" /* for esbot, esp */
+#include "ostack.h" /* for osbot, osp */
+#include "opdef.h" /* for defining init procedure */
+#include "store.h" /* for make_array */
+
+/* Import preparation and cleanup routines. */
+extern void ialloc_gc_prepare(gs_ref_memory_t *);
+
+/* Forward references */
+static void gs_vmreclaim(gs_dual_memory_t *, bool);
+
+/* Initialize the GC hook in the allocator. */
+static int ireclaim(gs_dual_memory_t *, int);
+static int
+ireclaim_init(i_ctx_t *i_ctx_p)
+{
+ gs_imemory.reclaim = ireclaim;
+ return 0;
+}
+
+/* GC hook called when the allocator signals a GC is needed (space = -1), */
+/* or for vmreclaim (space = the space to collect). */
+static int
+ireclaim(gs_dual_memory_t * dmem, int space)
+{
+ bool global;
+ gs_ref_memory_t *mem;
+
+ if (space < 0) {
+ /* Determine which allocator exceeded the limit. */
+ int i;
+
+ mem = dmem->space_global; /* just in case */
+ for (i = 0; i < countof(dmem->spaces_indexed); ++i) {
+ mem = dmem->spaces_indexed[i];
+ if (mem == 0)
+ continue;
+ if (mem->gc_status.requested > 0 ||
+ ((gs_ref_memory_t *)mem->stable_memory)->gc_status.requested > 0
+ )
+ break;
+ }
+ } else {
+ mem = dmem->spaces_indexed[space >> r_space_shift];
+ }
+ if_debug3m('0', (gs_memory_t *)mem, "[0]GC called, space=%d, requestor=%d, requested=%ld\n",
+ space, mem->space, (long)mem->gc_status.requested);
+ global = mem->space != avm_local;
+ /* Since dmem may move, reset the request now. */
+ ialloc_reset_requested(dmem);
+ gs_vmreclaim(dmem, global);
+ ialloc_set_limit(mem);
+ if (space < 0) {
+ gs_memory_status_t stats;
+ ulong allocated;
+
+ /* If the ammount still allocated after the GC is complete */
+ /* exceeds the max_vm setting, then return a VMerror */
+ gs_memory_status((gs_memory_t *) mem, &stats);
+ allocated = stats.allocated;
+ if (mem->stable_memory != (gs_memory_t *)mem) {
+ gs_memory_status(mem->stable_memory, &stats);
+ allocated += stats.allocated;
+ }
+ if (allocated >= mem->gc_status.max_vm) {
+ /* We can't satisfy this request within max_vm. */
+ return_error(gs_error_VMerror);
+ }
+ }
+ return 0;
+}
+
+/* Interpreter entry to garbage collector. */
+static void
+gs_vmreclaim(gs_dual_memory_t *dmem, bool global)
+{
+ /* HACK: we know the gs_dual_memory_t is embedded in a context state. */
+ i_ctx_t *i_ctx_p =
+ (i_ctx_t *)((char *)dmem - offset_of(i_ctx_t, memory));
+ gs_ref_memory_t *lmem = dmem->space_local;
+ int code = context_state_store(i_ctx_p);
+ gs_ref_memory_t *memories[5];
+ gs_ref_memory_t *mem;
+ int nmem, i;
+
+ memories[0] = dmem->space_system;
+ memories[1] = mem = dmem->space_global;
+ nmem = 2;
+ if (lmem != dmem->space_global)
+ memories[nmem++] = lmem;
+ for (i = nmem; --i >= 0;) {
+ mem = memories[i];
+ if (mem->stable_memory != (gs_memory_t *)mem)
+ memories[nmem++] = (gs_ref_memory_t *)mem->stable_memory;
+ }
+
+ /****** ABORT IF code < 0 ******/
+ for (i = nmem; --i >= 0; )
+ alloc_close_chunk(memories[i]);
+
+ /* Prune the file list so it won't retain potentially collectible */
+ /* files. */
+
+ for (i = (global ? i_vm_system : i_vm_local);
+ i < countof(dmem->spaces_indexed);
+ ++i
+ ) {
+ gs_ref_memory_t *mem = dmem->spaces_indexed[i];
+
+ if (mem == 0 || (i > 0 && mem == dmem->spaces_indexed[i - 1]))
+ continue;
+ if (mem->stable_memory != (gs_memory_t *)mem)
+ ialloc_gc_prepare((gs_ref_memory_t *)mem->stable_memory);
+ for (;; mem = &mem->saved->state) {
+ ialloc_gc_prepare(mem);
+ if (mem->saved == 0)
+ break;
+ }
+ }
+
+ /* Do the actual collection. */
+
+ {
+ void *ctxp = i_ctx_p;
+ gs_gc_root_t context_root;
+
+ gs_register_struct_root((gs_memory_t *)lmem, &context_root,
+ &ctxp, "i_ctx_p root");
+ GS_RECLAIM(&dmem->spaces, global);
+ gs_unregister_root((gs_memory_t *)lmem, &context_root, "i_ctx_p root");
+ i_ctx_p = ctxp;
+ dmem = &i_ctx_p->memory;
+ }
+
+ /* Update caches not handled by context_state_load. */
+
+ *systemdict = *ref_stack_index(&d_stack, ref_stack_count(&d_stack) - 1);
+
+ /* Update the cached value pointers in names. */
+
+ dicts_gc_cleanup();
+
+ /* Reopen the active chunks. */
+
+ for (i = 0; i < nmem; ++i)
+ alloc_open_chunk(memories[i]);
+
+ /* Reload the context state. Note this should be done
+ AFTER the chunks are reopened, since the context state
+ load could do allocations that must remain.
+ If it were done while the chunks were still closed,
+ we would lose those allocations when the chunks were opened */
+
+ code = context_state_load(i_ctx_p);
+
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def ireclaim_l2_op_defs[] =
+{
+ op_def_end(ireclaim_init)
+};
diff --git a/psi/iref.h b/psi/iref.h
new file mode 100644
index 000000000..ac0660a85
--- /dev/null
+++ b/psi/iref.h
@@ -0,0 +1,622 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Object structure and type definitions for Ghostscript */
+
+#ifndef iref_INCLUDED
+# define iref_INCLUDED
+
+#include "stdint_.h"
+
+/*
+ * Note: this file defines a large number of macros. Many of these are
+ * only used for internal purposes within this file, to help in the
+ * definition of other macros or data structures, and should never be
+ * referenced outside this file. It is intended that all such internal
+ * macros have names beginning with an underscore (_).
+ */
+
+/* The typedef for object references */
+#ifndef ref_DEFINED
+typedef struct ref_s ref;
+# define ref_DEFINED
+#endif
+
+/*
+ * Define the type for packed object references. This is opaque here:
+ * the details are in ipacked.h.
+ */
+typedef ushort ref_packed;
+
+#define log2_sizeof_ref_packed arch_log2_sizeof_short
+#define sizeof_ref_packed (1 << log2_sizeof_ref_packed)
+
+/* PS integer objects default to 64 bit, and the relevant operator
+ * C functions have code to allow the QL tests to pass when
+ * CPSI mode is "true".
+ * 32 bit PS integer objects can be configured at build time.
+ */
+#if !defined(PSINT32BIT) || PSINT32BIT == 0
+#define PSINT32BIT 0
+#else
+#define PSINT32BIT 1
+#endif
+
+#if PSINT32BIT==1
+typedef int ps_int;
+typedef uint ps_uint;
+typedef int ps_int32;
+typedef uint ps_uint32;
+#define MAX_PS_INT max_int
+#define MIN_PS_INT min_int
+#define MAX_PS_UINT max_uint
+#define MAX_PS_INT32 max_int
+#define MIN_PS_INT32 min_int
+#define MAX_PS_UINT32 max_uint
+
+#define PRIpsint PRId32
+#define PRIpsint32 PRId32
+#define PRIpsuint PRIu32
+#define PRIpsuint32 PRIu32
+
+#else
+typedef int64_t ps_int;
+typedef uint64_t ps_uint;
+typedef int ps_int32;
+typedef uint ps_uint32;
+#define MAX_PS_INT max_int64_t
+#define MIN_PS_INT min_int64_t
+#define MAX_PS_UINT max_uint64_t
+#define MAX_PS_INT32 max_int
+#define MIN_PS_INT32 min_int
+#define MAX_PS_UINT32 max_uint
+
+#define PRIpsint PRId64
+#define PRIpsint32 PRId32
+#define PRIpsuint PRIu64
+#define PRIpsuint32 PRIu32
+
+#endif
+
+/*
+ * Define the object types.
+ * The types marked with @ are composite and hence use the a_space field;
+ * objects of all other types must have a_space cleared.
+ * The types marked with ! behave differently in the interpreter
+ * depending on whether they are executable or literal.
+ * The types marked with + use the read/write/execute
+ * attributes; the rest only use the executable attribute.
+ * The types marked with # use the size field.
+ *
+ * Note that for the object types that support getinterval (array and
+ * string types), there is no way to tell whether a given reference
+ * designates an original object or a sub-interval. This is a deliberate
+ * design decision.
+ */
+typedef enum {
+
+/*
+ * Type 0 must be left unassigned, so that the type (and type_attrs)
+ * of a valid ref will never be zero. This speeds up simultaneous
+ * type/space checking in def (see dstack.h for details) and a similar
+ * check in ref_save (see store.h for details). We may as well use
+ * type 0 for t__invalid, which will never appear in a real ref.
+ *
+ * The "invalid" type is only used in a few special places: the guard
+ * entries at the bottom of the o-stack that detect stack underflow, and
+ * (perhaps eventually) the ref that the cached value pointer in names
+ * points to if the binding isn't known. It never appears on a stack or in
+ * a program-visible data structure.
+ */
+
+ t__invalid, /* (no value) */
+ t_boolean, /* value.boolval */
+ t_dictionary, /* @ + value.pdict */
+ t_file, /* @!+# value.pfile, uses size for id */
+
+/*
+ * The 4 array types must be kept together, and must start at
+ * a multiple of 4, for the sake of r_is_array and r_is_proc (see below).
+ */
+
+#define _REF_T_ARRAY_SPAN 4
+ t_array, /* @!+# value.refs */
+ /*
+ * The following are the two implementations of packed arrays.
+ * See ipacked.h for details.
+ */
+ t_mixedarray, /* @!+# value.packed */
+ t_shortarray, /* @!+# value.packed */
+ t_unused_array_, /* (an unused array type) */
+
+/*
+ * t_[a]struct is an "umbrella" for other types that are represented by
+ * allocated objects (structures). Objects of these types are composite
+ * and hence use the a_local attribute. The type name is taken from
+ * the allocator template for the structure. t_astruct objects use the
+ * access attributes; t_struct objects do not. Neither t_struct nor
+ * t_astruct objects use the size.
+ *
+ * t_struct is currently used for the following PostScript types:
+ * condition, lock.
+ * We could use it for fontIDs, except that they may have subclasses.
+ * Eventually it will also be used for the new 'device' type.
+ * t_astruct is currently used for the following PostScript types:
+ * gstate.
+ *
+ * The 2 structure types must be kept together, and must start at
+ * a multiple of 2, for the sake of r_has_stype (see below).
+ */
+
+#define _REF_T_STRUCT_SPAN 2
+ t_struct, /* @ value.pstruct */
+ t_astruct, /* @ + value.pstruct */
+
+/*
+ * We now continue with individual types.
+ */
+ t_fontID, /* @ value.pstruct */
+ t_integer, /* value.intval */
+ t_mark, /* (no value) */
+/*
+ * Name objects use the a_space field because they really are composite
+ * objects internally.
+ */
+ t_name, /* @! # value.pname, uses size for index */
+ t_null, /* ! # (value.opproc, uses size for mark */
+ /* type, on e-stack only) */
+/*
+ * Operator objects use the a_space field because they may actually be
+ * disguised procedures. (Real operators always have a_space = 0.)
+ */
+ t_operator, /* @! # value.opproc, uses size for index */
+ t_real, /* value.realval */
+ t_save, /* value.saveid, see isave.h for why */
+ /* this isn't a t_struct */
+ t_string, /* @!+# value.bytes */
+/*
+ * The following are extensions to the PostScript type set.
+ * If you add new types, be sure to edit:
+ * - REF_TYPE_*STRINGS* and REF_TYPE_PROPERTIES_DATA below;
+ * - the table in gs_init.ps (==only operator);
+ * - the printing routine in idebug.c;
+ * - the dispatches in igc.c, igcref.c, and interp.c;
+ * - obj_cvs, obj_cvp, and obj_eq in iutil.c;
+ * - restore_check_stack in zvmem.c.
+ */
+ t_device, /* @ + value.pdevice */
+ t_oparray, /* @! # value.const_refs, uses size */
+ /* for index */
+ t_next_index /*** first available index ***/
+} ref_type;
+
+/*
+ * The interpreter uses types starting at t_next_index for representing
+ * a few high-frequency operators.
+ * Since there are no operations specifically on operators,
+ * there is no need for any operators to check specifically for these
+ * types. The r_btype macro takes care of the conversion when required.
+ */
+/*extern const int tx_next_index; *//* in interp.c */
+/*
+ * Define a table giving properties of types, similar to the table used
+ * by the isxxx functions (macros) in <ctype.h>.
+ */
+#define _REF_TYPE_USES_ACCESS 1 /* type uses w/r/x attributes ("+" above) */
+#define _REF_TYPE_USES_SIZE 2 /* type uses size attribute ("#" above) */
+#define _REF_TYPE_IS_NULL 4 /* type is t_null */
+#define _REF_TYPE_IS_DICTIONARY 8 /* type is t_dictionary */
+extern const byte ref_type_properties[1 << 6]; /* r_type_bits */
+
+#define REF_TYPE_PROPERTIES_DATA\
+ 0, /* t__invalid */\
+ 0, /* t_boolean */\
+ _REF_TYPE_USES_ACCESS | _REF_TYPE_IS_DICTIONARY, /* t_dictionary */\
+ _REF_TYPE_USES_ACCESS | _REF_TYPE_USES_SIZE, /* t_file */\
+ _REF_TYPE_USES_ACCESS | _REF_TYPE_USES_SIZE, /* t_array */\
+ _REF_TYPE_USES_ACCESS | _REF_TYPE_USES_SIZE, /* t_mixedarray */\
+ _REF_TYPE_USES_ACCESS | _REF_TYPE_USES_SIZE, /* t_shortarray */\
+ _REF_TYPE_USES_ACCESS | _REF_TYPE_USES_SIZE, /* (unused array type) */\
+ 0, /* t_struct */\
+ _REF_TYPE_USES_ACCESS, /* t_astruct */\
+ 0, /* t_fontID */\
+ 0, /* t_integer */\
+ 0, /* t_mark */\
+ _REF_TYPE_USES_SIZE, /* t_name */\
+ _REF_TYPE_IS_NULL, /* t_null, uses size only on e-stack */\
+ _REF_TYPE_USES_SIZE, /* t_operator */\
+ 0, /* t_real */\
+ 0, /* t_save */\
+ _REF_TYPE_USES_ACCESS | _REF_TYPE_USES_SIZE, /* t_string */\
+ _REF_TYPE_USES_ACCESS, /* t_device */\
+ _REF_TYPE_USES_SIZE, /* t_oparray */\
+ /*\
+ * The remaining types are the extended pseudo-types used by the\
+ * interpreter for operators. We need to fill up the table.\
+ */\
+ _REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE, /*24*/\
+ _REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE, /*28*/\
+ _REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE, /*32*/\
+ _REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE, /*36*/\
+ _REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE, /*40*/\
+ _REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE, /*44*/\
+ _REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE, /*48*/\
+ _REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE, /*52*/\
+ _REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE, /*56*/\
+ _REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE, /*60*/\
+ _REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE,_REF_TYPE_USES_SIZE /*64 */
+#define _REF_TYPE_HAS(rtype,props)\
+ ((ref_type_properties[rtype] & (props)) != 0)
+#define ref_type_uses_access(rtype)\
+ _REF_TYPE_HAS(rtype, _REF_TYPE_USES_ACCESS)
+#define ref_type_uses_size(rtype)\
+ _REF_TYPE_HAS(rtype, _REF_TYPE_USES_SIZE)
+#define ref_type_uses_size_or_null(rtype)\
+ _REF_TYPE_HAS(rtype, _REF_TYPE_USES_SIZE | _REF_TYPE_IS_NULL)
+/*
+ * Define the type names for debugging printout.
+ * All names must be the same length, so that columns will line up.
+ */
+#define REF_TYPE_DEBUG_PRINT_STRINGS\
+ "INVL","bool","dict","file",\
+ "arry","mpry","spry","u?ry",\
+ "STRC","ASTR",\
+ "font","int ","mark","name","null",\
+ "oper","real","save","str ",\
+ "devc","opry"
+/*
+ * Define the type names for the type operator.
+ */
+#define REF_TYPE_NAME_STRINGS\
+ 0,"booleantype","dicttype","filetype",\
+ "arraytype","packedarraytype","packedarraytype","arraytype",\
+ 0,0,\
+ "fonttype","integertype","marktype","nametype","nulltype",\
+ "operatortype","realtype","savetype","stringtype",\
+ "devicetype","operatortype"
+/*
+ * Define the type names for obj_cvp (the == operator). We only need these
+ * for types that obj_cvp and obj_cvs don't handle specially.
+ */
+#define REF_TYPE_PRINT_STRINGS\
+ 0,0,"-dict-","-file-",\
+ "-array-","-packedarray-","-packedarray-","-array-",\
+ 0,0,\
+ "-fontID-",0,"-mark-",0,0,\
+ 0,0,"-save-","-string-",\
+ "-device-",0
+
+/*
+ * The following factors affect the encoding of attributes:
+ *
+ * - The packed array format requires the high-order bits of the
+ * type/attributes field to be 0. (see packed.h)
+ *
+ * - The interpreter wants the type, executable bit, and execute
+ * permission to be adjacent, and in that order from high to low.
+ *
+ * - Type testing is most efficient if the type is in a byte by itself.
+ *
+ * The layout given below results in the most efficient code overall.
+ */
+
+/*
+ * A few of the fields of a ref are associated with the *location*;
+ * most are associated with the ref that is *stored* in that location.
+ * When a ref is copied from one location to another, the former are
+ * preserved in the destination, the latter are copied.
+ */
+/*
+ * The following are the attributes associated with the location:
+ */
+#define l_mark 1 /* mark for garbage collector */
+#define l_new 2 /* stored into since last save */
+/*
+ * The following are attributes associated with the ref itself (the
+ * contents of the location). These are visible to PostScript code.
+ */
+/*
+ * Reserve bits for VM space information (defined in ivmspace.h). Note that
+ * These bits refer to the VM space of the pointer in the ref, if any, not
+ * the location in which the ref itself is stored. For scalars, these bits
+ * are always zero.
+ */
+#define r_space_bits 2
+#define r_space_shift 2
+/*
+ * Define the protection attributes. Only 4 combinations are legal:
+ * 0, execute, execute + read, execute + read + write. Note that some
+ * refs (such as scalars) do not use these: in such refs, they are
+ * always zero.
+ */
+#define a_write 0x10
+#define a_read 0x20
+#define a_execute 0x40
+#define a_readonly (a_read + a_execute)
+#define a_all (a_write + a_read+a_execute)
+/*
+ * Define the executable attribute. All refs use this.
+ */
+#define a_executable 0x80
+/*
+ * Define the bits used for the ref type. See ipacked.h for more
+ * information about the possible values of the type byte.
+ */
+#define r_type_shift 8
+#define r_type_bits 6
+
+/*
+ * Define the attribute names for debugging printout.
+ * Each entry has the form <mask, value, character>.
+ */
+typedef struct ref_attr_print_mask_s {
+ ushort mask;
+ ushort value;
+ char print;
+} ref_attr_print_mask_t;
+
+#define _REF_ATTR_PRINT_FLAG(m,c)\
+ {m,m,c},{m,0,'-'}
+#define _REF_ATTR_PRINT_SPACE(v,c)\
+ {((1<<r_space_bits)-1)<<r_space_shift,v,c}
+#define REF_ATTR_PRINT_MASKS\
+ _REF_ATTR_PRINT_FLAG(l_mark,'m'),\
+ _REF_ATTR_PRINT_FLAG(l_new,'n'),\
+ _REF_ATTR_PRINT_SPACE(avm_foreign,'F'),\
+ _REF_ATTR_PRINT_SPACE(avm_system,'S'),\
+ _REF_ATTR_PRINT_SPACE(avm_global,'G'),\
+ _REF_ATTR_PRINT_SPACE(avm_local,'L'),\
+ _REF_ATTR_PRINT_FLAG(a_write,'w'),\
+ _REF_ATTR_PRINT_FLAG(a_read,'r'),\
+ _REF_ATTR_PRINT_FLAG(a_execute,'x'),\
+ _REF_ATTR_PRINT_FLAG(a_executable,'e'),\
+ _REF_ATTR_PRINT_FLAG(0x4000,'?'),\
+ _REF_ATTR_PRINT_FLAG(0x8000,'?')
+
+/* Abstract types */
+typedef struct dict_s dict;
+typedef struct name_s name;
+
+#ifndef stream_DEFINED
+# define stream_DEFINED
+typedef struct stream_s stream;
+#endif
+#ifndef gx_device_DEFINED
+# define gx_device_DEFINED
+typedef struct gx_device_s gx_device;
+#endif
+#ifndef obj_header_DEFINED
+# define obj_header_DEFINED
+typedef struct obj_header_s obj_header_t;
+#endif
+
+/*
+ * Define the argument type for operator procedures. Note that the
+ * argument name is not arbitrary: it is used in access macros, so all
+ * operator procedures must use it.
+ */
+#ifndef i_ctx_t_DEFINED
+# define i_ctx_t_DEFINED
+typedef struct gs_context_state_s i_ctx_t;
+#endif
+typedef int (*op_proc_t)(i_ctx_t *i_ctx_p);
+/* real_opproc is a holdover.... */
+#define real_opproc(pref) ((pref)->value.opproc)
+
+/* Object reference */
+/*
+ * Note that because of the way packed arrays are represented,
+ * the type_attrs member must be the first one in the ref structure.
+ */
+ /* The _pad entry ensures that the struct ref_s is the required size
+ * (16 bytes) even with compilers that tightly pack structures - the
+ * size requirement is imposed by the garbarge collector
+ */
+struct tas_s {
+/* type_attrs is a single element for fast dispatching in the interpreter */
+ ushort type_attrs;
+ ushort _pad;
+ uint32_t rsize;
+};
+struct ref_s {
+
+ struct tas_s tas;
+
+ union v { /* name the union to keep gdb happy */
+ ps_int intval;
+ ushort boolval;
+ float realval;
+ ulong saveid;
+ byte *bytes;
+ const byte *const_bytes;
+ ref *refs;
+ const ref *const_refs;
+ name *pname;
+ const name *const_pname;
+ dict *pdict;
+ const dict *const_pdict;
+ /*
+ * packed is the normal variant for referring to packed arrays,
+ * but we need a writable variant for memory management and for
+ * storing into packed dictionary key arrays.
+ */
+ const ref_packed *packed;
+ ref_packed *writable_packed;
+ op_proc_t opproc;
+ struct stream_s *pfile;
+ struct gx_device_s *pdevice;
+ obj_header_t *pstruct;
+ uint64_t dummy; /* force 16-byte ref on 32-bit platforms */
+ } value;
+};
+
+/* ---------------- Private ref macros ---------------- */
+
+/*
+ * Test whether a ref has a type within a given span, and also has all of a
+ * given set of attributes.
+ */
+#define _REF_HAS_MASKED_TYPE_ATTRS(rp,typ,tspan,mask)\
+ (((rp)->tas.type_attrs &\
+ ((((1 << r_type_bits) - (tspan)) << r_type_shift) + (mask))) ==\
+ (((typ) << r_type_shift) + (mask)))
+
+/* ---------------- Public ref macros ---------------- */
+
+/*
+ * All of these macros take an argument "rp" which is a pointer to a ref.
+ * Unless otherwise specified, they only apply to full-size (not packed)
+ * refs.
+ */
+
+/*
+ * Read, set, increment, and decrement the size field of a ref.
+ */
+#define r_size(rp) ((rp)->tas.rsize)
+#define r_inc_size(rp,inc) ((rp)->tas.rsize += (inc))
+#define r_dec_size(rp,dec) ((rp)->tas.rsize -= (dec))
+#define r_set_size(rp,siz) ((rp)->tas.rsize = (siz))
+
+/*
+ * Get the type of a ref; test whether a ref has a given type. The
+ * difference between r_type and r_btype is that for refs with types greater
+ * than or equal to t_next_index, r_type returns that type, but r_btype
+ * returns t_operator (since those types just encode specific operators for
+ * faster dispatch in the interpreter -- see interp.h for more information).
+ */
+#if r_type_shift == 8
+# if arch_is_big_endian
+# define r_type(rp) (((const byte *)&((rp)->tas.type_attrs))[sizeof(ushort)-2])
+# else
+# define r_type(rp) (((const byte *)&((rp)->tas.type_attrs))[1])
+# endif
+# define r_has_type(rp,typ) (r_type(rp) == (typ))
+#else
+# define r_type(rp) ((rp)->tas.type_attrs >> r_type_shift)
+# define r_has_type(rp,typ) r_has_type_attrs(rp,typ,0) /* see below */
+#endif
+#define r_btype(rp)\
+ ((rp)->tas.type_attrs >= (t_next_index << r_type_shift) ?\
+ t_operator : r_type(rp))
+
+/*
+ * Test whether a ref is an array, or a procedure, or a(n) [a]struct.
+ */
+#define r_is_array(rp)\
+ _REF_HAS_MASKED_TYPE_ATTRS(rp,t_array,_REF_T_ARRAY_SPAN,0)
+#define r_is_proc(rp)\
+ _REF_HAS_MASKED_TYPE_ATTRS(rp,t_array,_REF_T_ARRAY_SPAN,a_execute+a_executable)
+#define r_is_struct(rp)\
+ _REF_HAS_MASKED_TYPE_ATTRS(rp,t_struct,_REF_T_STRUCT_SPAN,0)
+
+/*
+ * Test whether a ref is a struct or astruct with a specific structure type
+ * (GC descriptor).
+ */
+#define r_has_stype(rp,mem,styp)\
+ (r_is_struct(rp) && gs_object_type(mem, (rp)->value.pstruct) == &styp)
+
+/*
+ * Set the type of a ref. This is only used in a few very special places.
+ * Normally the type of a ref is set when the ref is created (by one of
+ * the make_xxx macros in store.h) and never changed.
+ */
+#define r_set_type(rp,typ) ((rp)->tas.type_attrs = (typ) << r_type_shift)
+
+/*
+ * Get, test, or set the type and attributes of a ref together as a single
+ * value. This too is only used in a few special places.
+ */
+#define r_type_attrs(rp) ((rp)->tas.type_attrs) /* reading only */
+#define r_has_type_attrs(rp,typ,mask)\
+ _REF_HAS_MASKED_TYPE_ATTRS(rp,typ,1,mask)
+#define r_set_type_attrs(rp,typ,mask)\
+ ((rp)->tas.type_attrs = ((typ) << r_type_shift) + (mask))
+
+/*
+ * Get the combined type, a_executable, and a_execute bits of a ref,
+ * for fast dispatching in the interpreter.
+ */
+/*
+ * The r_type_xe macro is used in (and only in) the main interpreter loop,
+ * where its rp operand may be a ref_packed, not necessarily aligned as
+ * strictly as a full-size ref. The DEC C compiler, and possibly others,
+ * may compile code assuming that rp is ref-aligned. Therefore, we
+ * explicitly cast the pointer to a less-strictly-aligned type.
+ * In order to convince the compiler, we have to do the cast before
+ * indexing into the structure.
+ */
+#define _REF_TYPE_XE_SHIFT (r_type_shift - 2)
+#define _REF_TAS_TYPE_XE(tas) ((tas) >> _REF_TYPE_XE_SHIFT)
+#define r_type_xe(rp)\
+ _REF_TAS_TYPE_XE(((const ushort *)(rp))[offset_of(ref, tas.type_attrs) / sizeof(ushort)])
+#define type_xe_value(typ,xe) _REF_TAS_TYPE_XE(((typ) << r_type_shift) + (xe))
+
+/*
+ * Test whether a ref has a given attribute, or all the given attributes.
+ */
+#define r_has_attr(rp,mask1) /* optimize 1-bit case */\
+ (r_type_attrs(rp) & (mask1))
+#define r_has_attrs(rp,mask) !(~r_type_attrs(rp) & (mask))
+
+/*
+ * Test whether those attributes of a ref selected by a mask have a
+ * given value.
+ */
+#define r_has_masked_attrs(rp,attrs,mask)\
+ ((r_type_attrs(rp) & (mask)) == (attrs))
+
+/*
+ * Set, clear, store, or copy the attributes of a ref. These are rarely
+ * needed. Note that, unfortunately, the attrs and mask parameters of
+ * r_store_attrs are reversed from r_has_masked_attrs.
+ */
+#define r_set_attrs(rp,mask) ((rp)->tas.type_attrs |= (mask))
+#define r_clear_attrs(rp,mask) ((rp)->tas.type_attrs &= ~(mask))
+#define r_store_attrs(rp,mask,attrs)\
+ ((rp)->tas.type_attrs = ((rp)->tas.type_attrs & ~(mask)) | (attrs))
+#define r_copy_attrs(rp,mask,sp)\
+ r_store_attrs(rp,mask,(sp)->tas.type_attrs & (mask))
+
+/*
+ * Get or set the pointer field of a struct or astruct ref. The typ
+ * argument of r_ptr is the (C) type of the structure.
+ */
+#define r_ptr(rp,typ) ((typ *)((rp)->value.pstruct))
+#define r_set_ptr(rp,ptr) ((rp)->value.pstruct = (obj_header_t *)(ptr))
+
+/* ---------------- End of ref macros ---------------- */
+
+/* Define data for initializing an empty array or string. */
+#define empty_ref_data(type, attrs)\
+ { /*tas*/ { /*type_attrs*/ ((type) << r_type_shift) | (attrs),\
+ /*rsize*/ 0 } }
+
+/* Define the size of a ref. */
+#define arch_sizeof_ref sizeof(ref)
+/* Define the required alignment for refs. */
+/* We assume all alignment values are powers of 2. */
+#define ARCH_ALIGN_REF_MOD\
+ (((ARCH_ALIGN_LONG_MOD - 1) | (ARCH_ALIGN_FLOAT_MOD - 1) |\
+ (ARCH_ALIGN_PTR_MOD - 1)) + 1)
+
+/* Select reasonable values for PDF interpreter */
+/* The maximum array size cannot exceed max_uint/arch_sizeof_ref */
+/* because the allocator cannot allocate a block larger than max_uint. */
+#define max_array_size (16*1024*1024)
+#define max_string_size (16*1024*1024)
+
+#endif /* iref_INCLUDED */
diff --git a/psi/isave.c b/psi/isave.c
new file mode 100644
index 000000000..701db620f
--- /dev/null
+++ b/psi/isave.c
@@ -0,0 +1,1422 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Save/restore manager for Ghostscript interpreter */
+#include "ghost.h"
+#include "memory_.h"
+#include "ierrors.h"
+#include "gsexit.h"
+#include "gsstruct.h"
+#include "stream.h" /* for linking for forgetsave */
+#include "iastate.h"
+#include "inamedef.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "isave.h"
+#include "isstate.h"
+#include "store.h" /* for ref_assign */
+#include "ivmspace.h"
+#include "igc.h"
+#include "gsutil.h" /* gs_next_ids prototype */
+
+/* Structure descriptor */
+private_st_alloc_save();
+
+/* Define the maximum amount of data we are willing to scan repeatedly -- */
+/* see below for details. */
+static const long max_repeated_scan = 100000;
+
+/* Define the minimum space for creating an inner chunk. */
+/* Must be at least sizeof(chunk_head_t). */
+static const long min_inner_chunk_space = sizeof(chunk_head_t) + 500;
+
+/*
+ * The logic for saving and restoring the state is complex.
+ * Both the changes to individual objects, and the overall state
+ * of the memory manager, must be saved and restored.
+ */
+
+/*
+ * To save the state of the memory manager:
+ * Save the state of the current chunk in which we are allocating.
+ * Shrink all chunks to their inner unallocated region.
+ * Save and reset the free block chains.
+ * By doing this, we guarantee that no object older than the save
+ * can be freed.
+ *
+ * To restore the state of the memory manager:
+ * Free all chunks newer than the save, and the descriptors for
+ * the inner chunks created by the save.
+ * Make current the chunk that was current at the time of the save.
+ * Restore the state of the current chunk.
+ *
+ * In addition to save ("start transaction") and restore ("abort transaction"),
+ * we support forgetting a save ("commit transation"). To forget a save:
+ * Reassign to the next outer save all chunks newer than the save.
+ * Free the descriptors for the inners chunk, updating their outer
+ * chunks to reflect additional allocations in the inner chunks.
+ * Concatenate the free block chains with those of the outer save.
+ */
+
+/*
+ * For saving changes to individual objects, we add an "attribute" bit
+ * (l_new) that logically belongs to the slot where the ref is stored,
+ * not to the ref itself. The bit means "the contents of this slot
+ * have been changed, or the slot was allocated, since the last save."
+ * To keep track of changes since the save, we associate a chain of
+ * <slot, old_contents> pairs that remembers the old contents of slots.
+ *
+ * When creating an object, if the save level is non-zero:
+ * Set l_new in all slots.
+ *
+ * When storing into a slot, if the save level is non-zero:
+ * If l_new isn't set, save the address and contents of the slot
+ * on the current contents chain.
+ * Set l_new after storing the new value.
+ *
+ * To do a save:
+ * If the save level is non-zero:
+ * Reset l_new in all slots on the contents chain, and in all
+ * objects created since the previous save.
+ * Push the head of the contents chain, and reset the chain to empty.
+ *
+ * To do a restore:
+ * Check all the stacks to make sure they don't contain references
+ * to objects created since the save.
+ * Restore all the slots on the contents chain.
+ * Pop the contents chain head.
+ * If the save level is now non-zero:
+ * Scan the newly restored contents chain, and set l_new in all
+ * the slots it references.
+ * Scan all objects created since the previous save, and set
+ * l_new in all the slots of each object.
+ *
+ * To forget a save:
+ * If the save level is greater than 1:
+ * Set l_new as for a restore, per the next outer save.
+ * Concatenate the next outer contents chain to the end of
+ * the current one.
+ * If the save level is 1:
+ * Reset l_new as for a save.
+ * Free the contents chain.
+ */
+
+/*
+ * A consequence of the foregoing algorithms is that the cost of a save is
+ * proportional to the total amount of data allocated since the previous
+ * save. If a PostScript program reads in a large amount of setup code and
+ * then uses save/restore heavily, each save/restore will be expensive. To
+ * mitigate this, we check to see how much data we have scanned at this save
+ * level: if it is large, we do a second, invisible save. This greatly
+ * reduces the cost of inner saves, at the expense of possibly saving some
+ * changes twice that otherwise would only have to be saved once.
+ */
+
+/*
+ * The presence of global and local VM complicates the situation further.
+ * There is a separate save chain and contents chain for each VM space.
+ * When multiple contexts are fully implemented, save and restore will have
+ * the following effects, according to the privacy status of the current
+ * context's global and local VM:
+ * Private global, private local:
+ * The outermost save saves both global and local VM;
+ * otherwise, save only saves local VM.
+ * Shared global, private local:
+ * Save only saves local VM.
+ * Shared global, shared local:
+ * Save only saves local VM, and suspends all other contexts
+ * sharing the same local VM until the matching restore.
+ * Since we do not currently implement multiple contexts, only the first
+ * case is relevant.
+ *
+ * Note that when saving the contents of a slot, the choice of chain
+ * is determined by the VM space in which the slot is allocated,
+ * not by the current allocation mode.
+ */
+
+/* Tracing printout */
+static void
+print_save(const char *str, uint spacen, const alloc_save_t *sav)
+{
+ if_debug5('u', "[u]%s space %u 0x%lx: cdata = 0x%lx, id = %lu\n",\
+ str, spacen, (ulong)sav, (ulong)sav->client_data, (ulong)sav->id);
+}
+
+/* A link to igcref.c . */
+ptr_proc_reloc(igc_reloc_ref_ptr_nocheck, ref_packed);
+
+static
+CLEAR_MARKS_PROC(change_clear_marks)
+{
+ alloc_change_t *const ptr = (alloc_change_t *)vptr;
+
+ if (r_is_packed(&ptr->contents))
+ r_clear_pmark((ref_packed *) & ptr->contents);
+ else
+ r_clear_attrs(&ptr->contents, l_mark);
+}
+static
+ENUM_PTRS_WITH(change_enum_ptrs, alloc_change_t *ptr) return 0;
+ENUM_PTR(0, alloc_change_t, next);
+case 1:
+ if (ptr->offset >= 0)
+ ENUM_RETURN((byte *) ptr->where - ptr->offset);
+ else
+ if (ptr->offset != AC_OFFSET_ALLOCATED)
+ ENUM_RETURN_REF(ptr->where);
+ else {
+ /* Don't enumerate ptr->where, because it
+ needs a special processing with
+ alloc_save__filter_changes. */
+ ENUM_RETURN(0);
+ }
+case 2:
+ ENUM_RETURN_REF(&ptr->contents);
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(change_reloc_ptrs, alloc_change_t *ptr)
+{
+ RELOC_VAR(ptr->next);
+ switch (ptr->offset) {
+ case AC_OFFSET_STATIC:
+ break;
+ case AC_OFFSET_REF:
+ RELOC_REF_PTR_VAR(ptr->where);
+ break;
+ case AC_OFFSET_ALLOCATED:
+ /* We know that ptr->where may point to an unmarked object
+ because change_enum_ptrs skipped it,
+ and we know it always points to same space
+ because we took a special care when calling alloc_save_change_alloc.
+ Therefore we must skip the check for the mark,
+ which would happen if we call the regular relocation function
+ igc_reloc_ref_ptr from RELOC_REF_PTR_VAR.
+ Calling igc_reloc_ref_ptr_nocheck instead. */
+ { /* A sanity check. */
+ obj_header_t *pre = (obj_header_t *)ptr->where - 1, *pre1 = 0;
+
+ if (pre->o_type != &st_refs)
+ pre1->o_type = 0; /* issue a segfault. */
+ }
+ if (ptr->where != 0 && !gcst->relocating_untraced)
+ ptr->where = igc_reloc_ref_ptr_nocheck(ptr->where, gcst);
+ break;
+ default:
+ {
+ byte *obj = (byte *) ptr->where - ptr->offset;
+
+ RELOC_VAR(obj);
+ ptr->where = (ref_packed *) (obj + ptr->offset);
+ }
+ break;
+ }
+ if (r_is_packed(&ptr->contents))
+ r_clear_pmark((ref_packed *) & ptr->contents);
+ else {
+ RELOC_REF_VAR(ptr->contents);
+ r_clear_attrs(&ptr->contents, l_mark);
+ }
+}
+RELOC_PTRS_END
+gs_private_st_complex_only(st_alloc_change, alloc_change_t, "alloc_change",
+ change_clear_marks, change_enum_ptrs, change_reloc_ptrs, 0);
+
+/* Debugging printout */
+#ifdef DEBUG
+static void
+alloc_save_print(const gs_memory_t *mem, alloc_change_t * cp, bool print_current)
+{
+ dmprintf2(mem, " 0x%lx: 0x%lx: ", (ulong) cp, (ulong) cp->where);
+ if (r_is_packed(&cp->contents)) {
+ if (print_current)
+ dmprintf2(mem, "saved=%x cur=%x\n", *(ref_packed *) & cp->contents,
+ *cp->where);
+ else
+ dmprintf1(mem, "%x\n", *(ref_packed *) & cp->contents);
+ } else {
+ if (print_current)
+ dmprintf6(mem, "saved=%x %x %lx cur=%x %x %lx\n",
+ r_type_attrs(&cp->contents), r_size(&cp->contents),
+ (ulong) cp->contents.value.intval,
+ r_type_attrs((ref *) cp->where),
+ r_size((ref *) cp->where),
+ (ulong) ((ref *) cp->where)->value.intval);
+ else
+ dmprintf3(mem, "%x %x %lx\n",
+ r_type_attrs(&cp->contents), r_size(&cp->contents),
+ (ulong) cp->contents.value.intval);
+ }
+}
+#endif
+
+/* Forward references */
+static int restore_resources(alloc_save_t *, gs_ref_memory_t *);
+static void restore_free(gs_ref_memory_t *);
+static int save_set_new(gs_ref_memory_t * mem, bool to_new, bool set_limit, ulong *pscanned);
+static int save_set_new_changes(gs_ref_memory_t *, bool, bool);
+static bool check_l_mark(void *obj);
+
+/* Initialize the save/restore machinery. */
+void
+alloc_save_init(gs_dual_memory_t * dmem)
+{
+ alloc_set_not_in_save(dmem);
+}
+
+/* Record that we are in a save. */
+static void
+alloc_set_masks(gs_dual_memory_t *dmem, uint new_mask, uint test_mask)
+{
+ int i;
+ gs_ref_memory_t *mem;
+
+ dmem->new_mask = new_mask;
+ dmem->test_mask = test_mask;
+ for (i = 0; i < countof(dmem->spaces.memories.indexed); ++i)
+ if ((mem = dmem->spaces.memories.indexed[i]) != 0) {
+ mem->new_mask = new_mask, mem->test_mask = test_mask;
+ if (mem->stable_memory != (gs_memory_t *)mem) {
+ mem = (gs_ref_memory_t *)mem->stable_memory;
+ mem->new_mask = new_mask, mem->test_mask = test_mask;
+ }
+ }
+}
+void
+alloc_set_in_save(gs_dual_memory_t *dmem)
+{
+ alloc_set_masks(dmem, l_new, l_new);
+}
+
+/* Record that we are not in a save. */
+void
+alloc_set_not_in_save(gs_dual_memory_t *dmem)
+{
+ alloc_set_masks(dmem, 0, ~0);
+}
+
+/* Save the state. */
+static alloc_save_t *alloc_save_space(gs_ref_memory_t *mem,
+ gs_dual_memory_t *dmem,
+ ulong sid);
+static void
+alloc_free_save(gs_ref_memory_t *mem, alloc_save_t *save, const char *scn)
+{
+ gs_ref_memory_t save_mem;
+ save_mem = mem->saved->state;
+ gs_free_object((gs_memory_t *)mem, save, scn);
+ /* Free any inner chunk structures. This is the easiest way to do it. */
+ restore_free(mem);
+ /* Restore the 'saved' state - this pulls our object off the linked
+ * list of states. Without this we hit a SEGV in the gc later. */
+ *mem = save_mem;
+}
+int
+alloc_save_state(gs_dual_memory_t * dmem, void *cdata, ulong *psid)
+{
+ gs_ref_memory_t *lmem = dmem->space_local;
+ gs_ref_memory_t *gmem = dmem->space_global;
+ ulong sid = gs_next_ids((const gs_memory_t *)lmem->stable_memory, 2);
+ bool global =
+ lmem->save_level == 0 && gmem != lmem &&
+ gmem->num_contexts == 1;
+ alloc_save_t *gsave =
+ (global ? alloc_save_space(gmem, dmem, sid + 1) : (alloc_save_t *) 0);
+ alloc_save_t *lsave = alloc_save_space(lmem, dmem, sid);
+
+ if (lsave == 0 || (global && gsave == 0)) {
+ /* Only 1 of lsave or gsave will have been allocated, but
+ * nevertheless (in case things change in future), we free
+ * lsave, then gsave, so they 'pop' correctly when restoring
+ * the mem->saved states. */
+ if (lsave != 0)
+ alloc_free_save(lmem, lsave, "alloc_save_state(local save)");
+ if (gsave != 0)
+ alloc_free_save(gmem, gsave, "alloc_save_state(global save)");
+ return_error(gs_error_VMerror);
+ }
+ if (gsave != 0) {
+ gsave->client_data = 0;
+ print_save("save", gmem->space, gsave);
+ /* Restore names when we do the local restore. */
+ lsave->restore_names = gsave->restore_names;
+ gsave->restore_names = false;
+ }
+ lsave->id = sid;
+ lsave->client_data = cdata;
+ print_save("save", lmem->space, lsave);
+ /* Reset the l_new attribute in all slots. The only slots that */
+ /* can have the attribute set are the ones on the changes chain, */
+ /* and ones in objects allocated since the last save. */
+ if (lmem->save_level > 1) {
+ ulong scanned;
+ int code = save_set_new(&lsave->state, false, true, &scanned);
+
+ if (code < 0)
+ return code;
+#if 0 /* Disable invisible save levels. */
+ if ((lsave->state.total_scanned += scanned) > max_repeated_scan) {
+ /* Do a second, invisible save. */
+ alloc_save_t *rsave;
+
+ rsave = alloc_save_space(lmem, dmem, 0L);
+ if (rsave != 0) {
+ rsave->client_data = cdata;
+#if 0 /* Bug 688153 */
+ rsave->id = lsave->id;
+ print_save("save", lmem->space, rsave);
+ lsave->id = 0; /* mark as invisible */
+ rsave->state.save_level--; /* ditto */
+ lsave->client_data = 0;
+#else
+ rsave->id = 0; /* mark as invisible */
+ print_save("save", lmem->space, rsave);
+ rsave->state.save_level--; /* ditto */
+ rsave->client_data = 0;
+#endif
+ /* Inherit the allocated space count -- */
+ /* we need this for triggering a GC. */
+ print_save("save", lmem->space, lsave);
+ }
+ }
+#endif
+ }
+
+ alloc_set_in_save(dmem);
+ *psid = sid;
+ return 0;
+}
+/* Save the state of one space (global or local). */
+static alloc_save_t *
+alloc_save_space(gs_ref_memory_t * mem, gs_dual_memory_t * dmem, ulong sid)
+{
+ gs_ref_memory_t save_mem;
+ alloc_save_t *save;
+ chunk_t *cp;
+ chunk_t *new_pcc = 0;
+
+ save_mem = *mem;
+ alloc_close_chunk(mem);
+ mem->pcc = 0;
+ gs_memory_status((gs_memory_t *) mem, &mem->previous_status);
+ ialloc_reset(mem);
+
+ /* Create inner chunks wherever it's worthwhile. */
+
+ for (cp = save_mem.cfirst; cp != 0; cp = cp->cnext) {
+ if (cp->ctop - cp->cbot > min_inner_chunk_space) {
+ /* Create an inner chunk to cover only the unallocated part. */
+ chunk_t *inner =
+ gs_raw_alloc_struct_immovable(mem->non_gc_memory, &st_chunk,
+ "alloc_save_space(inner)");
+
+ if (inner == 0)
+ break; /* maybe should fail */
+ alloc_init_chunk(inner, cp->cbot, cp->ctop, cp->sreloc != 0, cp);
+ alloc_link_chunk(inner, mem);
+ if_debug2m('u', (gs_memory_t *)mem, "[u]inner chunk: cbot=0x%lx ctop=0x%lx\n",
+ (ulong) inner->cbot, (ulong) inner->ctop);
+ if (cp == save_mem.pcc)
+ new_pcc = inner;
+ }
+ }
+ mem->pcc = new_pcc;
+ alloc_open_chunk(mem);
+
+ save = gs_alloc_struct((gs_memory_t *) mem, alloc_save_t,
+ &st_alloc_save, "alloc_save_space(save)");
+ if_debug2m('u', (gs_memory_t *)mem, "[u]save space %u at 0x%lx\n",
+ mem->space, (ulong) save);
+ if (save == 0) {
+ /* Free the inner chunk structures. This is the easiest way. */
+ restore_free(mem);
+ *mem = save_mem;
+ return 0;
+ }
+ save->client_data = NULL;
+ save->state = save_mem;
+ save->spaces = dmem->spaces;
+ save->restore_names = (name_memory(mem) == (gs_memory_t *) mem);
+ save->is_current = (dmem->current == mem);
+ save->id = sid;
+ mem->saved = save;
+ if_debug2m('u', (gs_memory_t *)mem, "[u%u]file_save 0x%lx\n",
+ mem->space, (ulong) mem->streams);
+ mem->streams = 0;
+ mem->total_scanned = 0;
+ mem->total_scanned_after_compacting = 0;
+ if (sid)
+ mem->save_level++;
+ return save;
+}
+
+/* Record a state change that must be undone for restore, */
+/* and mark it as having been saved. */
+int
+alloc_save_change_in(gs_ref_memory_t *mem, const ref * pcont,
+ ref_packed * where, client_name_t cname)
+{
+ register alloc_change_t *cp;
+
+ if (mem->new_mask == 0)
+ return 0; /* no saving */
+ cp = gs_alloc_struct((gs_memory_t *)mem, alloc_change_t,
+ &st_alloc_change, "alloc_save_change");
+ if (cp == 0)
+ return -1;
+ cp->next = mem->changes;
+ cp->where = where;
+ if (pcont == NULL)
+ cp->offset = AC_OFFSET_STATIC;
+ else if (r_is_array(pcont) || r_has_type(pcont, t_dictionary))
+ cp->offset = AC_OFFSET_REF;
+ else if (r_is_struct(pcont))
+ cp->offset = (byte *) where - (byte *) pcont->value.pstruct;
+ else {
+ lprintf3("Bad type %u for save! pcont = 0x%lx, where = 0x%lx\n",
+ r_type(pcont), (ulong) pcont, (ulong) where);
+ gs_abort((const gs_memory_t *)mem);
+ }
+ if (r_is_packed(where))
+ *(ref_packed *)&cp->contents = *where;
+ else {
+ ref_assign_inline(&cp->contents, (ref *) where);
+ r_set_attrs((ref *) where, l_new);
+ }
+ mem->changes = cp;
+#ifdef DEBUG
+ if (gs_debug_c('U')) {
+ dmlprintf1((const gs_memory_t *)mem, "[U]save(%s)", client_name_string(cname));
+ alloc_save_print((const gs_memory_t *)mem, cp, false);
+ }
+#endif
+ return 0;
+}
+int
+alloc_save_change(gs_dual_memory_t * dmem, const ref * pcont,
+ ref_packed * where, client_name_t cname)
+{
+ gs_ref_memory_t *mem =
+ (pcont == NULL ? dmem->space_local :
+ dmem->spaces_indexed[r_space(pcont) >> r_space_shift]);
+
+ return alloc_save_change_in(mem, pcont, where, cname);
+}
+
+/* Allocate a structure for recording an allocation event. */
+int
+alloc_save_change_alloc(gs_ref_memory_t *mem, client_name_t cname, alloc_change_t **pcp)
+{
+ register alloc_change_t *cp;
+
+ if (mem->new_mask == 0)
+ return 0; /* no saving */
+ cp = gs_alloc_struct((gs_memory_t *)mem, alloc_change_t,
+ &st_alloc_change, "alloc_save_change");
+ if (cp == 0)
+ return_error(gs_error_VMerror);
+ cp->next = mem->changes;
+ cp->where = 0;
+ cp->offset = AC_OFFSET_ALLOCATED;
+ make_null(&cp->contents);
+ *pcp = cp;
+ return 1;
+}
+
+/* Remove an AC_OFFSET_ALLOCATED element. */
+void
+alloc_save_remove(gs_ref_memory_t *mem, ref_packed *obj, client_name_t cname)
+{
+ alloc_change_t **cpp = &mem->changes;
+
+ for (; *cpp != NULL;) {
+ alloc_change_t *cp = *cpp;
+
+ if (cp->offset == AC_OFFSET_ALLOCATED && cp->where == obj) {
+ if (mem->scan_limit == cp)
+ mem->scan_limit = cp->next;
+ *cpp = cp->next;
+ gs_free_object((gs_memory_t *)mem, cp, "alloc_save_remove");
+ } else
+ cpp = &(*cpp)->next;
+ }
+}
+
+/* Filter save change lists. */
+static inline void
+alloc_save__filter_changes_in_space(gs_ref_memory_t *mem)
+{
+ /* This is a special function, which is called
+ from the garbager after setting marks and before collecting
+ unused space. Therefore it just resets marks for
+ elements being released instead releasing them really. */
+ alloc_change_t **cpp = &mem->changes;
+
+ for (; *cpp != NULL; ) {
+ alloc_change_t *cp = *cpp;
+
+ if (cp->offset == AC_OFFSET_ALLOCATED && !check_l_mark(cp->where)) {
+ obj_header_t *pre = (obj_header_t *)cp - 1;
+
+ *cpp = cp->next;
+ cp->where = 0;
+ if (mem->scan_limit == cp)
+ mem->scan_limit = cp->next;
+ o_set_unmarked(pre);
+ } else
+ cpp = &(*cpp)->next;
+ }
+}
+
+/* Filter save change lists. */
+void
+alloc_save__filter_changes(gs_ref_memory_t *memory)
+{
+ gs_ref_memory_t *mem = memory;
+
+ for (; mem; mem = &mem->saved->state)
+ alloc_save__filter_changes_in_space(mem);
+}
+
+/* Return (the id of) the innermost externally visible save object, */
+/* i.e., the innermost save with a non-zero ID. */
+ulong
+alloc_save_current_id(const gs_dual_memory_t * dmem)
+{
+ const alloc_save_t *save = dmem->space_local->saved;
+
+ while (save != 0 && save->id == 0)
+ save = save->state.saved;
+ return save->id;
+}
+alloc_save_t *
+alloc_save_current(const gs_dual_memory_t * dmem)
+{
+ return alloc_find_save(dmem, alloc_save_current_id(dmem));
+}
+
+/* Test whether a reference would be invalidated by a restore. */
+bool
+alloc_is_since_save(const void *vptr, const alloc_save_t * save)
+{
+ /* A reference postdates a save iff it is in a chunk allocated */
+ /* since the save (including any carried-over inner chunks). */
+
+ const char *const ptr = (const char *)vptr;
+ register const gs_ref_memory_t *mem = save->space_local;
+
+ if_debug2m('U', (gs_memory_t *)mem, "[U]is_since_save 0x%lx, 0x%lx:\n",
+ (ulong) ptr, (ulong) save);
+ if (mem->saved == 0) { /* This is a special case, the final 'restore' from */
+ /* alloc_restore_all. */
+ return true;
+ }
+ /* Check against chunks allocated since the save. */
+ /* (There may have been intermediate saves as well.) */
+ for (;; mem = &mem->saved->state) {
+ const chunk_t *cp;
+
+ if_debug1m('U', (gs_memory_t *)mem, "[U]checking mem=0x%lx\n", (ulong) mem);
+ for (cp = mem->cfirst; cp != 0; cp = cp->cnext) {
+ if (ptr_is_within_chunk(ptr, cp)) {
+ if_debug3m('U', (gs_memory_t *)mem, "[U+]in new chunk 0x%lx: 0x%lx, 0x%lx\n",
+ (ulong) cp,
+ (ulong) cp->cbase, (ulong) cp->cend);
+ return true;
+ }
+ if_debug1m('U', (gs_memory_t *)mem, "[U-]not in 0x%lx\n", (ulong) cp);
+ }
+ if (mem->saved == save) { /* We've checked all the more recent saves, */
+ /* must be OK. */
+ break;
+ }
+ }
+
+ /*
+ * If we're about to do a global restore (a restore to the level 0),
+ * and there is only one context using this global VM
+ * (the normal case, in which global VM is saved by the
+ * outermost save), we also have to check the global save.
+ * Global saves can't be nested, which makes things easy.
+ */
+ if (save->state.save_level == 0 /* Restoring to save level 0 - see bug 688157, 688161 */ &&
+ (mem = save->space_global) != save->space_local &&
+ save->space_global->num_contexts == 1
+ ) {
+ const chunk_t *cp;
+
+ if_debug1m('U', (gs_memory_t *)mem, "[U]checking global mem=0x%lx\n", (ulong) mem);
+ for (cp = mem->cfirst; cp != 0; cp = cp->cnext)
+ if (ptr_is_within_chunk(ptr, cp)) {
+ if_debug3m('U', (gs_memory_t *)mem, "[U+] new chunk 0x%lx: 0x%lx, 0x%lx\n",
+ (ulong) cp, (ulong) cp->cbase, (ulong) cp->cend);
+ return true;
+ }
+ }
+ return false;
+
+#undef ptr
+}
+
+/* Test whether a name would be invalidated by a restore. */
+bool
+alloc_name_is_since_save(const gs_memory_t *mem,
+ const ref * pnref, const alloc_save_t * save)
+{
+ const name_string_t *pnstr;
+
+ if (!save->restore_names)
+ return false;
+ pnstr = names_string_inline(mem->gs_lib_ctx->gs_name_table, pnref);
+ if (pnstr->foreign_string)
+ return false;
+ return alloc_is_since_save(pnstr->string_bytes, save);
+}
+bool
+alloc_name_index_is_since_save(const gs_memory_t *mem,
+ uint nidx, const alloc_save_t *save)
+{
+ const name_string_t *pnstr;
+
+ if (!save->restore_names)
+ return false;
+ pnstr = names_index_string_inline(mem->gs_lib_ctx->gs_name_table, nidx);
+ if (pnstr->foreign_string)
+ return false;
+ return alloc_is_since_save(pnstr->string_bytes, save);
+}
+
+/* Check whether any names have been created since a given save */
+/* that might be released by the restore. */
+bool
+alloc_any_names_since_save(const alloc_save_t * save)
+{
+ return save->restore_names;
+}
+
+/* Get the saved state with a given ID. */
+alloc_save_t *
+alloc_find_save(const gs_dual_memory_t * dmem, ulong sid)
+{
+ alloc_save_t *sprev = dmem->space_local->saved;
+
+ if (sid == 0)
+ return 0; /* invalid id */
+ while (sprev != 0) {
+ if (sprev->id == sid)
+ return sprev;
+ sprev = sprev->state.saved;
+ }
+ return 0;
+}
+
+/* Get the client data from a saved state. */
+void *
+alloc_save_client_data(const alloc_save_t * save)
+{
+ return save->client_data;
+}
+
+/*
+ * Do one step of restoring the state. The client is responsible for
+ * calling alloc_find_save to get the save object, and for ensuring that
+ * there are no surviving pointers for which alloc_is_since_save is true.
+ * Return true if the argument was the innermost save, in which case
+ * this is the last (or only) step.
+ * Note that "one step" may involve multiple internal steps,
+ * if this is the outermost restore (which requires restoring both local
+ * and global VM) or if we created extra save levels to reduce scanning.
+ */
+static void restore_finalize(gs_ref_memory_t *);
+static void restore_space(gs_ref_memory_t *, gs_dual_memory_t *);
+
+int
+alloc_restore_step_in(gs_dual_memory_t *dmem, alloc_save_t * save)
+{
+ /* Get save->space_* now, because the save object will be freed. */
+ gs_ref_memory_t *lmem = save->space_local;
+ gs_ref_memory_t *gmem = save->space_global;
+ gs_ref_memory_t *mem = lmem;
+ alloc_save_t *sprev;
+ int code;
+
+ /* Finalize all objects before releasing resources or undoing changes. */
+ do {
+ ulong sid;
+
+ sprev = mem->saved;
+ sid = sprev->id;
+ /* In case the saved state was created during a previous
+ call to interp() and co., we need to copy the psignal
+ pointer from the state we're throwing away, back to
+ one about to come active again.
+ */
+ sprev->state.gc_status.psignal = mem->gc_status.psignal;
+ restore_finalize(mem); /* finalize objects */
+ mem = &sprev->state;
+ if (sid != 0)
+ break;
+ }
+ while (sprev != save);
+ if (mem->save_level == 0) {
+ /* This is the outermost save, which might also */
+ /* need to restore global VM. */
+ mem = gmem;
+ if (mem != lmem && mem->saved != 0) {
+ mem->saved->state.gc_status.psignal = mem->gc_status.psignal;
+ restore_finalize(mem);
+ }
+ }
+
+ /* Do one (externally visible) step of restoring the state. */
+ mem = lmem;
+ do {
+ ulong sid;
+
+ sprev = mem->saved;
+ sid = sprev->id;
+ sprev->state.gc_status.psignal = mem->gc_status.psignal;
+ code = restore_resources(sprev, mem); /* release other resources */
+ if (code < 0)
+ return code;
+ restore_space(mem, dmem); /* release memory */
+ if (sid != 0)
+ break;
+ }
+ while (sprev != save);
+
+ if (mem->save_level == 0) {
+ /* This is the outermost save, which might also */
+ /* need to restore global VM. */
+ mem = gmem;
+ if (mem != lmem && mem->saved != 0) {
+ mem->saved->state.gc_status.psignal = mem->gc_status.psignal;
+ code = restore_resources(mem->saved, mem);
+ if (code < 0)
+ return code;
+ restore_space(mem, dmem);
+ }
+ alloc_set_not_in_save(dmem);
+ } else { /* Set the l_new attribute in all slots that are now new. */
+ ulong scanned;
+
+ code = save_set_new(mem, true, false, &scanned);
+ if (code < 0)
+ return code;
+ }
+
+ return sprev == save;
+}
+/* Restore the memory of one space, by undoing changes and freeing */
+/* memory allocated since the save. */
+static void
+restore_space(gs_ref_memory_t * mem, gs_dual_memory_t *dmem)
+{
+ alloc_save_t *save = mem->saved;
+ alloc_save_t saved;
+
+ print_save("restore", mem->space, save);
+
+ /* Undo changes since the save. */
+ {
+ register alloc_change_t *cp = mem->changes;
+
+ while (cp) {
+#ifdef DEBUG
+ if (gs_debug_c('U')) {
+ dmlputs((const gs_memory_t *)mem, "[U]restore");
+ alloc_save_print((const gs_memory_t *)mem, cp, true);
+ }
+#endif
+ if (cp->offset == AC_OFFSET_ALLOCATED)
+ DO_NOTHING;
+ else
+ if (r_is_packed(&cp->contents))
+ *cp->where = *(ref_packed *) & cp->contents;
+ else
+ ref_assign_inline((ref *) cp->where, &cp->contents);
+ cp = cp->next;
+ }
+ }
+
+ /* Free memory allocated since the save. */
+ /* Note that this frees all chunks except the inner ones */
+ /* belonging to this level. */
+ saved = *save;
+ restore_free(mem);
+
+ /* Restore the allocator state. */
+ {
+ int num_contexts = mem->num_contexts; /* don't restore */
+
+ *mem = saved.state;
+ mem->num_contexts = num_contexts;
+ }
+ alloc_open_chunk(mem);
+
+ /* Make the allocator current if it was current before the save. */
+ if (saved.is_current) {
+ dmem->current = mem;
+ dmem->current_space = mem->space;
+ }
+}
+
+/* Restore to the initial state, releasing all resources. */
+/* The allocator is no longer usable after calling this routine! */
+int
+alloc_restore_all(gs_dual_memory_t * dmem)
+{
+ /*
+ * Save the memory pointers, since freeing space_local will also
+ * free dmem itself.
+ */
+ gs_ref_memory_t *lmem = dmem->space_local;
+ gs_ref_memory_t *gmem = dmem->space_global;
+ gs_ref_memory_t *smem = dmem->space_system;
+ gs_ref_memory_t *mem;
+ int code;
+
+ /* Restore to a state outside any saves. */
+ while (lmem->save_level != 0) {
+ code = alloc_restore_step_in(dmem, lmem->saved);
+ if (code < 0)
+ return code;
+ }
+
+ /* Finalize memory. */
+ restore_finalize(lmem);
+ if ((mem = (gs_ref_memory_t *)lmem->stable_memory) != lmem)
+ restore_finalize(mem);
+ if (gmem != lmem && gmem->num_contexts == 1) {
+ restore_finalize(gmem);
+ if ((mem = (gs_ref_memory_t *)gmem->stable_memory) != gmem)
+ restore_finalize(mem);
+ }
+ restore_finalize(smem);
+
+ /* Release resources other than memory, using fake */
+ /* save and memory objects. */
+ {
+ alloc_save_t empty_save;
+
+ empty_save.spaces = dmem->spaces;
+ empty_save.restore_names = false; /* don't bother to release */
+ code = restore_resources(&empty_save, NULL);
+ if (code < 0)
+ return code;
+ }
+
+ /* Finally, release memory. */
+ restore_free(lmem);
+ if ((mem = (gs_ref_memory_t *)lmem->stable_memory) != lmem)
+ restore_free(mem);
+ if (gmem != lmem) {
+ if (!--(gmem->num_contexts)) {
+ restore_free(gmem);
+ if ((mem = (gs_ref_memory_t *)gmem->stable_memory) != gmem)
+ restore_free(mem);
+ }
+ }
+ restore_free(smem);
+ return 0;
+}
+
+/*
+ * Finalize objects that will be freed by a restore.
+ * Note that we must temporarily disable the freeing operations
+ * of the allocator while doing this.
+ */
+static void
+restore_finalize(gs_ref_memory_t * mem)
+{
+ chunk_t *cp;
+
+ alloc_close_chunk(mem);
+ gs_enable_free((gs_memory_t *) mem, false);
+ for (cp = mem->clast; cp != 0; cp = cp->cprev) {
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ struct_proc_finalize((*finalize)) =
+ pre->o_type->finalize;
+ if (finalize != 0) {
+ if_debug2m('u', (gs_memory_t *)mem, "[u]restore finalizing %s 0x%lx\n",
+ struct_type_name_string(pre->o_type),
+ (ulong) (pre + 1));
+ (*finalize) ((gs_memory_t *) mem, pre + 1);
+ }
+ END_OBJECTS_SCAN
+ }
+ gs_enable_free((gs_memory_t *) mem, true);
+}
+
+/* Release resources for a restore */
+static int
+restore_resources(alloc_save_t * sprev, gs_ref_memory_t * mem)
+{
+ int code;
+#ifdef DEBUG
+ if (mem) {
+ /* Note restoring of the file list. */
+ if_debug4m('u', (gs_memory_t *)mem, "[u%u]file_restore 0x%lx => 0x%lx for 0x%lx\n",
+ mem->space, (ulong)mem->streams,
+ (ulong)sprev->state.streams, (ulong) sprev);
+ }
+#endif
+
+ /* Remove entries from font and character caches. */
+ code = font_restore(sprev);
+ if (code < 0)
+ return code;
+
+ /* Adjust the name table. */
+ if (sprev->restore_names)
+ names_restore(mem->gs_lib_ctx->gs_name_table, sprev);
+ return 0;
+}
+
+/* Release memory for a restore. */
+static void
+restore_free(gs_ref_memory_t * mem)
+{
+ /* Free chunks allocated since the save. */
+ gs_free_all((gs_memory_t *) mem);
+}
+
+/* Forget a save, by merging this level with the next outer one. */
+static void file_forget_save(gs_ref_memory_t *);
+static void combine_space(gs_ref_memory_t *);
+static void forget_changes(gs_ref_memory_t *);
+int
+alloc_forget_save_in(gs_dual_memory_t *dmem, alloc_save_t * save)
+{
+ gs_ref_memory_t *mem = save->space_local;
+ alloc_save_t *sprev;
+ ulong scanned;
+ int code;
+
+ print_save("forget_save", mem->space, save);
+
+ /* Iteratively combine the current level with the previous one. */
+ do {
+ sprev = mem->saved;
+ if (sprev->id != 0)
+ mem->save_level--;
+ if (mem->save_level != 0) {
+ alloc_change_t *chp = mem->changes;
+
+ code = save_set_new(&sprev->state, true, false, &scanned);
+ if (code < 0)
+ return code;
+ /* Concatenate the changes chains. */
+ if (chp == 0)
+ mem->changes = sprev->state.changes;
+ else {
+ while (chp->next != 0)
+ chp = chp->next;
+ chp->next = sprev->state.changes;
+ }
+ file_forget_save(mem);
+ combine_space(mem); /* combine memory */
+ } else {
+ forget_changes(mem);
+ code = save_set_new(mem, false, false, &scanned);
+ if (code < 0)
+ return code;
+ file_forget_save(mem);
+ combine_space(mem); /* combine memory */
+ /* This is the outermost save, which might also */
+ /* need to combine global VM. */
+ mem = save->space_global;
+ if (mem != save->space_local && mem->saved != 0) {
+ forget_changes(mem);
+ code = save_set_new(mem, false, false, &scanned);
+ if (code < 0)
+ return code;
+ file_forget_save(mem);
+ combine_space(mem);
+ }
+ alloc_set_not_in_save(dmem);
+ break; /* must be outermost */
+ }
+ }
+ while (sprev != save);
+ return 0;
+}
+/* Combine the chunks of the next outer level with those of the current one, */
+/* and free the bookkeeping structures. */
+static void
+combine_space(gs_ref_memory_t * mem)
+{
+ alloc_save_t *saved = mem->saved;
+ gs_ref_memory_t *omem = &saved->state;
+ chunk_t *cp;
+ chunk_t *csucc;
+
+ alloc_close_chunk(mem);
+ for (cp = mem->cfirst; cp != 0; cp = csucc) {
+ csucc = cp->cnext; /* save before relinking */
+ if (cp->outer == 0)
+ alloc_link_chunk(cp, omem);
+ else {
+ chunk_t *outer = cp->outer;
+
+ outer->inner_count--;
+ if (mem->pcc == cp)
+ mem->pcc = outer;
+ if (mem->cfreed.cp == cp)
+ mem->cfreed.cp = outer;
+ /* "Free" the header of the inner chunk, */
+ /* and any immediately preceding gap left by */
+ /* the GC having compacted the outer chunk. */
+ {
+ obj_header_t *hp = (obj_header_t *) outer->cbot;
+
+ hp->o_pad = 0;
+ hp->o_alone = 0;
+ hp->o_size = (char *)(cp->chead + 1)
+ - (char *)(hp + 1);
+ hp->o_type = &st_bytes;
+ /* The following call is probably not safe. */
+#if 0 /* **************** */
+ gs_free_object((gs_memory_t *) mem,
+ hp + 1, "combine_space(header)");
+#endif /* **************** */
+ }
+ /* Update the outer chunk's allocation pointers. */
+ outer->cbot = cp->cbot;
+ outer->rcur = cp->rcur;
+ outer->rtop = cp->rtop;
+ outer->ctop = cp->ctop;
+ outer->has_refs |= cp->has_refs;
+ gs_free_object(mem->non_gc_memory, cp,
+ "combine_space(inner)");
+ }
+ }
+ /* Update relevant parts of allocator state. */
+ mem->cfirst = omem->cfirst;
+ mem->clast = omem->clast;
+ mem->allocated += omem->allocated;
+ mem->gc_allocated += omem->allocated;
+ mem->lost.objects += omem->lost.objects;
+ mem->lost.refs += omem->lost.refs;
+ mem->lost.strings += omem->lost.strings;
+ mem->saved = omem->saved;
+ mem->previous_status = omem->previous_status;
+ { /* Concatenate free lists. */
+ int i;
+
+ for (i = 0; i < num_freelists; i++) {
+ obj_header_t *olist = omem->freelists[i];
+ obj_header_t *list = mem->freelists[i];
+
+ if (olist == 0);
+ else if (list == 0)
+ mem->freelists[i] = olist;
+ else {
+ while (*(obj_header_t **) list != 0)
+ list = *(obj_header_t **) list;
+ *(obj_header_t **) list = olist;
+ }
+ }
+ if (omem->largest_free_size > mem->largest_free_size)
+ mem->largest_free_size = omem->largest_free_size;
+ }
+ gs_free_object((gs_memory_t *) mem, saved, "combine_space(saved)");
+ alloc_open_chunk(mem);
+}
+/* Free the changes chain for a level 0 .forgetsave, */
+/* resetting the l_new flag in the changed refs. */
+static void
+forget_changes(gs_ref_memory_t * mem)
+{
+ register alloc_change_t *chp = mem->changes;
+ alloc_change_t *next;
+
+ for (; chp; chp = next) {
+ ref_packed *prp = chp->where;
+
+ if_debug1m('U', (gs_memory_t *)mem, "[U]forgetting change 0x%lx\n", (ulong) chp);
+ if (chp->offset == AC_OFFSET_ALLOCATED)
+ DO_NOTHING;
+ else
+ if (!r_is_packed(prp))
+ r_clear_attrs((ref *) prp, l_new);
+ next = chp->next;
+ gs_free_object((gs_memory_t *) mem, chp, "forget_changes");
+ }
+ mem->changes = 0;
+}
+/* Update the streams list when forgetting a save. */
+static void
+file_forget_save(gs_ref_memory_t * mem)
+{
+ const alloc_save_t *save = mem->saved;
+ stream *streams = mem->streams;
+ stream *saved_streams = save->state.streams;
+
+ if_debug4m('u', (gs_memory_t *)mem, "[u%d]file_forget_save 0x%lx + 0x%lx for 0x%lx\n",
+ mem->space, (ulong) streams, (ulong) saved_streams,
+ (ulong) save);
+ if (streams == 0)
+ mem->streams = saved_streams;
+ else if (saved_streams != 0) {
+ while (streams->next != 0)
+ streams = streams->next;
+ streams->next = saved_streams;
+ saved_streams->prev = streams;
+ }
+}
+
+static inline int
+mark_allocated(void *obj, bool to_new, uint *psize)
+{
+ obj_header_t *pre = (obj_header_t *)obj - 1;
+ uint size = pre_obj_contents_size(pre);
+ ref_packed *prp = (ref_packed *) (pre + 1);
+ ref_packed *next = (ref_packed *) ((char *)prp + size);
+#ifdef ALIGNMENT_ALIASING_BUG
+ ref *rpref;
+# define RP_REF(rp) (rpref = (ref *)rp, rpref)
+#else
+# define RP_REF(rp) ((ref *)rp)
+#endif
+
+ if (pre->o_type != &st_refs) {
+ /* Must not happen. */
+ if_debug0('u', "Wrong object type when expected a ref.\n");
+ return_error(gs_error_Fatal);
+ }
+ /* We know that every block of refs ends with */
+ /* a full-size ref, so we only need the end check */
+ /* when we encounter one of those. */
+ if (to_new)
+ while (1) {
+ if (r_is_packed(prp))
+ prp++;
+ else {
+ RP_REF(prp)->tas.type_attrs |= l_new;
+ prp += packed_per_ref;
+ if (prp >= next)
+ break;
+ }
+ } else
+ while (1) {
+ if (r_is_packed(prp))
+ prp++;
+ else {
+ RP_REF(prp)->tas.type_attrs &= ~l_new;
+ prp += packed_per_ref;
+ if (prp >= next)
+ break;
+ }
+ }
+#undef RP_REF
+ *psize = size;
+ return 0;
+}
+
+/* Check if a block contains refs marked by garbager. */
+static bool
+check_l_mark(void *obj)
+{
+ obj_header_t *pre = (obj_header_t *)obj - 1;
+ uint size = pre_obj_contents_size(pre);
+ ref_packed *prp = (ref_packed *) (pre + 1);
+ ref_packed *next = (ref_packed *) ((char *)prp + size);
+#ifdef ALIGNMENT_ALIASING_BUG
+ ref *rpref;
+# define RP_REF(rp) (rpref = (ref *)rp, rpref)
+#else
+# define RP_REF(rp) ((ref *)rp)
+#endif
+
+ /* We know that every block of refs ends with */
+ /* a full-size ref, so we only need the end check */
+ /* when we encounter one of those. */
+ while (1) {
+ if (r_is_packed(prp)) {
+ if (r_has_pmark(prp))
+ return true;
+ prp++;
+ } else {
+ if (r_has_attr(RP_REF(prp), l_mark))
+ return true;
+ prp += packed_per_ref;
+ if (prp >= next)
+ return false;
+ }
+ }
+#undef RP_REF
+}
+
+/* Set or reset the l_new attribute in every relevant slot. */
+/* This includes every slot on the current change chain, */
+/* and every (ref) slot allocated at this save level. */
+/* Return the number of bytes of data scanned. */
+static int
+save_set_new(gs_ref_memory_t * mem, bool to_new, bool set_limit, ulong *pscanned)
+{
+ ulong scanned = 0;
+ int code;
+
+ /* Handle the change chain. */
+ code = save_set_new_changes(mem, to_new, set_limit);
+ if (code < 0)
+ return code;
+
+ /* Handle newly allocated ref objects. */
+ SCAN_MEM_CHUNKS(mem, cp) {
+ if (cp->has_refs) {
+ bool has_refs = false;
+
+ SCAN_CHUNK_OBJECTS(cp)
+ DO_ALL
+ if_debug3m('U', (gs_memory_t *)mem, "[U]set_new scan(0x%lx(%u), %d)\n",
+ (ulong) pre, size, to_new);
+ if (pre->o_type == &st_refs) {
+ /* These are refs, scan them. */
+ ref_packed *prp = (ref_packed *) (pre + 1);
+ uint size;
+
+ code = mark_allocated(prp, to_new, &size);
+ if (code < 0)
+ return code;
+ scanned += size;
+ } else
+ scanned += sizeof(obj_header_t);
+ END_OBJECTS_SCAN
+ cp->has_refs = has_refs;
+ }
+ }
+ END_CHUNKS_SCAN
+ if_debug2m('u', (gs_memory_t *)mem, "[u]set_new (%s) scanned %ld\n",
+ (to_new ? "restore" : "save"), scanned);
+ *pscanned = scanned;
+ return 0;
+}
+
+/* Drop redundant elements from the changes list and set l_new. */
+static void
+drop_redundant_changes(gs_ref_memory_t * mem)
+{
+ register alloc_change_t *chp = mem->changes, *chp_back = NULL, *chp_forth;
+
+ /* As we are trying to throw away redundant changes in an allocator instance
+ that has already been "saved", the active chunk has already been "closed"
+ by alloc_save_space(). Using such an allocator (for example, by calling
+ gs_free_object() with it) can leave it in an unstable state, causing
+ problems for the garbage collector (specifically, the chunk validator code).
+ So, before we might use it, open the current chunk, and then close it again
+ when we're done.
+ */
+ alloc_open_chunk(mem);
+
+ /* First reverse the list and set all. */
+ for (; chp; chp = chp_forth) {
+ chp_forth = chp->next;
+ if (chp->offset != AC_OFFSET_ALLOCATED) {
+ ref_packed *prp = chp->where;
+
+ if (!r_is_packed(prp)) {
+ ref *const rp = (ref *)prp;
+
+ rp->tas.type_attrs |= l_new;
+ }
+ }
+ chp->next = chp_back;
+ chp_back = chp;
+ }
+ mem->changes = chp_back;
+ chp_back = NULL;
+ /* Then filter, reset and reverse again. */
+ for (chp = mem->changes; chp; chp = chp_forth) {
+ chp_forth = chp->next;
+ if (chp->offset != AC_OFFSET_ALLOCATED) {
+ ref_packed *prp = chp->where;
+
+ if (!r_is_packed(prp)) {
+ ref *const rp = (ref *)prp;
+
+ if ((rp->tas.type_attrs & l_new) == 0) {
+ if (mem->scan_limit == chp)
+ mem->scan_limit = chp_back;
+ if (mem->changes == chp)
+ mem->changes = chp_back;
+ gs_free_object((gs_memory_t *)mem, chp, "alloc_save_remove");
+ continue;
+ } else
+ rp->tas.type_attrs &= ~l_new;
+ }
+ }
+ chp->next = chp_back;
+ chp_back = chp;
+ }
+ mem->changes = chp_back;
+
+ alloc_close_chunk(mem);
+}
+
+/* Set or reset the l_new attribute on the changes chain. */
+static int
+save_set_new_changes(gs_ref_memory_t * mem, bool to_new, bool set_limit)
+{
+ register alloc_change_t *chp;
+ register uint new = (to_new ? l_new : 0);
+ ulong scanned = 0;
+
+ if (!to_new && mem->total_scanned_after_compacting > max_repeated_scan * 16) {
+ mem->total_scanned_after_compacting = 0;
+ drop_redundant_changes(mem);
+ }
+ for (chp = mem->changes; chp; chp = chp->next) {
+ if (chp->offset == AC_OFFSET_ALLOCATED) {
+ if (chp->where != 0) {
+ uint size;
+ int code = mark_allocated((void *)chp->where, to_new, &size);
+
+ if (code < 0)
+ return code;
+ scanned += size;
+ }
+ } else {
+ ref_packed *prp = chp->where;
+
+ if_debug3m('U', (gs_memory_t *)mem, "[U]set_new 0x%lx: (0x%lx, %d)\n",
+ (ulong)chp, (ulong)prp, new);
+ if (!r_is_packed(prp)) {
+ ref *const rp = (ref *) prp;
+
+ rp->tas.type_attrs =
+ (rp->tas.type_attrs & ~l_new) + new;
+ }
+ }
+ if (mem->scan_limit == chp)
+ break;
+ }
+ if (set_limit) {
+ mem->total_scanned_after_compacting += scanned;
+ if (scanned + mem->total_scanned >= max_repeated_scan) {
+ mem->scan_limit = mem->changes;
+ mem->total_scanned = 0;
+ } else
+ mem->total_scanned += scanned;
+ }
+ return 0;
+}
+
+gs_memory_t *
+gs_save_any_memory(const alloc_save_t *save)
+{
+ return((gs_memory_t *)save->space_local);
+}
diff --git a/psi/isave.h b/psi/isave.h
new file mode 100644
index 000000000..f8a6f32ec
--- /dev/null
+++ b/psi/isave.h
@@ -0,0 +1,125 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Procedures for save/restore */
+/* Requires imemory.h */
+
+#ifndef isave_INCLUDED
+# define isave_INCLUDED
+
+#include "idosave.h"
+
+/*
+ * In PLRM2, save objects are simple, not composite. Consequently, we
+ * cannot use their natural representation, namely a t_struct pointing to an
+ * alloc_save_t, since we aren't willing to allocate them all in global VM
+ * and rely on garbage collection to clean them up. Instead, we assign each
+ * one a unique "save ID", and store this in the alloc_save_t object.
+ * Mapping the number to the object requires at most searching the local
+ * save chain for the current gs_dual_memory_t, and this approach means we
+ * don't have to do anything to invalidate save objects when we do a
+ * restore.
+ *
+ * In PLRM3, Adobe did the reasonable thing and changed save objects to
+ * composite. However, this means that 'restore' must treat save objects on
+ * the stack differently in LL2 vs. LL3 (yes, the Genoa LL2 and LL3 tests
+ * require this!). See zvmem.c:restore_check_stack.
+ */
+#ifndef alloc_save_t_DEFINED /* also in inamedef.h */
+typedef struct alloc_save_s alloc_save_t;
+# define alloc_save_t_DEFINED
+#endif
+
+/* Initialize the save machinery. */
+extern void alloc_save_init(gs_dual_memory_t *);
+
+/* Map a save ID to its save object. Return 0 if the ID is invalid. */
+alloc_save_t *alloc_find_save(const gs_dual_memory_t *, ulong);
+
+/*
+ * Save the state. Return 0 if we can't allocate the save object,
+ * otherwise return the save ID. The second argument is a client data
+ * pointer, assumed to point to an object.
+ */
+int alloc_save_state(gs_dual_memory_t * dmem, void *cdata, ulong *psid);
+
+/* Get the client pointer passed to alloc_saved_state. */
+void *alloc_save_client_data(const alloc_save_t *);
+
+/* Return (the id of) the innermost externally visible save object. */
+ulong alloc_save_current_id(const gs_dual_memory_t *);
+alloc_save_t *alloc_save_current(const gs_dual_memory_t *);
+
+/* Check whether a pointer refers to an object allocated since a given save. */
+bool alloc_is_since_save(const void *, const alloc_save_t *);
+
+/* Check whether a name was created since a given save. */
+bool alloc_name_is_since_save(const gs_memory_t *mem, const ref *, const alloc_save_t *);
+bool alloc_name_index_is_since_save(const gs_memory_t *mem, uint, const alloc_save_t *);
+
+/*
+ * Check whether any names have been created since a given save
+ * that might be released by the restore.
+ */
+bool alloc_any_names_since_save(const alloc_save_t *);
+
+/*
+ * Do one step of restoring the state. Return true if the argument
+ * was the innermost save, in which case this is the last (or only) step.
+ * Assume the caller obtained the argument by calling alloc_find_save;
+ * if this is the case, the operation cannot fail.
+ */
+int alloc_restore_step_in(gs_dual_memory_t *, alloc_save_t *);
+
+/*
+ * Forget a save -- like committing a transaction (restore is like
+ * aborting a transaction). Assume the caller obtained the argument
+ * by calling alloc_find_save. Note that forgetting a save does not
+ * require checking pointers for recency.
+ */
+int alloc_forget_save_in(gs_dual_memory_t *, alloc_save_t *);
+
+/* Release all memory -- like doing a restore "past the bottom". */
+int alloc_restore_all(gs_dual_memory_t *);
+/* Filter save change lists. */
+void alloc_save__filter_changes(gs_ref_memory_t *mem);
+
+/* ------ Internals ------ */
+
+/*
+ * If we are in a save, we want to save the old contents if l_new is
+ * not set; if we are not in a save, we never want to save old contents.
+ * We can test this quickly with a single mask that is l_new if we are
+ * in a save, and -1 if we are not, since type_attrs of a valid ref
+ * cannot be 0; this is the test_mask in a gs_dual_memory_t. Similarly,
+ * we want to set the l_new bit in newly allocated objects iff we are in
+ * a save; this is the new_mask in a gs_dual_memory_t.
+ */
+
+/* Record that we are in a save. */
+void alloc_set_in_save(gs_dual_memory_t *);
+
+/* Record that we are not in a save. */
+void alloc_set_not_in_save(gs_dual_memory_t *);
+
+/* Remove entries from font and character caches. */
+int font_restore(const alloc_save_t * save);
+
+/* Accessor to get a memory pointer from the saved state for the
+ express purpose of getting the library context. */
+gs_memory_t *gs_save_any_memory(const alloc_save_t *save);
+
+#endif /* isave_INCLUDED */
diff --git a/psi/iscan.c b/psi/iscan.c
new file mode 100644
index 000000000..2cb18aa94
--- /dev/null
+++ b/psi/iscan.c
@@ -0,0 +1,1253 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Token scanner for Ghostscript interpreter */
+#include "ghost.h"
+#include "memory_.h"
+#include "string_.h"
+#include "stream.h"
+#include "ierrors.h"
+#include "btoken.h" /* for ref_binary_object_format */
+#include "files.h" /* for fptr */
+#include "ialloc.h"
+#include "idict.h" /* for //name lookup */
+#include "dstack.h" /* ditto */
+#include "ilevel.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "iparray.h"
+#include "strimpl.h" /* for string decoding */
+#include "sa85d.h" /* ditto */
+#include "sfilter.h" /* ditto */
+#include "ostack.h" /* for accumulating proc bodies; */
+ /* must precede iscan.h */
+#include "iscan.h" /* defines interface */
+#include "iscanbin.h"
+#include "iscannum.h"
+#include "istream.h"
+#include "istruct.h" /* for RELOC_REF_VAR */
+#include "iutil.h"
+#include "ivmspace.h"
+#include "store.h"
+#include "scanchar.h"
+
+#define recognize_btokens()\
+ (ref_binary_object_format.value.intval != 0 && level2_enabled)
+
+/* Procedure for handling DSC comments if desired. */
+/* Set at initialization if a DSC handling module is included. */
+int (*gs_scan_dsc_proc) (const byte *, uint) = NULL;
+
+/* Procedure for handling all comments if desired. */
+/* Set at initialization if a comment handling module is included. */
+/* If both gs_scan_comment_proc and gs_scan_dsc_proc are set, */
+/* scan_comment_proc is called only for non-DSC comments. */
+int (*gs_scan_comment_proc) (const byte *, uint) = NULL;
+
+/*
+ * Level 2 includes some changes in the scanner:
+ * - \ is always recognized in strings, regardless of the data source;
+ * - << and >> are legal tokens;
+ * - <~ introduces an ASCII85 encoded string (terminated by ~>);
+ * - Character codes above 127 introduce binary objects.
+ * We explicitly enable or disable these changes here.
+ */
+#define scan_enable_level2 level2_enabled /* from ilevel.h */
+
+/* ------ Dynamic strings ------ */
+
+/* Begin collecting a dynamically allocated string. */
+static inline void
+dynamic_init(da_ptr pda, gs_memory_t *mem)
+{
+ pda->is_dynamic = false;
+ pda->limit = pda->buf + sizeof(pda->buf);
+ pda->next = pda->base = pda->buf;
+ pda->memory = mem;
+}
+
+/* Free a dynamic string. */
+static void
+dynamic_free(da_ptr pda)
+{
+ if (pda->is_dynamic)
+ gs_free_string(pda->memory, pda->base, da_size(pda), "scanner");
+}
+
+/* Resize a dynamic string. */
+/* If the allocation fails, return gs_error_VMerror; otherwise, return 0. */
+static int
+dynamic_resize(da_ptr pda, uint new_size)
+{
+ uint old_size = da_size(pda);
+ uint pos = pda->next - pda->base;
+ gs_memory_t *mem = pda->memory;
+ byte *base;
+
+ if (pda->is_dynamic) {
+ base = gs_resize_string(mem, pda->base, old_size,
+ new_size, "scanner");
+ if (base == 0)
+ return_error(gs_error_VMerror);
+ } else { /* switching from static to dynamic */
+ base = gs_alloc_string(mem, new_size, "scanner");
+ if (base == 0)
+ return_error(gs_error_VMerror);
+ memcpy(base, pda->base, min(old_size, new_size));
+ pda->is_dynamic = true;
+ }
+ pda->base = base;
+ pda->next = base + pos;
+ pda->limit = base + new_size;
+ return 0;
+}
+
+/* Grow a dynamic string. */
+/* Return 0 if the allocation failed, the new 'next' ptr if OK. */
+/* Return 0 or an error code, updating pda->next to point to the first */
+/* available byte after growing. */
+static int
+dynamic_grow(da_ptr pda, byte * next, uint max_size)
+{
+ uint old_size = da_size(pda);
+ uint new_size = (old_size < 10 ? 20 :
+ old_size >= (max_size >> 1) ? max_size :
+ old_size << 1);
+ int code;
+
+ pda->next = next;
+ if (old_size >= max_size)
+ return_error(gs_error_limitcheck);
+ while ((code = dynamic_resize(pda, new_size)) < 0 &&
+ new_size > old_size
+ ) { /* Try trimming down the requested new size. */
+ new_size -= (new_size - old_size + 1) >> 1;
+ }
+ return code;
+}
+
+/* Ensure that a dynamic string is either on the heap or in the */
+/* private buffer. */
+static void
+dynamic_save(da_ptr pda)
+{
+ if (!pda->is_dynamic && pda->base != pda->buf) {
+ int len = da_size(pda);
+
+ if (len > sizeof(pda->buf))
+ len = sizeof(pda->buf);
+ memcpy(pda->buf, pda->base, len);
+ pda->next = pda->buf + len;
+ pda->base = pda->buf;
+ }
+}
+
+/* Finish collecting a dynamic string. */
+static int
+dynamic_make_string(i_ctx_t *i_ctx_p, ref * pref, da_ptr pda, byte * next)
+{
+ uint size = (pda->next = next) - pda->base;
+ int code = dynamic_resize(pda, size);
+
+ if (code < 0)
+ return code;
+ make_tasv_new(pref, t_string,
+ a_all | imemory_space((gs_ref_memory_t *) pda->memory),
+ size, bytes, pda->base);
+ return 0;
+}
+
+/* ------ Main scanner ------ */
+
+/* GC procedures */
+#define ssarray ssptr->s_ss.binary.bin_array
+static
+CLEAR_MARKS_PROC(scanner_clear_marks)
+{
+ scanner_state *const ssptr = vptr;
+
+ r_clear_attrs(&ssptr->s_file, l_mark);
+ r_clear_attrs(&ssarray, l_mark);
+ r_clear_attrs(&ssptr->s_error.object, l_mark);
+}
+static
+ENUM_PTRS_WITH(scanner_enum_ptrs, scanner_state *ssptr) return 0;
+case 0:
+ ENUM_RETURN_REF(&ssptr->s_file);
+case 1:
+ ENUM_RETURN_REF(&ssptr->s_error.object);
+case 2:
+ if (ssptr->s_scan_type == scanning_none ||
+ !ssptr->s_da.is_dynamic
+ )
+ ENUM_RETURN(0);
+ return ENUM_STRING2(ssptr->s_da.base, da_size(&ssptr->s_da));
+case 3:
+ if (ssptr->s_scan_type != scanning_binary)
+ return 0;
+ ENUM_RETURN_REF(&ssarray);
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(scanner_reloc_ptrs, scanner_state *ssptr)
+{
+ RELOC_REF_VAR(ssptr->s_file);
+ r_clear_attrs(&ssptr->s_file, l_mark);
+ if (ssptr->s_scan_type != scanning_none && ssptr->s_da.is_dynamic) {
+ gs_string sda;
+
+ sda.data = ssptr->s_da.base;
+ sda.size = da_size(&ssptr->s_da);
+ RELOC_STRING_VAR(sda);
+ ssptr->s_da.limit = sda.data + sda.size;
+ ssptr->s_da.next = sda.data + (ssptr->s_da.next - ssptr->s_da.base);
+ ssptr->s_da.base = sda.data;
+ }
+ if (ssptr->s_scan_type == scanning_binary) {
+ RELOC_REF_VAR(ssarray);
+ r_clear_attrs(&ssarray, l_mark);
+ }
+ RELOC_REF_VAR(ssptr->s_error.object);
+ r_clear_attrs(&ssptr->s_error.object, l_mark);
+}
+RELOC_PTRS_END
+/* Structure type */
+public_st_scanner_state_dynamic();
+
+/* Initialize a scanner. */
+void
+gs_scanner_init_options(scanner_state *sstate, const ref *fop, int options)
+{
+ ref_assign(&sstate->s_file, fop);
+ sstate->s_scan_type = scanning_none;
+ sstate->s_pstack = 0;
+ sstate->s_options = options;
+ SCAN_INIT_ERROR(sstate);
+}
+void gs_scanner_init_stream_options(scanner_state *sstate, stream *s,
+ int options)
+{
+ /*
+ * The file 'object' will never be accessed, but it must be in correct
+ * form for the GC.
+ */
+ ref fobj;
+
+ make_file(&fobj, a_read, 0, s);
+ gs_scanner_init_options(sstate, &fobj, options);
+}
+
+/*
+ * Return the "error object" to be stored in $error.command instead of
+ * --token--, if any, or <0 if no special error object is available.
+ */
+int
+gs_scanner_error_object(i_ctx_t *i_ctx_p, const scanner_state *pstate,
+ ref *pseo)
+{
+ if (!r_has_type(&pstate->s_error.object, t__invalid)) {
+ ref_assign(pseo, &pstate->s_error.object);
+ return 0;
+ }
+ if (pstate->s_error.string[0]) {
+ int len = strlen(pstate->s_error.string);
+
+ if (pstate->s_error.is_name) {
+ int code = name_ref(imemory, (const byte *)pstate->s_error.string, len, pseo, 1);
+
+ if (code < 0)
+ return code;
+ r_set_attrs(pseo, a_executable); /* Adobe compatibility */
+ return 0;
+ } else {
+ byte *estr = ialloc_string(len, "gs_scanner_error_object");
+
+ if (estr == 0)
+ return -1; /* VMerror */
+ memcpy(estr, (const byte *)pstate->s_error.string, len);
+ make_string(pseo, a_all | icurrent_space, len, estr);
+ return 0;
+ }
+ }
+ return -1; /* no error object */
+}
+
+/* Handle a scan_Refill return from gs_scan_token. */
+/* This may return o_push_estack, 0 (meaning just call gs_scan_token */
+/* again), or an error code. */
+int
+gs_scan_handle_refill(i_ctx_t *i_ctx_p, scanner_state * sstate,
+ bool save, op_proc_t cont)
+{
+ const ref *const fop = &sstate->s_file;
+ stream *s = fptr(fop);
+ uint avail = sbufavailable(s);
+ int status;
+
+ if (s->end_status == EOFC) {
+ /* More data needed, but none available, so this is a syntax error. */
+ return_error(gs_error_syntaxerror);
+ }
+ status = s_process_read_buf(s);
+ if (sbufavailable(s) > avail)
+ return 0;
+ if (status == 0)
+ status = s->end_status;
+ switch (status) {
+ case EOFC:
+ /* We just discovered that we're at EOF. */
+ /* Let the caller find this out. */
+ return 0;
+ case ERRC:
+ return_error(gs_error_ioerror);
+ case INTC:
+ case CALLC:
+ {
+ ref rstate[1];
+ scanner_state *pstate;
+
+ if (save) {
+ pstate = (scanner_state *)
+ ialloc_struct(scanner_state_dynamic, &st_scanner_state_dynamic,
+ "gs_scan_handle_refill");
+ if (pstate == 0)
+ return_error(gs_error_VMerror);
+ ((scanner_state_dynamic *)pstate)->mem = imemory;
+ *pstate = *sstate;
+ } else
+ pstate = sstate;
+ make_istruct(&rstate[0], 0, pstate);
+ return s_handle_read_exception(i_ctx_p, status, fop,
+ rstate, 1, cont);
+ }
+ }
+ /* No more data available, but no exception. */
+ /* A filter is consuming headers but returns nothing. */
+ return 0;
+}
+
+/*
+ * Handle a comment. The 'saved' argument is needed only for
+ * tracing printout.
+ */
+static int
+scan_comment(i_ctx_t *i_ctx_p, ref *pref, scanner_state *pstate,
+ const byte * base, const byte * end, bool saved)
+{
+ uint len = (uint) (end - base);
+ int code;
+#ifdef DEBUG
+ const char *sstr = (saved ? ">" : "");
+#endif
+
+ if (len > 1 && (base[1] == '%' || base[1] == '!')) {
+ /* Process as a DSC comment if requested. */
+#ifdef DEBUG
+ if (gs_debug_c('%')) {
+ dmlprintf2(imemory, "[%%%%%s%c]", sstr, (len >= 3 ? '+' : '-'));
+ debug_print_string(imemory, base, len);
+ dmputs(imemory, "\n");
+ }
+#endif
+ if (gs_scan_dsc_proc != NULL) {
+ code = gs_scan_dsc_proc(base, len);
+ return (code < 0 ? code : 0);
+ }
+ if (pstate->s_options & SCAN_PROCESS_DSC_COMMENTS) {
+ code = scan_DSC_Comment;
+ goto comment;
+ }
+ /* Treat as an ordinary comment. */
+ }
+#ifdef DEBUG
+ else {
+ if (gs_debug_c('%')) {
+ dmlprintf2(imemory, "[%% %s%c]", sstr, (len >= 2 ? '+' : '-'));
+ debug_print_string(imemory, base, len);
+ dmputs(imemory, "\n");
+ }
+ }
+#endif
+ if (gs_scan_comment_proc != NULL) {
+ code = gs_scan_comment_proc(base, len);
+ return (code < 0 ? code : 0);
+ }
+ if (pstate->s_options & SCAN_PROCESS_COMMENTS) {
+ code = scan_Comment;
+ goto comment;
+ }
+ return 0;
+ comment:
+ {
+ byte *cstr = ialloc_string(len, "scan_comment");
+
+ if (cstr == 0)
+ return_error(gs_error_VMerror);
+ memcpy(cstr, base, len);
+ make_string(pref, a_all | icurrent_space, len, cstr);
+ }
+ return code;
+}
+
+/* Read a token from a string. */
+/* Update the string if succesful. */
+/* Store the error object in i_ctx_p->error_object if not. */
+int
+gs_scan_string_token_options(i_ctx_t *i_ctx_p, ref * pstr, ref * pref,
+ int options)
+{
+ stream st;
+ stream *s = &st;
+ scanner_state state;
+ int code;
+
+ if (!r_has_attr(pstr, a_read))
+ return_error(gs_error_invalidaccess);
+ s_init(s, NULL);
+ sread_string(s, pstr->value.bytes, r_size(pstr));
+ gs_scanner_init_stream_options(&state, s, options | SCAN_FROM_STRING);
+ switch (code = gs_scan_token(i_ctx_p, pref, &state)) {
+ default: /* error or comment */
+ if (code < 0)
+ break;
+ /* falls through */
+ case 0: /* read a token */
+ case scan_BOS:
+ {
+ uint pos = stell(s);
+
+ pstr->value.bytes += pos;
+ r_dec_size(pstr, pos);
+ }
+ break;
+ case scan_Refill: /* error */
+ code = gs_note_error(gs_error_syntaxerror);
+ case scan_EOF:
+ break;
+ }
+ if (code < 0)
+ gs_scanner_error_object(i_ctx_p, &state, &i_ctx_p->error_object);
+ return code;
+}
+
+/*
+ * Read a token from a stream. Return 0 if an ordinary token was read,
+ * >0 for special situations (see iscan.h).
+ * If the token required a terminating character (i.e., was a name or
+ * number) and the next character was whitespace, read and discard
+ * that character. Note that the state is relevant for gs_error_VMerror
+ * as well as for scan_Refill.
+ */
+int
+gs_scan_token(i_ctx_t *i_ctx_p, ref * pref, scanner_state * pstate)
+{
+ stream *const s = pstate->s_file.value.pfile;
+ ref *myref = pref;
+ int retcode = 0;
+ int c;
+
+ s_declare_inline(s, sptr, endptr);
+#define scan_begin_inline() s_begin_inline(s, sptr, endptr)
+#define scan_getc() sgetc_inline(s, sptr, endptr)
+#define scan_putback() sputback_inline(s, sptr, endptr)
+#define scan_end_inline() s_end_inline(s, sptr, endptr)
+ const byte *newptr;
+ byte *daptr;
+
+#define sreturn(code)\
+ { retcode = gs_note_error(code); goto sret; }
+#define if_not_spush1()\
+ if ( osp < ostop ) osp++;\
+ else if ( (retcode = ref_stack_push(&o_stack, 1)) >= 0 )\
+ ;\
+ else
+#define spop1()\
+ if ( osp >= osbot ) osp--;\
+ else ref_stack_pop(&o_stack, 1)
+ int max_name_ctype =
+ (recognize_btokens()? ctype_name : ctype_btoken);
+
+#define scan_sign(sign, ptr)\
+ switch ( *ptr ) {\
+ case '-': sign = -1; ptr++; break;\
+ case '+': sign = 1; ptr++; break;\
+ default: sign = 0;\
+ }
+#define refill2_back(styp,nback)\
+ BEGIN sptr -= nback; scan_type = styp; goto pause; END
+#define ensure2_back(styp,nback)\
+ if ( sptr >= endptr ) refill2_back(styp,nback)
+#define ensure2(styp) ensure2_back(styp, 1)
+#define refill2(styp) refill2_back(styp, 1)
+ byte s1[2];
+ const byte *const decoder = scan_char_decoder;
+ int status;
+ int sign;
+ const bool check_only = (pstate->s_options & SCAN_CHECK_ONLY) != 0;
+ const bool PDFScanRules = (i_ctx_p->scanner_options & SCAN_PDF_RULES) != 0;
+ /*
+ * The following is a hack so that ^D will be self-delimiting in PS files
+ * (to compensate for bugs in some PostScript-generating applications)
+ * but not in strings (to match CPSI on the CET) or PDF.
+ */
+ const int ctrld = (pstate->s_options & SCAN_FROM_STRING ||
+ PDFScanRules ? 0x04 : 0xffff);
+ scanner_state sstate;
+
+#define pstack sstate.s_pstack
+#define pdepth sstate.s_pdepth
+#define scan_type sstate.s_scan_type
+#define da sstate.s_da
+#define name_type sstate.s_ss.s_name.s_name_type
+#define try_number sstate.s_ss.s_name.s_try_number
+
+ sptr = endptr = NULL; /* Quiet compiler */
+ if (pstate->s_pstack != 0) {
+ if_not_spush1()
+ return retcode;
+ myref = osp;
+ }
+ /* Check whether we are resuming after an interruption. */
+ if (pstate->s_scan_type != scanning_none) {
+ sstate = *pstate;
+ if (!da.is_dynamic && da.base != da.buf) {
+ /* The da contains some self-referencing pointers. */
+ /* Fix them up now. */
+ uint next = da.next - da.base;
+ uint limit = da.limit - da.base;
+
+ da.base = da.buf;
+ da.next = da.buf + next;
+ da.limit = da.buf + limit;
+ }
+ daptr = da.next;
+ switch (scan_type) {
+ case scanning_binary:
+ retcode = (*sstate.s_ss.binary.cont)
+ (i_ctx_p, myref, &sstate);
+ scan_begin_inline();
+ if (retcode == scan_Refill)
+ goto pause;
+ goto sret;
+ case scanning_comment:
+ scan_begin_inline();
+ goto cont_comment;
+ case scanning_name:
+ goto cont_name;
+ case scanning_string:
+ goto cont_string;
+ default:
+ return_error(gs_error_Fatal);
+ }
+ }
+ /* Fetch any state variables that are relevant even if */
+ /* scan_type == scanning_none. */
+ pstack = pstate->s_pstack;
+ pdepth = pstate->s_pdepth;
+ ref_assign(&sstate.s_file, &pstate->s_file);
+ sstate.s_options = pstate->s_options;
+ SCAN_INIT_ERROR(&sstate);
+ scan_begin_inline();
+ /*
+ * Loop invariants:
+ * If pstack != 0, myref = osp, and *osp is a valid slot.
+ */
+ top:c = scan_getc();
+ if_debug1m('S', imemory, (c >= 32 && c <= 126 ? "`%c'" : c >= 0 ? "`\\%03o'" : "`%d'"), c);
+ switch (c) {
+ case ' ':
+ case '\f':
+ case '\t':
+ case char_CR:
+ case char_EOL:
+ case char_NULL:
+ goto top;
+ case 0x04: /* see ctrld above */
+ if (c == ctrld) /* treat as ordinary name char */
+ goto begin_name;
+ case '[':
+ case ']':
+ s1[0] = (byte) c;
+ retcode = name_ref(imemory, s1, 1, myref, 1); /* can't fail */
+ r_set_attrs(myref, a_executable);
+ break;
+ case '<':
+ if (scan_enable_level2) {
+ ensure2(scanning_none);
+ c = scan_getc();
+ switch (c) {
+ case '<':
+ scan_putback();
+ name_type = 0;
+ try_number = false;
+ goto try_funny_name;
+ case '~':
+ s_A85D_init_inline(&sstate.s_ss.a85d);
+ sstate.s_ss.st.templat = &s_A85D_template;
+ sstate.s_ss.a85d.require_eod = true;
+ goto str;
+ }
+ scan_putback();
+ }
+ s_AXD_init_inline(&sstate.s_ss.axd);
+ sstate.s_ss.st.templat = &s_AXD_template;
+ str:scan_end_inline();
+ dynamic_init(&da, imemory);
+ cont_string:for (;;) {
+ stream_cursor_write w;
+
+ w.ptr = da.next - 1;
+ w.limit = da.limit - 1;
+ status = (*sstate.s_ss.st.templat->process)
+ (&sstate.s_ss.st, &s->cursor.r, &w,
+ s->end_status == EOFC);
+ if (!check_only)
+ da.next = w.ptr + 1;
+ switch (status) {
+ case 0:
+ status = s->end_status;
+ if (status < 0) {
+ if (status == EOFC) {
+ if (check_only) {
+ retcode = scan_Refill;
+ scan_type = scanning_string;
+ goto suspend;
+ } else
+ sreturn(gs_error_syntaxerror);
+ }
+ break;
+ }
+ s_process_read_buf(s);
+ continue;
+ case 1:
+ if (!check_only) {
+ retcode = dynamic_grow(&da, da.next, max_string_size);
+ if (retcode == gs_error_VMerror) {
+ scan_type = scanning_string;
+ goto suspend;
+ } else if (retcode < 0)
+ sreturn(retcode);
+ }
+ continue;
+ }
+ break;
+ }
+ scan_begin_inline();
+ switch (status) {
+ default:
+ /*case ERRC: */
+ sreturn(gs_error_syntaxerror);
+ case INTC:
+ case CALLC:
+ scan_type = scanning_string;
+ goto pause;
+ case EOFC:
+ ;
+ }
+ retcode = dynamic_make_string(i_ctx_p, myref, &da, da.next);
+ if (retcode < 0) { /* VMerror */
+ sputback(s); /* rescan ) */
+ scan_type = scanning_string;
+ goto suspend;
+ }
+ break;
+ case '(':
+ sstate.s_ss.pssd.from_string =
+ ((pstate->s_options & SCAN_FROM_STRING) != 0) &&
+ !scan_enable_level2;
+ s_PSSD_partially_init_inline(&sstate.s_ss.pssd);
+ sstate.s_ss.st.templat = &s_PSSD_template;
+ goto str;
+ case '{':
+ if (pstack == 0) { /* outermost procedure */
+ if_not_spush1() {
+ scan_putback();
+ scan_type = scanning_none;
+ goto pause_ret;
+ }
+ pdepth = ref_stack_count_inline(&o_stack);
+ }
+ make_int(osp, pstack);
+ pstack = ref_stack_count_inline(&o_stack);
+ if_debug3m('S', imemory, "[S{]d=%d, s=%d->%d\n",
+ pdepth, (int)osp->value.intval, pstack);
+ goto snext;
+ case '>':
+ if (scan_enable_level2) {
+ ensure2(scanning_none);
+ name_type = 0;
+ try_number = false;
+ goto try_funny_name;
+ }
+ /* falls through */
+ case ')':
+ sreturn(gs_error_syntaxerror);
+ case '}':
+ if (pstack == 0)
+ sreturn(gs_error_syntaxerror);
+ osp--;
+ {
+ uint size = ref_stack_count_inline(&o_stack) - pstack;
+ ref arr;
+
+ if_debug4m('S', imemory, "[S}]d=%"PRIu32", s=%"PRIu32"->%"PRIpsint", c=%"PRIu32"\n",
+ pdepth, pstack,
+ (pstack == pdepth ? 0 :
+ ref_stack_index(&o_stack, size)->value.intval),
+ size + pstack);
+ if (size > max_array_size)
+ sreturn(gs_error_limitcheck);
+ myref = (pstack == pdepth ? pref : &arr);
+ if (check_only) {
+ make_empty_array(myref, 0);
+ ref_stack_pop(&o_stack, size);
+ } else if (ref_array_packing.value.boolval) {
+ retcode = make_packed_array(myref, &o_stack, size,
+ idmemory, "scanner(packed)");
+ if (retcode < 0) { /* must be VMerror */
+ osp++;
+ scan_putback();
+ scan_type = scanning_none;
+ goto pause_ret;
+ }
+ r_set_attrs(myref, a_executable);
+ } else {
+ retcode = ialloc_ref_array(myref,
+ a_executable + a_all, size,
+ "scanner(proc)");
+ if (retcode < 0) { /* must be VMerror */
+ osp++;
+ scan_putback();
+ scan_type = scanning_none;
+ goto pause_ret;
+ }
+ retcode = ref_stack_store(&o_stack, myref, size, 0, 1,
+ false, idmemory, "scanner");
+ if (retcode < 0) {
+ ifree_ref_array(myref, "scanner(proc)");
+ sreturn(retcode);
+ }
+ ref_stack_pop(&o_stack, size);
+ }
+ if (pstack == pdepth) { /* This was the top-level procedure. */
+ spop1();
+ pstack = 0;
+ } else {
+ if (osp < osbot)
+ ref_stack_pop_block(&o_stack);
+ pstack = osp->value.intval;
+ *osp = arr;
+ goto snext;
+ }
+ }
+ break;
+ case '/':
+ /*
+ * If the last thing in the input is a '/', don't try to read
+ * any more data.
+ */
+ if (sptr >= endptr && s->end_status != EOFC) {
+ refill2(scanning_none);
+ }
+ c = scan_getc();
+ if (!PDFScanRules && (c == '/')) {
+ name_type = 2;
+ c = scan_getc();
+ } else
+ name_type = 1;
+ try_number = false;
+ switch (decoder[c]) {
+ case ctype_name:
+ default:
+ goto do_name;
+ case ctype_btoken:
+ if (!recognize_btokens())
+ goto do_name;
+ /* otherwise, an empty name */
+ case ctype_exception:
+ case ctype_space:
+ /*
+ * Amazingly enough, the Adobe implementations don't accept
+ * / or // followed by [, ], <<, or >>, so we do the same.
+ * (Older versions of our code had a ctype_other case here
+ * that handled these specially.)
+ */
+ case ctype_other:
+ if (c == ctrld) /* see above */
+ goto do_name;
+ da.base = da.limit = daptr = 0;
+ da.is_dynamic = false;
+ goto nx;
+ }
+ case '%':
+ { /* Scan as much as possible within the buffer. */
+ const byte *base = sptr;
+ const byte *end;
+
+ while (++sptr < endptr) /* stop 1 char early */
+ switch (*sptr) {
+ case char_CR:
+ end = sptr;
+ if (sptr[1] == char_EOL)
+ sptr++;
+ cend: /* Check for externally processed comments. */
+ retcode = scan_comment(i_ctx_p, myref, &sstate,
+ base, end, false);
+ if (retcode != 0)
+ goto comment;
+ goto top;
+ case char_EOL:
+ case '\f':
+ end = sptr;
+ goto cend;
+ }
+ /*
+ * We got to the end of the buffer while inside a comment.
+ * If there is a possibility that we must pass the comment
+ * to an external procedure, move what we have collected
+ * so far into a private buffer now.
+ */
+#define comment_line da.buf
+ --sptr;
+ comment_line[1] = 0;
+ {
+ /* Could be an externally processable comment. */
+ uint len = sptr + 1 - base;
+ if (len > sizeof(comment_line))
+ len = sizeof(comment_line);
+
+ memcpy(comment_line, base, len);
+ daptr = comment_line + len;
+ }
+ da.base = comment_line;
+ da.is_dynamic = false;
+ }
+ /* Enter here to continue scanning a comment. */
+ /* daptr must be set. */
+ cont_comment:for (;;) {
+ switch ((c = scan_getc())) {
+ default:
+ if (c < 0)
+ switch (c) {
+ case INTC:
+ case CALLC:
+ da.next = daptr;
+ scan_type = scanning_comment;
+ goto pause;
+ case EOFC:
+ /*
+ * One would think that an EOF in a comment
+ * should be a syntax error, but there are
+ * quite a number of files that end that way.
+ */
+ goto end_comment;
+ default:
+ sreturn(gs_error_syntaxerror);
+ }
+ if (daptr < comment_line + max_comment_line)
+ *daptr++ = c;
+ continue;
+ case char_CR:
+ case char_EOL:
+ case '\f':
+ end_comment:
+ retcode = scan_comment(i_ctx_p, myref, &sstate,
+ comment_line, daptr, true);
+ if (retcode != 0)
+ goto comment;
+ goto top;
+ }
+ }
+#undef comment_line
+ /*NOTREACHED */
+ case EOFC:
+ if (pstack != 0) {
+ if (check_only)
+ goto pause;
+ sreturn(gs_error_syntaxerror);
+ }
+ retcode = scan_EOF;
+ break;
+ case ERRC:
+ sreturn(gs_error_ioerror);
+
+ /* Check for a Level 2 funny name (<< or >>). */
+ /* c is '<' or '>'. We already did an ensure2. */
+ try_funny_name:
+ {
+ int c1 = scan_getc();
+
+ if (c1 == c) {
+ s1[0] = s1[1] = c;
+ name_ref(imemory, s1, 2, myref, 1); /* can't fail */
+ goto have_name;
+ }
+ scan_putback();
+ }
+ sreturn(gs_error_syntaxerror);
+
+ /* Handle separately the names that might be a number. */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '.':
+ sign = 0;
+ nr: /*
+ * Skip a leading sign, if any, by conditionally passing
+ * sptr + 1 rather than sptr. Also, if the last character
+ * in the buffer is a CR, we must stop the scan 1 character
+ * early, to be sure that we can test for CR+LF within the
+ * buffer, by passing endptr rather than endptr + 1.
+ */
+ retcode = scan_number(sptr + (sign & 1),
+ endptr /*(*endptr == char_CR ? endptr : endptr + 1) */ ,
+ sign, myref, &newptr, i_ctx_p->scanner_options);
+ if (retcode == 1 && decoder[newptr[-1]] == ctype_space) {
+ sptr = newptr - 1;
+ if (*sptr == char_CR && sptr[1] == char_EOL)
+ sptr++;
+ retcode = 0;
+ ref_mark_new(myref);
+ break;
+ }
+ name_type = 0;
+ try_number = true;
+ goto do_name;
+ case '+':
+ sign = 1;
+ goto nr;
+ case '-':
+ sign = -1;
+ goto nr;
+
+ /* Check for a binary object */
+#define case4(c) case c: case c+1: case c+2: case c+3
+ case4(128): case4(132): case4(136): case4(140):
+ case4(144): case4(148): case4(152): case4(156):
+#undef case4
+ if (recognize_btokens()) {
+ scan_end_inline();
+ retcode = scan_binary_token(i_ctx_p, myref, &sstate);
+ scan_begin_inline();
+ if (retcode == scan_Refill)
+ goto pause;
+ break;
+ }
+ /* Not a binary object, fall through. */
+
+ /* The default is a name. */
+ default:
+ if (c < 0) {
+ dynamic_init(&da, name_memory(imemory)); /* da state must be clean */
+ scan_type = scanning_none;
+ goto pause;
+ }
+ /* Populate the switch with enough cases to force */
+ /* simple compilers to use a dispatch rather than tests. */
+ case '!':
+ case '"':
+ case '#':
+ case '$':
+ case '&':
+ case '\'':
+ case '*':
+ case ',':
+ case '=':
+ case ':':
+ case ';':
+ case '?':
+ case '@':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '\\':
+ case '^':
+ case '_':
+ case '`':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ case '|':
+ case '~':
+ begin_name:
+ /* Common code for scanning a name. */
+ /* try_number and name_type are already set. */
+ /* We know c has ctype_name (or maybe ctype_btoken, */
+ /* or is ^D) or is a digit. */
+ name_type = 0;
+ try_number = false;
+ do_name:
+ /* Try to scan entirely within the stream buffer. */
+ /* We stop 1 character early, so we don't switch buffers */
+ /* looking ahead if the name is terminated by \r\n. */
+ da.base = (byte *) sptr;
+ da.is_dynamic = false;
+ {
+ const byte *endp1 = endptr - 1;
+
+ do {
+ if (sptr >= endp1) /* stop 1 early! */
+ goto dyn_name;
+ }
+ while (decoder[*++sptr] <= max_name_ctype || *sptr == ctrld); /* digit or name */
+ }
+ /* Name ended within the buffer. */
+ daptr = (byte *) sptr;
+ c = *sptr;
+ goto nx;
+ dyn_name: /* Name extended past end of buffer. */
+ scan_end_inline();
+ /* Initialize the dynamic area. */
+ /* We have to do this before the next */
+ /* sgetc, which will overwrite the buffer. */
+ da.limit = (byte *)++ sptr;
+ da.memory = name_memory(imemory);
+ retcode = dynamic_grow(&da, da.limit, name_max_string);
+ if (retcode < 0) {
+ dynamic_save(&da);
+ if (retcode != gs_error_VMerror)
+ sreturn(retcode);
+ scan_type = scanning_name;
+ goto pause_ret;
+ }
+ daptr = da.next;
+ /* Enter here to continue scanning a name. */
+ /* daptr must be set. */
+ cont_name:scan_begin_inline();
+ while (decoder[c = scan_getc()] <= max_name_ctype || c == ctrld) {
+ if (daptr == da.limit) {
+ retcode = dynamic_grow(&da, daptr,
+ name_max_string);
+ if (retcode < 0) {
+ dynamic_save(&da);
+ if (retcode != gs_error_VMerror)
+ sreturn(retcode);
+ scan_putback();
+ scan_type = scanning_name;
+ goto pause_ret;
+ }
+ daptr = da.next;
+ }
+ *daptr++ = c;
+ }
+ nx:switch (decoder[c]) {
+ case ctype_other:
+ if (c == ctrld) /* see above */
+ break;
+ case ctype_btoken:
+ scan_putback();
+ break;
+ case ctype_space:
+ /* Check for \r\n */
+ if (c == char_CR) {
+ if (sptr >= endptr) { /* ensure2 *//* We have to check specially for */
+ /* the case where the very last */
+ /* character of a file is a CR. */
+ if (s->end_status != EOFC) {
+ sptr--;
+ goto pause_name;
+ }
+ } else if (sptr[1] == char_EOL)
+ sptr++;
+ }
+ break;
+ case ctype_exception:
+ switch (c) {
+ case INTC:
+ case CALLC:
+ goto pause_name;
+ case ERRC:
+ sreturn(gs_error_ioerror);
+ case EOFC:
+ break;
+ }
+ }
+ /* Check for a number */
+ if (try_number) {
+ const byte *base = da.base;
+
+ scan_sign(sign, base);
+ retcode = scan_number(base, daptr, sign, myref, &newptr, i_ctx_p->scanner_options);
+ if (retcode == 1) {
+ ref_mark_new(myref);
+ retcode = 0;
+ } else if (retcode != gs_error_syntaxerror) {
+ dynamic_free(&da);
+ if (name_type == 2)
+ sreturn(gs_error_syntaxerror);
+ break; /* might be gs_error_limitcheck */
+ }
+ }
+ if (da.is_dynamic) { /* We've already allocated the string on the heap. */
+ uint size = daptr - da.base;
+
+ retcode = name_ref(imemory, da.base, size, myref, -1);
+ if (retcode >= 0) {
+ dynamic_free(&da);
+ } else {
+ retcode = dynamic_resize(&da, size);
+ if (retcode < 0) { /* VMerror */
+ if (c != EOFC)
+ scan_putback();
+ scan_type = scanning_name;
+ goto pause_ret;
+ }
+ retcode = name_ref(imemory, da.base, size, myref, 2);
+ }
+ } else {
+ retcode = name_ref(imemory, da.base, (uint) (daptr - da.base),
+ myref, !s->foreign);
+ }
+ /* Done scanning. Check for preceding /'s. */
+ if (retcode < 0) {
+ if (retcode != gs_error_VMerror)
+ sreturn(retcode);
+ if (!da.is_dynamic) {
+ da.next = daptr;
+ dynamic_save(&da);
+ }
+ if (c != EOFC)
+ scan_putback();
+ scan_type = scanning_name;
+ goto pause_ret;
+ }
+ have_name:switch (name_type) {
+ case 0: /* ordinary executable name */
+ if (r_has_type(myref, t_name)) /* i.e., not a number */
+ r_set_attrs(myref, a_executable);
+ case 1: /* quoted name */
+ break;
+ case 2: /* immediate lookup */
+ {
+ ref *pvalue;
+
+ if (!r_has_type(myref, t_name) ||
+ (pvalue = dict_find_name(myref)) == 0) {
+ ref_assign(&sstate.s_error.object, myref);
+ r_set_attrs(&sstate.s_error.object,
+ a_executable); /* Adobe compatibility */
+ sreturn(gs_error_undefined);
+ }
+ if (pstack != 0 &&
+ r_space(pvalue) > ialloc_space(idmemory)
+ )
+ sreturn(gs_error_invalidaccess);
+ ref_assign_new(myref, pvalue);
+ }
+ }
+ }
+ sret:if (retcode < 0) {
+ scan_end_inline();
+ pstate->s_error = sstate.s_error;
+ if (pstack != 0) {
+ if (retcode == gs_error_undefined)
+ *pref = *osp; /* return undefined name as error token */
+ ref_stack_pop(&o_stack,
+ ref_stack_count(&o_stack) - (pdepth - 1));
+ }
+ return retcode;
+ }
+ /* If we are at the top level, return the object, */
+ /* otherwise keep going. */
+ if (pstack == 0) {
+ scan_end_inline();
+ return retcode;
+ }
+ snext:if_not_spush1() {
+ scan_end_inline();
+ scan_type = scanning_none;
+ goto save;
+ }
+ myref = osp;
+ goto top;
+
+ /* Pause for an interrupt or callout. */
+ pause_name:
+ /* If we're still scanning within the stream buffer, */
+ /* move the characters to the private buffer (da.buf) now. */
+ da.next = daptr;
+ dynamic_save(&da);
+ scan_type = scanning_name;
+ pause:
+ retcode = scan_Refill;
+ pause_ret:
+ scan_end_inline();
+ suspend:
+ if (pstack != 0)
+ osp--; /* myref */
+ save:
+ *pstate = sstate;
+ return retcode;
+
+ /* Handle a scanned comment. */
+ comment:
+ if (retcode < 0)
+ goto sret;
+ scan_end_inline();
+ scan_type = scanning_none;
+ goto save;
+}
diff --git a/psi/iscan.h b/psi/iscan.h
new file mode 100644
index 000000000..b37713206
--- /dev/null
+++ b/psi/iscan.h
@@ -0,0 +1,207 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Token scanner state and interface */
+/* Requires gsstruct.h, ostack.h, stream.h */
+
+#ifndef iscan_INCLUDED
+# define iscan_INCLUDED
+
+#include "sa85x.h"
+#include "sstring.h"
+#include "inamestr.h"
+
+/*
+ * Define the state of the scanner. Before first calling gs_scan_token,
+ * the caller must initialize the state by calling scanner_state_init.
+ * Most of the state is only used if scanning is suspended because of
+ * an interrupt or a callout.
+ *
+ * Note that the scanner state includes a reference to the stream being
+ * scanned. We do this primarily for Adobe compatibility, so that we can
+ * remove the source object from the operand stack after the initial checks.
+ *
+ * We expose the entire state definition to the caller so that
+ * the state can normally be allocated on the stack.
+ */
+#ifndef scanner_state_DEFINED
+# define scanner_state_DEFINED
+typedef struct scanner_state_s scanner_state;
+#endif
+
+/*
+ * Define a structure for dynamically growable strings.
+ * If is_dynamic is true, base/next/limit point to a string on the heap;
+ * if is_dynamic is false, base/next/limit point either to the local buffer
+ * or (only while control is inside gs_scan_token) into the source stream
+ * buffer.
+ */
+#define max_comment_line 255 /* max size of an externally processable comment */
+typedef struct dynamic_area_s {
+ byte *base;
+ byte *next;
+ byte *limit;
+ bool is_dynamic;
+ byte buf[max_name_string]; /* initial buffer, enough for a valid string */
+ gs_memory_t *memory;
+} dynamic_area;
+
+#define da_size(pda) ((uint)((pda)->limit - (pda)->base))
+typedef dynamic_area *da_ptr;
+
+/* Define state specific to binary tokens and binary object sequences. */
+typedef struct scan_binary_state_s {
+ int num_format;
+ int (*cont)(i_ctx_t *, ref *, scanner_state *);
+ ref bin_array;
+ uint index;
+ uint max_array_index; /* largest legal index in objects */
+ uint min_string_index; /* smallest legal index in strings */
+ uint top_size;
+ uint size;
+ int token_type; /* binary token type for error reporting */
+ ulong lsize; /* b.o.s. size ibid. */
+} scan_binary_state;
+
+/* Define the scanner state. */
+struct scanner_state_s {
+ ref s_file; /* source file */
+ uint s_pstack; /* stack depth when starting current */
+ /* procedure, after pushing old pstack */
+ uint s_pdepth; /* pstack for very first { encountered, */
+ /* for error recovery */
+ int s_options;
+ enum {
+ scanning_none,
+ scanning_binary,
+ scanning_comment,
+ scanning_name,
+ scanning_string
+ } s_scan_type;
+ dynamic_area s_da;
+ union sss_ { /* scanning sub-state */
+ scan_binary_state binary; /* binary */
+ struct sns_ { /* name */
+ int s_name_type; /* number of /'s preceding a name */
+ bool s_try_number; /* true if should try scanning name */
+ /* as number */
+ } s_name;
+ stream_state st; /* string */
+ stream_A85D_state a85d; /* string */
+ stream_AXD_state axd; /* string */
+ stream_PSSD_state pssd; /* string */
+ } s_ss;
+ /* The following are used only to return information for errors. */
+ struct se_ { /* scanner error */
+ ref object; /* normally t__invalid */
+ bool is_name; /* true if 'string' is name, false if string */
+#define SCANNER_MAX_ERROR_STRING 120 /* adhoc, for Adobe-compatible messages */
+ char string[SCANNER_MAX_ERROR_STRING+1]; /* normally empty */
+ } s_error;
+#define SCAN_INIT_ERROR(pstate)\
+ (make_t(&(pstate)->s_error.object, t__invalid),\
+ (pstate)->s_error.is_name = false,\
+ (pstate)->s_error.string[0] = 0)
+};
+
+/* Remember the allocator for proper freeing. */
+typedef struct scanner_state_dynamic_s {
+ scanner_state state;
+ gs_memory_t *mem;
+} scanner_state_dynamic;
+
+/* The type descriptor is public only for checking. */
+extern_st(st_scanner_state_dynamic);
+#define public_st_scanner_state_dynamic() /* in iscan.c */\
+ gs_public_st_complex_only(st_scanner_state_dynamic, scanner_state_dynamic, "scanner state",\
+ scanner_clear_marks, scanner_enum_ptrs, scanner_reloc_ptrs, 0)
+
+/* Initialize a scanner with a given set of options. */
+#define SCAN_FROM_STRING 1 /* true if string is source of data */
+ /* (for Level 1 `\' handling) */
+#define SCAN_CHECK_ONLY 2 /* true if just checking for syntax errors */
+ /* and complete statements (no value) */
+#define SCAN_PROCESS_COMMENTS 4 /* return scan_Comment for comments */
+ /* (all comments or only non-DSC) */
+#define SCAN_PROCESS_DSC_COMMENTS 8 /* return scan_DSC_Comment */
+#define SCAN_PDF_RULES 16 /* PDF scanning rules (for names) */
+#define SCAN_PDF_INV_NUM 32 /* Adobe ignores invalid numbers */
+ /* This is for compatibility with Adobe */
+ /* Acrobat Reader */
+#define SCAN_PDF_UNSIGNED 64 /* Scan 2147483648..4294967295 as unsigned numbers */
+ /* This is needed in some contexts for */
+ /* compatibility with Adobe */
+#define SCAN_CPSI_MODE 128 /* Flag to indicate CPSI compatible integer parsing */
+
+void gs_scanner_init_options(scanner_state *sstate, const ref *fop,
+ int options);
+#define gs_scanner_init(sstate, fop)\
+ gs_scanner_init_options(sstate, fop, 0)
+void gs_scanner_init_stream_options(scanner_state *sstate, stream *s,
+ int options);
+#define gs_scanner_init_stream(sstate, s)\
+ gs_scanner_init_stream_options(sstate, s, 0)
+
+/*
+ * Read a token from a stream. As usual, 0 is a normal return,
+ * <0 is an error. There are also some special return codes:
+ */
+#define scan_BOS 1 /* binary object sequence */
+#define scan_EOF 2 /* end of stream */
+#define scan_Refill 3 /* get more input data, then call again */
+#define scan_Comment 4 /* comment, non-DSC if processing DSC */
+#define scan_DSC_Comment 5 /* DSC comment */
+int gs_scan_token(i_ctx_t *i_ctx_p, ref * pref, scanner_state * pstate);
+
+/*
+ * Read a token from a string. Return like gs_scan_token, but also
+ * update the string to move past the token (if no error).
+ */
+int gs_scan_string_token_options(i_ctx_t *i_ctx_p, ref * pstr, ref * pref,
+ int options);
+#define gs_scan_string_token(i_ctx_p, pstr, pref)\
+ gs_scan_string_token_options(i_ctx_p, pstr, pref, 0)
+
+/*
+ * Return the "error object" to be stored in $error.command instead of
+ * --token--, if any, or -1 if no special error object is required.
+ */
+int gs_scanner_error_object(i_ctx_t *i_ctx_p, const scanner_state *pstate,
+ ref *pseo);
+
+/*
+ * Handle a scan_Refill return from gs_scan_token.
+ * This may return o_push_estack, 0 (meaning just call gs_scan_token
+ * again), or an error code.
+ */
+int gs_scan_handle_refill(i_ctx_t *i_ctx_p, scanner_state * pstate,
+ bool save, op_proc_t cont);
+
+/*
+ * Define the procedure "hook" for parsing DSC comments. If not NULL,
+ * this procedure is called for every DSC comment seen by the scanner.
+ */
+extern int (*gs_scan_dsc_proc) (const byte *, uint);
+
+/*
+ * Define the procedure "hook" for parsing general comments. If not NULL,
+ * this procedure is called for every comment seen by the scanner.
+ * If both gs_scan_dsc_proc and gs_scan_comment_proc are set,
+ * gs_scan_comment_proc is called only for non-DSC comments.
+ */
+extern int (*gs_scan_comment_proc) (const byte *, uint);
+
+#endif /* iscan_INCLUDED */
diff --git a/psi/iscanbin.c b/psi/iscanbin.c
new file mode 100644
index 000000000..aefb9abe0
--- /dev/null
+++ b/psi/iscanbin.c
@@ -0,0 +1,941 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Ghostscript binary token scanner and writer */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "gsutil.h"
+#include "gxalloc.h" /* for names_array in allocator */
+#include "stream.h"
+#include "strimpl.h" /* for sfilter.h */
+#include "sfilter.h" /* for iscan.h */
+#include "ierrors.h"
+#include "ialloc.h"
+#include "iddict.h"
+#include "dstack.h" /* for immediately evaluated names */
+#include "ostack.h" /* must precede iscan.h */
+#include "iname.h"
+#include "iscan.h" /* for scan_Refill */
+#include "iscanbin.h"
+#include "iutil.h"
+#include "ivmspace.h"
+#include "store.h"
+#include "btoken.h"
+#include "ibnum.h"
+
+/* Define the binary token types. */
+typedef enum {
+ BT_SEQ = 128, /* binary object sequence: */
+ BT_SEQ_IEEE_MSB = 128, /* IEEE floats, big-endian */
+ BT_SEQ_IEEE_LSB = 129, /* IEEE float, little-endian */
+ BT_SEQ_NATIVE_MSB = 130, /* native floats, big-endian */
+ BT_SEQ_NATIVE_LSB = 131, /* native floats, little-endian */
+#define BT_IS_SEQ(btype) (((btype) & ~3) == BT_SEQ)
+ BT_INT32_MSB = 132,
+ BT_INT32_LSB = 133,
+ BT_INT16_MSB = 134,
+ BT_INT16_LSB = 135,
+ BT_INT8 = 136,
+ BT_FIXED = 137,
+ BT_FLOAT_IEEE_MSB = 138,
+ BT_FLOAT_IEEE_LSB = 139,
+ BT_FLOAT_NATIVE = 140,
+ BT_BOOLEAN = 141,
+ BT_STRING_256 = 142,
+ BT_STRING_64K_MSB = 143,
+ BT_STRING_64K_LSB = 144,
+ BT_LITNAME_SYSTEM = 145,
+ BT_EXECNAME_SYSTEM = 146,
+ BT_LITNAME_USER = 147,
+ BT_EXECNAME_USER = 148,
+ BT_NUM_ARRAY = 149
+} bin_token_type_t;
+
+#define MIN_BIN_TOKEN_TYPE 128
+#define MAX_BIN_TOKEN_TYPE 159
+#define NUM_BIN_TOKEN_TYPES (MAX_BIN_TOKEN_TYPE - MIN_BIN_TOKEN_TYPE + 1)
+
+/* Define the number of required initial bytes for binary tokens. */
+static const byte bin_token_bytes[NUM_BIN_TOKEN_TYPES] =
+{
+ 4, 4, 4, 4, 5, 5, 3, 3, 2, 2, 5, 5, 5,
+ 2, 2, 3, 3, 2, 2, 2, 2, 4,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* undefined */
+};
+
+/* Define the number formats for those binary tokens that need them. */
+static const byte bin_token_num_formats[NUM_BIN_TOKEN_TYPES] =
+{
+ num_msb + num_float_IEEE, /* BT_SEQ_IEEE_MSB */
+ num_lsb + num_float_IEEE, /* BT_SEQ_IEEE_LSB */
+#if ARCH_FLOATS_ARE_IEEE && BYTE_SWAP_IEEE_NATIVE_REALS
+ /* Treat native floats like IEEE floats for byte swapping. */
+ num_msb + num_float_IEEE, /* BT_SEQ_NATIVE_MSB */
+ num_lsb + num_float_IEEE, /* BT_SEQ_NATIVE_LSB */
+#else
+ num_msb + num_float_native, /* BT_SEQ_NATIVE_MSB */
+ num_lsb + num_float_native, /* BT_SEQ_NATIVE_LSB */
+#endif
+ num_msb + num_int32, /* BT_INT32_MSB */
+ num_lsb + num_int32, /* BT_INT32_LSB */
+ num_msb + num_int16, /* BT_INT16_MSB */
+ num_lsb + num_int16, /* BT_INT16_LSB */
+ 0, /* BT_INT8, not used */
+ 0, /* BT_FIXED, not used */
+ num_msb + num_float_IEEE, /* BT_FLOAT_IEEE_MSB */
+ num_lsb + num_float_IEEE, /* BT_FLOAT_IEEE_LSB */
+ num_float_native, /* BT_FLOAT_NATIVE */
+ 0, /* BT_BOOLEAN, not used */
+ 0, /* BT_STRING_256, not used */
+ num_msb, /* BT_STRING_64K_MSB */
+ num_lsb /* BT_STRING_64K_LSB */
+ /* rest not used */
+};
+
+/* Binary object sequence element types */
+typedef enum {
+ BS_TYPE_NULL = 0,
+ BS_TYPE_INTEGER = 1,
+ BS_TYPE_REAL = 2,
+ BS_TYPE_NAME = 3,
+ BS_TYPE_BOOLEAN = 4,
+ BS_TYPE_STRING = 5,
+ BS_TYPE_EVAL_NAME = 6,
+ BS_TYPE_ARRAY = 9,
+ BS_TYPE_MARK = 10,
+ /*
+ * We extend the PostScript language definition by allowing
+ * dictionaries in binary object sequences. The data for
+ * a dictionary is like that for an array, with the following
+ * changes:
+ * - If the size is an even number, the value is the index of
+ * the first of a series of alternating keys and values.
+ * - If the size is 1, the value is the index of another
+ * object (which must also be a dictionary, and must not have
+ * size = 1); this object represents the same object as that one.
+ */
+ BS_TYPE_DICTIONARY = 15
+} bin_seq_type_t;
+
+#define BS_EXECUTABLE 128
+#define SIZEOF_BIN_SEQ_OBJ ((uint)8)
+
+/* Forward references */
+static int scan_bos(i_ctx_t *, ref *, scanner_state *);
+static void scan_bos_error(scanner_state *, const char *);
+static int scan_bin_scalar(i_ctx_t *, ref *, scanner_state *);
+static int scan_bin_get_name(scanner_state *, const gs_memory_t *mem, const ref *, int, ref *, const char *);
+static int scan_bin_num_array_continue(i_ctx_t *, ref *, scanner_state *);
+static int scan_bin_string_continue(i_ctx_t *, ref *, scanner_state *);
+static int scan_bos_continue(i_ctx_t *, ref *, scanner_state *);
+static byte *scan_bos_resize(i_ctx_t *, scanner_state *, uint, uint);
+static int scan_bos_string_continue(i_ctx_t *, ref *, scanner_state *);
+
+/* Scan a binary token. Called from the main scanner */
+/* when it encounters an ASCII code 128-159, */
+/* if binary tokens are being recognized (object format != 0). */
+int
+scan_binary_token(i_ctx_t *i_ctx_p, ref *pref, scanner_state *pstate)
+{
+ stream *const s = pstate->s_file.value.pfile;
+ scan_binary_state *const pbs = &pstate->s_ss.binary;
+ s_declare_inline(s, p, rlimit);
+ int btype, code;
+ uint wanted;
+
+ s_begin_inline(s, p, rlimit);
+ pbs->token_type = btype = *p;
+ wanted = bin_token_bytes[btype - MIN_BIN_TOKEN_TYPE] - 1;
+ if (rlimit - p < wanted) {
+ s_end_inline(s, p - 1, rlimit);
+ pstate->s_scan_type = scanning_none;
+ code = scan_Refill;
+ } else {
+ pbs->num_format = bin_token_num_formats[btype - MIN_BIN_TOKEN_TYPE];
+ if (BT_IS_SEQ(btype))
+ code = scan_bos(i_ctx_p, pref, pstate);
+ else
+ code = scan_bin_scalar(i_ctx_p, pref, pstate);
+ }
+ if (code == scan_Refill && s->end_status == EOFC)
+ code = gs_note_error(gs_error_syntaxerror);
+ if (code < 0 && pstate->s_error.string[0] == 0)
+ snprintf(pstate->s_error.string, sizeof(pstate->s_error.string),
+ "binary token, type=%d", btype);
+ return code;
+}
+
+/* Scan a binary object sequence. */
+static int
+scan_bos(i_ctx_t *i_ctx_p, ref *pref, scanner_state *pstate)
+{
+ stream *const s = pstate->s_file.value.pfile;
+ scan_binary_state *const pbs = &pstate->s_ss.binary;
+ s_declare_inline(s, p, rlimit);
+ int num_format = pbs->num_format;
+ int code;
+
+ s_begin_inline(s, p, rlimit);
+ {
+ uint rcnt = rlimit - p;
+ uint top_size = p[1];
+ uint hsize, size;
+
+ if (top_size == 0) {
+ /* Extended header (2-byte array size, 4-byte length) */
+ uint lsize;
+
+ if (rcnt < 7) {
+ s_end_inline(s, p - 1, rlimit);
+ pstate->s_scan_type = scanning_none;
+ return scan_Refill;
+ }
+ pbs->top_size = top_size = sdecodeushort(p + 2, num_format);
+ pbs->lsize = lsize = sdecodeint32(p + 4, num_format);
+ if ((size = lsize) != lsize) {
+ scan_bos_error(pstate, "bin obj seq length too large");
+ return_error(gs_error_limitcheck);
+ }
+ hsize = 8;
+ } else {
+ /* Normal header (1-byte array size, 2-byte length). */
+ /* We already checked rcnt >= 3. */
+ pbs->top_size = top_size;
+ pbs->lsize = size = sdecodeushort(p + 2, num_format);
+ hsize = 4;
+ }
+ if (size < hsize || (size - hsize) >> 3 < top_size) {
+ scan_bos_error(pstate, "sequence too short");
+ return_error(gs_error_syntaxerror); /* size too small */
+ }
+ { /* Preliminary syntax check to avoid potentialy large
+ * memory allocation on junk data. Bug 688833
+ */
+ const unsigned char *q, *rend = p + hsize + top_size*8;
+
+ if (rend > rlimit)
+ rend = rlimit;
+ for (q = p + hsize + 1; q < rend; q += 8) {
+ int c = q[-1] & 0x7f;
+ if (c > 10 && c != BS_TYPE_DICTIONARY) {
+ scan_bos_error(pstate, "invalid object type");
+ return_error(gs_error_syntaxerror);
+ }
+ if (*q != 0) {
+ scan_bos_error(pstate, "non-zero unused field");
+ return_error(gs_error_syntaxerror);
+ }
+ }
+ }
+ /*
+ * Preallocate an array large enough for the worst case,
+ * namely, all objects and no strings. Note that we must
+ * divide size by 8, not sizeof(ref), since array elements
+ * in binary tokens always occupy 8 bytes regardless of the
+ * size of a ref.
+ */
+ code = ialloc_ref_array(&pbs->bin_array,
+ a_all + a_executable, size / 8,
+ "binary object sequence(objects)");
+ if (code < 0)
+ return code;
+ p += hsize - 1;
+ size -= hsize;
+ s_end_inline(s, p, rlimit);
+ pbs->max_array_index = pbs->top_size = top_size;
+ pbs->min_string_index = pbs->size = size;
+ pbs->index = 0;
+ pstate->s_da.is_dynamic = false;
+ pstate->s_da.base = pstate->s_da.next =
+ pstate->s_da.limit = pstate->s_da.buf;
+ code = scan_bos_continue(i_ctx_p, pref, pstate);
+ if (code == scan_Refill || code < 0) {
+ /* Clean up array for GC. */
+ uint index = pbs->index;
+
+ refset_null(pbs->bin_array.value.refs + index,
+ r_size(&pbs->bin_array) - index);
+ }
+ return code;
+ }
+}
+
+/* Report an error in a binary object sequence. */
+static void
+scan_bos_error(scanner_state *pstate, const char *msg)
+{
+ snprintf(pstate->s_error.string, sizeof(pstate->s_error.string),
+ "bin obj seq, type=%d, elements=%u, size=%lu, %s",
+ pstate->s_ss.binary.token_type,
+ pstate->s_ss.binary.top_size,
+ pstate->s_ss.binary.lsize, msg);
+}
+
+/* Scan a non-sequence binary token. */
+static int
+scan_bin_scalar(i_ctx_t *i_ctx_p, ref *pref, scanner_state *pstate)
+{
+ stream *const s = pstate->s_file.value.pfile;
+ scan_binary_state *const pbs = &pstate->s_ss.binary;
+ s_declare_inline(s, p, rlimit);
+ int num_format = pbs->num_format, code;
+ uint wanted, arg;
+
+ s_begin_inline(s, p, rlimit);
+ wanted = bin_token_bytes[*p - MIN_BIN_TOKEN_TYPE] - 1;
+ switch (*p) {
+ case BT_INT8:
+ make_int(pref, (p[1] ^ 128) - 128);
+ s_end_inline(s, p + 1, rlimit);
+ return 0;
+ case BT_FIXED:
+ num_format = p[1];
+ if (!num_is_valid(num_format))
+ return_error(gs_error_syntaxerror);
+ wanted = 1 + encoded_number_bytes(num_format);
+ if (rlimit - p < wanted) {
+ s_end_inline(s, p - 1, rlimit);
+ pstate->s_scan_type = scanning_none;
+ return scan_Refill;
+ }
+ code = sdecode_number(p + 2, num_format, pref);
+ goto rnum;
+ case BT_INT32_MSB:
+ case BT_INT32_LSB:
+ case BT_INT16_MSB:
+ case BT_INT16_LSB:
+ case BT_FLOAT_IEEE_MSB:
+ case BT_FLOAT_IEEE_LSB:
+ case BT_FLOAT_NATIVE:
+ code = sdecode_number(p + 1, num_format, pref);
+ rnum:
+ switch (code) {
+ case t_integer:
+ case t_real:
+ r_set_type(pref, code);
+ break;
+ case t_null:
+ return_error(gs_error_syntaxerror);
+ default:
+ return code;
+ }
+ s_end_inline(s, p + wanted, rlimit);
+ return 0;
+ case BT_BOOLEAN:
+ arg = p[1];
+ if (arg & ~1)
+ return_error(gs_error_syntaxerror);
+ make_bool(pref, arg);
+ s_end_inline(s, p + 1, rlimit);
+ return 0;
+ case BT_STRING_256:
+ arg = *++p;
+ goto str;
+ case BT_STRING_64K_MSB:
+ case BT_STRING_64K_LSB:
+ arg = sdecodeushort(p + 1, num_format);
+ p += 2;
+ str:
+ if (s->foreign && rlimit - p >= arg) {
+ /*
+ * Reference the string directly in the buffer. It is
+ * marked writable for consistency with the non-direct
+ * case, but since the "buffer" may be data compiled into
+ * the executable, it is probably actually read-only.
+ */
+ s_end_inline(s, p, rlimit);
+ make_const_string(pref, a_all | avm_foreign, arg, sbufptr(s));
+ sbufskip(s, arg);
+ return 0;
+ } else {
+ byte *str = ialloc_string(arg, "string token");
+
+ if (str == 0)
+ return_error(gs_error_VMerror);
+ s_end_inline(s, p, rlimit);
+ pstate->s_da.base = pstate->s_da.next = str;
+ pstate->s_da.limit = str + arg;
+ code = scan_bin_string_continue(i_ctx_p, pref, pstate);
+ if (code == scan_Refill || code < 0) {
+ pstate->s_da.is_dynamic = true;
+ make_null(&pbs->bin_array); /* clean up for GC */
+ pbs->cont = scan_bin_string_continue;
+ }
+ return code;
+ }
+ case BT_LITNAME_SYSTEM:
+ code = scan_bin_get_name(pstate, imemory, system_names_p, p[1],
+ pref, "system");
+ goto lname;
+ case BT_EXECNAME_SYSTEM:
+ code = scan_bin_get_name(pstate, imemory, system_names_p, p[1],
+ pref, "system");
+ goto xname;
+ case BT_LITNAME_USER:
+ code = scan_bin_get_name(pstate, imemory, user_names_p, p[1],
+ pref, "user");
+ lname:
+ if (code < 0)
+ return code;
+ s_end_inline(s, p + 1, rlimit);
+ return 0;
+ case BT_EXECNAME_USER:
+ code = scan_bin_get_name(pstate, imemory, user_names_p, p[1],
+ pref, "user");
+ xname:
+ if (code < 0)
+ return code;
+ r_set_attrs(pref, a_executable);
+ s_end_inline(s, p + 1, rlimit);
+ return 0;
+ case BT_NUM_ARRAY:
+ num_format = p[1];
+ if (!num_is_valid(num_format))
+ return_error(gs_error_syntaxerror);
+ arg = sdecodeushort(p + 2, num_format);
+ code = ialloc_ref_array(&pbs->bin_array, a_all, arg,
+ "number array token");
+ if (code < 0)
+ return code;
+ pbs->num_format = num_format;
+ pbs->index = 0;
+ p += 3;
+ s_end_inline(s, p, rlimit);
+ code = scan_bin_num_array_continue(i_ctx_p, pref, pstate);
+ if (code == scan_Refill || code < 0) {
+ /* Make sure the array is clean for the GC. */
+ refset_null(pbs->bin_array.value.refs + pbs->index,
+ arg - pbs->index);
+ pbs->cont = scan_bin_num_array_continue;
+ }
+ return code;
+ }
+ return_error(gs_error_syntaxerror);
+}
+
+/* Get a system or user name. */
+static int
+scan_bin_get_name(scanner_state *pstate, const gs_memory_t *mem,
+ const ref *pnames /*t_array*/, int index, ref *pref,
+ const char *usstring)
+{
+ /* Convert all errors to gs_error_undefined to match Adobe. */
+ if (pnames == 0 || array_get(mem, pnames, (long)index, pref) < 0 ||
+ !r_has_type(pref, t_name)) {
+ snprintf(pstate->s_error.string,
+ sizeof(pstate->s_error.string),
+ "%s%d", usstring, index);
+ pstate->s_error.is_name = true;
+ return_error(gs_error_undefined);
+ }
+ return 0;
+}
+
+/* Continue collecting a binary string. */
+static int
+scan_bin_string_continue(i_ctx_t *i_ctx_p, ref * pref, scanner_state * pstate)
+{
+ stream *const s = pstate->s_file.value.pfile;
+ byte *q = pstate->s_da.next;
+ uint wanted = pstate->s_da.limit - q;
+ uint rcnt;
+
+ /* We don't check the return status from 'sgets' here.
+ If there is an error in sgets, the condition rcnt==wanted
+ would be false and this function will return scan_Refill.
+ */
+ sgets(s, q, wanted, &rcnt);
+ if (rcnt == wanted) {
+ /* Finished collecting the string. */
+ make_string(pref, a_all | icurrent_space,
+ pstate->s_da.limit - pstate->s_da.base,
+ pstate->s_da.base);
+ return 0;
+ }
+ pstate->s_da.next = q + rcnt;
+ pstate->s_scan_type = scanning_binary;
+ return scan_Refill;
+}
+
+/* Continue scanning a binary number array. */
+static int
+scan_bin_num_array_continue(i_ctx_t *i_ctx_p, ref * pref,
+ scanner_state * pstate)
+{
+ stream *const s = pstate->s_file.value.pfile;
+ scan_binary_state *const pbs = &pstate->s_ss.binary;
+ uint index = pbs->index;
+ ref *np = pbs->bin_array.value.refs + index;
+ uint wanted = encoded_number_bytes(pbs->num_format);
+
+ for (; index < r_size(&pbs->bin_array); index++, np++) {
+ int code;
+
+ if (sbufavailable(s) < wanted) {
+ pbs->index = index;
+ pstate->s_scan_type = scanning_binary;
+ return scan_Refill;
+ }
+ code = sdecode_number(sbufptr(s), pbs->num_format, np);
+ switch (code) {
+ case t_integer:
+ case t_real:
+ r_set_type(np, code);
+ sbufskip(s, wanted);
+ break;
+ case t_null:
+ scan_bos_error(pstate, "bad number format");
+ return_error(gs_error_syntaxerror);
+ default:
+ return code;
+ }
+ }
+ *pref = pbs->bin_array;
+ return 0;
+}
+
+/*
+ * Continue scanning a binary object sequence. We preallocated space for
+ * the largest possible number of objects, but not for strings, since
+ * the latter would probably be a gross over-estimate. Instead,
+ * we wait until we see the first string or name, and allocate string space
+ * based on the hope that its string index is the smallest one we will see.
+ * If this turns out to be wrong, we may have to reallocate, and adjust
+ * all the pointers.
+ */
+static int
+scan_bos_continue(i_ctx_t *i_ctx_p, ref * pref, scanner_state * pstate)
+{
+ stream *const s = pstate->s_file.value.pfile;
+ scan_binary_state *const pbs = &pstate->s_ss.binary;
+ s_declare_inline(s, p, rlimit);
+ uint max_array_index = pbs->max_array_index;
+ uint min_string_index = pbs->min_string_index;
+ int num_format = pbs->num_format;
+ uint index = pbs->index;
+ uint size = pbs->size;
+ ref *abase = pbs->bin_array.value.refs;
+ int code;
+
+ pbs->cont = scan_bos_continue; /* in case of premature return */
+ s_begin_inline(s, p, rlimit);
+ for (; index < max_array_index; p += SIZEOF_BIN_SEQ_OBJ, index++) {
+ ref *op = abase + index;
+ uint osize;
+ int value, atype, attrs;
+
+ s_end_inline(s, p, rlimit); /* in case of error */
+ if (rlimit - p < SIZEOF_BIN_SEQ_OBJ) {
+ pbs->index = index;
+ pbs->max_array_index = max_array_index;
+ pbs->min_string_index = min_string_index;
+ pstate->s_scan_type = scanning_binary;
+ return scan_Refill;
+ }
+ if (p[2] != 0) { /* reserved, must be 0 */
+ scan_bos_error(pstate, "non-zero unused field");
+ return_error(gs_error_syntaxerror);
+ }
+ attrs = (p[1] & 128 ? a_executable : 0);
+ /*
+ * We always decode all 8 bytes of the object, so we can signal
+ * syntaxerror if any unused field is non-zero (per PLRM).
+ */
+ osize = sdecodeushort(p + 3, num_format);
+ value = sdecodeint32(p + 5, num_format);
+ switch (p[1] & 0x7f) {
+ case BS_TYPE_NULL:
+ if (osize | value) { /* unused */
+ scan_bos_error(pstate, "non-zero unused field");
+ return_error(gs_error_syntaxerror);
+ }
+ make_null(op);
+ break;
+ case BS_TYPE_INTEGER:
+ if (osize) { /* unused */
+ scan_bos_error(pstate, "non-zero unused field");
+ return_error(gs_error_syntaxerror);
+ }
+ make_int(op, value);
+ break;
+ case BS_TYPE_REAL:{
+ float vreal;
+
+ if (osize != 0) { /* fixed-point number */
+ if (osize > 31) {
+ scan_bos_error(pstate, "invalid number format");
+ return_error(gs_error_syntaxerror);
+ }
+ /* ldexp requires a signed 2nd argument.... */
+ vreal = (float)ldexp((double)value, -(int)osize);
+ } else {
+ code = sdecode_float(p + 5, num_format, &vreal);
+ if (code < 0) {
+ scan_bos_error(pstate, "invalid real number");
+ return code;
+ }
+ }
+ make_real(op, vreal);
+ break;
+ }
+ case BS_TYPE_BOOLEAN:
+ if (osize) { /* unused */
+ scan_bos_error(pstate, "non-zero unused field");
+ return_error(gs_error_syntaxerror);
+ }
+ make_bool(op, value != 0);
+ break;
+ case BS_TYPE_STRING:
+ attrs |= a_all;
+ str:
+ if (osize == 0) {
+ /* For zero-length strings, the offset */
+ /* doesn't matter, and may be zero. */
+ make_empty_string(op, attrs);
+ break;
+ }
+ if (value < max_array_index * SIZEOF_BIN_SEQ_OBJ ||
+ value + osize > size
+ ) {
+ scan_bos_error(pstate, "invalid string offset");
+ return_error(gs_error_syntaxerror);
+ }
+ if (value < min_string_index) {
+ /* We have to (re)allocate the strings. */
+ uint str_size = size - value;
+ byte *sbase;
+
+ if (pstate->s_da.is_dynamic)
+ sbase = scan_bos_resize(i_ctx_p, pstate, str_size,
+ index);
+ else
+ sbase = ialloc_string(str_size,
+ "bos strings");
+ if (sbase == 0)
+ return_error(gs_error_VMerror);
+ pstate->s_da.is_dynamic = true;
+ pstate->s_da.base = pstate->s_da.next = sbase;
+ pstate->s_da.limit = sbase + str_size;
+ min_string_index = value;
+ }
+ make_string(op, attrs | icurrent_space, osize,
+ pstate->s_da.base +
+ (value - min_string_index));
+ break;
+ case BS_TYPE_EVAL_NAME:
+ attrs |= a_readonly; /* mark as executable for later */
+ /* falls through */
+ case BS_TYPE_NAME:
+ switch (osize) {
+ case 0:
+ code = scan_bin_get_name(pstate, imemory,
+ user_names_p, value, op,
+ "user");
+ goto usn;
+ case 0xffff:
+ code = scan_bin_get_name(pstate, imemory,
+ system_names_p, value, op,
+ "system");
+ usn:
+ if (code < 0)
+ return code;
+ r_set_attrs(op, attrs);
+ break;
+ default:
+ goto str;
+ }
+ break;
+ case BS_TYPE_ARRAY:
+ atype = t_array;
+ arr:
+ if (value + osize > min_string_index ||
+ value & (SIZEOF_BIN_SEQ_OBJ - 1)
+ ) {
+ scan_bos_error(pstate, "bad array offset");
+ return_error(gs_error_syntaxerror);
+ }
+ {
+ uint aindex = value / SIZEOF_BIN_SEQ_OBJ;
+
+ max_array_index =
+ max(max_array_index, aindex + osize);
+ make_tasv_new(op, atype,
+ attrs | a_all | icurrent_space,
+ osize, refs, abase + aindex);
+ }
+ break;
+ case BS_TYPE_DICTIONARY: /* EXTENSION */
+ if ((osize & 1) != 0 && osize != 1)
+ return_error(gs_error_syntaxerror);
+ atype = t_mixedarray; /* mark as dictionary */
+ goto arr;
+ case BS_TYPE_MARK:
+ if (osize | value) { /* unused */
+ scan_bos_error(pstate, "non-zero unused field");
+ return_error(gs_error_syntaxerror);
+ }
+ make_mark(op);
+ break;
+ default:
+ scan_bos_error(pstate, "invalid object type");
+ return_error(gs_error_syntaxerror);
+ }
+ }
+ s_end_inline(s, p, rlimit);
+ /* Shorten the objects to remove the space that turned out */
+ /* to be used for strings. */
+ pbs->index = max_array_index;
+ iresize_ref_array(&pbs->bin_array, max_array_index,
+ "binary object sequence(objects)");
+ code = scan_bos_string_continue(i_ctx_p, pref, pstate);
+ if (code == scan_Refill)
+ pbs->cont = scan_bos_string_continue;
+ return code;
+}
+
+/* Reallocate the strings for a binary object sequence, */
+/* adjusting all the pointers to them from objects. */
+static byte *
+scan_bos_resize(i_ctx_t *i_ctx_p, scanner_state * pstate, uint new_size,
+ uint index)
+{
+ scan_binary_state *const pbs = &pstate->s_ss.binary;
+ uint old_size = da_size(&pstate->s_da);
+ byte *old_base = pstate->s_da.base;
+ byte *new_base = iresize_string(old_base, old_size, new_size,
+ "scan_bos_resize");
+ byte *relocated_base = new_base + (new_size - old_size);
+ uint i;
+ ref *aptr = pbs->bin_array.value.refs;
+
+ if (new_base == 0)
+ return 0;
+ /* Since the allocator normally extends strings downward, */
+ /* it's quite possible that new and old addresses are the same. */
+ if (relocated_base != old_base)
+ for (i = index; i != 0; i--, aptr++)
+ if (r_has_type(aptr, t_string) && r_size(aptr) != 0)
+ aptr->value.bytes =
+ aptr->value.bytes - old_base + relocated_base;
+ return new_base;
+}
+
+/* Continue reading the strings for a binary object sequence. */
+static int
+scan_bos_string_continue(i_ctx_t *i_ctx_p, ref * pref,
+ scanner_state * pstate)
+{
+ scan_binary_state *const pbs = &pstate->s_ss.binary;
+ ref rstr;
+ ref *op;
+ int code = scan_bin_string_continue(i_ctx_p, &rstr, pstate);
+ uint space = ialloc_space(idmemory);
+ bool rescan = false;
+ uint i;
+
+ if (code != 0)
+ return code;
+
+ /* Fix up names. We must do this before creating dictionaries. */
+
+ for (op = pbs->bin_array.value.refs, i = r_size(&pbs->bin_array);
+ i != 0; i--, op++
+ )
+ switch (r_type(op)) {
+ case t_string:
+ if (r_has_attr(op, a_write)) /* a real string */
+ break;
+ /* This is actually a name; look it up now. */
+ {
+ uint attrs = r_type_attrs(op) & (a_read | a_executable);
+
+ code = name_ref(imemory, op->value.bytes, r_size(op), op, 1);
+ if (code < 0)
+ return code;
+ r_set_attrs(op, attrs);
+ }
+ /* falls through */
+ case t_name:
+ if (r_has_attr(op, a_read)) { /* BS_TYPE_EVAL_NAME */
+ ref *defp = dict_find_name(op);
+
+ if (defp == 0)
+ return_error(gs_error_undefined);
+ store_check_space(space, defp);
+ ref_assign(op, defp);
+ }
+ break;
+ case t_mixedarray: /* actually a dictionary */
+ rescan = true;
+ }
+
+ /* Create dictionaries, if any. */
+
+ if (rescan) {
+ rescan = false;
+ for (op = pbs->bin_array.value.refs, i = r_size(&pbs->bin_array);
+ i != 0; i--, op++
+ )
+ switch (r_type(op)) {
+ case t_mixedarray: /* actually a dictionary */
+ {
+ uint count = r_size(op);
+ ref rdict;
+
+ if (count == 1) {
+ /* Indirect reference. */
+ if (op->value.refs < op)
+ ref_assign(&rdict, op->value.refs);
+ else {
+ rescan = true;
+ continue;
+ }
+ } else {
+ code = dict_create(count >> 1, &rdict);
+ if (code < 0)
+ return code;
+ while (count) {
+ count -= 2;
+ code = idict_put(&rdict,
+ &op->value.refs[count],
+ &op->value.refs[count + 1]);
+ if (code < 0)
+ return code;
+ }
+ }
+ r_set_attrs(&rdict, a_all);
+ r_copy_attrs(&rdict, a_executable, op);
+ ref_assign(op, &rdict);
+ }
+ break;
+ }
+ }
+
+ /* If there were any forward indirect references, fix them up now. */
+
+ if (rescan)
+ for (op = pbs->bin_array.value.refs, i = r_size(&pbs->bin_array);
+ i != 0; i--, op++
+ )
+ if (r_has_type(op, t_mixedarray)) {
+ const ref *piref = op->value.const_refs;
+ ref rdict;
+
+ if (r_has_type(piref, t_mixedarray)) /* ref to indirect */
+ return_error(gs_error_syntaxerror);
+ ref_assign(&rdict, piref);
+ r_copy_attrs(&rdict, a_executable, op);
+ ref_assign(op, &rdict);
+ }
+
+ ref_assign(pref, &pbs->bin_array);
+ r_set_size(pref, pbs->top_size);
+ return scan_BOS;
+}
+
+/* ---------------- Writing ---------------- */
+
+/*
+ * Encode a single object for a binary object sequence, for printobject and
+ * write object. Note that this does not modify the always-unused byte (1),
+ * but it always write bytes 0 and 2-7.
+ */
+int
+encode_binary_token(i_ctx_t *i_ctx_p, const ref *obj, ps_int *ref_offset,
+ ps_int *char_offset, byte *str)
+{
+ bin_seq_type_t type;
+ uint size = 0;
+ int format = (int)ref_binary_object_format.value.intval;
+ ps_int value = 0;
+ ref nstr;
+
+ switch (r_type(obj)) {
+ case t_null:
+ type = BS_TYPE_NULL;
+ break; /* always set all fields */
+ case t_mark:
+ type = BS_TYPE_MARK;
+ break; /* always set all fields */
+ case t_integer:
+ type = BS_TYPE_INTEGER;
+ value = obj->value.intval;
+ break;
+ case t_real:
+ type = BS_TYPE_REAL;
+ if (sizeof(obj->value.realval) != sizeof(int)) {
+ /* The PLRM allocates exactly 4 bytes for reals. */
+ return_error(gs_error_rangecheck);
+ }
+ value = *(const ps_int *)&obj->value.realval;
+#if !(ARCH_FLOATS_ARE_IEEE && BYTE_SWAP_IEEE_NATIVE_REALS)
+ if (format >= 3) {
+ /* Never byte-swap native reals -- use native byte order. */
+ format = 4 - ARCH_IS_BIG_ENDIAN;
+ }
+#endif
+ break;
+ case t_boolean:
+ type = BS_TYPE_BOOLEAN;
+ value = obj->value.boolval;
+ break;
+ case t_array:
+ type = BS_TYPE_ARRAY;
+ size = r_size(obj);
+ goto aod;
+ case t_dictionary: /* EXTENSION */
+ type = BS_TYPE_DICTIONARY;
+ size = dict_length(obj) << 1;
+ aod:value = *ref_offset;
+ *ref_offset += size * SIZEOF_BIN_SEQ_OBJ;
+ break;
+ case t_string:
+ type = BS_TYPE_STRING;
+nos:
+ size = r_size(obj);
+ value = *char_offset;
+ *char_offset += size;
+ break;
+ case t_name:
+ type = BS_TYPE_NAME;
+ name_string_ref(imemory, obj, &nstr);
+ r_copy_attrs(&nstr, a_executable, obj);
+ obj = &nstr;
+ goto nos;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ {
+ byte s0 = (byte) size, s1 = (byte) (size >> 8);
+ byte v0 = (byte) value, v1 = (byte) (value >> 8),
+ v2 = (byte) (value >> 16), v3 = (byte) (value >> 24);
+
+ if (format & 1) {
+ /* Store big-endian */
+ str[2] = s1, str[3] = s0;
+ str[4] = v3, str[5] = v2, str[6] = v1, str[7] = v0;
+ } else {
+ /* Store little-endian */
+ str[2] = s0, str[3] = s1;
+ str[4] = v0, str[5] = v1, str[6] = v2, str[7] = v3;
+ }
+ }
+ if (r_has_attr(obj, a_executable))
+ type += BS_EXECUTABLE;
+ str[0] = (byte) type;
+ return 0;
+}
diff --git a/psi/iscanbin.h b/psi/iscanbin.h
new file mode 100644
index 000000000..827a6de7e
--- /dev/null
+++ b/psi/iscanbin.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to binary token scanner */
+
+#ifndef iscanbin_INCLUDED
+# define iscanbin_INCLUDED
+
+/*
+ * Scan a binary token. The main scanner calls this iff recognize_btokens()
+ * is true. Return gs_error_unregistered if Level 2 features are not included.
+ * Return 0 or scan_BOS on success, <0 on failure.
+ *
+ * This header file exists only because there are two implementations of
+ * this procedure: a dummy one for Level 1 systems, and the real one.
+ * The interface is entirely internal to the scanner.
+ */
+int scan_binary_token(i_ctx_t *i_ctx_p, ref *pref, scanner_state *pstate);
+
+#endif /* iscanbin_INCLUDED */
diff --git a/psi/iscannum.c b/psi/iscannum.c
new file mode 100644
index 000000000..0b52dcb27
--- /dev/null
+++ b/psi/iscannum.c
@@ -0,0 +1,379 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Number scanner for Ghostscript interpreter */
+#include "math_.h"
+#include "ghost.h"
+#include "ierrors.h"
+#include "scommon.h"
+#include "iscan.h"
+#include "iscannum.h" /* defines interface */
+#include "scanchar.h"
+#include "store.h"
+
+/*
+ * Warning: this file has a "spaghetti" control structure. But since this
+ * code accounts for over 10% of the execution time of some PostScript
+ * files, this is one of the few places we feel this is justified.
+ */
+
+/*
+ * Scan a number. If the number consumes the entire string, return 0;
+ * if not, set *psp to the first character beyond the number and return 1.
+ */
+int
+scan_number(const byte * str, const byte * end, int sign,
+ ref * pref, const byte ** psp, int scanner_options)
+{
+ const byte *sp = str;
+#define GET_NEXT(cvar, sp, end_action)\
+ if (sp >= end) { end_action; } else cvar = *sp++
+
+ /*
+ * Powers of 10 up to 6 can be represented accurately as
+ * a single-precision float.
+ */
+#define NUM_POWERS_10 6
+ static const float powers_10[NUM_POWERS_10 + 1] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6
+ };
+ static const double neg_powers_10[NUM_POWERS_10 + 1] = {
+ 1e0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6
+ };
+
+ ps_int ival;
+ double dval;
+ int exp10;
+ int code = 0;
+ int c, d;
+ ps_uint max_scan; /* max signed or unsigned int */
+ ps_int max_ps_int_scan, min_ps_int_scan;
+ const byte *const decoder = scan_char_decoder;
+#define IS_DIGIT(d, c)\
+ ((d = decoder[c]) < 10)
+#define WOULD_OVERFLOW(val, d, maxv)\
+ (val >= maxv / 10 && (val > maxv / 10 || d > (int64_t)(maxv % 10)))
+
+ GET_NEXT(c, sp, return_error(gs_error_syntaxerror));
+ if (!IS_DIGIT(d, c)) {
+ if (c != '.')
+ return_error(gs_error_syntaxerror);
+ /* Might be a number starting with '.'. */
+ GET_NEXT(c, sp, return_error(gs_error_syntaxerror));
+ if (!IS_DIGIT(d, c))
+ return_error(gs_error_syntaxerror);
+ ival = 0;
+ goto i2r;
+ }
+ /* Accumulate an integer in ival. */
+ /* Do up to 4 digits without a loop, */
+ /* since we know this can't overflow and since */
+ /* most numbers have 4 (integer) digits or fewer. */
+ ival = d;
+ if (end - sp >= 3) { /* just check once */
+ if (!IS_DIGIT(d, (c = *sp))) {
+ sp++;
+ goto ind;
+ }
+ ival = ival * 10 + d;
+ if (!IS_DIGIT(d, (c = sp[1]))) {
+ sp += 2;
+ goto ind;
+ }
+ ival = ival * 10 + d;
+ sp += 3;
+ if (!IS_DIGIT(d, (c = sp[-1])))
+ goto ind;
+ ival = ival * 10 + d;
+ }
+
+ max_ps_int_scan = scanner_options & SCAN_CPSI_MODE ? MAX_PS_INT32 : MAX_PS_INT;
+ min_ps_int_scan = scanner_options & SCAN_CPSI_MODE ? MIN_PS_INT32 : MIN_PS_INT;
+
+ max_scan = scanner_options & SCAN_PDF_UNSIGNED && sign >= 0 ? ~((ps_int)0) : max_ps_int_scan;
+
+ for (;; ival = ival * 10 + d) {
+ GET_NEXT(c, sp, goto iret);
+ if (!IS_DIGIT(d, c))
+ break;
+ if (WOULD_OVERFLOW(((ps_uint)ival), d, max_scan)) {
+ if (ival == max_ps_int_scan / 10 && d == (max_ps_int_scan % 10) + 1 && sign < 0) {
+ GET_NEXT(c, sp, c = EOFC);
+ dval = -(double)min_ps_int_scan;
+ if (c == 'e' || c == 'E') {
+ exp10 = 0;
+ goto fs;
+ } else if (c == '.') {
+ GET_NEXT(c, sp, c = EOFC);
+ exp10 = 0;
+ goto fd;
+ } else if (!IS_DIGIT(d, c)) {
+ ival = min_ps_int_scan;
+ break;
+ }
+ } else
+ dval = ival;
+ goto l2d;
+ }
+ }
+ ind: /* We saw a non-digit while accumulating an integer in ival. */
+ switch (c) {
+ case '.':
+ GET_NEXT(c, sp, c = EOFC);
+ goto i2r;
+ default:
+ *psp = sp;
+ code = 1;
+ break;
+ case EOFC:
+ break;
+ case 'e':
+ case 'E':
+ if (sign < 0)
+ ival = -ival;
+ dval = ival;
+ exp10 = 0;
+ goto fe;
+ case '#':
+ {
+ const int radix = ival;
+ ps_int uval = 0, imax;
+
+ if (sign || radix < min_radix || radix > max_radix)
+ return_error(gs_error_syntaxerror);
+ /* Avoid multiplies for power-of-2 radix. */
+ if (!(radix & (radix - 1))) {
+ int shift;
+
+ switch (radix) {
+ case 2:
+ shift = 1, imax = MAX_PS_UINT >> 1;
+ break;
+ case 4:
+ shift = 2, imax = MAX_PS_UINT >> 2;
+ break;
+ case 8:
+ shift = 3, imax = MAX_PS_UINT >> 3;
+ break;
+ case 16:
+ shift = 4, imax = MAX_PS_UINT >> 4;
+ break;
+ case 32:
+ shift = 5, imax = MAX_PS_UINT >> 5;
+ break;
+ default: /* can't happen */
+ return_error(gs_error_rangecheck);
+ }
+ for (;; uval = (uval << shift) + d) {
+ GET_NEXT(c, sp, break);
+ d = decoder[c];
+ if (d >= radix) {
+ *psp = sp;
+ code = 1;
+ break;
+ }
+ if (uval > imax)
+ return_error(gs_error_limitcheck);
+ }
+ } else {
+ ps_int irem = MAX_PS_UINT % radix;
+
+ imax = MAX_PS_UINT / radix;
+ for (;; uval = uval * radix + d) {
+ GET_NEXT(c, sp, break);
+ d = decoder[c];
+ if (d >= radix) {
+ *psp = sp;
+ code = 1;
+ break;
+ }
+ if (uval >= imax &&
+ (uval > imax || d > irem)
+ )
+ return_error(gs_error_limitcheck);
+ }
+ }
+ if (scanner_options & SCAN_CPSI_MODE) {
+ ps_uint32 int1 = 0;
+ int1 |= (uval & 0xffffffff);
+ make_int(pref, (ps_int)((ps_int32)int1));
+ }
+ else
+ make_int(pref, uval);
+
+ return code;
+ }
+ }
+iret:
+ if (scanner_options & SCAN_CPSI_MODE) {
+ make_int(pref, (sign < 0 ? (ps_int32)-ival : (ps_int32)ival));
+ }
+ else {
+ make_int(pref, (sign < 0 ? (ps_int)-ival : (ps_int)ival));
+ }
+ return code;
+
+ /* Accumulate a double in dval. */
+l2d:
+ exp10 = 0;
+ for (;;) {
+ dval = dval * 10 + d;
+ GET_NEXT(c, sp, c = EOFC);
+ if (!IS_DIGIT(d, c))
+ break;
+ }
+ switch (c) {
+ case '.':
+ GET_NEXT(c, sp, c = EOFC);
+ exp10 = 0;
+ goto fd;
+ default:
+ *psp = sp;
+ code = 1;
+ /* falls through */
+ case EOFC:
+ if (sign < 0)
+ dval = -dval;
+ goto rret;
+ case 'e':
+ case 'E':
+ exp10 = 0;
+ goto fs;
+ case '#':
+ return_error(gs_error_syntaxerror);
+ }
+
+ /* We saw a '.' while accumulating an integer in ival. */
+i2r:
+ exp10 = 0;
+ while (IS_DIGIT(d, c) || c == '-') {
+ /*
+ * PostScript gives an error on numbers with a '-' following a '.'
+ * Adobe Acrobat Reader (PDF) apparently doesn't treat this as an
+ * error. Experiments show that the numbers following the '-' are
+ * ignored, so we swallow the fractional part. SCAN_PDF_INV_NUM
+ * enables this compatibility kloodge.
+ */
+ if (c == '-') {
+ if ((SCAN_PDF_INV_NUM & scanner_options) == 0)
+ break;
+ do {
+ GET_NEXT(c, sp, c = EOFC);
+ } while (IS_DIGIT(d, c));
+ break;
+ }
+ if (WOULD_OVERFLOW(ival, d, max_int)) {
+ dval = ival;
+ goto fd;
+ }
+ ival = ival * 10 + d;
+ exp10--;
+ GET_NEXT(c, sp, c = EOFC);
+ }
+ if (sign < 0)
+ ival = -ival;
+ /* Take a shortcut for the common case */
+ if (!(c == 'e' || c == 'E' || exp10 < -NUM_POWERS_10)) { /* Check for trailing garbage */
+ if (c != EOFC)
+ *psp = sp, code = 1;
+ make_real(pref, ival * neg_powers_10[-exp10]);
+ return code;
+ }
+ dval = ival;
+ goto fe;
+
+ /* Now we are accumulating a double in dval. */
+fd:
+ while (IS_DIGIT(d, c)) {
+ dval = dval * 10 + d;
+ exp10--;
+ GET_NEXT(c, sp, c = EOFC);
+ }
+fs:
+ if (sign < 0)
+ dval = -dval;
+fe:
+ /* Now dval contains the value, negated if necessary. */
+ switch (c) {
+ case 'e':
+ case 'E':
+ { /* Check for a following exponent. */
+ int esign = 0;
+ int iexp;
+
+ GET_NEXT(c, sp, return_error(gs_error_syntaxerror));
+ switch (c) {
+ case '-':
+ esign = 1;
+ case '+':
+ GET_NEXT(c, sp, return_error(gs_error_syntaxerror));
+ }
+ /* Scan the exponent. We limit it arbitrarily to 999. */
+ if (!IS_DIGIT(d, c))
+ return_error(gs_error_syntaxerror);
+ iexp = d;
+ for (;; iexp = iexp * 10 + d) {
+ GET_NEXT(c, sp, break);
+ if (!IS_DIGIT(d, c)) {
+ *psp = sp;
+ code = 1;
+ break;
+ }
+ if (iexp > 99)
+ return_error(gs_error_limitcheck);
+ }
+ if (esign)
+ exp10 -= iexp;
+ else
+ exp10 += iexp;
+ break;
+ }
+ default:
+ *psp = sp;
+ code = 1;
+ case EOFC:
+ ;
+ }
+ /* Compute dval * 10^exp10. */
+ if (exp10 > 0) {
+ while (exp10 > NUM_POWERS_10)
+ dval *= powers_10[NUM_POWERS_10],
+ exp10 -= NUM_POWERS_10;
+ if (exp10 > 0)
+ dval *= powers_10[exp10];
+ } else if (exp10 < 0) {
+ while (exp10 < -NUM_POWERS_10)
+ dval /= powers_10[NUM_POWERS_10],
+ exp10 += NUM_POWERS_10;
+ if (exp10 < 0)
+ dval /= powers_10[-exp10];
+ }
+ /*
+ * Check for an out-of-range result. Currently we don't check for
+ * absurdly large numbers of digits in the accumulation loops,
+ * but we should.
+ */
+ if (dval >= 0) {
+ if (dval > MAX_FLOAT)
+ return_error(gs_error_limitcheck);
+ } else {
+ if (dval < -MAX_FLOAT)
+ return_error(gs_error_limitcheck);
+ }
+rret:
+ make_real(pref, dval);
+ return code;
+}
diff --git a/psi/iscannum.h b/psi/iscannum.h
new file mode 100644
index 000000000..da0c5bd4d
--- /dev/null
+++ b/psi/iscannum.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to Ghostscript number scanner */
+
+#ifndef iscannum_INCLUDED
+# define iscannum_INCLUDED
+
+/*
+ * Scan a number. If the number consumes the entire string, return 0;
+ * if not, set *psp to the first character beyond the number and return 1.
+ * Note that scan_number does not mark the result ref as "new".
+ */
+int scan_number(const byte * sp, const byte * end, int sign, ref * pref,
+ const byte ** psp, int scanner_options);
+
+#endif /* iscannum_INCLUDED */
diff --git a/psi/isdata.h b/psi/isdata.h
new file mode 100644
index 000000000..65410755a
--- /dev/null
+++ b/psi/isdata.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Structure for expandable stacks of refs */
+/* Requires iref.h */
+
+#ifndef isdata_INCLUDED
+# define isdata_INCLUDED
+
+/*
+ * In order to detect under- and overflow with minimum overhead, we put
+ * guard elements at the top and bottom of each stack block (see idsdata.h,
+ * iesdata.h, and iosdata.h for details of the individual stacks). Note that
+ * the 'current' and 'next' arrays include the guard elements. See
+ * istack.h for the details of stack blocks.
+ */
+
+/*
+ * The garbage collector requires that the entire contents of every block
+ * be 'clean', i.e., contain legitimate refs; we also need to ensure that
+ * at GC time, pointers in unused areas of a block will not be followed
+ * (since they may be dangling). We ensure this as follows:
+ * - When allocating a new block, we set the entire body to nulls.
+ * This is necessary because the block may be freed before the next GC,
+ * and the GC must be able to scan (parse) refs even if they are free.
+ * - When adding a new block to the top of the stack, we set to nulls
+ * the unused area of the new next-to-top blocks.
+ * - At the beginning of garbage collection, we set to nulls the unused
+ * elements of the top block.
+ */
+
+/*
+ * Define pointers into stacks. Formerly, these were short (unsegmented)
+ * pointers, but this distinction is no longer needed.
+ */
+typedef ref *s_ptr;
+typedef const ref *const_s_ptr;
+
+/* Define an opaque allocator type. */
+#ifndef gs_ref_memory_DEFINED
+# define gs_ref_memory_DEFINED
+typedef struct gs_ref_memory_s gs_ref_memory_t;
+#endif
+
+/*
+ * Define the state of a stack, other than the data it holds.
+ * Note that the total size of a stack cannot exceed max_uint,
+ * because it has to be possible to copy a stack to a PostScript array.
+ */
+#ifndef ref_stack_DEFINED
+typedef struct ref_stack_s ref_stack_t; /* also defined in idebug.h */
+# define ref_stack_DEFINED
+#endif
+/*
+ * We divide the stack structure into two parts: ref_stack_params_t, which
+ * is set when the stack is created and (almost) never changed after that,
+ * and ref_stack_t, which changes dynamically.
+ */
+typedef struct ref_stack_params_s ref_stack_params_t;
+struct ref_stack_s {
+ /* Following are updated dynamically. */
+ s_ptr p; /* current top element */
+ /* Following are updated when adding or deleting blocks. */
+ s_ptr bot; /* bottommost valid element */
+ s_ptr top; /* topmost valid element = */
+ /* bot + data_size */
+ ref current; /* t_array for current top block */
+ uint extension_size; /* total sizes of extn. blocks */
+ uint extension_used; /* total used sizes of extn. blocks */
+ /* Following are updated rarely. */
+ ref max_stack; /* t_integer, Max...Stack user param */
+ uint requested; /* amount of last failing */
+ /* push or pop request */
+ uint margin; /* # of slots to leave between limit */
+ /* and top */
+ uint body_size; /* data_size - margin */
+ /* Following are set only at initialization. */
+ ref_stack_params_t *params;
+ gs_ref_memory_t *memory; /* allocator for params and blocks */
+};
+#define public_st_ref_stack() /* in istack.c */\
+ gs_public_st_complex_only(st_ref_stack, ref_stack_t, "ref_stack_t",\
+ ref_stack_clear_marks, ref_stack_enum_ptrs, ref_stack_reloc_ptrs, 0)
+#define st_ref_stack_num_ptrs 2 /* current, params */
+
+#endif /* isdata_INCLUDED */
diff --git a/psi/isstate.h b/psi/isstate.h
new file mode 100644
index 000000000..4ef41c017
--- /dev/null
+++ b/psi/isstate.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definition of 'save' structure */
+/* Requires isave.h */
+
+#ifndef isstate_INCLUDED
+# define isstate_INCLUDED
+
+/* Saved state of allocator and other things as needed. */
+/*typedef struct alloc_save_s alloc_save_t; *//* in isave.h */
+struct alloc_save_s {
+ gs_ref_memory_t state; /* must be first for subclassing */
+ vm_spaces spaces;
+ bool restore_names;
+ bool is_current;
+ ulong id;
+ void *client_data;
+};
+
+#define private_st_alloc_save() /* in isave.c */\
+ gs_private_st_suffix_add1(st_alloc_save, alloc_save_t, "alloc_save",\
+ save_enum_ptrs, save_reloc_ptrs, st_ref_memory, client_data)
+
+#endif /* isstate_INCLUDED */
diff --git a/psi/istack.c b/psi/istack.c
new file mode 100644
index 000000000..c0fac5ed8
--- /dev/null
+++ b/psi/istack.c
@@ -0,0 +1,642 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Manager for expandable stacks of refs */
+#include "memory_.h"
+#include "ghost.h"
+#include "gsstruct.h"
+#include "gsutil.h"
+#include "ierrors.h"
+#include "ialloc.h"
+#include "istack.h"
+#include "istkparm.h"
+#include "istruct.h" /* for RELOC_REF_VAR */
+#include "iutil.h"
+#include "ivmspace.h" /* for local/global test */
+#include "store.h"
+
+/* Forward references */
+static void init_block(ref_stack_t *pstack, const ref *pblock_array,
+ uint used);
+static int ref_stack_push_block(ref_stack_t *pstack, uint keep, uint add);
+
+/* GC descriptors and procedures */
+private_st_ref_stack_params();
+static
+CLEAR_MARKS_PROC(ref_stack_clear_marks)
+{
+ ref_stack_t *const sptr = vptr;
+
+ r_clear_attrs(&sptr->current, l_mark);
+}
+static
+ENUM_PTRS_WITH(ref_stack_enum_ptrs, ref_stack_t *sptr) return 0;
+case 0: ENUM_RETURN_REF(&sptr->current);
+case 1: return ENUM_OBJ(sptr->params);
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(ref_stack_reloc_ptrs, ref_stack_t *sptr)
+{
+ /* Note that the relocation must be a multiple of sizeof(ref_packed) */
+ /* * align_packed_per_ref, but it need not be a multiple of */
+ /* sizeof(ref). Therefore, we must do the adjustments using */
+ /* ref_packed pointers rather than ref pointers. */
+ ref_packed *bot = (ref_packed *) sptr->current.value.refs;
+ long reloc;
+
+ RELOC_REF_VAR(sptr->current);
+ r_clear_attrs(&sptr->current, l_mark);
+ reloc = bot - (ref_packed *) sptr->current.value.refs;
+#define RELOC_P(p)\
+ sptr->p = (ref *)((ref_packed *)sptr->p - reloc);
+ RELOC_P(p);
+ RELOC_P(bot);
+ RELOC_P(top);
+#undef RELOC_P
+ RELOC_OBJ_VAR(sptr->params);
+} RELOC_PTRS_END
+/* Structure type for a ref_stack. */
+public_st_ref_stack();
+
+/* Initialize a stack. */
+int
+ref_stack_init(ref_stack_t *pstack, const ref *pblock_array,
+ uint bot_guard, uint top_guard, const ref *pguard_value,
+ gs_ref_memory_t *mem, ref_stack_params_t *params)
+{
+ uint size = r_size(pblock_array);
+ uint avail = size - (stack_block_refs + bot_guard + top_guard);
+ ref_stack_block *pblock = (ref_stack_block *)pblock_array->value.refs;
+ s_ptr body = (s_ptr)(pblock + 1);
+
+ if (params == 0) {
+ params = gs_alloc_struct((gs_memory_t *)mem, ref_stack_params_t,
+ &st_ref_stack_params,
+ "ref_stack_alloc(stack.params)");
+ if (params == 0)
+ return_error(-1); /* avoid binding in any error codes */
+ }
+
+ pstack->bot = body + bot_guard;
+ pstack->p = pstack->bot - 1;
+ pstack->top = pstack->p + avail;
+ pstack->current = *pblock_array;
+ pstack->extension_size = 0;
+ pstack->extension_used = 0;
+
+ make_int(&pstack->max_stack, avail);
+ pstack->requested = 0;
+ pstack->margin = 0;
+ pstack->body_size = avail;
+
+ pstack->params = params;
+ pstack->memory = mem;
+
+ params->bot_guard = bot_guard;
+ params->top_guard = top_guard;
+ params->block_size = size;
+ params->data_size = avail;
+ if (pguard_value != 0)
+ params->guard_value = *pguard_value;
+ else
+ make_tav(&params->guard_value, t__invalid, 0, intval, 0);
+ params->underflow_error = -1;
+ params->overflow_error = -1;
+ params->allow_expansion = true;
+ init_block(pstack, pblock_array, 0);
+ refset_null_new(pstack->bot, avail, 0);
+ make_empty_array(&pblock->next, 0);
+ return 0;
+}
+
+/* Set whether a stack is allowed to expand. The initial value is true. */
+void
+ref_stack_allow_expansion(ref_stack_t *pstack, bool expand)
+{
+ pstack->params->allow_expansion = expand;
+}
+
+/* Set the error codes for under- and overflow. The initial values are -1. */
+void
+ref_stack_set_error_codes(ref_stack_t *pstack, int underflow_error,
+ int overflow_error)
+{
+ pstack->params->underflow_error = underflow_error;
+ pstack->params->overflow_error = overflow_error;
+}
+
+/* Set the maximum number of elements allowed on a stack. */
+int
+ref_stack_set_max_count(ref_stack_t *pstack, long nmax)
+{
+ uint nmin = ref_stack_count_inline(pstack);
+
+ if (nmax < nmin)
+ nmax = nmin;
+ if (nmax > max_uint / sizeof(ref))
+ nmax = max_uint / sizeof(ref);
+ if (!pstack->params->allow_expansion) {
+ uint ncur = pstack->body_size;
+
+ if (nmax > ncur)
+ nmax = ncur;
+ }
+ pstack->max_stack.value.intval = nmax;
+ return 0;
+}
+
+/*
+ * Set the margin between the limit and the top of the stack.
+ * Note that this may require allocating a block.
+ */
+int
+ref_stack_set_margin(ref_stack_t *pstack, uint margin)
+{
+ const ref_stack_params_t *params = pstack->params;
+ uint data_size = params->data_size;
+
+ if (margin <= pstack->margin) {
+ refset_null_new(pstack->top + 1, pstack->margin - margin, 0);
+ } else {
+ if (margin > data_size >> 1)
+ return_error(gs_error_rangecheck);
+ if (pstack->top - pstack->p < margin) {
+ uint used = pstack->p + 1 - pstack->bot;
+ uint keep = data_size - margin;
+ int code = ref_stack_push_block(pstack, keep, used - keep);
+
+ if (code < 0)
+ return code;
+ }
+ }
+ pstack->margin = margin;
+ pstack->body_size = data_size - margin;
+ pstack->top = pstack->bot + pstack->body_size - 1;
+ return 0;
+}
+
+/* Return the number of elements on a stack. */
+uint
+ref_stack_count(const ref_stack_t *pstack)
+{
+ return ref_stack_count_inline(pstack);
+}
+
+/*
+ * Return a pointer to a given element from the stack, counting from
+ * 0 as the top element. If the index is out of range, return 0.
+ */
+ref *
+ref_stack_index(const ref_stack_t *pstack, long idx)
+{
+ ref_stack_block *pblock;
+ uint used = pstack->p + 1 - pstack->bot;
+
+ if (idx < 0)
+ return NULL;
+ if (idx < used) /* common case */
+ return pstack->p - (uint) idx;
+ pblock = (ref_stack_block *) pstack->current.value.refs;
+ do {
+ pblock = (ref_stack_block *) pblock->next.value.refs;
+ if (pblock == 0)
+ return NULL;
+ idx -= used;
+ used = r_size(&pblock->used);
+ } while (idx >= used);
+ return pblock->used.value.refs + (used - 1 - (uint) idx);
+}
+
+/*
+ * Count the number of elements down to and including the first mark.
+ * If no mark is found, return 0.
+ */
+uint
+ref_stack_counttomark(const ref_stack_t *pstack)
+{
+ uint scanned = 0;
+ ref_stack_enum_t rsenum;
+
+ ref_stack_enum_begin(&rsenum, pstack);
+ do {
+ uint count = rsenum.size;
+ const ref *p = rsenum.ptr + count - 1;
+
+ for (; count; count--, p--)
+ if (r_has_type(p, t_mark))
+ return scanned + (rsenum.size - count + 1);
+ scanned += rsenum.size;
+ } while (ref_stack_enum_next(&rsenum));
+ return 0;
+}
+
+/*
+ * Do the store check for storing 'count' elements of a stack, starting
+ * 'skip' elements below the top, into an array. Return 0 or gs_error_invalidaccess.
+ */
+int
+ref_stack_store_check(const ref_stack_t *pstack, ref *parray, uint count,
+ uint skip)
+{
+ uint space = r_space(parray);
+
+ if (space != avm_local) {
+ uint left = count, pass = skip;
+ ref_stack_enum_t rsenum;
+
+ ref_stack_enum_begin(&rsenum, pstack);
+ do {
+ ref *ptr = rsenum.ptr;
+ uint size = rsenum.size;
+
+ if (size <= pass)
+ pass -= size;
+ else {
+ int code;
+
+ if (pass != 0)
+ size -= pass, pass = 0;
+ ptr += size;
+ if (size > left)
+ size = left;
+ left -= size;
+ code = refs_check_space(ptr - size, size, space);
+ if (code < 0)
+ return code;
+ if (left == 0)
+ break;
+ }
+ } while (ref_stack_enum_next(&rsenum));
+ }
+ return 0;
+}
+
+/*
+ * Store the top 'count' elements of a stack, starting 'skip' elements below
+ * the top, into an array, with or without store/undo checking. age=-1 for
+ * no check, 0 for old, 1 for new. May return gs_error_rangecheck or
+ * gs_error_invalidaccess.
+ */
+#undef idmemory /****** NOTA BENE ******/
+int
+ref_stack_store(const ref_stack_t *pstack, ref *parray, uint count,
+ uint skip, int age, bool check, gs_dual_memory_t *idmemory,
+ client_name_t cname)
+{
+ uint left, pass;
+ ref *to;
+ ref_stack_enum_t rsenum;
+
+ if (count > ref_stack_count(pstack) || count > r_size(parray))
+ return_error(gs_error_rangecheck);
+ if (check) {
+ int code = ref_stack_store_check(pstack, parray, count, skip);
+
+ if (code < 0)
+ return code;
+ }
+ to = parray->value.refs + count;
+ left = count, pass = skip;
+ ref_stack_enum_begin(&rsenum, pstack);
+ do {
+ ref *from = rsenum.ptr;
+ uint size = rsenum.size;
+
+ if (size <= pass)
+ pass -= size;
+ else {
+ if (pass != 0)
+ size -= pass, pass = 0;
+ from += size;
+ if (size > left)
+ size = left;
+ left -= size;
+ switch (age) {
+ case -1: /* not an array */
+ while (size--) {
+ from--, to--;
+ ref_assign(to, from);
+ }
+ break;
+ case 0: /* old array */
+ while (size--) {
+ from--, to--;
+ ref_assign_old(parray, to, from, cname);
+ }
+ break;
+ case 1: /* new array */
+ while (size--) {
+ from--, to--;
+ ref_assign_new(to, from);
+ }
+ break;
+ }
+ if (left == 0)
+ break;
+ }
+ } while (ref_stack_enum_next(&rsenum));
+ r_set_size(parray, count);
+ return 0;
+}
+
+/*
+ * Pop the top N elements off a stack.
+ * The number must not exceed the number of elements in use.
+ */
+void
+ref_stack_pop(ref_stack_t *pstack, uint count)
+{
+ uint used;
+
+ while ((used = pstack->p + 1 - pstack->bot) < count) {
+ count -= used;
+ pstack->p = pstack->bot - 1;
+ ref_stack_pop_block(pstack);
+ }
+ pstack->p -= count;
+}
+
+/* Pop the top block off a stack. May return underflow_error. */
+int
+ref_stack_pop_block(ref_stack_t *pstack)
+{
+ s_ptr bot = pstack->bot;
+ uint count = pstack->p + 1 - bot;
+ ref_stack_block *pcur =
+ (ref_stack_block *) pstack->current.value.refs;
+ ref_stack_block *pnext =
+ (ref_stack_block *) pcur->next.value.refs;
+ uint used;
+ ref *body;
+ ref next;
+
+ if (pnext == 0)
+ return_error(pstack->params->underflow_error);
+ used = r_size(&pnext->used);
+ body = (ref *) (pnext + 1) + pstack->params->bot_guard;
+ next = pcur->next;
+ /*
+ * If the contents of the two blocks won't fit in a single block, we
+ * move up the used part of the top block, and copy up as much of
+ * the contents of the next block under it as will fit. If the
+ * contents of both blocks fit in a single block, we copy the used
+ * part of the top block to the top of the next block, and free the
+ * top block.
+ */
+ if (used + count > pstack->body_size) {
+ /*
+ * The contents of the two blocks won't fit into a single block.
+ * On the assumption that we're recovering from a local stack
+ * underflow and need to increase the number of contiguous
+ * elements available, move up the used part of the top block, and
+ * copy up as much of the contents of the next block under it as
+ * will fit.
+ */
+ uint moved = pstack->body_size - count;
+ uint left;
+
+ if (moved == 0)
+ return_error(gs_error_Fatal);
+ memmove(bot + moved, bot, count * sizeof(ref));
+ left = used - moved;
+ memcpy(bot, body + left, moved * sizeof(ref));
+ refset_null_new(body + left, moved, 0);
+ r_dec_size(&pnext->used, moved);
+ pstack->p = pstack->top;
+ pstack->extension_used -= moved;
+ } else {
+ /*
+ * The contents of the two blocks will fit into a single block.
+ * Copy the used part of the top block to the top of the next
+ * block, and free the top block.
+ */
+ memcpy(body + used, bot, count * sizeof(ref));
+ pstack->bot = bot = body;
+ pstack->top = bot + pstack->body_size - 1;
+ gs_free_ref_array(pstack->memory, &pstack->current,
+ "ref_stack_pop_block");
+ pstack->current = next;
+ pstack->p = bot + (used + count - 1);
+ pstack->extension_size -= pstack->body_size;
+ pstack->extension_used -= used;
+ }
+ return 0;
+}
+
+/*
+ * Extend a stack to recover from an overflow condition.
+ * May return overflow_error or gs_error_VMerror.
+ */
+int
+ref_stack_extend(ref_stack_t *pstack, uint request)
+{
+ uint keep = (pstack->top - pstack->bot + 1) / 3;
+ uint count = pstack->p - pstack->bot + 1;
+ const ref_stack_params_t *params = pstack->params;
+
+ if (request > params->data_size)
+ return_error(params->overflow_error);
+ if (keep + request > pstack->body_size)
+ keep = pstack->body_size - request;
+ if (keep > count)
+ keep = count; /* required by ref_stack_push_block */
+ return ref_stack_push_block(pstack, keep, request);
+}
+
+/*
+ * Push N empty slots onto a stack. These slots are not initialized:
+ * the caller must immediately fill them. May return overflow_error
+ * (if max_stack would be exceeded, or the stack has no allocator)
+ * or gs_error_VMerror.
+ */
+int
+ref_stack_push(ref_stack_t *pstack, uint count)
+{
+ /* Don't bother to pre-check for overflow: we must be able to */
+ /* back out in the case of a VMerror anyway, and */
+ /* ref_stack_push_block will make the check itself. */
+ uint needed = count;
+ uint added;
+
+ for (; (added = pstack->top - pstack->p) < needed; needed -= added) {
+ int code;
+
+ pstack->p = pstack->top;
+ code = ref_stack_push_block(pstack,
+ (pstack->top - pstack->bot + 1) / 3,
+ added);
+ if (code < 0) {
+ /* Back out. */
+ ref_stack_pop(pstack, count - needed + added);
+ pstack->requested = count;
+ return code;
+ }
+ }
+ pstack->p += needed;
+ return 0;
+}
+
+/*
+ * Push a block onto the stack, specifying how many elements of the current
+ * top block should remain in the top block and also how many elements we
+ * are trying to add. Requires keep <= count. May return overflow_error or
+ * gs_error_VMerror.
+ */
+static int
+ref_stack_push_block(ref_stack_t *pstack, uint keep, uint add)
+{
+ const ref_stack_params_t *params = pstack->params;
+ uint count = pstack->p - pstack->bot + 1;
+ uint move = count - keep;
+ ref_stack_block *pcur = (ref_stack_block *) pstack->current.value.refs;
+ ref next;
+ ref_stack_block *pnext;
+ ref *body;
+ int code;
+
+ if (keep > count)
+ return_error(gs_error_Fatal);
+ /* Check for overflowing the maximum size, */
+ /* or expansion not allowed. */
+ if (pstack->extension_used + (pstack->top - pstack->bot) + add >=
+ pstack->max_stack.value.intval ||
+ !params->allow_expansion
+ )
+ return_error(params->overflow_error);
+ code = gs_alloc_ref_array(pstack->memory, &next, 0,
+ params->block_size, "ref_stack_push_block");
+ if (code < 0)
+ return code;
+ pnext = (ref_stack_block *) next.value.refs;
+ body = (ref *) (pnext + 1);
+ /* Copy the top keep elements into the new block, */
+ /* and make the new block the top block. */
+ init_block(pstack, &next, keep);
+ body += params->bot_guard;
+ memcpy(body, pstack->bot + move, keep * sizeof(ref));
+ /* Clear the elements above the top of the new block. */
+ refset_null_new(body + keep, params->data_size - keep, 0);
+ /* Clear the elements above the top of the old block. */
+ refset_null_new(pstack->bot + move, keep, 0);
+ pnext->next = pstack->current;
+ pcur->used.value.refs = pstack->bot;
+ r_set_size(&pcur->used, move);
+ pstack->current = next;
+ pstack->bot = body;
+ pstack->top = pstack->bot + pstack->body_size - 1;
+ pstack->p = pstack->bot + keep - 1;
+ pstack->extension_size += pstack->body_size;
+ pstack->extension_used += move;
+ return 0;
+}
+
+/* Begin enumerating the blocks of a stack. */
+void
+ref_stack_enum_begin(ref_stack_enum_t *prse, const ref_stack_t *pstack)
+{
+ prse->block = (ref_stack_block *)pstack->current.value.refs;
+ prse->ptr = pstack->bot;
+ prse->size = pstack->p + 1 - pstack->bot;
+}
+
+bool
+ref_stack_enum_next(ref_stack_enum_t *prse)
+{
+ ref_stack_block *block =
+ prse->block = (ref_stack_block *)prse->block->next.value.refs;
+
+ if (block == 0)
+ return false;
+ prse->ptr = block->used.value.refs;
+ prse->size = r_size(&block->used);
+ return true;
+}
+
+/* Clean up a stack for garbage collection. */
+void
+ref_stack_cleanup(ref_stack_t *pstack)
+{
+ ref_stack_block *pblock =
+ (ref_stack_block *) pstack->current.value.refs;
+
+ refset_null_new(pstack->p + 1, pstack->top - pstack->p, 0);
+ pblock->used = pstack->current; /* set attrs */
+ pblock->used.value.refs = pstack->bot;
+ r_set_size(&pblock->used, pstack->p + 1 - pstack->bot);
+}
+
+/*
+ * Free the entire contents of a stack, including the bottom block.
+ * The client must still call ref_stack_free. Note that after calling
+ * ref_stack_release, the stack is no longer usable.
+ */
+void
+ref_stack_release(ref_stack_t *pstack)
+{
+ gs_ref_memory_t *mem = pstack->memory;
+
+ ref_stack_clear(pstack);
+ /* Free the parameter structure. */
+ gs_free_object((gs_memory_t *)mem, pstack->params,
+ "ref_stack_release(stack.params)");
+ /* Free the original (bottom) block. */
+ gs_free_ref_array(mem, &pstack->current, "ref_stack_release");
+}
+
+/*
+ * Release a stack and then free the ref_stack object.
+ */
+void
+ref_stack_free(ref_stack_t *pstack)
+{
+ gs_memory_t *mem = (gs_memory_t *)pstack->memory;
+
+ ref_stack_release(pstack);
+ gs_free_object(mem, pstack, "ref_stack_free");
+}
+
+/* ------ Internal routines ------ */
+
+/* Initialize the guards and body of a stack block. */
+static void
+init_block(ref_stack_t *pstack, const ref *psb, uint used)
+{
+ ref_stack_params_t *params = pstack->params;
+ ref *brefs = psb->value.refs;
+ uint i;
+ ref *p;
+
+ for (i = params->bot_guard, p = brefs + stack_block_refs;
+ i != 0; i--, p++
+ )
+ ref_assign(p, &params->guard_value);
+ /* The top guard elements will never be read, */
+ /* but we need to initialize them for the sake of the GC. */
+ /* We can use refset_null for this, because even though it uses */
+ /* make_null_new and stack elements must not be marked new, */
+ /* these slots will never actually be read or written. */
+ if (params->top_guard) {
+ ref *top = brefs + r_size(psb);
+ int top_guard = params->top_guard;
+
+ refset_null_new(top - top_guard, top_guard, 0);
+ } {
+ ref_stack_block *const pblock = (ref_stack_block *) brefs;
+
+ pblock->used = *psb;
+ pblock->used.value.refs = brefs + stack_block_refs + params->bot_guard;
+ r_set_size(&pblock->used, 0);
+ }
+}
diff --git a/psi/istack.h b/psi/istack.h
new file mode 100644
index 000000000..909b9d7c1
--- /dev/null
+++ b/psi/istack.h
@@ -0,0 +1,194 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definitions for expandable stacks of refs */
+/* Requires iref.h */
+
+#ifndef istack_INCLUDED
+# define istack_INCLUDED
+
+#include "isdata.h"
+
+/*
+ * The 3 principal Ghostscript stacks (operand, execution, and dictionary)
+ * are implemented as a linked list of blocks.
+ *
+ * Since all operators exit cleanly in case of stack under- or overflow,
+ * we handle all issues related to stack blocks in the top-level error
+ * recovery code in interp.c. A few situations require special treatment:
+ * see ostack.h, estack.h, and dstack.h for details.
+ */
+
+/*
+ * Define the structure for a stack block.
+ * In order to simplify allocation, stack blocks are implemented as
+ * t_array objects, with the first few elements used for special purposes.
+ * The actual layout is as follows:
+ * ref_stack_block structure
+ * bottom guard if any (see below)
+ * used elements of block
+ * unused elements of block
+ * top guard if any (see below)
+ * The `next' member of the next higher stack block includes all of this.
+ * The `used' member only includes the used elements of this block.
+ * Notes:
+ * - In the top block, the size of the `used' member may not be correct.
+ * - In all blocks but the top, we fill the unused elements with nulls.
+ */
+typedef struct ref_stack_block_s {
+ ref next; /* t_array, next lower block on stack */
+ ref used; /* t_array, subinterval of this block */
+ /* Actual stack starts here */
+} ref_stack_block;
+
+#define stack_block_refs (sizeof(ref_stack_block) / sizeof(ref))
+
+/* ------ Procedural interface ------ */
+
+/*
+ * Initialize a stack. Note that this allocates the stack parameter
+ * structure iff params is not NULL.
+ */
+int ref_stack_init(ref_stack_t *pstack, const ref *pblock_array,
+ uint bot_guard, uint top_guard,
+ const ref *pguard_value, gs_ref_memory_t *mem,
+ ref_stack_params_t *params);
+
+/* Set whether a stack is allowed to expand. The initial value is true. */
+void ref_stack_allow_expansion(ref_stack_t *pstack, bool expand);
+
+/* Set the error codes for under- and overflow. The initial values are -1. */
+void ref_stack_set_error_codes(ref_stack_t *pstack, int underflow_error,
+ int overflow_error);
+
+/*
+ * Set the maximum number of elements allowed on a stack.
+ * Note that the value is a long, not a uint or a ulong.
+ */
+int ref_stack_set_max_count(ref_stack_t *pstack, long nmax);
+
+/*
+ * Set the margin between the limit and the top of the stack.
+ * Note that this may require allocating a block.
+ */
+int ref_stack_set_margin(ref_stack_t *pstack, uint margin);
+
+/* Return the number of elements on a stack. */
+uint ref_stack_count(const ref_stack_t *pstack);
+
+#define ref_stack_count_inline(pstk)\
+ ((pstk)->p + 1 - (pstk)->bot + (pstk)->extension_used)
+
+/* Return the maximum number of elements allowed on a stack. */
+#define ref_stack_max_count(pstk) (uint)((pstk)->max_stack.value.intval)
+
+/*
+ * Return a pointer to a given element from the stack, counting from
+ * 0 as the top element. If the index is out of range, return 0.
+ * Note that the index is a long, not a uint or a ulong.
+ */
+ref *ref_stack_index(const ref_stack_t *pstack, long index);
+
+/*
+ * Count the number of elements down to and including the first mark.
+ * If no mark is found, return 0.
+ */
+uint ref_stack_counttomark(const ref_stack_t *pstack);
+
+/*
+ * Do the store check for storing 'count' elements of a stack, starting
+ * 'skip' elements below the top, into an array. Return 0 or gs_error_invalidaccess.
+ */
+int ref_stack_store_check(const ref_stack_t *pstack, ref *parray,
+ uint count, uint skip);
+
+/*
+ * Store the top 'count' elements of a stack, starting 'skip' elements below
+ * the top, into an array, with or without store/undo checking. age=-1 for
+ * no check, 0 for old, 1 for new. May return gs_error_rangecheck or
+ * gs_error_invalidaccess.
+ */
+#ifndef gs_dual_memory_DEFINED
+# define gs_dual_memory_DEFINED
+typedef struct gs_dual_memory_s gs_dual_memory_t;
+#endif
+int ref_stack_store(const ref_stack_t *pstack, ref *parray, uint count,
+ uint skip, int age, bool check,
+ gs_dual_memory_t *idmem, client_name_t cname);
+
+/*
+ * Pop the top N elements off a stack.
+ * The number must not exceed the number of elements in use.
+ */
+void ref_stack_pop(ref_stack_t *pstack, uint count);
+
+#define ref_stack_clear(pstk) ref_stack_pop(pstk, ref_stack_count(pstk))
+#define ref_stack_pop_to(pstk, depth)\
+ ref_stack_pop(pstk, ref_stack_count(pstk) - (depth))
+
+/* Pop the top block off a stack. May return underflow_error. */
+int ref_stack_pop_block(ref_stack_t *pstack);
+
+/*
+ * Extend a stack to recover from an overflow condition.
+ * Uses the requested value to decide what to do.
+ * May return overflow_error or gs_error_VMerror.
+ */
+int ref_stack_extend(ref_stack_t *pstack, uint request);
+
+/*
+ * Push N empty slots onto a stack. These slots are not initialized:
+ * the caller must immediately fill them. May return overflow_error
+ * (if max_stack would be exceeded, or the stack has no allocator)
+ * or gs_error_VMerror.
+ */
+int ref_stack_push(ref_stack_t *pstack, uint count);
+
+/*
+ * Enumerate the blocks of a stack from top to bottom, as follows:
+
+ ref_stack_enum_t rsenum;
+
+ ref_stack_enum_begin(&rsenum, pstack);
+ do {
+ ... process rsenum.size refs starting at rsenum.ptr ...
+ } while (ref_stack_enum_next(&rsenum));
+
+ */
+typedef struct ref_stack_enum_s {
+ ref_stack_block *block;
+ ref *ptr;
+ uint size;
+} ref_stack_enum_t;
+void ref_stack_enum_begin(ref_stack_enum_t *prse, const ref_stack_t *pstack);
+bool ref_stack_enum_next(ref_stack_enum_t *prse);
+
+/* Clean up a stack for garbage collection. */
+void ref_stack_cleanup(ref_stack_t *pstack);
+
+/*
+ * Free the entire contents of a stack, including the bottom block. The
+ * client must still free the ref_stack_t object. Note that after calling
+ * ref_stack_release, the stack is no longer usable.
+ */
+void ref_stack_release(ref_stack_t *pstack);
+
+/*
+ * Release a stack and then free the stack object.
+ */
+void ref_stack_free(ref_stack_t *pstack);
+
+#endif /* istack_INCLUDED */
diff --git a/psi/istkparm.h b/psi/istkparm.h
new file mode 100644
index 000000000..b2887eb78
--- /dev/null
+++ b/psi/istkparm.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Parameter structure for expandable stacks of refs */
+
+#ifndef istkparm_INCLUDED
+# define istkparm_INCLUDED
+
+/*
+ * Define the structure for stack parameters set at initialization.
+ */
+/*typedef struct ref_stack_params_s ref_stack_params_t;*/ /* in istack.h */
+struct ref_stack_params_s {
+ uint bot_guard; /* # of guard elements below bot */
+ uint top_guard; /* # of guard elements above top */
+ uint block_size; /* size of each block */
+ uint data_size; /* # of data slots in each block */
+ ref guard_value; /* t__invalid or t_operator, */
+ /* bottom guard value */
+ int underflow_error; /* error code for underflow */
+ int overflow_error; /* error code for overflow */
+ bool allow_expansion; /* if false, don't expand */
+};
+#define private_st_ref_stack_params() /* in istack.c */\
+ gs_private_st_simple(st_ref_stack_params, ref_stack_params_t,\
+ "ref_stack_params_t")
+
+#endif /* istkparm_INCLUDED */
diff --git a/psi/istream.h b/psi/istream.h
new file mode 100644
index 000000000..f1051aaf5
--- /dev/null
+++ b/psi/istream.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter support procedures for streams */
+/* Requires scommon.h */
+
+#ifndef istream_INCLUDED
+# define istream_INCLUDED
+
+/* Procedures exported by zfproc.c */
+
+ /* for zfilter.c - procedure stream initialization */
+int sread_proc(ref *, stream **, gs_ref_memory_t *);
+int swrite_proc(ref *, stream **, gs_ref_memory_t *);
+
+ /* for interp.c, zfileio.c, zpaint.c - handle a procedure */
+ /* callback or an interrupt */
+int s_handle_read_exception(i_ctx_t *, int, const ref *, const ref *,
+ int, op_proc_t);
+int s_handle_write_exception(i_ctx_t *, int, const ref *, const ref *,
+ int, op_proc_t);
+
+#endif /* istream_INCLUDED */
diff --git a/psi/istruct.h b/psi/istruct.h
new file mode 100644
index 000000000..108ff62da
--- /dev/null
+++ b/psi/istruct.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interpreter-level extension of gsstruct.h */
+
+#ifndef istruct_INCLUDED
+# define istruct_INCLUDED
+
+#include "gsstruct.h"
+
+/* ================ Refs ================ */
+
+/*
+ * Define the pointer type for refs. Note that if a structure contains refs,
+ * both its clear_marks and its reloc_ptrs procedure must unmark them,
+ * since the GC will never see the refs during the unmarking sweep.
+ */
+extern const gs_ptr_procs_t ptr_ref_procs;
+#define ptr_ref_type (&ptr_ref_procs)
+
+/* The structure type descriptor for (blocks of) refs. */
+/* This is defined in igc.c and exported for isave.c. */
+extern_st(st_refs);
+
+/*
+ * Extend the GC procedure vector to include refs.
+ */
+#define refs_proc_reloc(proc)\
+ void proc(ref_packed *from, ref_packed *to, gc_state_t *gcst)
+typedef struct gc_procs_with_refs_s {
+ gc_procs_common;
+ /* Relocate a pointer to a ref[_packed]. */
+ ptr_proc_reloc((*reloc_ref_ptr), ref_packed);
+ /* Relocate a block of ref[_packed]s. */
+ refs_proc_reloc((*reloc_refs));
+} gc_procs_with_refs_t;
+
+#undef gc_proc
+#define gc_proc(gcst, proc) ((*(const gc_procs_with_refs_t **)(gcst))->proc)
+
+/*
+ * Define enumeration and relocation macros analogous to those for
+ * structures and strings. (We should go back and change the names of
+ * those macros to be consistent which these, which are better, but it's
+ * not worth the trouble.)
+ */
+#define ENUM_RETURN_REF(rptr)\
+ return (pep->ptr = (const void *)(rptr), ptr_ref_type)
+#define ENUM_RETURN_REF_MEMBER(typ, memb)\
+ ENUM_RETURN_REF(&((typ *)vptr)->memb)
+#define RELOC_REF_PTR_VAR(ptrvar)\
+ ptrvar = (*gc_proc(gcst, reloc_ref_ptr))((const void *)(ptrvar), gcst)
+#define RELOC_REF_PTR_MEMBER(typ, memb)\
+ RELOC_REF_PTR_VAR(((typ *)vptr)->memb)
+#define RELOC_REFS(from, upto)\
+ (*gc_proc(gcst, reloc_refs))((ref_packed *)(from), (ref_packed *)(upto), gcst)
+#define RELOC_REF_VAR(refvar)\
+ RELOC_REFS(&(refvar), &(refvar) + 1)
+
+/*
+ * Define an object allocated as a struct, but actually containing refs.
+ * Such objects are useful as the client_data of library structures
+ * (currently only gstates and fonts).
+ */
+struct_proc_clear_marks(ref_struct_clear_marks);
+struct_proc_enum_ptrs(ref_struct_enum_ptrs);
+struct_proc_reloc_ptrs(ref_struct_reloc_ptrs);
+#define gs__st_ref_struct(scope_st, stname, stype, sname)\
+ gs__st_complex_only(scope_st, stname, stype, sname, ref_struct_clear_marks,\
+ ref_struct_enum_ptrs, ref_struct_reloc_ptrs, 0)
+#define gs_public_st_ref_struct(stname, stype, sname)\
+ gs__st_ref_struct(public_st, stname, stype, sname)
+#define gs_private_st_ref_struct(stname, stype, sname)\
+ gs__st_ref_struct(private_st, stname, stype, sname)
+
+#endif /* istruct_INCLUDED */
diff --git a/psi/itoken.h b/psi/itoken.h
new file mode 100644
index 000000000..63785b016
--- /dev/null
+++ b/psi/itoken.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to exported procedures in ztoken.c */
+
+#ifndef itoken_INCLUDED
+# define itoken_INCLUDED
+
+/*
+ * Continue after handling a procedure stream refill or other callout
+ * while reading tokens in the interpreter.
+ */
+int ztokenexec_continue(i_ctx_t *i_ctx_p);
+
+/*
+ * Handle a scan_Comment or scan_DSC_Comment return from gs_scan_token.
+ */
+#ifndef scanner_state_DEFINED
+# define scanner_state_DEFINED
+typedef struct scanner_state_s scanner_state;
+#endif
+int ztoken_handle_comment(i_ctx_t *i_ctx_p,
+ scanner_state *sstate, const ref *ptoken,
+ int scan_code, bool save, bool push_file,
+ op_proc_t cont);
+
+/*
+ * Update the cached scanner_options in the context state after doing a
+ * setuserparams. (We might move this procedure somewhere else eventually.)
+ */
+int ztoken_scanner_options(const ref *upref, int old_options);
+/*
+ * Get the value for a scanner option.
+ * return -1 if no such option, 1/0 for on/off and option's name in *pname as a C string
+ */
+int ztoken_get_scanner_option(const ref *psref, int options, const char **pname);
+
+#endif /* itoken_INCLUDED */
diff --git a/psi/iutil.c b/psi/iutil.c
new file mode 100644
index 000000000..41befbbab
--- /dev/null
+++ b/psi/iutil.c
@@ -0,0 +1,994 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Utilities for Ghostscript interpreter */
+#include "math_.h" /* for fabs */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "ierrors.h"
+#include "gsccode.h" /* for gxfont.h */
+#include "gsmatrix.h"
+#include "gsutil.h"
+#include "gxfont.h"
+#include "strimpl.h"
+#include "sstring.h"
+#include "idict.h"
+#include "ifont.h" /* for FontID equality */
+#include "imemory.h"
+#include "iname.h"
+#include "ipacked.h" /* for array_get */
+#include "iutil.h" /* for checking prototypes */
+#include "ivmspace.h"
+#include "oper.h"
+#include "store.h"
+
+/*
+ * By design choice, none of the procedures in this file take a context
+ * pointer (i_ctx_p). Since a number of them require a gs_dual_memory_t
+ * for store checking or save bookkeeping, we need to #undef idmemory.
+ */
+#undef idmemory
+
+/* ------ Object utilities ------ */
+
+/* Define the table of ref type properties. */
+const byte ref_type_properties[] = {
+ REF_TYPE_PROPERTIES_DATA
+};
+
+/* Copy refs from one place to another. */
+int
+refcpy_to_old(ref * aref, uint index, const ref * from,
+ uint size, gs_dual_memory_t *idmemory, client_name_t cname)
+{
+ ref *to = aref->value.refs + index;
+ int code = refs_check_space(from, size, r_space(aref));
+
+ if (code < 0)
+ return code;
+ /* We have to worry about aliasing.... */
+ if (to <= from || from + size <= to)
+ while (size--)
+ ref_assign_old(aref, to, from, cname), to++, from++;
+ else
+ for (from += size, to += size; size--;)
+ from--, to--, ref_assign_old(aref, to, from, cname);
+ return 0;
+}
+void
+refcpy_to_new(ref * to, const ref * from, uint size,
+ gs_dual_memory_t *idmemory)
+{
+ while (size--)
+ ref_assign_new(to, from), to++, from++;
+}
+
+/* Fill a new object with nulls. */
+void
+refset_null_new(ref * to, uint size, uint new_mask)
+{
+ for (; size--; ++to)
+ make_ta(to, t_null, new_mask);
+}
+
+/* Compare two objects for equality. */
+static bool fid_eq(const gs_memory_t *mem, const gs_font *pfont1,
+ const gs_font *pfont2);
+bool
+obj_eq(const gs_memory_t *mem, const ref * pref1, const ref * pref2)
+{
+ ref nref;
+
+ if (r_type(pref1) != r_type(pref2)) {
+ /*
+ * Only a few cases need be considered here:
+ * integer/real (and vice versa), name/string (and vice versa),
+ * arrays, and extended operators.
+ */
+ switch (r_type(pref1)) {
+ case t_integer:
+ return (r_has_type(pref2, t_real) &&
+ pref2->value.realval == pref1->value.intval);
+ case t_real:
+ return (r_has_type(pref2, t_integer) &&
+ pref2->value.intval == pref1->value.realval);
+ case t_name:
+ if (!r_has_type(pref2, t_string))
+ return false;
+ name_string_ref(mem, pref1, &nref);
+ pref1 = &nref;
+ break;
+ case t_string:
+ if (!r_has_type(pref2, t_name))
+ return false;
+ name_string_ref(mem, pref2, &nref);
+ pref2 = &nref;
+ break;
+ /*
+ * Differing implementations of packedarray can be eq,
+ * if the length is zero, but an array is never eq to a
+ * packedarray.
+ */
+ case t_mixedarray:
+ case t_shortarray:
+ /*
+ * Since r_type(pref1) is one of the above, this is a
+ * clever fast check for r_type(pref2) being the other.
+ */
+ return ((int)r_type(pref1) + (int)r_type(pref2) ==
+ t_mixedarray + t_shortarray) &&
+ r_size(pref1) == 0 && r_size(pref2) == 0;
+ default:
+ if (r_btype(pref1) != r_btype(pref2))
+ return false;
+ }
+ }
+ /*
+ * Now do a type-dependent comparison. This would be very simple if we
+ * always filled in all the bytes of a ref, but we currently don't.
+ */
+ switch (r_btype(pref1)) {
+ case t_array:
+ return ((pref1->value.refs == pref2->value.refs ||
+ r_size(pref1) == 0) &&
+ r_size(pref1) == r_size(pref2));
+ case t_mixedarray:
+ case t_shortarray:
+ return ((pref1->value.packed == pref2->value.packed ||
+ r_size(pref1) == 0) &&
+ r_size(pref1) == r_size(pref2));
+ case t_boolean:
+ return (pref1->value.boolval == pref2->value.boolval);
+ case t_dictionary:
+ return (pref1->value.pdict == pref2->value.pdict);
+ case t_file:
+ return (pref1->value.pfile == pref2->value.pfile &&
+ r_size(pref1) == r_size(pref2));
+ case t_integer:
+ return (pref1->value.intval == pref2->value.intval);
+ case t_mark:
+ case t_null:
+ return true;
+ case t_name:
+ return (pref1->value.pname == pref2->value.pname);
+ case t_oparray:
+ case t_operator:
+ return (op_index(pref1) == op_index(pref2));
+ case t_real:
+ return (pref1->value.realval == pref2->value.realval);
+ case t_save:
+ return (pref2->value.saveid == pref1->value.saveid);
+ case t_string:
+ return (!bytes_compare(pref1->value.bytes, r_size(pref1),
+ pref2->value.bytes, r_size(pref2)));
+ case t_device:
+ return (pref1->value.pdevice == pref2->value.pdevice);
+ case t_struct:
+ case t_astruct:
+ return (pref1->value.pstruct == pref2->value.pstruct);
+ case t_fontID:
+ /* This is complicated enough to deserve a separate procedure. */
+ return fid_eq(mem, r_ptr(pref1, gs_font), r_ptr(pref2, gs_font));
+ }
+ return false; /* shouldn't happen! */
+}
+
+/*
+ * Compare two FontIDs for equality. In the Adobe implementations,
+ * different scalings of a font have "equal" FIDs, so we do the same.
+ * Furthermore, in more recent Adobe interpreters, different *copies* of a
+ * font have equal FIDs -- at least for Type 1 and Type 3 fonts -- as long
+ * as the "contents" of the font are the same. We aren't sure that the
+ * following matches the Adobe algorithm, but it's close enough to pass the
+ * Genoa CET.
+ */
+/* (This is a single-use procedure, for clearer code.) */
+static bool
+fid_eq(const gs_memory_t *mem, const gs_font *pfont1, const gs_font *pfont2)
+{
+ while (pfont1->base != pfont1)
+ pfont1 = pfont1->base;
+ while (pfont2->base != pfont2)
+ pfont2 = pfont2->base;
+ if (pfont1 == pfont2)
+ return true;
+ switch (pfont1->FontType) {
+ case 1: case 3:
+ if (pfont1->FontType == pfont2->FontType)
+ break;
+ default:
+ return false;
+ }
+ /* The following, while peculiar, appears to match CPSI. */
+ {
+ const gs_uid *puid1 = &((const gs_font_base *)pfont1)->UID;
+ const gs_uid *puid2 = &((const gs_font_base *)pfont2)->UID;
+ if (uid_is_UniqueID(puid1) || uid_is_UniqueID(puid2) ||
+ ((uid_is_XUID(puid1) || uid_is_XUID(puid2)) &&
+ !uid_equal(puid1, puid2)))
+ return false;
+ }
+ {
+ const font_data *pfd1 = (const font_data *)pfont1->client_data;
+ const font_data *pfd2 = (const font_data *)pfont2->client_data;
+
+ if (!(obj_eq(mem, &pfd1->BuildChar, &pfd2->BuildChar) &&
+ obj_eq(mem, &pfd1->BuildGlyph, &pfd2->BuildGlyph) &&
+ obj_eq(mem, &pfd1->Encoding, &pfd2->Encoding) &&
+ obj_eq(mem, &pfd1->CharStrings, &pfd2->CharStrings)))
+ return false;
+ if (pfont1->FontType == 1) {
+ ref *ppd1, *ppd2;
+
+ if (dict_find_string(&pfd1->dict, "Private", &ppd1) > 0 &&
+ dict_find_string(&pfd2->dict, "Private", &ppd2) > 0 &&
+ !obj_eq(mem, ppd1, ppd2))
+ return false;
+ }
+ }
+ return true;
+}
+
+/* Compare two objects for identity. */
+bool
+obj_ident_eq(const gs_memory_t *mem, const ref * pref1, const ref * pref2)
+{
+ if (r_type(pref1) != r_type(pref2))
+ return false;
+ if (r_has_type(pref1, t_string))
+ return (pref1->value.bytes == pref2->value.bytes &&
+ r_size(pref1) == r_size(pref2));
+ return obj_eq(mem, pref1, pref2);
+}
+
+/*
+ * Set *pchars and *plen to point to the data of a name or string, and
+ * return 0. If the object isn't a name or string, return gs_error_typecheck.
+ * If the object is a string without read access, return gs_error_invalidaccess.
+ */
+int
+obj_string_data(const gs_memory_t *mem, const ref *op, const byte **pchars, uint *plen)
+{
+ switch (r_type(op)) {
+ case t_name: {
+ ref nref;
+
+ name_string_ref(mem, op, &nref);
+ *pchars = nref.value.bytes;
+ *plen = r_size(&nref);
+ return 0;
+ }
+ case t_string:
+ check_read(*op);
+ *pchars = op->value.bytes;
+ *plen = r_size(op);
+ return 0;
+ default:
+ return_error(gs_error_typecheck);
+ }
+}
+
+/*
+ * Create a printable representation of an object, a la cvs and =
+ * (full_print = 0), == (full_print = 1), or === (full_print = 2). Return 0
+ * if OK, 1 if the destination wasn't large enough, gs_error_invalidaccess if the
+ * object's contents weren't readable. If the return value is 0 or 1,
+ * *prlen contains the amount of data returned. start_pos is the starting
+ * output position -- the first start_pos bytes of output are discarded.
+ *
+ * When (restart = false) return gs_error_rangecheck the when destination wasn't
+ * large enough without modifying the destination. This is needed for
+ * compatibility with Adobe implementation of cvs and cvrs, which don't
+ * change the destination string on failure.
+ *
+ * The mem argument is only used for getting the type of structures,
+ * not for allocating; if it is NULL and full_print != 0, structures will
+ * print as --(struct)--.
+ *
+ * This rather complex API is needed so that a client can call obj_cvp
+ * repeatedly to print on a stream, which may require suspending at any
+ * point to handle stream callouts.
+ */
+static void ensure_dot(char *);
+int
+obj_cvp(const ref * op, byte * str, uint len, uint * prlen,
+ int full_print, uint start_pos, const gs_memory_t *mem, bool restart)
+{
+ char buf[50]; /* big enough for any float, double, or struct name */
+ const byte *data = (const byte *)buf;
+ uint size;
+ int code;
+ ref nref;
+
+ if (full_print) {
+ static const char * const type_strings[] = { REF_TYPE_PRINT_STRINGS };
+
+ switch (r_btype(op)) {
+ case t_boolean:
+ case t_integer:
+ break;
+ case t_real: {
+ /*
+ * To get fully accurate output results for IEEE
+ * single-precision floats (24 bits of mantissa), the ANSI %g
+ * default of 6 digits is not enough; 9 are needed.
+ * Unfortunately, using %.9g for floats (as opposed to doubles)
+ * produces unfortunate artifacts such as 0.01 5 mul printing as
+ * 0.049999997. Therefore, we print using %g, and if the result
+ * isn't accurate enough, print again using %.9g.
+ * Unfortunately, a few PostScript programs 'know' that the
+ * printed representation of floats fits into 6 digits (e.g.,
+ * with cvs). We resolve this by letting cvs, cvrs, and = do
+ * what the Adobe interpreters appear to do (use %g), and only
+ * produce accurate output for ==, for which there is no
+ * analogue of cvs. What a hack!
+ */
+ float value = op->value.realval;
+ float scanned;
+
+ gs_sprintf(buf, "%g", value);
+ sscanf(buf, "%f", &scanned);
+ if (scanned != value)
+ gs_sprintf(buf, "%.9g", value);
+ ensure_dot(buf);
+ goto rs;
+ }
+ case t_operator:
+ case t_oparray:
+ code = obj_cvp(op, (byte *)buf + 2, sizeof(buf) - 4, &size, 0, 0, mem, restart);
+ if (code < 0)
+ return code;
+ buf[0] = buf[1] = buf[size + 2] = buf[size + 3] = '-';
+ size += 4;
+ goto nl;
+ case t_name:
+ if (r_has_attr(op, a_executable)) {
+ code = obj_string_data(mem, op, &data, &size);
+ if (code < 0)
+ return code;
+ goto nl;
+ }
+ if (start_pos > 0)
+ return obj_cvp(op, str, len, prlen, 0, start_pos - 1, mem, restart);
+ if (len < 1)
+ return_error(gs_error_rangecheck);
+ code = obj_cvp(op, str + 1, len - 1, prlen, 0, 0, mem, restart);
+ if (code < 0)
+ return code;
+ str[0] = '/';
+ ++*prlen;
+ return code;
+ case t_null:
+ data = (const byte *)"null";
+ goto rs;
+ case t_string:
+ if (!r_has_attr(op, a_read))
+ goto other;
+ size = r_size(op);
+ {
+ bool truncate = (full_print == 1 && size > CVP_MAX_STRING);
+ stream_cursor_read r;
+ stream_cursor_write w;
+ uint skip;
+ byte *wstr;
+ uint len1;
+ int status = 1;
+
+ if (start_pos == 0) {
+ if (len < 1)
+ return_error(gs_error_rangecheck);
+ str[0] = '(';
+ skip = 0;
+ wstr = str + 1;
+ } else {
+ skip = start_pos - 1;
+ wstr = str;
+ }
+ len1 = len + (str - wstr);
+ r.ptr = op->value.const_bytes - 1;
+ r.limit = r.ptr + (truncate ? CVP_MAX_STRING : size);
+ while (skip && status == 1) {
+ uint written;
+
+ w.ptr = (byte *)buf - 1;
+ w.limit = w.ptr + min(skip + len1, sizeof(buf));
+ status = s_PSSE_template.process(NULL, &r, &w, false);
+ written = w.ptr - ((byte *)buf - 1);
+ if (written > skip) {
+ written -= skip;
+ memcpy(wstr, buf + skip, written);
+ wstr += written;
+ skip = 0;
+ break;
+ }
+ skip -= written;
+ }
+ /*
+ * We can reach here with status == 0 (and skip != 0) if
+ * start_pos lies within the trailing ")" or "...)".
+ */
+ if (status == 0) {
+#ifdef DEBUG
+ if (skip > (truncate ? 4 : 1)) {
+ return_error(gs_error_Fatal);
+ }
+#endif
+ }
+ w.ptr = wstr - 1;
+ w.limit = str - 1 + len;
+ if (status == 1)
+ status = s_PSSE_template.process(NULL, &r, &w, false);
+ *prlen = w.ptr - (str - 1);
+ if (status != 0)
+ return 1;
+ if (truncate) {
+ if (len - *prlen < 4 - skip)
+ return 1;
+ memcpy(w.ptr + 1, "...)" + skip, 4 - skip);
+ *prlen += 4 - skip;
+ } else {
+ if (len - *prlen < 1 - skip)
+ return 1;
+ memcpy(w.ptr + 1, ")" + skip, 1 - skip);
+ *prlen += 1 - skip;
+ }
+ }
+ return 0;
+ case t_astruct:
+ case t_struct:
+ if (r_is_foreign(op)) {
+ /* gs_object_type may not work. */
+ data = (const byte *)"-foreign-struct-";
+ goto rs;
+ }
+ if (!mem) {
+ data = (const byte *)"-(struct)-";
+ goto rs;
+ }
+ data = (const byte *)
+ gs_struct_type_name_string(
+ gs_object_type(mem,
+ (const obj_header_t *)op->value.pstruct));
+ size = strlen((const char *)data);
+ if (size > 4 && !memcmp(data + size - 4, "type", 4))
+ size -= 4;
+ if (size > sizeof(buf) - 2)
+ return_error(gs_error_rangecheck);
+ buf[0] = '-';
+ memcpy(buf + 1, data, size);
+ buf[size + 1] = '-';
+ size += 2;
+ data = (const byte *)buf;
+ goto nl;
+ default:
+other:
+ {
+ int rtype = r_btype(op);
+
+ if (rtype >= countof(type_strings))
+ return_error(gs_error_rangecheck);
+ data = (const byte *)type_strings[rtype];
+ if (data == 0)
+ return_error(gs_error_rangecheck);
+ }
+ goto rs;
+ }
+ }
+ /* full_print = 0 */
+ switch (r_btype(op)) {
+ case t_boolean:
+ data = (const byte *)(op->value.boolval ? "true" : "false");
+ break;
+ case t_integer:
+ gs_sprintf(buf, "%"PRIpsint, op->value.intval);
+ break;
+ case t_string:
+ check_read(*op);
+ /* falls through */
+ case t_name:
+ code = obj_string_data(mem, op, &data, &size);
+ if (code < 0)
+ return code;
+ goto nl;
+ case t_oparray: {
+ uint index = op_index(op);
+ const op_array_table *opt = get_op_array(mem, index);
+
+ name_index_ref(mem, opt->nx_table[index - opt->base_index], &nref);
+ name_string_ref(mem, &nref, &nref);
+ code = obj_string_data(mem, &nref, &data, &size);
+ if (code < 0)
+ return code;
+ goto nl;
+ }
+ case t_operator: {
+ /* Recover the name from the initialization table. */
+ uint index = op_index(op);
+
+ /*
+ * Check the validity of the index. (An out-of-bounds index
+ * is only possible when examining an invalid object using
+ * the debugger.)
+ */
+ if (index > 0 && index < op_def_count) {
+ data = (const byte *)(op_index_def(index)->oname + 1);
+ break;
+ }
+ /* Internal operator, no name. */
+ gs_sprintf(buf, "@0x%lx", (ulong) op->value.opproc);
+ break;
+ }
+ case t_real:
+ /*
+ * The value 0.0001 is a boundary case that the Adobe interpreters
+ * print in f-format but at least some gs versions print in
+ * e-format, presumably because of differences in the underlying C
+ * library implementation. Work around this here.
+ */
+ if (op->value.realval == (float)0.0001) {
+ strcpy(buf, "0.0001");
+ } else {
+ gs_sprintf(buf, "%g", op->value.realval);
+ }
+ ensure_dot(buf);
+ break;
+ default:
+ data = (const byte *)"--nostringval--";
+ }
+rs: size = strlen((const char *)data);
+nl: if (size < start_pos)
+ return_error(gs_error_rangecheck);
+ if (!restart && size > len)
+ return_error(gs_error_rangecheck);
+ size -= start_pos;
+ *prlen = min(size, len);
+ memmove(str, data + start_pos, *prlen);
+ return (size > len);
+}
+/*
+ * Make sure the converted form of a real number has at least one of an 'e'
+ * or a decimal point, so it won't be mistaken for an integer.
+ * Re-format the exponent to satisfy Genoa CET test.
+ */
+static void
+ensure_dot(char *buf)
+{
+ char *pe = strchr(buf, 'e');
+ if (pe) {
+ int i;
+ sscanf(pe + 1, "%d", &i);
+ /* MSVC .net 2005 express doesn't support "%+02d" */
+ if (i >= 0)
+ gs_sprintf(pe + 1, "+%02d", i);
+ else
+ gs_sprintf(pe + 1, "-%02d", -i);
+ } else if (strchr(buf, '.') == NULL) {
+ strcat(buf, ".0");
+ }
+}
+
+/*
+ * Create a printable representation of an object, a la cvs and =. Return 0
+ * if OK, gs_error_rangecheck if the destination wasn't large enough,
+ * gs_error_invalidaccess if the object's contents weren't readable. If pchars !=
+ * NULL, then if the object was a string or name, store a pointer to its
+ * characters in *pchars even if it was too large; otherwise, set *pchars =
+ * str. In any case, store the length in *prlen.
+ */
+int
+obj_cvs(const gs_memory_t *mem, const ref * op, byte * str, uint len, uint * prlen,
+ const byte ** pchars)
+{
+ int code = obj_cvp(op, str, len, prlen, 0, 0, mem, false); /* NB: NULL memptr */
+
+ if (code == 1) {
+ if (pchars)
+ obj_string_data(mem, op, pchars, prlen);
+ return gs_note_error(gs_error_rangecheck);
+ } else {
+ if (pchars)
+ *pchars = str;
+ return code;
+ }
+}
+
+/* Find the index of an operator that doesn't have one stored in it. */
+ushort
+op_find_index(const ref * pref /* t_operator */ )
+{
+ op_proc_t proc = real_opproc(pref);
+ const op_def *const *opp = op_defs_all;
+ const op_def *const *opend = opp + (op_def_count / OP_DEFS_MAX_SIZE);
+
+ for (; opp < opend; ++opp) {
+ const op_def *def = *opp;
+
+ for (; def->oname != 0; ++def)
+ if (def->proc == proc)
+ return (opp - op_defs_all) * OP_DEFS_MAX_SIZE + (def - *opp);
+ }
+ /* Lookup failed! This isn't possible.... */
+ return 0;
+}
+
+/*
+ * Convert an operator index to an operator or oparray ref.
+ * This is only used for debugging and for 'get' from packed arrays,
+ * so it doesn't have to be very fast.
+ */
+void
+op_index_ref(const gs_memory_t *mem, uint index, ref * pref)
+{
+ const op_array_table *opt;
+
+ if (op_index_is_operator(index)) {
+ make_oper(pref, index, op_index_proc(index));
+ return;
+ }
+ opt = get_op_array(mem, index);
+ make_tasv(pref, t_oparray, opt->attrs, index,
+ const_refs, (opt->table.value.const_refs
+ + index - opt->base_index));
+}
+
+/* Get an element from an array of some kind. */
+/* This is also used to index into Encoding vectors, */
+/* the error name vector, etc. */
+int
+array_get(const gs_memory_t *mem, const ref * aref, long index_long, ref * pref)
+{
+ if ((ulong)index_long >= r_size(aref))
+ return_error(gs_error_rangecheck);
+ switch (r_type(aref)) {
+ case t_array:
+ {
+ const ref *pvalue = aref->value.refs + index_long;
+
+ ref_assign(pref, pvalue);
+ }
+ break;
+ case t_mixedarray:
+ {
+ const ref_packed *packed = aref->value.packed;
+ uint index = (uint)index_long;
+
+ for (; index--;)
+ packed = packed_next(packed);
+ packed_get(mem, packed, pref);
+ }
+ break;
+ case t_shortarray:
+ {
+ const ref_packed *packed = aref->value.packed + index_long;
+
+ packed_get(mem, packed, pref);
+ }
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ return 0;
+}
+
+/* Get an element from a packed array. */
+/* (This works for ordinary arrays too.) */
+/* Source and destination are allowed to overlap if the source is packed, */
+/* or if they are identical. */
+void
+packed_get(const gs_memory_t *mem, const ref_packed * packed, ref * pref)
+{
+ const ref_packed elt = *packed;
+ uint value = elt & packed_value_mask;
+
+ switch (elt >> r_packed_type_shift) {
+ default: /* (shouldn't happen) */
+ make_null(pref);
+ break;
+ case pt_executable_operator:
+ op_index_ref(mem, value, pref);
+ break;
+ case pt_integer:
+ make_int(pref, (ps_int)value + packed_min_intval);
+ break;
+ case pt_literal_name:
+ name_index_ref(mem, value, pref);
+ break;
+ case pt_executable_name:
+ name_index_ref(mem, value, pref);
+ r_set_attrs(pref, a_executable);
+ break;
+ case pt_full_ref:
+ case pt_full_ref + 1:
+ ref_assign(pref, (const ref *)packed);
+ }
+}
+
+/* Check to make sure an interval contains no object references */
+/* to a space younger than a given one. */
+/* Return 0 or gs_error_invalidaccess. */
+int
+refs_check_space(const ref * bot, uint size, uint space)
+{
+ for (; size--; bot++)
+ store_check_space(space, bot);
+ return 0;
+}
+
+/* ------ String utilities ------ */
+
+/* Convert a C string to a Ghostscript string */
+int
+string_to_ref(const char *cstr, ref * pref, gs_ref_memory_t * mem,
+ client_name_t cname)
+{
+ uint size = strlen(cstr);
+ int code = gs_alloc_string_ref(mem, pref, a_all, size, cname);
+
+ if (code < 0)
+ return code;
+ memcpy(pref->value.bytes, cstr, size);
+ return 0;
+}
+
+/* Convert a Ghostscript string to a C string. */
+/* Return 0 iff the buffer can't be allocated. */
+char *
+ref_to_string(const ref * pref, gs_memory_t * mem, client_name_t cname)
+{
+ uint size = r_size(pref);
+ char *str = (char *)gs_alloc_string(mem, size + 1, cname);
+
+ if (str == 0)
+ return 0;
+ memcpy(str, (const char *)pref->value.bytes, size);
+ str[size] = 0;
+ return str;
+}
+
+/* ------ Operand utilities ------ */
+
+/* Get N numeric operands from the stack or an array. */
+/* Return a bit-mask indicating which ones are integers, */
+/* or a (negative) error indication. */
+/* The 1-bit in the bit-mask refers to the first operand. */
+/* Store float versions of the operands at pval. */
+/* The stack underflow check (check for t__invalid) is harmless */
+/* if the operands come from somewhere other than the stack. */
+int
+num_params(const ref * op, int count, double *pval)
+{
+ int mask = 0;
+
+ pval += count;
+ while (--count >= 0) {
+ mask <<= 1;
+ switch (r_type(op)) {
+ case t_real:
+ *--pval = op->value.realval;
+ break;
+ case t_integer:
+ *--pval = op->value.intval;
+ mask++;
+ break;
+ case t__invalid:
+ return_error(gs_error_stackunderflow);
+ default:
+ return_error(gs_error_typecheck);
+ }
+ op--;
+ }
+ /* If count is very large, mask might overflow. */
+ /* In this case we clearly don't care about the value of mask. */
+ return (mask < 0 ? 0 : mask);
+}
+/* float_params doesn't bother to keep track of the mask. */
+int
+float_params(const ref * op, int count, float *pval)
+{
+ for (pval += count; --count >= 0; --op)
+ switch (r_type(op)) {
+ case t_real:
+ *--pval = op->value.realval;
+ break;
+ case t_integer:
+ *--pval = (float)op->value.intval;
+ break;
+ case t__invalid:
+ return_error(gs_error_stackunderflow);
+ default:
+ return_error(gs_error_typecheck);
+ }
+ return 0;
+}
+
+/* Get N numeric parameters (as floating point numbers) from an array */
+int
+process_float_array(const gs_memory_t *mem, const ref * parray, int count, float * pval)
+{
+ int code = 0, indx0 = 0;
+
+ /* we assume parray is an array of some type, of adequate length */
+ if (r_has_type(parray, t_array))
+ return float_params(parray->value.refs + count - 1, count, pval);
+
+ /* short/mixed array; convert the entries to refs */
+ while (count > 0 && code >= 0) {
+ int i, subcount;
+ ref ref_buff[20]; /* 20 is arbitrary */
+
+ subcount = (count > countof(ref_buff) ? countof(ref_buff) : count);
+ for (i = 0; i < subcount && code >= 0; i++)
+ code = array_get(mem, parray, (long)(i + indx0), &ref_buff[i]);
+ if (code >= 0)
+ code = float_params(ref_buff + subcount - 1, subcount, pval);
+ count -= subcount;
+ pval += subcount;
+ indx0 += subcount;
+ }
+
+ return code;
+}
+
+/* Get a single real parameter. */
+/* The only possible errors are gs_error_typecheck and gs_error_stackunderflow. */
+/* If an error is returned, the return value is not updated. */
+int
+real_param(const ref * op, double *pparam)
+{
+ switch (r_type(op)) {
+ case t_integer:
+ *pparam = op->value.intval;
+ break;
+ case t_real:
+ *pparam = op->value.realval;
+ break;
+ case t__invalid:
+ return_error(gs_error_stackunderflow);
+ default:
+ return_error(gs_error_typecheck);
+ }
+ return 0;
+}
+int
+float_param(const ref * op, float *pparam)
+{
+ double dval;
+ int code = real_param(op, &dval);
+
+ if (code >= 0)
+ *pparam = (float)dval; /* can't overflow */
+ return code;
+}
+
+/* Get an integer parameter in a given range. */
+int
+int_param(const ref * op, int max_value, int *pparam)
+{
+ check_int_leu(*op, max_value);
+ *pparam = (int)op->value.intval;
+ return 0;
+}
+
+/* Make real values on the operand stack. */
+int
+make_reals(ref * op, const double *pval, int count)
+{
+ /* This should return gs_error_limitcheck if any real is too large */
+ /* to fit into a float on the stack. */
+ for (; count--; op++, pval++)
+ make_real(op, *pval);
+ return 0;
+}
+int
+make_floats(ref * op, const float *pval, int count)
+{
+ /* This should return gs_error_undefinedresult for infinities. */
+ for (; count--; op++, pval++)
+ make_real(op, *pval);
+ return 0;
+}
+
+/* Compute the error code when check_proc fails. */
+/* Note that the client, not this procedure, uses return_error. */
+/* The stack underflow check is harmless in the off-stack case. */
+int
+check_proc_failed(const ref * pref)
+{
+ if (r_is_array(pref)) {
+ if (r_has_attr(pref, a_executable))
+ return gs_error_invalidaccess;
+ else
+ return gs_error_typecheck;
+ } else {
+ if (r_has_type(pref, t__invalid))
+ return gs_error_stackunderflow;
+ else
+ return gs_error_typecheck;
+ }
+}
+
+/* Compute the error code when a type check on the stack fails. */
+/* Note that the client, not this procedure, uses return_error. */
+int
+check_type_failed(const ref * op)
+{
+ return (r_has_type(op, t__invalid) ? gs_error_stackunderflow : gs_error_typecheck);
+}
+
+/* ------ Matrix utilities ------ */
+
+/* Read a matrix operand. */
+/* Return 0 if OK, error code if not. */
+int
+read_matrix(const gs_memory_t *mem, const ref * op, gs_matrix * pmat)
+{
+ int code;
+ ref values[6];
+ const ref *pvalues;
+
+ switch (r_type(op)) {
+ case t_array:
+ pvalues = op->value.refs;
+ break;
+ case t_mixedarray:
+ case t_shortarray:
+ {
+ int i;
+
+ for (i = 0; i < 6; ++i) {
+ code = array_get(mem, op, (long)i, &values[i]);
+ if (code < 0)
+ return code;
+ }
+ pvalues = values;
+ }
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ check_read(*op);
+ if (r_size(op) != 6)
+ return_error(gs_error_rangecheck);
+ code = float_params(pvalues + 5, 6, (float *)pmat);
+ return (code < 0 ? code : 0);
+}
+
+/* Write a matrix operand. */
+/* Return 0 if OK, error code if not. */
+int
+write_matrix_in(ref * op, const gs_matrix * pmat, gs_dual_memory_t *idmemory,
+ gs_ref_memory_t *imem)
+{
+ ref *aptr;
+ const float *pel;
+ int i;
+
+ check_write_type(*op, t_array);
+ if (r_size(op) != 6)
+ return_error(gs_error_rangecheck);
+ aptr = op->value.refs;
+ pel = (const float *)pmat;
+ for (i = 5; i >= 0; i--, aptr++, pel++) {
+ if (idmemory) {
+ ref_save(op, aptr, "write_matrix");
+ make_real_new(aptr, *pel);
+ } else {
+ make_tav(aptr, t_real, imemory_new_mask(imem), realval, *pel);
+ }
+ }
+ return 0;
+}
diff --git a/psi/iutil.h b/psi/iutil.h
new file mode 100644
index 000000000..f51790f29
--- /dev/null
+++ b/psi/iutil.h
@@ -0,0 +1,153 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to interpreter utilities */
+/* Requires imemory.h, ostack.h */
+
+#ifndef iutil_INCLUDED
+# define iutil_INCLUDED
+
+/* ------ Object utilities ------ */
+
+/* Copy refs from one place to another. */
+/* (If we are copying to the stack, we can just use memcpy.) */
+void refcpy_to_new(ref * to, const ref * from, uint size,
+ gs_dual_memory_t *dmem);
+int refcpy_to_old(ref * aref, uint index, const ref * from, uint size,
+ gs_dual_memory_t *dmem, client_name_t cname);
+
+/*
+ * Fill an array with nulls.
+ * For backward compatibility, we define the procedure with a new name.
+ */
+void refset_null_new(ref * to, uint size, uint new_mask);
+#define refset_null(to, size) refset_null_new(to, size, ialloc_new_mask)
+
+/* Compare two objects for equality. */
+bool obj_eq(const gs_memory_t *mem, const ref *, const ref *);
+
+/* Compare two objects for identity. */
+/* (This is not a standard PostScript concept.) */
+bool obj_ident_eq(const gs_memory_t *mem, const ref *, const ref *);
+
+/*
+ * Set *pchars and *plen to point to the data of a name or string, and
+ * return 0. If the object isn't a name or string, return gs_error_typecheck.
+ * If the object is a string without read access, return gs_error_invalidaccess.
+ */
+int obj_string_data(const gs_memory_t *mem, const ref *op, const byte **pchars, uint *plen);
+
+/*
+ * Create a printable representation of an object, a la cvs and =
+ * (full_print = 0), == (full_print = 1), or === (full_print = 2). Return 0
+ * if OK, 1 if the destination wasn't large enough, gs_error_invalidaccess if the
+ * object's contents weren't readable. If the return value is 0 or 1,
+ * *prlen contains the amount of data returned. start_pos is the starting
+ * output position -- the first start_pos bytes of output are discarded.
+ *
+ * The mem argument is only used for getting the type of structures,
+ * not for allocating; if it is NULL and full_print != 0, structures will
+ * print as --(struct)--.
+ */
+#define CVP_MAX_STRING 200 /* strings are truncated here if full_print = 1 */
+int obj_cvp(const ref * op, byte *str, uint len, uint * prlen,
+ int full_print, uint start_pos, const gs_memory_t *mem, bool restart);
+
+/*
+ * Create a printable representation of an object, a la cvs and =. Return 0
+ * if OK, gs_error_rangecheck if the destination wasn't large enough,
+ * gs_error_invalidaccess if the object's contents weren't readable. If pchars !=
+ * NULL, then if the object was a string or name, store a pointer to its
+ * characters in *pchars even if it was too large; otherwise, set *pchars =
+ * str. In any case, store the length in *prlen.
+ *
+ * obj_cvs is different from obj_cvp in two respects: if the printed
+ * representation is too large, it returns gs_error_rangecheck rather than 1;
+ * and it can return a pointer to the data for names and strings, like
+ * obj_string_data.
+ */
+int obj_cvs(const gs_memory_t *mem, const ref * op, byte * str, uint len, uint * prlen,
+ const byte ** pchars);
+
+/* Get an element from an array (packed or not). */
+int array_get(const gs_memory_t *mem, const ref *, long, ref *);
+
+/* Get an element from a packed array. */
+/* (This works for ordinary arrays too.) */
+/* Source and destination are allowed to overlap if the source is packed, */
+/* or if they are identical. */
+void packed_get(const gs_memory_t *mem, const ref_packed *, ref *);
+
+/* Check to make sure an interval contains no object references */
+/* to a space younger than a given one. */
+/* Return 0 or gs_error_invalidaccess. */
+int refs_check_space(const ref * refs, uint size, uint space);
+
+/* ------ String utilities ------ */
+
+/* Convert a C string to a string object. */
+int string_to_ref(const char *, ref *, gs_ref_memory_t *, client_name_t);
+
+/* Convert a string object to a C string. */
+/* Return 0 iff the buffer can't be allocated. */
+char *ref_to_string(const ref *, gs_memory_t *, client_name_t);
+
+/* ------ Operand utilities ------ */
+
+/* Get N numeric operands from the stack or an array. */
+int num_params(const ref *, int, double *);
+
+/* float_params can lose accuracy for large integers. */
+int float_params(const ref *, int, float *);
+
+/* process_float_array can lose accuracy for large integers */
+int process_float_array(const gs_memory_t *mem, const ref *, int, float *);
+
+/* Get a single real parameter. */
+/* The only possible error is gs_error_typecheck. */
+int real_param(const ref *, double *);
+
+/* float_param can lose accuracy for large integers. */
+int float_param(const ref *, float *);
+
+/* Get an integer parameter in a given range. */
+int int_param(const ref *, int, int *);
+
+/* Make real values on the stack. */
+/* Return gs_error_limitcheck for infinities or double->float overflow. */
+int make_reals(ref *, const double *, int);
+int make_floats(ref *, const float *, int);
+
+/* Define the gs_matrix type if necessary. */
+#ifndef gs_matrix_DEFINED
+# define gs_matrix_DEFINED
+typedef struct gs_matrix_s gs_matrix;
+#endif
+
+/* Read a matrix operand. */
+int read_matrix(const gs_memory_t *mem, const ref *, gs_matrix *);
+
+/* Write a matrix operand. */
+/* If dmem is NULL, the array is guaranteed newly allocated in imem. */
+/* If dmem is not NULL, imem is ignored. */
+int write_matrix_in(ref *op, const gs_matrix *pmat, gs_dual_memory_t *dmem,
+ gs_ref_memory_t *imem);
+#define write_matrix_new(op, pmat, imem)\
+ write_matrix_in(op, pmat, NULL, imem)
+#define write_matrix(op, pmat)\
+ write_matrix_in(op, pmat, idmemory, NULL)
+
+#endif /* iutil_INCLUDED */
diff --git a/psi/iutil2.c b/psi/iutil2.c
new file mode 100644
index 000000000..3d04eeaa3
--- /dev/null
+++ b/psi/iutil2.c
@@ -0,0 +1,147 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 2 utilities for Ghostscript interpreter */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "ierrors.h"
+#include "opcheck.h"
+#include "gsparam.h"
+#include "gsutil.h" /* bytes_compare prototype */
+#include "idict.h"
+#include "imemory.h" /* for iutil.h */
+#include "iutil.h"
+#include "iutil2.h"
+
+/* ------ Password utilities ------ */
+
+/* Read a password from a parameter list. */
+/* Return 0 if present, 1 if absent, or an error code. */
+int
+param_read_password(gs_param_list * plist, const char *kstr, password * ppass)
+{
+ gs_param_string ps;
+ long ipass;
+ int code = param_read_string(plist, kstr, &ps);
+
+ switch (code) {
+ case 0: /* OK */
+ if (ps.size > MAX_PASSWORD)
+ return_error(gs_error_limitcheck);
+ /* Copy the data back. */
+ memcpy(ppass->data, ps.data, ps.size);
+ ppass->size = ps.size;
+ return 0;
+ case 1: /* key is missing */
+ return 1;
+ }
+ /* We might have gotten a typecheck because */
+ /* the supplied password was an integer. */
+ if (code != gs_error_typecheck)
+ return code;
+ code = param_read_long(plist, kstr, &ipass);
+ if (code != 0) /* error or missing */
+ return code;
+ gs_sprintf((char *)ppass->data, "%ld", ipass);
+ ppass->size = strlen((char *)ppass->data);
+ return 0;
+}
+/* Write a password to a parameter list. */
+int
+param_write_password(gs_param_list * plist, const char *kstr,
+ const password * ppass)
+{
+ gs_param_string ps;
+
+ ps.data = (const byte *)ppass->data, ps.size = ppass->size,
+ ps.persistent = false;
+ if (ps.size > MAX_PASSWORD)
+ return_error(gs_error_limitcheck);
+ return param_write_string(plist, kstr, &ps);
+}
+
+/* Check a password from a parameter list. */
+/* Return 0 if OK, 1 if not OK, or an error code. */
+int
+param_check_password(gs_param_list * plist, const password * ppass)
+{
+ if (ppass->size != 0) {
+ password pass;
+ int code = param_read_password(plist, "Password", &pass);
+
+ if (code)
+ return code;
+ if (pass.size != ppass->size ||
+ bytes_compare(&pass.data[0], pass.size,
+ &ppass->data[0],
+ ppass->size) != 0
+ )
+ return 1;
+ }
+ return 0;
+}
+
+/* Read a password from, or write a password into, a dictionary */
+/* (presumably systemdict). */
+static int
+dict_find_password(ref ** ppvalue, const ref * pdref, const char *kstr)
+{
+ ref *pvalue;
+
+ if (dict_find_string(pdref, kstr, &pvalue) <= 0)
+ return_error(gs_error_undefined);
+ if (!r_has_type(pvalue, t_string) ||
+ r_has_attrs(pvalue, a_read) ||
+ pvalue->value.const_bytes[0] >= r_size(pvalue)
+ )
+ return_error(gs_error_rangecheck);
+ *ppvalue = pvalue;
+ return 0;
+}
+int
+dict_read_password(password * ppass, const ref * pdref, const char *pkey)
+{
+ ref *pvalue;
+ int code = dict_find_password(&pvalue, pdref, pkey);
+
+ if (code < 0)
+ return code;
+ if (pvalue->value.const_bytes[0] > MAX_PASSWORD)
+ return_error(gs_error_rangecheck); /* limitcheck? */
+ memcpy(ppass->data, pvalue->value.const_bytes + 1,
+ (ppass->size = pvalue->value.const_bytes[0]));
+ return 0;
+}
+int
+dict_write_password(const password * ppass, ref * pdref, const char *pkey,
+ bool change_allowed)
+{
+ ref *pvalue;
+ int code = dict_find_password(&pvalue, pdref, pkey);
+
+ if (code < 0)
+ return code;
+ if (ppass->size >= r_size(pvalue))
+ return_error(gs_error_rangecheck);
+ if (!change_allowed &&
+ bytes_compare(pvalue->value.bytes + 1, pvalue->value.bytes[0],
+ ppass->data, ppass->size) != 0)
+ return_error(gs_error_invalidaccess);
+ memcpy(pvalue->value.bytes + 1, ppass->data,
+ (pvalue->value.bytes[0] = ppass->size));
+ return 0;
+}
diff --git a/psi/iutil2.h b/psi/iutil2.h
new file mode 100644
index 000000000..6208f7a14
--- /dev/null
+++ b/psi/iutil2.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Interface to procedures in iutil2.c */
+
+#ifndef iutil2_INCLUDED
+# define iutil2_INCLUDED
+
+/* ------ Password utilities ------ */
+
+/* Define the password structure. */
+/* NOTE: MAX_PASSWORD must match the initial password lengths in gs_lev2.ps. */
+#define MAX_PASSWORD 64 /* must be at least 11 */
+typedef struct password_s {
+ uint size;
+ byte data[MAX_PASSWORD];
+} password;
+
+# define NULL_PASSWORD {0, {0}}
+
+/* Transmit a password to or from a parameter list. */
+int param_read_password(gs_param_list *, const char *, password *);
+int param_write_password(gs_param_list *, const char *, const password *);
+
+/* Check a password from a parameter list. */
+/* Return 0 if OK, 1 if not OK, or an error code. */
+int param_check_password(gs_param_list *, const password *);
+
+/* Read a password from, or write a password into, a dictionary */
+/* (presumably systemdict). */
+int dict_read_password(password *, const ref *, const char *);
+int dict_write_password(const password *, ref *, const char *, bool);
+
+#endif /* iutil2_INCLUDED */
diff --git a/psi/ivmem2.h b/psi/ivmem2.h
new file mode 100644
index 000000000..d0e057598
--- /dev/null
+++ b/psi/ivmem2.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* VM control user parameter procedures */
+
+#ifndef ivmem2_INCLUDED
+# define ivmem2_INCLUDED
+
+/* Exported by zvmem2.c for zusparam.c */
+int set_vm_reclaim(i_ctx_t *, long);
+int set_vm_threshold(i_ctx_t *, long);
+
+#endif /* ivmem2_INCLUDED */
diff --git a/psi/ivmspace.h b/psi/ivmspace.h
new file mode 100644
index 000000000..a028ccfdd
--- /dev/null
+++ b/psi/ivmspace.h
@@ -0,0 +1,102 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Local/global space management */
+/* Requires iref.h */
+
+#ifndef ivmspace_INCLUDED
+# define ivmspace_INCLUDED
+
+#include "gsgc.h"
+
+/*
+ * r_space_bits and r_space_shift, which define the bits in a ref
+ * that carry VM space information, are defined in iref.h.
+ * r_space_bits must be at least 2.
+ */
+#define a_space (((1 << r_space_bits) - 1) << r_space_shift)
+/*
+ * The i_vm_xxx values are defined in gsgc.h.
+ */
+typedef enum {
+ avm_foreign = (i_vm_foreign << r_space_shift),
+ avm_system = (i_vm_system << r_space_shift),
+ avm_global = (i_vm_global << r_space_shift),
+ avm_local = (i_vm_local << r_space_shift),
+ avm_max = avm_local
+} avm_space;
+
+#define r_space(rp) (avm_space)(r_type_attrs(rp) & a_space)
+#define r_space_index(rp) ((int)r_space(rp) >> r_space_shift)
+#define r_set_space(rp,space) r_store_attrs(rp, a_space, (uint)space)
+
+/*
+ * According to the PostScript language specification, attempting to store
+ * a reference to a local object into a global object must produce an
+ * invalidaccess error. However, systemdict must be able to refer to
+ * a number of local dictionaries such as userdict and errordict.
+ * Therefore, we implement a special hack in 'def' that allows such stores
+ * if the dictionary being stored into is systemdict (which is normally
+ * only writable during initialization) or a dictionary that appears
+ * in systemdict (such as level2dict), and the current save level is zero
+ * (to guarantee that we can't get dangling pointers).
+ * We could allow this for any global dictionary, except that the garbage
+ * collector must treat any such dictionaries as roots when collecting
+ * local VM without collecting global VM.
+ * We make a similar exception for .makeglobaloperator; this requires
+ * treating the operator table as a GC root as well.
+ *
+ * We extend the local-into-global store check because we have four VM
+ * spaces (local, global, system, and foreign), and we allow PostScript
+ * programs to create objects in any of the first three. If we define
+ * the "generation" of an object as foreign = 0, system = 1, global = 2,
+ * and local = 3, then a store is legal iff the generation of the object
+ * into which a pointer is being stored is greater than or equal to
+ * the generation of the object into which the store is occurring.
+ *
+ * We must check for local-into-global stores in three categories of places:
+ *
+ * - The scanner, when it encounters a //name inside {}.
+ *
+ * - All operators that allocate ref-containing objects and also
+ * store into them:
+ * packedarray gstate makepattern?
+ * makefont scalefont definefont filter
+ *
+ * - All operators that store refs into existing objects
+ * ("operators" marked with * are actually PostScript procedures):
+ * put(array) putinterval(array) astore copy(to array)
+ * def store* put(dict) copy(dict)
+ * dictstack execstack .make(global)operator
+ * currentgstate defineusername
+ */
+
+/* Test whether an object is in local space, */
+/* which implies that we need not check when storing into it. */
+#define r_is_local(rp) (r_space(rp) == avm_local)
+/* Test whether an object is foreign, i.e., outside known space. */
+#define r_is_foreign(rp) (r_space(rp) == avm_foreign)
+/* Check whether a store is allowed. */
+#define store_check_space(destspace,rpnew)\
+ if ( r_space(rpnew) > (destspace) )\
+ return_error(gs_error_invalidaccess)
+#define store_check_dest(rpdest,rpnew)\
+ store_check_space(r_space(rpdest), rpnew)
+/* BACKWARD COMPATIBILITY (not used by any Ghostscript code per se) */
+#define check_store_space(rdest,rnewcont)\
+ store_check_dest(&(rdest),&(rnewcont))
+
+#endif /* ivmspace_INCLUDED */
diff --git a/psi/main.h b/psi/main.h
new file mode 100644
index 000000000..7fe214ddf
--- /dev/null
+++ b/psi/main.h
@@ -0,0 +1,107 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Backward-compatible interface to gsmain.c */
+
+#ifndef main_INCLUDED
+# define main_INCLUDED
+
+#include "iapi.h"
+#include "imain.h"
+#include "iminst.h"
+
+/*
+ * This file adds to imain.h some backward-compatible procedures and
+ * data elements that assume there is only a single instance of
+ * the interpreter.
+ */
+
+/* conditional out the entire file */
+#if 0
+
+/* ================ Data elements ================ */
+
+/* Clients should never access these directly. */
+
+#define gs_user_errors (gs_main_instance_default()->user_errors)
+#define gs_lib_path (gs_main_instance_default()->lib_path)
+/* gs_lib_paths removed in release 3.65 */
+/* gs_lib_env_path removed in release 3.65 */
+
+/* ================ Exported procedures from gsmain.c ================ */
+
+/* ---------------- Initialization ---------------- */
+
+#define gs_init0(in, out, err, mlp)\
+ gs_main_init0(gs_main_instance_default(), in, out, err, mlp)
+
+#define gs_init1()\
+ gs_main_init1(gs_main_instance_default())
+
+#define gs_init2()\
+ gs_main_init2(gs_main_instance_default())
+
+#define gs_add_lib_path(path)\
+ gs_main_add_lib_path(gs_main_instance_default(), path)
+
+#define gs_set_lib_paths()\
+ gs_main_set_lib_paths(gs_main_instance_default())
+
+#define gs_lib_open(fname, pfile)\
+ gs_main_lib_open(gs_main_instance_default(), fname, pfile)
+
+/* ---------------- Execution ---------------- */
+
+#define gs_run_file(fn, ue, pec, peo)\
+ gs_main_run_file(gs_main_instance_default(), fn, ue, pec, peo)
+
+#define gs_run_string(str, ue, pec, peo)\
+ gs_main_run_string(gs_main_instance_default(), str, ue, pec, peo)
+
+#define gs_run_string_with_length(str, len, ue, pec, peo)\
+ gs_main_run_string_with_length(gs_main_instance_default(),\
+ str, len, ue, pec, peo)
+
+#define gs_run_file_open(fn, pfref)\
+ gs_main_run_file_open(gs_main_instance_default(), fn, pfref)
+
+#define gs_run_string_begin(ue, pec, peo)\
+ gs_main_run_string_begin(gs_main_instance_default(), ue, pec, peo)
+
+#define gs_run_string_continue(str, len, ue, pec, peo)\
+ gs_main_run_string_continue(gs_main_instance_default(),\
+ str, len, ue, pec, peo)
+
+#define gs_run_string_end(ue, pec, peo)\
+ gs_main_run_string_end(gs_main_instance_default(), ue, pec, peo)
+
+/* ---------------- Debugging ---------------- */
+
+/*
+ * We should have the following definition:
+
+#define gs_debug_dump_stack(code, peo)\
+ gs_main_dump_stack(gs_main_instance_default(), code, peo)
+
+ * but we make it a procedure instead so it can be called from debuggers.
+ */
+void gs_debug_dump_stack(int code, ref * perror_object);
+
+/* ---------------- Termination ---------------- */
+
+#endif /* full file conditional */
+
+#endif /* main_INCLUDED */
diff --git a/psi/mkfilelt.cpp b/psi/mkfilelt.cpp
new file mode 100644
index 000000000..30040ed1d
--- /dev/null
+++ b/psi/mkfilelt.cpp
@@ -0,0 +1,368 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+//
+//
+// This is the setup program for Win32 GPL Ghostscript
+//
+// The starting point is a self extracting zip archive
+// with the following contents:
+// setupgs.exe
+// uninstgs.exe
+// filelist.txt (contains list of program files)
+// gs#.##\* (files listed in filelist.txt)
+// This is the same as the zip file created by Aladdin Enterprises,
+// with the addition of setupgs.exe, uninstgs.exe, and filelist.txt
+//
+// The first line of the file filelist.txt
+// contains the uninstall name to be used.
+// The second line contains name of the main directory where
+// uninstall log files are to be placed.
+// Subsequent lines contain files to be copied (but not directories).
+// For example, filelist.txt might contain:
+// GPL Ghostscript 8.55
+// gs8.55
+// gs8.55\bin\gsdll32.dll
+// gs8.55\lib\gs_init.ps
+//
+// The default install directory is c:\gs.
+// The default Start Menu Folder is Ghostscript.
+// These are set in the resources.
+// The setup program will create the following uninstall log files
+// c:\gs\gs#.##\uninstal.txt
+// The uninstall program (accessed through control panel) will not
+// remove directories nor will it remove itself.
+//
+// If the install directory is the same as the current file
+// location, no files will be copied, but the existence of each file
+// will be checked. This allows the archive to be unzipped, then
+// configured in its current location. Running the uninstall will not
+// remove uninstgs.exe, setupgs.exe, or filelist.txt.
+
+
+#define STRICT
+#include <windows.h>
+#include <shellapi.h>
+#include <objbase.h>
+#include <shlobj.h>
+#include <stdio.h>
+#include <direct.h>
+#include <ctype.h>
+
+#pragma comment(lib,"user32.lib")
+
+#ifdef MAX_PATH
+#define MAXSTR MAX_PATH
+#else
+#define MAXSTR 256
+#endif
+
+CHAR g_szAppName[MAXSTR];
+
+// Prototypes
+BOOL init();
+BOOL make_filelist(int argc, char *argv[]);
+
+
+//////////////////////////////////////////////////////////////////////
+// Entry point
+//////////////////////////////////////////////////////////////////////
+
+int APIENTRY WinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine,
+ int nCmdShow)
+{
+ strcpy(g_szAppName, "make_filelist");
+ if (!init()) {
+ MessageBox(HWND_DESKTOP, "Initialisation failed",
+ g_szAppName, MB_OK);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////
+// Initialisation and Main dialog box
+//////////////////////////////////////////////////////////////////////
+
+void
+message_box(const char *str)
+{
+ MessageBox(HWND_DESKTOP, str, g_szAppName, MB_OK);
+}
+
+
+BOOL
+init()
+{
+ DWORD dwVersion = GetVersion();
+ // get source directory
+
+
+ if (LOBYTE(LOWORD(dwVersion)) < 4) {
+ MessageBox(HWND_DESKTOP,
+ "This install program needs Windows 4.0 or later",
+ g_szAppName, MB_OK);
+ return FALSE;
+ }
+
+
+#define MAXCMDTOKENS 128
+
+ int argc;
+ LPSTR argv[MAXCMDTOKENS];
+ LPSTR p;
+ char *args;
+ char *d, *e;
+
+ p = GetCommandLine();
+
+ argc = 0;
+ args = (char *)malloc(lstrlen(p)+1);
+ if (args == (char *)NULL)
+ return 1;
+
+ // Parse command line handling quotes.
+ d = args;
+ while (*p) {
+ // for each argument
+
+ if (argc >= MAXCMDTOKENS - 1)
+ break;
+
+ e = d;
+ while ((*p) && (*p != ' ')) {
+ if (*p == '\042') {
+ // Remove quotes, skipping over embedded spaces.
+ // Doesn't handle embedded quotes.
+ p++;
+ while ((*p) && (*p != '\042'))
+ *d++ =*p++;
+ }
+ else
+ *d++ = *p;
+ if (*p)
+ p++;
+ }
+ *d++ = '\0';
+ argv[argc++] = e;
+
+ while ((*p) && (*p == ' '))
+ p++; // Skip over trailing spaces
+ }
+ argv[argc] = NULL;
+
+ if (argc > 2) {
+ // Probably creating filelist.txt
+ return make_filelist(argc, argv);
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// Create file list
+//////////////////////////////////////////////////////////////////////
+
+FILE *fList;
+
+typedef int (*PFN_dodir)(const char *name);
+
+/* Called once for each directory */
+int
+dodir(const char *filename)
+{
+ return 0;
+}
+
+/* Called once for each file */
+int
+dofile(const char *filename)
+{
+ if (fList != (FILE *)NULL) {
+ fputs(filename, fList);
+ fputs("\n", fList);
+ }
+
+ return 0;
+}
+
+
+/* Walk through directory 'path', calling dodir() for given directory
+ * and dofile() for each file.
+ * If recurse=1, recurse into subdirectories, calling dodir() for
+ * each directory.
+ */
+int
+dirwalk(char *path, int recurse, PFN_dodir dodir, PFN_dodir dofile)
+{
+ WIN32_FIND_DATA find_data;
+ HANDLE find_handle;
+ char pattern[MAXSTR]; /* orig pattern + modified pattern */
+ char base[MAXSTR];
+ char name[MAXSTR];
+ BOOL bMore = TRUE;
+ char *p;
+
+
+ if (path) {
+ strcpy(pattern, path);
+ if (strlen(pattern) != 0) {
+ p = pattern + strlen(pattern) -1;
+ if (*p == '\\')
+ *p = '\0'; // truncate trailing backslash
+ }
+
+ strcpy(base, pattern);
+ if (strchr(base, '*') != NULL) {
+ // wildcard already included
+ // truncate it from the base path
+ if ( (p = strrchr(base, '\\')) != NULL )
+ *(++p) = '\0';
+ }
+ else if (isalpha(pattern[0]) &&
+ pattern[1]==':' && pattern[2]=='\0') {
+ strcat(pattern, "\\*"); // search entire disk
+ strcat(base, "\\");
+ }
+ else {
+ // wildcard NOT included
+ // check to see if path is a directory
+ find_handle = FindFirstFile(pattern, &find_data);
+ if (find_handle != INVALID_HANDLE_VALUE) {
+ FindClose(find_handle);
+ if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ strcat(pattern, "\\*"); // yes, search files
+ strcat(base, "\\");
+ }
+ else {
+ dofile(path); // no, return just this file
+ return 0;
+ }
+ }
+ else
+ return 1; // path invalid
+ }
+ }
+ else {
+ base[0] = '\0';
+ strcpy(pattern, "*");
+ }
+
+ find_handle = FindFirstFile(pattern, &find_data);
+ if (find_handle == INVALID_HANDLE_VALUE)
+ return 1;
+
+ while (bMore) {
+ strcpy(name, base);
+ strcat(name, find_data.cFileName);
+ if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if ( strcmp(find_data.cFileName, ".") &&
+ strcmp(find_data.cFileName, "..") ) {
+ dodir(name);
+ if (recurse)
+ dirwalk(name, recurse, dodir, dofile);
+ }
+ }
+ else {
+ dofile(name);
+ }
+ bMore = FindNextFile(find_handle, &find_data);
+ }
+ FindClose(find_handle);
+
+ return 0;
+}
+
+
+
+// This is used when creating a file list.
+
+BOOL make_filelist(int argc, char *argv[])
+{
+ char *title = NULL;
+ char *dir = NULL;
+ char *list = NULL;
+ int i;
+
+ for (i=1; i<argc; i++) {
+ if (strcmp(argv[i], "-title") == 0) {
+ i++;
+ title = argv[i];
+ }
+ else if (strcmp(argv[i], "-dir") == 0) {
+ i++;
+ dir = argv[i];
+ }
+ else if (strcmp(argv[i], "-list") == 0) {
+ i++;
+ list = argv[i];
+ }
+ else {
+ if ((title == NULL) || (strlen(title) == 0) ||
+ (dir == NULL) || (strlen(dir) == 0) ||
+ (list == NULL) || (strlen(list) == 0)) {
+ message_box("Usage: make_filelist -title \042GPL Ghostscript #.##\042 -dir \042gs#.##\042 -list \042filelist.txt\042 spec1 spec2 specn\n");
+ return FALSE;
+ }
+ if (fList == (FILE *)NULL) {
+ if ( (fList = fopen(list, "w")) == (FILE *)NULL ) {
+ message_box("Can't write list file\n");
+ return FALSE;
+ }
+ fputs(title, fList);
+ fputs("\n", fList);
+ fputs(dir, fList);
+ fputs("\n", fList);
+ }
+ if (argv[i][0] == '@') {
+ // Use @filename with list of files/directories
+ // to avoid DOS command line limit
+ FILE *f;
+ char buf[MAXSTR];
+ int j;
+ if ( (f = fopen(&(argv[i][1]), "r")) != (FILE *)NULL) {
+ while (fgets(buf, sizeof(buf), f)) {
+ // remove trailing newline and spaces
+ while ( ((j = strlen(buf)-1) >= 0) &&
+ ((buf[j] == '\n') || (buf[j] == ' ')) )
+ buf[j] = '\0';
+ dirwalk(buf, TRUE, &dodir, &dofile);
+ }
+ fclose(f);
+ }
+ else {
+ wsprintf(buf, "Can't open @ file \042%s\042",
+ &argv[i][1]);
+ message_box(buf);
+ }
+ }
+ else
+ dirwalk(argv[i], TRUE, &dodir, &dofile);
+ }
+ }
+
+ if (fList != (FILE *)NULL) {
+ fclose(fList);
+ fList = NULL;
+ }
+ return TRUE;
+}
+
diff --git a/psi/msvc.mak b/psi/msvc.mak
new file mode 100644
index 000000000..74ec63531
--- /dev/null
+++ b/psi/msvc.mak
@@ -0,0 +1,1788 @@
+# Copyright (C) 2001-2015 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+# CA 94903, U.S.A., +1(415)492-9861, for further information.
+#
+#
+# $Id: msvc32.mak 12087 2011-02-01 11:57:26Z robin $
+# makefile for 32-bit Microsoft Visual C++, Windows NT or Windows 95 platform.
+#
+# All configurable options are surrounded by !ifndef/!endif to allow
+# preconfiguration from within another makefile.
+#
+# Optimization /O2 seems OK with MSVC++ 4.1, but not with 5.0.
+# Created 1997-01-24 by Russell Lang from MSVC++ 2.0 makefile.
+# Enhanced 97-05-15 by JD
+# Common code factored out 1997-05-22 by L. Peter Deutsch.
+# Made pre-configurable by JD 6/4/98
+# Revised to use subdirectories 1998-11-13 by lpd.
+
+# Note: If you are planning to make self-extracting executables,
+# see winint.mak to find out about third-party software you will need.
+
+# If we are building MEMENTO=1, then adjust default debug flags
+!if "$(MEMENTO)"=="1"
+!ifndef DEBUG
+DEBUG=1
+!endif
+!ifndef TDEBUG
+TDEBUG=1
+!endif
+!ifndef DEBUGSYM
+DEBUGSYM=1
+!endif
+!endif
+
+# If we are building PROFILE=1, then adjust default debug flags
+!if "$(PROFILE)"=="1"
+!ifndef DEBUG
+DEBUG=0
+!endif
+!ifndef TDEBUG
+TDEBUG=0
+!endif
+!ifndef DEBUGSYM
+DEBUGSYM=1
+!endif
+!endif
+
+# Pick the target architecture file
+!if "$(TARGET_ARCH_FILE)"==""
+!ifdef WIN64
+TARGET_ARCH_FILE=$(GLSRCDIR)\..\arch\windows-x64-msvc.h
+!else
+!ifdef ARM
+TARGET_ARCH_FILE=$(GLSRCDIR)\..\arch\windows-arm-msvc.h
+!else
+TARGET_ARCH_FILE=$(GLSRCDIR)\..\arch\windows-x86-msvc.h
+!endif
+!endif
+!endif
+
+# ------------------------------- Options ------------------------------- #
+
+###### This section is the only part of the file you should need to edit.
+
+# ------ Generic options ------ #
+
+# Define the directory for the final executable, and the
+# source, generated intermediate file, and object directories
+# for the graphics library (GL) and the PostScript/PDF interpreter (PS).
+
+!if "$(MEMENTO)"=="1"
+DEFAULT_OBJ_DIR=.\$(PRODUCT_PREFIX)memobj
+!else
+!if "$(PROFILE)"=="1"
+DEFAULT_OBJ_DIR=.\$(PRODUCT_PREFIX)profobj
+!else
+!if "$(DEBUG)"=="1"
+DEFAULT_OBJ_DIR=.\$(PRODUCT_PREFIX)debugobj
+!else
+DEFAULT_OBJ_DIR=.\$(PRODUCT_PREFIX)obj
+!endif
+!endif
+!endif
+!ifdef METRO
+DEFAULT_OBJ_DIR=$(DEFAULT_OBJ_DIR)rt
+!endif
+!ifdef WIN64
+DEFAULT_OBJ_DIR=$(DEFAULT_OBJ_DIR)64
+!endif
+
+!ifndef AUXDIR
+AUXDIR=$(DEFAULT_OBJ_DIR)\aux_
+!endif
+
+# Note that 32-bit and 64-bit binaries reside in a common directory
+# since the names are unique
+!ifndef BINDIR
+!if "$(MEMENTO)"=="1"
+BINDIR=.\membin
+!else
+!if "$(DEBUG)"=="1"
+BINDIR=.\debugbin
+!else
+!if "$(DEBUGSYM)"=="1"
+BINDIR=.\profbin
+!else
+BINDIR=.\bin
+!endif
+!endif
+!endif
+!endif
+!ifndef GLSRCDIR
+GLSRCDIR=.\base
+!endif
+!ifndef GLGENDIR
+GLGENDIR=$(DEFAULT_OBJ_DIR)
+!endif
+!ifndef DEVSRCDIR
+DEVSRCDIR=.\devices
+!endif
+!ifndef GLOBJDIR
+GLOBJDIR=$(DEFAULT_OBJ_DIR)
+!endif
+!ifndef DEVGENDIR
+DEVGENDIR=$(DEFAULT_OBJ_DIR)
+!endif
+!ifndef DEVOBJDIR
+DEVOBJDIR=$(DEFAULT_OBJ_DIR)
+!endif
+
+!ifndef PSSRCDIR
+PSSRCDIR=.\psi
+!endif
+!ifndef PSLIBDIR
+PSLIBDIR=.\lib
+!endif
+!ifndef PSRESDIR
+PSRESDIR=.\Resource
+!endif
+!ifndef PSGENDIR
+PSGENDIR=$(DEFAULT_OBJ_DIR)
+!endif
+!ifndef PSOBJDIR
+PSOBJDIR=$(DEFAULT_OBJ_DIR)
+!endif
+!ifndef SBRDIR
+SBRDIR=$(DEFAULT_OBJ_DIR)
+!endif
+
+!ifndef EXPATGENDIR
+EXPATGENDIR=$(GLGENDIR)
+!endif
+
+!ifndef EXPATOBJDIR
+EXPATOBJDIR=$(GLGENDIR)
+!endif
+
+!ifndef JPEGXR_GENDIR
+JPEGXR_GENDIR=$(GLGENDIR)
+!endif
+
+!ifndef JPEGXR_OBJDIR
+JPEGXR_OBJDIR=$(GLGENDIR)
+!endif
+
+
+!ifndef PCL5SRCDIR
+PCL5SRCDIR=.\pcl\pcl
+!endif
+
+!ifndef PCL5GENDIR
+PCL5GENDIR=.\$(DEFAULT_OBJ_DIR)
+!endif
+
+!ifndef PCL5OBJDIR
+PCL5OBJDIR=.\$(DEFAULT_OBJ_DIR)
+!endif
+
+!ifndef PXLSRCDIR
+PXLSRCDIR=.\pcl\pxl
+!endif
+
+!ifndef PXLGENDIR
+PXLGENDIR=.\$(DEFAULT_OBJ_DIR)
+!endif
+
+!ifndef PXLOBJDIR
+PXLOBJDIR=.\$(DEFAULT_OBJ_DIR)
+!endif
+
+!ifndef PLSRCDIR
+PLSRCDIR=.\pcl\pl
+!endif
+
+!ifndef PLGENDIR
+PLGENDIR=.\$(DEFAULT_OBJ_DIR)
+!endif
+
+!ifndef PLOBJDIR
+PLOBJDIR=.\$(DEFAULT_OBJ_DIR)
+!endif
+
+!ifndef XPSSRCDIR
+XPSSRCDIR=.\xps
+!endif
+
+!ifndef XPSGENDIR
+XPSGENDIR=.\$(DEFAULT_OBJ_DIR)
+!endif
+
+!ifndef XPSOBJDIR
+XPSOBJDIR=.\$(DEFAULT_OBJ_DIR)
+!endif
+
+GPDLSRCDIR=.\gpdl
+GPDLGENDIR=.\$(DEFAULT_OBJ_DIR)
+GPDLOBJDIR=.\$(DEFAULT_OBJ_DIR)
+
+CONTRIBDIR=.\contrib
+
+# Can we build PCL and XPS
+!ifndef BUILD_PCL
+BUILD_PCL=0
+!if exist ("$(PLSRCDIR)\pl.mak")
+BUILD_PCL=1
+!endif
+!endif
+
+!ifndef BUILD_XPS
+BUILD_XPS=0
+!if exist ("$(XPSSRCDIR)\xps.mak")
+BUILD_XPS=1
+!endif
+!endif
+
+!ifndef BUILD_GPDL
+BUILD_GPDL=0
+!if exist ("$(GPDLSRCDIR)\gpdl.mak")
+BUILD_GPDL=1
+!endif
+!endif
+
+PCL_TARGET=
+XPS_TARGET=
+
+!if $(BUILD_PCL)
+PCL_TARGET=pcl
+!endif
+
+!if $(BUILD_XPS)
+XPS_TARGET=xps
+!endif
+
+!if $(BUILD_GPDL)
+GPDL_TARGET=gpdl
+!endif
+
+PCL_XPS_TARGETS=$(PCL_TARGET) $(XPS_TARGET) $(GPDL_TARGET)
+
+# Define the root directory for Ghostscript installation.
+
+!ifndef AROOTDIR
+AROOTDIR=c:/gs
+!endif
+!ifndef GSROOTDIR
+GSROOTDIR=$(AROOTDIR)/gs$(GS_DOT_VERSION)
+!endif
+
+# Define the directory that will hold documentation at runtime.
+
+!ifndef GS_DOCDIR
+GS_DOCDIR=$(GSROOTDIR)/doc
+!endif
+
+# Define the default directory/ies for the runtime initialization, resource and
+# font files. Separate multiple directories with ';'.
+# Use / to indicate directories, not \.
+# MSVC will not allow \'s here because it sees '\;' CPP-style as an
+# illegal escape.
+
+!ifndef GS_LIB_DEFAULT
+GS_LIB_DEFAULT=$(GSROOTDIR)/Resource/Init;$(GSROOTDIR)/lib;$(GSROOTDIR)/Resource/Font;$(AROOTDIR)/fonts
+!endif
+
+# Define whether or not searching for initialization files should always
+# look in the current directory first. This leads to well-known security
+# and confusion problems, but may be convenient sometimes.
+
+!ifndef SEARCH_HERE_FIRST
+SEARCH_HERE_FIRST=0
+!endif
+
+# Define the name of the interpreter initialization file.
+# (There is no reason to change this.)
+
+!ifndef GS_INIT
+GS_INIT=gs_init.ps
+!endif
+
+# Choose generic configuration options.
+
+# Setting DEBUG=1 includes debugging features in the build:
+# 1. It defines the C preprocessor symbol DEBUG. The latter includes
+# tracing and self-validation code fragments into compilation.
+# Particularly it enables the -Z and -T switches in Ghostscript.
+# 2. It compiles code fragments for C stack overflow checks.
+# Code produced with this option is somewhat larger and runs
+# somewhat slower.
+
+!ifndef DEBUG
+DEBUG=0
+!endif
+
+# Setting TDEBUG=1 disables code optimization in the C compiler and
+# includes symbol table information for the debugger.
+# Code is substantially larger and slower.
+
+# NOTE: The MSVC++ 5.0 compiler produces incorrect output code with TDEBUG=0.
+# Also MSVC 6 must be service pack >= 3 to prevent INTERNAL COMPILER ERROR
+
+# Default to 0 anyway since the execution times are so much better.
+!ifndef TDEBUG
+TDEBUG=0
+!endif
+
+# Setting DEBUGSYM=1 is only useful with TDEBUG=0.
+# This option is for advanced developers. It includes symbol table
+# information for the debugger in an optimized (release) build.
+# NOTE: The debugging information generated for the optimized code may be
+# significantly misleading. For general MSVC users we recommend TDEBUG=1.
+
+!ifndef DEBUGSYM
+DEBUGSYM=0
+!endif
+
+
+# We can compile for a 32-bit or 64-bit target
+# WIN32 and WIN64 are mutually exclusive. WIN32 is the default.
+!if !defined(WIN32) && (!defined(Win64) || !defined(WIN64))
+WIN32=0
+!endif
+
+# We can build either 32-bit or 64-bit target on a 64-bit platform
+# but the location of the binaries differs. Would be nice if the
+# detection of the platform could be automatic.
+!ifndef BUILD_SYSTEM
+!if "$(PROCESSOR_ARCHITEW6432)"=="AMD64" || "$(PROCESSOR_ARCHITECTURE)"=="AMD64"
+BUILD_SYSTEM=64
+PGMFILES=$(SYSTEMDRIVE)\Program Files
+PGMFILESx86=$(SYSTEMDRIVE)\Program Files (x86)
+!else
+BUILD_SYSTEM=32
+PGMFILES=$(SYSTEMDRIVE)\Program Files
+PGMFILESx86=$(SYSTEMDRIVE)\Program Files
+!endif
+!endif
+
+!ifndef MSWINSDKPATH
+!if exist ("$(PGMFILESx86)\Microsoft SDKs\Windows")
+!if exist ("$(PGMFILESx86)\Microsoft SDKs\Windows\v7.1A")
+MSWINSDKPATH=$(PGMFILESx86)\Microsoft SDKs\Windows\v7.1A
+!else
+!if exist ("$(PGMFILESx86)\Microsoft SDKs\Windows\v7.1")
+MSWINSDKPATH=$(PGMFILESx86)\Microsoft SDKs\Windows\v7.1
+!else
+!if exist ("$(PGMFILESx86)\Microsoft SDKs\Windows\v7.0A")
+MSWINSDKPATH=$(PGMFILESx86)\Microsoft SDKs\Windows\v7.0A
+!else
+!if exist ("$(PGMFILESx86)\Microsoft SDKs\Windows\v7.0")
+MSWINSDKPATH=$(PGMFILESx86)\Microsoft SDKs\Windows\v7.0
+!endif
+!endif
+!endif
+!endif
+!else
+!if exist ("$(PGMFILES)\Microsoft SDKs\Windows")
+!if exist ("$(PGMFILES)\Microsoft SDKs\Windows\v7.1A")
+MSWINSDKPATH=$(PGMFILES)\Microsoft SDKs\Windows\v7.1A
+!else
+!if exist ("$(PGMFILES)\Microsoft SDKs\Windows\v7.1")
+MSWINSDKPATH=$(PGMFILES)\Microsoft SDKs\Windows\v7.1
+!else
+!if exist ("$(PGMFILES)\Microsoft SDKs\Windows\v7.0A")
+MSWINSDKPATH=$(PGMFILES)\Microsoft SDKs\Windows\v7.0A
+!else
+!if exist ("$(PGMFILES)\Microsoft SDKs\Windows\v7.0")
+MSWINSDKPATH=$(PGMFILES)\Microsoft SDKs\Windows\v7.0
+!endif
+!endif
+!endif
+!endif
+!endif
+!endif
+!endif
+
+XPSPRINTCFLAGS=
+XPSPRINT=0
+
+!ifdef MSWINSDKPATH
+!if exist ("$(MSWINSDKPATH)\Include\XpsPrint.h")
+XPSPRINTCFLAGS=/DXPSPRINT=1 /I"$(MSWINSDKPATH)\Include"
+XPSPRINT=1
+!endif
+!endif
+
+# Define the name of the executable file.
+
+!ifndef GS
+!ifdef WIN64
+GS=gswin64
+PCL=gpclwin64
+XPS=gxpswin64
+GPDL=gpdlwin64
+!else
+!ifdef ARM
+GS=gswinARM
+PCL=gpclwinARM
+XPS=gxpswinARM
+GPDL=gpdlwinARM
+!else
+GS=gswin32
+PCL=gpclwin32
+XPS=gxpswin32
+GPDL=gpdlwin32
+!endif
+!endif
+!endif
+!ifndef GSCONSOLE
+GSCONSOLE=$(GS)c
+!endif
+!ifndef GSDLL
+!ifdef METRO
+!ifdef WIN64
+GSDLL=gsdll64metro
+!else
+!ifdef ARM
+GSDLL=gsdllARM32metro
+!else
+GSDLL=gsdll32metro
+!endif
+!endif
+!else
+!ifdef WIN64
+GSDLL=gsdll64
+!else
+GSDLL=gsdll32
+!endif
+!endif
+!endif
+
+# To build two small executables and a large DLL use MAKEDLL=1
+# To build two large executables use MAKEDLL=0
+
+!ifndef MAKEDLL
+MAKEDLL=1
+!endif
+
+# Should we build in the cups device....
+!ifdef WITH_CUPS
+!if "$(WITH_CUPS)"!="0"
+WITH_CUPS=1
+!else
+WITH_CUPS=0
+!endif
+!else
+WITH_CUPS=0
+!endif
+
+# We can't build cups libraries in a Metro friendly way,
+# so if building for Metro, disable cups regardless of the
+# request
+!ifdef METRO
+WITH_CUPS=0
+!endif
+
+# Define the directory where the FreeType2 library sources are stored.
+# See freetype.mak for more information.
+
+!ifdef UFST_BRIDGE
+!if "$(UFST_BRIDGE)"=="1"
+FT_BRIDGE=0
+!endif
+!endif
+
+!ifndef FT_BRIDGE
+FT_BRIDGE=1
+!endif
+
+!ifndef FTSRCDIR
+FTSRCDIR=freetype
+!endif
+!ifndef FT_CFLAGS
+FT_CFLAGS=-I$(FTSRCDIR)\include
+!endif
+
+!ifdef BITSTREAM_BRIDGE
+FT_BRIDGE=0
+!endif
+
+# Define the directory where the IJG JPEG library sources are stored,
+# and the major version of the library that is stored there.
+# You may need to change this if the IJG library version changes.
+# See jpeg.mak for more information.
+
+!ifndef JSRCDIR
+JSRCDIR=jpeg
+!endif
+
+# Define the directory where the PNG library sources are stored,
+# and the version of the library that is stored there.
+# You may need to change this if the libpng version changes.
+# See png.mak for more information.
+
+!ifndef PNGSRCDIR
+PNGSRCDIR=libpng
+!endif
+
+!ifndef TIFFSRCDIR
+TIFFSRCDIR=tiff$(D)
+TIFFCONFDIR=$(TIFFSRCDIR)
+TIFFCONFIG_SUFFIX=.vc
+TIFFPLATFORM=win32
+!endif
+
+# Define the directory where the zlib sources are stored.
+# See zlib.mak for more information.
+
+!ifndef ZSRCDIR
+ZSRCDIR=.\zlib
+!endif
+
+# Define which jbig2 library to use
+!if !defined(JBIG2_LIB) && (!defined(NO_LURATECH) || "$(NO_LURATECH)" != "1")
+!if exist("luratech\ldf_jb2")
+JBIG2_LIB=luratech
+!endif
+!endif
+
+!ifndef JBIG2_LIB
+JBIG2_LIB=jbig2dec
+!endif
+
+!if "$(JBIG2_LIB)" == "luratech" || "$(JBIG2_LIB)" == "ldf_jb2"
+# Set defaults for using the Luratech JB2 implementation
+!ifndef JBIG2SRCDIR
+# CSDK source code location
+JBIG2SRCDIR=luratech\ldf_jb2
+!endif
+!ifndef JBIG2_CFLAGS
+# required compiler flags
+!ifdef WIN64
+JBIG2_CFLAGS=-DUSE_LDF_JB2 -DWIN64
+!else
+JBIG2_CFLAGS=-DUSE_LDF_JB2 -DWIN32
+!endif
+!endif
+!else
+# Use jbig2dec by default. See jbig2.mak for more information.
+!ifndef JBIG2SRCDIR
+# location of included jbig2dec library source
+JBIG2SRCDIR=jbig2dec
+!endif
+!endif
+
+# Alternatively, you can build a separate DLL
+# and define SHARE_JBIG2=1 in src/winlib.mak
+
+# Define which jpeg2k library to use
+!if !defined(JPX_LIB) && (!defined(NO_LURATECH) || "$(NO_LURATECH)" != "1")
+!if exist("luratech\lwf_jp2")
+JPX_LIB=luratech
+!endif
+!endif
+
+!ifndef JPX_LIB
+JPX_LIB=openjpeg
+!endif
+
+# Alternatively, you can build a separate DLL
+# and define SHARE_JPX=1 in src/winlib.mak
+
+# Define the directory where the lcms source is stored.
+# See lcms.mak for more information
+!ifndef LCMSSRCDIR
+LCMSSRCDIR=lcms
+!endif
+
+# Define the directory where the lcms2 source is stored.
+# See lcms2.mak for more information
+!ifndef LCMS2SRCDIR
+LCMS2SRCDIR=lcms2
+!endif
+
+# Define the directory where the ijs source is stored,
+# and the process forking method to use for the server.
+# See ijs.mak for more information.
+
+!ifndef IJSSRCDIR
+SHARE_IJS=0
+IJS_NAME=
+IJSSRCDIR=ijs
+IJSEXECTYPE=win
+!endif
+
+# Define the directory where the CUPS library sources are stored,
+
+!ifndef LCUPSSRCDIR
+SHARE_LCUPS=0
+LCUPS_NAME=
+LCUPSSRCDIR=cups
+LCUPSBUILDTYPE=win
+CUPS_CC=$(CC) $(CFLAGS) -DWIN32
+!endif
+
+!ifndef LCUPSISRCDIR
+SHARE_LCUPSI=0
+LCUPSI_NAME=
+LCUPSISRCDIR=cups
+CUPS_CC=$(CC) $(CFLAGS) -DWIN32 -DHAVE_BOOLEAN
+!endif
+
+!ifndef JPEGXR_SRCDIR
+JPEGXR_SRCDIR=.\jpegxr
+!endif
+
+!ifndef SHARE_JPEGXR
+SHARE_JPEGXR=0
+!endif
+
+!ifndef JPEGXR_CFLAGS
+JPEGXR_CFLAGS=/TP /DNDEBUG
+!endif
+
+!ifndef EXPATSRCDIR
+EXPATSRCDIR=.\expat
+!endif
+
+!ifndef SHARE_EXPAT
+SHARE_EXPAT=0
+!endif
+
+!ifndef EXPAT_CFLAGS
+EXPAT_CFLAGS=/DHAVE_MEMMOVE
+!endif
+
+# Define any other compilation flags.
+
+# support XCFLAGS for parity with the unix makefiles
+!ifndef XCFLAGS
+XCFLAGS=
+!endif
+
+# We now build with unicode support by default. To avoid this, build
+# with GS_NO_UTF8=1.
+!if "$(GS_NO_UTF8)" != ""
+UNICODECFLAGS=/DGS_NO_UTF8
+!else
+UNICODECFLAGS=
+!endif
+
+!ifndef CFLAGS
+CFLAGS=
+!endif
+
+!if "$(MEMENTO)"=="1"
+CFLAGS=$(CFLAGS) -DMEMENTO
+!endif
+
+!ifdef METRO
+# Ideally the TIF_PLATFORM_CONSOLE define should only be for libtiff,
+# but we aren't set up to do that easily
+CFLAGS=$(CFLAGS) -DMETRO -DWINAPI_FAMILY=WINAPI_PARTITION_APP -DTIF_PLATFORM_CONSOLE
+# WinRT doesn't allow ExitProcess() so we have to suborn it here.
+# it shouldn't matter since we actually rely on setjmp()/longjmp() for error handling in libtiff
+PNG_CFLAGS=/DExitProcess=exit
+!endif
+
+CFLAGS=$(CFLAGS) $(XCFLAGS) $(UNICODECFLAGS)
+
+# 1 --> Use 64 bits for gx_color_index. This is required only for
+# non standard devices or DeviceN process color model devices.
+USE_LARGE_COLOR_INDEX=0
+
+!if $(USE_LARGE_COLOR_INDEX) == 1
+# Definitions to force gx_color_index to 64 bits
+LARGEST_UINTEGER_TYPE=unsigned __int64
+GX_COLOR_INDEX_TYPE=$(LARGEST_UINTEGER_TYPE)
+
+CFLAGS=$(CFLAGS) /DGX_COLOR_INDEX_TYPE="$(GX_COLOR_INDEX_TYPE)"
+!endif
+
+# -W3 generates too much noise.
+!ifndef WARNOPT
+WARNOPT=-W2
+!endif
+
+#
+# Do not edit the next group of lines.
+
+#!include $(COMMONDIR)\msvcdefs.mak
+#!include $(COMMONDIR)\pcdefs.mak
+#!include $(COMMONDIR)\generic.mak
+!include $(GLSRCDIR)\version.mak
+# The following is a hack to get around the special treatment of \ at
+# the end of a line.
+NUL=
+DD=$(GLGENDIR)\$(NUL)
+GLD=$(GLGENDIR)\$(NUL)
+PSD=$(PSGENDIR)\$(NUL)
+
+!ifdef SBR
+SBRFLAGS=/FR$(SBRDIR)\$(NUL)
+!endif
+
+# ------ Platform-specific options ------ #
+
+# Define which major version of MSVC is being used
+# (currently, 4, 5, 6, 7, and 8 are supported).
+# Define the minor version of MSVC, currently only
+# used for Microsoft Visual Studio .NET 2003 (7.1)
+
+#MSVC_VERSION=6
+#MSVC_MINOR_VERSION=0
+
+# Make a guess at the version of MSVC in use
+# This will not work if service packs change the version numbers.
+
+!if defined(_NMAKE_VER) && !defined(MSVC_VERSION)
+!if "$(_NMAKE_VER)" == "162"
+MSVC_VERSION=5
+!endif
+!if "$(_NMAKE_VER)" == "6.00.8168.0"
+MSVC_VERSION=6
+!endif
+!if "$(_NMAKE_VER)" == "7.00.9466"
+MSVC_VERSION=7
+!endif
+!if "$(_NMAKE_VER)" == "7.00.9955"
+MSVC_VERSION=7
+!endif
+!if "$(_NMAKE_VER)" == "7.10.3077"
+MSVC_VERSION=7
+MSVC_MINOR_VERSION=1
+!endif
+!if "$(_NMAKE_VER)" == "8.00.40607.16"
+MSVC_VERSION=8
+!endif
+!if "$(_NMAKE_VER)" == "8.00.50727.42"
+MSVC_VERSION=8
+!endif
+!if "$(_NMAKE_VER)" == "8.00.50727.762"
+MSVC_VERSION=8
+!endif
+!if "$(_NMAKE_VER)" == "9.00.21022.08"
+MSVC_VERSION=9
+!endif
+!if "$(_NMAKE_VER)" == "9.00.30729.01"
+MSVC_VERSION=9
+!endif
+!if "$(_NMAKE_VER)" == "10.00.30319.01"
+MSVC_VERSION=10
+!endif
+!if "$(_NMAKE_VER)" == "11.00.50522.1"
+MSVC_VERSION=11
+!endif
+!if "$(_NMAKE_VER)" == "11.00.50727.1"
+MSVC_VERSION=11
+!endif
+!if "$(_NMAKE_VER)" == "11.00.60315.1"
+MSVC_VERSION=11
+!endif
+!if "$(_NMAKE_VER)" == "11.00.60610.1"
+MSVC_VERSION=11
+!endif
+!if "$(_NMAKE_VER)" == "12.00.21005.1"
+MSVC_VERSION=12
+!endif
+!endif
+
+!ifndef MSVC_VERSION
+MSVC_VERSION=6
+!endif
+!ifndef MSVC_MINOR_VERSION
+MSVC_MINOR_VERSION=0
+!endif
+
+# Define the drive, directory, and compiler name for the Microsoft C files.
+# COMPDIR contains the compiler and linker (normally \msdev\bin).
+# MSINCDIR contains the include files (normally \msdev\include).
+# LIBDIR contains the library files (normally \msdev\lib).
+# COMP is the full C compiler path name (normally \msdev\bin\cl).
+# COMPCPP is the full C++ compiler path name (normally \msdev\bin\cl).
+# COMPAUX is the compiler name for DOS utilities (normally \msdev\bin\cl).
+# RCOMP is the resource compiler name (normallly \msdev\bin\rc).
+# LINK is the full linker path name (normally \msdev\bin\link).
+# Note that when MSINCDIR and LIBDIR are used, they always get a '\' appended,
+# so if you want to use the current directory, use an explicit '.'.
+
+!if $(MSVC_VERSION) == 4
+! ifndef DEVSTUDIO
+DEVSTUDIO=c:\msdev
+! endif
+COMPBASE=$(DEVSTUDIO)
+SHAREDBASE=$(DEVSTUDIO)
+!endif
+
+!if $(MSVC_VERSION) == 5
+! ifndef DEVSTUDIO
+DEVSTUDIO=C:\Program Files\Devstudio
+! endif
+!if "$(DEVSTUDIO)"==""
+COMPBASE=
+SHAREDBASE=
+!else
+COMPBASE=$(DEVSTUDIO)\VC
+SHAREDBASE=$(DEVSTUDIO)\SharedIDE
+!endif
+!endif
+
+!if $(MSVC_VERSION) == 6
+! ifndef DEVSTUDIO
+DEVSTUDIO=C:\Program Files\Microsoft Visual Studio
+! endif
+!if "$(DEVSTUDIO)"==""
+COMPBASE=
+SHAREDBASE=
+!else
+COMPBASE=$(DEVSTUDIO)\VC98
+SHAREDBASE=$(DEVSTUDIO)\Common\MSDev98
+!endif
+!endif
+
+!if $(MSVC_VERSION) == 7
+! ifndef DEVSTUDIO
+!if $(MSVC_MINOR_VERSION) == 0
+DEVSTUDIO=C:\Program Files\Microsoft Visual Studio .NET
+!else
+DEVSTUDIO=C:\Program Files\Microsoft Visual Studio .NET 2003
+!endif
+! endif
+!if "$(DEVSTUDIO)"==""
+COMPBASE=
+SHAREDBASE=
+!else
+COMPBASE=$(DEVSTUDIO)\Vc7
+SHAREDBASE=$(DEVSTUDIO)\Vc7
+!endif
+!endif
+
+!if $(MSVC_VERSION) == 8
+! ifndef DEVSTUDIO
+!if $(BUILD_SYSTEM) == 64
+DEVSTUDIO=C:\Program Files (x86)\Microsoft Visual Studio 8
+!else
+DEVSTUDIO=C:\Program Files\Microsoft Visual Studio 8
+!endif
+! endif
+!if "$(DEVSTUDIO)"==""
+COMPBASE=
+SHAREDBASE=
+!else
+COMPBASE=$(DEVSTUDIO)\VC
+SHAREDBASE=$(DEVSTUDIO)\VC
+!ifdef WIN64
+COMPDIR64=$(COMPBASE)\bin\amd64
+LINKLIBPATH=/LIBPATH:"$(COMPBASE)\lib\amd64" /LIBPATH:"$(COMPBASE)\PlatformSDK\Lib\AMD64"
+!endif
+!endif
+!endif
+
+!if $(MSVC_VERSION) == 9
+! ifndef DEVSTUDIO
+!if $(BUILD_SYSTEM) == 64
+DEVSTUDIO=C:\Program Files (x86)\Microsoft Visual Studio 9.0
+!else
+DEVSTUDIO=C:\Program Files\Microsoft Visual Studio 9.0
+!endif
+! endif
+!if "$(DEVSTUDIO)"==""
+COMPBASE=
+SHAREDBASE=
+!else
+# There are at least 4 different values:
+# "v6.0"=Vista, "v6.0A"=Visual Studio 2008,
+# "v6.1"=Windows Server 2008, "v7.0"=Windows 7
+! ifdef MSSDK
+RCDIR=$(MSSDK)\bin
+! else
+RCDIR=C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin
+! endif
+COMPBASE=$(DEVSTUDIO)\VC
+SHAREDBASE=$(DEVSTUDIO)\VC
+!ifdef WIN64
+COMPDIR64=$(COMPBASE)\bin\amd64
+LINKLIBPATH=/LIBPATH:"$(COMPBASE)\lib\amd64" /LIBPATH:"$(COMPBASE)\PlatformSDK\Lib\AMD64"
+!endif
+!endif
+!endif
+
+!if $(MSVC_VERSION) == 10
+! ifndef DEVSTUDIO
+!if $(BUILD_SYSTEM) == 64
+DEVSTUDIO=C:\Program Files (x86)\Microsoft Visual Studio 10.0
+!else
+DEVSTUDIO=C:\Program Files\Microsoft Visual Studio 10.0
+!endif
+! endif
+!if "$(DEVSTUDIO)"==""
+COMPBASE=
+SHAREDBASE=
+!else
+# There are at least 4 different values:
+# "v6.0"=Vista, "v6.0A"=Visual Studio 2008,
+# "v6.1"=Windows Server 2008, "v7.0"=Windows 7
+! ifdef MSSDK
+! ifdef WIN64
+RCDIR=$(MSSDK)\bin\x64
+! else
+RCDIR=$(MSSDK)\bin
+! endif
+! else
+!ifdef WIN64
+RCDIR=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0\bin
+!else
+RCDIR=C:\Program Files\Microsoft SDKs\Windows\v7.0\bin
+!endif
+! endif
+COMPBASE=$(DEVSTUDIO)\VC
+SHAREDBASE=$(DEVSTUDIO)\VC
+!ifdef WIN64
+!if $(BUILD_SYSTEM) == 64
+COMPDIR64=$(COMPBASE)\bin\amd64
+LINKLIBPATH=/LIBPATH:"$(MSSDK)\lib\x64" /LIBPATH:"$(COMPBASE)\lib\amd64"
+!else
+COMPDIR64=$(COMPBASE)\bin\x86_amd64
+LINKLIBPATH=/LIBPATH:"$(COMPBASE)\lib\amd64" /LIBPATH:"$(COMPBASE)\PlatformSDK\Lib\x64"
+!endif
+!endif
+!endif
+!endif
+
+!if $(MSVC_VERSION) == 11
+! ifndef DEVSTUDIO
+!if $(BUILD_SYSTEM) == 64
+DEVSTUDIO=C:\Program Files (x86)\Microsoft Visual Studio 11.0
+!else
+DEVSTUDIO=C:\Program Files\Microsoft Visual Studio 11.0
+!endif
+! endif
+!if "$(DEVSTUDIO)"==""
+COMPBASE=
+SHAREDBASE=
+!else
+# There are at least 4 different values:
+# "v6.0"=Vista, "v6.0A"=Visual Studio 2008,
+# "v6.1"=Windows Server 2008, "v7.0"=Windows 7
+! ifdef MSSDK
+! ifdef WIN64
+RCDIR=$(MSSDK)\bin\x64
+! else
+RCDIR=$(MSSDK)\bin
+! endif
+! else
+!if $(BUILD_SYSTEM) == 64
+RCDIR=C:\Program Files (x86)\Windows Kits\8.0\bin\x64
+!else
+RCDIR=C:\Program Files\Windows Kits\8.0\bin\x86
+!endif
+! endif
+COMPBASE=$(DEVSTUDIO)\VC
+SHAREDBASE=$(DEVSTUDIO)\VC
+!ifdef WIN64
+!if $(BUILD_SYSTEM) == 64
+COMPDIR64=$(COMPBASE)\bin\x86_amd64
+LINKLIBPATH=/LIBPATH:"$(MSSDK)\lib\x64" /LIBPATH:"$(COMPBASE)\lib\amd64"
+!else
+COMPDIR64=$(COMPBASE)\bin\x86_amd64
+LINKLIBPATH=/LIBPATH:"$(COMPBASE)\lib\amd64" /LIBPATH:"$(COMPBASE)\PlatformSDK\Lib\x64"
+!endif
+!endif
+!endif
+!endif
+
+!if $(MSVC_VERSION) == 12
+! ifndef DEVSTUDIO
+!if $(BUILD_SYSTEM) == 64
+DEVSTUDIO=C:\Program Files (x86)\Microsoft Visual Studio 12.0
+!else
+DEVSTUDIO=C:\Program Files\Microsoft Visual Studio 12.0
+!endif
+! endif
+!if "$(DEVSTUDIO)"==""
+COMPBASE=
+SHAREDBASE=
+!else
+# There are at least 4 different values:
+# "v6.0"=Vista, "v6.0A"=Visual Studio 2008,
+# "v6.1"=Windows Server 2008, "v7.0"=Windows 7
+! ifdef MSSDK
+! ifdef WIN64
+RCDIR=$(MSSDK)\bin\x64
+! else
+RCDIR=$(MSSDK)\bin
+! endif
+! else
+!if $(BUILD_SYSTEM) == 64
+RCDIR=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin
+!else
+RCDIR=C:\Program Files\Microsoft SDKs\Windows\v7.1A\Bin
+!endif
+! endif
+COMPBASE=$(DEVSTUDIO)\VC
+SHAREDBASE=$(DEVSTUDIO)\VC
+!ifdef WIN64
+!if $(BUILD_SYSTEM) == 64
+COMPDIR64=$(COMPBASE)\bin\x86_amd64
+LINKLIBPATH=/LIBPATH:"$(COMPBASE)\lib\amd64"
+!else
+COMPDIR64=$(COMPBASE)\bin\x86_amd64
+LINKLIBPATH=/LIBPATH:"$(COMPBASE)\lib\amd64" /LIBPATH:"$(COMPBASE)\PlatformSDK\Lib\x64"
+!endif
+!endif
+!endif
+!endif
+
+!if "$(ARM)"=="1"
+VCINSTDIR=$(VS110COMNTOOLS)..\..\VC\
+
+!ifndef WINSDKVER
+WINSDKVER=8.0
+!endif
+
+!ifndef WINSDKDIR
+WINSDKDIR=$(VS110COMNTOOLS)..\..\..\Windows Kits\$(WINSDKVER)\
+!endif
+
+COMPAUX__="$(VCINSTDIR)\bin\cl.exe"
+COMPAUXCFLAGS=/I"$(VCINSTDIR)\INCLUDE" /I"$(VCINSTDIR)\ATLMFC\INCLUDE" \
+/I"$(WINSDKDIR)\include\shared" /I"$(WINSDKDIR)\include\um" \
+/I"$(WINDSKDIR)include\winrt"
+
+COMPAUXLDFLAGS=/LIBPATH:"$(VCINSTDIR)\LIB" \
+/LIBPATH:"$(VCINSTDIR)\ATLMFC\LIB" \
+/LIBPATH:"$(WINSDKDIR)\lib\win8\um\x86"
+
+COMPAUX=$(COMPAUX__) $(COMPAUXCFLAGS)
+
+!else
+
+COMPAUXLDFLAGS=""
+
+!endif
+
+# Some environments don't want to specify the path names for the tools at all.
+# Typical definitions for such an environment would be:
+# MSINCDIR= LIBDIR= COMP=cl COMPAUX=cl RCOMP=rc LINK=link
+# COMPDIR, LINKDIR, and RCDIR are irrelevant, since they are only used to
+# define COMP, LINK, and RCOMP respectively, but we allow them to be
+# overridden anyway for completeness.
+!ifndef COMPDIR
+!if "$(COMPBASE)"==""
+COMPDIR=
+!else
+!ifdef WIN64
+COMPDIR=$(COMPDIR64)
+!else
+COMPDIR=$(COMPBASE)\bin
+!endif
+!endif
+!endif
+
+!ifndef LINKDIR
+!if "$(COMPBASE)"==""
+LINKDIR=
+!else
+!ifdef WIN64
+LINKDIR=$(COMPDIR64)
+!else
+LINKDIR=$(COMPBASE)\bin
+!endif
+!endif
+!endif
+
+!ifndef RCDIR
+!if "$(SHAREDBASE)"==""
+RCDIR=
+!else
+RCDIR=$(SHAREDBASE)\bin
+!endif
+!endif
+
+!ifndef MSINCDIR
+!if "$(COMPBASE)"==""
+MSINCDIR=
+!else
+MSINCDIR=$(COMPBASE)\include
+!endif
+!endif
+
+!ifndef LIBDIR
+!if "$(COMPBASE)"==""
+LIBDIR=
+!else
+!ifdef WIN64
+LIBDIR=$(COMPBASE)\lib\amd64
+!else
+LIBDIR=$(COMPBASE)\lib
+!endif
+!endif
+!endif
+
+!ifndef COMP
+!if "$(COMPDIR)"==""
+COMP=cl
+!else
+COMP="$(COMPDIR)\cl"
+!endif
+!endif
+!ifndef COMPCPP
+COMPCPP=$(COMP)
+!endif
+!ifndef COMPAUX
+!ifdef WIN64
+COMPAUX=$(COMP)
+!else
+COMPAUX=$(COMP)
+!endif
+!endif
+
+!ifndef RCOMP
+!if "$(RCDIR)"==""
+RCOMP=rc
+!else
+RCOMP="$(RCDIR)\rc"
+!endif
+!endif
+
+!ifndef LINK
+!if "$(LINKDIR)"==""
+LINK=link
+!else
+LINK="$(LINKDIR)\link"
+!endif
+!endif
+
+# nmake does not have a form of .BEFORE or .FIRST which can be used
+# to specify actions before anything else is done. If LIB and INCLUDE
+# are not defined then we want to define them before we link or
+# compile. Here is a kludge which allows us to to do what we want.
+# nmake does evaluate preprocessor directives when they are encountered.
+# So the desired set statements are put into dummy preprocessor
+# directives.
+!ifndef INCLUDE
+!if "$(MSINCDIR)"!=""
+!if [set INCLUDE=$(MSINCDIR)]==0
+!endif
+!endif
+!endif
+!ifndef LIB
+!if "$(LIBDIR)"!=""
+!if [set LIB=$(LIBDIR)]==0
+!endif
+!endif
+!endif
+
+!ifndef LINKLIBPATH
+LINKLIBPATH=
+!endif
+
+# Define the processor architecture. (i386, ppc, alpha)
+
+!ifndef CPU_FAMILY
+CPU_FAMILY=i386
+#CPU_FAMILY=ppc
+#CPU_FAMILY=alpha # not supported yet - we need someone to tweak
+!endif
+
+# Define the processor (CPU) type. Allowable values depend on the family:
+# i386: 386, 486, 586
+# ppc: 601, 604, 620
+# alpha: not currently used.
+
+!ifndef CPU_TYPE
+CPU_TYPE=486
+#CPU_TYPE=601
+!endif
+
+# Define special features of CPUs
+
+# We'll assume that if you have an x86 machine, you've got a modern
+# enough one to have SSE2 instructions. If you don't, then predefine
+# DONT_HAVE_SSE2 when calling this makefile
+!ifndef ARM
+!if "$(CPU_FAMILY)" == "i386"
+!ifndef DONT_HAVE_SSE2
+!ifndef HAVE_SSE2
+!message **************************************************************
+!message * Assuming that target has SSE2 instructions available. If *
+!message * this is NOT the case, define DONT_HAVE_SSE2 when building. *
+!message **************************************************************
+!endif
+HAVE_SSE2=1
+CFLAGS=$(CFLAGS) /DHAVE_SSE2
+# add "/D__SSE__" here, but causes crashes just now
+JPX_SSE_CFLAGS=
+!endif
+!endif
+!endif
+
+# Define the .dev module that implements thread and synchronization
+# primitives for this platform. Don't change this unless you really know
+# what you're doing.
+
+!ifndef SYNC
+SYNC=winsync
+!endif
+
+# Luratech jp2 flags depend on the compiler version
+#
+!if "$(JPX_LIB)" == "luratech" || "$(JPX_LIB)" == "lwf_jp2"
+# Set defaults for using the Luratech JP2 implementation
+!ifndef JPXSRCDIR
+# CSDK source code location
+JPXSRCDIR=luratech\lwf_jp2
+!endif
+!ifndef JPX_CFLAGS
+# required compiler flags
+!ifdef WIN64
+JPX_CFLAGS=-DUSE_LWF_JP2 -DWIN64 -DNO_ASSEMBLY
+!else
+JPX_CFLAGS=-DUSE_LWF_JP2 -DWIN32 -DNO_ASSEMBLY
+!endif
+!endif
+!endif
+
+# OpenJPEG compiler flags
+#
+!if "$(JPX_LIB)" == "openjpeg"
+!ifndef JPXSRCDIR
+JPXSRCDIR=openjpeg
+!endif
+!ifndef JPX_CFLAGS
+!ifdef WIN64
+JPX_CFLAGS=-DUSE_OPENJPEG_JP2 -DUSE_JPIP $(JPX_SSE_CFLAGS) -DWIN64
+!else
+JPX_CFLAGS=-DUSE_OPENJPEG_JP2 -DUSE_JPIP $(JPX_SSE_CFLAGS) -DWIN32
+!endif
+!else
+JPX_CFLAGS = $JPX_CFLAGS -DUSE_JPIP -DUSE_OPENJPEG_JP2
+!endif
+!endif
+
+# ------ Devices and features ------ #
+
+# Choose the language feature(s) to include. See gs.mak for details.
+
+!ifndef FEATURE_DEVS
+# Choose the language feature(s) to include. See gs.mak for details.
+
+# if it's included, $(PSD)gs_pdfwr.dev should always be one of the last in the list
+PSI_FEATURE_DEVS=$(PSD)psl3.dev $(PSD)pdf.dev $(PSD)dpsnext.dev $(PSD)epsf.dev $(PSD)ttfont.dev \
+ $(PSD)jbig2.dev $(PSD)jpx.dev $(PSD)fapi_ps.dev $(GLD)winutf8.dev $(PSD)gs_pdfwr.dev
+
+
+PCL_FEATURE_DEVS=$(PLOBJDIR)/pl.dev $(PLOBJDIR)/pjl.dev $(PXLOBJDIR)/pxl.dev $(PCL5OBJDIR)/pcl5c.dev \
+ $(PCL5OBJDIR)/hpgl2c.dev
+
+XPS_FEATURE_DEVS=$(XPSOBJDIR)/pl.dev $(XPSOBJDIR)/xps.dev
+
+FEATURE_DEVS=$(GLD)pipe.dev $(GLD)gsnogc.dev $(GLD)htxlib.dev $(GLD)psl3lib.dev $(GLD)psl2lib.dev \
+ $(GLD)dps2lib.dev $(GLD)path1lib.dev $(GLD)patlib.dev $(GLD)psl2cs.dev $(GLD)rld.dev $(GLD)gxfapiu$(UFST_BRIDGE).dev\
+ $(GLD)ttflib.dev $(GLD)cielib.dev $(GLD)pipe.dev $(GLD)htxlib.dev $(GLD)sdctd.dev $(GLD)libpng.dev\
+ $(GLD)seprlib.dev $(GLD)translib.dev $(GLD)cidlib.dev $(GLD)psf0lib.dev $(GLD)psf1lib.dev\
+ $(GLD)psf2lib.dev $(GLD)lzwd.dev $(GLD)sicclib.dev $(GLD)mshandle.dev $(GLD)mspoll.dev \
+ $(GLD)ramfs.dev $(GLD)sjpx.dev $(GLD)sjbig2.dev
+
+
+!ifndef METRO
+FEATURE_DEVS=$(FEATURE_DEVS) $(PSD)msprinter.dev $(GLD)pipe.dev
+!endif
+!endif
+
+# Choose whether to compile the .ps initialization files into the executable.
+# See gs.mak for details.
+
+!ifndef COMPILE_INITS
+COMPILE_INITS=1
+!endif
+
+# Choose whether to store band lists on files or in memory.
+# The choices are 'file' or 'memory'.
+
+!ifndef BAND_LIST_STORAGE
+BAND_LIST_STORAGE=file
+!endif
+
+# Choose which compression method to use when storing band lists in memory.
+# The choices are 'lzw' or 'zlib'.
+
+!ifndef BAND_LIST_COMPRESSOR
+BAND_LIST_COMPRESSOR=zlib
+!endif
+
+# Choose the implementation of file I/O: 'stdio', 'fd', or 'both'.
+# See gs.mak and sfxfd.c for more details.
+
+!ifndef FILE_IMPLEMENTATION
+FILE_IMPLEMENTATION=stdio
+!endif
+
+# Choose the implementation of stdio: '' for file I/O and 'c' for callouts
+# See gs.mak and ziodevs.c/ziodevsc.c for more details.
+
+!ifndef STDIO_IMPLEMENTATION
+STDIO_IMPLEMENTATION=c
+!endif
+
+# Choose the device(s) to include. See devs.mak for details,
+# devs.mak and contrib.mak for the list of available devices.
+
+!ifndef DEVICE_DEVS
+!ifdef METRO
+DEVICE_DEVS=
+!else
+# $(DD)mswindll.dev
+DEVICE_DEVS=$(DD)display.dev $(DD)mswinpr2.dev $(DD)ijs.dev $(DD)mswindll.dev
+!endif
+DEVICE_DEVS2=$(DD)epson.dev $(DD)eps9high.dev $(DD)eps9mid.dev $(DD)epsonc.dev $(DD)ibmpro.dev
+DEVICE_DEVS3=$(DD)deskjet.dev $(DD)djet500.dev $(DD)laserjet.dev $(DD)ljetplus.dev $(DD)ljet2p.dev
+DEVICE_DEVS4=$(DD)cdeskjet.dev $(DD)cdjcolor.dev $(DD)cdjmono.dev $(DD)cdj550.dev
+DEVICE_DEVS5=$(DD)uniprint.dev $(DD)djet500c.dev $(DD)declj250.dev $(DD)lj250.dev
+DEVICE_DEVS6=$(DD)st800.dev $(DD)stcolor.dev $(DD)bj10e.dev $(DD)bj200.dev
+DEVICE_DEVS7=$(DD)t4693d2.dev $(DD)t4693d4.dev $(DD)t4693d8.dev $(DD)tek4696.dev
+DEVICE_DEVS8=$(DD)pcxmono.dev $(DD)pcxgray.dev $(DD)pcx16.dev $(DD)pcx256.dev $(DD)pcx24b.dev $(DD)pcxcmyk.dev
+DEVICE_DEVS9=$(DD)pbm.dev $(DD)pbmraw.dev $(DD)pgm.dev $(DD)pgmraw.dev $(DD)pgnm.dev $(DD)pgnmraw.dev $(DD)pkmraw.dev
+DEVICE_DEVS10=$(DD)tiffcrle.dev $(DD)tiffg3.dev $(DD)tiffg32d.dev $(DD)tiffg4.dev $(DD)tifflzw.dev $(DD)tiffpack.dev
+DEVICE_DEVS11=$(DD)bmpmono.dev $(DD)bmpgray.dev $(DD)bmp16.dev $(DD)bmp256.dev $(DD)bmp16m.dev $(DD)tiff12nc.dev $(DD)tiff24nc.dev $(DD)tiff48nc.dev $(DD)tiffgray.dev $(DD)tiff32nc.dev $(DD)tiff64nc.dev $(DD)tiffsep.dev $(DD)tiffsep1.dev $(DD)tiffscaled.dev $(DD)tiffscaled8.dev $(DD)tiffscaled24.dev $(DD)tiffscaled32.dev $(DD)tiffscaled4.dev
+DEVICE_DEVS12=$(DD)bit.dev $(DD)bitrgb.dev $(DD)bitcmyk.dev
+DEVICE_DEVS13=$(DD)pngmono.dev $(DD)pngmonod.dev $(DD)pnggray.dev $(DD)png16.dev $(DD)png256.dev $(DD)png16m.dev $(DD)pngalpha.dev $(DD)fpng.dev $(DD)psdcmykog.dev
+DEVICE_DEVS14=$(DD)jpeg.dev $(DD)jpeggray.dev $(DD)jpegcmyk.dev
+DEVICE_DEVS15=$(DD)pdfwrite.dev $(DD)ps2write.dev $(DD)eps2write.dev $(DD)txtwrite.dev $(DD)pxlmono.dev $(DD)pxlcolor.dev $(DD)xpswrite.dev $(DD)inkcov.dev $(DD)ink_cov.dev
+DEVICE_DEVS16=$(DD)bbox.dev $(DD)plib.dev $(DD)plibg.dev $(DD)plibm.dev $(DD)plibc.dev $(DD)plibk.dev $(DD)plan.dev $(DD)plang.dev $(DD)planm.dev $(DD)planc.dev $(DD)plank.dev
+!if "$(WITH_CUPS)" == "1"
+DEVICE_DEVS16=$(DEVICE_DEVS16) $(DD)cups.dev
+!endif
+# Overflow for DEVS3,4,5,6,9
+DEVICE_DEVS17=$(DD)ljet3.dev $(DD)ljet3d.dev $(DD)ljet4.dev $(DD)ljet4d.dev
+DEVICE_DEVS18=$(DD)pj.dev $(DD)pjxl.dev $(DD)pjxl300.dev $(DD)jetp3852.dev $(DD)r4081.dev
+DEVICE_DEVS19=$(DD)lbp8.dev $(DD)m8510.dev $(DD)necp6.dev $(DD)bjc600.dev $(DD)bjc800.dev
+DEVICE_DEVS20=$(DD)pnm.dev $(DD)pnmraw.dev $(DD)ppm.dev $(DD)ppmraw.dev $(DD)pamcmyk32.dev $(DD)pamcmyk4.dev $(DD)pnmcmyk.dev $(DD)pam.dev
+DEVICE_DEVS21=$(DD)spotcmyk.dev $(DD)devicen.dev $(DD)bmpsep1.dev $(DD)bmpsep8.dev $(DD)bmp16m.dev $(DD)bmp32b.dev $(DD)psdcmyk.dev $(DD)psdrgb.dev $(DD)cp50.dev
+!endif
+CONTRIB_DEVS=$(DD)pcl3.dev $(DD)hpdjplus.dev $(DD)hpdjportable.dev $(DD)hpdj310.dev $(DD)hpdj320.dev $(DD)hpdj340.dev $(DD)hpdj400.dev $(DD)hpdj500.dev $(DD)hpdj500c.dev $(DD)hpdj510.dev $(DD)hpdj520.dev $(DD)hpdj540.dev $(DD)hpdj550c.dev $(DD)hpdj560c.dev $(DD)hpdj600.dev $(DD)hpdj660c.dev $(DD)hpdj670c.dev $(DD)hpdj680c.dev $(DD)hpdj690c.dev $(DD)hpdj850c.dev $(DD)hpdj855c.dev $(DD)hpdj870c.dev $(DD)hpdj890c.dev $(DD)hpdj1120c.dev $(DD)cdj670.dev $(DD)cdj850.dev $(DD)cdj880.dev $(DD)cdj890.dev $(DD)cdj970.dev $(DD)cdj1600.dev $(DD)cdnj500.dev $(DD)chp2200.dev $(DD)lips3.dev $(DD)lxm3200.dev $(DD)lex2050.dev $(DD)lxm3200.dev $(DD)lex5700.dev $(DD)lex7000.dev $(DD)oki4w.dev $(DD)gdi.dev $(DD)samsunggdi.dev $(DD)dl2100.dev $(DD)la50.dev $(DD)la70.dev $(DD)la75.dev $(DD)la75plus.dev $(DD)ln03.dev $(DD)xes.dev $(DD)md2k.dev $(DD)md5k.dev $(DD)lips4.dev $(DD)bj10v.dev $(DD)bj10vh.dev $(DD)md50Mono.dev $(DD)md50Eco.dev $(DD)md1xMono.dev $(DD)lp2000.dev $(DD)escpage.dev $(DD)npdl.dev $(DD)rpdl.dev $(DD)fmpr.dev $(DD)fmlbp.dev $(DD)jj100.dev $(DD)lbp310.dev $(DD)lbp320.dev $(DD)mj700v2c.dev $(DD)mj500c.dev $(DD)mj6000c.dev $(DD)mj8000c.dev $(DD)pr201.dev $(DD)pr150.dev $(DD)pr1000.dev $(DD)pr1000_4.dev $(DD)lips2p.dev $(DD)bjc880j.dev $(DD)mag16.dev $(DD)mag256.dev $(DD)bjcmono.dev $(DD)bjcgray.dev $(DD)bjccmyk.dev $(DD)bjccolor.dev
+
+!if "$(WITH_CONTRIB)" == "1"
+DEVICE_DEVS16=$(DEVICE_DEVS16) $(CONTRIB_DEVS)
+!endif
+
+# FAPI compilation options :
+UFST_CFLAGS=-DMSVC
+
+BITSTREAM_CFLAGS=
+
+# ---------------------------- End of options ---------------------------- #
+
+# Define the name of the makefile -- used in dependencies.
+
+MAKEFILE=$(PSSRCDIR)\msvc32.mak
+TOP_MAKEFILES=$(MAKEFILE) $(GLSRCDIR)\msvccmd.mak $(GLSRCDIR)\msvctail.mak $(GLSRCDIR)\winlib.mak $(PSSRCDIR)\winint.mak
+
+# Define the files to be removed by `make clean'.
+# nmake expands macros when encountered, not when used,
+# so this must precede the !include statements.
+
+BEGINFILES2=$(GLGENDIR)\lib.rsp\
+ $(GLOBJDIR)\*.exp $(GLOBJDIR)\*.ilk $(GLOBJDIR)\*.pdb $(GLOBJDIR)\*.lib\
+ $(BINDIR)\*.exp $(BINDIR)\*.ilk $(BINDIR)\*.pdb $(BINDIR)\*.lib obj.pdb\
+ obj.idb $(GLOBJDIR)\gs.pch $(SBRDIR)\*.sbr $(GLOBJDIR)\cups\*.h
+
+!ifdef BSCFILE
+BEGINFILES2=$(BEGINFILES2) $(BSCFILE)
+!endif
+
+!include $(GLSRCDIR)\msvccmd.mak
+# *romfs.mak must precede lib.mak
+!include $(PSSRCDIR)\psromfs.mak
+
+!if $(BUILD_PCL)
+!include $(PLSRCDIR)\plromfs.mak
+!endif
+!if $(BUILD_XPS)
+!include $(XPSSRCDIR)\xpsromfs.mak
+!endif
+
+!include $(GLSRCDIR)\winlib.mak
+
+!if $(BUILD_PCL)
+!include $(PLSRCDIR)\pl.mak
+!include $(PCL5SRCDIR)\pcl.mak
+!include $(PCL5SRCDIR)\pcl_top.mak
+!include $(PXLSRCDIR)\pxl.mak
+!endif
+
+!if $(BUILD_XPS)
+!include $(XPSSRCDIR)\xps.mak
+!endif
+
+!if $(BUILD_GPDL)
+!include $(GPDLSRCDIR)\gpdl.mak
+!endif
+
+!include $(GLSRCDIR)\msvctail.mak
+!include $(PSSRCDIR)\winint.mak
+# ----------------------------- Main program ------------------------------ #
+
+GSCONSOLE_XE=$(BINDIR)\$(GSCONSOLE).exe
+GSDLL_DLL=$(BINDIR)\$(GSDLL).dll
+GSDLL_OBJS=$(PSOBJ)gsdll.$(OBJ) $(GLOBJ)gp_msdll.$(OBJ)
+INT_ARCHIVE_SOME=$(GLOBJ)gconfig.$(OBJ) $(GLOBJ)gscdefs.$(OBJ)
+INT_ARCHIVE_ALL=$(PSOBJ)imainarg.$(OBJ) $(PSOBJ)imain.$(OBJ) $(GLOBJ)iconfig.$(OBJ) \
+ $(INT_ARCHIVE_SOME)
+
+!if $(TDEBUG) != 0
+$(PSGEN)lib.rsp: $(TOP_MAKEFILES)
+ echo /NODEFAULTLIB:LIBC.lib > $(PSGEN)lib.rsp
+ echo /NODEFAULTLIB:LIBCMT.lib >> $(PSGEN)lib.rsp
+!ifdef METRO
+ echo kernel32.lib runtimeobject.lib rpcrt4.lib >> $(PSGEN)lib.rsp
+!else
+ echo LIBCMTD.lib >> $(PSGEN)lib.rsp
+!endif
+!else
+$(PSGEN)lib.rsp: $(TOP_MAKEFILES)
+ echo /NODEFAULTLIB:LIBC.lib > $(PSGEN)lib.rsp
+ echo /NODEFAULTLIB:LIBCMTD.lib >> $(PSGEN)lib.rsp
+!ifdef METRO
+ echo kernel32.lib runtimeobject.lib rpcrt4.lib >> $(PSGEN)lib.rsp
+!else
+ echo LIBCMT.lib >> $(PSGEN)lib.rsp
+!endif
+!endif
+
+# a bit naff - find some way to combine above and this....
+!if $(TDEBUG) != 0
+$(PCLGEN)pcllib.rsp: $(TOP_MAKEFILES)
+ echo /NODEFAULTLIB:LIBC.lib > $(PCLGEN)pcllib.rsp
+ echo /NODEFAULTLIB:LIBCMT.lib >> $(PCLGEN)pcllib.rsp
+!ifdef METRO
+ echo kernel32.lib runtimeobject.lib rpcrt4.lib >> $(PCLGEN)pcllib.rsp
+!else
+ echo LIBCMTD.lib >> $(PCLGEN)pcllib.rsp
+!endif
+!else
+$(PCLGEN)pcllib.rsp: $(TOP_MAKEFILES)
+ echo /NODEFAULTLIB:LIBC.lib > $(PCLGEN)pcllib.rsp
+ echo /NODEFAULTLIB:LIBCMTD.lib >> $(PCLGEN)pcllib.rsp
+!ifdef METRO
+ echo kernel32.lib runtimeobject.lib rpcrt4.lib >> $(PCLGEN)pcllib.rsp
+!else
+ echo LIBCMT.lib >> $(PCLGEN)pcllib.rsp
+!endif
+!endif
+
+!if $(TDEBUG) != 0
+
+$(XPSGEN)xpslib.rsp: $(TOP_MAKEFILES)
+ echo /NODEFAULTLIB:LIBC.lib > $(XPSGEN)xpslib.rsp
+ echo /NODEFAULTLIB:LIBCMT.lib >> $(XPSGEN)xpslib.rsp
+!ifdef METRO
+ echo kernel32.lib runtimeobject.lib rpcrt4.lib >> $(XPSGEN)xpslib.rsp
+!else
+ echo LIBCMTD.lib >> $(XPSGEN)xpslib.rsp
+!endif
+!else
+$(XPSGEN)xpslib.rsp: $(TOP_MAKEFILES)
+ echo /NODEFAULTLIB:LIBC.lib > $(XPSGEN)xpslib.rsp
+ echo /NODEFAULTLIB:LIBCMTD.lib >> $(XPSGEN)xpslib.rsp
+!ifdef METRO
+ echo kernel32.lib runtimeobject.lib rpcrt4.lib >> $(XPSGEN)xpslib.rsp
+!else
+ echo LIBCMT.lib >> $(XPSGEN)xpslib.rsp
+!endif
+!endif
+
+!if $(TDEBUG) != 0
+
+$(GPDLGEN)gpdllib.rsp: $(TOP_MAKEFILES)
+ echo /NODEFAULTLIB:LIBC.lib > $(XPSGEN)gpdllib.rsp
+ echo /NODEFAULTLIB:LIBCMT.lib >> $(XPSGEN)gpdllib.rsp
+!ifdef METRO
+ echo kernel32.lib runtimeobject.lib rpcrt4.lib >> $(XPSGEN)gpdllib.rsp
+!else
+ echo LIBCMTD.lib >> $(XPSGEN)gpdllib.rsp
+!endif
+!else
+$(GPDLGEN)gpdllib.rsp: $(TOP_MAKEFILES)
+ echo /NODEFAULTLIB:LIBC.lib > $(XPSGEN)gpdllib.rsp
+ echo /NODEFAULTLIB:LIBCMTD.lib >> $(XPSGEN)gpdllib.rsp
+!ifdef METRO
+ echo kernel32.lib runtimeobject.lib rpcrt4.lib >> $(XPSGEN)gpdllib.rsp
+!else
+ echo LIBCMT.lib >> $(XPSGEN)gpdllib.rsp
+!endif
+!endif
+
+
+!if $(MAKEDLL)
+# The graphical small EXE loader
+!ifdef METRO
+# For METRO build only the dll
+$(GS_XE): $(GSDLL_DLL)
+
+!else
+$(GS_XE): $(GSDLL_DLL) $(DWOBJ) $(GSCONSOLE_XE) $(GLOBJ)gp_wutf8.$(OBJ)
+ echo /SUBSYSTEM:WINDOWS > $(PSGEN)gswin.rsp
+!if "$(PROFILE)"=="1"
+ echo /PROFILE >> $(PSGEN)gswin.rsp
+!endif
+!ifdef WIN64
+ echo /DEF:$(PSSRCDIR)\dwmain64.def /OUT:$(GS_XE) >> $(PSGEN)gswin.rsp
+!else
+ echo /DEF:$(PSSRCDIR)\dwmain32.def /OUT:$(GS_XE) >> $(PSGEN)gswin.rsp
+!endif
+ $(LINK) $(LCT) @$(PSGEN)gswin.rsp $(DWOBJ) $(LINKLIBPATH) @$(LIBCTR) $(GS_OBJ).res $(GLOBJ)gp_wutf8.$(OBJ)
+ del $(PSGEN)gswin.rsp
+!endif
+
+# The console mode small EXE loader
+$(GSCONSOLE_XE): $(OBJC) $(GS_OBJ).res $(PSSRCDIR)\dw64c.def $(PSSRCDIR)\dw32c.def $(GLOBJ)gp_wutf8.$(OBJ)
+ echo /SUBSYSTEM:CONSOLE > $(PSGEN)gswin.rsp
+!if "$(PROFILE)"=="1"
+ echo /PROFILE >> $(PSGEN)gswin.rsp
+!endif
+!ifdef WIN64
+ echo /DEF:$(PSSRCDIR)\dw64c.def /OUT:$(GSCONSOLE_XE) >> $(PSGEN)gswin.rsp
+!else
+ echo /DEF:$(PSSRCDIR)\dw32c.def /OUT:$(GSCONSOLE_XE) >> $(PSGEN)gswin.rsp
+!endif
+ $(LINK) $(LCT) @$(PSGEN)gswin.rsp $(OBJC) $(LINKLIBPATH) @$(LIBCTR) $(GS_OBJ).res $(GLOBJ)gp_wutf8.$(OBJ)
+ del $(PSGEN)gswin.rsp
+
+# The big DLL
+$(GSDLL_DLL): $(ECHOGS_XE) $(gs_tr) $(GS_ALL) $(DEVS_ALL) $(GSDLL_OBJS) $(GSDLL_OBJ).res $(PSGEN)lib.rsp $(PSOBJ)gsromfs$(COMPILE_INITS).$(OBJ)
+ echo Linking $(GSDLL) $(GSDLL_DLL) $(METRO)
+ echo /DLL /DEF:$(PSSRCDIR)\$(GSDLL).def /OUT:$(GSDLL_DLL) > $(PSGEN)gswin.rsp
+!if "$(PROFILE)"=="1"
+ echo /PROFILE >> $(PSGEN)gswin.rsp
+!endif
+ $(LINK) $(LCT) @$(PSGEN)gswin.rsp $(GSDLL_OBJS) @$(gsld_tr) $(PSOBJ)gsromfs$(COMPILE_INITS).$(OBJ) @$(PSGEN)lib.rsp $(LINKLIBPATH) @$(LIBCTR) $(GSDLL_OBJ).res
+ del $(PSGEN)gswin.rsp
+
+!else
+# The big graphical EXE
+$(GS_XE): $(GSCONSOLE_XE) $(GS_ALL) $(DEVS_ALL) $(GSDLL_OBJS) $(DWOBJNO) $(GSDLL_OBJ).res $(PSSRCDIR)\dwmain32.def\
+ $(ld_tr) $(gs_tr) $(PSSRCDIR)\dwmain64.def $(PSGEN)lib.rsp $(PSOBJ)gsromfs$(COMPILE_INITS).$(OBJ)
+ copy $(gsld_tr) $(PSGEN)gswin.tr
+ echo $(PSOBJ)gsromfs$(COMPILE_INITS).$(OBJ) >> $(PSGEN)gswin.tr
+ echo $(PSOBJ)dwnodll.obj >> $(PSGEN)gswin.tr
+ echo $(GLOBJ)dwimg.obj >> $(PSGEN)gswin.tr
+ echo $(PSOBJ)dwmain.obj >> $(PSGEN)gswin.tr
+ echo $(GLOBJ)dwtext.obj >> $(PSGEN)gswin.tr
+ echo $(GLOBJ)dwreg.obj >> $(PSGEN)gswin.tr
+!ifdef WIN64
+ echo /DEF:$(PSSRCDIR)\dwmain64.def /OUT:$(GS_XE) > $(PSGEN)gswin.rsp
+!else
+ echo /DEF:$(PSSRCDIR)\dwmain32.def /OUT:$(GS_XE) > $(PSGEN)gswin.rsp
+!endif
+!if "$(PROFILE)"=="1"
+ echo /PROFILE >> $(PSGEN)gswin.rsp
+!endif
+ $(LINK) $(LCT) @$(PSGEN)gswin.rsp $(GLOBJ)gsdll @$(PSGEN)gswin.tr $(LINKLIBPATH) @$(LIBCTR) @$(PSGEN)lib.rsp $(GSDLL_OBJ).res $(DWTRACE)
+ del $(PSGEN)gswin.tr
+ del $(PSGEN)gswin.rsp
+
+# The big console mode EXE
+$(GSCONSOLE_XE): $(ECHOGS_XE) $(gs_tr) $(GS_ALL) $(DEVS_ALL) $(GSDLL_OBJS) $(OBJCNO) $(GS_OBJ).res $(PSSRCDIR)\dw64c.def $(PSSRCDIR)\dw32c.def \
+ $(PSGEN)lib.rsp $(PSOBJ)gsromfs$(COMPILE_INITS).$(OBJ)
+ copy $(gsld_tr) $(PSGEN)gswin.tr
+ echo $(PSOBJ)gsromfs$(COMPILE_INITS).$(OBJ) >> $(PSGEN)gswin.tr
+ echo $(PSOBJ)dwnodllc.obj >> $(PSGEN)gswin.tr
+ echo $(GLOBJ)dwimg.obj >> $(PSGEN)gswin.tr
+ echo $(PSOBJ)dwmainc.obj >> $(PSGEN)gswin.tr
+ echo $(PSOBJ)dwreg.obj >> $(PSGEN)gswin.tr
+ echo /SUBSYSTEM:CONSOLE > $(PSGEN)gswin.rsp
+!ifdef WIN64
+ echo /DEF:$(PSSRCDIR)\dw64c.def /OUT:$(GSCONSOLE_XE) >> $(PSGEN)gswin.rsp
+!else
+ echo /DEF:$(PSSRCDIR)\dw32c.def /OUT:$(GSCONSOLE_XE) >> $(PSGEN)gswin.rsp
+!endif
+ $(LINK) $(LCT) @$(PSGEN)gswin.rsp $(GLOBJ)gsdll @$(PSGEN)gswin.tr $(LINKLIBPATH) @$(LIBCTR) @$(PSGEN)lib.rsp $(GS_OBJ).res $(DWTRACE)
+ del $(PSGEN)gswin.rsp
+ del $(PSGEN)gswin.tr
+!endif
+
+
+$(GPCL_XE): $(ECHOGS_XE) $(LIBCTR) $(LIB_ALL) $(WINMAINOBJS) $(PCL_DEVS_ALL) $(PCLGEN)pcllib.rsp \
+ $(PCLOBJ)gsromfs$(COMPILE_INITS).$(OBJ) \
+ $(ld_tr) $(pcl_tr) $(REALMAIN_OBJ) $(MAIN_OBJ) $(TOP_OBJ) $(XOBJS) $(INT_ARCHIVE_SOME)
+ copy $(ld_tr) $(PCLGEN)gpclwin.tr
+ $(ECHOGS_XE) -a $(PCLGEN)gpclwin.tr -n -R $(pcl_tr)
+ echo $(WINMAINOBJS) $(TOP_OBJ) $(INT_ARCHIVE_SOME) $(XOBJS) >> $(PCLGEN)gpclwin.tr
+ echo $(PCLOBJ)gsromfs$(COMPILE_INITS).$(OBJ) >> $(PCLGEN)gpclwin.tr
+ echo /SUBSYSTEM:CONSOLE > $(PCLGEN)pclwin.rsp
+ echo /OUT:$(GPCL_XE) >> $(PCLGEN)pclwin.rsp
+ $(LINK) $(LCT) @$(PCLGEN)pclwin.rsp @$(PCLGEN)gpclwin.tr $(LINKLIBPATH) @$(LIBCTR) @$(PCLGEN)pcllib.rsp $(DWTRACE)
+ del $(PCLGEN)pclwin.rsp
+ del $(PCLGEN)gpclwin.tr
+
+$(GXPS_XE): $(ECHOGS_XE) $(LIBCTR) $(LIB_ALL) $(WINMAINOBJS) $(XPS_DEVS_ALL) $(XPSGEN)xpslib.rsp \
+ $(XPS_TOP_OBJS) $(XPSOBJ)gsromfs$(COMPILE_INITS).$(OBJ) \
+ $(ld_tr) $(xps_tr) $(REALMAIN_OBJ) $(MAIN_OBJ) $(XOBJS) $(INT_ARCHIVE_SOME)
+ copy $(ld_tr) $(XPSGEN)gxpswin.tr
+ $(ECHOGS_XE) -a $(PCLGEN)gxpswin.tr -n -R $(xps_tr)
+ echo $(WINMAINOBJS) $(XPS_TOP_OBJS) $(INT_ARCHIVE_SOME) $(XOBJS) >> $(XPSGEN)gxpswin.tr
+ echo $(PCLOBJ)gsromfs$(COMPILE_INITS).$(OBJ) >> $(XPSGEN)gxpswin.tr
+ echo /SUBSYSTEM:CONSOLE > $(XPSGEN)xpswin.rsp
+ echo /OUT:$(GXPS_XE) >> $(XPSGEN)xpswin.rsp
+ $(LINK) $(LCT) @$(XPSGEN)xpswin.rsp @$(XPSGEN)gxpswin.tr $(LINKLIBPATH) @$(LIBCTR) @$(XPSGEN)xpslib.rsp $(DWTRACE)
+ del $(XPSGEN)xpswin.rsp
+ del $(XPSGEN)gxpswin.tr
+
+$(GPDL_XE): $(ECHOGS_XE) $(ld_tr) $(gpdl_tr) $(LIBCTR) $(LIB_ALL) $(WINMAINOBJS) $(XPS_DEVS_ALL) $(PCL_DEVS_ALL) $(GS_ALL) \
+ $(GPDLGEN)gpdllib.rsp \
+ $(GPDL_PSI_TOP_OBJS) $(PCL_PXL_TOP_OBJS) $(PSI_TOP_OBJ) $(XPS_TOP_OBJ) \
+ $(GPDLOBJ)gsromfs$(COMPILE_INITS).$(OBJ) \
+ $(REALMAIN_OBJ) $(MAIN_OBJ) $(XOBJS) $(INT_ARCHIVE_SOME)
+ copy $(gpdlld_tr) $(GPDLGEN)gpdlwin.tr
+ echo $(WINMAINOBJS) $(GPDL_PSI_TOP_OBJS) $(PCL_PXL_TOP_OBJS) $(PSI_TOP_OBJ) $(XPS_TOP_OBJ) $(XOBJS) >> $(GPDLGEN)gpdlwin.tr
+ echo $(PCLOBJ)gsromfs$(COMPILE_INITS).$(OBJ) >> $(GPDLGEN)gpdlwin.tr
+ echo /SUBSYSTEM:CONSOLE > $(GPDLGEN)gpdlwin.rsp
+ echo /OUT:$(GPDL_XE) >> $(GPDLGEN)gpdlwin.rsp
+ $(LINK) $(LCT) @$(GPDLGEN)gpdlwin.rsp @$(GPDLGEN)gpdlwin.tr $(LINKLIBPATH) @$(LIBCTR) @$(GPDLGEN)gpdllib.rsp $(DWTRACE)
+ del $(GPDLGEN)gpdlwin.rsp
+ del $(GPDLGEN)gpdlwin.tr
+
+# ---------------------- Debug targets ---------------------- #
+# Simply set some definitions and call ourselves back #
+
+!ifdef WIN64
+WINDEFS=WIN64= BUILD_SYSTEM="$(BUILD_SYSTEM)" PGMFILES="$(PGMFILES)" PGMFILESx86="$(PGMFILESx86)"
+!else
+WINDEFS=BUILD_SYSTEM="$(BUILD_SYSTEM)" PGMFILES="$(PGMFILES)" PGMFILESx86="$(PGMFILESx86)"
+!endif
+
+DEBUGDEFS=DEBUG=1 TDEBUG=1
+
+debug:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(DEBUGDEFS) $(WINDEFS)
+
+gsdebug:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(DEBUGDEFS) $(WINDEFS) gs
+
+pcldebug:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(DEBUGDEFS) $(WINDEFS) pcl
+
+xpsdebug:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(DEBUGDEFS) $(WINDEFS) xps
+
+gpdldebug:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(DEBUGDEFS) $(WINDEFS) gpdl
+
+debugclean:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(DEBUGDEFS) $(WINDEFS) clean
+
+debugbsc:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(DEBUGDEFS) $(WINDEFS) bsc
+
+# --------------------- Memento targets --------------------- #
+# Simply set some definitions and call ourselves back #
+
+MEMENTODEFS=$(DEBUGDEFS) MEMENTO=1
+
+memento-target:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(MEMENTODEFS) $(WINDEFS)
+
+gs-memento-target:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(MEMENTODEFS) $(WINDEFS) gs
+
+pcl-memento-target:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(MEMENTODEFS) $(WINDEFS) pcl
+
+xps-memento-target:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(MEMENTODEFS) $(WINDEFS) xps
+
+gpdl-memento-target:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(MEMENTODEFS) $(WINDEFS) gpdl
+
+mementoclean:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(MEMENTODEFS) $(WINDEFS) clean
+
+mementobsc:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(MEMENTODEFS) $(WINDEFS) bsc
+
+# --------------------- Profile targets --------------------- #
+# Simply set some definitions and call ourselves back #
+
+PROFILEDEFS=PROFILE=1
+
+profile:
+profile-target:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(PROFILEDEFS) $(WINDEFS)
+
+profileclean:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(PROFILEDEFS) $(WINDEFS) clean
+
+profilebsc:
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" FT_BRIDGE=$(FT_BRIDGE) $(PROFILEDEFS) $(WINDEFS) bsc
+
+
+
+# ---------------------- UFST targets ---------------------- #
+# Simply set some definitions and call ourselves back #
+
+!ifndef UFST_ROOT
+UFST_ROOT=C:\ufst
+!endif
+
+UFST_ROMFS_ARGS=-b \
+ -P $(UFST_ROOT)/fontdata/mtfonts/pcl45/mt3/ -d fontdata/mtfonts/pcl45/mt3/ pcl___xj.fco plug__xi.fco wd____xh.fco \
+ -P $(UFST_ROOT)/fontdata/mtfonts/pclps2/mt3/ -d fontdata/mtfonts/pclps2/mt3/ pclp2_xj.fco \
+ -c -P $(PSSRCDIR)/../lib/ -d Resource/Init/ FAPIconfig-FCO
+
+UFSTROMFONTDIR=\\\"%%%%%rom%%%%%fontdata/\\\"
+UFSTDISCFONTDIR="$(UFST_ROOT)/fontdata"
+
+UFSTBASEDEFS=UFST_BRIDGE=1 FT_BRIDGE=1 UFST_ROOT="$(UFST_ROOT)" UFST_ROMFS_ARGS="$(UFST_ROMFS_ARGS)" UFSTFONTDIR="$(UFSTFONTDIR)" UFSTROMFONTDIR="$(UFSTROMFONTDIR)"
+
+!ifdef WIN64
+UFSTDEBUGDEFS=BINDIR=.\ufstdebugbin GLGENDIR=.\ufstdebugobj64 GLOBJDIR=.\ufstdebugobj64 PSLIBDIR=.\lib PSGENDIR=.\ufstdebugobj64 PSOBJDIR=.\ufstdebugobj64 DEBUG=1 TDEBUG=1 SBRDIR=.\ufstdebugobj64
+UFSTDEFS=BINDIR=.\ufstbin GLGENDIR=.\ufstobj64 GLOBJDIR=.\ufstobj64 PSLIBDIR=.\lib PSGENDIR=.\ufstobj64 PSOBJDIR=.\ufstobj64 SBRDIR=.\ufstobj64
+!else
+UFSTDEBUGDEFS=BINDIR=.\ufstdebugbin GLGENDIR=.\ufstdebugobj GLOBJDIR=.\ufstdebugobj PSLIBDIR=.\lib PSGENDIR=.\ufstdebugobj PSOBJDIR=.\ufstdebugobj DEBUG=1 TDEBUG=1 SBRDIR=.\ufstdebugobj
+UFSTDEFS=BINDIR=.\ufstbin GLGENDIR=.\ufstobj GLOBJDIR=.\ufstobj PSLIBDIR=.\lib PSGENDIR=.\ufstobj PSOBJDIR=.\ufstobj SBRDIR=.\ufstobj
+!endif
+
+ufst-lib:
+# Could make this call a makefile in the ufst code?
+# cd $(UFST_ROOT)\rts\lib
+# nmake -f makefile.artifex fco_lib.a if_lib.a psi_lib.a tt_lib.a
+
+ufst-debug: ufst-lib
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" $(UFSTBASEDEFS) $(UFSTDEBUGDEFS) UFST_CFLAGS="$(UFST_CFLAGS)" $(WINDEFS)
+
+ufst-debugclean: ufst-lib
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" $(UFSTBASEDEFS) $(UFSTDEBUGDEFS) UFST_CFLAGS="$(UFST_CFLAGS)" $(WINDEFS) clean
+
+ufst-debugbsc: ufst-lib
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" $(UFSTBASEDEFS) $(UFSTDEBUGDEFS) UFST_CFLAGS="$(UFST_CFLAGS)" $(WINDEFS) bsc
+
+ufst: ufst-lib
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" $(UFSTBASEDEFS) $(UFSTDEFS) UFST_CFLAGS="$(UFST_CFLAGS)" $(WINDEFS)
+
+ufst-clean: ufst-lib
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" $(UFSTBASEDEFS) $(UFSTDEFS) UFST_CFLAGS="$(UFST_CFLAGS)" $(WINDEFS) clean
+
+ufst-bsc: ufst-lib
+ nmake -f $(MAKEFILE) DEVSTUDIO="$(DEVSTUDIO)" $(UFSTBASEDEFS) $(UFSTDEFS) UFST_CFLAGS="$(UFST_CFLAGS)" $(WINDEFS) bsc
+
+#----------------------- Individual Product Targets --------------------#
+gs:$(GS_XE) $(GSCONSOLE_XE)
+ $(NO_OP)
+
+gpcl6:$(GPCL_XE)
+ $(NO_OP)
+
+gxps:$(GXPS_XE)
+ $(NO_OP)
+
+gpdl:$(GPDL_XE)
+ $(NO_OP)
+
+
+# ---------------------- Browse information step ---------------------- #
+
+bsc:
+ bscmake /o $(SBRDIR)\ghostscript.bsc /v $(GLOBJDIR)\*.sbr
+
+# end of makefile
diff --git a/psi/msvc32.mak b/psi/msvc32.mak
new file mode 100644
index 000000000..02380721e
--- /dev/null
+++ b/psi/msvc32.mak
@@ -0,0 +1,25 @@
+# Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+# CA 94903, U.S.A., +1(415)492-9861, for further information.
+#
+#
+# makefile for 32-bit target with Microsoft Visual C++ on 32-bit or 64-bit Windows
+
+!ifndef PSSRCDIR
+PSSRCDIR=.\psi
+!endif
+
+# Note that we don't override (undefine) any command line define of WIN64, so
+# this makefile can be used for a 64-bit build or 32-bit
+
+!include $(PSSRCDIR)\msvc.mak
diff --git a/psi/msvc64.mak b/psi/msvc64.mak
new file mode 100644
index 000000000..693bfe8ca
--- /dev/null
+++ b/psi/msvc64.mak
@@ -0,0 +1,23 @@
+# Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+# CA 94903, U.S.A., +1(415)492-9861, for further information.
+#
+#
+# $Id: msvc64.mak $
+# makefile for 64-bit target with Microsoft Visual C++ on 32-bit or 64-bit Windows
+
+!ifndef PSSRCDIR
+PSSRCDIR=.\psi
+!endif
+
+!include $(PSSRCDIR)\msvc.mak
diff --git a/psi/nsisinst.nsi b/psi/nsisinst.nsi
new file mode 100644
index 000000000..2c605a2b7
--- /dev/null
+++ b/psi/nsisinst.nsi
@@ -0,0 +1,268 @@
+; Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+; CA 94903, U.S.A., +1(415)492-9861, for further information.
+;
+
+; This script should be compiled with e.g.:
+; makensis -NOCD -DTARGET=gs900w32 -DVERSION=9.00 psi/nsisinst.nsi
+
+
+; Significant differences with the older winzipse-based installer are:
+
+; The Winzipse-based installer opens README on successful install.
+; Besides the installation directory, the name of the short-cuts and
+; whether it applies to "All Users" are optional, and so is whether
+; to generate a CJK cidfmap, (default to NO for silent installation).
+
+; The NSIS-based installer does not open README on successful install;
+; only whether to generate a CJK cidfmap (default to YES for silent
+; installation) is optional; short-cuts are for "All Users". On the other
+; hand, it removes the short-cuts on Uninstall (which the Winzipse-based
+; installer doesn't do) and also does not leave behind empty directories.
+
+; the following is from: http://nsis.sourceforge.net/StrRep
+!define StrRep "!insertmacro StrRep"
+!macro StrRep output string old new
+ Push "${string}"
+ Push "${old}"
+ Push "${new}"
+ !ifdef __UNINSTALL__
+ Call un.StrRep
+ !else
+ Call StrRep
+ !endif
+ Pop ${output}
+!macroend
+
+!macro Func_StrRep un
+ Function ${un}StrRep
+ Exch $R2 ;new
+ Exch 1
+ Exch $R1 ;old
+ Exch 2
+ Exch $R0 ;string
+ Push $R3
+ Push $R4
+ Push $R5
+ Push $R6
+ Push $R7
+ Push $R8
+ Push $R9
+
+ StrCpy $R3 0
+ StrLen $R4 $R1
+ StrLen $R6 $R0
+ StrLen $R9 $R2
+ loop:
+ StrCpy $R5 $R0 $R4 $R3
+ StrCmp $R5 $R1 found
+ StrCmp $R3 $R6 done
+ IntOp $R3 $R3 + 1 ;move offset by 1 to check the next character
+ Goto loop
+ found:
+ StrCpy $R5 $R0 $R3
+ IntOp $R8 $R3 + $R4
+ StrCpy $R7 $R0 "" $R8
+ StrCpy $R0 $R5$R2$R7
+ StrLen $R6 $R0
+ IntOp $R3 $R3 + $R9 ;move offset by length of the replacement string
+ Goto loop
+ done:
+
+ Pop $R9
+ Pop $R8
+ Pop $R7
+ Pop $R6
+ Pop $R5
+ Pop $R4
+ Pop $R3
+ Push $R0
+ Push $R1
+ Pop $R0
+ Pop $R1
+ Pop $R0
+ Pop $R2
+ Exch $R1
+ FunctionEnd
+!macroend
+!insertmacro Func_StrRep ""
+
+!ifndef TARGET
+!define TARGET gs899w32
+!endif
+
+!ifndef VERSION
+!define VERSION 8.99
+!endif
+
+SetCompressor /SOLID /FINAL lzma
+XPStyle on
+CRCCheck on
+
+!include "MUI2.nsh"
+; for detecting if running on x64 machine.
+!include "x64.nsh"
+
+!define MUI_FINISHPAGE_RUN
+!define MUI_FINISHPAGE_RUN_TEXT "Generate cidfmap for Windows CJK TrueType fonts"
+!define MUI_FINISHPAGE_RUN_FUNCTION CJKGen
+; !define MUI_FINISHPAGE_RUN_NOTCHECKED
+!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\doc\Readme.htm"
+; MUI_FINISHPAGE_SHOWREADME_NOTCHECKED
+!define MUI_FINISHPAGE_LINK "Visit the Ghostscript web site"
+!define MUI_FINISHPAGE_LINK_LOCATION http://www.ghostscript.com/
+
+!insertmacro MUI_PAGE_WELCOME
+!insertmacro MUI_PAGE_LICENSE "LICENSE"
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_INSTFILES
+!insertmacro MUI_PAGE_FINISH
+
+!insertmacro MUI_LANGUAGE "English"
+
+!searchparse /ignorecase /noerrors "${TARGET}" w WINTYPE
+!echo "Building ${WINTYPE}-bit installer"
+
+Name "GPL Ghostscript"
+OutFile "${TARGET}.exe"
+!if "${WINTYPE}" == "64"
+Icon obj64\gswin.ico
+UninstallIcon obj64\gswin.ico
+!else
+Icon obj\gswin.ico
+UninstallIcon obj\gswin.ico
+!endif
+
+RequestExecutionLevel admin
+
+!ifndef VERSION
+!define VERSION 8.72
+!endif
+
+; Some default compiler settings (uncomment and change at will):
+; SetCompress auto ; (can be off or force)
+; SetDatablockOptimize on ; (can be off)
+; CRCCheck on ; (can be off)
+; AutoCloseWindow false ; (can be true for the window go away automatically at end)
+; ShowInstDetails hide ; (can be show to have them shown, or nevershow to disable)
+; SetDateSave off ; (can be on to have files restored to their orginal date)
+
+BrandingText "Artifex Software Inc."
+LicenseText "You must agree to this license before installing."
+LicenseData "LICENSE"
+
+!if "${WINTYPE}" == "64"
+InstallDir "$PROGRAMFILES64\gs\gs${VERSION}"
+!else
+InstallDir "$PROGRAMFILES\gs\gs${VERSION}"
+!endif
+
+DirText "Select the directory to install GPL Ghostscript in:"
+
+Section "" ; (default section)
+SetOutPath "$INSTDIR"
+CreateDirectory "$INSTDIR\bin"
+; add files / whatever that need to be installed here.
+File /r /x contrib /x lcms /x lcms2 /x expat /x .svn doc
+File /r /x zlib /x expat /x .svn /x lcms2 examples
+File /r /x contrib /x expat /x luratech /x lwf_jp2 /x lcms /x lcms2 /x .svn /x lib/gssetgs.bat lib
+File /oname=lib\gssetgs.bat .\lib\gssetgs${WINTYPE}.bat
+File /oname=bin\gsdll${WINTYPE}.dll .\bin\gsdll${WINTYPE}.dll
+File /oname=bin\gsdll${WINTYPE}.lib .\bin\gsdll${WINTYPE}.lib
+File /oname=bin\gswin${WINTYPE}.exe .\bin\gswin${WINTYPE}.exe
+File /oname=bin\gswin${WINTYPE}c.exe .\bin\gswin${WINTYPE}c.exe
+
+!if "${WINTYPE}" == "64"
+ SetRegView 64
+!endif
+
+WriteRegStr HKEY_LOCAL_MACHINE "Software\GPL Ghostscript\${VERSION}" "GS_DLL" "$INSTDIR\bin\gsdll${WINTYPE}.dll"
+WriteRegStr HKEY_LOCAL_MACHINE "Software\GPL Ghostscript\${VERSION}" "GS_LIB" "$INSTDIR\bin;$INSTDIR\lib;$INSTDIR\fonts"
+WriteRegStr HKEY_LOCAL_MACHINE "Software\Artifex\GPL Ghostscript\${VERSION}" "" "$INSTDIR"
+WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPL Ghostscript ${VERSION}" "DisplayName" "GPL Ghostscript"
+WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPL Ghostscript ${VERSION}" "UninstallString" '"$INSTDIR\uninstgs.exe"'
+WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPL Ghostscript ${VERSION}" "Publisher" "Artifex Software Inc."
+WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPL Ghostscript ${VERSION}" "HelpLink" "http://www.ghostscript.com/"
+WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPL Ghostscript ${VERSION}" "URLInfoAbout" "http://www.ghostscript.com/"
+WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPL Ghostscript ${VERSION}" "DisplayVersion" "${VERSION}"
+WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPL Ghostscript ${VERSION}" "NoModify" "1"
+WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPL Ghostscript ${VERSION}" "NoRepair" "1"
+; write out uninstaller
+WriteUninstaller "$INSTDIR\uninstgs.exe"
+SectionEnd ; end of default section
+
+Function .onInstSuccess
+ SetShellVarContext all
+ CreateDirectory "$SMPROGRAMS\Ghostscript"
+ CreateShortCut "$SMPROGRAMS\Ghostscript\Ghostscript ${VERSION}.LNK" "$INSTDIR\bin\gswin${WINTYPE}.exe" '"-I$INSTDIR\lib;$INSTDIR\..\fonts"'
+ CreateShortCut "$SMPROGRAMS\Ghostscript\Ghostscript Readme ${VERSION}.LNK" "$INSTDIR\doc\Readme.htm"
+ CreateShortCut "$SMPROGRAMS\Ghostscript\Uninstall Ghostscript ${VERSION}.LNK" "$INSTDIR\uninstgs.exe"
+FunctionEnd
+
+Function CJKGen
+ ${StrRep} $0 "$FONTS" "\" "/"
+ ${StrRep} $1 "$INSTDIR\lib\cidfmap" "\" "/"
+ ${StrRep} $2 "$INSTDIR\lib\mkcidfm.ps" "\" "/"
+ ExecWait '"$INSTDIR\bin\gswin${WINTYPE}c.exe" -q -dBATCH "-sFONTDIR=$0" "-sCIDFMAP=$1" "$2"'
+FunctionEnd
+
+Function .onInit
+!if "${WINTYPE}" == "64"
+ SetRegView 64
+ ${IfNot} ${RunningX64}
+ MessageBox MB_OK "64-bit Ghostscript should not be installed on 32-bit machines" /SD IDOK
+ Abort
+ ${EndIf}
+!endif
+ System::Call 'kernel32::CreateMutexA(i 0, i 0, t "GhostscriptInstaller") i .r1 ?e'
+ Pop $R0
+ StrCmp $R0 0 +3
+ MessageBox MB_OK "The Ghostscript installer is already running." /SD IDOK
+ Abort
+FunctionEnd
+
+Function Un.onInit
+!if "${WINTYPE}" == "64"
+ SetRegView 64
+!endif
+FunctionEnd
+
+; begin uninstall settings/section
+UninstallText "This will uninstall GPL Ghostscript from your system"
+
+Section Uninstall
+; add delete commands to delete whatever files/registry keys/etc you installed here.
+SetShellVarContext all
+Delete "$SMPROGRAMS\Ghostscript\Ghostscript ${VERSION}.LNK"
+Delete "$SMPROGRAMS\Ghostscript\Ghostscript Readme ${VERSION}.LNK"
+Delete "$SMPROGRAMS\Ghostscript\Uninstall Ghostscript ${VERSION}.LNK"
+RMDir "$SMPROGRAMS\Ghostscript"
+Delete "$INSTDIR\uninstgs.exe"
+!if "${WINTYPE}" == "64"
+ SetRegView 64
+!endif
+DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Artifex\GPL Ghostscript\${VERSION}"
+DeleteRegKey HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPL Ghostscript ${VERSION}"
+DeleteRegKey HKEY_LOCAL_MACHINE "Software\GPL Ghostscript\${VERSION}"
+RMDir /r "$INSTDIR\doc"
+RMDir /r "$INSTDIR\examples"
+RMDir /r "$INSTDIR\lib"
+Delete "$INSTDIR\bin\gsdll${WINTYPE}.dll"
+Delete "$INSTDIR\bin\gsdll${WINTYPE}.lib"
+Delete "$INSTDIR\bin\gswin${WINTYPE}.exe"
+Delete "$INSTDIR\bin\gswin${WINTYPE}c.exe"
+RMDir "$INSTDIR\bin"
+RMDir "$INSTDIR"
+SectionEnd ; end of uninstall section
+
+; eof
diff --git a/psi/oparc.h b/psi/oparc.h
new file mode 100644
index 000000000..72d60ab6e
--- /dev/null
+++ b/psi/oparc.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Arc operator declarations */
+
+#ifndef oparc_INCLUDED
+# define oparc_INCLUDED
+
+/*
+ * These declarations are in a separate from, rather than in opextern.h,
+ * because these operators are not included in PDF-only configurations.
+ */
+
+int zarc(i_ctx_t *);
+int zarcn(i_ctx_t *);
+int zarct(i_ctx_t *);
+
+#endif /* oparc_INCLUDED */
diff --git a/psi/opcheck.h b/psi/opcheck.h
new file mode 100644
index 000000000..9a5d40774
--- /dev/null
+++ b/psi/opcheck.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definitions for operator operand checking */
+/* Requires ialloc.h (for imemory), iref.h, ierrors.h */
+
+#ifndef opcheck_INCLUDED
+# define opcheck_INCLUDED
+
+/*
+ * Check the type of an object. Operators almost always use check_type,
+ * which is defined in oper.h; check_type_only is for checking
+ * subsidiary objects obtained from places other than the stack.
+ */
+#define check_type_only(rf,typ)\
+ BEGIN if ( !r_has_type(&rf,typ) ) return_error(gs_error_typecheck); END
+#define check_stype_only(rf,styp)\
+ BEGIN if ( !r_has_stype(&rf,imemory,styp) ) return_error(gs_error_typecheck); END
+/* Check for array */
+#define check_array_else(rf,errstat)\
+ BEGIN if ( !r_has_type(&rf, t_array) ) errstat; END
+#define check_array_only(rf)\
+ check_array_else(rf, return_error(gs_error_typecheck))
+/* Check for procedure. check_proc_failed includes the stack underflow */
+/* check, but it doesn't do any harm in the off-stack case. */
+int check_proc_failed(const ref *);
+
+#define check_proc(rf)\
+ BEGIN if ( !r_is_proc(&rf) ) return_error(check_proc_failed(&rf)); END
+#define check_proc_only(rf) check_proc(rf)
+
+/* Check for read, write, or execute access. */
+#define check_access(rf,acc1)\
+ BEGIN if ( !r_has_attr(&rf,acc1) ) return_error(gs_error_invalidaccess); END
+#define check_read(rf) check_access(rf,a_read)
+#define check_write(rf) check_access(rf,a_write)
+#define check_execute(rf) check_access(rf,a_execute)
+#define check_type_access_only(rf,typ,acc1)\
+ BEGIN\
+ if ( !r_has_type_attrs(&rf,typ,acc1) )\
+ return_error((!r_has_type(&rf,typ) ? gs_error_typecheck : gs_error_invalidaccess));\
+ END
+#define check_read_type_only(rf,typ)\
+ check_type_access_only(rf,typ,a_read)
+#define check_write_type_only(rf,typ)\
+ check_type_access_only(rf,typ,a_write)
+
+/* Check for an integer value within an unsigned bound. */
+#define check_int_leu(orf, u)\
+ BEGIN\
+ check_type(orf, t_integer);\
+ if ( (ulong)(orf).value.intval > (u) ) return_error(gs_error_rangecheck);\
+ END
+#define check_int_leu_only(rf, u)\
+ BEGIN\
+ check_type_only(rf, t_integer);\
+ if ( (ulong)(rf).value.intval > (u) ) return_error(gs_error_rangecheck);\
+ END
+#define check_int_ltu(orf, u)\
+ BEGIN\
+ check_type(orf, t_integer);\
+ if ( (ulong)(orf).value.intval >= (u) ) return_error(gs_error_rangecheck);\
+ END
+
+#endif /* opcheck_INCLUDED */
diff --git a/psi/opdef.h b/psi/opdef.h
new file mode 100644
index 000000000..e514273bc
--- /dev/null
+++ b/psi/opdef.h
@@ -0,0 +1,148 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Operator definition interface for Ghostscript */
+
+#ifndef opdef_INCLUDED
+# define opdef_INCLUDED
+
+/*
+ * Define the structure for initializing the operator table. Each operator
+ * file zxxx.c declares an array of these as follows:
+
+ const op_def * const zxxx_op_defs[] = {
+ {"1name", zname},
+ ...
+ op_def_end(iproc)
+ };
+
+ * where iproc is an initialization procedure for the file or 0, and, for
+ * each operator defined, the initial digit of the name string indicates
+ * the number of arguments and zname is the address of the associated C
+ * function to invoke.
+ *
+ * The array definition always appears at the END of the file, to avoid
+ * the need for forward declarations for all the operator procedures.
+ *
+ * Operators may be stored in dictionaries other than systemdict.
+ * We support this with op_def entries of a special form:
+
+ op_def_begin_dict("dictname"),
+
+ */
+typedef struct {
+ const char *oname;
+ op_proc_t proc;
+} op_def;
+
+#define op_def_begin_dict(dname) {dname, 0}
+#define op_def_begin_filter() op_def_begin_dict("filterdict")
+#define op_def_begin_level2() op_def_begin_dict("level2dict")
+#define op_def_begin_ll3() op_def_begin_dict("ll3dict")
+#define op_def_is_begin_dict(def) ((def)->proc == 0)
+#define op_def_end(iproc) {0, iproc}
+
+/*
+ * NOTE: for implementation reasons, a single table of operator definitions
+ * is limited to 16 entries, including op_def_begin_xxx entries. If a file
+ * defines more operators than this, it must split them into multiple
+ * tables and have multiple -oper entries in the makefile. Currently,
+ * only 4 out of 85 operator files require this.
+ */
+#define OP_DEFS_LOG2_MAX_SIZE 4
+#define OP_DEFS_MAX_SIZE (1 << OP_DEFS_LOG2_MAX_SIZE)
+
+/*
+ * Define the table of pointers to all operator definition tables.
+ */
+extern const op_def *const op_defs_all[];
+
+/*
+ * Internal operators whose names begin with %, such as continuation
+ * operators, do not appear in systemdict. Ghostscript assumes
+ * that these operators cannot appear anywhere (in executable form)
+ * except on the e-stack; to maintain this invariant, the execstack
+ * operator converts them to literal form, and cvx refuses to convert
+ * them back. As a result of this invariant, they do not need to
+ * push themselves back on the e-stack when executed, since the only
+ * place they could have come from was the e-stack.
+ */
+#define op_def_is_internal(def) ((def)->oname[1] == '%')
+
+/*
+ * All operators are catalogued in a table; this is necessary because
+ * they must have a short packed representation for the sake of 'bind'.
+ * The `size' of an operator is normally its index in this table;
+ * however, internal operators have a `size' of 0, and their true index
+ * must be found by searching the table for their procedure address.
+ */
+ushort op_find_index(const ref *);
+
+#define op_index(opref)\
+ (r_size(opref) == 0 ? op_find_index(opref) : r_size(opref))
+
+/*
+ * There are actually two kinds of operators: the real ones (t_operator),
+ * and ones defined by procedures (t_oparray). The catalog for t_operators
+ * is (indirectly) op_defs_all, and their index is in the range
+ * [1..op_def_count-1].
+ */
+#define op_index_is_operator(index) ((index) < op_def_count)
+extern const uint op_def_count;
+
+#define op_index_def(index)\
+ (&op_defs_all[(index) >> OP_DEFS_LOG2_MAX_SIZE]\
+ [(index) & (OP_DEFS_MAX_SIZE - 1)])
+#define op_num_args(opref) (op_index_def(op_index(opref))->oname[0] - '0')
+#define op_index_proc(index) (op_index_def(index)->proc)
+
+/*
+ * There are two catalogs for t_oparrays, one global and one local.
+ * Operator indices for the global table are in the range
+ * [op_def_count..op_def_count+op_array_global.count-1]
+ * Operator indices for the local table are in the range
+ * [op_def_count+r_size(&op_array_global.table)..
+ * op_def_count+r_size(&op_array_global.table)+op_array_local.count-1]
+ */
+typedef struct op_array_table_s {
+ ref table; /* t_array */
+ ushort *nx_table; /* name indices */
+ uint count; /* # of occupied entries */
+ uint base_index; /* operator index of first entry */
+ uint attrs; /* ref attrs of ops in this table */
+} op_array_table;
+
+#define op_index_op_array_table(i_ctx_p,index)\
+ ((index) < i_ctx_p->op_array_table_local.base_index ?\
+ &i_ctx_p->op_array_table_global : &i_ctx_p->op_array_table_local)
+
+op_array_table *
+get_op_array(const gs_memory_t *, int);
+
+op_array_table *
+get_global_op_array(const gs_memory_t *);
+
+op_array_table *
+get_local_op_array(const gs_memory_t *);
+
+/*
+ * Convert an operator index to an operator or oparray ref.
+ * This is only used for debugging and for 'get' from packed arrays,
+ * so it doesn't have to be very fast.
+ */
+void op_index_ref(const gs_memory_t *,uint, ref *);
+
+#endif /* opdef_INCLUDED */
diff --git a/psi/oper.h b/psi/oper.h
new file mode 100644
index 000000000..b8fd180c9
--- /dev/null
+++ b/psi/oper.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definitions for Ghostscript operators */
+
+#ifndef oper_INCLUDED
+# define oper_INCLUDED
+
+#include "ierrors.h"
+#include "ostack.h"
+#include "opdef.h"
+#include "opextern.h"
+#include "opcheck.h"
+#include "iutil.h"
+
+/*
+ * Operator procedures take a single argument. This is currently a pointer
+ * to the current context state, but might conceivably change in the future.
+ * They return 0 for success, a negative code for an error, or a positive
+ * code for some uncommon situations (see below).
+ */
+
+/*
+ * In order to combine typecheck and stackunderflow error checking
+ * into a single test, we guard the bottom of the o-stack with
+ * additional entries of type t__invalid. However, if a type check fails,
+ * we must make an additional check to determine which error
+ * should be reported. In order not to have to make this check in-line
+ * in every type check in every operator, we define a procedure that takes
+ * an o-stack pointer and returns gs_error_stackunderflow if it points to
+ * a guard entry, gs_error_typecheck otherwise.
+ *
+ * Note that we only need to do this for typecheck, not for any other
+ * kind of error such as invalidaccess, since any operator that can
+ * generate the latter will do a check_type or a check_op first.
+ * (See ostack.h for more information.)
+ *
+ * We define the operand type of check_type_failed as const ref * rather than
+ * const_os_ptr simply because there are a number of routines that might
+ * be used either with a stack operand or with a general operand, and
+ * the check for t__invalid is harmless when applied to off-stack refs.
+ */
+int check_type_failed(const ref *);
+
+/*
+ * Check the type of an object. Operators almost always use check_type,
+ * which includes the stack underflow check described just above;
+ * check_type_only is for checking subsidiary objects obtained from
+ * places other than the stack.
+ */
+#define return_op_typecheck(op)\
+ return_error(check_type_failed(op))
+#define check_type(orf,typ)\
+ if ( !r_has_type(&orf,typ) ) return_op_typecheck(&orf)
+#define check_stype(orf,styp)\
+ if ( !r_has_stype(&orf,imemory,styp) ) return_op_typecheck(&orf)
+#define check_array(orf)\
+ check_array_else(orf, return_op_typecheck(&orf))
+#define check_type_access(orf,typ,acc1)\
+ if ( !r_has_type_attrs(&orf,typ,acc1) )\
+ return_error((!r_has_type(&orf,typ) ? check_type_failed(&orf) :\
+ gs_error_invalidaccess))
+#define check_read_type(orf,typ)\
+ check_type_access(orf,typ,a_read)
+#define check_write_type(orf,typ)\
+ check_type_access(orf,typ,a_write)
+
+/* Macro for as yet unimplemented operators. */
+/* The if ( 1 ) is to prevent the compiler from complaining about */
+/* unreachable code. */
+#define NYI(msg) if ( 1 ) return_error(gs_error_undefined)
+
+/*
+ * If an operator has popped or pushed something on the control stack,
+ * it must return o_pop_estack or o_push_estack respectively,
+ * rather than 0, to indicate success.
+ * It is OK to return o_pop_estack if nothing has been popped,
+ * but it is not OK to return o_push_estack if nothing has been pushed.
+ *
+ * If an operator has suspended the current context and wants the
+ * interpreter to call the scheduler, it must return o_reschedule.
+ * It may also have pushed or popped elements on the control stack.
+ * (This is only used when the Display PostScript option is included.)
+ *
+ * These values must be greater than 1, and far enough apart from zero and
+ * from each other not to tempt a compiler into implementing a 'switch'
+ * on them using indexing rather than testing.
+ */
+#define o_push_estack 5
+#define o_pop_estack 14
+#define o_reschedule 22
+
+#endif /* oper_INCLUDED */
diff --git a/psi/opextern.h b/psi/opextern.h
new file mode 100644
index 000000000..388309404
--- /dev/null
+++ b/psi/opextern.h
@@ -0,0 +1,153 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Externally accessible operator declarations */
+
+#ifndef opextern_INCLUDED
+# define opextern_INCLUDED
+
+/*
+ * Normally, the procedures that implement PostScript operators (named zX
+ * where X is the name of the operator, e.g., zadd) are private to the
+ * file in which they are defined. There are, however, a surprising
+ * number of these procedures that are used from other files.
+ * This file, opextern.h, declares all z* procedures that are
+ * - referenced from outside their defining file, and
+ * - present in *all* configurations of the interpreter.
+ * For z* procedures referenced from outside their file but not present
+ * in all configurations (e.g., Level 2 operators), the file making the
+ * reference must include a local extern. Not pretty, but c'est la vie.
+ */
+
+/* Operators exported for the special operator encoding in interp.c. */
+int zadd(i_ctx_t *);
+int zdef(i_ctx_t *);
+int zdup(i_ctx_t *);
+int zexch(i_ctx_t *);
+int zif(i_ctx_t *);
+int zifelse(i_ctx_t *);
+int zindex(i_ctx_t *);
+int zpop(i_ctx_t *);
+int zrepeat(i_ctx_t *);
+int zroll(i_ctx_t *);
+int zsub(i_ctx_t *);
+/* Internal entry points for the interpreter. */
+int zop_add(i_ctx_t *);
+int zop_def(i_ctx_t *);
+int zop_sub(i_ctx_t *);
+
+/* Operators exported for server loop implementations. */
+int zflush(i_ctx_t *);
+int zflushpage(i_ctx_t *);
+int zsave(i_ctx_t *);
+int zrestore(i_ctx_t *);
+
+/* Operators exported for save/restore. */
+int zgsave(i_ctx_t *);
+int zgrestore(i_ctx_t *);
+
+/* Operators exported for Level 2 pagedevice facilities. */
+int zcopy_gstate(i_ctx_t *);
+int zcurrentgstate(i_ctx_t *);
+int zgrestoreall(i_ctx_t *);
+int zgstate(i_ctx_t *);
+int zreadonly(i_ctx_t *);
+int zsetdevice(i_ctx_t *);
+int zsetgstate(i_ctx_t *);
+
+/* Operators exported for Level 2 "wrappers". */
+int zcopy(i_ctx_t *);
+int zimage(i_ctx_t *);
+int zimagemask(i_ctx_t *);
+int zwhere(i_ctx_t *);
+
+/* Operators exported for specific-VM operators. */
+int zarray(i_ctx_t *);
+int zdict(i_ctx_t *);
+int zpackedarray(i_ctx_t *);
+int zstring(i_ctx_t *);
+int zfile(i_ctx_t *);
+int zlibfile(i_ctx_t *);
+int zSFD(i_ctx_t *);
+
+/* Operators exported for user path decoding. */
+/* Note that only operators defined in all configurations are declared here. */
+int zclosepath(i_ctx_t *);
+int zcurveto(i_ctx_t *);
+int zlineto(i_ctx_t *);
+int zmoveto(i_ctx_t *);
+int zrcurveto(i_ctx_t *);
+int zrlineto(i_ctx_t *);
+int zrmoveto(i_ctx_t *);
+
+/* Operators exported for the FunctionType 4 interpreter. */
+/* zarith.c: */
+int zabs(i_ctx_t *);
+int zceiling(i_ctx_t *);
+int zdiv(i_ctx_t *);
+int zfloor(i_ctx_t *);
+int zidiv(i_ctx_t *);
+int zmod(i_ctx_t *);
+int zmul(i_ctx_t *);
+int zneg(i_ctx_t *);
+int zround(i_ctx_t *);
+int ztruncate(i_ctx_t *);
+/* zmath.c: */
+int zatan(i_ctx_t *);
+int zcos(i_ctx_t *);
+int zexp(i_ctx_t *);
+int zln(i_ctx_t *);
+int zlog(i_ctx_t *);
+int zsin(i_ctx_t *);
+int zsqrt(i_ctx_t *);
+/* zrelbit.c: */
+int zand(i_ctx_t *);
+int zbitshift(i_ctx_t *);
+int zeq(i_ctx_t *);
+int zge(i_ctx_t *);
+int zgt(i_ctx_t *);
+int zle(i_ctx_t *);
+int zlt(i_ctx_t *);
+int zne(i_ctx_t *);
+int znot(i_ctx_t *);
+int zor(i_ctx_t *);
+int zxor(i_ctx_t *);
+/* ztype.c: */
+int zcvi(i_ctx_t *);
+int zcvr(i_ctx_t *);
+
+/* Operators exported for CIE cache loading. */
+int zcvx(i_ctx_t *);
+int zexec(i_ctx_t *); /* also for .runexec and .errorexec */
+int zfor(i_ctx_t *);
+
+/* Odds and ends */
+int zbegin(i_ctx_t *);
+int zcleartomark(i_ctx_t *);
+int zclosefile(i_ctx_t *); /* for runexec_cleanup */
+int zcopy_dict(i_ctx_t *); /* for zcopy */
+int zend(i_ctx_t *);
+int zfor_samples(i_ctx_t *); /* for function sampling */
+int zsetfont(i_ctx_t *); /* for cshow_continue */
+
+/* Operators exported for special customer needs. */
+int zcurrentdevice(i_ctx_t *);
+int ztoken(i_ctx_t *);
+int ztokenexec(i_ctx_t *);
+int zwrite(i_ctx_t *);
+
+int zspec_op(i_ctx_t *i_ctx_p);
+#endif /* opextern_INCLUDED */
diff --git a/psi/os2.mak b/psi/os2.mak
new file mode 100644
index 000000000..b434537dd
--- /dev/null
+++ b/psi/os2.mak
@@ -0,0 +1,670 @@
+# Copyright (C) 2001-2008 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 that
+# license. Refer to licensing information at http://www.artifex.com/
+# or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+# San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
+#
+# makefile for MS-DOS or OS/2 GCC/EMX platform.
+# Uses Borland (MSDOS) MAKER or
+# Uses IBM NMAKE.EXE Version 2.000.000 Mar 27 1992
+
+# ------------------------------- Options ------------------------------- #
+
+###### This section is the only part of the file you should need to edit.
+
+# ------ Generic options ------ #
+
+# Define the directory for the final executable, and the
+# source, generated intermediate file, and object directories
+# for the graphics library (GL) and the PostScript/PDF interpreter (PS).
+
+# This makefile has never been tested with any other values than these,
+# and almost certainly won't work with other values.
+BINDIR=bin
+GLSRCDIR=base
+GLGENDIR=obj
+GLOBJDIR=obj
+AUXDIR=$(GLGENDIR)/aux_
+PSSRCDIR=psi
+PSLIBDIR=lib
+PSRESDIR=Resource
+PSGENDIR=obj
+PSOBJDIR=obj
+
+# Define the root directory for Ghostscript installation.
+
+AROOTDIR=c:/gs
+GSROOTDIR=$(AROOTDIR)/gs$(GS_DOT_VERSION)
+
+# Define the directory that will hold documentation at runtime.
+
+GS_DOCDIR=$(GSROOTDIR)/doc
+
+# Define the default directory/ies for the runtime
+# initialization, resource and font files. Separate multiple directories with ;.
+# Use / to indicate directories, not a single \.
+
+GS_LIB_DEFAULT=$(GSROOTDIR)/Resource/Init;$(GSROOTDIR)/lib;$(GSROOTDIR)/Resource/Font;$(AROOTDIR)/fonts
+
+# Define whether or not searching for initialization files should always
+# look in the current directory first. This leads to well-known security
+# and confusion problems, but may be convenient sometimes.
+
+SEARCH_HERE_FIRST=0
+
+# Define the name of the interpreter initialization file.
+# (There is no reason to change this.)
+
+GS_INIT=gs_init.ps
+
+# Choose generic configuration options.
+
+# Setting DEBUG=1 includes debugging features (-Z switch) in the code.
+# Code runs substantially slower even if no debugging switches are set,
+# and also takes about another 25K of memory.
+
+DEBUG=0
+
+# Setting GDEBUG=1 includes symbol table information for GDB.
+# Produces larger .OBJ and .EXE files.
+
+GDEBUG=0
+
+# Setting MAKEDLL=1 makes the target a DLL instead of an EXE
+MAKEDLL=1
+
+# Setting EMX=1 uses GCC/EMX
+# Setting IBMCPP=1 uses IBM C++
+EMX=1
+IBMCPP=0
+
+# Setting BUILD_X11=1 builds X11 client using Xfree86
+BUILD_X11=0
+!if $(BUILD_X11)
+X11INCLUDE=-I$(X11ROOT)\XFree86\include
+X11LIBS=$(X11ROOT)\XFree86\lib\Xt.lib $(X11ROOT)\XFree86\lib\X11.lib
+MT_OPT=-Zmtd
+!endif
+
+# Define the name of the executable file.
+
+GS=gsos2
+GSDLL=gsdll2
+
+# Define the directory where the IJG JPEG library sources are stored,
+# and the major version of the library that is stored there.
+# You may need to change this if the IJG library version changes.
+# See jpeg.mak for more information.
+
+JSRCDIR=jpeg
+
+# Define the directory where the PNG library sources are stored,
+# and the version of the library that is stored there.
+# You may need to change this if the libpng version changes.
+# See png.mak for more information.
+
+PNGSRCDIR=libpng
+
+# Define the directory where the lcms source is stored.
+# See lcms.mak for more information
+LCMSSRCDIR=lcms
+
+# Define the directory where the lcms2 source is stored.
+# See lcms2.mak for more information
+LCMS2SRCDIR=lcms2
+
+# Which CMS are we using?
+# Options are currently lcms or lcms2
+WHICH_CMS=lcms2
+
+# Define the directory where the zlib sources are stored.
+# See zlib.mak for more information.
+
+ZSRCDIR=zlib
+
+# Define the jbig2dec library source location.
+# See jbig2.mak for more information.
+
+JBIG2_LIB=jbig2dec
+JBIG2SRCDIR=jbig2dec
+
+# IJS has not been ported to OS/2. If you do the port,
+# you'll need to set these values. You'll also need to
+# include the ijs.mak makefile
+#
+# Define the directory where the ijs source is stored,
+# and the process forking method to use for the server.
+# See ijs.mak for more information.
+
+#IJSSRCDIR=ijs
+#IJSEXECTYPE=win
+
+# 1 --> Use 64 bits for gx_color_index. This is required only for
+# non standard devices or DeviceN process color model devices.
+USE_LARGE_COLOR_INDEX=1
+
+!if $(USE_LARGE_COLOR_INDEX) == 1
+# Definitions to force gx_color_index to 64 bits
+LARGEST_UINTEGER_TYPE=unsigned long long
+GX_COLOR_INDEX_TYPE=$(LARGEST_UINTEGER_TYPE)
+GCIFLAGS=-DGX_COLOR_INDEX_TYPE="$(GX_COLOR_INDEX_TYPE)"
+!else
+GCIFLAGS=
+!endif
+
+
+# The following is a hack to get around the special treatment of \ at
+# the end of a line.
+NUL=
+DD=$(GLGENDIR)\$(NUL)
+GLD=$(GLGENDIR)\$(NUL)
+PSD=$(PSGENDIR)\$(NUL)
+
+
+# ------ Platform-specific options ------ #
+
+# Define the drive, directory, and compiler name for the EMX files.
+# COMP is the compiler name (gcc)
+# COMPDIR contains the compiler and linker (normally \emx\bin).
+# EMXPATH contains the path to the EMX directory (normally /emx)
+# INCDIR contains the include files (normally /emx/include).
+# LIBDIR contains the library files (normally /emx/lib).
+# Note that these prefixes are always followed by a \,
+# so if you want to use the current directory, use an explicit '.'.
+
+!if $(EMX)
+COMP=gcc $(X11INCLUDE)
+COMPBASE=\emx
+EMXPATH=/emx
+COMPDIR=$(COMPBASE)\bin
+INCDIR=$(EMXPATH)/include
+LIBDIR=$(EMXPATH)/lib
+CPNG=-DPNGAPI=
+!endif
+
+!if $(IBMCPP)
+COMP=icc /Q
+COMPBASE=\ibmcpp
+TOOLPATH=\toolkit
+COMPDIR=$(COMPBASE)\bin
+INCDIR=$(TOOLPATH)\h;$(COMPBASE)\include
+LIBDIR=$(TOOLPATH)\lib;$(COMPBASE)\lib
+!endif
+
+# Choose platform-specific options.
+
+# Define the processor (CPU) type. Options are 86 (8086 or 8088),
+# 186, 286, 386, 485 (486SX or Cyrix 486SLC), 486 (486DX), or 586 (Pentium).
+# Higher numbers produce code that may be significantly smaller and faster,
+# but the executable will bail out with an error message on any processor
+# less capable than the designated one.
+
+# EMX requires 386 or higher
+CPU_TYPE=386
+
+# Define the .dev module that implements thread and synchronization
+# primitives for this platform. Don't change this unless you really know
+# what you're doing.
+
+SYNC=nosync
+
+# ---------------------------- End of options ---------------------------- #
+
+# Note that built-in libpng and zlib aren't available.
+
+SHARE_JPEG=0
+SHARE_LIBPNG=0
+SHARE_ZLIB=0
+SHARE_JBIG2=0
+
+# Swapping `make' out of memory makes linking much faster.
+# only used by Borland MAKER.EXE
+
+#.swap
+
+# Define the platform name.
+
+GSPLATFORM=os2_
+
+# Define the name of the makefile -- used in dependencies.
+
+MAKEFILE=$(PSSRCDIR)\os2.mak
+TOP_MAKEFILES=$(MAKEFILE)
+
+# Define the files to be deleted by 'make clean'.
+
+BEGINFILES=$(BINDIR)\gspmdrv.exe $(GLOBJDIR)\gspmdrv.o $(GLOBJDIR)\gs*.res $(GLOBJDIR)\gs*.ico $(BINDIR)\$(GSDLL).dll
+
+# Define the auxiliary program dependency. We don't use this.
+
+AK=
+
+#Compiler Optimiser option
+!if $(EMX)
+CO=-O
+!endif
+!if $(IBMCPP)
+#CO=/O+
+CO=/O-
+!endif
+
+# Make sure we get the right default target for make.
+
+dosdefault: default $(BINDIR)\gspmdrv.exe
+
+# Define a rule for invoking just the preprocessor.
+
+.c.i:
+ $(COMPDIR)\cpp $(CCFLAGS) $<
+
+# Define the extensions for command, object, and executable files.
+
+# Work around the fact that some `make' programs drop trailing spaces
+# or interpret == as a special definition operator.
+NULL=
+
+CMD=.cmd
+C_=-c
+D_=-D
+_D_=$(NULL)=
+_D=
+I_=-I
+II=-I
+_I=
+O_=-o $(NULL)
+!if $(MAKEDLL)
+OBJ=obj
+!else
+OBJ=o
+!endif
+Q=
+XE=.exe
+XEAUX=.exe
+
+# Define the current directory prefix and shell name.
+
+D=\#
+
+EXP=
+SH=
+
+# Define generic commands.
+
+# We use cp.cmd rather than copy /B so that we update the write date.
+CP_=$(GLSRCDIR)\cp.cmd
+# We use rm.cmd rather than erase because rm.cmd never generates
+# a non-zero return code.
+RM_=$(GLSRCDIR)\rm.cmd
+# OS/2 erase, unlike MS-DOS erase, accepts multiple files or patterns.
+RMN_=$(GLSRCDIR)\rm.cmd
+
+# Define the arguments for genconf.
+
+!if $(MAKEDLL)
+CONFILES=-p %%s+
+!else
+CONFILES=
+!endif
+CONFLDTR=-ol
+
+# Define the generic compilation flags.
+
+!if $(CPU_TYPE) >= 486
+PLATOPT=-DFOR80386 -DFOR80486
+!else
+!if $(CPU_TYPE) >= 386
+PLATOPT=-DFOR80386
+!endif
+!endif
+
+# ---------------------- MS-DOS I/O debugging option ---------------------- #
+
+dosio_=$(PSOBJ)zdosio.$(OBJ)
+dosio.dev: $(PSGEN)dosio.dev
+ $(NO_OP)
+
+$(PSGEN)dosio.dev: $(dosio_)
+ $(SETMOD) $(PSGEN)dosio $(dosio_)
+ $(ADDMOD) $(PSGEN)dosio -oper zdosio
+
+$(PSOBJ)zdosio.$(OBJ): $(PSSRC)zdosio.c $(OP) $(store_h)
+ $(PSCC) $(PSO_)zdosio.$(OBJ) $(C_) $(PSSRC)zdosio.c
+
+# Define the compilation flags.
+
+!if $(DEBUG)
+CD=-DDEBUG
+!else
+CD=
+!endif
+
+!if $(GDEBUG)
+!if $(EMX)
+CGDB=-g
+!endif
+!if $(IBMCPP)
+CGDB=/Ti+
+!endif
+!else
+CGDB=
+!endif
+
+!if $(MAKEDLL)
+!if $(EMX)
+CDLL=-Zdll -Zso -Zsys -Zomf $(MT_OPT) -D__DLL__
+!endif
+!if $(IBMCPP)
+CDLL=/Gd- /Ge- /Gm+ /Gs+ /D__DLL__
+!endif
+!else
+CDLL=
+!endif
+
+!if $(EMX)
+CEXE=-Zomf
+CEXESYS=-Zomf -Zsys
+!endif
+
+GENOPT=$(CD) $(CGDB) $(CDLL) $(CO) $(CPNG)
+
+CCFLAGS0=$(GENOPT) $(PLATOPT) -D__OS2__ $(GCIFLAGS)
+CCFLAGS=$(CCFLAGS0)
+CC=$(COMPDIR)\$(COMP) $(CCFLAGS0)
+CCAUX=$(CC)
+CC_=$(CC)
+CCAUX_=$(CCAUX)
+CC_NO_WARN=$(CC_)
+CCAUX_NO_WARN=$(CCAUX_)
+CC_SHARED=$(CC_)
+
+# ------ Devices and features ------ #
+
+# Choose the language feature(s) to include. See gs.mak for details.
+# Since we have a large address space, we include some optional features.
+
+FEATURE_DEVS=$(PSD)psl3.dev $(PSD)pdf.dev $(PSD)dpsnext.dev $(PSD)ttfont.dev $(PSD)epsf.dev $(PSD)os2print.dev
+
+# Choose whether to compile the .ps initialization files into the executable.
+# See gs.mak for details.
+
+COMPILE_INITS=1
+
+# Choose whether to store band lists on files or in memory.
+# The choices are 'file' or 'memory'.
+
+BAND_LIST_STORAGE=file
+
+# Choose which compression method to use when storing band lists in memory.
+# The choices are 'lzw' or 'zlib'.
+
+BAND_LIST_COMPRESSOR=zlib
+
+# Choose the implementation of file I/O: 'stdio', 'fd', or 'both'.
+# See gs.mak and sfxfd.c for more details.
+
+FILE_IMPLEMENTATION=stdio
+
+# Choose the implementation of stdio: '' for file I/O and 'c' for callouts
+# See gs.mak and ziodevs.c/ziodevsc.c for more details.
+
+STDIO_IMPLEMENTATION=c
+
+# Choose the device(s) to include. See devs.mak for details,
+# devs.mak, pcwin.mak, and contrib.mak for the list of available devices.
+
+!if $(MAKEDLL)
+DEVICE_DEVS=$(DD)display.dev $(DD)os2prn.dev
+!endif
+!if $(BUILD_X11)
+DEVICE_DEVS1=$(DD)x11.dev $(DD)x11alpha.dev $(DD)x11cmyk.dev $(DD)x11gray2.dev $(DD)x11gray4.dev $(DD)x11mono.dev
+!else
+DEVICE_DEVS1=
+!endif
+DEVICE_DEVS2=$(DD)epson.dev $(DD)eps9high.dev $(DD)eps9mid.dev $(DD)epsonc.dev $(DD)ibmpro.dev
+DEVICE_DEVS3=$(DD)deskjet.dev $(DD)djet500.dev $(DD)laserjet.dev $(DD)ljetplus.dev $(DD)ljet2p.dev
+DEVICE_DEVS4=$(DD)cdeskjet.dev $(DD)cdjcolor.dev $(DD)cdjmono.dev $(DD)cdj550.dev
+DEVICE_DEVS5=$(DD)uniprint.dev $(DD)djet500c.dev $(DD)declj250.dev $(DD)lj250.dev
+DEVICE_DEVS6=$(DD)st800.dev $(DD)stcolor.dev $(DD)bj10e.dev $(DD)bj200.dev
+DEVICE_DEVS7=$(DD)t4693d2.dev $(DD)t4693d4.dev $(DD)t4693d8.dev $(DD)tek4696.dev
+DEVICE_DEVS8=$(DD)pcxmono.dev $(DD)pcxgray.dev $(DD)pcx16.dev $(DD)pcx256.dev $(DD)pcx24b.dev $(DD)pcxcmyk.dev
+DEVICE_DEVS9=$(DD)pbm.dev $(DD)pbmraw.dev $(DD)pgm.dev $(DD)pgmraw.dev $(DD)pgnm.dev $(DD)pgnmraw.dev $(DD)pkmraw.dev
+DEVICE_DEVS10=$(DD)tiffcrle.dev $(DD)tiffg3.dev $(DD)tiffg32d.dev $(DD)tiffg4.dev $(DD)tifflzw.dev $(DD)tiffpack.dev
+DEVICE_DEVS11=$(DD)bmpmono.dev $(DD)bmpgray.dev $(DD)bmp16.dev $(DD)bmp256.dev $(DD)bmp16m.dev $(DD)tiff12nc.dev $(DD)tiff24nc.dev $(DD)tiffgray.dev $(DD)tiff32nc.dev $(DD)tiffsep.dev $(DD)tiffsep1.dev
+DEVICE_DEVS12=$(DD)bit.dev $(DD)bitrgb.dev $(DD)bitcmyk.dev
+DEVICE_DEVS13=$(DD)pngmono.dev $(DD)pngmonod.dev $(DD)pnggray.dev $(DD)png16.dev $(DD)png256.dev $(DD)png16m.dev $(DD)pngalpha.dev
+DEVICE_DEVS14=$(DD)jpeg.dev $(DD)jpeggray.dev $(DD)jpegcmyk.dev
+DEVICE_DEVS15=$(DD)pdfwrite.dev $(DD)ps2write.dev $(DD)eps2write.dev $(DD)txtwrite.dev $(DD)pxlmono.dev $(DD)pxlcolor.dev
+DEVICE_DEVS16=$(DD)bbox.dev
+# Overflow for DEVS3,4,5,6,9
+DEVICE_DEVS17=$(DD)ljet3.dev $(DD)ljet3d.dev $(DD)ljet4.dev $(DD)ljet4d.dev
+DEVICE_DEVS18=$(DD)pj.dev $(DD)pjxl.dev $(DD)pjxl300.dev $(DD)jetp3852.dev $(DD)r4081.dev
+DEVICE_DEVS19=$(DD)lbp8.dev $(DD)m8510.dev $(DD)necp6.dev $(DD)bjc600.dev $(DD)bjc800.dev
+DEVICE_DEVS20=$(DD)pnm.dev $(DD)pnmraw.dev $(DD)ppm.dev $(DD)ppmraw.dev $(DD)pamcmyk32.dev
+DEVICE_DEVS21= $(DD)spotcmyk.dev $(DD)devicen.dev $(DD)bmpsep1.dev $(DD)bmpsep8.dev $(DD)bmp16m.dev $(DD)bmp32b.dev $(DD)psdcmyk.dev $(DD)psdrgb.dev
+
+# Include the generic makefiles.
+!include "$(GLSRCDIR)\version.mak"
+!include "$(GLSRCDIR)\gs.mak"
+# psromfs.mak must precede lib.mak
+!include "$(PSSRCDIR)\psromfs.mak"
+!include "$(GLSRCDIR)\lib.mak"
+!include "$(GLSRCDIR)\jpeg.mak"
+# zlib.mak must precede png.mak
+!include "$(GLSRCDIR)\zlib.mak"
+!include "$(GLSRCDIR)\png.mak"
+!include "$(GLSRCDIR)\jbig2.mak"
+!include "$(GLSRCDIR)\$(WHICH_CMS).mak"
+!include "$(GLSRCDIR)\devs.mak"
+!include "$(GLSRCDIR)\pcwin.mak"
+!include "$(GLSRCDIR)\contrib.mak"
+!include "$(PSSRCDIR)\int.mak"
+
+# -------------------------------- Library -------------------------------- #
+
+# The GCC/EMX platform
+
+os2__=$(GLOBJ)gp_getnv.$(OBJ) $(GLOBJ)gp_getnv.$(OBJ) $(GLOBJ)gp_os2.$(OBJ) $(GLOBJ)gp_os2fs.$(OBJ) $(GLOBJ)gp_paper.$(OBJ) $(GLOBJ)gp_stdia.$(OBJ)
+$(GLGEN)os2_.dev: $(os2__) $(GLD)nosync.dev
+ $(SETMOD) $(GLGEN)os2_ $(os2__) -include $(GLD)nosync
+
+$(GLOBJ)gp_os2.$(OBJ): $(GLSRC)gp_os2.c $(GLSRC)gp_os2.h\
+ $(dos__h) $(pipe__h) $(string__h) $(time__h)\
+ $(gx_h) $(gsexit_h) $(gsutil_h) $(gp_h) $(gpmisc_h)
+ $(GLCC) $(GLO_)gp_os2.$(OBJ) $(C_) $(GLSRC)gp_os2.c
+
+$(GLOBJ)gp_os2fs.$(OBJ): $(GLSRC)gp_os2fs.c $(GLSRC)gp_os2.h\
+ $(dos__h) $(pipe__h) $(string__h) $(time__h)\
+ $(gx_h) $(gsexit_h) $(gsutil_h) $(gp_h) $(gpmisc_h)
+ $(GLCC) $(GLO_)gp_os2fs.$(OBJ) $(C_) $(GLSRC)gp_os2fs.c
+
+$(GLOBJ)gp_paper.$(OBJ): $(GLSRC)gp_paper.c $(AK) $(gp_h)
+ $(GLCCWIN) $(GLO_)gp_paper.$(OBJ) $(C_) $(GLSRC)gp_paper.c
+
+$(GLOBJ)gp_stdia.$(OBJ): $(GLSRC)gp_stdia.c $(AK)\
+ $(stdio__h) $(time__h) $(unistd__h) $(gx_h) $(gp_h)
+ $(GLCC) $(GLO_)gp_stdia.$(OBJ) $(C_) $(GLSRC)gp_stdia.c
+
+# Define OS/2 printer (file system) as a separable feature.
+
+os2print_=$(GLOBJ)gp_os2pr.$(OBJ)
+$(GLD)os2print.dev: $(ECHOGS_XE) $(os2print_)
+ $(SETMOD) $(GLD)os2print $(os2print_)
+ $(ADDMOD) $(GLD)os2print -iodev printer
+
+$(GLOBJ)gp_os2pr.$(OBJ): $(GLSRC)gp_os2pr.c $(GLSRC)gp_os2.h $(AK)\
+ $(ctype__h) $(errno__h) $(stdio__h) $(string__h)\
+ $(gsmemory_h) $(gstypes_h) $(gxiodev_h)
+ $(GLCC) $(GLO_)gp_os2pr.$(OBJ) $(C_) $(GLSRC)gp_os2pr.c
+
+
+# -------------------------- Auxiliary programs --------------------------- #
+
+#CCAUX=$(COMPDIR)\$(COMP) $(CO)
+# emx 0.9d (gcc 2.8.1) crashes when compiling genarch.c with optimizer
+CCAUX=$(COMPDIR)\$(COMP)
+
+$(ECHOGS_XE): $(GLSRCDIR)\echogs.c
+!if $(EMX)
+ $(CCAUX) -o $(AUXGEN)echogs $(GLSRCDIR)\echogs.c
+ $(COMPDIR)\emxbind $(EMXPATH)/bin/emxl.exe $(AUXGEN)echogs $(ECHOGS_XE)
+ del $(AUXGEN)echogs
+!endif
+!if $(IBMCPP)
+ $(CCAUX) /Fe$(ECHOGS_XE) $(GLSRCDIR)\echogs.c
+!endif
+
+$(GENARCH_XE): $(GLSRCDIR)\genarch.c $(GENARCH_DEPS)
+ -mkdir $(GLGENDIR)
+ -mkdir $(BINDIR)
+ -mkdir $(AUXDIR)
+!if $(EMX)
+ $(CCAUX) -DHAVE_LONG_LONG -o $(AUXGEN)genarch $(GLSRCDIR)\genarch.c
+ $(COMPDIR)\emxbind $(EMXPATH)/bin/emxl.exe $(AUXGEN)genarch $(GENARCH_XE)
+ del $(AUXGEN)genarch
+!endif
+!if $(IBMCPP)
+ $(CCAUX) /Fe$(GENARCH_XE) $(GLSRCDIR)\genarch.c
+!endif
+
+$(GENCONF_XE): $(GLSRCDIR)\genconf.c $(GENCONF_DEPS)
+!if $(EMX)
+ $(CCAUX) -o $(AUXGEN)genconf $(GLSRCDIR)\genconf.c
+ $(COMPDIR)\emxbind $(EMXPATH)/bin/emxl.exe $(AUXGEN)genconf $(GENCONF_XE)
+ del $(AUXGEN)genconf
+!endif
+!if $(IBMCPP)
+ $(CCAUX) /Fe$(GENCONF_XE) $(GLSRCDIR)\genconf.c
+!endif
+
+$(GENDEV_XE): $(GLSRCDIR)\gendev.c $(GENDEV_DEPS)
+!if $(EMX)
+ $(CCAUX) -o $(AUXGEN)gendev $(GLSRCDIR)\gendev.c
+ $(COMPDIR)\emxbind $(EMXPATH)/bin/emxl.exe $(AUXGEN)gendev $(GENDEV_XE)
+ del $(AUXGEN)gendev
+!endif
+!if $(IBMCPP)
+ $(CCAUX) /Fe$(GENDEV_XE) $(GLSRCDIR)\gendev.c
+!endif
+
+$(GENHT_XE): $(PSSRC)genht.c $(GENHT_DEPS)
+!if $(EMX)
+ $(CCAUX) -o $(AUXGEN)genht $(GENHT_CFLAGS) $(PSSRC)genht.c
+ $(COMPDIR)\emxbind $(EMXPATH)/bin/emxl.exe $(AUXGEN)genht $(GENHT_XE)
+ del $(AUXGEN)genht
+!endif
+!if $(IBMCPP)
+ $(CCAUX) /Fe$(GENHT_XE) genht.c
+!endif
+
+MKROMFS_OBJS=$(MKROMFS_ZLIB_OBJS) $(GLOBJ)gscdefs.$(OBJ) $(GLOBJ)gpmisc.$(OBJ) $(GLOBJ)gp_getnv.obj $(GLOBJ)gp_os2fs.obj
+$(MKROMFS_XE): $(GLSRC)mkromfs.c $(MKROMFS_COMMON_DEPS) $(MKROMFS_OBJS)
+!if $(EMX)
+ $(CCAUX) -o $(AUXGEN)mkromfs.exe -D__OS2__ $(CD) $(CEXESYS) $(CO) $(CPNG) -I$(GLOBJ) -I$(GLSRCDIR) -I$(ZSRCDIR) $(GLSRC)mkromfs.c $(MKROMFS_OBJS)
+!endif
+!if $(IBMCPP)
+ $(CCAUX) /Fe$(MKROMFS_XE) mkromfs.c
+!endif
+
+# No special gconfig_.h is needed.
+$(gconfig__h): $(TOP_MAKEFILES) $(ECHOGS_XE)
+ $(ECHOGS_XE) -w $(gconfig__h) /* This file deliberately left blank. */
+
+# ----------------------------- Main program ------------------------------ #
+
+# Interpreter main program
+
+ICONS=$(PSOBJ)gsos2.ico $(GLOBJ)gspmdrv.ico
+
+$(PSOBJ)dpmain.$(OBJ): $(PSSRC)dpmain.c $(AK)\
+ $(gdevdsp_h) $(iapi_h) $(gscdefs_h) $(ierrors_h)
+ $(CC) $(CEXE) -I$(PSSRCDIR) -I$(GLSRCDIR) -I$(GLGENDIR) $(PSO_)dpmain.$(OBJ) $(C_) $(PSSRC)dpmain.c
+
+!if $(MAKEDLL)
+#making a DLL
+GS_ALL=$(INT_ALL) \
+ $(LIB_ALL) $(LIBCTR) $(ld_tr) $(PSOBJ)$(GS).res $(ICONS) $(PSOBJ)gsromfs$(COMPILE_INITS).$(OBJ)
+
+$(GS_XE): $(BINDIR)\$(GSDLL).dll $(PSSRC)dpmain.c $(PSSRC)gsos2.rc $(GLOBJ)gscdefs.$(OBJ)
+!if $(EMX)
+ $(COMPDIR)\$(COMP) $(CGDB) $(CO) -Zomf $(MT_OPT) -I$(PSSRCDIR) -I$(GLSRCDIR) -I$(PSOBJDIR) -I$(GLOBJDIR) -o$(GS_XE) $(PSSRC)dpmain.c $(GLOBJ)gscdefs.$(OBJ) $(PSSRC)gsos2.def
+!endif
+!if $(IBMCPP)
+ $(CCAUX) -I$(PSSRCDIR) -I$(GLSRCDIR) -I$(PSOBJDIR) -I$(GLOBJDIR) /Fe$(GX_XE) $(PSSRC)dpmain.c $(GLOBJ)gscdefs.$(OBJ)
+!endif
+ rc $(PSOBJ)$(GS).res $(GS_XE)
+
+$(BINDIR)\$(GSDLL).dll: $(GS_ALL) $(ALL_DEVS)
+!if $(EMX)
+ LINK386 /DEBUG $(COMPBASE)\lib\dll0.obj $(COMPBASE)\lib\end.lib @$(ld_tr) $(PSOBJ)gsromfs$(COMPILE_INITS).$(OBJ), $(BINDIR)\$(GSDLL).dll, ,$(X11LIBS) $(COMPBASE)\lib\gcc.lib $(COMPBASE)\lib\st\c.lib $(COMPBASE)\lib\st\c_dllso.lib $(COMPBASE)\lib\st\sys.lib $(COMPBASE)\lib\c_alias.lib $(COMPBASE)\lib\os2.lib, $(PSSRC)gsdll2.def
+!endif
+!if $(IBMCPP)
+ LINK386 /NOE /DEBUG @$(ld_tr) $(PSOBJ)gsromfs$(COMPILE_INITS).$(OBJ), $(BINDIR)\$(GSDLL).dll, , , $(PSSRC)gsdll2.def
+!endif
+
+!else
+#making an EXE
+GS_ALL=$(PSOBJ)gs.$(OBJ) $(INT_ALL) \
+ $(LIB_ALL) $(LIBCTR) $(ld_tr) $(PSOBJ)$(GS).res $(ICONS) $(PSOBJ)gsromfs$(COMPILE_INITS).$(OBJ)
+
+$(GS_XE): $(GS_ALL) $(ALL_DEVS)
+ $(COMPDIR)\$(COMP) $(CGDB) I$(PSSRCDIR) -I$(GLSRCDIR) -o $(PSOBJ)$(GS) $(PSOBJ)gs.$(OBJ) @$(ld_tr) $(PSOBJ)gsromfs$(COMPILE_INITS).$(OBJ) -lm
+ $(COMPDIR)\emxbind -r$(PSOBJ)$(GS).res $(COMPDIR)\emxl.exe $(PSOBJ)$(GS) $(GS_XE) -ac
+ del $(PSOBJ)$(GS)
+!endif
+
+# Make the icons from their text form.
+
+$(PSOBJ)gsos2.ico: $(PSSRC)gsos2.icx $(ECHOGS_XE)
+ $(ECHOGS_XE) -wb $(PSOBJ)gsos2.ico -n -X -r $(PSSRC)gsos2.icx
+
+$(GLOBJ)gspmdrv.ico: $(GLSRC)gspmdrv.icx $(ECHOGS_XE)
+ $(ECHOGS_XE) -wb $(GLOBJ)gspmdrv.ico -n -X -r $(GLSRC)gspmdrv.icx
+
+$(PSOBJ)$(GS).res: $(PSSRC)$(GS).rc $(PSOBJ)gsos2.ico
+ rc -i $(COMPBASE)\include -i $(PSSRCDIR) -i $(PSOBJDIR) -r $(PSSRC)$(GS).rc $(PSOBJ)$(GS).res
+
+
+# PM driver program
+
+$(GLOBJ)gspmdrv.o: $(GLSRC)gspmdrv.c $(GLSRC)gspmdrv.h
+ $(COMPDIR)\$(COMP) $(CGDB) $(CO) -I$(GLSRCDIR) -o $(GLOBJ)gspmdrv.o -c $(GLSRC)gspmdrv.c
+
+$(GLOBJ)gspmdrv.res: $(GLSRC)gspmdrv.rc $(GLSRC)gspmdrv.h $(GLOBJ)gspmdrv.ico
+ rc -i $(COMPBASE)\include -i $(GLSRCDIR) -i $(GLOBJDIR) -r $(GLSRC)gspmdrv.rc $(GLOBJ)gspmdrv.res
+
+$(BINDIR)\gspmdrv.exe: $(GLOBJ)gspmdrv.o $(GLOBJ)gspmdrv.res $(GLSRC)gspmdrv.def
+ $(COMPDIR)\$(COMP) $(CGDB) $(CO) -o $(GLOBJ)gspmdrv $(GLOBJ)gspmdrv.o
+ $(COMPDIR)\emxbind -p -r$(GLOBJ)gspmdrv.res -d$(GLSRC)gspmdrv.def $(COMPDIR)\emxl.exe $(GLOBJ)gspmdrv $(BINDIR)\gspmdrv.exe
+ del $(GLOBJ)gspmdrv
+
+# Create a ZIP archive
+# This assumes that the current directory is named gs#.## relative to its
+# parent, where #.## is the Ghostscript version.
+
+ZIP_XE=zip
+ZIPPROGFILE1=gs$(GS_DOT_VERSION)\bin\gsos2.exe
+ZIPPROGFILE2=gs$(GS_DOT_VERSION)\bin\gsdll2.dll
+ZIPPROGFILE3=gs$(GS_DOT_VERSION)\bin\gspmdrv.exe
+ZIPPROGFILE4=gs$(GS_DOT_VERSION)\doc
+ZIPPROGFILE5=gs$(GS_DOT_VERSION)\examples
+ZIPPROGFILE6=gs$(GS_DOT_VERSION)\lib
+ZIPFONTDIR=fonts
+
+# Make the zip archive.
+zip:
+ cd ..
+ -del gs$(GS_VERSION)os2.zip
+ $(ZIP_XE) -9 -r gs$(GS_VERSION)os2.zip $(ZIPFONTDIR)
+ $(ZIP_XE) -9 -r gs$(GS_VERSION)os2.zip $(ZIPPROGFILE1)
+ $(ZIP_XE) -9 -r gs$(GS_VERSION)os2.zip $(ZIPPROGFILE2)
+ $(ZIP_XE) -9 -r gs$(GS_VERSION)os2.zip $(ZIPPROGFILE3)
+ $(ZIP_XE) -9 -r gs$(GS_VERSION)os2.zip $(ZIPPROGFILE4)
+ $(ZIP_XE) -9 -r gs$(GS_VERSION)os2.zip $(ZIPPROGFILE5)
+ $(ZIP_XE) -9 -r gs$(GS_VERSION)os2.zip $(ZIPPROGFILE6)
+ cd gs$(GS_DOT_VERSION)
diff --git a/psi/ostack.h b/psi/ostack.h
new file mode 100644
index 000000000..ec28731fe
--- /dev/null
+++ b/psi/ostack.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definitions for Ghostscript operand stack */
+
+#ifndef ostack_INCLUDED
+# define ostack_INCLUDED
+
+#include "iostack.h"
+#include "icstate.h" /* for access to op_stack */
+
+/* Define the operand stack pointers for operators. */
+#define iop_stack (i_ctx_p->op_stack)
+#define o_stack (iop_stack.stack)
+
+#define osbot (o_stack.bot)
+#define osp (o_stack.p)
+#define ostop (o_stack.top)
+
+/* Macro to ensure enough room on the operand stack */
+#define check_ostack(n)\
+ if ( ostop - osp < (n) )\
+ { o_stack.requested = (n); return_error(gs_error_stackoverflow); }
+
+/* Operand stack manipulation. */
+
+/* Note that push sets osp to (the new value of) op. */
+#define push(n)\
+ BEGIN\
+ if ( (op += (n)) > ostop )\
+ { o_stack.requested = (n); return_error(gs_error_stackoverflow); }\
+ else osp = op;\
+ END
+
+/*
+ * Note that the pop macro only decrements osp, not op. For this reason,
+ *
+ * >>> pop should only be used just before returning, <<<
+ * >>> or else op must be decremented explicitly. <<<
+ */
+#define pop(n) (osp -= (n))
+
+/*
+ * Note that the interpreter does not check for operand stack underflow
+ * before calling the operator procedure. There are "guard" entries
+ * with invalid types and attributes just below the bottom of the
+ * operand stack: if the operator returns with a typecheck error,
+ * the interpreter checks for underflow at that time.
+ * Operators that don't typecheck their arguments must check for
+ * operand stack underflow explicitly; operators that take a variable
+ * number of arguments must also check for stack underflow in those cases
+ * where they expect more than their minimum number of arguments.
+ * (This is because the interpreter can only recognize that a typecheck
+ * is really a stackunderflow when the stack has fewer than the
+ * operator's declared minimum number of entries.)
+ */
+#define check_op(nargs)\
+ if ( op < osbot + ((nargs) - 1) ) return_error(gs_error_stackunderflow)
+/*
+ * Similarly, in order to simplify some overflow checks, we allocate
+ * a few guard entries just above the top of the o-stack.
+ */
+
+/*
+ * The operand stack is implemented as a linked list of blocks:
+ * operators that can push or pop an unbounded number of values, or that
+ * access the entire o-stack, must take this into account. These are:
+ * (int)copy index roll clear count cleartomark
+ * counttomark aload astore packedarray
+ * .get/.putdeviceparams .gethardwareparams
+ */
+
+#endif /* ostack_INCLUDED */
diff --git a/psi/psi.mak b/psi/psi.mak
deleted file mode 100644
index 07912a0dc..000000000
--- a/psi/psi.mak
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
-# CA 94903, U.S.A., +1(415)492-9861, for further information.
-#
-
-# makefile for PS Interface (PSI) to Ghostscript PostScript.
-# Users of this makefile must define the following:
-# PSSRCDIR - the PS interpreter source directory
-# GLSRCDIR - the GS library source directory
-# GLGENDIR - the GS library generated file directory
-# PLSRCDIR - the PCL* support library source directory
-# PLOBJDIR - the PCL* support library object / executable directory
-# PSISRCDIR - the source directory
-# PSIGENDIR - the directory for source files generated during building
-# PSIOBJDIR - the object / executable directory
-# PSI_TOP_OBJ - object file to top-level interpreter API
-
-PLOBJ=$(PLOBJDIR)$(D)
-
-PSISRC=$(PSISRCDIR)$(D)
-PSIGEN=$(PSIGENDIR)$(D)
-PSIOBJ=$(PSIOBJDIR)$(D)
-PSIO_=$(O_)$(PSIOBJ)
-
-PSICCC=$(CC_) $(I_)$(PSISRCDIR)$(_I) $(I_)$(PSIGENDIR)$(_I) $(I_)$(PLSRCDIR)$(_I) $(I_)$(PSSRCDIR)$(_I) $(I_)$(GLSRCDIR)$(_I) $(I_)$(GLGENDIR)$(_I) $(C_)
-
-# Define the name of this makefile.
-PSI_MAK=$(PSISRC)psi.mak
-
-psi.clean: psi.config-clean psi.clean-not-config-clean
-
-psi.clean-not-config-clean: clean_gs
- $(RM_) $(PSIOBJ)*.$(OBJ)
- $(RM_) $(PSIOBJ)devs.tr6
-
-# devices are still created in the current directory. Until that
-# is fixed we will have to remove them from both directories.
-psi.config-clean:
- $(RM_) $(PSIOBJ)*.dev
- $(RM_) *.dev
-
-################ PS Language Interface ################
-
-# Top-level API
-$(PSI_TOP_OBJ): $(PSISRC)psitop.c $(AK) $(stdio__h)\
- $(string__h) $(gdebug_h) $(gp_h) $(gsdevice_h) $(gserrors_h) $(gsmemory_h)\
- $(gsstate_h) $(gsstruct_h) $(gspaint_h) $(gstypes_h) $(gxalloc_h) $(gxstate_h)\
- $(gsnogc_h) $(pltop_h) $(psitop_h) $(plparse_h) $(gsicc_manage_h)\
- $(PSIGEN)pconf.h $(plfont_h) $(uconfig_h) $(pconfig_h)
- $(PSICCC) $(PSISRC)psitop.c $(O_)$(PSI_TOP_OBJ)
-
-$(PSIOBJ)psi.dev: $(PSI_MAK) $(ECHOGS_XE) $(PLOBJ)pl.dev $(PLOBJ)pjl.dev
- $(SETMOD) $(PSIOBJ)psi $(PSI_TOP_OBJ)
- $(ADDMOD) $(PSIOBJ)psi -include $(PLOBJ)pl $(PLOBJ)pjl
-
diff --git a/psi/psitop.c b/psi/psitop.c
deleted file mode 100644
index 54e7238eb..000000000
--- a/psi/psitop.c
+++ /dev/null
@@ -1,677 +0,0 @@
-/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
- CA 94903, U.S.A., +1(415)492-9861, for further information.
-*/
-
-/* psitop.c */
-/* Top-level API implementation of PS Language Interface */
-
-#include "stdio_.h"
-#include "ghost.h"
-#include "imain.h"
-#include "imainarg.h"
-#include "iapi.h"
-#include "string_.h"
-#include "gdebug.h"
-#include "gp.h"
-#include "gserrors.h"
-#include "ierrors.h"
-#include "gstypes.h"
-#include "gsmemory.h"
-#include "gsmalloc.h"
-#include "gsstate.h" /* must precede gsdevice.h */
-#include "gxdevice.h" /* must precede gsdevice.h */
-#include "gsdevice.h"
-#include "icstate.h" /* for i_ctx_t */
-#include "iminst.h"
-#include "gsstruct.h" /* for gxalloc.h */
-#include "gspaint.h"
-#include "gxalloc.h"
-#include "gxstate.h"
-#include "plparse.h"
-#include "pltop.h"
-#include "gzstate.h"
-#include "uconfig.h" /* for UFSTFONTDIR */
-#include "gsicc_manage.h"
-
-/* Forward decls */
-
-/************************************************************/
-/******** Language wrapper implementation (see pltop.h) *****/
-/************************************************************/
-
-/* Import operator procedures */
-extern int zflush(i_ctx_t *);
-
-/*
- * PS interpreter instance: derived from pl_interp_instance_t
- */
-typedef struct ps_interp_instance_s {
- pl_interp_instance_t pl; /* common part: must be first */
- gs_memory_t *plmemory; /* memory allocator to use with pl objects */
- gs_main_instance *minst; /* PS interp main instance */
- pl_page_action_t pre_page_action; /* action before page out */
- void *pre_page_closure; /* closure to call pre_page_action with */
- pl_page_action_t post_page_action; /* action before page out */
- void *post_page_closure; /* closure to call post_page_action with */
- bool fresh_job; /* true if we are starting a new job */
- bool pdf_stream; /* current stream is pdf */
- char pdf_file_name[gp_file_name_sizeof];
- FILE * pdf_filep; /* temporary file for writing out pdf file */
- ref job_save;
-} ps_interp_instance_t;
-
-/* Get implemtation's characteristics */
-const pl_interp_characteristics_t * /* always returns a descriptor */
-ps_impl_characteristics(
- const pl_interp_implementation_t *impl /* implementation of interpereter to alloc */
-)
-{
- /* version and build date are not currently used */
-#define PSVERSION NULL
-#define PSBUILDDATE NULL
- static const pl_interp_characteristics_t ps_characteristics = {
- "POSTSCRIPT",
- /* NOTE - we don't look for %! because we want to recognize pdf as well */
- "%",
- "Artifex",
- PSVERSION,
- PSBUILDDATE,
- 1 /* minimum input size to PostScript */
- };
-#undef PSVERSION
-#undef PSBUILDDATE
-
- return &ps_characteristics;
-}
-
-/* Don't need to do anything to PS interpreter */
-static int /* ret 0 ok, else -ve error code */
-ps_impl_allocate_interp(
- pl_interp_t **interp, /* RETURNS abstract interpreter struct */
- const pl_interp_implementation_t *impl, /* implementation of interpereter to alloc */
- gs_memory_t *mem /* allocator to allocate interp from */
-)
-{
-
- static pl_interp_t pi; /* there's only one interpreter */
- /* There's only one PS interp, so return the static */
- *interp = &pi;
- return 0; /* success */
-}
-
-/* defaults for locations of font collection objects (fco's) and
- plugins the root data directory. These are internally separated with
- ':' but environment variable use the gp separator */
-#ifndef UFSTFONTDIR
- /* not using UFST */
-# define UFSTFONTDIR ""
-#endif
-
-/* Do per-instance interpreter allocation/init. No device is set yet */
-static int /* ret 0 ok, else -ve error code */
-ps_impl_allocate_interp_instance(
- pl_interp_instance_t **instance, /* RETURNS instance struct */
- pl_interp_t *interp, /* dummy interpreter */
- gs_memory_t *mem /* allocator to allocate instance from */
-)
-{
-#ifdef DEBUG_WITH_EXPERIMENTAL_GSOPTIONS_FILE
-# define MAX_ARGS 40
-#else
-# define MAX_ARGS /* unspecified */
-#endif
- int code = 0, exit_code;
- const char *argv[MAX_ARGS] = {
- "",
- "-dNOPAUSE",
-#ifndef DEBUG
- "-dQUIET",
-#else
- "-dOSTACKPRINT", // NB: debuggging postscript Needs to be removed.
- "-dESTACKPRINT", // NB: debuggging postscript Needs to be removed.
-#endif
-#if UFST_BRIDGE==1
- "-dJOBSERVER",
- "-sUFST_PlugIn=" UFSTFONTDIR "mtfonts/pcl45/mt3/plug__xi.fco",
- "-sFCOfontfile=" UFSTFONTDIR "mtfonts/pclps2/mt3/pclp2_xj.fco",
- "-sFCOfontfile2=" UFSTFONTDIR "mtfonts/pcl45/mt3/wd____xh.fco",
- "-sFAPIfontmap=FCOfontmap-PCLPS2",
- "-sFAPIconfig=FAPIconfig-FCO",
-#endif
- 0
- };
-#ifndef DEBUG
- int argc = 9;
-#else
- int argc = 10;
-#endif
-#ifdef DEBUG_WITH_EXPERIMENTAL_GSOPTIONS_FILE
- char argbuf[1024];
-#endif
-# undef MAX_ARGS
- ps_interp_instance_t *psi /****** SHOULD HAVE A STRUCT DESCRIPTOR ******/
- = (ps_interp_instance_t *)
- gs_alloc_bytes( mem,
- sizeof(ps_interp_instance_t),
- "ps_allocate_interp_instance(ps_interp_instance_t)"
- );
-
-#if UFST_BRIDGE!=1
- argc -= 6;
-#endif
-
- /* If allocation error, deallocate & return */
- if (!psi) {
- return gs_error_VMerror;
- }
- /* Initialize for pl_main_universe_dnit/pl_deallocate_interp_instance
- in case of gs_main_init_with_args returns with error code. */
- psi->pl.interp = interp;
- /* Setup pointer to mem used by PostScript */
- psi->plmemory = mem;
- psi->minst = gs_main_alloc_instance(mem->non_gc_memory);
-
-#ifdef DEBUG_WITH_EXPERIMENTAL_GSOPTIONS_FILE
- { /* Fetch more GS arguments (debug purposes only).
- Pulling debugging arguments from a file allows easy additions
- of postscript arguments to a debug system, it is not recommended for
- production systems since some options will conflict with commandline
- arguments in unpleasant ways.
- */
- FILE *f = fopen("gsoptions", "rb"); /* Sorry we handle
- the current directory only.
- Assuming it always fails with no crash
- in a real embedded system. */
-
- if (f != NULL) {
- int i;
- int l = fread(argbuf, 1, sizeof(argbuf) - 1, f);
-
- if (l >= sizeof(argbuf) - 1)
- errprintf("The gsoptions file is too big. Truncated to the buffer length %d.\n", l - 1);
- if (l > 0) {
- argbuf[l] = 0;
- if (argbuf[0] && argbuf[0] != '\r' && argbuf[0] != '\n') /* Silently skip empty lines. */
- argv[argc++] = argbuf;
- for (i = 0; i < l; i++)
- if (argbuf[i] == '\r' || argbuf[i] == '\n') {
- argbuf[i] = 0;
- if (argbuf[i + 1] == 0 || argbuf[i + 1] == '\r' || argbuf[i + 1] == '\n')
- continue; /* Silently skip empty lines. */
- if (argc >= count_of(argv)) {
- errprintf("The gsoptions file contains too many options. "
- "Truncated to the buffer length %d.\n", argc);
- break;
- }
- argv[argc++] = argbuf + i + 1;
- }
- }
- fclose(f);
- }
- }
-#endif
-
- *instance = (pl_interp_instance_t *)psi;
- code = gs_main_init_with_args(psi->minst, argc, (char**)argv);
- if (code<0)
- return code;
-
- /* General init of PS interp instance */
-
- if ((code = gs_main_run_string_begin(psi->minst, 0, &exit_code, &psi->minst->error_object)) < 0)
- return exit_code;
-
- {
- gs_state *pgs = psi->minst->i_ctx_p->pgs;
- gsicc_init_iccmanager(pgs);
- }
- /* inialize fresh job to false so that we can check for a pdf
- file next job. */
- psi->fresh_job = true;
- /* default is a postscript stream */
- psi->pdf_stream = false;
-
- /* Return success */
- return 0;
-}
-
-/* NB this pointer should be placed in the ps instance */
-
-/* Set a client language into an interpreter instance */
-static int /* ret 0 ok, else -ve error code */
-ps_impl_set_client_instance(
- pl_interp_instance_t *instance, /* interp instance to use */
- pl_interp_instance_t *client, /* client to set */
- pl_interp_instance_clients_t which_client
-)
-{
- return 0;
-}
-
-/* Set an interpreter instance's pre-page action */
-static int /* ret 0 ok, else -ve err */
-ps_impl_set_pre_page_action(
- pl_interp_instance_t *instance, /* interp instance to use */
- pl_page_action_t action, /* action to execute (rets 1 to abort w/o err) */
- void *closure /* closure to call action with */
-)
-{
- ps_interp_instance_t *psi = (ps_interp_instance_t *)instance;
- psi->pre_page_action = action;
- psi->pre_page_closure = closure;
- return 0;
-}
-
-/* Set an interpreter instance's post-page action */
-static int /* ret 0 ok, else -ve err */
-ps_impl_set_post_page_action(
- pl_interp_instance_t *instance, /* interp instance to use */
- pl_page_action_t action, /* action to execute */
- void *closure /* closure to call action with */
-)
-{
- ps_interp_instance_t *psi = (ps_interp_instance_t *)instance;
- psi->post_page_action = action;
- psi->post_page_closure = closure;
- return 0;
-}
-
-/* Set a device into an interpreter instance */
-static int /* ret 0 ok, else -ve error code */
-ps_impl_set_device(
- pl_interp_instance_t *instance, /* interp instance to use */
- gx_device *device /* device to set (open or closed) */
-)
-{
- int code = 0;
- int exit_code = 0;
- ps_interp_instance_t *psi = (ps_interp_instance_t *)instance;
- gs_state *pgs = psi->minst->i_ctx_p->pgs;
-
- /* Initialize device ICC profile */
- code = gsicc_init_device_profile_struct(device, NULL, 0);
- if (code < 0)
- return code;
-
- /* Set the device into the gstate */
- code = gs_setdevice_no_erase(pgs, device);
- if (code < 0)
- return code;
-
- /* install a screen appropriate for the device */
- {
- const char *screen_str = ".setdefaultscreen\n";
- code = gsapi_run_string_continue(psi->plmemory->gs_lib_ctx,
- screen_str, strlen(screen_str),
- 0, &exit_code);
- /* needs more input this is not an error */
- if ( code == gs_error_NeedInput )
- code = 0;
-
- if (code < 0)
- return code;
- }
-
- /* this shouldn't fail */
- pgs = psi->minst->i_ctx_p->pgs; /* stuff moves if a GC happens during run_string */
- code = gs_erasepage(pgs);
- if (code < 0)
- return code;
-
- return exit_code;
-}
-
-/* fetch the gs_memory_t ptr so that the device and ps use the same
- * garbage collection aware a memory
- */
-static int
-ps_impl_get_device_memory(
- pl_interp_instance_t *instance, /* interp instance to use */
- gs_memory_t **pmem)
-{
- ps_interp_instance_t *psi = (ps_interp_instance_t *)instance;
- gs_dual_memory_t *dmem = &psi->minst->i_ctx_p->memory;
- gs_ref_memory_t *mem = dmem->spaces.memories.named.global;
-
- *pmem = mem->stable_memory;
- /* Lock against alloc_restore_all to release the device when called from gsapi_exit : */
- mem->num_contexts++;
- return 0;
-}
-
-gs_main_instance *ps_impl_get_minst( const gs_memory_t *mem )
-{
- ps_interp_instance_t *psi = (ps_interp_instance_t *)get_interpreter_from_memory(mem);
- return psi->minst;
-}
-
-/* Prepare interp instance for the next "job" */
-static int /* ret 0 ok, else -ve error code */
-ps_impl_init_job(
- pl_interp_instance_t *instance /* interp instance to start job in */
-)
-{
- ps_interp_instance_t *psi = (ps_interp_instance_t *)instance;
- static const char *buf = "\004"; /* use ^D to start a new encapsulated job */
- int exit_code;
-
- /* starting a new job */
- psi->fresh_job = true;
- gsapi_run_string_continue(psi->plmemory->gs_lib_ctx, buf, strlen(buf), 0, &exit_code); /* ^D */
- return 0;
-}
-
-/* Parse a buffer full of data */
-static int /* ret 0 or +ve if ok, else -ve error code */
-ps_impl_process(
- pl_interp_instance_t *instance, /* interp instance to process data job in */
- stream_cursor_read *cursor /* data to process */
-)
-{
- ps_interp_instance_t *psi = (ps_interp_instance_t *)instance;
- int code, exit_code;
- uint avail = cursor->limit - cursor->ptr;
- /* if we are at the beginning of a job check for pdf and set
- appropriate state variables to process either a pdf or ps
- job */
- if ( psi->fresh_job ) {
- const char pdf_idstr[] = "%PDF-1.";
- /* do we have enough data? */
- const uint pdf_idstr_len = strlen(pdf_idstr);
- if ( avail < pdf_idstr_len )
- /* more data. NB update ptr ?? */
- return 0;
- else
- /* compare beginning of stream with pdf id */
- if ( !strncmp(pdf_idstr, (const char *)cursor->ptr + 1, pdf_idstr_len) ) {
- char fmode[4];
- /* open the temporary pdf file. If the file open
- fails PDF fails and we allow the job to be sent
- to postscript and generate an error. It turns
- out this is easier than restoring the state and
- returning */
- strcpy(fmode, "w+");
- strcat(fmode, gp_fmode_binary_suffix);
- psi->pdf_filep = gp_open_scratch_file(psi->plmemory,
- gp_scratch_file_name_prefix,
- psi->pdf_file_name,
- fmode);
- if ( psi->pdf_filep == NULL )
- psi->pdf_stream = false;
- else
- psi->pdf_stream = true;
- }
- else
- psi->pdf_stream = false;
- /* we only check for pdf at the beginning of the job */
- psi->fresh_job = false;
- }
-
- /* for a pdf stream we append to the open pdf file but for
- postscript we hand it directly to the ps interpreter. PDF
- files are processed subsequently, at end job time */
- code = 0;
- if ( psi->pdf_stream ) {
- uint bytes_written = fwrite((cursor->ptr + 1), 1, avail, psi->pdf_filep);
- if ( bytes_written != avail )
- code = gs_error_invalidfileaccess;
- } else {
- /* Send the buffer to Ghostscript */
- code = gsapi_run_string_continue(psi->plmemory->gs_lib_ctx, (const char *)(cursor->ptr + 1),
- avail, 0, &exit_code);
- /* needs more input this is not an error */
- if ( code == gs_error_NeedInput )
- code = 0;
- /* error - I guess it gets "exit code" - nonsense */
- if ( code < 0 )
- code = exit_code;
- }
- /* update the cursor */
- cursor->ptr += avail;
- /* flush stdout on error. */
- if (code < 0)
- zflush(psi->minst->i_ctx_p);
- /* return the exit code */
- return code;
-}
-
-/* Skip to end of job ret 1 if done, 0 ok but EOJ not found, else -ve error code */
-static int
-ps_impl_flush_to_eoj(
- pl_interp_instance_t *instance, /* interp instance to flush for */
- stream_cursor_read *cursor /* data to process */
-)
-{
- const byte *p = cursor->ptr;
- const byte *rlimit = cursor->limit;
-
- /* Skip to, but leave UEL in buffer for PJL to find later */
- for (; p < rlimit; ++p)
- if (p[1] == '\033') {
- uint avail = rlimit - p;
-
- if (memcmp(p + 1, "\033%-12345X", min(avail, 9)))
- continue;
- if (avail < 9)
- break;
- cursor->ptr = p;
- return 1; /* found eoj */
- }
- cursor->ptr = p;
- return 0; /* need more */
-}
-
-/* Parser action for end-of-file */
-static int /* ret 0 or +ve if ok, else -ve error code */
-ps_impl_process_eof(
- pl_interp_instance_t *instance /* interp instance to process data job in */
-)
-{
- int code = 0;
-
- return code;
-}
-
-/* Report any errors after running a job */
-static int /* ret 0 ok, else -ve error code */
-ps_impl_report_errors(pl_interp_instance_t *instance, /* interp instance to wrap up job in */
- int code, /* prev termination status */
- long file_position, /* file position of error, -1 if unknown */
- bool force_to_cout /* force errors to cout */
-)
-{
- /* ps_interp_instance_t *psi = (ps_interp_instance_t *)instance;
- */
- return code;
-}
-
-/* Wrap up interp instance after a "job" */
-static int /* ret 0 ok, else -ve error code */
-ps_impl_dnit_job(
- pl_interp_instance_t *instance /* interp instance to wrap up job in */
-)
-{
- int code = 0;
- int exit_code = 0;
- static const char *buf = "\n.endjob\n"; /* restore to initial state, non-encapsualted */
- ps_interp_instance_t *psi = (ps_interp_instance_t *)instance;
-
- /* take care of a stored pdf file */
- if ( psi->pdf_stream ) {
-
- /*
- * Hex encode to avoid problems with window's directory
- * separators '\' being interpreted in postscript as escape
- * sequences. The buffer length is the maximum file name size
- * (gp_file_name_sizeof) * 2 (hex encoding) + 2 (hex
- * delimiters '<' and '>') + the rest of the run command 7
- * (space + (run) + new line + null).
- */
- char buf[gp_file_name_sizeof * 2 + 2 + 7];
- const char *run_str = " run\n";
- const char *hex_digits = "0123456789ABCDEF";
- char *pd = buf; /* destination */
- const char *ps = psi->pdf_file_name; /* source */
-
- *pd = '<'; pd++;
- while (*ps) {
- *pd = hex_digits[*ps >> 4 ]; pd++;
- *pd = hex_digits[*ps & 0xf]; pd++;
- ps++;
- }
- *pd = '>'; pd++;
- strcpy(pd, run_str);
-
- /* at this point we have finished writing the spooled pdf file
- and we need to close it */
- fclose(psi->pdf_filep);
-
- /* Send the buffer to Ghostscript */
- code = gsapi_run_string_continue(psi->plmemory->gs_lib_ctx, buf, strlen(buf), 0, &exit_code);
-
- /* indicate we are done with the pdf stream */
- psi->pdf_stream = false;
- unlink(psi->pdf_file_name);
- /* handle errors... normally job deinit failures are
- considered fatal but pdf runs the spooled job when the job
- is deinitialized so handle error processing here and return code is always 0. */
- if (( code < 0) && (code != gs_error_NeedInput)) {
- errprintf(psi->plmemory, "PDF interpreter exited with exit code %d\n", exit_code);
- errprintf(psi->plmemory, "Flushing to EOJ\n");
- }
- code = 0;
- }
-
- /* We use string_end to send an EOF in case the job was reading in a loop */
- gsapi_run_string_end(psi->plmemory->gs_lib_ctx, 0, &exit_code); /* sends EOF to PS process */
- gsapi_run_string_begin(psi->plmemory->gs_lib_ctx, 0, &exit_code); /* prepare to send .endjob */
- gsapi_run_string_continue(psi->plmemory->gs_lib_ctx, buf, strlen(buf), 0, &exit_code); /* .endjob */
- /* Note the above will restore to the server save level and will not be encapsulated */
-
- /* Flush stdout. */
- zflush(psi->minst->i_ctx_p);
-
- return 0;
-}
-
-/* Remove a device from an interpreter instance */
-static int /* ret 0 ok, else -ve error code */
-ps_impl_remove_device(
- pl_interp_instance_t *instance /* interp instance to use */
-)
-{
- /* Assuming the interpreter's stack contains a single graphic state.
- Otherwise this procedure is not effective.
- The Postscript job server logic must provide that.
- */
- ps_interp_instance_t *psi = (ps_interp_instance_t *)instance;
- gs_state *pgs = psi->minst->i_ctx_p->pgs;
- int code = gs_nulldevice(pgs);
-
- if ( code < 0 )
- dprintf1("error code %d installing nulldevice, continuing\n", code );
- return 0;
-}
-
-/* Deallocate a interpreter instance */
-static int /* ret 0 ok, else -ve error code */
-ps_impl_deallocate_interp_instance(
- pl_interp_instance_t *instance /* instance to dealloc */
-)
-{
- int code = 0, exit_code;
- ps_interp_instance_t *psi = (ps_interp_instance_t *)instance;
- gs_memory_t *mem = psi->plmemory;
-
- /* do total dnit of interp state */
- code = gsapi_run_string_end(mem->gs_lib_ctx, 0, &exit_code);
-
- gsapi_exit(psi->minst);
-
- gs_free_object(mem, psi, "ps_impl_deallocate_interp_instance(ps_interp_instance_t)");
-
- return (code < 0) ? exit_code : 0;
-}
-
-/* Do static deinit of PS interpreter */
-static int /* ret 0 ok, else -ve error code */
-ps_impl_deallocate_interp(
- pl_interp_t *interp /* interpreter to deallocate */
-)
-{
- /* nothing to do */
- return 0;
-}
-
-/*
- * End-of-page called back by PS
- */
-int
-ps_end_page_top(const gs_memory_t *mem, int num_copies, bool flush)
-{
- pl_interp_instance_t *instance = get_interpreter_from_memory(mem);
- ps_interp_instance_t *psi = (ps_interp_instance_t *)instance;
- int code = 0;
-
- if (psi == 0)
- return 0;
-
- /* do pre-page action */
- if (psi->pre_page_action) {
- code = psi->pre_page_action(instance, psi->pre_page_closure);
- if (code < 0)
- return code;
- if (code != 0)
- return 0; /* code > 0 means abort w/no error */
- }
-
- /* output the page */
- code = gs_output_page(psi->minst->i_ctx_p->pgs, num_copies, flush);
- if (code < 0)
- return code;
-
- /* Flush stdout. */
- zflush(psi->minst->i_ctx_p);
-
- /* do post-page action */
- if (psi->post_page_action) {
- code = psi->post_page_action(instance, psi->post_page_closure);
- if (code < 0)
- return code;
- }
-
- return 0;
-}
-
-/* Parser implementation descriptor */
-const pl_interp_implementation_t ps_implementation = {
- ps_impl_characteristics,
- ps_impl_allocate_interp,
- ps_impl_allocate_interp_instance,
- ps_impl_set_client_instance,
- ps_impl_set_pre_page_action,
- ps_impl_set_post_page_action,
- ps_impl_set_device,
- ps_impl_init_job,
- NULL, /* process_file */
- ps_impl_process,
- ps_impl_flush_to_eoj,
- ps_impl_process_eof,
- ps_impl_report_errors,
- ps_impl_dnit_job,
- ps_impl_remove_device,
- ps_impl_deallocate_interp_instance,
- ps_impl_deallocate_interp,
- ps_impl_get_device_memory,
-};
diff --git a/psi/psromfs.mak b/psi/psromfs.mak
new file mode 100644
index 000000000..4188d4ea4
--- /dev/null
+++ b/psi/psromfs.mak
@@ -0,0 +1,438 @@
+# Copyright (C) 2001-2006 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 that
+# license. Refer to licensing information at http://www.artifex.com/
+# or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+# San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
+#
+# mkromfs macros for PostScript %rom% when COMPILE_INITS=1
+
+# The following list of files needed by the interpreter is maintained here.
+# This changes infrequently, but is a potential point of bitrot, but since
+# unix-inst.mak uses this macro, problems should surface when testing installed
+# versions.
+
+# Resource files go into Resource/...
+# The init files are in the %rom%Resource/Init/ directory
+# Any EXTRA_INIT_FILES go into %rom%lib/
+
+PDF_RESOURCE_LIST=CMap$(D)*
+
+MISC_INIT_FILES=FCOfontmap-PCLPS2 -C cidfmap \
+ FAPIcidfmap FAPIconfig FAPIfontmap Fontmap Fontmap.GS xlatmap \
+ gs_cet.ps gs_diskf.ps gs_diskn.ps gs_dscp.ps gs_trap.ps \
+ pdf_cslayer.ps -B
+
+# In the below list, the Font contents are _not_ compressed since it doesn't help.
+RESOURCE_LIST=SubstCID$(D)* CIDFSubst$(D)* CIDFont$(D)* -C $(PDF_RESOURCE_LIST) ColorSpace$(D)* Decoding$(D)* Encoding$(D)* -B -b Font$(D)* -c -C IdiomSet$(D)* ProcSet$(D)* -P $(PSRESDIR)$(D)Init$(D) -d Resource/Init/ -B $(MISC_INIT_FILES)
+
+# Notes: gs_cet.ps is only needed to match Adobe CPSI defaults
+PS_ROMFS_ARGS=-c \
+ -d Resource/Init/ -P $(PSRESDIR)$(D)Init$(D) -g gs_init.ps $(iconfig_h) \
+ -d Resource/ -P $(PSRESDIR)$(D) $(RESOURCE_LIST) \
+ -d lib/ -P $(PSLIBDIR)$(D) $(EXTRA_INIT_FILES)
+
+# If you add a file remember to add it here. If you forget then builds from
+# clean will work (as all files in the directory are included), but rebuilds
+# after changes to unlisted files will not cause the romfs to be regenerated.
+
+# A list of all of the files in Resource/CIDFont dependencies for COMPILE_INITS=1
+PS_CIDFONT_DEPS=
+
+# A list of all of the files in Resource/CMap dependencies for COMPILE_INITS=1
+PS_CMAP_DEPS=\
+ $(PSRESDIR)$(D)CMap$(D)78-EUC-H \
+ $(PSRESDIR)$(D)CMap$(D)78-EUC-V \
+ $(PSRESDIR)$(D)CMap$(D)78-H \
+ $(PSRESDIR)$(D)CMap$(D)78-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)78-RKSJ-V \
+ $(PSRESDIR)$(D)CMap$(D)78-V \
+ $(PSRESDIR)$(D)CMap$(D)78ms-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)78ms-RKSJ-V \
+ $(PSRESDIR)$(D)CMap$(D)83pv-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)90ms-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)90ms-RKSJ-UCS2 \
+ $(PSRESDIR)$(D)CMap$(D)90ms-RKSJ-V \
+ $(PSRESDIR)$(D)CMap$(D)90msp-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)90msp-RKSJ-V \
+ $(PSRESDIR)$(D)CMap$(D)90pv-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)90pv-RKSJ-UCS2 \
+ $(PSRESDIR)$(D)CMap$(D)90pv-RKSJ-UCS2C \
+ $(PSRESDIR)$(D)CMap$(D)90pv-RKSJ-V \
+ $(PSRESDIR)$(D)CMap$(D)Add-H \
+ $(PSRESDIR)$(D)CMap$(D)Add-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)Add-RKSJ-V \
+ $(PSRESDIR)$(D)CMap$(D)Add-V \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-0 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-1 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-2 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-3 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-4 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-5 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-6 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-B5pc \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-ETenms-B5 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-H-CID \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-H-Host \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-H-Mac \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-CNS1-UCS2 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-GB1-0 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-GB1-1 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-GB1-2 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-GB1-3 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-GB1-4 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-GB1-5 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-GB1-GBK-EUC \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-GB1-GBpc-EUC \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-GB1-H-CID \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-GB1-H-Host \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-GB1-H-Mac \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-GB1-UCS2 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-0 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-1 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-2 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-3 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-4 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-5 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-6 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-90ms-RKSJ \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-90pv-RKSJ \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-H-CID \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-H-Host \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-H-Mac \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-PS-H \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-PS-V \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan1-UCS2 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Japan2-0 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Korea1-0 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Korea1-1 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Korea1-2 \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Korea1-H-CID \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Korea1-H-Host \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Korea1-H-Mac \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Korea1-KSCms-UHC \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Korea1-KSCpc-EUC \
+ $(PSRESDIR)$(D)CMap$(D)Adobe-Korea1-UCS2 \
+ $(PSRESDIR)$(D)CMap$(D)B5-H \
+ $(PSRESDIR)$(D)CMap$(D)B5-V \
+ $(PSRESDIR)$(D)CMap$(D)B5pc-H \
+ $(PSRESDIR)$(D)CMap$(D)B5pc-UCS2 \
+ $(PSRESDIR)$(D)CMap$(D)B5pc-UCS2C \
+ $(PSRESDIR)$(D)CMap$(D)B5pc-V \
+ $(PSRESDIR)$(D)CMap$(D)CNS-EUC-H \
+ $(PSRESDIR)$(D)CMap$(D)CNS-EUC-V \
+ $(PSRESDIR)$(D)CMap$(D)CNS01-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)CNS02-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)CNS03-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)CNS04-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)CNS05-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)CNS06-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)CNS07-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)CNS1-H \
+ $(PSRESDIR)$(D)CMap$(D)CNS1-V \
+ $(PSRESDIR)$(D)CMap$(D)CNS15-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)CNS2-H \
+ $(PSRESDIR)$(D)CMap$(D)CNS2-V \
+ $(PSRESDIR)$(D)CMap$(D)ETHK-B5-H \
+ $(PSRESDIR)$(D)CMap$(D)ETHK-B5-V \
+ $(PSRESDIR)$(D)CMap$(D)ETen-B5-H \
+ $(PSRESDIR)$(D)CMap$(D)ETen-B5-UCS2 \
+ $(PSRESDIR)$(D)CMap$(D)ETen-B5-V \
+ $(PSRESDIR)$(D)CMap$(D)ETenms-B5-H \
+ $(PSRESDIR)$(D)CMap$(D)ETenms-B5-V \
+ $(PSRESDIR)$(D)CMap$(D)EUC-H \
+ $(PSRESDIR)$(D)CMap$(D)EUC-V \
+ $(PSRESDIR)$(D)CMap$(D)Ext-H \
+ $(PSRESDIR)$(D)CMap$(D)Ext-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)Ext-RKSJ-V \
+ $(PSRESDIR)$(D)CMap$(D)Ext-V \
+ $(PSRESDIR)$(D)CMap$(D)GB-EUC-H \
+ $(PSRESDIR)$(D)CMap$(D)GB-EUC-V \
+ $(PSRESDIR)$(D)CMap$(D)GB-H \
+ $(PSRESDIR)$(D)CMap$(D)GB-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)GB-V \
+ $(PSRESDIR)$(D)CMap$(D)GBK-EUC-H \
+ $(PSRESDIR)$(D)CMap$(D)GBK-EUC-UCS2 \
+ $(PSRESDIR)$(D)CMap$(D)GBK-EUC-V \
+ $(PSRESDIR)$(D)CMap$(D)GBK2K-H \
+ $(PSRESDIR)$(D)CMap$(D)GBK2K-V \
+ $(PSRESDIR)$(D)CMap$(D)GBKp-EUC-H \
+ $(PSRESDIR)$(D)CMap$(D)GBKp-EUC-V \
+ $(PSRESDIR)$(D)CMap$(D)GBT-EUC-H \
+ $(PSRESDIR)$(D)CMap$(D)GBT-EUC-V \
+ $(PSRESDIR)$(D)CMap$(D)GBT-H \
+ $(PSRESDIR)$(D)CMap$(D)GBT-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)GBT-V \
+ $(PSRESDIR)$(D)CMap$(D)GBTpc-EUC-H \
+ $(PSRESDIR)$(D)CMap$(D)GBTpc-EUC-V \
+ $(PSRESDIR)$(D)CMap$(D)GBpc-EUC-H \
+ $(PSRESDIR)$(D)CMap$(D)GBpc-EUC-UCS2 \
+ $(PSRESDIR)$(D)CMap$(D)GBpc-EUC-UCS2C \
+ $(PSRESDIR)$(D)CMap$(D)GBpc-EUC-V \
+ $(PSRESDIR)$(D)CMap$(D)H \
+ $(PSRESDIR)$(D)CMap$(D)HK-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)HKdla-B5-H \
+ $(PSRESDIR)$(D)CMap$(D)HKdla-B5-V \
+ $(PSRESDIR)$(D)CMap$(D)HKdlb-B5-H \
+ $(PSRESDIR)$(D)CMap$(D)HKdlb-B5-V \
+ $(PSRESDIR)$(D)CMap$(D)HKgccs-B5-H \
+ $(PSRESDIR)$(D)CMap$(D)HKgccs-B5-V \
+ $(PSRESDIR)$(D)CMap$(D)HKm314-B5-H \
+ $(PSRESDIR)$(D)CMap$(D)HKm314-B5-V \
+ $(PSRESDIR)$(D)CMap$(D)HKm471-B5-H \
+ $(PSRESDIR)$(D)CMap$(D)HKm471-B5-V \
+ $(PSRESDIR)$(D)CMap$(D)HKscs-B5-H \
+ $(PSRESDIR)$(D)CMap$(D)HKscs-B5-V \
+ $(PSRESDIR)$(D)CMap$(D)Hankaku \
+ $(PSRESDIR)$(D)CMap$(D)Hiragana \
+ $(PSRESDIR)$(D)CMap$(D)Hojo-EUC-H \
+ $(PSRESDIR)$(D)CMap$(D)Hojo-EUC-V \
+ $(PSRESDIR)$(D)CMap$(D)Hojo-H \
+ $(PSRESDIR)$(D)CMap$(D)Hojo-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)Hojo-V \
+ $(PSRESDIR)$(D)CMap$(D)Identity-H \
+ $(PSRESDIR)$(D)CMap$(D)Identity-UTF16-H \
+ $(PSRESDIR)$(D)CMap$(D)Identity-UTF16-V \
+ $(PSRESDIR)$(D)CMap$(D)Identity-V \
+ $(PSRESDIR)$(D)CMap$(D)KSC-EUC-H \
+ $(PSRESDIR)$(D)CMap$(D)KSC-EUC-V \
+ $(PSRESDIR)$(D)CMap$(D)KSC-H \
+ $(PSRESDIR)$(D)CMap$(D)KSC-Johab-H \
+ $(PSRESDIR)$(D)CMap$(D)KSC-Johab-V \
+ $(PSRESDIR)$(D)CMap$(D)KSC-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)KSC-V \
+ $(PSRESDIR)$(D)CMap$(D)KSC2-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)KSCms-UHC-H \
+ $(PSRESDIR)$(D)CMap$(D)KSCms-UHC-HW-H \
+ $(PSRESDIR)$(D)CMap$(D)KSCms-UHC-HW-V \
+ $(PSRESDIR)$(D)CMap$(D)KSCms-UHC-UCS2 \
+ $(PSRESDIR)$(D)CMap$(D)KSCms-UHC-V \
+ $(PSRESDIR)$(D)CMap$(D)KSCpc-EUC-H \
+ $(PSRESDIR)$(D)CMap$(D)KSCpc-EUC-UCS2 \
+ $(PSRESDIR)$(D)CMap$(D)KSCpc-EUC-UCS2C \
+ $(PSRESDIR)$(D)CMap$(D)KSCpc-EUC-V \
+ $(PSRESDIR)$(D)CMap$(D)Katakana \
+ $(PSRESDIR)$(D)CMap$(D)NWP-H \
+ $(PSRESDIR)$(D)CMap$(D)NWP-V \
+ $(PSRESDIR)$(D)CMap$(D)RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)RKSJ-V \
+ $(PSRESDIR)$(D)CMap$(D)Roman \
+ $(PSRESDIR)$(D)CMap$(D)TCVN-RKSJ-H \
+ $(PSRESDIR)$(D)CMap$(D)UCS2-90ms-RKSJ \
+ $(PSRESDIR)$(D)CMap$(D)UCS2-90pv-RKSJ \
+ $(PSRESDIR)$(D)CMap$(D)UCS2-B5pc \
+ $(PSRESDIR)$(D)CMap$(D)UCS2-ETen-B5 \
+ $(PSRESDIR)$(D)CMap$(D)UCS2-GBK-EUC \
+ $(PSRESDIR)$(D)CMap$(D)UCS2-GBpc-EUC \
+ $(PSRESDIR)$(D)CMap$(D)UCS2-KSCms-UHC \
+ $(PSRESDIR)$(D)CMap$(D)UCS2-KSCpc-EUC \
+ $(PSRESDIR)$(D)CMap$(D)UniCNS-UCS2-H \
+ $(PSRESDIR)$(D)CMap$(D)UniCNS-UCS2-V \
+ $(PSRESDIR)$(D)CMap$(D)UniCNS-UTF16-H \
+ $(PSRESDIR)$(D)CMap$(D)UniCNS-UTF16-V \
+ $(PSRESDIR)$(D)CMap$(D)UniCNS-UTF32-H \
+ $(PSRESDIR)$(D)CMap$(D)UniCNS-UTF32-V \
+ $(PSRESDIR)$(D)CMap$(D)UniCNS-UTF8-H \
+ $(PSRESDIR)$(D)CMap$(D)UniCNS-UTF8-V \
+ $(PSRESDIR)$(D)CMap$(D)UniGB-UCS2-H \
+ $(PSRESDIR)$(D)CMap$(D)UniGB-UCS2-V \
+ $(PSRESDIR)$(D)CMap$(D)UniGB-UTF16-H \
+ $(PSRESDIR)$(D)CMap$(D)UniGB-UTF16-V \
+ $(PSRESDIR)$(D)CMap$(D)UniGB-UTF32-H \
+ $(PSRESDIR)$(D)CMap$(D)UniGB-UTF32-V \
+ $(PSRESDIR)$(D)CMap$(D)UniGB-UTF8-H \
+ $(PSRESDIR)$(D)CMap$(D)UniGB-UTF8-V \
+ $(PSRESDIR)$(D)CMap$(D)UniHojo-UCS2-H \
+ $(PSRESDIR)$(D)CMap$(D)UniHojo-UCS2-V \
+ $(PSRESDIR)$(D)CMap$(D)UniHojo-UTF16-H \
+ $(PSRESDIR)$(D)CMap$(D)UniHojo-UTF16-V \
+ $(PSRESDIR)$(D)CMap$(D)UniHojo-UTF32-H \
+ $(PSRESDIR)$(D)CMap$(D)UniHojo-UTF32-V \
+ $(PSRESDIR)$(D)CMap$(D)UniHojo-UTF8-H \
+ $(PSRESDIR)$(D)CMap$(D)UniHojo-UTF8-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS-UCS2-H \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS-UCS2-HW-H \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS-UCS2-HW-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS-UCS2-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS-UTF16-H \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS-UTF16-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS-UTF32-H \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS-UTF32-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS-UTF8-H \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS-UTF8-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS2004-UTF16-H \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS2004-UTF16-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS2004-UTF32-H \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS2004-UTF32-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS2004-UTF8-H \
+ $(PSRESDIR)$(D)CMap$(D)UniJIS2004-UTF8-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJISPro-UCS2-HW-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJISPro-UCS2-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJISPro-UTF8-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJISX0213-UTF32-H \
+ $(PSRESDIR)$(D)CMap$(D)UniJISX0213-UTF32-V \
+ $(PSRESDIR)$(D)CMap$(D)UniJISX02132004-UTF32-H \
+ $(PSRESDIR)$(D)CMap$(D)UniJISX02132004-UTF32-V \
+ $(PSRESDIR)$(D)CMap$(D)UniKS-UCS2-H \
+ $(PSRESDIR)$(D)CMap$(D)UniKS-UCS2-V \
+ $(PSRESDIR)$(D)CMap$(D)UniKS-UTF16-H \
+ $(PSRESDIR)$(D)CMap$(D)UniKS-UTF16-V \
+ $(PSRESDIR)$(D)CMap$(D)UniKS-UTF32-H \
+ $(PSRESDIR)$(D)CMap$(D)UniKS-UTF32-V \
+ $(PSRESDIR)$(D)CMap$(D)UniKS-UTF8-H \
+ $(PSRESDIR)$(D)CMap$(D)UniKS-UTF8-V \
+ $(PSRESDIR)$(D)CMap$(D)V \
+ $(PSRESDIR)$(D)CMap$(D)WP-Symbol
+
+# A list of all of the files in Resource/ColorSpace dependencies for COMPILE_INITS=1
+PS_COLORSPACE_DEPS=\
+ $(PSRESDIR)$(D)ColorSpace$(D)DefaultCMYK \
+ $(PSRESDIR)$(D)ColorSpace$(D)DefaultGray \
+ $(PSRESDIR)$(D)ColorSpace$(D)DefaultRGB \
+ $(PSRESDIR)$(D)ColorSpace$(D)TrivialCMYK \
+ $(PSRESDIR)$(D)ColorSpace$(D)sGray \
+ $(PSRESDIR)$(D)ColorSpace$(D)sRGB
+
+# A list of all of the files in Resource/Decoding
+PS_DECODING_DEPS=\
+ $(PSRESDIR)$(D)Decoding$(D)FCO_Dingbats \
+ $(PSRESDIR)$(D)Decoding$(D)FCO_Symbol \
+ $(PSRESDIR)$(D)Decoding$(D)FCO_Unicode \
+ $(PSRESDIR)$(D)Decoding$(D)FCO_Wingdings \
+ $(PSRESDIR)$(D)Decoding$(D)Latin1 \
+ $(PSRESDIR)$(D)Decoding$(D)StandardEncoding \
+ $(PSRESDIR)$(D)Decoding$(D)Unicode
+
+# A list of all of the files in Resource/Encoding
+PS_ENCODING_DEPS=\
+ $(PSRESDIR)$(D)Encoding$(D)Wingdings
+
+# A list of all of the files in Resource/Font
+PS_FONT_DEPS=\
+ $(PSRESDIR)$(D)Font$(D)BookmanURW-DemBol \
+ $(PSRESDIR)$(D)Font$(D)BookmanURW-DemBolIta \
+ $(PSRESDIR)$(D)Font$(D)BookmanURW-Lig \
+ $(PSRESDIR)$(D)Font$(D)BookmanURW-LigIta \
+ $(PSRESDIR)$(D)Font$(D)CenturySchURW-Bol \
+ $(PSRESDIR)$(D)Font$(D)CenturySchURW-BolIta \
+ $(PSRESDIR)$(D)Font$(D)CenturySchURW-Ita \
+ $(PSRESDIR)$(D)Font$(D)CenturySchURW-Rom \
+ $(PSRESDIR)$(D)Font$(D)ChanceryURW-MedIta \
+ $(PSRESDIR)$(D)Font$(D)Dingbats \
+ $(PSRESDIR)$(D)Font$(D)NimbusMono-Bold \
+ $(PSRESDIR)$(D)Font$(D)NimbusMono-BoldOblique \
+ $(PSRESDIR)$(D)Font$(D)NimbusMono-Oblique \
+ $(PSRESDIR)$(D)Font$(D)NimbusMono-Regular \
+ $(PSRESDIR)$(D)Font$(D)NimbusRomNo9L-RegIta \
+ $(PSRESDIR)$(D)Font$(D)NimbusRomNo9L-Med \
+ $(PSRESDIR)$(D)Font$(D)NimbusRomNo9L-MedIta \
+ $(PSRESDIR)$(D)Font$(D)NimbusRomNo9L-Reg \
+ $(PSRESDIR)$(D)Font$(D)NimbusSanL-Bol \
+ $(PSRESDIR)$(D)Font$(D)NimbusSanL-BolIta \
+ $(PSRESDIR)$(D)Font$(D)NimbusSanL-RegIta \
+ $(PSRESDIR)$(D)Font$(D)NimbusSanNar-Bol \
+ $(PSRESDIR)$(D)Font$(D)NimbusSanNar-BolIta \
+ $(PSRESDIR)$(D)Font$(D)NimbusSanNar-Ita \
+ $(PSRESDIR)$(D)Font$(D)NimbusSanNar-Reg \
+ $(PSRESDIR)$(D)Font$(D)NimbusSanL-Reg \
+ $(PSRESDIR)$(D)Font$(D)PalladioURW-Bol \
+ $(PSRESDIR)$(D)Font$(D)PalladioURW-BolIta \
+ $(PSRESDIR)$(D)Font$(D)PalladioURW-Ita \
+ $(PSRESDIR)$(D)Font$(D)PalladioURW-Rom \
+ $(PSRESDIR)$(D)Font$(D)StandardSymL \
+ $(PSRESDIR)$(D)Font$(D)URWGothic-Boo \
+ $(PSRESDIR)$(D)Font$(D)URWGothic-BooObl \
+ $(PSRESDIR)$(D)Font$(D)URWGothic-Dem \
+ $(PSRESDIR)$(D)Font$(D)URWGothic-DemObl
+
+# A list of all of the files in Resource/IdimSet
+PS_IDIOMSET_DEPS=
+
+# A list of all of the files in Resource/ProcSet
+PS_PROCSET_DEPS=
+
+# A list of all the files in Resource/Init/ as dependencies for COMPILE_INITS=1.
+PS_INIT_DEPS=\
+ $(PSRESDIR)$(D)Init$(D)cidfmap \
+ $(PSRESDIR)$(D)Init$(D)FCOfontmap-PCLPS2 \
+ $(PSRESDIR)$(D)Init$(D)Fontmap \
+ $(PSRESDIR)$(D)Init$(D)Fontmap.GS \
+ $(PSRESDIR)$(D)Init$(D)gs_agl.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_btokn.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_cff.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_cidcm.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_ciddc.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_cidfm.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_cidfn.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_cidtt.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_cmap.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_cspace.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_css_e.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_dbt_e.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_diskf.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_diskn.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_dpnxt.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_dps.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_dps1.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_dps2.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_dscp.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_epsf.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_fapi.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_fntem.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_fonts.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_frsd.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_icc.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_il1_e.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_img.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_l2img.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_lev2.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_ll3.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_mex_e.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_mgl_e.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_mro_e.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_pdfwr.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_pdf_e.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_res.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_resmp.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_setpd.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_statd.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_std_e.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_sym_e.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_trap.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_ttf.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_typ32.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_typ42.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_type1.ps \
+ $(PSRESDIR)$(D)Init$(D)gs_wan_e.ps \
+ $(PSRESDIR)$(D)Init$(D)pdf_base.ps \
+ $(PSRESDIR)$(D)Init$(D)pdf_cslayer.ps \
+ $(PSRESDIR)$(D)Init$(D)pdf_draw.ps \
+ $(PSRESDIR)$(D)Init$(D)pdf_font.ps \
+ $(PSRESDIR)$(D)Init$(D)pdf_main.ps \
+ $(PSRESDIR)$(D)Init$(D)pdf_ops.ps \
+ $(PSRESDIR)$(D)Init$(D)pdf_rbld.ps \
+ $(PSRESDIR)$(D)Init$(D)pdf_sec.ps \
+ $(PSRESDIR)$(D)Init$(D)xlatmap
+
+PS_SUBSTCID_DEPS=\
+ $(PSRESDIR)$(D)SubstCID$(D)CNS1-WMode \
+ $(PSRESDIR)$(D)SubstCID$(D)GB1-WMode \
+ $(PSRESDIR)$(D)SubstCID$(D)Japan1-WMode \
+ $(PSRESDIR)$(D)SubstCID$(D)Korea1-WMode
+
+PS_MISC_DEPS=\
+ $(PSRESDIR)$(D)Init$(D)FCOfontmap-PCLPS2 \
+ $(PSRESDIR)$(D)Init$(D)cidfmap \
+ $(PSRESDIR)$(D)Init$(D)gs_cet.ps \
+ $(PSRESDIR)$(D)Init$(D)pdf_cslayer.ps
+
+PS_ROMFS_DEPS=$(PSSRCDIR)$(D)psromfs.mak $(gconfig_h) \
+ $(PDF_RESOURCE_DEPS) $(PS_COLORSPACE_DEPS) $(PS_DECODING_DEPS) $(PS_ENCODING_DEPS) \
+ $(PS_FONT_DEPS) $(PS_IDIOMSET_DEPS) $(PS_PROCSET_DEPS) $(PS_INIT_DEPS) $(PS_SUBSTCID_DEPS) \
+ $(PS_MISC_DEPS)
+
diff --git a/psi/sfilter1.c b/psi/sfilter1.c
new file mode 100644
index 000000000..9b7a9cef1
--- /dev/null
+++ b/psi/sfilter1.c
@@ -0,0 +1,338 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Filters included in Level 1 systems: NullEncode/Decode, PFBDecode, */
+/* SubFileDecode. */
+#include "stdio_.h" /* includes std.h */
+#include "memory_.h"
+#include "strimpl.h"
+#include "sfilter.h"
+
+/* ------ PFBDecode ------ */
+
+private_st_PFBD_state();
+
+/* Initialize the state */
+static int
+s_PFBD_init(stream_state * st)
+{
+ stream_PFBD_state *const ss = (stream_PFBD_state *) st;
+
+ ss->record_type = -1;
+ return 0;
+}
+
+/* Process a buffer */
+static int
+s_PFBD_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_PFBD_state *const ss = (stream_PFBD_state *) st;
+ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ int rcount, wcount;
+ int c;
+ int status = 0;
+
+top:
+ rcount = pr->limit - p;
+ wcount = pw->limit - q;
+ switch (ss->record_type) {
+ case -1: /* new record */
+ if (rcount < 2)
+ goto out;
+ if (p[1] != 0x80)
+ goto err;
+ c = p[2];
+ switch (c) {
+ case 1:
+ case 2:
+ break;
+ case 3:
+ status = EOFC;
+ p += 2;
+ goto out;
+ default:
+ p += 2;
+ goto err;
+ }
+ if (rcount < 6)
+ goto out;
+ ss->record_type = c;
+ ss->record_left = p[3] + ((uint) p[4] << 8) +
+ ((ulong) p[5] << 16) +
+ ((ulong) p[6] << 24);
+
+ /* Check for an invalid counter found in an Adobe font, bug 689617 */
+ if (ss->record_left == 0 && ss->record_type == 1) {
+ if (p + 7 < pr->limit) {
+ if (p[6] == 128)
+ ; /* normal empty block */
+ else {
+ ss->record_type = 4; /* ASCII stuff between blocks */
+ ss->record_left = ~0;
+ }
+ } else {
+ if (!last)
+ goto out;
+ }
+ }
+ p += 6;
+ goto top;
+ case 1: /* text data */
+ /* Translate \r to \n. */
+ {
+ int count = (wcount < rcount ? (status = 1, wcount) : rcount);
+
+ if (count > ss->record_left)
+ count = ss->record_left,
+ status = 0;
+ ss->record_left -= count;
+ for (; count != 0; count--) {
+ c = *++p;
+ *++q = (c == '\r' ? '\n' : c);
+ }
+ }
+ break;
+ case 2: /* binary data */
+ if (ss->binary_to_hex) {
+ /* Translate binary to hex. */
+ int count;
+ const char *const hex_digits = "0123456789abcdef";
+
+ wcount >>= 1; /* 2 chars per input byte */
+ count = (wcount < rcount ? (status = 1, wcount) : rcount);
+ if (count > ss->record_left)
+ count = ss->record_left,
+ status = 0;
+ ss->record_left -= count;
+ for (; count != 0; count--) {
+ c = *++p;
+ q[1] = hex_digits[c >> 4];
+ q[2] = hex_digits[c & 0xf];
+ q += 2;
+ }
+ } else { /* Just read binary data. */
+ int count = (wcount < rcount ? (status = 1, wcount) : rcount);
+
+ if (count > ss->record_left)
+ count = ss->record_left,
+ status = 0;
+ ss->record_left -= count;
+ memcpy(q + 1, p + 1, count);
+ p += count;
+ q += count;
+ }
+ break;
+ case 4:
+ /* Treat the text after empty ASCII block as ACSII stream */
+ /* Translate \r to \n. */
+ {
+ int count = (wcount < rcount ? (status = 1, wcount) : rcount);
+ for (; count != 0; count--) {
+ c = *++p;
+ if (c == 128)
+ { --p;
+ ss->record_left = 0;
+ break;
+ }
+ *++q = (c == '\r' ? '\n' : c);
+ }
+ }
+ break;
+ }
+ if (ss->record_left == 0) {
+ ss->record_type = -1;
+ goto top;
+ }
+out:
+ pr->ptr = p;
+ pw->ptr = q;
+ return status;
+err:
+ pr->ptr = p;
+ pw->ptr = q;
+ return ERRC;
+}
+
+/* Stream template */
+const stream_template s_PFBD_template = {
+ &st_PFBD_state, s_PFBD_init, s_PFBD_process, 6, 2
+};
+
+/* ------ SubFileDecode ------ */
+
+private_st_SFD_state();
+
+/* Set default parameter values. */
+static void
+s_SFD_set_defaults(stream_state * st)
+{
+ stream_SFD_state *const ss = (stream_SFD_state *) st;
+
+ ss->count = 0;
+ ss->eod.data = 0;
+ ss->eod.size = 0;
+ ss->skip_count = 0;
+}
+
+/* Initialize the stream */
+static int
+s_SFD_init(stream_state * st)
+{
+ stream_SFD_state *const ss = (stream_SFD_state *) st;
+
+ ss->match = 0;
+ ss->copy_count = 0;
+ ss->min_left = (ss->eod.size != 0);
+
+ return 0;
+}
+
+/* Refill the buffer */
+static int
+s_SFD_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_SFD_state *const ss = (stream_SFD_state *) st;
+ register const byte *p = pr->ptr;
+ register byte *q = pw->ptr;
+ const byte *rlimit = pr->limit;
+ byte *wlimit = pw->limit;
+ int status = 0;
+
+ if (ss->eod.size == 0) { /* Just read, with no EOD pattern. */
+ int rcount = rlimit - p;
+ int wcount = wlimit - q;
+ int count;
+
+ if (rcount <= ss->skip_count) { /* skipping */
+ ss->skip_count -= rcount;
+ pr->ptr = rlimit;
+ return 0;
+ } else if (ss->skip_count > 0) {
+ rcount -= ss->skip_count;
+ pr->ptr = p += ss->skip_count;
+ ss->skip_count = 0;
+ }
+ count = min(rcount, wcount);
+ if (ss->count == 0) /* no EOD limit */
+ return stream_move(pr, pw);
+ else if (ss->count > count) { /* not EOD yet */
+ ss->count -= count;
+ return stream_move(pr, pw);
+ } else { /* We're going to reach EOD. */
+ count = ss->count;
+ if (count > 0) {
+ memcpy(q + 1, p + 1, count);
+ pr->ptr = p + count;
+ pw->ptr = q + count;
+ }
+ ss->count = -1;
+ return EOFC;
+ }
+ } else { /* Read looking for an EOD pattern. */
+ const byte *pattern = ss->eod.data;
+ uint match = ss->match;
+
+cp:
+ /* Check whether we're still copying a partial match. */
+ if (ss->copy_count) {
+ int count = min(wlimit - q, ss->copy_count);
+
+ memcpy(q + 1, ss->eod.data + ss->copy_ptr, count);
+ ss->copy_count -= count;
+ ss->copy_ptr += count;
+ q += count;
+ if (ss->copy_count != 0) { /* hit wlimit */
+ status = 1;
+ goto xit;
+ } else if (ss->count < 0) {
+ status = EOFC;
+ goto xit;
+ }
+ }
+ while (p < rlimit) {
+ int c = *++p;
+
+ if (c == pattern[match]) {
+ if (++match == ss->eod.size) {
+ if (ss->skip_count > 0) {
+ q = pw->ptr; /* undo any writes */
+ ss->skip_count--;
+ match = 0;
+ continue;
+ }
+ /*
+ * We use if/else rather than switch because the value
+ * is long, which is not supported as a switch value in
+ * pre-ANSI C.
+ */
+ if (ss->count <= 0) {
+ status = EOFC;
+ goto xit;
+ } else if (ss->count == 1) {
+ ss->count = -1;
+ } else
+ ss->count--;
+ ss->copy_ptr = 0;
+ ss->copy_count = match;
+ match = 0;
+ goto cp;
+ }
+ continue;
+ }
+ /*
+ * No match here, back up to find the longest one.
+ * This may be quadratic in string_size, but
+ * we don't expect this to be a real problem.
+ */
+ if (match > 0) {
+ int end = match;
+
+ while (match > 0) {
+ match--;
+ if (!memcmp(pattern, pattern + end - match, match))
+ break;
+ }
+ /*
+ * Copy the unmatched initial portion of
+ * the EOD string to the output.
+ */
+ p--;
+ ss->copy_ptr = 0;
+ ss->copy_count = end - match;
+ goto cp;
+ }
+ if (q == wlimit) {
+ p--;
+ status = 1;
+ break;
+ }
+ *++q = c;
+ }
+xit: pr->ptr = p;
+ if (ss->skip_count <= 0)
+ pw->ptr = q;
+ ss->match = match;
+ }
+ return status;
+}
+
+/* Stream template */
+const stream_template s_SFD_template = {
+ &st_SFD_state, s_SFD_init, s_SFD_process, 1, 1, 0, s_SFD_set_defaults
+};
diff --git a/psi/store.h b/psi/store.h
new file mode 100644
index 000000000..49550eb17
--- /dev/null
+++ b/psi/store.h
@@ -0,0 +1,265 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Assignment-related macros */
+
+#ifndef store_INCLUDED
+# define store_INCLUDED
+
+#include "ialloc.h" /* for imemory masks & checks */
+#include "idosave.h"
+
+/*
+ * Macros for storing a ref. We use macros for storing into objects,
+ * since the storage manager needs to be able to track stores for
+ * save/restore and also for global/local checking.
+ * We also use macros for other ref assignments, because (as it happens)
+ * Turbo C generates pretty awful code for doing this.
+ *
+ * There are three cases that we need to distinguish:
+ * - Storing to a stack (no special action);
+ * - Storing into a newly created object (set l_new);
+ * - Storing into a slot of an existing object (check l_new in
+ * old value, set in new value).
+ * The macros are called
+ * <make/store><new_type><case>(place_to_store, new_value)
+ * where <case> is nothing for storing to the stack, _new for storing into
+ * a new object, and _old for storing into an existing object.
+ * (The _old macros also take a client name for tracing and debugging.)
+ * <new_type> and new_value are chosen from the following alternatives:
+ * ref_assign POINTER TO arbitrary ref
+ * make_t type (only for null and mark)
+ * make_tv type, value field name, value
+ * (only for scalars, which don't have attributes)
+ * make_tav type, attributes, value field name, value
+ * make_tasv type, attributes, size, value field name, value
+ * There are also specialized make_ macros for specific types:
+ * make_array, make_int, make_real, make_bool, make_false, make_true,
+ * make_mark, make_null, make_oper, make_[const_]string, make_struct.
+ * Not all of the specialized make_ macros have _new and _old variants.
+ *
+ * For _tav and _tasv, we must store the value first, because sometimes
+ * it depends on the contents of the place being stored into.
+ *
+ * Note that for composite objects (dictionary, file, array, string, device,
+ * struct), we must set a_foreign if the contents are allocated statically
+ * (e.g., for constant C strings) or not by the Ghostscript allocator
+ * (e.g., with malloc).
+ */
+
+/*
+ * Define the most efficient ref assignment macro for the platform.
+ */
+/*
+ * Assigning the components individually is fastest on Turbo C,
+ * and on Watcom C when one or both of the addresses are
+ * already known or in a register.
+ *
+ * Though, it sends wrong signals to the compiler that believes it's okay-ish
+ * to update the structure in two calls, and risks very wrong reordering. This
+ * _MUST_ be done in one call, and trust the compiler to do the proper thing,
+ * if not too bad. And we're using GCC anyways on Debian.
+ */
+#define ref_assign_inline(pto,pfrom)\
+ (*(pto) = *(pfrom))
+#ifdef __TURBOC__
+ /*
+ * Move the data in two 32-bit chunks, because
+ * otherwise the compiler calls SCOPY@.
+ * The cast to void is to discourage the compiler from
+ * wanting to deliver the value of the expression.
+ */
+# define ref_assign(pto,pfrom)\
+ discard(ref_assign_inline(pto, pfrom))
+#else
+ /*
+ * Trust the compiler and hope for the best.
+ * The MIPS compiler doesn't like the cast to void.
+ */
+# define ref_assign(pto,pfrom)\
+ (*(pto) = *(pfrom))
+#endif
+
+#define ialloc_new_mask (idmemory->new_mask)
+ /*
+ * The mmem argument may be either a gs_dual_memory_t or a
+ * gs_ref_memory_t, since it is only used for accessing the masks.
+ */
+#define ref_saving_in(mmem)\
+ ((mmem)->new_mask != 0)
+#define ref_must_save_in(mmem,pto)\
+ ((r_type_attrs(pto) & (mmem)->test_mask) == 0)
+#define ref_must_save(pto) ref_must_save_in(idmemory, pto)
+#define ref_do_save_in(mem, pcont, pto, cname)\
+ alloc_save_change_in(mem, pcont, (ref_packed *)(pto), cname)
+#define ref_do_save(pcont, pto, cname)\
+ alloc_save_change(idmemory, pcont, (ref_packed *)(pto), cname)
+#define ref_save_in(mem, pcont, pto, cname)\
+ discard((ref_must_save_in(mem, pto) ?\
+ ref_do_save_in(mem, pcont, pto, cname) : 0))
+#define ref_save(pcont, pto, cname)\
+ discard((ref_must_save(pto) ? ref_do_save(pcont, pto, cname) : 0))
+#define ref_mark_new_in(mmem,pto)\
+ ((pto)->tas.type_attrs |= (mmem)->new_mask)
+#define ref_mark_new(pto) ref_mark_new_in(idmemory, pto)
+#define ref_assign_new_in(mem,pto,pfrom)\
+ discard((ref_assign(pto,pfrom), ref_mark_new_in(mem,pto)))
+#define ref_assign_new(pto,pfrom)\
+ discard((ref_assign(pto,pfrom), ref_mark_new(pto)))
+#define ref_assign_new_inline(pto,pfrom)\
+ discard((ref_assign_inline(pto,pfrom), ref_mark_new(pto)))
+#define ref_assign_old_in(mem,pcont,pto,pfrom,cname)\
+ (ref_save_in(mem,pcont,pto,cname), ref_assign_new_in(mem,pto,pfrom))
+#define ref_assign_old(pcont,pto,pfrom,cname)\
+ (ref_save(pcont,pto,cname), ref_assign_new(pto,pfrom))
+#define ref_assign_old_inline(pcont,pto,pfrom,cname)\
+ (ref_save(pcont,pto,cname), ref_assign_new_inline(pto,pfrom))
+/* ref_mark_old is only needed in very unusual situations, namely, */
+/* when we want to do a ref_save just before a save instead of */
+/* when the actual assignment occurs. */
+#define ref_mark_old(pto) ((pto)->tas.type_attrs &= ~ialloc_new_mask)
+
+/* Define macros for conditionally clearing the parts of a ref */
+/* that aren't being set to anything useful. */
+
+#ifdef DEBUG
+# define and_fill_s(pref)\
+ , (gs_debug['$'] ? r_set_size(pref, 0xfeed) : 0)
+/*
+ * The following nonsense avoids compiler warnings about signed/unsigned
+ * integer constants.
+ */
+#define DEADBEEF ((int)(((uint)0xdead << 16) | 0xbeef))
+# define and_fill_sv(pref)\
+ , (gs_debug['$'] ? (r_set_size(pref, 0xfeed),\
+ (pref)->value.intval = DEADBEEF) : 0)
+#else /* !DEBUG */
+# define and_fill_s(pref) /* */
+# define and_fill_sv(pref) /* */
+#endif
+
+/* make_t must set the attributes to 0 to clear a_local! */
+#define make_ta(pref,newtype,newattrs)\
+ (r_set_type_attrs(pref, newtype, newattrs) and_fill_sv(pref))
+#define make_t(pref,newtype)\
+ make_ta(pref, newtype, 0)
+#define make_t_new_in(mem,pref,newtype)\
+ make_ta(pref, newtype, imemory_new_mask(mem))
+#define make_t_new(pref,newtype)\
+ make_ta(pref, newtype, ialloc_new_mask)
+#define make_t_old_in(mem,pcont,pref,newtype,cname)\
+ (ref_save_in(mem,pcont,pref,cname), make_t_new_in(mem,pref,newtype))
+#define make_t_old(pcont,pref,newtype,cname)\
+ (ref_save(pcont,pref,cname), make_t_new(pref,newtype))
+
+#define make_tav(pref,newtype,newattrs,valfield,newvalue)\
+ ((pref)->value.valfield = (newvalue),\
+ r_set_type_attrs(pref, newtype, newattrs)\
+ and_fill_s(pref))
+#define make_tav_new(pref,t,a,vf,v)\
+ make_tav(pref,t,(a)|ialloc_new_mask,vf,v)
+#define make_tav_old(pcont,pref,t,a,vf,v,cname)\
+ (ref_save(pcont,pref,cname), make_tav_new(pref,t,a,vf,v))
+
+#define make_tv(pref,newtype,valfield,newvalue)\
+ make_tav(pref,newtype,0,valfield,newvalue)
+#define make_tv_new(pref,t,vf,v)\
+ make_tav_new(pref,t,0,vf,v)
+#define make_tv_old(pcont,pref,t,vf,v,cname)\
+ make_tav_old(pcont,pref,t,0,vf,v,cname)
+
+#define make_tasv(pref,newtype,newattrs,newsize,valfield,newvalue)\
+ ((pref)->value.valfield = (newvalue),\
+ r_set_type_attrs(pref, newtype, newattrs),\
+ r_set_size(pref, newsize))
+#define make_tasv_new(pref,t,a,s,vf,v)\
+ make_tasv(pref,t,(a)|ialloc_new_mask,s,vf,v)
+#define make_tasv_old(pcont,pref,t,a,s,vf,v,cname)\
+ (ref_save(pcont,pref,cname), make_tasv_new(pref,t,a,s,vf,v))
+
+/* Type-specific constructor macros for scalar (non-composite) types */
+
+#define make_bool(pref,bval)\
+ make_tv(pref, t_boolean, boolval, bval)
+#define make_false(pref)\
+ make_bool(pref, 0)
+#define make_true(pref)\
+ make_bool(pref, 1)
+
+#define make_int(pref,ival)\
+ make_tv(pref, t_integer, intval, ival)
+#define make_int_new(pref,ival)\
+ make_tv_new(pref, t_integer, intval, ival)
+
+#define make_mark(pref)\
+ make_t(pref, t_mark)
+
+#define make_null(pref)\
+ make_t(pref, t_null)
+#define make_null_new(pref)\
+ make_t_new(pref, t_null)
+#define make_null_old_in(mem,pcont,pref,cname)\
+ make_t_old_in(mem, pcont, pref, t_null, cname)
+#define make_null_old(pcont,pref,cname)\
+ make_t_old(pcont, pref, t_null, cname)
+
+#define make_oper(pref,opidx,proc)\
+ make_tasv(pref, t_operator, a_executable, opidx, opproc, proc)
+#define make_oper_new(pref,opidx,proc)\
+ make_tasv_new(pref, t_operator, a_executable, opidx, opproc, proc)
+
+#define make_real(pref,rval)\
+ make_tv(pref, t_real, realval, rval)
+#define make_real_new(pref,rval)\
+ make_tv_new(pref, t_real, realval, rval)
+
+/* Type-specific constructor macros for composite types */
+
+/* For composite types, the a_space field is relevant; however, */
+/* as noted in ivmspace.h, a value of 0 designates the most static space, */
+/* so for making empty composites, a space value of 0 is appropriate. */
+
+#define make_array(pref,attrs,size,elts)\
+ make_tasv(pref, t_array, attrs, size, refs, elts)
+#define make_array_new(pref,attrs,size,elts)\
+ make_tasv_new(pref, t_array, attrs, size, refs, elts)
+#define make_const_array(pref,attrs,size,elts)\
+ make_tasv(pref, t_array, attrs, size, const_refs, elts)
+#define make_empty_array(pref,attrs)\
+ make_array(pref, attrs, 0, (ref *)NULL)
+#define make_empty_const_array(pref,attrs)\
+ make_const_array(pref, attrs, 0, (const ref *)NULL)
+
+#define make_string(pref,attrs,size,chars)\
+ make_tasv(pref, t_string, attrs, size, bytes, chars)
+#define make_const_string(pref,attrs,size,chars)\
+ make_tasv(pref, t_string, attrs, size, const_bytes, chars)
+#define make_empty_string(pref,attrs)\
+ make_string(pref, attrs, 0, (byte *)NULL)
+#define make_empty_const_string(pref,attrs)\
+ make_const_string(pref, attrs, 0, (const byte *)NULL)
+
+#define make_struct(pref,attrs,ptr)\
+ make_tav(pref, t_struct, attrs, pstruct, (obj_header_t *)(ptr))
+#define make_struct_new(pref,attrs,ptr)\
+ make_tav_new(pref, t_struct, attrs, pstruct, (obj_header_t *)(ptr))
+
+#define make_astruct(pref,attrs,ptr)\
+ make_tav(pref, t_astruct, attrs, pstruct, (obj_header_t *)(ptr))
+#define make_astruct_new(pref,attrs,ptr)\
+ make_tav_new(pref, t_astruct, attrs, pstruct, (obj_header_t *)(ptr))
+
+#endif /* store_INCLUDED */
diff --git a/psi/winint.mak b/psi/winint.mak
new file mode 100644
index 000000000..7b3b160ef
--- /dev/null
+++ b/psi/winint.mak
@@ -0,0 +1,217 @@
+# Copyright (C) 2001-2015 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+# CA 94903, U.S.A., +1(415)492-9861, for further information.
+#
+#
+# Common interpreter makefile section for 32-bit MS Windows.
+
+# This makefile must be acceptable to Microsoft Visual C++, Watcom C++,
+# and Borland C++. For this reason, the only conditional directives
+# allowed are !if[n]def, !else, and !endif.
+
+
+# Include the generic makefile.
+!include $(PSSRCDIR)\int.mak
+
+# Define the location of the NSIS makensis installer utility
+!ifndef MAKENSIS_XE
+!if $(BUILD_SYSTEM) == 64
+MAKENSIS_XE="C:\Program Files (x86)\NSIS\makensis.exe"
+!else
+MAKENSIS_XE="C:\Program Files\NSIS\makensis.exe"
+!endif
+!endif
+
+!ifdef WIN64
+NSISTARGET=gs$(GS_VERSION)w64
+!else
+NSISTARGET=gs$(GS_VERSION)w32
+!endif
+
+# Define the C++ compiler invocation for library modules.
+GLCPP=$(CPP) $(CO) $(I_)$(GLI_)$(_I)
+
+# Define the compilation rule for Windows interpreter code.
+# This requires PS*_ to be defined, so it has to come after int.mak.
+PSCCWIN=$(CC_WX) $(CCWINFLAGS) $(I_)$(PSI_)$(_I) $(I_)$(DEVSRCDIR)$(_I) $(PSF_)
+
+# Define the name of this makefile.
+WININT_MAK=$(PSSRC)winint.mak
+
+# Define the RCOMP switch for including INCDIR.
+!if "$(INCDIR)"==""
+i_INCDIR=
+!else
+i_INCDIR=-i$(INCDIR)
+!endif
+
+
+# ----------------------------- Main program ------------------------------ #
+
+ICONS=$(GLGEN)gswin.ico $(GLGEN)gswin16.ico
+
+GS_ALL=$(INT_ALL)\
+ $(LIB_ALL) $(LIBCTR) $(ld_tr) $(GSDLL_OBJ).res $(PSSRC)$(GSDLL).def $(ICONS)
+
+dwdll_h=$(PSSRC)dwdll.h
+dwimg_h=$(PSSRC)dwimg.h
+dwtrace_h=$(PSSRC)dwtrace.h
+dwres_h=$(PSSRC)dwres.h
+dwtext_h=$(PSSRC)dwtext.h
+dwreg_h=$(PSSRC)dwreg.h
+
+# Make the icons from their text form.
+
+$(GLGEN)gswin.ico: $(GLSRC)gswin.icx $(ECHOGS_XE) $(WININT_MAK)
+ $(ECHOGS_XE) -wb $(GLGEN)gswin.ico -n -X -r $(GLSRC)gswin.icx
+
+$(GLGEN)gswin16.ico: $(GLSRC)gswin16.icx $(ECHOGS_XE) $(WININT_MAK)
+ $(ECHOGS_XE) -wb $(GLGEN)gswin16.ico -n -X -r $(GLSRC)gswin16.icx
+
+# resources for short EXE loader (no dialogs)
+$(GS_OBJ).res: $(PSSRC)dwmain.rc $(dwres_h) $(ICONS) $(WININT_MAK)
+ $(ECHOGS_XE) -w $(PSGEN)_exe.rc -x 23 define -s gstext_ico $(GLGENDIR)\gswin.ico
+ $(ECHOGS_XE) -a $(PSGEN)_exe.rc -x 23 define -s gsgraph_ico $(GLGENDIR)\gswin.ico
+ $(ECHOGS_XE) -a $(PSGEN)_exe.rc -R $(PSSRC)dwmain.rc
+ $(RCOMP) -i$(PSSRCDIR) -i$(PSGENDIR) $(i_INCDIR) -r $(RO_)$(GS_OBJ).res $(PSGEN)_exe.rc
+ del $(PSGEN)_exe.rc
+
+# resources for main program (includes dialogs)
+$(GSDLL_OBJ).res: $(PSSRC)gsdll32.rc $(gp_mswin_h) $(ICONS) $(WININT_MAK)
+ $(ECHOGS_XE) -w $(PSGEN)_dll.rc -x 23 define -s gstext_ico $(GLGENDIR)\gswin.ico
+ $(ECHOGS_XE) -a $(PSGEN)_dll.rc -x 23 define -s gsgraph_ico $(GLGENDIR)\gswin.ico
+ $(ECHOGS_XE) -a $(PSGEN)_dll.rc -R $(PSSRC)gsdll32.rc
+ $(RCOMP) -i$(PSSRCDIR) -i$(PSGENDIR) -i$(GLSRCDIR) $(i_INCDIR) -r $(RO_)$(GSDLL_OBJ).res $(PSGEN)_dll.rc
+ del $(PSGEN)_dll.rc
+
+
+# Modules for big EXE
+
+!if $(DEBUG)
+DWTRACE=$(GLOBJ)dwtrace.obj
+!else
+DWTRACE=
+!endif
+
+
+DWOBJNO = $(PSOBJ)dwnodll.obj $(GLOBJ)dwimg.obj $(DWTRACE) $(PSOBJ)dwmain.obj \
+$(GLOBJ)dwtext.obj $(GLOBJ)dwreg.obj
+
+$(PSOBJ)dwnodll.obj: $(PSSRC)dwnodll.c $(AK)\
+ $(dwdll_h) $(iapi_h)
+ $(PSCCWIN) $(COMPILE_FOR_EXE) $(PSO_)dwnodll.obj $(C_) $(PSSRC)dwnodll.c
+
+# Compile gsdll.c, the main program of the DLL.
+
+$(PSOBJ)gsdll.obj: $(PSSRC)gsdll.c $(AK) $(iapi_h) $(ghost_h)
+ $(PSCCWIN) $(COMPILE_FOR_DLL) $(PSO_)gsdll.$(OBJ) $(C_) $(PSSRC)gsdll.c
+
+$(GLOBJ)gp_msdll.obj: $(GLSRC)gp_msdll.c $(AK) $(iapi_h)
+ $(PSCCWIN) $(COMPILE_FOR_DLL) $(GLO_)gp_msdll.$(OBJ) $(C_) $(GLSRC)gp_msdll.c
+
+# Modules for console mode EXEs
+
+OBJC=$(PSOBJ)dwmainc.obj $(PSOBJ)dwdllc.obj $(GLOBJ)gscdefs.obj\
+!ifdef METRO
+ $(GLOBJ)gp_wgetv.obj \
+!else
+ $(GLOBJ)gp_wgetv.obj $(GLOBJ)dwimg.obj $(DWTRACE) $(GLOBJ)dwreg.obj
+!endif
+
+OBJCNO=$(PSOBJ)dwmainc.obj $(PSOBJ)dwnodllc.obj $(GLOBJ)dwimg.obj $(DWTRACE) $(GLOBJ)dwreg.obj
+
+$(PSOBJ)dwmainc.obj: $(PSSRC)dwmainc.c $(AK) $(windows__h) $(fcntl__h) $(unistd__h) \
+ $(iapi_h) $(vdtrace_h) $(gdevdsp_h) $(dwdll_h) $(dwimg_h) $(dwtrace_h)
+ $(PSCCWIN) $(COMPILE_FOR_CONSOLE_EXE) $(PSO_)dwmainc.obj $(C_) $(PSSRC)dwmainc.c
+
+$(PSOBJ)dwdllc.obj: $(PSSRC)dwdll.c $(AK) $(dwdll_h) $(iapi_h)
+ $(PSCCWIN) $(COMPILE_FOR_CONSOLE_EXE) $(PSO_)dwdllc.obj $(C_) $(PSSRC)dwdll.c
+
+$(PSOBJ)dwnodllc.obj: $(PSSRC)dwnodll.c $(AK) $(dwdll_h) $(iapi_h)
+ $(PSCCWIN) $(COMPILE_FOR_CONSOLE_EXE) $(PSO_)dwnodllc.obj $(C_) $(PSSRC)dwnodll.c
+
+
+# Modules for small EXE loader.
+
+DWOBJ=$(PSOBJ)dwdll.obj $(GLOBJ)dwimg.obj $(DWTRACE) $(PSOBJ)dwmain.obj \
+$(PSOBJ)dwtext.obj $(GLOBJ)gscdefs.obj $(GLOBJ)gp_wgetv.obj $(PSOBJ)dwreg.obj
+
+$(PSOBJ)dwdll.obj: $(PSSRC)dwdll.c $(AK)\
+ $(dwdll_h) $(iapi_h)
+ $(PSCCWIN) $(COMPILE_FOR_EXE) $(PSO_)dwdll.obj $(C_) $(PSSRC)dwdll.c
+
+$(PSOBJ)dwimg.obj: $(PSSRC)dwimg.c $(AK)\
+ $(dwres_h) $(dwdll_h) $(dwtext_h) $(dwimg_h) $(gdevdsp_h) $(stdio__h) \
+ $(gscdefs_h) $(dwreg_h)
+ $(PSCCWIN) $(COMPILE_FOR_EXE) $(PSO_)dwimg.obj $(C_) $(PSSRC)dwimg.c
+
+$(PSOBJ)dwtrace.obj: $(PSSRC)dwtrace.c $(AK)\
+ $(dwimg_h) $(dwtrace_h)\
+ $(gscdefs_h) $(stdpre_h) $(gsdll_h) $(vdtrace_h)
+ $(PSCCWIN) $(COMPILE_FOR_EXE) $(PSO_)dwtrace.obj $(C_) $(PSSRC)dwtrace.c
+
+$(PSOBJ)dwmain.obj: $(PSSRC)dwmain.c $(AK) $(windows__h) \
+ $(iapi_h) $(vdtrace_h) $(dwres_h) $(dwdll_h) $(dwtext_h) $(dwimg_h) $(dwtrace_h) \
+ $(dwreg_h) $(gdevdsp_h)
+ $(PSCCWIN) $(COMPILE_FOR_EXE) $(PSO_)dwmain.obj $(C_) $(PSSRC)dwmain.c
+
+$(PSOBJ)dwtext.obj: $(PSSRC)dwtext.c $(AK) $(dwtext_h)
+ $(PSCCWIN) $(COMPILE_FOR_EXE) $(PSO_)dwtext.obj $(C_) $(PSSRC)dwtext.c
+
+$(PSOBJ)dwreg.obj: $(PSSRC)dwreg.c $(AK) $(dwreg_h)
+ $(PSCCWIN) $(COMPILE_FOR_EXE) $(PSO_)dwreg.obj $(C_) $(PSSRC)dwreg.c
+
+
+# ------------ Windows version of the .locale_to_utf8 operator ------------ #
+
+zwinutf8_=$(PSOBJ)zwinutf8.$(OBJ)
+$(PSD)winutf8.dev : $(MAKEFILE) $(ECHOGS_XE) $(zwinutf8_)
+ $(SETMOD) $(PSD)winutf8 $(zwinutf8_)
+ $(ADDMOD) $(PSD)winutf8 -oper zwinutf8
+
+$(PSOBJ)zwinutf8.$(OBJ) : $(PSSRC)zwinutf8.c $(OP)\
+ $(ghost_h) $(oper_h) $(iutil_h) $(store_h) $(windows__h)
+ $(PSCCWIN) $(PSO_)zwinutf8.$(OBJ) $(C_) $(PSSRC)zwinutf8.c
+
+# -------------------- NSIS Installer -------------------------------- #
+nsis: $(PSSRC)nsisinst.nsi $(GSCONSOLE_XE) $(GS_ALL) $(GS_XE) $(GSDLL_DLL) $(BINDIR)\$(GSDLL).lib
+ $(MAKENSIS_XE) -NOCD -DTARGET=$(NSISTARGET) -DVERSION=$(GS_DOT_VERSION) $(PSSRC)nsisinst.nsi
+
+# -------------------- Distribution source archive ------------------- #
+# This creates a zip file containing the files needed to build
+# ghostscript on MS-Windows. We don't distribute this zip file,
+# but use it to build the executable distribution.
+#
+# The MS-Windows build process for a release is
+# gzip -d ghostscript-N.NN.tar.gz
+# tar -xvf ghostscript-N.NN.tar
+# cd ghostscript-N.NN
+# nmake -f psi/msvc32.mak srczip
+# cd gsN.NN
+# nmake -f psi/msvc32.mak
+# nmake -f psi/msvc32.mak archive
+
+gs$(GS_VERSION)src.zip:
+ -rmdir /s /q gs$(GS_DOT_VERSION)
+ -del temp.zip
+ zip -r -X temp.zip LICENSE Resource arch base conrib cups doc examples expat freetype iccprofiles ijs jbig2dec jpeg jpegxr lcms lcms2 lib libpng man openjpeg psi tiff toolbin trio zlib -x ".svn/*" -x "*/.svn/*" -x "*/*/.svn/*" -x "*/*/*/.svn/*" -x "*/*/*/*/.svn/*" -x "*/*/*/*/*/.svn/*"
+ mkdir gs$(GS_DOT_VERSION)
+ cd gs$(GS_DOT_VERSION)
+ unzip -a ../temp.zip
+ cd ..
+ zip -9 -r -X gs$(GS_VERSION)src.zip gs$(GS_DOT_VERSION)
+
+srczip: gs$(GS_VERSION)src.zip
+
+# end of winint.mak
+
diff --git a/psi/zalg.c b/psi/zalg.c
new file mode 100644
index 000000000..7182c0769
--- /dev/null
+++ b/psi/zalg.c
@@ -0,0 +1,208 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Operators for general-purpose algorithms. For now, only sorting. */
+#include "ghost.h"
+#include "gserrors.h"
+#include "oper.h"
+#include "store.h"
+#include "estack.h"
+
+/* ========================================================================= */
+
+/*
+ * The "heap sort" algorithm, as implementation of the .sort operator
+ *
+ * The implementation follows Algorithm H from Donald Knuth's
+ * "The Art of Computer Programming", volume 3, section 5.2.3
+ *
+ * Notes:
+ * i. Execution time: O(n log n) in the average and worst cases.
+ * ii. The sort is not "stable" (the relative order of elements with
+ * equal keys is not necessarily preserved).
+ * iii. Status variables:
+ * - stored on the e-stack;
+ * - "l", "r", "i", "j" and "R" correspond directly to variables in
+ * Algorithm H (including the fact that indices are 1-based);
+ * - variable "K" from Algorithm H is not used here, because we don't
+ * distinguish a "key part" of the array elements;
+ * - "H" indicates the step to execute; used when resuming after executing
+ * <lt> (to execute it, we have to return to the interpreter).
+ * - "array" and "lt" are refs to the parameters; avoids using them from the
+ * o-stack after resuming, in case the predicate has odd side-efects
+ */
+
+static int zsort(i_ctx_t *i_ctx_p);
+static int zsort_continue(i_ctx_t *i_ctx_p);
+static int zsort_cleanup(i_ctx_t *i_ctx_p);
+
+/* <array> <lt> .sort <array> */
+static int
+zsort(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint N;
+
+ /* Check operands for type and access */
+ /* we can sort only writable [and unpacked] arrays */
+ if (r_type(&op[-1]) == t_mixedarray || r_type(&op[-1]) == t_shortarray)
+ return_error(gs_error_invalidaccess);
+ check_write_type(op[-1], t_array);
+ /* the predicate must be an executable array/ string/ name/ [pseudo-]operator */
+ if (!r_has_attr(&op[0], a_executable))
+ return_op_typecheck(&op[0]);
+ switch (r_btype(&op[0])) {
+ case t_array:
+ case t_mixedarray:
+ case t_shortarray:
+ case t_string:
+ if (!r_has_attr(&op[0], a_execute))
+ return_error(gs_error_invalidaccess);
+ break;
+ case t_name:
+ case t_operator:
+ case t_oparray:
+ break;
+ default:
+ return_op_typecheck(&op[0]);
+ }
+ /*
+ * if array length <= 1, then nothing to sort
+ * else prepare the status variables and launch the main sorting routine zsort_continue()
+ */
+ N = r_size(&op[-1]);
+ if (N <= 1) {
+ pop(1);
+ return 0;
+ } else {
+ check_estack(11);
+ push_mark_estack(es_other, zsort_cleanup);
+/*H1:*/ make_int(&esp[1], N / 2 + 1); /* l */
+ make_int(&esp[2], N); /* r */
+ make_int(&esp[3], 0); /* i */
+ make_int(&esp[4], 0); /* j */
+ make_null(&esp[5]); /* R */
+ make_int(&esp[6], 2); /* H */
+ ref_assign(&esp[7], &op[0]); /* lt */
+ ref_assign(&esp[8], &op[-1]); /* the array */
+ esp += 8;
+ make_op_estack(&esp[1], zsort_continue);
+ make_null(&op[0]); /* result of <lt>, not used when H = 2 */
+ return zsort_continue(i_ctx_p);
+ }
+}
+
+/* Continuation operator for .sort */
+static int
+zsort_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *status;
+ ref *Rn;
+# define l (status[1].value.intval)
+# define r (status[2].value.intval)
+# define i (status[3].value.intval)
+# define j (status[4].value.intval)
+# define R (status[5])
+# define H (status[6].value.intval)
+# define lt (status[7])
+# define arry (status[8])
+
+ status = esp - 8;
+ Rn = arry.value.refs - 1; /* the -1 compensates for using 1-based indices */
+ switch (H) {
+ case 2:
+H2: if (l > 1) {
+ l--;
+ ref_assign(&R, &Rn[l]);
+ } else {
+ ref_assign(&R, &Rn[r]);
+ ref_assign_old(&arry, &Rn[r], &Rn[1], ".sort(H2-a)");
+ r--;
+ if (r <= 1) {
+ ref_assign_old(&arry, &Rn[1], &R, ".sort(H2-b)");
+ esp -= 9;
+ pop(1);
+ return o_pop_estack;
+ }
+ }
+/* H3: */ j = l;
+H4: i = j;
+ j <<= 1;
+ if (j >= r)
+ if (j == r)
+ goto H6;
+ else
+ goto H8;
+ else {
+/* H5: */ H = 5;
+ push(1);
+ ref_assign(&op[-1], &Rn[j]);
+ ref_assign(&op[0], &Rn[j + 1]);
+ break;
+ }
+ case 5:
+/*H5_cont:*/if (!r_has_type(&op[0], t_boolean))
+ return_error(gs_error_typecheck);
+ if (op[0].value.boolval)
+ j++;
+H6: H = 6;
+ push(1);
+ ref_assign(&op[-1], &R);
+ ref_assign(&op[0], &Rn[j]);
+ break;
+ case 6:
+/*H6_cont:*/if (!r_has_type(&op[0], t_boolean))
+ return_error(gs_error_typecheck);
+ if (op[0].value.boolval) {
+/* H7: */ ref_assign_old(&arry, &Rn[i], &Rn[j], ".sort(H7)");
+ goto H4;
+ } else {
+H8: ref_assign_old(&arry, &Rn[i], &R, ".sort(H8)");
+ goto H2;
+ }
+ default:
+ pop(1);
+ return_error(gs_error_unregistered); /* Must not happen. */
+ }
+ esp += 2;
+ ref_assign(esp, &lt);
+ return o_push_estack;
+#undef l
+#undef r
+#undef i
+#undef j
+#undef R
+#undef H
+#undef lt
+}
+
+/* No-op cleanup routine for .sort */
+static int
+zsort_cleanup(i_ctx_t *i_ctx_p)
+{
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zalg_op_defs[] =
+{
+ {"2.sort", zsort},
+ /* Internal operators */
+ {"1%zsort_continue", zsort_continue},
+ op_def_end(0)
+};
diff --git a/psi/zarith.c b/psi/zarith.c
new file mode 100644
index 000000000..9e7c7eae2
--- /dev/null
+++ b/psi/zarith.c
@@ -0,0 +1,457 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Arithmetic operators */
+#include "math_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "store.h"
+#include "gsstate.h"
+
+/****** NOTE: none of the arithmetic operators ******/
+/****** currently check for floating exceptions ******/
+
+/*
+ * Many of the procedures in this file are public only so they can be
+ * called from the FunctionType 4 interpreter (zfunc4.c).
+ */
+
+/* <num1> <num2> add <sum> */
+/* We make this into a separate procedure because */
+/* the interpreter will almost always call it directly. */
+int
+zop_add(i_ctx_t *i_ctx_p)
+{
+ register os_ptr op = osp;
+
+ switch (r_type(op)) {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ switch (r_type(op - 1)) {
+ default:
+ return_op_typecheck(op - 1);
+ case t_real:
+ op[-1].value.realval += op->value.realval;
+ break;
+ case t_integer:
+ make_real(op - 1, (double)op[-1].value.intval + op->value.realval);
+ }
+ break;
+ case t_integer:
+ switch (r_type(op - 1)) {
+ default:
+ return_op_typecheck(op - 1);
+ case t_real:
+ op[-1].value.realval += (double)op->value.intval;
+ break;
+ case t_integer: {
+ if (sizeof(ps_int) != 4 && gs_currentcpsimode(imemory)) {
+ ps_int32 int1 = (ps_int32)op[-1].value.intval;
+ ps_int32 int2 = (ps_int32)op->value.intval;
+
+ if (((int1 += int2) ^ int2) < 0 &&
+ ((int1 - int2) ^ int2) >= 0
+ ) { /* Overflow, convert to real */
+ make_real(op - 1, (double)(int1 - int2) + int2);
+ }
+ else {
+ op[-1].value.intval = (ps_int)int1;
+ }
+ }
+ else {
+ ps_int int2 = op->value.intval;
+
+ if (((op[-1].value.intval += int2) ^ int2) < 0 &&
+ ((op[-1].value.intval - int2) ^ int2) >= 0
+ ) { /* Overflow, convert to real */
+ make_real(op - 1, (double)(op[-1].value.intval - int2) + int2);
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+int
+zadd(i_ctx_t *i_ctx_p)
+{
+ int code = zop_add(i_ctx_p);
+
+ if (code == 0) {
+ pop(1);
+ }
+ return code;
+}
+
+/* <num1> <num2> div <real_quotient> */
+int
+zdiv(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+
+ /* We can't use the non_int_cases macro, */
+ /* because we have to check explicitly for op == 0. */
+ switch (r_type(op)) {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ if (op->value.realval == 0)
+ return_error(gs_error_undefinedresult);
+ switch (r_type(op1)) {
+ default:
+ return_op_typecheck(op1);
+ case t_real:
+ op1->value.realval /= op->value.realval;
+ break;
+ case t_integer:
+ make_real(op1, (double)op1->value.intval / op->value.realval);
+ }
+ break;
+ case t_integer:
+ if (op->value.intval == 0)
+ return_error(gs_error_undefinedresult);
+ switch (r_type(op1)) {
+ default:
+ return_op_typecheck(op1);
+ case t_real:
+ op1->value.realval /= (double)op->value.intval;
+ break;
+ case t_integer:
+ make_real(op1, (double)op1->value.intval / (double)op->value.intval);
+ }
+ }
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> mul <product> */
+int
+zmul(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ switch (r_type(op - 1)) {
+ default:
+ return_op_typecheck(op - 1);
+ case t_real:
+ op[-1].value.realval *= op->value.realval;
+ break;
+ case t_integer:
+ make_real(op - 1, (double)op[-1].value.intval * op->value.realval);
+ }
+ break;
+ case t_integer:
+ switch (r_type(op - 1)) {
+ default:
+ return_op_typecheck(op - 1);
+ case t_real:
+ op[-1].value.realval *= (double)op->value.intval;
+ break;
+ case t_integer: {
+ if (sizeof(ps_int) != 4 && gs_currentcpsimode(imemory)) {
+ double ab = (double)op[-1].value.intval * op->value.intval;
+ if (ab > (double)MAX_PS_INT32) /* (double)0x7fffffff */
+ make_real(op - 1, ab);
+ else if (ab < (double)MIN_PS_INT32) /* (double)(int)0x80000000 */
+ make_real(op - 1, ab);
+ else
+ op[-1].value.intval = (ps_int)ab;
+ }
+ else {
+ double ab = (double)op[-1].value.intval * op->value.intval;
+ if (ab > (double)MAX_PS_INT) /* (double)0x7fffffffffffffff */
+ make_real(op - 1, ab);
+ else if (ab < (double)MIN_PS_INT) /* (double)(int64_t)0x8000000000000000 */
+ make_real(op - 1, ab);
+ else
+ op[-1].value.intval = (ps_int)ab;
+ }
+ }
+ }
+ }
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> sub <difference> */
+/* We make this into a separate procedure because */
+/* the interpreter will almost always call it directly. */
+int
+zop_sub(i_ctx_t *i_ctx_p)
+{
+ register os_ptr op = osp;
+
+ switch (r_type(op)) {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ switch (r_type(op - 1)) {
+ default:
+ return_op_typecheck(op - 1);
+ case t_real:
+ op[-1].value.realval -= op->value.realval;
+ break;
+ case t_integer:
+ make_real(op - 1, (double)op[-1].value.intval - op->value.realval);
+ }
+ break;
+ case t_integer:
+ switch (r_type(op - 1)) {
+ default:
+ return_op_typecheck(op - 1);
+ case t_real:
+ op[-1].value.realval -= (double)op->value.intval;
+ break;
+ case t_integer: {
+ if (sizeof(ps_int) != 4 && gs_currentcpsimode(imemory)) {
+ ps_int32 int1 = (ps_int)op[-1].value.intval;
+ ps_int32 int2 = (ps_int)op->value.intval;
+ ps_int32 int3;
+
+ if ((int1 ^ (int3 = int1 - int2)) < 0 &&
+ (int1 ^ int2) < 0
+ ) { /* Overflow, convert to real */
+ make_real(op - 1, (float)int1 - op->value.intval);
+ }
+ else {
+ op[-1].value.intval = (ps_int)int3;
+ }
+ }
+ else {
+ ps_int int1 = op[-1].value.intval;
+
+ if ((int1 ^ (op[-1].value.intval = int1 - op->value.intval)) < 0 &&
+ (int1 ^ op->value.intval) < 0
+ ) { /* Overflow, convert to real */
+ make_real(op - 1, (float)int1 - op->value.intval);
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+int
+zsub(i_ctx_t *i_ctx_p)
+{
+ int code = zop_sub(i_ctx_p);
+
+ if (code == 0) {
+ pop(1);
+ }
+ return code;
+}
+
+/* <num1> <num2> idiv <int_quotient> */
+int
+zidiv(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ check_type(op[-1], t_integer);
+ if (sizeof(ps_int) && gs_currentcpsimode(imemory)) {
+ int tmpval;
+ if ((op->value.intval == 0) || (op[-1].value.intval == (ps_int)MIN_PS_INT32 && op->value.intval == -1)) {
+ /* Anomalous boundary case: -MININT / -1, fail. */
+ return_error(gs_error_undefinedresult);
+ }
+ tmpval = (int)op[-1].value.intval / op->value.intval;
+ op[-1].value.intval = (int64_t)tmpval;
+ }
+ else {
+ if ((op->value.intval == 0) || (op[-1].value.intval == MIN_PS_INT && op->value.intval == -1)) {
+ /* Anomalous boundary case: -MININT / -1, fail. */
+ return_error(gs_error_undefinedresult);
+ }
+ op[-1].value.intval /= op->value.intval;
+ }
+ pop(1);
+ return 0;
+}
+
+/* <int1> <int2> mod <remainder> */
+int
+zmod(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ check_type(op[-1], t_integer);
+ if (op->value.intval == 0)
+ return_error(gs_error_undefinedresult);
+ op[-1].value.intval %= op->value.intval;
+ pop(1);
+ return 0;
+}
+
+/* <num1> neg <num2> */
+int
+zneg(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ op->value.realval = -op->value.realval;
+ break;
+ case t_integer:
+ if (sizeof(ps_int) != 32 && gs_currentcpsimode(imemory)) {
+ if (((unsigned int)op->value.intval) == MIN_PS_INT32)
+ make_real(op, -(float)(ps_uint32)MIN_PS_INT32);
+ else
+ op->value.intval = -op->value.intval;
+ }
+ else {
+ if (op->value.intval == MIN_PS_INT)
+ make_real(op, -(float)MIN_PS_INT);
+ else
+ op->value.intval = -op->value.intval;
+ }
+ }
+ return 0;
+}
+
+/* <num1> abs <num2> */
+int
+zabs(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ if (op->value.realval >= 0)
+ return 0;
+ break;
+ case t_integer:
+ if (op->value.intval >= 0)
+ return 0;
+ break;
+ }
+ return zneg(i_ctx_p);
+}
+
+/* <num1> ceiling <num2> */
+int
+zceiling(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ op->value.realval = ceil(op->value.realval);
+ case t_integer:;
+ }
+ return 0;
+}
+
+/* <num1> floor <num2> */
+int
+zfloor(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ op->value.realval = floor(op->value.realval);
+ case t_integer:;
+ }
+ return 0;
+}
+
+/* <num1> round <num2> */
+int
+zround(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ op->value.realval = floor(op->value.realval + 0.5);
+ case t_integer:;
+ }
+ return 0;
+}
+
+/* <num1> truncate <num2> */
+int
+ztruncate(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ default:
+ return_op_typecheck(op);
+ case t_real:
+ op->value.realval =
+ (op->value.realval < 0.0 ?
+ ceil(op->value.realval) :
+ floor(op->value.realval));
+ case t_integer:;
+ }
+ return 0;
+}
+
+/* Non-standard operators */
+
+/* <int1> <int2> .bitadd <sum> */
+static int
+zbitadd(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ check_type(op[-1], t_integer);
+ op[-1].value.intval += op->value.intval;
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization table ------ */
+
+const op_def zarith_op_defs[] =
+{
+ {"1abs", zabs},
+ {"2add", zadd},
+ {"2.bitadd", zbitadd},
+ {"1ceiling", zceiling},
+ {"2div", zdiv},
+ {"2idiv", zidiv},
+ {"1floor", zfloor},
+ {"2mod", zmod},
+ {"2mul", zmul},
+ {"1neg", zneg},
+ {"1round", zround},
+ {"2sub", zsub},
+ {"1truncate", ztruncate},
+ op_def_end(0)
+};
diff --git a/psi/zarray.c b/psi/zarray.c
new file mode 100644
index 000000000..659f2a590
--- /dev/null
+++ b/psi/zarray.c
@@ -0,0 +1,136 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Array operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "ialloc.h"
+#include "ipacked.h"
+#include "oper.h"
+#include "store.h"
+
+/* The generic operators (copy, get, put, getinterval, putinterval, */
+/* length, and forall) are implemented in zgeneric.c. */
+
+/* <int> array <array> */
+int
+zarray(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint size;
+ int code;
+
+ check_type(*op, t_integer);
+ if (op->value.intval < 0)
+ return_error(gs_error_rangecheck);
+ if (op->value.intval > max_array_size)
+ return_error(gs_error_limitcheck);
+ size = op->value.intval;
+ code = ialloc_ref_array((ref *)op, a_all, size, "array");
+ if (code < 0)
+ return code;
+ refset_null(op->value.refs, size);
+ return 0;
+}
+
+/* <array> aload <obj_0> ... <obj_n-1> <array> */
+static int
+zaload(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref aref;
+ uint asize;
+
+ ref_assign(&aref, op);
+ if (!r_is_array(&aref))
+ return_op_typecheck(op);
+ check_read(aref);
+ asize = r_size(&aref);
+ if (asize > ostop - op) { /* Use the slow, general algorithm. */
+ int code = ref_stack_push(&o_stack, asize);
+ uint i;
+ const ref_packed *packed = aref.value.packed;
+
+ if (code < 0)
+ return code;
+ for (i = asize; i > 0; i--, packed = packed_next(packed))
+ packed_get(imemory, packed, ref_stack_index(&o_stack, i));
+ *osp = aref;
+ return 0;
+ }
+ if (r_has_type(&aref, t_array))
+ memcpy(op, aref.value.refs, asize * sizeof(ref));
+ else {
+ uint i;
+ const ref_packed *packed = aref.value.packed;
+ os_ptr pdest = op;
+
+ for (i = 0; i < asize; i++, pdest++, packed = packed_next(packed))
+ packed_get(imemory, packed, pdest);
+ }
+ push(asize);
+ ref_assign(op, &aref);
+ return 0;
+}
+
+/* <obj_0> ... <obj_n-1> <array> astore <array> */
+static int
+zastore(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint size;
+ int code;
+
+ if (!r_is_array(op))
+ return_op_typecheck(op);
+ size = r_size(op);
+ /* Amazingly, the following is valid: 0 array noaccess astore */
+ if (size == 0)
+ return 0;
+ if (!r_has_type_attrs(op, t_array, a_write))
+ return_error(gs_error_invalidaccess);
+ if (size > op - osbot) {
+ /* The store operation might involve other stack segments. */
+ ref arr;
+
+ if (size >= ref_stack_count(&o_stack))
+ return_error(gs_error_stackunderflow);
+ arr = *op;
+ code = ref_stack_store(&o_stack, &arr, size, 1, 0, true, idmemory,
+ "astore");
+ if (code < 0)
+ return code;
+ ref_stack_pop(&o_stack, size);
+ *ref_stack_index(&o_stack, 0) = arr;
+ } else {
+ code = refcpy_to_old(op, 0, op - size, size, idmemory, "astore");
+ if (code < 0)
+ return code;
+ op[-(int)size] = *op;
+ pop(size);
+ }
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zarray_op_defs[] =
+{
+ {"1aload", zaload},
+ {"1array", zarray},
+ {"1astore", zastore},
+ op_def_end(0)
+};
diff --git a/psi/zbfont.c b/psi/zbfont.c
new file mode 100644
index 000000000..8c8237403
--- /dev/null
+++ b/psi/zbfont.c
@@ -0,0 +1,935 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Font creation utilities */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gxfixed.h"
+#include "gscencs.h"
+#include "gsmatrix.h"
+#include "gxdevice.h"
+#include "gxfont.h"
+#include "bfont.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ilevel.h"
+#include "iname.h"
+#include "inamedef.h" /* for inlining name_index */
+#include "interp.h" /* for initial_enter_name */
+#include "ipacked.h"
+#include "istruct.h"
+#include "store.h"
+#include "gsstate.h" /* for gs_currentcpsimode() */
+/* Structure descriptor and GC procedures for font_data */
+public_st_font_data();
+static
+CLEAR_MARKS_PROC(font_data_clear_marks)
+{
+ ref_struct_clear_marks(cmem, vptr, offset_of(font_data, u.type42.mru_sfnts_index)/*size*/, pstype);
+}
+static
+ENUM_PTRS_BEGIN_PROC(font_data_enum_ptrs)
+{
+ return ref_struct_enum_ptrs(mem, vptr, offset_of(font_data, u.type42.mru_sfnts_index)/*size*/, index, pep, pstype, gcst);
+}
+ENUM_PTRS_END_PROC
+static
+RELOC_PTRS_BEGIN(font_data_reloc_ptrs)
+{
+ ref_struct_reloc_ptrs(vptr, offset_of(font_data, u.type42.mru_sfnts_index)/*size*/, pstype, gcst);
+}
+RELOC_PTRS_END
+
+/* <string|name> <font_dict> .buildfont3 <string|name> <font> */
+/* Build a type 3 (user-defined) font. */
+static int
+zbuildfont3(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ build_proc_refs build;
+ gs_font_base *pfont;
+
+ check_type(*op, t_dictionary);
+ code = build_gs_font_procs(op, &build);
+ if (code < 0)
+ return code;
+ code = build_gs_simple_font(i_ctx_p, op, &pfont, ft_user_defined,
+ &st_gs_font_base, &build, bf_options_none);
+ if (code < 0)
+ return code;
+ return define_gs_font(i_ctx_p, (gs_font *) pfont);
+}
+
+/* Encode a character. */
+gs_glyph
+zfont_encode_char(gs_font *pfont, gs_char chr, gs_glyph_space_t gspace)
+{
+ font_data *pdata = pfont_data(pfont);
+ const ref *pencoding = &pdata->Encoding;
+ ulong index = chr; /* work around VAX widening bug */
+ ref cname;
+ int code = array_get(pfont->memory, pencoding, (long)index, &cname);
+
+ if (code < 0 || !r_has_type(&cname, t_name))
+ return gs_no_glyph;
+ if (pfont->FontType == ft_user_defined && r_type(&pdata->BuildGlyph) == t_null) {
+ ref nsref, tname;
+
+ name_string_ref(pfont->memory, &cname, &nsref);
+ if (r_size(&nsref) == 7 &&
+ !memcmp(nsref.value.const_bytes, ".notdef", r_size(&nsref))) {
+ /* A special support for high level devices.
+ They need a glyph name but the font doesn't provide one
+ due to an instandard BuildChar.
+ Such fonts don't conform to PLRM section 5.3.7,
+ but we've got real examples that we want to handle (Bug 686982).
+ Construct a name here.
+ Low level devices don't pass here, because regular PS interpretation
+ doesn't need such names.
+ */
+ char buf[20];
+ int code;
+
+ if (gspace == GLYPH_SPACE_NOGEN)
+ return gs_no_glyph;
+ gs_sprintf(buf, "j%ld", chr); /* 'j' is arbutrary. */
+ code = name_ref(pfont->memory, (const byte *)buf, strlen(buf), &tname, 1);
+ if (code < 0) {
+ /* Can't propagate the error due to interface limitation,
+ return with .notdef */
+ } else
+ cname = tname;
+ }
+ }
+ return (gs_glyph)name_index(pfont->memory, &cname);
+}
+
+/* Get the name of a glyph. */
+static int
+zfont_glyph_name(gs_font *font, gs_glyph index, gs_const_string *pstr)
+{
+ ref nref, sref;
+
+ if (index >= gs_min_cid_glyph) { /* Fabricate a numeric name. */
+ char cid_name[sizeof(gs_glyph) * 3 + 1];
+ int code;
+
+ gs_sprintf(cid_name, "%lu", (ulong) index);
+ code = name_ref(font->memory, (const byte *)cid_name, strlen(cid_name),
+ &nref, 1);
+ if (code < 0)
+ return code;
+ } else
+ name_index_ref(font->memory, index, &nref);
+ name_string_ref(font->memory, &nref, &sref);
+ pstr->data = sref.value.const_bytes;
+ pstr->size = r_size(&sref);
+ return 0;
+}
+
+static gs_char
+gs_font_map_glyph_by_dict(const gs_memory_t *mem, const ref *map, gs_glyph glyph)
+{
+ ref *v, n;
+ if (glyph >= gs_min_cid_glyph) {
+ uint cid = glyph - gs_min_cid_glyph;
+
+ if (dict_find_string(map, "CIDCount", &v) > 0) {
+ /* This is a CIDDEcoding resource. */
+ make_int(&n, cid / 256);
+ if (dict_find(map, &n, &v) > 0) {
+ ref vv;
+
+ if (array_get(mem, v, cid % 256, &vv) == 0 && r_type(&vv) == t_integer)
+ return vv.value.intval;
+ }
+ return GS_NO_CHAR; /* Absent in the map. */
+ }
+ /* This is GlyphNames2Unicode dictionary. */
+ make_int(&n, cid);
+ } else
+ name_index_ref(mem, glyph, &n);
+ if (dict_find(map, &n, &v) > 0) {
+ if (r_has_type(v, t_string)) {
+ int i, l = r_size(v);
+ gs_char c = 0;
+
+ for (i = 0; i < l; i++)
+ c = (c << 8) | v->value.const_bytes[i];
+ return c;
+ }
+ if (r_type(v) == t_integer)
+ return v->value.intval;
+ }
+ return GS_NO_CHAR; /* Absent in the map. */
+}
+
+/* Get Unicode UTF-16 code for a glyph. */
+gs_char
+gs_font_map_glyph_to_unicode(gs_font *font, gs_glyph glyph, int ch)
+{
+ font_data *pdata = pfont_data(font);
+ const ref *UnicodeDecoding;
+
+ if (r_type(&pdata->GlyphNames2Unicode) == t_dictionary) {
+ gs_char c = gs_font_map_glyph_by_dict(font->memory,
+ &pdata->GlyphNames2Unicode, glyph);
+
+ if (c != GS_NO_CHAR)
+ return c;
+ if (ch != -1) { /* -1 indicates a CIDFont */
+ /* Its possible that we have a GlyphNames2Unicode dictionary
+ * which contains integers and Unicode values, rather than names
+ * and Unicode values. This happens if the input was PDF, the font
+ * has a ToUnicode Cmap, but no Encoding. In this case we need to
+ * use the character code as an index into the dictionary. Try that
+ * now before we fall back to the UnicodeDecoding.
+ */
+ ref *v, n;
+
+ make_int(&n, ch);
+ if (dict_find(&pdata->GlyphNames2Unicode, &n, &v) > 0) {
+ if (r_has_type(v, t_string)) {
+ int i, l = r_size(v);
+ gs_char c = 0;
+
+ for (i = 0; i < l; i++)
+ c = (c << 8) | v->value.const_bytes[i];
+ return c;
+ }
+ if (r_type(v) == t_integer)
+ return v->value.intval;
+ }
+ }
+ /*
+ * Fall through, because test.ps for SF bug #529103 requres
+ * to examine both tables. Due to that the Unicode Decoding resource
+ * can't be a default value for FontInfo.GlyphNames2Unicode .
+ */
+ }
+ if (glyph <= GS_MIN_CID_GLYPH) {
+ UnicodeDecoding = zfont_get_to_unicode_map(font->dir);
+ if (UnicodeDecoding != NULL && r_type(UnicodeDecoding) == t_dictionary)
+ return gs_font_map_glyph_by_dict(font->memory, UnicodeDecoding, glyph);
+ }
+ return GS_NO_CHAR; /* No map. */
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zbfont_op_defs[] =
+{
+ {"2.buildfont3", zbuildfont3},
+ op_def_end(0)
+};
+
+/* ------ Subroutines ------ */
+
+/* Convert strings to executable names for build_proc_refs. */
+int
+build_proc_name_refs(const gs_memory_t *mem, build_proc_refs * pbuild,
+ const char *bcstr, const char *bgstr)
+{
+ int code;
+
+ if (!bcstr)
+ make_null(&pbuild->BuildChar);
+ else {
+ if ((code = name_ref(mem, (const byte *)bcstr,
+ strlen(bcstr), &pbuild->BuildChar, 0)) < 0)
+ return code;
+ r_set_attrs(&pbuild->BuildChar, a_executable);
+ }
+ if (!bgstr)
+ make_null(&pbuild->BuildGlyph);
+ else {
+ if ((code = name_ref(mem, (const byte *)bgstr,
+ strlen(bgstr), &pbuild->BuildGlyph, 0)) < 0)
+ return code;
+ r_set_attrs(&pbuild->BuildGlyph, a_executable);
+ }
+ return 0;
+}
+
+/* Get the BuildChar and/or BuildGlyph routines from a (base) font. */
+int
+build_gs_font_procs(os_ptr op, build_proc_refs * pbuild)
+{
+ int ccode, gcode;
+ ref *pBuildChar;
+ ref *pBuildGlyph;
+
+ check_type(*op, t_dictionary);
+ ccode = dict_find_string(op, "BuildChar", &pBuildChar);
+ gcode = dict_find_string(op, "BuildGlyph", &pBuildGlyph);
+ if (ccode <= 0) {
+ if (gcode <= 0)
+ return_error(gs_error_invalidfont);
+ make_null(&pbuild->BuildChar);
+ } else {
+ check_proc(*pBuildChar);
+ pbuild->BuildChar = *pBuildChar;
+ }
+ if (gcode <= 0)
+ make_null(&pbuild->BuildGlyph);
+ else {
+ check_proc(*pBuildGlyph);
+ pbuild->BuildGlyph = *pBuildGlyph;
+ }
+ return 0;
+}
+
+static int font_with_same_UID_and_another_metrics(const gs_font *pfont0, const gs_font *pfont1)
+{
+ const gs_font_base *pbfont0 = (const gs_font_base *)pfont0;
+ const gs_font_base *pbfont1 = (const gs_font_base *)pfont1;
+
+ if (uid_equal(&pbfont0->UID, &pbfont1->UID)) {
+ const ref *pfdict0 = &pfont_data(gs_font_parent(pbfont0))->dict;
+ const ref *pfdict1 = &pfont_data(gs_font_parent(pbfont1))->dict;
+ ref *pmdict0, *pmdict1;
+
+ if (pbfont0->WMode || dict_find_string(pfdict0, "Metrics", &pmdict0) <= 0)
+ pmdict0 = NULL;
+ if (pbfont1->WMode || dict_find_string(pfdict1, "Metrics", &pmdict1) <= 0)
+ pmdict1 = NULL;
+ if (!pmdict0 != !pmdict1)
+ return 1;
+ if (pmdict0 != NULL && !obj_eq(pfont0->memory, pmdict0, pmdict1))
+ return 1;
+ if (!pbfont0->WMode || dict_find_string(pfdict0, "Metrics2", &pmdict0) <= 0)
+ pmdict0 = NULL;
+ if (!pbfont0->WMode || dict_find_string(pfdict1, "Metrics2", &pmdict1) <= 0)
+ pmdict1 = NULL;
+ if (!pmdict0 != !pmdict1)
+ return 1;
+ if (pmdict0 != NULL && !obj_eq(pfont0->memory, pmdict0, pmdict1))
+ return 1;
+ }
+ return 0;
+}
+
+/* Do the common work for building a primitive font -- one whose execution */
+/* algorithm is implemented in C (Type 1, Type 2, Type 4, or Type 42). */
+/* The caller guarantees that *op is a dictionary. */
+int
+build_gs_primitive_font(i_ctx_t *i_ctx_p, os_ptr op, gs_font_base ** ppfont,
+ font_type ftype, gs_memory_type_ptr_t pstype,
+ const build_proc_refs * pbuild,
+ build_font_options_t options)
+{
+ ref *pcharstrings = 0;
+ ref CharStrings;
+ gs_font_base *pfont;
+ font_data *pdata;
+ int code;
+
+ if (dict_find_string(op, "CharStrings", &pcharstrings) <= 0) {
+ if (!(options & bf_CharStrings_optional))
+ return_error(gs_error_invalidfont);
+ } else {
+ ref *ignore;
+
+ if (!r_has_type(pcharstrings, t_dictionary))
+ return_error(gs_error_invalidfont);
+ if ((options & bf_notdef_required) != 0 &&
+ dict_find_string(pcharstrings, ".notdef", &ignore) <= 0
+ )
+ return_error(gs_error_invalidfont);
+ /*
+ * Since build_gs_simple_font may resize the dictionary and cause
+ * pointers to become invalid, save CharStrings.
+ */
+ CharStrings = *pcharstrings;
+ }
+ code = build_gs_outline_font(i_ctx_p, op, ppfont, ftype, pstype, pbuild,
+ options, build_gs_simple_font);
+ if (code != 0)
+ return code;
+ pfont = *ppfont;
+ pdata = pfont_data(pfont);
+ if (pcharstrings)
+ ref_assign(&pdata->CharStrings, &CharStrings);
+ else
+ make_null(&pdata->CharStrings);
+ /* Check that the UniqueIDs match. This is part of the */
+ /* Adobe protection scheme, but we may as well emulate it. */
+ if (uid_is_valid(&pfont->UID) &&
+ !dict_check_uid_param(op, &pfont->UID)
+ )
+ uid_set_invalid(&pfont->UID);
+ if (uid_is_valid(&pfont->UID)) {
+ const gs_font *pfont0 = (const gs_font *)pfont;
+
+ code = gs_font_find_similar(ifont_dir, &pfont0,
+ font_with_same_UID_and_another_metrics);
+ if (code < 0)
+ return code; /* Must not happen. */
+ if (code)
+ uid_set_invalid(&pfont->UID);
+ }
+ return 0;
+}
+
+/* Build a FDArray entry for a CIDFontType 0 font. */
+/* Note that as of Adobe PostScript version 3011, this may be either */
+/* a Type 1 or Type 2 font. */
+static int
+build_FDArray_sub_font(i_ctx_t *i_ctx_p, ref *op,
+ gs_font_base **ppfont,
+ font_type ftype, gs_memory_type_ptr_t pstype,
+ const build_proc_refs * pbuild,
+ build_font_options_t ignore_options)
+{
+ gs_font *pfont;
+ int code = build_gs_sub_font(i_ctx_p, op, &pfont, ftype, pstype, pbuild,
+ NULL, op);
+
+ if (code >= 0)
+ *ppfont = (gs_font_base *)pfont;
+ return code;
+}
+int
+build_gs_FDArray_font(i_ctx_t *i_ctx_p, ref *op,
+ gs_font_base **ppfont,
+ font_type ftype, gs_memory_type_ptr_t pstype,
+ const build_proc_refs * pbuild)
+{
+ gs_font_base *pfont;
+ font_data *pdata;
+ int code = build_gs_outline_font(i_ctx_p, op, &pfont, ftype, pstype,
+ pbuild, bf_options_none,
+ build_FDArray_sub_font);
+ static const double bbox[4] = { 0, 0, 0, 0 };
+ gs_uid uid;
+
+ if (code < 0)
+ return code;
+ pdata = pfont_data(pfont);
+ /* Fill in members normally set by build_gs_primitive_font. */
+ make_null(&pdata->CharStrings);
+ /* Fill in members normally set by build_gs_simple_font. */
+ uid_set_invalid(&uid);
+ init_gs_simple_font(pfont, bbox, &uid);
+ pfont->encoding_index = ENCODING_INDEX_UNKNOWN;
+ pfont->nearest_encoding_index = ENCODING_INDEX_UNKNOWN;
+ /* Fill in members normally set by build_gs_font. */
+ pfont->key_name = pfont->font_name;
+ *ppfont = pfont;
+ return 0;
+}
+
+/* Do the common work for building an outline font -- a non-composite font */
+/* for which PaintType and StrokeWidth are meaningful. */
+/* The caller guarantees that *op is a dictionary. */
+int
+build_gs_outline_font(i_ctx_t *i_ctx_p, os_ptr op, gs_font_base ** ppfont,
+ font_type ftype, gs_memory_type_ptr_t pstype,
+ const build_proc_refs * pbuild,
+ build_font_options_t options,
+ build_base_font_proc_t build_base_font)
+{
+ int painttype;
+ float strokewidth;
+ gs_font_base *pfont;
+ int code = dict_int_param(op, "PaintType", 0, 3, 0, &painttype);
+
+ if (code < 0)
+ return code;
+ code = dict_float_param(op, "StrokeWidth", 0.0, &strokewidth);
+ if (code < 0)
+ return code;
+ code = build_base_font(i_ctx_p, op, ppfont, ftype, pstype, pbuild,
+ options);
+ if (code != 0)
+ return code;
+ pfont = *ppfont;
+ pfont->PaintType = painttype;
+ pfont->StrokeWidth = strokewidth;
+ return 0;
+}
+
+void
+get_GlyphNames2Unicode(i_ctx_t *i_ctx_p, gs_font *pfont, ref *pdref)
+{
+ ref *pfontinfo = NULL, *g2u = NULL;
+ font_data *pdata;
+
+ if (dict_find_string(pdref, "FontInfo", &pfontinfo) <= 0 ||
+ !r_has_type(pfontinfo, t_dictionary) ||
+ dict_find_string(pfontinfo, "GlyphNames2Unicode", &g2u) <= 0 ||
+ !r_has_type(pfontinfo, t_dictionary))
+ return;
+ /*
+ * Since build_gs_font may resize the dictionary and cause
+ * pointers to become invalid, save Glyph2Unicode
+ */
+ pdata = pfont_data(pfont);
+ ref_assign_new(&pdata->GlyphNames2Unicode, g2u);
+}
+
+/* Do the common work for building a font of any non-composite FontType. */
+/* The caller guarantees that *op is a dictionary. */
+int
+build_gs_simple_font(i_ctx_t *i_ctx_p, os_ptr op, gs_font_base ** ppfont,
+ font_type ftype, gs_memory_type_ptr_t pstype,
+ const build_proc_refs * pbuild,
+ build_font_options_t options)
+{
+ double bbox[4];
+ gs_uid uid;
+ int code;
+ gs_font_base *pfont;
+ uint space = ialloc_space(idmemory);
+
+ code = font_bbox_param(imemory, op, bbox);
+ if (code < 0)
+ return code;
+ /*
+ * Make sure that we allocate uid
+ * in the same VM as the font dictionary
+ * (see build_gs_sub_font).
+ */
+ ialloc_set_space(idmemory, r_space(op));
+ code = dict_uid_param(op, &uid, 0, imemory, i_ctx_p);
+ ialloc_set_space(idmemory, space);
+ if (code < 0)
+ return code;
+ if ((options & bf_UniqueID_ignored) && uid_is_UniqueID(&uid))
+ uid_set_invalid(&uid);
+ code = build_gs_font(i_ctx_p, op, (gs_font **) ppfont, ftype, pstype,
+ pbuild, options);
+ if (code != 0) /* invalid or scaled font */
+ return code;
+ pfont = *ppfont;
+ pfont->procs.init_fstack = gs_default_init_fstack;
+ pfont->procs.define_font = gs_no_define_font;
+ pfont->procs.decode_glyph = gs_font_map_glyph_to_unicode;
+ pfont->procs.make_font = zbase_make_font;
+ pfont->procs.next_char_glyph = gs_default_next_char_glyph;
+ pfont->FAPI = 0;
+ pfont->FAPI_font_data = 0;
+ init_gs_simple_font(pfont, bbox, &uid);
+ lookup_gs_simple_font_encoding(pfont);
+ get_GlyphNames2Unicode(i_ctx_p, (gs_font *)pfont, op);
+ return 0;
+}
+
+/* Initialize the FontBBox and UID of a non-composite font. */
+void
+init_gs_simple_font(gs_font_base *pfont, const double bbox[4],
+ const gs_uid *puid)
+{
+ pfont->FontBBox.p.x = bbox[0];
+ pfont->FontBBox.p.y = bbox[1];
+ pfont->FontBBox.q.x = bbox[2];
+ pfont->FontBBox.q.y = bbox[3];
+ pfont->UID = *puid;
+}
+
+/* Compare the encoding of a simple font with the registered encodings. */
+void
+lookup_gs_simple_font_encoding(gs_font_base * pfont)
+{
+ const ref *pfe = &pfont_data(pfont)->Encoding;
+ int index = -1;
+
+ pfont->encoding_index = index;
+ if (r_type(pfe) == t_array && r_size(pfe) <= 256) {
+ /* Look for an encoding that's "close". */
+ uint esize = r_size(pfe);
+ int near_index = -1;
+ uint best = esize / 3; /* must match at least this many */
+ gs_const_string fstrs[256];
+ int i;
+
+ /* Get the string names of the glyphs in the font's Encoding. */
+ for (i = 0; i < esize; ++i) {
+ ref fchar;
+
+ if (array_get(pfont->memory, pfe, (long)i, &fchar) < 0 ||
+ !r_has_type(&fchar, t_name)
+ )
+ fstrs[i].data = 0, fstrs[i].size = 0;
+ else {
+ ref nsref;
+
+ name_string_ref(pfont->memory, &fchar, &nsref);
+ fstrs[i].data = nsref.value.const_bytes;
+ fstrs[i].size = r_size(&nsref);
+ }
+ }
+ /* Compare them against the known encodings. */
+ for (index = 0; index < NUM_KNOWN_REAL_ENCODINGS; ++index) {
+ uint match = esize;
+
+ for (i = esize; --i >= 0;) {
+ gs_const_string rstr;
+
+ gs_c_glyph_name(gs_c_known_encode((gs_char)i, index), &rstr);
+ if (rstr.size == fstrs[i].size &&
+ !memcmp(rstr.data, fstrs[i].data, rstr.size)
+ )
+ continue;
+ if (--match <= best)
+ break;
+ }
+ if (match > best) {
+ best = match;
+ near_index = index;
+ /* If we have a perfect match, stop now. */
+ if (best == esize)
+ break;
+ }
+ }
+ index = near_index;
+ if (best == esize)
+ pfont->encoding_index = index;
+ }
+ pfont->nearest_encoding_index = index;
+}
+
+/* Get FontMatrix and FontName parameters. */
+static int
+sub_font_params(gs_memory_t *mem, const ref *op, gs_matrix *pmat, gs_matrix *pomat, ref *pfname)
+{
+ ref *pmatrix, *pfontname, *pfontstyle, *porigfont, *pfontinfo;
+
+ if (dict_find_string(op, "FontMatrix", &pmatrix) <= 0 ||
+ read_matrix(mem, pmatrix, pmat) < 0
+ )
+ return_error(gs_error_invalidfont);
+ if (dict_find_string(op, "OrigFont", &porigfont) <= 0)
+ porigfont = NULL;
+ if (pomat!= NULL) {
+ if (porigfont == NULL ||
+ dict_find_string(porigfont, "FontMatrix", &pmatrix) <= 0 ||
+ read_matrix(mem, pmatrix, pomat) < 0
+ )
+ memset(pomat, 0, sizeof(*pomat));
+ }
+ /* Use the FontInfo/OrigFontName key preferrentially (created by MS PSCRIPT driver) */
+ if ((dict_find_string((porigfont != NULL ? porigfont : op), "FontInfo", &pfontinfo) > 0) &&
+ r_has_type(pfontinfo, t_dictionary) &&
+ (dict_find_string(pfontinfo, "OrigFontName", &pfontname) > 0)) {
+ if ((dict_find_string(pfontinfo, "OrigFontStyle", &pfontstyle) > 0) &&
+ r_size(pfontstyle) > 0) {
+ const byte *tmpStr1 = pfontname->value.const_bytes;
+ const byte *tmpStr2 = pfontstyle->value.const_bytes;
+ int fssize1 = r_size(pfontname), fssize2 = r_size(pfontstyle), fssize = fssize1 + fssize2 + 1;
+ byte *sfname = gs_alloc_string(mem, fssize, "sub_font_params");
+
+ if (sfname == NULL)
+ return_error(gs_error_VMerror);
+ memcpy(sfname, tmpStr1, fssize1);
+ sfname[fssize1]=',' ;
+ memcpy(sfname + fssize1 + 1, tmpStr2, fssize2);
+ make_string(pfname, a_readonly, fssize, sfname);
+ } else
+ get_font_name(mem, pfname, pfontname);
+ } else if (dict_find_string((porigfont != NULL ? porigfont : op), ".Alias", &pfontname) > 0) {
+ /* If we emulate the font, we want the requested name rather than a substitute. */
+ get_font_name(mem, pfname, pfontname);
+ } else if (dict_find_string((porigfont != NULL ? porigfont : op), "FontName", &pfontname) > 0) {
+ get_font_name(mem, pfname, pfontname);
+ } else
+ make_empty_string(pfname, a_readonly);
+ return 0;
+}
+
+/* Do the common work for building a font of any FontType. */
+/* The caller guarantees that *op is a dictionary. */
+/* op[-1] must be the key under which the font is being registered */
+/* in FontDirectory, normally a name or string. */
+/* Return 0 for a new font, 1 for a font made by makefont or scalefont, */
+/* or a negative error code. */
+int
+build_gs_font(i_ctx_t *i_ctx_p, os_ptr op, gs_font ** ppfont, font_type ftype,
+ gs_memory_type_ptr_t pstype, const build_proc_refs * pbuild,
+ build_font_options_t options)
+{
+ ref kname; /* t_string */
+ ref *pftype;
+ ref *pencoding = 0;
+ bool bitmapwidths;
+ int exactsize, inbetweensize, transformedchar;
+ int wmode;
+ int code;
+ gs_font *pfont;
+ ref *pfid;
+ ref *aop = dict_access_ref(op);
+ bool cpsi_mode = gs_currentcpsimode(imemory);
+
+ get_font_name(imemory, &kname, op - 1);
+ if (dict_find_string(op, "FontType", &pftype) <= 0 ||
+ !r_has_type(pftype, t_integer) ||
+ pftype->value.intval != (int)ftype
+ )
+ return_error(gs_error_invalidfont);
+ if (dict_find_string(op, "Encoding", &pencoding) <= 0) {
+ if (!(options & bf_Encoding_optional))
+ return_error(gs_error_invalidfont);
+ pencoding = 0;
+ } else {
+ if (!r_is_array(pencoding))
+ return_error(gs_error_invalidfont);
+ }
+ if (pencoding) { /* observed Adobe behavior */
+ int count = r_size(pencoding);
+ int type = ftype ? t_name : t_integer;
+ bool fixit = false;
+
+ while (count--) {
+ ref r;
+ if ((code = array_get(imemory, pencoding, count, &r)) < 0 ||
+ !(r_has_type(&r, type) || r_has_type(&r, t_null))) {
+ if (!cpsi_mode && ftype == ft_user_defined) {
+ if (code < 0 || r_has_type(&r, t_null)) {
+ return_error(gs_error_typecheck);
+ }
+ fixit = true;
+ break;
+ }
+ else {
+ return_error(gs_error_typecheck);
+ }
+ }
+ }
+
+ /* For at least Type 3 fonts, Adobe Distiller will "fix" an Encoding array, as in, for example
+ * Bug 692681 where the arrays contain integers rather than names. Once the font is instantiated
+ * the integers have been converted to names.
+ * It is preferable to to this manipulation here, rather than in Postscript, because we are less
+ * restricted by read-only attributes and VM save levels.
+ */
+ if (fixit) {
+ ref penc;
+ uint size = 0;
+ char buf[32], *bptr;
+ avm_space curglob = ialloc_space(idmemory);
+ avm_space useglob = r_is_local(pencoding) ? avm_local : avm_global;
+
+ ialloc_set_space(idmemory, useglob);
+
+ count = r_size(pencoding);
+ if ((code = ialloc_ref_array(&penc, (r_type_attrs(pencoding) & a_readonly), count, "build_gs_font")) < 0)
+ return code;
+
+ while (count--) {
+ ref r;
+ if (array_get(imemory, pencoding, count, &r) < 0){
+ return_error(gs_error_typecheck);
+ }
+ /* For type 3, we know the Encoding entries must be names */
+ if (r_has_type(&r, t_name)){
+ ref_assign(&(penc.value.refs[count]), &r);
+ }
+ else {
+
+ if ((code = obj_cvs(imemory, &r, (byte *)buf, 32, &size, (const byte **)(&bptr))) < 0) {
+ return(code);
+ }
+ if ((code = name_ref(imemory, (const byte *)bptr, size, &r, true)) < 0)
+ return code;
+ ref_assign(&(penc.value.refs[count]), &r);
+ }
+ }
+
+ if ((code = dict_put_string(osp, "Encoding", &penc, NULL)) < 0)
+ return code;
+ ialloc_set_space(idmemory, curglob);
+ }
+ }
+ if ((code = dict_int_param(op, "WMode", 0, 1, 0, &wmode)) < 0 ||
+ (code = dict_bool_param(op, "BitmapWidths", false, &bitmapwidths)) < 0 ||
+ (code = dict_int_param(op, "ExactSize", 0, 2, fbit_use_bitmaps, &exactsize)) < 0 ||
+ (code = dict_int_param(op, "InBetweenSize", 0, 2, fbit_use_outlines, &inbetweensize)) < 0 ||
+ (code = dict_int_param(op, "TransformedChar", 0, 2, fbit_use_outlines, &transformedchar)) < 0
+ )
+ return code;
+ code = dict_find_string(op, "FID", &pfid);
+ if (code > 0 && r_has_type(pfid, t_fontID)) { /* silently ignore invalid FID per CET 13-05.ps */
+ /*
+ * If this font has a FID entry already, it might be a scaled font
+ * made by makefont or scalefont; in a Level 2 environment, it might
+ * be an existing font being registered under a second name, or a
+ * re-encoded font (which was invalid in Level 1, but dvips did it
+ * anyway).
+ */
+ pfont = r_ptr(pfid, gs_font);
+ /*
+ * If the following condition is false this is a re-encoded font,
+ * or some other questionable situation in which the FID
+ * was preserved. Pretend the FID wasn't there.
+ */
+ if (obj_eq(pfont->memory, pfont_dict(pfont), op)) {
+ if (pfont->base == pfont) { /* original font */
+ if (!level2_enabled)
+ return_error(gs_error_invalidfont);
+ *ppfont = pfont;
+ return 1;
+ } else { /* This was made by makefont or scalefont. */
+ /* Just insert the new name. */
+ gs_matrix mat;
+ ref fname; /* t_string */
+
+ code = sub_font_params(imemory, op, &mat, NULL, &fname);
+ if (code < 0)
+ return code;
+ code = 1;
+ copy_font_name(&pfont->font_name, &fname);
+ goto set_name;
+ }
+ }
+ }
+ /* This is a new font. */
+ if (!r_has_attr(aop, a_write))
+ return_error(gs_error_invalidaccess);
+ {
+ ref encoding;
+
+ /*
+ * Since add_FID may resize the dictionary and cause
+ * pencoding to become invalid, save the Encoding.
+ */
+ if (pencoding) {
+ encoding = *pencoding;
+ pencoding = &encoding;
+ }
+ code = build_gs_sub_font(i_ctx_p, op, &pfont, ftype, pstype,
+ pbuild, pencoding, op);
+ if (code < 0)
+ return code;
+ }
+ pfont->BitmapWidths = bitmapwidths;
+ pfont->ExactSize = (fbit_type)exactsize;
+ pfont->InBetweenSize = (fbit_type)inbetweensize;
+ pfont->TransformedChar = (fbit_type)transformedchar;
+ pfont->WMode = wmode;
+ pfont->procs.font_info = zfont_info;
+ code = 0;
+set_name:
+ copy_font_name(&pfont->key_name, &kname);
+ *ppfont = pfont;
+ return code;
+}
+
+/* Create a sub-font -- a font or an entry in the FDArray of a CIDFontType 0 */
+/* font. Default BitmapWidths, ExactSize, InBetweenSize, TransformedChar, */
+/* and WMode; do not fill in key_name. */
+/* The caller guarantees that *op is a dictionary. */
+int
+build_gs_sub_font(i_ctx_t *i_ctx_p, const ref *op, gs_font **ppfont,
+ font_type ftype, gs_memory_type_ptr_t pstype,
+ const build_proc_refs * pbuild, const ref *pencoding,
+ ref *fid_op)
+{
+ gs_matrix mat, omat;
+ ref fname; /* t_string */
+ gs_font *pfont;
+ font_data *pdata;
+ /*
+ * Make sure that we allocate the font data
+ * in the same VM as the font dictionary.
+ */
+ uint space = ialloc_space(idmemory);
+ int code = sub_font_params(imemory, op, &mat, &omat, &fname);
+
+ if (code < 0)
+ return code;
+ ialloc_set_space(idmemory, r_space(op));
+ pfont = gs_font_alloc(imemory, pstype, &gs_font_procs_default, NULL,
+ "buildfont(font)");
+ pdata = ialloc_struct(font_data, &st_font_data,
+ "buildfont(data)");
+ if (pfont == 0 || pdata == 0)
+ code = gs_note_error(gs_error_VMerror);
+ else if (fid_op)
+ code = add_FID(i_ctx_p, fid_op, pfont, iimemory);
+ if (code < 0) {
+ ifree_object(pdata, "buildfont(data)");
+ ifree_object(pfont, "buildfont(font)");
+ ialloc_set_space(idmemory, space);
+ return code;
+ }
+ refset_null((ref *) pdata, sizeof(font_data) / sizeof(ref));
+ ref_assign_new(&pdata->dict, op);
+ ref_assign_new(&pdata->BuildChar, &pbuild->BuildChar);
+ ref_assign_new(&pdata->BuildGlyph, &pbuild->BuildGlyph);
+ if (pencoding)
+ ref_assign_new(&pdata->Encoding, pencoding);
+ pfont->client_data = pdata;
+ pfont->FontType = ftype;
+ pfont->FontMatrix = mat;
+ pfont->orig_FontMatrix = omat;
+ pfont->BitmapWidths = false;
+ pfont->ExactSize = fbit_use_bitmaps;
+ pfont->InBetweenSize = fbit_use_outlines;
+ pfont->TransformedChar = fbit_use_outlines;
+ pfont->WMode = 0;
+ pfont->procs.encode_char = zfont_encode_char;
+ pfont->procs.glyph_name = zfont_glyph_name;
+ ialloc_set_space(idmemory, space);
+ copy_font_name(&pfont->font_name, &fname);
+ *ppfont = pfont;
+ return 0;
+}
+
+/* Get the string corresponding to a font name. */
+/* If the font name isn't a name or a string, return an empty string. */
+void
+get_font_name(const gs_memory_t *mem, ref * pfname, const ref * op)
+{
+ switch (r_type(op)) {
+ case t_string:
+ *pfname = *op;
+ break;
+ case t_name:
+ name_string_ref(mem, op, pfname);
+ break;
+ default:
+ /* This is weird, but legal.... */
+ make_empty_string(pfname, a_readonly);
+ }
+}
+
+/* Copy a font name into the gs_font structure. */
+void
+copy_font_name(gs_font_name * pfstr, const ref * pfname)
+{
+ uint size = r_size(pfname);
+
+ if (size > gs_font_name_max)
+ size = gs_font_name_max;
+ memcpy(&pfstr->chars[0], pfname->value.const_bytes, size);
+ /* Following is only for debugging printout. */
+ pfstr->chars[size] = 0;
+ pfstr->size = size;
+}
+
+/* Finish building a font, by calling gs_definefont if needed. */
+int
+define_gs_font(i_ctx_t *i_ctx_p, gs_font * pfont)
+{
+ return (pfont->base == pfont && pfont->dir == 0 ?
+ /* i.e., unregistered original font */
+ gs_definefont(ifont_dir, pfont) :
+ 0);
+}
diff --git a/psi/zbseq.c b/psi/zbseq.c
new file mode 100644
index 000000000..40a394919
--- /dev/null
+++ b/psi/zbseq.c
@@ -0,0 +1,150 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 2 binary object sequence operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "gxalloc.h" /* for names_array in allocator */
+#include "oper.h"
+#include "ialloc.h"
+#include "istruct.h"
+#include "btoken.h"
+#include "store.h"
+
+/*
+ * Define the structure that holds the t_*array ref for the system or
+ * user name table.
+ */
+typedef struct names_array_ref_s {
+ ref names;
+} names_array_ref_t;
+gs_private_st_ref_struct(st_names_array_ref, names_array_ref_t,
+ "names_array_ref_t");
+
+/* Create a system or user name table (in the stable memory of mem). */
+int
+create_names_array(ref **ppnames, gs_memory_t *mem, client_name_t cname)
+{
+ ref *pnames = (ref *)
+ gs_alloc_struct(gs_memory_stable(mem), names_array_ref_t,
+ &st_names_array_ref, cname);
+
+ if (pnames == 0)
+ return_error(gs_error_VMerror);
+ make_empty_array(pnames, a_readonly);
+ *ppnames = pnames;
+ return 0;
+}
+
+/* Initialize the binary token machinery. */
+static int
+zbseq_init(i_ctx_t *i_ctx_p)
+{
+ /* Initialize a fake system name table. */
+ /* PostScript code will install the real system name table. */
+ ref *psystem_names = 0;
+ int code = create_names_array(&psystem_names, imemory_global,
+ "zbseq_init(system_names)");
+
+ if (code < 0)
+ return code;
+ system_names_p = psystem_names;
+ return 0;
+}
+
+/* <names> .installsystemnames - */
+static int
+zinstallsystemnames(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ if (r_space(op) != avm_global || imemory_save_level(iimemory_global) != 0)
+ return_error(gs_error_invalidaccess);
+ check_read_type(*op, t_shortarray);
+ ref_assign_old(NULL, system_names_p, op, ".installsystemnames");
+ pop(1);
+ return 0;
+}
+
+/* - currentobjectformat <int> */
+static int
+zcurrentobjectformat(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ *op = ref_binary_object_format;
+ return 0;
+}
+
+/* <int> setobjectformat - */
+static int
+zsetobjectformat(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref cont;
+
+ check_type(*op, t_integer);
+ if (op->value.intval < 0 || op->value.intval > 4)
+ return_error(gs_error_rangecheck);
+ make_struct(&cont, avm_local, ref_binary_object_format_container);
+ ref_assign_old(&cont, &ref_binary_object_format, op, "setobjectformat");
+ pop(1);
+ return 0;
+}
+
+/* <ref_offset> <char_offset> <obj> <string8> .bosobject */
+/* <ref_offset'> <char_offset'> <string8> */
+/*
+ * This converts a single object to its binary object sequence
+ * representation, doing the dirty work of printobject and writeobject.
+ * (The main control is in PostScript code, so that we don't have to worry
+ * about interrupts or callouts in the middle of writing the various data
+ * items.) See encode_binary_token for more details.
+ */
+
+static int
+zbosobject(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_type(op[-3], t_integer);
+ check_type(op[-2], t_integer);
+ check_write_type(*op, t_string);
+ if (r_size(op) < 8)
+ return_error(gs_error_rangecheck);
+ code = encode_binary_token(i_ctx_p, op - 1, &op[-3].value.intval,
+ &op[-2].value.intval, op->value.bytes);
+ if (code < 0)
+ return code;
+ op[-1] = *op;
+ r_set_size(op - 1, 8);
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zbseq_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"1.installsystemnames", zinstallsystemnames},
+ {"0currentobjectformat", zcurrentobjectformat},
+ {"1setobjectformat", zsetobjectformat},
+ {"4.bosobject", zbosobject},
+ op_def_end(zbseq_init)
+};
diff --git a/psi/zcfont.c b/psi/zcfont.c
new file mode 100644
index 000000000..b61cb3312
--- /dev/null
+++ b/psi/zcfont.c
@@ -0,0 +1,172 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Composite font-related character operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gsmatrix.h" /* for gxfont.h */
+#include "gxfixed.h" /* for gxfont.h */
+#include "gxfont.h"
+#include "gxtext.h"
+#include "estack.h"
+#include "ichar.h"
+#include "ifont.h"
+#include "igstate.h"
+#include "store.h"
+
+/* Forward references */
+static int cshow_continue(i_ctx_t *);
+static int cshow_restore_font(i_ctx_t *);
+
+/* <proc> <string> cshow - */
+static int
+zcshow(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr proc_op = op - 1;
+ os_ptr str_op = op;
+ gs_text_enum_t *penum;
+ int code;
+
+ /*
+ * Even though this is not documented anywhere by Adobe,
+ * some Adobe interpreters apparently allow the string and
+ * the procedure to be provided in either order!
+ */
+ if (r_is_proc(proc_op))
+ ;
+ else if (r_is_proc(op)) { /* operands reversed */
+ proc_op = op;
+ str_op = op - 1;
+ } else {
+ check_op(2);
+ return_error(gs_error_typecheck);
+ }
+ if ((code = op_show_setup(i_ctx_p, str_op)) != 0 ||
+ (code = gs_cshow_begin(igs, str_op->value.bytes, r_size(str_op),
+ imemory, &penum)) < 0)
+ return code;
+ if ((code = op_show_finish_setup(i_ctx_p, penum, 2, NULL)) < 0) {
+ ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ sslot = *proc_op; /* save kerning proc */
+ pop(2);
+ return cshow_continue(i_ctx_p);
+}
+static int
+cshow_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_text_enum_t *penum = senum;
+ int code;
+
+ check_estack(4); /* in case we call the procedure */
+ code = gs_text_process(penum);
+ if (code != TEXT_PROCESS_INTERVENE) {
+ code = op_show_continue_dispatch(i_ctx_p, 0, code);
+ if (code == o_push_estack) /* must be TEXT_PROCESS_RENDER */
+ make_op_estack(esp - 1, cshow_continue);
+ return code;
+ }
+ /* Push the character code and width, and call the procedure. */
+ {
+ ref *pslot = &sslot;
+ gs_point wpt;
+ gs_font *font = gs_text_current_font(penum);
+ gs_font *root_font = gs_rootfont(igs);
+ gs_font *scaled_font;
+ uint font_space = r_space(pfont_dict(font));
+ uint root_font_space = r_space(pfont_dict(root_font));
+ int fdepth = penum->fstack.depth;
+
+ gs_text_current_width(penum, &wpt);
+ if (font == root_font)
+ scaled_font = font;
+ else if (fdepth > 0) {
+ /* Construct a scaled version of the leaf font.
+ If font stack is deep enough, get the matrix for scaling
+ from the immediate parent of current font.
+ (The font matrix of root font is not good for the purpose
+ in some case.)
+ assert_(penum->fstack.items[fdepth].font == font
+ && penum->fstack.items[0].font == root_font); */
+ uint save_space = idmemory->current_space;
+ gs_font * immediate_parent = penum->fstack.items[fdepth - 1].font;
+
+ ialloc_set_space(idmemory, font_space);
+ code = gs_makefont(font->dir, font,
+ &immediate_parent->FontMatrix,
+ &scaled_font);
+ ialloc_set_space(idmemory, save_space);
+ if (code < 0)
+ return code;
+ }
+ else {
+ /* Construct a scaled version of the leaf font. */
+ uint save_space = idmemory->current_space;
+
+ ialloc_set_space(idmemory, font_space);
+ code = gs_makefont(font->dir, font, &root_font->FontMatrix,
+ &scaled_font);
+ ialloc_set_space(idmemory, save_space);
+ if (code < 0)
+ return code;
+ }
+ push(3);
+ make_int(op - 2, gs_text_current_char(penum) & 0xff);
+ make_real(op - 1, wpt.x);
+ make_real(op, wpt.y);
+ make_struct(&ssfont, font_space, font);
+ make_struct(&srfont, root_font_space, root_font);
+ push_op_estack(cshow_restore_font);
+ /* cshow does not change rootfont for user procedure */
+ gs_set_currentfont(igs, scaled_font);
+ *++esp = *pslot; /* user procedure */
+ }
+ return o_push_estack;
+}
+static int
+cshow_restore_font(i_ctx_t *i_ctx_p)
+{
+ /* We must restore both the root font and the current font. */
+ gs_setfont(igs, r_ptr(&srfont, gs_font));
+ gs_set_currentfont(igs, r_ptr(&ssfont, gs_font));
+ return cshow_continue(i_ctx_p);
+}
+
+/* - rootfont <font> */
+static int
+zrootfont(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ *op = *pfont_dict(gs_rootfont(igs));
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcfont_op_defs[] =
+{
+ {"2cshow", zcshow},
+ {"0rootfont", zrootfont},
+ /* Internal operators */
+ {"0%cshow_continue", cshow_continue},
+ {"0%cshow_restore_font", cshow_restore_font},
+ op_def_end(0)
+};
diff --git a/psi/zchar.c b/psi/zchar.c
new file mode 100644
index 000000000..51081f0b7
--- /dev/null
+++ b/psi/zchar.c
@@ -0,0 +1,967 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Character operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gstext.h"
+#include "gxarith.h"
+#include "gxfixed.h"
+#include "gxmatrix.h" /* for ifont.h */
+#include "gxdevice.h" /* for gxfont.h */
+#include "gxfont.h"
+#include "gxfont42.h"
+#include "gxfont0.h"
+#include "gzstate.h"
+#include "dstack.h" /* for stack depth */
+#include "estack.h"
+#include "ialloc.h"
+#include "ichar.h"
+#include "ichar1.h"
+#include "idict.h"
+#include "ifont.h"
+#include "igstate.h"
+#include "ilevel.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "store.h"
+#include "zchar42.h"
+
+/* Forward references */
+static bool map_glyph_to_char(const gs_memory_t *mem,
+ const ref *, const ref *, ref *);
+static int finish_show(i_ctx_t *);
+static int op_show_cleanup(i_ctx_t *);
+static int op_show_return_width(i_ctx_t *, uint, double *);
+
+static int zawidthshow(i_ctx_t *i_ctx_p);
+static int zwidthshow(i_ctx_t *i_ctx_p);
+
+
+/* <string> show - */
+static int
+zshow(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_text_enum_t *penum;
+ int code = op_show_setup(i_ctx_p, op);
+
+ if (code != 0 ||
+ (code = gs_show_begin(igs, op->value.bytes, r_size(op), imemory_local, &penum)) < 0)
+ return code;
+ *(op_proc_t *)&penum->enum_client_data = zshow;
+ if ((code = op_show_finish_setup(i_ctx_p, penum, 1, finish_show)) < 0) {
+ ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ return op_show_continue_pop(i_ctx_p, 1);
+}
+
+/* <ax> <ay> <string> ashow - */
+static int
+zashow(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_text_enum_t *penum;
+ double axy[2];
+ int code = num_params(op - 1, 2, axy);
+
+ if (code < 0 ||
+ (code = op_show_setup(i_ctx_p, op)) != 0 ||
+ (code = gs_ashow_begin(igs, axy[0], axy[1], op->value.bytes, r_size(op), imemory_local, &penum)) < 0)
+ return code;
+ *(op_proc_t *)&penum->enum_client_data = zashow;
+ if ((code = op_show_finish_setup(i_ctx_p, penum, 3, finish_show)) < 0) {
+ ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ return op_show_continue_pop(i_ctx_p, 3);
+}
+
+static int
+widthshow_aux(i_ctx_t *i_ctx_p, bool single_byte_space)
+{
+ os_ptr op = osp;
+ gs_text_enum_t *penum;
+ double cxy[2];
+ int code;
+
+ if ((code = op_show_setup(i_ctx_p, op)) != 0 )
+ return code;
+ check_type(op[-1], t_integer);
+ if (gs_currentfont(igs)->FontType == ft_composite) {
+ if ((gs_char) (op[-1].value.intval) != op[-1].value.intval)
+ return_error(gs_error_rangecheck);
+ } else {
+ if (op[-1].value.intval < 0 || op[-1].value.intval > 255)
+ return_error(gs_error_rangecheck); /* per PLRM and CET 13-26 */
+ }
+ if ((code = num_params(op - 2, 2, cxy)) < 0 )
+ return code;
+ if ((code = gs_widthshow_begin(igs, cxy[0], cxy[1],
+ (gs_char) op[-1].value.intval,
+ op->value.bytes, r_size(op),
+ imemory_local, &penum)) < 0)
+ return code;
+ *(op_proc_t *)&penum->enum_client_data = zwidthshow;
+
+ penum->single_byte_space = single_byte_space;
+
+ if ((code = op_show_finish_setup(i_ctx_p, penum, 4, finish_show)) < 0) {
+ ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+
+ return op_show_continue_pop(i_ctx_p, 4);
+}
+
+/* <cx> <cy> <char> <string> widthshow - */
+static int
+zwidthshow(i_ctx_t *i_ctx_p)
+{
+ return(widthshow_aux(i_ctx_p, false));
+}
+
+/* For PDF word spacing we need to identify strictly
+ single byte character codes of the value 32, and
+ this conflicts with Postscript's widthshow character
+ code matching, where any character code, regardless of
+ its length will match. For example, in widthshow, a
+ character code of <0032> will match a parameter value
+ of 32, but for PDF word spacing, <0032> will not match
+ the space character, and won't have the word spacing
+ applied, but <32> will.
+ Hence, we have a couple of custom operators to cover
+ the different requirements.
+*/
+/* <cx> <cy> <char> <string> .pdfwidthshow - */
+static int
+zpdfwidthshow(i_ctx_t *i_ctx_p)
+{
+ return(widthshow_aux(i_ctx_p, true));
+}
+
+static int
+awidthshow_aux(i_ctx_t *i_ctx_p, bool single_byte_space)
+{
+ os_ptr op = osp;
+ gs_text_enum_t *penum;
+ double cxy[2], axy[2];
+ int code;
+
+ if ((code = op_show_setup(i_ctx_p, op)) != 0 )
+ return code;
+ if ((code = num_params(op - 1, 2, axy)) < 0 )
+ return code;
+ check_type(op[-3], t_integer);
+ if (gs_currentfont(igs)->FontType == ft_composite) {
+ if ((gs_char) (op[-3].value.intval) != op[-3].value.intval)
+ return_error(gs_error_rangecheck);
+ } else {
+ if (op[-3].value.intval < 0 || op[-3].value.intval > 255)
+ return_error(gs_error_rangecheck); /* per PLRM and CET 13-02 */
+ }
+ if ((code = num_params(op - 4, 2, cxy)) < 0 )
+ return code;
+ if ((code = gs_awidthshow_begin(igs, cxy[0], cxy[1],
+ (gs_char) op[-3].value.intval,
+ axy[0], axy[1],
+ op->value.bytes, r_size(op),
+ imemory_local, &penum)) < 0)
+ return code;
+ *(op_proc_t *)&penum->enum_client_data = zawidthshow;
+
+ penum->single_byte_space = single_byte_space;
+
+ if ((code = op_show_finish_setup(i_ctx_p, penum, 6, finish_show)) < 0) {
+ ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+
+ return op_show_continue_pop(i_ctx_p, 6);
+}
+
+/* <cx> <cy> <char> <ax> <ay> <string> awidthshow - */
+static int
+zawidthshow(i_ctx_t *i_ctx_p)
+{
+ return(awidthshow_aux(i_ctx_p, false));
+}
+
+/* <cx> <cy> <char> <ax> <ay> <string> .pdfawidthshow - */
+static int
+zpdfawidthshow(i_ctx_t *i_ctx_p)
+{
+ return(awidthshow_aux(i_ctx_p, true));
+}
+
+/* <proc> <string> kshow - */
+static int
+zkshow(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_text_enum_t *penum;
+ int code;
+
+ check_read_type(*op, t_string);
+ check_proc(op[-1]);
+ /*
+ * Per PLRM Section xx.x, kshow is illegal if the current font is a
+ * composite font. The graphics library does not have this limitation,
+ * so we check for it here.
+ */
+ if (gs_currentfont(igs)->FontType == ft_composite)
+ return_error(gs_error_invalidfont);
+ if ((code = op_show_setup(i_ctx_p, op)) != 0 ||
+ (code = gs_kshow_begin(igs, op->value.bytes, r_size(op),
+ imemory_local, &penum)) < 0)
+ return code;
+ *(op_proc_t *)&penum->enum_client_data = zkshow;
+ if ((code = op_show_finish_setup(i_ctx_p, penum, 2, finish_show)) < 0) {
+ ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ sslot = op[-1]; /* save kerning proc */
+ return op_show_continue_pop(i_ctx_p, 2);
+}
+
+/* Common finish procedure for all show operations. */
+/* Doesn't have to do anything. */
+static int
+finish_show(i_ctx_t *i_ctx_p)
+{
+ return 0;
+}
+
+/* <string> stringwidth <wx> <wy> */
+static int
+zstringwidth(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_text_enum_t *penum;
+ int code = op_show_setup(i_ctx_p, op);
+
+ if (code != 0 ||
+ (code = gs_stringwidth_begin(igs, op->value.bytes, r_size(op),
+ imemory, &penum)) < 0)
+ return code;
+ *(op_proc_t *)&penum->enum_client_data = zstringwidth;
+ if ((code = op_show_finish_setup(i_ctx_p, penum, 1, finish_stringwidth)) < 0) {
+ ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ return op_show_continue_pop(i_ctx_p, 1);
+}
+/* Finishing procedure for stringwidth. */
+/* Pushes the accumulated width. */
+/* This is exported for .glyphwidth (in zcharx.c). */
+int
+finish_stringwidth(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_point width;
+
+ gs_text_total_width(senum, &width);
+ push(2);
+ make_real(op - 1, width.x);
+ make_real(op, width.y);
+ return 0;
+}
+
+/* Common code for charpath and .charboxpath. */
+static int
+zchar_path(i_ctx_t *i_ctx_p, op_proc_t proc,
+ int (*begin)(gs_state *, const byte *, uint,
+ bool, gs_memory_t *, gs_text_enum_t **))
+{
+ os_ptr op = osp;
+ gs_text_enum_t *penum;
+ int code;
+
+ check_type(*op, t_boolean);
+ code = op_show_setup(i_ctx_p, op - 1);
+ if (code != 0 ||
+ (code = begin(igs, op[-1].value.bytes, r_size(op - 1),
+ op->value.boolval, imemory, &penum)) < 0)
+ return code;
+ *(op_proc_t *)&penum->enum_client_data = proc;
+ if ((code = op_show_finish_setup(i_ctx_p, penum, 2, finish_show)) < 0) {
+ ifree_object(penum, "op_show_enum_setup");
+ return code;
+ }
+ return op_show_continue_pop(i_ctx_p, 2);
+}
+/* <string> <outline_bool> charpath - */
+static int
+zcharpath(i_ctx_t *i_ctx_p)
+{
+ return zchar_path(i_ctx_p, zcharpath, gs_charpath_begin);
+}
+/* <string> <box_bool> .charboxpath - */
+static int
+zcharboxpath(i_ctx_t *i_ctx_p)
+{
+ return zchar_path(i_ctx_p, zcharboxpath, gs_charboxpath_begin);
+}
+
+/* <wx> <wy> <llx> <lly> <urx> <ury> setcachedevice - */
+int
+zsetcachedevice(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double wbox[6];
+ gs_text_enum_t *penum = op_show_find(i_ctx_p);
+ int code = num_params(op, 6, wbox);
+
+ if (penum == 0)
+ return_error(gs_error_undefined);
+ if (code < 0)
+ return code;
+ if (zchar_show_width_only(penum))
+ return op_show_return_width(i_ctx_p, 6, &wbox[0]);
+ code = gs_text_setcachedevice(penum, wbox);
+ if (code < 0)
+ return code;
+ pop(6);
+ if (code == 1)
+ clear_pagedevice(istate);
+ return 0;
+}
+
+/* <w0x> <w0y> <llx> <lly> <urx> <ury> <w1x> <w1y> <vx> <vy> setcachedevice2 - */
+int
+zsetcachedevice2(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double wbox[10];
+ gs_text_enum_t *penum = op_show_find(i_ctx_p);
+ int code = num_params(op, 10, wbox);
+
+ if (penum == 0)
+ return_error(gs_error_undefined);
+ if (code < 0)
+ return code;
+ if (zchar_show_width_only(penum))
+ return op_show_return_width(i_ctx_p, 10,
+ (gs_rootfont(igs)->WMode ?
+ &wbox[6] : &wbox[0]));
+ code = gs_text_setcachedevice2(penum, wbox);
+ if (code < 0)
+ return code;
+ pop(10);
+ if (code == 1)
+ clear_pagedevice(istate);
+ return 0;
+}
+
+/* <wx> <wy> setcharwidth - */
+static int
+zsetcharwidth(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double width[2];
+ gs_text_enum_t *penum = op_show_find(i_ctx_p);
+ int code = num_params(op, 2, width);
+
+ if (penum == 0)
+ return_error(gs_error_undefined);
+ if (code < 0)
+ return code;
+ if (zchar_show_width_only(penum))
+ return op_show_return_width(i_ctx_p, 2, &width[0]);
+ code = gs_text_setcharwidth(penum, width);
+ if (code < 0)
+ return code;
+ pop(2);
+ return 0;
+}
+
+/* <dict> .fontbbox <llx> <lly> <urx> <ury> -true- */
+/* <dict> .fontbbox -false- */
+static int
+zfontbbox(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double bbox[4];
+ int code;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ code = font_bbox_param(imemory, op, bbox);
+ if (code < 0)
+ return code;
+ if (bbox[0] < bbox[2] && bbox[1] < bbox[3]) {
+ push(4);
+ make_reals(op - 4, bbox, 4);
+ make_true(op);
+ } else { /* No bbox, or an empty one. */
+ make_false(op);
+ }
+ return 0;
+}
+
+/* Export in_cachedevice flag for PDF interpreter, which, unlike
+ * PS unterpreter, ignores color operations in the inappropriate context.
+ */
+/* - .incachedevice <bool> */
+static int
+zincachedevice(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_bool(op, !!igs->in_cachedevice);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zchar_a_op_defs[] =
+{
+ {"3ashow", zashow},
+ {"6awidthshow", zawidthshow},
+ {"2charpath", zcharpath},
+ {"2.charboxpath", zcharboxpath},
+ {"2kshow", zkshow},
+ {"6setcachedevice", zsetcachedevice},
+ {":setcachedevice2", zsetcachedevice2},
+ {"2setcharwidth", zsetcharwidth},
+ {"1show", zshow},
+ {"1stringwidth", zstringwidth},
+ {"4widthshow", zwidthshow},
+ /* Extensions */
+ {"1.fontbbox", zfontbbox},
+ {"6.pdfawidthshow", zpdfawidthshow},
+ {"4.pdfwidthshow", zpdfwidthshow},
+
+ /* Internal operators */
+ {"0%finish_show", finish_show},
+ op_def_end(0)
+};
+
+const op_def zchar_b_op_defs[] =
+{
+ {"0%finish_stringwidth", finish_stringwidth},
+ {"0%op_show_continue", op_show_continue},
+ {"0.incachedevice", zincachedevice},
+ op_def_end(0)
+};
+
+/* ------ Subroutines ------ */
+
+/* Most of these are exported for zchar2.c. */
+
+/* Convert a glyph to a ref. */
+void
+glyph_ref(const gs_memory_t *mem, gs_glyph glyph, ref * gref)
+{
+ if (glyph < gs_min_cid_glyph)
+ name_index_ref(mem, glyph, gref);
+ else
+ make_int(gref, glyph - gs_min_cid_glyph);
+}
+
+/* Prepare to set up for a text operator. */
+/* Don't change any state yet. */
+int
+op_show_setup(i_ctx_t *i_ctx_p, os_ptr op)
+{
+ check_read_type(*op, t_string);
+ return op_show_enum_setup(i_ctx_p);
+}
+int
+op_show_enum_setup(i_ctx_t *i_ctx_p)
+{
+ check_estack(snumpush + 2);
+ return 0;
+}
+
+/* Finish setting up a text operator. */
+int
+op_show_finish_setup(i_ctx_t *i_ctx_p, gs_text_enum_t * penum, int npop,
+ op_proc_t endproc /* end procedure */ )
+{
+ gs_text_enum_t *osenum = op_show_find(i_ctx_p);
+ es_ptr ep = esp + snumpush;
+ gs_glyph glyph;
+
+ if (gs_currentcpsimode(igs->memory)) {
+ /* CET 14-03.PS page 2 emits rangecheck before rendering a character.
+ Early check the text to font compatibility
+ with decomposing the text into characters.*/
+ int code = gs_text_count_chars(igs, gs_get_text_params(penum), imemory);
+
+ if (code < 0)
+ return code;
+ }
+ /*
+ * If we are in the procedure of a cshow for a CID font and this is
+ * a show operator, do something special, per the Red Book.
+ */
+ if (osenum &&
+ SHOW_IS_ALL_OF(osenum,
+ TEXT_FROM_STRING | TEXT_DO_NONE | TEXT_INTERVENE) &&
+ SHOW_IS_ALL_OF(penum, TEXT_FROM_STRING | TEXT_RETURN_WIDTH) &&
+ (glyph = gs_text_current_glyph(osenum)) != gs_no_glyph &&
+ glyph >= gs_min_cid_glyph &&
+
+ /* According to PLRM, we don't need to raise a rangecheck error,
+ if currentfont is changed in the proc of the operator 'cshow'. */
+ gs_default_same_font (gs_text_current_font(osenum),
+ gs_text_current_font(penum), true)
+ ) {
+ gs_text_params_t text;
+
+ if (!(penum->text.size == 1 &&
+ penum->text.data.bytes[0] ==
+ (gs_text_current_char(osenum) & 0xff))
+ )
+ return_error(gs_error_rangecheck);
+ text = penum->text;
+ text.operation =
+ (text.operation &
+ ~(TEXT_FROM_STRING | TEXT_FROM_BYTES | TEXT_FROM_CHARS |
+ TEXT_FROM_GLYPHS | TEXT_FROM_SINGLE_CHAR)) |
+ TEXT_FROM_SINGLE_GLYPH;
+ text.data.d_glyph = glyph;
+ text.size = 1;
+ gs_text_restart(penum, &text);
+ }
+ if (osenum && osenum->current_font->FontType == ft_user_defined &&
+ osenum->orig_font->FontType == ft_composite &&
+ ((const gs_font_type0 *)osenum->orig_font)->data.FMapType == fmap_CMap) {
+ /* A special behavior defined in PLRM3 section 5.11 page 389. */
+ penum->outer_CID = osenum->returned.current_glyph;
+ }
+ if (osenum == NULL && !(penum->text.operation & (TEXT_FROM_GLYPHS | TEXT_FROM_SINGLE_GLYPH))) {
+ int ft = igs->root_font->FontType;
+
+ if ((ft >= ft_CID_encrypted && ft <= ft_CID_TrueType) || ft == ft_CID_bitmap)
+ return_error(gs_error_typecheck);
+ }
+ make_mark_estack(ep - (snumpush - 1), es_show, op_show_cleanup);
+ if (endproc == NULL)
+ endproc = finish_show;
+ make_null(&esslot(ep));
+ make_int(&esodepth(ep), ref_stack_count_inline(&o_stack) - npop); /* Save stack depth for */
+ make_int(&esddepth(ep), ref_stack_count_inline(&d_stack)); /* correct interrupt processing */
+ make_int(&esgslevel(ep), igs->level);
+ make_null(&essfont(ep));
+ make_null(&esrfont(ep));
+ make_op_estack(&eseproc(ep), endproc);
+ make_istruct(ep, 0, penum);
+ esp = ep;
+ return 0;
+}
+
+/* Continuation operator for character rendering. */
+int
+op_show_continue(i_ctx_t *i_ctx_p)
+{
+ int code = gs_text_update_dev_color(igs, senum);
+
+ if (code >= 0)
+ code = op_show_continue_dispatch(i_ctx_p, 0, gs_text_process(senum));
+ return code;
+}
+int
+op_show_continue_pop(i_ctx_t *i_ctx_p, int npop)
+{
+ return op_show_continue_dispatch(i_ctx_p, npop, gs_text_process(senum));
+}
+/*
+ * Note that op_show_continue_dispatch sets osp = op explicitly iff the
+ * dispatch succeeds. This is so that the show operators don't pop anything
+ * from the o-stack if they don't succeed. Note also that if it returns an
+ * error, it has freed the enumerator.
+ */
+int
+op_show_continue_dispatch(i_ctx_t *i_ctx_p, int npop, int code)
+{
+ os_ptr op = osp - npop;
+ gs_text_enum_t *penum = senum;
+
+ switch (code) {
+ case 0: { /* all done */
+ os_ptr save_osp = osp;
+
+ osp = op;
+ code = (*real_opproc(&seproc)) (i_ctx_p);
+ op_show_free(i_ctx_p, code);
+ if (code < 0) {
+ osp = save_osp;
+ return code;
+ }
+ return o_pop_estack;
+ }
+ case TEXT_PROCESS_INTERVENE: {
+ ref *pslot = &sslot; /* only used for kshow */
+
+ push(2);
+ make_int(op - 1, gs_text_current_char(penum)); /* previous char */
+ make_int(op, gs_text_next_char(penum));
+ push_op_estack(op_show_continue); /* continue after kerning */
+ *++esp = *pslot; /* kerning procedure */
+ return o_push_estack;
+ }
+ case TEXT_PROCESS_RENDER: {
+ gs_font *pfont = gs_currentfont(igs);
+ font_data *pfdata = pfont_data(pfont);
+ gs_char chr = gs_text_current_char(penum);
+ gs_glyph glyph = gs_text_current_glyph(penum);
+
+ push(2);
+ op[-1] = pfdata->dict; /* push the font */
+ /*
+ * For Type 1 and Type 4 fonts, prefer BuildChar to BuildGlyph
+ * if there is no glyph, or if there is both a character and a
+ * glyph and the glyph is the one that corresponds to the
+ * character in the Encoding, so that PostScript procedures
+ * appearing in the CharStrings dictionary will receive the
+ * character code rather than the character name; for Type 3
+ * fonts, prefer BuildGlyph to BuildChar. For other font types
+ * (such as CID fonts), only BuildGlyph will be present.
+ */
+ if (pfont->FontType == ft_user_defined) {
+ /* Type 3 font, prefer BuildGlyph. */
+ if (level2_enabled &&
+ !r_has_type(&pfdata->BuildGlyph, t_null) &&
+ glyph != gs_no_glyph
+ ) {
+ glyph_ref(imemory, glyph, op);
+ esp[2] = pfdata->BuildGlyph;
+ } else if (r_has_type(&pfdata->BuildChar, t_null))
+ goto err;
+ else if (chr == gs_no_char) {
+ /* glyphshow, reverse map the character */
+ /* through the Encoding */
+ ref gref;
+ const ref *pencoding = &pfdata->Encoding;
+
+ glyph_ref(imemory, glyph, &gref);
+ if (!map_glyph_to_char(imemory, &gref, pencoding,
+ (ref *) op)
+ ) { /* Not found, try .notdef */
+ name_enter_string(imemory, ".notdef", &gref);
+ if (!map_glyph_to_char(imemory, &gref,
+ pencoding,
+ (ref *) op)
+ )
+ goto err;
+ }
+ esp[2] = pfdata->BuildChar;
+ } else {
+ make_int(op, chr & 0xff);
+ esp[2] = pfdata->BuildChar;
+ }
+ } else {
+ /*
+ * For a Type 1 or Type 4 font, prefer BuildChar or
+ * BuildGlyph as described above: we know that both
+ * BuildChar and BuildGlyph are present. For other font
+ * types, only BuildGlyph is available.
+ */
+ ref eref, gref;
+
+ if (chr != gs_no_char &&
+ !r_has_type(&pfdata->BuildChar, t_null) &&
+ (glyph == gs_no_glyph ||
+ (!r_has_type(&pfdata->Encoding, t_null) &&
+ array_get(imemory, &pfdata->Encoding, (long)(chr & 0xff), &eref) >= 0 &&
+ (glyph_ref(imemory, glyph, &gref), obj_eq(imemory, &gref, &eref))))
+ ) {
+ make_int(op, chr & 0xff);
+ esp[2] = pfdata->BuildChar;
+ } else {
+ /* We might not have a glyph: substitute 0. **HACK** */
+ if (glyph == gs_no_glyph)
+ make_int(op, 0);
+ else
+ glyph_ref(imemory, glyph, op);
+ esp[2] = pfdata->BuildGlyph;
+ }
+ }
+ /* Save the stack depths in case we bail out. */
+ sodepth.value.intval = ref_stack_count(&o_stack) - 2;
+ sddepth.value.intval = ref_stack_count(&d_stack);
+ push_op_estack(op_show_continue);
+ ++esp; /* skip BuildChar or BuildGlyph proc */
+ return o_push_estack;
+ }
+ case TEXT_PROCESS_CDEVPROC:
+ { gs_font *pfont = penum->current_font;
+ ref cnref;
+ op_proc_t cont = op_show_continue, exec_cont = 0;
+ gs_glyph glyph = penum->returned.current_glyph;
+ int code;
+
+ pop(npop);
+ op = osp;
+ glyph_ref(imemory, glyph, &cnref);
+ if (pfont->FontType == ft_CID_TrueType) {
+ gs_font_type42 *pfont42 = (gs_font_type42 *)pfont;
+ uint glyph_index = pfont42->data.get_glyph_index(pfont42, glyph);
+
+ code = zchar42_set_cache(i_ctx_p, (gs_font_base *)pfont42,
+ &cnref, glyph_index, cont, &exec_cont);
+ } else if (pfont->FontType == ft_CID_encrypted)
+ code = z1_set_cache(i_ctx_p, (gs_font_base *)pfont,
+ &cnref, glyph, cont, &exec_cont);
+ else
+ return_error(gs_error_unregistered); /* Unimplemented. */
+ if (exec_cont != 0)
+ return_error(gs_error_unregistered); /* Must not happen. */
+ return code;
+ }
+ default: /* error */
+err:
+ if (code >= 0)
+ code = gs_note_error(gs_error_invalidfont);
+ return op_show_free(i_ctx_p, code);
+ }
+}
+/* Reverse-map a glyph name to a character code for glyphshow. */
+static bool
+map_glyph_to_char(const gs_memory_t *mem, const ref * pgref, const ref * pencoding, ref * pch)
+{
+ uint esize = r_size(pencoding);
+ uint ch;
+ ref eref;
+
+ for (ch = 0; ch < esize; ch++) {
+ array_get(mem, pencoding, (long)ch, &eref);
+ if (obj_eq(mem, pgref, &eref)) {
+ make_int(pch, ch);
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Find the index of the e-stack mark for the current show enumerator. */
+/* Return 0 if we can't find the mark. */
+static uint
+op_show_find_index(i_ctx_t *i_ctx_p)
+{
+ ref_stack_enum_t rsenum;
+ uint count = 0;
+
+ ref_stack_enum_begin(&rsenum, &e_stack);
+ do {
+ es_ptr ep = rsenum.ptr;
+ uint size = rsenum.size;
+
+ for (ep += size - 1; size != 0; size--, ep--, count++)
+ if (r_is_estack_mark(ep) && estack_mark_index(ep) == es_show)
+ return count;
+ } while (ref_stack_enum_next(&rsenum));
+ return 0; /* no mark */
+}
+
+/* Find the current show enumerator on the e-stack. */
+gs_text_enum_t *
+op_show_find(i_ctx_t *i_ctx_p)
+{
+ uint index = op_show_find_index(i_ctx_p);
+
+ if (index == 0)
+ return 0; /* no mark */
+ return r_ptr(ref_stack_index(&e_stack, index - (snumpush - 1)),
+ gs_text_enum_t);
+}
+
+/*
+ * Return true if we only need the width from the rasterizer
+ * and can short-circuit the full rendering of the character,
+ * false if we need the actual character bits. This is only safe if
+ * we know the character is well-behaved, i.e., is not defined by an
+ * arbitrary PostScript procedure.
+ */
+bool
+zchar_show_width_only(const gs_text_enum_t * penum)
+{
+ if (!gs_text_is_width_only(penum))
+ return false;
+ switch (penum->orig_font->FontType) {
+ case ft_encrypted:
+ case ft_encrypted2:
+ case ft_CID_encrypted:
+ case ft_CID_TrueType:
+ case ft_CID_bitmap:
+ case ft_TrueType:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Shortcut the BuildChar or BuildGlyph procedure at the point */
+/* of the setcharwidth or the setcachedevice[2] if we are in */
+/* a stringwidth or cshow, or if we are only collecting the scalable */
+/* width for an xfont character. */
+static int
+op_show_return_width(i_ctx_t *i_ctx_p, uint npop, double *pwidth)
+{
+ uint index = op_show_find_index(i_ctx_p);
+ es_ptr ep = (es_ptr) ref_stack_index(&e_stack, index - (snumpush - 1));
+ int code = gs_text_setcharwidth(esenum(ep), pwidth);
+ uint ocount, dsaved, dcount;
+
+ if (code < 0)
+ return code;
+ /* Restore the operand and dictionary stacks. */
+ ocount = ref_stack_count(&o_stack) - (uint) esodepth(ep).value.intval;
+ if (ocount < npop)
+ return_error(gs_error_stackunderflow);
+ dsaved = (uint) esddepth(ep).value.intval;
+ dcount = ref_stack_count(&d_stack);
+ if (dcount < dsaved)
+ return_error(gs_error_dictstackunderflow);
+ while (dcount > dsaved) {
+ code = zend(i_ctx_p);
+ if (code < 0)
+ return code;
+ dcount--;
+ }
+ ref_stack_pop(&o_stack, ocount);
+ /* We don't want to pop the mark or the continuation */
+ /* procedure (op_show_continue or cshow_continue). */
+ pop_estack(i_ctx_p, index - snumpush);
+ return o_pop_estack;
+}
+
+/*
+ * Restore state after finishing, or unwinding from an error within, a show
+ * operation. Note that we assume op == osp, and may reset osp.
+ */
+static int
+op_show_restore(i_ctx_t *i_ctx_p, bool for_error)
+{
+ register es_ptr ep = esp + snumpush;
+ gs_text_enum_t *penum = esenum(ep);
+ int saved_level = esgslevel(ep).value.intval;
+ int code = 0;
+
+ if (for_error) {
+#if 0 /* Disabled for CPSI compatibility for 13-12-4.
+ CPSI doesn't remove cshow, kshow proc operands. */
+ uint saved_count = esodepth(ep).value.intval;
+ uint count = ref_stack_count(&o_stack);
+
+ if (count > saved_count) /* if <, we're in trouble */
+ ref_stack_pop(&o_stack, count - saved_count);
+#endif
+ if (ep[1].value.opproc == op_show_continue && penum->enum_client_data != NULL) {
+ /* Replace the continuation operaton on estack with the right operator : */
+ op_proc_t proc;
+
+ *(void **)&proc = penum->enum_client_data;
+ make_op_estack(ep + 1, proc);
+ }
+ }
+ if (SHOW_IS_STRINGWIDTH(penum) && igs->text_rendering_mode != 3) {
+ /* stringwidth does an extra gsave */
+ --saved_level;
+ }
+ if (penum->text.operation & TEXT_REPLACE_WIDTHS) {
+ gs_free_const_object(penum->memory, penum->text.y_widths, "y_widths");
+ if (penum->text.x_widths != penum->text.y_widths)
+ gs_free_const_object(penum->memory, penum->text.x_widths, "x_widths");
+ }
+ /*
+ * We might have been inside a cshow, in which case currentfont was
+ * reset temporarily, as though we were inside a BuildChar/ BuildGlyph
+ * procedure. To handle this case, set currentfont back to its original
+ * state. NOTE: this code previously used fstack[0] in the enumerator
+ * for the root font: we aren't sure that this change is correct.
+ */
+ gs_set_currentfont(igs, penum->orig_font);
+ while (igs->level > saved_level && code >= 0) {
+ if (igs->saved == 0 || igs->saved->saved == 0) {
+ /*
+ * Bad news: we got an error inside a save inside a BuildChar or
+ * BuildGlyph. Don't attempt to recover.
+ */
+ code = gs_note_error(gs_error_Fatal);
+ } else
+ code = gs_grestore(igs);
+ }
+ gs_text_release(penum, "op_show_restore");
+ return code;
+}
+/* Clean up after an error. */
+static int
+op_show_cleanup(i_ctx_t *i_ctx_p)
+{
+ return op_show_restore(i_ctx_p, true);
+}
+/* Clean up after termination of a show operation. */
+int
+op_show_free(i_ctx_t *i_ctx_p, int code)
+{
+ int rcode;
+
+ esp -= snumpush;
+ rcode = op_show_restore(i_ctx_p, code < 0);
+ return (rcode < 0 ? rcode : code);
+}
+
+/* Get a FontBBox parameter from a font dictionary. */
+int
+font_bbox_param(const gs_memory_t *mem, const ref * pfdict, double bbox[4])
+{
+ ref *pbbox;
+
+ /*
+ * Pre-clear the bbox in case it's invalid. The Red Books say that
+ * FontBBox is required, but old Adobe interpreters don't require
+ * it, and a few user-written fonts don't supply it, or supply one
+ * of the wrong size (!); also, PageMaker 5.0 (an Adobe product!)
+ * sometimes emits an absurd bbox for Type 1 fonts converted from
+ * TrueType.
+ */
+ bbox[0] = bbox[1] = bbox[2] = bbox[3] = 0.0;
+ if (dict_find_string(pfdict, "FontBBox", &pbbox) > 0) {
+ if (!r_is_array(pbbox))
+ return_error(gs_error_typecheck);
+ if (r_size(pbbox) == 4) {
+ const ref_packed *pbe = pbbox->value.packed;
+ ref rbe[4];
+ int i;
+ int code;
+ float dx, dy, ratio;
+ const float max_ratio = 12; /* From the bug 687594. */
+
+ for (i = 0; i < 4; i++) {
+ packed_get(mem, pbe, rbe + i);
+ pbe = packed_next(pbe);
+ }
+ if ((code = num_params(rbe + 3, 4, bbox)) < 0)
+ return code;
+ /* Require "reasonable" values. */
+ dx = bbox[2] - bbox[0];
+ dy = bbox[3] - bbox[1];
+ if (dx <= 0 || dy <= 0 ||
+ (ratio = dy / dx) < 1 / max_ratio || ratio > max_ratio
+ )
+ bbox[0] = bbox[1] = bbox[2] = bbox[3] = 0.0;
+ }
+ } else if (gs_currentcpsimode(mem)) {
+ return_error(gs_error_invalidfont); /* CPSI requires FontBBox */
+ }
+ return 0;
+}
diff --git a/psi/zchar1.c b/psi/zchar1.c
new file mode 100644
index 000000000..72f7b5358
--- /dev/null
+++ b/psi/zchar1.c
@@ -0,0 +1,1305 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Type 1 character display operator */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxdevice.h" /* for gxfont.h */
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+#include "gxfcid.h"
+#include "gxchar.h"
+#include "gzstate.h" /* for path for gs_type1_init */
+ /* (should only be gsstate.h) */
+#include "gscencs.h"
+#include "gspaint.h" /* for gs_fill, gs_stroke */
+#include "gspath.h"
+#include "gsrect.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "ichar.h"
+#include "ichar1.h"
+#include "icharout.h"
+#include "idict.h"
+#include "ifont.h"
+#include "igstate.h"
+#include "iname.h"
+#include "iutil.h"
+#include "store.h"
+
+/*
+ * Properly designed fonts, which have no self-intersecting outlines
+ * and in which outer and inner outlines are drawn in opposite
+ * directions, aren't affected by choice of filling rule; but some
+ * badly designed fonts in the Genoa test suite seem to require
+ * using the even-odd rule to match Adobe interpreters.
+ *
+ * Properly designed fonts will render correctly with: eofill
+ * (required for Adobe CPSI compliant behavior
+ */
+/*
+ * On April 4, 2002, we received bug report #539359
+ * which we interpret as some Genoa test are now obsolete,
+ * so we need to drop the bad font tolerance feature
+ * explained above. This temporary patch changes
+ * the even-odd rule back to non-zero rule.
+ * This patch to be kept until we accumulate
+ * enough information from regression testing and
+ * from user responses.
+ */
+
+/* *********************************************************************
+ * Make this dynamic via a global (somewhat better than a COMPILE option
+ ***********************************************************************/
+#define GS_CHAR_FILL gs_fill
+
+/* ---------------- Utilities ---------------- */
+
+/* Test whether a font is a CharString font. */
+static bool
+font_uses_charstrings(const gs_font *pfont)
+{
+ return (pfont->FontType == ft_encrypted ||
+ pfont->FontType == ft_encrypted2 ||
+ pfont->FontType == ft_disk_based);
+}
+
+/* Initialize a Type 1 interpreter. */
+static int
+type1_exec_init(gs_type1_state *pcis, gs_text_enum_t *penum,
+ gs_state *pgs, gs_font_type1 *pfont1)
+{
+ /*
+ * We have to disregard penum->pis and penum->path, and render to
+ * the current gstate and path. This is a design bug that we will
+ * have to address someday!
+ */
+
+ int alpha_bits = 1;
+ gs_log2_scale_point log2_subpixels;
+
+ if (color_is_pure(gs_currentdevicecolor_inline(pgs))) /* Keep consistency with alpha_buffer_bits() */
+ alpha_bits = (*dev_proc(pgs->device, get_alpha_bits)) (pgs->device, go_text);
+ if (alpha_bits <= 1) {
+ /* We render to cache device or the target device has no alpha bits. */
+ log2_subpixels = penum->log2_scale;
+ } else {
+ /* We'll render to target device through alpha buffer. */
+ /* Keep consistency with alpha_buffer_init() */
+ log2_subpixels.x = log2_subpixels.y = ilog2(alpha_bits);
+ }
+ return gs_type1_interp_init(pcis, (gs_imager_state *)pgs, pgs->path,
+ &penum->log2_scale, &log2_subpixels,
+ (penum->text.operation & TEXT_DO_ANY_CHARPATH) != 0 ||
+ penum->device_disabled_grid_fitting,
+ pfont1->PaintType, pfont1);
+}
+
+/* ---------------- .type1execchar ---------------- */
+
+/*
+ * This is the workhorse for %Type1/2BuildChar, %Type1/2BuildGlyph,
+ * CCRun, and CID fonts. Eventually this will appear in the C API;
+ * even now, its normal control path doesn't use any continuations.
+ */
+
+/*
+ * Define the state record for this operator, which must save the metrics
+ * separately as well as the Type 1 interpreter state.
+ */
+typedef struct gs_type1exec_state_s {
+ gs_type1_state cis; /* must be first */
+ i_ctx_t *i_ctx_p; /* so push/pop can access o-stack */
+ double sbw[4];
+ int /*metrics_present */ present;
+ gs_rect char_bbox;
+ bool use_FontBBox_as_Metrics2;
+ /*
+ * The following elements are only used locally to make the stack clean
+ * for OtherSubrs: they don't need to be declared for the garbage
+ * collector.
+ */
+ ref save_args[6];
+ int num_args;
+ bool AlignToPixels;
+} gs_type1exec_state;
+
+gs_private_st_suffix_add1(st_gs_type1exec_state, gs_type1exec_state,
+ "gs_type1exec_state", gs_type1exec_state_enum_ptrs,
+ gs_type1exec_state_reloc_ptrs, st_gs_type1_state,
+ i_ctx_p);
+
+/* Forward references */
+static int bbox_continue(i_ctx_t *);
+static int nobbox_continue(i_ctx_t *);
+static int type1_push_OtherSubr(i_ctx_t *, const gs_type1exec_state *,
+ int (*)(i_ctx_t *), const ref *);
+static int type1_call_OtherSubr(i_ctx_t *, const gs_type1exec_state *,
+ int (*)(i_ctx_t *), const ref *);
+static int type1_callout_dispatch(i_ctx_t *, int (*)(i_ctx_t *), int);
+static int type1_continue_dispatch(i_ctx_t *, gs_type1exec_state *,
+ const ref *, ref *, int);
+static int op_type1_cleanup(i_ctx_t *);
+static void op_type1_free(i_ctx_t *);
+static int bbox_getsbw_continue(i_ctx_t *);
+static int type1exec_bbox(i_ctx_t *, gs_text_enum_t *, gs_type1exec_state *, gs_font *, op_proc_t *exec_cont);
+static int bbox_finish_fill(i_ctx_t *);
+static int bbox_finish_stroke(i_ctx_t *);
+static int bbox_fill(i_ctx_t *);
+static int bbox_stroke(i_ctx_t *);
+static int nobbox_finish(i_ctx_t *, gs_type1exec_state *);
+static int nobbox_draw(i_ctx_t *, int (*)(gs_state *));
+static int nobbox_fill(i_ctx_t *);
+static int nobbox_stroke(i_ctx_t *);
+
+/* <font> <code|name> <name> <charstring> .type1execchar - */
+static int
+ztype1execchar(i_ctx_t *i_ctx_p)
+{
+ return charstring_execchar(i_ctx_p, (1 << (int)ft_encrypted) |
+ (1 << (int)ft_disk_based));
+}
+static int
+charstring_execchar_aux(i_ctx_t *i_ctx_p, gs_text_enum_t *penum, gs_font *pfont)
+{
+ os_ptr op = osp;
+ gs_font_base *const pbfont = (gs_font_base *) pfont;
+ gs_font_type1 *const pfont1 = (gs_font_type1 *) pfont;
+ const gs_type1_data *pdata;
+ gs_type1exec_state cxs;
+ gs_type1_state *const pcis = &cxs.cis;
+ gs_rect FontBBox = pfont1->FontBBox;
+ int code;
+
+ if (penum->current_font->FontType == ft_CID_encrypted) {
+ if (FontBBox.q.x <= FontBBox.p.x && FontBBox.q.y <= FontBBox.p.y) {
+ gs_font_cid0 *pfcid0 = (gs_font_cid0 *)penum->current_font;
+
+ FontBBox = pfcid0->FontBBox;
+ }
+ }
+
+ pdata = &pfont1->data;
+ /*
+ * Any reasonable implementation would execute something like
+ * 1 setmiterlimit 0 setlinejoin 0 setlinecap
+ * here, but the Adobe implementations don't.
+ *
+ * If this is a stroked font, set the stroke width.
+ */
+ if (pfont->PaintType)
+ gs_setlinewidth(igs, pfont->StrokeWidth);
+ check_estack(3); /* for continuations */
+ /*
+ * Execute the definition of the character.
+ */
+ if (r_is_proc(op))
+ return zchar_exec_char_proc(i_ctx_p);
+ /*
+ * The definition must be a Type 1 CharString.
+ * Note that we do not require read access: this is deliberate.
+ */
+ check_type(*op, t_string);
+ if (r_size(op) <= max(pdata->lenIV, 0))
+ return_error(gs_error_invalidfont);
+ /*
+ * In order to make character oversampling work, we must
+ * set up the cache before calling .type1addpath.
+ * To do this, we must get the bounding box from the FontBBox,
+ * and the width from the CharString or the Metrics.
+ * If the FontBBox isn't valid, we can't do any of this.
+ */
+
+ if ((penum->FontBBox_as_Metrics2.x == 0 &&
+ penum->FontBBox_as_Metrics2.y == 0) ||
+ gs_rootfont(igs)->WMode == 0 ) {
+ code = zchar_get_metrics(pbfont, op - 1, cxs.sbw);
+ if (code < 0)
+ return code;
+ cxs.present = code;
+ cxs.use_FontBBox_as_Metrics2 = false;
+ } else { /* pass here if FontType==9,11 && WMode==1*/
+ cxs.sbw[0] = penum->FontBBox_as_Metrics2.x / 2;
+ cxs.sbw[1] = penum->FontBBox_as_Metrics2.y;
+ cxs.sbw[2] = 0;
+ cxs.sbw[3] = -penum->FontBBox_as_Metrics2.x; /* Sic! */
+ cxs.use_FontBBox_as_Metrics2 = true;
+ cxs.present = metricsNone;
+ }
+ /* Establish a current point. */
+ code = gs_moveto(igs, 0.0, 0.0);
+ if (code < 0)
+ return code;
+ code = type1_exec_init(pcis, penum, igs, pfont1);
+ if (code < 0)
+ return code;
+ gs_type1_set_callback_data(pcis, &cxs);
+ if (FontBBox.q.x > FontBBox.p.x &&
+ FontBBox.q.y > FontBBox.p.y
+ ) {
+ /* The FontBBox appears to be valid. */
+ op_proc_t exec_cont = 0;
+
+ cxs.char_bbox = pfont1->FontBBox;
+ code = type1exec_bbox(i_ctx_p, penum, &cxs, pfont, &exec_cont);
+ if (code >= 0 && exec_cont != 0)
+ code = (*exec_cont)(i_ctx_p);
+ return code;
+ } else {
+ /* The FontBBox is not valid */
+ const ref *opstr = op;
+ ref other_subr;
+ const gs_matrix * pctm = &ctm_only(igs);
+
+ /* First, check for singular CTM */
+ if (pctm->xx * pctm->yy == pctm->xy * pctm->yx) {
+ /* The code below won't be able to find the FontBBox but we
+ * don't need it anyway. Set an empty box and consider it valid.
+ */
+ op_proc_t exec_cont = 0;
+
+ cxs.char_bbox.p.x = 0;
+ cxs.char_bbox.p.y = 0;
+ cxs.char_bbox.q.x = 0;
+ cxs.char_bbox.q.y = 0;
+ code = type1exec_bbox(i_ctx_p, penum, &cxs, pfont, &exec_cont);
+ if (code >= 0 && exec_cont != 0)
+ code = (*exec_cont)(i_ctx_p);
+ return code;
+ }
+ /* Now we create the path first, then do the setcachedevice.
+ * If we are oversampling (in this case, only for anti-
+ * aliasing, not just to improve quality), we have to
+ * create the path twice, since we can't know the
+ * oversampling factor until after setcachedevice.
+ */
+ switch (cxs.present) {
+ case metricsSideBearingAndWidth: {
+ gs_point pt;
+
+ pt.x = cxs.sbw[0], pt.y = cxs.sbw[1];
+ gs_type1_set_lsb(pcis, &pt);
+ }
+ /* fall through */
+ case metricsWidthOnly: {
+ gs_point pt;
+
+ pt.x = cxs.sbw[2], pt.y = cxs.sbw[3];
+ gs_type1_set_width(pcis, &pt);
+ }
+ }
+
+ /* Continue interpreting. */
+ icont:
+ code = type1_continue_dispatch(i_ctx_p, &cxs, opstr, &other_subr, 4);
+ op = osp; /* OtherSubrs might change it */
+ switch (code) {
+ case 0: /* all done */
+ return nobbox_finish(i_ctx_p, &cxs);
+ default: /* code < 0, error */
+ return code;
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ return type1_call_OtherSubr(i_ctx_p, &cxs, nobbox_continue,
+ &other_subr);
+ case type1_result_sbw: /* [h]sbw, just continue */
+ switch (cxs.present) {
+ case metricsNone:
+ cxs.sbw[0] = fixed2float(pcis->lsb.x);
+ cxs.sbw[1] = fixed2float(pcis->lsb.y);
+ /* fall through */
+ case metricsWidthOnly:
+ cxs.sbw[2] = fixed2float(pcis->width.x);
+ cxs.sbw[3] = fixed2float(pcis->width.y);
+ }
+ opstr = 0;
+ goto icont;
+ }
+ }
+}
+
+int
+charstring_execchar(i_ctx_t *i_ctx_p, int font_type_mask)
+{
+ gs_text_enum_t *penum = op_show_find(i_ctx_p);
+ gs_font *pfont;
+ os_ptr op = osp;
+ int code = font_param(op - 3, &pfont);
+
+ if (code < 0)
+ return code;
+ if (penum == 0 ||
+ pfont->FontType >= sizeof(font_type_mask) * 8 ||
+ !(font_type_mask & (1 << (int)pfont->FontType)))
+ return_error(gs_error_undefined);
+ code = charstring_execchar_aux(i_ctx_p, penum, pfont);
+ if (code < 0 && igs->in_cachedevice == CACHE_DEVICE_CACHING) {
+ /* Perform the cache cleanup, when the cached character data
+ has been allocated (gx_alloc_char_bits) but
+ the character has not been added to the cache (gx_add_cached_char)
+ due to a falure in the character renderer.
+ */
+ gs_show_enum *const penum_s = (gs_show_enum *)penum;
+
+ if (penum_s->cc != NULL) {
+ gx_free_cached_char(pfont->dir, penum_s->cc);
+ penum_s->cc = NULL;
+ }
+ }
+ return code;
+}
+
+/* -------- bbox case -------- */
+
+/* Do all the work for the case where we have a bounding box. */
+/* Returns exec_cont - a function, which must be called by caller after this function. */
+static int
+type1exec_bbox(i_ctx_t *i_ctx_p, gs_text_enum_t *penum, gs_type1exec_state * pcxs,
+ gs_font * pfont, op_proc_t *exec_cont)
+{
+ os_ptr op = osp;
+ gs_type1_state *const pcis = &pcxs->cis;
+ gs_font_base *const pbfont = (gs_font_base *) pfont;
+ op_proc_t cont = (pbfont->PaintType == 0 && penum->orig_font->PaintType == 0
+ ? bbox_finish_fill : bbox_finish_stroke);
+ ref *pcdevproc;
+
+ /*
+ * We appear to have a valid bounding box. If we don't have Metrics for
+ * this character, start interpreting the CharString; do the
+ * setcachedevice as soon as we know the (side bearing and) width.
+ */
+ if ((pcxs->present == metricsNone && !pcxs->use_FontBBox_as_Metrics2) ||
+ (penum->orig_font->WMode && zchar_get_CDevProc(pbfont, &pcdevproc))) {
+ /* Get the width from the CharString,
+ * then set the cache device. */
+ /* We pass here when WMode==1 and the font has CDevProc,
+ * because we do need sbw as CDevProc's argument.
+ * A more natural way would be not setting pcxs->use_FontBBox_as_Metrics2
+ * when the font has CDevProc, except for missing sbw in the glyph.
+ * We prefer to pass here because we've got examples
+ * of Tyoe 1 fonts with empty glyphs, i.e. with no sbw,
+ * so we don't want to assume that they'll never appear in a CID font.
+ * In that case penum->FontBBox_as_Metrics2 will go here to zchar_set_cache. */
+ ref cnref;
+ ref other_subr;
+ int code;
+
+ /* Since an OtherSubr callout might change osp, */
+ /* save the character name now. */
+ ref_assign(&cnref, op - 1);
+ code = type1_continue_dispatch(i_ctx_p, pcxs, op, &other_subr, 4);
+ op = osp; /* OtherSubrs might change it */
+ switch (code) {
+ default: /* code < 0 or done, error */
+ return ((code < 0 ? code :
+ gs_note_error(gs_error_invalidfont)));
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ return type1_call_OtherSubr(i_ctx_p, pcxs,
+ bbox_getsbw_continue,
+ &other_subr);
+ case type1_result_sbw: /* [h]sbw, done */
+ break;
+ }
+ type1_cis_get_metrics(pcis, pcxs->sbw);
+ return zchar_set_cache(i_ctx_p, pbfont, &cnref,
+ NULL, pcxs->sbw + 2,
+ &pcxs->char_bbox,
+ cont, exec_cont, NULL);
+ } else {
+ /* We have the width and bounding box: */
+ /* set up the cache device now. */
+ return zchar_set_cache(i_ctx_p, pbfont, op - 1,
+ (pcxs->present == metricsSideBearingAndWidth
+ && !pcxs->use_FontBBox_as_Metrics2 ?
+ pcxs->sbw : NULL),
+ pcxs->sbw + 2,
+ &pcxs->char_bbox,
+ cont, exec_cont,
+ (pcxs->use_FontBBox_as_Metrics2 ? pcxs->sbw : NULL));
+ }
+}
+
+/* Continue from an OtherSubr callout while getting metrics. */
+static int
+bbox_getsbw_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref other_subr;
+ gs_type1exec_state *pcxs = r_ptr(esp, gs_type1exec_state);
+ gs_type1_state *const pcis = &pcxs->cis;
+ int code;
+
+ code = type1_continue_dispatch(i_ctx_p, pcxs, NULL, &other_subr, 4);
+ op = osp; /* in case z1_push/pop_proc was called */
+ switch (code) {
+ default: /* code < 0 or done, error */
+ op_type1_free(i_ctx_p);
+ return ((code < 0 ? code : gs_note_error(gs_error_invalidfont)));
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ return type1_push_OtherSubr(i_ctx_p, pcxs, bbox_getsbw_continue,
+ &other_subr);
+ case type1_result_sbw: { /* [h]sbw, done */
+ double sbw[4];
+ const gs_font_base *const pbfont =
+ (const gs_font_base *)pcis->pfont;
+ gs_rect bbox;
+ op_proc_t cont = (pbfont->PaintType == 0 ? bbox_finish_fill : bbox_finish_stroke), exec_cont = 0;
+
+ /* Get the metrics before freeing the state. */
+ type1_cis_get_metrics(pcis, sbw);
+ bbox = pcxs->char_bbox;
+ op_type1_free(i_ctx_p);
+ code = zchar_set_cache(i_ctx_p, pbfont, op - 1, sbw, sbw + 2, &bbox,
+ cont, &exec_cont, NULL);
+ if (code >= 0 && exec_cont != 0)
+ code = (*exec_cont)(i_ctx_p);
+ return code;
+ }
+ }
+}
+
+/* <font> <code|name> <name> <charstring> <sbx> <sby> %bbox_{fill|stroke} - */
+/* <font> <code|name> <name> <charstring> %bbox_{fill|stroke} - */
+static int bbox_finish(i_ctx_t *i_ctx_p, op_proc_t cont, op_proc_t *exec_cont);
+static int
+bbox_finish_fill(i_ctx_t *i_ctx_p)
+{
+ op_proc_t exec_cont = 0;
+ int code;
+
+ code = bbox_finish(i_ctx_p, bbox_fill, &exec_cont);
+ if (code >= 0 && exec_cont != 0)
+ code = exec_cont(i_ctx_p);
+ return code;
+}
+static int
+bbox_finish_stroke(i_ctx_t *i_ctx_p)
+{
+ op_proc_t exec_cont = 0;
+ int code;
+
+ code = bbox_finish(i_ctx_p, bbox_stroke, &exec_cont);
+ if (code >= 0 && exec_cont != 0)
+ code = exec_cont(i_ctx_p);
+ return code;
+}
+
+static int
+bbox_finish(i_ctx_t *i_ctx_p, op_proc_t cont, op_proc_t *exec_cont)
+{ /* Returns exec_cont - a function, which must be called by caller after this function. */
+ os_ptr op = osp;
+ gs_font *pfont;
+ int code;
+ gs_text_enum_t *penum = op_show_find(i_ctx_p);
+ gs_type1exec_state cxs; /* stack allocate to avoid sandbars */
+ gs_type1_state *const pcis = &cxs.cis;
+ double sbxy[2];
+ gs_point sbpt;
+ gs_point *psbpt = 0;
+ os_ptr opc = op;
+ const ref *opstr;
+ ref other_subr;
+
+ if (!r_has_type(opc, t_string)) {
+ check_op(3);
+ code = num_params(op, 2, sbxy);
+ if (code < 0)
+ return code;
+ sbpt.x = sbxy[0];
+ sbpt.y = sbxy[1];
+ psbpt = &sbpt;
+ opc -= 2;
+ check_type(*opc, t_string);
+ }
+ code = font_param(opc - 3, &pfont);
+ if (code < 0)
+ return code;
+ if (penum == 0 || !font_uses_charstrings(pfont))
+ return_error(gs_error_undefined);
+ {
+ gs_font_type1 *const pfont1 = (gs_font_type1 *) pfont;
+ int lenIV = pfont1->data.lenIV;
+
+ if (lenIV > 0 && r_size(opc) <= lenIV)
+ return_error(gs_error_invalidfont);
+ check_estack(5); /* in case we need to do a callout */
+ code = type1_exec_init(pcis, penum, igs, pfont1);
+ if (code < 0)
+ return code;
+ if (psbpt)
+ gs_type1_set_lsb(pcis, psbpt);
+ }
+ opstr = opc;
+ icont:
+ code = type1_continue_dispatch(i_ctx_p, &cxs, opstr, &other_subr,
+ (psbpt ? 6 : 4));
+ op = osp; /* OtherSubrs might have altered it */
+ switch (code) {
+ case 0: /* all done */
+ /* Call the continuation now. */
+ if (psbpt)
+ pop(2);
+ *exec_cont = cont;
+ return 0;
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ push_op_estack(cont); /* call later */
+ return type1_call_OtherSubr(i_ctx_p, &cxs, bbox_continue,
+ &other_subr);
+ case type1_result_sbw: /* [h]sbw, just continue */
+ opstr = 0;
+ goto icont;
+ default: /* code < 0, error */
+ return code;
+ }
+}
+
+static int
+bbox_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int npop = (r_has_type(op, t_string) ? 4 : 6);
+ int code = type1_callout_dispatch(i_ctx_p, bbox_continue, npop);
+
+ if (code == 0) {
+ op = osp; /* OtherSubrs might have altered it */
+ npop -= 4; /* nobbox_fill/stroke handles the rest */
+ pop(npop);
+ op -= npop;
+ op_type1_free(i_ctx_p);
+ }
+ return code;
+}
+
+/*
+ * Check the path against FontBBox before drawing. The original operands
+ * of type1execchar are still on the o-stack.
+ * Returns exec_cont - a function, which must be called by caller after this function.
+ */
+static int
+bbox_draw(i_ctx_t *i_ctx_p, int (*draw)(gs_state *), op_proc_t *exec_cont)
+{
+ os_ptr op = osp;
+ gs_rect bbox;
+ gs_font *pfont;
+ gs_text_enum_t *penum;
+ gs_font_base * pbfont;
+ gs_font_type1 * pfont1;
+ gs_type1exec_state cxs;
+ int code;
+
+ if (igs->in_cachedevice < 2) /* not caching */
+ return nobbox_draw(i_ctx_p, draw);
+ if ((code = font_param(op - 3, &pfont)) < 0)
+ return code;
+ penum = op_show_find(i_ctx_p);
+ if (penum == 0 || !font_uses_charstrings(pfont))
+ return_error(gs_error_undefined);
+ if ((code = gs_pathbbox(igs, &bbox)) < 0) {
+ /*
+ * If the matrix is singular, all user coordinates map onto a
+ * straight line. Don't bother rendering the character at all.
+ */
+ if (code == gs_error_undefinedresult) {
+ pop(4);
+ gs_newpath(igs);
+ return 0;
+ }
+ return code;
+ }
+ if (draw == gs_stroke) {
+ /* Expand the bounding box by the line width. */
+ float width = gs_currentlinewidth(igs) * 1.41422;
+
+ bbox.p.x -= width, bbox.p.y -= width;
+ bbox.q.x += width, bbox.q.y += width;
+ }
+ pbfont = (gs_font_base *)pfont;
+ if (rect_within(bbox, pbfont->FontBBox)) /* within bounds */
+ return nobbox_draw(i_ctx_p, draw);
+ /* Enlarge the FontBBox to save work in the future. */
+ rect_merge(pbfont->FontBBox, bbox);
+ /* Dismantle everything we've done, and start over. */
+ gs_text_retry(penum);
+ pfont1 = (gs_font_type1 *) pfont;
+ if ((penum->FontBBox_as_Metrics2.x == 0 &&
+ penum->FontBBox_as_Metrics2.y == 0) ||
+ gs_rootfont(igs)->WMode == 0 ) {
+ code = zchar_get_metrics(pbfont, op - 1, cxs.sbw);
+ if (code < 0)
+ return code;
+ cxs.present = code;
+ cxs.use_FontBBox_as_Metrics2 = false;
+ } else {
+ cxs.sbw[0] = penum->FontBBox_as_Metrics2.x / 2;
+ cxs.sbw[1] = penum->FontBBox_as_Metrics2.y;
+ cxs.sbw[2] = 0;
+ cxs.sbw[3] = -penum->FontBBox_as_Metrics2.x; /* Sic! */
+ cxs.use_FontBBox_as_Metrics2 = true;
+ cxs.present = metricsSideBearingAndWidth;
+ }
+ code = type1_exec_init(&cxs.cis, penum, igs, pfont1);
+ if (code < 0)
+ return code;
+ cxs.char_bbox = pfont1->FontBBox;
+ code = type1exec_bbox(i_ctx_p, penum, &cxs, pfont, exec_cont);
+ return code;
+}
+static int
+bbox_fill(i_ctx_t *i_ctx_p)
+{
+ op_proc_t exec_cont = 0;
+ int code;
+
+ /* See above re GS_CHAR_FILL. */
+ code = bbox_draw(i_ctx_p, GS_CHAR_FILL, &exec_cont);
+ if (code >= 0 && exec_cont != 0)
+ code = (*exec_cont)(i_ctx_p);
+ return code;
+}
+static int
+bbox_stroke(i_ctx_t *i_ctx_p)
+{
+ op_proc_t exec_cont = 0;
+ int code;
+
+ code = bbox_draw(i_ctx_p, gs_stroke, &exec_cont);
+ if (code >= 0 && exec_cont != 0)
+ code = (*exec_cont)(i_ctx_p);
+ return code;
+}
+
+/* -------- Common code -------- */
+
+/* Handle the results of interpreting the CharString. */
+/* pcref points to a t_string ref. */
+static int
+type1_continue_dispatch(i_ctx_t *i_ctx_p, gs_type1exec_state *pcxs,
+ const ref * pcref, ref *pos, int num_args)
+{
+ int value;
+ int code;
+ gs_glyph_data_t cs_data;
+ gs_glyph_data_t *pcsd;
+
+ cs_data.memory = imemory;
+ if (pcref == 0) {
+ pcsd = 0;
+ } else {
+ gs_glyph_data_from_string(&cs_data, pcref->value.const_bytes,
+ r_size(pcref), NULL);
+ pcsd = &cs_data;
+ }
+ /*
+ * Since OtherSubrs may push or pop values on the PostScript operand
+ * stack, remove the arguments of .type1execchar before calling the
+ * Type 1 interpreter, and put them back afterwards unless we're
+ * about to execute an OtherSubr procedure. Also, we must set up
+ * the callback data for pushing OtherSubrs arguments.
+ */
+ pcxs->i_ctx_p = i_ctx_p;
+ pcxs->num_args = num_args;
+ memcpy(pcxs->save_args, osp - (num_args - 1), num_args * sizeof(ref));
+ osp -= num_args;
+ gs_type1_set_callback_data(&pcxs->cis, pcxs);
+ code = pcxs->cis.pfont->data.interpret(&pcxs->cis, pcsd, &value);
+ switch (code) {
+ case type1_result_callothersubr: {
+ /*
+ * The Type 1 interpreter handles all known OtherSubrs,
+ * so this must be an unknown one.
+ */
+ const font_data *pfdata = pfont_data(gs_currentfont(igs));
+
+ code = array_get(imemory, &pfdata->u.type1.OtherSubrs, (long)value, pos);
+ if (code >= 0)
+ return type1_result_callothersubr;
+ }
+ }
+ /* Put back the arguments removed above. */
+ memcpy(osp + 1, pcxs->save_args, num_args * sizeof(ref));
+ osp += num_args;
+ return code;
+}
+
+/*
+ * Push a continuation, the arguments removed for the OtherSubr, and
+ * the OtherSubr procedure.
+ */
+static int
+type1_push_OtherSubr(i_ctx_t *i_ctx_p, const gs_type1exec_state *pcxs,
+ int (*cont)(i_ctx_t *), const ref *pos)
+{
+ int i, n = pcxs->num_args;
+
+ push_op_estack(cont);
+ /*
+ * Push the saved arguments (in reverse order, so they will get put
+ * back on the operand stack in the correct order) on the e-stack.
+ */
+ for (i = n; --i >= 0; ) {
+ *++esp = pcxs->save_args[i];
+ r_clear_attrs(esp, a_executable); /* just in case */
+ }
+ ++esp;
+ *esp = *pos;
+ return o_push_estack;
+}
+
+/*
+ * Do a callout to an OtherSubr implemented in PostScript.
+ * The caller must have done a check_estack(4 + num_args).
+ */
+static int
+type1_call_OtherSubr(i_ctx_t *i_ctx_p, const gs_type1exec_state * pcxs,
+ int (*cont) (i_ctx_t *),
+ const ref * pos)
+{
+ /* Move the Type 1 interpreter state to the heap. */
+ gs_type1exec_state *hpcxs =
+ ialloc_struct(gs_type1exec_state, &st_gs_type1exec_state,
+ "type1_call_OtherSubr");
+
+ if (hpcxs == 0)
+ return_error(gs_error_VMerror);
+ *hpcxs = *pcxs;
+ gs_type1_set_callback_data(&hpcxs->cis, hpcxs);
+ push_mark_estack(es_show, op_type1_cleanup);
+ ++esp;
+ make_istruct(esp, 0, hpcxs);
+ return type1_push_OtherSubr(i_ctx_p, pcxs, cont, pos);
+}
+
+/* Continue from an OtherSubr callout while building the path. */
+static int
+type1_callout_dispatch(i_ctx_t *i_ctx_p, int (*cont)(i_ctx_t *),
+ int num_args)
+{
+ ref other_subr;
+ gs_type1exec_state *pcxs = r_ptr(esp, gs_type1exec_state);
+ int code;
+
+ icont:
+ code = type1_continue_dispatch(i_ctx_p, pcxs, NULL, &other_subr,
+ num_args);
+ switch (code) {
+ case 0: /* callout done, cont is on e-stack */
+ return 0;
+ default: /* code < 0 or done, error */
+ op_type1_free(i_ctx_p);
+ return ((code < 0 ? code : gs_note_error(gs_error_invalidfont)));
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ return type1_push_OtherSubr(i_ctx_p, pcxs, cont, &other_subr);
+ case type1_result_sbw: /* [h]sbw, just continue */
+ goto icont;
+ }
+}
+
+/* Clean up after a Type 1 callout. */
+static int
+op_type1_cleanup(i_ctx_t *i_ctx_p)
+{
+ ifree_object(r_ptr(esp + 2, void), "op_type1_cleanup");
+ return 0;
+}
+static void
+op_type1_free(i_ctx_t *i_ctx_p)
+{
+ ifree_object(r_ptr(esp, void), "op_type1_free");
+ /*
+ * In order to avoid popping from the e-stack and then pushing onto
+ * it, which would violate an interpreter invariant, we simply
+ * overwrite the two e-stack items being discarded (hpcxs and the
+ * cleanup operator) with empty procedures.
+ */
+ make_empty_const_array(esp - 1, a_readonly + a_executable);
+ make_empty_const_array(esp, a_readonly + a_executable);
+}
+
+/* -------- no-bbox case -------- */
+
+static int
+nobbox_continue(i_ctx_t *i_ctx_p)
+{
+ int code = type1_callout_dispatch(i_ctx_p, nobbox_continue, 4);
+
+ if (code)
+ return code;
+ {
+ gs_type1exec_state *pcxs = r_ptr(esp, gs_type1exec_state);
+ gs_type1exec_state cxs;
+
+ cxs = *pcxs;
+ gs_type1_set_callback_data(&cxs.cis, &cxs);
+ op_type1_free(i_ctx_p);
+ return nobbox_finish(i_ctx_p, &cxs);
+ }
+}
+
+/* Finish the no-FontBBox case after constructing the path. */
+/* If we are oversampling for anti-aliasing, we have to go around again. */
+/* <font> <code|name> <name> <charstring> %nobbox_continue - */
+static int
+nobbox_finish(i_ctx_t *i_ctx_p, gs_type1exec_state * pcxs)
+{
+ os_ptr op = osp;
+ int code;
+ gs_text_enum_t *penum = op_show_find(i_ctx_p);
+ gs_font *pfont;
+
+ if ((code = gs_pathbbox(igs, &pcxs->char_bbox)) < 0 ||
+ (code = font_param(op - 3, &pfont)) < 0
+ )
+ return code;
+ if (penum == 0 || !font_uses_charstrings(pfont))
+ return_error(gs_error_undefined);
+ {
+ gs_font_base *const pbfont = (gs_font_base *) pfont;
+ gs_font_type1 *const pfont1 = (gs_font_type1 *) pfont;
+ op_proc_t cont, exec_cont = 0;
+
+ if (pcxs->present == metricsNone) {
+ gs_point endpt;
+
+ if ((code = gs_currentpoint(igs, &endpt)) < 0)
+ return code;
+ pcxs->sbw[2] = endpt.x, pcxs->sbw[3] = endpt.y;
+ pcxs->present = metricsSideBearingAndWidth;
+ }
+ /*
+ * We only need to rebuild the path from scratch if we might
+ * oversample for anti-aliasing.
+ */
+ if ((*dev_proc(igs->device, get_alpha_bits))(igs->device, go_text) > 1
+ ) {
+ gs_newpath(igs);
+ gs_moveto(igs, 0.0, 0.0);
+ code = type1_exec_init(&pcxs->cis, penum, igs, pfont1);
+ if (code < 0)
+ return code;
+ code = type1exec_bbox(i_ctx_p, penum, pcxs, pfont, &exec_cont);
+ } else {
+ cont = (pbfont->PaintType == 0 && penum->orig_font->PaintType == 0
+ ? nobbox_fill : nobbox_stroke);
+ exec_cont = 0;
+ code = zchar_set_cache(i_ctx_p, pbfont, op - 1, NULL,
+ pcxs->sbw + 2,
+ &pcxs->char_bbox,
+ cont, &exec_cont,
+ (pcxs->use_FontBBox_as_Metrics2 ? pcxs->sbw : NULL));
+ }
+ if (code >= 0 && exec_cont != 0)
+ code = (*exec_cont)(i_ctx_p);
+ return code;
+ }
+}
+/* Finish by popping the operands and filling or stroking. */
+static int
+nobbox_draw(i_ctx_t *i_ctx_p, int (*draw)(gs_state *))
+{
+ int code = draw(igs);
+
+ if (code >= 0)
+ pop(4);
+ return code;
+}
+static int
+nobbox_fill(i_ctx_t *i_ctx_p)
+{
+ /* See above re GS_CHAR_FILL. */
+ return nobbox_draw(i_ctx_p, GS_CHAR_FILL);
+}
+static int
+nobbox_stroke(i_ctx_t *i_ctx_p)
+{
+ /* As a compatibility to Adobe, use the exact "StrokeWidth".
+ Reset fill_adjust for that. */
+ int code;
+ gs_fixed_point fa = i_ctx_p->pgs->fill_adjust;
+
+ i_ctx_p->pgs->fill_adjust.x = i_ctx_p->pgs->fill_adjust.y = 0;
+ code = nobbox_draw(i_ctx_p, gs_stroke);
+ i_ctx_p->pgs->fill_adjust = fa;
+ return code;
+}
+
+/* <font> <array> .setweightvector - */
+static int
+zsetweightvector(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_font *pfont;
+ int code = font_param(op - 1, &pfont);
+ gs_font_type1 *pfont1;
+ int size;
+
+ if (code < 0) {
+ /* The font was not defined yet. Just ignore. See lib/gs_type1.ps . */
+ pop(2);
+ return 0;
+ }
+ if (pfont->FontType != ft_encrypted && pfont->FontType != ft_encrypted2)
+ return_error(gs_error_invalidfont);
+ pfont1 = (gs_font_type1 *)pfont;
+ size = r_size(op);
+ if (size != pfont1->data.WeightVector.count)
+ return_error(gs_error_invalidfont);
+ code = process_float_array(imemory, op, size, pfont1->data.WeightVector.values);
+ if (code < 0)
+ return code;
+ pop(2);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zchar1_op_defs[] =
+{
+ {"4.type1execchar", ztype1execchar},
+ /* Internal operators */
+ {"4%bbox_getsbw_continue", bbox_getsbw_continue},
+ {"4%bbox_continue", bbox_continue},
+ {"4%bbox_finish_fill", bbox_finish_fill},
+ {"4%bbox_finish_stroke", bbox_finish_stroke},
+ {"4%nobbox_continue", nobbox_continue},
+ {"4%nobbox_fill", nobbox_fill},
+ {"4%nobbox_stroke", nobbox_stroke},
+ {"4.setweightvector", zsetweightvector},
+ op_def_end(0)
+};
+
+/* ------ Auxiliary procedures for type 1 fonts ------ */
+
+static int
+z1_glyph_data(gs_font_type1 * pfont, gs_glyph glyph, gs_glyph_data_t *pgd)
+{
+ ref gref;
+
+ glyph_ref(pfont->memory, glyph, &gref);
+ return zchar_charstring_data((gs_font *)pfont, &gref, pgd);
+}
+
+static int
+z1_subr_data(gs_font_type1 * pfont, int index, bool global,
+ gs_glyph_data_t *pgd)
+{
+ const font_data *pfdata = pfont_data(pfont);
+ ref subr;
+ int code;
+
+ code = array_get(pfont->memory, (global ? &pfdata->u.type1.GlobalSubrs :
+ &pfdata->u.type1.Subrs),
+ index, &subr);
+ if (code < 0)
+ return code;
+ check_type_only(subr, t_string);
+ gs_glyph_data_from_string(pgd, subr.value.const_bytes, r_size(&subr),
+ NULL);
+ return 0;
+}
+
+static int
+z1_seac_data(gs_font_type1 *pfont, int ccode, gs_glyph *pglyph,
+ gs_const_string *gstr, gs_glyph_data_t *pgd)
+{
+ gs_glyph glyph = gs_c_known_encode((gs_char)ccode,
+ ENCODING_INDEX_STANDARD);
+ int code;
+ ref rglyph;
+
+ if (glyph == GS_NO_GLYPH)
+ return_error(gs_error_rangecheck);
+ if ((code = gs_c_glyph_name(glyph, gstr)) < 0 ||
+ (code = name_ref(pfont->memory, gstr->data, gstr->size, &rglyph, 0)) < 0
+ )
+ return code;
+ if (pglyph)
+ *pglyph = name_index(pfont->memory, &rglyph);
+ if (pgd)
+ code = zchar_charstring_data((gs_font *)pfont, &rglyph, pgd);
+ return code;
+}
+
+static int
+z1_push(void *callback_data, const fixed * pf, int count)
+{
+ gs_type1exec_state *pcxs = callback_data;
+ i_ctx_t *i_ctx_p = pcxs->i_ctx_p;
+ const fixed *p = pf + count - 1;
+ int i;
+
+ check_ostack(count);
+ for (i = 0; i < count; i++, p--) {
+ osp++;
+ make_real(osp, fixed2float(*p));
+ }
+ return 0;
+}
+
+static int
+z1_pop(void *callback_data, fixed * pf)
+{
+ gs_type1exec_state *pcxs = callback_data;
+ i_ctx_t *i_ctx_p = pcxs->i_ctx_p;
+ double val;
+ int code = real_param(osp, &val);
+
+ if (code < 0)
+ return code;
+ *pf = float2fixed(val);
+ osp--;
+ return 0;
+}
+
+/* Define the Type 1 procedure vector. */
+const gs_type1_data_procs_t z1_data_procs = {
+ z1_glyph_data, z1_subr_data, z1_seac_data, z1_push, z1_pop
+};
+
+/* ------ Font procedures for Type 1 fonts ------ */
+
+/*
+ * Get a Type 1 or Type 2 glyph outline. This is the glyph_outline
+ * procedure for the font.
+ */
+int
+zchar1_glyph_outline(gs_font *font, int WMode, gs_glyph glyph, const gs_matrix *pmat,
+ gx_path *ppath, double sbw[4])
+{
+ gs_font_type1 *const pfont1 = (gs_font_type1 *)font;
+ ref gref;
+ gs_glyph_data_t gdata;
+ int code;
+
+ glyph_ref(font->memory, glyph, &gref);
+ gdata.memory = font->memory;
+ code = zchar_charstring_data(font, &gref, &gdata);
+ if (code < 0)
+ return code;
+ return zcharstring_outline(pfont1, WMode, &gref, &gdata, pmat, ppath, sbw);
+}
+/*
+ * Get a glyph outline given a CharString. The glyph_outline procedure
+ * for CIDFontType 0 fonts uses this.
+ */
+int
+zcharstring_outline(gs_font_type1 *pfont1, int WMode, const ref *pgref,
+ const gs_glyph_data_t *pgd_orig,
+ const gs_matrix *pmat, gx_path *ppath, double sbw[4])
+{
+ const gs_glyph_data_t *pgd = pgd_orig;
+ int code;
+ gs_type1exec_state cxs;
+ gs_type1_state *const pcis = &cxs.cis;
+ const gs_type1_data *pdata;
+ int value;
+ gs_imager_state gis;
+ double wv[4];
+ gs_point mpt;
+
+ pdata = &pfont1->data;
+ if (pgd->bits.size <= max(pdata->lenIV, 0))
+ return_error(gs_error_invalidfont);
+#if 0 /* Ignore CDevProc for now. */
+ if (zchar_get_CDevProc((const gs_font_base *)pfont1, &pcdevproc))
+ return_error(gs_error_rangecheck); /* can't call CDevProc from here */
+#endif
+ switch (WMode) {
+ default:
+ code = zchar_get_metrics2((gs_font_base *)pfont1, pgref, wv);
+ if (code) {
+ sbw[0] = wv[2];
+ sbw[1] = wv[3];
+ sbw[2] = wv[0];
+ sbw[3] = wv[1];
+ break;
+ }
+ /* falls through */
+ case 0:
+ code = zchar_get_metrics((gs_font_base *)pfont1, pgref, sbw);
+ }
+ if (code < 0)
+ return code;
+ cxs.present = code;
+ /* Initialize just enough of the imager state. */
+ if (pmat)
+ gs_matrix_fixed_from_matrix(&gis.ctm, pmat);
+ else {
+ gs_matrix imat;
+
+ gs_make_identity(&imat);
+ gs_matrix_fixed_from_matrix(&gis.ctm, &imat);
+ }
+ gis.flatness = 0;
+ code = gs_type1_interp_init(&cxs.cis, &gis, ppath, NULL, NULL, true, 0,
+ pfont1);
+ if (code < 0)
+ return code;
+ cxs.cis.no_grid_fitting = true;
+ gs_type1_set_callback_data(pcis, &cxs);
+ switch (cxs.present) {
+ case metricsSideBearingAndWidth:
+ mpt.x = sbw[0], mpt.y = sbw[1];
+ gs_type1_set_lsb(pcis, &mpt);
+ /* falls through */
+ case metricsWidthOnly:
+ mpt.x = sbw[2], mpt.y = sbw[3];
+ gs_type1_set_width(pcis, &mpt);
+ case metricsNone:
+ ;
+ }
+ /* Continue interpreting. */
+icont:
+ code = pfont1->data.interpret(pcis, pgd, &value);
+ switch (code) {
+ case 0: /* all done */
+ /* falls through */
+ default: /* code < 0, error */
+ return code;
+ case type1_result_callothersubr: /* unknown OtherSubr */
+ return_error(gs_error_rangecheck); /* can't handle it */
+ case type1_result_sbw: /* [h]sbw, just continue */
+ type1_cis_get_metrics(pcis, cxs.sbw);
+ type1_cis_get_metrics(pcis, sbw);
+ pgd = 0;
+ goto icont;
+ }
+}
+
+/*
+ * Redefine glyph_info to take Metrics[2] and CDevProc into account (unless
+ * GLYPH_INFO_OUTLINE_WIDTHS is set). If CDevProc is present, return
+ * gs_error_rangecheck, since we can't call the interpreter from here.
+ */
+int
+z1_glyph_info_generic(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
+ int members, gs_glyph_info_t *info, font_proc_glyph_info((*proc)), int wmode)
+{
+ ref gref;
+ ref *pcdevproc;
+ gs_font_base *const pbfont = (gs_font_base *)font;
+ int width_members = members & (GLYPH_INFO_WIDTH0 << wmode);
+ int outline_widths = members & GLYPH_INFO_OUTLINE_WIDTHS;
+ bool modified_widths = false;
+ int default_members = members & ~(width_members + outline_widths +
+ GLYPH_INFO_VVECTOR0 + GLYPH_INFO_VVECTOR1 +
+ GLYPH_INFO_CDEVPROC);
+ int done_members = 0;
+ int code;
+
+ if (!width_members)
+ return (*proc)(font, glyph, pmat, members, info);
+ if (!outline_widths && zchar_get_CDevProc(pbfont, &pcdevproc)) {
+ done_members |= GLYPH_INFO_CDEVPROC;
+ if (members & GLYPH_INFO_CDEVPROC) {
+ info->members = done_members;
+ return_error(gs_error_rangecheck);
+ } else {
+ /* Ignore CDevProc. Used to compure MissingWidth.*/
+ }
+ }
+ glyph_ref(pbfont->memory, glyph, &gref);
+ if (width_members == GLYPH_INFO_WIDTH1) {
+ double wv[4];
+ code = zchar_get_metrics2(pbfont, &gref, wv);
+ if (code > 0) {
+ modified_widths = true;
+ info->width[1].x = wv[0];
+ info->width[1].y = wv[1];
+ info->v.x = wv[2];
+ info->v.y = wv[3];
+ done_members = width_members | GLYPH_INFO_VVECTOR1;
+ width_members = 0;
+ }
+ }
+ if (width_members) {
+ double sbw[4];
+ code = zchar_get_metrics(pbfont, &gref, sbw);
+ if (code > 0) {
+ modified_widths = true;
+ info->width[wmode].x = sbw[2];
+ info->width[wmode].y = sbw[3];
+ if (code == metricsSideBearingAndWidth) {
+ info->v.x = sbw[0];
+ info->v.y = sbw[1];
+ width_members |= GLYPH_INFO_VVECTOR0;
+ } else {
+ info->v.x = 0;
+ info->v.y = 0;
+ }
+ done_members = width_members;
+ width_members = 0;
+ }
+ }
+
+ if (outline_widths) {
+ if (modified_widths || zchar_get_CDevProc(pbfont, &pcdevproc)) {
+ /* Discard the modified widths, but indicate they exist. */
+ width_members |= done_members;
+ done_members = outline_widths;
+ }
+ }
+ default_members |= width_members;
+ if (default_members) {
+ code = (*proc)(font, glyph, pmat, default_members, info);
+
+ if (code < 0)
+ return code;
+ } else
+ info->members = 0;
+ info->members |= done_members;
+ return 0;
+}
+
+int
+z1_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
+ int members, gs_glyph_info_t *info)
+{
+ int wmode = font->WMode;
+
+ return z1_glyph_info_generic(font, glyph, pmat, members, info,
+ &gs_type1_glyph_info, wmode);
+}
+
+/* Get a Type 1 or Type 9 character metrics and set the cache device. */
+int
+z1_set_cache(i_ctx_t *i_ctx_p, gs_font_base *pbfont, ref *cnref,
+ gs_glyph glyph, op_proc_t cont, op_proc_t *exec_cont)
+{ /* This function is similar to zchar42_set_cache. */
+ double sbw[4];
+ gs_glyph_info_t info;
+ int wmode = gs_rootfont(igs)->WMode;
+ int code;
+ gs_matrix id_matrix = { identity_matrix_body };
+
+ code = gs_default_glyph_info((gs_font *)pbfont, glyph, &id_matrix,
+ ((GLYPH_INFO_WIDTH0 | GLYPH_INFO_VVECTOR0) << wmode) | GLYPH_INFO_BBOX,
+ &info);
+ if (code < 0)
+ return code;
+ sbw[0] = info.v.x;
+ sbw[1] = info.v.y;
+ sbw[2] = info.width[wmode].x;
+ sbw[3] = info.width[wmode].y;
+ return zchar_set_cache(i_ctx_p, pbfont, cnref, NULL,
+ sbw + 2, &info.bbox,
+ cont, exec_cont,
+ wmode ? sbw : NULL);
+}
diff --git a/psi/zchar2.c b/psi/zchar2.c
new file mode 100644
index 000000000..24ee70944
--- /dev/null
+++ b/psi/zchar2.c
@@ -0,0 +1,40 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Type 2 character display operator */
+#include "ghost.h"
+#include "oper.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxtype1.h"
+#include "ichar1.h"
+
+/* <font> <code|name> <name> <charstring> .type2execchar - */
+static int
+ztype2execchar(i_ctx_t *i_ctx_p)
+{
+ return charstring_execchar(i_ctx_p, (1 << (int)ft_encrypted2));
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zchar2_op_defs[] =
+{
+ {"4.type2execchar", ztype2execchar},
+ op_def_end(0)
+};
diff --git a/psi/zchar32.c b/psi/zchar32.c
new file mode 100644
index 000000000..f31293d71
--- /dev/null
+++ b/psi/zchar32.c
@@ -0,0 +1,210 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Type 32 font glyph operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gsccode.h" /* for gxfont.h */
+#include "gsmatrix.h"
+#include "gsutil.h"
+#include "gxfixed.h"
+#include "gxfont.h"
+#include "gxfcache.h"
+#include "ifont.h"
+#include "igstate.h"
+#include "store.h"
+
+/* ([wx wy llx lly urx ury] | [w0x w0y llx lly urx ury w1x w1y vx vy]) */
+/* <bitmap> <cid> <type32font> <str22> .makeglyph32 <<same with substr>> */
+static int
+zmakeglyph32(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ bool long_form;
+ uint msize;
+ double metrics[10];
+ int wx, llx, lly, urx, ury;
+ int width, height, raster;
+ gs_font *pfont;
+ int code;
+ byte *str;
+
+ check_array(op[-4]);
+ msize = r_size(op - 4);
+ switch (msize) {
+ case 10:
+ long_form = true;
+ break;
+ case 6:
+ long_form = false;
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ code = num_params(op[-4].value.refs + msize - 1, msize, metrics);
+ if (code < 0)
+ return code;
+ if (~code & 0x3c) /* check llx .. ury for integers */
+ return_error(gs_error_typecheck);
+ check_read_type(op[-3], t_string);
+ llx = (int)metrics[2];
+ lly = (int)metrics[3];
+ urx = (int)metrics[4];
+ ury = (int)metrics[5];
+ width = urx - llx;
+ height = ury - lly;
+ raster = (width + 7) >> 3;
+ if (width < 0 || height < 0 || r_size(op - 3) != raster * height)
+ return_error(gs_error_rangecheck);
+ check_int_leu(op[-2], 65535);
+ code = font_param(op - 1, &pfont);
+ if (code < 0)
+ return code;
+ if (pfont->FontType != ft_CID_bitmap)
+ return_error(gs_error_invalidfont);
+ check_write_type(*op, t_string);
+ if (r_size(op) < 22)
+ return_error(gs_error_rangecheck);
+ str = op->value.bytes;
+ if (long_form || metrics[0] != (wx = (int)metrics[0]) ||
+ metrics[1] != 0 || height == 0 ||
+ ((wx | width | height | (llx + 128) | (lly + 128)) & ~255) != 0
+ ) {
+ /* Use the long form. */
+ int i, n = (long_form ? 10 : 6);
+
+ str[0] = 0;
+ str[1] = long_form;
+ for (i = 0; i < n; ++i) {
+ int v = (int)metrics[i]; /* no floating point widths yet */
+
+ str[2 + 2 * i] = (byte)(v >> 8);
+ str[2 + 2 * i + 1] = (byte)v;
+ }
+ r_set_size(op, 2 + n * 2);
+ } else {
+ /* Use the short form. */
+ str[0] = (byte)width;
+ str[1] = (byte)height;
+ str[2] = (byte)wx;
+ str[3] = (byte)(llx + 128);
+ str[4] = (byte)(lly + 128);
+ r_set_size(op, 5);
+ }
+ return code;
+}
+
+/* <cid_min> <cid_max> <type32font> .removeglyphs - */
+typedef struct {
+ gs_glyph cid_min, cid_max;
+ gs_font *font;
+} font_cid_range_t;
+static bool
+select_cid_range(const gs_memory_t *mem, cached_char * cc, void *range_ptr)
+{
+ const font_cid_range_t *range = range_ptr;
+
+ return (cc->code >= range->cid_min &&
+ cc->code <= range->cid_max &&
+ cc->pair->font == range->font);
+}
+static int
+zremoveglyphs(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ font_cid_range_t range;
+
+ check_int_leu(op[-2], 65535);
+ check_int_leu(op[-1], 65535);
+ code = font_param(op, &range.font);
+ if (code < 0)
+ return code;
+ if (range.font->FontType != ft_CID_bitmap)
+ return_error(gs_error_invalidfont);
+ range.cid_min = gs_min_cid_glyph + op[-2].value.intval;
+ range.cid_max = gs_min_cid_glyph + op[-1].value.intval;
+ gx_purge_selected_cached_chars(range.font->dir, select_cid_range,
+ &range);
+ pop(3);
+ return 0;
+}
+
+/* <str5/14/22> .getmetrics32 <width> <height> <wx> ... <ury> 5/14 */
+/* <str5/14/22> .getmetrics32 <width> <height> <w0x> ... <vy> 22 */
+static int
+zgetmetrics32(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const byte *data;
+ uint size;
+ int i, n = 6;
+ os_ptr wop;
+
+ check_read_type(*op, t_string);
+ data = op->value.const_bytes;
+ size = r_size(op);
+ if (size < 5)
+ return_error(gs_error_rangecheck);
+ if (data[0]) {
+ /* Short form. */
+ int llx = (int)data[3] - 128, lly = (int)data[4] - 128;
+
+ n = 6;
+ size = 5;
+ push(8);
+ make_int(op - 6, data[2]); /* wx */
+ make_int(op - 5, 0); /* wy */
+ make_int(op - 4, llx);
+ make_int(op - 3, lly);
+ make_int(op - 2, llx + data[0]); /* urx */
+ make_int(op - 1, lly + data[1]); /* ury */
+ } else {
+ if (data[1]) {
+ /* Long form, both WModes. */
+ if (size < 22)
+ return_error(gs_error_rangecheck);
+ n = 10;
+ size = 22;
+ } else {
+ /* Long form, WMode = 0 only. */
+ if (size < 14)
+ return_error(gs_error_rangecheck);
+ n = 6;
+ size = 14;
+ }
+ push(2 + n);
+ for (i = 0; i < n; ++i)
+ make_int(op - n + i,
+ ((int)((data[2 * i + 2] << 8) + data[2 * i + 3]) ^ 0x8000)
+ - 0x8000);
+ }
+ wop = op - n;
+ make_int(wop - 2, wop[4].value.intval - wop[2].value.intval);
+ make_int(wop - 1, wop[5].value.intval - wop[3].value.intval);
+ make_int(op, size);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zchar32_op_defs[] =
+{
+ {"1.getmetrics32", zgetmetrics32},
+ {"4.makeglyph32", zmakeglyph32},
+ {"3.removeglyphs", zremoveglyphs},
+ op_def_end(0)
+};
diff --git a/psi/zchar42.c b/psi/zchar42.c
new file mode 100644
index 000000000..cca86b99c
--- /dev/null
+++ b/psi/zchar42.c
@@ -0,0 +1,297 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Type 42 character display operator */
+#include "ghost.h"
+#include "oper.h"
+#include "gsmatrix.h"
+#include "gspaint.h" /* for gs_fill, gs_stroke */
+#include "gspath.h"
+#include "gxfixed.h"
+#include "gxfont.h"
+#include "gxfont42.h"
+#include "gxistate.h"
+#include "gxpath.h"
+#include "gxtext.h"
+#include "gzstate.h" /* only for ->path */
+#include "dstack.h" /* only for systemdict */
+#include "estack.h"
+#include "ichar.h"
+#include "icharout.h"
+#include "ifont.h" /* for font_param */
+#include "igstate.h"
+#include "iname.h"
+#include "store.h"
+#include "string_.h"
+#include "zchar42.h"
+#include "idict.h"
+
+/* Get a Type 42 character metrics and set the cache device. */
+int
+zchar42_set_cache(i_ctx_t *i_ctx_p, gs_font_base *pbfont, ref *cnref,
+ uint glyph_index, op_proc_t cont, op_proc_t *exec_cont)
+{ double sbw[4];
+ double w[2];
+ int present;
+ gs_font_type42 *pfont42 = (gs_font_type42 *)pbfont;
+ int code = zchar_get_metrics(pbfont, cnref, sbw);
+ gs_rect bbox;
+ int vertical = gs_rootfont(igs)->WMode;
+ float sbw_bbox[8];
+ float sbw_bbox_h[8];
+ ref *fdict = (ref *)pbfont->client_data;
+ ref *rpath = NULL;
+ bool embedded = true;
+
+ if (code < 0)
+ return code;
+ present = code;
+
+ if (dict_find_string(fdict, "Path", &rpath) > 0) {
+ embedded = false;
+ }
+
+ if (vertical) { /* for vertically-oriented metrics */
+
+ /* Always call get_metrics because we'll need glyph bbox below in any case
+ as a workaround for Dynalab fonts. We can't recognize Dynalab here. */
+ code = pfont42->data.get_metrics(pfont42, glyph_index,
+ gs_type42_metrics_options_WMODE0_AND_BBOX, sbw_bbox_h);
+ if (code < 0)
+ return code;
+ code = pfont42->data.get_metrics(pfont42, glyph_index,
+ gs_type42_metrics_options_WMODE1_AND_BBOX, sbw_bbox);
+ /* Here code=0 means success, code<0 means no vertical metrics. */
+ /* We only want to create fake vertical metrics for TTF fonts
+ being used to emulate a vertical writing CIDFont. If we have
+ a CIDType 2 font, without vertical metrics, we're supposed to
+ treat it as a horizontal writing font, regardless of the wmode
+ setting
+ */
+ if (code < 0 && !embedded) {
+ /* No vertical metrics in the font,
+ hewristically compose vertical metrics from bounding boxes. */
+ sbw_bbox[0] = 0;
+ sbw_bbox[1] = pbfont->FontBBox.q.y - 1;
+ sbw_bbox[2] = 0;
+ sbw_bbox[3] = -1;
+ }
+ else {
+ vertical = false;
+ }
+ }
+ if (vertical) {
+ if (present != metricsSideBearingAndWidth) {
+ /* metricsNone or metricsWidthOnly. */
+ /* No top side bearing (in Metrics2) in Postscript font. */
+ /* Note that Postscript wants the 'V' vector in sbw[0:1],
+ and True Type supplies Top Side Bearing in sbw_bbox[0:1],
+ and Left Side Bearing in sbw_bbox_h[0:1].
+ So we need to compute V from FontBBox as we do for FontType 9
+ (see FontBBox_as_Metrics2) and add TSB to it. */
+# if 0 /* old code taken from empirics, keepping it for a while to compare results. */
+ sbw[0] = (sbw_bbox[6] + sbw_bbox[4]) / 2;
+ sbw[1] = (pbfont->FontBBox.q.y + pbfont->FontBBox.p.y - sbw_bbox[3]) / 2;
+# else
+ sbw[0] = sbw_bbox_h[2] / 2;
+ sbw[1] = sbw_bbox[1] - sbw_bbox[3];
+# endif
+ }
+ if (present == metricsNone) {
+ /* No adwance width (in Metrcis2) in Postscript font. */
+ sbw[2] = 0;
+ sbw[3] = sbw_bbox[3];
+ }
+ } else {
+ /* Always call get_metrics because we'll need glyph bbox below in any case
+ as a workaround for Dynalab fonts. We can't recognize Dynalab here. */
+ code = pfont42->data.get_metrics(pfont42, glyph_index,
+ gs_type42_metrics_options_WMODE0_AND_BBOX, sbw_bbox);
+ if (code < 0)
+ return code;
+ if (present != metricsSideBearingAndWidth) {
+ /* metricsNone or metricsWidthOnly. */
+ /* No left side bearing (in Metrics) in Postscript font. */
+ sbw[0] = sbw_bbox[0];
+ sbw[1] = sbw_bbox[1];
+ }
+ if (present == metricsNone) {
+ /* No advance width (in Metrics) in Postscript font. */
+ sbw[2] = sbw_bbox[2];
+ sbw[3] = sbw_bbox[3];
+ }
+ }
+ w[0] = sbw[2];
+ w[1] = sbw[3];
+ if (!vertical) {
+ sbw_bbox[6] = (sbw_bbox[6] - sbw_bbox[4]) + sbw_bbox[0];
+ sbw_bbox[4] = sbw_bbox[0];
+ }
+ /* Note: The glyph bbox usn't useful for Dynalab fonts,
+ which stretch subglyphs. Uniting with FontBBox helps.
+ In same time, FontBBox with no glyph bbox
+ doesn't work for 34_all.PS page 4. */
+ bbox.p.x = min(sbw_bbox[4], pbfont->FontBBox.p.y);
+ bbox.p.y = min(sbw_bbox[5], pbfont->FontBBox.p.y);
+ bbox.q.x = max(sbw_bbox[6], pbfont->FontBBox.q.x);
+ bbox.q.y = max(sbw_bbox[7], pbfont->FontBBox.q.y);
+ return zchar_set_cache(i_ctx_p, pbfont, cnref,
+ NULL,
+ w, &bbox,
+ cont, exec_cont,
+ vertical ? sbw : NULL);
+}
+
+/* <font> <code|name> <name> <glyph_index> .type42execchar - */
+static int type42_fill(i_ctx_t *);
+static int type42_stroke(i_ctx_t *);
+static int
+ztype42execchar(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_font *pfont;
+ int code = font_param(op - 3, &pfont);
+ gs_font_base *const pbfont = (gs_font_base *) pfont;
+ gs_font_type42 *const pfont42 = (gs_font_type42 *) pfont;
+ gs_text_enum_t *penum = op_show_find(i_ctx_p);
+ op_proc_t cont = (pbfont->PaintType == 0 ? type42_fill : type42_stroke), exec_cont = 0;
+ ref *cnref;
+ uint glyph_index;
+
+ if (code < 0)
+ return code;
+ if (penum == 0 ||
+ (pfont->FontType != ft_TrueType &&
+ pfont->FontType != ft_CID_TrueType)
+ )
+ return_error(gs_error_undefined);
+ /*
+ * Any reasonable implementation would execute something like
+ * 1 setmiterlimit 0 setlinejoin 0 setlinecap
+ * here, but apparently the Adobe implementations aren't reasonable.
+ *
+ * If this is a stroked font, set the stroke width.
+ */
+ if (pfont->PaintType)
+ gs_setlinewidth(igs, pfont->StrokeWidth);
+ check_estack(3); /* for continuations */
+ /*
+ * Execute the definition of the character.
+ */
+ if (r_is_proc(op))
+ return zchar_exec_char_proc(i_ctx_p);
+ /*
+ * The definition must be a Type 42 glyph index.
+ * Note that we do not require read access: this is deliberate.
+ */
+ check_type(*op, t_integer);
+ check_ostack(3); /* for lsb values */
+ /* Establish a current point. */
+ code = gs_moveto(igs, 0.0, 0.0);
+ if (code < 0)
+ return code;
+ cnref = op - 1;
+ glyph_index = (uint)op->value.intval;
+ if (pfont42->data.gsub_size) {
+ glyph_index = pfont42->data.substitute_glyph_index_vertical(pfont42, glyph_index,
+ gs_rootfont(igs)->WMode, penum->returned.current_glyph);
+ make_int(op, glyph_index);
+ }
+ code = zchar42_set_cache(i_ctx_p, pbfont, cnref, glyph_index, cont, &exec_cont);
+ if (code >= 0 && exec_cont != 0)
+ code = (*exec_cont)(i_ctx_p);
+ return code;
+}
+
+/* Continue after a CDevProc callout. */
+static int type42_finish(i_ctx_t *i_ctx_p,
+ int (*cont)(gs_state *));
+static int
+type42_fill(i_ctx_t *i_ctx_p)
+{
+ int code;
+ gs_fixed_point fa = i_ctx_p->pgs->fill_adjust;
+
+ i_ctx_p->pgs->fill_adjust.x = i_ctx_p->pgs->fill_adjust.y = -1;
+ code = type42_finish(i_ctx_p, gs_fill);
+ i_ctx_p->pgs->fill_adjust = fa; /* Not sure whether we need to restore it,
+ but this isn't harmful. */
+ return code;
+}
+static int
+type42_stroke(i_ctx_t *i_ctx_p)
+{
+ return type42_finish(i_ctx_p, gs_stroke);
+}
+/* <font> <code|name> <name> <glyph_index> %type42_{fill|stroke} - */
+static int
+type42_finish(i_ctx_t *i_ctx_p, int (*cont) (gs_state *))
+{
+ os_ptr op = osp;
+ gs_font *pfont;
+ gs_font_type42 *pfont42;
+ int code;
+ gs_text_enum_t *penum = op_show_find(i_ctx_p);
+ os_ptr opc = op;
+ uint glyph_index;
+
+ check_type(*opc, t_integer);
+ code = font_param(opc - 3, &pfont);
+ if (code < 0)
+ return code;
+ if (penum == 0 || (pfont->FontType != ft_TrueType &&
+ pfont->FontType != ft_CID_TrueType)
+ )
+ return_error(gs_error_undefined);
+ pfont42 = (gs_font_type42 *)pfont;
+
+ if (!i_ctx_p->RenderTTNotdef) {
+ if (r_has_type(opc - 1, t_name)) {
+ ref gref;
+
+ name_string_ref(imemory, opc - 1, &gref);
+ if ((gref.tas.rsize == 7 && strncmp((const char *)gref.value.const_bytes, ".notdef", 7) == 0) ||
+ (gref.tas.rsize > 9 && strncmp((const char *)gref.value.const_bytes, ".notdef~GS", 10) == 0)) {
+ pop(4);
+ return (*cont)(igs);
+ }
+ }
+ }
+ glyph_index = (uint)opc->value.intval;
+ if (pfont42->data.gsub_size)
+ glyph_index = pfont42->data.substitute_glyph_index_vertical(pfont42, glyph_index,
+ gs_rootfont(igs)->WMode, penum->returned.current_glyph);
+ /*
+ * We have to disregard penum->pis and penum->path, and render to
+ * the current gstate and path. This is a design bug that we will
+ * have to address someday!
+ */
+ code = gs_type42_append(glyph_index, igs,
+ igs->path, penum, pfont,
+ (penum->text.operation & TEXT_DO_ANY_CHARPATH) != 0);
+ if (code < 0)
+ return code;
+ pop(4);
+ return (*cont)(igs);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zchar42_op_defs[] =
+{
+ {"4.type42execchar", ztype42execchar},
+ op_def_end(0)
+};
diff --git a/psi/zchar42.h b/psi/zchar42.h
new file mode 100644
index 000000000..aa19cc4d5
--- /dev/null
+++ b/psi/zchar42.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+#ifndef zchar42_INCLUDED
+# define zchar42_INCLUDED
+
+/* Get a Type 42 character metrics and set the cache device. */
+int zchar42_set_cache(i_ctx_t *i_ctx_p, gs_font_base *pbfont, ref *cnref,
+ uint glyph_index, op_proc_t cont, op_proc_t *exec_cont);
+
+#endif /* zchar42_INCLUDED */
diff --git a/psi/zcharout.c b/psi/zcharout.c
new file mode 100644
index 000000000..548584d0f
--- /dev/null
+++ b/psi/zcharout.c
@@ -0,0 +1,423 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Common code for outline (Type 1 / 4 / 42) fonts */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gscrypt1.h"
+#include "gstext.h"
+#include "gxdevice.h" /* for gxfont.h */
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "dstack.h" /* only for systemdict */
+#include "estack.h"
+#include "ichar.h"
+#include "icharout.h"
+#include "idict.h"
+#include "ifont.h"
+#include "igstate.h"
+#include "iname.h"
+#include "store.h"
+
+/*
+ * Execute an outline defined by a PostScript procedure.
+ * The top elements of the stack are:
+ * <font> <code|name> <name> <outline_id>
+ */
+int
+zchar_exec_char_proc(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ /*
+ * The definition is a PostScript procedure. Execute
+ * <code|name> proc
+ * within a systemdict begin/end and a font begin/end.
+ */
+ es_ptr ep;
+
+ check_estack(5);
+ ep = esp += 5;
+ make_op_estack(ep - 4, zend);
+ make_op_estack(ep - 3, zend);
+ ref_assign(ep - 2, op);
+ make_op_estack(ep - 1, zbegin);
+ make_op_estack(ep, zbegin);
+ ref_assign(op - 1, systemdict);
+ {
+ ref rfont;
+
+ ref_assign(&rfont, op - 3);
+ ref_assign(op - 3, op - 2);
+ ref_assign(op - 2, &rfont);
+ }
+ pop(1);
+ return o_push_estack;
+}
+
+/*
+ * Get the metrics for a character from the Metrics dictionary of a base
+ * font. If present, store the l.s.b. in psbw[0,1] and the width in
+ * psbw[2,3].
+ */
+int /*metrics_present*/
+zchar_get_metrics(const gs_font_base * pbfont, const ref * pcnref,
+ double psbw[4])
+{
+ const ref *pfdict = &pfont_data(gs_font_parent(pbfont))->dict;
+ ref *pmdict;
+
+ if (dict_find_string(pfdict, "Metrics", &pmdict) > 0) {
+ ref *pmvalue;
+
+ check_type_only(*pmdict, t_dictionary);
+ check_dict_read(*pmdict);
+ if (dict_find(pmdict, pcnref, &pmvalue) > 0) {
+ if (num_params(pmvalue, 1, psbw + 2) >= 0) { /* <wx> only */
+ psbw[3] = 0;
+ return metricsWidthOnly;
+ } else {
+ int code;
+
+ check_read_type_only(*pmvalue, t_array);
+ switch (r_size(pmvalue)) {
+ case 2: /* [<sbx> <wx>] */
+ code = num_params(pmvalue->value.refs + 1,
+ 2, psbw);
+ psbw[2] = psbw[1];
+ psbw[1] = psbw[3] = 0;
+ break;
+ case 4: /* [<sbx> <sby> <wx> <wy>] */
+ code = num_params(pmvalue->value.refs + 3,
+ 4, psbw);
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ if (code < 0)
+ return code;
+ return metricsSideBearingAndWidth;
+ }
+ }
+ }
+ return metricsNone;
+}
+
+/* Get the vertical metrics for a character from Metrics2, if present. */
+int
+zchar_get_metrics2(const gs_font_base * pbfont, const ref * pcnref,
+ double pwv[4])
+{
+ const ref *pfdict = &pfont_data(gs_font_parent(pbfont))->dict;
+ ref *pmdict;
+
+ if (dict_find_string(pfdict, "Metrics2", &pmdict) > 0) {
+ ref *pmvalue;
+
+ check_type_only(*pmdict, t_dictionary);
+ check_dict_read(*pmdict);
+ if (dict_find(pmdict, pcnref, &pmvalue) > 0) {
+ check_read_type_only(*pmvalue, t_array);
+ if (r_size(pmvalue) == 4) {
+ int code = num_params(pmvalue->value.refs + 3, 4, pwv);
+
+ return (code < 0 ? code : metricsSideBearingAndWidth);
+ }
+ }
+ }
+ return metricsNone;
+}
+
+/*
+ * Get CDevProc.
+ */
+bool
+zchar_get_CDevProc(const gs_font_base * pbfont, ref **ppcdevproc)
+{
+ const ref *pfdict = &pfont_data(gs_font_parent(pbfont))->dict;
+
+ return dict_find_string(pfdict, "CDevProc", ppcdevproc) > 0;
+}
+
+/*
+ * Consult Metrics2 and CDevProc, and call setcachedevice[2]. Return
+ * o_push_estack if we had to call a CDevProc, or if we are skipping the
+ * rendering process (only getting the metrics).
+ * Returns exec_cont - a function, which must be called by caller after this function.
+ */
+int
+zchar_set_cache(i_ctx_t *i_ctx_p, const gs_font_base * pbfont,
+ const ref * pcnref, const double psb[2],
+ const double pwidth[2], const gs_rect * pbbox,
+ op_proc_t cont, op_proc_t *exec_cont,
+ const double Metrics2_sbw_default[4])
+{
+ os_ptr op = osp;
+ ref *pcdevproc, *valueref;
+ int have_cdevproc;
+ ref rpop;
+ ref cid, *cidptr;
+ bool metrics2;
+ bool metrics2_use_default = false;
+ double w2[10];
+ gs_text_enum_t *penum = op_show_find(i_ctx_p);
+
+ w2[0] = pwidth[0], w2[1] = pwidth[1];
+
+ /* Adjust the bounding box for stroking if needed. */
+
+ w2[2] = pbbox->p.x, w2[3] = pbbox->p.y;
+ w2[4] = pbbox->q.x, w2[5] = pbbox->q.y;
+ if (pbfont->PaintType != 0) {
+ double expand = max(1.415, gs_currentmiterlimit(igs)) *
+ gs_currentlinewidth(igs) / 2;
+
+ w2[2] -= expand, w2[3] -= expand;
+ w2[4] += expand, w2[5] += expand;
+ }
+
+ /* Check for Metrics2. */
+
+ {
+ int code = zchar_get_metrics2(pbfont, pcnref, w2 + 6);
+
+ if (code < 0)
+ return code;
+ metrics2 = code > 0;
+ }
+
+ /*
+ * For FontType 9 and 11, if Metrics2 is missing, the caller provides
+ * default Metrics2 values derived from the FontBBox.
+ */
+ if (!metrics2 && Metrics2_sbw_default != NULL) {
+ w2[6] = Metrics2_sbw_default[2];
+ w2[7] = Metrics2_sbw_default[3];
+ w2[8] = Metrics2_sbw_default[0];
+ w2[9] = Metrics2_sbw_default[1];
+ metrics2 = true;
+ metrics2_use_default = true;
+ }
+
+ /* Check for CDevProc or "short-circuiting". */
+
+ have_cdevproc = zchar_get_CDevProc(pbfont, &pcdevproc);
+
+ /* Obscure test. The CDevProc is supposed to be called with the original CID but what we get passed
+ * here is the TT GID. So the CDevProc won't do the right thing. We need to extract the CID from the
+ * enumerator, and use that instead.
+ */
+ cidptr = (ref *)pcnref;
+ if (pbfont->FontType == ft_CID_TrueType && dict_find_string(&pfont_data(gs_font_parent(pbfont))->dict, "File", &valueref) > 0) {
+ if (pbfont->key_name.size != pbfont->font_name.size ||
+ strncmp((const char *)pbfont->key_name.chars, (const char *)pbfont->font_name.chars, pbfont->key_name.size)) {
+
+ if (penum->returned.current_glyph >= GS_MIN_CID_GLYPH) {
+ make_int(&cid, penum->returned.current_glyph - GS_MIN_CID_GLYPH);
+ }
+ else {
+ make_int(&cid, penum->returned.current_glyph);
+ }
+ cidptr = &cid;
+ }
+ }
+ if (have_cdevproc || zchar_show_width_only(penum)) {
+ int i;
+ op_proc_t zsetc;
+ int nparams;
+
+ if (have_cdevproc) {
+ check_proc_only(*pcdevproc);
+ zsetc = zsetcachedevice2;
+
+ /* If we have cdevproc and the font type is CID type 0,
+ we'll throw away Metrics2_sbw_default that is calculated
+ from FontBBox. */
+ if (!metrics2
+ || (penum->current_font->FontType == ft_CID_encrypted
+ && metrics2_use_default)) {
+ w2[6] = w2[0], w2[7] = w2[1];
+ w2[8] = w2[9] = 0;
+ }
+ nparams = 10;
+ } else {
+ make_oper(&rpop, 0, zpop);
+ pcdevproc = &rpop;
+ if (metrics2)
+ zsetc = zsetcachedevice2, nparams = 10;
+ else
+ zsetc = zsetcachedevice, nparams = 6;
+ }
+ check_estack(3);
+ /* Push the l.s.b. for .type1addpath if necessary. */
+ if (psb != 0) {
+ push(nparams + 3);
+ make_real(op - (nparams + 2), psb[0]);
+ make_real(op - (nparams + 1), psb[1]);
+ } else {
+ push(nparams + 1);
+ }
+ for (i = 0; i < nparams; ++i)
+ make_real(op - nparams + i, w2[i]);
+ ref_assign(op, cidptr);
+ push_op_estack(cont);
+ push_op_estack(zsetc);
+ ++esp;
+ ref_assign(esp, pcdevproc);
+ return o_push_estack;
+ } {
+ int code =
+ (metrics2 ? gs_text_setcachedevice2(penum, w2) :
+ gs_text_setcachedevice(penum, w2));
+
+ if (code < 0)
+ return code;
+ }
+
+ /* No metrics modification, do the stroke or fill now. */
+
+ /* Push the l.s.b. for .type1addpath if necessary. */
+ if (psb != 0) {
+ push(2);
+ make_real(op - 1, psb[0]);
+ make_real(op, psb[1]);
+ }
+ *exec_cont = cont;
+ return 0;
+}
+
+/*
+ * Get the CharString data corresponding to a glyph. Return typecheck
+ * if it isn't a string.
+ */
+static bool charstring_is_notdef_proc(const gs_memory_t *mem, const ref *);
+static int charstring_make_notdef(gs_glyph_data_t *, gs_font *);
+int
+zchar_charstring_data(gs_font *font, const ref *pgref, gs_glyph_data_t *pgd)
+{
+ ref *pcstr;
+
+ if (dict_find(&pfont_data(font)->CharStrings, pgref, &pcstr) <= 0)
+ return_error(gs_error_undefined);
+ if (!r_has_type(pcstr, t_string)) {
+ /*
+ * The ADOBEPS4 Windows driver replaces the .notdef entry of
+ * otherwise normal Type 1 fonts with the procedure
+ * {pop 0 0 setcharwidth}
+ * To prevent this from making the font unembeddable in PDF files
+ * (with our present font-writing code), we recognize this as a
+ * special case and return a Type 1 CharString consisting of
+ * 0 0 hsbw endchar
+ */
+ if (font->FontType == ft_encrypted &&
+ charstring_is_notdef_proc(font->memory, pcstr)
+ )
+ return charstring_make_notdef(pgd, font);
+ else
+ return_error(gs_error_typecheck);
+ }
+ gs_glyph_data_from_string(pgd, pcstr->value.const_bytes, r_size(pcstr),
+ NULL);
+ return 0;
+}
+static bool
+charstring_is_notdef_proc(const gs_memory_t *mem, const ref *pcstr)
+{
+ if (r_is_array(pcstr) && r_size(pcstr) == 4) {
+ ref elts[4];
+ long i;
+
+ for (i = 0; i < 4; ++i)
+ array_get(mem, pcstr, i, &elts[i]);
+ if (r_has_type(&elts[0], t_name) &&
+ r_has_type(&elts[1], t_integer) && elts[1].value.intval == 0 &&
+ r_has_type(&elts[2], t_integer) && elts[2].value.intval == 0 &&
+ r_has_type(&elts[3], t_name)
+ ) {
+ ref nref;
+
+ name_enter_string(mem, "pop", &nref);
+ if (name_eq(&elts[0], &nref)) {
+ name_enter_string(mem, "setcharwidth", &nref);
+ if (name_eq(&elts[3], &nref))
+ return true;
+ }
+ }
+ }
+ return false;
+}
+static int
+charstring_make_notdef(gs_glyph_data_t *pgd, gs_font *font)
+{
+ gs_font_type1 *const pfont = (gs_font_type1 *)font;
+ static const byte char_data[4] = {
+ 139, /* 0 */
+ 139, /* 0 */
+ c1_hsbw,
+ cx_endchar
+ };
+ uint len = max(pfont->data.lenIV, 0) + sizeof(char_data);
+ byte *chars = gs_alloc_string(font->memory, len, "charstring_make_notdef");
+
+ if (chars == 0)
+ return_error(gs_error_VMerror);
+ gs_glyph_data_from_string(pgd, chars, len, font);
+ if (pfont->data.lenIV < 0)
+ memcpy(chars, char_data, sizeof(char_data));
+ else {
+ crypt_state state = crypt_charstring_seed;
+
+ memcpy(chars + pfont->data.lenIV, char_data, sizeof(char_data));
+ gs_type1_encrypt(chars, chars, len, &state);
+ }
+ return 0;
+}
+
+/*
+ * Enumerate the next glyph from a directory. This is essentially a
+ * wrapper around dict_first/dict_next to implement the enumerate_glyph
+ * font procedure.
+ *
+ * Note that *prdict will be null if the font is a subfont of a
+ * CIDFontType 0 CIDFont.
+ */
+int
+zchar_enumerate_glyph(const gs_memory_t *mem, const ref *prdict, int *pindex, gs_glyph *pglyph)
+{
+ int index = *pindex - 1;
+ ref elt[2];
+
+ if (!r_has_type(prdict, t_dictionary))
+ return 0; /* *pindex was 0, is still 0 */
+ if (index < 0)
+ index = dict_first(prdict);
+next:
+ index = dict_next(prdict, index, elt);
+ *pindex = index + 1;
+ if (index >= 0) {
+ switch (r_type(elt)) {
+ case t_integer:
+ *pglyph = gs_min_cid_glyph + elt[0].value.intval;
+ break;
+ case t_name:
+ *pglyph = name_index(mem, elt);
+ break;
+ default: /* can't handle it */
+ goto next;
+ }
+ }
+ return 0;
+}
diff --git a/psi/zcharx.c b/psi/zcharx.c
new file mode 100644
index 000000000..7e2fc3172
--- /dev/null
+++ b/psi/zcharx.c
@@ -0,0 +1,189 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 2 character operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gsmatrix.h" /* for gxfont.h */
+#include "gstext.h"
+#include "gxfixed.h" /* for gxfont.h */
+#include "gxfont.h"
+#include "gxtext.h"
+#include "ialloc.h"
+#include "ichar.h"
+#include "igstate.h"
+#include "iname.h"
+#include "ibnum.h"
+#include "memory_.h"
+
+/* Common setup for glyphshow and .glyphwidth. */
+static int
+glyph_show_setup(i_ctx_t *i_ctx_p, gs_glyph *pglyph)
+{
+ os_ptr op = osp;
+
+ switch (gs_currentfont(igs)->FontType) {
+ case ft_CID_encrypted:
+ case ft_CID_user_defined:
+ case ft_CID_TrueType:
+ case ft_CID_bitmap:
+ check_int_leu(*op, gs_max_glyph - gs_min_cid_glyph);
+ *pglyph = (gs_glyph) op->value.intval + gs_min_cid_glyph;
+ break;
+ default:
+ check_type(*op, t_name);
+ *pglyph = name_index(imemory, op);
+ }
+ return op_show_enum_setup(i_ctx_p);
+}
+
+/* <charname> glyphshow - */
+static int
+zglyphshow(i_ctx_t *i_ctx_p)
+{
+ gs_glyph glyph;
+ gs_text_enum_t *penum;
+ int code;
+
+ if ((code = glyph_show_setup(i_ctx_p, &glyph)) != 0 ||
+ (code = gs_glyphshow_begin(igs, glyph, imemory_local, &penum)) < 0)
+ return code;
+ *(op_proc_t *)&penum->enum_client_data = zglyphshow;
+ if ((code = op_show_finish_setup(i_ctx_p, penum, 1, NULL)) < 0) {
+ ifree_object(penum, "zglyphshow");
+ return code;
+ }
+ return op_show_continue_pop(i_ctx_p, 1);
+}
+
+/* <charname> .glyphwidth <wx> <wy> */
+static int
+zglyphwidth(i_ctx_t *i_ctx_p)
+{
+ gs_glyph glyph;
+ gs_text_enum_t *penum;
+ int code;
+
+ if ((code = glyph_show_setup(i_ctx_p, &glyph)) != 0 ||
+ (code = gs_glyphwidth_begin(igs, glyph, imemory, &penum)) < 0)
+ return code;
+ if ((code = op_show_finish_setup(i_ctx_p, penum, 1, finish_stringwidth)) < 0) {
+ ifree_object(penum, "zglyphwidth");
+ return code;
+ }
+ return op_show_continue_pop(i_ctx_p, 1);
+}
+
+/* <string> <numarray|numstring> xshow - */
+/* <string> <numarray|numstring> yshow - */
+/* <string> <numarray|numstring> xyshow - */
+static int
+moveshow(i_ctx_t *i_ctx_p, bool have_x, bool have_y)
+{
+ os_ptr op = osp;
+ gs_text_enum_t *penum = NULL;
+ int code = op_show_setup(i_ctx_p, op - 1);
+ int format;
+ uint i, size, widths_needed;
+ float *values;
+ bool CPSI_mode = gs_currentcpsimode(imemory);
+
+ if (code != 0)
+ return code;
+ format = num_array_format(op);
+ if (format < 0)
+ return format;
+ size = num_array_size(op, format);
+ values = (float *)ialloc_byte_array(size, sizeof(float), "moveshow");
+ if (values == 0)
+ return_error(gs_error_VMerror);
+ if (CPSI_mode)
+ memset(values, 0, size * sizeof(values[0])); /* Safety. */
+ if ((code = gs_xyshow_begin(igs, op[-1].value.bytes, r_size(op - 1),
+ (have_x ? values : (float *)0),
+ (have_y ? values : (float *)0),
+ size, imemory_local, &penum)) < 0) {
+ ifree_object(values, "moveshow");
+ if (penum) /* if there was an error, the text_enum may not have been allocated */
+ penum->text.x_widths = penum->text.y_widths = NULL;
+ return code;
+ }
+ if (CPSI_mode) {
+ /* CET 13-29.PS page 2 defines a longer width array
+ then the text requires, and CPSI silently ignores extra elements.
+ So we need to compute exact number of characters
+ to know how many elements to load and type check. */
+ code = gs_text_count_chars(igs, gs_get_text_params(penum), imemory);
+ if (code < 0)
+ return code;
+ widths_needed = code;
+ if (have_x && have_y)
+ widths_needed <<= 1;
+ } else
+ widths_needed = size;
+ for (i = 0; i < widths_needed; ++i) {
+ ref value;
+
+ switch (code = num_array_get(imemory, op, format, i, &value)) {
+ case t_integer:
+ values[i] = (float)value.value.intval; break;
+ case t_real:
+ values[i] = value.value.realval; break;
+ case t_null:
+ code = gs_note_error(gs_error_rangecheck);
+ /* falls through */
+ default:
+ ifree_object(values, "moveshow");
+ penum->text.x_widths = penum->text.y_widths = NULL;
+ return code;
+ }
+ }
+ if ((code = op_show_finish_setup(i_ctx_p, penum, 2, NULL)) < 0) {
+ ifree_object(values, "moveshow");
+ penum->text.x_widths = penum->text.y_widths = NULL;
+ return code;
+ }
+ pop(2);
+ return op_show_continue(i_ctx_p);
+}
+static int
+zxshow(i_ctx_t *i_ctx_p)
+{
+ return moveshow(i_ctx_p, true, false);
+}
+static int
+zyshow(i_ctx_t *i_ctx_p)
+{
+ return moveshow(i_ctx_p, false, true);
+}
+static int
+zxyshow(i_ctx_t *i_ctx_p)
+{
+ return moveshow(i_ctx_p, true, true);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcharx_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"1glyphshow", zglyphshow},
+ {"1.glyphwidth", zglyphwidth},
+ {"2xshow", zxshow},
+ {"2xyshow", zxyshow},
+ {"2yshow", zyshow},
+ op_def_end(0)
+};
diff --git a/psi/zcid.c b/psi/zcid.c
new file mode 100644
index 000000000..00c0c8f27
--- /dev/null
+++ b/psi/zcid.c
@@ -0,0 +1,245 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* CMap and CID-keyed font services */
+#include "ghost.h"
+#include "ierrors.h"
+#include "gxcid.h"
+#include "icid.h" /* for checking prototype */
+#include "idict.h"
+#include "idparam.h"
+#include "store.h"
+#include "oper.h"
+#include "gserrors.h"
+
+/* Get the information from a CIDSystemInfo dictionary. */
+int
+cid_system_info_param(gs_cid_system_info_t *pcidsi, const ref *prcidsi)
+{
+ ref *pregistry;
+ ref *pordering;
+ int code;
+
+ if (!r_has_type(prcidsi, t_dictionary))
+ return_error(gs_error_typecheck);
+ if (dict_find_string(prcidsi, "Registry", &pregistry) <= 0 ||
+ dict_find_string(prcidsi, "Ordering", &pordering) <= 0
+ )
+ return_error(gs_error_rangecheck);
+ check_read_type_only(*pregistry, t_string);
+ check_read_type_only(*pordering, t_string);
+ pcidsi->Registry.data = pregistry->value.const_bytes;
+ pcidsi->Registry.size = r_size(pregistry);
+ pcidsi->Ordering.data = pordering->value.const_bytes;
+ pcidsi->Ordering.size = r_size(pordering);
+ code = dict_int_param(prcidsi, "Supplement", 0, max_int, -1,
+ &pcidsi->Supplement);
+ return (code < 0 ? code : 0);
+}
+
+/* Convert a CID into TT char code or to TT glyph index. */
+static bool
+TT_char_code_from_CID_no_subst(const gs_memory_t *mem,
+ const ref *Decoding, const ref *TT_cmap, uint nCID, uint *c)
+{ ref *DecodingArray, char_code, char_code1, ih, *glyph_index;
+ bool found = false;
+ int i = nCID % 256, n;
+
+ make_int(&ih, nCID / 256);
+ if (dict_find(Decoding, &ih, &DecodingArray) <= 0 ||
+ !r_has_type(DecodingArray, t_array) ||
+ array_get(mem, DecodingArray, i, &char_code) < 0)
+ return false;
+ if (r_has_type(&char_code, t_integer))
+ n = 1;
+ else if (r_has_type(&char_code, t_array)) {
+ DecodingArray = &char_code;
+ i = 0;
+ n = r_size(DecodingArray);
+ } else
+ return false; /* Must not happen. */
+ for (;n--; i++) {
+ if (array_get(mem, DecodingArray, i, &char_code1) < 0 ||
+ !r_has_type(&char_code1, t_integer))
+ return false; /* Must not happen. */
+ if (dict_find(TT_cmap, &char_code1, &glyph_index) >= 0 &&
+ r_has_type(glyph_index, t_integer)) {
+ *c = glyph_index->value.intval;
+ found = true;
+ if (*c != 0)
+ return true;
+ }
+ }
+ return found;
+}
+
+/* Convert a CID into a TT char code or into a TT glyph index, using SubstNWP. */
+/* Returns 1 if a glyph presents, 0 if not, <0 if error. */
+int
+cid_to_TT_charcode(const gs_memory_t *mem,
+ const ref *Decoding, const ref *TT_cmap, const ref *SubstNWP,
+ uint nCID, uint *c, ref *src_type, ref *dst_type)
+{
+ int SubstNWP_length = r_size(SubstNWP), i, code;
+
+ if (TT_char_code_from_CID_no_subst(mem, Decoding, TT_cmap, nCID, c)) {
+ make_null(src_type);
+ /* Leaving dst_type uninitialized. */
+ return 1;
+ }
+ for (i = 0; i < SubstNWP_length; i += 5) {
+ ref rb, re, rs;
+ int nb, ne, ns;
+
+ if ((code = array_get(mem, SubstNWP, i + 1, &rb)) < 0)
+ return code;
+ if ((code = array_get(mem, SubstNWP, i + 2, &re)) < 0)
+ return code;
+ if ((code = array_get(mem, SubstNWP, i + 3, &rs)) < 0)
+ return code;
+ nb = rb.value.intval;
+ ne = re.value.intval;
+ ns = rs.value.intval;
+ if (nCID >= nb && nCID <= ne)
+ if (TT_char_code_from_CID_no_subst(mem, Decoding, TT_cmap, ns + (nCID - nb), c)) {
+ if ((code = array_get(mem, SubstNWP, i + 0, src_type)) < 0)
+ return code;
+ if ((code = array_get(mem, SubstNWP, i + 4, dst_type)) < 0)
+ return code;
+ return 1;
+ }
+ if (nCID >= ns && nCID <= ns + (ne - nb))
+ if (TT_char_code_from_CID_no_subst(mem, Decoding, TT_cmap, nb + (nCID - ns), c)) {
+ if ((code = array_get(mem, SubstNWP, i + 0, dst_type)) < 0)
+ return code;
+ if ((code = array_get(mem, SubstNWP, i + 4, src_type)) < 0)
+ return code;
+ return 1;
+ }
+ }
+ *c = 0;
+ return 0;
+}
+
+/* Set a CIDMap element. */
+static int
+set_CIDMap_element(const gs_memory_t *mem, ref *CIDMap, uint cid, uint glyph_index)
+{ /* Assuming the CIDMap is already type-checked. */
+ /* Assuming GDBytes == 2. */
+ int offset = cid * 2;
+ int count = r_size(CIDMap), size, i;
+ ref s;
+ uchar *c;
+
+ if (glyph_index >= 65536)
+ return_error(gs_error_rangecheck); /* Can't store with GDBytes == 2. */
+ for (i = 0; i < count; i++) {
+ array_get(mem, CIDMap, i, &s);
+ size = r_size(&s) & ~1;
+ if (offset < size) {
+ c = s.value.bytes + offset;
+ c[0] = (uchar)(glyph_index >> 8);
+ c[1] = (uchar)(glyph_index & 255);
+ break;
+ }
+ offset -= size;
+ }
+ /* We ignore the substitution if it goes out the CIDMap range.
+ It must not happen, except for empty Decoding elements */
+ return 0;
+}
+
+/* Create a CIDMap from a True Type cmap array, Decoding and SubstNWP. */
+int
+cid_fill_CIDMap(const gs_memory_t *mem,
+ const ref *Decoding, const ref *TT_cmap, const ref *SubstNWP, int GDBytes,
+ ref *CIDMap)
+{ int dict_enum;
+ ref el[2];
+ int count, i;
+
+ if (GDBytes != 2)
+ return_error(gs_error_unregistered); /* Unimplemented. */
+ if (r_type(CIDMap) != t_array)
+ return_error(gs_error_unregistered); /* Unimplemented. It could be a single string. */
+ count = r_size(CIDMap);
+ /* Checking the CIDMap structure correctness : */
+ for (i = 0; i < count; i++) {
+ ref s;
+ int code = array_get(mem, CIDMap, i, &s);
+
+ if (code < 0)
+ return code;
+ check_type(s, t_string); /* fixme : optimize with moving to TT_char_code_from_CID. */
+ }
+ /* Compute the CIDMap : */
+ dict_enum = dict_first(Decoding);
+ for (;;) {
+ int index, count, i;
+
+ if ((dict_enum = dict_next(Decoding, dict_enum, el)) == -1)
+ break;
+ if (!r_has_type(&el[0], t_integer))
+ continue;
+ if (!r_has_type(&el[1], t_array))
+ return_error(gs_error_typecheck);
+ index = el[0].value.intval;
+ count = r_size(&el[1]);
+ for (i = 0; i < count; i++) {
+ uint cid = index * 256 + i, glyph_index;
+ ref src_type, dst_type;
+ int code = cid_to_TT_charcode(mem, Decoding, TT_cmap, SubstNWP,
+ cid, &glyph_index, &src_type, &dst_type);
+
+ if (code < 0)
+ return code;
+ if (code > 0) {
+ code = set_CIDMap_element(mem, CIDMap, cid, glyph_index);
+ if (code < 0)
+ return code;
+ }
+ }
+ }
+ return 0;
+}
+
+int
+cid_fill_Identity_CIDMap(const gs_memory_t *mem,
+ ref *CIDMap)
+{ int count, i;
+
+ count = r_size(CIDMap);
+ if (count != 3)
+ return_error(gs_error_rangecheck);
+
+ /* Checking the CIDMap structure correctness : */
+ for (i = 0; i < count; i++) {
+ ref s;
+ int code = array_get(mem, CIDMap, i, &s);
+
+ if (code < 0)
+ return code;
+ check_type(s, t_string); /* fixme : optimize with moving to TT_char_code_from_CID. */
+ }
+ for (i=0; i < 255*255; i++) {
+ int code;
+
+ code = set_CIDMap_element(mem, CIDMap, i, i);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
diff --git a/psi/zcidtest.c b/psi/zcidtest.c
new file mode 100644
index 000000000..c69d36e22
--- /dev/null
+++ b/psi/zcidtest.c
@@ -0,0 +1,144 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Operators for testing CIDFont and CMap facilities */
+#include "string_.h"
+#include "ghost.h"
+#include "gxfont.h"
+#include "gxfont0c.h"
+#include "gdevpsf.h"
+#include "stream.h"
+#include "spprint.h"
+#include "oper.h"
+#include "files.h"
+#include "idict.h"
+#include "ifont.h"
+#include "igstate.h"
+#include "iname.h"
+#include "store.h"
+
+/* - .wrapfont - */
+static int
+zwrapfont(i_ctx_t *i_ctx_p)
+{
+ gs_font *font = gs_currentfont(igs);
+ gs_font_type0 *font0;
+ int wmode = 0;
+ int code;
+
+ switch (font->FontType) {
+ case ft_TrueType:
+ code = gs_font_type0_from_type42(&font0, (gs_font_type42 *)font, wmode,
+ true, font->memory);
+ if (code < 0)
+ return code;
+ /*
+ * Patch up BuildChar and CIDMap. This isn't necessary for
+ * TrueType fonts in general, only for Type 42 fonts whose
+ * BuildChar is implemented in PostScript code.
+ */
+ {
+ font_data *pdata = pfont_data(font);
+ const char *bgstr = "%Type11BuildGlyph";
+ ref temp;
+
+ make_int(&temp, 0);
+ ref_assign(&pdata->u.type42.CIDMap, &temp);
+ code = name_ref((const byte *)bgstr, strlen(bgstr), &temp, 1);
+ if (code < 0)
+ return code;
+ r_set_attrs(&temp, a_executable);
+ ref_assign(&pdata->BuildGlyph, &temp);
+ }
+ break;
+ case ft_CID_encrypted:
+ case ft_CID_user_defined:
+ case ft_CID_TrueType:
+ code = gs_font_type0_from_cidfont(&font0, font, wmode, NULL,
+ font->memory);
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ if (code < 0)
+ return code;
+ gs_setfont(igs, (gs_font *)font0);
+ return 0;
+}
+
+/* <file> <cmap> .writecmap - */
+static int
+zfcmap_put_name_default(stream *s, const byte *str, uint size)
+{
+ stream_putc(s, '/');
+ stream_write(s, str, size);
+ return 0;
+}
+static int
+zwritecmap(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *pcodemap;
+ gs_cmap_t *pcmap;
+ int code;
+ stream *s;
+
+ check_type(*op, t_dictionary);
+ if (dict_find_string(op, "CodeMap", &pcodemap) <= 0 ||
+ !r_is_struct(pcodemap)
+ )
+ return_error(gs_error_typecheck);
+ check_write_file(s, op - 1);
+ pcmap = r_ptr(pcodemap, gs_cmap_t);
+ code = psf_write_cmap(s, pcmap, zfcmap_put_name_default, NULL, -1);
+ if (code >= 0)
+ pop(2);
+ return code;
+}
+
+/* <file> <cid9font> .writefont9 - */
+static int
+zwritefont9(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_font *pfont;
+ gs_font_cid0 *pfcid;
+ int code = font_param(op, &pfont);
+ stream *s;
+
+ if (code < 0)
+ return code;
+ if (pfont->FontType != ft_CID_encrypted)
+ return_error(gs_error_invalidfont);
+ check_write_file(s, op - 1);
+ pfcid = (gs_font_cid0 *)pfont;
+ code = psf_write_cid0_font(s, pfcid,
+ WRITE_TYPE2_NO_LENIV | WRITE_TYPE2_CHARSTRINGS,
+ NULL, 0, NULL);
+ if (code >= 0)
+ pop(2);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcidtest_op_defs[] =
+{
+ {"1.wrapfont", zwrapfont},
+ {"2.writecmap", zwritecmap},
+ {"2.writefont9", zwritefont9},
+ op_def_end(0)
+};
diff --git a/psi/zcie.c b/psi/zcie.c
new file mode 100644
index 000000000..5085ede3a
--- /dev/null
+++ b/psi/zcie.c
@@ -0,0 +1,963 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* CIE color operators */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gxcspace.h" /* gscolor2.h requires gscspace.h */
+#include "gscolor2.h"
+#include "gscie.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "icie.h"
+#include "isave.h"
+#include "ivmspace.h"
+#include "store.h" /* for make_null */
+#include "zcie.h"
+#include "gsicc_create.h"
+#include "gsicc_manage.h"
+#include "gsicc_profilecache.h"
+
+/* Prototype */
+int cieicc_prepare_caches(i_ctx_t *i_ctx_p, const gs_range * domains,
+ const ref * procs,
+ cie_cache_floats * pc0, cie_cache_floats * pc1,
+ cie_cache_floats * pc2, cie_cache_floats * pc3,
+ void *container,
+ const gs_ref_memory_t * imem, client_name_t cname);
+static int
+cie_prepare_iccproc(i_ctx_t *i_ctx_p, const gs_range * domain, const ref * proc,
+ cie_cache_floats * pcache, void *container,
+ const gs_ref_memory_t * imem, client_name_t cname);
+
+/* Empty procedures */
+static const ref empty_procs[4] =
+{
+ empty_ref_data(t_array, a_readonly | a_executable),
+ empty_ref_data(t_array, a_readonly | a_executable),
+ empty_ref_data(t_array, a_readonly | a_executable),
+ empty_ref_data(t_array, a_readonly | a_executable)
+};
+
+/* ------ Parameter extraction utilities ------ */
+
+/* Get a range array parameter from a dictionary. */
+/* We know that count <= 4. */
+int
+dict_ranges_param(const gs_memory_t *mem,
+ const ref * pdref, const char *kstr, int count,
+ gs_range * prange)
+{
+ int code = dict_floats_param(mem, pdref, kstr, count * 2,
+ (float *)prange, NULL);
+
+ if (code < 0)
+ return code;
+ else if (code == 0)
+ memcpy(prange, Range4_default.ranges, count * sizeof(gs_range));
+ return 0;
+}
+
+/* Get an array of procedures from a dictionary. */
+/* We know count <= countof(empty_procs). */
+int
+dict_proc_array_param(const gs_memory_t *mem,
+ const ref *pdict, const char *kstr,
+ uint count, ref *pparray)
+{
+ ref *pvalue;
+
+ if (dict_find_string(pdict, kstr, &pvalue) > 0) {
+ uint i;
+
+ check_array_only(*pvalue);
+ if (r_size(pvalue) != count)
+ return_error(gs_error_rangecheck);
+ for (i = 0; i < count; i++) {
+ ref proc;
+
+ array_get(mem, pvalue, (long)i, &proc);
+ check_proc_only(proc);
+ }
+ *pparray = *pvalue;
+ return 0;
+ } else {
+ make_const_array(pparray, a_readonly | avm_foreign,
+ count, &empty_procs[0]);
+ return 1;
+ }
+}
+
+/* Get 3 ranges from a dictionary. */
+int
+dict_range3_param(const gs_memory_t *mem,
+ const ref *pdref, const char *kstr,
+ gs_range3 *prange3)
+{
+ return dict_ranges_param(mem, pdref, kstr, 3, prange3->ranges);
+}
+
+/* Get a 3x3 matrix from a dictionary. */
+int
+dict_matrix3_param(const gs_memory_t *mem,
+ const ref *pdref, const char *kstr, gs_matrix3 *pmat3)
+{
+ /*
+ * We can't simply call dict_float_array_param with the matrix
+ * cast to a 9-element float array, because compilers may insert
+ * padding elements after each of the vectors. However, we can be
+ * confident that there is no padding within a single vector.
+ */
+ float values[9], defaults[9];
+ int code;
+
+ memcpy(&defaults[0], &Matrix3_default.cu, 3 * sizeof(float));
+ memcpy(&defaults[3], &Matrix3_default.cv, 3 * sizeof(float));
+ memcpy(&defaults[6], &Matrix3_default.cw, 3 * sizeof(float));
+ code = dict_floats_param(mem, pdref, kstr, 9, values, defaults);
+ if (code < 0)
+ return code;
+ memcpy(&pmat3->cu, &values[0], 3 * sizeof(float));
+ memcpy(&pmat3->cv, &values[3], 3 * sizeof(float));
+ memcpy(&pmat3->cw, &values[6], 3 * sizeof(float));
+ return 0;
+}
+
+/* Get 3 procedures from a dictionary. */
+int
+dict_proc3_param(const gs_memory_t *mem, const ref *pdref, const char *kstr, ref proc3[3])
+{
+ return dict_proc_array_param(mem, pdref, kstr, 3, proc3);
+}
+
+/* Get WhitePoint and BlackPoint values. */
+int
+cie_points_param(const gs_memory_t *mem,
+ const ref * pdref, gs_cie_wb * pwb)
+{
+ int code;
+
+ if ((code = dict_floats_param(mem, pdref, "WhitePoint", 3,
+ (float *)&pwb->WhitePoint, NULL)) < 0 ||
+ (code = dict_floats_param(mem, pdref, "BlackPoint", 3,
+ (float *)&pwb->BlackPoint, (const float *)&BlackPoint_default)) < 0
+ )
+ return code;
+ if (pwb->WhitePoint.u <= 0 ||
+ pwb->WhitePoint.v != 1 ||
+ pwb->WhitePoint.w <= 0 ||
+ pwb->BlackPoint.u < 0 ||
+ pwb->BlackPoint.v < 0 ||
+ pwb->BlackPoint.w < 0
+ )
+ return_error(gs_error_rangecheck);
+ return 0;
+}
+
+/* Process a 3- or 4-dimensional lookup table from a dictionary. */
+/* The caller has set pclt->n and pclt->m. */
+/* ptref is known to be a readable array of size at least n+1. */
+static int cie_3d_table_param(const ref * ptable, uint count, uint nbytes,
+ gs_const_string * strings);
+int
+cie_table_param(const ref * ptref, gx_color_lookup_table * pclt,
+ const gs_memory_t * mem)
+{
+ int n = pclt->n, m = pclt->m;
+ const ref *pta = ptref->value.const_refs;
+ int i;
+ uint nbytes;
+ int code;
+ gs_const_string *table;
+
+ for (i = 0; i < n; ++i) {
+ check_type_only(pta[i], t_integer);
+ if (pta[i].value.intval <= 1 || pta[i].value.intval > max_ushort)
+ return_error(gs_error_rangecheck);
+ pclt->dims[i] = (int)pta[i].value.intval;
+ }
+ nbytes = m * pclt->dims[n - 2] * pclt->dims[n - 1];
+ if (n == 3) {
+ table =
+ gs_alloc_struct_array(mem->stable_memory, pclt->dims[0], gs_const_string,
+ &st_const_string_element, "cie_table_param");
+ if (table == 0)
+ return_error(gs_error_VMerror);
+ code = cie_3d_table_param(pta + 3, pclt->dims[0], nbytes, table);
+ } else { /* n == 4 */
+ int d0 = pclt->dims[0], d1 = pclt->dims[1];
+ uint ntables = d0 * d1;
+ const ref *psuba;
+
+ check_read_type(pta[4], t_array);
+ if (r_size(pta + 4) != d0)
+ return_error(gs_error_rangecheck);
+ table =
+ gs_alloc_struct_array(mem->stable_memory, ntables, gs_const_string,
+ &st_const_string_element, "cie_table_param");
+ if (table == 0)
+ return_error(gs_error_VMerror);
+ psuba = pta[4].value.const_refs;
+ /*
+ * We know that d0 > 0, so code will always be set in the loop:
+ * we initialize code to 0 here solely to pacify stupid compilers.
+ */
+ for (code = 0, i = 0; i < d0; ++i) {
+ code = cie_3d_table_param(psuba + i, d1, nbytes, table + d1 * i);
+ if (code < 0)
+ break;
+ }
+ }
+ if (code < 0) {
+ gs_free_object((gs_memory_t *)mem, table, "cie_table_param");
+ return code;
+ }
+ pclt->table = table;
+ return 0;
+}
+static int
+cie_3d_table_param(const ref * ptable, uint count, uint nbytes,
+ gs_const_string * strings)
+{
+ const ref *rstrings;
+ uint i;
+
+ check_read_type(*ptable, t_array);
+ if (r_size(ptable) != count)
+ return_error(gs_error_rangecheck);
+ rstrings = ptable->value.const_refs;
+ for (i = 0; i < count; ++i) {
+ const ref *const prt2 = rstrings + i;
+
+ check_read_type(*prt2, t_string);
+ if (r_size(prt2) != nbytes)
+ return_error(gs_error_rangecheck);
+ strings[i].data = prt2->value.const_bytes;
+ strings[i].size = nbytes;
+ }
+ return 0;
+}
+
+/* ------ CIE setcolorspace ------ */
+
+/* Common code for the CIEBased* cases of setcolorspace. */
+static int
+cie_lmnp_param(const gs_memory_t *mem, const ref * pdref, gs_cie_common * pcie,
+ ref_cie_procs * pcprocs, bool *has_lmn_procs)
+{
+ int code;
+
+ if ((code = dict_range3_param(mem, pdref, "RangeLMN", &pcie->RangeLMN)) < 0 ||
+ (code = dict_matrix3_param(mem, pdref, "MatrixLMN", &pcie->MatrixLMN)) < 0 ||
+ (code = cie_points_param(mem, pdref, &pcie->points)) < 0
+ )
+ return code;
+ code = dict_proc3_param(mem, pdref, "DecodeLMN", &pcprocs->DecodeLMN);
+ if (code < 0)
+ return code;
+ *has_lmn_procs = !code; /* Need to know for efficient creation of ICC profile */
+ pcie->DecodeLMN = DecodeLMN_default;
+ return 0;
+}
+
+/* Get objects associated with cie color space */
+static int
+cie_a_param(const gs_memory_t *mem, const ref * pdref, gs_cie_a * pcie,
+ ref_cie_procs * pcprocs, bool *has_a_procs, bool *has_lmn_procs)
+{
+ int code;
+
+ code = dict_floats_param(mem, pdref, "RangeA", 2, (float *)&pcie->RangeA,
+ (const float *)&RangeA_default);
+ if (code < 0)
+ return code;
+ code = dict_floats_param(mem, pdref, "MatrixA", 3, (float *)&pcie->MatrixA,
+ (const float *)&MatrixA_default);
+ if (code < 0)
+ return code;
+ code = cie_lmnp_param(mem, pdref, &pcie->common, pcprocs, has_lmn_procs);
+ if (code < 0)
+ return code;
+ if ((code = dict_proc_param(pdref, "DecodeA", &(pcprocs->Decode.A), true)) < 0)
+ return code;
+ *has_a_procs = !code;
+ return 0;
+}
+
+/* Common code for the CIEBasedABC/DEF[G] cases of setcolorspace. */
+static int
+cie_abc_param(i_ctx_t *i_ctx_p, const gs_memory_t *mem, const ref * pdref,
+ gs_cie_abc * pcie, ref_cie_procs * pcprocs,
+ bool *has_abc_procs, bool *has_lmn_procs)
+{
+ int code;
+ gs_ref_memory_t *imem = (gs_ref_memory_t *)mem;
+
+ if ((code = dict_range3_param(mem, pdref, "RangeABC", &pcie->RangeABC)) < 0 ||
+ (code = dict_matrix3_param(mem, pdref, "MatrixABC", &pcie->MatrixABC)) < 0 ||
+ (code = cie_lmnp_param(mem, pdref, &pcie->common, pcprocs, has_lmn_procs)) < 0
+ )
+ return code;
+ code = dict_proc3_param(mem, pdref, "DecodeABC", &pcprocs->Decode.ABC);
+ if (code < 0)
+ return code;
+ *has_abc_procs = !code;
+ pcie->DecodeABC = DecodeABC_default;
+ /* At this point, we have all the parameters in pcie including knowing if
+ there
+ are procedures present. If there are no procedures, life is simple for us.
+ If there are procedures, we can not create the ICC profile until we have the procedures
+ sampled, which requires pushing the appropriate commands upon the postscript execution stack
+ to create the sampled procs and then having a follow up operation to create the ICC profile.
+ Because the procs may have to be merged with other operators and/or packed
+ in a particular form, we will have the PS operators stuff them in the already
+ existing static buffers that already exist for this purpose in the cie structures
+ e.g. gx_cie_vector_cache3_t that are in the common (params.abc.common.caches.DecodeLMN)
+ and unique entries (e.g. params.abc.caches.DecodeABC.caches) */
+ if (*has_abc_procs) {
+ cieicc_prepare_caches(i_ctx_p, (&pcie->RangeABC)->ranges,
+ pcprocs->Decode.ABC.value.const_refs,
+ &(pcie->caches.DecodeABC.caches)->floats,
+ &(pcie->caches.DecodeABC.caches)[1].floats,
+ &(pcie->caches.DecodeABC.caches)[2].floats,
+ NULL, pcie, imem, "Decode.ABC(ICC)");
+ } else {
+ pcie->caches.DecodeABC.caches->floats.params.is_identity = true;
+ (pcie->caches.DecodeABC.caches)[1].floats.params.is_identity = true;
+ (pcie->caches.DecodeABC.caches)[2].floats.params.is_identity = true;
+ }
+ if (*has_lmn_procs) {
+ cieicc_prepare_caches(i_ctx_p, (&pcie->common.RangeLMN)->ranges,
+ pcprocs->DecodeLMN.value.const_refs,
+ &(pcie->common.caches.DecodeLMN)->floats,
+ &(pcie->common.caches.DecodeLMN)[1].floats,
+ &(pcie->common.caches.DecodeLMN)[2].floats,
+ NULL, pcie, imem, "Decode.LMN(ICC)");
+ } else {
+ pcie->common.caches.DecodeLMN->floats.params.is_identity = true;
+ (pcie->common.caches.DecodeLMN)[1].floats.params.is_identity = true;
+ (pcie->common.caches.DecodeLMN)[2].floats.params.is_identity = true;
+ }
+ return 0;
+}
+
+/* Finish setting a CIE space (successful or not). */
+int
+cie_set_finish(i_ctx_t *i_ctx_p, gs_color_space * pcs,
+ const ref_cie_procs * pcprocs, int edepth, int code)
+{
+ if (code >= 0)
+ code = gs_setcolorspace(igs, pcs);
+ /* Delete the extra reference to the parameter tables. */
+ rc_decrement_only_cs(pcs, "cie_set_finish");
+ if (code < 0) {
+ ref_stack_pop_to(&e_stack, edepth);
+ return code;
+ }
+ istate->colorspace[0].procs.cie = *pcprocs;
+ pop(1);
+ return (ref_stack_count(&e_stack) == edepth ? 0 : o_push_estack);
+}
+
+/* Forward references */
+static int cie_defg_finish(i_ctx_t *);
+
+static int
+cie_defg_param(i_ctx_t *i_ctx_p, const gs_memory_t *mem, const ref * pdref,
+ gs_cie_defg * pcie, ref_cie_procs * pcprocs, bool *has_abc_procs,
+ bool *has_lmn_procs, bool *has_defg_procs, ref *ptref)
+{
+ int code;
+ gs_ref_memory_t *imem = (gs_ref_memory_t *)mem;
+
+ /* First get all the ABC and LMN information related to this space */
+ code = cie_abc_param(i_ctx_p, mem, pdref, (gs_cie_abc *) pcie, pcprocs,
+ has_abc_procs, has_lmn_procs);
+ if (code < 0)
+ return code;
+ code = dict_ranges_param(mem, pdref, "RangeDEFG", 4, pcie->RangeDEFG.ranges);
+ if (code < 0)
+ return code;
+ code = dict_ranges_param(mem, pdref, "RangeHIJK", 4, pcie->RangeHIJK.ranges);
+ if (code < 0)
+ return code;
+ code = cie_table_param(ptref, &pcie->Table, mem);
+ if (code < 0)
+ return code;
+ code = dict_proc_array_param(mem, pdref, "DecodeDEFG", 4,
+ &(pcprocs->PreDecode.DEFG));
+ if (code < 0)
+ return code;
+ *has_defg_procs = !code;
+ if (*has_defg_procs) {
+ cieicc_prepare_caches(i_ctx_p, (&pcie->RangeDEFG)->ranges,
+ pcprocs->PreDecode.DEFG.value.const_refs,
+ &(pcie->caches_defg.DecodeDEFG)->floats,
+ &(pcie->caches_defg.DecodeDEFG)[1].floats,
+ &(pcie->caches_defg.DecodeDEFG)[2].floats,
+ &(pcie->caches_defg.DecodeDEFG)[3].floats,
+ pcie, imem, "Decode.DEFG(ICC)");
+ } else {
+ pcie->caches_defg.DecodeDEFG->floats.params.is_identity = true;
+ (pcie->caches_defg.DecodeDEFG)[1].floats.params.is_identity = true;
+ (pcie->caches_defg.DecodeDEFG)[2].floats.params.is_identity = true;
+ (pcie->caches_defg.DecodeDEFG)[3].floats.params.is_identity = true;
+ }
+ return(0);
+}
+int
+ciedefgspace(i_ctx_t *i_ctx_p, ref *CIEDict, ulong dictkey)
+{
+ os_ptr op = osp;
+ int edepth = ref_stack_count(&e_stack);
+ gs_memory_t *mem = gs_state_memory(igs);
+ gs_color_space *pcs;
+ ref_cie_procs procs;
+ gs_cie_defg *pcie;
+ int code = 0;
+ ref *ptref;
+ bool has_defg_procs, has_abc_procs, has_lmn_procs;
+ gs_ref_memory_t *imem = (gs_ref_memory_t *)mem;
+
+/* pcs = gsicc_find_cs(dictkey, igs); */
+ pcs = NULL;
+ push(1); /* Sacrificial */
+ procs = istate->colorspace[0].procs.cie;
+ if (pcs == NULL ) {
+ if ((code = dict_find_string(CIEDict, "Table", &ptref)) <= 0)
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+ check_read_type(*ptref, t_array);
+ if (r_size(ptref) != 5)
+ return_error(gs_error_rangecheck);
+ /* Stable memory due to current caching of color space */
+ code = gs_cspace_build_CIEDEFG(&pcs, NULL, mem->stable_memory);
+ if (code < 0)
+ return code;
+ pcie = pcs->params.defg;
+ pcie->Table.n = 4;
+ pcie->Table.m = 3;
+ code = cie_cache_push_finish(i_ctx_p, cie_defg_finish, imem, pcie);
+ code = cie_defg_param(i_ctx_p, imemory, CIEDict, pcie, &procs,
+ &has_abc_procs, &has_lmn_procs, &has_defg_procs,ptref);
+ /* Add the color space to the profile cache */
+ gsicc_add_cs(igs, pcs,dictkey);
+ } else {
+ rc_increment(pcs);
+ }
+ return cie_set_finish(i_ctx_p, pcs, &procs, edepth, code);
+}
+
+static int
+cie_defg_finish(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_cie_defg *pcie = r_ptr(op, gs_cie_defg);
+
+ pcie->DecodeDEFG = DecodeDEFG_from_cache;
+ pcie->DecodeABC = DecodeABC_from_cache;
+ pcie->common.DecodeLMN = DecodeLMN_from_cache;
+ gs_cie_defg_complete(pcie);
+ pop(1);
+ return 0;
+}
+
+static int
+cie_def_param(i_ctx_t *i_ctx_p, const gs_memory_t *mem, const ref * pdref,
+ gs_cie_def * pcie, ref_cie_procs * pcprocs,
+ bool *has_abc_procs, bool *has_lmn_procs,
+ bool *has_def_procs, ref *ptref)
+{
+ int code;
+ gs_ref_memory_t *imem = (gs_ref_memory_t *)mem;
+
+ /* First get all the ABC and LMN information related to this space */
+ code = cie_abc_param(i_ctx_p, mem, pdref, (gs_cie_abc *) pcie, pcprocs,
+ has_abc_procs, has_lmn_procs);
+ if (code < 0)
+ return code;
+ code = dict_range3_param(mem, pdref, "RangeDEF", &pcie->RangeDEF);
+ if (code < 0)
+ return code;
+ code = dict_range3_param(mem, pdref, "RangeHIJ", &pcie->RangeHIJ);
+ if (code < 0)
+ return code;
+ code = cie_table_param(ptref, &pcie->Table, mem);
+ if (code < 0)
+ return code;
+ /* The DEF procs */
+ code = dict_proc3_param(mem, pdref, "DecodeDEF", &(pcprocs->PreDecode.DEF));
+ if (code < 0)
+ return code;
+ *has_def_procs = !code;
+ if (*has_def_procs) {
+ cieicc_prepare_caches(i_ctx_p, (&pcie->RangeDEF)->ranges,
+ pcprocs->PreDecode.DEF.value.const_refs,
+ &(pcie->caches_def.DecodeDEF)->floats,
+ &(pcie->caches_def.DecodeDEF)[1].floats,
+ &(pcie->caches_def.DecodeDEF)[2].floats,
+ NULL, pcie, imem, "Decode.DEF(ICC)");
+ } else {
+ pcie->caches_def.DecodeDEF->floats.params.is_identity = true;
+ (pcie->caches_def.DecodeDEF)[1].floats.params.is_identity = true;
+ (pcie->caches_def.DecodeDEF)[2].floats.params.is_identity = true;
+ }
+ return(0);
+}
+
+static int cie_def_finish(i_ctx_t *);
+int
+ciedefspace(i_ctx_t *i_ctx_p, ref *CIEDict, ulong dictkey)
+{
+ os_ptr op = osp;
+ int edepth = ref_stack_count(&e_stack);
+ gs_memory_t *mem = gs_state_memory(igs);
+ gs_color_space *pcs;
+ ref_cie_procs procs;
+ gs_cie_def *pcie;
+ int code = 0;
+ ref *ptref;
+ bool has_def_procs, has_lmn_procs, has_abc_procs;
+ gs_ref_memory_t *imem = (gs_ref_memory_t *)mem;
+
+/* pcs = gsicc_find_cs(dictkey, igs); */
+ pcs = NULL;
+ push(1); /* Sacrificial */
+ procs = istate->colorspace[0].procs.cie;
+ if (pcs == NULL ) {
+ if ((code = dict_find_string(CIEDict, "Table", &ptref)) <= 0)
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+ check_read_type(*ptref, t_array);
+ if (r_size(ptref) != 4)
+ return_error(gs_error_rangecheck);
+ /* Stable memory due to current caching of color space */
+ code = gs_cspace_build_CIEDEF(&pcs, NULL, mem->stable_memory);
+ if (code < 0)
+ return code;
+ pcie = pcs->params.def;
+ pcie->Table.n = 3;
+ pcie->Table.m = 3;
+ code = cie_cache_push_finish(i_ctx_p, cie_def_finish, imem, pcie);
+ code = cie_def_param(i_ctx_p, imemory, CIEDict, pcie, &procs,
+ &has_abc_procs, &has_lmn_procs, &has_def_procs, ptref);
+ /* Add the color space to the profile cache */
+ gsicc_add_cs(igs, pcs,dictkey);
+ } else {
+ rc_increment(pcs);
+ }
+ return cie_set_finish(i_ctx_p, pcs, &procs, edepth, code);
+}
+
+static int
+cie_def_finish(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_cie_def *pcie = r_ptr(op, gs_cie_def);
+
+ pcie->DecodeDEF = DecodeDEF_from_cache;
+ pcie->DecodeABC = DecodeABC_from_cache;
+ pcie->common.DecodeLMN = DecodeLMN_from_cache;
+ gs_cie_def_complete(pcie);
+ pop(1);
+ return 0;
+}
+
+static int cie_abc_finish(i_ctx_t *);
+
+int
+cieabcspace(i_ctx_t *i_ctx_p, ref *CIEDict, ulong dictkey)
+{
+ os_ptr op = osp;
+ int edepth = ref_stack_count(&e_stack);
+ gs_memory_t *mem = gs_state_memory(igs);
+ gs_color_space *pcs;
+ ref_cie_procs procs;
+ gs_cie_abc *pcie;
+ int code = 0;
+ bool has_lmn_procs, has_abc_procs;
+ gs_ref_memory_t *imem = (gs_ref_memory_t *)mem;
+
+/* See if the color space is in the profile cache */
+/* pcs = gsicc_find_cs(dictkey, igs); */
+ pcs = NULL;
+ push(1); /* Sacrificial */
+ procs = istate->colorspace[0].procs.cie;
+ if (pcs == NULL ) {
+ /* Stable memory due to current caching of color space */
+ code = gs_cspace_build_CIEABC(&pcs, NULL, mem->stable_memory);
+ if (code < 0)
+ return code;
+ pcie = pcs->params.abc;
+ code = cie_cache_push_finish(i_ctx_p, cie_abc_finish, imem, pcie);
+ code = cie_abc_param(i_ctx_p, imemory, CIEDict, pcie, &procs,
+ &has_abc_procs, &has_lmn_procs);
+ /* Set the color space in the graphic state. The ICC profile
+ will be set later if we actually use the space. Procs will be
+ sampled now though. Also, the finish procedure is on the stack
+ since that is where the vector cache is completed from the scalar
+ caches. We may need the vector cache if we are going to go
+ ahead and create an MLUT for this thing */
+ /* Add the color space to the profile cache */
+ gsicc_add_cs(igs, pcs,dictkey);
+ } else {
+ rc_increment(pcs);
+ }
+ return cie_set_finish(i_ctx_p, pcs, &procs, edepth, code);
+}
+
+static int
+cie_abc_finish(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_cie_abc *pcie = r_ptr(op, gs_cie_abc);
+
+ pcie->DecodeABC = DecodeABC_from_cache;
+ pcie->common.DecodeLMN = DecodeLMN_from_cache;
+ gs_cie_abc_complete(pcie);
+ pop(1);
+ return 0;
+}
+
+static int cie_a_finish(i_ctx_t *);
+
+int
+cieaspace(i_ctx_t *i_ctx_p, ref *CIEdict, ulong dictkey)
+{
+ os_ptr op = osp;
+ int edepth = ref_stack_count(&e_stack);
+ gs_memory_t *mem = gs_state_memory(igs);
+ const gs_ref_memory_t *imem = (gs_ref_memory_t *)mem;
+ gs_color_space *pcs;
+ ref_cie_procs procs;
+ gs_cie_a *pcie;
+ int code = 0;
+ bool has_a_procs = false;
+ bool has_lmn_procs;
+
+/* See if the color space is in the profile cache */
+/* pcs = gsicc_find_cs(dictkey, igs); */
+ pcs = NULL;
+ push(1); /* Sacrificial */
+ procs = istate->colorspace[0].procs.cie;
+ if (pcs == NULL ) {
+ /* Stable memory due to current caching of color space */
+ code = gs_cspace_build_CIEA(&pcs, NULL, mem->stable_memory);
+ if (code < 0)
+ return code;
+ pcie = pcs->params.a;
+ code = cie_a_param(imemory, CIEdict, pcie, &procs, &has_a_procs,
+ &has_lmn_procs);
+ if (code < 0)
+ return code;
+ /* Push finalize procedure on the execution stack */
+ code = cie_cache_push_finish(i_ctx_p, cie_a_finish, (gs_ref_memory_t *)imem, pcie);
+ if (code < 0)
+ return code;
+ if (!has_a_procs && !has_lmn_procs) {
+ pcie->common.caches.DecodeLMN->floats
+ .params.is_identity = true;
+ (pcie->common.caches.DecodeLMN)[1].floats.params.is_identity = true;
+ (pcie->common.caches.DecodeLMN)[2].floats.params.is_identity = true;
+ pcie->caches.DecodeA.floats.params.is_identity = true;
+ } else {
+ if (has_a_procs) {
+ code = cie_prepare_iccproc(i_ctx_p, &pcie->RangeA,
+ &procs.Decode.A, &pcie->caches.DecodeA.floats, pcie, imem, "Decode.A");
+ if (code < 0)
+ return code;
+ } else {
+ pcie->caches.DecodeA.floats.params.is_identity = true;
+ }
+ if (has_lmn_procs) {
+ cieicc_prepare_caches(i_ctx_p, (&pcie->common.RangeLMN)->ranges,
+ procs.DecodeLMN.value.const_refs,
+ &(pcie->common.caches.DecodeLMN)->floats,
+ &(pcie->common.caches.DecodeLMN)[1].floats,
+ &(pcie->common.caches.DecodeLMN)[2].floats,
+ NULL, pcie, imem, "Decode.LMN(ICC)");
+ } else {
+ pcie->common.caches.DecodeLMN->floats.params.is_identity = true;
+ (pcie->common.caches.DecodeLMN)[1].floats.params.is_identity = true;
+ (pcie->common.caches.DecodeLMN)[2].floats.params.is_identity = true;
+ }
+ }
+ /* Add the color space to the profile cache */
+ gsicc_add_cs(igs, pcs,dictkey);
+ } else {
+ rc_increment(pcs);
+ }
+ /* Set the color space in the graphic state. The ICC profile may be set after this
+ due to the needed sampled procs */
+ return cie_set_finish(i_ctx_p, pcs, &procs, edepth, code);
+}
+
+static int
+cie_a_finish(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_cie_a *pcie = r_ptr(op, gs_cie_a);
+
+ pcie->DecodeA = DecodeA_from_cache;
+ pcie->common.DecodeLMN = DecodeLMN_from_cache;
+ gs_cie_a_complete(pcie);
+ pop(1);
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+/* Prepare to cache the values for one or more procedures. */
+/* RJW: No longer used, but keeping it around in case it becomes useful
+ * again in future.
+ * static int cie_cache_finish1(i_ctx_t *);
+ */
+static int cie_cache_finish(i_ctx_t *);
+int
+cie_prepare_cache(i_ctx_t *i_ctx_p, const gs_range * domain, const ref * proc,
+ cie_cache_floats * pcache, void *container,
+ gs_ref_memory_t * imem, client_name_t cname)
+{
+ int space = imemory_space(imem);
+ gs_sample_loop_params_t lp;
+ es_ptr ep;
+
+ gs_cie_cache_init(&pcache->params, &lp, domain, cname);
+ pcache->params.is_identity = r_size(proc) == 0;
+ check_estack(9);
+ ep = esp;
+ make_real(ep + 9, lp.A);
+ make_int(ep + 8, lp.N);
+ make_real(ep + 7, lp.B);
+ ep[6] = *proc;
+ r_clear_attrs(ep + 6, a_executable);
+ make_op_estack(ep + 5, zcvx);
+ make_op_estack(ep + 4, zfor_samples);
+ make_op_estack(ep + 3, cie_cache_finish);
+ esp += 9;
+ /*
+ * The caches are embedded in the middle of other
+ * structures, so we represent the pointer to the cache
+ * as a pointer to the container plus an offset.
+ */
+ make_int(ep + 2, (char *)pcache - (char *)container);
+ make_struct(ep + 1, space, container);
+ return o_push_estack;
+}
+/* Note that pc3 may be 0, indicating that there are only 3 caches to load. */
+int
+cie_prepare_caches_4(i_ctx_t *i_ctx_p, const gs_range * domains,
+ const ref * procs,
+ cie_cache_floats * pc0, cie_cache_floats * pc1,
+ cie_cache_floats * pc2, cie_cache_floats * pc3,
+ void *container,
+ gs_ref_memory_t * imem, client_name_t cname)
+{
+ cie_cache_floats *pcn[4];
+ int i, n, code = 0;
+
+ pcn[0] = pc0, pcn[1] = pc1, pcn[2] = pc2;
+ if (pc3 == 0)
+ n = 3;
+ else
+ pcn[3] = pc3, n = 4;
+ for (i = 0; i < n && code >= 0; ++i)
+ code = cie_prepare_cache(i_ctx_p, domains + i, procs + i, pcn[i],
+ container, imem, cname);
+ return code;
+}
+
+/* Store the result of caching one procedure. */
+static int
+cie_cache_finish_store(i_ctx_t *i_ctx_p, bool replicate)
+{
+ os_ptr op = osp;
+ cie_cache_floats *pcache;
+ int code;
+
+ check_esp(2);
+ /* See above for the container + offset representation of */
+ /* the pointer to the cache. */
+ pcache = (cie_cache_floats *) (r_ptr(esp - 1, char) + esp->value.intval);
+
+ pcache->params.is_identity = false; /* cache_set_linear computes this */
+ if_debug3m('c', imemory, "[c]cache 0x%lx base=%g, factor=%g:\n",
+ (ulong) pcache, pcache->params.base, pcache->params.factor);
+ if (replicate ||
+ (code = float_params(op, gx_cie_cache_size, &pcache->values[0])) < 0
+ ) {
+ /* We might have underflowed the current stack block. */
+ /* Handle the parameters one-by-one. */
+ uint i;
+
+ for (i = 0; i < gx_cie_cache_size; i++) {
+ code = float_param(ref_stack_index(&o_stack,
+ (replicate ? 0 : gx_cie_cache_size - 1 - i)),
+ &pcache->values[i]);
+ if (code < 0)
+ return code;
+ }
+ }
+#ifdef DEBUG
+ if (gs_debug_c('c')) {
+ int i;
+
+ for (i = 0; i < gx_cie_cache_size; i += 4)
+ dmlprintf5(imemory, "[c] cache[%3d]=%g, %g, %g, %g\n", i,
+ pcache->values[i], pcache->values[i + 1],
+ pcache->values[i + 2], pcache->values[i + 3]);
+ }
+#endif
+ ref_stack_pop(&o_stack, (replicate ? 1 : gx_cie_cache_size));
+ esp -= 2; /* pop pointer to cache */
+ return o_pop_estack;
+}
+static int
+cie_cache_finish(i_ctx_t *i_ctx_p)
+{
+ return cie_cache_finish_store(i_ctx_p, false);
+}
+#if 0
+/* RJW: No longer used, but might be useful in future. */
+static int
+cie_cache_finish1(i_ctx_t *i_ctx_p)
+{
+ return cie_cache_finish_store(i_ctx_p, true);
+}
+#endif
+
+/* Push a finishing procedure on the e-stack. */
+/* ptr will be the top element of the o-stack. */
+int
+cie_cache_push_finish(i_ctx_t *i_ctx_p, op_proc_t finish_proc,
+ gs_ref_memory_t * imem, void *data)
+{
+ check_estack(2);
+ push_op_estack(finish_proc);
+ ++esp;
+ make_struct(esp, imemory_space(imem), data);
+ return o_push_estack;
+}
+
+/* Special functions related to the creation of ICC profiles
+ from the PS CIE color management objects. These basically
+ make use of the existing objects in the CIE stuctures to
+ store the sampled procs. These sampled procs are then
+ used in the creation of the ICC profiles */
+
+/* Push the sequence of commands onto the execution stack
+ so that we sample the procs */
+static int cie_create_icc(i_ctx_t *);
+static int
+cie_prepare_iccproc(i_ctx_t *i_ctx_p, const gs_range * domain, const ref * proc,
+ cie_cache_floats * pcache, void *container,
+ const gs_ref_memory_t * imem, client_name_t cname)
+{
+ int space = imemory_space(imem);
+ gs_sample_loop_params_t lp;
+ es_ptr ep;
+
+ gs_cie_cache_init(&pcache->params, &lp, domain, cname);
+ pcache->params.is_identity = r_size(proc) == 0;
+ check_estack(9);
+ ep = esp;
+ make_real(ep + 9, lp.A);
+ make_int(ep + 8, lp.N);
+ make_real(ep + 7, lp.B);
+ ep[6] = *proc;
+ r_clear_attrs(ep + 6, a_executable);
+ make_op_estack(ep + 5, zcvx);
+ make_op_estack(ep + 4, zfor_samples);
+ make_op_estack(ep + 3, cie_create_icc);
+ esp += 9;
+ /*
+ * The caches are embedded in the middle of other
+ * structures, so we represent the pointer to the cache
+ * as a pointer to the container plus an offset.
+ */
+ make_int(ep + 2, (char *)pcache - (char *)container);
+ make_struct(ep + 1, space, container);
+ return o_push_estack;
+}
+
+int
+cieicc_prepare_caches(i_ctx_t *i_ctx_p, const gs_range * domains,
+ const ref * procs,
+ cie_cache_floats * pc0, cie_cache_floats * pc1,
+ cie_cache_floats * pc2, cie_cache_floats * pc3,
+ void *container,
+ const gs_ref_memory_t * imem, client_name_t cname)
+{
+ cie_cache_floats *pcn[4];
+ int i, n, code = 0;
+
+ pcn[0] = pc0, pcn[1] = pc1, pcn[2] = pc2;
+ if (pc3 == 0)
+ n = 3;
+ else
+ pcn[3] = pc3, n = 4;
+ for (i = 0; i < n && code >= 0; ++i)
+ code = cie_prepare_iccproc(i_ctx_p, domains + i, procs + i, pcn[i],
+ container, imem, cname);
+ return code;
+}
+
+/* We have sampled the procs. Go ahead and create the ICC profile. */
+static int
+cie_create_icc(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ cie_cache_floats *pcache;
+ int code;
+
+ check_esp(2);
+ /* See above for the container + offset representation of */
+ /* the pointer to the cache. */
+ pcache = (cie_cache_floats *) (r_ptr(esp - 1, char) + esp->value.intval);
+
+ pcache->params.is_identity = false; /* cache_set_linear computes this */
+ if_debug3m('c', imemory, "[c]icc_sample_proc 0x%lx base=%g, factor=%g:\n",
+ (ulong) pcache, pcache->params.base, pcache->params.factor);
+ if ((code = float_params(op, gx_cie_cache_size, &pcache->values[0])) < 0) {
+ /* We might have underflowed the current stack block. */
+ /* Handle the parameters one-by-one. */
+ uint i;
+
+ for (i = 0; i < gx_cie_cache_size; i++) {
+ code = float_param(ref_stack_index(&o_stack,gx_cie_cache_size - 1 - i),
+ &pcache->values[i]);
+ if (code < 0)
+ return code;
+ }
+ }
+#ifdef DEBUG
+ if (gs_debug_c('c')) {
+ int i;
+
+ for (i = 0; i < gx_cie_cache_size; i += 4)
+ dmlprintf5(imemory, "[c] icc_sample_proc[%3d]=%g, %g, %g, %g\n", i,
+ pcache->values[i], pcache->values[i + 1],
+ pcache->values[i + 2], pcache->values[i + 3]);
+ }
+#endif
+ ref_stack_pop(&o_stack, gx_cie_cache_size);
+ esp -= 2; /* pop pointer to cache */
+ return o_pop_estack;
+}
diff --git a/psi/zcie.h b/psi/zcie.h
new file mode 100644
index 000000000..34777aa00
--- /dev/null
+++ b/psi/zcie.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* $Id: zcie.h $ */
+/* Definitions for CIEBased color spaces */
+
+#ifndef zcie_INCLUDED
+# define zcie_INCLUDED
+
+int cieaspace(i_ctx_t *i_ctx_p, ref *CIEdict, ulong dictkey);
+int cieabcspace(i_ctx_t *i_ctx_p, ref *CIEDict, ulong dictkey);
+int ciedefspace(i_ctx_t *i_ctx_p, ref *CIEDict, ulong dictkey);
+int ciedefgspace(i_ctx_t *i_ctx_p, ref *CIEDict, ulong dictkey);
+
+#endif /* zcie_INCLUDED */
diff --git a/psi/zcolor.c b/psi/zcolor.c
new file mode 100644
index 000000000..424ef53c8
--- /dev/null
+++ b/psi/zcolor.c
@@ -0,0 +1,6474 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Color operators */
+#include "memory_.h"
+#include "math_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "dstack.h" /* for systemdict */
+#include "estack.h"
+#include "ialloc.h"
+#include "igstate.h"
+#include "iutil.h"
+#include "store.h"
+#include "gscolor.h" /* for gs_setgray and gs_setrgbcolor */
+#include "gscsepr.h" /* For declarartion of Separation functions */
+#include "gscdevn.h" /* For declarartion of DeviceN functions */
+#include "gscpixel.h" /* For declarartion of DevicePixel functions */
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gzstate.h"
+#include "gxdcolor.h" /* for gxpcolor.h */
+#include "gxdevice.h"
+#include "gxdevmem.h" /* for gxpcolor.h */
+#include "gxcmap.h"
+#include "gxcspace.h"
+#include "gxcolor2.h"
+#include "gxpcolor.h"
+#include "idict.h"
+#include "icolor.h"
+#include "idparam.h"
+#include "iname.h"
+#include "iutil.h"
+#include "ifunc.h" /* For declaration of buildfunction */
+#include "icsmap.h"
+#include "ifunc.h"
+#include "zht2.h"
+#include "zcolor.h" /* For the PS_colour_space_t structure */
+#include "zcie.h" /* For CIE space function declarations */
+#include "zicc.h" /* For declaration of seticc */
+#include "gscspace.h" /* Needed for checking if current pgs colorspace is CIE */
+#include "iddict.h" /* for idict_put_string */
+#include "zfrsd.h" /* for make_rss() */
+
+/* Reject color spaces with excessive values of various parameters */
+/* to avoid dealing with overflows, INF, NaN during cache generation */
+/* The limit is arbitrary but sufficient. Bug 689658 ./gsciemap.c/... */
+#define MAX_CIE_RANGE 10000
+
+static const float default_0_1[] = {0, 1, 0, 1, 0, 1, 0, 1};
+
+/* imported from gsht.c */
+extern void gx_set_effective_transfer(gs_state *);
+
+/* Essential forward declarations */
+static int validate_spaces(i_ctx_t *i_ctx_p, ref *arr, int *depth);
+static int setcolorspace_cont(i_ctx_t *i_ctx_p);
+static int setcolor_cont(i_ctx_t *i_ctx_p);
+
+/* define the number of stack slots needed for zcolor_remap_one */
+const int zcolor_remap_one_ostack = 4;
+const int zcolor_remap_one_estack = 3;
+
+/* utility to test whether a Pattern instance uses a base space */
+static inline bool
+pattern_instance_uses_base_space(const gs_pattern_instance_t * pinst)
+{
+ return pinst->type->procs.uses_base_space(
+ pinst->type->procs.get_pattern(pinst) );
+}
+
+/*
+ * - currentcolor <param1> ... <paramN>
+ *
+ * Return the current color. <paramN> may be a dictionary or a null
+ * object, if the current color space is a pattern color space. The
+ * other parameters will be numeric.
+ *
+ * Note that the results of this operator differ slightly from those of
+ * most currentcolor implementations. If a color component value is
+ * integral (e.g.: 0, 1), it will be pushed on the stack as an integer.
+ * Most currentcolor implementations, including the earlier
+ * implementation in Ghostscript, would push real objects for all
+ * color spaces except indexed color space. The approach taken here is
+ * equally legitimate, and avoids special handling of indexed color
+ * spaces.
+ */
+static int
+zcurrentcolor(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ const gs_color_space * pcs = gs_currentcolorspace(igs);
+ const gs_client_color * pcc = gs_currentcolor(igs);
+ int i, n = cs_num_components(pcs);
+ bool push_pattern = n < 0;
+
+ /* check for pattern */
+ if (push_pattern) {
+ gs_pattern_instance_t * pinst = pcc->pattern;
+
+ if (pinst == 0 || !pattern_instance_uses_base_space(pinst))
+ n = 1;
+ else
+ n = -n;
+ }
+
+ /* check for sufficient space on the stack */
+ push(n);
+ op -= n - 1;
+
+ /* push the numeric operands, if any */
+ if (push_pattern)
+ --n;
+ for (i = 0; i < n; i++, op++) {
+ float rval = pcc->paint.values[i];
+ int ival = (int)rval;
+
+ /* the following handles indexed color spaces */
+ if (rval == ival && pcs->type->index == gs_color_space_index_Indexed)
+ make_int(op, ival);
+ else
+ make_real(op, rval);
+ }
+
+ /* push the pattern dictionary or null object, if appropriate */
+ if (push_pattern)
+ *op = istate->pattern[0];
+
+ return 0;
+}
+
+/*
+ * - .currentcolorspace <array>
+ *
+ * Return the current color space. Unlike the prior implementation, the
+ * istate->color_space.array field will now always have a legitimate
+ * (array) value.
+ */
+static int
+zcurrentcolorspace(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp; /* required by "push" macro */
+ int code;
+ ref namestr,stref;
+ byte *body;
+
+ /* Adobe applications expect that the Device spaces (DeviceGray
+ * DeviceRGB and DeviceCMYK) will always return the same array.
+ * Not merely the same content but the same actual array. To do
+ * this we define the arrays at startup (see gs_cspace.ps), and
+ * recover them here by executing PostScript.
+ */
+ if (r_has_type(&istate->colorspace[0].array, t_name)) {
+ name_string_ref(imemory, &istate->colorspace[0].array, &namestr);
+ if (r_size(&namestr) == 10 && !memcmp(namestr.value.bytes, "DeviceGray", 10)) {
+ body = ialloc_string(32, "string");
+ if (body == 0)
+ return_error(gs_error_VMerror);
+ memcpy(body, "systemdict /DeviceGray_array get", 32);
+ make_string(&stref, a_all | icurrent_space, 32, body);
+ } else {
+ if (r_size(&namestr) == 9 && !memcmp(namestr.value.bytes, "DeviceRGB", 9)) {
+ body = ialloc_string(31, "string");
+ if (body == 0)
+ return_error(gs_error_VMerror);
+ memcpy(body, "systemdict /DeviceRGB_array get", 31);
+ make_string(&stref, a_all | icurrent_space, 31, body);
+ } else {
+ if (r_size(&namestr) == 10 && !memcmp(namestr.value.bytes, "DeviceCMYK", 10)) {
+ body = ialloc_string(32, "string");
+ if (body == 0)
+ return_error(gs_error_VMerror);
+ memcpy(body, "systemdict /DeviceCMYK_array get", 32);
+ make_string(&stref, a_all | icurrent_space, 32, body);
+ } else {
+ /* Not one of the Device spaces, but still just a name. Give
+ * up and return the name on the stack.
+ */
+ push(1);
+ code = ialloc_ref_array(op, a_all, 1, "currentcolorspace");
+ if (code < 0)
+ return code;
+ refset_null(op->value.refs, 1);
+ ref_assign_old(op, op->value.refs,
+ &istate->colorspace[0].array,
+ "currentcolorspace");
+ return 0;
+ }
+ }
+ }
+ r_set_attrs(&stref, a_executable);
+ esp++;
+ ref_assign(esp, &stref);
+ return o_push_estack;
+ } else {
+ /* If the space isn't a simple name, then we don't need any special
+ * action and can simply use it.
+ */
+ push(1);
+ *op = istate->colorspace[0].array;
+ }
+ return 0;
+}
+
+/*
+ * - .getuseciecolor <bool>
+ *
+ * Return the current setting of the use_cie_color graphic state parameter,
+ * which tracks the UseCIEColor page device parameter. This parameter may be
+ * read (via this operator) at all language leves, but may only be set (via
+ * the .setuseciecolor operator; see zcolor3.c) only in language level 3.
+ *
+ * We handle this parameter separately from the page device primarily for
+ * performance reasons (the parameter may be queried frequently), but as a
+ * side effect achieve proper behavior relative to the language level. The
+ * interpreter is always initialized with this parameter set to false, and
+ * it can only be updated (via setpagedevice) in language level 3.
+ */
+static int
+zgetuseciecolor(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ *op = istate->use_cie_color;
+ return 0;
+}
+
+/* Clean up when unwinding the stack on an error. (No action needed.) */
+static int
+colour_cleanup(i_ctx_t *i_ctx_p)
+{
+ return 0;
+}
+
+/*
+ * <param1> ... <paramN> setcolor -
+ *
+ * Set the current color. All of the parameters except the topmost (paramN) are
+ * numbers; the topmost (and possibly only) entry may be pattern dictionary or
+ * a null object.
+ *
+ * The use of one operator to set both patterns and "normal" colors is
+ * consistent with Adobe's documentation, but primarily reflects the use of
+ * gs_setcolor for both purposes in the graphic library. An alternate
+ * implementation would use a .setpattern operator, which would interface with
+ * gs_setpattern.
+ *
+ * This operator is hidden by a pseudo-operator of the same name, so it will
+ * only be invoked under controlled situations. Hence, it does no operand
+ * checking.
+ */
+static int
+zsetcolor(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep = esp;
+ const gs_color_space * pcs = gs_currentcolorspace(igs);
+ gs_client_color cc;
+ int n_comps, n_numeric_comps, num_offset = 0, code, depth;
+ PS_colour_space_t *space;
+
+ /* initialize the client color pattern pointer for GC */
+ cc.pattern = 0;
+
+ /* check for a pattern color space */
+ if ((n_comps = cs_num_components(pcs)) < 0) {
+ n_comps = -n_comps;
+ if (r_has_type(op, t_dictionary)) {
+ ref *pImpl, pPatInst;
+
+ code = dict_find_string(op, "Implementation", &pImpl);
+ if (code != 0) {
+ code = array_get(imemory, pImpl, 0, &pPatInst);
+ if (code < 0)
+ return code;
+ cc.pattern = r_ptr(&pPatInst, gs_pattern_instance_t);
+ n_numeric_comps = ( pattern_instance_uses_base_space(cc.pattern)
+ ? n_comps - 1
+ : 0 );
+ } else
+ n_numeric_comps = 0;
+ } else
+ n_numeric_comps = 0;
+ num_offset = 1;
+ } else
+ n_numeric_comps = n_comps;
+
+ /* gather the numeric operands */
+ code = float_params(op - num_offset, n_numeric_comps, cc.paint.values);
+ if (code < 0)
+ return code;
+ /* The values are copied to graphic state and compared with */
+ /* other colors by memcmp() in gx_hld_saved_color_equal() */
+ /* This is the easiest way to avoid indeterminism */
+ memset(cc.paint.values + n_numeric_comps, 0,
+ sizeof(cc.paint.values) - sizeof(*cc.paint.values)*n_numeric_comps);
+
+ code = get_space_object(i_ctx_p, &istate->colorspace[0].array, &space);
+ if (code < 0)
+ return code;
+ if (space->validatecomponents) {
+ code = space->validatecomponents(i_ctx_p,
+ &istate->colorspace[0].array,
+ cc.paint.values, n_numeric_comps);
+ if (code < 0)
+ return code;
+ }
+
+ /* pass the color to the graphic library */
+ if ((code = gs_setcolor(igs, &cc)) >= 0) {
+
+ if (n_comps > n_numeric_comps) {
+ istate->pattern[0] = *op; /* save pattern dict or null */
+ n_comps = n_numeric_comps + 1;
+ }
+ }
+
+ /* Check the color spaces, to see if we need to run any tint transform
+ * procedures. Some Adobe applications *eg Photoshop) expect that the
+ * tint transform will be run and use this to set up duotone DeviceN
+ * spaces.
+ */
+ code = validate_spaces(i_ctx_p, &istate->colorspace[0].array, &depth);
+ if (code < 0)
+ return code;
+ /* Set up for the continuation procedure which will do the work */
+ /* Make sure the exec stack has enough space */
+ check_estack(5);
+ /* A place holder for data potentially used by transform functions */
+ ep = esp += 1;
+ make_int(ep, 0);
+ /* Store the 'depth' of the space returned during checking above */
+ ep = esp += 1;
+ make_int(ep, 0);
+ /* Store the 'stage' of processing (initially 0) */
+ ep = esp += 1;
+ make_int(ep, 0);
+ /* Store a pointer to the color space stored on the operand stack
+ * as the stack may grow unpredictably making further access
+ * to the space difficult
+ */
+ ep = esp += 1;
+ *ep = istate->colorspace[0].array;
+ /* Finally, the actual continuation routine */
+ push_op_estack(setcolor_cont);
+ return o_push_estack;
+}
+
+/* This is used to detect color space changes due
+ to the changing of UseCIEColor during transparency
+ soft mask processing */
+
+static bool name_is_device_color( char *cs_name )
+{
+
+ return( strcmp(cs_name, "DeviceGray") == 0 ||
+ strcmp(cs_name, "DeviceRGB") == 0 ||
+ strcmp(cs_name, "DeviceCMYK") == 0);
+
+}
+
+/*
+ * Given two color space arrays, attempts to determine if they are the
+ * same space by comparing their contents recursively. For some spaces,
+ * especially CIE based color spaces, it can significantly improve
+ * performance if the same space is frequently re-used.
+ */
+static int is_same_colorspace(i_ctx_t * i_ctx_p, ref *space1, ref *space2, bool isCIE)
+{
+ PS_colour_space_t *oldcspace = 0, *newcspace = 0;
+ ref oldspace, *poldspace = &oldspace, newspace, *pnewspace = &newspace;
+ int code, CIESubst;
+
+ /* Silence compiler warnings */
+ oldspace.tas.type_attrs = 0;
+ oldspace.tas.type_attrs = 0;
+
+ ref_assign(pnewspace, space1);
+ ref_assign(poldspace, space2);
+
+ do {
+ if (r_type(poldspace) != r_type(pnewspace))
+ return 0;
+
+ code = get_space_object(i_ctx_p, poldspace, &oldcspace);
+ if (code < 0)
+ return 0;
+
+ code = get_space_object(i_ctx_p, pnewspace, &newcspace);
+ if (code < 0)
+ return 0;
+
+ /* Check the two color space types are the same
+ * (Indexed, Separation, DeviceCMYK etc).
+ */
+ if (strcmp(oldcspace->name, newcspace->name) != 0)
+ return 0;
+
+ /* Call the space-specific comparison routine */
+ if (!oldcspace->compareproc(i_ctx_p, poldspace, pnewspace))
+ return 0;
+
+ /* See if current space is CIE based (which could happen
+ if UseCIE had been true previously), but UseCIE is false
+ and incoming space is device based. This can occur
+ when we are now processing a soft mask, which should not
+ use the UseCIEColor option.
+
+ Need to detect this case at both transitions
+
+ Device Color UseCIEColor true
+ Soft mask
+ Device color UseCIEColor false
+ Soft mask
+ Device color UseCIEColor true
+ */
+
+ if ( name_is_device_color(newcspace->name) ){
+ if ( gs_color_space_is_CIE(gs_currentcolorspace_inline(i_ctx_p->pgs)) ){
+ if ( !isCIE ) return 0; /* The color spaces will be different */
+ } else {
+ if ( isCIE ) return 0; /* The color spaces will be different */
+ }
+ }
+
+ /* The current space is OK, if there is no alternate, then that's
+ * good enough.
+ */
+ if (!oldcspace->alternateproc)
+ break;
+
+ /* Otherwise, retrieve the alternate space for each, and continue
+ * round the loop, checking those.
+ */
+ code = oldcspace->alternateproc(i_ctx_p, poldspace, &poldspace, &CIESubst);
+ if (code < 0)
+ return 0;
+
+ code = newcspace->alternateproc(i_ctx_p, pnewspace, &pnewspace, &CIESubst);
+ if (code < 0)
+ return 0;
+ }
+ while(1);
+
+ return 1;
+}
+
+/*
+ * <array> setcolorspace -
+ *
+ * Set the nominal color space. This color space will be pushd by the
+ * currentcolorspace operator, but is not directly used to pass color
+ * space information to the graphic library.
+ *
+ */
+static int
+zsetcolorspace(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep = esp;
+ int code, depth;
+ bool is_CIE;
+
+ /* Make sure we have an operand... */
+ check_op(1);
+ /* Check its either a name (base space) or an array */
+ if (!r_has_type(op, t_name))
+ if (!r_is_array(op))
+ return_error(gs_error_typecheck);
+
+ code = validate_spaces(i_ctx_p, op, &depth);
+ if (code < 0)
+ return code;
+
+ is_CIE = istate->use_cie_color.value.boolval;
+
+ /* See if its the same as the current space */
+ if (is_same_colorspace(i_ctx_p, op, &istate->colorspace[0].array, is_CIE)) {
+ PS_colour_space_t *cspace;
+
+ /* Even if its the same space, we still need to set the correct
+ * initial color value.
+ */
+ code = get_space_object(i_ctx_p, &istate->colorspace[0].array, &cspace);
+ if (code < 0)
+ return 0;
+ if (cspace->initialcolorproc) {
+ cspace->initialcolorproc(i_ctx_p, &istate->colorspace[0].array);
+ }
+ /* Pop the space off the stack */
+ pop(1);
+ return 0;
+ }
+ /* Set up for the continuation procedure which will do the work */
+ /* Make sure the exec stack has enough space */
+ check_estack(5);
+ /* Store the initial value of CIE substitution (not substituting) */
+ ep = esp += 1;
+ make_int(ep, 0);
+ /* Store the 'depth' of the space returned during checking above */
+ ep = esp += 1;
+ make_int(ep, depth);
+ /* Store the 'stage' of processing (initially 0) */
+ ep = esp += 1;
+ make_int(ep, 0);
+ /* Store a pointer to the color space stored on the operand stack
+ * as the stack may grow unpredictably making further access
+ * to the space difficult
+ */
+ ep = esp += 1;
+ *ep = *op;
+ /* Finally, the actual continuation routine */
+ push_op_estack(setcolorspace_cont);
+ return o_push_estack;
+}
+
+/*
+ * A special version of the setcolorspace operation above. This sets the
+ * CIE substitution flag to true before starting, which prevents any further
+ * CIE substitution taking place.
+ */
+static int
+setcolorspace_nosubst(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep = esp;
+ int code, depth;
+
+ /* Make sure we have an operand... */
+ check_op(1);
+ /* Check its either a name (base space) or an array */
+ if (!r_has_type(op, t_name))
+ if (!r_is_array(op))
+ return_error(gs_error_typecheck);
+
+ code = validate_spaces(i_ctx_p, op, &depth);
+ if (code < 0)
+ return code;
+
+ /* Set up for the continuation procedure which will do the work */
+ /* Make sure the exec stack has enough space */
+ check_estack(5);
+ /* Store the initial value of CIE substitution (substituting) */
+ ep = esp += 1;
+ make_int(ep, 1);
+ /* Store the 'depth' of the space returned during checking above */
+ ep = esp += 1;
+ make_int(ep, depth);
+ /* Store the 'stage' of processing (initially 0) */
+ ep = esp += 1;
+ make_int(ep, 0);
+ /* Store a pointer to the color space stored on the operand stack
+ * as the stack may grow unpredictably making further access
+ * to the space difficult
+ */
+ ep = esp += 1;
+ *ep = *op;
+ /* Finally, the actual continuation routine */
+ push_op_estack(setcolorspace_cont);
+ return o_push_estack;
+}
+
+/*
+ * <name> .includecolorspace -
+ *
+ * See the comment for gs_includecolorspace in gscolor2.c .
+ */
+static int
+zincludecolorspace(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ ref nsref;
+ int code;
+
+ check_type(*op, t_name);
+ name_string_ref(imemory, op, &nsref);
+ code = gs_includecolorspace(igs, nsref.value.const_bytes, r_size(&nsref));
+ if (!code)
+ pop(1);
+ return code;
+}
+
+/* - currenttransfer <proc> */
+static int
+zcurrenttransfer(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ *op = istate->transfer_procs.gray;
+ return 0;
+}
+
+/*
+ * - processcolors <int> -
+ *
+ * Note: this is an undocumented operator that is not supported
+ * in Level 2.
+ */
+static int
+zprocesscolors(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, gs_currentdevice(igs)->color_info.num_components);
+ return 0;
+}
+
+/* <proc> settransfer - */
+static int
+zsettransfer(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_proc(*op);
+ check_ostack(zcolor_remap_one_ostack - 1);
+ check_estack(1 + zcolor_remap_one_estack);
+ istate->transfer_procs.red =
+ istate->transfer_procs.green =
+ istate->transfer_procs.blue =
+ istate->transfer_procs.gray = *op;
+ if ((code = gs_settransfer_remap(igs, gs_mapped_transfer, false)) < 0)
+ return code;
+ push_op_estack(zcolor_reset_transfer);
+ pop(1);
+ return zcolor_remap_one( i_ctx_p,
+ &istate->transfer_procs.gray,
+ igs->set_transfer.gray,
+ igs,
+ zcolor_remap_one_finish );
+}
+
+/*
+ * Internal routines
+ */
+
+/*
+ * Prepare to remap one color component (also used for black generation
+ * and undercolor removal). Use the 'for' operator to gather the values.
+ * The caller must have done the necessary check_ostack and check_estack.
+ */
+int
+zcolor_remap_one(
+ i_ctx_t * i_ctx_p,
+ const ref * pproc,
+ gx_transfer_map * pmap,
+ const gs_state * pgs,
+ op_proc_t finish_proc )
+{
+ os_ptr op;
+
+ /*
+ * Detect the identity function, which is a common value for one or
+ * more of these functions.
+ */
+ if (r_size(pproc) == 0) {
+ gx_set_identity_transfer(pmap);
+ /*
+ * Even though we don't actually push anything on the e-stack, all
+ * clients do, so we return o_push_estack in this case. This is
+ * needed so that clients' finishing procedures will get run.
+ */
+ return o_push_estack;
+ }
+ op = osp += 4;
+ make_real(op - 3, 0);
+ make_int(op - 2, transfer_map_size - 1);
+ make_real(op - 1, 1);
+ *op = *pproc;
+ ++esp;
+ make_struct(esp, imemory_space((gs_ref_memory_t *) pgs->memory),
+ pmap);
+ push_op_estack(finish_proc);
+ push_op_estack(zfor_samples);
+ return o_push_estack;
+}
+
+/* Store the result of remapping a component. */
+static int
+zcolor_remap_one_store(i_ctx_t *i_ctx_p, double min_value)
+{
+ int i;
+ gx_transfer_map *pmap = r_ptr(esp, gx_transfer_map);
+
+ if (ref_stack_count(&o_stack) < transfer_map_size)
+ return_error(gs_error_stackunderflow);
+ for (i = 0; i < transfer_map_size; i++) {
+ double v;
+ int code =
+ real_param(ref_stack_index(&o_stack, transfer_map_size - 1 - i),
+ &v);
+
+ if (code < 0)
+ return code;
+ pmap->values[i] =
+ (v < min_value ? float2frac(min_value) :
+ v >= 1.0 ? frac_1 :
+ float2frac(v));
+ }
+ ref_stack_pop(&o_stack, transfer_map_size);
+ esp--; /* pop pointer to transfer map */
+ return o_pop_estack;
+}
+int
+zcolor_remap_one_finish(i_ctx_t *i_ctx_p)
+{
+ return zcolor_remap_one_store(i_ctx_p, 0.0);
+}
+int
+zcolor_remap_one_signed_finish(i_ctx_t *i_ctx_p)
+{
+ return zcolor_remap_one_store(i_ctx_p, -1.0);
+}
+
+/* Finally, reset the effective transfer functions and */
+/* invalidate the current color. */
+int
+zcolor_reset_transfer(i_ctx_t *i_ctx_p)
+{
+ gx_set_effective_transfer(igs);
+ return zcolor_remap_color(i_ctx_p);
+}
+int
+zcolor_remap_color(i_ctx_t *i_ctx_p)
+{
+ /* Remap both colors. This should never hurt. */
+ gs_swapcolors(igs);
+ gx_unset_dev_color(igs);
+ gs_swapcolors(igs);
+ gx_unset_dev_color(igs);
+ return 0;
+}
+
+/*
+ * <param1> ... <paramN> .color_test <param1> ... <paramN>
+ *
+ * encode and decode color to allow mapping to be tested.
+ */
+static int
+zcolor_test(i_ctx_t *i_ctx_p)
+{
+ gx_color_value cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ gx_device *dev = gs_currentdevice(igs);
+ int ncomp = dev->color_info.num_components;
+ gx_color_index color;
+ os_ptr op = osp - (ncomp-1);
+ int i;
+ if (ref_stack_count(&o_stack) < ncomp)
+ return_error(gs_error_stackunderflow);
+ for (i = 0; i < ncomp; i++) {
+ if (r_has_type(op+i, t_real))
+ cv[i] = (gx_color_value)
+ (op[i].value.realval * gx_max_color_value);
+ else if (r_has_type(op+i, t_integer))
+ cv[i] = (gx_color_value)
+ (op[i].value.intval * gx_max_color_value);
+ else
+ return_error(gs_error_typecheck);
+ }
+ color = (*dev_proc(dev, encode_color)) (dev, cv);
+ (*dev_proc(dev, decode_color)) (dev, color, cv);
+ for (i = 0; i < ncomp; i++)
+ make_real(op+i, (float)cv[i] / (float)gx_max_color_value);
+ return 0;
+}
+
+/*
+ * <levels> .color_test_all <value0> ... <valueN>
+ *
+ * Test encode/decode color procedures for a range of values.
+ * Return value with the worst error in a single component.
+ */
+static int
+zcolor_test_all(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_color_value cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ gx_color_value cvout[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ gx_color_value cvbad[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int counter[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ gx_device *dev = gs_currentdevice(igs);
+ int ncomp = dev->color_info.num_components;
+ int steps;
+ int maxerror = 0;
+ int err;
+ int acceptable_error;
+ int linsep = dev->color_info.separable_and_linear == GX_CINFO_SEP_LIN;
+ int linsepfailed = 0;
+ int lsmaxerror = 0;
+ gx_color_index color, lscolor;
+ int i, j, k;
+ int finished = 0;
+
+ if (ncomp == 1)
+ acceptable_error = gx_max_color_value / dev->color_info.max_gray + 1;
+ else
+ acceptable_error = gx_max_color_value / dev->color_info.max_color + 1;
+
+ if (ref_stack_count(&o_stack) < 1)
+ return_error(gs_error_stackunderflow);
+ if (!r_has_type(&osp[0], t_integer))
+ return_error(gs_error_typecheck);
+ steps = osp[0].value.intval;
+ for (i = 0; i < ncomp; i++) {
+ counter[i] = 0;
+ cvbad[i] = 0;
+ }
+
+ dmprintf1(dev->memory, "Number of components = %d\n", ncomp);
+ dmprintf1(dev->memory, "Depth = %d\n", dev->color_info.depth);
+ dmprintf2(dev->memory, "max_gray = %d dither_grays = %d\n",
+ dev->color_info.max_gray, dev->color_info.dither_grays);
+ dmprintf2(dev->memory, "max_color = %d dither_colors = %d\n",
+ dev->color_info.max_color, dev->color_info.dither_colors);
+ dmprintf1(dev->memory, "polarity = %s\n",
+ dev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE ? "Additive" :
+ dev->color_info.polarity == GX_CINFO_POLARITY_SUBTRACTIVE ?"Subtractive":
+ "Unknown");
+ /* Indicate color index value with all colorants = zero */
+ for (i = 0; i < ncomp; i++)
+ cv[i] = 0;
+ color = (*dev_proc(dev, encode_color)) (dev, cv);
+ if (sizeof(color) <= sizeof(ulong))
+ dmprintf1(dev->memory, "Zero color index: %8lx\n", (ulong)color);
+ else
+ dmprintf2(dev->memory, "Zero color index: %8lx%08lx\n",
+ (ulong)(color >> 8*(sizeof(color) - sizeof(ulong))), (ulong)color);
+
+ dmprintf1(dev->memory, "separable_and_linear = %s\n",
+ linsep == GX_CINFO_SEP_LIN_NONE ? "No" :
+ linsep == GX_CINFO_SEP_LIN ? "Yes" :
+ "Unknown");
+ if (dev->color_info.gray_index == GX_CINFO_COMP_INDEX_UNKNOWN)
+ dmprintf(dev->memory, "gray_index is unknown\n");
+ else
+ dmprintf1(dev->memory, "gray_index = %d\n", dev->color_info.gray_index);
+ if (linsep) {
+ dmprintf(dev->memory, " Shift Mask Bits\n");
+ for (i = 0; i < ncomp; i++) {
+ dmprintf3(dev->memory, " %5d %8x %4d\n",
+ (int)(dev->color_info.comp_shift[i]),
+ (int)(dev->color_info.comp_mask[i]),
+ (int)(dev->color_info.comp_bits[i]));
+ }
+ }
+
+ while (!finished) {
+ for (j = 0; j <= steps; j++) {
+ for (i = 0; i < ncomp; i++)
+ cv[i] = counter[i] * gx_max_color_value / steps;
+ color = (*dev_proc(dev, encode_color)) (dev, cv);
+ if (linsep) {
+ /* Derive it the other way */
+ lscolor = gx_default_encode_color(dev, cv);
+ if ((color != lscolor) && (linsepfailed < 5)) {
+ linsepfailed++;
+ dmprintf(dev->memory, "Failed separable_and_linear for");
+ for (i = 0; i < ncomp; i++)
+ dmprintf1(dev->memory, " %d", cv[i]);
+ dmprintf(dev->memory, "\n");
+ dmprintf2(dev->memory, "encode_color=%x gx_default_encode_color=%x\n",
+ (int)color, (int)lscolor);
+ }
+ }
+ (*dev_proc(dev, decode_color)) (dev, color, cvout);
+ for (i = 0; i < ncomp; i++) {
+ err = (int)cvout[i] - (int)cv[i];
+ if (err < 0)
+ err = -err;
+ if (err > maxerror) {
+ maxerror = err;
+ for (k=0; k < ncomp; k++)
+ cvbad[k] = cv[k];
+ }
+ }
+ if (linsep) {
+ gx_default_decode_color(dev, color, cvout);
+ for (i = 0; i < ncomp; i++) {
+ err = (int)cvout[i] - (int)cv[i];
+ if (err < 0)
+ err = -err;
+ if (err > lsmaxerror) {
+ lsmaxerror = err;
+ }
+ }
+ }
+ counter[0] += 1;
+ }
+ counter[0] = 0;
+ i = 1;
+ while (i < ncomp) {
+ counter[i] += 1;
+ if (counter[i] > steps) {
+ counter[i] = 0;
+ i++;
+ }
+ else
+ break;
+ }
+ if (i >= ncomp)
+ finished = 1;
+ }
+
+ dmprintf2(dev->memory, "Maximum error %g %s\n",
+ (float)maxerror / (float)gx_max_color_value,
+ maxerror <= acceptable_error ? "is Ok" :
+ maxerror <= 3*acceptable_error/2 ? "is POOR" : "FAILED");
+
+ if (linsep)
+ dmprintf2(dev->memory, "Maximum linear_and_separable error %g %s\n",
+ (float)lsmaxerror / (float)gx_max_color_value,
+ lsmaxerror <= acceptable_error ? "is Ok" :
+ lsmaxerror <= 3*acceptable_error/2 ? "is POOR" : "FAILED");
+
+ /* push worst value */
+ push(ncomp-1);
+ op -= ncomp - 1;
+ for (i = 0; i < ncomp; i++)
+ make_real(op+i, (float)cvbad[i] / (float)gx_max_color_value);
+
+ return 0;
+}
+
+/* Convert between RGB and HSB colors, using the hexcone approach (see
+ * Rogers, David, "Procedureal Elements For Computer Graphics",
+ * (McGraw-Hill, 1985), pp. 402 - 3).
+ *
+ * The rgb ==> hsb calculation is:
+ *
+ * br = max(r, g, b)
+ *
+ * if (br == 0)
+ * h = 0, s = 0;
+ * else {
+ * v = min(r, g, b)
+ * diff = br - v;
+ * sat = diff / br;
+ * if (r == br)
+ * h = (g - b) / (6 * diff) + (b > g ? 1 : 0);
+ * else if (g == br)
+ * h = 1/3 + (b - r) / (6 * diff);
+ * else
+ * h = 2/3 + (r - g) / (6 * diff);
+ * }
+ */
+static int rgb2hsb(float *RGB)
+{
+ float HSB[3], v, diff;
+ int i, j=0;
+
+ v = 1.0;
+ for (i=0;i<3;i++)
+ HSB[i] = 0.0;
+ for (i=0;i<3;i++) {
+ if (RGB[i] > HSB[2]) {
+ HSB[2] = RGB[i];
+ j = i;
+ }
+ if (RGB[i] < v)
+ v = RGB[i];
+ }
+ if (HSB[2] != 0) {
+ diff = HSB[2] - v;
+ HSB[1] = diff / HSB[2];
+ switch (j) {
+ case 0 : /* R == Brightness */
+ /* diff can only be zero if r == br, so we need to make sure here we
+ * don't divide by zero
+ */
+ if (diff)
+ HSB[0] = ((RGB[1] - RGB[2]) / (6.0 * diff)) + (RGB[2] > RGB[1] ? 1.0 : 0.0);
+ else
+ HSB[0] = (RGB[1] - RGB[2]) + (RGB[2] > RGB[1] ? 1.0 : 0.0);
+ break;
+ case 1 : /* G == Brightness */
+ HSB[0] = (1.0 / 3.0) + (RGB[2] - RGB[0]) / (6.0 * diff);
+ break;
+ case 2 : /* B == Brightness */
+ HSB[0] = (2.0 / 3.0) + (RGB[0] - RGB[1]) / (6.0 * diff);
+ break;
+ }
+ }
+ for (i=0;i<3;i++) {
+ if (HSB[i] < 0)
+ HSB[i] = 0;
+ if (RGB[i] > 1)
+ HSB[i] = 1;
+ RGB[i] = HSB[i];
+ }
+ return 0;
+}
+/* The hsb ==> rgb conversion is:
+ *
+ * mn = (1 - s) * br, md = 6 * s * br;
+ *
+ * switch ((int)floor(6 * h)) {
+ * case 0: %% r >= g >= b
+ * r = br;
+ * g = mn + h * md;
+ * b = mn;
+ * break;
+ *
+ * case 1: %% g >= r >= b
+ * r = mn + md * (1/3 - h);
+ * g = br;
+ * b = mn;
+ * break;
+ *
+ * case 2: %% g >= b >= r
+ * r = mn;
+ * g = br;
+ * b = mn + (h - 1/3) * md;
+ * break;
+ *
+ * case 3: %% b >= g >= r
+ * r = mn;
+ * g = mn + (2/3 - h) * md;
+ * b = br;
+ * break;
+ *
+ * case 4: %% b >= r >= g
+ * r = mn + (h - 2/3) * md;
+ * g = mn;
+ * b = br;
+ * break;
+ *
+ * case 5: %% r >= b >= g
+ * r = br;
+ * g = mn;
+ * b = mn + (1 - h) * md;
+ * break;
+ *
+ * case 6: %% We have wrapped around the hexcone. Thus this case is
+ * the same as case 0 with h = 0
+ * h = 0;
+ * r = br;
+ * g = mn + h * md = mn;
+ * b = mn;
+ * break;
+ * }
+ */
+static int hsb2rgb(float *HSB)
+{
+ float RGB[3], mn, md;
+ int i;
+
+ mn = (1.0 - HSB[1]) * HSB[2];
+ md = 6.0 * HSB[1] * HSB[2];
+
+ switch ((int)floor(6.0 * HSB[0])) {
+ case 6:
+ HSB[0] = (float)0;
+ default: /* Shuts up compiler warning about RGB being uninited */
+ case 0:
+ RGB[0] = HSB[2];
+ RGB[1] = mn + (HSB[0] * md);
+ RGB[2] = mn;
+ break;
+ case 1:
+ RGB[0] = mn + (md * ((1.0 / 3.0) - HSB[0]));
+ RGB[1] = HSB[2];
+ RGB[2] = mn;
+ break;
+ case 2:
+ RGB[0] = mn;
+ RGB[1] = HSB[2];
+ RGB[2] = mn + ((HSB[0] - (1.0 / 3.0)) * md);
+ break;
+ case 3:
+ RGB[0] = mn;
+ RGB[1] = mn + (((2.0 / 3.0f) - HSB[0]) * md);
+ RGB[2] = HSB[2];
+ break;
+ case 4:
+ RGB[0] = mn + ((HSB[0] - (2.0 / 3.0)) * md);
+ RGB[1] = mn;
+ RGB[2] = HSB[2];
+ break;
+ case 5:
+ RGB[0] = HSB[2];
+ RGB[1] = mn;
+ RGB[2] = mn + ((1.0 - HSB[0]) * md);
+ break;
+ }
+ for (i=0;i<3;i++) {
+ if (RGB[i] < 0)
+ RGB[i] = 0;
+ if (RGB[i] > 1)
+ RGB[i] = 1;
+ HSB[i] = RGB[i];
+ }
+ return 0;
+}
+
+/* The routines for handling colors and color spaces, moved from
+ * PostScript to C, start here.
+ */
+
+/* DeviceGray */
+static int setgrayspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ os_ptr op = osp;
+ gs_color_space *pcs;
+ int code=0;
+ ref stref;
+
+ do {
+ switch (*stage) {
+ case 0:
+ if (istate->use_cie_color.value.boolval && !CIESubst) {
+ byte *body;
+ ref *nosubst;
+
+ code = dict_find_string(systemdict, "NOSUBSTDEVICECOLORS", &nosubst);
+ if (code != 0) {
+ if (!r_has_type(nosubst, t_boolean))
+ return_error(gs_error_typecheck);
+ }
+ if (code != 0 && nosubst->value.boolval) {
+ *stage = 4;
+ *cont = 1;
+ body = ialloc_string(32, "string");
+ if (body == 0)
+ return_error(gs_error_VMerror);
+ memcpy(body, "/DefaultGray ..nosubstdevicetest",32);
+ make_string(&stref, a_all | icurrent_space, 32, body);
+ r_set_attrs(&stref, a_executable);
+ esp++;
+ ref_assign(esp, &stref);
+ return o_push_estack;
+ } else {
+ *stage = 2;
+ *cont = 1;
+ body = ialloc_string(47, "string");
+ if (body == 0)
+ return_error(gs_error_VMerror);
+ memcpy(body, "{/DefaultGray /ColorSpace findresource} stopped",47);
+ make_string(&stref, a_all | icurrent_space, 47, body);
+ r_set_attrs(&stref, a_executable);
+ esp++;
+ ref_assign(esp, &stref);
+ return o_push_estack;
+ }
+ break;
+ }
+ /* fall through */
+ case 1:
+ pcs = gs_cspace_new_DeviceGray(imemory);
+ if (pcs == NULL)
+ return_error(gs_error_VMerror);
+ code = gs_setcolorspace(igs, pcs);
+ if (code >= 0) {
+ gs_client_color *pcc = gs_currentcolor_inline(igs);
+
+ cs_adjust_color_count(igs, -1); /* not strictly necessary */
+ pcc->paint.values[0] = (0);
+ pcc->pattern = 0; /* for GC */
+ gx_unset_dev_color(igs);
+ }
+ rc_decrement_only_cs(pcs, "zsetdevcspace");
+ *cont = 0;
+ *stage = 0;
+ break;
+ case 2:
+ if (!r_has_type(op, t_boolean))
+ return_error(gs_error_typecheck);
+ if (op->value.boolval) {
+ /* Failed to find the /DefaultGray CSA, so give up and
+ * just use DeviceGray
+ */
+ pop(1);
+ *stage = 1;
+ break;
+ }
+ pop(1);
+ *cont = 1;
+ *stage = 3;
+ code = setcolorspace_nosubst(i_ctx_p);
+ if (code != 0)
+ return code;
+ break;
+ case 3:
+ /* We end up here after setting the DefaultGray space
+ * We've finished setting the gray color space, so we
+ * just exit now
+ */
+ *cont = 0;
+ *stage = 0;
+ break;
+ case 4:
+ /* We come here if /UseCIEColor is true, and NOSUBSTDEVICECOLORS
+ * is also true. We will have a boolean on the stack, if its true
+ * then we need to set the space (also on the stack), invoke
+ * .includecolorspace, and set /DeviceGray, otherwise we just need
+ * to set DeviceGray. See gs_cspace.ps.
+ */
+ if (!r_has_type(op, t_boolean))
+ return_error(gs_error_typecheck);
+ pop(1);
+ *stage = 1;
+ *cont = 1;
+ if (op->value.boolval) {
+ *stage = 5;
+ code = setcolorspace_nosubst(i_ctx_p);
+ if (code != 0)
+ return code;
+ }
+ break;
+ case 5:
+ /* After stage 4 above, if we had to set a color space, we come
+ * here. Now we need to use .includecolorspace to register the space
+ * with any high-level devices which want it.
+ */
+ *stage = 1;
+ *cont = 1;
+ code = zincludecolorspace(i_ctx_p);
+ if (code != 0)
+ return code;
+ break;
+ }
+ } while (*stage);
+ return code;
+}
+static int graydomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ ptr[0] = 0;
+ ptr[1] = 1;
+ return 0;
+}
+static int grayrange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ ptr[0] = 0;
+ ptr[1] = 1;
+ return 0;
+}
+/* This routine converts a Gray value into its equivalent in a different
+ * device space, required by currentgray, currentrgb, currenthsb and
+ * currentcmyk. The actual color value will have been processed through
+ * the tint transform(s) of the parent space(s) until it reaches a device
+ * space. This converts that final value into the requested space.
+ */
+static int graybasecolor(i_ctx_t * i_ctx_p, ref *space, int base, int *stage, int *cont, int *stack_depth)
+{
+ os_ptr op = osp;
+ float Gray, RGB[3];
+
+ *cont = 0;
+ *stage = 0;
+ check_op(1);
+ if (!r_has_type(op, t_integer)) {
+ if (r_has_type(op, t_real)) {
+ Gray = op->value.realval;
+ } else
+ return_error(gs_error_typecheck);
+ } else
+ Gray = (float)op->value.intval;
+
+ if (Gray < 0 || Gray > 1)
+ return_error(gs_error_rangecheck);
+
+ switch (base) {
+ case 0:
+ /* Requested space is DeviceGray, just use the value */
+ make_real(op, Gray);
+ break;
+ case 1:
+ /* Requested space is HSB */
+ case 2:
+ /* Requested space is RGB, set all the components
+ * to the gray value
+ */
+ push(2);
+ RGB[0] = RGB[1] = RGB[2] = Gray;
+ if (base == 1)
+ /* If the requested space is HSB, convert the RGB to HSB */
+ rgb2hsb((float *)&RGB);
+ make_real(&op[-2], RGB[0]);
+ make_real(&op[-1], RGB[1]);
+ make_real(op, RGB[2]);
+ break;
+ case 3:
+ /* Requested space is CMYK, use the gray value to set the
+ * black channel.
+ */
+ push(3);
+ make_real(&op[-3], (float)0);
+ make_real(&op[-2], (float)0);
+ make_real(&op[-1], (float)0);
+ make_real(op, (float)1.0 - Gray);
+ break;
+ default:
+ return_error(gs_error_undefined);
+ }
+ return 0;
+}
+static int grayvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ os_ptr op = osp;
+
+ if (!r_has_type(op, t_integer) && !r_has_type(op, t_real))
+ return_error(gs_error_typecheck);
+
+ if (num_comps < 1)
+ return_error(gs_error_stackunderflow);
+
+ if (*values > 1.0)
+ *values = 1.0;
+
+ if ( *values < 0.0)
+ *values = 0.0;
+
+ return 0;
+}
+static int grayinitialproc(i_ctx_t *i_ctx_p, ref *space)
+{
+ gs_client_color cc;
+
+ cc.pattern = 0x00;
+ cc.paint.values[0] = 0;
+ return gs_setcolor(igs, &cc);
+}
+
+/* DeviceRGB */
+static int setrgbspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ os_ptr op = osp;
+ gs_color_space *pcs;
+ int code=0;
+ ref stref;
+
+ do {
+ switch (*stage) {
+ case 0:
+ if (istate->use_cie_color.value.boolval && !CIESubst) {
+ byte *body;
+ ref *nosubst;
+
+ code = dict_find_string(systemdict, "NOSUBSTDEVICECOLORS", &nosubst);
+ if (code != 0) {
+ if (!r_has_type(nosubst, t_boolean))
+ return_error(gs_error_typecheck);
+ }
+ if (code != 0 && nosubst->value.boolval) {
+ *stage = 4;
+ *cont = 1;
+ body = ialloc_string(31, "string");
+ if (body == 0)
+ return_error(gs_error_VMerror);
+ memcpy(body, "/DefaultRGB ..nosubstdevicetest",31);
+ make_string(&stref, a_all | icurrent_space, 31, body);
+ r_set_attrs(&stref, a_executable);
+ esp++;
+ ref_assign(esp, &stref);
+ return o_push_estack;
+ } else {
+ *stage = 2;
+ *cont = 1;
+ body = ialloc_string(46, "string");
+ if (body == 0)
+ return_error(gs_error_VMerror);
+ memcpy(body, "{/DefaultRGB /ColorSpace findresource} stopped", 46);
+ make_string(&stref, a_all | icurrent_space, 46, body);
+ r_set_attrs(&stref, a_executable);
+ esp++;
+ ref_assign(esp, &stref);
+ return o_push_estack;
+ }
+ }
+ /* fall through */
+ case 1:
+ pcs = gs_cspace_new_DeviceRGB(imemory);
+ if (pcs == NULL)
+ return_error(gs_error_VMerror);
+ code = gs_setcolorspace(igs, pcs);
+ if (code >= 0) {
+ gs_client_color *pcc = gs_currentcolor_inline(igs);
+
+ cs_adjust_color_count(igs, -1); /* not strictly necessary */
+ pcc->paint.values[0] = 0;
+ pcc->paint.values[1] = 0;
+ pcc->paint.values[2] = 0;
+ pcc->pattern = 0; /* for GC */
+ gx_unset_dev_color(igs);
+ }
+ rc_decrement_only_cs(pcs, "zsetdevcspace");
+ *cont = 0;
+ *stage = 0;
+ break;
+ case 2:
+ if (!r_has_type(op, t_boolean))
+ return_error(gs_error_typecheck);
+ if (op->value.boolval) {
+ /* Failed to find the /DefaultRGB CSA, so give up and
+ * just use DeviceRGB
+ */
+ pop(1);
+ *stage = 1;
+ break;
+ }
+ pop(1);
+ *stage = 3;
+ code = setcolorspace_nosubst(i_ctx_p);
+ if (code != 0)
+ return code;
+ break;
+ case 3:
+ /* We end up here after setting the DefaultGray CIE space
+ * We've finished setting the gray color space, so we
+ * just exit now
+ */
+ *cont = 0;
+ *stage = 0;
+ break;
+ case 4:
+ /* We come here if /UseCIEColor is true, and NOSUBSTDEVICECOLORS
+ * is also true. We will have a boolean on the stack, if its true
+ * then we need to set the space (also on the stack), invoke
+ * .includecolorspace, and set /DeviceGray, otherwise we just need
+ * to set DeviceGray. See gs-cspace.ps.
+ */
+ if (!r_has_type(op, t_boolean))
+ return_error(gs_error_typecheck);
+ pop(1);
+ *stage = 1;
+ *cont = 1;
+ if (op->value.boolval) {
+ *stage = 5;
+ code = setcolorspace_nosubst(i_ctx_p);
+ if (code != 0)
+ return code;
+ }
+ break;
+ case 5:
+ /* After stage 4 above, if we had to set a color space, we come
+ * here. Now we need to use .includecolorspace to register the space
+ * with any high-level devices which want it.
+ */
+ *stage = 1;
+ *cont = 1;
+ code = zincludecolorspace(i_ctx_p);
+ if (code != 0)
+ return code;
+ break;
+ }
+ } while (*stage);
+ return code;
+}
+static int rgbdomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int i;
+
+ for (i = 0;i < 6;i+=2) {
+ ptr[i] = 0;
+ ptr[i+1] = 1;
+ }
+ return 0;
+}
+static int rgbrange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int i;
+
+ for (i = 0;i < 6;i+=2) {
+ ptr[i] = 0;
+ ptr[i+1] = 1;
+ }
+ return 0;
+}
+/* This routine converts an RGB value into its equivalent in a different
+ * device space, required by currentgray, currentrgb, currenthsb and
+ * currentcmyk. The actual color value will have been processed through
+ * the tint transform(s) of the parent space(s) until it reaches a device
+ * space. This converts that final value into the requested space.
+ */
+static int rgbbasecolor(i_ctx_t * i_ctx_p, ref *space, int base, int *stage, int *cont, int *stack_depth)
+{
+ os_ptr op = osp;
+ float RGB[3], CMYK[4], Gray, UCR, BG;
+ int i;
+ const gs_color_space * pcs = gs_currentcolorspace(igs);
+
+ if (pcs->id == cs_DeviceGray_id) {
+ /* UGLY hack. Its possible for the graphics library to change the
+ * colour space to DeviceGray (setcachedevice), but this does not
+ * change the PostScript space. It can't, because the graphics library
+ * doesn't know about the PostScript objects. If we get a current*
+ * operation before the space has been restored, the colour space in
+ * the graphics library and the PostScript stored space won't match.
+ * If that happens then we need to pretend the PS colour space was
+ * DeviceGray
+ */
+ return(graybasecolor(i_ctx_p, space, base, stage, cont, stack_depth));
+ }
+
+ switch (*stage) {
+ case 0:
+ *cont = 0;
+ check_op(3);
+ op -= 2;
+ for (i=0;i<3;i++) {
+ if (!r_has_type(op, t_integer)) {
+ if (r_has_type(op, t_real)) {
+ RGB[i] = op->value.realval;
+ } else
+ return_error(gs_error_typecheck);
+ } else
+ RGB[i] = (float)op->value.intval;
+ if (RGB[i] < 0 || RGB[i] > 1)
+ return_error(gs_error_rangecheck);
+ op++;
+ }
+ op = osp;
+
+ switch (base) {
+ case 0:
+ pop(2);
+ op = osp;
+ /* If R == G == B, then this is gray, so just use it. Avoids
+ * rounding errors.
+ */
+ if (RGB[0] == RGB[1] && RGB[1] == RGB[2])
+ Gray = RGB[0];
+ else
+ Gray = (0.3 * RGB[0]) + (0.59 * RGB[1]) + (0.11 * RGB[2]);
+ make_real(op, Gray);
+ return 0;
+ break;
+ case 1:
+ rgb2hsb((float *)&RGB);
+ make_real(&op[-2], RGB[0]);
+ make_real(&op[-1], RGB[1]);
+ make_real(op, RGB[2]);
+ return 0;
+ break;
+ case 2:
+ make_real(&op[-2], RGB[0]);
+ make_real(&op[-1], RGB[1]);
+ make_real(op, RGB[2]);
+ return 0;
+ break;
+ case 3:
+ *stage = 1;
+ *cont = 1;
+ for (i=0;i<3;i++)
+ CMYK[i] = 1 - RGB[i];
+ if (CMYK[0] < CMYK[1]) {
+ if (CMYK[0] < CMYK[2])
+ CMYK[3] = CMYK[0];
+ else
+ CMYK[3] = CMYK[2];
+ } else {
+ if (CMYK[1] < CMYK[2])
+ CMYK[3] = CMYK[1];
+ else
+ CMYK[3] = CMYK[2];
+ }
+ check_estack(1);
+ push(2);
+ op = osp - 4;
+ for (i=0;i<4;i++) {
+ make_real(op, CMYK[i]);
+ op++;
+ }
+ make_real(op, CMYK[3]);
+ esp++;
+ *esp = istate->undercolor_removal;
+ return o_push_estack;
+ break;
+ default:
+ return_error(gs_error_undefined);
+ break;
+ }
+ break;
+ case 1:
+ (*stage)++;
+ *cont = 1;
+ check_estack(1);
+ check_op(5);
+ op -= 4;
+ for (i=0;i<4;i++) {
+ if (!r_has_type(op, t_integer)) {
+ if (r_has_type(op, t_real)) {
+ CMYK[i] = op->value.realval;
+ } else
+ return_error(gs_error_typecheck);
+ } else
+ CMYK[i] = (float)op->value.intval;
+ op++;
+ }
+ if (!r_has_type(op, t_integer)) {
+ if (r_has_type(op, t_real)) {
+ UCR = op->value.realval;
+ } else
+ return_error(gs_error_typecheck);
+ } else
+ UCR = (float)op->value.intval;
+ for (i=0;i<3;i++) {
+ CMYK[i] = CMYK[i] - UCR;
+ if (CMYK[i] < 0)
+ CMYK[i] = 0;
+ if (CMYK[i] > 1)
+ CMYK[i] = 1.0;
+ }
+ op -= 4;
+ for (i=0;i<4;i++) {
+ make_real(op, CMYK[i]);
+ op++;
+ }
+ make_real(op, CMYK[3]);
+ esp++;
+ *esp = istate->black_generation;
+ return o_push_estack;
+ break;
+ case 2:
+ *stage = 0;
+ *cont = 0;
+ check_op(5);
+ if (!r_has_type(op, t_integer)) {
+ if (r_has_type(op, t_real)) {
+ BG = op->value.realval;
+ } else
+ return_error(gs_error_typecheck);
+ } else
+ BG = (float)op->value.intval;
+ pop(1);
+ op = osp;
+ if (BG < 0)
+ BG = 0;
+ if (BG > 1)
+ BG = 1;
+ make_real(op, BG);
+ break;
+ }
+ return 0;
+}
+static int rgbvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ os_ptr op = osp;
+ int i;
+
+ if (num_comps < 3)
+ return_error(gs_error_stackunderflow);
+
+ op -= 2;
+ for (i=0;i<3;i++) {
+ if (!r_has_type(op, t_integer) && !r_has_type(op, t_real))
+ return_error(gs_error_typecheck);
+ op++;
+ }
+
+ for (i=0;i < 3; i++) {
+ if (values[i] > 1.0)
+ values[i] = 1.0;
+
+ if (values[i] < 0.0)
+ values[i] = 0.0;
+ }
+
+ return 0;
+}
+static int rgbinitialproc(i_ctx_t *i_ctx_p, ref *space)
+{
+ gs_client_color cc;
+
+ cc.pattern = 0x00;
+ cc.paint.values[0] = 0;
+ cc.paint.values[1] = 0;
+ cc.paint.values[2] = 0;
+ return gs_setcolor(igs, &cc);
+}
+
+/* DeviceCMYK */
+static int setcmykspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ os_ptr op = osp;
+ gs_color_space *pcs;
+ int code=0;
+ ref stref;
+
+ do {
+ switch (*stage) {
+ case 0:
+ if (istate->use_cie_color.value.boolval && !CIESubst) {
+ byte *body;
+ ref *nosubst;
+
+ code = dict_find_string(systemdict, "NOSUBSTDEVICECOLORS", &nosubst);
+ if (code != 0) {
+ if (!r_has_type(nosubst, t_boolean))
+ return_error(gs_error_typecheck);
+ }
+ if (code != 0 && nosubst->value.boolval) {
+ *stage = 4;
+ *cont = 1;
+ body = ialloc_string(32, "string");
+ if (body == 0)
+ return_error(gs_error_VMerror);
+ memcpy(body, "/DefaultCMYK ..nosubstdevicetest",32);
+ make_string(&stref, a_all | icurrent_space, 32, body);
+ r_set_attrs(&stref, a_executable);
+ esp++;
+ ref_assign(esp, &stref);
+ return o_push_estack;
+ } else {
+ *stage = 2;
+ *cont = 1;
+ body = ialloc_string(47, "string");
+ if (body == 0)
+ return_error(gs_error_VMerror);
+ memcpy(body, "{/DefaultCMYK /ColorSpace findresource} stopped", 47);
+ make_string(&stref, a_all | icurrent_space, 47, body);
+ r_set_attrs(&stref, a_executable);
+ esp++;
+ ref_assign(esp, &stref);
+ return o_push_estack;
+ }
+ }
+ /* fall through */
+ case 1:
+ pcs = gs_cspace_new_DeviceCMYK(imemory);
+ if (pcs == NULL)
+ return_error(gs_error_VMerror);
+ code = gs_setcolorspace(igs, pcs);
+ if (code >= 0) {
+ gs_client_color *pcc = gs_currentcolor_inline(igs);
+
+ cs_adjust_color_count(igs, -1); /* not strictly necessary */
+ pcc->paint.values[0] = 0;
+ pcc->paint.values[1] = 0;
+ pcc->paint.values[2] = 0;
+ pcc->paint.values[3] = 1;
+ pcc->pattern = 0; /* for GC */
+ gx_unset_dev_color(igs);
+ }
+ rc_decrement_only_cs(pcs, "zsetdevcspace");
+ *cont = 0;
+ *stage = 0;
+ break;
+ case 2:
+ if (!r_has_type(op, t_boolean))
+ return_error(gs_error_typecheck);
+ if (op->value.boolval) {
+ /* Failed to find the /DefaultCMYK CSA, so give up and
+ * just use DeviceCMYK
+ */
+ pop(1);
+ *stage = 1;
+ break;
+ }
+ pop(1);
+ *stage = 3;
+ code = setcolorspace_nosubst(i_ctx_p);
+ if (code != 0)
+ return code;
+ break;
+ case 3:
+ /* We end up here after setting the DefaultGray CIE space
+ * We've finished setting the gray color space, so we
+ * just exit now
+ */
+ *cont = 0;
+ *stage = 0;
+ break;
+ case 4:
+ /* We come here if /UseCIEColor is true, and NOSUBSTDEVICECOLORS
+ * is also true. We will have a boolean on the stack, if its true
+ * then we need to set the space (also on the stack), invoke
+ * .includecolorspace, and set /DeviceGray, otherwise we just need
+ * to set DeviceGray. See gs-cspace.ps.
+ */
+ if (!r_has_type(op, t_boolean))
+ return_error(gs_error_typecheck);
+ pop(1);
+ *stage = 1;
+ *cont = 1;
+ if (op->value.boolval) {
+ *stage = 5;
+ code = setcolorspace_nosubst(i_ctx_p);
+ if (code != 0)
+ return code;
+ }
+ break;
+ case 5:
+ /* After stage 4 above, if we had to set a color space, we come
+ * here. Now we need to use .includecolorspace to register the space
+ * with any high-level devices which want it.
+ */
+ *stage = 1;
+ *cont = 1;
+ code = zincludecolorspace(i_ctx_p);
+ if (code != 0)
+ return code;
+ break;
+ }
+ } while (*stage);
+ return code;
+}
+static int cmykdomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int i;
+
+ for (i = 0;i < 8;i+=2) {
+ ptr[i] = 0;
+ ptr[i+1] = 1;
+ }
+ return 0;
+}
+static int cmykrange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int i;
+
+ for (i = 0;i < 8;i+=2) {
+ ptr[i] = 0;
+ ptr[i+1] = 1;
+ }
+ return 0;
+}
+/* This routine converts a CMYK value into its equivalent in a different
+ * device space, required by currentgray, currentrgb, currenthsb and
+ * currentcmyk. The actual color value will have been processed through
+ * the tint transform(s) of the parent space(s) until it reaches a device
+ * space. This converts that final value into the requested space.
+ */
+static int cmykbasecolor(i_ctx_t * i_ctx_p, ref *space, int base, int *stage, int *cont, int *stack_depth)
+{
+ os_ptr op = osp;
+ float CMYK[4], Gray, RGB[3];
+ int i;
+ const gs_color_space * pcs = gs_currentcolorspace(igs);
+
+ if (pcs->id == cs_DeviceGray_id) {
+ /* UGLY hack. Its possible for the graphics library to change the
+ * colour space to DeviceGray (setcachedevice), but this does not
+ * change the PostScript space. It can't, because the graphics library
+ * doesn't know about the PostScript objects. If we get a current*
+ * operation before the space has been restored, the colour space in
+ * the graphics library and the PostScript stored space won't match.
+ * If that happens then we need to pretend the PS colour space was
+ * DeviceGray
+ */
+ return(graybasecolor(i_ctx_p, space, base, stage, cont, stack_depth));
+ }
+
+ *cont = 0;
+ *stage = 0;
+ check_op(4);
+ op -= 3;
+ for (i=0;i<4;i++) {
+ if (!r_has_type(op, t_integer)) {
+ if (r_has_type(op, t_real)) {
+ CMYK[i] = op->value.realval;
+ } else
+ return_error(gs_error_typecheck);
+ } else
+ CMYK[i] = (float)op->value.intval;
+ if (CMYK[i] < 0 || CMYK[i] > 1)
+ return_error(gs_error_rangecheck);
+ op++;
+ }
+
+ switch (base) {
+ case 0:
+ pop(3);
+ op = osp;
+ Gray = (0.3 * CMYK[0]) + (0.59 * CMYK[1]) + (0.11 * CMYK[2]) + CMYK[3];
+ if (Gray > 1.0)
+ Gray = 0;
+ else
+ Gray = 1.0 - Gray;
+ make_real(op, Gray);
+ break;
+ case 1:
+ case 2:
+ pop(1);
+ op = osp;
+ RGB[0] = 1.0 - (CMYK[0] + CMYK[3]);
+ if (RGB[0] < 0)
+ RGB[0] = 0;
+ RGB[1] = 1.0 - (CMYK[1] + CMYK[3]);
+ if (RGB[1] < 0)
+ RGB[1] = 0;
+ RGB[2] = 1.0 - (CMYK[2] + CMYK[3]);
+ if (RGB[2] < 0)
+ RGB[2] = 0;
+ if (base == 1)
+ rgb2hsb((float *)&RGB);
+ make_real(&op[-2], RGB[0]);
+ make_real(&op[-1], RGB[1]);
+ make_real(op, RGB[2]);
+ break;
+ case 3:
+ op = osp;
+ make_real(&op[-3], CMYK[0]);
+ make_real(&op[-2], CMYK[1]);
+ make_real(&op[-1], CMYK[2]);
+ make_real(op, CMYK[3]);
+ break;
+ default:
+ return_error(gs_error_undefined);
+ }
+ return 0;
+}
+static int cmykvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ os_ptr op = osp;
+ int i;
+
+ if (num_comps < 4)
+ return_error(gs_error_stackunderflow);
+
+ op -= 3;
+ for (i=0;i < 4;i++) {
+ if (!r_has_type(op, t_integer) && !r_has_type(op, t_real))
+ return_error(gs_error_typecheck);
+ op++;
+ }
+
+ for (i=0;i < 4; i++) {
+ if (values[i] > 1.0)
+ values[i] = 1.0;
+
+ if (values[i] < 0.0)
+ values[i] = 0.0;
+ }
+
+ return 0;
+}
+static int cmykinitialproc(i_ctx_t *i_ctx_p, ref *space)
+{
+ gs_client_color cc;
+
+ cc.pattern = 0x00;
+ cc.paint.values[0] = 0;
+ cc.paint.values[1] = 0;
+ cc.paint.values[2] = 0;
+ cc.paint.values[3] = 1;
+ return gs_setcolor(igs, &cc);
+}
+
+/* CIEBased */
+/* A utility routine to check whether two arrays contain the same
+ * contents. Used to check whether two color spaces are the same
+ * Note that this can be recursive if the array contains arrays.
+ */
+static int comparearrays(i_ctx_t * i_ctx_p, ref *m1, ref *m2)
+{
+ int i, code;
+ ref ref1, ref2;
+
+ if (r_size(m1) != r_size(m2))
+ return 0;
+
+ for (i=0;i < r_size(m1);i++) {
+ code = array_get(imemory, m1, i, &ref1);
+ if (code < 0)
+ return 0;
+ code = array_get(imemory, m2, i, &ref2);
+ if (code < 0)
+ return 0;
+
+ if (r_type(&ref1) != r_type(&ref2))
+ return 0;
+
+ code = r_type(&ref1);
+ switch(r_type(&ref1)) {
+ case t_null:
+ break;
+ case t_boolean:
+ if (ref1.value.boolval != ref2.value.boolval)
+ return 0;
+ break;
+ case t_integer:
+ if (ref1.value.intval != ref2.value.intval)
+ return 0;
+ break;
+ case t_real:
+ if (ref1.value.realval != ref2.value.realval)
+ return 0;
+ break;
+ case t_name:
+ if (!name_eq(&ref1, &ref2))
+ return 0;
+ break;
+ case t_string:
+ if (r_size(&ref1) != r_size(&ref2))
+ return 0;
+ if (strncmp((const char *)ref1.value.const_bytes, (const char *)ref2.value.const_bytes, r_size(&ref1)) != 0)
+ return 0;
+ break;
+ case t_array:
+ case t_mixedarray:
+ case t_shortarray:
+ if (!comparearrays(i_ctx_p, &ref1, &ref2))
+ return 0;
+ break;
+ case t_oparray:
+ break;
+ case t_operator:
+ if (ref1.value.opproc != ref2.value.opproc)
+ return 0;
+ break;
+ case t__invalid:
+ case t_dictionary:
+ case t_file:
+ case t_unused_array_:
+ case t_struct:
+ case t_astruct:
+ case t_fontID:
+ case t_save:
+ case t_mark:
+ case t_device:
+ return 0;
+ default:
+ /* Some high frequency operators are defined starting at t_next_index
+ * I think as long as the 'type' of each is the same, we are OK
+ */
+ break;
+ }
+ }
+ return 1;
+}
+/* A utility routine to check whether two dictionaries contain the same
+ * arrays. This is a simple routine, unlike comparearrays above it is only
+ * used by the CIE comparison code and expects only to check that the
+ * dictionary contains an array, and checks the arrays.
+ */
+static int comparedictkey(i_ctx_t * i_ctx_p, ref *CIEdict1, ref *CIEdict2, char *key)
+{
+ int code, code1;
+ ref *tempref1, *tempref2;
+
+ code = dict_find_string(CIEdict1, key, &tempref1);
+ code1 = dict_find_string(CIEdict2, key, &tempref2);
+ if (code != code1)
+ return 0;
+
+ if (code <= 0)
+ return 1;
+
+ if (r_type(tempref1) != r_type(tempref2))
+ return 0;
+
+ if (r_type(tempref1) == t_null)
+ return 1;
+
+ return comparearrays(i_ctx_p, tempref1, tempref2);
+}
+
+static int get_cie_param_array(const gs_memory_t *mem, const ref *src, int n, float *dst) {
+ ref valref;
+ int i;
+ int code = 0;
+
+ for (i = 0; i < n; i++) {
+ code = array_get(mem, src, i, &valref);
+ if (code < 0)
+ return code;
+ if (r_has_type(&valref, t_integer))
+ dst[i] = (float)valref.value.intval;
+ else if (r_has_type(&valref, t_real))
+ dst[i] = (float)valref.value.realval;
+ else
+ return_error(gs_error_typecheck);
+ if (dst[i] < -MAX_CIE_RANGE || dst[i] > MAX_CIE_RANGE)
+ return_error(gs_error_limitcheck);
+ }
+ return code;
+}
+
+/* Check that the WhitePoint of a CIE space is valid */
+static int checkWhitePoint(i_ctx_t * i_ctx_p, ref *CIEdict)
+{
+ int code;
+ float value[3];
+ ref *tempref;
+
+ code = dict_find_string(CIEdict, "WhitePoint", &tempref);
+ if (code < 0)
+ return code;
+
+ if (code == 0 || r_has_type(tempref, t_null))
+ return gs_note_error(gs_error_undefined);
+
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != 3)
+ return_error(gs_error_rangecheck);
+
+ code = get_cie_param_array(imemory, tempref, 3, value);
+ if (code < 0)
+ return code;
+
+ /* Xw and Zw must be positive and Yw must be 1 (3rd edition PLRM p230) */
+ if (value[0] < 0 || value[1] != 1 || value[2] < 0 )
+ return_error(gs_error_rangecheck);
+
+ return 0;
+}
+/* Check that the BlackPoint of a CIE space is valid */
+static int checkBlackPoint(i_ctx_t * i_ctx_p, ref *CIEdict)
+{
+ int code;
+ float value[3];
+ ref *tempref;
+
+ code = dict_find_string(CIEdict, "BlackPoint", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != 3)
+ return_error(gs_error_rangecheck);
+
+ code = get_cie_param_array(imemory, tempref, 3, value);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+/* Check that the RangeLMN of a CIE space is valid */
+static int checkRangeLMN(i_ctx_t * i_ctx_p, ref *CIEdict)
+{
+ int code;
+ float value[6];
+ ref *tempref;
+
+ code = dict_find_string(CIEdict, "RangeLMN", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != 6)
+ return_error(gs_error_rangecheck);
+ code = get_cie_param_array(imemory, tempref, 6, value);
+ if (code < 0)
+ return code;
+ if (value[1] < value[0] || value[3] < value[2] || value[5] < value[4])
+ return_error(gs_error_rangecheck);
+ }
+ return 0;
+}
+/* Check that the DecodeLMN of a CIE space is valid */
+static int checkDecodeLMN(i_ctx_t * i_ctx_p, ref *CIEdict)
+{
+ int code = 0, i;
+ ref *tempref, valref;
+
+ code = dict_find_string(CIEdict, "DecodeLMN", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != 3)
+ return_error(gs_error_rangecheck);
+
+ for (i=0;i<3;i++) {
+ code = array_get(imemory, tempref, i, &valref);
+ if (code < 0)
+ return code;
+ check_proc(valref);
+ }
+ }
+ return 0;
+}
+/* Check that the MatrixLMN of a CIE space is valid */
+static int checkMatrixLMN(i_ctx_t * i_ctx_p, ref *CIEdict)
+{
+ int code;
+ float value[9];
+ ref *tempref;
+
+ code = dict_find_string(CIEdict, "MatrixLMN", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != 9)
+ return_error(gs_error_rangecheck);
+ code = get_cie_param_array(imemory, tempref, 9, value);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* CIEBasedA */
+static int setcieaspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ int code = 0;
+ ref CIEDict, *nocie;
+ ulong dictkey;
+
+ if (i_ctx_p->language_level < 2)
+ return_error(gs_error_undefined);
+
+ code = dict_find_string(systemdict, "NOCIE", &nocie);
+ if (code > 0) {
+ if (!r_has_type(nocie, t_boolean))
+ return_error(gs_error_typecheck);
+ if (nocie->value.boolval)
+ return setgrayspace(i_ctx_p, r, stage, cont, 1);
+ }
+
+ *cont = 0;
+ code = array_get(imemory, r, 1, &CIEDict);
+ if (code < 0)
+ return code;
+ if ((*stage) > 0) {
+ gs_client_color cc;
+
+ cc.pattern = 0x00;
+ cc.paint.values[0] = 0;
+ code = gs_setcolor(igs, &cc);
+ *stage = 0;
+ return code;
+ }
+ dictkey = r->value.refs->value.saveid;
+ code = cieaspace(i_ctx_p, &CIEDict, dictkey);
+ (*stage)++;
+ *cont = 1;
+ return code;
+}
+static int validatecieaspace(i_ctx_t * i_ctx_p, ref **r)
+{
+ int code;
+ float value[9];
+ ref CIEdict, *CIEspace = *r, *tempref;
+
+ if (!r_is_array(CIEspace))
+ return_error(gs_error_typecheck);
+ /* Validate parameters, check we have enough operands */
+ if (r_size(CIEspace) != 2)
+ return_error(gs_error_rangecheck);
+
+ code = array_get(imemory, CIEspace, 1, &CIEdict);
+ if (code < 0)
+ return code;
+
+ check_read_type(CIEdict, t_dictionary);
+
+ /* Check white point exists, and is an array of three numbers */
+ code = checkWhitePoint(i_ctx_p, &CIEdict);
+ if (code != 0)
+ return code;
+
+ /* Remaining parameters are optional, but we must validate
+ * them if they are present
+ */
+ code = dict_find_string(&CIEdict, "RangeA", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ /* Array of two numbers A0 < A1 */
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != 2)
+ return_error(gs_error_rangecheck);
+
+ code = get_cie_param_array(imemory, tempref, 2, value);
+ if (code < 0)
+ return code;
+ if (value[1] < value[0])
+ return_error(gs_error_rangecheck);
+ }
+
+ code = dict_find_string(&CIEdict, "DecodeA", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ check_proc(*tempref);
+ }
+
+ code = dict_find_string(&CIEdict, "MatrixA", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != 3)
+ return_error(gs_error_rangecheck);
+ code = get_cie_param_array(imemory, tempref, 3, value);
+ if (code < 0)
+ return code;
+ }
+
+ code = checkRangeLMN(i_ctx_p, &CIEdict);
+ if (code != 0)
+ return code;
+
+ code = checkDecodeLMN(i_ctx_p, &CIEdict);
+ if (code != 0)
+ return code;
+
+ code = checkMatrixLMN(i_ctx_p, &CIEdict);
+ if (code != 0)
+ return code;
+
+ code = checkBlackPoint(i_ctx_p, &CIEdict);
+ if (code != 0)
+ return code;
+
+ *r = 0;
+ return 0;
+}
+static int cieadomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int code;
+ ref CIEdict, *tempref;
+
+ code = array_get(imemory, space, 1, &CIEdict);
+ if (code < 0)
+ return code;
+
+ /* If we have a RangeA entry in the dictionary, get the
+ * values from that
+ */
+ code = dict_find_string(&CIEdict, "RangeA", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ code = get_cie_param_array(imemory, tempref, 2, ptr);
+ if (code < 0)
+ return code;
+ } else {
+ /* Default values for CIEBasedA */
+ ptr[0] = 0;
+ ptr[1] = 1;
+ }
+ return 0;
+}
+static int ciearange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int code;
+ ref CIEdict, *tempref;
+
+ code = array_get(imemory, space, 1, &CIEdict);
+ if (code < 0)
+ return code;
+
+ /* If we have a RangeA entry in the dictionary, get the
+ * values from that
+ */
+ code = dict_find_string(&CIEdict, "RangeA", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ code = get_cie_param_array(imemory, tempref, 2, ptr);
+ if (code < 0)
+ return code;
+ } else {
+ /* Default values for CIEBasedA */
+ ptr[0] = 0;
+ ptr[1] = 1;
+ }
+ return 0;
+}
+static int cieavalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ os_ptr op = osp;
+
+ if (num_comps < 1)
+ return_error(gs_error_stackunderflow);
+
+ if (!r_has_type(op, t_integer) && !r_has_type(op, t_real))
+ return_error(gs_error_typecheck);
+
+ return 0;
+}
+static int cieacompareproc(i_ctx_t *i_ctx_p, ref *space, ref *testspace)
+{
+ int code = 0;
+ ref CIEdict1, CIEdict2;
+
+ code = array_get(imemory, space, 1, &CIEdict1);
+ if (code < 0)
+ return 0;
+ code = array_get(imemory, testspace, 1, &CIEdict2);
+ if (code < 0)
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"WhitePoint"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"BlackPoint"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"RangeA"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"DecodeA"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"MatrixA"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"RangeLMN"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"DecodeLMN"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"MatrixMN"))
+ return 0;
+ return 1;
+}
+
+/* CIEBasedABC */
+static int setcieabcspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ int code = 0;
+ ref CIEDict, *nocie;
+ ulong dictkey;
+
+ if (i_ctx_p->language_level < 2)
+ return_error(gs_error_undefined);
+
+ code = dict_find_string(systemdict, "NOCIE", &nocie);
+ if (code > 0) {
+ if (!r_has_type(nocie, t_boolean))
+ return_error(gs_error_typecheck);
+ if (nocie->value.boolval)
+ return setrgbspace(i_ctx_p, r, stage, cont, 1);
+ }
+
+ *cont = 0;
+ code = array_get(imemory, r, 1, &CIEDict);
+ if (code < 0)
+ return code;
+
+ if ((*stage) > 0) {
+ gs_client_color cc;
+ int i;
+
+ cc.pattern = 0x00;
+ for (i=0;i<3;i++)
+ cc.paint.values[i] = 0;
+ code = gs_setcolor(igs, &cc);
+ *stage = 0;
+ return code;
+ }
+ dictkey = r->value.refs->value.saveid;
+ code = cieabcspace(i_ctx_p, &CIEDict,dictkey);
+ *cont = 1;
+ (*stage)++;
+ return code;
+}
+static int validatecieabcspace(i_ctx_t * i_ctx_p, ref **r)
+{
+ int code = 0, i;
+ float value[9];
+ ref CIEdict, *CIEspace = *r, *tempref, valref;
+
+ if (!r_is_array(CIEspace))
+ return_error(gs_error_typecheck);
+ /* Validate parameters, check we have enough operands */
+ if (r_size(CIEspace) != 2)
+ return_error(gs_error_rangecheck);
+
+ code = array_get(imemory, CIEspace, 1, &CIEdict);
+ if (code < 0)
+ return code;
+ check_read_type(CIEdict, t_dictionary);
+
+ /* Check white point exists, and is an array of three numbers */
+ code = checkWhitePoint(i_ctx_p, &CIEdict);
+ if (code != 0)
+ return code;
+
+ /* Remaining parameters are optional, but we must validate
+ * them if they are present
+ */
+ code = dict_find_string(&CIEdict, "RangeABC", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != 6)
+ return_error(gs_error_rangecheck);
+ code = get_cie_param_array(imemory, tempref, 6, value);
+ if (code < 0)
+ return code;
+ if (value[1] < value[0] || value[3] < value[2] || value[5] < value[4])
+ return_error(gs_error_rangecheck);
+ }
+
+ code = dict_find_string(&CIEdict, "DecodeABC", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != 3)
+ return_error(gs_error_rangecheck);
+
+ for (i=0;i<3;i++) {
+ code = array_get(imemory, tempref, i, &valref);
+ if (code < 0)
+ return code;
+ check_proc(valref);
+ }
+ }
+
+ code = dict_find_string(&CIEdict, "MatrixABC", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != 9)
+ return_error(gs_error_rangecheck);
+ code = get_cie_param_array(imemory, tempref, 9, value);
+ if (code < 0)
+ return code;
+ }
+
+ code = checkRangeLMN(i_ctx_p, &CIEdict);
+ if (code != 0)
+ return code;
+
+ code = checkDecodeLMN(i_ctx_p, &CIEdict);
+ if (code != 0)
+ return code;
+
+ code = checkMatrixLMN(i_ctx_p, &CIEdict);
+ if (code != 0)
+ return code;
+
+ code = checkBlackPoint(i_ctx_p, &CIEdict);
+ if (code != 0)
+ return code;
+
+ *r = 0;
+ return 0;
+}
+static int cieabcdomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int code;
+ ref CIEdict, *tempref;
+
+ code = array_get(imemory, space, 1, &CIEdict);
+ if (code < 0)
+ return code;
+
+ /* If we have a RangeABC, get the values from that */
+ code = dict_find_string(&CIEdict, "RangeABC", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ code = get_cie_param_array(imemory, tempref, 6, ptr);
+ if (code < 0)
+ return code;
+ } else {
+ /* Default values for CIEBasedABC */
+ memcpy(ptr, default_0_1, 6*sizeof(float));
+ }
+ return 0;
+}
+static int cieabcrange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int code;
+ ref CIEdict, *tempref;
+
+ code = array_get(imemory, space, 1, &CIEdict);
+ if (code < 0)
+ return code;
+
+ /* If we have a RangeABC, get the values from that */
+ code = dict_find_string(&CIEdict, "RangeABC", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ code = get_cie_param_array(imemory, tempref, 6, ptr);
+ if (code < 0)
+ return code;
+ } else {
+ /* Default values for CIEBasedABC */
+ memcpy(ptr, default_0_1, 6*sizeof(float));
+ }
+ return 0;
+}
+static int cieabcvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ os_ptr op = osp;
+ int i;
+
+ if (num_comps < 3)
+ return_error(gs_error_stackunderflow);
+
+ op -= 2;
+ for (i=0;i<3;i++) {
+ if (!r_has_type(op, t_integer) && !r_has_type(op, t_real))
+ return_error(gs_error_typecheck);
+ op++;
+ }
+
+ return 0;
+}
+static int cieabccompareproc(i_ctx_t *i_ctx_p, ref *space, ref *testspace)
+{
+ int code = 0;
+ ref CIEdict1, CIEdict2;
+
+ code = array_get(imemory, space, 1, &CIEdict1);
+ if (code < 0)
+ return 0;
+ code = array_get(imemory, testspace, 1, &CIEdict2);
+ if (code < 0)
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"WhitePoint"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"BlackPoint"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"RangeABC"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"DecodeABC"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"MatrixABC"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"RangeLMN"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"DecodeLMN"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"MatrixMN"))
+ return 0;
+ return 1;
+}
+
+/* CIEBasedDEF */
+static int setciedefspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ int code = 0;
+ ref CIEDict, *nocie;
+ ulong dictkey;
+
+ if (i_ctx_p->language_level < 3)
+ return_error(gs_error_undefined);
+
+ code = dict_find_string(systemdict, "NOCIE", &nocie);
+ if (code > 0) {
+ if (!r_has_type(nocie, t_boolean))
+ return_error(gs_error_typecheck);
+ if (nocie->value.boolval)
+ return setrgbspace(i_ctx_p, r, stage, cont, 1);
+ }
+
+ *cont = 0;
+ code = array_get(imemory, r, 1, &CIEDict);
+ if (code < 0)
+ return code;
+ if ((*stage) > 0) {
+ gs_client_color cc;
+ int i;
+
+ cc.pattern = 0x00;
+ for (i=0;i<3;i++)
+ cc.paint.values[i] = 0;
+ code = gs_setcolor(igs, &cc);
+ *stage = 0;
+ return code;
+ }
+ dictkey = r->value.refs->value.saveid;
+ code = ciedefspace(i_ctx_p, &CIEDict, dictkey);
+ *cont = 1;
+ (*stage)++;
+ return code;
+}
+static int validateciedefspace(i_ctx_t * i_ctx_p, ref **r)
+{
+ int code = 0, i;
+ float value[6];
+ ref CIEdict, *pref, *CIEspace = *r, tempref, valref;
+
+ if (!r_is_array(CIEspace))
+ return_error(gs_error_typecheck);
+ /* Validate parameters, check we have enough operands */
+ if (r_size(CIEspace) != 2)
+ return_error(gs_error_rangecheck);
+
+ code = array_get(imemory, CIEspace, 1, &CIEdict);
+ if (code < 0)
+ return code;
+ check_read_type(CIEdict, t_dictionary);
+
+ code = validatecieabcspace(i_ctx_p, r);
+ if (code != 0)
+ return code;
+
+ pref = &tempref;
+ code = dict_find_string(&CIEdict, "Table", &pref);
+ if (code > 0) {
+ if (!r_is_array(pref))
+ return_error(gs_error_typecheck);
+ if (r_size(pref) != 4)
+ return_error(gs_error_rangecheck);
+ code = get_cie_param_array(imemory, pref, 3, value);
+ if (code < 0)
+ return code;
+ if (value[0] <= 1 || value[1] <= 1 || value[2] <= 1)
+ return_error(gs_error_rangecheck);
+
+ code = array_get(imemory, pref, 3, &valref);
+ if (code < 0)
+ return code;
+ if (!r_is_array(&valref))
+ return_error(gs_error_typecheck);
+ if (r_size(&valref) != value[0])
+ return_error(gs_error_rangecheck);
+
+ for (i=0;i<value[0];i++) {
+ code = array_get(imemory, &valref, i, &tempref);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&tempref, t_string))
+ return_error(gs_error_typecheck);
+
+ if (r_size(&tempref) != (3 * value[1] * value[2]))
+ return_error(gs_error_rangecheck);
+ }
+ } else {
+ return_error(gs_error_rangecheck);
+ }
+
+ /* Remaining parameters are optional, but we must validate
+ * them if they are present
+ */
+ code = dict_find_string(&CIEdict, "RangeDEF", &pref);
+ if (code > 0 && !r_has_type(&tempref, t_null)) {
+ if (!r_is_array(pref))
+ return_error(gs_error_typecheck);
+ if (r_size(pref) != 6)
+ return_error(gs_error_rangecheck);
+ code = get_cie_param_array(imemory, pref, 6, value);
+ if (code < 0)
+ return code;
+ if (value[1] < value[0] || value[3] < value[2] || value[5] < value[4])
+ return_error(gs_error_rangecheck);
+ }
+
+ code = dict_find_string(&CIEdict, "DecodeDEF", &pref);
+ if (code > 0 && !r_has_type(pref, t_null)) {
+ if (!r_is_array(pref))
+ return_error(gs_error_typecheck);
+ if (r_size(pref) != 3)
+ return_error(gs_error_rangecheck);
+
+ for (i=0;i<3;i++) {
+ code = array_get(imemory, pref, i, &valref);
+ if (code < 0)
+ return code;
+ check_proc(valref);
+ }
+ }
+
+ code = dict_find_string(&CIEdict, "RangeHIJ", &pref);
+ if (code > 0 && !r_has_type(pref, t_null)) {
+ if (!r_is_array(pref))
+ return_error(gs_error_typecheck);
+ if (r_size(pref) != 6)
+ return_error(gs_error_rangecheck);
+ code = get_cie_param_array(imemory, pref, 6, value);
+ if (code < 0)
+ return code;
+ if (value[1] < value[0] || value[3] < value[2] || value[5] < value[4])
+ return_error(gs_error_rangecheck);
+ }
+
+ *r = 0;
+ return 0;
+}
+static int ciedefdomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int code;
+ ref CIEdict, *tempref;
+
+ code = array_get(imemory, space, 1, &CIEdict);
+ if (code < 0)
+ return code;
+
+ /* If we have a RangeDEF, get the values from that */
+ code = dict_find_string(&CIEdict, "RangeDEF", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ code = get_cie_param_array(imemory, tempref, 6, ptr);
+ if (code < 0)
+ return code;
+ } else {
+ /* Default values for a CIEBasedDEF */
+ memcpy(ptr, default_0_1, 6*sizeof(float));
+ }
+ return 0;
+}
+static int ciedefrange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int code;
+ ref CIEdict, *tempref;
+
+ code = array_get(imemory, space, 1, &CIEdict);
+ if (code < 0)
+ return code;
+
+ /* If we have a RangeDEF, get the values from that */
+ code = dict_find_string(&CIEdict, "RangeDEF", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ code = get_cie_param_array(imemory, tempref, 6, ptr);
+ if (code < 0)
+ return code;
+ } else {
+ /* Default values for a CIEBasedDEF */
+ memcpy(ptr, default_0_1, 6*sizeof(float));
+ }
+ return 0;
+}
+static int ciedefvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ os_ptr op = osp;
+ int i;
+
+ if (num_comps < 3)
+ return_error(gs_error_stackunderflow);
+
+ op -= 2;
+ for (i=0;i<3;i++) {
+ if (!r_has_type(op, t_integer) && !r_has_type(op, t_real))
+ return_error(gs_error_typecheck);
+ op++;
+ }
+
+ return 0;
+}
+static int ciedefcompareproc(i_ctx_t *i_ctx_p, ref *space, ref *testspace)
+{
+ int code = 0;
+ ref CIEdict1, CIEdict2;
+
+ code = array_get(imemory, space, 1, &CIEdict1);
+ if (code < 0)
+ return 0;
+ code = array_get(imemory, testspace, 1, &CIEdict2);
+ if (code < 0)
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"WhitePoint"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"BlackPoint"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"RangeABC"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"DecodeABC"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"MatrixABC"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"RangeLMN"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"DecodeLMN"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"MatrixMN"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"RangeDEF"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"DecodeDEF"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"RangeHIJ"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"Table"))
+ return 0;
+ return 1;
+}
+
+/* CIEBasedDEFG */
+static int setciedefgspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ int code = 0;
+ ref CIEDict, *nocie;
+ ulong dictkey;
+
+ if (i_ctx_p->language_level < 3)
+ return_error(gs_error_undefined);
+
+ code = dict_find_string(systemdict, "NOCIE", &nocie);
+ if (code > 0) {
+ if (!r_has_type(nocie, t_boolean))
+ return_error(gs_error_typecheck);
+ if (nocie->value.boolval)
+ return setcmykspace(i_ctx_p, r, stage, cont, 1);
+ }
+
+ *cont = 0;
+ code = array_get(imemory, r, 1, &CIEDict);
+ if (code < 0)
+ return code;
+ if ((*stage) > 0) {
+ gs_client_color cc;
+ int i;
+
+ cc.pattern = 0x00;
+ for (i=0;i<4;i++)
+ cc.paint.values[i] = 0;
+ code = gs_setcolor(igs, &cc);
+ *stage = 0;
+ return code;
+ }
+ dictkey = r->value.refs->value.saveid;
+ code = ciedefgspace(i_ctx_p, &CIEDict,dictkey);
+ *cont = 1;
+ (*stage)++;
+ return code;
+}
+static int validateciedefgspace(i_ctx_t * i_ctx_p, ref **r)
+{
+ int code = 0, i, j;
+ float value[8];
+ ref CIEdict, *CIEspace = *r, tempref, arrayref, valref, *pref = &tempref;
+
+ if (!r_is_array(CIEspace))
+ return_error(gs_error_typecheck);
+ /* Validate parameters, check we have enough operands */
+ if (r_size(CIEspace) != 2)
+ return_error(gs_error_rangecheck);
+
+ code = array_get(imemory, CIEspace, 1, &CIEdict);
+ if (code < 0)
+ return code;
+ check_read_type(CIEdict, t_dictionary);
+
+ code = validatecieabcspace(i_ctx_p, r);
+ if (code != 0)
+ return code;
+
+ code = dict_find_string(&CIEdict, "Table", &pref);
+ if (code > 0) {
+ if (!r_is_array(pref))
+ return_error(gs_error_typecheck);
+ if (r_size(pref) != 5)
+ return_error(gs_error_rangecheck);
+
+ for (i=0;i<4;i++) {
+ code = array_get(imemory, pref, i, &valref);
+ if (code < 0)
+ return code;
+ if (r_has_type(&valref, t_integer))
+ value[i] = (float)valref.value.intval;
+ else
+ return_error(gs_error_typecheck);
+ }
+ if (value[0] <= 1 || value[1] <= 1 || value[2] <= 1 || value[3] <= 1)
+ return_error(gs_error_rangecheck);
+
+ code = array_get(imemory, pref, 4, &arrayref);
+ if (code < 0)
+ return code;
+ if (!r_is_array(&arrayref))
+ return_error(gs_error_typecheck);
+ if (r_size(&arrayref) != value[0])
+ return_error(gs_error_rangecheck);
+
+ for (i=0;i<value[0];i++) {
+ code = array_get(imemory, &arrayref, i, &tempref);
+ if (code < 0)
+ return code;
+ for (j=0;j<value[1];j++) {
+ code = array_get(imemory, &tempref, i, &valref);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&valref, t_string))
+ return_error(gs_error_typecheck);
+
+ if (r_size(&valref) != (3 * value[2] * value[3]))
+ return_error(gs_error_rangecheck);
+ }
+ }
+ } else {
+ return_error(gs_error_rangecheck);
+ }
+
+ /* Remaining parameters are optional, but we must validate
+ * them if they are present
+ */
+ code = dict_find_string(&CIEdict, "RangeDEFG", &pref);
+ if (code > 0 && !r_has_type(pref, t_null)) {
+ if (!r_is_array(pref))
+ return_error(gs_error_typecheck);
+ if (r_size(pref) != 8)
+ return_error(gs_error_rangecheck);
+ code = get_cie_param_array(imemory, pref, 8, value);
+ if (code < 0)
+ return code;
+ if (value[1] < value[0] || value[3] < value[2] || value[5] < value[4] || value[7] < value[6])
+ return_error(gs_error_rangecheck);
+ }
+
+ code = dict_find_string(&CIEdict, "DecodeDEFG", &pref);
+ if (code > 0 && !r_has_type(pref, t_null)) {
+ if (!r_is_array(pref))
+ return_error(gs_error_typecheck);
+ if (r_size(pref) != 4)
+ return_error(gs_error_rangecheck);
+
+ for (i=0;i<4;i++) {
+ code = array_get(imemory, pref, i, &valref);
+ if (code < 0)
+ return code;
+ check_proc(valref);
+ }
+ }
+
+ code = dict_find_string(&CIEdict, "RangeHIJK", &pref);
+ if (code > 0 && !r_has_type(pref, t_null)) {
+ if (!r_is_array(pref))
+ return_error(gs_error_typecheck);
+ if (r_size(pref) != 8)
+ return_error(gs_error_rangecheck);
+ code = get_cie_param_array(imemory, pref, 8, value);
+ if (code < 0)
+ return code;
+ if (value[1] < value[0] || value[3] < value[2] || value[5] < value[4] || value[7] < value[6])
+ return_error(gs_error_rangecheck);
+ }
+
+ *r = 0;
+ return 0;
+}
+static int ciedefgdomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int code;
+ ref CIEdict, *tempref;
+
+ code = array_get(imemory, space, 1, &CIEdict);
+ if (code < 0)
+ return code;
+
+ /* If we have a RangeDEFG, get the values from that */
+ code = dict_find_string(&CIEdict, "RangeDEFG", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ code = get_cie_param_array(imemory, tempref, 8, ptr);
+ if (code < 0)
+ return code;
+ } else {
+ /* Default values for a CIEBasedDEFG */
+ memcpy(ptr, default_0_1, 8*sizeof(float));
+ }
+ return 0;
+}
+static int ciedefgrange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int code;
+ ref CIEdict, *tempref;
+
+ code = array_get(imemory, space, 1, &CIEdict);
+ if (code < 0)
+ return code;
+
+ /* If we have a RangeDEFG, get the values from that */
+ code = dict_find_string(&CIEdict, "RangeDEFG", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ code = get_cie_param_array(imemory, tempref, 8, ptr);
+ if (code < 0)
+ return code;
+ } else {
+ /* Default values for a CIEBasedDEFG */
+ memcpy(ptr, default_0_1, 8*sizeof(float));
+ }
+ return 0;
+}
+static int ciedefgvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ os_ptr op = osp;
+ int i;
+
+ if (num_comps < 4)
+ return_error(gs_error_stackunderflow);
+
+ op -= 3;
+ for (i=0;i < 4;i++) {
+ if (!r_has_type(op, t_integer) && !r_has_type(op, t_real))
+ return_error(gs_error_typecheck);
+ op++;
+ }
+ return 0;
+}
+static int ciedefgcompareproc(i_ctx_t *i_ctx_p, ref *space, ref *testspace)
+{
+ /* If the problems mentioned above are resolved, then this code could
+ * be re-instated.
+ */
+ int code = 0;
+ ref CIEdict1, CIEdict2;
+
+ code = array_get(imemory, space, 1, &CIEdict1);
+ if (code < 0)
+ return 0;
+ code = array_get(imemory, testspace, 1, &CIEdict2);
+ if (code < 0)
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"WhitePoint"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"BlackPoint"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"RangeABC"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"DecodeABC"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"MatrixABC"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"RangeLMN"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"DecodeLMN"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"MatrixMN"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"RangeDEFG"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"DecodeDEFG"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"RangeHIJK"))
+ return 0;
+ if (!comparedictkey(i_ctx_p, &CIEdict1, &CIEdict2, (char *)"Table"))
+ return 0;
+ return 1;
+}
+
+static char const * const CIESpaces[] = {
+ "CIEBasedA",
+ "CIEBasedABC",
+ "CIEBasedDEF",
+ "CIEBasedDEFG"
+};
+/* This routine returns the Device space equivalents for a CIEBased space.
+ * Used by currentgray, currentrgb and currentcmyk, the PLRM says that CIE
+ * spaces return 0 for all components.
+ */
+static int ciebasecolor(i_ctx_t * i_ctx_p, ref *space, int base, int *stage, int *cont, int *stack_depth)
+{
+ os_ptr op;
+ ref *spacename, nref;
+ int i, components=1, code;
+
+ /* If the spaece is an array, the first element is always the name */
+ if (r_is_array(space))
+ spacename = space->value.refs;
+ else
+ spacename = space;
+ /* Check that it really is a name */
+ if (!r_has_type(spacename, t_name))
+ return_error(gs_error_typecheck);
+
+ /* Find the relevant color space object */
+ for (i=0;i<4;i++) {
+ code = names_ref(imemory->gs_lib_ctx->gs_name_table, (const byte *)CIESpaces[i], strlen(CIESpaces[i]), &nref, 0);
+ if (code < 0)
+ return code;
+ if (name_eq(spacename, &nref)) {
+ break;
+ }
+ }
+ /* Find out how many values are on the stack, which depends
+ * on what kind of CIEBased space this is.
+ */
+ switch(i){
+ case 0:
+ components = 1;
+ break;
+ case 1:
+ case 2:
+ components = 3;
+ break;
+ case 3:
+ components = 4;
+ break;
+ }
+ /* Remove teh requisite number of values */
+ pop(components);
+ op = osp;
+ /* Find out how many values we need to return, which
+ * depends on the requested space.
+ */
+ switch(base) {
+ case 0:
+ components = 1;
+ break;
+ case 1:
+ case 2:
+ components = 3;
+ break;
+ case 3:
+ components = 4;
+ break;
+ }
+ push(components);
+ /* The PLRM says that all the components should be returned as 0.0 */
+ op -= components-1;
+ for (i=0;i<components;i++) {
+ make_real(op, (float)0);
+ op++;
+ }
+ /* However, Adobe implementations actually return 1.0 for the black
+ * channel of CMYK...
+ */
+ if (components == 4) {
+ op--;
+ make_real(op, (float)1);
+ }
+ *stage = 0;
+ *cont = 0;
+ return 0;
+}
+
+/* This routine used by both Separatin and DeviceN spaces to convert
+ * a PostScript tint transform into a Function, either a type 4
+ * 'PostScript calculator (see PDF reference) or a type 0 'sampled'
+ * function.
+ */
+static int convert_transform(i_ctx_t * i_ctx_p, ref *arr, ref *pproc)
+{
+ os_ptr op = osp; /* required by "push" macro */
+ int code;
+
+ /* buildfunction returns an operand on the stack. In fact
+ * it replaces the lowest operand, so make sure there is an
+ * empty sacrificial one present.
+ */
+ push(1);
+ /* Start by trying a type 4 function */
+ code = buildfunction(i_ctx_p, arr, pproc, 4);
+
+ if (code < 0)
+ /* If that fails, try a type 0. We prefer a type 4
+ * because a type 0 will require us to sample the
+ * space, which is expensive
+ */
+ code = buildfunction(i_ctx_p, arr, pproc, 0);
+
+ return code;
+}
+
+/* Separation */
+static int setseparationspace(i_ctx_t * i_ctx_p, ref *sepspace, int *stage, int *cont, int CIESubst)
+{
+ os_ptr op = osp; /* required by "push" macro */
+ int code = 0;
+ ref sname, proc;
+ ref name_none, name_all;
+ separation_type sep_type;
+ ref_colorspace cspace_old;
+ gs_color_space *pcs;
+ gs_color_space * pacs;
+ gs_function_t *pfn = NULL;
+ gs_client_color cc;
+
+ if (i_ctx_p->language_level < 2)
+ return_error(gs_error_undefined);
+
+ *cont = 0;
+ if ((*stage) == 0) {
+ code = array_get(imemory, sepspace, 3, &proc);
+ if (code < 0)
+ return code;
+ /* Check to see if we already have a function (eg from a PDF file) */
+ pfn = ref_function(&proc);
+ if (pfn == NULL) {
+ /* Convert tint transform to a PostScript function */
+ code = convert_transform(i_ctx_p, sepspace, &proc);
+ if (code < 0)
+ return code;
+ if (code > 0) {
+ *cont = 1;
+ (*stage)++;
+ return code;
+ }
+ /* We can only get here if the transform converted to a function
+ * without requiring a continuation. Most likely this means its a
+ * type 4 function. If so then it is still on the stack.
+ */
+ op = osp;
+ pfn = ref_function(op);
+ pop (1);
+ }
+ } else {
+ /* The function is returned on the operand stack */
+ op = osp;
+ pfn = ref_function(op);
+ pop (1);
+ }
+
+ *stage = 0;
+ if ((code = name_ref(imemory, (const byte *)"All", 3, &name_all, 0)) < 0)
+ return code;
+ if ((code = name_ref(imemory, (const byte *)"None", 4, &name_none, 0)) < 0)
+ return code;
+ /* Check separation name is a string or name object */
+ code = array_get(imemory, sepspace, 1, &sname);
+ if (code < 0)
+ return code;
+
+ if (r_has_type(&sname, t_string)) {
+ code = name_from_string(imemory, &sname, &sname);
+ if (code < 0)
+ return code;
+ }
+ sep_type = ( name_eq(&sname, &name_all) ? SEP_ALL :
+ name_eq(&sname, &name_none) ? SEP_NONE : SEP_OTHER);
+
+ /* The alternate color space has been selected as the current color space */
+ pacs = gs_currentcolorspace(igs);
+
+ cspace_old = istate->colorspace[0];
+ /* Now set the current color space as Separation */
+ code = gs_cspace_new_Separation(&pcs, pacs, imemory);
+ if (code < 0)
+ return code;
+ pcs->params.separation.sep_type = sep_type;
+ pcs->params.separation.sep_name = name_index(imemory, &sname);
+ pcs->params.separation.get_colorname_string = gs_get_colorname_string;
+ code = array_get(imemory, sepspace, 1, &proc);
+ if (code < 0)
+ return code;
+ istate->colorspace[0].procs.special.separation.layer_name = proc;
+ code = array_get(imemory, sepspace, 3, &proc);
+ if (code < 0)
+ return code;
+ istate->colorspace[0].procs.special.separation.tint_transform = proc;
+ if (code >= 0)
+ code = gs_cspace_set_sepr_function(pcs, pfn);
+ if (code >= 0)
+ code = gs_setcolorspace(igs, pcs);
+ /* release reference from construction */
+ rc_decrement_only_cs(pcs, "setseparationspace");
+ if (code < 0) {
+ istate->colorspace[0] = cspace_old;
+ return code;
+ }
+ cc.pattern = 0x00;
+ cc.paint.values[0] = 1.0;
+ code = gs_setcolor(igs, &cc);
+ return code;
+}
+static int validateseparationspace(i_ctx_t * i_ctx_p, ref **space)
+{
+ int code = 0;
+ ref *sepspace = *space;
+ ref nameref, sref, sname, altspace, tref;
+
+ if (!r_is_array(sepspace))
+ return_error(gs_error_typecheck);
+ /* Validate parameters, check we have enough operands */
+ if (r_size(sepspace) != 4)
+ return_error(gs_error_rangecheck);
+
+ /* Check separation name is a string or name object */
+ code = array_get(imemory, sepspace, 1, &sname);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&sname, t_name)) {
+ if (!r_has_type(&sname, t_string))
+ return_error(gs_error_typecheck);
+ else {
+ code = name_from_string(imemory, &sname, &sname);
+ if (code < 0)
+ return code;
+ }
+ }
+
+ /* Check the tint transform is a procedure */
+ code = array_get(imemory, sepspace, 3, &tref);
+ if (code < 0)
+ return code;
+ check_proc(tref);
+
+ /* Get the name of the alternate space */
+ code = array_get(imemory, sepspace, 2, &altspace);
+ if (code < 0)
+ return code;
+ if (r_has_type(&altspace, t_name))
+ ref_assign(&nameref, &altspace);
+ else {
+ /* Make sure the alternate space is an array */
+ if (!r_is_array(&altspace))
+ return_error(gs_error_typecheck);
+ /* And has a name for its type */
+ code = array_get(imemory, &altspace, 0, &tref);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&tref, t_name))
+ return_error(gs_error_typecheck);
+ ref_assign(&nameref, &tref);
+ }
+
+ /* Convert alternate space name to string */
+ name_string_ref(imemory, &nameref, &sref);
+ /* Check its not /Indexed or /Pattern or /DeviceN */
+ if (r_size(&sref) == 7) {
+ if (strncmp((const char *)sref.value.const_bytes, "Indexed", 7) == 0)
+ return_error(gs_error_typecheck);
+ if (strncmp((const char *)sref.value.const_bytes, "Pattern", 7) == 0)
+ return_error(gs_error_typecheck);
+ if (strncmp((const char *)sref.value.const_bytes, "DeviceN", 7) == 0)
+ return_error(gs_error_typecheck);
+ }
+ /* and also not /Separation */
+ if (r_size(&sref) == 9 && strncmp((const char *)sref.value.const_bytes, "Separation", 9) == 0)
+ return_error(gs_error_typecheck);
+
+ ref_assign(*space, &altspace);
+ return 0;
+}
+static int separationalternatespace(i_ctx_t * i_ctx_p, ref *sepspace, ref **r, int *CIESubst)
+{
+ ref tref;
+ int code;
+
+ code = array_get(imemory, sepspace, 2, &tref);
+ if (code < 0)
+ return code;
+ ref_assign(*r, &tref);
+ return 0;
+}
+static int sepdomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ ptr[0] = 0;
+ ptr[1] = 1;
+ return 0;
+}
+static int seprange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ ptr[0] = 0;
+ ptr[1] = 1;
+ return 0;
+}
+static int septransform(i_ctx_t *i_ctx_p, ref *sepspace, int *usealternate, int *stage, int *stack_depth)
+{
+ gx_device * dev = igs->device;
+ ref sname, proc;
+ int code, colorant_number;
+
+ code = array_get(imemory, sepspace, 1, &sname);
+ if (code < 0)
+ return code;
+ if (r_has_type(&sname, t_name)) {
+ name_string_ref(imemory, &sname, &sname);
+ }
+
+ /* Check for /All and /None, never need the alternate for these */
+ if (r_size(&sname) == 3 &&
+ strncmp("All", (const char *)sname.value.bytes, r_size(&sname)) == 0) {
+ *usealternate = 0;
+ return 0;
+ }
+ if (r_size(&sname) == 4 &&
+ strncmp("None", (const char *)sname.value.bytes, r_size(&sname)) == 0) {
+ *usealternate = 0;
+ return 0;
+ }
+ /*
+ * Compare the colorant name to the device's. If the device's
+ * compare routine returns GX_DEVICE_COLOR_MAX_COMPONENTS then the
+ * colorant is in the SeparationNames list but not in the
+ * SeparationOrder list.
+ */
+ colorant_number = (*dev_proc(dev, get_color_comp_index))
+ (dev, (const char *)sname.value.bytes, r_size(&sname), SEPARATION_NAME);
+ if (colorant_number >= 0) { /* If valid colorant name */
+ *usealternate = 0;
+ } else
+ *usealternate = 1;
+
+ if (*usealternate && *stage == 0) {
+ (*stage)++;
+ esp++;
+ code = array_get(imemory, sepspace, 3, &proc);
+ if (code < 0)
+ return code;
+ *esp = proc;
+ return o_push_estack;
+ }
+ *stage = 0;
+ return 0;
+}
+static int sepbasecolor(i_ctx_t * i_ctx_p, ref *space, int base, int *stage, int *cont, int *stack_depth)
+{
+ os_ptr op = osp; /* required by "push" macro */
+ int use, code;
+
+ code = septransform(i_ctx_p, space, &use, stage, stack_depth);
+ if (code != 0)
+ return code;
+ if (!use) {
+ *stage = 0;
+ *cont = 0;
+ pop(1);
+ op = osp;
+ switch(base) {
+ case 0:
+ push(1);
+ make_real(op, 0.0);
+ break;
+ case 1:
+ case 2:
+ push(3);
+ make_real(&op[-2], 0.0);
+ make_real(&op[-1], 0.0);
+ make_real(op, 0.0);
+ break;
+ case 3:
+ push(4);
+ make_real(&op[-3], 0.0);
+ make_real(&op[-2], 0.0);
+ make_real(&op[-1], 0.0);
+ make_real(op, 0.0);
+ break;
+ }
+ } else {
+ *stage = 0;
+ *cont = 1;
+ }
+ return 0;
+}
+static int sepvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ os_ptr op = osp;
+
+ if (num_comps < 1)
+ return_error(gs_error_stackunderflow);
+
+ if (!r_has_type(op, t_integer) && !r_has_type(op, t_real))
+ return_error(gs_error_typecheck);
+
+ if (*values > 1.0)
+ *values = 1.0;
+
+ if (*values < 0.0)
+ *values = 0.0;
+
+ return 0;
+}
+static int sepcompareproc(i_ctx_t *i_ctx_p, ref *space, ref *testspace)
+{
+ ref sname1, sname2;
+ int code;
+
+ code = array_get(imemory, space, 1, &sname1);
+ if (code < 0)
+ return 0;
+
+ code = array_get(imemory, testspace, 1, &sname2);
+ if (code < 0)
+ return 0;
+
+ if (r_type(&sname1) != r_type(&sname2))
+ return 0;
+
+ switch(r_type(&sname1)) {
+ case t_name:
+ if (!name_eq(&sname1, &sname2))
+ return 0;
+ break;
+ case t_string:
+ if (r_size(&sname1) != r_size(&sname2))
+ return 0;
+ if (strncmp((const char *)sname1.value.const_bytes, (const char *)sname2.value.const_bytes, r_size(&sname1)) != 0)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+ code = array_get(imemory, testspace, 2, &sname1);
+ if (code < 0)
+ return 0;
+ code = array_get(imemory, testspace, 2, &sname2);
+ if (code < 0)
+ return 0;
+ if (r_type(&sname1) != r_type(&sname2))
+ return 0;
+
+ if (r_is_array(&sname1)) {
+ if (!comparearrays(i_ctx_p, &sname1, &sname2))
+ return 0;
+ } else {
+ if (!r_has_type(&sname1, t_name))
+ return 0;
+ if (!name_eq(&sname1, &sname2))
+ return 0;
+ }
+ code = array_get(imemory, space, 3, &sname1);
+ if (code < 0)
+ return 0;
+ code = array_get(imemory, testspace, 3, &sname2);
+ if (code < 0)
+ return 0;
+ return(comparearrays(i_ctx_p, &sname1, &sname2));
+}
+static int sepinitialproc(i_ctx_t *i_ctx_p, ref *space)
+{
+ gs_client_color cc;
+
+ cc.pattern = 0x00;
+ cc.paint.values[0] = 1.0;
+ return gs_setcolor(igs, &cc);
+}
+
+/* DeviceN */
+static int devicencolorants_cont(i_ctx_t *i_ctx_p)
+{
+ ref dict, *pdict = &dict, space[2], sname;
+ int index, code, depth, stage;
+ es_ptr ep = esp, pindex, pstage;
+ os_ptr op = osp;
+ gs_separation_name sep_name;
+
+ pindex = &ep[-2];
+ pstage = &ep[-1];
+ index = (int)pindex->value.intval;
+ stage = (int)pstage->value.intval;
+ ref_assign(&dict, ep);
+
+ do {
+ if (index >= dict_length(pdict)) {
+ esp -= 4;
+ return o_pop_estack;
+ }
+
+ if (stage == 0) {
+ code = gs_gsave(igs);
+ if (code < 0)
+ return code;
+
+ code = dict_index_entry(pdict, index, (ref *)&space);
+ if (code < 0) {
+ make_int(pindex, ++index);
+ code = gs_grestore(igs);
+ if (code < 0)
+ return code;
+ continue;
+ }
+
+ code = validate_spaces(i_ctx_p, &space[1], &depth);
+ if (code < 0) {
+ make_int(pindex, ++index);
+ code = gs_grestore(igs);
+ if (code < 0)
+ return code;
+ return o_push_estack;
+ }
+
+ /* If we get a continuation from a sub-procedure, we will want to come back
+ * here afterward, to do any remaining stages. We need to set up for that now.
+ * so that our continuation is ahead of the sub-proc's continuation.
+ */
+ check_estack(1);
+ push(1);
+ /* The push_op_estack macro increments esp before use, so we don't need to */
+ push_op_estack(devicencolorants_cont);
+
+ make_int(pstage, 1);
+ *op = space[1];
+ code = zsetcolorspace(i_ctx_p);
+ if (code != 0)
+ return code;
+ } else {
+ stage = 0;
+ code = dict_index_entry(pdict, index, (ref *)&space);
+ if (code == 0) {
+ switch (r_type(&space[0])) {
+ case t_string:
+ code = name_from_string(imemory, &space[0], &sname);
+ if (code == 0)
+ sep_name = name_index(imemory, &sname);
+ break;
+ case t_name:
+ sep_name = name_index(imemory, &space[0]);
+ break;
+ default:
+ code = gs_error_typecheck;
+ }
+ }
+ make_int(pindex, ++index);
+ make_int(pstage, stage);
+ if (code == 0)
+ gs_attachattributecolorspace(sep_name, igs);
+ code = gs_grestore(igs);
+ if (code < 0)
+ return code;
+ }
+ }
+ while(1);
+}
+
+static int setdevicenspace(i_ctx_t * i_ctx_p, ref *devicenspace, int *stage, int *cont, int CIESubst)
+{
+ os_ptr op = osp; /* required by "push" macro */
+ int code = 0, num_components, i;
+ ref namesarray, proc, sname, tname, sref;
+ ref_colorspace cspace_old;
+ gs_color_space *pcs;
+ gs_color_space * pacs;
+ gs_function_t *pfn = NULL;
+ gs_separation_name *names;
+ gs_client_color cc;
+
+ if (i_ctx_p->language_level < 3)
+ return_error(gs_error_undefined);
+
+ *cont = 0;
+ if ((*stage) == 2) {
+ if (r_size(devicenspace) == 5) {
+ /* We have a Colorants dictionary from a PDF file. We need to handle this by
+ * temporarily setting each of the spaces in the dict, and attaching the
+ * resulting space to the DeviceN array. This is complicated, because
+ * each space must be fully set up, and may result in running tint transform
+ * procedures and caching results. We need to handle this in yet another
+ * layering of continuation procedures.
+ */
+ ref *colorants;
+
+ code = array_get(imemory, devicenspace, 4, &sref);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&sref, t_dictionary)) {
+ *stage = 0;
+ return 0;
+ }
+ if (dict_find_string(&sref, "Colorants", &colorants) <= 0) {
+ *stage = 0;
+ return 0;
+ }
+ if (!r_has_type(colorants, t_dictionary)) {
+ *stage = 0;
+ return 0;
+ }
+ *stage = 3;
+ *cont = 1;
+ check_estack(5);
+ push_mark_estack(es_other, colour_cleanup);
+ esp++;
+ /* variable to hold index of the space we are dealing with */
+ make_int(esp, 0);
+ esp++;
+ /* variable to hold processing step */
+ make_int(esp, 0);
+ esp++;
+ /* Store a pointer to the Colorants dictionary
+ */
+ ref_assign(esp, colorants);
+ push_op_estack(devicencolorants_cont);
+ return o_push_estack;
+ } else {
+ *stage = 0;
+ return 0;
+ }
+ }
+ if ((*stage) == 3) {
+ *stage = 0;
+ return 0;
+ }
+ if ((*stage) == 0) {
+ code = array_get(imemory, devicenspace, 3, &proc);
+ if (code < 0)
+ return code;
+ pfn = ref_function(&proc);
+ if (pfn == NULL) {
+ /* Convert tint transform to a PostScript function */
+ code = convert_transform(i_ctx_p, devicenspace, &proc);
+ if (code < 0)
+ return code;
+ if (code > 0) {
+ *cont = 1;
+ *stage = 1;
+ return code;
+ }
+ /* We can only get here if the transform converted to a function
+ * without requiring a continuation. Most likely this means its a
+ * type 4 function. If so then it is still on the stack.
+ */
+ op = osp;
+ pfn = ref_function(op);
+ pop (1);
+ }
+ } else {
+ /* The function is returned on the operand stack */
+ op = osp;
+ pfn = ref_function(op);
+ pop (1);
+ }
+
+ *stage = 2;
+
+ code = array_get(imemory, devicenspace, 1, &namesarray);
+ if (code < 0)
+ return code;
+ num_components = r_size(&namesarray);
+ /* The alternate color space has been selected as the current color space */
+ pacs = gs_currentcolorspace(igs);
+
+ if (num_components == 1) {
+ array_get(imemory, &namesarray, (long)0, &sname);
+ switch (r_type(&sname)) {
+ case t_string:
+ tname = sname;
+ break;
+ case t_name:
+ name_string_ref(imemory, &sname, &tname);
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ break;
+ }
+ if (strncmp((const char *)tname.value.const_bytes, "All", 3) == 0 && r_size(&tname) == 3) {
+ separation_type sep_type;
+
+ /* Sigh, Acrobat allows this, even though its contra the spec. Convert to
+ * a /Separation space and go on
+ */
+ sep_type = SEP_ALL;
+
+ /* The alternate color space has been selected as the current color space */
+ pacs = gs_currentcolorspace(igs);
+
+ cspace_old = istate->colorspace[0];
+ /* Now set the current color space as Separation */
+ code = gs_cspace_new_Separation(&pcs, pacs, imemory);
+ if (code < 0)
+ return code;
+ pcs->params.separation.sep_type = sep_type;
+ pcs->params.separation.sep_name = name_index(imemory, &sname);
+ pcs->params.separation.get_colorname_string = gs_get_colorname_string;
+ code = array_get(imemory, &namesarray, (long)0, &sname);
+ if (code < 0)
+ return code;
+ istate->colorspace[0].procs.special.separation.layer_name = sname;
+ code = array_get(imemory, devicenspace, 3, &proc);
+ if (code < 0)
+ return code;
+ istate->colorspace[0].procs.special.separation.tint_transform = proc;
+ if (code >= 0)
+ code = gs_cspace_set_sepr_function(pcs, pfn);
+ if (code >= 0)
+ code = gs_setcolorspace(igs, pcs);
+ /* release reference from construction */
+ rc_decrement_only_cs(pcs, "setseparationspace");
+ if (code < 0) {
+ istate->colorspace[0] = cspace_old;
+ return code;
+ }
+ cc.pattern = 0x00;
+ cc.paint.values[0] = 1.0;
+ code = gs_setcolor(igs, &cc);
+ return code;
+ }
+ }
+ code = gs_cspace_new_DeviceN(&pcs, num_components, pacs, imemory);
+ if (code < 0)
+ return code;
+ names = pcs->params.device_n.names;
+ pcs->params.device_n.get_colorname_string = gs_get_colorname_string;
+
+ /* Pick up the names of the components */
+ {
+ uint i;
+ ref sname;
+
+ for (i = 0; i < num_components; ++i) {
+ array_get(imemory, &namesarray, (long)i, &sname);
+ switch (r_type(&sname)) {
+ case t_string:
+ code = name_from_string(imemory, &sname, &sname);
+ if (code < 0) {
+ rc_decrement_cs(pcs, "setdevicenspace");
+ return code;
+ }
+ /* falls through */
+ case t_name:
+ names[i] = name_index(imemory, &sname);
+ break;
+ default:
+ rc_decrement_cs(pcs, "setdevicenspace");
+ return_error(gs_error_typecheck);
+ }
+ }
+ }
+
+ /* Now set the current color space as DeviceN */
+
+ cspace_old = istate->colorspace[0];
+ istate->colorspace[0].procs.special.device_n.layer_names = namesarray;
+ code = array_get(imemory, devicenspace, 3, &proc);
+ if (code < 0)
+ return code;
+ istate->colorspace[0].procs.special.device_n.tint_transform = proc;
+ code = gs_cspace_set_devn_function(pcs, pfn);
+ if (code < 0) {
+ return code;
+ }
+ code = gs_setcolorspace(igs, pcs);
+ /* release reference from construction */
+ rc_decrement_only_cs(pcs, "setdevicenspace");
+ if (code < 0) {
+ istate->colorspace[0] = cspace_old;
+ return code;
+ }
+
+ cc.pattern = 0x00;
+ for (i=0;i<num_components;i++)
+ cc.paint.values[i] = 1.0;
+ code = gs_setcolor(igs, &cc);
+ *cont = 1;
+ return code;
+}
+static int validatedevicenspace(i_ctx_t * i_ctx_p, ref **space)
+{
+ int i, code = 0;
+ ref *devicenspace = *space, proc;
+ ref nameref, sref, altspace, namesarray, sname;
+
+ /* Check enough arguments in the space */
+ if (r_size(devicenspace) < 4)
+ return_error(gs_error_rangecheck);
+ /* Check the names parameter is an array */
+ code = array_get(imemory, devicenspace, 1, &namesarray);
+ if (code < 0)
+ return code;
+ if (!r_is_array(&namesarray))
+ return_error(gs_error_typecheck);
+ /* Ensure we have at least one ink */
+ if (r_size(&namesarray) < 1)
+ return_error(gs_error_typecheck);
+ /* Make sure no more inks than we can cope with */
+ if (r_size(&namesarray) > MAX_COMPONENTS_IN_DEVN) /* MUST match psi/icremap.h int_remap_color_info_s */
+ return_error(gs_error_limitcheck);
+ /* Check the tint transform is a procedure */
+ code = array_get(imemory, devicenspace, 3, &proc);
+ if (code < 0)
+ return code;
+ check_proc(proc);
+
+ /* Check the array of ink names only contains names or strings */
+ for (i = 0; i < r_size(&namesarray); ++i) {
+ array_get(imemory, &namesarray, (long)i, &sname);
+ switch (r_type(&sname)) {
+ case t_string:
+ case t_name:
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ }
+
+ /* Get the name of the alternate space */
+ code = array_get(imemory, devicenspace, 2, &altspace);
+ if (code < 0)
+ return code;
+ if (r_has_type(&altspace, t_name))
+ ref_assign(&nameref, &altspace);
+ else {
+ /* Make sure the alternate space is an array */
+ if (!r_is_array(&altspace))
+ return_error(gs_error_typecheck);
+ /* And has a name for its type */
+ code = array_get(imemory, &altspace, 0, &nameref);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&nameref, t_name))
+ return_error(gs_error_typecheck);
+ }
+ /* Convert alternate space name to string */
+ name_string_ref(imemory, &nameref, &sref);
+ /* Check its not /Indexed, /Pattern, /DeviceN */
+ if (r_size(&sref) == 7) {
+ if (strncmp((const char *)sref.value.const_bytes, "Indexed", 7) == 0)
+ return_error(gs_error_typecheck);
+ if (strncmp((const char *)sref.value.const_bytes, "Pattern", 7) == 0)
+ return_error(gs_error_typecheck);
+ if (strncmp((const char *)sref.value.const_bytes, "DeviceN", 7) == 0)
+ return_error(gs_error_typecheck);
+ }
+ /* and also not /Separation */
+ if (r_size(&sref) == 9 && strncmp((const char *)sref.value.const_bytes, "Separation", 9) == 0)
+ return_error(gs_error_typecheck);
+
+ ref_assign(*space, &altspace);
+ return 0;
+}
+static int devicenalternatespace(i_ctx_t * i_ctx_p, ref *space, ref **r, int *CIESubst)
+{
+ ref altspace;
+ int code;
+
+ code = array_get(imemory, space, 2, &altspace);
+ if (code < 0)
+ return code;
+ ref_assign(*r, &altspace);
+ return 0;
+}
+static int devicencomponents(i_ctx_t * i_ctx_p, ref *space, int *n)
+{
+ ref namesarray;
+ int code;
+
+ code = array_get(imemory, space, 1, &namesarray);
+ if (code < 0)
+ return code;
+ *n = r_size(&namesarray);
+ return 0;
+}
+static int devicendomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int i, limit, code;
+ ref namesarray;
+
+ code = array_get(imemory, space, 1, &namesarray);
+ if (code < 0)
+ return code;
+
+ limit = r_size(&namesarray) * 2;
+ for (i = 0;i < limit;i+=2) {
+ ptr[i] = 0;
+ ptr[i+1] = 1;
+ }
+ return 0;
+}
+static int devicenrange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int i, limit, code;
+ PS_colour_space_t *cspace;
+
+ ref altspace;
+
+ code = array_get(imemory, space, 1, &altspace);
+ if (code < 0)
+ return code;
+
+ code = get_space_object(i_ctx_p, &altspace, &cspace);
+ if (code < 0)
+ return code;
+
+ code = cspace->numcomponents(i_ctx_p, &altspace, &limit);
+ if (code < 0)
+ return code;
+
+ for (i = 0;i < limit * 2;i+=2) {
+ ptr[i] = 0;
+ ptr[i+1] = 1;
+ }
+ return 0;
+}
+static int devicentransform(i_ctx_t *i_ctx_p, ref *devicenspace, int *usealternate, int *stage, int *stack_depth)
+{
+ gx_device * dev = igs->device;
+ ref narray, sname, proc;
+ int i, code, colorant_number;
+
+ *usealternate = 0;
+ code = array_get(imemory, devicenspace, 1, &narray);
+ if (code < 0)
+ return code;
+ if (!r_is_array(&narray))
+ return_error(gs_error_typecheck);
+
+ for (i=0;i<r_size(&narray);i++) {
+ code = array_get(imemory, &narray, i, &sname);
+ if (code < 0)
+ return code;
+ if (r_has_type(&sname, t_name)) {
+ name_string_ref(imemory, &sname, &sname);
+ }
+
+ /* Check for /All and /None, never need the alternate for these */
+ if (r_size(&sname) == 3 &&
+ strncmp("All", (const char *)sname.value.bytes, r_size(&sname)) == 0)
+ continue;
+ if (r_size(&sname) == 4 &&
+ strncmp("None", (const char *)sname.value.bytes, r_size(&sname)) == 0)
+ continue;
+ /*
+ * Compare the colorant name to the device's. If the device's
+ * compare routine returns GX_DEVICE_COLOR_MAX_COMPONENTS then the
+ * colorant is in the SeparationNames list but not in the
+ * SeparationOrder list.
+ */
+ colorant_number = (*dev_proc(dev, get_color_comp_index))
+ (dev, (const char *)sname.value.bytes, r_size(&sname), SEPARATION_NAME);
+ if (colorant_number < 0) { /* If not valid colorant name */
+ *usealternate = 1;
+ break;
+ }
+ }
+ if (*usealternate && *stage == 0) {
+ (*stage)++;
+ esp++;
+ code = array_get(imemory, devicenspace, 3, &proc);
+ if (code < 0)
+ return code;
+ *esp = proc;
+ return o_push_estack;
+ }
+
+ if (*stage == 1){
+ *stack_depth = 0;
+ *stage = 0;
+ }
+ return 0;
+}
+static int devicenbasecolor(i_ctx_t * i_ctx_p, ref *space, int base, int *stage, int *cont, int *stack_depth)
+{
+ os_ptr op = osp; /* required by "push" macro */
+ int code, use, n_comp;
+ ref narray;
+
+ code = devicentransform(i_ctx_p, space, &use, stage, stack_depth);
+ if (code != 0)
+ return code;
+ if (!use) {
+ *stage = 0;
+ *cont = 0;
+ code = array_get(imemory, space, 1, &narray);
+ if (code < 0)
+ return code;
+ n_comp = r_size(&narray);
+ pop(n_comp);
+ op = osp;
+ switch(base) {
+ case 0:
+ push(1);
+ make_real(op, 0.0);
+ break;
+ case 1:
+ case 2:
+ push(3);
+ make_real(&op[-2], 0.0);
+ make_real(&op[-1], 0.0);
+ make_real(op, 0.0);
+ break;
+ case 3:
+ push(4);
+ make_real(&op[-3], 0.0);
+ make_real(&op[-2], 0.0);
+ make_real(&op[-1], 0.0);
+ make_real(op, 0.0);
+ break;
+ }
+ } else {
+ *stage = 0;
+ *cont = 1;
+ }
+ return 0;
+}
+static int devicenvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ int i, code;
+ ref narray;
+ os_ptr op = osp;
+
+ code = array_get(imemory, space, 1, &narray);
+ if (code < 0)
+ return code;
+ if (!r_is_array(&narray))
+ return_error(gs_error_typecheck);
+
+ if (num_comps < r_size(&narray))
+ return_error(gs_error_stackunderflow);
+
+ op -= r_size(&narray) - 1;
+
+ for (i=0;i < r_size(&narray); i++) {
+ if (!r_has_type(op, t_integer) && !r_has_type(op, t_real))
+ return_error(gs_error_typecheck);
+
+ if (values[i] > 1.0)
+ values[i] = 1.0;
+
+ if (values[i] < 0.0)
+ values[i] = 0.0;
+ op++;
+ }
+
+ return 0;
+}
+static int devicencompareproc(i_ctx_t *i_ctx_p, ref *space, ref *testspace)
+{
+ ref sname1, sname2;
+ int code;
+
+ code = array_get(imemory, space, 1, &sname1);
+ if (code < 0)
+ return 0;
+
+ code = array_get(imemory, testspace, 1, &sname2);
+ if (code < 0)
+ return 0;
+
+ if (!r_is_array(&sname1))
+ return 0;
+ if (!r_is_array(&sname2))
+ return 0;
+
+ if (!comparearrays(i_ctx_p, &sname1, &sname2))
+ return 0;
+
+ code = array_get(imemory, testspace, 2, &sname1);
+ if (code < 0)
+ return 0;
+ code = array_get(imemory, testspace, 2, &sname2);
+ if (code < 0)
+ return 0;
+ if (r_type(&sname1) != r_type(&sname2))
+ return 0;
+
+ if (r_is_array(&sname1)) {
+ if (!comparearrays(i_ctx_p, &sname1, &sname2))
+ return 0;
+ } else {
+ if (!r_has_type(&sname1, t_name))
+ return 0;
+ if (!name_eq(&sname1, &sname2))
+ return 0;
+ }
+ code = array_get(imemory, space, 3, &sname1);
+ if (code < 0)
+ return 0;
+ code = array_get(imemory, testspace, 3, &sname2);
+ if (code < 0)
+ return 0;
+ return(comparearrays(i_ctx_p, &sname1, &sname2));
+}
+static int deviceninitialproc(i_ctx_t *i_ctx_p, ref *space)
+{
+ gs_client_color cc;
+ int i, num_components, code;
+ ref namesarray;
+
+ code = array_get(imemory, space, 1, &namesarray);
+ if (code < 0)
+ return code;
+ num_components = r_size(&namesarray);
+ cc.pattern = 0x00;
+ for (i=0;i<num_components;i++)
+ cc.paint.values[i] = 1.0;
+ return gs_setcolor(igs, &cc);
+}
+
+/* Indexed */
+/*
+ * This routine samples the indexed space, it pushes values from -1
+ * to 'hival', then executes the tint transform procedure. Returns here
+ * to store the resulting value(s) in the indexed map.
+ */
+static int
+indexed_cont(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep = esp;
+ int i = (int)ep[csme_index].value.intval;
+
+ if (i >= 0) { /* i.e., not first time */
+ int m = (int)ep[csme_num_components].value.intval;
+ int code = float_params(op, m, &r_ptr(&ep[csme_map], gs_indexed_map)->values[i * m]);
+
+ if (code < 0)
+ return code;
+ pop(m);
+ op -= m;
+ if (i == (int)ep[csme_hival].value.intval) { /* All done. */
+ esp -= num_csme;
+ return o_pop_estack;
+ }
+ }
+ push(1);
+ ep[csme_index].value.intval = ++i;
+ make_int(op, i);
+ make_op_estack(ep + 1, indexed_cont);
+ ep[2] = ep[csme_proc]; /* lookup proc */
+ esp = ep + 2;
+ return o_push_estack;
+}
+static int setindexedspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ ref *pproc = &istate->colorspace[0].procs.special.index_proc;
+ int code = 0;
+ uint edepth = ref_stack_count(&e_stack);
+ ref_colorspace cspace_old;
+ ref hival, lookup;
+ gs_color_space *pcs;
+ gs_color_space *pcs_base;
+ gs_color_space_index base_type;
+
+ if (i_ctx_p->language_level < 2)
+ return_error(gs_error_undefined);
+
+ *cont = 0;
+ if (*stage == 1) {
+ *stage = 0;
+ return 0;
+ }
+
+ cspace_old = istate->colorspace[0];
+
+ pcs_base = gs_currentcolorspace(igs);
+ base_type = gs_color_space_get_index(pcs_base);
+
+ code = array_get(imemory, r, 3, &lookup);
+ if (code < 0)
+ return code;
+ code = array_get(imemory, r, 2, &hival);
+ if (code < 0)
+ return code;
+ if (r_has_type(&lookup, t_string)) {
+ int num_values = (hival.value.intval + 1) * cs_num_components(pcs_base);
+ byte *data_tmp;
+
+ check_read(lookup);
+ /*
+ * The PDF and PS specifications state that the lookup table must have
+ * the exact number of of data bytes needed. However we have found
+ * PDF files from Amyuni with extra data bytes. Acrobat 6.0 accepts
+ * these files without complaint, so we ignore the extra data.
+ */
+ if (r_size(&lookup) < num_values)
+ return_error(gs_error_rangecheck);
+ /* If we have a named color profile and the base space is DeviceN or
+ Separation use a different set of procedures to ensure the named
+ color remapping code is used */
+ if (igs->icc_manager->device_named != NULL &&
+ (base_type == gs_color_space_index_Separation ||
+ base_type == gs_color_space_index_DeviceN))
+ pcs = gs_cspace_alloc(imemory, &gs_color_space_type_Indexed_Named);
+ else
+ pcs = gs_cspace_alloc(imemory, &gs_color_space_type_Indexed);
+ if (!pcs) {
+ return_error(gs_error_VMerror);
+ }
+ pcs->base_space = pcs_base;
+ rc_increment_cs(pcs_base);
+
+ data_tmp = (byte *) (pcs->params.indexed.lookup.table.data = ialloc_string (lookup.tas.rsize, "setindexedspace"));
+ if (!data_tmp) {
+ rc_decrement(pcs, "setindexedspace");
+ return_error(gs_error_VMerror);
+ }
+
+ memcpy(data_tmp, lookup.value.const_bytes, lookup.tas.rsize);
+
+ pcs->params.indexed.lookup.table.size = num_values;
+ pcs->params.indexed.use_proc = 0;
+ make_null(pproc);
+ code = 0;
+ } else {
+ gs_indexed_map *map;
+
+ /*
+ * We have to call zcs_begin_map before moving the parameters,
+ * since if the color space is a DeviceN or Separation space,
+ * the memmove will overwrite its parameters.
+ */
+ code = zcs_begin_map(i_ctx_p, &map, &lookup, (hival.value.intval + 1),
+ pcs_base, indexed_cont);
+ if (code < 0)
+ return code;
+ if (igs->icc_manager->device_named != NULL &&
+ (base_type == gs_color_space_index_Separation ||
+ base_type == gs_color_space_index_DeviceN))
+ pcs = gs_cspace_alloc(imemory, &gs_color_space_type_Indexed_Named);
+ else
+ pcs = gs_cspace_alloc(imemory, &gs_color_space_type_Indexed);
+ pcs->base_space = pcs_base;
+ rc_increment_cs(pcs_base);
+ pcs->params.indexed.use_proc = 1;
+ *pproc = lookup;
+ map->proc.lookup_index = lookup_indexed_map;
+ pcs->params.indexed.lookup.map = map;
+ }
+ pcs->params.indexed.hival = hival.value.intval;
+ pcs->params.indexed.n_comps = cs_num_components(pcs_base);
+ code = gs_setcolorspace(igs, pcs);
+ /* release reference from construction */
+ rc_decrement_only_cs(pcs, "setindexedspace");
+ if (code < 0) {
+ istate->colorspace[0] = cspace_old;
+ ref_stack_pop_to(&e_stack, edepth);
+ return code;
+ }
+ *stage = 0;
+ if (ref_stack_count(&e_stack) == edepth) {
+ return 0;
+ } else {
+ *cont = 1;
+ *stage = 1;
+ return o_push_estack; /* installation will load the caches */
+ }
+}
+static int validateindexedspace(i_ctx_t * i_ctx_p, ref **space)
+{
+ int code = 0;
+ ref *r = *space;
+ ref nameref, sref, hival, lookup, altspace;
+
+ if (!r_is_array(r))
+ return_error(gs_error_typecheck);
+ /* Validate parameters, check we have enough operands */
+ if (r_size(r) != 4)
+ return_error(gs_error_rangecheck);
+ /* Check operand type(s) */
+ /* Make sure 'hival' is an integer */
+ code = array_get(imemory, r, 2, &hival);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&hival, t_integer))
+ return_error(gs_error_typecheck);
+ /* Make sure 'hival' lies between 0 and 4096 */
+ if (hival.value.intval < 0 || hival.value.intval > 4096)
+ return_error(gs_error_rangecheck);
+ /* Ensure the 'lookup' is either a string or a procedure */
+ code = array_get(imemory, r, 3, &lookup);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&lookup, t_string))
+ check_proc(lookup);
+
+ /* Get the name of the alternate space */
+ code = array_get(imemory, r, 1, &altspace);
+ if (code < 0)
+ return code;
+ if (r_has_type(&altspace, t_name))
+ ref_assign(&nameref, &altspace);
+ else {
+ if (!r_is_array(&altspace))
+ return_error(gs_error_typecheck);
+ code = array_get(imemory, &altspace, 0, &nameref);
+ if (code < 0)
+ return code;
+ }
+ /* Convert alternate space name to string */
+ name_string_ref(imemory, &nameref, &sref);
+ /* Check its not /Indexed or /Pattern */
+ if (r_size(&sref) == 7) {
+ if (strncmp((const char *)sref.value.const_bytes, "Indexed", 7) == 0)
+ return_error(gs_error_typecheck);
+ if (strncmp((const char *)sref.value.const_bytes, "Pattern", 7) == 0)
+ return_error(gs_error_typecheck);
+ }
+ ref_assign(*space, &altspace);
+ return 0;
+}
+static int indexedalternatespace(i_ctx_t * i_ctx_p, ref *space, ref **r, int *CIESubst)
+{
+ ref alt;
+ int code;
+
+ code = array_get(imemory, *r, 1, &alt);
+ if (code < 0)
+ return code;
+ ref_assign(*r, &alt);
+ return 0;
+}
+static int indexeddomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ ref hival;
+ int code;
+
+ code = array_get(imemory, space, 2, &hival);
+ if (code < 0)
+ return code;
+ ptr[0] = 0;
+ ptr[1] = (float)hival.value.intval;
+ return 0;
+}
+static int indexedrange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ ref hival;
+ int code;
+
+ code = array_get(imemory, space, 2, &hival);
+ if (code < 0)
+ return code;
+ ptr[0] = 0;
+ ptr[1] = (float)hival.value.intval;
+ return 0;
+}
+static int indexedbasecolor(i_ctx_t * i_ctx_p, ref *space, int base, int *stage, int *cont, int *stack_depth)
+{
+ int code;
+
+ if (*stage == 0) {
+ /* Usefully /Indexed can't be the base of any other space, so we know
+ * the current space in the graphics state is this one.
+ */
+ gs_color_space *pcs;
+ pcs = gs_currentcolorspace(igs);
+
+ /* Update the counters */
+ *stage = 1;
+ *cont = 1;
+
+ /* Indexed spaces can have *either* a procedure or a string for the
+ * lookup.
+ */
+ if (pcs->params.indexed.use_proc) {
+ es_ptr ep = ++esp;
+ ref proc;
+
+ /* We have a procedure, set up the continuation to run the
+ * lookup procedure. (The index is already on the operand stack)
+ */
+ check_estack(1);
+ code = array_get(imemory, space, 3, &proc);
+ if (code < 0)
+ return code;
+ *ep = proc; /* lookup proc */
+ return o_push_estack;
+ } else {
+ int i, index;
+ os_ptr op = osp;
+ unsigned char *ptr = (unsigned char *)pcs->params.indexed.lookup.table.data;
+
+ *stage = 0;
+ /* We have a string, start by retrieving the index from the op stack */
+ /* Make sure its an integer! */
+ if (!r_has_type(op, t_integer))
+ return_error (gs_error_typecheck);
+ index = op->value.intval;
+ /* And remove it from the stack. */
+ pop(1);
+ op = osp;
+
+ /* Make sure we have enough space on the op stack to hold
+ * one value for each component of the alternate space
+ */
+ push(pcs->params.indexed.n_comps);
+ op -= pcs->params.indexed.n_comps - 1;
+
+ /* Move along the lookup table, one byte for each component , the
+ * number of times required to get to the lookup for this index
+ */
+ ptr += index * pcs->params.indexed.n_comps;
+
+ /* For all the components of the alternate space, push the value
+ * of the component on the stack. The value is given by the byte
+ * from the lookup table divided by 255 to give a value between
+ * 0 and 1.
+ */
+ for (i = 0; i < pcs->params.indexed.n_comps; i++, op++) {
+ float rval = (*ptr++) / 255.0;
+ make_real(op, rval);
+ }
+ return 0;
+ }
+ } else {
+ *stage = 0;
+ *cont = 1;
+ return 0;
+ }
+}
+static int indexedvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ int code;
+ ref hival;
+ os_ptr op = osp;
+
+ if (num_comps < 1)
+ return_error(gs_error_stackunderflow);
+
+ if (!r_has_type(op, t_integer) && !r_has_type(op, t_real))
+ return_error(gs_error_typecheck);
+
+ code = array_get(imemory, space, 2, &hival);
+ if (code < 0)
+ return code;
+
+ if (*values > hival.value.intval)
+ *values = (float)hival.value.intval;
+
+ if (*values < 0)
+ *values = 0;
+
+ /* The PLRM says 'If it is a real number, it is rounded to the nearest integer
+ * but in fact Acrobat simply floors the value.
+ */
+ *values = floor(*values);
+
+ return 0;
+}
+
+/* Pattern */
+static int setpatternspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ gs_color_space *pcs;
+ gs_color_space *pcs_base;
+ uint edepth = ref_stack_count(&e_stack);
+ int code = 0;
+
+ if (i_ctx_p->language_level < 2)
+ return_error(gs_error_undefined);
+
+ *cont = 0;
+ pcs_base = NULL;
+ if (r_is_array(r)) {
+ check_read(*r);
+
+ switch (r_size(r)) {
+ case 1: /* no base space */
+ pcs_base = NULL;
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ case 2:
+ pcs_base = gs_currentcolorspace(igs);
+ if (cs_num_components(pcs_base) < 0) /* i.e., Pattern space */
+ return_error(gs_error_rangecheck);
+ }
+ }
+ pcs = gs_cspace_alloc(imemory, &gs_color_space_type_Pattern);
+ pcs->base_space = pcs_base;
+ pcs->params.pattern.has_base_space = (pcs_base != NULL);
+ rc_increment_cs(pcs_base);
+ code = gs_setcolorspace(igs, pcs);
+ /* release reference from construction */
+ rc_decrement_only_cs(pcs, "zsetpatternspace");
+ if (code < 0) {
+ ref_stack_pop_to(&e_stack, edepth);
+ return code;
+ }
+ make_null(&istate->pattern[0]); /* PLRM: initial color value is a null object */
+ *stage = 0;
+ return (ref_stack_count(&e_stack) == edepth ? 0 : o_push_estack); /* installation will load the caches */
+}
+static int validatepatternspace(i_ctx_t * i_ctx_p, ref **r)
+{
+ int code;
+ ref tref;
+
+ /* since makepattern has already been run, we don't need to do much validation */
+ if (!r_has_type(*r, t_name)) {
+ if (r_is_array(*r)) {
+ if (r_size(*r) > 1) {
+ code = array_get(imemory, *r, 1, &tref);
+ if (code < 0)
+ return code;
+ ref_assign(*r, &tref);
+ } else
+ *r = 0;
+ } else
+ return_error(gs_error_typecheck);
+ } else
+ *r = 0;
+ return 0;
+}
+static int patternalternatespace(i_ctx_t * i_ctx_p, ref *space, ref **r, int *CIESubst)
+{
+ ref tref;
+ int code;
+
+ if (!r_has_type(*r, t_name)) {
+ if (r_is_array(*r)) {
+ if (r_size(*r) > 1) {
+ code = array_get(imemory, space, 1, &tref);
+ if (code < 0)
+ return code;
+ ref_assign(*r, &tref);
+ } else
+ *r = 0;
+ } else
+ return_error(gs_error_typecheck);
+ } else
+ *r = 0;
+ return 0;
+}
+static int patterncomponent(i_ctx_t * i_ctx_p, ref *space, int *n)
+{
+ os_ptr op = osp;
+ int n_comps, code;
+ const gs_color_space * pcs = gs_currentcolorspace(igs);
+ gs_client_color cc;
+
+ /* check for a pattern color space */
+ if ((n_comps = cs_num_components(pcs)) < 0) {
+ n_comps = -n_comps;
+ if (r_has_type(op, t_dictionary)) {
+ ref *pImpl, pPatInst;
+
+ code = dict_find_string(op, "Implementation", &pImpl);
+ if (code > 0) {
+ code = array_get(imemory, pImpl, 0, &pPatInst);
+ if (code < 0)
+ return code;
+ cc.pattern = r_ptr(&pPatInst, gs_pattern_instance_t);
+ if (pattern_instance_uses_base_space(cc.pattern))
+ *n = n_comps;
+ else
+ *n = 1;
+ } else
+ *n = 1;
+ } else
+ *n = 1;
+ } else
+ return_error(gs_error_typecheck);
+
+ return 0;
+}
+static int patternbasecolor(i_ctx_t * i_ctx_p, ref *space, int base, int *stage, int *cont, int *stack_depth)
+{
+ os_ptr op;
+ int i, components=0;
+
+ if (r_size(space) > 1) {
+ const gs_color_space * pcs = gs_currentcolorspace(igs);
+ const gs_client_color * pcc = gs_currentcolor(igs);
+ int n = cs_num_components(pcs);
+ bool push_pattern = n < 0;
+ gs_pattern_instance_t * pinst = pcc->pattern;
+
+ if (pinst != 0 && pattern_instance_uses_base_space(pinst)) {
+ /* check for pattern */
+ if (push_pattern)
+ pop(1); /* The pattern instance */
+ *stage = 0;
+ *cont = 1;
+ return 0;
+ }
+ /* If the pattern isn't yet initialised, or doesn't use the
+ * base space, treat as uncolored and return defaults below
+ * Fall Through.
+ */
+ }
+
+ pop(1);
+ op = osp;
+ switch(base) {
+ case 0:
+ components = 1;
+ break;
+ case 1:
+ case 2:
+ components = 3;
+ break;
+ case 3:
+ components = 4;
+ break;
+ }
+ push(components);
+ op -= components-1;
+ for (i=0;i<components;i++) {
+ make_real(op, (float)0);
+ op++;
+ }
+ if (components == 4) {
+ op--;
+ make_real(op, (float)1);
+ }
+ *stage = 0;
+ *cont = 0;
+ return 0;
+}
+static int patternvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ os_ptr op = osp;
+
+ check_op(1);
+
+ if (!r_has_type(op, t_dictionary) && !r_has_type(op, t_null))
+ return_error(gs_error_typecheck);
+
+ return 0;
+}
+
+/* DevicePixel */
+static int setdevicepspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ int code = 0;
+ gs_color_space *pcs;
+ ref bpp;
+
+ /* The comment in the original PostScript (gs_lev2.ps) said
+ * "DevicePixel is actually a LanguageLevel 3 feature; it is here for
+ * historical reasons." Actually DevicePixel is a Display PostScript
+ * space, as far as I can tell. It certainly isn't a level 3 space.
+ * Preserve the old behaviour anyway.
+ */
+ if (i_ctx_p->language_level < 2)
+ return_error(gs_error_undefined);
+
+ *cont = 0;
+ code = array_get(imemory, r, 1, &bpp);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&bpp, t_integer))
+ return_error(gs_error_typecheck);
+ code = gs_cspace_new_DevicePixel(imemory, &pcs, (int)bpp.value.intval);
+ if (code < 0)
+ return code;
+ code = gs_setcolorspace(igs, pcs);
+ /* release reference from construction */
+ *stage = 0;
+ rc_decrement_only_cs(pcs, "setseparationspace");
+ return code;
+}
+static int validatedevicepspace(i_ctx_t * i_ctx_p, ref **space)
+{
+ int code = 0;
+ ref *r = *space, bpp;
+
+ if (!r_is_array(r))
+ return_error(gs_error_typecheck);
+ /* Validate parameters, check we have enough operands */
+ if (r_size(r) != 2)
+ return_error(gs_error_rangecheck);
+ /* Make sure 'bits per pixel' is an integer */
+ code = array_get(imemory, r, 1, &bpp);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&bpp, t_integer))
+ return_error(gs_error_typecheck);
+
+ /* Make sure 'bits per pixel' lies between 0 and 31 */
+ if (bpp.value.intval < 0 || bpp.value.intval > 31)
+ return_error(gs_error_rangecheck);
+
+ *space = 0;
+ return code;
+}
+static int devicepdomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int code;
+ ref tref;
+
+ code = array_get(imemory, space, 1, &tref);
+ if (code < 0)
+ return code;
+ ptr[0] = 0;
+ ptr[1] = (float)(1 << tref.value.intval);
+ return 0;
+}
+static int deviceprange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int code;
+ ref tref;
+
+ code = array_get(imemory, space, 1, &tref);
+ if (code < 0)
+ return code;
+ ptr[0] = 0;
+ ptr[1] = (float)(1 << tref.value.intval);
+ return 0;
+}
+static int devicepbasecolor(i_ctx_t * i_ctx_p, ref *space, int base, int *stage, int *cont, int *stack_depth)
+{
+ os_ptr op = osp;
+
+ *stage = 0;
+ *cont = 0;
+ make_int(op, 0);
+ return 0;
+}
+static int devicepvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ return 0;
+}
+
+static int set_dev_space(i_ctx_t * i_ctx_p, int components)
+{
+ int code, stage = 1, cont = 0;
+ switch(components) {
+ case 1:
+ code = setgrayspace(i_ctx_p, (ref *)0, &stage, &cont, 1);
+ break;
+ case 3:
+ code = setrgbspace(i_ctx_p, (ref *)0, &stage, &cont, 1);
+ break;
+ case 4:
+ code = setcmykspace(i_ctx_p, (ref *)0, &stage, &cont, 1);
+ break;
+ default:
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ return code;
+}
+
+/* Lab Space */
+
+/* Check that the range of a the ab values is valid */
+static int checkrangeab(i_ctx_t * i_ctx_p, ref *labdict)
+{
+ int code = 0, i;
+ float value[4];
+ ref *tempref, valref;
+
+ code = dict_find_string(labdict, "Range", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != 4)
+ return_error(gs_error_rangecheck);
+
+ for (i=0;i<4;i++) {
+ code = array_get(imemory, tempref, i, &valref);
+ if (code < 0)
+ return code;
+ if (r_has_type(&valref, t_integer))
+ value[i] = (float)valref.value.intval;
+ else if (r_has_type(&valref, t_real))
+ value[i] = (float)valref.value.realval;
+ else
+ return_error(gs_error_typecheck);
+ }
+ if (value[1] < value[0] || value[3] < value[2] )
+ return_error(gs_error_rangecheck);
+ }
+ return 0;
+}
+
+static int setlabspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont,
+ int CIESubst)
+{
+ /* In this case, we will treat this as an ICC color space, with a
+ CIELAB 16 bit profile */
+ ref labdict;
+ int code = 0;
+ float range_buff[4], white[3], black[3];
+ static const float dflt_range[4] = { -100, 100, -100, 100 };
+ static const float dflt_black[3] = {0,0,0}, dflt_white[3] = {0,0,0};
+ int i;
+ gs_client_color cc;
+
+ *cont = 0;
+ code = array_get(imemory, r, 1, &labdict);
+ if (code < 0)
+ return code;
+/* Get all the parts */
+ code = dict_floats_param( imemory, &labdict, "Range", 4, range_buff,
+ dflt_range );
+ for (i = 0; i < 4 && range_buff[i + 1] >= range_buff[i]; i += 2);
+ if (i != 4)
+ return_error(gs_error_rangecheck);
+ code = dict_floats_param( imemory, &labdict, "BlackPoint", 3, black,
+ dflt_black );
+ code = dict_floats_param( imemory, &labdict, "WhitePoint", 3, white,
+ dflt_white );
+ if (white[0] <= 0 || white[1] != 1.0 || white[2] <= 0)
+ return_error(gs_error_rangecheck);
+ code = seticc_lab(i_ctx_p, white, black, range_buff);
+ if ( code < 0)
+ return gs_rethrow(code, "setting PDF lab color space");
+ cc.pattern = 0x00;
+ for (i=0;i<3;i++)
+ cc.paint.values[i] = 0;
+ code = gs_setcolor(igs, &cc);
+ return code;
+}
+
+static int validatelabspace(i_ctx_t * i_ctx_p, ref **r)
+{
+ int code=0;
+ ref *space, labdict;
+
+ space = *r;
+ if (!r_is_array(space))
+ return_error(gs_error_typecheck);
+ /* Validate parameters, check we have enough operands */
+ if (r_size(space) < 2)
+ return_error(gs_error_rangecheck);
+ code = array_get(imemory, space, 1, &labdict);
+ if (code < 0)
+ return code;
+ check_type(labdict, t_dictionary);
+ /* Check the white point, which is required. */
+ code = checkWhitePoint(i_ctx_p, &labdict);
+ if (code != 0)
+ return code;
+ /* The rest are optional. Need to validate though */
+ code = checkBlackPoint(i_ctx_p, &labdict);
+ if (code < 0)
+ return code;
+ /* Range on a b values */
+ code = checkrangeab(i_ctx_p, &labdict);
+ if (code < 0)
+ return code;
+ *r = 0; /* No nested space */
+ return 0;
+}
+
+static int labrange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int i, code;
+ ref CIEdict, *tempref, valref;
+
+ code = array_get(imemory, space, 1, &CIEdict);
+ if (code < 0)
+ return code;
+
+ /* If we have a Range entry, get the values from that */
+ code = dict_find_string(&CIEdict, "Range", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ for (i=0;i<4;i++) {
+ code = array_get(imemory, tempref, i, &valref);
+ if (code < 0)
+ return code;
+ if (r_has_type(&valref, t_integer))
+ ptr[i] = (float)valref.value.intval;
+ else if (r_has_type(&valref, t_real))
+ ptr[i] = (float)valref.value.realval;
+ else
+ return_error(gs_error_typecheck);
+ }
+ } else {
+ /* Default values for Lab */
+ for (i=0;i<2;i++) {
+ ptr[2 * i] = -100;
+ ptr[(2 * i) + 1] = 100;
+ }
+ }
+ return 0;
+}
+
+static int labdomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int i, code;
+ ref CIEdict, *tempref, valref;
+
+ code = array_get(imemory, space, 1, &CIEdict);
+ if (code < 0)
+ return code;
+
+ /* If we have a Range, get the values from that */
+ code = dict_find_string(&CIEdict, "Range", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ for (i=0;i<4;i++) {
+ code = array_get(imemory, tempref, i, &valref);
+ if (code < 0)
+ return code;
+ if (r_has_type(&valref, t_integer))
+ ptr[i] = (float)valref.value.intval;
+ else if (r_has_type(&valref, t_real))
+ ptr[i] = (float)valref.value.realval;
+ else
+ return_error(gs_error_typecheck);
+ }
+ } else {
+ /* Default values for Lab */
+ for (i=0;i<2;i++) {
+ ptr[2 * i] = -100;
+ ptr[(2 * i) + 1] = 100;
+ }
+ }
+ return 0;
+}
+
+static int labbasecolor(i_ctx_t * i_ctx_p, ref *space, int base, int *stage, int *cont, int *stack_depth)
+{
+ os_ptr op;
+ int i, components=1;
+
+ components = 3;
+ pop(components);
+ op = osp;
+ components = 3;
+ push(components);
+ op -= components-1;
+ for (i=0;i<components;i++) {
+ make_real(op, (float)0);
+ op++;
+ }
+ *stage = 0;
+ *cont = 0;
+ return 0;
+}
+
+static int labvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ os_ptr op = osp;
+ int i;
+
+ if (num_comps < 3)
+ return_error(gs_error_stackunderflow);
+ op -= 2;
+ for (i=0;i<3;i++) {
+ if (!r_has_type(op, t_integer) && !r_has_type(op, t_real))
+ return_error(gs_error_typecheck);
+ op++;
+ }
+ return 0;
+}
+
+/* Check that the Matrix of a CalRGB and CalGray space is valid */
+static int checkCalMatrix(i_ctx_t * i_ctx_p, ref *CIEdict)
+{
+ int code;
+ float value[9];
+ ref *tempref;
+
+ code = dict_find_string(CIEdict, "Matrix", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != 9)
+ return_error(gs_error_rangecheck);
+ code = get_cie_param_array(imemory, tempref, 9, value);
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* Check that the Gamma of a CalRGB and CalGray space is valid */
+static int checkGamma(i_ctx_t * i_ctx_p, ref *CIEdict, int numvalues)
+{
+ int code = 0, i;
+ float value[3];
+ ref *tempref, valref;
+
+ code = dict_find_string(CIEdict, "Gamma", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ if (numvalues > 1) {
+ /* Array of gammas (RGB) */
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) != numvalues)
+ return_error(gs_error_rangecheck);
+ for (i=0;i<numvalues;i++) {
+ code = array_get(imemory, tempref, i, &valref);
+ if (code < 0)
+ return code;
+ if (r_has_type(&valref, t_integer))
+ value[i] = (float)valref.value.intval;
+ else if (r_has_type(&valref, t_real))
+ value[i] = (float)valref.value.realval;
+ else
+ return_error(gs_error_typecheck);
+ if (value[i] <= 0) return_error(gs_error_rangecheck);
+ }
+ } else {
+ /* Single gamma (gray) */
+ if (r_has_type(tempref, t_real))
+ value[0] = (float)(tempref->value.realval);
+ else if (r_has_type(tempref, t_integer))
+ value[0] = (float)(tempref->value.intval);
+ else
+ return_error(gs_error_typecheck);
+ if (value[0] <= 0) return_error(gs_error_rangecheck);
+ }
+ }
+ return 0;
+}
+
+/* Here we set up an equivalent ICC form for the CalGray color space */
+static int setcalgrayspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ ref graydict;
+ int code = 0;
+ float gamma, white[3], black[3];
+ double dflt_gamma = 1.0;
+ static const float dflt_black[3] = {0,0,0}, dflt_white[3] = {0,0,0};
+ gs_client_color cc;
+
+ *cont = 0;
+ code = array_get(imemory, r, 1, &graydict);
+ if (code < 0)
+ return code;
+/* Get all the parts */
+ code = dict_float_param(&graydict, "Gamma",
+ dflt_gamma, &gamma);
+ if (gamma <= 0 ) return_error(gs_error_rangecheck);
+ code = dict_floats_param( imemory,
+ &graydict,
+ "BlackPoint",
+ 3,
+ black,
+ dflt_black );
+ code = dict_floats_param( imemory,
+ &graydict,
+ "WhitePoint",
+ 3,
+ white,
+ dflt_white );
+ if (white[0] <= 0 || white[1] != 1.0 || white[2] <= 0)
+ return_error(gs_error_rangecheck);
+ code = seticc_cal(i_ctx_p, white, black, &gamma, NULL, 1,
+ graydict.value.saveid);
+ if ( code < 0)
+ return gs_rethrow(code, "setting CalGray color space");
+ cc.pattern = 0x00;
+ cc.paint.values[0] = 0;
+ code = gs_setcolor(igs, &cc);
+ return code;
+}
+
+static int validatecalgrayspace(i_ctx_t * i_ctx_p, ref **r)
+{
+ int code=0;
+ ref *space, calgraydict;
+
+ space = *r;
+ if (!r_is_array(space))
+ return_error(gs_error_typecheck);
+ /* Validate parameters, check we have enough operands */
+ if (r_size(space) < 2)
+ return_error(gs_error_rangecheck);
+ code = array_get(imemory, space, 1, &calgraydict);
+ if (code < 0)
+ return code;
+ check_type(calgraydict, t_dictionary);
+ /* Check the white point, which is required */
+ /* We have to have a white point */
+ /* Check white point exists, and is an array of three numbers */
+ code = checkWhitePoint(i_ctx_p, &calgraydict);
+ if (code != 0)
+ return code;
+ /* The rest are optional. Need to validate though */
+ code = checkBlackPoint(i_ctx_p, &calgraydict);
+ if (code < 0)
+ return code;
+ /* Check Gamma values */
+ code = checkGamma(i_ctx_p, &calgraydict, 1);
+ if (code < 0)
+ return code;
+ *r = 0; /* No nested space */
+ return 0;
+}
+
+/* Here we set up an equivalent ICC form for the CalRGB color space */
+static int setcalrgbspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ ref rgbdict;
+ int code = 0;
+ float gamma[3], white[3], black[3], matrix[9];
+ static const float dflt_gamma[3] = { 1.0, 1.0, 1.0 };
+ static const float dflt_black[3] = {0,0,0}, dflt_white[3] = {0,0,0};
+ static const float dflt_matrix[9] = {1,0,0,0,1,0,0,0,1};
+ int i;
+ gs_client_color cc;
+
+ *cont = 0;
+ code = array_get(imemory, r, 1, &rgbdict);
+ if (code < 0)
+ return code;
+/* Get all the parts */
+ code = dict_floats_param( imemory,
+ &rgbdict,
+ "Gamma",
+ 3,
+ gamma,
+ dflt_gamma );
+ if (gamma[0] <= 0 || gamma[1] <= 0 || gamma[2] <= 0)
+ return_error(gs_error_rangecheck);
+ code = dict_floats_param( imemory,
+ &rgbdict,
+ "BlackPoint",
+ 3,
+ black,
+ dflt_black );
+ code = dict_floats_param( imemory,
+ &rgbdict,
+ "WhitePoint",
+ 3,
+ white,
+ dflt_white );
+ if (white[0] <= 0 || white[1] != 1.0 || white[2] <= 0)
+ return_error(gs_error_rangecheck);
+ code = dict_floats_param( imemory,
+ &rgbdict,
+ "Matrix",
+ 9,
+ matrix,
+ dflt_matrix );
+ code = seticc_cal(i_ctx_p, white, black, gamma, matrix, 3, rgbdict.value.saveid);
+ if ( code < 0)
+ return gs_rethrow(code, "setting CalRGB color space");
+ cc.pattern = 0x00;
+ for (i=0;i<3;i++)
+ cc.paint.values[i] = 0;
+ code = gs_setcolor(igs, &cc);
+ return code;
+}
+
+static int validatecalrgbspace(i_ctx_t * i_ctx_p, ref **r)
+{
+ int code=0;
+ ref *space, calrgbdict;
+
+ space = *r;
+ if (!r_is_array(space))
+ return_error(gs_error_typecheck);
+ /* Validate parameters, check we have enough operands */
+ if (r_size(space) < 2)
+ return_error(gs_error_rangecheck);
+ code = array_get(imemory, space, 1, &calrgbdict);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&calrgbdict, t_dictionary))
+ return_error(gs_error_typecheck);
+ /* Check the white point, which is required */
+ code = checkWhitePoint(i_ctx_p, &calrgbdict);
+ if (code != 0)
+ return code;
+ /* The rest are optional. Need to validate though */
+ code = checkBlackPoint(i_ctx_p, &calrgbdict);
+ if (code < 0)
+ return code;
+ /* Check Gamma values */
+ code = checkGamma(i_ctx_p, &calrgbdict, 3);
+ if (code < 0)
+ return code;
+ /* Check Matrix */
+ code = checkCalMatrix(i_ctx_p, &calrgbdict);
+ if (code < 0)
+ return code;
+ *r = 0; /* No nested space */
+ return 0;
+}
+
+/* ICCBased */
+static int iccrange(i_ctx_t * i_ctx_p, ref *space, float *ptr);
+static int seticcspace(i_ctx_t * i_ctx_p, ref *r, int *stage, int *cont, int CIESubst)
+{
+ os_ptr op = osp;
+ ref ICCdict, *tempref, *altref=NULL, *nocie = NULL;
+ int components, code;
+ float range[8];
+
+ code = dict_find_string(systemdict, "NOCIE", &nocie);
+ if (code > 0) {
+ if (!r_has_type(nocie, t_boolean))
+ return_error(gs_error_typecheck);
+ }
+ *cont = 0;
+ do {
+ switch(*stage) {
+ case 0:
+ (*stage)++;
+ code = array_get(imemory, r, 1, &ICCdict);
+ if (code < 0)
+ return code;
+ code = dict_find_string(&ICCdict, "N", &tempref);
+ if (code < 0)
+ return code;
+ if (code == 0)
+ return gs_note_error(gs_error_undefined);
+ components = tempref->value.intval;
+ if (components > count_of(range)/2)
+ return_error(gs_error_rangecheck);
+
+ /* Don't allow ICCBased spaces if NOCIE is true */
+ if (nocie && nocie->value.boolval) {
+ code = dict_find_string(&ICCdict, "Alternate", &altref); /* Alternate is optional */
+ if (code > 0 && (altref != NULL) && (r_type(altref) != t_null)) {
+ /* The PDF interpreter sets a null Alternate. If we have an
+ * Alternate, and its not null, and NOCIE is true, then use the
+ * Alternate instead of the ICC
+ */
+ push(1);
+ ref_assign(op, altref);
+ /* If CIESubst, we are already substituting for CIE, so use nosubst
+ * to prevent further substitution!
+ */
+ return setcolorspace_nosubst(i_ctx_p);
+ } else {
+ /* There's no /Alternate (or it is null), set a default space
+ * based on the number of components in the ICCBased space
+ */
+ code = set_dev_space(i_ctx_p, components);
+ if (code != 0)
+ return code;
+ *stage = 0;
+ }
+ } else {
+ code = iccrange(i_ctx_p, r, (float *)&range);
+ if (code < 0)
+ return code;
+ code = dict_find_string(&ICCdict, "DataSource", &tempref);
+ if (code == 0)
+ return gs_note_error(gs_error_undefined);
+ /* Check for string based ICC and convert to a file */
+ if (r_has_type(tempref, t_string)){
+ uint n = r_size(tempref);
+ ref rss;
+
+ code = make_rss(i_ctx_p, &rss, tempref->value.const_bytes, n, r_space(tempref), 0L, n, false);
+ if (code < 0)
+ return code;
+ ref_assign(tempref, &rss);
+ }
+ /* Make space on operand stack to pass the ICC dictionary */
+ push(1);
+ ref_assign(op, &ICCdict);
+ code = seticc(i_ctx_p, components, op, (float *)&range);
+ if (code < 0) {
+ code = dict_find_string(&ICCdict, "Alternate", &altref); /* Alternate is optional */
+ if (code > 0 && (altref != NULL) && (r_type(altref) != t_null)) {
+ /* We have a /Alternate in the ICC space */
+ /* Our ICC dictionary still on operand stack, we can reuse the
+ * slot on the stack to hold the alternate space.
+ */
+ ref_assign(op, (ref *)altref);
+ /* If CIESubst, we are already substituting for CIE, so use nosubst
+ * to prevent further substitution!
+ */
+ if (CIESubst)
+ return setcolorspace_nosubst(i_ctx_p);
+ else
+ return zsetcolorspace(i_ctx_p);
+ } else {
+ /* We have no /Alternate in the ICC space, use hte /N key to
+ * determine an 'appropriate' default space.
+ */
+ code = set_dev_space(i_ctx_p, components);
+ if (code != 0)
+ return code;
+ *stage = 0;
+ }
+ pop(1);
+ }
+ if (code != 0)
+ return code;
+ }
+ break;
+ case 1:
+ /* All done, exit */
+ *stage = 0;
+ code = 0;
+ break;
+ default:
+ return_error (gs_error_rangecheck);
+ break;
+ }
+ }while(*stage);
+ return code;
+}
+
+static int iccompareproc(i_ctx_t *i_ctx_p, ref *space, ref *testspace)
+{
+ int code1, code2;
+ ref ICCdict1, ICCdict2, *tempref1, *tempref2;
+ int buff_size;
+
+ code1 = array_get(imemory, space, 1, &ICCdict1);
+ if (code1 < 0)
+ return 0;
+ code2 = array_get(imemory, testspace, 1, &ICCdict2);
+ if (code2 < 0)
+ return 0;
+
+ /* As a quick check see if current is same as new */
+ if (ICCdict1.value.bytes == ICCdict2.value.bytes)
+ return 1;
+
+ /* Need to check all the various parts */
+ code1 = dict_find_string(&ICCdict1, "N", &tempref1);
+ code2 = dict_find_string(&ICCdict2, "N", &tempref2);
+ if (code1 != code2)
+ return 0;
+ if (tempref1->value.intval != tempref2->value.intval)
+ return 0;
+
+ if (!comparedictkey(i_ctx_p, &ICCdict1, &ICCdict2, (char *)"Range"))
+ return 0;
+
+ code1 = dict_find_string(&ICCdict1, "DataSource", &tempref1);
+ if (code1 <= 0)
+ return 0;
+ code2 = dict_find_string(&ICCdict2, "DataSource", &tempref2);
+ if (code2 <= 0)
+ return 0;
+ if (r_size(tempref1) != r_size(tempref2))
+ return 0;
+
+ buff_size = r_size(tempref1);
+ if (memcmp(tempref1->value.const_bytes, tempref2->value.const_bytes, buff_size) == 0)
+ return 1;
+ else
+ return 0;
+}
+
+static int validateiccspace(i_ctx_t * i_ctx_p, ref **r)
+{
+ int code=0, i, components = 0;
+ ref *space, *tempref, valref, ICCdict, sref;
+
+ space = *r;
+ if (!r_is_array(space))
+ return_error(gs_error_typecheck);
+ /* Validate parameters, check we have enough operands */
+ if (r_size(space) != 2)
+ return_error(gs_error_rangecheck);
+
+ code = array_get(imemory, space, 1, &ICCdict);
+ if (code < 0)
+ return code;
+
+ check_type(ICCdict, t_dictionary);
+
+ code = dict_find_string(&ICCdict, "N", &tempref);
+ if (code < 0)
+ return code;
+ if (code == 0)
+ return gs_note_error(gs_error_undefined);
+ if (!r_has_type(tempref, t_null)) {
+ if (!r_has_type(tempref, t_integer))
+ return_error(gs_error_typecheck);
+ components = tempref->value.intval;
+ } else
+ return_error(gs_error_typecheck);
+ code = dict_find_string(&ICCdict, "DataSource", &tempref);
+ if (code <= 0)
+ return_error(gs_error_typecheck);
+ if (!r_has_type(tempref, t_null)) {
+ if (!r_has_type(tempref, t_string) && !r_has_type(tempref, t_file))
+ return_error(gs_error_typecheck);
+ } else
+ return_error(gs_error_typecheck);
+
+ /* Following are optional entries */
+ code = dict_find_string(&ICCdict, "Range", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ if (!r_is_array(tempref))
+ return_error(gs_error_typecheck);
+ if (r_size(tempref) < (components * 2))
+ return_error(gs_error_rangecheck);
+ for (i=0;i<components * 2;i++) {
+ code = array_get(imemory, tempref, i, &valref);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&valref, t_integer) && !r_has_type(&valref, t_real))
+ return_error(gs_error_typecheck);
+ }
+ }
+ code = dict_find_string(&ICCdict, "Alternate", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ ref_assign(*r, tempref);
+ if (r_has_type(tempref, t_name)) {
+ name_string_ref(imemory, tempref, &sref);
+ if (sref.value.bytes && strncmp((const char *)sref.value.bytes, "Pattern", 7) == 0)
+ return_error(gs_error_typecheck);
+ } else {
+ if (r_is_array(tempref)) {
+ code = array_get(imemory, tempref, 0, &valref);
+ if (code < 0)
+ return code;
+ if (!r_has_type(&valref, t_name) && !r_has_type(&valref, t_string))
+ return_error(gs_error_typecheck);
+ if (r_has_type(&valref, t_name))
+ name_string_ref(imemory, &valref, &sref);
+ else
+ sref.value.bytes = valref.value.bytes;
+ if (sref.value.bytes && strncmp((const char *)sref.value.bytes, "Pattern", 7) == 0)
+ return_error(gs_error_typecheck);
+ } else
+ return_error(gs_error_typecheck);
+ }
+ } else {
+ ref nameref;
+
+ switch (components) {
+ case 1:
+ code = name_enter_string(imemory, "DeviceGray", &nameref);
+ break;
+ case 3:
+ code = name_enter_string(imemory, "DeviceRGB", &nameref);
+ break;
+ case 4:
+ code = name_enter_string(imemory, "DeviceCMYK", &nameref);
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ /* In case this space is the /ALternate for a previous ICCBased space
+ * insert the named space into the ICC dictionary. If we simply returned
+ * the named space, as before, then we are replacing the second ICCBased
+ * space in the first ICCBased space with the named space!
+ */
+ code = idict_put_string(&ICCdict, "Alternate", &nameref);
+ if (code < 0)
+ return code;
+
+ /* And now revalidate with the newly updated dictionary */
+ return validateiccspace(i_ctx_p, r);
+ }
+ return code;
+}
+
+static int iccalternatespace(i_ctx_t * i_ctx_p, ref *space, ref **r, int *CIESubst)
+{
+ int components, code = 0;
+ ref *tempref, ICCdict;
+
+ if (!r_is_array(space))
+ return_error(gs_error_typecheck);
+ /* Validate parameters, check we have enough operands */
+ if (r_size(space) != 2)
+ return_error(gs_error_rangecheck);
+
+ code = array_get(imemory, space, 1, &ICCdict);
+ if (code < 0)
+ return code;
+
+ code = dict_find_string(&ICCdict, "N", &tempref);
+ if (code < 0)
+ return code;
+ if (code == 0)
+ return gs_note_error(gs_error_undefined);
+
+ components = tempref->value.intval;
+
+ code = dict_find_string(&ICCdict, "Alternate", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ *r = tempref;
+ } else {
+ switch (components) {
+ case 1:
+ code = name_enter_string(imemory, "DeviceGray", *r);
+ break;
+ case 3:
+ code = name_enter_string(imemory, "DeviceRGB", *r);
+ break;
+ case 4:
+ code = name_enter_string(imemory, "DeviceCMYK", *r);
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ }
+ *CIESubst = 1;
+ return code;
+}
+static int icccomponents(i_ctx_t * i_ctx_p, ref *space, int *n)
+{
+ int code = 0;
+ ref *tempref, ICCdict;
+
+ code = array_get(imemory, space, 1, &ICCdict);
+ if (code < 0)
+ return code;
+
+ code = dict_find_string(&ICCdict, "N", &tempref);
+ if (code < 0)
+ return code;
+ if (code == 0)
+ return gs_note_error(gs_error_undefined);
+ *n = tempref->value.intval;
+ return 0;
+}
+static int iccdomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int components, i, code = 0;
+ ref *tempref, ICCdict, valref;
+
+ code = array_get(imemory, space, 1, &ICCdict);
+ if (code < 0)
+ return code;
+ code = dict_find_string(&ICCdict, "N", &tempref);
+ if (code < 0)
+ return code;
+ if (code == 0)
+ return gs_note_error(gs_error_undefined);
+ components = tempref->value.intval;
+ code = dict_find_string(&ICCdict, "Range", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ for (i=0;i<components * 2;i++) {
+ code = array_get(imemory, tempref, i, &valref);
+ if (code < 0)
+ return code;
+ if (r_has_type(&valref, t_integer))
+ ptr[i * 2] = (float)valref.value.intval;
+ else
+ ptr[i * 2] = valref.value.realval;
+ }
+ } else {
+ for (i=0;i<components;i++) {
+ ptr[i * 2] = 0;
+ ptr[(i * 2) + 1] = 1;
+ }
+ }
+ return 0;
+}
+static int iccrange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ int components, i, code = 0;
+ ref *tempref, ICCdict, valref;
+
+ code = array_get(imemory, space, 1, &ICCdict);
+ if (code < 0)
+ return code;
+ code = dict_find_string(&ICCdict, "N", &tempref);
+ if (code < 0)
+ return code;
+ if (code == 0)
+ return gs_note_error(gs_error_undefined);
+ components = tempref->value.intval;
+ code = dict_find_string(&ICCdict, "Range", &tempref);
+ if (code > 0 && !r_has_type(tempref, t_null)) {
+ for (i=0;i<components * 2;i++) {
+ code = array_get(imemory, tempref, i, &valref);
+ if (code < 0)
+ return code;
+ if (r_has_type(&valref, t_integer))
+ ptr[i] = (float)valref.value.intval;
+ else
+ ptr[i] = (float)valref.value.realval;
+ }
+ } else {
+ for (i=0;i<components;i++) {
+ ptr[i * 2] = 0;
+ ptr[(i * 2) + 1] = 1;
+ }
+ }
+ return 0;
+}
+static int iccbasecolor(i_ctx_t * i_ctx_p, ref *space, int base, int *stage, int *cont, int *stack_depth)
+{
+ *stage = 0;
+ *cont = 1;
+ return 0;
+}
+static int iccvalidate(i_ctx_t *i_ctx_p, ref *space, float *values, int num_comps)
+{
+ return 0;
+}
+
+static int dummydomain(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ return 0;
+}
+static int dummyrange(i_ctx_t * i_ctx_p, ref *space, float *ptr)
+{
+ return 0;
+}
+static int onecomponent(i_ctx_t * i_ctx_p, ref *space, int *n)
+{
+ *n = 1;
+ return 0;
+}
+static int threecomponent(i_ctx_t * i_ctx_p, ref *space, int *n)
+{
+ *n = 3;
+ return 0;
+}
+static int fourcomponent(i_ctx_t * i_ctx_p, ref *space, int *n)
+{
+ *n = 4;
+ return 0;
+}
+static int truecompareproc(i_ctx_t *i_ctx_p, ref *space, ref *testspace)
+{
+ return 1;
+}
+static int falsecompareproc(i_ctx_t *i_ctx_p, ref *space, ref *testspace)
+{
+ return 0;
+}
+
+PS_colour_space_t colorProcs[] = {
+ {(char *)"DeviceGray", setgrayspace, 0, 0, onecomponent, grayrange, graydomain,
+ graybasecolor, 0, grayvalidate, truecompareproc, grayinitialproc},
+ {(char *)"DeviceRGB", setrgbspace, 0, 0, threecomponent, rgbrange, rgbdomain,
+ rgbbasecolor, 0, rgbvalidate, truecompareproc, rgbinitialproc},
+ {(char *)"DeviceCMYK", setcmykspace, 0, 0, fourcomponent, cmykrange, cmykdomain,
+ cmykbasecolor, 0, cmykvalidate, truecompareproc, cmykinitialproc},
+ {(char *)"CIEBasedA", setcieaspace, validatecieaspace, 0, onecomponent, ciearange, cieadomain,
+ ciebasecolor, 0, cieavalidate, cieacompareproc, 0},
+ {(char *)"CIEBasedABC", setcieabcspace, validatecieabcspace, 0, threecomponent, cieabcrange, cieabcdomain,
+ ciebasecolor, 0, cieabcvalidate, cieabccompareproc, 0},
+ {(char *)"CIEBasedDEF", setciedefspace, validateciedefspace, 0, threecomponent, ciedefrange, ciedefdomain,
+ ciebasecolor, 0, ciedefvalidate, ciedefcompareproc, 0},
+ {(char *)"CIEBasedDEFG", setciedefgspace, validateciedefgspace, 0, fourcomponent, ciedefgrange, ciedefgdomain,
+ ciebasecolor, 0, ciedefgvalidate, ciedefgcompareproc, 0},
+ {(char *)"Separation", setseparationspace, validateseparationspace, separationalternatespace, onecomponent, seprange, sepdomain,
+ sepbasecolor, septransform, sepvalidate, sepcompareproc, sepinitialproc},
+ {(char *)"DeviceN", setdevicenspace, validatedevicenspace, devicenalternatespace, devicencomponents, devicenrange, devicendomain,
+ devicenbasecolor, devicentransform, devicenvalidate, devicencompareproc, deviceninitialproc},
+ {(char *)"Indexed", setindexedspace, validateindexedspace, indexedalternatespace, onecomponent, indexedrange, indexeddomain,
+ indexedbasecolor, 0, indexedvalidate, falsecompareproc, 0},
+ {(char *)"Pattern", setpatternspace, validatepatternspace, patternalternatespace, patterncomponent, dummyrange, dummydomain,
+ patternbasecolor, 0, patternvalidate, falsecompareproc, 0},
+ {(char *)"DevicePixel", setdevicepspace, validatedevicepspace, 0, onecomponent, deviceprange, devicepdomain,
+ devicepbasecolor, 0, devicepvalidate, falsecompareproc, 0},
+ {(char *)"ICCBased", seticcspace, validateiccspace, iccalternatespace, icccomponents, iccrange, iccdomain,
+ iccbasecolor, 0, iccvalidate, iccompareproc, 0},
+ {(char *)"Lab", setlabspace, validatelabspace, 0, threecomponent, labrange, labdomain,
+ labbasecolor, 0, labvalidate, truecompareproc, 0},
+ {(char *)"CalGray", setcalgrayspace, validatecalgrayspace, 0, onecomponent, grayrange, graydomain,
+ graybasecolor, 0, grayvalidate, truecompareproc, grayinitialproc},
+ {(char *)"CalRGB", setcalrgbspace, validatecalrgbspace, 0, threecomponent, rgbrange, rgbdomain,
+ rgbbasecolor, 0, rgbvalidate, truecompareproc, rgbinitialproc}
+};
+
+/*
+ * Given a color space, this finds the appropriate object from the list above
+ */
+int get_space_object(i_ctx_t *i_ctx_p, ref *arr, PS_colour_space_t **obj)
+{
+ ref spacename, nref;
+ int i, nprocs = sizeof(colorProcs) / sizeof(PS_colour_space_t), code;
+
+ /* If the spaece is an array, the first element is always the name */
+ if (r_is_array(arr))
+ code = array_get(imemory, arr, 0, &spacename);
+ else
+ ref_assign(&spacename, arr);
+
+ /* Check that it really is a name */
+ if (!r_has_type(&spacename, t_name))
+ return_error(gs_error_typecheck);
+
+ /* Find the relevant color space object */
+ for (i=0;i<nprocs;i++) {
+ code = names_ref(imemory->gs_lib_ctx->gs_name_table, (const byte *)colorProcs[i].name, strlen(colorProcs[i].name), &nref, 0);
+ if (code < 0)
+ return code;
+ if (name_eq(&spacename, &nref)) {
+ *obj = &colorProcs[i];
+ return 0;
+ }
+ }
+ return_error(gs_error_undefined);
+}
+/*
+ * This routine checks all the color spaces in an operand by
+ * calling the specific 'validate' method for each in turn. It also
+ * returns the 'depth' which is the number of nested spaces.
+ */
+static int validate_spaces(i_ctx_t *i_ctx_p, ref *arr, int *depth)
+{
+ ref space, *sp = &space;
+ int code = 0;
+ PS_colour_space_t *obj;
+
+ ref_assign(&space, arr);
+ *depth = 0;
+ do {
+ code = get_space_object(i_ctx_p, sp, &obj);
+ if (code < 0)
+ return code;
+
+ (*depth)++;
+ if (!obj->validateproc)
+ break;
+
+ code = obj->validateproc(i_ctx_p, &sp);
+ if (code < 0)
+ return code;
+ }while(sp);
+ return 0;
+}
+/*
+ * The routine which does all the setcolor dispatching. This is initially set up by
+ * zsetcolor above. Because setcolorspace samples the space and converts the tint
+ * transform to a function, we don't need to run the PS tint transform in order to
+ * set the color. However, some applications, notably Photoshop 5 and above, rely
+ * on the tint transform being executed, so we must do so if the normal PostScript
+ * processing would result in the tintr transform being executed.
+ *
+ * We check each space in turn to see whether we would normally run the tint
+ * transform, eg Indexed is always executed, Separation and DeviceN only if the
+ * required ink(s) aren't present in the device. If we discover that any space
+ * doesn't require a tint transform, then we can short-circuit the processing.
+ * Otherwise we set up to execute the tint transform.
+ */
+static int
+setcolor_cont(i_ctx_t *i_ctx_p)
+{
+ ref arr, *parr = &arr;
+ es_ptr ep = esp;
+ int i=0, code = 0,depth, usealternate, stage, stack_depth, CIESubst = 0;
+ PS_colour_space_t *obj;
+
+ stack_depth = (int)ep[-3].value.intval;
+ depth = (int)ep[-2].value.intval;
+ stage = (int)ep[-1].value.intval;
+ /* If we get a continuation from a sub-procedure, we will want to come back
+ * here afterward, to do any remaining spaces. We need to set up for that now.
+ * so that our continuation is ahead of the sub-proc's continuation.
+ */
+ check_estack(1);
+ push_op_estack(setcolor_cont);
+
+ while (code == 0) {
+ ref_assign(&arr, ep);
+ /* Run along the nested color spaces until we get to the first one
+ * that we haven't yet processed (given by 'depth')
+ */
+ for (i=0;i<=depth;i++) {
+ code = get_space_object(i_ctx_p, parr, &obj);
+ if (code < 0)
+ return code;
+
+ if (i < (depth)) {
+ if (!obj->alternateproc) {
+ return_error(gs_error_typecheck);
+ }
+ code = obj->alternateproc(i_ctx_p, parr, &parr, &CIESubst);
+ if (code < 0)
+ return code;
+ }
+ }
+ if (obj->runtransformproc) {
+ code = obj->runtransformproc(i_ctx_p, &istate->colorspace[0].array, &usealternate, &stage, &stack_depth);
+ make_int(&ep[-3], stack_depth);
+ make_int(&ep[-1], stage);
+ if (code != 0) {
+ return code;
+ }
+ make_int(&ep[-2], ++depth);
+ if (!usealternate)
+ break;
+ } else
+ break;
+ }
+ /* Remove our next continuation and our data */
+ obj->numcomponents(i_ctx_p, parr, &i);
+ pop(i);
+ esp -= 5;
+ return o_pop_estack;
+}
+/*
+ * The routine which does all the setcolorspace dispatching. This is initially set up by
+ * zsetcolorspace above. It starts by descending to the bottom-most space
+ * and setting that as the current space. It then descends the array again
+ * to the next-to-bottom- space and sets that as the current, and so on.
+ *
+ * The 'stage' parameter is passed in to each 'set' method. If a method needs
+ * to do a continuation itself (eg sample a space) then it should set the stage
+ * to a non-zero value. When the continuation is complete we return here, and
+ * attempt to 'set' the same space again. This time stage will be whatever was
+ * set the first time, which is a signal to the 'set' routine that a continuation
+ * took place, and is complete. Stage must always be set to 0 when a 'set'
+ * of a color space is complete.
+ */
+static int
+setcolorspace_cont(i_ctx_t *i_ctx_p)
+{
+ ref arr, *parr = &arr;
+ os_ptr op = osp;
+ es_ptr ep = esp, pdepth, pstage, pCIESubst;
+ int i, code = 0,depth, stage, cont, CIESubst = 0;
+ PS_colour_space_t *obj;
+
+ pCIESubst = &ep[-3];
+ pdepth = &ep[-2];
+ pstage = &ep[-1];
+
+ CIESubst = (int)pCIESubst->value.intval;
+ depth = (int)pdepth->value.intval;
+ stage = (int)pstage->value.intval;
+ /* If we get a continuation from a sub-procedure, we will want to come back
+ * here afterward, to do any remaining stages. We need to set up for that now.
+ * so that our continuation is ahead of the sub-proc's continuation.
+ */
+ check_estack(1);
+ push_op_estack(setcolorspace_cont);
+
+ while (code == 0 && depth) {
+ ref_assign(&arr, ep);
+ /* Run along the nested color spaces until we get to the lowest one
+ * that we haven't yet processed (given by 'depth')
+ */
+ for (i = 0;i < depth;i++) {
+ code = get_space_object(i_ctx_p, parr, &obj);
+ if (code < 0)
+ return code;
+
+ if (i < (depth - 1)) {
+ if (!obj->alternateproc) {
+ return_error(gs_error_typecheck);
+ }
+ code = obj->alternateproc(i_ctx_p, parr, &parr, &CIESubst);
+ if (code < 0)
+ return code;
+ }
+ }
+
+ code = obj->setproc(i_ctx_p, parr, &stage, &cont, CIESubst);
+ make_int(pstage, stage);
+ if (code != 0) {
+ if (code < 0)
+ esp -= 5;
+ return code;
+ }
+ if (!cont) {
+ /* Completed that space, decrement the 'depth' */
+ make_int(pdepth, --depth);
+ parr = &arr;
+ }
+ }
+ if (code == 0) {
+ /* Remove our next continuation and our data */
+ esp -= 5;
+ op = osp;
+ istate->colorspace[0].array = *op;
+ /* Remove the colorspace array form the operand stack */
+ pop(1);
+ code = o_pop_estack;
+ }
+ return code;
+}
+/*
+ * The routine which does all the dispatching for the device-space specific
+ * operators below (eg setgray). This is initially set up by the routines below.
+ *
+ * It would seem unnecessary to have a continuation procedure, because at first
+ * sight these can only be a single space with no alternate and can't require
+ * sampling, because they are device space. However if UseCIEColor is true, then
+ * we will actually use a Default Color Space Array in place of the requested color
+ * space. These are often CIEBased spaces, and these do need to be sampled. So
+ * actually we do need a continuation procedure, unfortunately.
+ *
+ * Also, we need to set the initial color value after we have set the color space.
+ */
+static int
+setdevicecolor_cont(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep = esp, pstage;
+ int code = 0, stage, base;
+
+ pstage = ep;
+ base = (int)ep[-1].value.intval;
+ stage = (int)pstage->value.intval;
+ /* If we get a continuation from a sub-procedure, we will want to come back
+ * here afterward, to do any remaining stages. We need to set up for that now.
+ * so that our continuation is ahead of the sub-proc's continuation.
+ */
+ check_estack(1);
+ /* May need to push a /Device... name on the stack so make sure we have space */
+ check_ostack(1);
+ /* The push_op_estack macro increments esp before use, so we don't need to */
+ push_op_estack(setdevicecolor_cont);
+
+ do {
+ switch(stage) {
+ case 0:
+ make_int(pstage, ++stage);
+ push(1);
+ switch(base) {
+ case 0: /* DeviceGray */
+ code = name_enter_string(imemory, "DeviceGray", op);
+ break;
+ case 1: /* DeviceRGB */
+ code = name_enter_string(imemory, "DeviceRGB", op);
+ break;
+ case 2: /* DeviceCMYK */
+ code = name_enter_string(imemory, "DeviceCMYK", op);
+ break;
+ }
+ if (code < 0)
+ return code;
+ code = zsetcolorspace(i_ctx_p);
+ if (code != 0)
+ return code;
+ break;
+ case 1:
+ make_int(pstage, ++stage);
+ code = zsetcolor(i_ctx_p);
+ if (code != 0)
+ return code;
+ break;
+ case 2:
+ esp -= 3;
+ return o_pop_estack;
+ break;
+ }
+ }while(1);
+ return 0;
+}
+
+/* These routines implement the device-space set color routines
+ * These set both the space and the color in a single operation.
+ * Previously these were implemented in PostScript.
+ */
+static int
+zsetgray(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp; /* required by "push" macro */
+ float value;
+ int code;
+
+ /* Gather numeric operand value(s) */
+ code = float_params(op, 1, &value);
+ if (code < 0)
+ return code;
+ /* Clamp numeric operand range(s) */
+ if (value < 0)
+ value = 0;
+ else if (value > 1)
+ value = 1;
+ code = make_floats(op, &value, 1);
+ if (code < 0)
+ return code;
+
+ /* Set up for the continuation procedure which will do the work */
+ /* Make sure the exec stack has enough space */
+ check_estack(5);
+ push_mark_estack(es_other, colour_cleanup);
+ esp++;
+ /* variable to hold base type (0 = gray) */
+ make_int(esp, 0);
+ esp++;
+ /* Store the 'stage' of processing (initially 0) */
+ make_int(esp, 0);
+ /* Finally, the actual continuation routine */
+ push_op_estack(setdevicecolor_cont);
+ return o_push_estack;
+}
+static int
+zsethsbcolor(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp; /* required by "push" macro */
+ int code, i;
+ float values[3];
+
+ /* Gather numeric operand value(s) (also checks type) */
+ code = float_params(op, 3, (float *)&values);
+ if (code < 0)
+ return code;
+ /* Clamp numeric operand range(s) */
+ for (i = 0;i < 3; i++) {
+ if (values[i] < 0)
+ values[i] = 0;
+ else if (values[i] > 1)
+ values[i] = 1;
+ }
+
+ hsb2rgb((float *)&values);
+
+ code = make_floats(&op[-2], (const float *)&values, 3);
+ if (code < 0)
+ return code;
+
+ /* Set up for the continuation procedure which will do the work */
+ /* Make sure the exec stack has enough space */
+ check_estack(5);
+ push_mark_estack(es_other, colour_cleanup);
+ esp++;
+ /* variable to hold base type (1 = RGB) */
+ make_int(esp, 1);
+ esp++;
+ /* Store the 'stage' of processing (initially 0) */
+ make_int(esp, 0);
+ /* Finally, the actual continuation routine */
+ push_op_estack(setdevicecolor_cont);
+ return o_push_estack;
+}
+static int
+zsetrgbcolor(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp; /* required by "push" macro */
+ int code, i;
+ float values[3];
+
+ /* Gather numeric operand value(s) (also checks type) */
+ code = float_params(op, 3, (float *)&values);
+ if (code < 0)
+ return code;
+ /* Clamp numeric operand range(s) */
+ for (i = 0;i < 3; i++) {
+ if (values[i] < 0)
+ values[i] = 0;
+ else if (values[i] > 1)
+ values[i] = 1;
+ }
+
+ code = make_floats(&op[-2], (const float *)&values, 3);
+ if (code < 0)
+ return code;
+
+ /* Set up for the continuation procedure which will do the work */
+ /* Make sure the exec stack has enough space */
+ check_estack(5);
+ push_mark_estack(es_other, colour_cleanup);
+ esp++;
+ /* variable to hold base type (1 = RGB) */
+ make_int(esp, 1);
+ esp++;
+ /* Store the 'stage' of processing (initially 0) */
+ make_int(esp, 0);
+ /* Finally, the actual continuation routine */
+ push_op_estack(setdevicecolor_cont);
+ return o_push_estack;
+}
+
+static int
+zsetcmykcolor(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp; /* required by "push" macro */
+ int code, i;
+ float values[4];
+
+ /* Gather numeric operand value(s) (also checks type) */
+ code = float_params(op, 4, (float *)&values);
+ if (code < 0)
+ return code;
+ /* Clamp numeric operand range(s) */
+ for (i = 0;i < 4; i++) {
+ if (values[i] < 0)
+ values[i] = 0;
+ else if (values[i] > 1)
+ values[i] = 1;
+ }
+
+ code = make_floats(&op[-3], (const float *)&values, 4);
+ if (code < 0)
+ return code;
+
+ /* Set up for the continuation procedure which will do the work */
+ /* Make sure the exec stack has enough space */
+ check_estack(5);
+ push_mark_estack(es_other, colour_cleanup);
+ esp++;
+ /* variable to hold base type (2 = CMYK) */
+ make_int(esp, 2);
+ esp++;
+ /* Store the 'stage' of processing (initially 0) */
+ make_int(esp, 0);
+ /* Finally, the actual continuation routine */
+ push_op_estack(setdevicecolor_cont);
+ return o_push_estack;
+}
+
+/*
+ * The routine which does all the dispatching for the device-space specific
+ * 'current color' routines currentgray, currentrgbcolo and currentcmykcolor.
+ *
+ * Starting with the top-level color space we need to take the current color
+ * value(s) and pass it through the tint transform procedure (actually we use the
+ * converted function) to get equivalent components for the next space. We then
+ * repeat with each alternate space in turn until we reach a 'terminal' space.
+ * That can be a device space (eg DeviceGray), a CIEBased or ICCBased space, or
+ * a Separation or DeviceN space which is not using its alternate space.
+ *
+ * Depending on which kind of terminal space we reach we will either return
+ * fixed values (all 0.0) or we will convert the terminal device space components
+ * into the requested device space.
+ *
+ * Because we might need to run a tint transform procedure, this requires a
+ * continuation procedure.
+ */
+static int
+currentbasecolor_cont(i_ctx_t *i_ctx_p)
+{
+ ref arr, *parr = &arr;
+ es_ptr ep = esp;
+ int i, code = 0,depth, stage, base, cont=1, stack_depth = 0, CIESubst=0;
+ PS_colour_space_t *obj;
+
+ stack_depth = (int)ep[-4].value.intval;
+ base = (int)ep[-3].value.intval;
+ depth = (int)ep[-2].value.intval;
+ stage = (int)ep[-1].value.intval;
+ /* If we get a continuation from a sub-procedure, we will want to come back
+ * here afterward, to do any remaining stages. We need to set up for that now.
+ * so that our continuation is ahead of the sub-proc's continuation.
+ */
+ check_estack(1);
+ /* The push_op_estack macro increments esp before use, so we don't need to */
+ push_op_estack(currentbasecolor_cont);
+
+ while (code == 0 && cont) {
+ ref_assign(&arr, ep);
+ parr = &arr;
+ /* Run along the nested color spaces until we get to the lowest one
+ * that we haven't yet processed (given by 'depth')
+ */
+ for (i = 0;i < depth;i++) {
+ code = get_space_object(i_ctx_p, parr, &obj);
+ if (code < 0)
+ return code;
+
+ if (i < (depth - 1)) {
+ if (!obj->alternateproc) {
+ return_error(gs_error_typecheck);
+ }
+ code = obj->alternateproc(i_ctx_p, parr, &parr, &CIESubst);
+ if (code < 0)
+ return code;
+ }
+ }
+
+ code = obj->basecolorproc(i_ctx_p, parr, base, &stage, &cont, &stack_depth);
+ make_int(&ep[-4], stack_depth);
+ make_int(&ep[-1], stage);
+ if (code != 0)
+ return code;
+ /* Completed that space, increment the 'depth' */
+ make_int(&ep[-2], ++depth);
+ }
+ if (code == 0) {
+ /* Remove our next continuation and our data */
+ esp -= 7;
+ code = o_pop_estack;
+ }
+ return code;
+}
+
+/* These routines implement the device-space 'current' color routines.
+ * Previously these were implemented in PostScript.
+ */
+static int
+zcurrentgray(i_ctx_t * i_ctx_p)
+{
+ int code, depth;
+
+ code = validate_spaces(i_ctx_p, &istate->colorspace[0].array, &depth);
+ if (code < 0)
+ return code;
+
+ code = zcurrentcolor(i_ctx_p);
+ if (code < 0)
+ return code;
+ /* Set up for the continuation procedure which will do the work */
+ /* Make sure the exec stack has enough space */
+ check_estack(7);
+ push_mark_estack(es_other, colour_cleanup);
+ esp++;
+ /* variable to hold stack depth for tint transform */
+ make_int(&esp[0], 0);
+ esp++;
+ /* Store the 'base' type color wanted, in this case Gray */
+ make_int(&esp[0], 0);
+ make_int(&esp[1], 1);
+ /* Store the 'stage' of processing (initially 0) */
+ make_int(&esp[2], 0);
+ /* Store a pointer to the color space stored on the operand stack
+ * as the stack may grow unpredictably making further access
+ * to the space difficult
+ */
+ esp[3] = istate->colorspace[0].array;
+ esp += 3; /* The push_op_estack macro increments esp before using it */
+ /* Finally, the actual continuation routine */
+ push_op_estack(currentbasecolor_cont);
+ return o_push_estack;
+}
+static int
+zcurrenthsbcolor(i_ctx_t * i_ctx_p)
+{
+ int code, depth;
+
+ code = validate_spaces(i_ctx_p, &istate->colorspace[0].array, &depth);
+ if (code < 0)
+ return code;
+
+ code = zcurrentcolor(i_ctx_p);
+ if (code < 0)
+ return code;
+ /* Set up for the continuation procedure which will do the work */
+ /* Make sure the exec stack has enough space */
+ check_estack(7);
+ push_mark_estack(es_other, colour_cleanup);
+ esp++;
+ /* variable to hold stack depth for tint transform */
+ make_int(&esp[0], 0);
+ esp++;
+ /* Store the 'base' type color wanted, in this case HSB */
+ make_int(&esp[0], 1);
+ make_int(&esp[1], 1);
+ /* Store the 'stage' of processing (initially 0) */
+ make_int(&esp[2], 0);
+ /* Store a pointer to the color space stored on the operand stack
+ * as the stack may grow unpredictably making further access
+ * to the space difficult
+ */
+ esp[3] = istate->colorspace[0].array;
+ esp += 3; /* The push_op_estack macro increments esp before using it */
+ /* Finally, the actual continuation routine */
+ push_op_estack(currentbasecolor_cont);
+ return o_push_estack;
+}
+static int
+zcurrentrgbcolor(i_ctx_t * i_ctx_p)
+{
+ int code;
+
+ code = zcurrentcolor(i_ctx_p);
+ if (code < 0)
+ return code;
+ /* Set up for the continuation procedure which will do the work */
+ /* Make sure the exec stack has enough space */
+ check_estack(7);
+ push_mark_estack(es_other, colour_cleanup);
+ esp++;
+ /* variable to hold stack depth for tint transform */
+ make_int(&esp[0], 0);
+ esp++;
+ /* Store the 'base' type color wanted, in this case RGB */
+ make_int(&esp[0], 2);
+ make_int(&esp[1], 1);
+ /* Store the 'stage' of processing (initially 0) */
+ make_int(&esp[2], 0);
+ /* Store a pointer to the color space stored on the operand stack
+ * as the stack may grow unpredictably making further access
+ * to the space difficult
+ */
+ esp[3] = istate->colorspace[0].array;
+ esp += 3; /* The push_op_estack macro increments esp before using it */
+ /* Finally, the actual continuation routine */
+ push_op_estack(currentbasecolor_cont);
+ return o_push_estack;
+}
+static int
+zcurrentcmykcolor(i_ctx_t * i_ctx_p)
+{
+ int code;
+
+ code = zcurrentcolor(i_ctx_p);
+ if (code < 0)
+ return code;
+ /* Set up for the continuation procedure which will do the work */
+ /* Make sure the exec stack has enough space */
+ check_estack(7);
+ push_mark_estack(es_other, colour_cleanup);
+ esp++;
+ /* variable to hold stack depth for tint transform */
+ make_int(&esp[0], 0);
+ esp++;
+ /* Store the 'base' type color wanted, in this case CMYK */
+ make_int(&esp[0], 3);
+ make_int(&esp[1], 1);
+ /* Store the 'stage' of processing (initially 0) */
+ make_int(&esp[2], 0);
+ /* Store a pointer to the color space stored on the operand stack
+ * as the stack may grow unpredictably making further access
+ * to the space difficult
+ */
+ esp[3] = istate->colorspace[0].array;
+ esp += 3; /* The push_op_estack macro increments esp before using it */
+ /* Finally, the actual continuation routine */
+ push_op_estack(currentbasecolor_cont);
+ return o_push_estack;
+}
+
+static int
+zswapcolors(i_ctx_t * i_ctx_p)
+{
+ ref_colorspace tmp_cs;
+ ref tmp_pat;
+
+ tmp_cs = istate->colorspace[0];
+ istate->colorspace[0] = istate->colorspace[1];
+ istate->colorspace[1] = tmp_cs;
+
+ tmp_pat = istate->pattern[0];
+ istate->pattern[0] = istate->pattern[1];
+ istate->pattern[1] = tmp_pat;
+
+ return gs_swapcolors(igs);
+}
+
+/* ------ Initialization procedure ------ */
+
+/* We need to split the table because of the 16-element limit. */
+const op_def zcolor_op_defs[] =
+{
+ { "0currentcolor", zcurrentcolor },
+ { "0currentcolorspace", zcurrentcolorspace },
+ { "0.getuseciecolor", zgetuseciecolor },
+ { "1setcolor", zsetcolor },
+ { "1setcolorspace", zsetcolorspace },
+
+ /* basic transfer operators */
+ { "0currenttransfer", zcurrenttransfer },
+ { "0processcolors", zprocesscolors },
+ { "1settransfer", zsettransfer },
+
+ /* internal operators */
+ { "1%zcolor_remap_one_finish", zcolor_remap_one_finish },
+ { "1%zcolor_remap_one_signed_finish", zcolor_remap_one_signed_finish },
+ { "0%zcolor_reset_transfer", zcolor_reset_transfer },
+ { "0%zcolor_remap_color", zcolor_remap_color },
+ { "0.color_test", zcolor_test },
+ { "1.color_test_all", zcolor_test_all },
+
+ /* high level device support */
+ { "0.includecolorspace", zincludecolorspace },
+ op_def_end(0)
+};
+
+const op_def zcolor_ext_op_defs[] =
+{
+ { "0currentgray", zcurrentgray },
+ { "1setgray", zsetgray },
+ { "0currenthsbcolor", zcurrenthsbcolor },
+ { "3sethsbcolor", zsethsbcolor },
+ { "0currentrgbcolor", zcurrentrgbcolor },
+ { "3setrgbcolor", zsetrgbcolor },
+ { "0currentcmykcolor", zcurrentcmykcolor },
+ { "4setcmykcolor", zsetcmykcolor },
+
+ /* Operators to deal with setting stroking/non-stroking colors
+ * individually */
+ { "1.swapcolors", zswapcolors },
+
+ /* internal operators, entries here only used for error reporting */
+ { "0%setcolorspace_cont", setcolorspace_cont },
+ { "0%setcolor_cont", setcolor_cont },
+ { "0%devicencolorants_cont", devicencolorants_cont },
+ { "0%indexed_cont", indexed_cont },
+ { "0%setdevicecolor_cont", setdevicecolor_cont },
+ { "0%currentbasecolor_cont", currentbasecolor_cont },
+op_def_end(0)
+};
diff --git a/psi/zcolor.h b/psi/zcolor.h
new file mode 100644
index 000000000..717be1541
--- /dev/null
+++ b/psi/zcolor.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* $Id: zcolor.h $ */
+/* Definitions for setcolorspace */
+
+#ifndef zcolor_INCLUDED
+# define zcolor_INCLUDED
+
+/*
+ * The code to set color space and color values has been moved from PostScript
+ * into C (mostly). The new C code broadly follows the old PostScript method, each
+ * possible color space is defined by an instance of the PS_colour_space_s object below. These
+ * are stored in an array (in zcolor.c) called colorProcs. When color spaces or
+ * color values are set, or values retrieved, we examine the array or name which
+ * represents the space, and extract a C string representation of the color space
+ * name. We compare the name with each of the names in the colorProcs array to
+ * retrieve the instance which handles that color space.
+ *
+ * When setting a new color space, we must follow the existing Ghostscript policy
+ * which requires us to set the spaces 'backwards'. That is, before we set a space
+ * which has an alternate color space (eg Separation) we must first set the
+ * alternate space as the current.
+ *
+ * Now, when setting a color space, we convert any tint transform procedure from
+ * a PostScript procedure into a PostScript Function, either a type 4 PostScript
+ * calculator or a type 0 sampled function. This has performance benefits, especially
+ * wehn dealing with images. Now, if we are converting into a type 4 function this is
+ * easy, however a type 0 function requires us to sample the color space and in order
+ * to do this, we must actually execute the tint transform procedure. This means we
+ * must exit the C world and hand control back to the PostScript interpreter
+ * temporarily.
+ *
+ * We do this with a 'continuation function', we store our procdure on the PostScript
+ * execution stack, and when we need to run a tint transform, we push the procedure
+ * onto the execution stack after our function, and exit back to the interpreter.
+ * When the interpreter has finished executing the transform, it executes the next
+ * entry on the execution stack, which is our C procedure, and returns control back
+ * to our code. Of course, we need to know what stage of processing we were up to
+ * and so we also store some variables on the execution stack. Continuation
+ * procedures are basically state machines.
+ *
+ * In fact, we need quite a few of these, for setting the color space, the current
+ * color (we may need to execute tint transforms), converting the current color into
+ * a device space (for currentcmykcolor and so on). These are all defined in zcolor.c.
+ */
+typedef struct PS_colour_space_s PS_colour_space_t;
+struct PS_colour_space_s {
+ char *name; /* C string representing the name of the space */
+ int (*setproc)(i_ctx_t * i_ctx_p, ref *r, /* Routine to set the color space, if CIESubst */
+ int *stage, int *cont, int CIESubst); /* is true then we are already doing CIE substitution */
+ int (*validateproc)(i_ctx_t * i_ctx_p, /* Validates the color space operands */
+ ref **r);
+ int (*alternateproc)(i_ctx_t * i_ctx_p, /* Retrieve the alternate color space (if any) */
+ ref *space, ref **r, int *CIESubst); /* If CIESubst comes back true, then don't do further CIE substitution */
+ int (*numcomponents)(i_ctx_t * i_ctx_p, /* Returns the number of components in the space */
+ ref *space, int *n);
+ int (*range)(i_ctx_t * i_ctx_p, ref *space, /* Returns 'components' pairs of values which represent */
+ float *ptr); /* the valid ranges of values for this space */
+ int (*domain)(i_ctx_t * i_ctx_p, /* Returns 'components' pairs of values which represent */
+ ref *space, float *ptr); /* the domain over which values are valid */
+ int (*basecolorproc)(i_ctx_t * i_ctx_p, /* convert the current color values into device space */
+ ref *space, int base, int *stage, /* values. 'base' is the requested base space, 0=gray */
+ int *cont, int *stack_depth); /* 1 = HSB, 2 = RGB, 3 = CMYK. Separation and DeviceN */
+ /* spaces will return default values if they are not using */
+ /* the alternate space, and will convert the components */
+ /* into values in the alternate otherwise, by executing */
+ /* the tint transform procedure */
+ int (*runtransformproc)(i_ctx_t *i_ctx_p, /* executes the tint transform for this space */
+ ref *space, int *usealternate,
+ int *stage, int *stack_depth);
+ int (*validatecomponents)(i_ctx_t *i_ctx_p, /* Check the components supplied as an argument to setcolor */
+ ref *space, float *values, /* are valid */
+ int num_comps);
+ int (*compareproc)(i_ctx_t *i_ctx_p, /* Compare two color spaces of this type, to see if they */
+ ref *space, ref *testspace); /* are the same */
+ int (*initialcolorproc)(i_ctx_t *i_ctx_p, /* Set the appropriate initial color for this space */
+ ref *space);
+};
+
+/* Given a pointer to a color space (name or array), returns
+ * the appropriate instance of PS_colour_space_s from the colorProcs array
+ */
+int get_space_object(i_ctx_t *i_ctx_p, ref *arr, PS_colour_space_t **obj);
+
+#endif /* zcolor_INCLUDED */
diff --git a/psi/zcolor1.c b/psi/zcolor1.c
new file mode 100644
index 000000000..29ff2e3be
--- /dev/null
+++ b/psi/zcolor1.c
@@ -0,0 +1,170 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 1 extended color operators */
+#include "ghost.h"
+#include "oper.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "igstate.h"
+#include "iutil.h"
+#include "store.h"
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gzstate.h"
+#include "gxdevice.h"
+#include "gxcmap.h"
+#include "gscolor1.h"
+#include "gxcspace.h"
+#include "icolor.h"
+#include "iimage.h"
+
+/* - currentblackgeneration <proc> */
+static int
+zcurrentblackgeneration(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ *op = istate->black_generation;
+ return 0;
+}
+
+/* - currentcolortransfer <redproc> <greenproc> <blueproc> <grayproc> */
+static int
+zcurrentcolortransfer(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(4);
+ op[-3] = istate->transfer_procs.red;
+ op[-2] = istate->transfer_procs.green;
+ op[-1] = istate->transfer_procs.blue;
+ *op = istate->transfer_procs.gray;
+ return 0;
+}
+
+/* - currentundercolorremoval <proc> */
+static int
+zcurrentundercolorremoval(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ *op = istate->undercolor_removal;
+ return 0;
+}
+
+/* <proc> setblackgeneration - */
+static int
+zsetblackgeneration(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_proc(*op);
+ check_ostack(zcolor_remap_one_ostack - 1);
+ check_estack(1 + zcolor_remap_one_estack);
+ code = gs_setblackgeneration_remap(igs, gs_mapped_transfer, false);
+ if (code < 0)
+ return code;
+ istate->black_generation = *op;
+ pop(1);
+ push_op_estack(zcolor_remap_color);
+ return zcolor_remap_one(i_ctx_p, &istate->black_generation,
+ igs->black_generation, igs,
+ zcolor_remap_one_finish);
+}
+
+/* <redproc> <greenproc> <blueproc> <grayproc> setcolortransfer - */
+static int
+zsetcolortransfer(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_proc(op[-3]);
+ check_proc(op[-2]);
+ check_proc(op[-1]);
+ check_proc(*op);
+ check_ostack(zcolor_remap_one_ostack * 4 - 4);
+ check_estack(1 + zcolor_remap_one_estack * 4);
+ istate->transfer_procs.red = op[-3];
+ istate->transfer_procs.green = op[-2];
+ istate->transfer_procs.blue = op[-1];
+ istate->transfer_procs.gray = *op;
+ if ((code = gs_setcolortransfer_remap(igs,
+ gs_mapped_transfer, gs_mapped_transfer,
+ gs_mapped_transfer, gs_mapped_transfer,
+ false)) < 0
+ )
+ return code;
+ /* Use osp rather than op here, because zcolor_remap_one pushes. */
+ pop(4);
+ push_op_estack(zcolor_reset_transfer);
+ if ((code = zcolor_remap_one(i_ctx_p,
+ &istate->transfer_procs.red,
+ igs->set_transfer.red, igs,
+ zcolor_remap_one_finish)) < 0 ||
+ (code = zcolor_remap_one(i_ctx_p,
+ &istate->transfer_procs.green,
+ igs->set_transfer.green, igs,
+ zcolor_remap_one_finish)) < 0 ||
+ (code = zcolor_remap_one(i_ctx_p,
+ &istate->transfer_procs.blue,
+ igs->set_transfer.blue, igs,
+ zcolor_remap_one_finish)) < 0 ||
+ (code = zcolor_remap_one(i_ctx_p, &istate->transfer_procs.gray,
+ igs->set_transfer.gray, igs,
+ zcolor_remap_one_finish)) < 0
+ )
+ return code;
+ return o_push_estack;
+}
+
+/* <proc> setundercolorremoval - */
+static int
+zsetundercolorremoval(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_proc(*op);
+ check_ostack(zcolor_remap_one_ostack - 1);
+ check_estack(1 + zcolor_remap_one_estack);
+ code = gs_setundercolorremoval_remap(igs, gs_mapped_transfer, false);
+ if (code < 0)
+ return code;
+ istate->undercolor_removal = *op;
+ pop(1);
+ push_op_estack(zcolor_remap_color);
+ return zcolor_remap_one(i_ctx_p, &istate->undercolor_removal,
+ igs->undercolor_removal, igs,
+ zcolor_remap_one_signed_finish);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcolor1_op_defs[] =
+{
+ {"0currentblackgeneration", zcurrentblackgeneration},
+ {"0currentcolortransfer", zcurrentcolortransfer},
+ {"0currentundercolorremoval", zcurrentundercolorremoval},
+ {"1setblackgeneration", zsetblackgeneration},
+ {"4setcolortransfer", zsetcolortransfer},
+ {"1setundercolorremoval", zsetundercolorremoval},
+ op_def_end(0)
+};
diff --git a/psi/zcolor2.c b/psi/zcolor2.c
new file mode 100644
index 000000000..c63068ee5
--- /dev/null
+++ b/psi/zcolor2.c
@@ -0,0 +1,52 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 2 color operators */
+#include "ghost.h"
+#include "string_.h"
+#include "oper.h"
+#include "gsmatrix.h"
+#include "gsstruct.h"
+#include "gxcspace.h"
+#include "gscolor2.h"
+#include "igstate.h"
+#include "store.h"
+
+/*
+ * - .useralternate <bool>
+ *
+ * Push true if the current color space contains a base or alternate
+ * color space and makes use of that color space (e.g.: a Separation
+ * color space for a component not supported by the process color model.
+ */
+static int
+zusealternate(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ const gs_color_space * pcs = gs_currentcolorspace(igs);
+
+ push(1);
+ make_bool(op, pcs->base_space != 0);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcolor2_l2_op_defs[] = {
+ op_def_begin_level2(),
+ { "0.usealternate", zusealternate },
+ op_def_end(0)
+};
diff --git a/psi/zcolor3.c b/psi/zcolor3.c
new file mode 100644
index 000000000..a90d6e645
--- /dev/null
+++ b/psi/zcolor3.c
@@ -0,0 +1,112 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 3 color operators */
+#include "ghost.h"
+#include "oper.h"
+#include "igstate.h"
+#include "store.h"
+
+/*
+ * <bool> .setuseciecolor -
+ *
+ * Set the use_cie_color parameter for the interpreter state, which
+ * corresponds to the UseCIEColor page device parameter. This parameter
+ * may be read at all language levels, but it may be set only for
+ * language level 3. The parameter is handled separately from the page
+ * device dictionary primarily for performance reasons (it may need to
+ * be checked frequently), but also to ensure proper language level
+ * specific behavior.
+ *
+ * This operator is accessible only during initialization and is called
+ * only under controlled conditions. Hence, it does not do any operand
+ * checking.
+ */
+static int
+zsetuseciecolor(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+
+ istate->use_cie_color = *op;
+ pop(1);
+ return 0;
+}
+
+/* - .currentrenderingintent <int> */
+static int
+zcurrentrenderingintent(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, gs_currentrenderingintent(igs));
+ return 0;
+}
+
+/* <int> .setrenderingintent -
+ * See the comment in gsstate.c about the argumet interepretation.
+ */
+static int
+zsetrenderingintent(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ int param;
+ int code = int_param(op, max_int, &param);
+
+ if (code < 0 || (code = gs_setrenderingintent(igs, param)) < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* - .currentblackptcomp <int> */
+static int
+zcurrentblackptcomp(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, gs_currentblackptcomp(igs));
+ return 0;
+}
+
+/* <int> .setblackptcomp */
+static int
+zsetblackptcomp(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ int param;
+ int code = int_param(op, max_int, &param);
+
+ if (code < 0 || (code = gs_setblackptcomp(igs, param)) < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/*
+ * Initialization procedure
+ */
+
+const op_def zcolor3_l3_op_defs[] = {
+ op_def_begin_ll3(),
+ { "0.setuseciecolor", zsetuseciecolor },
+ { "0.currentrenderintent", zcurrentrenderingintent },
+ { "1.setrenderingintent", zsetrenderingintent },
+ { "2.currentblackptcomp", zcurrentblackptcomp },
+ { "3.setblackptcomp", zsetblackptcomp },
+ op_def_end(0)
+};
diff --git a/psi/zcontext.c b/psi/zcontext.c
new file mode 100644
index 000000000..5b7d2bb18
--- /dev/null
+++ b/psi/zcontext.c
@@ -0,0 +1,1330 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Display PostScript context operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "gp.h" /* for usertime */
+#include "oper.h"
+#include "gsexit.h"
+#include "gsgc.h"
+#include "gsstruct.h"
+#include "gsutil.h"
+#include "gxalloc.h"
+#include "gxstate.h" /* for copying gstate stack */
+#include "stream.h" /* for files.h */
+#include "files.h"
+#include "idict.h"
+#include "igstate.h"
+#include "icontext.h"
+#include "interp.h"
+#include "isave.h"
+#include "istruct.h"
+#include "dstack.h"
+#include "estack.h"
+#include "ostack.h"
+#include "store.h"
+
+/*
+ * Define the reschedule_interval, the number of ticks between time
+ * slice reschedules. This may be changed by the user by setting the
+ * TIME_SLICE_INTERVAL variable in systemdict to the desired integer
+ * prior to running. If set to be less than the minimum interval (100),
+ * time slicing will be disabled. In this case, the time_slice_ticks
+ * field of the context state will be still be set to the default
+ * reschedule interval of 250 ticks. This will still permit the
+ * interpreter to garbage collect unclaimed memory at appropriate
+ * intervals, even when time slicing between contexts is disabled.
+ */
+enum {
+ default_reschedule_interval = 250,
+ minimum_reschedule_interval = 100
+};
+
+static int reschedule_interval = default_reschedule_interval;
+
+/* Context structure */
+typedef enum {
+ cs_active,
+ cs_done
+} ctx_status_t;
+typedef long ctx_index_t; /* >= 0 */
+typedef struct gs_context_s gs_context_t;
+typedef struct gs_scheduler_s gs_scheduler_t;
+
+/*
+ * If several contexts share local VM, then if any one of them has done an
+ * unmatched save, the others are not allowed to run. We handle this by
+ * maintaining the following invariant:
+ * When control reaches the point in the scheduler that decides
+ * what context to run next, then for each group of contexts
+ * sharing local VM, if the save level for that VM is non-zero,
+ * saved_local_vm is only set in the context that has unmatched
+ * saves.
+ * We maintain this invariant as follows: when control enters the
+ * scheduler, if a context was running, we set its saved_local_vm flag
+ * to (save_level > 0). When selecting a context to run, we ignore
+ * contexts where saved_local_vm is false and the local VM save_level > 0.
+ */
+struct gs_context_s {
+ gs_context_state_t state; /* (must be first for subclassing) */
+ /* Private state */
+ gs_scheduler_t *scheduler;
+ ctx_status_t status;
+ ctx_index_t index; /* > 0 */
+ bool detach; /* true if a detach has been */
+ /* executed for this context */
+ bool saved_local_vm; /* (see above) */
+ bool visible; /* during GC, true if visible; */
+ /* otherwise, always true */
+ ctx_index_t next_index; /* next context with same status */
+ /* (active, waiting on same lock, */
+ /* waiting on same condition, */
+ /* waiting to be destroyed) */
+ ctx_index_t joiner_index; /* context waiting on a join */
+ /* for this one */
+ gs_context_t *table_next; /* hash table chain -- this must be a real */
+ /* pointer, for looking up indices */
+};
+static inline bool
+context_is_visible(const gs_context_t *pctx)
+{
+ return (pctx && pctx->visible);
+}
+static inline gs_context_t *
+visible_context(gs_context_t *pctx)
+{
+ return (pctx && pctx->visible ? pctx : (gs_context_t *)0);
+}
+
+/* GC descriptor */
+static
+CLEAR_MARKS_PROC(context_clear_marks)
+{
+ gs_context_t *const pctx = vptr;
+
+ (*st_context_state.clear_marks)
+ (cmem, &pctx->state, sizeof(pctx->state), &st_context_state);
+}
+static
+ENUM_PTRS_WITH(context_enum_ptrs, gs_context_t *pctx)
+ENUM_PREFIX(st_context_state, 2);
+case 0: return ENUM_OBJ(pctx->scheduler);
+case 1: {
+ /* Return the next *visible* context. */
+ const gs_context_t *next = pctx->table_next;
+
+ while (next && !next->visible)
+ next = next->table_next;
+ return ENUM_OBJ(next);
+}
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(context_reloc_ptrs, gs_context_t *pctx)
+ RELOC_PREFIX(st_context_state);
+ RELOC_VAR(pctx->scheduler);
+ /* Don't relocate table_next -- the scheduler object handles that. */
+RELOC_PTRS_END
+gs_private_st_complex_only(st_context, gs_context_t, "gs_context_t",
+ context_clear_marks, context_enum_ptrs, context_reloc_ptrs, 0);
+
+/*
+ * Context list structure. Note that this uses context indices, not
+ * pointers, to avoid having to worry about pointers between local VMs.
+ */
+typedef struct ctx_list_s {
+ ctx_index_t head_index;
+ ctx_index_t tail_index;
+} ctx_list_t;
+
+/* Condition structure */
+typedef struct gs_condition_s {
+ ctx_list_t waiting; /* contexts waiting on this condition */
+} gs_condition_t;
+gs_private_st_simple(st_condition, gs_condition_t, "conditiontype");
+
+/* Lock structure */
+typedef struct gs_lock_s {
+ ctx_list_t waiting; /* contexts waiting for this lock, */
+ /* must be first for subclassing */
+ ctx_index_t holder_index; /* context holding the lock, if any */
+ gs_scheduler_t *scheduler;
+} gs_lock_t;
+gs_private_st_ptrs1(st_lock, gs_lock_t, "locktype",
+ lock_enum_ptrs, lock_reloc_ptrs, scheduler);
+
+/* Global state */
+/*typedef struct gs_scheduler_s gs_scheduler_t; *//* (above) */
+struct gs_scheduler_s {
+ gs_context_t *current;
+ long usertime_initial; /* usertime when current started running */
+ ctx_list_t active;
+ vm_reclaim_proc((*save_vm_reclaim));
+ ctx_index_t dead_index;
+#define CTX_TABLE_SIZE 19
+ gs_context_t *table[CTX_TABLE_SIZE];
+};
+
+/* Convert a context index to a context pointer. */
+static gs_context_t *
+index_context(const gs_scheduler_t *psched, long index)
+{
+ gs_context_t *pctx;
+
+ if (index == 0)
+ return NULL;
+ pctx = psched->table[index % CTX_TABLE_SIZE];
+ while (pctx != NULL && pctx->index != index)
+ pctx = pctx->table_next;
+ return pctx;
+}
+
+/* Structure definition */
+gs_private_st_composite(st_scheduler, gs_scheduler_t, "gs_scheduler",
+ scheduler_enum_ptrs, scheduler_reloc_ptrs);
+/*
+ * The only cross-local-VM pointers in the context machinery are the
+ * table_next pointers in contexts, and the current and table[] pointers
+ * in the scheduler. We need to handle all of these specially.
+ */
+static ENUM_PTRS_WITH(scheduler_enum_ptrs, gs_scheduler_t *psched)
+{
+ index -= 1;
+ if (index < CTX_TABLE_SIZE) {
+ gs_context_t *pctx = psched->table[index];
+
+ while (pctx && !pctx->visible)
+ pctx = pctx->table_next;
+ return ENUM_OBJ(pctx);
+ }
+ return 0;
+}
+case 0: return ENUM_OBJ(visible_context(psched->current));
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(scheduler_reloc_ptrs, gs_scheduler_t *psched)
+{
+ if (psched->current->visible)
+ RELOC_VAR(psched->current);
+ {
+ int i;
+
+ for (i = 0; i < CTX_TABLE_SIZE; ++i) {
+ gs_context_t **ppctx = &psched->table[i];
+ gs_context_t **pnext;
+
+ for (; *ppctx; ppctx = pnext) {
+ pnext = &(*ppctx)->table_next;
+ if ((*ppctx)->visible)
+ RELOC_VAR(*ppctx);
+ }
+ }
+ }
+}
+RELOC_PTRS_END
+
+/*
+ * The context scheduler requires special handling during garbage
+ * collection, since it is the only structure that can legitimately
+ * reference objects in multiple local VMs. To deal with this, we wrap the
+ * interpreter's garbage collector with code that prevents it from seeing
+ * contexts in other than the current local VM. ****** WORKS FOR LOCAL GC,
+ * NOT FOR GLOBAL ******
+ */
+static void
+context_reclaim(vm_spaces * pspaces, bool global)
+{
+ /*
+ * Search through the registered roots to find the current context.
+ * (This is a hack so we can find the scheduler.)
+ */
+ int i;
+ gs_context_t *pctx = 0; /* = 0 is bogus to pacify compilers */
+ gs_scheduler_t *psched = 0;
+ gs_ref_memory_t *lmem = 0; /* = 0 is bogus to pacify compilers */
+ chunk_locator_t loc;
+
+ for (i = countof(pspaces->memories.indexed) - 1; psched == 0 && i > 0; --i) {
+ gs_ref_memory_t *mem = pspaces->memories.indexed[i];
+ const gs_gc_root_t *root = mem->roots;
+
+ for (; root; root = root->next) {
+ if (gs_object_type((gs_memory_t *)mem, *root->p) == &st_context) {
+ pctx = *root->p;
+ psched = pctx->scheduler;
+ lmem = mem;
+ break;
+ }
+ }
+ }
+
+ /* Hide all contexts in other (local) VMs. */
+ /*
+ * See context_create below for why we look for the context
+ * in stable memory.
+ */
+ loc.memory = (gs_ref_memory_t *)gs_memory_stable((gs_memory_t *)lmem);
+ loc.cp = 0;
+ for (i = 0; i < CTX_TABLE_SIZE; ++i)
+ for (pctx = psched->table[i]; pctx; pctx = pctx->table_next)
+ pctx->visible = chunk_locate_ptr(pctx, &loc);
+
+#ifdef DEBUG
+ if (!psched->current->visible) {
+ lprintf("Current context is invisible!\n");
+ gs_abort((gs_memory_t *)lmem);
+ }
+#endif
+
+ /* Do the actual garbage collection. */
+ psched->save_vm_reclaim(pspaces, global);
+
+ /* Make all contexts visible again. */
+ for (i = 0; i < CTX_TABLE_SIZE; ++i)
+ for (pctx = psched->table[i]; pctx; pctx = pctx->table_next)
+ pctx->visible = true;
+}
+
+/* Forward references */
+static int context_create(gs_scheduler_t *, gs_context_t **,
+ const gs_dual_memory_t *,
+ const gs_context_state_t *, bool);
+static long context_usertime(void);
+static int context_param(const gs_scheduler_t *, os_ptr, gs_context_t **);
+static void context_destroy(gs_context_t *);
+static void stack_copy(ref_stack_t *, const ref_stack_t *, uint, uint);
+static int lock_acquire(os_ptr, gs_context_t *);
+static int lock_release(ref *);
+
+/* Internal procedures */
+static void
+context_load(gs_scheduler_t *psched, gs_context_t *pctx)
+{
+ if_debug1('"', "[\"]loading %ld\n", pctx->index);
+ if ( pctx->state.keep_usertime )
+ psched->usertime_initial = context_usertime();
+ context_state_load(&pctx->state);
+}
+static void
+context_store(gs_scheduler_t *psched, gs_context_t *pctx)
+{
+ if_debug1('"', "[\"]storing %ld\n", pctx->index);
+ context_state_store(&pctx->state);
+ if ( pctx->state.keep_usertime )
+ pctx->state.usertime_total +=
+ context_usertime() - psched->usertime_initial;
+}
+
+/* List manipulation */
+static void
+add_last(const gs_scheduler_t *psched, ctx_list_t *pl, gs_context_t *pc)
+{
+ pc->next_index = 0;
+ if (pl->head_index == 0)
+ pl->head_index = pc->index;
+ else
+ index_context(psched, pl->tail_index)->next_index = pc->index;
+ pl->tail_index = pc->index;
+}
+
+/* ------ Initialization ------ */
+
+static int ctx_initialize(i_ctx_t **);
+static int ctx_reschedule(i_ctx_t **);
+static int ctx_time_slice(i_ctx_t **);
+static int
+zcontext_init(i_ctx_t *i_ctx_p)
+{
+ /* Complete initialization after the interpreter is entered. */
+ i_ctx_p->reschedule_proc = ctx_initialize;
+ i_ctx_p->time_slice_proc = ctx_initialize;
+ i_ctx_p->time_slice_ticks = 0;
+ return 0;
+}
+/*
+ * The interpreter calls this procedure at the first reschedule point.
+ * It completes context initialization.
+ */
+static int
+ctx_initialize(i_ctx_t **pi_ctx_p)
+{
+ int interval = reschedule_interval;
+ i_ctx_t *i_ctx_p = *pi_ctx_p; /* for gs_imemory */
+ gs_ref_memory_t *imem = iimemory_system;
+ gs_scheduler_t *psched =
+ gs_alloc_struct_immovable((gs_memory_t *) imem, gs_scheduler_t,
+ &st_scheduler, "gs_scheduler");
+ if (psched == NULL)
+ return_error(gs_error_VMerror);
+
+ psched->current = 0;
+ psched->active.head_index = psched->active.tail_index = 0;
+ psched->save_vm_reclaim = i_ctx_p->memory.spaces.vm_reclaim;
+ i_ctx_p->memory.spaces.vm_reclaim = context_reclaim;
+ psched->dead_index = 0;
+ memset(psched->table, 0, sizeof(psched->table));
+ /* Create an initial context. */
+ if (context_create(psched, &psched->current, &gs_imemory, *pi_ctx_p, true) < 0) {
+ lprintf("Can't create initial context!");
+ gs_abort(imemory);
+ }
+ psched->current->scheduler = psched;
+ /* Hook into the interpreter. */
+ *pi_ctx_p = &psched->current->state;
+
+ if (interval < minimum_reschedule_interval)
+ interval = minimum_reschedule_interval;
+ psched->current->state.reschedule_proc = ctx_reschedule;
+ psched->current->state.time_slice_proc = ctx_time_slice;
+ psched->current->state.time_slice_ticks = interval;
+ return 0;
+}
+
+/* ------ Interpreter interface to scheduler ------ */
+
+/* When an operator decides it is time to run a new context, */
+/* it returns o_reschedule. The interpreter saves all its state in */
+/* memory, calls ctx_reschedule, and then loads the state from memory. */
+static int
+ctx_reschedule(i_ctx_t **pi_ctx_p)
+{
+ gs_context_t *current = (gs_context_t *)*pi_ctx_p;
+ gs_scheduler_t *psched = current->scheduler;
+
+#ifdef DEBUG
+ if (*pi_ctx_p != &current->state) {
+ lprintf2("current->state = 0x%lx, != i_ctx_p = 0x%lx!\n",
+ (ulong)&current->state, (ulong)*pi_ctx_p);
+ }
+#endif
+ /* If there are any dead contexts waiting to be released, */
+ /* take care of that now. */
+ while (psched->dead_index != 0) {
+ gs_context_t *dead = index_context(psched, psched->dead_index);
+ long next_index = dead->next_index;
+
+ if (current == dead) {
+ if_debug1('"', "[\"]storing dead %ld\n", current->index);
+ context_state_store(&current->state);
+ current = 0;
+ }
+ context_destroy(dead);
+ psched->dead_index = next_index;
+ }
+ /* Update saved_local_vm. See above for the invariant. */
+ if (current != 0)
+ current->saved_local_vm =
+ current->state.memory.space_local->saved != 0;
+ /* Run the first ready context, taking the 'save' lock into account. */
+ {
+ gs_context_t *prev = 0;
+ gs_context_t *ready;
+
+ for (ready = index_context(psched, psched->active.head_index);;
+ prev = ready, ready = index_context(psched, ready->next_index)
+ ) {
+ if (ready == 0) {
+ if (current != 0)
+ context_store(psched, current);
+ lprintf("No context to run!");
+ return_error(gs_error_Fatal);
+ }
+ /* See above for an explanation of the following test. */
+ if (ready->state.memory.space_local->saved != 0 &&
+ !ready->saved_local_vm
+ )
+ continue;
+ /* Found a context to run. */
+ {
+ ctx_index_t next_index = ready->next_index;
+
+ if (prev)
+ prev->next_index = next_index;
+ else
+ psched->active.head_index = next_index;
+ if (!next_index)
+ psched->active.tail_index = (prev ? prev->index : 0);
+ }
+ break;
+ }
+ if (ready == current)
+ return 0; /* no switch */
+ /*
+ * Save the state of the current context in psched->current,
+ * if any context is current.
+ */
+ if (current != 0)
+ context_store(psched, current);
+ psched->current = ready;
+ /* Load the state of the new current context. */
+ context_load(psched, ready);
+ /* Switch the interpreter's context state pointer. */
+ *pi_ctx_p = &ready->state;
+ }
+ return 0;
+}
+
+/* If the interpreter wants to time-slice, it saves its state, */
+/* calls ctx_time_slice, and reloads its state. */
+static int
+ctx_time_slice(i_ctx_t **pi_ctx_p)
+{
+ gs_scheduler_t *psched = ((gs_context_t *)*pi_ctx_p)->scheduler;
+
+ if (psched->active.head_index == 0 ||
+ reschedule_interval < minimum_reschedule_interval)
+ return 0;
+ if_debug0('"', "[\"]time-slice\n");
+ add_last(psched, &psched->active, psched->current);
+ return ctx_reschedule(pi_ctx_p);
+}
+
+/* ------ Context operators ------ */
+
+/* - currentcontext <context> */
+static int
+zcurrentcontext(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const gs_context_t *current = (const gs_context_t *)i_ctx_p;
+
+ push(1);
+ make_int(op, current->index);
+ return 0;
+}
+
+/* <context> detach - */
+static int
+zdetach(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const gs_scheduler_t *psched = ((gs_context_t *)i_ctx_p)->scheduler;
+ gs_context_t *pctx;
+ int code;
+
+ if ((code = context_param(psched, op, &pctx)) < 0)
+ return code;
+ if_debug2('\'', "[']detach %ld, status = %d\n",
+ pctx->index, pctx->status);
+ if (pctx->joiner_index != 0 || pctx->detach)
+ return_error(gs_error_invalidcontext);
+ switch (pctx->status) {
+ case cs_active:
+ pctx->detach = true;
+ break;
+ case cs_done:
+ context_destroy(pctx);
+ }
+ pop(1);
+ return 0;
+}
+
+static int
+ do_fork(i_ctx_t *i_ctx_p, os_ptr op, const ref * pstdin,
+ const ref * pstdout, uint mcount, bool local),
+ values_older_than(const ref_stack_t * pstack, uint first, uint last,
+ int max_space);
+static int
+ fork_done(i_ctx_t *),
+ fork_done_with_error(i_ctx_t *),
+ finish_join(i_ctx_t *),
+ reschedule_now(i_ctx_t *);
+
+/* <mark> <obj1> ... <objN> <proc> .fork <context> */
+/* <mark> <obj1> ... <objN> <proc> <stdin|null> <stdout|null> */
+/* .localfork <context> */
+static int
+zfork(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint mcount = ref_stack_counttomark(&o_stack);
+ ref rnull;
+
+ if (mcount == 0)
+ return_error(gs_error_unmatchedmark);
+ make_null(&rnull);
+ return do_fork(i_ctx_p, op, &rnull, &rnull, mcount, false);
+}
+static int
+zlocalfork(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint mcount = ref_stack_counttomark(&o_stack);
+ int code;
+
+ if (mcount == 0)
+ return_error(gs_error_unmatchedmark);
+ code = values_older_than(&o_stack, 1, mcount - 1, avm_local);
+ if (code < 0)
+ return code;
+ code = do_fork(i_ctx_p, op - 2, op - 1, op, mcount - 2, true);
+ if (code < 0)
+ return code;
+ op = osp;
+ op[-2] = *op;
+ pop(2);
+ return code;
+}
+
+/* Internal procedure to actually do the fork operation. */
+static int
+do_fork(i_ctx_t *i_ctx_p, os_ptr op, const ref * pstdin, const ref * pstdout,
+ uint mcount, bool local)
+{
+ gs_context_t *pcur = (gs_context_t *)i_ctx_p;
+ gs_scheduler_t *psched = pcur->scheduler;
+ stream *s;
+ gs_dual_memory_t dmem;
+ gs_context_t *pctx;
+ ref old_userdict, new_userdict;
+ int code, interval;
+
+ check_proc(*op);
+ if (iimemory_local->save_level)
+ return_error(gs_error_invalidcontext);
+ if (r_has_type(pstdout, t_null)) {
+ code = zget_stdout(i_ctx_p, &s);
+ if (code < 0)
+ return code;
+ pstdout = &ref_stdio[1];
+ } else
+ check_read_file(i_ctx_p, s, pstdout);
+ if (r_has_type(pstdin, t_null)) {
+ code = zget_stdin(i_ctx_p, &s);
+ if (code < 0)
+ return code;
+ pstdin = &ref_stdio[0];
+ } else
+ check_read_file(i_ctx_p, s, pstdin);
+ dmem = gs_imemory;
+ if (local) {
+ /* Share global VM, private local VM. */
+ ref *puserdict;
+ uint userdict_size;
+ gs_memory_t *parent = iimemory_local->non_gc_memory;
+ gs_ref_memory_t *lmem;
+ gs_ref_memory_t *lmem_stable;
+
+ if (dict_find_string(systemdict, "userdict", &puserdict) <= 0 ||
+ !r_has_type(puserdict, t_dictionary)
+ )
+ return_error(gs_error_Fatal);
+ old_userdict = *puserdict;
+ userdict_size = dict_maxlength(&old_userdict);
+ lmem = ialloc_alloc_state(parent, iimemory_local->chunk_size);
+ lmem_stable = ialloc_alloc_state(parent, iimemory_local->chunk_size);
+ if (lmem == 0 || lmem_stable == 0) {
+ gs_free_object(parent, lmem_stable, "do_fork");
+ gs_free_object(parent, lmem, "do_fork");
+ return_error(gs_error_VMerror);
+ }
+ lmem->space = avm_local;
+ lmem_stable->space = avm_local;
+ lmem->stable_memory = (gs_memory_t *)lmem_stable;
+ dmem.space_local = lmem;
+ code = context_create(psched, &pctx, &dmem, &pcur->state, false);
+ if (code < 0) {
+ /****** FREE lmem ******/
+ return code;
+ }
+ /*
+ * Create a new userdict. PostScript code will take care of
+ * the rest of the initialization of the new context.
+ */
+ code = dict_alloc(lmem, userdict_size, &new_userdict);
+ if (code < 0) {
+ context_destroy(pctx);
+ /****** FREE lmem ******/
+ return code;
+ }
+ } else {
+ /* Share global and local VM. */
+ code = context_create(psched, &pctx, &dmem, &pcur->state, false);
+ if (code < 0) {
+ /****** FREE lmem ******/
+ return code;
+ }
+ /*
+ * Copy the gstate stack. The current method is not elegant;
+ * in fact, I'm not entirely sure it works.
+ */
+ {
+ int n;
+ const gs_state *old;
+ gs_state *new;
+
+ for (n = 0, old = igs; old != 0; old = gs_state_saved(old))
+ ++n;
+ for (old = pctx->state.pgs; old != 0; old = gs_state_saved(old))
+ --n;
+ for (; n > 0 && code >= 0; --n)
+ code = gs_gsave(pctx->state.pgs);
+ if (code < 0) {
+/****** FREE lmem & GSTATES ******/
+ return code;
+ }
+ for (old = igs, new = pctx->state.pgs;
+ old != 0 /* (== new != 0) */ && code >= 0;
+ old = gs_state_saved(old), new = gs_state_saved(new)
+ )
+ code = gs_setgstate(new, old);
+ if (code < 0) {
+/****** FREE lmem & GSTATES ******/
+ return code;
+ }
+ }
+ }
+ zcontext_init(&pctx->state);
+
+ interval = reschedule_interval;
+ if (interval < minimum_reschedule_interval)
+ interval = default_reschedule_interval;
+
+ pctx->state.reschedule_proc = ctx_reschedule;
+ pctx->state.time_slice_proc = ctx_time_slice;
+ pctx->state.time_slice_ticks = interval;
+
+ pctx->state.op_array_table_global = i_ctx_p->op_array_table_global;
+ pctx->state.op_array_table_local = i_ctx_p->op_array_table_local;
+ pctx->state.language_level = i_ctx_p->language_level;
+ pctx->state.dict_stack.min_size = idict_stack.min_size;
+ pctx->state.dict_stack.userdict_index = idict_stack.userdict_index;
+ pctx->state.stdio[0] = *pstdin;
+ pctx->state.stdio[1] = *pstdout;
+ pctx->state.stdio[2] = pcur->state.stdio[2];
+ /* Initialize the interpreter stacks. */
+ {
+ ref_stack_t *dstack = (ref_stack_t *)&pctx->state.dict_stack;
+ uint count = ref_stack_count(&d_stack);
+ uint copy = (local ? min_dstack_size : count);
+
+ ref_stack_push(dstack, copy);
+ stack_copy(dstack, &d_stack, copy, count - copy);
+ if (local) {
+ /* Substitute the new userdict for the old one. */
+ long i;
+
+ for (i = 0; i < copy; ++i) {
+ ref *pdref = ref_stack_index(dstack, i);
+
+ if (obj_eq(imemory, pdref, &old_userdict))
+ *pdref = new_userdict;
+ }
+ }
+ }
+ {
+ ref_stack_t *estack = (ref_stack_t *)&pctx->state.exec_stack;
+
+ ref_stack_push(estack, 3);
+ /* fork_done must be executed in both normal and error cases. */
+ make_mark_estack(estack->p - 2, es_other, fork_done_with_error);
+ make_oper(estack->p - 1, 0, fork_done);
+ *estack->p = *op;
+ }
+ {
+ ref_stack_t *ostack = (ref_stack_t *)&pctx->state.op_stack;
+ uint count = mcount - 2;
+
+ ref_stack_push(ostack, count);
+ stack_copy(ostack, &o_stack, count, osp - op + 1);
+ }
+ pctx->state.binary_object_format = pcur->state.binary_object_format;
+ add_last(psched, &psched->active, pctx);
+ pop(mcount - 1);
+ op = osp;
+ make_int(op, pctx->index);
+ return 0;
+}
+
+/*
+ * Check that all values being passed by fork or join are old enough
+ * to be valid in the environment to which they are being transferred.
+ */
+static int
+values_older_than(const ref_stack_t * pstack, uint first, uint last,
+ int next_space)
+{
+ uint i;
+
+ for (i = first; i <= last; ++i)
+ if (r_space(ref_stack_index(pstack, (long)i)) >= next_space)
+ return_error(gs_error_invalidaccess);
+ return 0;
+}
+
+/* This gets executed when a context terminates normally. */
+/****** MUST DO ALL RESTORES ******/
+/****** WHAT IF invalidrestore? ******/
+static int
+fork_done(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_context_t *pcur = (gs_context_t *)i_ctx_p;
+ gs_scheduler_t *psched = pcur->scheduler;
+
+ if_debug2('\'', "[']done %ld%s\n", pcur->index,
+ (pcur->detach ? ", detached" : ""));
+ /*
+ * Clear the context's dictionary, execution and graphics stacks
+ * now, to retain as little as possible in case of a garbage
+ * collection or restore. We know that fork_done is the
+ * next-to-bottom entry on the execution stack.
+ */
+ ref_stack_pop_to(&d_stack, min_dstack_size);
+ pop_estack(&pcur->state, ref_stack_count(&e_stack) - 1);
+ gs_grestoreall(igs);
+ /*
+ * If there are any unmatched saves, we need to execute restores
+ * until there aren't. An invalidrestore is possible and will
+ * result in an error termination.
+ */
+ if (iimemory_local->save_level) {
+ ref *prestore;
+
+ if (dict_find_string(systemdict, "restore", &prestore) <= 0) {
+ lprintf("restore not found in systemdict!");
+ return_error(gs_error_Fatal);
+ }
+ if (pcur->detach) {
+ ref_stack_clear(&o_stack); /* help avoid invalidrestore */
+ op = osp;
+ }
+ push(1);
+ make_tv(op, t_save, saveid, alloc_save_current_id(&gs_imemory));
+ push_op_estack(fork_done);
+ ++esp;
+ ref_assign(esp, prestore);
+ return o_push_estack;
+ }
+ if (pcur->detach) {
+ /*
+ * We would like to free the context's memory, but we can't do
+ * it yet, because the interpreter still has references to it.
+ * Instead, queue the context to be freed the next time we
+ * reschedule. We can, however, clear its operand stack now.
+ */
+ ref_stack_clear(&o_stack);
+ context_store(psched, pcur);
+ pcur->next_index = psched->dead_index;
+ psched->dead_index = pcur->index;
+ psched->current = 0;
+ } else {
+ gs_context_t *pctx = index_context(psched, pcur->joiner_index);
+
+ pcur->status = cs_done;
+ /* Schedule the context waiting to join this one, if any. */
+ if (pctx != 0)
+ add_last(psched, &psched->active, pctx);
+ }
+ return o_reschedule;
+}
+/*
+ * This gets executed when the stack is being unwound for an error
+ * termination.
+ */
+static int
+fork_done_with_error(i_ctx_t *i_ctx_p)
+{
+/****** WHAT TO DO? ******/
+ return fork_done(i_ctx_p);
+}
+
+/* <context> join <mark> <obj1> ... <objN> */
+static int
+zjoin(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_context_t *current = (gs_context_t *)i_ctx_p;
+ gs_scheduler_t *psched = current->scheduler;
+ gs_context_t *pctx;
+ int code;
+
+ if ((code = context_param(psched, op, &pctx)) < 0)
+ return code;
+ if_debug2('\'', "[']join %ld, status = %d\n",
+ pctx->index, pctx->status);
+ /*
+ * It doesn't seem logically necessary, but the Red Book says that
+ * the context being joined must share both global and local VM with
+ * the current context.
+ */
+ if (pctx->joiner_index != 0 || pctx->detach || pctx == current ||
+ pctx->state.memory.space_global !=
+ current->state.memory.space_global ||
+ pctx->state.memory.space_local !=
+ current->state.memory.space_local ||
+ iimemory_local->save_level != 0
+ )
+ return_error(gs_error_invalidcontext);
+ switch (pctx->status) {
+ case cs_active:
+ /*
+ * We need to re-execute the join after the joined
+ * context is done. Since we can't return both
+ * o_push_estack and o_reschedule, we push a call on
+ * reschedule_now, which accomplishes the latter.
+ */
+ check_estack(2);
+ push_op_estack(finish_join);
+ push_op_estack(reschedule_now);
+ pctx->joiner_index = current->index;
+ return o_push_estack;
+ case cs_done:
+ {
+ const ref_stack_t *ostack =
+ (ref_stack_t *)&pctx->state.op_stack;
+ uint count = ref_stack_count(ostack);
+
+ push(count);
+ {
+ ref *rp = ref_stack_index(&o_stack, count);
+
+ make_mark(rp);
+ }
+ stack_copy(&o_stack, ostack, count, 0);
+ context_destroy(pctx);
+ }
+ }
+ return 0;
+}
+
+/* Finish a deferred join. */
+static int
+finish_join(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_context_t *current = (gs_context_t *)i_ctx_p;
+ gs_scheduler_t *psched = current->scheduler;
+ gs_context_t *pctx;
+ int code;
+
+ if ((code = context_param(psched, op, &pctx)) < 0)
+ return code;
+ if_debug2('\'', "[']finish_join %ld, status = %d\n",
+ pctx->index, pctx->status);
+ if (pctx->joiner_index != current->index)
+ return_error(gs_error_invalidcontext);
+ pctx->joiner_index = 0;
+ return zjoin(i_ctx_p);
+}
+
+/* Reschedule now. */
+static int
+reschedule_now(i_ctx_t *i_ctx_p)
+{
+ return o_reschedule;
+}
+
+/* - yield - */
+static int
+zyield(i_ctx_t *i_ctx_p)
+{
+ gs_context_t *current = (gs_context_t *)i_ctx_p;
+ gs_scheduler_t *psched = current->scheduler;
+
+ if (psched->active.head_index == 0)
+ return 0;
+ if_debug0('"', "[\"]yield\n");
+ add_last(psched, &psched->active, current);
+ return o_reschedule;
+}
+
+/* ------ Condition and lock operators ------ */
+
+static int
+ monitor_cleanup(i_ctx_t *),
+ monitor_release(i_ctx_t *),
+ await_lock(i_ctx_t *);
+static void
+ activate_waiting(gs_scheduler_t *, ctx_list_t * pcl);
+
+/* - condition <condition> */
+static int
+zcondition(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_condition_t *pcond =
+ ialloc_struct(gs_condition_t, &st_condition, "zcondition");
+
+ if (pcond == 0)
+ return_error(gs_error_VMerror);
+ pcond->waiting.head_index = pcond->waiting.tail_index = 0;
+ push(1);
+ make_istruct(op, a_all, pcond);
+ return 0;
+}
+
+/* - lock <lock> */
+static int
+zlock(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_lock_t *plock = ialloc_struct(gs_lock_t, &st_lock, "zlock");
+
+ if (plock == 0)
+ return_error(gs_error_VMerror);
+ plock->holder_index = 0;
+ plock->waiting.head_index = plock->waiting.tail_index = 0;
+ push(1);
+ make_istruct(op, a_all, plock);
+ return 0;
+}
+
+/* <lock> <proc> monitor - */
+static int
+zmonitor(i_ctx_t *i_ctx_p)
+{
+ gs_context_t *current = (gs_context_t *)i_ctx_p;
+ os_ptr op = osp;
+ gs_lock_t *plock;
+ gs_context_t *pctx;
+ int code;
+
+ check_stype(op[-1], st_lock);
+ check_proc(*op);
+ plock = r_ptr(op - 1, gs_lock_t);
+ pctx = index_context(current->scheduler, plock->holder_index);
+ if_debug1('\'', "[']monitor 0x%lx\n", (ulong) plock);
+ if (pctx != 0) {
+ if (pctx == current ||
+ (iimemory_local->save_level != 0 &&
+ pctx->state.memory.space_local ==
+ current->state.memory.space_local)
+ )
+ return_error(gs_error_invalidcontext);
+ }
+ /*
+ * We push on the e-stack:
+ * The lock object
+ * An e-stack mark with monitor_cleanup, to release the lock
+ * in case of an error
+ * monitor_release, to release the lock in the normal case
+ * The procedure to execute
+ */
+ check_estack(4);
+ code = lock_acquire(op - 1, current);
+ if (code != 0) { /* We didn't acquire the lock. Re-execute this later. */
+ push_op_estack(zmonitor);
+ return code; /* o_reschedule */
+ }
+ *++esp = op[-1];
+ push_mark_estack(es_other, monitor_cleanup);
+ push_op_estack(monitor_release);
+ *++esp = *op;
+ pop(2);
+ return o_push_estack;
+}
+/* Release the monitor lock when unwinding for an error or exit. */
+static int
+monitor_cleanup(i_ctx_t *i_ctx_p)
+{
+ int code = lock_release(esp);
+
+ if (code < 0)
+ return code;
+ --esp;
+ return o_pop_estack;
+}
+/* Release the monitor lock when the procedure completes. */
+static int
+monitor_release(i_ctx_t *i_ctx_p)
+{
+ int code = lock_release(esp - 1);
+
+ if (code < 0)
+ return code;
+ esp -= 2;
+ return o_pop_estack;
+}
+
+/* <condition> notify - */
+static int
+znotify(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_context_t *current = (gs_context_t *)i_ctx_p;
+ gs_condition_t *pcond;
+
+ check_stype(*op, st_condition);
+ pcond = r_ptr(op, gs_condition_t);
+ if_debug1('"', "[\"]notify 0x%lx\n", (ulong) pcond);
+ pop(1);
+ op--;
+ if (pcond->waiting.head_index == 0) /* nothing to do */
+ return 0;
+ activate_waiting(current->scheduler, &pcond->waiting);
+ return zyield(i_ctx_p);
+}
+
+/* <lock> <condition> wait - */
+static int
+zwait(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_context_t *current = (gs_context_t *)i_ctx_p;
+ gs_scheduler_t *psched = current->scheduler;
+ gs_lock_t *plock;
+ gs_context_t *pctx;
+ gs_condition_t *pcond;
+
+ check_stype(op[-1], st_lock);
+ plock = r_ptr(op - 1, gs_lock_t);
+ check_stype(*op, st_condition);
+ pcond = r_ptr(op, gs_condition_t);
+ if_debug2('"', "[\"]wait lock 0x%lx, condition 0x%lx\n",
+ (ulong) plock, (ulong) pcond);
+ pctx = index_context(psched, plock->holder_index);
+ if (pctx == 0 || pctx != psched->current ||
+ (iimemory_local->save_level != 0 &&
+ (r_space(op - 1) == avm_local || r_space(op) == avm_local))
+ )
+ return_error(gs_error_invalidcontext);
+ check_estack(1);
+ lock_release(op - 1);
+ add_last(psched, &pcond->waiting, pctx);
+ push_op_estack(await_lock);
+ return o_reschedule;
+}
+/* When the condition is signaled, wait for acquiring the lock. */
+static int
+await_lock(i_ctx_t *i_ctx_p)
+{
+ gs_context_t *current = (gs_context_t *)i_ctx_p;
+ os_ptr op = osp;
+ int code = lock_acquire(op - 1, current);
+
+ if (code == 0) {
+ pop(2);
+ return 0;
+ }
+ /* We didn't acquire the lock. Re-execute the wait. */
+ push_op_estack(await_lock);
+ return code; /* o_reschedule */
+}
+
+/* Activate a list of waiting contexts, and reset the list. */
+static void
+activate_waiting(gs_scheduler_t *psched, ctx_list_t * pcl)
+{
+ gs_context_t *pctx = index_context(psched, pcl->head_index);
+ gs_context_t *next;
+
+ for (; pctx != 0; pctx = next) {
+ next = index_context(psched, pctx->next_index);
+ add_last(psched, &psched->active, pctx);
+ }
+ pcl->head_index = pcl->tail_index = 0;
+}
+
+/* ------ Miscellaneous operators ------ */
+
+/* - usertime <int> */
+static int
+zusertime_context(i_ctx_t *i_ctx_p)
+{
+ gs_context_t *current = (gs_context_t *)i_ctx_p;
+ gs_scheduler_t *psched = current->scheduler;
+ os_ptr op = osp;
+ long utime = context_usertime();
+
+ push(1);
+ if (!current->state.keep_usertime) {
+ /*
+ * This is the first time this context has executed usertime:
+ * we must track its execution time from now on.
+ */
+ psched->usertime_initial = utime;
+ current->state.keep_usertime = true;
+ }
+ make_int(op, current->state.usertime_total + utime -
+ psched->usertime_initial);
+ return 0;
+}
+
+/* ------ Internal procedures ------ */
+
+/* Create a context. */
+static int
+context_create(gs_scheduler_t * psched, gs_context_t ** ppctx,
+ const gs_dual_memory_t * dmem,
+ const gs_context_state_t *i_ctx_p, bool copy_state)
+{
+ /*
+ * Contexts are always created at the outermost save level, so they do
+ * not need to be allocated in stable memory for the sake of
+ * save/restore. However, context_reclaim needs to be able to test
+ * whether a given context belongs to a given local VM, and allocating
+ * contexts in stable local VM avoids the need to scan multiple save
+ * levels when making this test.
+ */
+ gs_memory_t *mem = gs_memory_stable((gs_memory_t *)dmem->space_local);
+ gs_context_t *pctx;
+ int code;
+ long ctx_index;
+ gs_context_t **pte;
+ ref *slice_ref;
+
+ pctx = gs_alloc_struct(mem, gs_context_t, &st_context, "context_create");
+ if (pctx == 0)
+ return_error(gs_error_VMerror);
+ if (copy_state) {
+ pctx->state = *i_ctx_p;
+ } else {
+ gs_context_state_t *pctx_st = &pctx->state;
+
+ code = context_state_alloc(&pctx_st, systemdict, dmem);
+ if (code < 0) {
+ gs_free_object(mem, pctx, "context_create");
+ return code;
+ }
+ }
+
+ if (dict_find_string(systemdict, "TIME_SLICE_INTERVAL", &slice_ref) > 0) {
+ reschedule_interval = slice_ref->value.intval;
+ }
+
+ ctx_index = gs_next_ids(mem, 1);
+ pctx->scheduler = psched;
+ pctx->status = cs_active;
+ pctx->index = ctx_index;
+ pctx->detach = false;
+ pctx->saved_local_vm = false;
+ pctx->visible = true;
+ pctx->next_index = 0;
+ pctx->joiner_index = 0;
+ pte = &psched->table[ctx_index % CTX_TABLE_SIZE];
+ pctx->table_next = *pte;
+ *pte = pctx;
+ *ppctx = pctx;
+ if (gs_debug_c('\'') | gs_debug_c('"'))
+ dmlprintf2(imemory, "[']create %ld at 0x%lx\n", ctx_index, (ulong) pctx);
+ return 0;
+}
+
+/* Check a context ID. Note that we do not check for context validity. */
+static int
+context_param(const gs_scheduler_t * psched, os_ptr op, gs_context_t ** ppctx)
+{
+ gs_context_t *pctx;
+
+ check_type(*op, t_integer);
+ pctx = index_context(psched, op->value.intval);
+ if (pctx == 0)
+ return_error(gs_error_invalidcontext);
+ *ppctx = pctx;
+ return 0;
+}
+
+/* Read the usertime as a single value. */
+static long
+context_usertime(void)
+{
+ long secs_ns[2];
+
+ gp_get_usertime(secs_ns);
+ return secs_ns[0] * 1000 + secs_ns[1] / 1000000;
+}
+
+/* Destroy a context. */
+static void
+context_destroy(gs_context_t * pctx)
+{
+ gs_ref_memory_t *mem = pctx->state.memory.space_local;
+ gs_scheduler_t *psched = pctx->scheduler;
+ gs_context_t **ppctx = &psched->table[pctx->index % CTX_TABLE_SIZE];
+
+ while (*ppctx != pctx)
+ ppctx = &(*ppctx)->table_next;
+ *ppctx = (*ppctx)->table_next;
+ if (gs_debug_c('\'') | gs_debug_c('"'))
+ dmlprintf3((const gs_memory_t *)mem,
+ "[']destroy %ld at 0x%lx, status = %d\n",
+ pctx->index, (ulong) pctx, pctx->status);
+ if (!context_state_free(&pctx->state))
+ gs_free_object((gs_memory_t *) mem, pctx, "context_destroy");
+}
+
+/* Copy the top elements of one stack to another. */
+/* Note that this does not push the elements: */
+/* the destination stack must have enough space preallocated. */
+static void
+stack_copy(ref_stack_t * to, const ref_stack_t * from, uint count,
+ uint from_index)
+{
+ long i;
+
+ for (i = (long)count - 1; i >= 0; --i)
+ *ref_stack_index(to, i) = *ref_stack_index(from, i + from_index);
+}
+
+/* Acquire a lock. Return 0 if acquired, o_reschedule if not. */
+static int
+lock_acquire(os_ptr op, gs_context_t * pctx)
+{
+ gs_lock_t *plock = r_ptr(op, gs_lock_t);
+
+ if (plock->holder_index == 0) {
+ plock->holder_index = pctx->index;
+ plock->scheduler = pctx->scheduler;
+ return 0;
+ }
+ add_last(pctx->scheduler, &plock->waiting, pctx);
+ return o_reschedule;
+}
+
+/* Release a lock. Return 0 if OK, gs_error_invalidcontext if not. */
+static int
+lock_release(ref * op)
+{
+ gs_lock_t *plock = r_ptr(op, gs_lock_t);
+ gs_scheduler_t *psched = plock->scheduler;
+ gs_context_t *pctx = index_context(psched, plock->holder_index);
+
+ if (pctx != 0 && pctx == psched->current) {
+ plock->holder_index = 0;
+ activate_waiting(psched, &plock->waiting);
+ return 0;
+ }
+ return_error(gs_error_invalidcontext);
+}
+
+/* ------ Initialization procedure ------ */
+
+/* We need to split the table because of the 16-element limit. */
+const op_def zcontext1_op_defs[] = {
+ {"0condition", zcondition},
+ {"0currentcontext", zcurrentcontext},
+ {"1detach", zdetach},
+ {"2.fork", zfork},
+ {"1join", zjoin},
+ {"4.localfork", zlocalfork},
+ {"0lock", zlock},
+ {"2monitor", zmonitor},
+ {"1notify", znotify},
+ {"2wait", zwait},
+ {"0yield", zyield},
+ /* Note that the following replace prior definitions */
+ /* in the indicated files: */
+ {"0usertime", zusertime_context}, /* zmisc.c */
+ op_def_end(0)
+};
+const op_def zcontext2_op_defs[] = {
+ /* Internal operators */
+ {"0%fork_done", fork_done},
+ {"1%finish_join", finish_join},
+ {"0%monitor_cleanup", monitor_cleanup},
+ {"0%monitor_release", monitor_release},
+ {"2%await_lock", await_lock},
+ op_def_end(zcontext_init)
+};
diff --git a/psi/zcontrol.c b/psi/zcontrol.c
new file mode 100644
index 000000000..5509fe7d7
--- /dev/null
+++ b/psi/zcontrol.c
@@ -0,0 +1,1086 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Control operators */
+#include "string_.h"
+#include "ghost.h"
+#include "stream.h"
+#include "oper.h"
+#include "estack.h"
+#include "files.h"
+#include "ipacked.h"
+#include "iutil.h"
+#include "store.h"
+
+/* Forward references */
+static int check_for_exec(const_os_ptr);
+static int no_cleanup(i_ctx_t *);
+static uint count_exec_stack(i_ctx_t *, bool);
+static uint count_to_stopped(i_ctx_t *, long);
+static int unmatched_exit(os_ptr, op_proc_t);
+
+/* See the comment in opdef.h for an invariant which allows */
+/* more efficient implementation of for, loop, and repeat. */
+
+/* <[test0 body0 ...]> .cond - */
+static int cond_continue(i_ctx_t *);
+static int
+zcond(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep = esp;
+
+ /* Push the array on the e-stack and call the continuation. */
+ if (!r_is_array(op))
+ return_op_typecheck(op);
+ check_execute(*op);
+ if ((r_size(op) & 1) != 0)
+ return_error(gs_error_rangecheck);
+ if (r_size(op) == 0)
+ return zpop(i_ctx_p);
+ check_estack(3);
+ esp = ep += 3;
+ ref_assign(ep - 2, op); /* the cond body */
+ make_op_estack(ep - 1, cond_continue);
+ array_get(imemory, op, 0L, ep);
+ esfile_check_cache();
+ pop(1);
+ return o_push_estack;
+}
+static int
+cond_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep = esp;
+ int code;
+
+ /* The top element of the e-stack is the remaining tail of */
+ /* the cond body. The top element of the o-stack should be */
+ /* the (boolean) result of the test that is the first element */
+ /* of the tail. */
+ check_type(*op, t_boolean);
+ if (op->value.boolval) { /* true */
+ array_get(imemory, ep, 1L, ep);
+ esfile_check_cache();
+ code = o_pop_estack;
+ } else if (r_size(ep) > 2) { /* false */
+ const ref_packed *elts = ep->value.packed;
+
+ check_estack(2);
+ r_dec_size(ep, 2);
+ elts = packed_next(elts);
+ elts = packed_next(elts);
+ ep->value.packed = elts;
+ array_get(imemory, ep, 0L, ep + 2);
+ make_op_estack(ep + 1, cond_continue);
+ esp = ep + 2;
+ esfile_check_cache();
+ code = o_push_estack;
+ } else { /* fall off end of cond */
+ esp = ep - 1;
+ code = o_pop_estack;
+ }
+ pop(1); /* get rid of the boolean */
+ return code;
+}
+
+/* <obj> exec - */
+int
+zexec(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_op(1);
+ code = check_for_exec(op);
+ if (code < 0) {
+ return code;
+ }
+ if (!r_has_attr(op, a_executable)) {
+ return 0; /* shortcut, literal object just gets pushed back */
+ }
+ check_estack(1);
+ ++esp;
+ ref_assign(esp, op);
+ esfile_check_cache();
+ pop(1);
+ return o_push_estack;
+}
+
+/* <obj1> ... <objn> <n> .execn - */
+static int
+zexecn(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint n, i;
+ es_ptr esp_orig;
+
+ check_int_leu(*op, max_uint - 1);
+ n = (uint) op->value.intval;
+ check_op(n + 1);
+ check_estack(n);
+ esp_orig = esp;
+ for (i = 0; i < n; ++i) {
+ const ref *rp = ref_stack_index(&o_stack, (long)(i + 1));
+
+ /* Make sure this object is legal to execute. */
+ if (ref_type_uses_access(r_type(rp))) {
+ if (!r_has_attr(rp, a_execute) &&
+ r_has_attr(rp, a_executable)
+ ) {
+ esp = esp_orig;
+ return_error(gs_error_invalidaccess);
+ }
+ }
+ /* Executable nulls have a special meaning on the e-stack, */
+ /* so since they are no-ops, don't push them. */
+ if (!r_has_type_attrs(rp, t_null, a_executable)) {
+ ++esp;
+ ref_assign(esp, rp);
+ }
+ }
+ esfile_check_cache();
+ pop(n + 1);
+ return o_push_estack;
+}
+
+/* <obj> superexec - */
+static int end_superexec(i_ctx_t *);
+static int
+zsuperexec(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep;
+
+ check_op(1);
+ if (!r_has_attr(op, a_executable))
+ return 0; /* literal object just gets pushed back */
+ check_estack(2);
+ ep = esp += 3;
+ make_mark_estack(ep - 2, es_other, end_superexec); /* error case */
+ make_op_estack(ep - 1, end_superexec); /* normal case */
+ ref_assign(ep, op);
+ esfile_check_cache();
+ pop(1);
+ i_ctx_p->in_superexec++;
+ return o_push_estack;
+}
+static int
+end_superexec(i_ctx_t *i_ctx_p)
+{
+ i_ctx_p->in_superexec--;
+ return 0;
+}
+
+/* <array> <executable> .runandhide <obj> */
+/* before executing <executable>, <array> is been removed from */
+/* the operand stack and placed on the execstack with attributes */
+/* changed to 'noaccess'. */
+/* After execution, the array will be placed on the top of the */
+/* operand stack (on top of any elemetns pushed by <executable> */
+/* for both the normal case and for the error case. */
+static int end_runandhide(i_ctx_t *);
+static int err_end_runandhide(i_ctx_t *);
+static int
+zrunandhide(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep;
+
+ check_op(2);
+ if (!r_is_array(op - 1))
+ return_op_typecheck(op);
+ if (!r_has_attr(op, a_executable))
+ return 0; /* literal object just gets pushed back */
+ check_estack(5);
+ ep = esp += 5;
+ make_mark_estack(ep - 4, es_other, err_end_runandhide); /* error case */
+ make_op_estack(ep - 1, end_runandhide); /* normal case */
+ ref_assign(ep, op);
+ /* Store the object we are hiding and it's current tas.type_attrs */
+ /* on the exec stack then change to 'noaccess' */
+ make_int(ep - 3, (int)op[-1].tas.type_attrs);
+ ref_assign(ep - 2, op - 1);
+ r_clear_attrs(ep - 2, a_all);
+ /* replace the array with a special kind of mark that has a_read access */
+ esfile_check_cache();
+ pop(2);
+ return o_push_estack;
+}
+static int
+runandhide_restore_hidden(i_ctx_t *i_ctx_p, ref *obj, ref *attrs)
+{
+ os_ptr op = osp;
+
+ push(1);
+ /* restore the hidden_object and its type_attrs */
+ ref_assign(op, obj);
+ r_clear_attrs(op, a_all);
+ r_set_attrs(op, attrs->value.intval);
+ return 0;
+}
+
+/* - %end_runandhide hiddenobject */
+static int
+end_runandhide(i_ctx_t *i_ctx_p)
+{
+ int code;
+
+ if ((code = runandhide_restore_hidden(i_ctx_p, esp, esp - 1)) < 0)
+ return code;
+ esp -= 2; /* pop the hidden value and its atributes */
+ return o_pop_estack;
+}
+
+/* restore hidden object for error returns */
+static int
+err_end_runandhide(i_ctx_t *i_ctx_p)
+{
+ int code;
+
+ if ((code = runandhide_restore_hidden(i_ctx_p, esp + 3, esp + 2)) < 0)
+ return code;
+ return 0;
+}
+
+/* <bool> <proc> if - */
+int
+zif(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_proc(*op);
+ check_type(op[-1], t_boolean);
+ if (op[-1].value.boolval) {
+ check_estack(1);
+ ++esp;
+ ref_assign(esp, op);
+ esfile_check_cache();
+ }
+ pop(2);
+ return o_push_estack;
+}
+
+/* <bool> <proc_true> <proc_false> ifelse - */
+int
+zifelse(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_proc(*op);
+ check_proc(op[-1]);
+ check_type(op[-2], t_boolean);
+ check_estack(1);
+ ++esp;
+ if (op[-2].value.boolval) {
+ ref_assign(esp, op - 1);
+ } else {
+ ref_assign(esp, op);
+ }
+ esfile_check_cache();
+ pop(3);
+ return o_push_estack;
+}
+
+/* <init> <step> <limit> <proc> for - */
+static int
+ for_pos_int_continue(i_ctx_t *),
+ for_neg_int_continue(i_ctx_t *),
+ for_real_continue(i_ctx_t *);
+int
+zfor(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ register es_ptr ep;
+ int code;
+ float params[3];
+
+ /* Mostly undocumented, and somewhat bizarre Adobe behavior discovered */
+ /* with the CET (28-05) and FTS (124-01) is that the proc is not run */
+ /* if BOTH the initial value and increment are zero. */
+ if ((code = float_params(op - 1, 3, params)) < 0)
+ return code;
+ if ( params[0] == 0.0 && params[1] == 0.0 ) {
+ pop(4); /* don't run the proc */
+ return 0;
+ }
+ check_estack(7);
+ ep = esp + 6;
+ check_proc(*op);
+ /* Push a mark, the control variable set to the initial value, */
+ /* the increment, the limit, and the procedure, */
+ /* and invoke the continuation operator. */
+ if (r_has_type(op - 3, t_integer) &&
+ r_has_type(op - 2, t_integer)
+ ) {
+ make_int(ep - 4, op[-3].value.intval);
+ make_int(ep - 3, op[-2].value.intval);
+ switch (r_type(op - 1)) {
+ case t_integer:
+ make_int(ep - 2, op[-1].value.intval);
+ break;
+ case t_real:
+ make_int(ep - 2, (long)op[-1].value.realval);
+ break;
+ default:
+ return_op_typecheck(op - 1);
+ }
+ if (ep[-3].value.intval >= 0)
+ make_op_estack(ep, for_pos_int_continue);
+ else
+ make_op_estack(ep, for_neg_int_continue);
+ } else {
+ make_real(ep - 4, params[0]);
+ make_real(ep - 3, params[1]);
+ make_real(ep - 2, params[2]);
+ make_op_estack(ep, for_real_continue);
+ }
+ make_mark_estack(ep - 5, es_for, no_cleanup);
+ ref_assign(ep - 1, op);
+ esp = ep;
+ pop(4);
+ return o_push_estack;
+}
+/* Continuation operators for for, separate for positive integer, */
+/* negative integer, and real. */
+/* Execution stack contains mark, control variable, increment, */
+/* limit, and procedure (procedure is topmost.) */
+/* Continuation operator for positive integers. */
+static int
+for_pos_int_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ register es_ptr ep = esp;
+ int var = ep[-3].value.intval;
+
+ if (var > ep[-1].value.intval) {
+ esp -= 5; /* pop everything */
+ return o_pop_estack;
+ }
+ push(1);
+ make_int(op, var);
+ ep[-3].value.intval = var + ep[-2].value.intval;
+ ref_assign_inline(ep + 2, ep); /* saved proc */
+ esp = ep + 2;
+ return o_push_estack;
+}
+/* Continuation operator for negative integers. */
+static int
+for_neg_int_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ register es_ptr ep = esp;
+ int var = ep[-3].value.intval;
+
+ if (var < ep[-1].value.intval) {
+ esp -= 5; /* pop everything */
+ return o_pop_estack;
+ }
+ push(1);
+ make_int(op, var);
+ ep[-3].value.intval = var + ep[-2].value.intval;
+ ref_assign(ep + 2, ep); /* saved proc */
+ esp = ep + 2;
+ return o_push_estack;
+}
+/* Continuation operator for reals. */
+static int
+for_real_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep = esp;
+ float var = ep[-3].value.realval;
+ float incr = ep[-2].value.realval;
+
+ if (incr >= 0 ? (var > ep[-1].value.realval) :
+ (var < ep[-1].value.realval)
+ ) {
+ esp -= 5; /* pop everything */
+ return o_pop_estack;
+ }
+ push(1);
+ ref_assign(op, ep - 3);
+ ep[-3].value.realval = var + incr;
+ esp = ep + 2;
+ ref_assign(ep + 2, ep); /* saved proc */
+ return o_push_estack;
+}
+
+/*
+ * Here we provide an internal variant of 'for' that enumerates the values
+ * A, ((N-1)*A+1*B)/N, ((N-2)*A+2*B)/N, ..., B precisely. The arguments are
+ * A (real), N (integer), and B (real). We need this for loading caches such
+ * as the transfer function cache.
+ *
+ * NOTE: This computation must match the SAMPLE_LOOP_VALUE macro in gscie.h.
+ */
+static int for_samples_continue(i_ctx_t *);
+/* <first> <count> <last> <proc> %for_samples - */
+int
+zfor_samples(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep;
+
+ check_type(op[-3], t_real);
+ check_type(op[-2], t_integer);
+ check_type(op[-1], t_real);
+ check_proc(*op);
+ check_estack(8);
+ ep = esp + 7;
+ make_mark_estack(ep - 6, es_for, no_cleanup);
+ make_int(ep - 5, 0);
+ memcpy(ep - 4, op - 3, 3 * sizeof(ref));
+ ref_assign(ep - 1, op);
+ make_op_estack(ep, for_samples_continue);
+ esp = ep;
+ pop(4);
+ return o_push_estack;
+}
+/* Continuation procedure */
+static int
+for_samples_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep = esp;
+ int var = ep[-4].value.intval;
+ float a = ep[-3].value.realval;
+ int n = ep[-2].value.intval;
+ float b = ep[-1].value.realval;
+
+ if (var > n) {
+ esp -= 6; /* pop everything */
+ return o_pop_estack;
+ }
+ push(1);
+ make_real(op, ((n - var) * a + var * b) / n);
+ ep[-4].value.intval = var + 1;
+ ref_assign_inline(ep + 2, ep); /* saved proc */
+ esp = ep + 2;
+ return o_push_estack;
+}
+
+/* <int> <proc> repeat - */
+static int repeat_continue(i_ctx_t *);
+int
+zrepeat(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ check_proc(*op);
+ check_type(op[-1], t_integer);
+ if (op[-1].value.intval < 0)
+ return_error(gs_error_rangecheck);
+ check_estack(5);
+ /* Push a mark, the count, and the procedure, and invoke */
+ /* the continuation operator. */
+ push_mark_estack(es_for, no_cleanup);
+ *++esp = op[-1];
+ *++esp = *op;
+ make_op_estack(esp + 1, repeat_continue);
+ pop(2);
+ return repeat_continue(i_ctx_p);
+}
+/* Continuation operator for repeat */
+static int
+repeat_continue(i_ctx_t *i_ctx_p)
+{
+ es_ptr ep = esp; /* saved proc */
+
+ if (--(ep[-1].value.intval) >= 0) { /* continue */
+ esp += 2;
+ ref_assign(esp, ep);
+ return o_push_estack;
+ } else { /* done */
+ esp -= 3; /* pop mark, count, proc */
+ return o_pop_estack;
+ }
+}
+
+/* <proc> loop */
+static int loop_continue(i_ctx_t *);
+static int
+zloop(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_proc(*op);
+ check_estack(4);
+ /* Push a mark and the procedure, and invoke */
+ /* the continuation operator. */
+ push_mark_estack(es_for, no_cleanup);
+ *++esp = *op;
+ make_op_estack(esp + 1, loop_continue);
+ pop(1);
+ return loop_continue(i_ctx_p);
+}
+/* Continuation operator for loop */
+static int
+loop_continue(i_ctx_t *i_ctx_p)
+{
+ register es_ptr ep = esp; /* saved proc */
+
+ ref_assign(ep + 2, ep);
+ esp = ep + 2;
+ return o_push_estack;
+}
+
+/* - exit - */
+static int
+zexit(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref_stack_enum_t rsenum;
+ uint scanned = 0;
+
+ ref_stack_enum_begin(&rsenum, &e_stack);
+ do {
+ uint used = rsenum.size;
+ es_ptr ep = rsenum.ptr + used - 1;
+ uint count = used;
+
+ for (; count; count--, ep--)
+ if (r_is_estack_mark(ep))
+ switch (estack_mark_index(ep)) {
+ case es_for:
+ pop_estack(i_ctx_p, scanned + (used - count + 1));
+ return o_pop_estack;
+ case es_stopped:
+ return_error(gs_error_invalidexit); /* not a loop */
+ }
+ scanned += used;
+ } while (ref_stack_enum_next(&rsenum));
+ /* No mark, quit. (per Adobe documentation) */
+ push(2);
+ return unmatched_exit(op, zexit);
+}
+
+/*
+ * .stopped pushes the following on the e-stack:
+ * - A mark with type = es_stopped and procedure = no_cleanup.
+ * - The result to be pushed on a normal return.
+ * - The signal mask for .stop.
+ * - The procedure %stopped_push, to handle the normal return case.
+ */
+
+/* In the normal (no-error) case, pop the mask from the e-stack, */
+/* and move the result to the o-stack. */
+static int
+stopped_push(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ *op = esp[-1];
+ esp -= 3;
+ return o_pop_estack;
+}
+
+/* - stop - */
+/* Equivalent to true 1 .stop. */
+/* This is implemented in C because if were a pseudo-operator, */
+/* the stacks would get restored in case of an error. */
+static int
+zstop(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint count = count_to_stopped(i_ctx_p, 1L);
+
+ if (count) {
+ /*
+ * If there are any t_oparrays on the e-stack, they will pop
+ * any new items from the o-stack. Wait to push the 'true'
+ * until we have run all the unwind procedures.
+ */
+ check_ostack(2);
+ pop_estack(i_ctx_p, count);
+ op = osp;
+ push(1);
+ make_true(op);
+ return o_pop_estack;
+ }
+ /* No mark, quit. (per Adobe documentation) */
+ push(2);
+ return unmatched_exit(op, zstop);
+}
+
+/* <result> <mask> .stop - */
+static int
+zzstop(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint count;
+
+ check_type(*op, t_integer);
+ count = count_to_stopped(i_ctx_p, op->value.intval);
+ if (count) {
+ /*
+ * If there are any t_oparrays on the e-stack, they will pop
+ * any new items from the o-stack. Wait to push the result
+ * until we have run all the unwind procedures.
+ */
+ ref save_result;
+
+ check_op(2);
+ save_result = op[-1];
+ pop(2);
+ pop_estack(i_ctx_p, count);
+ op = osp;
+ push(1);
+ *op = save_result;
+ return o_pop_estack;
+ }
+ /* No mark, quit. (per Adobe documentation) */
+ return unmatched_exit(op, zzstop);
+}
+
+/* <obj> stopped <stopped> */
+/* Equivalent to false 1 .stopped. */
+/* This is implemented in C because if were a pseudo-operator, */
+/* the stacks would get restored in case of an error. */
+static int
+zstopped(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_op(1);
+ /* Mark the execution stack, and push the default result */
+ /* in case control returns normally. */
+ check_estack(5);
+ push_mark_estack(es_stopped, no_cleanup);
+ ++esp;
+ make_false(esp); /* save the result */
+ ++esp;
+ make_int(esp, 1); /* save the signal mask */
+ push_op_estack(stopped_push);
+ push_op_estack(zexec); /* execute the operand */
+ return o_push_estack;
+}
+
+/* <obj> <result> <mask> .stopped <result> */
+static int
+zzstopped(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ check_type(*op, t_integer);
+ check_op(3);
+ /* Mark the execution stack, and push the default result */
+ /* in case control returns normally. */
+ check_estack(5);
+ push_mark_estack(es_stopped, no_cleanup);
+ *++esp = op[-1]; /* save the result */
+ *++esp = *op; /* save the signal mask */
+ push_op_estack(stopped_push);
+ push_op_estack(zexec); /* execute the operand */
+ pop(2);
+ return o_push_estack;
+}
+
+/* <mask> .instopped false */
+/* <mask> .instopped <result> true */
+static int
+zinstopped(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint count;
+
+ check_type(*op, t_integer);
+ count = count_to_stopped(i_ctx_p, op->value.intval);
+ if (count) {
+ push(1);
+ op[-1] = *ref_stack_index(&e_stack, count - 2); /* default result */
+ make_true(op);
+ } else
+ make_false(op);
+ return 0;
+}
+
+/* <include_marks> .countexecstack <int> */
+/* - countexecstack <int> */
+/* countexecstack is an operator solely for the sake of the Genoa tests. */
+static int
+zcountexecstack(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, count_exec_stack(i_ctx_p, false));
+ return 0;
+}
+static int
+zcountexecstack1(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_boolean);
+ make_int(op, count_exec_stack(i_ctx_p, op->value.boolval));
+ return 0;
+}
+
+/* <array> <include_marks> .execstack <subarray> */
+/* <array> execstack <subarray> */
+/* execstack is an operator solely for the sake of the Genoa tests. */
+static int execstack_continue(i_ctx_t *);
+static int execstack2_continue(i_ctx_t *);
+static int
+push_execstack(i_ctx_t *i_ctx_p, os_ptr op1, bool include_marks,
+ op_proc_t cont)
+{
+ uint size;
+ /*
+ * We can't do this directly, because the interpreter
+ * might have cached some state. To force the interpreter
+ * to update the stored state, we push a continuation on
+ * the exec stack; the continuation is executed immediately,
+ * and does the actual transfer.
+ */
+ uint depth;
+
+ if (!r_is_array(op1))
+ return_op_typecheck(op1);
+ /* Check the length before the write access per CET 28-03 */
+ size = r_size(op1);
+ depth = count_exec_stack(i_ctx_p, include_marks);
+ if (depth > size)
+ return_error(gs_error_rangecheck);
+ check_write(*op1);
+ {
+ int code = ref_stack_store_check(&e_stack, op1, size, 0);
+
+ if (code < 0)
+ return code;
+ }
+ check_estack(1);
+ r_set_size(op1, depth);
+ push_op_estack(cont);
+ return o_push_estack;
+}
+static int
+zexecstack(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ return push_execstack(i_ctx_p, op, false, execstack_continue);
+}
+static int
+zexecstack2(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_boolean);
+ return push_execstack(i_ctx_p, op - 1, op->value.boolval, execstack2_continue);
+}
+/* Continuation operator to do the actual transfer. */
+/* r_size(op1) was set just above. */
+static int
+do_execstack(i_ctx_t *i_ctx_p, bool include_marks, os_ptr op1)
+{
+ os_ptr op = osp;
+ ref *arefs = op1->value.refs;
+ uint asize = r_size(op1);
+ uint i;
+ ref *rq;
+
+ /*
+ * Copy elements from the stack to the array,
+ * optionally skipping executable nulls.
+ * Clear the executable bit in any internal operators, and
+ * convert t_structs and t_astructs (which can only appear
+ * in connection with stack marks, which means that they will
+ * probably be freed when unwinding) to something harmless.
+ */
+ for (i = 0, rq = arefs + asize; rq != arefs; ++i) {
+ const ref *rp = ref_stack_index(&e_stack, (long)i);
+
+ if (r_has_type_attrs(rp, t_null, a_executable) && !include_marks)
+ continue;
+ --rq;
+ ref_assign_old(op1, rq, rp, "execstack");
+ switch (r_type(rq)) {
+ case t_operator: {
+ uint opidx = op_index(rq);
+
+ if (opidx == 0 || op_def_is_internal(op_index_def(opidx)))
+ r_clear_attrs(rq, a_executable);
+ break;
+ }
+ case t_struct:
+ case t_astruct: {
+ const char *tname = rq->value.pstruct ?
+ gs_struct_type_name_string(
+ gs_object_type(imemory, rq->value.pstruct))
+ : "NULL";
+
+ make_const_string(rq, a_readonly | avm_foreign,
+ strlen(tname), (const byte *)tname);
+ break;
+ }
+ default:
+ ;
+ }
+ }
+ pop(op - op1);
+ return 0;
+}
+static int
+execstack_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ return do_execstack(i_ctx_p, false, op);
+}
+static int
+execstack2_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ return do_execstack(i_ctx_p, op->value.boolval, op - 1);
+}
+
+/* - .needinput - */
+static int
+zneedinput(i_ctx_t *i_ctx_p)
+{
+ return gs_error_NeedInput; /* interpreter will exit to caller */
+}
+
+/* <obj> <int> .quit - */
+static int
+zquit(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_op(2);
+ check_type(*op, t_integer);
+ return_error(gs_error_Quit); /* Interpreter will do the exit */
+}
+
+/* - currentfile <file> */
+static ref *zget_current_file(i_ctx_t *);
+static int
+zcurrentfile(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *fp;
+
+ push(1);
+ /* Check the cache first */
+ if (esfile != 0) {
+#ifdef DEBUG
+ /* Check that esfile is valid. */
+ ref *efp = zget_current_file(i_ctx_p);
+
+ if (esfile != efp) {
+ lprintf2("currentfile: esfile=0x%lx, efp=0x%lx\n",
+ (ulong) esfile, (ulong) efp);
+ ref_assign(op, efp);
+ } else
+#endif
+ ref_assign(op, esfile);
+ } else if ((fp = zget_current_file(i_ctx_p)) == 0) { /* Return an invalid file object. */
+ /* This doesn't make a lot of sense to me, */
+ /* but it's what the PostScript manual specifies. */
+ make_invalid_file(i_ctx_p, op);
+ } else {
+ ref_assign(op, fp);
+ esfile_set_cache(fp);
+ }
+ /* Make the returned value literal. */
+ r_clear_attrs(op, a_executable);
+ return 0;
+}
+/* Get the current file from which the interpreter is reading. */
+static ref *
+zget_current_file(i_ctx_t *i_ctx_p)
+{
+ ref_stack_enum_t rsenum;
+
+ ref_stack_enum_begin(&rsenum, &e_stack);
+ do {
+ uint count = rsenum.size;
+ es_ptr ep = rsenum.ptr + count - 1;
+
+ for (; count; count--, ep--)
+ if (r_has_type_attrs(ep, t_file, a_executable))
+ return ep;
+ } while (ref_stack_enum_next(&rsenum));
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+/* We need to split the table because of the 16-element limit. */
+const op_def zcontrol1_op_defs[] = {
+ {"1.cond", zcond},
+ {"0countexecstack", zcountexecstack},
+ {"1.countexecstack", zcountexecstack1},
+ {"0currentfile", zcurrentfile},
+ {"1exec", zexec},
+ {"1.execn", zexecn},
+ {"1execstack", zexecstack},
+ {"2.execstack", zexecstack2},
+ {"0exit", zexit},
+ {"2if", zif},
+ {"3ifelse", zifelse},
+ {"0.instopped", zinstopped},
+ {"0.needinput", zneedinput},
+ op_def_end(0)
+};
+const op_def zcontrol2_op_defs[] = {
+ {"4for", zfor},
+ {"1loop", zloop},
+ {"2.quit", zquit},
+ {"2repeat", zrepeat},
+ {"0stop", zstop},
+ {"1.stop", zzstop},
+ {"1stopped", zstopped},
+ {"2.stopped", zzstopped},
+ op_def_end(0)
+};
+const op_def zcontrol3_op_defs[] = {
+ /* Internal operators */
+ {"1%cond_continue", cond_continue},
+ {"1%execstack_continue", execstack_continue},
+ {"2%execstack2_continue", execstack2_continue},
+ {"0%for_pos_int_continue", for_pos_int_continue},
+ {"0%for_neg_int_continue", for_neg_int_continue},
+ {"0%for_real_continue", for_real_continue},
+ {"4%for_samples", zfor_samples},
+ {"0%for_samples_continue", for_samples_continue},
+ {"0%loop_continue", loop_continue},
+ {"0%repeat_continue", repeat_continue},
+ {"0%stopped_push", stopped_push},
+ {"1superexec", zsuperexec},
+ {"0%end_superexec", end_superexec},
+ {"2.runandhide", zrunandhide},
+ {"0%end_runandhide", end_runandhide},
+ op_def_end(0)
+};
+
+/* ------ Internal routines ------ */
+
+/*
+ * Check the operand of exec or stopped. Return 0 if OK to execute, or a
+ * negative error code. We emulate an apparent bug in Adobe interpreters,
+ * which cause an invalidaccess error when 'exec'ing a noaccess literal
+ * (other than dictionaries). We also match the Adobe interpreters in that
+ * we catch noaccess executable objects here, rather than waiting for the
+ * interpreter to catch them, so that we can signal the error with the
+ * object still on the operand stack.
+ */
+static bool
+check_for_exec(const_os_ptr op)
+{
+ if (!r_has_attr(op, a_execute) && /* only true if noaccess */
+ ref_type_uses_access(r_type(op)) &&
+ (r_has_attr(op, a_executable) || !r_has_type(op, t_dictionary))
+ ) {
+ return_error(gs_error_invalidaccess);
+ }
+ return 0;
+}
+
+/* Vacuous cleanup routine */
+static int
+no_cleanup(i_ctx_t *i_ctx_p)
+{
+ return 0;
+}
+
+/*
+ * Count the number of elements on the exec stack, with or without
+ * the normally invisible elements (*op is a Boolean that indicates this).
+ */
+static uint
+count_exec_stack(i_ctx_t *i_ctx_p, bool include_marks)
+{
+ uint count = ref_stack_count(&e_stack);
+
+ if (!include_marks) {
+ uint i;
+
+ for (i = count; i--;)
+ if (r_has_type_attrs(ref_stack_index(&e_stack, (long)i),
+ t_null, a_executable))
+ --count;
+ }
+ return count;
+}
+
+/*
+ * Count the number of elements down to and including the first 'stopped'
+ * mark on the e-stack with a given mask. Return 0 if there is no 'stopped'
+ * mark.
+ */
+static uint
+count_to_stopped(i_ctx_t *i_ctx_p, long mask)
+{
+ ref_stack_enum_t rsenum;
+ uint scanned = 0;
+
+ ref_stack_enum_begin(&rsenum, &e_stack);
+ do {
+ uint used = rsenum.size;
+ es_ptr ep = rsenum.ptr + used - 1;
+ uint count = used;
+
+ for (; count; count--, ep--) {
+ if (r_is_estack_mark(ep)) {
+ if (estack_mark_index(ep) == es_stopped &&
+ (ep[2].value.intval & mask) != 0)
+ return scanned + (used - count + 1);
+ }
+ }
+ scanned += used;
+ } while (ref_stack_enum_next(&rsenum));
+ return 0;
+}
+
+/*
+ * Pop the e-stack, executing cleanup procedures as needed.
+ * We could make this more efficient using ref_stack_enum_*,
+ * but it isn't used enough to make this worthwhile.
+ */
+void
+pop_estack(i_ctx_t *i_ctx_p, uint count)
+{
+ uint idx = 0;
+ uint popped = 0;
+
+ esfile_clear_cache();
+ for (; idx < count; idx++) {
+ ref *ep = ref_stack_index(&e_stack, idx - popped);
+
+ if (r_is_estack_mark(ep)) {
+ ref_stack_pop(&e_stack, idx + 1 - popped);
+ popped = idx + 1;
+ (*real_opproc(ep)) (i_ctx_p);
+ }
+ }
+ ref_stack_pop(&e_stack, count - popped);
+}
+
+/*
+ * Execute a quit in the case of an exit or stop with no appropriate
+ * enclosing control scope (loop or stopped). The caller has already
+ * ensured two free slots on the top of the o-stack.
+ */
+static int
+unmatched_exit(os_ptr op, op_proc_t opproc)
+{
+ make_oper(op - 1, 0, opproc);
+ make_int(op, gs_error_invalidexit);
+ return_error(gs_error_Quit);
+}
diff --git a/psi/zcrd.c b/psi/zcrd.c
new file mode 100644
index 000000000..8e6a5b9d3
--- /dev/null
+++ b/psi/zcrd.c
@@ -0,0 +1,447 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* CIE color rendering operators */
+#include "math_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gscspace.h"
+#include "gscolor2.h"
+#include "gscrd.h"
+#include "gscrdp.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "icie.h"
+#include "iparam.h"
+#include "ivmspace.h"
+#include "store.h" /* for make_null */
+
+/* Forward references */
+static int zcrd1_proc_params(const gs_memory_t *mem, os_ptr op, ref_cie_render_procs * pcprocs);
+static int zcrd1_params(os_ptr op, gs_cie_render * pcrd,
+ ref_cie_render_procs * pcprocs, gs_memory_t * mem);
+/* - currentcolorrendering <dict> */
+static int
+zcurrentcolorrendering(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ *op = istate->colorrendering.dict;
+ return 0;
+}
+
+/* <dict> .buildcolorrendering1 <crd> */
+static int
+zbuildcolorrendering1(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_memory_t *mem = gs_state_memory(igs);
+ int code;
+ es_ptr ep = esp;
+ gs_cie_render *pcrd;
+ ref_cie_render_procs procs;
+
+ check_read_type(*op, t_dictionary);
+ check_dict_read(*op);
+ code = gs_cie_render1_build(&pcrd, mem, ".buildcolorrendering1");
+ if (code < 0)
+ return code;
+ code = zcrd1_params(op, pcrd, &procs, mem);
+ if (code < 0 ) {
+ rc_free_struct(pcrd, ".buildcolorrendering1");
+ esp = ep;
+ return code;
+ }
+ /****** FIX refct ******/
+ /*rc_decrement(pcrd, ".buildcolorrendering1"); *//* build sets rc = 1 */
+ istate->colorrendering.dict = *op;
+ make_istruct_new(op, a_readonly, pcrd);
+ return (esp == ep ? 0 : o_push_estack);
+}
+
+/* <dict> .builddevicecolorrendering1 <crd> */
+static int
+zbuilddevicecolorrendering1(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_memory_t *mem = gs_state_memory(igs);
+ dict_param_list list;
+ gs_cie_render *pcrd = 0;
+ int code;
+
+ check_type(*op, t_dictionary);
+ code = dict_param_list_read(&list, op, NULL, false, iimemory);
+ if (code < 0)
+ return code;
+ code = gs_cie_render1_build(&pcrd, mem, ".builddevicecolorrendering1");
+ if (code >= 0) {
+ code = param_get_cie_render1(pcrd, (gs_param_list *) & list,
+ gs_currentdevice(igs));
+ if (code >= 0) {
+ /****** FIX refct ******/
+ /*rc_decrement(pcrd, ".builddevicecolorrendering1"); *//* build sets rc = 1 */
+ }
+ }
+ iparam_list_release(&list);
+ if (code < 0) {
+ rc_free_struct(pcrd, ".builddevicecolorrendering1");
+ return code;
+ }
+ istate->colorrendering.dict = *op;
+ make_istruct_new(op, a_readonly, pcrd);
+ return 0;
+}
+
+/* <dict> <crd> .setcolorrendering1 - */
+static int
+zsetcolorrendering1(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr ep = esp;
+ ref_cie_render_procs procs;
+ int code;
+
+ check_type(op[-1], t_dictionary);
+ check_stype(*op, st_cie_render1);
+ code = zcrd1_proc_params(imemory, op - 1, &procs);
+ if (code < 0)
+ return code;
+ code = gs_setcolorrendering(igs, r_ptr(op, gs_cie_render));
+ if (code < 0)
+ return code;
+ if (gs_cie_cs_common(igs) != 0 &&
+ (code = cie_cache_joint(i_ctx_p, &procs, gs_cie_cs_common(igs), igs)) < 0
+ )
+ return code;
+ istate->colorrendering.dict = op[-1];
+ istate->colorrendering.procs = procs;
+ pop(2);
+ return (esp == ep ? 0 : o_push_estack);
+}
+
+/* <dict> <crd> .setdevicecolorrendering1 - */
+static int
+zsetdevicecolorrendering1(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ ref_cie_render_procs procs;
+
+ check_type(op[-1], t_dictionary);
+ check_stype(*op, st_cie_render1);
+ code = gs_setcolorrendering(igs, r_ptr(op, gs_cie_render));
+ if (code < 0)
+ return code;
+ refset_null((ref *)&procs, sizeof(procs) / sizeof(ref));
+ if (gs_cie_cs_common(igs) != 0 &&
+ (code = cie_cache_joint(i_ctx_p, &procs, gs_cie_cs_common(igs), igs)) < 0
+ )
+ return code;
+ istate->colorrendering.dict = op[-1];
+ refset_null((ref *)&istate->colorrendering.procs,
+ sizeof(istate->colorrendering.procs) / sizeof(ref));
+ pop(2);
+ return 0;
+}
+
+/* Get ColorRenderingType 1 procedures from the PostScript dictionary. */
+static int
+zcrd1_proc_params(const gs_memory_t *mem,
+ os_ptr op, ref_cie_render_procs * pcprocs)
+{
+ int code;
+ ref *pRT;
+
+ code = dict_proc3_param(mem, op, "EncodeLMN", &pcprocs->EncodeLMN);
+ if (code < 0)
+ return code;
+ code = dict_proc3_param(mem, op, "EncodeABC", &pcprocs->EncodeABC);
+ if (code < 0)
+ return code;
+ code = dict_proc3_param(mem, op, "TransformPQR", &pcprocs->TransformPQR);
+ if (code < 0)
+ return code;
+ if (code == 1)
+ return gs_note_error(gs_error_undefined);
+ if (dict_find_string(op, "RenderTable", &pRT) > 0) {
+ const ref *prte;
+ int size;
+ int i;
+
+ check_read_type(*pRT, t_array);
+ size = r_size(pRT);
+ if (size < 5)
+ return_error(gs_error_rangecheck);
+ prte = pRT->value.const_refs;
+ for (i = 5; i < size; i++)
+ check_proc_only(prte[i]);
+ make_const_array(&pcprocs->RenderTableT, a_readonly | r_space(pRT),
+ size - 5, prte + 5);
+ } else
+ make_null(&pcprocs->RenderTableT);
+ return 0;
+}
+
+/* Get ColorRenderingType 1 parameters from the PostScript dictionary. */
+static int
+zcrd1_params(os_ptr op, gs_cie_render * pcrd,
+ ref_cie_render_procs * pcprocs, gs_memory_t * mem)
+{
+ int code;
+ int ignore;
+ gx_color_lookup_table *const prtl = &pcrd->RenderTable.lookup;
+ ref *pRT;
+
+ if ((code = dict_int_param(op, "ColorRenderingType", 1, 1, 0, &ignore)) < 0)
+ return code;
+ if ((code = zcrd1_proc_params(mem, op, pcprocs)) < 0)
+ return code;
+ if ((code = dict_matrix3_param(mem, op, "MatrixLMN", &pcrd->MatrixLMN)) < 0)
+ return code;
+ if ((code = dict_range3_param(mem, op, "RangeLMN", &pcrd->RangeLMN)) < 0)
+ return code;
+ if ((code = dict_matrix3_param(mem, op, "MatrixABC", &pcrd->MatrixABC)) < 0)
+ return code;
+ if ((code = dict_range3_param(mem, op, "RangeABC", &pcrd->RangeABC)) < 0)
+ return code;
+ if ((code = cie_points_param(mem, op, &pcrd->points)) < 0)
+ return code;
+ if ((code = dict_matrix3_param(mem, op, "MatrixPQR", &pcrd->MatrixPQR)) < 0)
+ return code;
+ if ((code = dict_range3_param(mem,op, "RangePQR", &pcrd->RangePQR)) < 0)
+ return code;
+
+ if (dict_find_string(op, "RenderTable", &pRT) > 0) {
+ const ref *prte = pRT->value.const_refs;
+
+ /* Finish unpacking and checking the RenderTable parameter. */
+ check_type_only(prte[4], t_integer);
+ if (!(prte[4].value.intval == 3 || prte[4].value.intval == 4))
+ return_error(gs_error_rangecheck);
+ prtl->n = 3;
+ prtl->m = prte[4].value.intval;
+ if (r_size(pRT) != prtl->m + 5)
+ return_error(gs_error_rangecheck);
+ code = cie_table_param(pRT, prtl, mem);
+ if (code < 0)
+ return code;
+ } else {
+ prtl->table = 0;
+ }
+ pcrd->EncodeLMN = Encode_default;
+ pcrd->EncodeABC = Encode_default;
+ pcrd->TransformPQR = TransformPQR_default;
+ pcrd->RenderTable.T = RenderTableT_default;
+ return 0;
+}
+
+/* ------ Internal procedures ------ */
+
+/* Load the joint caches. */
+static int
+ cie_exec_tpqr(i_ctx_t *),
+ cie_post_exec_tpqr(i_ctx_t *),
+ cie_tpqr_finish(i_ctx_t *);
+int
+cie_cache_joint(i_ctx_t *i_ctx_p, const ref_cie_render_procs * pcrprocs,
+ const gs_cie_common *pcie, gs_state * pgs)
+{
+ const gs_cie_render *pcrd = gs_currentcolorrendering(pgs);
+ gx_cie_joint_caches *pjc = gx_unshare_cie_caches(pgs);
+ gs_ref_memory_t *imem = (gs_ref_memory_t *) gs_state_memory(pgs);
+ ref pqr_procs;
+ uint space;
+ int code;
+ int i;
+
+ if (pcrd == 0) /* cache is not set up yet */
+ return 0;
+ if (pjc == 0) /* must already be allocated */
+ return_error(gs_error_VMerror);
+ if (r_has_type(&pcrprocs->TransformPQR, t_null)) {
+ /*
+ * This CRD came from a driver, not from a PostScript dictionary.
+ * Resample TransformPQR in C code.
+ */
+ return gs_cie_cs_complete(pgs, true);
+ }
+ gs_cie_compute_points_sd(pjc, pcie, pcrd);
+ code = ialloc_ref_array(&pqr_procs, a_readonly, 3 * (1 + 4 + 4 * 6),
+ "cie_cache_common");
+ if (code < 0)
+ return code;
+ /* When we're done, deallocate the procs and complete the caches. */
+ check_estack(3);
+ cie_cache_push_finish(i_ctx_p, cie_tpqr_finish, imem, pgs);
+ *++esp = pqr_procs;
+ space = r_space(&pqr_procs);
+ for (i = 0; i < 3; i++) {
+ ref *p = pqr_procs.value.refs + 3 + (4 + 4 * 6) * i;
+ const float *ppt = (float *)&pjc->points_sd;
+ int j;
+
+ make_array(pqr_procs.value.refs + i, a_readonly | a_executable | space,
+ 4, p);
+ make_array(p, a_readonly | space, 4 * 6, p + 4);
+ p[1] = pcrprocs->TransformPQR.value.refs[i];
+ make_oper(p + 2, 0, cie_exec_tpqr);
+ make_oper(p + 3, 0, cie_post_exec_tpqr);
+ for (j = 0, p += 4; j < 4 * 6; j++, p++, ppt++)
+ make_real(p, *ppt);
+ }
+ return cie_prepare_cache3(i_ctx_p, &pcrd->RangePQR,
+ pqr_procs.value.const_refs,
+ pjc->TransformPQR.caches,
+ pjc, imem, "Transform.PQR");
+}
+
+/* Private operator to shuffle arguments for the TransformPQR procedure: */
+/* v [ws wd bs bd] proc -> -mark- ws wd bs bd v proc + exec */
+static int
+cie_exec_tpqr(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const ref *ppt = op[-1].value.const_refs;
+ uint space = r_space(op - 1);
+ int i;
+
+ check_op(3);
+ push(4);
+ *op = op[-4]; /* proc */
+ op[-1] = op[-6]; /* v */
+ for (i = 0; i < 4; i++)
+ make_const_array(op - 5 + i, a_readonly | space,
+ 6, ppt + i * 6);
+ make_mark(op - 6);
+ return zexec(i_ctx_p);
+}
+
+/* Remove extraneous values from the stack after executing */
+/* the TransformPQR procedure. -mark- ... v -> v */
+static int
+cie_post_exec_tpqr(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint count = ref_stack_counttomark(&o_stack);
+ ref vref;
+
+ if (count < 2)
+ return_error(gs_error_unmatchedmark);
+ vref = *op;
+ ref_stack_pop(&o_stack, count - 1);
+ *osp = vref;
+ return 0;
+}
+
+/* Free the procs array and complete the joint caches. */
+static int
+cie_tpqr_finish(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_state *pgs = r_ptr(op, gs_state);
+ gs_cie_render *pcrd =
+ (gs_cie_render *)gs_currentcolorrendering(pgs); /* break const */
+ int code;
+
+ ifree_ref_array(op - 1, "cie_tpqr_finish");
+ pcrd->TransformPQR = TransformPQR_from_cache;
+ code = gs_cie_cs_complete(pgs, false);
+ pop(2);
+ return code;
+}
+
+/* Ws Bs Wd Bd Ps .transformPQR_scale_wb[012] Pd
+
+ The default TransformPQR procedure is implemented in C, rather than
+ PostScript, as a speed optimization.
+
+ This TransformPQR implements a relative colorimetric intent by scaling
+ the XYZ values relative to the white and black points.
+*/
+static int
+ztpqr_scale_wb_common(i_ctx_t *i_ctx_p, int idx)
+{
+ os_ptr op = osp;
+ double a[4], Ps; /* a[0] = ws, a[1] = bs, a[2] = wd, a[3] = bd */
+ double result;
+ int code;
+ int i;
+
+ code = real_param(op, &Ps);
+ if (code < 0) return code;
+
+ for (i = 0; i < 4; i++) {
+ ref tmp;
+
+ code = array_get(imemory, op - 4 + i, idx, &tmp);
+ if (code >= 0)
+ code = real_param(&tmp, &a[i]);
+ if (code < 0) return code;
+ }
+
+ if (a[0] == a[1])
+ return_error(gs_error_undefinedresult);
+ result = a[3] + (a[2] - a[3]) * (Ps - a[1]) / (a[0] - a[1]);
+ make_real(op - 4, result);
+ pop(4);
+ return 0;
+}
+
+/* Ws Bs Wd Bd Ps .TransformPQR_scale_wb0 Pd */
+static int
+ztpqr_scale_wb0(i_ctx_t *i_ctx_p)
+{
+ return ztpqr_scale_wb_common(i_ctx_p, 3);
+}
+
+/* Ws Bs Wd Bd Ps .TransformPQR_scale_wb2 Pd */
+static int
+ztpqr_scale_wb1(i_ctx_t *i_ctx_p)
+{
+ return ztpqr_scale_wb_common(i_ctx_p, 4);
+}
+
+/* Ws Bs Wd Bd Ps .TransformPQR_scale_wb2 Pd */
+static int
+ztpqr_scale_wb2(i_ctx_t *i_ctx_p)
+{
+ return ztpqr_scale_wb_common(i_ctx_p, 5);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcrd_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"0currentcolorrendering", zcurrentcolorrendering},
+ {"2.setcolorrendering1", zsetcolorrendering1},
+ {"2.setdevicecolorrendering1", zsetdevicecolorrendering1},
+ {"1.buildcolorrendering1", zbuildcolorrendering1},
+ {"1.builddevicecolorrendering1", zbuilddevicecolorrendering1},
+ /* Internal "operators" */
+ {"3%cie_exec_tpqr", cie_exec_tpqr},
+ {"2%cie_post_exec_tpqr", cie_post_exec_tpqr},
+ {"1%cie_tpqr_finish", cie_tpqr_finish},
+ {"5.TransformPQR_scale_WB0", ztpqr_scale_wb0},
+ {"5.TransformPQR_scale_WB1", ztpqr_scale_wb1},
+ {"5.TransformPQR_scale_WB2", ztpqr_scale_wb2},
+ op_def_end(0)
+};
diff --git a/psi/zcsdevn.c b/psi/zcsdevn.c
new file mode 100644
index 000000000..76504bc07
--- /dev/null
+++ b/psi/zcsdevn.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* DeviceN color space support */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gxcspace.h" /* must precede gscolor2.h */
+#include "gscolor2.h"
+#include "gscdevn.h"
+#include "gxcdevn.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "icremap.h"
+#include "ifunc.h"
+#include "igstate.h"
+#include "iname.h"
+#include "zht2.h"
+
+/* Imported from gscdevn.c */
+extern const gs_color_space_type gs_color_space_type_DeviceN;
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcsdevn_op_defs[] =
+{
+ op_def_begin_ll3(),
+#if 0
+ {"1.setdevicenspace", zsetdevicenspace},
+ {"1.attachdevicenattributespace", zattachdevicenattributespace},
+#endif
+ op_def_end(0)
+};
diff --git a/psi/zcsindex.c b/psi/zcsindex.c
new file mode 100644
index 000000000..18de0d29b
--- /dev/null
+++ b/psi/zcsindex.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Indexed color space support */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gscolor.h"
+#include "gsmatrix.h" /* for gxcolor2.h */
+#include "gxcspace.h"
+#include "gxfixed.h" /* ditto */
+#include "gxcolor2.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "icsmap.h"
+#include "igstate.h"
+#include "ivmspace.h"
+#include "store.h"
+
+/* ------ Internal routines ------ */
+
+/* Allocate, and prepare to load, the index or tint map. */
+int
+zcs_begin_map(i_ctx_t *i_ctx_p, gs_indexed_map ** pmap, const ref * pproc,
+ int num_entries, const gs_color_space * base_space,
+ op_proc_t map1)
+{
+ gs_memory_t *mem = gs_state_memory(igs);
+ int space = imemory_space((gs_ref_memory_t *)mem);
+ int num_components = cs_num_components(base_space);
+ int num_values = num_entries * num_components;
+ gs_indexed_map *map;
+ int code = alloc_indexed_map(&map, num_values, mem,
+ "setcolorspace(mapped)");
+ es_ptr ep;
+
+ if (code < 0)
+ return code;
+ *pmap = map;
+ /* Map the entire set of color indices. Since the */
+ /* o-stack may not be able to hold N*4096 values, we have */
+ /* to load them into the cache as they are generated. */
+ check_estack(num_csme + 1); /* 1 extra for map1 proc */
+ ep = esp += num_csme;
+ make_int(ep + csme_num_components, num_components);
+ make_struct(ep + csme_map, space, map);
+ ep[csme_proc] = *pproc;
+ make_int(ep + csme_hival, num_entries - 1);
+ make_int(ep + csme_index, -1);
+ push_op_estack(map1);
+ return o_push_estack;
+}
diff --git a/psi/zcspixel.c b/psi/zcspixel.c
new file mode 100644
index 000000000..30a856494
--- /dev/null
+++ b/psi/zcspixel.c
@@ -0,0 +1,25 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* DevicePixel color space support */
+#include "ghost.h"
+#include "oper.h"
+#include "igstate.h"
+#include "gscspace.h"
+#include "gsmatrix.h" /* for gscolor2.h */
+#include "gscolor2.h"
+#include "gscpixel.h"
+#include "ialloc.h"
diff --git a/psi/zcssepr.c b/psi/zcssepr.c
new file mode 100644
index 000000000..9c3b52b3f
--- /dev/null
+++ b/psi/zcssepr.c
@@ -0,0 +1,111 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Separation color space support */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gscolor.h"
+#include "gsmatrix.h" /* for gxcolor2.h */
+#include "gxcspace.h"
+#include "gxfixed.h" /* ditto */
+#include "gxcolor2.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "icsmap.h"
+#include "ifunc.h"
+#include "igstate.h"
+#include "iname.h"
+#include "ivmspace.h"
+#include "store.h"
+#include "gscsepr.h"
+#include "gscdevn.h"
+#include "gxcdevn.h"
+#include "zht2.h"
+
+/* Imported from gscsepr.c */
+extern const gs_color_space_type gs_color_space_type_Separation;
+/* Imported from gscdevn.c */
+extern const gs_color_space_type gs_color_space_type_DeviceN;
+
+/*
+ * Adobe first created the separation colorspace type and then later created
+ * the DeviceN colorspace. Logically the separation colorspace is the same
+ * as a DeviceN colorspace with a single component, except for the /None and
+ * /All parameter values. We treat the separation colorspace as a DeviceN
+ * colorspace except for the /All case.
+ */
+
+/* - currentoverprint <bool> */
+static int
+zcurrentoverprint(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_bool(op, gs_currentoverprint(igs));
+ return 0;
+}
+
+/* <bool> setoverprint - */
+static int
+zsetoverprint(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_boolean);
+ gs_setoverprint(igs, op->value.boolval);
+ pop(1);
+ return 0;
+}
+
+/* - .currentoverprintmode <int> */
+static int
+zcurrentoverprintmode(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, gs_currentoverprintmode(igs));
+ return 0;
+}
+
+/* <int> .setoverprintmode - */
+static int
+zsetoverprintmode(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int param;
+ int code = int_param(op, max_int, &param);
+
+ if (code < 0 || (code = gs_setoverprintmode(igs, param)) < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zcssepr_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"0currentoverprint", zcurrentoverprint},
+ {"0.currentoverprintmode", zcurrentoverprintmode},
+ {"1setoverprint", zsetoverprint},
+ {"1.setoverprintmode", zsetoverprintmode},
+ op_def_end(0)
+};
diff --git a/psi/zdevcal.c b/psi/zdevcal.c
new file mode 100644
index 000000000..793a99e00
--- /dev/null
+++ b/psi/zdevcal.c
@@ -0,0 +1,70 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* %Calendar% IODevice */
+#include "time_.h"
+#include "ghost.h"
+#include "gxiodev.h"
+#include "istack.h"
+#include "iparam.h"
+
+/* ------ %Calendar% ------ */
+
+static iodev_proc_get_params(calendar_get_params);
+const gx_io_device gs_iodev_calendar = {
+ "%Calendar%", "Special",
+ { iodev_no_init, iodev_no_open_device, iodev_no_open_file,
+ iodev_no_fopen, iodev_no_fclose,
+ iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,
+ iodev_no_enumerate_files, NULL, NULL,
+ calendar_get_params, iodev_no_put_params
+ }
+};
+
+/* Get the date and time. */
+static int
+calendar_get_params(gx_io_device * iodev, gs_param_list * plist)
+{
+ int code;
+ time_t t;
+ struct tm *pltime;
+ struct tm ltime;
+ static const gs_param_item_t items[] = {
+ {"Year", gs_param_type_int, offset_of(struct tm, tm_year)},
+ {"Month", gs_param_type_int, offset_of(struct tm, tm_mon)},
+ {"Day", gs_param_type_int, offset_of(struct tm, tm_mday)},
+ {"Weekday", gs_param_type_int, offset_of(struct tm, tm_wday)},
+ {"Hour", gs_param_type_int, offset_of(struct tm, tm_hour)},
+ {"Minute", gs_param_type_int, offset_of(struct tm, tm_min)},
+ {"Second", gs_param_type_int, offset_of(struct tm, tm_sec)},
+ gs_param_item_end
+ };
+ bool running;
+
+ if (time(&t) == (time_t)-1 || (pltime = localtime(&t)) == 0) {
+ ltime.tm_sec = ltime.tm_min = ltime.tm_hour =
+ ltime.tm_mday = ltime.tm_mon = ltime.tm_year = 0;
+ running = false;
+ } else {
+ ltime = *pltime;
+ ltime.tm_year += 1900;
+ ltime.tm_mon++; /* 1-origin */
+ running = true;
+ }
+ if ((code = gs_param_write_items(plist, &ltime, NULL, items)) < 0)
+ return code;
+ return param_write_bool(plist, "Running", &running);
+}
diff --git a/psi/zdevice.c b/psi/zdevice.c
new file mode 100644
index 000000000..34cc697dd
--- /dev/null
+++ b/psi/zdevice.c
@@ -0,0 +1,644 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Device-related operators */
+#include "string_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "igstate.h"
+#include "imain.h"
+#include "imemory.h"
+#include "iname.h"
+#include "interp.h"
+#include "iparam.h"
+#include "ivmspace.h"
+#include "gsmatrix.h"
+#include "gsstate.h"
+#include "gxdevice.h"
+#include "gxalloc.h"
+#include "gxgetbit.h"
+#include "store.h"
+#include "gsicc_manage.h"
+#include "gxdevsop.h"
+
+/* <device> <keep_open> .copydevice2 <newdevice> */
+static int
+zcopydevice2(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_device *new_dev;
+ int code;
+
+ check_read_type(op[-1], t_device);
+ check_type(*op, t_boolean);
+ code = gs_copydevice2(&new_dev, op[-1].value.pdevice, op->value.boolval,
+ imemory);
+ if (code < 0)
+ return code;
+ new_dev->memory = imemory;
+ make_tav(op - 1, t_device, icurrent_space | a_all, pdevice, new_dev);
+ pop(1);
+ return 0;
+}
+
+/* - currentdevice <device> */
+int
+zcurrentdevice(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_device *dev = gs_currentdevice(igs);
+ gs_ref_memory_t *mem = (gs_ref_memory_t *) dev->memory;
+
+ push(1);
+ make_tav(op, t_device,
+ (mem == 0 ? avm_foreign : imemory_space(mem)) | a_all,
+ pdevice, dev);
+ return 0;
+}
+
+/* <device> .devicename <string> */
+static int
+zdevicename(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const char *dname;
+
+ check_read_type(*op, t_device);
+ dname = op->value.pdevice->dname;
+ make_const_string(op, avm_foreign | a_readonly, strlen(dname),
+ (const byte *)dname);
+ return 0;
+}
+
+/* - .doneshowpage - */
+static int
+zdoneshowpage(i_ctx_t *i_ctx_p)
+{
+ gx_device *dev = gs_currentdevice(igs);
+ gx_device *tdev = (*dev_proc(dev, get_page_device)) (dev);
+
+ if (tdev != 0)
+ tdev->ShowpageCount++;
+ return 0;
+}
+
+/* - flushpage - */
+int
+zflushpage(i_ctx_t *i_ctx_p)
+{
+ return gs_flushpage(igs);
+}
+
+/* <device> <x> <y> <width> <max_height> <alpha?> <std_depth|null> <string> */
+/* .getbitsrect <height> <substring> */
+static int
+zgetbitsrect(i_ctx_t *i_ctx_p)
+{ /*
+ * alpha? is 0 for no alpha, -1 for alpha first, 1 for alpha last.
+ * std_depth is null for native pixels, depth/component for
+ * standard color space.
+ */
+ os_ptr op = osp;
+ gx_device *dev;
+ gs_int_rect rect;
+ gs_get_bits_params_t params;
+ int w, h;
+ gs_get_bits_options_t options =
+ GB_ALIGN_ANY | GB_RETURN_COPY | GB_OFFSET_0 | GB_RASTER_STANDARD |
+ GB_PACKING_CHUNKY;
+ int depth;
+ uint raster;
+ int num_rows;
+ int code;
+
+ check_read_type(op[-7], t_device);
+ dev = op[-7].value.pdevice;
+ check_int_leu(op[-6], dev->width);
+ rect.p.x = op[-6].value.intval;
+ check_int_leu(op[-5], dev->height);
+ rect.p.y = op[-5].value.intval;
+ check_int_leu(op[-4], dev->width);
+ w = op[-4].value.intval;
+ check_int_leu(op[-3], dev->height);
+ h = op[-3].value.intval;
+ check_type(op[-2], t_integer);
+ /*
+ * We use if/else rather than switch because the value is long,
+ * which is not supported as a switch value in pre-ANSI C.
+ */
+ if (op[-2].value.intval == -1)
+ options |= GB_ALPHA_FIRST;
+ else if (op[-2].value.intval == 0)
+ options |= GB_ALPHA_NONE;
+ else if (op[-2].value.intval == 1)
+ options |= GB_ALPHA_LAST;
+ else
+ return_error(gs_error_rangecheck);
+ if (r_has_type(op - 1, t_null)) {
+ options |= GB_COLORS_NATIVE;
+ depth = dev->color_info.depth;
+ } else {
+ static const gs_get_bits_options_t depths[17] = {
+ 0, GB_DEPTH_1, GB_DEPTH_2, 0, GB_DEPTH_4, 0, 0, 0, GB_DEPTH_8,
+ 0, 0, 0, GB_DEPTH_12, 0, 0, 0, GB_DEPTH_16
+ };
+ gs_get_bits_options_t depth_option;
+ int std_depth;
+
+ check_int_leu(op[-1], 16);
+ std_depth = (int)op[-1].value.intval;
+ depth_option = depths[std_depth];
+ if (depth_option == 0)
+ return_error(gs_error_rangecheck);
+ options |= depth_option | GB_COLORS_NATIVE;
+ depth = (dev->color_info.num_components +
+ (options & GB_ALPHA_NONE ? 0 : 1)) * std_depth;
+ }
+ if (w == 0)
+ return_error(gs_error_rangecheck);
+ raster = (w * depth + 7) >> 3;
+ check_write_type(*op, t_string);
+ num_rows = r_size(op) / raster;
+ h = min(h, num_rows);
+ if (h == 0)
+ return_error(gs_error_rangecheck);
+ rect.q.x = rect.p.x + w;
+ rect.q.y = rect.p.y + h;
+ params.options = options;
+ params.data[0] = op->value.bytes;
+ code = (*dev_proc(dev, get_bits_rectangle))(dev, &rect, &params, NULL);
+ if (code < 0)
+ return code;
+ make_int(op - 7, h);
+ op[-6] = *op;
+ r_set_size(op - 6, h * raster);
+ pop(6);
+ return 0;
+}
+
+/* <int> .getdevice <device> */
+static int
+zgetdevice(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const gx_device *dev;
+
+ check_type(*op, t_integer);
+ if (op->value.intval != (int)(op->value.intval))
+ return_error(gs_error_rangecheck); /* won't fit in an int */
+ dev = gs_getdevice((int)(op->value.intval));
+ if (dev == 0) /* index out of range */
+ return_error(gs_error_rangecheck);
+ /* Device prototypes are read-only; */
+ /* the cast is logically unnecessary. */
+ make_tav(op, t_device, avm_foreign | a_readonly, pdevice,
+ (gx_device *) dev);
+ return 0;
+}
+
+/* - .getdefaultdevice <device> */
+static int
+zgetdefaultdevice(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const gx_device *dev;
+
+ dev = gs_getdefaultlibdevice(imemory);
+ if (dev == 0) /* couldn't find a default device */
+ return_error(gs_error_unknownerror);
+ push(1);
+ make_tav(op, t_device, avm_foreign | a_readonly, pdevice,
+ (gx_device *) dev);
+ return 0;
+}
+
+/* Common functionality of zgethardwareparms & zgetdeviceparams */
+static int
+zget_device_params(i_ctx_t *i_ctx_p, bool is_hardware)
+{
+ os_ptr op = osp;
+ ref rkeys;
+ gx_device *dev;
+ stack_param_list list;
+ int code;
+ ref *pmark;
+
+ check_read_type(op[-1], t_device);
+ rkeys = *op;
+ dev = op[-1].value.pdevice;
+ pop(1);
+ stack_param_list_write(&list, &o_stack, &rkeys, iimemory);
+ code = gs_get_device_or_hardware_params(dev, (gs_param_list *) & list,
+ is_hardware);
+ if (code < 0) {
+ /* We have to put back the top argument. */
+ if (list.count > 0)
+ ref_stack_pop(&o_stack, list.count * 2 - 1);
+ else
+ ref_stack_push(&o_stack, 1);
+ *osp = rkeys;
+ return code;
+ }
+ pmark = ref_stack_index(&o_stack, list.count * 2);
+ make_mark(pmark);
+ return 0;
+}
+/* <device> <key_dict|null> .getdeviceparams <mark> <name> <value> ... */
+static int
+zgetdeviceparams(i_ctx_t *i_ctx_p)
+{
+ return zget_device_params(i_ctx_p, false);
+}
+/* <device> <key_dict|null> .gethardwareparams <mark> <name> <value> ... */
+static int
+zgethardwareparams(i_ctx_t *i_ctx_p)
+{
+ return zget_device_params(i_ctx_p, true);
+}
+
+/* <matrix> <width> <height> <palette> <word?> makewordimagedevice <device> */
+static int
+zmakewordimagedevice(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ gs_matrix imat;
+ gx_device *new_dev;
+ const byte *colors;
+ int colors_size;
+ int code;
+
+ check_int_leu(op[-3], max_uint >> 1); /* width */
+ check_int_leu(op[-2], max_uint >> 1); /* height */
+ check_type(*op, t_boolean);
+ if (r_has_type(op1, t_null)) { /* true color */
+ colors = 0;
+ colors_size = -24; /* 24-bit true color */
+ } else if (r_has_type(op1, t_integer)) {
+ /*
+ * We use if/else rather than switch because the value is long,
+ * which is not supported as a switch value in pre-ANSI C.
+ */
+ if (op1->value.intval != 16 && op1->value.intval != 24 &&
+ op1->value.intval != 32
+ )
+ return_error(gs_error_rangecheck);
+ colors = 0;
+ colors_size = -op1->value.intval;
+ } else {
+ check_type(*op1, t_string); /* palette */
+ if (r_size(op1) > 3 * 256)
+ return_error(gs_error_rangecheck);
+ colors = op1->value.bytes;
+ colors_size = r_size(op1);
+ }
+ if ((code = read_matrix(imemory, op - 4, &imat)) < 0)
+ return code;
+ /* Everything OK, create device */
+ code = gs_makewordimagedevice(&new_dev, &imat,
+ (int)op[-3].value.intval,
+ (int)op[-2].value.intval,
+ colors, colors_size,
+ op->value.boolval, true, imemory);
+ if (code == 0) {
+ new_dev->memory = imemory;
+ make_tav(op - 4, t_device, imemory_space(iimemory) | a_all,
+ pdevice, new_dev);
+ pop(4);
+ }
+ return code;
+}
+
+static void invalidate_stack_devices(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osbot;
+ while (op != ostop) {
+ if (r_has_type(op, t_device))
+ op->value.pdevice = 0;
+ op++;
+ }
+}
+
+/* - nulldevice - */
+/* Note that nulldevice clears the current pagedevice. */
+static int
+znulldevice(i_ctx_t *i_ctx_p)
+{
+ gs_nulldevice(igs);
+ invalidate_stack_devices(i_ctx_p);
+ clear_pagedevice(istate);
+ return 0;
+}
+
+extern void print_resource_usage(const gs_main_instance *, gs_dual_memory_t *,
+ const char *);
+
+/* <num_copies> <flush_bool> .outputpage - */
+static int
+zoutputpage(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_type(op[-1], t_integer);
+ check_type(*op, t_boolean);
+ if (gs_debug[':']) {
+ gs_main_instance *minst = get_minst_from_memory((gs_memory_t *)i_ctx_p->memory.current->non_gc_memory);
+
+ print_resource_usage(minst, &(i_ctx_p->memory), "Outputpage start");
+ }
+#ifdef PSI_INCLUDED
+ code = ps_end_page_top(imemory,
+ (int)op[-1].value.intval, op->value.boolval);
+#else
+ code = gs_output_page(igs, (int)op[-1].value.intval,
+ op->value.boolval);
+#endif
+ if (code < 0)
+ return code;
+ pop(2);
+ if (gs_debug[':']) {
+ gs_main_instance *minst = get_minst_from_memory((gs_memory_t *)i_ctx_p->memory.current->non_gc_memory);
+
+ print_resource_usage(minst, &(i_ctx_p->memory), "Outputpage end");
+ }
+ return 0;
+}
+
+/* <device> <policy_dict|null> <require_all> <mark> <name> <value> ... */
+/* .putdeviceparams */
+/* (on success) <device> <eraseflag> */
+/* (on failure) <device> <policy_dict|null> <require_all> <mark> */
+/* <name1> <error1> ... */
+/* For a key that simply was not recognized, if require_all is true, */
+/* the result will be an /undefined error; if require_all is false, */
+/* the key will be ignored. */
+/* Note that .putdeviceparams clears the current pagedevice. */
+static int
+zputdeviceparams(i_ctx_t *i_ctx_p)
+{
+ uint count = ref_stack_counttomark(&o_stack);
+ ref *prequire_all;
+ ref *ppolicy;
+ ref *pdev;
+ gx_device *dev;
+ stack_param_list list;
+ int code;
+ int old_width, old_height;
+ int i, dest;
+
+ if (count == 0)
+ return_error(gs_error_unmatchedmark);
+ prequire_all = ref_stack_index(&o_stack, count);
+ ppolicy = ref_stack_index(&o_stack, count + 1);
+ pdev = ref_stack_index(&o_stack, count + 2);
+ if (pdev == 0)
+ return_error(gs_error_stackunderflow);
+ check_type_only(*prequire_all, t_boolean);
+ check_write_type_only(*pdev, t_device);
+ dev = pdev->value.pdevice;
+ code = stack_param_list_read(&list, &o_stack, 0, ppolicy,
+ prequire_all->value.boolval, iimemory);
+ if (code < 0)
+ return code;
+ old_width = dev->width;
+ old_height = dev->height;
+ code = gs_putdeviceparams(dev, (gs_param_list *) & list);
+ /* Check for names that were undefined or caused errors. */
+ for (dest = count - 2, i = 0; i < count >> 1; i++)
+ if (list.results[i] < 0) {
+ *ref_stack_index(&o_stack, dest) =
+ *ref_stack_index(&o_stack, count - (i << 1) - 2);
+ gs_errorname(i_ctx_p, list.results[i],
+ ref_stack_index(&o_stack, dest - 1));
+ dest -= 2;
+ }
+ iparam_list_release(&list);
+ if (code < 0) { /* There were errors reported. */
+ ref_stack_pop(&o_stack, dest + 1);
+ return 0;
+ }
+ if (code > 0 || (code == 0 && (dev->width != old_width || dev->height != old_height))) {
+ /*
+ * The device was open and is now closed, or its dimensions have
+ * changed. If it was the current device, call setdevice to
+ * reinstall it and erase the page.
+ */
+ /****** DOESN'T FIND ALL THE GSTATES THAT REFERENCE THE DEVICE. ******/
+ if (gs_currentdevice(igs) == dev) {
+ bool was_open = dev->is_open;
+
+ code = gs_setdevice_no_erase(igs, dev);
+ /* If the device wasn't closed, setdevice won't erase the page. */
+ if (was_open && code >= 0)
+ code = 1;
+ }
+ }
+ if (code < 0)
+ return code;
+ ref_stack_pop(&o_stack, count + 1);
+ make_bool(osp, code);
+ clear_pagedevice(istate);
+ return 0;
+}
+
+/* <device> .setdevice <eraseflag> */
+/* Note that .setdevice clears the current pagedevice. */
+int
+zsetdevice(i_ctx_t *i_ctx_p)
+{
+ gx_device *dev = gs_currentdevice(igs);
+ os_ptr op = osp;
+ int code = 0;
+
+ check_write_type(*op, t_device);
+ if (dev->LockSafetyParams) { /* do additional checking if locked */
+ if(op->value.pdevice != dev) /* don't allow a different device */
+ return_error(gs_error_invalidaccess);
+ }
+ dev->ShowpageCount = 0;
+#ifndef PSI_INCLUDED
+ /* the language switching build shouldn't install a new device
+ here. The language switching machinery installs a shared
+ device. */
+
+ if (op->value.pdevice == 0)
+ return gs_note_error(gs_error_undefined);
+
+ code = gs_setdevice_no_erase(igs, op->value.pdevice);
+ if (code < 0)
+ return code;
+
+#endif
+ make_bool(op, code != 0); /* erase page if 1 */
+ invalidate_stack_devices(i_ctx_p);
+ clear_pagedevice(istate);
+ return code;
+}
+
+/* Custom PostScript operator '.special_op' is used to implement
+ * 'dev_spec_op' access from PostScript. Initially this is intended
+ * to be used to recover individual device parameters from certain
+ * devices (pdfwrite, ps2write etc). In the future we may choose to
+ * increase the devices which can support this, and make more types
+ * of 'spec_op' available from the PostScript world.
+ */
+
+/* We use this structure in a table below which allows us to add new
+ * 'spec_op's with minimum fuss.
+ */
+typedef struct spec_op_s spec_op_t;
+struct spec_op_s {
+ char *name; /* C string representing the name of the spec_op */
+ int spec_op; /* Integer used to switch on the name */
+};
+
+/* To ad d more spec_ops, put a key name (used to identify the specific
+ * spec_op required) in this table, the integer is just used in the switch
+ * in the main code to execute the required spec_op code.
+ */
+spec_op_t spec_op_defs[] = {
+ {(char *)"GetDeviceParam", 0}
+};
+
+/* <any> <any> .... /spec_op name .special_op <any> <any> .....
+ * The special_op operator takes at a minimum the name of the spec_op to execute
+ * and as many additional parameters as are required for the spec_op. It may
+ * return as many additional parameters as required.
+ */
+int
+zspec_op(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_device *dev = gs_currentdevice(igs);
+ int i, nprocs = sizeof(spec_op_defs) / sizeof(spec_op_t), code, proc = -1;
+ ref opname, nref, namestr;
+ char *data;
+
+ /* At the very minimum we need a name object telling us which sepc_op to perform */
+ check_op(1);
+ if (!r_has_type(op, t_name))
+ return_error(gs_error_typecheck);
+
+ ref_assign(&opname, op);
+
+ /* Find the relevant spec_op name */
+ for (i=0;i<nprocs;i++) {
+ code = names_ref(imemory->gs_lib_ctx->gs_name_table, (const byte *)spec_op_defs[i].name, strlen(spec_op_defs[i].name), &nref, 0);
+ if (code < 0)
+ return code;
+ if (name_eq(&opname, &nref)) {
+ proc = i;
+ break;
+ }
+ }
+
+ if (proc < 0)
+ return_error(gs_error_undefined);
+
+ pop(1); /* We don't need the name of the spec_op any more */
+ op = osp;
+
+ switch(proc) {
+ case 0:
+ {
+ stack_param_list list;
+ dev_param_req_t request;
+ ref rkeys;
+ /* Get a single device parameter, we should be supplied with
+ * the name of the paramter, as a name object.
+ */
+ check_op(1);
+ if (!r_has_type(op, t_name))
+ return_error(gs_error_typecheck);
+
+ ref_assign(&opname, op);
+ name_string_ref(imemory, &opname, &namestr);
+
+ data = (char *)gs_alloc_bytes(imemory, r_size(&namestr) + 1, "temporary special_op string");
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ memset(data, 0x00, r_size(&namestr) + 1);
+ memcpy(data, namestr.value.bytes, r_size(&namestr));
+
+ /* Discard the parameter name now, we're done with it */
+ pop (1);
+ /* Make a null object so that the stack param list won't check for requests */
+ make_null(&rkeys);
+ stack_param_list_write(&list, &o_stack, &rkeys, iimemory);
+ /* Stuff the data into a structure for passing to the spec_op */
+ request.Param = data;
+ request.list = &list;
+
+ code = dev_proc(dev, dev_spec_op)(dev, gxdso_get_dev_param, &request, sizeof(dev_param_req_t));
+
+ gs_free_object(imemory, data, "temporary special_op string");
+
+ if (code < 0) {
+ if (code == gs_error_undefined) {
+ op = osp;
+ push(1);
+ make_bool(op, 0);
+ } else
+ return_error(code);
+ } else {
+ op = osp;
+ push(1);
+ make_bool(op, 1);
+ }
+ }
+ break;
+ default:
+ /* Belt and braces; it shold not be possible to get here, as the table
+ * containing the names should mirror the entries in this switch. If we
+ * found a name there should be a matching case here.
+ */
+ return_error(gs_error_undefined);
+ break;
+ }
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zdevice_op_defs[] =
+{
+ {"1.copydevice2", zcopydevice2},
+ {"0currentdevice", zcurrentdevice},
+ {"1.devicename", zdevicename},
+ {"0.doneshowpage", zdoneshowpage},
+ {"0flushpage", zflushpage},
+ {"7.getbitsrect", zgetbitsrect},
+ {"1.getdevice", zgetdevice},
+ {"1.getdefaultdevice", zgetdefaultdevice},
+ {"2.getdeviceparams", zgetdeviceparams},
+ {"2.gethardwareparams", zgethardwareparams},
+ {"5makewordimagedevice", zmakewordimagedevice},
+ {"0nulldevice", znulldevice},
+ {"2.outputpage", zoutputpage},
+ {"3.putdeviceparams", zputdeviceparams},
+ {"1.setdevice", zsetdevice},
+ op_def_end(0)
+};
+
+/* We need to split the table because of the 16-element limit. */
+const op_def zdevice_ext_op_defs[] =
+{
+ {"0.special_op", zspec_op},
+ op_def_end(0)
+};
diff --git a/psi/zdevice2.c b/psi/zdevice2.c
new file mode 100644
index 000000000..2a8eeb99f
--- /dev/null
+++ b/psi/zdevice2.c
@@ -0,0 +1,379 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 2 device operators */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "dstack.h" /* for dict_find_name */
+#include "estack.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "iname.h"
+#include "iutil.h"
+#include "store.h"
+#include "gxdevice.h"
+#include "gsstate.h"
+
+/* Exported for zfunc4.c */
+int z2copy(i_ctx_t *);
+
+/* Forward references */
+static int z2copy_gstate(i_ctx_t *);
+static int push_callout(i_ctx_t *, const char *);
+
+/* Extend the `copy' operator to deal with gstates. */
+/* This is done with a hack -- we know that gstates are the only */
+/* t_astruct subtype that implements copy. */
+/* We export this for recognition in FunctionType 4 functions. */
+int
+z2copy(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = zcopy(i_ctx_p);
+
+ if (code >= 0)
+ return code;
+ if (!r_has_type(op, t_astruct))
+ return code;
+ return z2copy_gstate(i_ctx_p);
+}
+
+/* - .currentshowpagecount <count> true */
+/* - .currentshowpagecount false */
+static int
+zcurrentshowpagecount(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_device *dev1, *dev = gs_currentdevice(igs);
+
+ if ((*dev_proc(dev, get_page_device))(dev) == 0) {
+ push(1);
+ make_false(op);
+ } else {
+ dev1 = (*dev_proc(dev, get_page_device))(dev);
+ push(2);
+ make_int(op - 1, dev1->ShowpageCount);
+ make_true(op);
+ }
+ return 0;
+}
+
+/* - .currentpagedevice <dict> <bool> */
+static int
+zcurrentpagedevice(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_device *dev = gs_currentdevice(igs);
+
+ push(2);
+ if ((*dev_proc(dev, get_page_device))(dev) != 0) {
+ op[-1] = istate->pagedevice;
+ make_true(op);
+ } else {
+ make_null(op - 1);
+ make_false(op);
+ }
+ return 0;
+}
+
+/* <local_dict|null> .setpagedevice - */
+static int
+zsetpagedevice(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+/******
+ if ( igs->in_cachedevice )
+ return_error(gs_error_undefined);
+ ******/
+ if (r_has_type(op, t_dictionary)) {
+ check_dict_read(*op);
+#if 0 /****************/
+ /*
+ * In order to avoid invalidaccess errors on setpagedevice,
+ * the dictionary must be allocated in local VM.
+ */
+ if (!(r_is_local(op)))
+ return_error(gs_error_invalidaccess);
+#endif /****************/
+ /* Make the dictionary read-only. */
+ code = zreadonly(i_ctx_p);
+ if (code < 0)
+ return code;
+ } else {
+ check_type(*op, t_null);
+ }
+ istate->pagedevice = *op;
+ pop(1);
+ return 0;
+}
+
+/* Default Install/BeginPage/EndPage procedures */
+/* that just call the procedure in the device. */
+
+/* - .callinstall - */
+static int
+zcallinstall(i_ctx_t *i_ctx_p)
+{
+ gx_device *dev = gs_currentdevice(igs);
+
+ if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
+ int code = (*dev->page_procs.install) (dev, igs);
+
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* <showpage_count> .callbeginpage - */
+static int
+zcallbeginpage(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_device *dev = gs_currentdevice(igs);
+
+ check_type(*op, t_integer);
+ if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
+ int code = (*dev->page_procs.begin_page)(dev, igs);
+
+ if (code < 0)
+ return code;
+ }
+ pop(1);
+ return 0;
+}
+
+/* <showpage_count> <reason_int> .callendpage <flush_bool> */
+static int
+zcallendpage(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_device *dev = gs_currentdevice(igs);
+ int code;
+
+ check_type(op[-1], t_integer);
+ check_type(*op, t_integer);
+ if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
+ code = (*dev->page_procs.end_page)(dev, (int)op->value.intval, igs);
+ if (code < 0)
+ return code;
+ if (code > 1)
+ return_error(gs_error_rangecheck);
+ } else {
+ code = (op->value.intval == 2 ? 0 : 1);
+ }
+ make_bool(op - 1, code);
+ pop(1);
+ return 0;
+}
+
+/* ------ Wrappers for operators that save the graphics state. ------ */
+
+/* When saving the state with the current device a page device, */
+/* we need to make sure that the page device dictionary exists */
+/* so that grestore can use it to reset the device parameters. */
+/* This may have significant performance consequences, but we don't see */
+/* any way around it. */
+
+/* Check whether we need to call out to create the page device dictionary. */
+static bool
+save_page_device(gs_state *pgs)
+{
+ return
+ (r_has_type(&gs_int_gstate(pgs)->pagedevice, t_null) &&
+ (*dev_proc(gs_currentdevice(pgs), get_page_device))(gs_currentdevice(pgs)) != 0);
+}
+
+/* - gsave - */
+static int
+z2gsave(i_ctx_t *i_ctx_p)
+{
+ if (!save_page_device(igs))
+ return gs_gsave(igs);
+ return push_callout(i_ctx_p, "%gsavepagedevice");
+}
+
+/* - save - */
+static int
+z2save(i_ctx_t *i_ctx_p)
+{
+ if (!save_page_device(igs))
+ return zsave(i_ctx_p);
+ return push_callout(i_ctx_p, "%savepagedevice");
+}
+
+/* - gstate <gstate> */
+static int
+z2gstate(i_ctx_t *i_ctx_p)
+{
+ if (!save_page_device(igs))
+ return zgstate(i_ctx_p);
+ return push_callout(i_ctx_p, "%gstatepagedevice");
+}
+
+/* <gstate1> <gstate2> copy <gstate2> */
+static int
+z2copy_gstate(i_ctx_t *i_ctx_p)
+{
+ if (!save_page_device(igs))
+ return zcopy_gstate(i_ctx_p);
+ return push_callout(i_ctx_p, "%copygstatepagedevice");
+}
+
+/* <gstate> currentgstate <gstate> */
+static int
+z2currentgstate(i_ctx_t *i_ctx_p)
+{
+ if (!save_page_device(igs))
+ return zcurrentgstate(i_ctx_p);
+ return push_callout(i_ctx_p, "%currentgstatepagedevice");
+}
+
+/* ------ Wrappers for operators that reset the graphics state. ------ */
+
+/* Check whether we need to call out to restore the page device. */
+static bool
+restore_page_device(const gs_state * pgs_old, const gs_state * pgs_new)
+{
+ gx_device *dev_old = gs_currentdevice(pgs_old);
+ gx_device *dev_new;
+ gx_device *dev_t1;
+ gx_device *dev_t2;
+ bool samepagedevice = obj_eq(dev_old->memory, &gs_int_gstate(pgs_old)->pagedevice,
+ &gs_int_gstate(pgs_new)->pagedevice);
+
+ if ((dev_t1 = (*dev_proc(dev_old, get_page_device)) (dev_old)) == 0)
+ return false;
+ /* If we are going to putdeviceparams in a callout, we need to */
+ /* unlock temporarily. The device will be re-locked as needed */
+ /* by putdeviceparams from the pgs_old->pagedevice dict state. */
+ if (!samepagedevice)
+ dev_old->LockSafetyParams = false;
+ dev_new = gs_currentdevice(pgs_new);
+ if (dev_old != dev_new) {
+ if ((dev_t2 = (*dev_proc(dev_new, get_page_device)) (dev_new)) == 0)
+ return false;
+ if (dev_t1 != dev_t2)
+ return true;
+ }
+ /*
+ * The current implementation of setpagedevice just sets new
+ * parameters in the same device object, so we have to check
+ * whether the page device dictionaries are the same.
+ */
+ return !samepagedevice;
+}
+
+/* - grestore - */
+static int
+z2grestore(i_ctx_t *i_ctx_p)
+{
+ if (!restore_page_device(igs, gs_state_saved(igs)))
+ return gs_grestore(igs);
+ return push_callout(i_ctx_p, "%grestorepagedevice");
+}
+
+/* - grestoreall - */
+static int
+z2grestoreall(i_ctx_t *i_ctx_p)
+{
+ for (;;) {
+ if (!restore_page_device(igs, gs_state_saved(igs))) {
+ bool done = !gs_state_saved(gs_state_saved(igs));
+
+ gs_grestore(igs);
+ if (done)
+ break;
+ } else
+ return push_callout(i_ctx_p, "%grestoreallpagedevice");
+ }
+ return 0;
+}
+
+/* <save> restore - */
+static int
+z2restore(i_ctx_t *i_ctx_p)
+{
+ while (gs_state_saved(gs_state_saved(igs))) {
+ if (restore_page_device(igs, gs_state_saved(igs)))
+ return push_callout(i_ctx_p, "%restore1pagedevice");
+ gs_grestore(igs);
+ }
+ if (restore_page_device(igs, gs_state_saved(igs)))
+ return push_callout(i_ctx_p, "%restorepagedevice");
+ return zrestore(i_ctx_p);
+}
+
+/* <gstate> setgstate - */
+static int
+z2setgstate(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_stype(*op, st_igstate_obj);
+ if (!restore_page_device(igs, igstate_ptr(op)))
+ return zsetgstate(i_ctx_p);
+ return push_callout(i_ctx_p, "%setgstatepagedevice");
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zdevice2_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"0.currentshowpagecount", zcurrentshowpagecount},
+ {"0.currentpagedevice", zcurrentpagedevice},
+ {"1.setpagedevice", zsetpagedevice},
+ /* Note that the following replace prior definitions */
+ /* in the indicated files: */
+ {"1copy", z2copy}, /* zdps1.c */
+ {"0gsave", z2gsave}, /* zgstate.c */
+ {"0save", z2save}, /* zvmem.c */
+ {"0gstate", z2gstate}, /* zdps1.c */
+ {"1currentgstate", z2currentgstate}, /* zdps1.c */
+ {"0grestore", z2grestore}, /* zgstate.c */
+ {"0grestoreall", z2grestoreall}, /* zgstate.c */
+ {"1restore", z2restore}, /* zvmem.c */
+ {"1setgstate", z2setgstate}, /* zdps1.c */
+ /* Default Install/BeginPage/EndPage procedures */
+ /* that just call the procedure in the device. */
+ {"0.callinstall", zcallinstall},
+ {"1.callbeginpage", zcallbeginpage},
+ {"2.callendpage", zcallendpage},
+ op_def_end(0)
+};
+
+/* ------ Internal routines ------ */
+
+/* Call out to a PostScript procedure. */
+static int
+push_callout(i_ctx_t *i_ctx_p, const char *callout_name)
+{
+ int code;
+
+ check_estack(1);
+ code = name_enter_string(imemory, callout_name, esp + 1);
+ if (code < 0)
+ return code;
+ ++esp;
+ r_set_attrs(esp, a_executable);
+ return o_push_estack;
+}
diff --git a/psi/zdfilter.c b/psi/zdfilter.c
new file mode 100644
index 000000000..2d231694a
--- /dev/null
+++ b/psi/zdfilter.c
@@ -0,0 +1,50 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* PostScript operators for managing the device filter stack */
+
+/* We probably don't need all of these, they were copied from zdevice.c. */
+#include "string_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "igstate.h"
+#include "iname.h"
+#include "interp.h"
+#include "iparam.h"
+#include "ivmspace.h"
+#include "gsmatrix.h"
+#include "gsstate.h"
+#include "gxdevice.h"
+#include "store.h"
+#include "gsdfilt.h"
+
+/* pushpdf14devicefilter is defined in ztrans.c */
+
+/* - .popdevicefilter - */
+static int
+zpopdevicefilter(i_ctx_t *i_ctx_p)
+{
+ gs_memory_t *mem = gs_memory_stable(imemory);
+
+ return gs_pop_device_filter(mem, igs);
+}
+
+const op_def zdfilter_op_defs[] =
+{
+ {"0.popdevicefilter", zpopdevicefilter},
+ op_def_end(0)
+};
diff --git a/psi/zdict.c b/psi/zdict.c
new file mode 100644
index 000000000..4ffa49509
--- /dev/null
+++ b/psi/zdict.c
@@ -0,0 +1,559 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Dictionary operators */
+#include "ghost.h"
+#include "oper.h"
+#include "iddict.h"
+#include "dstack.h"
+#include "ilevel.h" /* for [count]dictstack */
+#include "iname.h" /* for dict_find_name */
+#include "ipacked.h" /* for inline dict lookup */
+#include "ivmspace.h"
+#include "store.h"
+#include "iscan.h" /* for SCAN_PDF_RULES */
+
+/* <int> dict <dict> */
+int
+zdict(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ if (op->value.intval < 0)
+ return_error(gs_error_rangecheck);
+ return dict_create((uint) op->value.intval, op);
+}
+
+/* <dict> maxlength <int> */
+static int
+zmaxlength(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ make_int(op, dict_maxlength(op));
+ return 0;
+}
+
+/* <dict> begin - */
+int
+zbegin(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if ( dsp == dstop ) {
+ int code = ref_stack_extend(&d_stack, 1);
+
+ if ( code < 0 ) {
+ if (code == gs_error_dictstackoverflow) {
+ /* Adobe doesn't restore the operand that caused stack */
+ /* overflow. We do the same to match CET 20-02-02 */
+ pop(1);
+ }
+ return code;
+ }
+ }
+ ++dsp;
+ ref_assign(dsp, op);
+ dict_set_top();
+ pop(1);
+ return 0;
+}
+
+/* - end - */
+int
+zend(i_ctx_t *i_ctx_p)
+{
+ if (ref_stack_count_inline(&d_stack) == min_dstack_size) {
+ /* We would underflow the d-stack. */
+ return_error(gs_error_dictstackunderflow);
+ }
+ while (dsp == dsbot) {
+ /* We would underflow the current block. */
+ ref_stack_pop_block(&d_stack);
+ }
+ dsp--;
+ dict_set_top();
+ return 0;
+}
+
+/* <key> <value> def - */
+/*
+ * We make this into a separate procedure because
+ * the interpreter will almost always call it directly.
+ */
+int
+zop_def(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ ref *pvslot;
+
+ /* The following combines a check_op(2) with a type check. */
+ switch (r_type(op1)) {
+ case t_name: {
+ /* We can use the fast single-probe lookup here. */
+ uint nidx = name_index(imemory, op1);
+ uint htemp;
+
+ if_dict_find_name_by_index_top(nidx, htemp, pvslot) {
+ if (dtop_can_store(op))
+ goto ra;
+ }
+ break; /* handle all slower cases */
+ }
+ case t_null:
+ return_error(gs_error_typecheck);
+ case t__invalid:
+ return_error(gs_error_stackunderflow);
+ }
+ /*
+ * Combine the check for a writable top dictionary with
+ * the global/local store check. See dstack.h for details.
+ */
+ if (!dtop_can_store(op)) {
+ check_dict_write(*dsp);
+ /*
+ * If the dictionary is writable, the problem must be
+ * an invalid store.
+ */
+ return_error(gs_error_invalidaccess);
+ }
+ /*
+ * Save a level of procedure call in the common (redefinition)
+ * case. With the current interfaces, we pay a double lookup
+ * in the uncommon case.
+ */
+ if (dict_find(dsp, op1, &pvslot) <= 0)
+ return idict_put(dsp, op1, op);
+ra:
+ if ((pvslot->tas.type_attrs & (&i_ctx_p->memory)->test_mask) == 0)
+ alloc_save_change(idmemory, &dsp->value.pdict->values, (ref_packed *)pvslot, "dict_put(value)");
+ ref_assign_new_inline(pvslot,op);
+
+ return 0;
+}
+int
+zdef(i_ctx_t *i_ctx_p)
+{
+ int code = zop_def(i_ctx_p);
+
+ if (code >= 0) {
+ pop(2);
+ }
+ return code;
+}
+
+/* <key> load <value> */
+static int
+zload(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *pvalue;
+
+ switch (r_type(op)) {
+ case t_name:
+ /* Use the fast lookup. */
+ if ((pvalue = dict_find_name(op)) == 0)
+ return_error(gs_error_undefined);
+ ref_assign(op, pvalue);
+ return 0;
+ case t_null:
+ return_error(gs_error_typecheck);
+ case t__invalid:
+ return_error(gs_error_stackunderflow);
+ default: {
+ /* Use an explicit loop. */
+ uint size = ref_stack_count(&d_stack);
+ uint i;
+
+ for (i = 0; i < size; i++) {
+ ref *dp = ref_stack_index(&d_stack, i);
+
+ check_dict_read(*dp);
+ if (dict_find(dp, op, &pvalue) > 0) {
+ ref_assign(op, pvalue);
+ return 0;
+ }
+ }
+ return_error(gs_error_undefined);
+ }
+ }
+}
+
+/* get - implemented in zgeneric.c */
+
+/* put - implemented in zgeneric.c */
+
+/* <dict> <key> .undef - */
+/* <dict> <key> undef - */
+static int
+zundef(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ int code;
+
+ check_type(*op1, t_dictionary);
+ if (i_ctx_p->in_superexec == 0)
+ check_dict_write(*op1);
+ code = idict_undef(op1, op);
+ if (code < 0 && code != gs_error_undefined) /* ignore undefined error */
+ return code;
+ pop(2);
+ return 0;
+}
+
+/* <dict> <key> known <bool> */
+static int
+zknown(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ register os_ptr op1 = op - 1;
+ ref *pvalue;
+ int code;
+
+ check_type(*op1, t_dictionary);
+ check_dict_read(*op1);
+ code = dict_find(op1, op, &pvalue);
+ switch (code) {
+ case gs_error_dictfull:
+ code = 0;
+ case 0: case 1:
+ break;
+ default:
+ return code;
+ }
+ make_bool(op1, code);
+ pop(1);
+ return 0;
+}
+
+/* <key> where <dict> true */
+/* <key> where false */
+int
+zwhere(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref_stack_enum_t rsenum;
+
+ check_op(1);
+ ref_stack_enum_begin(&rsenum, &d_stack);
+ do {
+ const ref *const bot = rsenum.ptr;
+ const ref *pdref = bot + rsenum.size;
+ ref *pvalue;
+ int code;
+
+ while (pdref-- > bot) {
+ check_dict_read(*pdref);
+ code = dict_find(pdref, op, &pvalue);
+ if (code < 0 && code != gs_error_dictfull)
+ return code;
+ if (code > 0) {
+ push(1);
+ ref_assign(op - 1, pdref);
+ make_true(op);
+ return 0;
+ }
+ }
+ } while (ref_stack_enum_next(&rsenum));
+ make_false(op);
+ return 0;
+}
+
+/* copy for dictionaries -- called from zcopy in zgeneric.c. */
+/* Only the type of *op has been checked. */
+int
+zcopy_dict(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ int code;
+
+ check_type(*op1, t_dictionary);
+ check_dict_read(*op1);
+ check_dict_write(*op);
+ if (!imemory->gs_lib_ctx->dict_auto_expand &&
+ (dict_length(op) != 0 || dict_maxlength(op) < dict_length(op1))
+ )
+ return_error(gs_error_rangecheck);
+ code = idict_copy(op1, op);
+ if (code < 0)
+ return code;
+ /*
+ * In Level 1 systems, we must copy the access attributes too.
+ * The only possible effect this can have is to make the
+ * copy read-only if the original dictionary is read-only.
+ */
+ if (!level2_enabled)
+ r_copy_attrs(dict_access_ref(op), a_write, dict_access_ref(op1));
+ ref_assign(op1, op);
+ pop(1);
+ return 0;
+}
+
+/* - currentdict <dict> */
+static int
+zcurrentdict(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ ref_assign(op, dsp);
+ return 0;
+}
+
+/* - countdictstack <int> */
+static int
+zcountdictstack(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint count = ref_stack_count(&d_stack);
+
+ push(1);
+ if (!level2_enabled)
+ count--; /* see dstack.h */
+ make_int(op, count);
+ return 0;
+}
+
+/* <array> dictstack <subarray> */
+static int
+zdictstack(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint count = ref_stack_count(&d_stack);
+
+ if (!level2_enabled)
+ count--; /* see dstack.h */
+ if (!r_is_array(op))
+ return_op_typecheck(op);
+ if (r_size(op) < count)
+ return_error(gs_error_rangecheck);
+ if (!r_has_type_attrs(op, t_array, a_write))
+ return_error(gs_error_invalidaccess);
+ return ref_stack_store(&d_stack, op, count, 0, 0, true, idmemory,
+ "dictstack");
+}
+
+/* - cleardictstack - */
+static int
+zcleardictstack(i_ctx_t *i_ctx_p)
+{
+ while (zend(i_ctx_p) >= 0)
+ DO_NOTHING;
+ return 0;
+}
+
+/* ------ Extensions ------ */
+
+/* -mark- <key0> <value0> <key1> <value1> ... .dicttomark <dict> */
+/* This is the Level 2 >> operator. */
+static int
+zdicttomark(i_ctx_t *i_ctx_p)
+{
+ uint count2 = ref_stack_counttomark(&o_stack);
+ ref rdict;
+ int code;
+ uint idx;
+
+ if (count2 == 0)
+ return_error(gs_error_unmatchedmark);
+ count2--;
+ if ((count2 & 1) != 0)
+ return_error(gs_error_rangecheck);
+ code = dict_create(count2 >> 1, &rdict);
+ if (code < 0)
+ return code;
+ if ((i_ctx_p->scanner_options & SCAN_PDF_RULES) != 0) {
+ for (idx = count2; idx > 0; idx -= 2) {
+ code = idict_put(&rdict,
+ ref_stack_index(&o_stack, idx - 1),
+ ref_stack_index(&o_stack, idx - 2));
+ if (code < 0) { /* There's no way to free the dictionary -- too bad. */
+ return code;
+ }
+ }
+ }
+ else {
+ /* << /a 1 /a 2 >> => << /a 1 >>, i.e., */
+ /* we must enter the keys in top-to-bottom order. */
+ for (idx = 0; idx < count2; idx += 2) {
+ code = idict_put(&rdict,
+ ref_stack_index(&o_stack, idx + 1),
+ ref_stack_index(&o_stack, idx));
+ if (code < 0) { /* There's no way to free the dictionary -- too bad. */
+ return code;
+ }
+ }
+ }
+ ref_stack_pop(&o_stack, count2);
+ ref_assign(osp, &rdict);
+ return code;
+}
+
+/* <dict1> <dict2> .forcecopynew <dict2> */
+/*
+ * This operator is a special-purpose accelerator for use by 'restore' (see
+ * gs_dps1.ps). Note that this operator does *not* require that dict2 be
+ * writable. Hence it is in the same category of "dangerous" operators as
+ * .forceput and .forceundef.
+ */
+static int
+zforcecopynew(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ int code;
+
+ check_type(*op1, t_dictionary);
+ check_dict_read(*op1);
+ check_type(*op, t_dictionary);
+ /*check_dict_write(*op);*/ /* see above */
+ /* This is only recognized in Level 2 mode. */
+ if (!imemory->gs_lib_ctx->dict_auto_expand)
+ return_error(gs_error_undefined);
+ code = idict_copy_new(op1, op);
+ if (code < 0)
+ return code;
+ ref_assign(op1, op);
+ pop(1);
+ return 0;
+}
+
+/* <dict> <key> .forceundef - */
+/*
+ * This forces an "undef" even if the dictionary is not writable.
+ * Like .forceput, it is meant to be used only in a few special situations,
+ * and should not be accessible by name after initialization.
+ */
+static int
+zforceundef(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(op[-1], t_dictionary);
+ /* Don't check_dict_write */
+ idict_undef(op - 1, op); /* ignore undefined error */
+ pop(2);
+ return 0;
+}
+
+/* <dict> <key> .knownget <value> true */
+/* <dict> <key> .knownget false */
+static int
+zknownget(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ register os_ptr op1 = op - 1;
+ ref *pvalue;
+
+ check_type(*op1, t_dictionary);
+ check_dict_read(*op1);
+ if (dict_find(op1, op, &pvalue) <= 0) {
+ make_false(op1);
+ pop(1);
+ } else {
+ ref_assign(op1, pvalue);
+ make_true(op);
+ }
+ return 0;
+}
+
+/* <dict> <key> .knownundef <bool> */
+static int
+zknownundef(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ int code;
+
+ check_type(*op1, t_dictionary);
+ check_dict_write(*op1);
+ code = idict_undef(op1, op);
+ make_bool(op1, code == 0);
+ pop(1);
+ return 0;
+}
+
+/* <dict> <int> .setmaxlength - */
+static int
+zsetmaxlength(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ uint new_size;
+ int code;
+
+ check_type(*op1, t_dictionary);
+ if (i_ctx_p->in_superexec == 0)
+ check_dict_write(*op1);
+ check_type(*op, t_integer);
+ if (op->value.intval < 0)
+ return_error(gs_error_rangecheck);
+ new_size = (uint) op->value.intval;
+ if (dict_length(op - 1) > new_size)
+ return_error(gs_error_dictfull);
+ code = idict_resize(op - 1, new_size);
+ if (code >= 0)
+ pop(2);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+/* We need to split the table because of the 16-element limit. */
+const op_def zdict1_op_defs[] = {
+ {"0cleardictstack", zcleardictstack},
+ {"1begin", zbegin},
+ {"0countdictstack", zcountdictstack},
+ {"0currentdict", zcurrentdict},
+ {"2def", zdef},
+ {"1dict", zdict},
+ {"0dictstack", zdictstack},
+ {"0end", zend},
+ {"2known", zknown},
+ {"1load", zload},
+ {"1maxlength", zmaxlength},
+ {"2.undef", zundef}, /* we need this even in Level 1 */
+ {"1where", zwhere},
+ op_def_end(0)
+};
+const op_def zdict2_op_defs[] = {
+ /* Extensions */
+ {"1.dicttomark", zdicttomark},
+ {"2.forcecopynew", zforcecopynew},
+ {"2.forceundef", zforceundef},
+ {"2.knownget", zknownget},
+ {"1.knownundef", zknownundef},
+ {"2.setmaxlength", zsetmaxlength},
+ /*
+ * In Level 2, >> is a synonym for .dicttomark, and undef for
+ * .undef. By giving the former their own entries, they will not be
+ * "eq" to .dicttomark and .undef, but that doesn't matter, since
+ * we're doing this only for the sake of Adobe- compatible error
+ * stacks.
+ */
+ op_def_begin_level2(),
+ {"1>>", zdicttomark},
+ {"2undef", zundef},
+ op_def_end(0)
+};
diff --git a/psi/zdosio.c b/psi/zdosio.c
new file mode 100644
index 000000000..7f2878f9a
--- /dev/null
+++ b/psi/zdosio.c
@@ -0,0 +1,107 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* MS-DOS direct I/O operators. */
+/* These should NEVER be included in a released configuration! */
+#include "dos_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "store.h"
+
+/* <port> .inport <word> */
+static int
+zinport(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ make_int(op, inport((int)op->value.intval));
+ return 0;
+}
+
+/* <port> .inportb <byte> */
+static int
+zinportb(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ make_int(op, inportb((int)op->value.intval));
+ return 0;
+}
+
+/* <port> <word> .outport - */
+static int
+zoutport(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ check_type(op[-1], t_integer);
+ outport((int)op[-1].value.intval, (int)op->value.intval);
+ pop(1);
+ return 0;
+}
+
+/* <port> <byte> .outportb - */
+static int
+zoutportb(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ check_int_leu(op[-1], 0xff);
+ outportb((int)op[-1].value.intval, (byte) op->value.intval);
+ pop(1);
+ return 0;
+}
+
+/* <loc> .peek <byte> */
+static int
+zpeek(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ make_int(op, *(byte *) (op->value.intval));
+ return 0;
+}
+
+/* <loc> <byte> .poke - */
+static int
+zpoke(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ check_int_leu(op[-1], 0xff);
+ *(byte *) (op[-1].value.intval) = (byte) op->value.intval;
+ pop(1);
+ return 0;
+}
+
+/* ------ Operator initialization ------ */
+
+const op_def zdosio_op_defs[] =
+{
+ {"1.inport", zinport},
+ {"1.inportb", zinportb},
+ {"2.outport", zoutport},
+ {"2.outportb", zoutportb},
+ {"1.peek", zpeek},
+ {"2.poke", zpoke},
+ op_def_end(0)
+};
diff --git a/psi/zdouble.c b/psi/zdouble.c
new file mode 100644
index 000000000..20736fa5e
--- /dev/null
+++ b/psi/zdouble.c
@@ -0,0 +1,551 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Double-precision floating point arithmetic operators */
+#include "math_.h"
+#include "memory_.h"
+#include "string_.h"
+#include "ctype_.h"
+#include "ghost.h"
+#include "gxfarith.h"
+#include "oper.h"
+#include "store.h"
+
+/*
+ * Thanks to Jean-Pierre Demailly of the Institut Fourier of the
+ * Universit\'e de Grenoble I <demailly@fourier.grenet.fr> for proposing
+ * this package and for arranging the funding for its creation.
+ *
+ * These operators work with doubles represented as 8-byte strings. When
+ * applicable, they write their result into a string supplied as an argument.
+ * They also accept ints and reals as arguments.
+ */
+
+/* Forward references */
+static int double_params_result(os_ptr, int, double *);
+static int double_params(os_ptr, int, double *);
+static int double_result(i_ctx_t *, int, double);
+static int double_unary(i_ctx_t *, double (*)(double));
+
+#define dbegin_unary()\
+ os_ptr op = osp;\
+ double num;\
+ int code = double_params_result(op, 1, &num);\
+\
+ if ( code < 0 )\
+ return code
+
+#define dbegin_binary()\
+ os_ptr op = osp;\
+ double num[2];\
+ int code = double_params_result(op, 2, num);\
+\
+ if ( code < 0 )\
+ return code
+
+/* ------ Arithmetic ------ */
+
+/* <dnum1> <dnum2> <dresult> .dadd <dresult> */
+static int
+zdadd(i_ctx_t *i_ctx_p)
+{
+ dbegin_binary();
+ return double_result(i_ctx_p, 2, num[0] + num[1]);
+}
+
+/* <dnum1> <dnum2> <dresult> .ddiv <dresult> */
+static int
+zddiv(i_ctx_t *i_ctx_p)
+{
+ dbegin_binary();
+ if (num[1] == 0.0)
+ return_error(gs_error_undefinedresult);
+ return double_result(i_ctx_p, 2, num[0] / num[1]);
+}
+
+/* <dnum1> <dnum2> <dresult> .dmul <dresult> */
+static int
+zdmul(i_ctx_t *i_ctx_p)
+{
+ dbegin_binary();
+ return double_result(i_ctx_p, 2, num[0] * num[1]);
+}
+
+/* <dnum1> <dnum2> <dresult> .dsub <dresult> */
+static int
+zdsub(i_ctx_t *i_ctx_p)
+{
+ dbegin_binary();
+ return double_result(i_ctx_p, 2, num[0] - num[1]);
+}
+
+/* ------ Simple functions ------ */
+
+/* <dnum> <dresult> .dabs <dresult> */
+static int
+zdabs(i_ctx_t *i_ctx_p)
+{
+ return double_unary(i_ctx_p, fabs);
+}
+
+/* <dnum> <dresult> .dceiling <dresult> */
+static int
+zdceiling(i_ctx_t *i_ctx_p)
+{
+ return double_unary(i_ctx_p, ceil);
+}
+
+/* <dnum> <dresult> .dfloor <dresult> */
+static int
+zdfloor(i_ctx_t *i_ctx_p)
+{
+ return double_unary(i_ctx_p, floor);
+}
+
+/* <dnum> <dresult> .dneg <dresult> */
+static int
+zdneg(i_ctx_t *i_ctx_p)
+{
+ dbegin_unary();
+ return double_result(i_ctx_p, 1, -num);
+}
+
+/* <dnum> <dresult> .dround <dresult> */
+static int
+zdround(i_ctx_t *i_ctx_p)
+{
+ dbegin_unary();
+ return double_result(i_ctx_p, 1, floor(num + 0.5));
+}
+
+/* <dnum> <dresult> .dsqrt <dresult> */
+static int
+zdsqrt(i_ctx_t *i_ctx_p)
+{
+ dbegin_unary();
+ if (num < 0.0)
+ return_error(gs_error_rangecheck);
+ return double_result(i_ctx_p, 1, sqrt(num));
+}
+
+/* <dnum> <dresult> .dtruncate <dresult> */
+static int
+zdtruncate(i_ctx_t *i_ctx_p)
+{
+ dbegin_unary();
+ return double_result(i_ctx_p, 1, (num < 0 ? ceil(num) : floor(num)));
+}
+
+/* ------ Transcendental functions ------ */
+
+static int
+darc(i_ctx_t *i_ctx_p, double (*afunc)(double))
+{
+ dbegin_unary();
+ return double_result(i_ctx_p, 1, (*afunc)(num) * radians_to_degrees);
+}
+/* <dnum> <dresult> .darccos <dresult> */
+static int
+zdarccos(i_ctx_t *i_ctx_p)
+{
+ return darc(i_ctx_p, acos);
+}
+/* <dnum> <dresult> .darcsin <dresult> */
+static int
+zdarcsin(i_ctx_t *i_ctx_p)
+{
+ return darc(i_ctx_p, asin);
+}
+
+/* <dnum> <ddenom> <dresult> .datan <dresult> */
+static int
+zdatan(i_ctx_t *i_ctx_p)
+{
+ double result;
+
+ dbegin_binary();
+ if (num[0] == 0) { /* on X-axis, special case */
+ if (num[1] == 0)
+ return_error(gs_error_undefinedresult);
+ result = (num[1] < 0 ? 180 : 0);
+ } else {
+ result = atan2(num[0], num[1]) * radians_to_degrees;
+ if (result < 0)
+ result += 360;
+ }
+ return double_result(i_ctx_p, 2, result);
+}
+
+/* <dnum> <dresult> .dcos <dresult> */
+static int
+zdcos(i_ctx_t *i_ctx_p)
+{
+ return double_unary(i_ctx_p, gs_cos_degrees);
+}
+
+/* <dbase> <dexponent> <dresult> .dexp <dresult> */
+static int
+zdexp(i_ctx_t *i_ctx_p)
+{
+ double ipart;
+
+ dbegin_binary();
+ if (num[0] == 0.0 && num[1] == 0.0)
+ return_error(gs_error_undefinedresult);
+ if (num[0] < 0.0 && modf(num[1], &ipart) != 0.0)
+ return_error(gs_error_undefinedresult);
+ return double_result(i_ctx_p, 2, pow(num[0], num[1]));
+}
+
+static int
+dlog(i_ctx_t *i_ctx_p, double (*lfunc)(double))
+{
+ dbegin_unary();
+ if (num <= 0.0)
+ return_error(gs_error_rangecheck);
+ return double_result(i_ctx_p, 1, (*lfunc)(num));
+}
+/* <dposnum> <dresult> .dln <dresult> */
+static int
+zdln(i_ctx_t *i_ctx_p)
+{
+ return dlog(i_ctx_p, log);
+}
+/* <dposnum> <dresult> .dlog <dresult> */
+static int
+zdlog(i_ctx_t *i_ctx_p)
+{
+ return dlog(i_ctx_p, log10);
+}
+
+/* <dnum> <dresult> .dsin <dresult> */
+static int
+zdsin(i_ctx_t *i_ctx_p)
+{
+ return double_unary(i_ctx_p, gs_sin_degrees);
+}
+
+/* ------ Comparison ------ */
+
+static int
+dcompare(i_ctx_t *i_ctx_p, int mask)
+{
+ os_ptr op = osp;
+ double num[2];
+ int code = double_params(op, 2, num);
+
+ if (code < 0)
+ return code;
+ make_bool(op - 1,
+ (mask & (num[0] < num[1] ? 1 : num[0] > num[1] ? 4 : 2))
+ != 0);
+ pop(1);
+ return 0;
+}
+/* <dnum1> <dnum2> .deq <bool> */
+static int
+zdeq(i_ctx_t *i_ctx_p)
+{
+ return dcompare(i_ctx_p, 2);
+}
+/* <dnum1> <dnum2> .dge <bool> */
+static int
+zdge(i_ctx_t *i_ctx_p)
+{
+ return dcompare(i_ctx_p, 6);
+}
+/* <dnum1> <dnum2> .dgt <bool> */
+static int
+zdgt(i_ctx_t *i_ctx_p)
+{
+ return dcompare(i_ctx_p, 4);
+}
+/* <dnum1> <dnum2> .dle <bool> */
+static int
+zdle(i_ctx_t *i_ctx_p)
+{
+ return dcompare(i_ctx_p, 3);
+}
+/* <dnum1> <dnum2> .dlt <bool> */
+static int
+zdlt(i_ctx_t *i_ctx_p)
+{
+ return dcompare(i_ctx_p, 1);
+}
+/* <dnum1> <dnum2> .dne <bool> */
+static int
+zdne(i_ctx_t *i_ctx_p)
+{
+ return dcompare(i_ctx_p, 5);
+}
+
+/* ------ Conversion ------ */
+
+/* Take the easy way out.... */
+#define MAX_CHARS 50
+
+/* <dnum> <dresult> .cvd <dresult> */
+static int
+zcvd(i_ctx_t *i_ctx_p)
+{
+ dbegin_unary();
+ return double_result(i_ctx_p, 1, num);
+}
+
+/* <string> <dresult> .cvsd <dresult> */
+static int
+zcvsd(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = double_params_result(op, 0, NULL);
+ double num;
+ char dot, buf[MAX_CHARS + 2];
+ char *str = buf;
+ uint len;
+ char end;
+
+ if (code < 0)
+ return code;
+ check_read_type(op[-1], t_string);
+ len = r_size(op - 1);
+ if (len > MAX_CHARS)
+ return_error(gs_error_limitcheck);
+ gs_sprintf(buf, "%f", 1.5);
+ dot = buf[1]; /* locale-dependent */
+ memcpy(str, op[-1].value.bytes, len);
+ /*
+ * We check syntax in the following way: we remove whitespace,
+ * verify that the string contains only [0123456789+-.dDeE],
+ * then append a $ and then check that the next character after
+ * the scanned number is a $.
+ */
+ while (len > 0 && isspace(*str))
+ ++str, --len;
+ while (len > 0 && isspace(str[len - 1]))
+ --len;
+ str[len] = 0;
+ if (strspn(str, "0123456789+-.dDeE") != len)
+ return_error(gs_error_syntaxerror);
+ strcat(str, "$");
+ if (dot != '.') {
+ char *pdot = strchr(str, '.');
+ if (pdot)
+ *pdot = dot;
+ }
+ if (sscanf(str, "%lf%c", &num, &end) != 2 || end != '$')
+ return_error(gs_error_syntaxerror);
+ return double_result(i_ctx_p, 1, num);
+}
+
+/* <dnum> .dcvi <int> */
+static int
+zdcvi(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+#define alt_min_long (-1L << (arch_sizeof_long * 8 - 1))
+#define alt_max_long (~(alt_min_long))
+ static const double min_int_real = (alt_min_long * 1.0 - 1);
+ static const double max_int_real = (alt_max_long * 1.0 + 1);
+ double num;
+ int code = double_params(op, 1, &num);
+
+ if (code < 0)
+ return code;
+
+ if (num < min_int_real || num > max_int_real)
+ return_error(gs_error_rangecheck);
+ make_int(op, (long)num); /* truncates toward 0 */
+ return 0;
+}
+
+/* <dnum> .dcvr <real> */
+static int
+zdcvr(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+#define b30 (0x40000000L * 1.0)
+#define max_mag (0xffffff * b30 * b30 * b30 * 0x4000)
+ static const float min_real = -max_mag;
+ static const float max_real = max_mag;
+#undef b30
+#undef max_mag
+ double num;
+ int code = double_params(op, 1, &num);
+
+ if (code < 0)
+ return code;
+ if (num < min_real || num > max_real)
+ return_error(gs_error_rangecheck);
+ make_real(op, (float)num);
+ return 0;
+}
+
+/* <dnum> <string> .dcvs <substring> */
+static int
+zdcvs(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double num;
+ int code = double_params(op - 1, 1, &num);
+ char dot, str[MAX_CHARS + 1];
+ int len;
+
+ if (code < 0)
+ return code;
+ check_write_type(*op, t_string);
+ gs_sprintf(str, "%f", 1.5);
+ dot = str[1]; /* locale-dependent */
+ /*
+ * To get fully accurate output results for IEEE double-
+ * precision floats (53 bits of mantissa), the ANSI
+ * %g default of 6 digits is not enough; 16 are needed.
+ * Unfortunately, using %.16g produces unfortunate artifacts such as
+ * 1.2 printing as 1.200000000000005. Therefore, we print using %g,
+ * and if the result isn't accurate enough, print again
+ * using %.16g.
+ */
+ {
+ double scanned;
+
+ gs_sprintf(str, "%g", num);
+ sscanf(str, "%lf", &scanned);
+ if (scanned != num)
+ gs_sprintf(str, "%.16g", num);
+ }
+ len = strlen(str);
+ if (len > r_size(op))
+ return_error(gs_error_rangecheck);
+ /* Juggling locales isn't thread-safe. Posix me harder. */
+ if (dot != '.') {
+ char *pdot = strchr(str, dot);
+ if (pdot)
+ *pdot = '.';
+ }
+ memcpy(op->value.bytes, str, len);
+ op[-1] = *op;
+ r_set_size(op - 1, len);
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization table ------ */
+
+/* We need to split the table because of the 16-element limit. */
+const op_def zdouble1_op_defs[] = {
+ /* Arithmetic */
+ {"3.dadd", zdadd},
+ {"3.ddiv", zddiv},
+ {"3.dmul", zdmul},
+ {"3.dsub", zdsub},
+ /* Comparison */
+ {"2.deq", zdeq},
+ {"2.dge", zdge},
+ {"2.dgt", zdgt},
+ {"2.dle", zdle},
+ {"2.dlt", zdlt},
+ {"2.dne", zdne},
+ /* Conversion */
+ {"2.cvd", zcvd},
+ {"2.cvsd", zcvsd},
+ {"1.dcvi", zdcvi},
+ {"1.dcvr", zdcvr},
+ {"2.dcvs", zdcvs},
+ op_def_end(0)
+};
+const op_def zdouble2_op_defs[] = {
+ /* Simple functions */
+ {"2.dabs", zdabs},
+ {"2.dceiling", zdceiling},
+ {"2.dfloor", zdfloor},
+ {"2.dneg", zdneg},
+ {"2.dround", zdround},
+ {"2.dsqrt", zdsqrt},
+ {"2.dtruncate", zdtruncate},
+ /* Transcendental functions */
+ {"2.darccos", zdarccos},
+ {"2.darcsin", zdarcsin},
+ {"3.datan", zdatan},
+ {"2.dcos", zdcos},
+ {"3.dexp", zdexp},
+ {"2.dln", zdln},
+ {"2.dlog", zdlog},
+ {"2.dsin", zdsin},
+ op_def_end(0)
+};
+
+/* ------ Internal procedures ------ */
+
+/* Get some double arguments. */
+static int
+double_params(os_ptr op, int count, double *pval)
+{
+ pval += count;
+ while (--count >= 0) {
+ switch (r_type(op)) {
+ case t_real:
+ *--pval = op->value.realval;
+ break;
+ case t_integer:
+ *--pval = op->value.intval;
+ break;
+ case t_string:
+ if (!r_has_attr(op, a_read) ||
+ r_size(op) != sizeof(double)
+ )
+ return_error(gs_error_typecheck);
+ --pval;
+ memcpy(pval, op->value.bytes, sizeof(double));
+ break;
+ case t__invalid:
+ return_error(gs_error_stackunderflow);
+ default:
+ return_error(gs_error_typecheck);
+ }
+ op--;
+ }
+ return 0;
+}
+
+/* Get some double arguments, and check for a double result. */
+static int
+double_params_result(os_ptr op, int count, double *pval)
+{
+ check_write_type(*op, t_string);
+ if (r_size(op) != sizeof(double))
+ return_error(gs_error_typecheck);
+ return double_params(op - 1, count, pval);
+}
+
+/* Return a double result. */
+static int
+double_result(i_ctx_t *i_ctx_p, int count, double result)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - count;
+
+ ref_assign_inline(op1, op);
+ memcpy(op1->value.bytes, &result, sizeof(double));
+ pop(count);
+ return 0;
+}
+
+/* Apply a unary function to a double operand. */
+static int
+double_unary(i_ctx_t *i_ctx_p, double (*func)(double))
+{
+ dbegin_unary();
+ return double_result(i_ctx_p, 1, (*func)(num));
+}
diff --git a/psi/zdpnext.c b/psi/zdpnext.c
new file mode 100644
index 000000000..310e9c0c6
--- /dev/null
+++ b/psi/zdpnext.c
@@ -0,0 +1,482 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* NeXT Display PostScript extensions */
+#include "math_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gscoord.h"
+#include "gscspace.h" /* for iimage.h */
+#include "gsdpnext.h"
+#include "gsmatrix.h"
+#include "gsiparam.h" /* for iimage.h */
+#include "gsiparm2.h"
+#include "gspath2.h"
+#include "gxcvalue.h"
+#include "gxdevice.h"
+#include "gxsample.h"
+#include "ialloc.h"
+#include "igstate.h"
+#include "iimage.h"
+#include "iimage2.h"
+#include "store.h"
+
+/* ------ alpha channel ------ */
+
+/* - currentalpha <alpha> */
+static int
+zcurrentalpha(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_real(op, gs_currentalpha(igs));
+ return 0;
+}
+
+/* <alpha> setalpha - */
+static int
+zsetalpha(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double alpha;
+ int code;
+
+ if (real_param(op, &alpha) < 0)
+ return_op_typecheck(op);
+ if ((code = gs_setalpha(igs, alpha)) < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* ------ Imaging/compositing ------ */
+
+/*
+ * Miscellaneous notes:
+ *
+ * composite / dissolve respect destination clipping (both clip & viewclip),
+ * but ignore source clipping.
+ * composite / dissolve must handle overlapping source/destination correctly.
+ * compositing converts the source to the destination's color model
+ * (including halftoning if needed).
+ */
+
+/*
+ * Define the operand and bookeeping structure for a compositing operation.
+ */
+typedef struct alpha_composite_state_s {
+ /* Compositing parameters */
+ gs_composite_alpha_params_t params;
+ /* Temporary structures */
+ gs_composite_t *pcte;
+ gx_device *cdev;
+ gx_device *orig_dev;
+} alpha_composite_state_t;
+
+/* Forward references */
+static int begin_composite(i_ctx_t *, alpha_composite_state_t *);
+static void end_composite(i_ctx_t *, alpha_composite_state_t *);
+static int xywh_param(os_ptr, double[4]);
+
+/* <dict> .alphaimage - */
+/* This is the dictionary version of the alphaimage operator, which is */
+/* now a pseudo-operator (see gs_dpnxt.ps). */
+static int
+zalphaimage(i_ctx_t *i_ctx_p)
+{
+ return image1_setup(i_ctx_p, true);
+}
+
+/* <destx> <desty> <width> <height> <op> compositerect - */
+static int
+zcompositerect(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double dest_rect[4];
+ alpha_composite_state_t cstate;
+ int code = xywh_param(op - 1, dest_rect);
+
+ if (code < 0)
+ return code;
+ check_int_leu(*op, compositerect_last);
+ cstate.params.op = (gs_composite_op_t) op->value.intval;
+ code = begin_composite(i_ctx_p, &cstate);
+ if (code < 0)
+ return code;
+ {
+ gs_rect rect;
+
+ rect.q.x = (rect.p.x = dest_rect[0]) + dest_rect[2];
+ rect.q.y = (rect.p.y = dest_rect[1]) + dest_rect[3];
+ code = gs_rectfill(igs, &rect, 1);
+ }
+ end_composite(i_ctx_p, &cstate);
+ if (code >= 0)
+ pop(5);
+ return code;
+}
+
+/* Common code for composite and dissolve. */
+static int
+composite_image(i_ctx_t *i_ctx_p, const gs_composite_alpha_params_t * params)
+{
+ os_ptr op = osp;
+ alpha_composite_state_t cstate;
+ gs_image2_t image;
+ double src_rect[4];
+ double dest_pt[2];
+ gs_matrix save_ctm;
+ int code = xywh_param(op - 4, src_rect);
+
+ cstate.params = *params;
+ gs_image2_t_init(&image);
+ if (code < 0 ||
+ (code = num_params(op - 1, 2, dest_pt)) < 0
+ )
+ return code;
+ if (r_has_type(op - 3, t_null))
+ image.DataSource = igs;
+ else {
+ check_stype(op[-3], st_igstate_obj);
+ check_read(op[-3]);
+ image.DataSource = igstate_ptr(op - 3);
+ }
+ image.XOrigin = src_rect[0];
+ image.YOrigin = src_rect[1];
+ image.Width = src_rect[2];
+ image.Height = src_rect[3];
+ image.PixelCopy = true;
+ /* Compute appropriate transformations. */
+ gs_currentmatrix(igs, &save_ctm);
+ gs_translate(igs, dest_pt[0], dest_pt[1]);
+ gs_make_identity(&image.ImageMatrix);
+ if (image.DataSource == igs) {
+ image.XOrigin -= dest_pt[0];
+ image.YOrigin -= dest_pt[1];
+ }
+ code = begin_composite(i_ctx_p, &cstate);
+ if (code >= 0) {
+ code = process_non_source_image(i_ctx_p,
+ (const gs_image_common_t *)&image,
+ "composite_image");
+ end_composite(i_ctx_p, &cstate);
+ if (code >= 0)
+ pop(8);
+ }
+ gs_setmatrix(igs, &save_ctm);
+ return code;
+}
+
+/* <srcx> <srcy> <width> <height> <srcgstate|null> <destx> <desty> <op> */
+/* composite - */
+static int
+zcomposite(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_composite_alpha_params_t params;
+
+ check_int_leu(*op, composite_last);
+ params.op = (gs_composite_op_t) op->value.intval;
+ return composite_image(i_ctx_p, &params);
+}
+
+/* <srcx> <srcy> <width> <height> <srcgstate|null> <destx> <desty> <delta> */
+/* dissolve - */
+static int
+zdissolve(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_composite_alpha_params_t params;
+ double delta;
+ int code = real_param(op, &delta);
+
+ if (code < 0)
+ return code;
+ if (delta < 0 || delta > 1)
+ return_error(gs_error_rangecheck);
+ params.op = composite_Dissolve;
+ params.delta = delta;
+ return composite_image(i_ctx_p, &params);
+}
+
+/* ------ Image reading ------ */
+
+static int device_is_true_color(gx_device * dev);
+
+/* <x> <y> <width> <height> <matrix> .sizeimagebox */
+/* <dev_x> <dev_y> <dev_width> <dev_height> <matrix> */
+static void box_confine(int *pp, int *pq, int wh);
+static int
+zsizeimagebox(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const gx_device *dev = gs_currentdevice(igs);
+ gs_rect srect, drect;
+ gs_matrix mat;
+ gs_int_rect rect;
+ int w, h;
+ int code;
+
+ check_type(op[-4], t_integer);
+ check_type(op[-3], t_integer);
+ check_type(op[-2], t_integer);
+ check_type(op[-1], t_integer);
+ srect.p.x = op[-4].value.intval;
+ srect.p.y = op[-3].value.intval;
+ srect.q.x = srect.p.x + op[-2].value.intval;
+ srect.q.y = srect.p.y + op[-1].value.intval;
+ gs_currentmatrix(igs, &mat);
+ gs_bbox_transform(&srect, &mat, &drect);
+ /*
+ * We want the dimensions of the image as a source, not a
+ * destination, so we need to expand it rather than pixround.
+ */
+ rect.p.x = (int)floor(drect.p.x);
+ rect.p.y = (int)floor(drect.p.y);
+ rect.q.x = (int)ceil(drect.q.x);
+ rect.q.y = (int)ceil(drect.q.y);
+ /*
+ * Clip the rectangle to the device boundaries, since that's what
+ * the NeXT implementation does.
+ */
+ box_confine(&rect.p.x, &rect.q.x, dev->width);
+ box_confine(&rect.p.y, &rect.q.y, dev->height);
+ w = rect.q.x - rect.p.x;
+ h = rect.q.y - rect.p.y;
+ /*
+ * The NeXT documentation doesn't specify very clearly what is
+ * supposed to be in the matrix: the following produces results
+ * that match testing on an actual NeXT system.
+ */
+ mat.tx -= rect.p.x;
+ mat.ty -= rect.p.y;
+ code = write_matrix(op, &mat);
+ if (code < 0)
+ return code;
+ make_int(op - 4, rect.p.x);
+ make_int(op - 3, rect.p.y);
+ make_int(op - 2, w);
+ make_int(op - 1, h);
+ return 0;
+}
+static void
+box_confine(int *pp, int *pq, int wh)
+{
+ if ( *pq <= 0 )
+ *pp = *pq = 0;
+ else if ( *pp >= wh )
+ *pp = *pq = wh;
+ else {
+ if ( *pp < 0 )
+ *pp = 0;
+ if ( *pq > wh )
+ *pq = wh;
+ }
+}
+
+/* - .sizeimageparams <bits/sample> <multiproc> <ncolors> */
+static int
+zsizeimageparams(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_device *dev = gs_currentdevice(igs);
+ int ncomp = dev->color_info.num_components;
+ int bps;
+
+ push(3);
+ if (device_is_true_color(dev))
+ bps = dev->color_info.depth / ncomp;
+ else {
+ /*
+ * Set bps to the smallest allowable number of bits that is
+ * sufficient to represent the number of different colors.
+ */
+ gx_color_value max_value =
+ (dev->color_info.num_components == 1 ?
+ dev->color_info.max_gray :
+ max(dev->color_info.max_gray, dev->color_info.max_color));
+ static const gx_color_value sizes[] = {
+ 1, 2, 4, 8, 12, sizeof(gx_max_color_value) * 8
+ };
+ int i;
+
+ for (i = 0;; ++i)
+ if (max_value <= ((ulong) 1 << sizes[i]) - 1)
+ break;
+ bps = sizes[i];
+ }
+ make_int(op - 2, bps);
+ make_false(op - 1);
+ make_int(op, ncomp);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zdpnext_op_defs[] =
+{
+ {"0currentalpha", zcurrentalpha},
+ {"1setalpha", zsetalpha},
+ {"1.alphaimage", zalphaimage},
+ {"8composite", zcomposite},
+ {"5compositerect", zcompositerect},
+ {"8dissolve", zdissolve},
+ {"5.sizeimagebox", zsizeimagebox},
+ {"0.sizeimageparams", zsizeimageparams},
+ op_def_end(0)
+};
+
+/* ------ Internal routines ------ */
+
+/* Collect a rect operand. */
+static int
+xywh_param(os_ptr op, double rect[4])
+{
+ int code = num_params(op, 4, rect);
+
+ if (code < 0)
+ return code;
+ if (rect[2] < 0)
+ rect[0] += rect[2], rect[2] = -rect[2];
+ if (rect[3] < 0)
+ rect[1] += rect[3], rect[3] = -rect[3];
+ return code;
+}
+
+/* Begin a compositing operation. */
+static int
+begin_composite(i_ctx_t *i_ctx_p, alpha_composite_state_t * pcp)
+{
+ gx_device *dev = gs_currentdevice(igs);
+ int code =
+ gs_create_composite_alpha(&pcp->pcte, &pcp->params, imemory);
+
+ if (code < 0)
+ return code;
+ pcp->orig_dev = pcp->cdev = dev; /* for end_composite */
+ code = (*dev_proc(dev, create_compositor))
+ (dev, &pcp->cdev, pcp->pcte, (gs_imager_state *)igs, imemory, NULL);
+ if (code < 0) {
+ end_composite(i_ctx_p, pcp);
+ return code;
+ }
+ gs_setdevice_no_init(igs, pcp->cdev);
+ return 0;
+}
+
+/* End a compositing operation. */
+static void
+end_composite(i_ctx_t *i_ctx_p, alpha_composite_state_t * pcp)
+{
+ /* Close and free the compositor and the compositing object. */
+ if (pcp->cdev != pcp->orig_dev) {
+ gs_closedevice(pcp->cdev); /* also frees the device */
+ gs_setdevice_no_init(igs, pcp->orig_dev);
+ }
+ ifree_object(pcp->pcte, "end_composite(gs_composite_t)");
+}
+
+/*
+ * Determine whether a device has decomposed pixels with the components
+ * in the standard PostScript order, and a 1-for-1 color map
+ * (possibly inverted). Return 0 if not true color, 1 if true color,
+ * -1 if inverted true color.
+ */
+static int
+device_is_true_color(gx_device * dev)
+{
+ int ncomp = dev->color_info.num_components;
+ int depth = dev->color_info.depth;
+ int i, max_v;
+
+#define CV(i) (gx_color_value)((ulong)gx_max_color_value * i / max_v)
+#define CV0 ((gx_color_value)0)
+
+ /****** DOESN'T HANDLE INVERSION YET ******/
+ switch (ncomp) {
+ case 1: /* gray-scale */
+ max_v = dev->color_info.max_gray;
+ if (max_v != (1 << depth) - 1)
+ return 0;
+ for (i = 0; i <= max_v; ++i) {
+ gx_color_value v[3];
+ v[0] = v[1] = v[2] = CV(i);
+ if ((*dev_proc(dev, map_rgb_color)) (dev, v) != i)
+ return 0;
+ }
+ return true;
+ case 3: /* RGB */
+ max_v = dev->color_info.max_color;
+ if (depth % 3 != 0 || max_v != (1 << (depth / 3)) - 1)
+ return false;
+ {
+ const int gs = depth / 3, rs = gs * 2;
+
+ for (i = 0; i <= max_v; ++i) {
+ gx_color_value red[3];
+ gx_color_value green[3];
+ gx_color_value blue[3];
+ red[0] = CV(i); red[1] = CV0, red[2] = CV0;
+ green[0] = CV0; green[1] = CV(i); green[2] = CV0;
+ blue[0] = CV0; blue[1] = CV0; blue[2] = CV(i);
+ if ((*dev_proc(dev, map_rgb_color)) (dev, red) !=
+ i << rs ||
+ (*dev_proc(dev, map_rgb_color)) (dev, green) !=
+ i << gs ||
+ (*dev_proc(dev, map_rgb_color)) (dev, blue) !=
+ i /*<< bs */
+ )
+ return 0;
+ }
+ }
+ return true;
+ case 4: /* CMYK */
+ max_v = dev->color_info.max_color;
+ if ((depth & 3) != 0 || max_v != (1 << (depth / 4)) - 1)
+ return false;
+ {
+ const int ys = depth / 4, ms = ys * 2, cs = ys * 3;
+
+ for (i = 0; i <= max_v; ++i) {
+
+ gx_color_value cyan[4];
+ gx_color_value magenta[4];
+ gx_color_value yellow[4];
+ gx_color_value black[4];
+ cyan[0] = CV(i); cyan[1] = cyan[2] = cyan[3] = CV0;
+ magenta[1] = CV(i); magenta[0] = magenta[2] = magenta[3] = CV0;
+ yellow[2] = CV(i); yellow[0] = yellow[1] = yellow[3] = CV0;
+ black[3] = CV(i); black[0] = black[1] = black[2] = CV0;
+ if ((*dev_proc(dev, map_cmyk_color)) (dev, cyan) !=
+ i << cs ||
+ (*dev_proc(dev, map_cmyk_color)) (dev, magenta) !=
+ i << ms ||
+ (*dev_proc(dev, map_cmyk_color)) (dev, yellow) !=
+ i << ys ||
+ (*dev_proc(dev, map_cmyk_color)) (dev, black) !=
+ i /*<< ks */
+ )
+ return 0;
+ }
+ }
+ return 1;
+ default:
+ return 0; /* DeviceN */
+ }
+#undef CV
+#undef CV0
+}
diff --git a/psi/zdps.c b/psi/zdps.c
new file mode 100644
index 000000000..fa1d6e93e
--- /dev/null
+++ b/psi/zdps.c
@@ -0,0 +1,277 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Display PostScript extensions */
+#include "ghost.h"
+#include "oper.h"
+#include "gsstate.h"
+#include "gsdps.h"
+#include "gsimage.h"
+#include "gsiparm2.h"
+#include "gxalloc.h" /* for names_array in allocator */
+#include "gxfixed.h" /* for gxpath.h */
+#include "gxpath.h"
+#include "btoken.h" /* for user_names_p */
+#include "iddict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "iimage2.h"
+#include "iname.h"
+#include "store.h"
+
+/* Import the procedure for constructing user paths. */
+extern int make_upath(i_ctx_t *, ref *, const gs_state *, gx_path *, bool);
+
+/* ------ Graphics state ------ */
+
+/* <screen_index> <x> <y> .setscreenphase - */
+static int
+zsetscreenphase(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ int x, y;
+
+ check_type(op[-2], t_integer);
+ check_type(op[-1], t_integer);
+ check_type(*op, t_integer);
+ x = op[-1].value.intval;
+ y = op->value.intval;
+ if (op[-2].value.intval < -1 ||
+ op[-2].value.intval >= gs_color_select_count
+ )
+ return_error(gs_error_rangecheck);
+ code = gs_setscreenphase(igs, x, y,
+ (gs_color_select_t) op[-2].value.intval);
+ if (code >= 0)
+ pop(3);
+ return code;
+}
+
+/* <screen_index> .currentscreenphase <x> <y> */
+static int
+zcurrentscreenphase(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_int_point phase;
+ int code;
+
+ check_type(*op, t_integer);
+ if (op->value.intval < -1 ||
+ op->value.intval >= gs_color_select_count
+ )
+ return_error(gs_error_rangecheck);
+ code = gs_currentscreenphase(igs, &phase,
+ (gs_color_select_t)op->value.intval);
+ if (code < 0)
+ return code;
+ push(1);
+ make_int(op - 1, phase.x);
+ make_int(op, phase.y);
+ return 0;
+}
+
+/* ------ Device-source images ------ */
+
+/* <dict> .image2 - */
+static int
+zimage2(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ {
+ gs_image2_t image;
+ ref *pDataSource;
+
+ gs_image2_t_init(&image);
+ if ((code = dict_matrix_param(imemory, op, "ImageMatrix",
+ &image.ImageMatrix)) < 0 ||
+ (code = dict_find_string(op, "DataSource", &pDataSource)) < 0 ||
+ (code = dict_float_param(op, "XOrigin", 0.0,
+ &image.XOrigin)) != 0 ||
+ (code = dict_float_param(op, "YOrigin", 0.0,
+ &image.YOrigin)) != 0 ||
+ (code = dict_float_param(op, "Width", 0.0,
+ &image.Width)) != 0 ||
+ image.Width <= 0 ||
+ (code = dict_float_param(op, "Height", 0.0,
+ &image.Height)) != 0 ||
+ image.Height <= 0 ||
+ (code = dict_bool_param(op, "PixelCopy", false,
+ &image.PixelCopy)) < 0
+ )
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+ check_stype(*pDataSource, st_igstate_obj);
+ image.DataSource = igstate_ptr(pDataSource);
+ {
+ ref *ignoref;
+
+ if (dict_find_string(op, "UnpaintedPath", &ignoref) > 0) {
+ check_dict_write(*op);
+ image.UnpaintedPath = gx_path_alloc(imemory,
+ ".image2 UnpaintedPath");
+ if (image.UnpaintedPath == 0)
+ return_error(gs_error_VMerror);
+ } else
+ image.UnpaintedPath = 0;
+ }
+ code = process_non_source_image(i_ctx_p,
+ (const gs_image_common_t *)&image,
+ ".image2");
+ if (image.UnpaintedPath) {
+ ref rupath;
+
+ if (code < 0)
+ return code;
+ if (gx_path_is_null(image.UnpaintedPath))
+ make_null(&rupath);
+ else
+ code = make_upath(i_ctx_p, &rupath, igs, image.UnpaintedPath,
+ false);
+ gx_path_free(image.UnpaintedPath, ".image2 UnpaintedPath");
+ if (code < 0)
+ return code;
+ code = idict_put_string(op, "UnpaintedPath", &rupath);
+ }
+ }
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+/* ------ View clipping ------ */
+
+/* - viewclip - */
+static int
+zviewclip(i_ctx_t *i_ctx_p)
+{
+ return gs_viewclip(igs);
+}
+
+/* - eoviewclip - */
+static int
+zeoviewclip(i_ctx_t *i_ctx_p)
+{
+ return gs_eoviewclip(igs);
+}
+
+/* - initviewclip - */
+static int
+zinitviewclip(i_ctx_t *i_ctx_p)
+{
+ return gs_initviewclip(igs);
+}
+
+/* - viewclippath - */
+static int
+zviewclippath(i_ctx_t *i_ctx_p)
+{
+ return gs_viewclippath(igs);
+}
+
+/* ------ User names ------ */
+
+/* <index> <name> defineusername - */
+static int
+zdefineusername(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref uname;
+
+ check_int_ltu(op[-1], max_array_size);
+ check_type(*op, t_name);
+ if (user_names_p == 0) {
+ int code = create_names_array(&user_names_p, imemory_local,
+ "defineusername");
+
+ if (code < 0)
+ return code;
+ }
+ if (array_get(imemory, user_names_p,
+ op[-1].value.intval, &uname) >= 0) {
+ switch (r_type(&uname)) {
+ case t_null:
+ break;
+ case t_name:
+ if (name_eq(&uname, op))
+ goto ret;
+ /* falls through */
+ default:
+ return_error(gs_error_invalidaccess);
+ }
+ } else { /* Expand the array. */
+ ref new_array;
+ uint old_size = r_size(user_names_p);
+ uint new_size = (uint) op[-1].value.intval + 1;
+
+ if (new_size < 100)
+ new_size = 100;
+ else if (new_size > max_array_size / 2)
+ new_size = max_array_size;
+ else if (new_size >> 1 < old_size)
+ new_size = (old_size > max_array_size / 2 ? max_array_size :
+ old_size << 1);
+ else
+ new_size <<= 1;
+ /*
+ * The user name array is allocated in stable local VM,
+ * because it must be immune to save/restore.
+ */
+ {
+ gs_ref_memory_t *slmem =
+ (gs_ref_memory_t *)gs_memory_stable(imemory_local);
+ int code;
+
+ code = gs_alloc_ref_array(slmem, &new_array, a_all, new_size,
+ "defineusername(new)");
+ if (code < 0)
+ return code;
+ refcpy_to_new(new_array.value.refs, user_names_p->value.refs,
+ old_size, idmemory);
+ refset_null(new_array.value.refs + old_size,
+ new_size - old_size);
+ if (old_size)
+ gs_free_ref_array(slmem, user_names_p, "defineusername(old)");
+ }
+ ref_assign(user_names_p, &new_array);
+ }
+ ref_assign(user_names_p->value.refs + op[-1].value.intval, op);
+ ret:
+ pop(2);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zdps_op_defs[] =
+{
+ /* Graphics state */
+ {"1.currentscreenphase", zcurrentscreenphase},
+ {"3.setscreenphase", zsetscreenphase},
+ /* Device-source images */
+ {"1.image2", zimage2},
+ /* View clipping */
+ {"0eoviewclip", zeoviewclip},
+ {"0initviewclip", zinitviewclip},
+ {"0viewclip", zviewclip},
+ {"0viewclippath", zviewclippath},
+ /* User names */
+ {"2defineusername", zdefineusername},
+ op_def_end(0)
+};
diff --git a/psi/zdps1.c b/psi/zdps1.c
new file mode 100644
index 000000000..71a7baa0d
--- /dev/null
+++ b/psi/zdps1.c
@@ -0,0 +1,488 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 2 / Display PostScript graphics extensions */
+#include "ghost.h"
+#include "oper.h"
+#include "gsmatrix.h"
+#include "gspath.h"
+#include "gspath2.h"
+#include "gsstate.h"
+#include "ialloc.h"
+#include "igstate.h"
+#include "ivmspace.h"
+#include "store.h"
+#include "stream.h"
+#include "ibnum.h"
+
+/* Forward references */
+static int gstate_unshare(i_ctx_t *);
+
+/* Declare exported procedures (for zupath.c) */
+int zsetbbox(i_ctx_t *);
+
+/* Structure descriptors */
+public_st_igstate_obj();
+
+/* Extend the `copy' operator to deal with gstates. */
+/* This is done with a hack -- we know that gstates are the only */
+/* t_astruct subtype that implements copy. */
+static int
+z1copy(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = zcopy(i_ctx_p);
+
+ if (code >= 0)
+ return code;
+ if (!r_has_type(op, t_astruct))
+ return code;
+ return zcopy_gstate(i_ctx_p);
+}
+
+/* ------ Graphics state ------ */
+
+/* <bool> setstrokeadjust - */
+static int
+zsetstrokeadjust(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_boolean);
+ gs_setstrokeadjust(igs, op->value.boolval);
+ pop(1);
+ return 0;
+}
+
+/* - currentstrokeadjust <bool> */
+static int
+zcurrentstrokeadjust(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_bool(op, gs_currentstrokeadjust(igs));
+ return 0;
+}
+
+/* ------ Graphics state objects ------ */
+
+/*
+ * Check to make sure that all the elements of a graphics state
+ * can be stored in the given allocation space.
+ */
+/* ****** DOESN'T CHECK THE NON-REFS. ****** */
+static int
+gstate_check_space(i_ctx_t *i_ctx_p, int_gstate *isp, uint space)
+{
+ /*
+ * ****** WORKAROUND ALERT ******
+ * This code doesn't check the space of the non-refs, or copy their
+ * contents, so it can create dangling references from global VM to
+ * local VM. Because of this, we simply disallow writing into gstates
+ * in global VM (including creating them in the first place) if the
+ * save level is greater than 0.
+ * ****** WORKAROUND ALERT ******
+ */
+#if 1 /* ****** WORKAROUND ****** */
+ if (space != avm_local && imemory_save_level(iimemory) > 0)
+ return_error(gs_error_invalidaccess);
+#endif /* ****** END ****** */
+#define gsref_check(p) store_check_space(space, p)
+ int_gstate_map_refs(isp, gsref_check);
+#undef gsref_check
+ return 0;
+}
+
+/* - gstate <gstate> */
+int
+zgstate(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ int code = gstate_check_space(i_ctx_p, istate, icurrent_space);
+ igstate_obj *pigo;
+ gs_state *pnew;
+ int_gstate *isp;
+
+ if (code < 0)
+ return code;
+ pigo = ialloc_struct(igstate_obj, &st_igstate_obj, "gstate");
+ if (pigo == 0)
+ return_error(gs_error_VMerror);
+ pnew = gs_state_copy(igs, imemory);
+ if (pnew == 0) {
+ ifree_object(pigo, "gstate");
+ return_error(gs_error_VMerror);
+ }
+ isp = gs_int_gstate(pnew);
+ int_gstate_map_refs(isp, ref_mark_new);
+ push(1);
+ /*
+ * Since igstate_obj isn't a ref, but only contains a ref, save won't
+ * clear its l_new bit automatically, and restore won't set it
+ * automatically; we have to make sure this ref is on the changes chain.
+ */
+ make_iastruct(op, a_all, pigo);
+#if 0 /* Bug 689849 "gstate leaks memory" */
+ make_null(&pigo->gstate);
+ ref_save(op, &pigo->gstate, "gstate");
+ make_istruct_new(&pigo->gstate, 0, pnew);
+#else
+ make_istruct(&pigo->gstate, 0, pnew);
+#endif
+ return 0;
+}
+
+/* copy for gstates */
+int
+zcopy_gstate(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ gs_state *pgs;
+ gs_state *pgs1;
+ int_gstate *pistate;
+ gs_memory_t *mem;
+ int code;
+
+ check_stype(*op, st_igstate_obj);
+ check_stype(*op1, st_igstate_obj);
+ check_write(*op);
+ code = gstate_unshare(i_ctx_p);
+ if (code < 0)
+ return code;
+ pgs = igstate_ptr(op);
+ pgs1 = igstate_ptr(op1);
+ pistate = gs_int_gstate(pgs);
+ code = gstate_check_space(i_ctx_p, gs_int_gstate(pgs1), r_space(op));
+ if (code < 0)
+ return code;
+#define gsref_save(p) ref_save(op, p, "copygstate")
+ int_gstate_map_refs(pistate, gsref_save);
+#undef gsref_save
+ mem = gs_state_swap_memory(pgs, imemory);
+ code = gs_copygstate(pgs, pgs1);
+ gs_state_swap_memory(pgs, mem);
+ if (code < 0)
+ return code;
+ int_gstate_map_refs(pistate, ref_mark_new);
+ *op1 = *op;
+ pop(1);
+ return 0;
+}
+
+/* <gstate> currentgstate <gstate> */
+int
+zcurrentgstate(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_state *pgs;
+ int_gstate *pistate;
+ int code;
+ gs_memory_t *mem;
+
+ check_stype(*op, st_igstate_obj);
+ check_write(*op);
+ code = gstate_unshare(i_ctx_p);
+ if (code < 0)
+ return code;
+ pgs = igstate_ptr(op);
+ pistate = gs_int_gstate(pgs);
+ code = gstate_check_space(i_ctx_p, istate, r_space(op));
+ if (code < 0)
+ return code;
+#define gsref_save(p) ref_save(op, p, "currentgstate")
+ int_gstate_map_refs(pistate, gsref_save);
+#undef gsref_save
+ mem = gs_state_swap_memory(pgs, imemory);
+ code = gs_currentgstate(pgs, igs);
+ gs_state_swap_memory(pgs, mem);
+ if (code < 0)
+ return code;
+ int_gstate_map_refs(pistate, ref_mark_new);
+ return 0;
+}
+
+/* <gstate> setgstate - */
+int
+zsetgstate(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_stype(*op, st_igstate_obj);
+ check_read(*op);
+ code = gs_setgstate(igs, igstate_ptr(op));
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* ------ Rectangles ------- */
+
+/*
+ * We preallocate a short list for rectangles, because
+ * the rectangle operators usually will involve very few rectangles.
+ */
+#define MAX_LOCAL_RECTS 5
+typedef struct local_rects_s {
+ gs_rect *pr;
+ uint count;
+ gs_rect rl[MAX_LOCAL_RECTS];
+} local_rects_t;
+
+/* Forward references */
+static int rect_get(local_rects_t *, os_ptr, gs_memory_t *);
+static void rect_release(local_rects_t *, gs_memory_t *);
+
+/* <x> <y> <width> <height> .rectappend - */
+/* <numarray|numstring> .rectappend - */
+static int
+zrectappend(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ local_rects_t lr;
+ int npop = rect_get(&lr, op, imemory);
+ int code;
+
+ if (npop < 0)
+ return npop;
+ code = gs_rectappend(igs, lr.pr, lr.count);
+ rect_release(&lr, imemory);
+ if (code < 0)
+ return code;
+ pop(npop);
+ return 0;
+}
+
+/* <x> <y> <width> <height> rectclip - */
+/* <numarray|numstring> rectclip - */
+static int
+zrectclip(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ local_rects_t lr;
+ int npop = rect_get(&lr, op, imemory);
+ int code;
+
+ if (npop < 0)
+ return npop;
+ code = gs_rectclip(igs, lr.pr, lr.count);
+ rect_release(&lr, imemory);
+ if (code < 0)
+ return code;
+ pop(npop);
+ return 0;
+}
+
+/* <x> <y> <width> <height> rectfill - */
+/* <numarray|numstring> rectfill - */
+static int
+zrectfill(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ local_rects_t lr;
+ int npop = rect_get(&lr, op, imemory);
+ int code;
+
+ if (npop < 0)
+ return npop;
+ code = gs_rectfill(igs, lr.pr, lr.count);
+ rect_release(&lr, imemory);
+ if (code < 0)
+ return code;
+ pop(npop);
+ return 0;
+}
+
+/* <x> <y> <width> <height> rectstroke - */
+/* <numarray|numstring> rectstroke - */
+static int
+zrectstroke(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_matrix mat;
+ local_rects_t lr;
+ int npop, code;
+
+ if (read_matrix(imemory, op, &mat) >= 0) {
+ /* Concatenate the matrix to the CTM just before stroking the path. */
+ npop = rect_get(&lr, op - 1, imemory);
+ if (npop < 0)
+ return npop;
+ code = gs_rectstroke(igs, lr.pr, lr.count, &mat);
+ npop++;
+ } else {
+ /* No matrix. */
+ npop = rect_get(&lr, op, imemory);
+ if (npop < 0)
+ return npop;
+ code = gs_rectstroke(igs, lr.pr, lr.count, (gs_matrix *) 0);
+ }
+ rect_release(&lr, imemory);
+ if (code < 0)
+ return code;
+ pop(npop);
+ return 0;
+}
+
+/* --- Internal routines --- */
+
+/* Get rectangles from the stack. */
+/* Return the number of elements to pop (>0) if OK, <0 if error. */
+static int
+rect_get(local_rects_t * plr, os_ptr op, gs_memory_t *mem)
+{
+ int format, code;
+ uint n, count;
+ gs_rect *pr;
+ double rv[4];
+
+ switch (r_type(op)) {
+ case t_array:
+ case t_mixedarray:
+ case t_shortarray:
+ case t_string:
+ code = num_array_format(op);
+ if (code < 0)
+ return code;
+ format = code;
+ count = num_array_size(op, format);
+ if (count % 4)
+ return_error(gs_error_typecheck);
+ count /= 4;
+ break;
+ default: /* better be 4 numbers */
+ code = num_params(op, 4, rv);
+ if (code < 0)
+ return code;
+ plr->pr = plr->rl;
+ plr->count = 1;
+ plr->rl[0].q.x = (plr->rl[0].p.x = rv[0]) + rv[2];
+ plr->rl[0].q.y = (plr->rl[0].p.y = rv[1]) + rv[3];
+ return 4;
+ }
+ plr->count = count;
+ if (count <= MAX_LOCAL_RECTS)
+ pr = plr->rl;
+ else {
+ pr = (gs_rect *)gs_alloc_byte_array(mem, count, sizeof(gs_rect),
+ "rect_get");
+ if (pr == 0)
+ return_error(gs_error_VMerror);
+ }
+ plr->pr = pr;
+ for (n = 0; n < count; n++, pr++) {
+ ref rnum;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ code = num_array_get(mem, (const ref *)op, format,
+ (n << 2) + i, &rnum);
+ switch (code) {
+ case t_integer:
+ rv[i] = rnum.value.intval;
+ break;
+ case t_real:
+ rv[i] = rnum.value.realval;
+ break;
+ default: /* code < 0 */
+ return code;
+ }
+ }
+ pr->q.x = (pr->p.x = rv[0]) + rv[2];
+ pr->q.y = (pr->p.y = rv[1]) + rv[3];
+ }
+ return 1;
+}
+
+/* Release the rectangle list if needed. */
+static void
+rect_release(local_rects_t * plr, gs_memory_t *mem)
+{
+ if (plr->pr != plr->rl)
+ gs_free_object(mem, plr->pr, "rect_release");
+}
+
+/* ------ Graphics state ------ */
+
+/* <llx> <lly> <urx> <ury> setbbox - */
+int
+zsetbbox(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double box[4];
+
+ int code = num_params(op, 4, box);
+
+ if (code < 0)
+ return code;
+ if ((code = gs_setbbox(igs, box[0], box[1], box[2], box[3])) < 0)
+ return code;
+ pop(4);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zdps1_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ /* Graphics state */
+ {"0currentstrokeadjust", zcurrentstrokeadjust},
+ {"1setstrokeadjust", zsetstrokeadjust},
+ /* Graphics state objects */
+ {"1copy", z1copy},
+ {"1currentgstate", zcurrentgstate},
+ {"0gstate", zgstate},
+ {"1setgstate", zsetgstate},
+ /* Rectangles */
+ {"1.rectappend", zrectappend},
+ {"1rectclip", zrectclip},
+ {"1rectfill", zrectfill},
+ {"1rectstroke", zrectstroke},
+ /* Graphics state components */
+ {"4setbbox", zsetbbox},
+ op_def_end(0)
+};
+
+/* ------ Internal routines ------ */
+
+/* Ensure that a gstate is not shared with an outer save level. */
+/* *op is of type t_astruct(igstate_obj). */
+static int
+gstate_unshare(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *pgsref = &r_ptr(op, igstate_obj)->gstate;
+ gs_state *pgs = r_ptr(pgsref, gs_state);
+ gs_state *pnew;
+ int_gstate *isp;
+
+ if (!ref_must_save(pgsref))
+ return 0;
+ /* Copy the gstate. */
+ pnew = gs_gstate(pgs);
+ if (pnew == 0)
+ return_error(gs_error_VMerror);
+ isp = gs_int_gstate(pnew);
+ int_gstate_map_refs(isp, ref_mark_new);
+ ref_do_save(op, pgsref, "gstate_unshare");
+ make_istruct_new(pgsref, 0, pnew);
+ return 0;
+}
diff --git a/psi/zdscpars.c b/psi/zdscpars.c
new file mode 100644
index 000000000..c05e15418
--- /dev/null
+++ b/psi/zdscpars.c
@@ -0,0 +1,517 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* C language interface routines to DSC parser */
+
+/*
+ * The DSC parser consists of three pieces. The first piece is a DSC parser
+ * which was coded by Russell Lang (dscparse.c and dscparse.h). The second
+ * piece is this module. These two are sufficient to parse DSC comments
+ * and make them available to a client written in PostScript. The third
+ * piece is a PostScript language module (gs_dscp.ps) that uses certain
+ * comments to affect the interpretation of the file.
+ *
+ * The .initialize_dsc_parser operator defined in this file creates an
+ * instance of Russell's parser, and puts it in a client-supplied dictionary
+ * under a known name (/DSC_struct).
+ *
+ * When the PostScript scanner sees a possible DSC comment (first characters
+ * in a line are %%), it calls the procedure that is the value of the user
+ * parameter ProcessDSCComments. This procedure should loads the dictionary
+ * that was passed to .initialize_dsc_parser, and then call the
+ * .parse_dsc_comments operator defined in this file.
+ *
+ * These two operators comprise the interface between PostScript and C code.
+ *
+ * There is a "feature" named usedsc that loads a PostScript file
+ * (gs_dscp.ps), which installs a simple framework for processing DSC
+ * comments and having them affect interpretation of the file (e.g., by
+ * setting page device parameters). See gs_dscp.ps for more information.
+ *
+ * .parse_dsc_comments pulls the comment string off of the stack and passes
+ * it to Russell's parser. That parser parses the comment and puts any
+ * parameter values into a DSC structure. That parser also returns a code
+ * which indicates which type of comment was found. .parse_dsc_comments
+ * looks at the return code and transfers any interesting parameters from
+ * the DSC structure into key value pairs in the dsc_dict dictionary. It
+ * also translates the comment type code into a key name (comment name).
+ * The key name is placed on the operand stack. Control then returns to
+ * PostScript code, which can pull the key name from the operand stack and
+ * use it to determine what further processing needs to be done at the PS
+ * language level.
+ *
+ * To add support for new DSC comments:
+ *
+ * 1. Verify that Russell's parser supports the comment. If not, then add
+ * the required support.
+ *
+ * 2. Add an entry into DSCcmdlist. This table contains three values for
+ * each command that we support. The first is Russell's return code for
+ * the command. The second is the key name that we pass back on the
+ * operand stack. (Thus this table translates Russell's codes into key
+ * names for the PostScript client.) The third entry is a pointer to a
+ * local function for transferring values from Russell's DSC structure
+ * into key/value pairs in dsc_dict.
+ *
+ * 3. Create the local function described at the end of the last item.
+ * There are some support routines like dsc_put_integer() and
+ * dsc_put_string() to help implement these functions.
+ *
+ * 4. If the usedsc feature should recognize and process the new comments,
+ * add a processing routine into the dictionary DSCparserprocs in
+ * gs_dscp.ps. The keys in this dictionary are the key names described
+ * in item 2 above.
+ */
+
+#include "ghost.h"
+#include "string_.h"
+#include "memory_.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "iname.h"
+#include "istack.h" /* for iparam.h */
+#include "iparam.h"
+#include "ivmspace.h"
+#include "oper.h"
+#include "estack.h"
+#include "store.h"
+#include "idict.h"
+#include "iddict.h"
+#include "dscparse.h"
+
+/*
+ * Declare the structure we use to represent an instance of the parser
+ * as a t_struct. Currently it just saves a pointer to Russell's
+ * data structure.
+ */
+typedef struct dsc_data_s {
+ CDSC *dsc_data_ptr;
+ int document_level;
+} dsc_data_t;
+
+/* Structure descriptors */
+static void dsc_finalize(const gs_memory_t *cmem, void *vptr);
+gs_private_st_simple_final(st_dsc_data_t, dsc_data_t, "dsc_data_struct", dsc_finalize);
+
+static void *zDSC_memalloc (size_t size, void *closure_data);
+static void zDSC_memfree(void *ptr, void *closure_data);
+
+
+/* Define the key name for storing the instance pointer in a dictionary. */
+static const char * const dsc_dict_name = "DSC_struct";
+
+/* ---------------- Initialization / finalization ---------------- */
+
+/*
+ * If we return CDSC_OK then Russell's parser will make it best guess when
+ * it encounters unexpected comment situations.
+ */
+static int
+dsc_error_handler(void *caller_data, CDSC *dsc, unsigned int explanation,
+ const char *line, unsigned int line_len)
+{
+ return CDSC_OK;
+}
+
+static void *zDSC_memalloc (size_t size, void *closure_data)
+{
+ gs_memory_t *cmem = (gs_memory_t *)closure_data;
+
+ return(gs_alloc_bytes(cmem, size, "zDSC_memalloc: DSC parsing memory alloc"));
+}
+
+static void zDSC_memfree(void *ptr, void *closure_data)
+{
+ gs_memory_t *cmem = (gs_memory_t *)closure_data;
+
+ gs_free_object(cmem, ptr, "zDSC_memfree: DSC parsing memory free");
+}
+
+/*
+ * This operator creates a new, initialized instance of the DSC parser.
+ */
+/* <dict> .initialize_dsc_parser - */
+static int
+zinitialize_dsc_parser(i_ctx_t *i_ctx_p)
+{
+ ref local_ref;
+ int code;
+ os_ptr const op = osp;
+ dict * const pdict = op->value.pdict;
+ gs_memory_t * const mem = (gs_memory_t *)dict_memory(pdict);
+ dsc_data_t * const data =
+ gs_alloc_struct(mem, dsc_data_t, &st_dsc_data_t, "DSC parser init");
+
+ if (!data)
+ return_error(gs_error_VMerror);
+ data->document_level = 0;
+
+ data->dsc_data_ptr = dsc_init_with_alloc((void *) "Ghostscript DSC parsing",
+ zDSC_memalloc, zDSC_memfree, (void *)mem->non_gc_memory);
+ if (!data->dsc_data_ptr)
+ return_error(gs_error_VMerror);
+ dsc_set_error_function(data->dsc_data_ptr, dsc_error_handler);
+ make_astruct(&local_ref, a_readonly | r_space(op), (byte *) data);
+ code = idict_put_string(op, dsc_dict_name, &local_ref);
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+/*
+ * This routine will free the memory associated with Russell's parser.
+ */
+static void
+dsc_finalize(const gs_memory_t *cmem, void *vptr)
+{
+ dsc_data_t * const st = vptr;
+ (void)cmem; /* unused */
+
+ if (st->dsc_data_ptr)
+ dsc_free(st->dsc_data_ptr);
+ st->dsc_data_ptr = NULL;
+}
+
+/* ---------------- Parsing ---------------- */
+
+/* ------ Utilities for returning values ------ */
+
+/* Return an integer value. */
+static int
+dsc_put_int(gs_param_list *plist, const char *keyname, int value)
+{
+ return param_write_int(plist, keyname, &value);
+}
+
+/* Return a string value. */
+static int
+dsc_put_string(gs_param_list *plist, const char *keyname,
+ const char *string)
+{
+ gs_param_string str;
+
+ param_string_from_transient_string(str, string);
+ return param_write_string(plist, keyname, &str);
+}
+
+/* Return a BoundingBox value. */
+static int
+dsc_put_bounding_box(gs_param_list *plist, const char *keyname,
+ const CDSCBBOX *pbbox)
+{
+ /* pbbox is NULL iff the bounding box values was "(atend)". */
+ int values[4];
+ gs_param_int_array va;
+
+ if (!pbbox)
+ return 0;
+ values[0] = pbbox->llx;
+ values[1] = pbbox->lly;
+ values[2] = pbbox->urx;
+ values[3] = pbbox->ury;
+ va.data = values;
+ va.size = 4;
+ va.persistent = false;
+ return param_write_int_array(plist, keyname, &va);
+}
+
+/* ------ Return values for individual comment types ------ */
+
+/*
+ * These routines transfer data from the C structure into Postscript
+ * key/value pairs in a dictionary.
+ */
+static int
+dsc_adobe_header(gs_param_list *plist, const CDSC *pData)
+{
+ return dsc_put_int(plist, "EPSF", (int)(pData->epsf? 1: 0));
+}
+
+static int
+dsc_creator(gs_param_list *plist, const CDSC *pData)
+{
+ return dsc_put_string(plist, "Creator", pData->dsc_creator );
+}
+
+static int
+dsc_creation_date(gs_param_list *plist, const CDSC *pData)
+{
+ return dsc_put_string(plist, "CreationDate", pData->dsc_date );
+}
+
+static int
+dsc_title(gs_param_list *plist, const CDSC *pData)
+{
+ return dsc_put_string(plist, "Title", pData->dsc_title );
+}
+
+static int
+dsc_for(gs_param_list *plist, const CDSC *pData)
+{
+ return dsc_put_string(plist, "For", pData->dsc_for);
+}
+
+static int
+dsc_bounding_box(gs_param_list *plist, const CDSC *pData)
+{
+ return dsc_put_bounding_box(plist, "BoundingBox", pData->bbox);
+}
+
+static int
+dsc_page(gs_param_list *plist, const CDSC *pData)
+{
+ int page_num = pData->page_count;
+
+ if (page_num) /* If we have page information */
+ return dsc_put_int(plist, "PageNum",
+ pData->page[page_num - 1].ordinal );
+ else /* No page info - so return page=0 */
+ return dsc_put_int(plist, "PageNum", 0 );
+}
+
+static int
+dsc_pages(gs_param_list *plist, const CDSC *pData)
+{
+ return dsc_put_int(plist, "NumPages", pData->page_pages);
+}
+
+static int
+dsc_page_bounding_box(gs_param_list *plist, const CDSC *pData)
+{
+ return dsc_put_bounding_box(plist, "PageBoundingBox", pData->page_bbox);
+}
+
+/*
+ * Translate Russell's defintions of orientation into Postscript's.
+ */
+static int
+convert_orient(CDSC_ORIENTATION_ENUM orient)
+{
+ switch (orient) {
+ case CDSC_PORTRAIT: return 0;
+ case CDSC_LANDSCAPE: return 1;
+ case CDSC_UPSIDEDOWN: return 2;
+ case CDSC_SEASCAPE: return 3;
+ default: return -1;
+ }
+}
+
+static int
+dsc_page_orientation(gs_param_list *plist, const CDSC *pData)
+{
+ int page_num = pData->page_count;
+
+ /*
+ * The PageOrientation comment might be either in the 'defaults'
+ * section or in a page section. If in the defaults then fhe value
+ * will be in page_orientation.
+ */
+ if (page_num && pData->page[page_num - 1].orientation != CDSC_ORIENT_UNKNOWN)
+ return dsc_put_int(plist, "PageOrientation",
+ convert_orient(pData->page[page_num - 1].orientation));
+ else
+ return dsc_put_int(plist, "Orientation",
+ convert_orient(pData->page_orientation));
+}
+
+static int
+dsc_orientation(gs_param_list *plist, const CDSC *pData)
+{
+ return dsc_put_int(plist, "Orientation",
+ convert_orient(pData->page_orientation));
+}
+
+static int
+dsc_viewing_orientation(gs_param_list *plist, const CDSC *pData)
+{
+ int page_num = pData->page_count;
+ const char *key;
+ const CDSCCTM *pctm;
+ float values[4];
+ gs_param_float_array va;
+
+ /*
+ * As for PageOrientation, ViewingOrientation may be either in the
+ * 'defaults' section or in a page section.
+ */
+ if (page_num && pData->page[page_num - 1].viewing_orientation != NULL) {
+ key = "PageViewingOrientation";
+ pctm = pData->page[page_num - 1].viewing_orientation;
+ } else if (pData->viewing_orientation) {
+ key = "ViewingOrientation";
+ pctm = pData->viewing_orientation;
+ } else
+ return 0; /* ignore broken comment */
+ values[0] = pctm->xx;
+ values[1] = pctm->xy;
+ values[2] = pctm->yx;
+ values[3] = pctm->yy;
+ va.data = values;
+ va.size = 4;
+ va.persistent = false;
+ return param_write_float_array(plist, key, &va);
+}
+
+/*
+ * This list is used to translate the commment code returned
+ * from Russell's DSC parser, define a name, and a parameter procedure.
+ */
+typedef struct cmdlist_s {
+ int code; /* Russell's DSC parser code (see dsc.h) */
+ const char *comment_name; /* A name to be returned to postscript caller */
+ int (*dsc_proc) (gs_param_list *, const CDSC *);
+ /* A routine for transferring parameter values
+ from C data structure to postscript dictionary
+ key/value pairs. */
+} cmdlist_t;
+
+static const cmdlist_t DSCcmdlist[] = {
+ { CDSC_PSADOBE, "Header", dsc_adobe_header },
+ { CDSC_CREATOR, "Creator", dsc_creator },
+ { CDSC_CREATIONDATE, "CreationDate", dsc_creation_date },
+ { CDSC_TITLE, "Title", dsc_title },
+ { CDSC_FOR, "For", dsc_for },
+ { CDSC_BOUNDINGBOX, "BoundingBox", dsc_bounding_box },
+ { CDSC_ORIENTATION, "Orientation", dsc_orientation },
+ { CDSC_BEGINDEFAULTS, "BeginDefaults", NULL },
+ { CDSC_ENDDEFAULTS, "EndDefaults", NULL },
+ { CDSC_PAGE, "Page", dsc_page },
+ { CDSC_PAGES, "Pages", dsc_pages },
+ { CDSC_PAGEORIENTATION, "PageOrientation", dsc_page_orientation },
+ { CDSC_PAGEBOUNDINGBOX, "PageBoundingBox", dsc_page_bounding_box },
+ { CDSC_VIEWINGORIENTATION, "ViewingOrientation", dsc_viewing_orientation },
+ { CDSC_EOF, "EOF", NULL },
+ { 0, "NOP", NULL } /* Table terminator */
+};
+
+/* ------ Parser interface ------ */
+
+/*
+ * There are a few comments that we do not want to send to Russell's
+ * DSC parser. If we send the data block type comments, Russell's
+ * parser will want to skip the specified block of data. This is not
+ * appropriate for our situation. So we use this list to check for this
+ * type of comment and do not send it to Russell's parser if found.
+ */
+static const char * const BadCmdlist[] = {
+ "%%BeginData:",
+ "%%EndData",
+ "%%BeginBinary:",
+ "%%EndBinary",
+ NULL /* List terminator */
+};
+
+/* See comments at start of module for description. */
+/* <dict> <string> .parse_dsc_comments <dict> <dsc code> */
+static int
+zparse_dsc_comments(i_ctx_t *i_ctx_p)
+{
+#define MAX_DSC_MSG_SIZE (DSC_LINE_LENGTH + 4) /* Allow for %% and CR/LF */
+ os_ptr const opString = osp;
+ os_ptr const opDict = opString - 1;
+ uint ssize;
+ int comment_code, code;
+ char dsc_buffer[MAX_DSC_MSG_SIZE + 2];
+ const cmdlist_t *pCmdList = DSCcmdlist;
+ const char * const *pBadList = BadCmdlist;
+ ref * pvalue;
+ dsc_data_t * dsc_state = NULL;
+ dict_param_list list;
+
+ /*
+ * Verify operand types and length of DSC comment string. If a comment
+ * is too long then we simply truncate it. Russell's parser gets to
+ * handle any errors that may result. (Crude handling but the comment
+ * is bad, so ...).
+ */
+ check_type(*opString, t_string);
+ check_dict_write(*opDict);
+ ssize = r_size(opString);
+ if (ssize > MAX_DSC_MSG_SIZE) /* need room for EOL + \0 */
+ ssize = MAX_DSC_MSG_SIZE;
+ /*
+ * Retrieve our state.
+ */
+ code = dict_find_string(opDict, dsc_dict_name, &pvalue);
+ if (code < 0)
+ return code;
+ dsc_state = r_ptr(pvalue, dsc_data_t);
+ /*
+ * Pick up the comment string to be parsed.
+ */
+ memcpy(dsc_buffer, opString->value.bytes, ssize);
+ dsc_buffer[ssize] = 0x0d; /* Russell wants a 'line end' */
+ dsc_buffer[ssize + 1] = 0; /* Terminate string */
+ /*
+ * Skip data block comments (see comments in front of BadCmdList).
+ */
+ while (*pBadList && strncmp(*pBadList, dsc_buffer, strlen(*pBadList)))
+ pBadList++;
+ if (*pBadList) { /* If found in list, then skip comment */
+ comment_code = 0; /* Ignore */
+ if (dsc_buffer[2] == 'B') {
+ dsc_state->document_level++;
+ } else if (dsc_state->document_level > 0) {
+ dsc_state->document_level--;
+ }
+ }
+ else if (dsc_state->document_level > 0) {
+ comment_code = 0; /* Ignore */
+ } else {
+ /*
+ * Parse comments - use Russell Lang's DSC parser. We need to get
+ * data area for Russell Lang's parser. Note: We have saved the
+ * location of the data area for the parser in our DSC dict.
+ */
+ comment_code = dsc_scan_data(dsc_state->dsc_data_ptr, dsc_buffer, ssize + 1);
+ if_debug1m('%', imemory, "[%%].parse_dsc_comments: code = %d\n", comment_code);
+ /*
+ * We ignore any errors from Russell's parser. The only value that
+ * it will return for an error is -1 so there is very little information.
+ * We also do not want bad DSC comments to abort processing of an
+ * otherwise valid PS file.
+ */
+ if (comment_code < 0)
+ comment_code = 0;
+ }
+ /*
+ * Transfer data from DSC structure to postscript variables.
+ * Look up proper handler in the local cmd decode list.
+ */
+ while (pCmdList->code && pCmdList->code != comment_code )
+ pCmdList++;
+ if (pCmdList->dsc_proc) {
+ code = dict_param_list_write(&list, opDict, NULL, iimemory);
+ if (code < 0)
+ return code;
+ code = (pCmdList->dsc_proc)((gs_param_list *)&list, dsc_state->dsc_data_ptr);
+ iparam_list_release(&list);
+ if (code < 0)
+ return code;
+ }
+
+ /* Put DSC comment name onto operand stack (replace string). */
+
+ return name_enter_string(imemory, pCmdList->comment_name, opString);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zdscpars_op_defs[] = {
+ {"1.initialize_dsc_parser", zinitialize_dsc_parser},
+ {"2.parse_dsc_comments", zparse_dsc_comments},
+ op_def_end(0)
+};
diff --git a/psi/zfaes.c b/psi/zfaes.c
new file mode 100644
index 000000000..ef97957e5
--- /dev/null
+++ b/psi/zfaes.c
@@ -0,0 +1,72 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/* this is the ps interpreter interface to the AES cipher filter
+ used in PDF encryption. We currently provide only decode support. */
+
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "ifilter.h"
+#include "saes.h"
+
+/* <source> <dict> aes/filter <file> */
+
+static int
+z_aes_d(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp; /* i_ctx_p->op_stack.stack.p defined in osstack.h */
+ ref *sop = NULL;
+ stream_aes_state state;
+ int use_padding;
+
+ /* extract the key from the parameter dictionary */
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if (dict_find_string(op, "Key", &sop) <= 0)
+ return_error(gs_error_rangecheck);
+
+ s_aes_set_key(&state, sop->value.const_bytes, r_size(sop));
+
+ /* extract the padding flag, which defaults to true for compatibility */
+ if (dict_bool_param(op, "Padding", 1, &use_padding) < 0)
+ return_error(gs_error_rangecheck);
+
+ s_aes_set_padding(&state, use_padding);
+
+ /* we pass npop=0, since we've no arguments left to consume */
+ /* FIXME: passing 0 instead of the usual rspace(sop) will allocate
+ storage for filter state from the same memory pool as the stream
+ it's coding. this caused no trouble when we were the arcfour cipher
+ and maintained no pointers. */
+ return filter_read(i_ctx_p, 0, &s_aes_template,
+ (stream_state *) & state, 0);
+}
+
+/* Match the above routine to its postscript filter name.
+ This is how our static routines get called externally. */
+const op_def zfaes_op_defs[] = {
+ op_def_begin_filter(),
+ {"2AESDecode", z_aes_d},
+ op_def_end(0)
+};
diff --git a/psi/zfapi.c b/psi/zfapi.c
new file mode 100644
index 000000000..8c06287c7
--- /dev/null
+++ b/psi/zfapi.c
@@ -0,0 +1,2453 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Font API client */
+
+#include "stdlib.h" /* abs() */
+
+#include "memory_.h"
+#include "math_.h"
+#include "stat_.h" /* include before definition of esp macro, bug 691123 */
+#include "string_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "oper.h"
+#include "gxdevice.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "gxchar.h"
+#include "gzpath.h"
+#include "gxpath.h"
+#include "gxfcache.h"
+#include "gxchrout.h"
+#include "gximask.h"
+#include "gscoord.h"
+#include "gspaint.h"
+#include "gsfont.h"
+#include "gspath.h"
+#include "bfont.h"
+#include "dstack.h"
+#include "estack.h"
+#include "ichar.h"
+#include "idict.h"
+#include "iname.h"
+#include "ifont.h"
+#include "icid.h"
+#include "igstate.h"
+#include "icharout.h"
+
+#include "ifapi.h"
+#include "iplugin.h"
+#include "store.h"
+#include "gzstate.h"
+/* #include "gdevpsf.h" */
+#include "stream.h" /* for files.h */
+#include "gscrypt1.h"
+#include "gxfcid.h"
+#include "gsstype.h"
+#include "gxchar.h" /* for st_gs_show_enum */
+#include "ipacked.h" /* for packed_next */
+#include "iddict.h"
+#include "ifont42.h" /* for string_array_access_proc */
+#include "gdebug.h"
+#include "gsimage.h"
+#include "gxcldev.h"
+#include "gxdevmem.h"
+
+#include "gxfapi.h"
+
+/* -------------------------------------------------------- */
+
+typedef struct sfnts_reader_s sfnts_reader;
+struct sfnts_reader_s
+{
+ ref *sfnts;
+ const gs_memory_t *memory;
+ const byte *p;
+ long index;
+ uint offset;
+ uint length;
+ bool error;
+ byte(*rbyte) (sfnts_reader *r);
+ ushort(*rword) (sfnts_reader *r);
+ ulong(*rlong) (sfnts_reader *r);
+ int (*rstring) (sfnts_reader *r, byte *v, int length);
+ void (*seek) (sfnts_reader *r, ulong pos);
+};
+
+static void
+sfnts_next_elem(sfnts_reader *r)
+{
+ ref s;
+ int code;
+
+ if (r->error)
+ return;
+ do {
+ r->index++;
+ code = array_get(r->memory, r->sfnts, r->index, &s);
+ if (code == gs_error_rangecheck) {
+ r->error |= 2;
+ }
+ else if (code < 0) {
+ r->error |= 1;
+ }
+ if (r->error)
+ return;
+ r->p = s.value.const_bytes;
+ r->length = r_size(&s) & ~(uint) 1; /* See Adobe Technical Note # 5012, section 4.2. */
+ } while (r->length == 0);
+ r->offset = 0;
+}
+
+static inline byte
+sfnts_reader_rbyte_inline(sfnts_reader *r)
+{
+ if (r->offset >= r->length)
+ sfnts_next_elem(r);
+ return (r->error ? 0 : r->p[r->offset++]);
+}
+
+static byte
+sfnts_reader_rbyte(sfnts_reader *r)
+{ /* old compiler compatibility */
+ return (sfnts_reader_rbyte_inline(r));
+}
+
+static ushort
+sfnts_reader_rword(sfnts_reader *r)
+{
+ return ((sfnts_reader_rbyte_inline(r) << 8) +
+ sfnts_reader_rbyte_inline(r));
+}
+
+static ulong
+sfnts_reader_rlong(sfnts_reader *r)
+{
+ return ((sfnts_reader_rbyte_inline(r) << 24) +
+ (sfnts_reader_rbyte_inline(r) << 16) +
+ (sfnts_reader_rbyte_inline(r) << 8) +
+ sfnts_reader_rbyte_inline(r));
+}
+
+static int
+sfnts_reader_rstring(sfnts_reader *r, byte *v, int length)
+{
+ int rlength = length;
+
+ if (length <= 0)
+ return (0);
+ while (!r->error) {
+ int l = min(length, r->length - r->offset);
+
+ memcpy(v, r->p + r->offset, l);
+ length -= l;
+ r->offset += l;
+ if (length <= 0)
+ return (rlength);
+ v += l;
+ sfnts_next_elem(r);
+ }
+ return (rlength - length);
+}
+
+static void
+sfnts_reader_seek(sfnts_reader *r, ulong pos)
+{ /* fixme : optimize */
+ ulong skipped = 0;
+
+ r->index = -1;
+ sfnts_next_elem(r);
+ while (skipped + r->length < pos && !r->error) {
+ skipped += r->length;
+ sfnts_next_elem(r);
+ }
+ r->offset = pos - skipped;
+}
+
+static void
+sfnts_reader_init(sfnts_reader *r, ref *pdr)
+{
+ r->rbyte = sfnts_reader_rbyte;
+ r->rword = sfnts_reader_rword;
+ r->rlong = sfnts_reader_rlong;
+ r->rstring = sfnts_reader_rstring;
+ r->seek = sfnts_reader_seek;
+ r->index = -1;
+ r->error = false;
+ if (r_type(pdr) != t_dictionary ||
+ dict_find_string(pdr, "sfnts", &r->sfnts) <= 0)
+ r->error = true;
+ sfnts_next_elem(r);
+}
+
+/* -------------------------------------------------------- */
+
+typedef struct sfnts_writer_s sfnts_writer;
+struct sfnts_writer_s
+{
+ byte *buf, *p;
+ int buf_size;
+ void (*wbyte) (sfnts_writer *w, byte v);
+ void (*wword) (sfnts_writer *w, ushort v);
+ void (*wlong) (sfnts_writer *w, ulong v);
+ void (*wstring) (sfnts_writer *w, byte *v, int length);
+};
+
+static void
+sfnts_writer_wbyte(sfnts_writer *w, byte v)
+{
+ if (w->buf + w->buf_size < w->p + 1)
+ return; /* safety */
+ w->p[0] = v;
+ w->p++;
+}
+
+static void
+sfnts_writer_wword(sfnts_writer *w, ushort v)
+{
+ if (w->buf + w->buf_size < w->p + 2)
+ return; /* safety */
+ w->p[0] = v / 256;
+ w->p[1] = v % 256;
+ w->p += 2;
+}
+
+static void
+sfnts_writer_wlong(sfnts_writer *w, ulong v)
+{
+ if (w->buf + w->buf_size < w->p + 4)
+ return; /* safety */
+ w->p[0] = v >> 24;
+ w->p[1] = (v >> 16) & 0xFF;
+ w->p[2] = (v >> 8) & 0xFF;
+ w->p[3] = v & 0xFF;
+ w->p += 4;
+}
+
+static void
+sfnts_writer_wstring(sfnts_writer *w, byte *v, int length)
+{
+ if (w->buf + w->buf_size < w->p + length)
+ return; /* safety */
+ memcpy(w->p, v, length);
+ w->p += length;
+}
+
+static const sfnts_writer sfnts_writer_stub = {
+ 0, 0, 0,
+ sfnts_writer_wbyte,
+ sfnts_writer_wword,
+ sfnts_writer_wlong,
+ sfnts_writer_wstring
+};
+
+/* -------------------------------------------------------- */
+
+static inline bool
+sfnts_need_copy_table(byte *tag)
+{
+ return (memcmp(tag, "glyf", 4) && memcmp(tag, "glyx", 4) && /* Presents in files created by AdobePS5.dll Version 5.1.2 */
+ memcmp(tag, "loca", 4) && memcmp(tag, "locx", 4) && /* Presents in files created by AdobePS5.dll Version 5.1.2 */
+ memcmp(tag, "cmap", 4));
+}
+
+static void
+sfnt_copy_table(sfnts_reader *r, sfnts_writer *w, int length)
+{
+ byte buf[1024];
+
+ while (length > 0 && !r->error) {
+ int l = min(length, sizeof(buf));
+
+ (void)r->rstring(r, buf, l);
+ w->wstring(w, buf, l);
+ length -= l;
+ }
+}
+
+static ulong
+sfnts_copy_except_glyf(sfnts_reader *r, sfnts_writer *w)
+{ /* Note : TTC is not supported and probably is unuseful for Type 42. */
+ /* This skips glyf, loca and cmap from copying. */
+ struct
+ {
+ byte tag[4];
+ ulong checkSum, offset, offset_new, length;
+ } tables[30];
+ const ushort alignment = 4; /* Not sure, maybe 2 */
+ ulong version = r->rlong(r);
+ ushort num_tables = r->rword(r);
+ ushort i, num_tables_new = 0;
+ ushort searchRange, entrySelector = 0, rangeShift, v;
+ ulong size_new = 12;
+
+ r->rword(r); /* searchRange */
+ r->rword(r); /* entrySelector */
+ r->rword(r); /* rangeShift */
+ for (i = 0; i < num_tables; i++) {
+ if (r->error)
+ return 0;
+ (void)r->rstring(r, tables[i].tag, 4);
+ tables[i].checkSum = r->rlong(r);
+ tables[i].offset = r->rlong(r);
+ tables[i].length = r->rlong(r);
+ tables[i].offset_new = size_new;
+ if (sfnts_need_copy_table(tables[i].tag)) {
+ num_tables_new++;
+ size_new +=
+ (tables[i].length + alignment - 1) / alignment * alignment;
+ }
+ }
+ size_new += num_tables_new * 16;
+ if (w == 0)
+ return size_new;
+
+ searchRange = v = num_tables_new * 16;
+ for (i = 0; v; i++) {
+ v >>= 1;
+ searchRange |= v;
+ entrySelector++;
+ }
+ searchRange -= searchRange >> 1;
+ rangeShift = num_tables_new * 16 - searchRange;
+
+ w->wlong(w, version);
+ w->wword(w, num_tables_new);
+ w->wword(w, searchRange);
+ w->wword(w, entrySelector);
+ w->wword(w, rangeShift);
+ for (i = 0; i < num_tables; i++) {
+ if (sfnts_need_copy_table(tables[i].tag)) {
+ w->wstring(w, tables[i].tag, 4);
+ w->wlong(w, tables[i].checkSum);
+ w->wlong(w, tables[i].offset_new + num_tables_new * 16);
+ w->wlong(w, tables[i].length);
+ }
+ }
+ for (i = 0; i < num_tables; i++) {
+ if (sfnts_need_copy_table(tables[i].tag)) {
+ int k = tables[i].length;
+
+ r->seek(r, tables[i].offset);
+ if (r->error)
+ return 0;
+ if (w->p - w->buf != tables[i].offset_new + num_tables_new * 16)
+ return 0; /* the algorithm consistency check */
+ sfnt_copy_table(r, w, tables[i].length);
+ for (; k & (alignment - 1); k++)
+ w->wbyte(w, 0);
+ }
+ }
+ return (size_new);
+}
+
+static ulong
+true_type_size(ref *pdr)
+{
+ sfnts_reader r;
+
+ sfnts_reader_init(&r, pdr);
+ return (sfnts_copy_except_glyf(&r, 0));
+}
+
+static ushort
+FAPI_FF_serialize_tt_font(gs_fapi_font *ff, void *buf, int buf_size)
+{
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+ sfnts_reader r;
+ sfnts_writer w = sfnts_writer_stub;
+
+ w.buf_size = buf_size;
+ w.buf = w.p = buf;
+ sfnts_reader_init(&r, pdr);
+ if (!sfnts_copy_except_glyf(&r, &w))
+ return (1);
+ return (r.error);
+}
+
+static inline ushort
+float_to_ushort(float v)
+{
+ return ((ushort) (v * 16)); /* fixme : the scale may depend on renderer */
+}
+
+static ushort
+FAPI_FF_get_word(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index)
+{
+ gs_font_type1 *pfont = (gs_font_type1 *) ff->client_font_data;
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+
+ switch ((int)var_id) {
+ case gs_fapi_font_feature_Weight:
+ return 0; /* wrong */
+ case gs_fapi_font_feature_ItalicAngle:
+ return 0; /* wrong */
+ case gs_fapi_font_feature_IsFixedPitch:
+ return 0; /* wrong */
+ case gs_fapi_font_feature_UnderLinePosition:
+ return 0; /* wrong */
+ case gs_fapi_font_feature_UnderlineThickness:
+ return 0; /* wrong */
+ case gs_fapi_font_feature_FontType:
+ return (pfont->FontType == 2 ? 2 : 1);
+ case gs_fapi_font_feature_FontBBox:
+ switch (index) {
+ case 0:
+ return ((ushort) pfont->FontBBox.p.x);
+ case 1:
+ return ((ushort) pfont->FontBBox.p.y);
+ case 2:
+ return ((ushort) pfont->FontBBox.q.x);
+ case 3:
+ return ((ushort) pfont->FontBBox.q.y);
+ }
+ return 0;
+ case gs_fapi_font_feature_BlueValues_count:
+ return (pfont->data.BlueValues.count);
+ case gs_fapi_font_feature_BlueValues:
+ return (float_to_ushort(pfont->data.BlueValues.values[index]));
+ case gs_fapi_font_feature_OtherBlues_count:
+ return (pfont->data.OtherBlues.count);
+ case gs_fapi_font_feature_OtherBlues:
+ return (float_to_ushort(pfont->data.OtherBlues.values[index]));
+ case gs_fapi_font_feature_FamilyBlues_count:
+ return (pfont->data.FamilyBlues.count);
+ case gs_fapi_font_feature_FamilyBlues:
+ return (float_to_ushort(pfont->data.FamilyBlues.values[index]));
+ case gs_fapi_font_feature_FamilyOtherBlues_count:
+ return (pfont->data.FamilyOtherBlues.count);
+ case gs_fapi_font_feature_FamilyOtherBlues:
+ return (float_to_ushort
+ (pfont->data.FamilyOtherBlues.values[index]));
+ case gs_fapi_font_feature_BlueShift:
+ return (float_to_ushort(pfont->data.BlueShift));
+ case gs_fapi_font_feature_BlueFuzz:
+ return (float_to_ushort(pfont->data.BlueShift));
+ case gs_fapi_font_feature_StdHW:
+ return (pfont->data.StdHW.count == 0 ? 0 : float_to_ushort(pfont->data.StdHW.values[0])); /* UFST bug ? */
+ case gs_fapi_font_feature_StdVW:
+ return (pfont->data.StdVW.count == 0 ? 0 : float_to_ushort(pfont->data.StdVW.values[0])); /* UFST bug ? */
+ case gs_fapi_font_feature_StemSnapH_count:
+ return (pfont->data.StemSnapH.count);
+ case gs_fapi_font_feature_StemSnapH:
+ return (float_to_ushort(pfont->data.StemSnapH.values[index]));
+ case gs_fapi_font_feature_StemSnapV_count:
+ return (pfont->data.StemSnapV.count);
+ case gs_fapi_font_feature_StemSnapV:
+ return (float_to_ushort(pfont->data.StemSnapV.values[index]));
+ case gs_fapi_font_feature_ForceBold:
+ return (pfont->data.ForceBold);
+ case gs_fapi_font_feature_LanguageGroup:
+ return (pfont->data.LanguageGroup);
+ case gs_fapi_font_feature_lenIV:
+ return (ff->need_decrypt ? 0 : pfont->data.lenIV);
+ case gs_fapi_font_feature_GlobalSubrs_count:
+ {
+ ref *Private, *GlobalSubrs;
+
+ if (pfont->FontType == ft_encrypted2) {
+ if (dict_find_string(pdr, "Private", &Private) <= 0)
+ return 0;
+ if (dict_find_string(Private, "GlobalSubrs", &GlobalSubrs)
+ <= 0)
+ return 0;;
+ return (r_size(GlobalSubrs));
+ }
+ /* Since we don't have an error return capability, use as unlikely a value as possible */
+ return (65535);
+ }
+ case gs_fapi_font_feature_Subrs_count:
+ {
+ ref *Private, *Subrs;
+
+ if (dict_find_string(pdr, "Private", &Private) <= 0)
+ return 0;
+ if (dict_find_string(Private, "Subrs", &Subrs) <= 0)
+ return 0;
+ return (r_size(Subrs));
+ }
+ case gs_fapi_font_feature_CharStrings_count:
+ {
+ ref *CharStrings;
+
+ if (dict_find_string(pdr, "CharStrings", &CharStrings) <= 0)
+ return 0;
+ return (dict_length(CharStrings));
+ }
+ /* Multiple Master specific */
+ case gs_fapi_font_feature_DollarBlend:
+ {
+ ref *DBlend;
+
+ if (dict_find_string(pdr, "$Blend", &DBlend) <= 0)
+ return 0;
+ return 1;
+ }
+ case gs_fapi_font_feature_BlendAxisTypes_count:
+ {
+ ref *Info, *Axes;
+
+ if (dict_find_string(pdr, "FontInfo", &Info) <= 0)
+ return 0;
+ if (dict_find_string(Info, "BlendAxisTypes", &Axes) <= 0)
+ return 0;
+ return (r_size(Axes));
+ }
+ case gs_fapi_font_feature_BlendFontInfo_count:
+ {
+ ref *Info, *FontInfo;
+
+ if (dict_find_string(pdr, "Blend", &Info) <= 0)
+ return 0;
+ if (dict_find_string(Info, "FontInfo", &FontInfo) <= 0)
+ return 0;
+ return (dict_length(FontInfo));
+ }
+ case gs_fapi_font_feature_BlendPrivate_count:
+ {
+ ref *Info, *Private;
+
+ if (dict_find_string(pdr, "Blend", &Info) <= 0)
+ return 0;
+ if (dict_find_string(Info, "Private", &Private) <= 0)
+ return 0;
+ return (dict_length(Private));
+ }
+ case gs_fapi_font_feature_WeightVector_count:
+ {
+ ref *Array;
+
+ if (dict_find_string(pdr, "WeightVector", &Array) <= 0)
+ return 0;
+ return (r_size(Array));
+ }
+ case gs_fapi_font_feature_BlendDesignPositionsArrays_count:
+ {
+ ref *Info, *Array;
+
+ if (dict_find_string(pdr, "FontInfo", &Info) <= 0)
+ return 0;
+ if (dict_find_string(Info, "BlendDesignPositions", &Array) <=
+ 0)
+ return 0;
+ return (r_size(Array));
+ }
+ case gs_fapi_font_feature_BlendDesignMapArrays_count:
+ {
+ ref *Info, *Array;
+
+ if (dict_find_string(pdr, "FontInfo", &Info) <= 0)
+ return 0;
+ if (dict_find_string(Info, "BlendDesignMap", &Array) <= 0)
+ return 0;
+ return (r_size(Array));
+ }
+ case gs_fapi_font_feature_BlendDesignMapSubArrays_count:
+ {
+ ref *Info, *Array, SubArray;
+
+ if (dict_find_string(pdr, "FontInfo", &Info) <= 0)
+ return 0;
+ if (dict_find_string(Info, "BlendDesignMap", &Array) <= 0)
+ return 0;
+ if (array_get(ff->memory, Array, index, &SubArray) < 0)
+ return 0;
+ return (r_size(&SubArray));
+ }
+ case gs_fapi_font_feature_DollarBlend_length:
+ {
+ ref *DBlend, Element, string;
+ int i, length = 0;
+ char Buffer[32];
+
+ if (dict_find_string(pdr, "$Blend", &DBlend) <= 0)
+ return 0;
+ for (i = 0; i < r_size(DBlend); i++) {
+ if (array_get(ff->memory, DBlend, i, &Element) < 0)
+ return 0;
+ switch (r_btype(&Element)) {
+ case t_name:
+ name_string_ref(ff->memory, &Element, &string);
+ length += r_size(&string) + 1;
+ break;
+ case t_real:
+ gs_sprintf(Buffer, "%f", Element.value.realval);
+ length += strlen(Buffer) + 1;
+ break;
+ case t_integer:
+ gs_sprintf(Buffer, "%"PRIpsint, Element.value.intval);
+ length += strlen(Buffer) + 1;
+ break;
+ case t_operator:
+ {
+ op_def const *op;
+
+ op = op_index_def(r_size(&Element));
+ length += strlen(op->oname + 1) + 1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return length;
+ }
+ /* End MM specifics */
+ }
+ return 0;
+}
+
+static ulong
+FAPI_FF_get_long(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index)
+{
+ gs_font_type1 *pfont = (gs_font_type1 *) ff->client_font_data;
+
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+
+ switch ((int)var_id) {
+ case gs_fapi_font_feature_UniqueID:
+ return (pfont->UID.id);
+ case gs_fapi_font_feature_BlueScale:
+ return ((ulong) (pfont->data.BlueScale * 65536));
+ case gs_fapi_font_feature_Subrs_total_size:
+ {
+ ref *Private, *Subrs, v;
+ int lenIV = max(pfont->data.lenIV, 0), k;
+ ulong size = 0;
+ long i;
+ const char *name[2] = { "Subrs", "GlobalSubrs" };
+ if (dict_find_string(pdr, "Private", &Private) <= 0)
+ return 0;
+ for (k = 0; k < 2; k++) {
+ if (dict_find_string(Private, name[k], &Subrs) > 0)
+ for (i = r_size(Subrs) - 1; i >= 0; i--) {
+ array_get(pfont->memory, Subrs, i, &v);
+ if (r_type(&v) == t_string) {
+ size += r_size(&v) - (ff->need_decrypt ? 0 : lenIV);
+ }
+ }
+ }
+ return size;
+ }
+ case gs_fapi_font_feature_TT_size:
+ return (true_type_size(pdr));
+ }
+ return 0;
+}
+
+static float
+FAPI_FF_get_float(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index)
+{
+ gs_font_base *pbfont = (gs_font_base *) ff->client_font_data2;
+ ref *pdr = pfont_dict(pbfont);
+
+ gs_fapi_server *I = pbfont->FAPI;
+
+ switch ((int)var_id) {
+ case gs_fapi_font_feature_FontMatrix:
+ {
+ double FontMatrix_div;
+ gs_matrix m, *mptr;
+
+ if (I && I->get_fontmatrix) {
+ FontMatrix_div = 1;
+ mptr = &m;
+ I->get_fontmatrix(I, mptr);
+ }
+ else {
+ FontMatrix_div =
+ ((ff->is_cid
+ && (!FAPI_ISCIDFONT(pbfont))) ? 1000 : 1);
+ mptr = &(pbfont->base->FontMatrix);
+ }
+ switch (index) {
+ case 0:
+ return (mptr->xx / FontMatrix_div);
+ case 1:
+ return (mptr->xy / FontMatrix_div);
+ case 2:
+ return (mptr->yx / FontMatrix_div);
+ case 3:
+ return (mptr->yy / FontMatrix_div);
+ case 4:
+ return (mptr->tx / FontMatrix_div);
+ case 5:
+ return (mptr->ty / FontMatrix_div);
+ }
+ }
+
+ case gs_fapi_font_feature_WeightVector:
+ {
+ ref *Array, value;
+
+ if (dict_find_string(pdr, "WeightVector", &Array) <= 0)
+ return 0;
+ if (array_get(ff->memory, Array, index, &value) < 0)
+ return 0;
+ if (!r_has_type(&value, t_integer)) {
+ if (r_has_type(&value, t_real)) {
+ return (value.value.realval);
+ }
+ else
+ return 0;
+ }
+ else
+ return ((float)value.value.intval);
+ }
+ case gs_fapi_font_feature_BlendDesignPositionsArrayValue:
+ {
+ ref *Info, *Array, SubArray, value;
+ int array_index = index / 8;
+
+ index %= 8;
+ if (dict_find_string(pdr, "FontInfo", &Info) <= 0)
+ return 0;
+ if (dict_find_string(Info, "BlendDesignPositions", &Array) <=
+ 0)
+ return 0;
+ if (array_get(ff->memory, Array, array_index, &SubArray) < 0)
+ return 0;
+ if (array_get(ff->memory, &SubArray, index, &value) < 0)
+ return 0;
+ if (!r_has_type(&value, t_integer)) {
+ if (r_has_type(&value, t_real)) {
+ return (value.value.realval);
+ }
+ else
+ return 0;
+ }
+ else
+ return ((float)value.value.intval);
+ }
+ case gs_fapi_font_feature_BlendDesignMapArrayValue:
+ {
+ ref *Info, *Array, SubArray, SubSubArray, value;
+ int array_index = index / 64;
+
+ index %= 8;
+ if (dict_find_string(pdr, "FontInfo", &Info) <= 0)
+ return 0;
+ if (dict_find_string(Info, "BlendDesignMap", &Array) <= 0)
+ return 0;
+ if (array_get(ff->memory, Array, array_index, &SubArray) < 0)
+ return 0;
+ if (array_get(ff->memory, &SubArray, index, &SubSubArray) < 0)
+ return 0;
+ if (array_get(ff->memory, &SubSubArray, index, &value) < 0)
+ return 0;
+ if (!r_has_type(&value, t_integer)) {
+ if (r_has_type(&value, t_real)) {
+ return (value.value.realval);
+ }
+ else
+ return 0;
+ }
+ else
+ return ((float)value.value.intval);
+ }
+ }
+ return 0;
+}
+
+static int
+FAPI_FF_get_name(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index,
+ char *Buffer, int len)
+{
+ ref name, string;
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+
+ switch ((int)var_id) {
+ case gs_fapi_font_feature_BlendAxisTypes:
+ {
+ ref *Info, *Axes;
+
+ if (dict_find_string(pdr, "FontInfo", &Info) <= 0)
+ return 0;
+ if (dict_find_string(Info, "BlendAxisTypes", &Axes) <= 0)
+ return 0;
+ if (!r_has_type(Axes, t_array))
+ return 0;
+ if (array_get(ff->memory, Axes, index, &name) < 0)
+ return 0;
+ }
+ }
+ name_string_ref(ff->memory, &name, &string);
+ if (r_size(&string) >= len)
+ return 0;
+ memcpy(Buffer, string.value.const_bytes, r_size(&string));
+ Buffer[r_size(&string)] = 0x00;
+ return 1;
+}
+
+static int
+FAPI_FF_get_proc(gs_fapi_font *ff, gs_fapi_font_feature var_id, int index,
+ char *Buffer)
+{
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+ char *ptr = Buffer;
+
+ if (!Buffer)
+ return 0;
+
+ switch ((int)var_id) {
+ case gs_fapi_font_feature_DollarBlend:
+ {
+ ref *DBlend, Element, string;
+ int i;
+ char Buf[32];
+
+ if (dict_find_string(pdr, "$Blend", &DBlend) <= 0)
+ return 0;
+ for (i = 0; i < r_size(DBlend); i++) {
+ *ptr++ = 0x20;
+ if (array_get(ff->memory, DBlend, i, &Element) < 0)
+ return 0;
+ switch (r_btype(&Element)) {
+ case t_name:
+ name_string_ref(ff->memory, &Element, &string);
+
+ strncpy(ptr, (char *)string.value.const_bytes,
+ r_size(&string));
+ ptr += r_size(&string);
+ break;
+ case t_real:
+ gs_sprintf(Buf, "%f", Element.value.realval);
+ strcpy(ptr, Buf);
+ ptr += strlen(Buf);
+ break;
+ case t_integer:
+ gs_sprintf(Buf, "%"PRIpsint, Element.value.intval);
+ strcpy(ptr, Buf);
+ ptr += strlen(Buf);
+ break;
+ case t_operator:
+ {
+ op_def const *op;
+
+ op = op_index_def(r_size(&Element));
+ strcpy(ptr, op->oname + 1);
+ ptr += strlen(op->oname + 1);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return (ptr - Buffer);
+}
+
+static inline void
+decode_bytes(byte *p, const byte *s, int l, int lenIV)
+{
+ ushort state = 4330;
+
+ for (; l; s++, l--) {
+ uchar c = (*s ^ (state >> 8));
+
+ state = (*s + state) * crypt_c1 + crypt_c2;
+ if (lenIV > 0)
+ lenIV--;
+ else {
+ *p = c;
+ p++;
+ }
+ }
+}
+
+static ushort
+get_type1_data(gs_fapi_font *ff, const ref *type1string,
+ byte *buf, ushort buf_length)
+{
+ gs_font_type1 *pfont = (gs_font_type1 *) ff->client_font_data;
+ int lenIV = max(pfont->data.lenIV, 0);
+ int length = r_size(type1string) - (ff->need_decrypt ? lenIV : 0);
+
+ if (buf != 0) {
+ int l = min(length, buf_length); /*safety */
+
+ if (ff->need_decrypt && pfont->data.lenIV >= 0)
+ decode_bytes(buf, type1string->value.const_bytes, l + lenIV,
+ lenIV);
+ else
+ memcpy(buf, type1string->value.const_bytes, l);
+ }
+ return length;
+}
+
+static ushort
+FAPI_FF_get_gsubr(gs_fapi_font *ff, int index, byte *buf, ushort buf_length)
+{
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+ ref *Private, *GlobalSubrs, subr;
+
+ if (dict_find_string(pdr, "Private", &Private) <= 0)
+ return 0;
+ if (dict_find_string(Private, "GlobalSubrs", &GlobalSubrs) <= 0)
+ return 0;
+ if (array_get(ff->memory,
+ GlobalSubrs, index, &subr) < 0 || r_type(&subr) != t_string)
+ return 0;
+ return (get_type1_data(ff, &subr, buf, buf_length));
+}
+
+static ushort
+FAPI_FF_get_subr(gs_fapi_font *ff, int index, byte *buf, ushort buf_length)
+{
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+ ref *Private, *Subrs, subr;
+
+ if (dict_find_string(pdr, "Private", &Private) <= 0)
+ return 0;
+ if (dict_find_string(Private, "Subrs", &Subrs) <= 0)
+ return 0;
+ if (array_get(ff->memory, Subrs, index, &subr) < 0
+ || r_type(&subr) != t_string)
+ return 0;
+ return (get_type1_data(ff, &subr, buf, buf_length));
+}
+
+static ushort
+FAPI_FF_get_raw_subr(gs_fapi_font *ff, int index, byte *buf,
+ ushort buf_length)
+{
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+ ref *Private, *Subrs, subr;
+
+ if (dict_find_string(pdr, "Private", &Private) <= 0)
+ return 0;
+ if (dict_find_string(Private, "Subrs", &Subrs) <= 0)
+ return 0;
+ if (array_get(ff->memory, Subrs, index, &subr) < 0
+ || r_type(&subr) != t_string)
+ return 0;
+ if (buf && buf_length && buf_length >= r_size(&subr)) {
+ memcpy(buf, subr.value.const_bytes, r_size(&subr));
+ }
+ return (r_size(&subr));
+}
+
+static ushort
+FAPI_FF_get_charstring_name(gs_fapi_font *ff, int index, byte *buf,
+ ushort buf_length)
+{
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+ ref *CharStrings, eltp[2], string;
+
+ if (dict_find_string(pdr, "CharStrings", &CharStrings) <= 0)
+ return 0;
+ if (dict_index_entry(CharStrings, index, eltp) < 0)
+ return 0;
+ name_string_ref(ff->memory, &eltp[0], &string);
+ if (r_size(&string) > buf_length)
+ return (r_size(&string));
+ memcpy(buf, string.value.const_bytes, r_size(&string));
+ buf[r_size(&string)] = 0x00;
+ return (r_size(&string));
+}
+
+static ushort
+FAPI_FF_get_charstring(gs_fapi_font *ff, int index, byte *buf,
+ ushort buf_length)
+{
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+ ref *CharStrings, eltp[2];
+
+ if (dict_find_string(pdr, "CharStrings", &CharStrings) <= 0)
+ return 0;
+ if (dict_index_entry(CharStrings, index, eltp) < 0)
+ return 0;
+ if (buf && buf_length && buf_length >= r_size(&eltp[1])) {
+ memcpy(buf, eltp[1].value.const_bytes, r_size(&eltp[1]));
+ }
+ return (r_size(&eltp[1]));
+}
+
+static bool
+sfnt_get_glyph_offset(ref *pdr, gs_font_type42 *pfont42, int index,
+ ulong *offset0)
+{ /* Note : TTC is not supported and probably is unuseful for Type 42. */
+ sfnts_reader r;
+ int glyf_elem_size = (pfont42->data.indexToLocFormat) ? 4 : 2;
+
+ sfnts_reader_init(&r, pdr);
+ r.seek(&r, pfont42->data.loca + index * glyf_elem_size);
+ *offset0 =
+ pfont42->data.glyf + (glyf_elem_size ==
+ 2 ? r.rword(&r) * 2 : r.rlong(&r));
+ return (r.error);
+}
+
+static int
+ps_get_GlyphDirectory_data_ptr(gs_fapi_font *ff, int char_code,
+ const byte **ptr)
+{
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+ ref *GlyphDirectory, glyph0, *glyph = &glyph0, glyph_index;
+
+ if (dict_find_string(pdr, "GlyphDirectory", &GlyphDirectory) > 0) {
+ if (((r_type(GlyphDirectory) == t_dictionary &&
+ (make_int(&glyph_index, char_code),
+ dict_find(GlyphDirectory, &glyph_index, &glyph) > 0)) ||
+ (r_type(GlyphDirectory) == t_array &&
+ array_get(ff->memory, GlyphDirectory, char_code, &glyph0) >= 0)
+ )
+ && r_type(glyph) == t_string) {
+ *ptr = glyph->value.const_bytes;
+ return (r_size(glyph));
+ }
+ else
+ /* We have a GlyphDirectory, but couldn't find the glyph. If we
+ * return -1 then we will attempt to use glyf and loca which
+ * will fail. Instead return 0, so we execute an 'empty' glyph.
+ */
+ return 0;
+ }
+ return -1;
+}
+
+static int
+get_charstring(gs_fapi_font *ff, int char_code, ref **proc, ref *char_name)
+{
+ ref *CharStrings;
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+
+ if (ff->is_type1) {
+ if (ff->is_cid)
+ return -1;
+ if (dict_find_string(pdr, "CharStrings", &CharStrings) <= 0)
+ return -1;
+
+ if (ff->char_data != NULL) {
+ /*
+ * Can't use char_code in this case because hooked Type 1 fonts
+ * with 'glyphshow' may render a character which has no
+ * Encoding entry.
+ */
+ if (name_ref
+ (ff->memory, ff->char_data, ff->char_data_len, char_name,
+ -1) < 0)
+ return -1;
+ }
+ else { /* seac */
+ i_ctx_t *i_ctx_p = (i_ctx_t *) ff->client_ctx_p;
+ ref *StandardEncoding;
+
+ if (dict_find_string
+ (systemdict, "StandardEncoding", &StandardEncoding) <= 0
+ || array_get(ff->memory, StandardEncoding, char_code,
+ char_name) < 0) {
+ if (name_ref
+ (ff->memory, (const byte *)".notdef", 7, char_name,
+ -1) < 0)
+ return -1;
+ }
+ }
+ if (dict_find(CharStrings, char_name, (ref **) proc) <= 0)
+ return -1;
+ }
+ return 0;
+}
+
+static int
+FAPI_FF_get_glyph(gs_fapi_font *ff, int char_code, byte *buf,
+ ushort buf_length)
+{
+ /*
+ * We assume that renderer requests glyph data with multiple
+ * consecutive calls to this function.
+ *
+ * For a simple glyph it calls this function exactly twice: first
+ * with buf == NULL for requesting the necessary buffer length, and
+ * second with buf != NULL for requesting the data (the second call
+ * may be skipped if the renderer discontinues the rendering).
+ *
+ * For a composite glyph it calls this function 2 * (N + 1)
+ * times: 2 calls for the main glyph (same as above) followed with
+ * 2 * N calls for subglyphs, where N is less or equal to the number
+ * of subglyphs (N may be less if the renderer caches glyph data,
+ * or discontinues rendering on an exception).
+ */
+ ref *pdr = pfont_dict(((gs_font_base *) ff->client_font_data2));
+
+ ushort glyph_length;
+ i_ctx_t *i_ctx_p = (i_ctx_t *) ff->client_ctx_p;
+
+ if (ff->is_type1) {
+ if (ff->is_cid) {
+ const gs_string *char_str = (const gs_string *)ff->char_data;
+ ref glyph;
+
+ make_string(&glyph, avm_foreign | a_readonly, char_str->size,
+ char_str->data);
+
+ glyph_length = get_type1_data(ff, &glyph, buf, buf_length);
+ }
+ else {
+ ref *CharStrings, char_name, *glyph;
+
+ if (ff->char_data != NULL) {
+ /*
+ * Can't use char_code in this case because hooked Type 1 fonts
+ * with 'glyphshow' may render a character which has no
+ * Encoding entry.
+ */
+ if (name_ref(ff->memory, ff->char_data,
+ ff->char_data_len, &char_name, -1) < 0)
+ return -1;
+ if (buf != NULL) {
+ /*
+ * Trigger the next call to the 'seac' case below.
+ * Here we use the assumption about call sequence
+ * being documented above.
+ */
+ ff->char_data = NULL;
+ }
+ }
+ else { /* seac */
+ ref *StandardEncoding;
+
+ if (dict_find_string
+ (systemdict, "StandardEncoding", &StandardEncoding) <= 0
+ || array_get(ff->memory, StandardEncoding, char_code,
+ &char_name) < 0)
+ if (name_ref
+ (ff->memory, (const byte *)".notdef", 7, &char_name,
+ -1) < 0)
+ return -1;
+ }
+ if (dict_find_string(pdr, "CharStrings", &CharStrings) <= 0)
+ return -1;
+
+ if (dict_find(CharStrings, &char_name, &glyph) <= 0) {
+ if (name_ref
+ (ff->memory, (const byte *)".notdef", 7, &char_name,
+ -1) < 0) {
+ return -1;
+ }
+ if (dict_find(CharStrings, &char_name, &glyph) <= 0) {
+ return -1;
+ }
+ }
+ if (r_has_type(glyph, t_array) || r_has_type(glyph, t_mixedarray))
+ return -1;
+ glyph_length = get_type1_data(ff, glyph, buf, buf_length);
+ }
+ }
+ else { /* type 42 */
+ const byte *data_ptr;
+ int l = ff->get_glyphdirectory_data(ff, char_code, &data_ptr);
+ ref *render_notdef_ref;
+ bool render_notdef = true;
+
+ if (dict_find_string(pdr, ".render_notdef", &render_notdef_ref) > 0
+ && r_has_type(render_notdef_ref, t_boolean)) {
+ render_notdef = render_notdef_ref->value.boolval;
+ }
+ else {
+ render_notdef = i_ctx_p->RenderTTNotdef;
+ }
+
+ /* We should only render the TT notdef if we've been told to - logic lifted from zchar42.c */
+ if (!render_notdef
+ &&
+ ((ff->char_data_len == 7
+ && strncmp((const char *)ff->char_data, ".notdef", 7) == 0)
+ || (ff->char_data_len > 9
+ && strncmp((const char *)ff->char_data, ".notdef~GS",
+ 10) == 0))) {
+ glyph_length = 0;
+ }
+ else {
+ if (l >= 0) {
+ int MetricsCount = gs_fapi_get_metrics_count(ff), mc =
+ MetricsCount << 1;
+
+ glyph_length = max((ushort) (l - mc), 0); /* safety */
+ if (buf != 0 && glyph_length > 0)
+ memcpy(buf, data_ptr + mc,
+ min(glyph_length, buf_length) /* safety */ );
+ }
+ else {
+ gs_font_type42 *pfont42 =
+ (gs_font_type42 *) ff->client_font_data;
+ ulong offset0, length_read;
+ bool error =
+ sfnt_get_glyph_offset(pdr, pfont42, char_code, &offset0);
+
+ glyph_length =
+ (error ? -1 : pfont42->data.len_glyphs[char_code]);
+
+ if (buf != 0 && !error) {
+ sfnts_reader r;
+
+ sfnts_reader_init(&r, pdr);
+
+ r.seek(&r, offset0);
+ length_read =
+ r.rstring(&r, buf,
+ min(glyph_length,
+ buf_length) /* safety */ );
+ if (r.error == 1) {
+ glyph_length = -1;
+ }
+ /* r.error == 2 means a rangecheck, and probably means that the
+ * font is broken, and the final glyph length is longer than the data available for it.
+ * In which case we need to return the number of bytes read.
+ */
+ if (r.error == 2) {
+ glyph_length = length_read;
+ }
+ }
+ }
+ }
+ }
+ return glyph_length;
+}
+
+static int
+ps_fapi_get_metrics(gs_fapi_font *ff, gs_string *char_name, int cid,
+ double *m, bool vertical)
+{
+ ref glyph;
+ int code;
+ gs_font_base *pbfont = ((gs_font_base *) ff->client_font_data2);
+
+ if (char_name->data != NULL) {
+ make_string(&glyph, avm_foreign | a_readonly, char_name->size,
+ char_name->data);
+ }
+ else {
+ make_int(&glyph, cid);
+ }
+
+ if (vertical) {
+ code = zchar_get_metrics2(pbfont, &glyph, m);
+ }
+ else {
+ code = zchar_get_metrics(pbfont, &glyph, m);
+ }
+
+ make_null(&glyph);
+
+ return (code);
+}
+
+
+/* forward declaration for the ps_ff_stub assignment */
+static int ps_get_glyphname_or_cid(gs_font_base *pbfont,
+ gs_string *charstring, gs_string *name,
+ int ccode, gs_string *enc_char_name,
+ char *font_file_path,
+ gs_fapi_char_ref *cr, bool bCID);
+
+static int ps_fapi_set_cache(gs_text_enum_t *penum,
+ const gs_font_base *pbfont,
+ const gs_string *char_name, int cid,
+ const double pwidth[2], const gs_rect *pbbox,
+ const double Metrics2_sbw_default[4],
+ bool *imagenow);
+
+static const gs_fapi_font ps_ff_stub = {
+ 0, /* server_font_data */
+ 0, /* need_decrypt */
+ NULL, /* const gs_memory_t */
+ 0, /* font_file_path */
+ 0, /* full_font_buf */
+ 0, /* full_font_buf_len */
+ 0, /* subfont */
+ false, /* is_type1 */
+ false, /* is_cid */
+ false, /* is_outline_font */
+ false, /* is_mtx_skipped */
+ false, /* is_vertical */
+ false, /* metrics_only */
+ {{-1, -1}}, /* ttf_cmap_req */
+ 0, /* client_ctx_p */
+ 0, /* client_font_data */
+ 0, /* client_font_data2 */
+ 0, /* char_data */
+ 0, /* char_data_len */
+ 0, /* embolden */
+ FAPI_FF_get_word,
+ FAPI_FF_get_long,
+ FAPI_FF_get_float,
+ FAPI_FF_get_name,
+ FAPI_FF_get_proc,
+ FAPI_FF_get_gsubr,
+ FAPI_FF_get_subr,
+ FAPI_FF_get_raw_subr,
+ FAPI_FF_get_glyph,
+ FAPI_FF_serialize_tt_font,
+ FAPI_FF_get_charstring,
+ FAPI_FF_get_charstring_name,
+ ps_get_GlyphDirectory_data_ptr,
+ ps_get_glyphname_or_cid,
+ ps_fapi_get_metrics,
+ ps_fapi_set_cache
+};
+
+static int
+FAPI_get_xlatmap(i_ctx_t *i_ctx_p, char **xlatmap)
+{
+ ref *pref;
+ int code;
+
+ if ((code = dict_find_string(systemdict, ".xlatmap", &pref)) < 0)
+ return code;
+ if (r_type(pref) != t_string)
+ return_error(gs_error_typecheck);
+ *xlatmap = (char *)pref->value.bytes;
+ /* Note : this supposes that xlatmap doesn't move in virtual memory.
+ Garbager must not be called while plugin executes get_scaled_font, get_decodingID.
+ Fix some day with making copy of xlatmap in system memory.
+ */
+ return 0;
+}
+
+static int
+renderer_retcode(gs_memory_t *mem, gs_fapi_server *I, gs_fapi_retcode rc)
+{
+ if (rc == 0)
+ return 0;
+ emprintf2(mem,
+ "Error: Font Renderer Plugin ( %s ) return code = %d\n",
+ I->ig.d->subtype, rc);
+ return rc < 0 ? rc : gs_error_invalidfont;
+}
+
+/* <server name>/<null> object .FAPIavailable bool */
+static int
+zFAPIavailable(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ char *serv_name = NULL;
+ ref name_ref;
+
+ if (r_has_type(op, t_name)) {
+ name_string_ref(imemory, op, &name_ref);
+
+ serv_name =
+ (char *) ref_to_string(&name_ref, imemory, "zFAPIavailable");
+ if (!serv_name) {
+ return_error(gs_error_VMerror);
+ }
+ }
+
+ make_bool(op, gs_fapi_available(imemory, serv_name));
+
+ if (serv_name) {
+ gs_free_string(imemory, (byte *) serv_name,
+ strlen((char *)serv_name) + 1, "zFAPIavailable");
+ }
+ return (0);
+}
+
+static void
+ps_get_server_param(gs_fapi_server *I, const byte *subtype,
+ byte **server_param, int *server_param_size)
+{
+ ref *FAPIconfig, *options, *server_options;
+ i_ctx_t *i_ctx_p = (i_ctx_t *) I->client_ctx_p;
+
+ if (dict_find_string(systemdict, ".FAPIconfig", &FAPIconfig) >= 0
+ && r_has_type(FAPIconfig, t_dictionary)) {
+ if (dict_find_string(FAPIconfig, "ServerOptions", &options) >= 0
+ && r_has_type(options, t_dictionary)) {
+ if (dict_find_string(options, (char *)subtype, &server_options) >=
+ 0 && r_has_type(server_options, t_string)) {
+ *server_param = (byte *) server_options->value.const_bytes;
+ *server_param_size = r_size(server_options);
+ }
+ }
+ }
+}
+
+static int
+FAPI_refine_font(i_ctx_t *i_ctx_p, os_ptr op, gs_font *pfont,
+ int subfont, const char *font_file_path)
+{
+ ref *pdr = op; /* font dict */
+ const char *decodingID = NULL;
+ char *xlatmap = NULL;
+ gs_font_base *pbfont = (gs_font_base *)pfont;
+ gs_fapi_server *I = pbfont->FAPI;
+ ref *Decoding_old;
+ int code;
+
+ if (font_file_path != NULL && pbfont->FAPI_font_data == NULL)
+ if ((code = FAPI_get_xlatmap(i_ctx_p, &xlatmap)) < 0)
+ return code;
+
+ gs_fapi_set_servers_client_data(imemory, NULL, i_ctx_p);
+
+ code =
+ gs_fapi_prepare_font(pfont, I, subfont, font_file_path,
+ NULL, xlatmap, &decodingID);
+ if (code < 0)
+ return code;
+
+ if (code > 0) {
+ /* save refined FontBBox back to PS world */
+ ref *v, mat[4], arr;
+ int attrs;
+
+ if (dict_find_string(op, "FontBBox", &v) > 0) {
+ if (!r_has_type(v, t_array) && !r_has_type(v, t_shortarray)
+ && !r_has_type(v, t_mixedarray))
+ return_error(gs_error_invalidfont);
+ make_real(&mat[0], pbfont->FontBBox.p.x);
+ make_real(&mat[1], pbfont->FontBBox.p.y);
+ make_real(&mat[2], pbfont->FontBBox.q.x);
+ make_real(&mat[3], pbfont->FontBBox.q.y);
+ if (r_has_type(v, t_shortarray) || r_has_type(v, t_mixedarray)
+ || r_size(v) < 4) {
+ /* Create a new full blown array in case the values are reals */
+ code = ialloc_ref_array(&arr, a_all, 4, "array");
+ if (code < 0)
+ return code;
+ v = &arr;
+ code = idict_put_string(op, "FontBBox", &arr);
+ if (code < 0)
+ return code;
+ ref_assign_new(v->value.refs + 0, &mat[0]);
+ ref_assign_new(v->value.refs + 1, &mat[1]);
+ ref_assign_new(v->value.refs + 2, &mat[2]);
+ ref_assign_new(v->value.refs + 3, &mat[3]);
+ }
+ else {
+ ref_assign_old(v, v->value.refs + 0, &mat[0],
+ "FAPI_refine_font_BBox");
+ ref_assign_old(v, v->value.refs + 1, &mat[1],
+ "FAPI_refine_font_BBox");
+ ref_assign_old(v, v->value.refs + 2, &mat[2],
+ "FAPI_refine_font_BBox");
+ ref_assign_old(v, v->value.refs + 3, &mat[3],
+ "FAPI_refine_font_BBox");
+ }
+ attrs = v->tas.type_attrs;
+ r_clear_attrs(v, a_all);
+ r_set_attrs(v, attrs | a_execute);
+ }
+ }
+
+ /* Assign a Decoding : */
+ if (decodingID != 0 && *decodingID
+ && dict_find_string(pdr, "Decoding", &Decoding_old) <= 0) {
+ ref Decoding;
+
+ if (FAPI_ISCIDFONT(pbfont)) {
+ ref *CIDSystemInfo, *Ordering, SubstNWP;
+ byte buf[30];
+ int ordering_length, decodingID_length =
+ min(strlen(decodingID), sizeof(buf) - 2);
+
+ if (dict_find_string(pdr, "CIDSystemInfo", &CIDSystemInfo) <= 0
+ || !r_has_type(CIDSystemInfo, t_dictionary))
+ return_error(gs_error_invalidfont);
+
+ if (dict_find_string(CIDSystemInfo, "Ordering", &Ordering) <= 0
+ || !r_has_type(Ordering, t_string)) {
+ return_error(gs_error_invalidfont);
+ }
+
+ ordering_length =
+ min(r_size(Ordering), sizeof(buf) - 2 - decodingID_length);
+ memcpy(buf, Ordering->value.const_bytes, ordering_length);
+ if ((code =
+ name_ref(imemory, buf, ordering_length, &SubstNWP, 0)) < 0)
+ return code;
+ if ((code =
+ dict_put_string(pdr, "SubstNWP", &SubstNWP, NULL)) < 0)
+ return code;
+ buf[ordering_length] = '.';
+ memcpy(buf + ordering_length + 1, decodingID, decodingID_length);
+ buf[decodingID_length + 1 + ordering_length] = 0; /* Debug purpose only */
+ if ((code = name_ref(imemory, buf,
+ decodingID_length + 1 + ordering_length,
+ &Decoding, 0)) < 0)
+ return code;
+ }
+ else if ((code = name_ref(imemory, (const byte *)decodingID,
+ strlen(decodingID), &Decoding, 0)) < 0)
+ return code;
+ if ((code = dict_put_string(pdr, "Decoding", &Decoding, NULL)) < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* <string|name> <font> <is_disk_font> .rebuildfontFAPI <string|name> <font> */
+/* Rebuild a font for handling it with an external renderer.
+
+ The font was built as a native GS font to allow easy access
+ to font features. Then zFAPIrebuildfont sets FAPI entry
+ into gx_font_base and replaces BuildGlyph and BuildChar
+ to enforce the FAPI handling.
+
+ This operator must not be called with devices which embed fonts.
+
+*/
+static int
+zFAPIrebuildfont(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ build_proc_refs build;
+ gs_font *pfont;
+ int code = font_param(op - 1, &pfont);
+ gs_font_base *pbfont = (gs_font_base *) pfont;
+ ref *v;
+ char *font_file_path = NULL;
+ char FAPI_ID[20];
+ const byte *pchars;
+ uint len;
+ font_data *pdata;
+ gs_fapi_server *I;
+ bool has_buildglyph;
+ bool has_buildchar;
+ int subfont;
+
+ if (code < 0)
+ return code;
+
+ check_type(*op, t_boolean);
+ /* If someone has copied the font dictionary, we may still
+ * have the FAPI entry in the dict, but not have the FAPI
+ * server assigned in the font object.
+ */
+ if (pbfont->FAPI == NULL) {
+ if (dict_find_string(op - 1, "FAPI", &v) <= 0
+ || !r_has_type(v, t_name))
+ return_error(gs_error_invalidfont);
+ obj_string_data(imemory, v, &pchars, &len);
+ len = min(len, sizeof(FAPI_ID) - 1);
+ strncpy((char *)FAPI_ID, (const char *)pchars, len);
+ FAPI_ID[len] = 0;
+
+ gs_fapi_set_servers_client_data(imemory, &ps_ff_stub, i_ctx_p);
+
+ code =
+ gs_fapi_find_server(imemory, FAPI_ID,
+ (gs_fapi_server **) & (pbfont->FAPI),
+ (gs_fapi_get_server_param_callback)
+ ps_get_server_param);
+ if (!pbfont->FAPI || code < 0) {
+ return_error(gs_error_invalidfont);
+ }
+ }
+
+ pdata = (font_data *) pfont->client_data;
+ I = pbfont->FAPI;
+
+ if (dict_find_string((op - 1), "SubfontId", &v) >= 0
+ && r_has_type(v, t_integer))
+ subfont = v->value.intval;
+ else
+ subfont = 0;
+
+
+ if (r_type(&(pdata->BuildGlyph)) != t_null) {
+ has_buildglyph = true;
+ }
+ else {
+ has_buildglyph = false;
+ }
+
+ if (r_type(&(pdata->BuildChar)) != t_null) {
+ has_buildchar = true;
+ }
+ else {
+ has_buildchar = false;
+ }
+
+ /* This shouldn't happen, but just in case */
+ if (has_buildglyph == false && has_buildchar == false) {
+ has_buildglyph = true;
+ }
+
+ if (dict_find_string(op - 1, "Path", &v) <= 0 || !r_has_type(v, t_string)) {
+ v = NULL;
+ }
+
+ if (pfont->FontType == ft_CID_encrypted && v == NULL) {
+ if ((code = build_proc_name_refs(imemory, &build, ".FAPIBuildGlyph9",
+ ".FAPIBuildGlyph9")) < 0) {
+ return code;
+ }
+ }
+ else {
+ if ((code = build_proc_name_refs(imemory, &build, ".FAPIBuildChar",
+ ".FAPIBuildGlyph")) < 0) {
+ return code;
+ }
+ }
+
+ if (!
+ ((r_type(&(pdata->BuildChar)) != t_null
+ && pdata->BuildChar.value.pname && build.BuildChar.value.pname
+ && name_index(imemory, &pdata->BuildChar) == name_index(imemory,
+ &build.
+ BuildChar))
+ || (r_type(&(pdata->BuildGlyph)) != t_null
+ && pdata->BuildGlyph.value.pname && build.BuildGlyph.value.pname
+ && name_index(imemory, &pdata->BuildGlyph) == name_index(imemory,
+ &build.
+ BuildGlyph))))
+ {
+
+ if (has_buildchar == true) {
+ ref_assign_new(&pdata->BuildChar, &build.BuildChar);
+ }
+ else {
+ make_null(&pdata->BuildChar);
+ }
+
+ if (has_buildglyph == true) {
+ ref_assign_new(&pdata->BuildGlyph, &build.BuildGlyph);
+ }
+ else {
+ make_null(&pdata->BuildGlyph);
+ }
+ if (v != NULL) {
+ font_file_path =
+ ref_to_string(v, imemory_global, "font file path");
+ }
+
+ code =
+ FAPI_refine_font(i_ctx_p, op - 1, pfont, subfont,
+ font_file_path);
+
+ memcpy(&I->initial_FontMatrix, &pbfont->FontMatrix,
+ sizeof(gs_matrix));
+
+ if (font_file_path != NULL) {
+ gs_free_string(imemory_global, (byte *) font_file_path,
+ r_size(v) + 1, "font file path");
+ }
+ }
+ pop(1);
+ return code;
+}
+
+static ulong
+array_find(const gs_memory_t *mem, ref *Encoding, ref *char_name)
+{
+ ulong n = r_size(Encoding), i;
+ ref v;
+
+ for (i = 0; i < n; i++)
+ if (array_get(mem, Encoding, i, &v) < 0)
+ break;
+ else if (r_type(char_name) == r_type(&v)
+ && char_name->value.const_pname == v.value.const_pname)
+ return i;
+ return 0;
+}
+
+static int
+zfapi_finish_render(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_font *pfont;
+ int code = font_param(op - 1, &pfont);
+
+ if (code == 0) {
+ gs_font_base *pbfont = (gs_font_base *) pfont;
+ gs_fapi_server *I = pbfont->FAPI;
+ gs_text_enum_t *penum = op_show_find(i_ctx_p);
+
+ gs_fapi_set_servers_client_data(imemory, NULL, i_ctx_p);
+
+ code = gs_fapi_finish_render(pfont, igs, penum, I);
+ pop(2);
+ I->release_char_data(I);
+ }
+ return code;
+}
+
+static int
+ps_fapi_set_cache(gs_text_enum_t *penum, const gs_font_base *pbfont,
+ const gs_string *char_name, int cid,
+ const double pwidth[2], const gs_rect *pbbox,
+ const double Metrics2_sbw_default[4], bool *imagenow)
+{
+ i_ctx_t *i_ctx_p = (i_ctx_t *) pbfont->FAPI->client_ctx_p;
+ op_proc_t exec_cont = 0; /* dummy - see below */
+ int code = 0;
+
+ if (cid < 0) {
+ ref cname;
+
+ make_string(&cname, avm_foreign | a_readonly, char_name->size,
+ char_name->data);
+ code =
+ zchar_set_cache(i_ctx_p, pbfont, &cname, NULL, pwidth, pbbox,
+ zfapi_finish_render, &exec_cont,
+ Metrics2_sbw_default);
+ }
+ else {
+ ref cidref;
+
+ make_int(&cidref, cid);
+ code = zchar_set_cache(i_ctx_p, pbfont, &cidref, NULL, pwidth, pbbox,
+ zfapi_finish_render, &exec_cont,
+ Metrics2_sbw_default);
+ }
+
+ if (code >= 0 && exec_cont != NULL) {
+ *imagenow = true;
+ }
+ else {
+ *imagenow = false;
+ }
+ /* We ignore the value of exec_cont here, and leave it up to
+ * gs_fapi_do_char() to do the "right" thing based on the
+ * return value
+ */
+ return (code);
+}
+
+
+static const byte *
+find_substring(const byte *where, int length, const char *what)
+{
+ int l = strlen(what);
+ int n = length - l;
+ const byte *p = where;
+
+ for (; n >= 0; n--, p++)
+ if (!memcmp(p, what, l))
+ return p;
+ return NULL;
+}
+
+static int
+ps_get_glyphname_or_cid(gs_font_base *pbfont, gs_string *charstring,
+ gs_string *name, int ccode,
+ gs_string *enc_char_name, char *font_file_path,
+ gs_fapi_char_ref *cr, bool bCID)
+{
+ ref *pdr = pfont_dict(pbfont);
+ int client_char_code = ccode;
+ ref char_name, cname_str;
+ int code = 0;
+ gs_fapi_server *I = pbfont->FAPI;
+ bool is_TT_from_type42 = (pbfont->FontType == ft_TrueType && font_file_path == NULL);
+ bool is_glyph_index = false;
+ bool is_embedded_type1 =
+ ((pbfont->FontType == ft_encrypted
+ || pbfont->FontType == ft_encrypted2) && font_file_path == NULL);
+ i_ctx_t *i_ctx_p = (i_ctx_t *) I->client_ctx_p;
+
+ /* Obtain the character name : */
+ if (bCID) {
+ client_char_code = ccode;
+ make_null(&char_name);
+ enc_char_name->data = NULL;
+ enc_char_name->size = 0;
+ }
+ else {
+ if (ccode >= 0) {
+ /* Translate from PS encoding to char name : */
+ ref *Encoding;
+
+ client_char_code = ccode;
+ if (dict_find_string(pdr, "Encoding", &Encoding) > 0 &&
+ (r_has_type(Encoding, t_array) ||
+ r_has_type(Encoding, t_shortarray)
+ || r_has_type(Encoding, t_mixedarray))) {
+ if (array_get(imemory, Encoding, client_char_code, &char_name)
+ < 0)
+ if ((code =
+ name_ref(imemory, (const byte *)".notdef", 7,
+ &char_name, -1)) < 0)
+ return code;
+ }
+ else {
+ return_error(gs_error_invalidfont);
+ }
+ }
+ else {
+ code =
+ names_ref(imemory->gs_lib_ctx->gs_name_table,
+ (const byte *)name->data, name->size, &char_name,
+ 0);
+
+ }
+ /* We need to store the name as we get it (from the Encoding array), in case it's
+ * had the name extended (with "~GS~xx"), we'll remove the extension before passing
+ * it to the renderer for a disk based font. But the metrics dictionary may have
+ * been constructed using the extended name....
+ */
+ if (!r_has_type(&char_name, t_name))
+ return_error(gs_error_invalidfont);
+ name_string_ref(imemory, &char_name, &cname_str);
+ enc_char_name->data = cname_str.value.bytes;
+ enc_char_name->size = r_size(&cname_str);
+ }
+
+ /* Obtain the character code or glyph index : */
+ cr->char_codes_count = 1;
+ if (bCID) {
+ if (font_file_path != NULL) {
+ ref *Decoding, *TT_cmap, *SubstNWP;
+ ref src_type, dst_type;
+ uint c = 0;
+
+ is_glyph_index = true;
+
+ if (dict_find_string(pdr, "Decoding", &Decoding) <= 0
+ || !r_has_type(Decoding, t_dictionary))
+ return_error(gs_error_invalidfont);
+ if (dict_find_string(pdr, "SubstNWP", &SubstNWP) <= 0
+ || !r_has_type(SubstNWP, t_array))
+ return_error(gs_error_invalidfont);
+ if (dict_find_string(pdr, "TT_cmap", &TT_cmap) <= 0
+ || !r_has_type(TT_cmap, t_dictionary)) {
+ ref *DecodingArray, char_code, char_code1, ih;
+ int i = client_char_code % 256, n;
+
+ make_int(&ih, client_char_code / 256);
+ /* Check the Decoding array for this block of CIDs */
+ if (dict_find(Decoding, &ih, &DecodingArray) <= 0
+ || !r_has_type(DecodingArray, t_array)
+ || array_get(imemory, DecodingArray, i, &char_code) < 0) {
+ return_error(gs_error_invalidfont);
+ }
+
+ /* Check the Decoding entry */
+ if (r_has_type(&char_code, t_integer)) {
+ n = 1;
+ }
+ else if (r_has_type(&char_code, t_array)) {
+ DecodingArray = &char_code;
+ i = 0;
+ n = r_size(DecodingArray);
+ }
+ else {
+ return_error(gs_error_invalidfont);
+ }
+
+ for (; n--; i++) {
+ if (array_get(imemory, DecodingArray, i, &char_code1) < 0
+ || !r_has_type(&char_code1, t_integer)) {
+ return_error(gs_error_invalidfont);
+ }
+
+ c = char_code1.value.intval;
+ I->check_cmap_for_GID(I, &c);
+ if (c != 0)
+ break;
+ }
+ }
+ else {
+ ref *CIDSystemInfo;
+ ref *Ordering;
+ ref *fdict, *CMapDict, *CMapName, CMapNameStr;
+ char *cmapnm = NULL;
+ int cmapnmlen = 0;
+ /* leave off the -H or -V */
+ char *utfcmap = "Identity-UTF16";
+ int utfcmaplen = strlen(utfcmap);
+
+ fdict = pfont_dict(gs_rootfont(igs));
+ code = dict_find_string(fdict, "CMap", &CMapDict);
+ if (code >= 0 && r_has_type(CMapDict, t_dictionary)) {
+ code = dict_find_string(CMapDict, "CMapName", &CMapName);
+ if (code >= 0 && r_has_type(CMapName, t_name)) {
+ name_string_ref(imemory, CMapName, &CMapNameStr);
+ cmapnm = (char *)CMapNameStr.value.bytes;
+ cmapnmlen = r_size(&CMapNameStr);
+ }
+ }
+ /* We only have to lookup the char code if we're *not* using an identity ordering
+ with the exception of Identity-UTF16 which is a different beast altogether */
+ if ((cmapnmlen > 0 && !strncmp(cmapnm, utfcmap, cmapnmlen > utfcmaplen ? utfcmaplen : cmapnmlen))
+ || (dict_find_string(pdr, "CIDSystemInfo", &CIDSystemInfo) >= 0
+ && r_has_type(CIDSystemInfo, t_dictionary)
+ && dict_find_string(CIDSystemInfo, "Ordering",
+ &Ordering) >= 0
+ && r_has_type(Ordering, t_string)
+ && strncmp((const char *)Ordering->value.bytes,
+ "Identity", 8) != 0)) {
+
+ if ((code =
+ cid_to_TT_charcode(imemory, Decoding, TT_cmap,
+ SubstNWP, client_char_code, &c,
+ &src_type, &dst_type)) < 0) {
+ return code;
+ }
+ }
+ else {
+ if (pbfont->FontType == ft_CID_TrueType) {
+ c = ((gs_font_cid2 *)pbfont)->cidata.CIDMap_proc(((gs_font_cid2 *)pbfont),
+ client_char_code + GS_MIN_CID_GLYPH);
+ }
+ else {
+ c = client_char_code;
+ }
+ }
+ }
+ cr->char_codes[0] = c;
+ cr->is_glyph_index = is_glyph_index;
+ /* fixme : process the narrow/wide/proportional mapping type,
+ using src_type, dst_type. Should adjust the 'matrix' above.
+ Call get_font_proportional_feature for proper choice.
+ */
+ }
+ else {
+ ref *CIDMap;
+ byte *Map;
+ int c_code = client_char_code;
+ int gdb = 2;
+ int i;
+ ref *GDBytes = NULL;
+
+ if ((dict_find_string(pdr, "GDBytes", &GDBytes) > 0)
+ && r_has_type(GDBytes, t_integer)) {
+ gdb = GDBytes->value.intval;
+ }
+
+ /* The PDF Reference says that we should use a CIDToGIDMap, but the PDF
+ * interpreter converts this into a CIDMap (see pdf_font.ps, processCIDToGIDMap)
+ */
+ if (dict_find_string(pdr, "CIDMap", &CIDMap) > 0
+ && !r_has_type(CIDMap, t_name) && (r_has_type(CIDMap, t_array)
+ || r_has_type(CIDMap,
+ t_string))) {
+
+ if (r_has_type(CIDMap, t_array)) {
+
+ /* Too big for single string, so its an array of 2 strings */
+ code = string_array_access_proc(pbfont->memory, CIDMap, 1,
+ client_char_code * gdb, gdb,
+ NULL, NULL,
+ (const byte **)&Map);
+ }
+ else {
+ if (CIDMap->tas.rsize < c_code * gdb) {
+ c_code = 0;
+ }
+ Map = &CIDMap->value.bytes[c_code * gdb];
+ }
+ cr->char_codes[0] = 0;
+
+ if (code >= 0) {
+ for (i = 0; i < gdb; i++) {
+ cr->char_codes[0] = (cr->char_codes[0] << 8) + Map[i];
+ }
+ }
+ else {
+ ref *cstr, *refcode;
+ code = dict_find_string(pdr, "CharStrings", &cstr);
+ if (code > 0) {
+ code = dict_find_string(cstr, ".notdef", &refcode);
+ if (code > 0) {
+ cr->char_codes[0] = refcode->value.intval;
+ }
+ }
+ }
+ }
+ else
+ cr->char_codes[0] = client_char_code;
+ }
+ }
+ else if (is_TT_from_type42) {
+ /* This font must not use 'cmap', so compute glyph index from CharStrings : */
+ ref *CharStrings, *glyph_index;
+
+ if (dict_find_string(pdr, "CharStrings", &CharStrings) <= 0
+ || !r_has_type(CharStrings, t_dictionary))
+ return_error(gs_error_invalidfont);
+ if ((dict_find(CharStrings, &char_name, &glyph_index) < 0)
+ || r_has_type(glyph_index, t_null)) {
+#ifdef DEBUG
+ ref *pvalue;
+
+ if (gs_debug_c('1')
+ && (dict_find_string(systemdict, "QUIET", &pvalue)) > 0
+ && (r_has_type(pvalue, t_boolean)
+ && pvalue->value.boolval == false)) {
+ char *glyphn;
+
+ name_string_ref(imemory, &char_name, &char_name);
+
+ glyphn =
+ ref_to_string(&char_name, imemory,
+ "ps_get_glyphname_or_cid");
+ if (glyphn) {
+ dmprintf2(imemory, " Substituting .notdef for %s in the font %s \n",
+ glyphn, pbfont->font_name.chars);
+ gs_free_string(imemory, (byte *) glyphn,
+ strlen(glyphn) + 1,
+ "ps_get_glyphname_or_cid");
+ }
+ }
+#endif
+
+ cr->char_codes[0] = 0; /* .notdef */
+ if ((code =
+ name_ref(imemory, (const byte *)".notdef", 7, &char_name,
+ -1)) < 0)
+ return code;
+ }
+ else if (r_has_type(glyph_index, t_integer)) {
+ cr->char_codes[0] = glyph_index->value.intval;
+ }
+ else {
+#if 1 /* I can't find this ever being used, no idea what it's for..... */
+ os_ptr op = osp;
+
+ /* Check execution stack has space for BuldChar proc and finish_render */
+ check_estack(2);
+ /* check space and duplicate the glyph index for BuildChar */
+ check_op(1);
+ push(1);
+ ref_assign_inline(op, op - 1);
+ /* Come back to fapi_finish_render after running the BuildChar */
+ push_op_estack(zfapi_finish_render);
+ ++esp;
+ ref_assign(esp, glyph_index);
+ return o_push_estack;
+#else
+ return (gs_error_invalidfont);
+#endif
+ }
+ cr->is_glyph_index = true;
+ }
+ else if (is_embedded_type1) {
+ /* Since the client passes charstring by callback using I->ff.char_data,
+ the client doesn't need to provide a good cr here.
+ Perhaps since UFST uses char codes as glyph cache keys (UFST 4.2 cannot use names),
+ we provide font char codes equal to document's char codes.
+ This trick assumes that Encoding can't point different glyphs
+ for same char code. The last should be true due to
+ PLRM3, "5.9.4 Subsetting and Incremental Definition of Glyphs".
+ */
+ if (ccode >= 0) {
+ cr->char_codes[0] = client_char_code;
+ }
+ else {
+ /*
+ * Reverse Encoding here, because it can be an incremental one.
+ * Note that this can cause problems with UFST (see the comment above),
+ * if the encoding doesn't contain the glyph name rendered with glyphshow.
+ */
+ ref *Encoding;
+ ref glyph;
+
+ name_ref(pbfont->memory, name->data, name->size, &glyph, false);
+
+ if (dict_find_string(osp - 1, "Encoding", &Encoding) > 0) {
+ cr->char_codes[0] =
+ (uint) array_find(imemory, Encoding, &glyph);
+ }
+ else
+ return_error(gs_error_invalidfont);
+ }
+ }
+ else { /* a non-embedded font, i.e. a disk font */
+ bool can_retrieve_char_by_name = false;
+ const byte *p;
+
+ obj_string_data(imemory, &char_name, &cr->char_name,
+ &cr->char_name_length);
+ p = find_substring(cr->char_name, cr->char_name_length,
+ gx_extendeg_glyph_name_separator);
+ if (p != NULL) {
+ cr->char_name_length = p - cr->char_name;
+ name_ref(pbfont->memory, cr->char_name, cr->char_name_length,
+ &char_name, true);
+ }
+ if ((code =
+ renderer_retcode(imemory, I,
+ I->can_retrieve_char_by_name(I, &I->ff, cr,
+ &can_retrieve_char_by_name)))
+ < 0)
+ return code;
+
+ if (!can_retrieve_char_by_name) {
+ /* Translate from char name to encoding used with 3d party font technology : */
+ ref *Decoding, *char_code;
+
+ if (dict_find_string(osp - 1, "Decoding", &Decoding) > 0
+ && r_has_type(Decoding, t_dictionary)) {
+ if (dict_find(Decoding, &char_name, &char_code) > 0) {
+ code = 0;
+ if (r_has_type(char_code, t_integer)) {
+ int c_code;
+ int_param(char_code, 0xFFFF, &c_code);
+ cr->char_codes[0] = (gs_glyph)c_code;
+ }
+ else if (r_has_type(char_code, t_array)
+ || r_has_type(char_code, t_shortarray)) {
+ int i;
+ ref v;
+
+ cr->char_codes_count = r_size(char_code);
+ if (cr->char_codes_count > count_of(cr->char_codes))
+ code = gs_note_error(gs_error_rangecheck);
+ if (code >= 0) {
+ for (i = 0; i < cr->char_codes_count; i++) {
+ code = array_get(imemory, char_code, i, &v);
+ if (code < 0)
+ break;
+ if (!r_has_type(char_code, t_integer)) {
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ cr->char_codes[i] = v.value.intval;
+ }
+ }
+ }
+ else {
+ code = gs_note_error(gs_error_rangecheck);
+ }
+ if (code < 0) {
+ char buf[16];
+ int l = cr->char_name_length;
+
+ if (l > sizeof(buf) - 1) {
+ l = sizeof(buf) - 1;
+ }
+ memcpy(buf, cr->char_name, l);
+ buf[l] = 0;
+ emprintf1(imemory,
+ "Wrong decoding entry for the character '%s'.\n",
+ buf);
+ return_error(gs_error_rangecheck);
+ }
+ }
+ }
+ }
+ }
+
+ /* Provide glyph data for renderer : */
+ /* Occasionally, char_name is already a glyph index to pass to the rendering engine
+ * so don't treat it as a name object.
+ * I believe this will only happen with a TTF/Type42, but checking the object type
+ * is cheap, and covers all font type eventualities.
+ */
+ if (!I->ff.is_cid && r_has_type(&char_name, t_name)) {
+ ref sname;
+
+ name_string_ref(imemory, &char_name, &sname);
+ I->ff.char_data = sname.value.const_bytes;
+ I->ff.char_data_len = r_size(&sname);
+ }
+ else if (I->ff.is_type1) {
+ I->ff.char_data = charstring;
+ }
+
+ cr->is_glyph_index = is_glyph_index;
+ cr->client_char_code = client_char_code;
+
+ return (code);
+}
+
+
+static int
+FAPI_char(i_ctx_t *i_ctx_p, bool bBuildGlyph, ref *charstring)
+{ /* Stack : <font> <code|name> --> - */
+ os_ptr op = osp;
+ ref *pdr = op - 1;
+ ref *v;
+ char *font_file_path = NULL;
+ gs_font *pfont;
+ int code = font_param(osp - 1, &pfont);
+
+ if (code == 0) {
+ gs_font_base *pbfont = (gs_font_base *) pfont;
+ bool bCID = (FAPI_ISCIDFONT(pbfont) || charstring != NULL);
+ int subfont;
+ gs_fapi_server *I = pbfont->FAPI;
+ gs_text_enum_t *penum = op_show_find(i_ctx_p);
+ gs_string char_string, *c_string_p = NULL;
+ gs_string char_name, *c_name_p = NULL;
+ int cindex = -1;
+ ref gname;
+
+ /* initialise the FAPI font, this includes language specific stuff */
+ I->ff = ps_ff_stub;
+
+ I->client_ctx_p = i_ctx_p;
+
+ if (bBuildGlyph && !bCID) {
+ if (r_type(op) != t_name) {
+ name_enter_string(imemory, ".notdef", op);
+ }
+ check_type(*op, t_name);
+
+ name_string_ref(imemory, op, &gname);
+ c_name_p = &char_name;
+ c_name_p->data = gname.value.bytes;
+ c_name_p->size = r_size(&gname);
+
+ }
+ else {
+ if (bBuildGlyph && pbfont->FontType == ft_CID_TrueType
+ && r_has_type(op, t_name)) {
+ ref *chstrs, *chs;
+
+ /* This logic is lifted from %Type11BuildGlyph in gs_cidfn.ps
+ * Note we only have to deal with mistakenly being given a name object
+ * here, the out of range CID is handled later
+ */
+ if ((dict_find_string(op - 1, "CharStrings", &chstrs)) <= 0) {
+ return_error(gs_error_undefined);
+ }
+
+ if ((dict_find_string(chstrs, ".notdef", &chs)) <= 0) {
+ return_error(gs_error_undefined);
+ }
+ ref_assign_inline(op, chs);
+ }
+
+ make_null(&gname);
+ check_type(*op, t_integer);
+ int_param(op, 0xFFFF, &cindex);
+ }
+
+ if (dict_find_string(pdr, "SubfontId", &v) > 0
+ && r_has_type(v, t_integer))
+ subfont = v->value.intval;
+ else
+ subfont = 0;
+
+ if (dict_find_string(osp - 1, "Path", &v) > 0
+ && r_has_type(v, t_string)) {
+ font_file_path = ref_to_string(v, imemory, "font file path");
+ }
+
+ if (charstring) {
+ c_string_p = &char_string;
+ c_string_p->data = charstring->value.bytes;
+ c_string_p->size = r_size(charstring);
+ }
+
+ code =
+ gs_fapi_do_char(pfont, igs, penum, font_file_path,
+ bBuildGlyph, c_string_p, c_name_p, (gs_char)cindex, (gs_glyph)cindex,
+ subfont);
+ if (font_file_path != NULL) {
+ gs_free_string(imemory, (byte *) font_file_path, r_size(v) + 1,
+ "font file path");
+ }
+ /* This handles the situation where a charstring has been replaced with a PS procedure.
+ * against the rules, but not *that* rare.
+ * It's also something that GS does internally to simulate font styles.
+ */
+ if (code == gs_error_unregistered) {
+ os_ptr op = osp;
+ ref *proc, gname;
+
+ if (I->ff.is_type1
+ && (get_charstring(&I->ff, cindex, &proc, &gname) >= 0)
+ && (r_has_type(proc, t_array)
+ || r_has_type(proc, t_mixedarray))) {
+ push(2);
+ ref_assign(op - 1, &gname);
+ ref_assign(op, proc);
+ return (zchar_exec_char_proc(i_ctx_p));
+ }
+ else {
+ return_error(gs_error_invalidfont);
+ }
+ }
+ }
+ /* We've already imaged teh glyph, pop the operands */
+ if (code == 0)
+ pop(2);
+ return code;
+}
+
+static int
+zFAPIBuildGlyph9(i_ctx_t *i_ctx_p)
+{
+ /* The alghorithm is taken from %Type9BuildGlyph - see gs_cidfn.ps . */
+ os_ptr lop, op = osp;
+ int cid, code;
+ avm_space s = ialloc_space(idmemory);
+ ref font9 = *pfont_dict(gs_currentfont(igs));
+ ref *rFDArray, f;
+ int font_index;
+
+ check_type(op[0], t_integer);
+ check_type(op[-1], t_dictionary);
+ cid = op[0].value.intval;
+ push(2);
+ op[-1] = *pfont_dict(gs_currentfont(igs));
+ op[0] = op[-2]; /* <font0> <cid> <font9> <cid> */
+ ialloc_set_space(idmemory, (r_is_local(op - 3) ? avm_global : avm_local)); /* for ztype9mapcid */
+
+ /* stack: <font0> <cid> <font9> <cid> */
+ if ((code = ztype9mapcid(i_ctx_p)) < 0)
+ return code; /* <font0> <cid> <charstring> <font_index> */
+ /* fixme: what happens if the charstring is absent ?
+ Can FDArray contain 'null' (see %Type9BuildGlyph in gs_cidfn.ps)? */
+ font_index = op[0].value.intval;
+ if (dict_find_string(&font9, "FDArray", &rFDArray) <= 0
+ || r_type(rFDArray) != t_array)
+ return_error(gs_error_invalidfont);
+ if (array_get(imemory, rFDArray, font_index, &f) < 0
+ || r_type(&f) != t_dictionary)
+ return_error(gs_error_invalidfont);
+
+ op[0] = op[-2];
+ op[-2] = op[-1]; /* Keep the charstring on ostack for the garbager. */
+ op[-1] = f; /* <font0> <charstring> <subfont> <cid> */
+ if ((code = FAPI_char(i_ctx_p, true, op - 2)) < 0)
+ return code;
+ /* stack: <font0> <charstring> */
+
+ lop = osp;
+ if (code == 5) {
+ int i, ind = (lop - op);
+
+ op = osp;
+
+ for (i = ind; i >= 0; i--) {
+ op[-i - 2] = op[-i];
+ }
+ pop(2);
+ }
+ else if (code < 0) { /* <font0> <dirty> <dirty> <dirty> */
+ /* Adjust ostack for the correct error handling : */
+ make_int(op - 2, cid);
+ pop(2); /* <font0> <cid> */
+ }
+ else if (code != 5) { /* <font0> <dirty> */
+
+
+ pop(2); /* */
+ /* Note that this releases the charstring, and it may be garbage-collected
+ before the interpreter calls fapi_finish_render. This requires the server
+ to keep glyph raster internally between calls to get_char_raster_metrics
+ and get_char_raster. Perhaps UFST cannot provide metrics without
+ building a raster, so this constraint actually goes from UFST.
+ */
+ }
+ ialloc_set_space(idmemory, s);
+ return code;
+}
+
+/* <font> <code> .FAPIBuildChar - */
+static int
+zFAPIBuildChar(i_ctx_t *i_ctx_p)
+{
+ return FAPI_char(i_ctx_p, false, NULL);
+}
+
+/* non-CID : <font> <code> .FAPIBuildGlyph - */
+/* CID : <font> <name> .FAPIBuildGlyph - */
+static int
+zFAPIBuildGlyph(i_ctx_t *i_ctx_p)
+{
+ return FAPI_char(i_ctx_p, true, NULL);
+}
+
+
+/* <font_dict> .FAPIpassfont bool <font_dict> */
+/* must insert /FAPI to font dictionary */
+static int
+zFAPIpassfont(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_font *pfont;
+ int code;
+ char *font_file_path = NULL;
+ ref *v;
+ char *xlatmap = NULL;
+ char *fapi_request = NULL;
+ char *fapi_id = NULL;
+ ref reqstr;
+ int subfont;
+
+ /* Normally embedded fonts have no Path, but if a CID font is
+ * emulated with a TT font, and it is hooked with FAPI,
+ * the path presents and is neccessary to access the full font data.
+ */
+ check_type(*op, t_dictionary);
+
+ code = font_param(osp, &pfont);
+ if (code < 0)
+ return code;
+
+ if (dict_find_string(op, "SubfontId", &v) >= 0
+ && r_has_type(v, t_integer))
+ subfont = v->value.intval;
+ else
+ subfont = 0;
+
+ code = FAPI_get_xlatmap(i_ctx_p, &xlatmap); /* Useful for emulated fonts hooked with FAPI. */
+ if (code < 0)
+ return code;
+
+ /* If the font dictionary contains a FAPIPlugInReq key, the the PS world wants us
+ * to try to use a specific FAPI plugin, so find it, and try it....
+ */
+ if (dict_find_string(op, "FAPIPlugInReq", &v) >= 0 && r_type(v) == t_name) {
+
+ name_string_ref(imemory, v, &reqstr);
+
+ fapi_request = ref_to_string(&reqstr, imemory, "zFAPIpassfont");
+ }
+
+ if (dict_find_string(op, "Path", &v) > 0 && r_has_type(v, t_string))
+ font_file_path = ref_to_string(v, imemory_global, "font file path");
+
+ gs_fapi_set_servers_client_data(imemory, &ps_ff_stub, i_ctx_p);
+
+ code =
+ gs_fapi_passfont(pfont, subfont, font_file_path, NULL, fapi_request, xlatmap,
+ &fapi_id, (gs_fapi_get_server_param_callback)ps_get_server_param);
+
+ if (font_file_path != NULL)
+ gs_free_string(imemory_global, (byte *) font_file_path, r_size(v) + 1,
+ "font file path");
+
+ if (fapi_request != NULL)
+ gs_free_string(imemory, (byte *) fapi_request,
+ strlen(fapi_request) + 1, "do_FAPIpassfont");
+ if (code < 0 && code != gs_error_invalidaccess)
+ return code;
+
+ if (code >= 0 && fapi_id != NULL) {
+ ref FAPI_ID;
+
+ if ((code =
+ name_ref(imemory, (const byte *)fapi_id,
+ strlen(fapi_id), &FAPI_ID, false)) < 0)
+ return code;
+ if ((code = dict_put_string(op, "FAPI", &FAPI_ID, NULL)) < 0)
+ return code; /* Insert FAPI entry to font dictionary. */
+ }
+ push(1);
+ make_bool(op, (fapi_id != NULL));
+ return 0;
+}
+
+const op_def zfapi_op_defs[] = {
+ {"1.FAPIavailable", zFAPIavailable},
+ {"2.FAPIpassfont", zFAPIpassfont},
+ {"2.FAPIrebuildfont", zFAPIrebuildfont},
+ {"2.FAPIBuildChar", zFAPIBuildChar},
+ {"2.FAPIBuildGlyph", zFAPIBuildGlyph},
+ {"2.FAPIBuildGlyph9", zFAPIBuildGlyph9},
+ op_def_end(0)
+};
diff --git a/psi/zfarc4.c b/psi/zfarc4.c
new file mode 100644
index 000000000..05832cd5c
--- /dev/null
+++ b/psi/zfarc4.c
@@ -0,0 +1,93 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/* this is the ps interpreter interface to the arcfour cipher filter
+ used in PDF encryption. We provide both Decode and Encode filters;
+ the cipher is symmetric, so the call to the underlying routines is
+ identical between the two filters */
+
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "ifilter.h"
+#include "sarc4.h"
+
+/* <source> <dict> arcfour/filter <file> */
+
+static int
+z_arcfour_d(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp; /* i_ctx_p->op_stack.stack.p defined in osstack.h */
+ ref *sop = NULL;
+ stream_arcfour_state state;
+
+ /* extract the key from the parameter dictionary */
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if (dict_find_string(op, "Key", &sop) <= 0)
+ return_error(gs_error_rangecheck);
+ if (!r_has_type(sop, t_string))
+ return_error(gs_error_typecheck);
+ s_arcfour_set_key(&state, sop->value.const_bytes, r_size(sop));
+
+ /* we pass npop=0, since we've no arguments left to consume */
+ /* we pass 0 instead of the usual rspace(sop) will allocate storage for
+ filter state from the same memory pool as the stream it's coding. this
+ causes no trouble because we maintain no pointers */
+ return filter_read(i_ctx_p, 0, &s_arcfour_template,
+ (stream_state *) & state, 0);
+}
+
+/* encode version of the filter */
+static int
+z_arcfour_e(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp; /* i_ctx_p->op_stack.stack.p defined in osstack.h */
+ ref *sop = NULL;
+ stream_arcfour_state state;
+
+ /* extract the key from the parameter dictionary */
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if (dict_find_string(op, "Key", &sop) <= 0)
+ return_error(gs_error_rangecheck);
+ if (!r_has_type(sop, t_string))
+ return_error(gs_error_typecheck);
+
+ s_arcfour_set_key(&state, sop->value.const_bytes, r_size(sop));
+
+ /* we pass npop=0, since we've no arguments left to consume */
+ /* we pass 0 instead of the usual rspace(sop) will allocate storage for
+ filter state from the same memory pool as the stream it's coding. this
+ causes no trouble because we maintain no pointers */
+ return filter_write(i_ctx_p, 0, &s_arcfour_template,
+ (stream_state *) & state, 0);
+}
+
+/* Match the above routines to their postscript filter names.
+ This is how our static routines get called externally. */
+const op_def zfarc4_op_defs[] = {
+ op_def_begin_filter(),
+ {"2ArcfourDecode", z_arcfour_d},
+ {"2ArcfourEncode", z_arcfour_e},
+ op_def_end(0)
+};
diff --git a/psi/zfbcp.c b/psi/zfbcp.c
new file mode 100644
index 000000000..02bb15684
--- /dev/null
+++ b/psi/zfbcp.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* (T)BCP filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sbcp.h"
+#include "ifilter.h"
+
+/* Define null handlers for the BCP out-of-band signals. */
+static int
+no_bcp_signal_interrupt(stream_state * st)
+{
+ return 0;
+}
+static int
+no_bcp_request_status(stream_state * st)
+{
+ return 0;
+}
+
+/* <source> BCPEncode/filter <file> */
+/* <source> <dict> BCPEncode/filter <file> */
+static int
+zBCPE(i_ctx_t *i_ctx_p)
+{
+ return filter_write_simple(i_ctx_p, &s_BCPE_template);
+}
+
+/* <target> BCPDecode/filter <file> */
+/* <target> <dict> BCPDecode/filter <file> */
+static int
+zBCPD(i_ctx_t *i_ctx_p)
+{
+ stream_BCPD_state state;
+
+ state.signal_interrupt = no_bcp_signal_interrupt;
+ state.request_status = no_bcp_request_status;
+ return filter_read(i_ctx_p, 0, &s_BCPD_template, (stream_state *)&state, 0);
+}
+
+/* <source> TBCPEncode/filter <file> */
+/* <source> <dict> TBCPEncode/filter <file> */
+static int
+zTBCPE(i_ctx_t *i_ctx_p)
+{
+ return filter_write_simple(i_ctx_p, &s_TBCPE_template);
+}
+
+/* <target> TBCPDecode/filter <file> */
+/* <target> <dict> TBCPDecode/filter <file> */
+static int
+zTBCPD(i_ctx_t *i_ctx_p)
+{
+ stream_BCPD_state state;
+
+ state.signal_interrupt = no_bcp_signal_interrupt;
+ state.request_status = no_bcp_request_status;
+ return filter_read(i_ctx_p, 0, &s_TBCPD_template, (stream_state *)&state, 0);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfbcp_op_defs[] =
+{
+ op_def_begin_filter(),
+ {"1BCPEncode", zBCPE},
+ {"1BCPDecode", zBCPD},
+ {"1TBCPEncode", zTBCPE},
+ {"1TBCPDecode", zTBCPD},
+ op_def_end(0)
+};
diff --git a/psi/zfcid.c b/psi/zfcid.c
new file mode 100644
index 000000000..ce583af30
--- /dev/null
+++ b/psi/zfcid.c
@@ -0,0 +1,92 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* CID-keyed font utilities */
+#include "ghost.h"
+#include "oper.h"
+#include "gsmatrix.h"
+#include "gxfcid.h"
+#include "bfont.h"
+#include "icid.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifcid.h"
+#include "store.h"
+
+/* Get the CIDSystemInfo of a CIDFont. */
+int
+cid_font_system_info_param(gs_cid_system_info_t *pcidsi, const ref *prfont)
+{
+ ref *prcidsi;
+
+ if (dict_find_string(prfont, "CIDSystemInfo", &prcidsi) <= 0)
+ return_error(gs_error_rangecheck);
+ return cid_system_info_param(pcidsi, prcidsi);
+}
+
+/* Get the additional information for a CIDFontType 0 or 2 CIDFont. */
+int
+cid_font_data_param(os_ptr op, gs_font_cid_data *pdata, ref *pGlyphDirectory)
+{
+ int code;
+ ref *pgdir;
+
+ check_type(*op, t_dictionary);
+ if ((code = cid_font_system_info_param(&pdata->CIDSystemInfo, op)) < 0 ||
+ (code = dict_int_param(op, "CIDCount", 0, max_int, -1,
+ &pdata->CIDCount)) < 0
+ )
+ return code;
+ /* Start by assigning the highest CID to be the count of CIDs. */
+ pdata->MaxCID = pdata->CIDCount + 1;
+ /*
+ * If the font doesn't have a GlyphDirectory, GDBytes is required.
+ * If it does have a GlyphDirectory, GDBytes may still be needed for
+ * CIDMap: it's up to the client to check this.
+ */
+ if (dict_find_string(op, "GlyphDirectory", &pgdir) <= 0) {
+ /* Standard CIDFont, require GDBytes. */
+ make_null(pGlyphDirectory);
+ return dict_int_param(op, "GDBytes", 1, MAX_GDBytes, 0,
+ &pdata->GDBytes);
+ }
+ if (r_has_type(pgdir, t_dictionary) || r_is_array(pgdir)) {
+ int index;
+ ref element[2];
+
+ /* GlyphDirectory, GDBytes is optional. */
+ *pGlyphDirectory = *pgdir;
+ code = dict_int_param(op, "GDBytes", 0, MAX_GDBytes, 0,
+ &pdata->GDBytes);
+
+ /* If we have a GlyphDirectory then the maximum CID is *not* given by
+ * the number of CIDs in the font. We need to know the maximum CID
+ * when copying fonts, so calculate and store it now.
+ */
+ index = dict_first(pgdir);
+ while (index >= 0) {
+ index = dict_next(pgdir, index, (ref *)&element);
+ if (index >= 0) {
+ if (element[0].value.intval > pdata->MaxCID)
+ pdata->MaxCID = element[0].value.intval;
+ }
+ }
+
+ return code;
+ } else {
+ return_error(gs_error_typecheck);
+ }
+}
diff --git a/psi/zfcid0.c b/psi/zfcid0.c
new file mode 100644
index 000000000..ff6910871
--- /dev/null
+++ b/psi/zfcid0.c
@@ -0,0 +1,581 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* CIDFontType 0 operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsmatrix.h"
+#include "gsccode.h"
+#include "gsstruct.h"
+#include "gxfcid.h"
+#include "gxfont1.h"
+#include "gxalloc.h" /* for gs_ref_memory_t */
+#include "stream.h" /* for files.h */
+#include "bfont.h"
+#include "files.h"
+#include "ichar.h"
+#include "ichar1.h"
+#include "icid.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifcid.h"
+#include "ifont1.h"
+#include "ifont2.h"
+#include "ifont42.h"
+#include "store.h"
+#include "imain.h"
+#include "iapi.h"
+#include "iminst.h"
+
+/* Type 1 font procedures (defined in zchar1.c) */
+font_proc_glyph_outline(zcharstring_glyph_outline);
+
+/* ---------------- CIDFontType 0 (FontType 9) ---------------- */
+
+/* ------ Accessing ------ */
+
+/* Parse a multi-byte integer from a string. */
+static int
+get_index(gs_glyph_data_t *pgd, int count, ulong *pval)
+{
+ int i;
+
+ if (pgd->bits.size < count)
+ return_error(gs_error_rangecheck);
+ *pval = 0;
+ for (i = 0; i < count; ++i)
+ *pval = (*pval << 8) + pgd->bits.data[i];
+ pgd->bits.data += count;
+ pgd->bits.size -= count;
+ return 0;
+}
+
+/* Get bytes from GlyphData or DataSource. */
+static int
+cid0_read_bytes(gs_font_cid0 *pfont, ulong base, uint count, byte *buf,
+ gs_glyph_data_t *pgd)
+{
+ const font_data *pfdata = pfont_data(pfont);
+ byte *data = buf;
+ gs_font *gdfont = 0; /* pfont if newly allocated, 0 if not */
+ int code = 0;
+
+ /* Check for overflow. */
+ if (base != (long)base || base > base + count)
+ return_error(gs_error_rangecheck);
+ if (r_has_type(&pfdata->u.cid0.DataSource, t_null)) {
+ /* Get the bytes from GlyphData (a string or array of strings). */
+ const ref *pgdata = &pfdata->u.cid0.GlyphData;
+
+ if (r_has_type(pgdata, t_string)) { /* single string */
+ uint size = r_size(pgdata);
+
+ if (base >= size || count > size - base)
+ return_error(gs_error_rangecheck);
+ data = pgdata->value.bytes + base;
+ } else { /* array of strings */
+ /*
+ * The algorithm is similar to the one in
+ * string_array_access_proc in zfont42.c, but it also has to
+ * deal with the case where the requested string crosses array
+ * elements.
+ */
+ ulong skip = base;
+ uint copied = 0;
+ uint index = 0;
+ ref rstr;
+ uint size;
+
+ for (;; skip -= size, ++index) {
+ int code = array_get(pfont->memory, pgdata, index, &rstr);
+
+ if (code < 0)
+ return code;
+ if (!r_has_type(&rstr, t_string))
+ return_error(gs_error_typecheck);
+ size = r_size(&rstr);
+ if (skip < size)
+ break;
+ }
+ size -= skip;
+ if (count <= size) {
+ data = rstr.value.bytes + skip;
+ } else { /* multiple strings needed */
+ if (data == 0) { /* no buffer provided */
+ data = gs_alloc_string(pfont->memory, count,
+ "cid0_read_bytes");
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ gdfont = (gs_font *)pfont; /* newly allocated */
+ }
+ memcpy(data, rstr.value.bytes + skip, size);
+ copied = size;
+ while (copied < count) {
+ int code = array_get(pfont->memory, pgdata, ++index, &rstr);
+
+ if (code < 0)
+ goto err;
+ if (!r_has_type(&rstr, t_string)) {
+ code = gs_note_error(gs_error_typecheck);
+ goto err;
+ }
+ size = r_size(&rstr);
+ if (size > count - copied)
+ size = count - copied;
+ memcpy(data + copied, rstr.value.bytes, size);
+ copied += size;
+ }
+ }
+ }
+ } else {
+ /* Get the bytes from DataSource (a stream). */
+ stream *s;
+ uint nread;
+ i_ctx_t *i_ctx_p = get_minst_from_memory(pfont->memory)->i_ctx_p;
+
+ check_read_known_file(i_ctx_p, s, &pfdata->u.cid0.DataSource, return_error);
+ if (sseek(s, base) < 0)
+ return_error(gs_error_ioerror);
+ if (data == 0) { /* no buffer provided */
+ data = gs_alloc_string(pfont->memory, count, "cid0_read_bytes");
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ gdfont = (gs_font *)pfont; /* newly allocated */
+ }
+ if (sgets(s, data, count, &nread) < 0 || nread != count) {
+ code = gs_note_error(gs_error_ioerror);
+ goto err;
+ }
+ }
+ gs_glyph_data_from_string(pgd, data, count, gdfont);
+ return code;
+ err:
+ if (data != buf)
+ gs_free_string(pfont->memory, data, count, "cid0_read_bytes");
+ return code;
+}
+
+/* Get the CharString data for a CIDFontType 0 font. */
+/* This is the glyph_data procedure in the font itself. */
+/* Note that pgd may be NULL. */
+static int
+z9_glyph_data(gs_font_base *pbfont, gs_glyph glyph, gs_glyph_data_t *pgd,
+ int *pfidx)
+{
+ gs_font_cid0 *pfont = (gs_font_cid0 *)pbfont;
+ const font_data *pfdata = pfont_data(pfont);
+ long glyph_index = (long)(glyph - gs_min_cid_glyph);
+ gs_glyph_data_t gdata;
+ ulong fidx;
+ int code;
+
+ gdata.memory = pfont->memory;
+ if (!r_has_type(&pfdata->u.cid0.GlyphDirectory, t_null)) {
+ code = font_gdir_get_outline(pfont->memory,
+ &pfdata->u.cid0.GlyphDirectory,
+ glyph_index, &gdata);
+ if (code < 0)
+ return code;
+ /* Get the definition from GlyphDirectory. */
+ if (!gdata.bits.data)
+ return_error(gs_error_rangecheck);
+ code = get_index(&gdata, pfont->cidata.FDBytes, &fidx);
+ if (code < 0)
+ return code;
+ if (fidx >= pfont->cidata.FDArray_size)
+ return_error(gs_error_rangecheck);
+ if (pgd)
+ *pgd = gdata;
+ *pfidx = (int)fidx;
+ return code;
+ }
+ /* Get the definition from the binary data (GlyphData or DataSource). */
+ if (glyph_index < 0 || glyph_index >= pfont->cidata.common.CIDCount) {
+ *pfidx = 0;
+ if (pgd)
+ gs_glyph_data_from_null(pgd);
+ return_error(gs_error_rangecheck);
+ }
+ {
+ byte fd_gd[(MAX_FDBytes + MAX_GDBytes) * 2];
+ uint num_bytes = pfont->cidata.FDBytes + pfont->cidata.common.GDBytes;
+ ulong base = pfont->cidata.CIDMapOffset + glyph_index * num_bytes;
+ ulong gidx, fidx_next, gidx_next;
+ int rcode = cid0_read_bytes(pfont, base, (ulong)(num_bytes * 2), fd_gd,
+ &gdata);
+ gs_glyph_data_t orig_data;
+
+ if (rcode < 0)
+ return rcode;
+ orig_data = gdata;
+ if ((code = get_index(&gdata, pfont->cidata.FDBytes, &fidx)) < 0 ||
+ (code = get_index(&gdata, pfont->cidata.common.GDBytes, &gidx)) < 0 ||
+ (code = get_index(&gdata, pfont->cidata.FDBytes, &fidx_next)) < 0 ||
+ (code = get_index(&gdata, pfont->cidata.common.GDBytes, &gidx_next)) < 0
+ )
+ DO_NOTHING;
+ gs_glyph_data_free(&orig_data, "z9_glyph_data");
+ if (code < 0)
+ return code;
+ /*
+ * Some CID fonts (from Adobe!) have invalid font indexes for
+ * missing glyphs. Handle this now.
+ */
+ if (gidx_next <= gidx) { /* missing glyph */
+ *pfidx = 0;
+ if (pgd)
+ gs_glyph_data_from_null(pgd);
+ return_error(gs_error_undefined);
+ }
+ if (fidx >= pfont->cidata.FDArray_size)
+ return_error(gs_error_rangecheck);
+ *pfidx = (int)fidx;
+ if (pgd == 0)
+ return 0;
+ return cid0_read_bytes(pfont, gidx, gidx_next - gidx, NULL, pgd);
+ }
+}
+
+/* Get the outline of a CIDFontType 0 glyph. */
+static int
+z9_glyph_outline(gs_font *font, int WMode, gs_glyph glyph, const gs_matrix *pmat,
+ gx_path *ppath, double sbw[4])
+{
+ gs_font_cid0 *const pfcid = (gs_font_cid0 *)font;
+ ref gref;
+ gs_glyph_data_t gdata;
+ int code, fidx, ocode;
+
+ gdata.memory = font->memory;
+ code = pfcid->cidata.glyph_data((gs_font_base *)pfcid, glyph, &gdata,
+ &fidx);
+ if (code < 0)
+ return code;
+ glyph_ref(font->memory, glyph, &gref);
+ ocode = zcharstring_outline(pfcid->cidata.FDArray[fidx], WMode, &gref, &gdata,
+ pmat, ppath, sbw);
+ gs_glyph_data_free(&gdata, "z9_glyph_outline");
+ return ocode;
+}
+
+static int
+z9_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
+ int members, gs_glyph_info_t *info)
+{ /* fixme : same as z11_glyph_info. */
+ int wmode = (members & GLYPH_INFO_WIDTH0 ? 0 : 1);
+
+ return z1_glyph_info_generic(font, glyph, pmat, members, info,
+ &gs_default_glyph_info, wmode);
+}
+
+/*
+ * The "fonts" in the FDArray don't have access to their outlines -- the
+ * outlines are always provided externally. Replace the accessor procedures
+ * with ones that will give an error if called.
+ */
+static int
+z9_FDArray_glyph_data(gs_font_type1 * pfont, gs_glyph glyph,
+ gs_glyph_data_t *pgd)
+{
+ return_error(gs_error_invalidfont);
+}
+static int
+z9_FDArray_seac_data(gs_font_type1 *pfont, int ccode, gs_glyph *pglyph,
+ gs_const_string *gstr, gs_glyph_data_t *pgd)
+{
+ return_error(gs_error_invalidfont);
+}
+
+/* ------ Defining ------ */
+
+/* Get one element of a FDArray. */
+static int
+fd_array_element(i_ctx_t *i_ctx_p, gs_font_type1 **ppfont, ref *prfd)
+{
+ charstring_font_refs_t refs;
+ gs_type1_data data1;
+ build_proc_refs build;
+ gs_font_base *pbfont;
+ gs_font_type1 *pfont;
+ /*
+ * Standard CIDFontType 0 fonts have Type 1 fonts in the FDArray, but
+ * CFF CIDFontType 0 fonts have Type 2 fonts there.
+ */
+ int fonttype = 1; /* default */
+ int code = charstring_font_get_refs(prfd, &refs);
+
+ if (code < 0 ||
+ (code = dict_int_param(prfd, "FontType", 1, 2, 1, &fonttype)) < 0
+ )
+ return code;
+ /*
+ * We don't handle the alternate Subr representation (SubrCount,
+ * SDBytes, SubrMapOffset) here: currently that is handled in
+ * PostScript code (lib/gs_cidfn.ps).
+ */
+ switch (fonttype) {
+ case 1:
+ data1.interpret = gs_type1_interpret;
+ data1.subroutineNumberBias = 0;
+ data1.lenIV = DEFAULT_LENIV_1;
+ code = charstring_font_params(imemory, prfd, &refs, &data1);
+ if (code < 0)
+ return code;
+ code = build_proc_name_refs(imemory, &build,
+ "%Type1BuildChar", "%Type1BuildGlyph");
+ break;
+ case 2:
+ code = type2_font_params(prfd, &refs, &data1);
+ if (code < 0)
+ return code;
+ code = charstring_font_params(imemory, prfd, &refs, &data1);
+ if (code < 0)
+ return code;
+ code = build_proc_name_refs(imemory, &build,
+ "%Type2BuildChar", "%Type2BuildGlyph");
+ break;
+ default: /* can't happen */
+ return_error(gs_error_Fatal);
+ }
+ if (code < 0)
+ return code;
+ code = build_gs_FDArray_font(i_ctx_p, prfd, &pbfont, fonttype,
+ &st_gs_font_type1, &build);
+ if (code < 0)
+ return code;
+ pfont = (gs_font_type1 *)pbfont;
+ pbfont->FAPI = NULL;
+ pbfont->FAPI_font_data = NULL;
+ charstring_font_init(pfont, &refs, &data1);
+ pfont->data.procs.glyph_data = z9_FDArray_glyph_data;
+ pfont->data.procs.seac_data = z9_FDArray_seac_data;
+ *ppfont = pfont;
+ return 0;
+}
+
+static int
+notify_remove_font_type9(void *proc_data, void *event_data)
+{ /* Likely type 9 font descendents are never released explicitly.
+ So releaseing a type 9 font we must reset pointers in descendents.
+ */
+ /* gs_font_finalize passes event_data == NULL, so check it here. */
+ if (event_data == NULL) {
+ gs_font_cid0 *pfcid = proc_data;
+ int i;
+
+ for (i = 0; i < pfcid->cidata.FDArray_size; ++i) {
+ if (pfcid->cidata.FDArray[i]->data.parent == (gs_font_base *)pfcid)
+ pfcid->cidata.FDArray[i]->data.parent = NULL;
+ }
+ }
+ return 0;
+}
+
+/* <string|name> <font_dict> .buildfont9 <string|name> <font> */
+static int
+zbuildfont9(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ build_proc_refs build;
+ int code = build_proc_name_refs(imemory, &build, NULL, "%Type9BuildGlyph");
+ gs_font_cid_data common;
+ ref GlyphDirectory, GlyphData, DataSource;
+ ref *prfda, cfnstr;
+ ref *pCIDFontName, CIDFontName;
+ gs_font_type1 **FDArray;
+ uint FDArray_size;
+ int FDBytes;
+ uint CIDMapOffset;
+ gs_font_base *pfont;
+ gs_font_cid0 *pfcid;
+ uint i;
+
+ /*
+ * If the CIDFont's data have been loaded into VM, GlyphData will be
+ * a string or an array of strings; if they are loaded incrementally
+ * from a file, GlyphData will be an integer, and DataSource will be
+ * a (reusable) stream.
+ */
+ if (code < 0 ||
+ (code = cid_font_data_param(op, &common, &GlyphDirectory)) < 0 ||
+ (code = dict_find_string(op, "FDArray", &prfda)) < 0 ||
+ (code = dict_find_string(op, "CIDFontName", &pCIDFontName)) <= 0 ||
+ (code = dict_int_param(op, "FDBytes", 0, MAX_FDBytes, -1, &FDBytes)) < 0
+ )
+ return code;
+ /*
+ * Since build_gs_simple_font may resize the dictionary and cause
+ * pointers to become invalid, save CIDFontName
+ */
+ CIDFontName = *pCIDFontName;
+ if (r_has_type(&GlyphDirectory, t_null)) {
+ /* Standard CIDFont, require GlyphData and CIDMapOffset. */
+ ref *pGlyphData;
+
+ if ((code = dict_find_string(op, "GlyphData", &pGlyphData)) < 0 ||
+ (code = dict_uint_param(op, "CIDMapOffset", 0, max_uint - 1,
+ max_uint, &CIDMapOffset)) < 0)
+ return code;
+ GlyphData = *pGlyphData;
+ if (r_has_type(&GlyphData, t_integer)) {
+ ref *pds;
+ stream *ignore_s;
+
+ if ((code = dict_find_string(op, "DataSource", &pds)) < 0)
+ return code;
+ check_read_file(i_ctx_p, ignore_s, pds);
+ DataSource = *pds;
+ } else {
+ if (!r_has_type(&GlyphData, t_string) && !r_is_array(&GlyphData))
+ return_error(gs_error_typecheck);
+ make_null(&DataSource);
+ }
+ } else {
+ make_null(&GlyphData);
+ make_null(&DataSource);
+ CIDMapOffset = 0;
+ }
+ if (!r_is_array(prfda))
+ return_error(gs_error_invalidfont);
+ FDArray_size = r_size(prfda);
+ if (FDArray_size == 0)
+ return_error(gs_error_invalidfont);
+ FDArray = ialloc_struct_array(FDArray_size, gs_font_type1 *,
+ &st_gs_font_type1_ptr_element,
+ "buildfont9(FDarray)");
+ if (FDArray == 0)
+ return_error(gs_error_VMerror);
+ memset(FDArray, 0, sizeof(gs_font_type1 *) * FDArray_size);
+ for (i = 0; i < FDArray_size; ++i) {
+ ref rfd;
+
+ array_get(imemory, prfda, (long)i, &rfd);
+ code = fd_array_element(i_ctx_p, &FDArray[i], &rfd);
+ if (code < 0)
+ goto fail;
+ }
+ code = build_gs_outline_font(i_ctx_p, op, &pfont, ft_CID_encrypted,
+ &st_gs_font_cid0, &build,
+ bf_Encoding_optional | bf_UniqueID_ignored,
+ build_gs_simple_font);
+ if (code < 0)
+ goto fail;
+ if (code == 1) {
+ /* The font already has a FID, don't need to build it again.
+ Release FDArray and return normally.
+ fixme: FDArray fonts are thrown for garbager.
+ We're not safe to build them after
+ build_gs_simple_font(..., &pfont, ...),
+ because a failure in building them would throw
+ an underbuilt font with unclear consequences.
+ */
+ ifree_object(FDArray, "buildfont9(FDarray)");
+ return 0;
+ }
+ pfont->procs.enumerate_glyph = gs_font_cid0_enumerate_glyph;
+ pfont->procs.glyph_outline = z9_glyph_outline;
+ pfont->procs.glyph_info = z9_glyph_info;
+ pfcid = (gs_font_cid0 *)pfont;
+ pfcid->cidata.common = common;
+ pfcid->cidata.CIDMapOffset = CIDMapOffset;
+ pfcid->cidata.FDArray = FDArray;
+ pfcid->cidata.FDArray_size = FDArray_size;
+ pfcid->cidata.FDBytes = FDBytes;
+ pfcid->cidata.glyph_data = z9_glyph_data;
+ pfcid->cidata.proc_data = 0; /* for GC */
+ if (pfcid->font_name.size == 0) {
+ get_font_name(imemory, &cfnstr, &CIDFontName);
+ copy_font_name(&pfcid->font_name, &cfnstr);
+ }
+ ref_assign(&pfont_data(pfont)->u.cid0.GlyphDirectory, &GlyphDirectory);
+ ref_assign(&pfont_data(pfont)->u.cid0.GlyphData, &GlyphData);
+ ref_assign(&pfont_data(pfont)->u.cid0.DataSource, &DataSource);
+ code = define_gs_font(i_ctx_p, (gs_font *)pfont);
+ if (code >= 0)
+ code = gs_notify_register(&pfont->notify_list, notify_remove_font_type9, pfont);
+ if (code >= 0) {
+ for (i = 0; i < FDArray_size; ++i) {
+ FDArray[i]->dir = pfont->dir;
+ FDArray[i]->data.parent = pfont;
+ }
+ return code;
+ }
+ fail:
+ ifree_object(FDArray, "buildfont9(FDarray)");
+ return code;
+}
+
+/* <cid9font> <cid> .type9mapcid <charstring> <font_index> */
+int
+ztype9mapcid(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_font *pfont;
+ gs_font_cid0 *pfcid;
+ int code = font_param(op - 1, &pfont);
+ gs_glyph_data_t gdata;
+ int fidx;
+
+ if (code < 0)
+ return code;
+ if (pfont->FontType != ft_CID_encrypted)
+ return_error(gs_error_invalidfont);
+ check_type(*op, t_integer);
+ pfcid = (gs_font_cid0 *)pfont;
+ gdata.memory = pfont->memory;
+ code = pfcid->cidata.glyph_data((gs_font_base *)pfcid,
+ (gs_glyph)(gs_min_cid_glyph + op->value.intval),
+ &gdata, &fidx);
+
+ /* return code; original error-sensitive & fragile code */
+ if (code < 0) { /* failed to load glyph data, put CID 0 */
+ ps_int default_fallback_CID = 0 ;
+
+ if_debug2m('J', imemory, "[J]ztype9cidmap() use CID %" PRIpsint " instead of glyph-missing CID %" PRIpsint "\n", default_fallback_CID, op->value.intval);
+
+ op->value.intval = default_fallback_CID;
+
+ /* reload glyph for default_fallback_CID */
+
+ code = pfcid->cidata.glyph_data((gs_font_base *)pfcid,
+ (gs_glyph)(gs_min_cid_glyph + default_fallback_CID),
+ &gdata, &fidx);
+
+ if (code < 0) {
+ if_debug1m('J', imemory, "[J]ztype9cidmap() could not load default glyph (CID %" PRIpsint ")\n", op->value.intval);
+ return_error(gs_error_invalidfont);
+ }
+
+ }
+
+ /****** FOLLOWING IS NOT GENERAL W.R.T. ALLOCATION OF GLYPH DATA ******/
+ make_const_string(op - 1,
+ a_readonly | imemory_space((gs_ref_memory_t *)pfont->memory),
+ gdata.bits.size,
+ gdata.bits.data);
+ make_int(op, fidx);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfcid0_op_defs[] =
+{
+ {"2.buildfont9", zbuildfont9},
+ {"2.type9mapcid", ztype9mapcid},
+ op_def_end(0)
+};
diff --git a/psi/zfcid1.c b/psi/zfcid1.c
new file mode 100644
index 000000000..be8cf618a
--- /dev/null
+++ b/psi/zfcid1.c
@@ -0,0 +1,585 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* CIDFontType 1 and 2 operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsmatrix.h"
+#include "gsccode.h"
+#include "gsstruct.h"
+#include "gsgcache.h"
+#include "gsutil.h"
+#include "gxfcid.h"
+#include "gxfcache.h"
+#include "bfont.h"
+#include "icid.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifcid.h"
+#include "ichar1.h"
+#include "ifont42.h"
+#include "store.h"
+#include "stream.h"
+#include "files.h"
+
+/* ---------------- CIDFontType 1 (FontType 10) ---------------- */
+
+/* <string|name> <font_dict> .buildfont10 <string|name> <font> */
+static int
+zbuildfont10(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ build_proc_refs build;
+ int code = build_gs_font_procs(op, &build);
+ gs_cid_system_info_t cidsi;
+ gs_font_base *pfont;
+
+ if (code < 0)
+ return code;
+ code = cid_font_system_info_param(&cidsi, op);
+ if (code < 0)
+ return code;
+ make_null(&build.BuildChar); /* only BuildGlyph */
+ code = build_gs_simple_font(i_ctx_p, op, &pfont, ft_CID_user_defined,
+ &st_gs_font_cid1, &build,
+ bf_Encoding_optional |
+ bf_UniqueID_ignored);
+ if (code < 0)
+ return code;
+ ((gs_font_cid1 *)pfont)->cidata.CIDSystemInfo = cidsi;
+ return define_gs_font(i_ctx_p, (gs_font *)pfont);
+}
+
+/* ---------------- CIDFontType 2 (FontType 11) ---------------- */
+
+/* ------ Accessing ------ */
+
+/* Map a glyph CID to a TrueType glyph number using the CIDMap. */
+static int
+z11_CIDMap_proc(gs_font_cid2 *pfont, gs_glyph glyph)
+{
+ const ref *pcidmap = &pfont_data(pfont)->u.type42.CIDMap;
+ ulong cid = glyph - gs_min_cid_glyph;
+ int gdbytes = pfont->cidata.common.GDBytes;
+ int gnum = 0;
+ const byte *data;
+ int i, code;
+ ref rcid;
+ ref *prgnum;
+
+ switch (r_type(pcidmap)) {
+ case t_string:
+ if (cid >= r_size(pcidmap) / gdbytes)
+ return_error(gs_error_rangecheck);
+ data = pcidmap->value.const_bytes + cid * gdbytes;
+ break;
+ case t_integer:
+ return cid + pcidmap->value.intval;
+ case t_dictionary:
+ make_int(&rcid, cid);
+ code = dict_find(pcidmap, &rcid, &prgnum);
+ if (code <= 0)
+ return (code < 0 ? code : gs_note_error(gs_error_undefined));
+ if (!r_has_type(prgnum, t_integer))
+ return_error(gs_error_typecheck);
+ return prgnum->value.intval;
+ default: /* array type */
+ code = string_array_access_proc(pfont->memory, pcidmap, 1, cid * gdbytes,
+ gdbytes, NULL, NULL, &data);
+
+ if (code < 0)
+ return code;
+ if ( code > 0 )
+ return_error(gs_error_invalidfont);
+ }
+ for (i = 0; i < gdbytes; ++i)
+ gnum = (gnum << 8) + data[i];
+ if (gnum >= pfont->data.trueNumGlyphs)
+ return_error(gs_error_invalidfont);
+ return gnum;
+}
+
+/* Handle MetricsCount when accessing outline or metrics information. */
+static int
+z11_get_outline(gs_font_type42 * pfont, uint glyph_index,
+ gs_glyph_data_t *pgd)
+{
+ gs_font_cid2 *const pfcid = (gs_font_cid2 *)pfont;
+ int skip = pfcid->cidata.MetricsCount << 1;
+ int code = pfcid->cidata.orig_procs.get_outline(pfont, glyph_index, pgd);
+
+ if (code >= 0) {
+ uint size = pgd->bits.size;
+
+ if (size <= skip) {
+ gs_glyph_data_free(pgd, "z11_get_outline");
+ gs_glyph_data_from_null(pgd);
+ } else {
+ gs_glyph_data_substring(pgd, skip, size - skip);
+ }
+ }
+ return code;
+}
+
+#define GET_U16_MSB(p) (((uint)((p)[0]) << 8) + (p)[1])
+#define GET_S16_MSB(p) (int)((GET_U16_MSB(p) ^ 0x8000) - 0x8000)
+
+static int
+z11_get_metrics(gs_font_type42 * pfont, uint glyph_index,
+ gs_type42_metrics_options_t options, float sbw[4])
+{
+ gs_font_cid2 *const pfcid = (gs_font_cid2 *)pfont;
+ int skip = pfcid->cidata.MetricsCount << 1;
+ gs_glyph_data_t gdata;
+ const byte *pmetrics;
+ int lsb, width;
+ int code = 0;
+ int wmode = gs_type42_metrics_options_wmode(options);
+
+ gdata.memory = pfont->memory;
+ if (wmode >= skip >> 2 ||
+ (code = pfcid->cidata.orig_procs.get_outline(pfont, glyph_index, &gdata)) < 0 ||
+ gdata.bits.size < skip
+ )
+ return pfcid->cidata.orig_procs.get_metrics(pfont, glyph_index, options, sbw);
+ if(gs_type42_metrics_options_bbox_requested(options)) {
+ code = pfcid->cidata.orig_procs.get_metrics(pfont, glyph_index,
+ gs_type42_metrics_options_BBOX, sbw);;
+ if (code < 0)
+ return code;
+ }
+ if (gs_type42_metrics_options_sbw_requested(options)) {
+ pmetrics = gdata.bits.data + skip - 4 - (wmode << 2);
+ lsb = GET_S16_MSB(pmetrics + 2);
+ width = GET_U16_MSB(pmetrics + 0);
+ {
+ double factor = 1.0 / pfont->data.unitsPerEm;
+
+ if (wmode) {
+ sbw[0] = 0, sbw[1] = -lsb * factor;
+ sbw[2] = 0, sbw[3] = -width * factor;
+ } else {
+ sbw[0] = lsb * factor, sbw[1] = 0;
+ sbw[2] = width * factor, sbw[3] = 0;
+ }
+ }
+ }
+ gs_glyph_data_free(&gdata, "z11_get_metrics");
+ return 0;
+}
+
+static int
+z11_glyph_info_aux(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
+ int members, gs_glyph_info_t *info)
+{
+ gs_font_cid2 *fontCID2 = (gs_font_cid2 *)font;
+ uint glyph_index;
+ int code = (glyph > GS_MIN_GLYPH_INDEX
+ ? glyph - GS_MIN_GLYPH_INDEX
+ : fontCID2->cidata.CIDMap_proc(fontCID2, glyph));
+
+ if(code < 0)
+ return code;
+ glyph_index = (uint)code;
+ return gs_type42_glyph_info_by_gid(font, glyph, pmat, members, info, glyph_index);
+}
+
+static int
+z11_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
+ int members, gs_glyph_info_t *info)
+{
+ int wmode = (members & GLYPH_INFO_WIDTH0 ? 0 : 1);
+
+ return z1_glyph_info_generic(font, glyph, pmat, members, info,
+ &z11_glyph_info_aux, wmode);
+}
+
+/* Enumerate glyphs (keys) from GlyphDirectory instead of loca / glyf. */
+static int
+z11_enumerate_glyph(gs_font *font, int *pindex,
+ gs_glyph_space_t glyph_space, gs_glyph *pglyph)
+{
+ gs_font_cid2 *pfont = (gs_font_cid2 *)font;
+ int code0 = z11_CIDMap_proc(pfont, GS_MIN_CID_GLYPH);
+ int code;
+
+ if(*pindex > pfont->cidata.common.CIDCount)
+ return_error(gs_error_rangecheck);
+
+ for (;;) {
+ code = z11_CIDMap_proc(pfont, GS_MIN_CID_GLYPH + *pindex);
+
+ if (code < 0) {
+ *pindex = 0;
+ return 0;
+ }
+ (*pindex)++;
+ if (*pindex == 1 || code != code0)
+ break;
+ /* else skip an underfined glyph */
+ }
+ if (glyph_space == GLYPH_SPACE_INDEX)
+ *pglyph = GS_MIN_GLYPH_INDEX + (uint)code;
+ else
+ *pglyph = GS_MIN_CID_GLYPH + (uint)(*pindex - 1);
+ return 0;
+}
+
+static uint
+z11_get_glyph_index(gs_font_type42 *pfont, gs_glyph glyph)
+{
+ int code = z11_CIDMap_proc((gs_font_cid2 *)pfont, glyph);
+
+ return (code < 0 ? 0 /* notdef */: (uint)code);
+}
+
+static int
+z11_glyph_outline(gs_font *font, int WMode, gs_glyph glyph, const gs_matrix *pmat,
+ gx_path *ppath, double sbw[4])
+{
+ return gs_type42_glyph_outline(font, WMode,
+ z11_get_glyph_index((gs_font_type42 *)font, glyph) + GS_MIN_GLYPH_INDEX,
+ pmat, ppath, sbw);
+}
+
+static int
+get_subst_CID_on_WMode(gs_subst_CID_on_WMode_t *subst, ref *t, int WMode)
+{
+ ref r, *a, e;;
+
+ make_int(&r, WMode);
+ if (dict_find(t, &r, &a) > 0 && r_type(a) == t_array) {
+ int n = r_size(a), i;
+ uint *s;
+
+ s = (uint *)gs_alloc_byte_array(subst->rc.memory, n, sizeof(int), "zbuildfont11");
+ if (s == NULL)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < n; i++) {
+ array_get(subst->rc.memory, a, (long)i, &e);
+ if (r_type(&e) != t_integer)
+ return_error(gs_error_invalidfont);
+ s[i] = e.value.intval;
+ }
+ subst->data[WMode] = s;
+ subst->size[WMode] = n;
+ }
+ return 0;
+}
+
+static GS_NOTIFY_PROC(release_subst_CID_on_WMode);
+
+static int
+release_subst_CID_on_WMode(void *data, void *event)
+{
+ gs_font_cid2 *pfcid = (gs_font_cid2 *)data;
+ gs_subst_CID_on_WMode_t *subst = pfcid->subst_CID_on_WMode;
+
+ gs_font_notify_unregister((gs_font *)pfcid, release_subst_CID_on_WMode, data);
+ pfcid->subst_CID_on_WMode = NULL;
+ rc_adjust(subst, -2, "release_subst_CID_on_WMode");
+ return 0;
+}
+
+static uint
+font11_substitute_glyph_index_vertical(gs_font_type42 *pfont, uint glyph_index,
+ int WMode, gs_glyph glyph)
+{
+ gs_font_cid2 *pfcid = (gs_font_cid2 *)pfont;
+ uint cid = (glyph >= GS_MIN_CID_GLYPH ? glyph - GS_MIN_CID_GLYPH : glyph);
+ int WMode1 = !WMode;\
+ gs_subst_CID_on_WMode_t *s = pfcid->subst_CID_on_WMode;
+
+ if (s != NULL) {
+ uint *subst = s->data[WMode1];
+ int bi, ei, i;
+
+ /* Binary search for cid in subst (2-int elements) : */
+ bi = 0;
+ ei = pfcid->subst_CID_on_WMode->size[WMode1];
+
+ if (ei > 0) {
+ for (;;) {
+ i = ((bi + ei) / 2) & ~1;
+ if (subst[i] == cid) {
+ WMode = WMode1;
+ break;
+ }
+ if (bi + 2 >= ei)
+ break;
+ if (subst[i] > cid)
+ ei = i;
+ else
+ bi = i;
+ }
+ }
+ }
+ return gs_type42_substitute_glyph_index_vertical(pfont, glyph_index, WMode, glyph);
+}
+
+/* ------ Defining ------ */
+
+extern_st(st_subst_CID_on_WMode);
+
+/* <string|name> <font_dict> .buildfont11 <string|name> <font> */
+static int
+zbuildfont11(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_font_cid_data common;
+ gs_font_type42 *pfont;
+ gs_font_cid2 *pfcid;
+ int MetricsCount;
+ ref rcidmap, ignore_gdir, file, *pfile, cfnstr, *pCIDFontName, CIDFontName, *t;
+ ulong loca_glyph_pos[2][2];
+ int code = cid_font_data_param(op, &common, &ignore_gdir);
+
+ if (code < 0 ||
+ (code = dict_find_string(op, "CIDFontName", &pCIDFontName)) <= 0 ||
+ (code = dict_int_param(op, "MetricsCount", 0, 4, 0, &MetricsCount)) < 0
+ )
+ return code;
+ /*
+ * Since build_gs_simple_font may resize the dictionary and cause
+ * pointers to become invalid, save CIDFontName
+ */
+ CIDFontName = *pCIDFontName;
+ if (MetricsCount & 1) /* only allowable values are 0, 2, 4 */
+ return_error(gs_error_rangecheck);
+ code = dict_find_string(op, "File", &pfile);
+ if (code < 0 && code != gs_error_undefined)
+ return code;
+ if (code > 0) {
+ ref *file_table_pos, *a, v;
+ const char *name[2] = {"loca", "glyf"};
+ int i, j;
+
+ /*
+ * Since build_gs_simple_font may resize the dictionary and cause
+ * pointers to become invalid, save File and CIDFontName
+ */
+ file = *pfile;
+ check_read_type(file, t_file);
+ code = dict_find_string(op, "file_table_pos", &file_table_pos);
+ if (code <= 0 || r_type(file_table_pos) != t_dictionary)
+ return_error(gs_error_invalidfont);
+ for (i = 0; i < 2; i++) {
+ code = dict_find_string(file_table_pos, name[i], &a);
+ if (code <= 0 || r_type(a) != t_array)
+ return_error(gs_error_invalidfont);
+ for (j = 0; j < 2; j++) {
+ code = array_get(imemory, a, j, &v);
+ if (code < 0 || r_type(&v) != t_integer)
+ return_error(gs_error_invalidfont);
+ loca_glyph_pos[i][j] = v.value.intval;
+ }
+ }
+ } else
+ pfile = NULL;
+ code = font_string_array_param(imemory, op, "CIDMap", &rcidmap);
+ switch (code) {
+ case 0: /* in PLRM3 */
+ gdb:
+ /* GDBytes is required for indexing a string or string array. */
+ if (common.GDBytes == 0)
+ return_error(gs_error_rangecheck);
+ break;
+ default:
+ return code;
+ case gs_error_typecheck:
+ switch (r_type(&rcidmap)) {
+ case t_string: /* in PLRM3 */
+ goto gdb;
+ case t_dictionary: /* added in 3011 */
+ case t_integer: /* added in 3011 */
+ break;
+ default:
+ return code;
+ }
+ break;
+ }
+ code = build_gs_TrueType_font(i_ctx_p, op, &pfont, ft_CID_TrueType,
+ &st_gs_font_cid2,
+ (const char *)0, "%Type11BuildGlyph",
+ bf_Encoding_optional |
+ bf_UniqueID_ignored |
+ bf_CharStrings_optional |
+ (pfile != NULL ? bf_has_font_file : 0));
+ if (code < 0)
+ return code;
+ pfcid = (gs_font_cid2 *)pfont;
+ if (dict_find_string(op, "subst_CID_on_WMode", &t) > 0 && r_type(t) == t_dictionary) {
+ gs_subst_CID_on_WMode_t *subst = NULL;
+ ref *o;
+ gs_font *font;
+
+ if (dict_find_string(t, "Ordering", &o) <= 0 || r_type(o) != t_string)
+ return_error(gs_error_invalidfont);
+ for (font = ifont_dir->orig_fonts; font != NULL; font = font->next) {
+ if (font->FontType == ft_CID_TrueType) {
+ gs_font_cid2 *pfcid1 = (gs_font_cid2 *)font;
+ if (pfcid1->subst_CID_on_WMode != NULL &&
+ /* We want the subst_CID_on_WMode to exist in same local/global
+ * VM as the CIDFont object that will hold a reference to it
+ */
+ pfcid1->memory == pfcid->memory &&
+ bytes_compare(o->value.const_bytes, r_size(o),
+ pfcid1->cidata.common.CIDSystemInfo.Ordering.data,
+ pfcid1->cidata.common.CIDSystemInfo.Ordering.size)) {
+ subst = pfcid1->subst_CID_on_WMode;
+ break;
+ }
+ }
+ }
+
+ if (subst == NULL) {
+ rc_alloc_struct_1(subst, gs_subst_CID_on_WMode_t, &st_subst_CID_on_WMode,
+ pfcid->memory, return_error(gs_error_VMerror), "zbuildfont11");
+ subst->data[0] = subst->data[1] = 0;
+ pfcid->subst_CID_on_WMode = subst;
+ code = get_subst_CID_on_WMode(subst, t, 0);
+ if (code < 0)
+ return code;
+ code = get_subst_CID_on_WMode(subst, t, 1);
+ if (code < 0)
+ return code;
+ } else {
+ pfcid->subst_CID_on_WMode = subst;
+ rc_increment(subst);
+ }
+ code = gs_font_notify_register((gs_font *)pfcid, release_subst_CID_on_WMode, (void *)pfcid);
+ if (code < 0)
+ return code;
+ rc_increment(subst);
+ }
+ pfcid->cidata.common = common;
+ pfcid->cidata.MetricsCount = MetricsCount;
+ ref_assign(&pfont_data(pfont)->u.type42.CIDMap, &rcidmap);
+ pfcid->cidata.CIDMap_proc = z11_CIDMap_proc;
+ pfcid->data.substitute_glyph_index_vertical = font11_substitute_glyph_index_vertical;
+ pfont->procs.enumerate_glyph = z11_enumerate_glyph;
+ pfont->procs.glyph_info = z11_glyph_info;
+ pfont->procs.glyph_outline = z11_glyph_outline;
+ pfont->data.get_glyph_index = z11_get_glyph_index;
+ if (pfcid->font_name.size == 0) {
+ get_font_name(imemory, &cfnstr, &CIDFontName);
+ copy_font_name(&pfcid->font_name, &cfnstr);
+ }
+ if (MetricsCount) {
+ /* "Wrap" the glyph accessor procedures. */
+ pfcid->cidata.orig_procs.get_outline = pfont->data.get_outline;
+ pfont->data.get_outline = z11_get_outline;
+ pfcid->cidata.orig_procs.get_metrics = pfont->data.get_metrics;
+ pfont->data.get_metrics = z11_get_metrics;
+ } else if(pfile != NULL) {
+ /*
+ * We assume that disk fonts has no MetricsCount.
+ * We could do not, but the number of virtual function wariants increases.
+ */
+ stream *s;
+
+ check_read_file(i_ctx_p, s, &file);
+ pfont->data.loca = loca_glyph_pos[0][0];
+ pfont->data.glyf = loca_glyph_pos[1][0];
+ pfont->data.get_outline = gs_get_glyph_data_cached;
+ pfont->data.gdcache = gs_glyph_cache__alloc(pfont, s, gs_type42_get_outline_from_TT_file);
+ }
+ return define_gs_font(i_ctx_p, (gs_font *)pfont);
+}
+
+/* <cid11font> <cid> .type11mapcid <glyph_index> */
+static int
+ztype11mapcid(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_font *pfont;
+ int code = font_param(op - 1, &pfont);
+
+ if (code < 0)
+ return code;
+ check_type(*op, t_integer);
+#if defined(TEST)
+ /* Allow a Type 42 font here, for testing .wrapfont. */
+ if (pfont->FontType == ft_TrueType) {
+ /* Use the CID as the glyph index. */
+ if (op->value.intval < 0 ||
+ op->value.intval >= ((gs_font_type42 *)pfont)->data.numGlyphs
+ )
+ return_error(gs_error_rangecheck);
+ code = (int)op->value.intval;
+ } else
+#endif
+ {
+ if (pfont->FontType != ft_CID_TrueType)
+ return_error(gs_error_invalidfont);
+ code = z11_CIDMap_proc((gs_font_cid2 *)pfont,
+ (gs_glyph)(gs_min_cid_glyph + op->value.intval));
+ }
+ if (code < 0)
+ return code;
+ make_int(op - 1, code);
+ pop(1);
+ return 0;
+}
+
+/* <Decoding> <TT_cmap> <SubstNWP> <GDBytes> <CIDMap> .fillCIDMap - */
+static int
+zfillCIDMap(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *Decoding = op - 4, *TT_cmap = op - 3, *SubstNWP = op - 2,
+ *GDBytes = op - 1, *CIDMap = op;
+ int code;
+
+ check_type(*Decoding, t_dictionary);
+ check_type(*TT_cmap, t_dictionary);
+ check_type(*SubstNWP, t_array);
+ check_type(*GDBytes, t_integer);
+ check_type(*CIDMap, t_array);
+ code = cid_fill_CIDMap(imemory, Decoding, TT_cmap, SubstNWP, GDBytes->value.intval, CIDMap);
+ pop(5);
+ return code;
+}
+
+static int
+zfillIdentityCIDMap(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ /* ref *Decoding = op - 4; */
+ /* ref *TT_cmap = op - 3; */
+ /* ref *SubstNWP = op - 2; */
+ /* ref *GDBytes = op - 1; */
+ ref *CIDMap = op;
+ int code;
+
+ check_type(*CIDMap, t_array);
+ code = cid_fill_Identity_CIDMap(imemory, CIDMap);
+ pop(1);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfcid1_op_defs[] =
+{
+ {"2.buildfont10", zbuildfont10},
+ {"2.buildfont11", zbuildfont11},
+ {"2.type11mapcid", ztype11mapcid},
+ {"2.fillCIDMap", zfillCIDMap},
+ {"2.fillIdentityCIDMap", zfillIdentityCIDMap},
+ op_def_end(0)
+};
diff --git a/psi/zfcmap.c b/psi/zfcmap.c
new file mode 100644
index 000000000..c4777f565
--- /dev/null
+++ b/psi/zfcmap.c
@@ -0,0 +1,488 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* CMap creation operator */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsmatrix.h" /* for gxfont.h */
+#include "gsstruct.h"
+#include "gsutil.h" /* for bytes_compare */
+#include "gxfcmap1.h"
+#include "gxfont.h"
+#include "ialloc.h"
+#include "icid.h"
+#include "iddict.h"
+#include "idparam.h"
+#include "ifont.h" /* for zfont_mark_glyph_name */
+#include "iname.h"
+#include "store.h"
+
+/* Exported for zfont0.c */
+int ztype0_get_cmap(const gs_cmap_t ** ppcmap, const ref * pfdepvector,
+ const ref * op, gs_memory_t *imem);
+
+/*
+ * Define whether to check the compatibility of CIDSystemInfo between the
+ * CMap and the composite font. PLRM2 says compatibility is required, but
+ * PLRM3 says the interpreter doesn't check it.
+ */
+/*#define CHECK_CID_SYSTEM_INFO_COMPATIBILITY*/
+
+/* ---------------- Internal procedures ---------------- */
+
+/* Free a code map in case of VMerror. */
+static void
+free_code_map(gx_code_map_t * pcmap, gs_memory_t * mem)
+{
+ if (pcmap->lookup) {
+ int i;
+
+ for (i = 0; i < pcmap->num_lookup; ++i) {
+ gx_cmap_lookup_range_t *pclr = &pcmap->lookup[i];
+
+ if (pclr->value_type == CODE_VALUE_GLYPH)
+ gs_free_string(mem, pclr->values.data, pclr->values.size,
+ "free_code_map(values)");
+ }
+ gs_free_object(mem, pcmap->lookup, "free_code_map(map)");
+ }
+}
+
+/* Convert code ranges to internal form. */
+static int
+acquire_code_ranges(gs_cmap_adobe1_t *cmap, const ref *pref, gs_memory_t *mem)
+{
+ uint num_ranges = 0;
+ gx_code_space_range_t *ranges;
+ uint i, j, elem_sz;
+ ref elem;
+
+ if (!r_is_array(pref))
+ return_error(gs_error_rangecheck);
+ for (i=0; i < r_size(pref); i++) {
+ int code = array_get(mem, pref, i, &elem);
+ if (code < 0)
+ return code;
+ elem_sz = r_size(&elem);
+ if (elem_sz & 1)
+ return_error(gs_error_rangecheck);
+ num_ranges += elem_sz;
+ }
+ if (num_ranges == 0)
+ return_error(gs_error_rangecheck);
+ num_ranges >>= 1;
+
+ ranges = (gx_code_space_range_t *)
+ gs_alloc_byte_array(mem, num_ranges, sizeof(gx_code_space_range_t),
+ "acquire_code_ranges");
+ if (ranges == 0)
+ return_error(gs_error_VMerror);
+ cmap->code_space.ranges = ranges;
+ cmap->code_space.num_ranges = num_ranges;
+
+ for (i = 0; i < r_size(pref); i++) {
+ array_get(mem, pref, i, &elem);
+ elem_sz = r_size(&elem);
+ for (j = 0; j < elem_sz; j += 2) {
+ ref rfirst, rlast;
+ int size;
+
+ array_get(mem, &elem, j, &rfirst);
+ array_get(mem, &elem, j + 1, &rlast);
+ if (!r_has_type(&rfirst, t_string) ||
+ !r_has_type(&rlast, t_string) ||
+ (size = r_size(&rfirst)) == 0 || size > MAX_CMAP_CODE_SIZE ||
+ r_size(&rlast) != size ||
+ memcmp(rfirst.value.bytes, rlast.value.bytes, size) > 0)
+ return_error(gs_error_rangecheck);
+ memcpy(ranges->first, rfirst.value.bytes, size);
+ memcpy(ranges->last, rlast.value.bytes, size);
+ ranges->size = size;
+ ++ranges;
+ }
+ }
+ return 0;
+}
+
+/* Convert a code map to internal form. */
+static int
+acquire_code_map(gx_code_map_t *pcmap, const ref *pref, gs_cmap_adobe1_t *root,
+ gs_memory_t *mem)
+{
+ uint num_lookup = 0;
+ gx_cmap_lookup_range_t *pclr;
+ long i;
+ ref elem;
+ uint elem_sz;
+
+ if (!r_is_array(pref))
+ return_error(gs_error_rangecheck);
+ for (i=0; i < r_size(pref); i++) {
+ int code = array_get(mem, pref, i, &elem);
+ if (code < 0)
+ return code;
+ elem_sz = r_size(&elem);
+ if (elem_sz % 5 != 0)
+ return_error(gs_error_rangecheck);
+ num_lookup += elem_sz;
+ }
+ num_lookup /= 5;
+ pclr = gs_alloc_struct_array(mem, num_lookup, gx_cmap_lookup_range_t,
+ &st_cmap_lookup_range_element,
+ "acquire_code_map(lookup ranges)");
+ if (pclr == 0)
+ return_error(gs_error_VMerror);
+ memset(pclr, 0, sizeof(*pclr) * num_lookup);
+ pcmap->lookup = pclr;
+ pcmap->num_lookup = num_lookup;
+
+ for (i = 0; i < r_size(pref); i++) {
+ uint j;
+ array_get(mem, pref, i, &elem);
+ elem_sz = r_size(&elem);
+ for (j = 0; j < elem_sz; j += 5) {
+ ref rprefix, rmisc, rkeys, rvalues, rfxs;
+
+ array_get(mem, &elem, j, &rprefix);
+ array_get(mem, &elem, j + 1, &rmisc);
+ array_get(mem, &elem, j + 2, &rkeys);
+ array_get(mem, &elem, j + 3, &rvalues);
+ array_get(mem, &elem, j + 4, &rfxs);
+
+ if (!r_has_type(&rprefix, t_string) ||
+ !r_has_type(&rmisc, t_string) ||
+ !r_has_type(&rkeys, t_string) ||
+ !(r_has_type(&rvalues, t_string) || r_is_array(&rvalues)) ||
+ !r_has_type(&rfxs, t_integer)
+ )
+ return_error(gs_error_typecheck);
+ if (r_size(&rmisc) != 4 ||
+ rmisc.value.bytes[0] > MAX_CMAP_CODE_SIZE - r_size(&rprefix) ||
+ rmisc.value.bytes[1] > 1 ||
+ rmisc.value.bytes[2] > CODE_VALUE_MAX ||
+ rmisc.value.bytes[3] == 0)
+ return_error(gs_error_rangecheck);
+ pclr->cmap = root;
+ pclr->key_size = rmisc.value.bytes[0];
+ pclr->key_prefix_size = r_size(&rprefix);
+ memcpy(pclr->key_prefix, rprefix.value.bytes, pclr->key_prefix_size);
+ pclr->key_is_range = rmisc.value.bytes[1];
+ if (pclr->key_size == 0) {
+ /* This is a single entry consisting only of the prefix. */
+ if (r_size(&rkeys) != 0)
+ return_error(gs_error_rangecheck);
+ pclr->num_entries = 1;
+ } else {
+ int step = pclr->key_size * (pclr->key_is_range ? 2 : 1);
+
+ if (r_size(&rkeys) % step != 0)
+ return_error(gs_error_rangecheck);
+ pclr->num_entries = r_size(&rkeys) / step;
+ }
+ pclr->keys.data = rkeys.value.bytes,
+ pclr->keys.size = r_size(&rkeys);
+ pclr->value_type = rmisc.value.bytes[2];
+ pclr->value_size = rmisc.value.bytes[3];
+ if (r_has_type(&rvalues, t_string)) {
+ if (pclr->value_type == CODE_VALUE_GLYPH)
+ return_error(gs_error_rangecheck);
+ if (r_size(&rvalues) % pclr->num_entries != 0 ||
+ r_size(&rvalues) / pclr->num_entries != pclr->value_size)
+ return_error(gs_error_rangecheck);
+ pclr->values.data = rvalues.value.bytes,
+ pclr->values.size = r_size(&rvalues);
+ } else {
+ uint values_size = pclr->num_entries * pclr->value_size;
+ long k;
+ byte *pvalue;
+
+ if (pclr->value_type != CODE_VALUE_GLYPH ||
+ r_size(&rvalues) != pclr->num_entries ||
+ pclr->value_size > sizeof(gs_glyph))
+ return_error(gs_error_rangecheck);
+ pclr->values.data = gs_alloc_string(mem, values_size,
+ "acquire_code_map(values)");
+ if (pclr->values.data == 0)
+ return_error(gs_error_VMerror);
+ pclr->values.size = values_size;
+ pvalue = pclr->values.data;
+ for (k = 0; k < pclr->num_entries; ++k) {
+ ref rvalue;
+ gs_glyph value;
+ int i;
+
+ array_get(mem, &rvalues, k, &rvalue);
+ if (!r_has_type(&rvalue, t_name))
+ return_error(gs_error_rangecheck);
+ value = name_index(mem, &rvalue);
+ /*
+ * We need a special check here because some CPUs cannot
+ * shift by the full size of an int or long.
+ */
+ if (pclr->value_size < sizeof(value) &&
+ (value >> (pclr->value_size * 8)) != 0
+ )
+ return_error(gs_error_rangecheck);
+ for (i = pclr->value_size; --i >= 0; )
+ *pvalue++ = (byte)(value >> (i * 8));
+ }
+ }
+ check_int_leu_only(rfxs, 0xff);
+ pclr->font_index = (int)rfxs.value.intval;
+ ++pclr;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Acquire the CIDSystemInfo array from a dictionary. If missing, fabricate
+ * a 0-element array and return 1.
+ */
+static int
+acquire_cid_system_info(ref *psia, const ref *op)
+{
+ ref *prcidsi;
+
+ if (dict_find_string(op, "CIDSystemInfo", &prcidsi) <= 0) {
+ make_empty_array(psia, a_readonly);
+ return 1;
+ }
+ if (r_has_type(prcidsi, t_dictionary)) {
+ make_array(psia, a_readonly, 1, prcidsi);
+ return 0;
+ }
+ if (!r_is_array(prcidsi))
+ return_error(gs_error_typecheck);
+ *psia = *prcidsi;
+ return 0;
+}
+
+/*
+ * Get one element of a CIDSystemInfo array. If the element is missing or
+ * null, return 1.
+ */
+static int
+get_cid_system_info(const gs_memory_t *mem, gs_cid_system_info_t *pcidsi, const ref *psia, uint index)
+{
+ ref rcidsi;
+ int code = array_get(mem, psia, (long)index, &rcidsi);
+
+ if (code < 0 || r_has_type(&rcidsi, t_null)) {
+ cid_system_info_set_null(pcidsi);
+ return 1;
+ }
+ return cid_system_info_param(pcidsi, &rcidsi);
+}
+
+#ifdef CHECK_CID_SYSTEM_INFO_COMPATIBILITY
+
+/* Check compatibility of CIDSystemInfo. */
+static bool
+bytes_eq(const gs_const_string *pcs1, const gs_const_string *pcs2)
+{
+ return !bytes_compare(pcs1->data, pcs1->size,
+ pcs2->data, pcs2->size);
+}
+static bool
+cid_system_info_compatible(const gs_cid_system_info_t * psi1,
+ const gs_cid_system_info_t * psi2)
+{
+ return bytes_eq(&psi1->Registry, &psi2->Registry) &&
+ bytes_eq(&psi1->Ordering, &psi2->Ordering);
+}
+
+#endif /* CHECK_CID_SYSTEM_INFO_COMPATIBILITY */
+
+/* ---------------- (Semi-)public procedures ---------------- */
+
+/* Get the CodeMap from a Type 0 font, and check the CIDSystemInfo of */
+/* its subsidiary fonts. */
+int
+ztype0_get_cmap(const gs_cmap_t **ppcmap, const ref *pfdepvector,
+ const ref *op, gs_memory_t *imem)
+{
+ ref *prcmap;
+ ref *pcodemap;
+ const gs_cmap_t *pcmap;
+ int code;
+ uint num_fonts;
+ uint i;
+
+ /*
+ * We have no way of checking whether the CodeMap is a concrete
+ * subclass of gs_cmap_t, so we just check that it is in fact a
+ * t_struct and is large enough.
+ */
+ if (dict_find_string(op, "CMap", &prcmap) <= 0 ||
+ !r_has_type(prcmap, t_dictionary) ||
+ dict_find_string(prcmap, "CodeMap", &pcodemap) <= 0 ||
+ !r_is_struct(pcodemap) ||
+ gs_object_size(imem, r_ptr(pcodemap, gs_cmap_t)) < sizeof(gs_cmap_t)
+ )
+ return_error(gs_error_invalidfont);
+ pcmap = r_ptr(pcodemap, gs_cmap_t);
+ num_fonts = r_size(pfdepvector);
+ for (i = 0; i < num_fonts; ++i) {
+ ref rfdep, rfsi;
+
+ array_get(imem, pfdepvector, (long)i, &rfdep);
+ code = acquire_cid_system_info(&rfsi, &rfdep);
+ if (code < 0)
+ return code;
+ if (code == 0) {
+ if (r_size(&rfsi) != 1)
+ return_error(gs_error_rangecheck);
+#ifdef CHECK_CID_SYSTEM_INFO_COMPATIBILITY
+ {
+ gs_cid_system_info_t cidsi;
+
+ get_cid_system_info(&cidsi, &rfsi, 0);
+ if (!cid_system_info_is_null(&cidsi) &&
+ !cid_system_info_compatible(&cidsi,
+ pcmap->CIDSystemInfo + i))
+ return_error(gs_error_rangecheck);
+ }
+#endif
+ }
+ }
+ *ppcmap = pcmap;
+ return 0;
+}
+
+/* ---------------- Operators ---------------- */
+
+/* <CMap> .buildcmap <CMap> */
+/*
+ * Create the internal form of a CMap. The initial CMap must be read-write
+ * and have an entry with key = CodeMap and value = null; the result is
+ * read-only and has a real CodeMap.
+ *
+ * This operator reads the CMapType, CMapName, CIDSystemInfo, CMapVersion,
+ * UIDOffset, XUID, WMode, and .CodeMapData elements of the CMap dictionary.
+ * For details, see lib/gs_cmap.ps and the Adobe documentation.
+ */
+static int
+zfcmap_glyph_name(const gs_memory_t *mem,
+ gs_glyph glyph, gs_const_string *pstr, void *proc_data)
+{
+ ref nref, nsref;
+
+ name_index_ref(mem, (uint)glyph, &nref);
+ name_string_ref(mem, &nref, &nsref);
+ pstr->data = nsref.value.const_bytes;
+ pstr->size = r_size(&nsref);
+ return 0;
+}
+static int
+zbuildcmap(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ ref *pcmapname;
+ ref *puidoffset;
+ ref *pcodemapdata;
+ ref *pcodemap;
+ ref rname, rcidsi, rcoderanges, rdefs, rnotdefs;
+ gs_cmap_adobe1_t *pcmap = 0;
+ ref rcmap;
+ uint i;
+
+ check_type(*op, t_dictionary);
+ check_dict_write(*op);
+ if ((code = dict_find_string(op, "CMapName", &pcmapname)) <= 0) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto fail;
+ }
+ if (!r_has_type(pcmapname, t_name)) {
+ code = gs_note_error(gs_error_typecheck);
+ goto fail;
+ }
+ name_string_ref(imemory, pcmapname, &rname);
+ if (dict_find_string(op, ".CodeMapData", &pcodemapdata) <= 0 ||
+ !r_has_type(pcodemapdata, t_array) ||
+ r_size(pcodemapdata) != 3 ||
+ dict_find_string(op, "CodeMap", &pcodemap) <= 0 ||
+ !r_has_type(pcodemap, t_null)
+ ) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto fail;
+ }
+ if ((code = acquire_cid_system_info(&rcidsi, op)) < 0)
+ goto fail;
+ if ((code = gs_cmap_adobe1_alloc(&pcmap, 0, rname.value.const_bytes,
+ r_size(&rname), r_size(&rcidsi),
+ 0, 0, 0, 0, 0, imemory)) < 0)
+ goto fail;
+ if ((code = dict_int_param(op, "CMapType", 0, 1, 0, &pcmap->CMapType)) < 0 ||
+ (code = dict_float_param(op, "CMapVersion", 0.0, &pcmap->CMapVersion)) < 0 ||
+ (code = dict_uid_param(op, &pcmap->uid, 0, imemory, i_ctx_p)) < 0 ||
+ (code = dict_int_param(op, "WMode", 0, 1, 0, &pcmap->WMode)) < 0
+ )
+ goto fail;
+ if (dict_find_string(op, "UIDOffset", &puidoffset) > 0) {
+ if (!r_has_type(puidoffset, t_integer)) {
+ code = gs_note_error(gs_error_typecheck);
+ goto fail;
+ }
+ pcmap->UIDOffset = puidoffset->value.intval; /* long, not int */
+ }
+ for (i = 0; i < r_size(&rcidsi); ++i) {
+ code = get_cid_system_info(imemory, pcmap->CIDSystemInfo + i, &rcidsi, i);
+ if (code < 0)
+ goto fail;
+ }
+ array_get(imemory, pcodemapdata, 0L, &rcoderanges);
+ array_get(imemory, pcodemapdata, 1L, &rdefs);
+ array_get(imemory, pcodemapdata, 2L, &rnotdefs);
+ if ((code = acquire_code_ranges(pcmap, &rcoderanges, imemory)) < 0)
+ goto fail;
+ if ((code = acquire_code_map(&pcmap->def, &rdefs, pcmap, imemory)) < 0)
+ goto fail;
+ if ((code = acquire_code_map(&pcmap->notdef, &rnotdefs, pcmap, imemory)) < 0)
+ goto fail;
+ if (!bytes_compare(pcmap->CIDSystemInfo->Registry.data, pcmap->CIDSystemInfo->Registry.size,
+ (const byte *)"Artifex", 7) &&
+ !bytes_compare(pcmap->CIDSystemInfo->Ordering.data, pcmap->CIDSystemInfo->Ordering.size,
+ (const byte *)"Unicode", 7))
+ pcmap->from_Unicode = true;
+ pcmap->mark_glyph = zfont_mark_glyph_name;
+ pcmap->mark_glyph_data = 0;
+ pcmap->glyph_name = zfcmap_glyph_name;
+ pcmap->glyph_name_data = 0;
+ make_istruct_new(&rcmap, a_readonly, pcmap);
+ code = idict_put_string(op, "CodeMap", &rcmap);
+ if (code < 0)
+ goto fail;
+ return zreadonly(i_ctx_p);
+fail:
+ if (pcmap) {
+ free_code_map(&pcmap->notdef, imemory);
+ free_code_map(&pcmap->def, imemory);
+ ifree_object(pcmap->CIDSystemInfo, "zbuildcmap(CIDSystemInfo)");
+ ifree_object(pcmap, "zbuildcmap(cmap)");
+ }
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfcmap_op_defs[] =
+{
+ {"1.buildcmap", zbuildcmap},
+ op_def_end(0)
+};
diff --git a/psi/zfdctd.c b/psi/zfdctd.c
new file mode 100644
index 000000000..f69465d0b
--- /dev/null
+++ b/psi/zfdctd.c
@@ -0,0 +1,115 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* DCTDecode filter creation */
+#include "memory_.h"
+#include "stdio_.h" /* for jpeglib.h */
+#include "jpeglib_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsmemory.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+#include "ialloc.h"
+#include "ifilter.h"
+#include "iparam.h"
+
+private_st_jpeg_decompress_data();
+
+/* Import the parameter processing procedure from sddparam.c */
+stream_state_proc_put_params(s_DCTD_put_params, stream_DCT_state);
+
+/* Find the memory that will be used for allocating the stream. */
+static gs_ref_memory_t *
+find_stream_memory(i_ctx_t *i_ctx_p, int npop, uint *space)
+{
+ uint use_space = max(*space, avm_global);
+ os_ptr sop = osp - npop;
+
+ if (r_has_type(sop, t_dictionary)) {
+ --sop;
+ }
+ *space = max(use_space, r_space(sop));
+
+ return idmemory->spaces_indexed[*space >> r_space_shift];
+}
+
+/* <source> <dict> DCTDecode/filter <file> */
+/* <source> DCTDecode/filter <file> */
+static int
+zDCTD(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_memory_t *mem;
+ stream_DCT_state state;
+ dict_param_list list;
+ jpeg_decompress_data *jddp;
+ int code;
+ const ref *dop;
+ uint dspace;
+
+ if (r_has_type(op, t_dictionary))
+ dop = op, dspace = r_space(op);
+ else
+ dop = 0, dspace = 0;
+ mem = (gs_memory_t *)find_stream_memory(i_ctx_p, 0, &dspace);
+ state.memory = mem;
+ /* First allocate space for IJG parameters. */
+ jddp = gs_alloc_struct_immovable(mem,jpeg_decompress_data,
+ &st_jpeg_decompress_data, "zDCTD");
+ if (jddp == 0)
+ return_error(gs_error_VMerror);
+ if (s_DCTD_template.set_defaults)
+ (*s_DCTD_template.set_defaults) ((stream_state *) & state);
+ state.data.decompress = jddp;
+ jddp->memory = state.jpeg_memory = mem; /* set now for allocation */
+ jddp->scanline_buffer = NULL; /* set this early for safe error exit */
+ state.report_error = filter_report_error; /* in case create fails */
+ if ((code = gs_jpeg_create_decompress(&state)) < 0)
+ goto fail; /* correct to do jpeg_destroy here */
+ /* Read parameters from dictionary */
+ if ((code = dict_param_list_read(&list, dop, NULL, false, iimemory)) < 0)
+ goto fail;
+ if ((code = s_DCTD_put_params((gs_param_list *) & list, &state)) < 0)
+ goto rel;
+ /* Create the filter. */
+ jddp->templat = s_DCTD_template;
+ code = filter_read(i_ctx_p, 0, &jddp->templat,
+ (stream_state *) & state, dspace);
+ if (code >= 0) /* Success! */
+ return code;
+ /*
+ * We assume that if filter_read fails, the stream has not been
+ * registered for closing, so s_DCTD_release will never be called.
+ * Therefore we free the allocated memory before failing.
+ */
+rel:
+ iparam_list_release(&list);
+fail:
+ gs_jpeg_destroy(&state);
+ gs_free_object(mem, jddp, "zDCTD fail");
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfdctd_op_defs[] =
+{
+ op_def_begin_filter(),
+ {"2DCTDecode", zDCTD},
+ op_def_end(0)
+};
diff --git a/psi/zfdcte.c b/psi/zfdcte.c
new file mode 100644
index 000000000..704e54f2e
--- /dev/null
+++ b/psi/zfdcte.c
@@ -0,0 +1,143 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* DCTEncode filter creation */
+#include "memory_.h"
+#include "stdio_.h" /* for jpeglib.h */
+#include "jpeglib_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsmemory.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "strimpl.h"
+#include "sdct.h"
+#include "sjpeg.h"
+#include "ifilter.h"
+#include "iparam.h"
+
+/*#define TEST*/
+/* Import the parameter processing procedure from sdeparam.c */
+stream_state_proc_put_params(s_DCTE_put_params, stream_DCT_state);
+#ifdef TEST
+stream_state_proc_get_params(s_DCTE_get_params, stream_DCT_state);
+#endif
+
+/* <target> <dict> DCTEncode/filter <file> */
+static int
+zDCTE(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_memory_t *mem = gs_memory_stable(imemory);
+ stream_DCT_state state;
+ dict_param_list list;
+ jpeg_compress_data *jcdp;
+ int code;
+ const ref *dop;
+ uint dspace;
+
+ /* First allocate space for IJG parameters. */
+ jcdp = gs_alloc_struct_immovable(mem, jpeg_compress_data,
+ &st_jpeg_compress_data, "zDCTE");
+ if (jcdp == 0)
+ return_error(gs_error_VMerror);
+ state.memory = mem;
+ if (s_DCTE_template.set_defaults)
+ (*s_DCTE_template.set_defaults) ((stream_state *) & state);
+ state.data.compress = jcdp;
+ jcdp->memory = state.jpeg_memory = mem; /* set now for allocation */
+ state.report_error = filter_report_error; /* in case create fails */
+ if ((code = gs_jpeg_create_compress(&state)) < 0)
+ goto fail; /* correct to do jpeg_destroy here */
+ /* Read parameters from dictionary */
+ if (r_has_type(op, t_dictionary))
+ dop = op, dspace = r_space(op);
+ else
+ dop = 0, dspace = 0;
+ if ((code = dict_param_list_read(&list, dop, NULL, false, iimemory)) < 0)
+ goto fail;
+ if ((code = s_DCTE_put_params((gs_param_list *) & list, &state)) < 0)
+ goto rel;
+ /* Create the filter. */
+ jcdp->templat = s_DCTE_template;
+ /* Make sure we get at least a full scan line of input. */
+ state.scan_line_size = jcdp->cinfo.input_components *
+ jcdp->cinfo.image_width;
+ state.icc_profile = NULL;
+ jcdp->templat.min_in_size =
+ max(s_DCTE_template.min_in_size, state.scan_line_size);
+ /* Make sure we can write the user markers in a single go. */
+ jcdp->templat.min_out_size =
+ max(s_DCTE_template.min_out_size, state.Markers.size);
+ code = filter_write(i_ctx_p, 0, &jcdp->templat,
+ (stream_state *) & state, dspace);
+ if (code >= 0) /* Success! */
+ return code;
+ /* We assume that if filter_write fails, the stream has not been
+ * registered for closing, so s_DCTE_release will never be called.
+ * Therefore we free the allocated memory before failing.
+ */
+rel:
+ iparam_list_release(&list);
+fail:
+ gs_jpeg_destroy(&state);
+ gs_free_object(mem, jcdp, "zDCTE fail");
+ return code;
+}
+
+#ifdef TEST
+#include "stream.h"
+#include "files.h"
+/* <dict> <filter> <bool> .dcteparams <dict> */
+static int
+zdcteparams(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+ dict_param_list list;
+ int code;
+
+ check_type(*op, t_boolean);
+ check_write_file(s, op - 1);
+ check_type(op[-2], t_dictionary);
+ /* The DCT filters copy the template.... */
+ if (s->state->templat->process != s_DCTE_template.process)
+ return_error(gs_error_rangecheck);
+ code = dict_param_list_write(&list, op - 2, NULL, iimemory);
+ if (code < 0)
+ return code;
+ code = s_DCTE_get_params((gs_param_list *) & list,
+ (stream_DCT_state *) s->state,
+ op->value.boolval);
+ iparam_list_release(&list);
+ if (code >= 0)
+ pop(2);
+ return code;
+}
+#endif
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfdcte_op_defs[] =
+{
+#ifdef TEST
+ {"3.dcteparams", zdcteparams},
+#endif
+ op_def_begin_filter(),
+ {"2DCTEncode", zDCTE},
+ op_def_end(0)
+};
diff --git a/psi/zfdecode.c b/psi/zfdecode.c
new file mode 100644
index 000000000..828e6fd13
--- /dev/null
+++ b/psi/zfdecode.c
@@ -0,0 +1,362 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Additional decoding filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsparam.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ilevel.h" /* for LL3 test */
+#include "iparam.h"
+#include "store.h"
+#include "stream.h" /* for setting is_temp */
+#include "strimpl.h"
+#include "sfilter.h"
+#include "sa85x.h"
+#include "scfx.h"
+#include "scf.h"
+#include "slzwx.h"
+#include "spdiffx.h"
+#include "spngpx.h"
+#include "ifilter.h"
+#include "ifilter2.h"
+#include "ifrpred.h"
+
+/* ------ ASCII85 filters ------ */
+
+/* We include both encoding and decoding filters here, */
+/* because it would be a nuisance to separate them. */
+
+/* <target> ASCII85Encode/filter <file> */
+/* <target> <dict> ASCII85Encode/filter <file> */
+static int
+zA85E(i_ctx_t *i_ctx_p)
+{
+ return filter_write_simple(i_ctx_p, &s_A85E_template);
+}
+
+/* <source> ASCII85Decode/filter <file> */
+/* <source> <dict> ASCII85Decode/filter <file> */
+static int
+zA85D(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_A85D_state ss;
+ int code;
+
+ if (r_has_type(op, t_dictionary)) {
+ check_dict_read(*op);
+ if ((code = dict_bool_param(op, "PDFRules", false, &ss.pdf_rules)) < 0)
+ return code;
+ } else {
+ ss.pdf_rules = false;
+ }
+ return filter_read(i_ctx_p, 0, &s_A85D_template, (stream_state *)&ss, 0);
+}
+
+/* ------ CCITTFaxDecode filter ------ */
+
+/* Common setup for encoding and decoding filters. */
+extern stream_state_proc_put_params(s_CF_put_params, stream_CF_state);
+int
+zcf_setup(os_ptr op, stream_CF_state *pcfs, gs_ref_memory_t *imem)
+{
+ dict_param_list list;
+ int code = dict_param_list_read(&list, op, NULL, false, imem);
+
+ if (code < 0)
+ return code;
+ s_CF_set_defaults_inline(pcfs);
+ code = s_CF_put_params((gs_param_list *)&list, pcfs);
+ iparam_list_release(&list);
+ return code;
+}
+
+/* <source> <dict> CCITTFaxDecode/filter <file> */
+/* <source> CCITTFaxDecode/filter <file> */
+static int
+zCFD(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr dop;
+ stream_CFD_state cfs;
+ int code;
+
+ if (r_has_type(op, t_dictionary)) {
+ check_dict_read(*op);
+ dop = op;
+ } else
+ dop = 0;
+ code = zcf_setup(dop, (stream_CF_state *)&cfs, iimemory);
+ if (code < 0)
+ return code;
+ return filter_read(i_ctx_p, 0, &s_CFD_template, (stream_state *)&cfs, 0);
+}
+
+/* ------ Common setup for possibly pixel-oriented decoding filters ------ */
+
+int
+filter_read_predictor(i_ctx_t *i_ctx_p, int npop,
+ const stream_template * templat, stream_state * st)
+{
+ os_ptr op = osp;
+ int predictor, code;
+ stream_PDiff_state pds;
+ stream_PNGP_state pps;
+
+ if (r_has_type(op, t_dictionary)) {
+ if ((code = dict_int_param(op, "Predictor", 0, 15, 1, &predictor)) < 0)
+ return code;
+ switch (predictor) {
+ case 0: /* identity */
+ predictor = 1;
+ case 1: /* identity */
+ break;
+ case 2: /* componentwise horizontal differencing */
+ code = zpd_setup(op, &pds);
+ break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ /* PNG prediction */
+ code = zpp_setup(op, &pps);
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ if (code < 0)
+ return code;
+ } else
+ predictor = 1;
+ if (predictor == 1)
+ return filter_read(i_ctx_p, npop, templat, st, 0);
+ {
+ /* We need to cascade filters. */
+ ref rsource, rdict;
+ int code;
+
+ /* Save the operands, just in case. */
+ ref_assign(&rsource, op - 1);
+ ref_assign(&rdict, op);
+ code = filter_read(i_ctx_p, 1, templat, st, 0);
+ if (code < 0)
+ return code;
+ /* filter_read changed osp.... */
+ op = osp;
+ code =
+ (predictor == 2 ?
+ filter_read(i_ctx_p, 0, &s_PDiffD_template, (stream_state *) & pds, 0) :
+ filter_read(i_ctx_p, 0, &s_PNGPD_template, (stream_state *) & pps, 0));
+ if (code < 0) {
+ /* Restore the operands. Don't bother trying to clean up */
+ /* the first stream. */
+ osp = ++op;
+ ref_assign(op - 1, &rsource);
+ ref_assign(op, &rdict);
+ return code;
+ }
+ /*
+ * Mark the compression stream as temporary, and propagate
+ * CloseSource from it to the predictor stream.
+ */
+ filter_mark_strm_temp(op, 2);
+ return code;
+ }
+}
+
+/* ------ Generalized LZW/GIF decoding filter ------ */
+
+/* Common setup for encoding and decoding filters. */
+int
+zlz_setup(os_ptr op, stream_LZW_state * plzs)
+{
+ int code;
+ const ref *dop;
+
+ if (r_has_type(op, t_dictionary)) {
+ check_dict_read(*op);
+ dop = op;
+ } else
+ dop = 0;
+ if ( (code = dict_int_param(dop, "EarlyChange", 0, 1, 1,
+ &plzs->EarlyChange)) < 0 ||
+ /*
+ * The following are not PostScript standard, although
+ * LanguageLevel 3 provides the first two under different
+ * names.
+ */
+ (code = dict_int_param(dop, "InitialCodeLength", 2, 11, 8,
+ &plzs->InitialCodeLength)) < 0 ||
+ (code = dict_bool_param(dop, "FirstBitLowOrder", false,
+ &plzs->FirstBitLowOrder)) < 0 ||
+ (code = dict_bool_param(dop, "BlockData", false,
+ &plzs->BlockData)) < 0
+ )
+ return code;
+ return 0;
+}
+
+/* <source> LZWDecode/filter <file> */
+/* <source> <dict> LZWDecode/filter <file> */
+static int
+zLZWD(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_LZW_state lzs;
+ int code = zlz_setup(op, &lzs);
+
+ if (code < 0)
+ return code;
+ if (LL3_ENABLED && r_has_type(op, t_dictionary)) {
+ int unit_size;
+
+ if ((code = dict_bool_param(op, "LowBitFirst", lzs.FirstBitLowOrder,
+ &lzs.FirstBitLowOrder)) < 0 ||
+ (code = dict_int_param(op, "UnitSize", 3, 8, 8,
+ &unit_size)) < 0
+ )
+ return code;
+ if (code == 0 /* UnitSize specified */ )
+ lzs.InitialCodeLength = unit_size + 1;
+ }
+ return filter_read_predictor(i_ctx_p, 0, &s_LZWD_template,
+ (stream_state *) & lzs);
+}
+
+/* ------ Color differencing filters ------ */
+
+/* We include both encoding and decoding filters here, */
+/* because it would be a nuisance to separate them. */
+
+/* Common setup for encoding and decoding filters. */
+int
+zpd_setup(os_ptr op, stream_PDiff_state * ppds)
+{
+ int code, bpc;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if ((code = dict_int_param(op, "Colors", 1, s_PDiff_max_Colors, 1,
+ &ppds->Colors)) < 0 ||
+ (code = dict_int_param(op, "BitsPerComponent", 1, 16, 8,
+ &bpc)) < 0 ||
+ (bpc & (bpc - 1)) != 0 ||
+ (code = dict_int_param(op, "Columns", 1, max_int, 1,
+ &ppds->Columns)) < 0
+ )
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+ ppds->BitsPerComponent = bpc;
+ return 0;
+}
+
+/* <target> <dict> PixelDifferenceEncode/filter <file> */
+static int
+zPDiffE(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_PDiff_state pds;
+ int code = zpd_setup(op, &pds);
+
+ if (code < 0)
+ return code;
+ return filter_write(i_ctx_p, 0, &s_PDiffE_template, (stream_state *) & pds, 0);
+}
+
+/* <source> <dict> PixelDifferenceDecode/filter <file> */
+static int
+zPDiffD(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_PDiff_state pds;
+ int code = zpd_setup(op, &pds);
+
+ if (code < 0)
+ return code;
+ return filter_read(i_ctx_p, 0, &s_PDiffD_template, (stream_state *) & pds, 0);
+}
+
+/* ------ PNG pixel predictor filters ------ */
+
+/* Common setup for encoding and decoding filters. */
+int
+zpp_setup(os_ptr op, stream_PNGP_state * ppps)
+{
+ int code, bpc;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if ((code = dict_int_param(op, "Colors", 1, s_PNG_max_Colors, 1,
+ &ppps->Colors)) < 0 ||
+ (code = dict_int_param(op, "BitsPerComponent", 1, 16, 8,
+ &bpc)) < 0 ||
+ (bpc & (bpc - 1)) != 0 ||
+ (code = dict_uint_param(op, "Columns", 1, max_uint, 1,
+ &ppps->Columns)) < 0 ||
+ (code = dict_int_param(op, "Predictor", 10, 15, 15,
+ &ppps->Predictor)) < 0
+ )
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+ ppps->BitsPerComponent = bpc;
+ return 0;
+}
+
+/* <target> <dict> PNGPredictorEncode/filter <file> */
+static int
+zPNGPE(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_PNGP_state pps;
+ int code = zpp_setup(op, &pps);
+
+ if (code < 0)
+ return code;
+ return filter_write(i_ctx_p, 0, &s_PNGPE_template, (stream_state *) & pps, 0);
+}
+
+/* <source> <dict> PNGPredictorDecode/filter <file> */
+static int
+zPNGPD(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_PNGP_state pps;
+ int code = zpp_setup(op, &pps);
+
+ if (code < 0)
+ return code;
+ return filter_read(i_ctx_p, 0, &s_PNGPD_template, (stream_state *) & pps, 0);
+}
+
+/* ---------------- Initialization procedure ---------------- */
+
+const op_def zfdecode_op_defs[] = {
+ op_def_begin_filter(),
+ {"1ASCII85Encode", zA85E},
+ {"1ASCII85Decode", zA85D},
+ {"2CCITTFaxDecode", zCFD},
+ {"1LZWDecode", zLZWD},
+ {"2PixelDifferenceDecode", zPDiffD},
+ {"2PixelDifferenceEncode", zPDiffE},
+ {"2PNGPredictorDecode", zPNGPD},
+ {"2PNGPredictorEncode", zPNGPE},
+ op_def_end(0)
+};
diff --git a/psi/zfile.c b/psi/zfile.c
new file mode 100644
index 000000000..121e403e8
--- /dev/null
+++ b/psi/zfile.c
@@ -0,0 +1,1200 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Non-I/O file operators */
+#include "memory_.h"
+#include "string_.h"
+#include "unistd_.h"
+#include "stat_.h" /* get system header early to avoid name clash on Cygwin */
+#include "ghost.h"
+#include "gscdefs.h" /* for gx_io_device_table */
+#include "gsutil.h" /* for bytes_compare */
+#include "gp.h"
+#include "gpmisc.h"
+#include "gsfname.h"
+#include "gsstruct.h" /* for registering root */
+#include "gxalloc.h" /* for streams */
+#include "oper.h"
+#include "dstack.h" /* for systemdict */
+#include "estack.h" /* for filenameforall, .execfile */
+#include "ialloc.h"
+#include "ilevel.h" /* %names only work in Level 2 */
+#include "iname.h"
+#include "isave.h" /* for restore */
+#include "idict.h"
+#include "iutil.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sfilter.h"
+#include "gxiodev.h" /* must come after stream.h */
+ /* and before files.h */
+#include "files.h"
+#include "main.h" /* for gs_lib_paths */
+#include "store.h"
+#include "zfile.h"
+
+/* Import the IODevice table. */
+extern_gx_io_device_table();
+
+/* Import the dtype of the stdio IODevices. */
+extern const char iodev_dtype_stdio[];
+
+/* Forward references: file name parsing. */
+static int parse_file_name(const ref * op, gs_parsed_file_name_t * pfn,
+ bool safemode, gs_memory_t *memory);
+static int parse_real_file_name(const ref * op,
+ gs_parsed_file_name_t * pfn,
+ gs_memory_t *mem, client_name_t cname);
+static int parse_file_access_string(const ref *op, char file_access[4]);
+
+/* Forward references: other. */
+static int execfile_finish(i_ctx_t *);
+static int execfile_cleanup(i_ctx_t *);
+static iodev_proc_open_file(iodev_os_open_file);
+stream_proc_report_error(filter_report_error);
+
+/*
+ * Since there can be many file objects referring to the same file/stream,
+ * we can't simply free a stream when we close it. On the other hand,
+ * we don't want freed streams to clutter up memory needlessly.
+ * Our solution is to retain the freed streams, and reuse them.
+ * To prevent an old file object from being able to access a reused stream,
+ * we keep a serial number in each stream, and check it against a serial
+ * number stored in the file object (as the "size"); when we close a file,
+ * we increment its serial number. If the serial number ever overflows,
+ * we leave it at zero, and do not reuse the stream.
+ * (This will never happen.)
+ *
+ * Storage management for this scheme is a little tricky. We maintain an
+ * invariant that says that a stream opened at a given save level always
+ * uses a stream structure allocated at that level. By doing this, we don't
+ * need to keep track separately of streams open at a level vs. streams
+ * allocated at a level. To make this interact properly with save and
+ * restore, we maintain a list of all streams allocated at this level, both
+ * open and closed. We store this list in the allocator: this is a hack,
+ * but it simplifies bookkeeping (in particular, it guarantees the list is
+ * restored properly by a restore).
+ *
+ * We want to close streams freed by restore and by garbage collection. We
+ * use the finalization procedure for this. For restore, we don't have to
+ * do anything special to make this happen. For garbage collection, we do
+ * something more drastic: we simply clear the list of known streams (at all
+ * save levels). Any streams open at the time of garbage collection will no
+ * longer participate in the list of known streams, but this does no harm;
+ * it simply means that they won't get reused, and can only be reclaimed by
+ * a future garbage collection or restore.
+ */
+
+/*
+ * Define the default stream buffer sizes. For file streams,
+ * this is arbitrary, since the C library or operating system
+ * does its own buffering in addition.
+ * However, a buffer size of at least 2K bytes is necessary to prevent
+ * JPEG decompression from running very slow. When less than 2K, an
+ * intermediate filter is installed that transfers 1 byte at a time
+ * causing many aborted roundtrips through the JPEG filter code.
+ */
+#define DEFAULT_BUFFER_SIZE 2048
+extern const uint file_default_buffer_size;
+
+/* Make an invalid file object. */
+void
+make_invalid_file(i_ctx_t *i_ctx_p, ref * fp)
+{
+ make_file(fp, avm_invalid_file_entry, ~0, i_ctx_p->invalid_file_stream);
+}
+
+/* Check a file name for permission by stringmatch on one of the */
+/* strings of the permitgroup array. */
+static int
+check_file_permissions_reduced(i_ctx_t *i_ctx_p, const char *fname, int len,
+ const char *permitgroup)
+{
+ long i;
+ ref *permitlist = NULL;
+ /* an empty string (first character == 0) if '\' character is */
+ /* recognized as a file name separator as on DOS & Windows */
+ const char *win_sep2 = "\\";
+ bool use_windows_pathsep = (gs_file_name_check_separator(win_sep2, 1, win_sep2) == 1);
+ uint plen = gp_file_name_parents(fname, len);
+
+ /* Assuming a reduced file name. */
+
+ if (dict_find_string(&(i_ctx_p->userparams), permitgroup, &permitlist) <= 0)
+ return 0; /* if Permissions not found, just allow access */
+
+ for (i=0; i<r_size(permitlist); i++) {
+ ref permitstring;
+ const string_match_params win_filename_params = {
+ '*', '?', '\\', true, true /* ignore case & '/' == '\\' */
+ };
+ const byte *permstr;
+ uint permlen;
+ int cwd_len = 0;
+
+ if (array_get(imemory, permitlist, i, &permitstring) < 0 ||
+ r_type(&permitstring) != t_string
+ )
+ break; /* any problem, just fail */
+ permstr = permitstring.value.bytes;
+ permlen = r_size(&permitstring);
+ /*
+ * Check if any file name is permitted with "*".
+ */
+ if (permlen == 1 && permstr[0] == '*')
+ return 0; /* success */
+ /*
+ * If the filename starts with parent references,
+ * the permission element must start with same number of parent references.
+ */
+ if (plen != 0 && plen != gp_file_name_parents((const char *)permstr, permlen))
+ continue;
+ cwd_len = gp_file_name_cwds((const char *)permstr, permlen);
+ /*
+ * If the permission starts with "./", absolute paths
+ * are not permitted.
+ */
+ if (cwd_len > 0 && gp_file_name_is_absolute(fname, len))
+ continue;
+ /*
+ * If the permission starts with "./", relative paths
+ * with no "./" are allowed as well as with "./".
+ * 'fname' has no "./" because it is reduced.
+ */
+ if (string_match( (const unsigned char*) fname, len,
+ permstr + cwd_len, permlen - cwd_len,
+ use_windows_pathsep ? &win_filename_params : NULL))
+ return 0; /* success */
+ }
+ /* not found */
+ return gs_error_invalidfileaccess;
+}
+
+/* Check a file name for permission by stringmatch on one of the */
+/* strings of the permitgroup array */
+static int
+check_file_permissions(i_ctx_t *i_ctx_p, const char *fname, int len,
+ const char *permitgroup)
+{
+ char fname_reduced[gp_file_name_sizeof];
+ uint rlen = sizeof(fname_reduced);
+
+ if (gp_file_name_reduce(fname, len, fname_reduced, &rlen) != gp_combine_success)
+ return gs_error_invalidaccess; /* fail if we couldn't reduce */
+ return check_file_permissions_reduced(i_ctx_p, fname_reduced, rlen, permitgroup);
+}
+
+/* <name_string> <access_string> file <file> */
+int /* exported for zsysvm.c */
+zfile(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ char file_access[4];
+ gs_parsed_file_name_t pname;
+ int code = parse_file_access_string(op, file_access);
+ stream *s;
+
+ if (code < 0)
+ return code;
+ code = parse_file_name(op-1, &pname, i_ctx_p->LockFilePermissions, imemory);
+ if (code < 0)
+ return code;
+ /*
+ * HACK: temporarily patch the current context pointer into the
+ * state pointer for stdio-related devices. See ziodev.c for
+ * more information.
+ */
+ if (pname.iodev && pname.iodev->dtype == iodev_dtype_stdio) {
+ bool statement = (strcmp(pname.iodev->dname, "%statementedit%") == 0);
+ bool lineedit = (strcmp(pname.iodev->dname, "%lineedit%") == 0);
+ if (pname.fname)
+ return_error(gs_error_invalidfileaccess);
+ if (statement || lineedit) {
+ /* These need special code to support callouts */
+ gx_io_device *indev = gs_findiodevice(imemory,
+ (const byte *)"%stdin", 6);
+ stream *ins;
+ if (strcmp(file_access, "r"))
+ return_error(gs_error_invalidfileaccess);
+ indev->state = i_ctx_p;
+ code = (indev->procs.open_device)(indev, file_access, &ins, imemory);
+ indev->state = 0;
+ if (code < 0)
+ return code;
+ check_ostack(2);
+ push(2);
+ make_stream_file(op - 3, ins, file_access);
+ make_bool(op-2, statement);
+ make_int(op-1, 0);
+ make_string(op, icurrent_space, 0, NULL);
+ return zfilelineedit(i_ctx_p);
+ }
+ pname.iodev->state = i_ctx_p;
+ code = (*pname.iodev->procs.open_device)(pname.iodev,
+ file_access, &s, imemory);
+ pname.iodev->state = NULL;
+ } else {
+ if (pname.iodev == NULL)
+ pname.iodev = iodev_default(imemory);
+ code = zopen_file(i_ctx_p, &pname, file_access, &s, imemory);
+ }
+ if (code < 0)
+ return code;
+ code = ssetfilename(s, op[-1].value.const_bytes, r_size(op - 1));
+ if (code < 0) {
+ sclose(s);
+ return_error(gs_error_VMerror);
+ }
+ make_stream_file(op - 1, s, file_access);
+ pop(1);
+ return code;
+}
+
+/*
+ * Files created with .tempfile permit some operations even if the
+ * temp directory is not explicitly named on the PermitFile... path
+ * The names 'SAFETY' and 'tempfiles' are defined by gs_init.ps
+*/
+static bool
+file_is_tempfile(i_ctx_t *i_ctx_p, const uchar *fname, int len)
+{
+ ref *SAFETY;
+ ref *tempfiles;
+ ref kname;
+
+ if (dict_find_string(systemdict, "SAFETY", &SAFETY) <= 0 ||
+ dict_find_string(SAFETY, "tempfiles", &tempfiles) <= 0)
+ return false;
+ if (name_ref(imemory, fname, len, &kname, -1) < 0 ||
+ dict_find(tempfiles, &kname, &SAFETY) <= 0)
+ return false;
+ return true;
+}
+
+/* ------ Level 2 extensions ------ */
+
+/* <string> deletefile - */
+static int
+zdeletefile(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_parsed_file_name_t pname;
+ int code = parse_real_file_name(op, &pname, imemory, "deletefile");
+
+ if (code < 0)
+ return code;
+ if (pname.iodev == iodev_default(imemory)) {
+ if ((code = check_file_permissions(i_ctx_p, pname.fname, pname.len,
+ "PermitFileControl")) < 0 &&
+ !file_is_tempfile(i_ctx_p, op->value.bytes, r_size(op))) {
+ return code;
+ }
+ }
+ code = (*pname.iodev->procs.delete_file)(pname.iodev, pname.fname);
+ gs_free_file_name(&pname, "deletefile");
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* <template> <proc> <scratch> filenameforall - */
+static int file_continue(i_ctx_t *);
+static int file_cleanup(i_ctx_t *);
+static int
+zfilenameforall(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ file_enum *pfen;
+ gx_io_device *iodev = NULL;
+ gs_parsed_file_name_t pname;
+ int code = 0;
+
+ check_write_type(*op, t_string);
+ check_proc(op[-1]);
+ check_read_type(op[-2], t_string);
+ /* Push a mark, the iodev, devicenamelen, the scratch string, the enumerator, */
+ /* and the procedure, and invoke the continuation. */
+ check_estack(7);
+ /* Get the iodevice */
+ code = parse_file_name(op-2, &pname, i_ctx_p->LockFilePermissions, imemory);
+ if (code < 0)
+ return code;
+ iodev = (pname.iodev == NULL) ? iodev_default(imemory) : pname.iodev;
+
+ /* Check for several conditions that just cause us to return success */
+ if (pname.len == 0 || iodev->procs.enumerate_files == iodev_no_enumerate_files) {
+ pop(3);
+ return 0; /* no pattern, or device not found -- just return */
+ }
+ pfen = iodev->procs.enumerate_files(iodev, (const char *)pname.fname,
+ pname.len, imemory);
+ if (pfen == 0)
+ return_error(gs_error_VMerror);
+ push_mark_estack(es_for, file_cleanup);
+ ++esp;
+ make_istruct(esp, 0, iodev);
+ ++esp;
+ make_int(esp, r_size(op-2) - pname.len);
+ *++esp = *op;
+ ++esp;
+ make_istruct(esp, 0, pfen);
+ *++esp = op[-1];
+ pop(3);
+ code = file_continue(i_ctx_p);
+ return (code == o_pop_estack ? o_push_estack : code);
+}
+/* Continuation operator for enumerating files */
+static int
+file_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr pscratch = esp - 2;
+ file_enum *pfen = r_ptr(esp - 1, file_enum);
+ int devlen = esp[-3].value.intval;
+ gx_io_device *iodev = r_ptr(esp - 4, gx_io_device);
+ uint len = r_size(pscratch);
+ uint code;
+
+ if (len < devlen)
+ return_error(gs_error_rangecheck); /* not even room for device len */
+ memcpy((char *)pscratch->value.bytes, iodev->dname, devlen);
+ code = iodev->procs.enumerate_next(pfen, (char *)pscratch->value.bytes + devlen,
+ len - devlen);
+ if (code == ~(uint) 0) { /* all done */
+ esp -= 5; /* pop proc, pfen, devlen, iodev , mark */
+ return o_pop_estack;
+ } else if (code > len) /* overran string */
+ return_error(gs_error_rangecheck);
+ else {
+ push(1);
+ ref_assign(op, pscratch);
+ r_set_size(op, code + devlen);
+ push_op_estack(file_continue); /* come again */
+ *++esp = pscratch[2]; /* proc */
+ return o_push_estack;
+ }
+}
+/* Cleanup procedure for enumerating files */
+static int
+file_cleanup(i_ctx_t *i_ctx_p)
+{
+ gx_io_device *iodev = r_ptr(esp + 2, gx_io_device);
+
+ iodev->procs.enumerate_close(r_ptr(esp + 5, file_enum));
+ return 0;
+}
+
+/* <string1> <string2> renamefile - */
+static int
+zrenamefile(i_ctx_t *i_ctx_p)
+{
+ int code;
+ os_ptr op = osp;
+ gs_parsed_file_name_t pname1, pname2;
+
+ code = parse_real_file_name(op, &pname2, imemory, "renamefile(to)");
+ if (code < 0)
+ return code;
+
+ pname1.fname = 0;
+ code = parse_real_file_name(op - 1, &pname1, imemory, "renamefile(from)");
+ if (code >= 0) {
+ gx_io_device *iodev_dflt = iodev_default(imemory);
+ if (pname1.iodev != pname2.iodev ) {
+ if (pname1.iodev == iodev_dflt)
+ pname1.iodev = pname2.iodev;
+ if (pname2.iodev == iodev_dflt)
+ pname2.iodev = pname1.iodev;
+ }
+ if (pname1.iodev != pname2.iodev ||
+ (pname1.iodev == iodev_dflt &&
+ /*
+ * We require FileControl permissions on the source path
+ * unless it is a temporary file. Also, we require FileControl
+ * and FileWriting permissions to the destination file/path.
+ */
+ ((check_file_permissions(i_ctx_p, pname1.fname, pname1.len,
+ "PermitFileControl") < 0 &&
+ !file_is_tempfile(i_ctx_p, op[-1].value.bytes, r_size(op - 1))) ||
+ (check_file_permissions(i_ctx_p, pname2.fname, pname2.len,
+ "PermitFileControl") < 0 ||
+ check_file_permissions(i_ctx_p, pname2.fname, pname2.len,
+ "PermitFileWriting") < 0 )))) {
+ code = gs_note_error(gs_error_invalidfileaccess);
+ } else {
+ code = (*pname1.iodev->procs.rename_file)(pname1.iodev,
+ pname1.fname, pname2.fname);
+ }
+ }
+ gs_free_file_name(&pname2, "renamefile(to)");
+ gs_free_file_name(&pname1, "renamefile(from)");
+ if (code < 0)
+ return code;
+ pop(2);
+ return 0;
+}
+
+/* <file> status <open_bool> */
+/* <string> status <pages> <bytes> <ref_time> <creation_time> true */
+/* <string> status false */
+static int
+zstatus(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ case t_file:
+ {
+ stream *s;
+
+ make_bool(op, (file_is_valid(s, op) ? 1 : 0));
+ }
+ return 0;
+ case t_string:
+ {
+ gs_parsed_file_name_t pname;
+ struct stat fstat;
+ int code = parse_file_name(op, &pname,
+ i_ctx_p->LockFilePermissions, imemory);
+ if (code < 0) {
+ if (code == gs_error_undefinedfilename) {
+ make_bool(op, 0);
+ code = 0;
+ }
+ return code;
+ }
+ code = gs_terminate_file_name(&pname, imemory, "status");
+ if (code < 0)
+ return code;
+ code = (*pname.iodev->procs.file_status)(pname.iodev,
+ pname.fname, &fstat);
+ switch (code) {
+ case 0:
+ check_ostack(4);
+ /*
+ * Check to make sure that the file size fits into
+ * a PostScript integer. (On some systems, long is
+ * 32 bits, but file sizes are 64 bits.)
+ */
+ push(4);
+ make_int(op - 4, stat_blocks(&fstat));
+ make_int(op - 3, fstat.st_size);
+ /*
+ * We can't check the value simply by using ==,
+ * because signed/unsigned == does the wrong thing.
+ * Instead, since integer assignment only keeps the
+ * bottom bits, we convert the values to double
+ * and then test for equality. This handles all
+ * cases of signed/unsigned or width mismatch.
+ */
+ if ((double)op[-4].value.intval !=
+ (double)stat_blocks(&fstat) ||
+ (double)op[-3].value.intval !=
+ (double)fstat.st_size
+ )
+ return_error(gs_error_limitcheck);
+ make_int(op - 2, fstat.st_mtime);
+ make_int(op - 1, fstat.st_ctime);
+ make_bool(op, 1);
+ break;
+ case gs_error_undefinedfilename:
+ make_bool(op, 0);
+ code = 0;
+ }
+ gs_free_file_name(&pname, "status");
+ return code;
+ }
+ default:
+ return_op_typecheck(op);
+ }
+}
+
+/* ------ Non-standard extensions ------ */
+
+/* <executable_file> .execfile - */
+static int
+zexecfile(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type_access(*op, t_file, a_executable | a_read | a_execute);
+ check_estack(4); /* cleanup, file, finish, file */
+ push_mark_estack(es_other, execfile_cleanup);
+ *++esp = *op;
+ push_op_estack(execfile_finish);
+ return zexec(i_ctx_p);
+}
+/* Finish normally. */
+static int
+execfile_finish(i_ctx_t *i_ctx_p)
+{
+ check_ostack(1);
+ esp -= 2;
+ execfile_cleanup(i_ctx_p);
+ return o_pop_estack;
+}
+/* Clean up by closing the file. */
+static int
+execfile_cleanup(i_ctx_t *i_ctx_p)
+{
+ check_ostack(1);
+ *++osp = esp[2];
+ return zclosefile(i_ctx_p);
+}
+
+/* - .filenamelistseparator <string> */
+static int
+zfilenamelistseparator(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_const_string(op, avm_foreign | a_readonly, 1,
+ (const byte *)&gp_file_name_list_separator);
+ return 0;
+}
+
+/* <name> .filenamesplit <dir> <base> <extension> */
+static int
+zfilenamesplit(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_read_type(*op, t_string);
+/****** NOT IMPLEMENTED YET ******/
+ return_error(gs_error_undefined);
+}
+
+/* <string> .libfile <file> true */
+/* <string> .libfile <string> false */
+int /* exported for zsysvm.c */
+zlibfile(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ byte cname[DEFAULT_BUFFER_SIZE];
+ uint clen;
+ gs_parsed_file_name_t pname;
+ stream *s;
+ gx_io_device *iodev_dflt;
+
+ check_ostack(2);
+ code = parse_file_name(op, &pname, i_ctx_p->LockFilePermissions, imemory);
+ if (code < 0)
+ return code;
+ iodev_dflt = iodev_default(imemory);
+ if (pname.iodev == NULL)
+ pname.iodev = iodev_dflt;
+ if (pname.iodev != iodev_dflt) { /* Non-OS devices don't have search paths (yet). */
+ code = zopen_file(i_ctx_p, &pname, "r", &s, imemory);
+ if (code >= 0) {
+ code = ssetfilename(s, op->value.const_bytes, r_size(op));
+ if (code < 0) {
+ sclose(s);
+ return_error(gs_error_VMerror);
+ }
+ }
+ if (code < 0) {
+ push(1);
+ make_false(op);
+ return 0;
+ }
+ make_stream_file(op, s, "r");
+ } else {
+ ref fref;
+
+ code = lib_file_open(i_ctx_p->lib_path, imemory, i_ctx_p, pname.fname, pname.len,
+ (char *)cname, sizeof(cname), &clen, &fref);
+ if (code >= 0) {
+ s = fptr(&fref);
+ code = ssetfilename(s, cname, clen);
+ if (code < 0) {
+ sclose(s);
+ return_error(gs_error_VMerror);
+ }
+ }
+ if (code < 0) {
+ if (code == gs_error_VMerror || code == gs_error_invalidfileaccess)
+ return code;
+ push(1);
+ make_false(op);
+ return 0;
+ }
+ ref_assign(op, &fref);
+ }
+ push(1);
+ make_true(op);
+ return 0;
+}
+
+/* A "simple" prefix is defined as a (possibly empty) string of
+ alphanumeric, underscore, and hyphen characters. */
+static bool
+prefix_is_simple(const char *pstr)
+{
+ int i;
+ char c;
+
+ for (i = 0; (c = pstr[i]) != 0; i++) {
+ if (!(c == '-' || c == '_' || (c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')))
+ return false;
+ }
+ return true;
+}
+
+/* <prefix|null> <access_string> .tempfile <name_string> <file> */
+static int
+ztempfile(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const char *pstr;
+ char fmode[4];
+ int code = parse_file_access_string(op, fmode);
+ char *prefix = NULL;
+ char *fname= NULL;
+ uint fnlen;
+ FILE *sfile;
+ stream *s;
+ byte *buf, *sbody;
+
+ if (code < 0)
+ return code;
+ prefix = (char *)gs_alloc_bytes(imemory, gp_file_name_sizeof, "ztempfile(prefix)");
+ fname = (char *)gs_alloc_bytes(imemory, gp_file_name_sizeof, "ztempfile(fname)");
+ if (!prefix || !fname) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+
+ strcat(fmode, gp_fmode_binary_suffix);
+ if (r_has_type(op - 1, t_null))
+ pstr = gp_scratch_file_name_prefix;
+ else {
+ uint psize;
+
+ check_read_type(op[-1], t_string);
+ psize = r_size(op - 1);
+ if (psize >= gp_file_name_sizeof) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto done;
+ }
+ memcpy(prefix, op[-1].value.const_bytes, psize);
+ prefix[psize] = 0;
+ pstr = prefix;
+ }
+
+ if (gp_file_name_is_absolute(pstr, strlen(pstr))) {
+ if (check_file_permissions(i_ctx_p, pstr, strlen(pstr),
+ "PermitFileWriting") < 0) {
+ code = gs_note_error(gs_error_invalidfileaccess);
+ goto done;
+ }
+ } else if (!prefix_is_simple(pstr)) {
+ code = gs_note_error(gs_error_invalidfileaccess);
+ goto done;
+ }
+
+ s = file_alloc_stream(imemory, "ztempfile(stream)");
+ if (s == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ buf = gs_alloc_bytes(imemory, file_default_buffer_size,
+ "ztempfile(buffer)");
+ if (buf == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ sfile = gp_open_scratch_file(imemory, pstr, fname, fmode);
+ if (sfile == 0) {
+ gs_free_object(imemory, buf, "ztempfile(buffer)");
+ code = gs_note_error(gs_error_invalidfileaccess);
+ goto done;
+ }
+ fnlen = strlen(fname);
+ sbody = ialloc_string(fnlen, ".tempfile(fname)");
+ if (sbody == 0) {
+ gs_free_object(imemory, buf, "ztempfile(buffer)");
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ memcpy(sbody, fname, fnlen);
+ file_init_stream(s, sfile, fmode, buf, file_default_buffer_size);
+ code = ssetfilename(s, (const unsigned char*) fname, fnlen);
+ if (code < 0) {
+ gx_io_device *iodev_dflt = iodev_default(imemory);
+ sclose(s);
+ iodev_dflt->procs.delete_file(iodev_dflt, fname);
+ ifree_string(sbody, fnlen, ".tempfile(fname)");
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ make_string(op - 1, a_readonly | icurrent_space, fnlen, sbody);
+ make_stream_file(op, s, fmode);
+
+done:
+ if (prefix)
+ gs_free_object(imemory, prefix, "ztempfile(prefix)");
+ if (fname)
+ gs_free_object(imemory, fname, "ztempfile(fname)");
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfile_op_defs[] =
+{
+ {"1deletefile", zdeletefile},
+ {"1.execfile", zexecfile},
+ {"2file", zfile},
+ {"3filenameforall", zfilenameforall},
+ {"0.filenamelistseparator", zfilenamelistseparator},
+ {"1.filenamesplit", zfilenamesplit},
+ {"1.libfile", zlibfile},
+ {"2renamefile", zrenamefile},
+ {"1status", zstatus},
+ {"2.tempfile", ztempfile},
+ /* Internal operators */
+ {"0%file_continue", file_continue},
+ {"0%execfile_finish", execfile_finish},
+ op_def_end(0)
+};
+
+/* ------ File name parsing ------ */
+
+/* Parse a file name into device and individual name. */
+/* See gsfname.c for details. */
+static int
+parse_file_name(const ref * op, gs_parsed_file_name_t * pfn, bool safemode,
+ gs_memory_t *memory)
+{
+ int code;
+
+ check_read_type(*op, t_string);
+ code = gs_parse_file_name(pfn, (const char *)op->value.const_bytes,
+ r_size(op), memory);
+ if (code < 0)
+ return code;
+ /*
+ * Check here for the %pipe device which is illegal when
+ * LockFilePermissions is true. In the future we might want to allow
+ * the %pipe device to be included on the PermitFile... paths, but
+ * for now it is simply disallowed.
+ */
+ if (pfn->iodev && safemode && strcmp(pfn->iodev->dname, "%pipe%") == 0)
+ return gs_error_invalidfileaccess;
+ return code;
+}
+
+/* Parse a real (non-device) file name and convert to a C string. */
+/* See gsfname.c for details. */
+static int
+parse_real_file_name(const ref *op, gs_parsed_file_name_t *pfn,
+ gs_memory_t *mem, client_name_t cname)
+{
+ check_read_type(*op, t_string);
+ return gs_parse_real_file_name(pfn, (const char *)op->value.const_bytes,
+ r_size(op), mem, cname);
+}
+
+/* Parse the access string for opening a file. */
+/* [4] is for r/w, +, b, \0. */
+static int
+parse_file_access_string(const ref *op, char file_access[4])
+{
+ const byte *astr;
+
+ check_read_type(*op, t_string);
+ astr = op->value.const_bytes;
+ switch (r_size(op)) {
+ case 2:
+ if (astr[1] != '+')
+ return_error(gs_error_invalidfileaccess);
+ file_access[1] = '+';
+ file_access[2] = 0;
+ break;
+ case 1:
+ file_access[1] = 0;
+ break;
+ default:
+ return_error(gs_error_invalidfileaccess);
+ }
+ switch (astr[0]) {
+ case 'r':
+ case 'w':
+ case 'a':
+ break;
+ default:
+ return_error(gs_error_invalidfileaccess);
+ }
+ file_access[0] = astr[0];
+ return 0;
+}
+
+/* ------ Stream opening ------ */
+
+/*
+ * Open a file specified by a parsed file name (which may be only a
+ * device).
+ */
+int
+zopen_file(i_ctx_t *i_ctx_p, const gs_parsed_file_name_t *pfn,
+ const char *file_access, stream **ps, gs_memory_t *mem)
+{
+ gx_io_device *const iodev = pfn->iodev;
+ int code = 0;
+
+ if (pfn->fname == NULL) { /* just a device */
+ iodev->state = i_ctx_p;
+ code = iodev->procs.open_device(iodev, file_access, ps, mem);
+ iodev->state = NULL;
+ return code;
+ }
+ else { /* file */
+ iodev_proc_open_file((*open_file)) = iodev->procs.open_file;
+
+ if (open_file == 0)
+ open_file = iodev_os_open_file;
+ /* Check OS files to make sure we allow the type of access */
+ if (open_file == iodev_os_open_file) {
+ code = check_file_permissions(i_ctx_p, pfn->fname, pfn->len,
+ file_access[0] == 'r' ? "PermitFileReading" : "PermitFileWriting");
+
+ if (code < 0 && !file_is_tempfile(i_ctx_p,
+ (const uchar *)pfn->fname, pfn->len))
+ return code;
+ }
+ return open_file(iodev, pfn->fname, pfn->len, file_access, ps, mem);
+ }
+}
+
+/*
+ * Define the file_open procedure for the %os% IODevice (also used, as the
+ * default, for %pipe% and possibly others).
+ */
+static int
+iodev_os_open_file(gx_io_device * iodev, const char *fname, uint len,
+ const char *file_access, stream ** ps, gs_memory_t * mem)
+{
+ return file_open_stream(fname, len, file_access,
+ file_default_buffer_size, ps,
+ iodev, iodev->procs.gp_fopen, mem);
+}
+
+/* Make a t_file reference to a stream. */
+void
+make_stream_file(ref * pfile, stream * s, const char *access)
+{
+ uint attrs =
+ (access[1] == '+' ? a_write + a_read + a_execute : 0) |
+ imemory_space((gs_ref_memory_t *) s->memory);
+
+ if (access[0] == 'r') {
+ make_file(pfile, attrs | (a_read | a_execute), s->read_id, s);
+ s->write_id = 0;
+ } else {
+ make_file(pfile, attrs | a_write, s->write_id, s);
+ s->read_id = 0;
+ }
+}
+
+static int
+check_file_permissions_aux(i_ctx_t *i_ctx_p, char *fname, uint flen)
+{ /* i_ctx_p is NULL running init files. */
+ /* fname must be reduced. */
+ if (i_ctx_p == NULL)
+ return 0;
+ if (check_file_permissions_reduced(i_ctx_p, fname, flen, "PermitFileReading") < 0)
+ return_error(gs_error_invalidfileaccess);
+ return 0;
+}
+
+/* return zero for success, -ve for error, +1 for continue */
+static int
+lib_file_open_search_with_no_combine(gs_file_path_ptr lib_path, const gs_memory_t *mem, i_ctx_t *i_ctx_p,
+ const char *fname, uint flen, char *buffer, int blen, uint *pclen, ref *pfile,
+ gx_io_device *iodev, bool starting_arg_file, char *fmode)
+{
+ stream *s;
+ uint blen1 = blen;
+ struct stat fstat;
+
+ if (gp_file_name_reduce(fname, flen, buffer, &blen1) != gp_combine_success)
+ goto skip;
+
+ if (starting_arg_file || check_file_permissions_aux(i_ctx_p, buffer, blen1) >= 0) {
+ if (iodev_os_open_file(iodev, (const char *)buffer, blen1,
+ (const char *)fmode, &s, (gs_memory_t *)mem) == 0) {
+ *pclen = blen1;
+ make_stream_file(pfile, s, "r");
+ return 0;
+ }
+ }
+ else {
+ /* If we are not allowed to open the file by check_file_permissions_aux()
+ * and if the file exists, throw an error.......
+ * Otherwise, keep searching.
+ */
+ if ((*iodev->procs.file_status)(iodev, buffer, &fstat) >= 0) {
+ return_error(gs_error_invalidfileaccess);
+ }
+ }
+
+ skip:
+ return 1;
+}
+
+/* return zero for success, -ve for error, +1 for continue */
+static int
+lib_file_open_search_with_combine(gs_file_path_ptr lib_path, const gs_memory_t *mem, i_ctx_t *i_ctx_p,
+ const char *fname, uint flen, char *buffer, int blen, uint *pclen, ref *pfile,
+ gx_io_device *iodev, bool starting_arg_file, char *fmode)
+{
+ stream *s;
+ const gs_file_path *pfpath = lib_path;
+ uint pi;
+
+ for (pi = 0; pi < r_size(&pfpath->list); ++pi) {
+ const ref *prdir = pfpath->list.value.refs + pi;
+ const char *pstr = (const char *)prdir->value.const_bytes;
+ uint plen = r_size(prdir), blen1 = blen;
+ gs_parsed_file_name_t pname;
+ gp_file_name_combine_result r;
+
+ /* We need to concatenate and parse the file name here
+ * if this path has a %device% prefix. */
+ if (pstr[0] == '%') {
+ int code;
+
+ /* We concatenate directly since gp_file_name_combine_*
+ * rules are not correct for other devices such as %rom% */
+ code = gs_parse_file_name(&pname, pstr, plen, mem);
+ if (code < 0)
+ continue;
+ if (blen < max(pname.len, plen) + flen)
+ return_error(gs_error_limitcheck);
+ memcpy(buffer, pname.fname, pname.len);
+ memcpy(buffer+pname.len, fname, flen);
+ code = pname.iodev->procs.open_file(pname.iodev, buffer, pname.len + flen, fmode,
+ &s, (gs_memory_t *)mem);
+ if (code < 0)
+ continue;
+ make_stream_file(pfile, s, "r");
+ /* fill in the buffer with the device concatenated */
+ memcpy(buffer, pstr, plen);
+ memcpy(buffer+plen, fname, flen);
+ *pclen = plen + flen;
+ return 0;
+ } else {
+ r = gp_file_name_combine(pstr, plen,
+ fname, flen, false, buffer, &blen1);
+ if (r != gp_combine_success)
+ continue;
+ if (iodev_os_open_file(iodev, (const char *)buffer, blen1, (const char *)fmode,
+ &s, (gs_memory_t *)mem) == 0) {
+ if (starting_arg_file ||
+ check_file_permissions_aux(i_ctx_p, buffer, blen1) >= 0) {
+ *pclen = blen1;
+ make_stream_file(pfile, s, "r");
+ return 0;
+ }
+ sclose(s);
+ return_error(gs_error_invalidfileaccess);
+ }
+ }
+ }
+ return 1;
+}
+
+/* Return a file object of of the file searched for using the search paths. */
+/* The fname cannot contain a device part (%...%) but the lib paths might. */
+/* The startup code calls this to open the initialization file gs_init.ps. */
+/* The startup code also calls this to open @-files. */
+int
+lib_file_open(gs_file_path_ptr lib_path, const gs_memory_t *mem, i_ctx_t *i_ctx_p,
+ const char *fname, uint flen, char *buffer, int blen, uint *pclen, ref *pfile)
+{ /* i_ctx_p is NULL running arg (@) files.
+ * lib_path and mem are never NULL
+ */
+ bool starting_arg_file = (i_ctx_p == NULL) ? true : i_ctx_p->starting_arg_file;
+ bool search_with_no_combine = false;
+ bool search_with_combine = false;
+ char fmode[4] = { 'r', 0, 0, 0 }; /* room for binary suffix */
+ gx_io_device *iodev = iodev_default(mem);
+ gs_main_instance *minst = get_minst_from_memory(mem);
+ int code;
+
+ /* when starting arg files (@ files) iodev_default is not yet set */
+ if (iodev == 0)
+ iodev = (gx_io_device *)gx_io_device_table[0];
+
+ strcat(fmode, gp_fmode_binary_suffix);
+ if (gp_file_name_is_absolute(fname, flen)) {
+ search_with_no_combine = true;
+ search_with_combine = false;
+ } else {
+ search_with_no_combine = starting_arg_file;
+ search_with_combine = true;
+ }
+ if (minst->search_here_first) {
+ if (search_with_no_combine) {
+ code = lib_file_open_search_with_no_combine(lib_path, mem, i_ctx_p,
+ fname, flen, buffer, blen, pclen, pfile,
+ iodev, starting_arg_file, fmode);
+ if (code <= 0) /* +ve means continue continue */
+ return code;
+ }
+ if (search_with_combine) {
+ code = lib_file_open_search_with_combine(lib_path, mem, i_ctx_p,
+ fname, flen, buffer, blen, pclen, pfile,
+ iodev, starting_arg_file, fmode);
+ if (code <= 0) /* +ve means continue searching */
+ return code;
+ }
+ } else {
+ if (search_with_combine) {
+ code = lib_file_open_search_with_combine(lib_path, mem, i_ctx_p,
+ fname, flen, buffer, blen, pclen, pfile,
+ iodev, starting_arg_file, fmode);
+ if (code <= 0) /* +ve means continue searching */
+ return code;
+ }
+ if (search_with_no_combine) {
+ code = lib_file_open_search_with_no_combine(lib_path, mem, i_ctx_p,
+ fname, flen, buffer, blen, pclen, pfile,
+ iodev, starting_arg_file, fmode);
+ if (code <= 0) /* +ve means continue searching */
+ return code;
+ }
+ }
+ return_error(gs_error_undefinedfilename);
+}
+
+/* The startup code calls this to open @-files. */
+FILE *
+lib_fopen(const gs_file_path_ptr pfpath, const gs_memory_t *mem, const char *fname)
+{
+ /* We need a buffer to hold the expanded file name. */
+ char filename_found[DEFAULT_BUFFER_SIZE];
+ FILE *file = NULL;
+ uint fnamelen;
+ ref obj;
+ int code;
+
+ /* open the usual 'stream', then if successful, return the file */
+ code = lib_file_open(pfpath, mem, NULL, fname, strlen(fname),
+ filename_found, sizeof(filename_found), &fnamelen, &obj);
+
+ if (code < 0)
+ return NULL;
+ file = ((stream *)(obj.value.pfile))->file;
+ return file;
+}
+
+/* Open a file stream that reads a string. */
+/* (This is currently used only by the ccinit feature.) */
+/* The string must be allocated in non-garbage-collectable (foreign) space. */
+int
+file_read_string(const byte *str, uint len, ref *pfile, gs_ref_memory_t *imem)
+{
+ stream *s = file_alloc_stream((gs_memory_t *)imem, "file_read_string");
+
+ if (s == 0)
+ return_error(gs_error_VMerror);
+ sread_string(s, str, len);
+ s->foreign = 1;
+ s->write_id = 0;
+ make_file(pfile, a_readonly | imemory_space(imem), s->read_id, s);
+ s->save_close = s->procs.close;
+ s->procs.close = file_close_disable;
+ return 0;
+}
+
+/* Report an error by storing it in the stream's error_string. */
+int
+filter_report_error(stream_state * st, const char *str)
+{
+ if_debug1m('s', st->memory, "[s]stream error: %s\n", str);
+ strncpy(st->error_string, str, STREAM_MAX_ERROR_STRING);
+ /* Ensure null termination. */
+ st->error_string[STREAM_MAX_ERROR_STRING] = 0;
+ return 0;
+}
+
+/* Open a file stream for a filter. */
+int
+filter_open(const char *file_access, uint buffer_size, ref * pfile,
+ const stream_procs * procs, const stream_template * templat,
+ const stream_state * st, gs_memory_t *mem)
+{
+ stream *s;
+ uint ssize = gs_struct_type_size(templat->stype);
+ stream_state *sst = 0;
+ int code;
+
+ if (templat->stype != &st_stream_state) {
+ sst = s_alloc_state(mem, templat->stype, "filter_open(stream_state)");
+ if (sst == 0)
+ return_error(gs_error_VMerror);
+ }
+ code = file_open_stream((char *)0, 0, file_access, buffer_size, &s,
+ (gx_io_device *)0, (iodev_proc_fopen_t)0, mem);
+ if (code < 0) {
+ gs_free_object(mem, sst, "filter_open(stream_state)");
+ return code;
+ }
+ s_std_init(s, s->cbuf, s->bsize, procs,
+ (*file_access == 'r' ? s_mode_read : s_mode_write));
+ s->procs.process = templat->process;
+ s->save_close = s->procs.close;
+ s->procs.close = file_close_file;
+ if (sst == 0) {
+ /* This stream doesn't have any state of its own. */
+ /* Hack: use the stream itself as the state. */
+ sst = (stream_state *) s;
+ } else if (st != 0) /* might not have client parameters */
+ memcpy(sst, st, ssize);
+ s->state = sst;
+ s_init_state(sst, templat, mem);
+ sst->report_error = filter_report_error;
+
+ if (templat->init != 0) {
+ code = (*templat->init)(sst);
+ if (code < 0) {
+ gs_free_object(mem, sst, "filter_open(stream_state)");
+ gs_free_object(mem, s->cbuf, "filter_open(buffer)");
+ return code;
+ }
+ }
+ make_stream_file(pfile, s, file_access);
+ return 0;
+}
+
+/* Close a file object. */
+/* This is exported only for gsmain.c. */
+int
+file_close(ref * pfile)
+{
+ stream *s;
+
+ if (file_is_valid(s, pfile)) { /* closing a closed file is a no-op */
+ if (sclose(s))
+ return_error(gs_error_ioerror);
+ }
+ return 0;
+}
diff --git a/psi/zfile.h b/psi/zfile.h
new file mode 100644
index 000000000..fdf13733a
--- /dev/null
+++ b/psi/zfile.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Export zopen_file() for zfrsd.c */
+
+#ifndef zfile_INCLUDED
+# define zfile_INCLUDED
+
+int zopen_file(i_ctx_t *i_ctx_p, const gs_parsed_file_name_t *pfn,
+ const char *file_access, stream **ps, gs_memory_t *mem);
+
+#endif
diff --git a/psi/zfile1.c b/psi/zfile1.c
new file mode 100644
index 000000000..7fa354311
--- /dev/null
+++ b/psi/zfile1.c
@@ -0,0 +1,128 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Special file operators */
+
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "ierrors.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "opdef.h"
+#include "opcheck.h"
+#include "store.h"
+#include "gpmisc.h"
+
+/* <string> <string> <bool> .file_name_combine <string> true */
+/* <string> <string> <bool> .file_name_combine <string> <string> false */
+static int
+zfile_name_combine(i_ctx_t *i_ctx_p)
+{
+ uint plen, flen, blen, blen0;
+ const byte *prefix, *fname;
+ byte *buffer;
+ os_ptr op = osp;
+ bool no_sibling;
+
+ check_type(op[ 0], t_boolean);
+ check_type(op[-1], t_string);
+ check_type(op[-2], t_string);
+ plen = r_size(op - 2);
+ flen = r_size(op - 1);
+ blen = blen0 = plen + flen + 2; /* Inserts separator and ending zero byte. */
+ buffer = ialloc_string(blen, "zfile_name_combine");
+ if (buffer == 0)
+ return_error(gs_error_VMerror);
+ prefix = op[-2].value.const_bytes;
+ fname = op[-1].value.const_bytes;
+ no_sibling = op[0].value.boolval;
+ if (gp_file_name_combine((const char *)prefix, plen,
+ (const char *)fname, flen, no_sibling,
+ (char *)buffer, &blen) != gp_combine_success) {
+ make_bool(op, false);
+ } else {
+ buffer = iresize_string(buffer, blen0, blen, "zfile_name_combine");
+ if (buffer == 0)
+ return_error(gs_error_VMerror);
+ make_string(op - 2, a_all | icurrent_space, blen, buffer);
+ make_bool(op - 1, true);
+ pop(1);
+ }
+ return 0;
+}
+
+/* This is compiled conditionally to let PS library to know
+ * whether it works with the new gp_combine_file_name.
+ */
+
+/* <string> .file_name_is_absolute <bool> */
+static int
+zfile_name_is_absolute(i_ctx_t *i_ctx_p)
+{ os_ptr op = osp;
+
+ check_type(op[0], t_string);
+ make_bool(op, gp_file_name_is_absolute((const char *)op->value.const_bytes,
+ r_size(op)));
+ return 0;
+}
+
+static int
+push_string(i_ctx_t *i_ctx_p, const char *v)
+{ os_ptr op = osp;
+ int len = strlen(v);
+
+ push(1);
+ make_const_string(op, avm_foreign | a_readonly,
+ len, (const byte *)v);
+ return 0;
+}
+
+/* - .file_name_separator <string> */
+static int
+zfile_name_separator(i_ctx_t *i_ctx_p)
+{ return push_string(i_ctx_p, gp_file_name_separator());
+}
+
+/* - .file_name_directory_separator <string> */
+static int
+zfile_name_directory_separator(i_ctx_t *i_ctx_p)
+{ return push_string(i_ctx_p, gp_file_name_directory_separator());
+}
+
+/* - .file_name_current <string> */
+static int
+zfile_name_current(i_ctx_t *i_ctx_p)
+{ return push_string(i_ctx_p, gp_file_name_current());
+}
+
+/* - .file_name_parent <string> */
+static int
+zfile_name_parent(i_ctx_t *i_ctx_p)
+{ return push_string(i_ctx_p, gp_file_name_parent());
+}
+
+const op_def zfile1_op_defs[] =
+{
+ {"0.file_name_combine", zfile_name_combine},
+ {"0.file_name_is_absolute", zfile_name_is_absolute},
+ {"0.file_name_separator", zfile_name_separator},
+ {"0.file_name_directory_separator", zfile_name_directory_separator},
+ {"0.file_name_current", zfile_name_current},
+ {"0.file_name_parent", zfile_name_parent},
+ op_def_end(0)
+};
diff --git a/psi/zfileio.c b/psi/zfileio.c
new file mode 100644
index 000000000..7de28f747
--- /dev/null
+++ b/psi/zfileio.c
@@ -0,0 +1,986 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* File I/O operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "oper.h"
+#include "stream.h"
+#include "files.h"
+#include "store.h"
+#include "strimpl.h" /* for ifilter.h */
+#include "ifilter.h" /* for procedure streams */
+#include "interp.h" /* for gs_errorinfo_put_string */
+#include "gsmatrix.h" /* for gxdevice.h */
+#include "gxdevice.h"
+#include "gxdevmem.h"
+#include "estack.h"
+#include "gsstate.h"
+
+/* Forward references */
+static int write_string(ref *, stream *);
+static int handle_read_status(i_ctx_t *, int, const ref *, const uint *,
+ op_proc_t);
+static int handle_write_status(i_ctx_t *, int, const ref *, const uint *,
+ op_proc_t);
+
+/* ------ Operators ------ */
+
+/* <file> closefile - */
+int
+zclosefile(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+
+ check_type(*op, t_file);
+ if (file_is_valid(s, op)) { /* closing a closed file is a no-op */
+ int status = sclose(s);
+
+ if (status != 0 && status != EOFC) {
+ if (s_is_writing(s))
+ return handle_write_status(i_ctx_p, status, op, NULL,
+ zclosefile);
+ else
+ return handle_read_status(i_ctx_p, status, op, NULL,
+ zclosefile);
+ }
+ }
+ pop(1);
+ return 0;
+}
+
+/* <file> read <int> -true- */
+/* <file> read -false- */
+static int
+zread(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+ int ch;
+
+ check_read_file(i_ctx_p, s, op);
+ /* We 'push' first in case of ostack block overflow and the */
+ /* usual case is we will need to push anyway. If we get EOF */
+ /* we will need to 'pop' and decrement the 'op' pointer. */
+ /* This is required since the 'push' macro might return with*/
+ /* stackoverflow which will result in another stack block */
+ /* added on, then the operator being retried. We can't read */
+ /* (sgetc) prior to having a place on the ostack to return */
+ /* the character. */
+ push(1);
+ ch = sgetc(s);
+ if (ch >= 0) {
+ make_int(op - 1, ch);
+ make_bool(op, 1);
+ } else {
+ pop(1); /* Adjust ostack back from preparatory 'pop' */
+ op--;
+ if (ch == EOFC)
+ make_bool(op, 0);
+ else
+ return handle_read_status(i_ctx_p, ch, op, NULL, zread);
+ }
+ return 0;
+}
+
+/* <file> <int> write - */
+int
+zwrite(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+ byte ch;
+ int status;
+
+ check_write_file(s, op - 1);
+ check_type(*op, t_integer);
+ ch = (byte) op->value.intval;
+ status = sputc(s, (byte) ch);
+ if (status >= 0) {
+ pop(2);
+ return 0;
+ }
+ return handle_write_status(i_ctx_p, status, op - 1, NULL, zwrite);
+}
+
+/* <file> <string> readhexstring <substring> <filled_bool> */
+static int zreadhexstring_continue(i_ctx_t *);
+
+/* We pack the odd digit above the the current position for the */
+/* convenience of reusing procedures that take 1 state parameter */
+static int
+zreadhexstring_at(i_ctx_t *i_ctx_p, os_ptr op, uint start, int odd)
+{
+ stream *s;
+ uint len, nread;
+ byte *str;
+ int odd_byte = odd;
+ stream_cursor_write cw;
+ int status;
+
+ check_read_file(i_ctx_p, s, op - 1);
+ /*check_write_type(*op, t_string); *//* done by caller */
+ str = op->value.bytes;
+ len = r_size(op);
+ cw.ptr = str + start - 1;
+ cw.limit = str + len - 1;
+ for (;;) {
+ status = s_hex_process(&s->cursor.r, &cw, &odd_byte,
+ hex_ignore_garbage);
+ if (status == 1) { /* filled the string */
+ ref_assign_inline(op - 1, op);
+ make_true(op);
+ return 0;
+ } else if (status != 0) /* error or EOF */
+ break;
+ /* Didn't fill, keep going. */
+ status = spgetc(s);
+ if (status < 0)
+ break;
+ sputback(s);
+ }
+ nread = cw.ptr + 1 - str;
+ if (status != EOFC) { /* Error */
+ nread |= odd_byte << 24;
+ return handle_read_status(i_ctx_p, status, op - 1, &nread,
+ zreadhexstring_continue);
+ }
+ /* Reached end-of-file before filling the string. */
+ /* Return an appropriate substring. */
+ ref_assign_inline(op - 1, op);
+ r_set_size(op - 1, nread);
+ make_false(op);
+ return 0;
+}
+static int
+zreadhexstring(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_write_type(*op, t_string);
+ return zreadhexstring_at(i_ctx_p, op, 0, -1);
+}
+/* Continue a readhexstring operation after a callout. */
+/* *op contains the index within the string and the odd flag. */
+static int
+zreadhexstring_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code, length, odd;
+
+ check_type(*op, t_integer);
+ length = op->value.intval & 0xFFFFFF;
+ odd = (char)(op->value.intval >> 24);
+
+ if (length > r_size(op - 1) || odd < -1 || odd > 0xF)
+ return_error(gs_error_rangecheck);
+ check_write_type(op[-1], t_string);
+ code = zreadhexstring_at(i_ctx_p, op - 1, (uint)length, odd);
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+/* <file> <string> writehexstring - */
+static int zwritehexstring_continue(i_ctx_t *);
+static int
+zwritehexstring_at(i_ctx_t *i_ctx_p, os_ptr op, uint odd)
+{
+ register stream *s;
+ register byte ch;
+ register const byte *p;
+ register const char *const hex_digits = "0123456789abcdef";
+ register uint len;
+ int status;
+
+#define MAX_HEX 128
+ byte buf[MAX_HEX];
+
+ check_write_file(s, op - 1);
+ check_read_type(*op, t_string);
+ p = op->value.bytes;
+ len = r_size(op);
+ while (len) {
+ uint len1 = min(len, MAX_HEX / 2);
+ register byte *q = buf;
+ uint count = len1;
+ ref rbuf;
+
+ do {
+ ch = *p++;
+ *q++ = hex_digits[ch >> 4];
+ *q++ = hex_digits[ch & 0xf];
+ }
+ while (--count);
+ r_set_size(&rbuf, (len1 << 1) - odd);
+ rbuf.value.bytes = buf + odd;
+ status = write_string(&rbuf, s);
+ switch (status) {
+ default:
+ return_error(gs_error_ioerror);
+ case 0:
+ len -= len1;
+ odd = 0;
+ continue;
+ case INTC:
+ case CALLC:
+ count = rbuf.value.bytes - buf;
+ op->value.bytes += count >> 1;
+ r_set_size(op, len - (count >> 1));
+ count &= 1;
+ return handle_write_status(i_ctx_p, status, op - 1, &count,
+ zwritehexstring_continue);
+ }
+ }
+ pop(2);
+ return 0;
+#undef MAX_HEX
+}
+static int
+zwritehexstring(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ return zwritehexstring_at(i_ctx_p, op, 0);
+}
+/* Continue a writehexstring operation after a callout. */
+/* *op is the odd/even hex digit flag for the first byte. */
+static int
+zwritehexstring_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_type(*op, t_integer);
+ if ((op->value.intval & ~1) != 0)
+ return_error(gs_error_rangecheck);
+ code = zwritehexstring_at(i_ctx_p, op - 1, (uint) op->value.intval);
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+/* <file> <string> readstring <substring> <filled_bool> */
+static int zreadstring_continue(i_ctx_t *);
+static int
+zreadstring_at(i_ctx_t *i_ctx_p, os_ptr op, uint start)
+{
+ stream *s;
+ uint len, rlen;
+ int status;
+
+ check_write_type(*op, t_string);
+ check_read_file(i_ctx_p, s, op - 1);
+ len = r_size(op);
+ status = sgets(s, op->value.bytes + start, len - start, &rlen);
+ rlen += start;
+ switch (status) {
+ case EOFC:
+ case 0:
+ break;
+ default:
+ return handle_read_status(i_ctx_p, status, op - 1, &rlen,
+ zreadstring_continue);
+ }
+ /*
+ * The most recent Adobe specification says that readstring
+ * must signal a rangecheck if the string length is zero.
+ * I can't imagine the motivation for this, but we emulate it.
+ * It's safe to check it here, rather than earlier, because if
+ * len is zero, sgets will return 0 immediately with rlen = 0.
+ */
+ if (len == 0)
+ return_error(gs_error_rangecheck);
+ r_set_size(op, rlen);
+ op[-1] = *op;
+ make_bool(op, (rlen == len ? 1 : 0));
+ return 0;
+}
+static int
+zreadstring(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ return zreadstring_at(i_ctx_p, op, 0);
+}
+/* Continue a readstring operation after a callout. */
+/* *op is the index within the string. */
+static int
+zreadstring_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_type(*op, t_integer);
+ if (op->value.intval < 0 || op->value.intval > r_size(op - 1))
+ return_error(gs_error_rangecheck);
+ code = zreadstring_at(i_ctx_p, op - 1, (uint) op->value.intval);
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+/* <file> <string> writestring - */
+static int
+zwritestring(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+ int status;
+
+ check_write_file(s, op - 1);
+ check_read_type(*op, t_string);
+ status = write_string(op, s);
+ if (status >= 0) {
+ pop(2);
+ return 0;
+ }
+ return handle_write_status(i_ctx_p, status, op - 1, NULL, zwritestring);
+}
+
+/* <file> <string> readline <substring> <bool> */
+static int zreadline(i_ctx_t *);
+static int zreadline_continue(i_ctx_t *);
+
+/*
+ * We could handle readline the same way as readstring,
+ * except for the anomalous situation where we get interrupted
+ * between the CR and the LF of an end-of-line marker.
+ * We hack around this in the following way: if we get interrupted
+ * before we've read any characters, we just restart the readline;
+ * if we get interrupted at any other time, we use readline_continue;
+ * we use start=0 (which we have just ruled out as a possible start value
+ * for readline_continue) to indicate interruption after the CR.
+ */
+static int
+zreadline_at(i_ctx_t *i_ctx_p, os_ptr op, uint count, bool in_eol)
+{
+ stream *s;
+ int status;
+ gs_string str;
+
+ check_write_type(*op, t_string);
+ check_read_file(i_ctx_p, s, op - 1);
+ str.data = op->value.bytes;
+ str.size = r_size(op);
+ status = zreadline_from(s, &str, NULL, &count, &in_eol);
+ switch (status) {
+ case 0:
+ case EOFC:
+ break;
+ case 1:
+ return_error(gs_error_rangecheck);
+ default:
+ if (count == 0 && !in_eol)
+ return handle_read_status(i_ctx_p, status, op - 1, NULL,
+ zreadline);
+ else {
+ if (in_eol) {
+ r_set_size(op, count);
+ count = 0;
+ }
+ return handle_read_status(i_ctx_p, status, op - 1, &count,
+ zreadline_continue);
+ }
+ }
+ r_set_size(op, count);
+ op[-1] = *op;
+ make_bool(op, status == 0);
+ return 0;
+}
+static int
+zreadline(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ return zreadline_at(i_ctx_p, op, 0, false);
+}
+/* Continue a readline operation after a callout. */
+/* *op is the index within the string, or 0 for an interrupt after a CR. */
+static int
+zreadline_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint size = r_size(op - 1);
+ uint start;
+ int code;
+
+ check_type(*op, t_integer);
+ if (op->value.intval < 0 || op->value.intval > size)
+ return_error(gs_error_rangecheck);
+ start = (uint) op->value.intval;
+ code = (start == 0 ? zreadline_at(i_ctx_p, op - 1, size, true) :
+ zreadline_at(i_ctx_p, op - 1, start, false));
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+/* Internal readline routine. */
+/* Returns a stream status value, or 1 if we overflowed the string. */
+/* This is exported for %lineedit. */
+int
+zreadline_from(stream *s, gs_string *buf, gs_memory_t *bufmem,
+ uint *pcount, bool *pin_eol)
+{
+ sreadline_proc((*readline));
+
+ if (zis_stdin(s))
+ readline = gp_readline;
+ else
+ readline = sreadline;
+ return readline(s, NULL, NULL /*WRONG*/, NULL, buf, bufmem,
+ pcount, pin_eol, zis_stdin);
+}
+
+/* <file> bytesavailable <int> */
+static int
+zbytesavailable(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+ gs_offset_t avail;
+
+ check_read_file(i_ctx_p, s, op);
+ switch (savailable(s, &avail)) {
+ default:
+ return_error(gs_error_ioerror);
+ case EOFC:
+ avail = -1;
+ case 0:
+ ;
+ }
+ if (gs_currentcpsimode(imemory)) {
+ avail = (ps_int32)avail;
+ }
+ make_int(op, avail);
+ return 0;
+}
+
+/* - flush - */
+int
+zflush(i_ctx_t *i_ctx_p)
+{
+ stream *s;
+ int status;
+ ref rstdout;
+ int code = zget_stdout(i_ctx_p, &s);
+
+ if (code < 0)
+ return code;
+
+ make_stream_file(&rstdout, s, "w");
+ status = sflush(s);
+ if (status == 0 || status == EOFC) {
+ return 0;
+ }
+ return
+ (s_is_writing(s) ?
+ handle_write_status(i_ctx_p, status, &rstdout, NULL, zflush) :
+ handle_read_status(i_ctx_p, status, &rstdout, NULL, zflush));
+}
+
+/* <file> flushfile - */
+static int
+zflushfile(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+ int status;
+
+ check_type(*op, t_file);
+ /*
+ * We think flushfile is a no-op on closed input files, but causes an
+ * error on closed output files.
+ */
+ if (file_is_invalid(s, op)) {
+ if (r_has_attr(op, a_write))
+ return_error(gs_error_invalidaccess);
+ pop(1);
+ return 0;
+ }
+ status = sflush(s);
+ if (status == 0 || status == EOFC) {
+ pop(1);
+ return 0;
+ }
+ return
+ (s_is_writing(s) ?
+ handle_write_status(i_ctx_p, status, op, NULL, zflushfile) :
+ handle_read_status(i_ctx_p, status, op, NULL, zflushfile));
+}
+
+/* <file> resetfile - */
+static int
+zresetfile(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+
+ /* According to Adobe, resetfile is a no-op on closed files. */
+ check_type(*op, t_file);
+ if (file_is_valid(s, op))
+ sreset(s);
+ pop(1);
+ return 0;
+}
+
+/* <string> print - */
+static int
+zprint(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+ int status;
+ ref rstdout;
+ int code;
+
+ check_read_type(*op, t_string);
+ code = zget_stdout(i_ctx_p, &s);
+ if (code < 0)
+ return code;
+ status = write_string(op, s);
+ if (status >= 0) {
+ pop(1);
+ return 0;
+ }
+ /* Convert print to writestring on the fly. */
+ make_stream_file(&rstdout, s, "w");
+ code = handle_write_status(i_ctx_p, status, &rstdout, NULL,
+ zwritestring);
+ if (code != o_push_estack)
+ return code;
+ push(1);
+ *op = op[-1];
+ op[-1] = rstdout;
+ return code;
+}
+
+/* <bool> echo - */
+static int
+zecho(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_boolean);
+ /****** NOT IMPLEMENTED YET ******/
+ pop(1);
+ return 0;
+}
+
+/* ------ Level 2 extensions ------ */
+
+/* <file> fileposition <int> */
+static int
+zfileposition(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+
+ check_file(s, op);
+ /*
+ * The PLRM says fileposition must give an error for non-seekable
+ * streams.
+ */
+ if (!s_can_seek(s))
+ return_error(gs_error_ioerror);
+ make_int(op, stell(s));
+ return 0;
+}
+/* <file> .fileposition <int> */
+static int
+zxfileposition(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+
+ check_file(s, op);
+ /*
+ * This version of fileposition doesn't give the error, so we can
+ * use it to get the position of string or procedure streams.
+ */
+ make_int(op, stell(s));
+ return 0;
+}
+
+/* <file> <int> setfileposition - */
+static int
+zsetfileposition(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+
+ check_type(*op, t_integer);
+ check_file(s, op - 1);
+ if (sseek(s, (gs_offset_t)op->value.intval) < 0)
+ return_error(gs_error_ioerror);
+ pop(2);
+ return 0;
+}
+
+/* ------ Non-standard extensions ------ */
+
+/* <file> .filename <string> true */
+/* <file> .filename false */
+static int
+zfilename(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+ gs_const_string fname;
+ byte *str;
+
+ check_file(s, op);
+ if (sfilename(s, &fname) < 0) {
+ make_false(op);
+ return 0;
+ }
+ check_ostack(1);
+ str = ialloc_string(fname.size, "filename");
+ if (str == 0)
+ return_error(gs_error_VMerror);
+ memcpy(str, fname.data, fname.size);
+ push(1); /* can't fail */
+ make_const_string( op - 1 ,
+ a_all | imemory_space((const struct gs_ref_memory_s*) imemory),
+ fname.size,
+ str);
+ make_true(op);
+ return 0;
+}
+
+/* <file> .isprocfilter <bool> */
+static int
+zisprocfilter(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+
+ check_file(s, op);
+ while (s->strm != 0)
+ s = s->strm;
+ make_bool(op, s_is_proc(s));
+ return 0;
+}
+
+/* <file> <string> .peekstring <substring> <filled_bool> */
+static int
+zpeekstring(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+ uint len, rlen;
+
+ check_read_file(i_ctx_p, s, op - 1);
+ check_write_type(*op, t_string);
+ len = r_size(op);
+ while ((rlen = sbufavailable(s)) < len) {
+ int status = s->end_status;
+
+ switch (status) {
+ case EOFC:
+ break;
+ case 0:
+ /*
+ * The following is a HACK. It should reallocate the buffer to hold
+ * at least len bytes. However, this raises messy problems about
+ * which allocator to use and how it should interact with restore.
+ */
+ if (len >= s->bsize)
+ return_error(gs_error_rangecheck);
+ s_process_read_buf(s);
+ continue;
+ default:
+ return handle_read_status(i_ctx_p, status, op - 1, NULL,
+ zpeekstring);
+ }
+ break;
+ }
+ if (rlen > len)
+ rlen = len;
+ /* Don't remove the data from the buffer. */
+ memcpy(op->value.bytes, sbufptr(s), rlen);
+ r_set_size(op, rlen);
+ op[-1] = *op;
+ make_bool(op, (rlen == len ? 1 : 0));
+ return 0;
+}
+
+/* <file> <int> .unread - */
+static int
+zunread(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+ ulong ch;
+
+ check_read_file(i_ctx_p, s, op - 1);
+ check_type(*op, t_integer);
+ ch = op->value.intval;
+ if (ch > 0xff)
+ return_error(gs_error_rangecheck);
+ if (sungetc(s, (byte) ch) < 0)
+ return_error(gs_error_ioerror);
+ pop(2);
+ return 0;
+}
+
+/* <file> <obj> <==flag> .writecvp - */
+static int zwritecvp_continue(i_ctx_t *);
+static int
+zwritecvp_at(i_ctx_t *i_ctx_p, os_ptr op, uint start, bool first)
+{
+ stream *s;
+ byte str[100]; /* arbitrary */
+ ref rstr;
+ const byte *data = str;
+ uint len;
+ int code, status;
+
+ check_write_file(s, op - 2);
+ check_type(*op, t_integer);
+ code = obj_cvp(op - 1, str, sizeof(str), &len, (int)op->value.intval,
+ start, imemory, true);
+ if (code == gs_error_rangecheck) {
+ code = obj_string_data(imemory, op - 1, &data, &len);
+ if (len < start)
+ return_error(gs_error_rangecheck);
+ data += start;
+ len -= start;
+ }
+ if (code < 0)
+ return code;
+ r_set_size(&rstr, len);
+ rstr.value.const_bytes = data;
+ status = write_string(&rstr, s);
+ switch (status) {
+ default:
+ return_error(gs_error_ioerror);
+ case 0:
+ break;
+ case INTC:
+ case CALLC:
+ len = start + len - r_size(&rstr);
+ if (!first)
+ --osp; /* pop(1) without affecting op */
+ return handle_write_status(i_ctx_p, status, op - 2, &len,
+ zwritecvp_continue);
+ }
+ if (code == 1) {
+ if (first)
+ check_ostack(1);
+ push_op_estack(zwritecvp_continue);
+ if (first)
+ push(1);
+ make_int(osp, start + len);
+ return o_push_estack;
+ }
+ if (first) /* zwritecvp */
+ pop(3);
+ else /* zwritecvp_continue */
+ pop(4);
+ return 0;
+}
+static int
+zwritecvp(i_ctx_t *i_ctx_p)
+{
+ return zwritecvp_at(i_ctx_p, osp, 0, true);
+}
+/* Continue a .writecvp after a callout. */
+/* *op is the index within the string. */
+static int
+zwritecvp_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ if (op->value.intval != (uint) op->value.intval)
+ return_error(gs_error_rangecheck);
+ return zwritecvp_at(i_ctx_p, op - 1, (uint) op->value.intval, false);
+}
+
+/* ------ Initialization procedure ------ */
+
+/* We need to split the table because of the 16-element limit. */
+const op_def zfileio1_op_defs[] = {
+ {"1bytesavailable", zbytesavailable},
+ {"1closefile", zclosefile},
+ /* currentfile is in zcontrol.c */
+ {"1echo", zecho},
+ {"1.filename", zfilename},
+ {"1.fileposition", zxfileposition},
+ {"1fileposition", zfileposition},
+ {"0flush", zflush},
+ {"1flushfile", zflushfile},
+ {"1.isprocfilter", zisprocfilter},
+ {"2.peekstring", zpeekstring},
+ {"1print", zprint},
+ {"1read", zread},
+ {"2readhexstring", zreadhexstring},
+ {"2readline", zreadline},
+ {"2readstring", zreadstring},
+ op_def_end(0)
+};
+const op_def zfileio2_op_defs[] = {
+ {"1resetfile", zresetfile},
+ {"2setfileposition", zsetfileposition},
+ {"2.unread", zunread},
+ {"2write", zwrite},
+ {"3.writecvp", zwritecvp},
+ {"2writehexstring", zwritehexstring},
+ {"2writestring", zwritestring},
+ /* Internal operators */
+ {"3%zreadhexstring_continue", zreadhexstring_continue},
+ {"3%zreadline_continue", zreadline_continue},
+ {"3%zreadstring_continue", zreadstring_continue},
+ {"4%zwritecvp_continue", zwritecvp_continue},
+ {"3%zwritehexstring_continue", zwritehexstring_continue},
+ op_def_end(0)
+};
+
+/* ------ Non-operator routines ------ */
+
+/* Switch a file open for read/write access but currently in write mode */
+/* to read mode. */
+int
+file_switch_to_read(const ref * op)
+{
+ stream *s = fptr(op);
+
+ if (s->write_id != r_size(op) || s->file == 0) /* not valid */
+ return_error(gs_error_invalidaccess);
+ if (sswitch(s, false) < 0)
+ return_error(gs_error_ioerror);
+ s->read_id = s->write_id; /* enable reading */
+ s->write_id = 0; /* disable writing */
+ return 0;
+}
+
+/* Switch a file open for read/write access but currently in read mode */
+/* to write mode. */
+int
+file_switch_to_write(const ref * op)
+{
+ stream *s = fptr(op);
+
+ if (s->read_id != r_size(op) || s->file == 0) /* not valid */
+ return_error(gs_error_invalidaccess);
+ if (sswitch(s, true) < 0)
+ return_error(gs_error_ioerror);
+ s->write_id = s->read_id; /* enable writing */
+ s->read_id = 0; /* disable reading */
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+/* Write a string on a file. The file and string have been validated. */
+/* If the status is INTC or CALLC, updates the string on the o-stack. */
+static int
+write_string(ref * op, stream * s)
+{
+ const byte *data = op->value.const_bytes;
+ uint len = r_size(op);
+ uint wlen;
+ int status = sputs(s, data, len, &wlen);
+
+ switch (status) {
+ case INTC:
+ case CALLC:
+ op->value.const_bytes = data + wlen;
+ r_set_size(op, len - wlen);
+ /* falls through */
+ default: /* 0, EOFC, ERRC */
+ return status;
+ }
+}
+
+/*
+ * Look for a stream error message that needs to be copied to
+ * $error.errorinfo, if any.
+ */
+static int
+copy_error_string(i_ctx_t *i_ctx_p, const ref *fop)
+{
+ stream *s;
+
+ for (s = fptr(fop); s->strm != 0 && s->state->error_string[0] == 0;)
+ s = s->strm;
+ if (s->state->error_string[0]) {
+ int code = gs_errorinfo_put_string(i_ctx_p, s->state->error_string);
+
+ if (code < 0)
+ return code;
+ s->state->error_string[0] = 0; /* just do it once */
+ }
+ return_error(gs_error_ioerror);
+}
+
+/* Handle an exceptional status return from a read stream. */
+/* fop points to the ref for the stream. */
+/* ch may be any stream exceptional value. */
+/* Return 0, 1 (EOF), o_push_estack, or an error. */
+static int
+handle_read_status(i_ctx_t *i_ctx_p, int ch, const ref * fop,
+ const uint * pindex, op_proc_t cont)
+{
+ switch (ch) {
+ default: /* error */
+ return copy_error_string(i_ctx_p, fop);
+ case EOFC:
+ return 1;
+ case INTC:
+ case CALLC:
+ if (pindex) {
+ ref index;
+
+ make_int(&index, *pindex);
+ return s_handle_read_exception(i_ctx_p, ch, fop, &index, 1,
+ cont);
+ } else
+ return s_handle_read_exception(i_ctx_p, ch, fop, NULL, 0,
+ cont);
+ }
+}
+
+/* Handle an exceptional status return from a write stream. */
+/* fop points to the ref for the stream. */
+/* ch may be any stream exceptional value. */
+/* Return 0, 1 (EOF), o_push_estack, or an error. */
+static int
+handle_write_status(i_ctx_t *i_ctx_p, int ch, const ref * fop,
+ const uint * pindex, op_proc_t cont)
+{
+ switch (ch) {
+ default: /* error */
+ return copy_error_string(i_ctx_p, fop);
+ case EOFC:
+ return 1;
+ case INTC:
+ case CALLC:
+ if (pindex) {
+ ref index;
+
+ make_int(&index, *pindex);
+ return s_handle_write_exception(i_ctx_p, ch, fop, &index, 1,
+ cont);
+ } else
+ return s_handle_write_exception(i_ctx_p, ch, fop, NULL, 0,
+ cont);
+ }
+}
diff --git a/psi/zfilter.c b/psi/zfilter.c
new file mode 100644
index 000000000..5fd33ad36
--- /dev/null
+++ b/psi/zfilter.c
@@ -0,0 +1,462 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ilevel.h" /* SubFileDecode is different in LL3 */
+#include "stream.h"
+#include "strimpl.h"
+#include "sfilter.h"
+#include "srlx.h"
+#include "sstring.h"
+#include "ifilter.h"
+#include "files.h" /* for filter_open, file_d'_buffer_size */
+
+/* <source> ASCIIHexEncode/filter <file> */
+/* <source> <dict> ASCIIHexEncode/filter <file> */
+static int
+zAXE(i_ctx_t *i_ctx_p)
+{
+ return filter_write_simple(i_ctx_p, &s_AXE_template);
+}
+
+/* <target> ASCIIHexDecode/filter <file> */
+/* <target> <dict> ASCIIHexDecode/filter <file> */
+static int
+zAXD(i_ctx_t *i_ctx_p)
+{
+ return filter_read_simple(i_ctx_p, &s_AXD_template);
+}
+
+/* <target> NullEncode/filter <file> */
+/* <target> <dict_ignored> NullEncode/filter <file> */
+static int
+zNullE(i_ctx_t *i_ctx_p)
+{
+ return filter_write_simple(i_ctx_p, &s_NullE_template);
+}
+
+/* <source> <bool> PFBDecode/filter <file> */
+/* <source> <dict> <bool> PFBDecode/filter <file> */
+static int
+zPFBD(i_ctx_t *i_ctx_p)
+{
+ os_ptr sop = osp;
+ stream_PFBD_state state;
+
+ check_type(*sop, t_boolean);
+ state.binary_to_hex = sop->value.boolval;
+ return filter_read(i_ctx_p, 1, &s_PFBD_template, (stream_state *)&state, 0);
+}
+
+/* <target> PSStringEncode/filter <file> */
+/* <target> <dict> PSStringEncode/filter <file> */
+static int
+zPSSE(i_ctx_t *i_ctx_p)
+{
+ return filter_write_simple(i_ctx_p, &s_PSSE_template);
+}
+
+/* ------ RunLength filters ------ */
+
+/* Common setup for RLE and RLD filters. */
+static int
+rl_setup(os_ptr dop, bool * eod)
+{
+ if (r_has_type(dop, t_dictionary)) {
+ int code;
+
+ check_dict_read(*dop);
+ if ((code = dict_bool_param(dop, "EndOfData", true, eod)) < 0)
+ return code;
+ return 1;
+ } else {
+ *eod = true;
+ return 0;
+ }
+}
+
+/* <target> <record_size> RunLengthEncode/filter <file> */
+/* <target> <dict> <record_size> RunLengthEncode/filter <file> */
+static int
+zRLE(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_RLE_state state;
+ int code;
+
+ check_op(2);
+ code = rl_setup(op - 1, &state.EndOfData);
+ if (code < 0)
+ return code;
+ check_int_leu(*op, max_uint);
+ state.record_size = op->value.intval;
+ return filter_write(i_ctx_p, 1, &s_RLE_template, (stream_state *) & state, 0);
+}
+
+/* <source> RunLengthDecode/filter <file> */
+/* <source> <dict> RunLengthDecode/filter <file> */
+static int
+zRLD(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_RLD_state state;
+ int code = rl_setup(op, &state.EndOfData);
+
+ if (code < 0)
+ return code;
+ return filter_read(i_ctx_p, 0, &s_RLD_template, (stream_state *) & state, 0);
+}
+
+/* <source> <EODcount> <EODstring> SubFileDecode/filter <file> */
+/* <source> <dict> <EODcount> <EODstring> SubFileDecode/filter <file> */
+/* <source> <dict> SubFileDecode/filter <file> *//* (LL3 only) */
+int /* exported for zsysvm.c */
+zSFD(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_SFD_state state;
+ ref *sop = op;
+ int npop;
+
+ if (s_SFD_template.set_defaults)
+ s_SFD_template.set_defaults((stream_state *)&state);
+ if (LL3_ENABLED && r_has_type(op, t_dictionary)) {
+ int count;
+ int code;
+
+ check_dict_read(*op);
+ /*
+ * The PLRM-3rd says that EODCount is a required parameter. However
+ * Adobe accepts files without this value and apparently defaults to
+ * zero. Thus we are doing the same.
+ */
+ if ((code = dict_int_param(op, "EODCount", 0, max_int, 0, &count)) < 0)
+ return code;
+ if (dict_find_string(op, "EODString", &sop) <= 0)
+ return_error(gs_error_rangecheck);
+ state.count = count;
+ npop = 0;
+ } else {
+ check_type(sop[-1], t_integer);
+ if (sop[-1].value.intval < 0)
+ return_error(gs_error_rangecheck);
+ state.count = sop[-1].value.intval;
+ npop = 2;
+ }
+ check_read_type(*sop, t_string);
+ state.eod.data = sop->value.const_bytes;
+ state.eod.size = r_size(sop);
+ return filter_read(i_ctx_p, npop, &s_SFD_template,
+ (stream_state *)&state, r_space(sop));
+}
+
+/* ------ Utilities ------ */
+
+/* Forward references */
+static int filter_ensure_buf(stream **, uint, gs_ref_memory_t *, bool, int );
+
+/* Set up an input filter. */
+int
+filter_read(i_ctx_t *i_ctx_p, int npop, const stream_template * templat,
+ stream_state * st, uint space)
+{
+ os_ptr op = osp;
+ uint min_size = templat->min_out_size + max_min_left;
+ uint save_space = ialloc_space(idmemory);
+ /* PLRM3 requires the following, *not* max(space, save_space). */
+ uint use_space = max(space, avm_system); /* don't alloc in foreign space */
+ os_ptr sop = op - npop;
+ stream *s;
+ stream *sstrm;
+ bool close = false;
+ int code;
+
+ /* Skip over an optional dictionary parameter. */
+ if (r_has_type(sop, t_dictionary)) {
+ check_dict_read(*sop);
+ if ((code = dict_bool_param(sop, "CloseSource", false, &close)) < 0)
+ return code;
+ --sop;
+ }
+ /*
+ * Check to make sure that the underlying data
+ * can function as a source for reading.
+ */
+ use_space = max(use_space, r_space(sop));
+ switch (r_type(sop)) {
+ case t_string:
+ check_read(*sop);
+ ialloc_set_space(idmemory, use_space);
+ sstrm = file_alloc_stream(imemory, "filter_read(string stream)");
+ if (sstrm == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto out;
+ }
+ sread_string(sstrm, sop->value.bytes, r_size(sop));
+ sstrm->is_temp = 1;
+ break;
+ case t_file:
+ check_read_known_file_else(sstrm, sop, return, DO_NOTHING);
+ ialloc_set_space(idmemory, use_space);
+ goto ens;
+ default:
+ check_proc(*sop);
+ ialloc_set_space(idmemory, use_space);
+ code = sread_proc(sop, &sstrm, iimemory);
+ if (code < 0)
+ goto out;
+ sstrm->is_temp = 2;
+ ens:
+ code = filter_ensure_buf(&sstrm,
+ templat->min_in_size +
+ sstrm->state->templat->min_out_size,
+ iimemory, false, close);
+ if (code < 0)
+ goto out;
+ break;
+ }
+ if (min_size < 128)
+ min_size = file_default_buffer_size;
+ code = filter_open("r", min_size, (ref *) sop,
+ &s_filter_read_procs, templat, st, imemory);
+ if (code < 0)
+ goto out;
+ s = fptr(sop);
+ s->strm = sstrm;
+ s->close_strm = close;
+ pop(op - sop);
+out:
+ ialloc_set_space(idmemory, save_space);
+ return code;
+}
+int
+filter_read_simple(i_ctx_t *i_ctx_p, const stream_template * templat)
+{
+ return filter_read(i_ctx_p, 0, templat, NULL, 0);
+}
+
+/* Set up an output filter. */
+int
+filter_write(i_ctx_t *i_ctx_p, int npop, const stream_template * templat,
+ stream_state * st, uint space)
+{
+ os_ptr op = osp;
+ uint min_size = templat->min_in_size + max_min_left;
+ uint save_space = ialloc_space(idmemory);
+ /* PLRM3 requires the following, *not* max(space, save_space). */
+ uint use_space = max(space, avm_system); /* don't alloc in foreign space */
+ os_ptr sop = op - npop;
+ stream *s;
+ stream *sstrm;
+ bool close = false;
+ int code;
+
+ /* Skip over an optional dictionary parameter. */
+ if (r_has_type(sop, t_dictionary)) {
+ check_dict_read(*sop);
+ if ((code = dict_bool_param(sop, "CloseTarget", false, &close)) < 0)
+ return code;
+ --sop;
+ }
+ /*
+ * Check to make sure that the underlying data
+ * can function as a sink for writing.
+ */
+ use_space = max(use_space, r_space(sop));
+ switch (r_type(sop)) {
+ case t_string:
+ check_write(*sop);
+ ialloc_set_space(idmemory, use_space);
+ sstrm = file_alloc_stream(imemory, "filter_write(string)");
+ if (sstrm == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto out;
+ }
+ swrite_string(sstrm, sop->value.bytes, r_size(sop));
+ sstrm->is_temp = 1;
+ break;
+ case t_file:
+ check_write_known_file(sstrm, sop, return);
+ ialloc_set_space(idmemory, use_space);
+ goto ens;
+ default:
+ check_proc(*sop);
+ ialloc_set_space(idmemory, use_space);
+ code = swrite_proc(sop, &sstrm, iimemory);
+ if (code < 0)
+ goto out;
+ sstrm->is_temp = 2;
+ ens:
+ code = filter_ensure_buf(&sstrm,
+ templat->min_out_size +
+ sstrm->state->templat->min_in_size,
+ iimemory, true, close);
+ if (code < 0)
+ goto out;
+ break;
+ }
+ if (min_size < 128)
+ min_size = file_default_buffer_size;
+ code = filter_open("w", min_size, (ref *) sop,
+ &s_filter_write_procs, templat, st, imemory);
+ if (code < 0)
+ goto out;
+ s = fptr(sop);
+ s->strm = sstrm;
+ s->close_strm = close;
+ pop(op - sop);
+out:
+ ialloc_set_space(idmemory, save_space);
+ return code;
+}
+int
+filter_write_simple(i_ctx_t *i_ctx_p, const stream_template * templat)
+{
+ return filter_write(i_ctx_p, 0, templat, NULL, 0);
+}
+
+/* Define a byte-at-a-time NullDecode filter for intermediate buffers. */
+/* (The standard NullDecode filter can read ahead too far.) */
+static int
+s_Null1D_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ if (pr->ptr >= pr->limit)
+ return 0;
+ if (pw->ptr >= pw->limit)
+ return 1;
+ *++(pw->ptr) = *++(pr->ptr);
+ return 1;
+}
+static const stream_template s_Null1D_template = {
+ &st_stream_state, NULL, s_Null1D_process, 1, 1
+};
+
+/* A utility filter that returns an immediate EOF without consuming */
+/* any data from its source. Used by PDF interpreter for unknown */
+/* filter types. */
+static int
+s_EOFD_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ return EOFC;
+}
+static const stream_template s_EOFD_template = {
+ &st_stream_state, NULL, s_EOFD_process, 1, 1
+};
+
+/* <target> /.EOFDecode filter <file> */
+/* <target> <dict> /.EOFDecode filter <file> */
+static int
+zEOFD(i_ctx_t *i_ctx_p)
+{
+ return filter_read_simple(i_ctx_p, &s_EOFD_template);
+}
+
+/* Ensure a minimum buffer size for a filter. */
+/* This may require creating an intermediate stream. */
+static int
+filter_ensure_buf(stream ** ps, uint min_buf_size, gs_ref_memory_t *imem,
+ bool writing, int close)
+{
+ stream *s = *ps;
+ uint min_size = min_buf_size + max_min_left;
+ stream *bs;
+ ref bsop;
+ int code;
+
+ if (s->modes == 0 /* stream is closed */ || s->bsize >= min_size)
+ return 0;
+ /* Otherwise, allocate an intermediate stream. */
+ if (s->cbuf == 0) {
+ /* This is a newly created procedure stream. */
+ /* Just allocate a buffer for it. */
+ uint len = max(min_size, 128);
+ byte *buf = gs_alloc_bytes((gs_memory_t *)imem, len,
+ "filter_ensure_buf");
+
+ if (buf == 0)
+ return_error(gs_error_VMerror);
+ s->cbuf = buf;
+ s->srptr = s->srlimit = s->swptr = buf - 1;
+ s->swlimit = buf - 1 + len;
+ s->bsize = s->cbsize = len;
+ return 0;
+ } else {
+ /* Allocate an intermediate stream. */
+ if (writing)
+ code = filter_open("w", min_size, &bsop, &s_filter_write_procs,
+ &s_NullE_template, NULL, (gs_memory_t *)imem);
+ else
+ code = filter_open("r", min_size, &bsop, &s_filter_read_procs,
+ &s_Null1D_template, NULL, (gs_memory_t *)imem);
+ if (code < 0)
+ return code;
+ bs = fptr(&bsop);
+ bs->strm = s;
+ bs->is_temp = 2;
+ bs->close_strm = close;
+ *ps = bs;
+ return code;
+ }
+}
+
+/* Mark a (filter) stream as temporary. */
+/* We define this here to avoid importing stream.h into zf*.c. */
+void
+filter_mark_temp(const ref * fop, int is_temp)
+{
+ fptr(fop)->is_temp = is_temp;
+}
+
+/* Mark the source or target of a filter as temporary, and propagate */
+/* close_strm from the temporary stream to the filter. */
+void
+filter_mark_strm_temp(const ref * fop, int is_temp)
+{
+ stream *s = fptr(fop);
+ stream *strm = s->strm;
+
+ strm->is_temp = is_temp;
+ s->close_strm = strm->close_strm;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfilter_op_defs[] = {
+ /* We enter PSStringEncode and SubFileDecode (only) */
+ /* as separate operators. */
+ {"1.psstringencode", zPSSE},
+ {"2.subfiledecode", zSFD},
+ op_def_begin_filter(),
+ {"1ASCIIHexEncode", zAXE},
+ {"1ASCIIHexDecode", zAXD},
+ {"1NullEncode", zNullE},
+ {"2PFBDecode", zPFBD},
+ {"1PSStringEncode", zPSSE},
+ {"2RunLengthEncode", zRLE},
+ {"1RunLengthDecode", zRLD},
+ {"3SubFileDecode", zSFD},
+ {"1.EOFDecode", zEOFD},
+ op_def_end(0)
+};
diff --git a/psi/zfilter2.c b/psi/zfilter2.c
new file mode 100644
index 000000000..fd3040c50
--- /dev/null
+++ b/psi/zfilter2.c
@@ -0,0 +1,153 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Additional filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "store.h"
+#include "strimpl.h"
+#include "sfilter.h"
+#include "scfx.h"
+#include "slzwx.h"
+#include "spdiffx.h"
+#include "spngpx.h"
+#include "ifilter.h"
+#include "ifilter2.h"
+#include "ifwpred.h"
+
+/* ------ CCITTFaxEncode filter ------ */
+
+/* <target> <dict> CCITTFaxEncode/filter <file> */
+static int
+zCFE(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_CFE_state cfs;
+ int code;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ code = zcf_setup(op, (stream_CF_state *)&cfs, iimemory);
+ if (code < 0)
+ return code;
+ return filter_write(i_ctx_p, 0, &s_CFE_template, (stream_state *)&cfs, 0);
+}
+
+/* ------ Common setup for possibly pixel-oriented encoding filters ------ */
+
+int
+filter_write_predictor(i_ctx_t *i_ctx_p, int npop,
+ const stream_template * templat, stream_state * st)
+{
+ os_ptr op = osp;
+ int predictor, code;
+ stream_PDiff_state pds;
+ stream_PNGP_state pps;
+
+ if (r_has_type(op, t_dictionary)) {
+ if ((code = dict_int_param(op, "Predictor", 0, 15, 1, &predictor)) < 0)
+ return code;
+ switch (predictor) {
+ case 0: /* identity */
+ predictor = 1;
+ case 1: /* identity */
+ break;
+ case 2: /* componentwise horizontal differencing */
+ code = zpd_setup(op, &pds);
+ break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ /* PNG prediction */
+ code = zpp_setup(op, &pps);
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ if (code < 0)
+ return code;
+ } else
+ predictor = 1;
+ if (predictor == 1)
+ return filter_write(i_ctx_p, npop, templat, st, 0);
+ {
+ /* We need to cascade filters. */
+ ref rtarget, rdict;
+ int code;
+
+ /* Save the operands, just in case. */
+ ref_assign(&rtarget, op - 1);
+ ref_assign(&rdict, op);
+ code = filter_write(i_ctx_p, npop, templat, st, 0);
+ if (code < 0)
+ return code;
+ /* filter_write changed osp.... */
+ op = osp;
+ code =
+ (predictor == 2 ?
+ filter_write(i_ctx_p, 0, &s_PDiffE_template, (stream_state *)&pds, 0) :
+ filter_write(i_ctx_p, 0, &s_PNGPE_template, (stream_state *)&pps, 0));
+ if (code < 0) {
+ /* Restore the operands. Don't bother trying to clean up */
+ /* the first stream. */
+ osp = ++op;
+ ref_assign(op - 1, &rtarget);
+ ref_assign(op, &rdict);
+ return code;
+ }
+ /*
+ * Mark the compression stream as temporary, and propagate
+ * CloseTarget from it to the predictor stream.
+ */
+ filter_mark_strm_temp(op, 2);
+ return code;
+ }
+}
+
+/* ------ LZW encoding filter ------ */
+
+/* <target> LZWEncode/filter <file> */
+/* <target> <dict> LZWEncode/filter <file> */
+static int
+zLZWE(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_LZW_state lzs;
+ int code = zlz_setup(op, &lzs);
+
+ if (code < 0)
+ return code;
+ return filter_write_predictor(i_ctx_p, 0, &s_LZWE_template,
+ (stream_state *) & lzs);
+}
+
+/* ================ Initialization procedure ================ */
+
+const op_def zfilter2_op_defs[] =
+{
+ op_def_begin_filter(),
+ {"2CCITTFaxEncode", zCFE},
+ {"1LZWEncode", zLZWE},
+ op_def_end(0)
+};
diff --git a/psi/zfilterx.c b/psi/zfilterx.c
new file mode 100644
index 000000000..32b3c7b37
--- /dev/null
+++ b/psi/zfilterx.c
@@ -0,0 +1,174 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Extended (non-standard) filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "store.h"
+#include "strimpl.h"
+#include "sfilter.h"
+#include "sbtx.h"
+#include "shcgen.h"
+#include "smtf.h"
+#include "ifilter.h"
+
+
+/* <array> <max_length> .computecodes <array> */
+/* The first max_length+1 elements of the array will be filled in with */
+/* the code counts; the remaining elements will be replaced with */
+/* the code values. This is the form needed for the Tables element of */
+/* the dictionary parameter for the BoundedHuffman filters. */
+static int
+zcomputecodes(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ uint asize;
+ hc_definition def;
+ ushort *data;
+ long *freqs;
+ int code = 0;
+
+ check_type(*op, t_integer);
+ check_write_type(*op1, t_array);
+ asize = r_size(op1);
+ if (op->value.intval < 1 || op->value.intval > max_hc_length)
+ return_error(gs_error_rangecheck);
+ def.num_counts = op->value.intval;
+ if (asize < def.num_counts + 2)
+ return_error(gs_error_rangecheck);
+ def.num_values = asize - (def.num_counts + 1);
+ data = (ushort *) gs_alloc_byte_array(imemory, asize, sizeof(ushort),
+ "zcomputecodes");
+ freqs = (long *)gs_alloc_byte_array(imemory, def.num_values,
+ sizeof(long),
+ "zcomputecodes(freqs)");
+
+ if (data == 0 || freqs == 0)
+ code = gs_note_error(gs_error_VMerror);
+ else {
+ uint i;
+
+ def.counts = data;
+ def.values = data + (def.num_counts + 1);
+ for (i = 0; i < def.num_values; i++) {
+ const ref *pf = op1->value.const_refs + i + def.num_counts + 1;
+
+ if (!r_has_type(pf, t_integer)) {
+ code = gs_note_error(gs_error_typecheck);
+ break;
+ }
+ freqs[i] = pf->value.intval;
+ }
+ if (!code) {
+ code = hc_compute(&def, freqs, imemory);
+ if (code >= 0) {
+ /* Copy back results. */
+ for (i = 0; i < asize; i++)
+ make_int(op1->value.refs + i, data[i]);
+ }
+ }
+ }
+ gs_free_object(imemory, freqs, "zcomputecodes(freqs)");
+ gs_free_object(imemory, data, "zcomputecodes");
+ if (code < 0)
+ return code;
+ pop(1);
+ return code;
+}
+
+
+/* ------ Byte translation filters ------ */
+
+/* Common setup */
+static int
+bt_setup(os_ptr op, stream_BT_state * pbts)
+{
+ check_read_type(*op, t_string);
+ if (r_size(op) != 256)
+ return_error(gs_error_rangecheck);
+ memcpy(pbts->table, op->value.const_bytes, 256);
+ return 0;
+}
+
+/* <target> <table> ByteTranslateEncode/filter <file> */
+/* <target> <table> <dict> ByteTranslateEncode/filter <file> */
+static int
+zBTE(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_BT_state bts;
+ int code = bt_setup(op, &bts);
+
+ if (code < 0)
+ return code;
+ return filter_write(op, 0, &s_BTE_template, (stream_state *)&bts, 0);
+}
+
+/* <target> <table> ByteTranslateDecode/filter <file> */
+/* <target> <table> <dict> ByteTranslateDecode/filter <file> */
+static int
+zBTD(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_BT_state bts;
+ int code = bt_setup(op, &bts);
+
+ if (code < 0)
+ return code;
+ return filter_read(i_ctx_p, 0, &s_BTD_template, (stream_state *)&bts, 0);
+}
+
+/* ------ Move-to-front filters ------ */
+
+/* <target> MoveToFrontEncode/filter <file> */
+/* <target> <dict> MoveToFrontEncode/filter <file> */
+static int
+zMTFE(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ return filter_write_simple(op, &s_MTFE_template);
+}
+
+/* <source> MoveToFrontDecode/filter <file> */
+/* <source> <dict> MoveToFrontDecode/filter <file> */
+static int
+zMTFD(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ return filter_read_simple(op, &s_MTFD_template);
+}
+
+/* ================ Initialization procedure ================ */
+
+const op_def zfilterx_op_defs[] =
+{
+ {"2.computecodes", zcomputecodes}, /* not a filter */
+ op_def_begin_filter(),
+ /* Non-standard filters */
+ {"2ByteTranslateEncode", zBTE},
+ {"2ByteTranslateDecode", zBTD},
+ {"1MoveToFrontEncode", zMTFE},
+ {"1MoveToFrontDecode", zMTFD},
+ op_def_end(0)
+};
diff --git a/psi/zfimscale.c b/psi/zfimscale.c
new file mode 100644
index 000000000..db86fe2b5
--- /dev/null
+++ b/psi/zfimscale.c
@@ -0,0 +1,77 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* $Id: zfimscale.c 6651 2006-03-13 16:18:19Z raph $ */
+
+/* This is the ps interpreter interface to the image mask interpolating
+ filter. */
+
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "ifilter.h"
+#include "idparam.h"
+#include "sisparam.h"
+#include "simscale.h"
+
+/* <source> <dict> imscale/filter <file> */
+
+static int
+z_imscale_d(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp; /* i_ctx_p->op_stack.stack.p defined in osstack.h */
+ int width, height;
+ stream_imscale_state state;
+
+ /* extract the key from the parameter dictionary */
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if (dict_int_param(op, "Width", 0, 1<<24, -1, &width) < 0)
+ return_error(gs_error_rangecheck);
+ if (dict_int_param(op, "Height", 0, 1<<24, -1, &height) < 0)
+ return_error(gs_error_rangecheck);
+
+ state.params.spp_decode = 1;
+ state.params.spp_interp = 1;
+ state.params.BitsPerComponentIn = 1;
+ state.params.MaxValueIn = 1;
+ state.params.WidthIn = width;
+ state.params.HeightIn = height;
+ state.params.BitsPerComponentOut = 1;
+ state.params.MaxValueOut = 1;
+ state.params.WidthOut = width << 2;
+ state.params.HeightOut = height << 2;
+
+ /* we pass npop=0, since we've no arguments left to consume */
+ /* we pass 0 instead of the usual rspace(sop) will allocate storage for
+ filter state from the same memory pool as the stream it's coding. this
+ causes no trouble because we maintain no pointers */
+ return filter_read(i_ctx_p, 0, &s_imscale_template,
+ (stream_state *) & state, 0);
+}
+
+/* Match the above routines to their postscript filter names.
+ This is how our static routines get called externally. */
+const op_def zfimscale_op_defs[] = {
+ op_def_begin_filter(),
+ {"2ImscaleDecode", z_imscale_d},
+ op_def_end(0)
+};
diff --git a/psi/zfjbig2.c b/psi/zfjbig2.c
new file mode 100644
index 000000000..4e325146a
--- /dev/null
+++ b/psi/zfjbig2.c
@@ -0,0 +1,140 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/* this is the ps interpreter interface to the jbig2decode filter
+ used for (1bpp) scanned image compression. PDF only specifies
+ a decoder filter, and we don't currently implement anything else */
+
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gstypes.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "store.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "ifilter.h"
+
+#ifdef USE_LDF_JB2
+#include "sjbig2_luratech.h"
+#else
+#include "sjbig2.h"
+#endif
+
+/* We define a structure, s_jbig2_global_data_t,
+ allocated in the postscript
+ memory space, to hold a pointer to the global decoder
+ context (which is allocated by libjbig2). This allows
+ us to pass the reference through postscript code to
+ the filter initializer. The opaque data pointer is not
+ enumerated and will not be garbage collected. We use
+ a finalize method to deallocate it when the reference
+ is no longer in use. */
+
+static void jbig2_global_data_finalize(const gs_memory_t *cmem, void *vptr);
+gs_private_st_simple_final(st_jbig2_global_data_t, s_jbig2_global_data_t,
+ "jbig2globaldata", jbig2_global_data_finalize);
+
+/* <source> /JBIG2Decode <file> */
+/* <source> <dict> /JBIG2Decode <file> */
+static int
+z_jbig2decode(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *sop = NULL;
+ s_jbig2_global_data_t *gref;
+ stream_jbig2decode_state state;
+
+ /* Extract the global context reference, if any, from the parameter
+ dictionary and embed it in our stream state. The original object
+ ref is under the JBIG2Globals key.
+ We expect the postscript code to resolve this and call
+ z_jbig2makeglobalctx() below to create an astruct wrapping the
+ global decoder data and store it under the .jbig2globalctx key
+ */
+ s_jbig2decode_set_global_data((stream_state*)&state, NULL);
+ if (r_has_type(op, t_dictionary)) {
+ check_dict_read(*op);
+ if ( dict_find_string(op, ".jbig2globalctx", &sop) > 0) {
+ gref = r_ptr(sop, s_jbig2_global_data_t);
+ s_jbig2decode_set_global_data((stream_state*)&state, gref);
+ }
+ }
+
+ /* we pass npop=0, since we've no arguments left to consume */
+ return filter_read(i_ctx_p, 0, &s_jbig2decode_template,
+ (stream_state *) & state, (sop ? r_space(sop) : 0));
+}
+
+/* <bytestring> .jbig2makeglobalctx <jbig2globalctx> */
+/* we call this from ps code to instantiate a jbig2_global_context
+ object which the JBIG2Decode filter uses if available. The
+ pointer to the global context is stored in an astruct object
+ and returned that way since it lives outside the interpreters
+ memory management */
+static int
+z_jbig2makeglobalctx(i_ctx_t * i_ctx_p)
+{
+ void *global = NULL;
+ s_jbig2_global_data_t *st;
+ os_ptr op = osp;
+ byte *data;
+ int size;
+ int code = 0;
+
+ check_type(*op, t_astruct);
+ size = gs_object_size(imemory, op->value.pstruct);
+ data = r_ptr(op, byte);
+
+ code = s_jbig2decode_make_global_data(data, size,
+ &global);
+ if (size > 0 && global == NULL) {
+ dmlprintf(imemory, "failed to create parsed JBIG2GLOBALS object.");
+ return_error(gs_error_unknownerror);
+ }
+
+ st = ialloc_struct(s_jbig2_global_data_t,
+ &st_jbig2_global_data_t,
+ "jbig2decode parsed global context");
+ if (st == NULL) return_error(gs_error_VMerror);
+
+ st->data = global;
+ make_astruct(op, a_readonly | icurrent_space, (byte*)st);
+
+ return code;
+}
+
+/* free our referenced global context data */
+static void jbig2_global_data_finalize(const gs_memory_t *cmem, void *vptr)
+{
+ s_jbig2_global_data_t *st = vptr;
+ (void)cmem; /* unused */
+
+ if (st->data) s_jbig2decode_free_global_data(st->data);
+ st->data = NULL;
+}
+
+/* Match the above routine to the corresponding filter name.
+ This is how our static routines get called externally. */
+const op_def zfjbig2_op_defs[] = {
+ {"1.jbig2makeglobalctx", z_jbig2makeglobalctx},
+ op_def_begin_filter(),
+ {"2JBIG2Decode", z_jbig2decode},
+ op_def_end(0)
+};
diff --git a/psi/zfjpx.c b/psi/zfjpx.c
new file mode 100644
index 000000000..91e75af9d
--- /dev/null
+++ b/psi/zfjpx.c
@@ -0,0 +1,149 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/* This is the ps interpreter interface to the JPXDecode filter
+ used for (JPEG2000) scanned image compression. PDF only specifies
+ a decoder filter, and we don't currently implement anything else. */
+
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gstypes.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "store.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "ifilter.h"
+#include "iname.h"
+#include "gdebug.h"
+
+#if defined(USE_LWF_JP2)
+# include "sjpx_luratech.h"
+#elif defined(USE_OPENJPEG_JP2)
+# include "sjpx_openjpeg.h"
+#else
+# include "sjpx.h"
+#endif
+
+/* macro to test a name ref against a C string */
+# define ISTRCMP(ref, string) (memcmp((ref)->value.const_bytes, string, \
+ min(strlen(string), r_size(ref))))
+
+/* <source> /JPXDecode <file> */
+/* <source> <dict> /JPXDecode <file> */
+static int
+z_jpx_decode(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *sop = NULL;
+ ref *csname = NULL;
+ stream_jpxd_state state;
+
+ /* it's our responsibility to call set_defaults() */
+ state.memory = imemory->non_gc_memory;
+ if (s_jpxd_template.set_defaults)
+ (*s_jpxd_template.set_defaults)((stream_state *)&state);
+ if (r_has_type(op, t_dictionary)) {
+ check_dict_read(*op);
+ if ( dict_find_string(op, "Alpha", &sop) > 0) {
+ check_type(*sop, t_boolean);
+ if (sop->value.boolval)
+ state.alpha = true;
+ }
+ if ( dict_find_string(op, "ColorSpace", &sop) > 0) {
+ /* parse the value */
+ if (r_is_array(sop)) {
+ /* assume it's the first array element */
+ csname = sop->value.refs;
+ } else if (r_has_type(sop,t_name)) {
+ /* use the name directly */
+ csname = sop;
+ } else {
+ dmprintf(imemory, "warning: JPX ColorSpace value is an unhandled type!\n");
+ }
+ if (csname != NULL) {
+ ref sref;
+ /* get a reference to the name's string value */
+ name_string_ref(imemory, csname, &sref);
+ /* request raw index values if the colorspace is /Indexed */
+ if (!ISTRCMP(&sref, "Indexed"))
+ state.colorspace = gs_jpx_cs_indexed;
+ /* tell the filter what output we want for other spaces */
+ else if (!ISTRCMP(&sref, "DeviceGray"))
+ state.colorspace = gs_jpx_cs_gray;
+ else if (!ISTRCMP(&sref, "DeviceRGB"))
+ state.colorspace = gs_jpx_cs_rgb;
+ else if (!ISTRCMP(&sref, "DeviceCMYK"))
+ state.colorspace = gs_jpx_cs_cmyk;
+ else if (!ISTRCMP(&sref, "ICCBased")) {
+ /* The second array element should be the profile's
+ stream dict */
+ ref *csdict = sop->value.refs + 1;
+ ref *nref;
+ ref altname;
+ if (r_is_array(sop) && (r_size(sop) > 1) &&
+ r_has_type(csdict, t_dictionary)) {
+ check_dict_read(*csdict);
+ /* try to look up the alternate space */
+ if (dict_find_string(csdict, "Alternate", &nref) > 0) {
+ name_string_ref(imemory, csname, &altname);
+ if (!ISTRCMP(&altname, "DeviceGray"))
+ state.colorspace = gs_jpx_cs_gray;
+ else if (!ISTRCMP(&altname, "DeviceRGB"))
+ state.colorspace = gs_jpx_cs_rgb;
+ else if (!ISTRCMP(&altname, "DeviceCMYK"))
+ state.colorspace = gs_jpx_cs_cmyk;
+ }
+ /* else guess based on the number of components */
+ if (state.colorspace == gs_jpx_cs_unset &&
+ dict_find_string(csdict, "N", &nref) > 0) {
+ if_debug1m('w', imemory, "[w] JPX image has an external %"PRIpsint
+ " channel colorspace\n", nref->value.intval);
+ switch (nref->value.intval) {
+ case 1: state.colorspace = gs_jpx_cs_gray;
+ break;
+ case 3: state.colorspace = gs_jpx_cs_rgb;
+ break;
+ case 4: state.colorspace = gs_jpx_cs_cmyk;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ if_debug0m('w', imemory, "[w] Couldn't read JPX ColorSpace key!\n");
+ }
+ }
+ }
+
+ /* we pass npop=0, since we've no arguments left to consume */
+ /* we pass 0 instead of the usual rspace(sop) which will allocate storage
+ for filter state from the same memory pool as the stream it's coding.
+ this causes no trouble because we maintain no pointers */
+ return filter_read(i_ctx_p, 0, &s_jpxd_template,
+ (stream_state *) & state, 0);
+}
+
+/* Match the above routine to the corresponding filter name.
+ This is how our static routines get called externally. */
+const op_def zfjpx_op_defs[] = {
+ op_def_begin_filter(),
+ {"2JPXDecode", z_jpx_decode},
+ op_def_end(0)
+};
diff --git a/psi/zfmd5.c b/psi/zfmd5.c
new file mode 100644
index 000000000..308327ee1
--- /dev/null
+++ b/psi/zfmd5.c
@@ -0,0 +1,43 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* MD5Encode filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "smd5.h"
+#include "ifilter.h"
+
+/* <source> MD5Encode/filter <file> */
+/* <source> <dict> MD5Encode/filter <file> */
+static int
+zMD5E(i_ctx_t *i_ctx_p)
+{
+ return filter_write_simple(i_ctx_p, &s_MD5E_template);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfmd5_op_defs[] =
+{
+ op_def_begin_filter(),
+ {"1MD5Encode", zMD5E},
+ op_def_end(0)
+};
diff --git a/psi/zfont.c b/psi/zfont.c
new file mode 100644
index 000000000..eb401a167
--- /dev/null
+++ b/psi/zfont.c
@@ -0,0 +1,653 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Generic font operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h" /* for registering root */
+#include "gzstate.h" /* must precede gxdevice */
+#include "gxdevice.h" /* must precede gxfont */
+#include "gxfont.h"
+#include "gxfcache.h"
+#include "bfont.h"
+#include "ialloc.h"
+#include "iddict.h"
+#include "igstate.h"
+#include "iname.h" /* for name_mark_index */
+#include "isave.h"
+#include "store.h"
+#include "ivmspace.h"
+#include "gscencs.h"
+
+/* Forward references */
+static int make_font(i_ctx_t *, const gs_matrix *);
+static void make_uint_array(os_ptr, const uint *, int);
+static int setup_unicode_decoder(i_ctx_t *i_ctx_p, ref *Decoding);
+
+/* Mark a glyph as a PostScript name (if it isn't a CID). */
+bool
+zfont_mark_glyph_name(const gs_memory_t *mem, gs_glyph glyph, void *ignore_data)
+{
+ return (glyph >= gs_c_min_std_encoding_glyph || glyph == gs_no_glyph ? false :
+ name_mark_index(mem, (uint) glyph));
+}
+
+/* Get a global glyph code. */
+static int
+zfont_global_glyph_code(const gs_memory_t *mem, gs_const_string *gstr, gs_glyph *pglyph)
+{
+ ref v;
+ int code = name_ref(mem, gstr->data, gstr->size, &v, 0);
+
+ if (code < 0)
+ return code;
+ *pglyph = (gs_glyph)name_index(mem, &v);
+ return 0;
+}
+
+/* Initialize the font operators */
+static int
+zfont_init(i_ctx_t *i_ctx_p)
+{
+ ifont_dir = gs_font_dir_alloc2(imemory->stable_memory, imemory->non_gc_memory);
+ if (ifont_dir == NULL)
+ return gs_error_VMerror;
+ ifont_dir->ccache.mark_glyph = zfont_mark_glyph_name;
+ ifont_dir->global_glyph_code = zfont_global_glyph_code;
+ return gs_register_struct_root(imemory, NULL, (void **)&ifont_dir,
+ "ifont_dir");
+}
+
+/* <font> <scale> scalefont <new_font> */
+static int
+zscalefont(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ double scale;
+ gs_matrix mat;
+
+ if ((code = real_param(op, &scale)) < 0)
+ return code;
+ if ((code = gs_make_scaling(scale, scale, &mat)) < 0)
+ return code;
+ return make_font(i_ctx_p, &mat);
+}
+
+/* <font> <matrix> makefont <new_font> */
+static int
+zmakefont(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ gs_matrix mat;
+
+ if ((code = read_matrix(imemory, op, &mat)) < 0)
+ return code;
+ return make_font(i_ctx_p, &mat);
+}
+
+/* <font> setfont - */
+int
+zsetfont(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_font *pfont;
+ int code = font_param(op, &pfont);
+
+ if (code < 0 || (code = gs_setfont(igs, pfont)) < 0)
+ return code;
+ pop(1);
+ return code;
+}
+
+/* - currentfont <font> */
+static int
+zcurrentfont(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ *op = *pfont_dict(gs_currentfont(igs));
+ return 0;
+}
+
+/* - cachestatus <mark> <bsize> <bmax> <msize> <mmax> <csize> <cmax> <blimit> */
+static int
+zcachestatus(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint status[7];
+
+ gs_cachestatus(ifont_dir, status);
+ push(7);
+ make_uint_array(op - 6, status, 7);
+ return 0;
+}
+
+/* <blimit> setcachelimit - */
+static int
+zsetcachelimit(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_int_leu(*op, max_uint);
+ gs_setcachelimit(ifont_dir, (uint) op->value.intval);
+ pop(1);
+ return 0;
+}
+
+/* <mark> <size> <lower> <upper> setcacheparams - */
+static int
+zsetcacheparams(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint params[3];
+ int i, code;
+ os_ptr opp = op;
+
+ for (i = 0; i < 3 && !r_has_type(opp, t_mark); i++, opp--) {
+ check_int_leu(*opp, max_uint);
+ params[i] = opp->value.intval;
+ }
+ switch (i) {
+ case 3:
+ if ((code = gs_setcachesize(igs, ifont_dir, params[2])) < 0)
+ return code;
+ case 2:
+ if ((code = gs_setcachelower(ifont_dir, params[1])) < 0)
+ return code;
+ case 1:
+ if ((code = gs_setcacheupper(ifont_dir, params[0])) < 0)
+ return code;
+ case 0:;
+ }
+ return zcleartomark(i_ctx_p);
+}
+
+/* - currentcacheparams <mark> <size> <lower> <upper> */
+static int
+zcurrentcacheparams(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint params[3];
+
+ params[0] = gs_currentcachesize(ifont_dir);
+ params[1] = gs_currentcachelower(ifont_dir);
+ params[2] = gs_currentcacheupper(ifont_dir);
+ push(4);
+ make_mark(op - 3);
+ make_uint_array(op - 2, params, 3);
+ return 0;
+}
+
+/* <font> .registerfont - */
+static int
+zregisterfont(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_font *pfont;
+ int code = font_param(op, &pfont);
+
+ if (code < 0)
+ return code;
+ pfont->is_resource = true;
+ pop(1);
+ return 0;
+}
+
+/* <Decoding> .setupUnicodeDecoder - */
+static int
+zsetupUnicodeDecoder(i_ctx_t *i_ctx_p)
+{ /* The allocation mode must be global. */
+ os_ptr op = osp;
+ int code;
+
+ check_type(*op, t_dictionary);
+ code = setup_unicode_decoder(i_ctx_p, op);
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfont_op_defs[] =
+{
+ {"0currentfont", zcurrentfont},
+ {"2makefont", zmakefont},
+ {"2scalefont", zscalefont},
+ {"1setfont", zsetfont},
+ {"0cachestatus", zcachestatus},
+ {"1setcachelimit", zsetcachelimit},
+ {"1setcacheparams", zsetcacheparams},
+ {"0currentcacheparams", zcurrentcacheparams},
+ {"1.registerfont", zregisterfont},
+ {"1.setupUnicodeDecoder", zsetupUnicodeDecoder},
+ op_def_end(zfont_init)
+};
+
+/* ------ Subroutines ------ */
+
+/* Validate a font parameter. */
+int
+font_param(const ref * pfdict, gs_font ** ppfont)
+{ /*
+ * Check that pfdict is a read-only dictionary, that it has a FID
+ * entry whose value is a fontID, and that the fontID points to a
+ * gs_font structure whose associated PostScript dictionary is
+ * pfdict.
+ */
+ ref *pid;
+ gs_font *pfont;
+ const font_data *pdata;
+
+ check_type(*pfdict, t_dictionary);
+ if (dict_find_string(pfdict, "FID", &pid) <= 0 ||
+ !r_has_type(pid, t_fontID)
+ )
+ return_error(gs_error_invalidfont);
+ pfont = r_ptr(pid, gs_font);
+ if (pfont == 0)
+ return_error(gs_error_invalidfont); /* unregistered font */
+ pdata = pfont->client_data;
+ if (!obj_eq(pfont->memory, &pdata->dict, pfdict))
+ return_error(gs_error_invalidfont);
+ *ppfont = pfont;
+ return 0;
+}
+
+/* Add the FID entry to a font dictionary. */
+/* Note that i_ctx_p may be NULL. */
+int
+add_FID(i_ctx_t *i_ctx_p, ref * fp /* t_dictionary */ , gs_font * pfont,
+ gs_ref_memory_t *imem)
+{
+ ref fid;
+
+ make_tav(&fid, t_fontID,
+ a_readonly | imemory_space(imem) | imemory_new_mask(imem),
+ pstruct, (void *)pfont);
+ return (i_ctx_p ? idict_put_string(fp, "FID", &fid) :
+ dict_put_string(fp, "FID", &fid, NULL));
+}
+
+/* Make a transformed font (common code for makefont/scalefont). */
+static int
+make_font(i_ctx_t *i_ctx_p, const gs_matrix * pmat)
+{
+ os_ptr op = osp;
+ os_ptr fp = op - 1;
+ gs_font *oldfont, *newfont;
+ int code;
+ ref *pencoding = 0;
+
+ code = font_param(fp, &oldfont);
+ if (code < 0)
+ return code;
+ {
+ uint space = ialloc_space(idmemory);
+
+ ialloc_set_space(idmemory, r_space(fp));
+ if (dict_find_string(fp, "Encoding", &pencoding) > 0 &&
+ !r_is_array(pencoding)
+ )
+ code = gs_note_error(gs_error_invalidfont);
+ else {
+ /*
+ * Temporarily substitute the new dictionary
+ * for the old one, in case the Encoding changed.
+ */
+ ref olddict;
+
+ olddict = *pfont_dict(oldfont);
+ *pfont_dict(oldfont) = *fp;
+ code = gs_makefont(ifont_dir, oldfont, pmat, &newfont);
+ *pfont_dict(oldfont) = olddict;
+ }
+ ialloc_set_space(idmemory, space);
+ }
+ if (code < 0)
+ return code;
+ /*
+ * We have to allow for the possibility that the font's Encoding
+ * is different from that of the base font. Note that the
+ * font_data of the new font was simply copied from the old one.
+ */
+ if (pencoding != 0 &&
+ !obj_eq(imemory, pencoding, &pfont_data(newfont)->Encoding)
+ ) {
+ if (newfont->FontType == ft_composite)
+ return_error(gs_error_rangecheck);
+ /* We should really do validity checking here.... */
+ ref_assign(&pfont_data(newfont)->Encoding, pencoding);
+ lookup_gs_simple_font_encoding((gs_font_base *) newfont);
+ }
+ *fp = *pfont_dict(newfont);
+ pop(1);
+ return 0;
+}
+/* Create the transformed font dictionary. */
+/* This is the make_font completion procedure for all non-composite fonts */
+/* created at the interpreter level (see build_gs_simple_font in zbfont.c.) */
+int
+zbase_make_font(gs_font_dir * pdir, const gs_font * oldfont,
+ const gs_matrix * pmat, gs_font ** ppfont)
+{
+ /*
+ * We must call gs_base_make_font so that the XUID gets copied
+ * if necessary.
+ */
+ int code = gs_base_make_font(pdir, oldfont, pmat, ppfont);
+
+ if (code < 0)
+ return code;
+ return zdefault_make_font(pdir, oldfont, pmat, ppfont);
+}
+int
+zdefault_make_font(gs_font_dir * pdir, const gs_font * oldfont,
+ const gs_matrix * pmat, gs_font ** ppfont)
+{
+ gs_font *newfont = *ppfont;
+ gs_memory_t *mem = newfont->memory;
+ /* HACK: we know this font was allocated by the interpreter. */
+ gs_ref_memory_t *imem = (gs_ref_memory_t *)mem;
+ ref *fp = pfont_dict(oldfont);
+ font_data *pdata;
+ ref newdict, newmat, scalemat;
+ uint dlen = dict_maxlength(fp);
+ uint mlen = dict_length(fp) + 3; /* FontID, OrigFont, ScaleMatrix */
+ int code;
+
+ if (dlen < mlen)
+ dlen = mlen;
+ if ((pdata = gs_alloc_struct(mem, font_data, &st_font_data,
+ "make_font(font_data)")) == 0
+ )
+ return_error(gs_error_VMerror);
+ /*
+ * This dictionary is newly created: it's safe to pass NULL as the
+ * dstack pointer to dict_copy and dict_put_string.
+ */
+ if ((code = dict_alloc(imem, dlen, &newdict)) < 0 ||
+ (code = dict_copy(fp, &newdict, NULL)) < 0 ||
+ (code = gs_alloc_ref_array(imem, &newmat, a_all, 12,
+ "make_font(matrices)")) < 0
+ )
+ return code;
+ refset_null_new(newmat.value.refs, 12, imemory_new_mask(imem));
+ ref_assign(&scalemat, &newmat);
+ r_set_size(&scalemat, 6);
+ scalemat.value.refs += 6;
+ /*
+ * Create the scaling matrix. We could do this several different
+ * ways: by "dividing" the new FontMatrix by the base FontMatrix, by
+ * multiplying the current scaling matrix by a ScaleMatrix kept in
+ * the gs_font, or by multiplying the current scaling matrix by the
+ * ScaleMatrix from the font dictionary. We opt for the last of
+ * these.
+ */
+ {
+ gs_matrix scale, prev_scale;
+ ref *ppsm;
+
+ if (!(dict_find_string(fp, "ScaleMatrix", &ppsm) > 0 &&
+ read_matrix(mem, ppsm, &prev_scale) >= 0 &&
+ gs_matrix_multiply(pmat, &prev_scale, &scale) >= 0)
+ )
+ scale = *pmat;
+ write_matrix_new(&scalemat, &scale, imem);
+ }
+ r_clear_attrs(&scalemat, a_write);
+ r_set_size(&newmat, 6);
+ write_matrix_new(&newmat, &newfont->FontMatrix, imem);
+ r_clear_attrs(&newmat, a_write);
+ if ((code = dict_put_string(&newdict, "FontMatrix", &newmat, NULL)) < 0 ||
+ (code = dict_put_string(&newdict, "OrigFont", pfont_dict(oldfont->base), NULL)) < 0 ||
+ (code = dict_put_string(&newdict, "ScaleMatrix", &scalemat, NULL)) < 0 ||
+ (code = add_FID(NULL, &newdict, newfont, imem)) < 0
+ )
+ return code;
+ newfont->client_data = pdata;
+ *pdata = *pfont_data(oldfont);
+ pdata->dict = newdict;
+ r_clear_attrs(dict_access_ref(&newdict), a_write);
+ return 0;
+}
+
+/* Convert an array of (unsigned) integers to stack form. */
+static void
+make_uint_array(register os_ptr op, const uint * intp, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++, op++, intp++)
+ make_int(op, *intp);
+}
+
+/* Remove scaled font and character cache entries that would be */
+/* invalidated by a restore. */
+static bool
+purge_if_name_removed(const gs_memory_t *mem, cached_char * cc, void *vsave)
+{
+ return alloc_name_index_is_since_save(mem, cc->code, vsave);
+}
+
+/* Remove entries from font and character caches. */
+int
+font_restore(const alloc_save_t * save)
+{
+
+ gs_memory_t *smem = gs_save_any_memory(save);
+ gs_font_dir *pdir = smem->gs_lib_ctx->font_dir;
+ const gs_memory_t *mem = 0;
+ int code;
+
+ if (pdir == 0) /* not initialized yet */
+ return 0;
+
+ /* Purge original (unscaled) fonts. */
+
+ {
+ gs_font *pfont;
+
+otop:
+ for (pfont = pdir->orig_fonts; pfont != 0;
+ pfont = pfont->next
+ ) {
+ mem = pfont->memory;
+ if (alloc_is_since_save((char *)pfont, save)) {
+ code = gs_purge_font(pfont);
+ if (code < 0)
+ return code;
+ goto otop;
+ }
+ }
+ }
+
+ /* Purge cached scaled fonts. */
+
+ {
+ gs_font *pfont;
+
+top:
+ for (pfont = pdir->scaled_fonts; pfont != 0;
+ pfont = pfont->next
+ ) {
+ if (alloc_is_since_save((char *)pfont, save)) {
+ code = gs_purge_font(pfont);
+ if (code < 0)
+ return code;
+ goto top;
+ }
+ }
+ }
+
+ /* Purge xfonts and uncached scaled fonts. */
+
+ {
+ cached_fm_pair *pair;
+ uint n;
+
+ for (pair = pdir->fmcache.mdata, n = pdir->fmcache.mmax;
+ n > 0; pair++, n--
+ )
+ if (!fm_pair_is_free(pair)) {
+#if 0
+ /* We disabled this code portion because
+ gx_add_fm_pair now copied xvalues
+ into a stable memory.
+ */
+ if ((uid_is_XUID(&pair->UID) &&
+ alloc_is_since_save((char *)pair->UID.xvalues,
+ save))
+ ) {
+ code = gs_purge_fm_pair(pdir, pair, 0);
+ if (code < 0)
+ return code;
+ continue;
+ }
+#endif
+ if (pair->font != 0 &&
+ alloc_is_since_save((char *)pair->font, save)
+ ) {
+ if (!uid_is_valid(&pair->UID))
+ gs_clean_fm_pair(pdir, pair);
+ /* Don't discard pairs with a surviving UID. */
+ pair->font = 0;
+ }
+ if (pair->xfont != 0 &&
+ alloc_is_since_save((char *)pair->xfont, save)
+ ) {
+ code = gs_purge_fm_pair(pdir, pair, 1);
+ if (code < 0)
+ return code;
+ }
+ }
+ }
+
+ /* Purge characters with names about to be removed. */
+ /* We only need to do this if any new names have been created */
+ /* since the save. */
+
+ if (alloc_any_names_since_save(save))
+ gx_purge_selected_cached_chars(pdir, purge_if_name_removed,
+ (void *)save);
+ return 0;
+}
+
+/* ------ Font procedures for PostScript fonts ------ */
+
+/* font_info procedure */
+static bool
+zfont_info_has(const ref *pfidict, const char *key, gs_const_string *pmember)
+{
+ ref *pvalue;
+
+ if (dict_find_string(pfidict, key, &pvalue) > 0 &&
+ r_has_type(pvalue, t_string)
+ ) {
+ pmember->data = pvalue->value.const_bytes;
+ pmember->size = r_size(pvalue);
+ return true;
+ }
+ return false;
+}
+int
+zfont_info(gs_font *font, const gs_point *pscale, int members,
+ gs_font_info_t *info)
+{
+ int code = gs_default_font_info(font, pscale, members &
+ ~(FONT_INFO_COPYRIGHT | FONT_INFO_NOTICE |
+ FONT_INFO_FAMILY_NAME | FONT_INFO_FULL_NAME),
+ info);
+ const ref *pfdict;
+ ref *pfontinfo, *pvalue;
+
+ if (code < 0)
+ return code;
+ pfdict = &pfont_data(font)->dict;
+ if (dict_find_string(pfdict, "FontInfo", &pfontinfo) <= 0 ||
+ !r_has_type(pfontinfo, t_dictionary))
+ return 0;
+ if ((members & FONT_INFO_COPYRIGHT) &&
+ zfont_info_has(pfontinfo, "Copyright", &info->Copyright))
+ info->members |= FONT_INFO_COPYRIGHT;
+ if ((members & FONT_INFO_NOTICE) &&
+ zfont_info_has(pfontinfo, "Notice", &info->Notice))
+ info->members |= FONT_INFO_NOTICE;
+ if ((members & FONT_INFO_FAMILY_NAME) &&
+ zfont_info_has(pfontinfo, "FamilyName", &info->FamilyName))
+ info->members |= FONT_INFO_FAMILY_NAME;
+ if ((members & FONT_INFO_FULL_NAME) &&
+ zfont_info_has(pfontinfo, "FullName", &info->FullName))
+ info->members |= FONT_INFO_FULL_NAME;
+ if ((members & FONT_INFO_EMBEDDING_RIGHTS)
+ && (dict_find_string(pfontinfo, "FSType", &pvalue) > 0)) {
+ info->EmbeddingRights = pvalue->value.intval;
+ info->members |= FONT_INFO_EMBEDDING_RIGHTS;
+ }
+ return code;
+}
+
+/* -------------------- Utilities --------------*/
+
+typedef struct gs_unicode_decoder_s {
+ ref data;
+} gs_unicode_decoder;
+
+/* GC procedures */
+static
+CLEAR_MARKS_PROC(unicode_decoder_clear_marks)
+{ gs_unicode_decoder *const pptr = vptr;
+
+ r_clear_attrs(&pptr->data, l_mark);
+}
+static
+ENUM_PTRS_WITH(unicode_decoder_enum_ptrs, gs_unicode_decoder *pptr) return 0;
+case 0:
+ENUM_RETURN_REF(&pptr->data);
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(unicode_decoder_reloc_ptrs, gs_unicode_decoder *pptr);
+RELOC_REF_VAR(pptr->data);
+r_clear_attrs(&pptr->data, l_mark);
+RELOC_PTRS_END
+
+gs_private_st_complex_only(st_unicode_decoder, gs_unicode_decoder,\
+ "unicode_decoder", unicode_decoder_clear_marks, unicode_decoder_enum_ptrs,
+ unicode_decoder_reloc_ptrs, 0);
+
+/* Get the Unicode value for a glyph. */
+const ref *
+zfont_get_to_unicode_map(gs_font_dir *dir)
+{
+ const gs_unicode_decoder *pud = (gs_unicode_decoder *)dir->glyph_to_unicode_table;
+
+ return (pud == NULL ? NULL : &pud->data);
+}
+
+static int
+setup_unicode_decoder(i_ctx_t *i_ctx_p, ref *Decoding)
+{
+ gs_unicode_decoder *pud = gs_alloc_struct(imemory, gs_unicode_decoder,
+ &st_unicode_decoder, "setup_unicode_decoder");
+ if (pud == NULL)
+ return_error(gs_error_VMerror);
+ ref_assign_new(&pud->data, Decoding);
+ ifont_dir->glyph_to_unicode_table = pud;
+ return 0;
+}
diff --git a/psi/zfont0.c b/psi/zfont0.c
new file mode 100644
index 000000000..6db3b84f8
--- /dev/null
+++ b/psi/zfont0.c
@@ -0,0 +1,357 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Composite font creation operator */
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+/*
+ * The following lines used to say:
+ * #include "gsmatrix.h"
+ * #include "gxdevice.h" /. for gxfont.h ./
+ * Tony Li says the longer list is necessary to keep the GNU compiler
+ * happy, but this is pretty hard to understand....
+ */
+#include "gxfixed.h"
+#include "gxmatrix.h"
+#include "gzstate.h" /* must precede gxdevice */
+#include "gxdevice.h" /* must precede gxfont */
+#include "gxfcmap.h"
+#include "gxfont.h"
+#include "gxfont0.h"
+#include "bfont.h"
+#include "ialloc.h"
+#include "iddict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "iname.h"
+#include "store.h"
+
+/* Imported from zfcmap.c */
+int ztype0_get_cmap(const gs_cmap_t ** ppcmap, const ref * pfdepvector,
+ const ref * op, gs_memory_t *imem);
+
+/* Forward references */
+static font_proc_define_font(ztype0_define_font);
+static font_proc_make_font(ztype0_make_font);
+static int ensure_char_entry(i_ctx_t *, os_ptr, const char *, byte *, int);
+
+/* <string|name> <font_dict> .buildfont0 <string|name> <font> */
+/* Build a type 0 (composite) font. */
+static int
+zbuildfont0(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_type0_data data;
+ ref fdepvector;
+ ref *pprefenc;
+ gs_font_type0 *pfont;
+ font_data *pdata;
+ ref save_FID;
+ int i;
+ int code = 0;
+
+ check_type(*op, t_dictionary);
+ {
+ ref *pfmaptype;
+ ref *pfdepvector;
+
+ if (dict_find_string(op, "FMapType", &pfmaptype) <= 0 ||
+ !r_has_type(pfmaptype, t_integer) ||
+ pfmaptype->value.intval < (int)fmap_type_min ||
+ pfmaptype->value.intval > (int)fmap_type_max ||
+ dict_find_string(op, "FDepVector", &pfdepvector) <= 0 ||
+ !r_is_array(pfdepvector)
+ )
+ return_error(gs_error_invalidfont);
+ data.FMapType = (fmap_type) pfmaptype->value.intval;
+ /*
+ * Adding elements below could cause the font dictionary to be
+ * resized, which would invalidate pfdepvector.
+ */
+ fdepvector = *pfdepvector;
+ }
+ /* Check that every element of the FDepVector is a font. */
+ data.fdep_size = r_size(&fdepvector);
+ for (i = 0; i < data.fdep_size; i++) {
+ ref fdep;
+ gs_font *psub;
+
+ array_get(imemory, &fdepvector, i, &fdep);
+ if ((code = font_param(&fdep, &psub)) < 0)
+ return code;
+ /*
+ * Check the inheritance rules. Allowed configurations
+ * (paths from root font) are defined by the regular
+ * expression:
+ * (shift | double_escape escape* | escape*)
+ * non_modal* non_composite
+ */
+ if (psub->FontType == ft_composite) {
+ const gs_font_type0 *const psub0 = (const gs_font_type0 *)psub;
+ fmap_type fmt = psub0->data.FMapType;
+
+ if (fmt == fmap_double_escape ||
+ fmt == fmap_shift ||
+ (fmt == fmap_escape &&
+ !(data.FMapType == fmap_escape ||
+ data.FMapType == fmap_double_escape))
+ )
+ return_error(gs_error_invalidfont);
+ }
+ }
+ switch (data.FMapType) {
+ case fmap_escape:
+ case fmap_double_escape: /* need EscChar */
+ code = ensure_char_entry(i_ctx_p, op, "EscChar", &data.EscChar, 255);
+ break;
+ case fmap_shift: /* need ShiftIn & ShiftOut */
+ code = ensure_char_entry(i_ctx_p, op, "ShiftIn", &data.ShiftIn, 15);
+ if (code >= 0)
+ code = ensure_char_entry(i_ctx_p, op, "ShiftOut", &data.ShiftOut, 14);
+ break;
+ case fmap_SubsVector: /* need SubsVector */
+ {
+ ref *psubsvector;
+ uint svsize;
+
+ if (dict_find_string(op, "SubsVector", &psubsvector) <= 0 ||
+ !r_has_type(psubsvector, t_string) ||
+ (svsize = r_size(psubsvector)) == 0 ||
+ (data.subs_width = (int)*psubsvector->value.bytes + 1) > 4 ||
+ (svsize - 1) % data.subs_width != 0
+ )
+ return_error(gs_error_invalidfont);
+ data.subs_size = (svsize - 1) / data.subs_width;
+ data.SubsVector.data = psubsvector->value.bytes + 1;
+ data.SubsVector.size = svsize - 1;
+ } break;
+ case fmap_CMap: /* need CMap */
+ code = ztype0_get_cmap(&data.CMap, (const ref *)&fdepvector,
+ (const ref *)op, imemory);
+ break;
+ default:
+ ;
+ }
+ if (code < 0)
+ return code;
+ /*
+ * Save the old FID in case we have to back out.
+ * build_gs_font will return an error if there is a FID entry
+ * but it doesn't reference a valid font.
+ */
+ {
+ ref *pfid;
+
+ if (dict_find_string(op, "FID", &pfid) <= 0)
+ make_null(&save_FID);
+ else
+ save_FID = *pfid;
+ }
+ {
+ build_proc_refs build;
+
+ code = build_proc_name_refs(imemory, &build,
+ "%Type0BuildChar", "%Type0BuildGlyph");
+ if (code < 0)
+ return code;
+ code = build_gs_font(i_ctx_p, op, (gs_font **) & pfont,
+ ft_composite, &st_gs_font_type0, &build,
+ bf_options_none);
+ }
+ if (code != 0)
+ return code;
+ /* Fill in the rest of the basic font data. */
+ pfont->procs.init_fstack = gs_type0_init_fstack;
+ pfont->procs.define_font = ztype0_define_font;
+ pfont->procs.make_font = ztype0_make_font;
+ pfont->procs.next_char_glyph = gs_type0_next_char_glyph;
+ pfont->procs.decode_glyph = gs_font_map_glyph_to_unicode; /* PDF needs. */
+ if (dict_find_string(op, "PrefEnc", &pprefenc) <= 0) {
+ ref nul;
+
+ make_null_new(&nul);
+ if ((code = idict_put_string(op, "PrefEnc", &nul)) < 0)
+ goto fail;
+ }
+ get_GlyphNames2Unicode(i_ctx_p, (gs_font *)pfont, op);
+ /* Fill in the font data */
+ pdata = pfont_data(pfont);
+ data.encoding_size = r_size(&pdata->Encoding);
+ /*
+ * Adobe interpreters apparently require that Encoding.size >= subs_size
+ * +1 (not sure whether the +1 only applies if the sum of the range
+ * sizes is less than the size of the code space). The gs library
+ * doesn't require this -- it only gives an error if a show operation
+ * actually would reference beyond the end of the Encoding -- so we
+ * check this here rather than in the library.
+ */
+ if (data.FMapType == fmap_SubsVector) {
+ if (data.subs_size >= r_size(&pdata->Encoding)) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto fail;
+ }
+ }
+ data.Encoding =
+ (uint *) ialloc_byte_array(data.encoding_size, sizeof(uint),
+ "buildfont0(Encoding)");
+ if (data.Encoding == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ /* Fill in the encoding vector, checking to make sure that */
+ /* each element is an integer between 0 and fdep_size-1. */
+ for (i = 0; i < data.encoding_size; i++) {
+ ref enc;
+
+ array_get(imemory, &pdata->Encoding, i, &enc);
+ if (!r_has_type(&enc, t_integer)) {
+ code = gs_note_error(gs_error_typecheck);
+ goto fail;
+ }
+ if ((ulong) enc.value.intval >= data.fdep_size) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto fail;
+ }
+ data.Encoding[i] = (uint) enc.value.intval;
+ }
+ data.FDepVector =
+ ialloc_struct_array(data.fdep_size, gs_font *,
+ &st_gs_font_ptr_element,
+ "buildfont0(FDepVector)");
+ if (data.FDepVector == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ for (i = 0; i < data.fdep_size; i++) {
+ ref fdep;
+ ref *pfid;
+
+ array_get(pfont->memory, &fdepvector, i, &fdep);
+ /* The lookup can't fail, because of the pre-check above. */
+ dict_find_string(&fdep, "FID", &pfid);
+ data.FDepVector[i] = r_ptr(pfid, gs_font);
+ }
+ pfont->data = data;
+ code = define_gs_font(i_ctx_p, (gs_font *) pfont);
+ if (code >= 0)
+ return code;
+fail:
+ /*
+ * Undo the insertion of the FID entry in the dictionary. Note that
+ * some allocations (Encoding, FDepVector) are not undone.
+ */
+ if (r_has_type(&save_FID, t_null)) {
+ ref rnfid;
+
+ name_enter_string(pfont->memory, "FID", &rnfid);
+ idict_undef(op, &rnfid);
+ } else
+ idict_put_string(op, "FID", &save_FID);
+ gs_free_object(pfont->memory, pfont, "buildfont0(font)");
+ return code;
+}
+/* If a newly defined or scaled composite font had to scale */
+/* any composite sub-fonts, adjust the parent font's FDepVector. */
+/* This is called only if gs_type0_define/make_font */
+/* actually changed the FDepVector. */
+static int
+ztype0_adjust_FDepVector(gs_font_type0 * pfont)
+{
+ gs_memory_t *mem = pfont->memory;
+ /* HACK: We know the font was allocated by the interpreter. */
+ gs_ref_memory_t *imem = (gs_ref_memory_t *)mem;
+ gs_font **pdep = pfont->data.FDepVector;
+ ref newdep;
+ uint fdep_size = pfont->data.fdep_size;
+ ref *prdep;
+ uint i;
+ int code = gs_alloc_ref_array(imem, &newdep, a_readonly, fdep_size,
+ "ztype0_adjust_matrix");
+
+ if (code < 0)
+ return code;
+ for (prdep = newdep.value.refs, i = 0; i < fdep_size; i++, prdep++) {
+ const ref *pdict = pfont_dict(pdep[i]);
+
+ ref_assign(prdep, pdict);
+ r_set_attrs(prdep, imemory_new_mask(imem));
+ }
+ /*
+ * The FDepVector is an existing key in the parent's dictionary,
+ * so it's safe to pass NULL as the dstack pointer to dict_put_string.
+ */
+ return dict_put_string(pfont_dict(pfont), "FDepVector", &newdep, NULL);
+}
+static int
+ztype0_define_font(gs_font_dir * pdir, gs_font * pfont)
+{
+ gs_font_type0 *const pfont0 = (gs_font_type0 *)pfont;
+ gs_font **pdep = pfont0->data.FDepVector;
+ int code = gs_type0_define_font(pdir, pfont);
+
+ if (code < 0 || pfont0->data.FDepVector == pdep)
+ return code;
+ return ztype0_adjust_FDepVector(pfont0);
+}
+static int
+ztype0_make_font(gs_font_dir * pdir, const gs_font * pfont,
+ const gs_matrix * pmat, gs_font ** ppfont)
+{
+ gs_font_type0 **const ppfont0 = (gs_font_type0 **)ppfont;
+ gs_font **pdep = (*ppfont0)->data.FDepVector;
+ int code;
+
+ code = zdefault_make_font(pdir, pfont, pmat, ppfont);
+ if (code < 0)
+ return code;
+ code = gs_type0_make_font(pdir, pfont, pmat, ppfont);
+ if (code < 0)
+ return code;
+ if ((*ppfont0)->data.FDepVector == pdep)
+ return 0;
+ return ztype0_adjust_FDepVector(*ppfont0);
+}
+
+/* ------ Internal routines ------ */
+
+/* Find or add a character entry in a font dictionary. */
+static int
+ensure_char_entry(i_ctx_t *i_ctx_p, os_ptr op, const char *kstr,
+ byte * pvalue, int default_value)
+{
+ ref *pentry;
+
+ if (dict_find_string(op, kstr, &pentry) <= 0) {
+ ref ent;
+
+ make_int(&ent, default_value);
+ *pvalue = (byte) default_value;
+ return idict_put_string(op, kstr, &ent);
+ } else {
+ check_int_leu_only(*pentry, 255);
+ *pvalue = (byte) pentry->value.intval;
+ return 0;
+ }
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfont0_op_defs[] =
+{
+ {"2.buildfont0", zbuildfont0},
+ op_def_end(0)
+};
diff --git a/psi/zfont1.c b/psi/zfont1.c
new file mode 100644
index 000000000..bb12b161f
--- /dev/null
+++ b/psi/zfont1.c
@@ -0,0 +1,353 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Type 1 and Type 4 font creation operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gxfixed.h"
+#include "gsmatrix.h"
+#include "gxdevice.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "bfont.h"
+#include "ialloc.h"
+#include "icharout.h"
+#include "ichar1.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifont1.h"
+#include "iname.h" /* for name_index in enumerate_glyph */
+#include "store.h"
+
+/* Type 1 font procedures (defined in zchar1.c) */
+extern const gs_type1_data_procs_t z1_data_procs;
+font_proc_glyph_info(z1_glyph_info);
+/* Font procedures defined here */
+static font_proc_same_font(z1_same_font);
+
+/* ------ Private utilities ------ */
+
+static void
+find_zone_height(float *pmax_height, int count, const float *values)
+{
+ int i;
+ float zone_height;
+
+ for (i = 0; i < count; i += 2)
+ if ((zone_height = values[i + 1] - values[i]) > *pmax_height)
+ *pmax_height = zone_height;
+}
+
+/* ------ Font procedures ------ */
+
+static int
+z1_enumerate_glyph(gs_font * pfont, int *pindex, gs_glyph_space_t ignored,
+ gs_glyph * pglyph)
+{
+ const gs_font_type1 *const pfont1 = (gs_font_type1 *)pfont;
+ const ref *pcsdict = &pfont_data(pfont1)->CharStrings;
+
+ return zchar_enumerate_glyph(pfont->memory, pcsdict, pindex, pglyph);
+}
+
+/* ------ Public procedures ------ */
+
+/* Extract pointers to internal structures. */
+int
+charstring_font_get_refs(const_os_ptr op, charstring_font_refs_t *pfr)
+{
+ check_type(*op, t_dictionary);
+ if (dict_find_string(op, "Private", &pfr->Private) <= 0 ||
+ !r_has_type(pfr->Private, t_dictionary)
+ )
+ return_error(gs_error_invalidfont);
+ make_empty_array(&pfr->no_subrs, 0);
+ if (dict_find_string(pfr->Private, "OtherSubrs", &pfr->OtherSubrs) > 0) {
+ if (!r_is_array(pfr->OtherSubrs))
+ return_error(gs_error_typecheck);
+ } else
+ pfr->OtherSubrs = &pfr->no_subrs;
+ if (dict_find_string(pfr->Private, "Subrs", &pfr->Subrs) > 0) {
+ if (!r_is_array(pfr->Subrs))
+ return_error(gs_error_typecheck);
+ } else
+ pfr->Subrs = &pfr->no_subrs;
+ pfr->GlobalSubrs = &pfr->no_subrs;
+ return 0;
+}
+
+/* Get the parameters of a CharString-based font or a FDArray entry. */
+int
+charstring_font_params(const gs_memory_t *mem,
+ const_os_ptr op, charstring_font_refs_t *pfr,
+ gs_type1_data *pdata1)
+{
+ const ref *pprivate = pfr->Private;
+ int code;
+
+ /* Get the rest of the information from the Private dictionary. */
+ if ((code = dict_int_param(pprivate, "lenIV", -1, 255, pdata1->lenIV, &pdata1->lenIV)) < 0)
+ return code;
+ if ((code = dict_uint_param(pprivate, "subroutineNumberBias",
+ 0, max_uint, pdata1->subroutineNumberBias, &pdata1->subroutineNumberBias)) < 0)
+ return code;
+ if ((code = dict_int_param(pprivate, "BlueFuzz", 0, 1999, 1, &pdata1->BlueFuzz)) < 0)
+ return code;
+ if ((code = dict_float_param(pprivate, "BlueScale", 0.039625, &pdata1->BlueScale)) < 0)
+ return code;
+ if ((code = dict_float_param(pprivate, "BlueShift", 7.0, &pdata1->BlueShift)) < 0)
+ return code;
+ if ((code = pdata1->BlueValues.count = dict_float_array_param(mem, pprivate, "BlueValues",
+ max_BlueValues * 2, &pdata1->BlueValues.values[0], NULL)) < 0)
+ return code;
+ if ((code = dict_float_param(pprivate, "ExpansionFactor", 0.06, &pdata1->ExpansionFactor)) < 0)
+ return code;
+ if ((code = pdata1->FamilyBlues.count = dict_float_array_param(mem, pprivate, "FamilyBlues",
+ max_FamilyBlues * 2, &pdata1->FamilyBlues.values[0], NULL)) < 0)
+ return code;
+ if ((code = pdata1->FamilyOtherBlues.count = dict_float_array_param(mem, pprivate, "FamilyOtherBlues",
+ max_FamilyOtherBlues * 2, &pdata1->FamilyOtherBlues.values[0], NULL)) < 0)
+ return code;
+ if ((code = dict_bool_param(pprivate, "ForceBold", false, &pdata1->ForceBold)) < 0)
+ return code;
+ /*
+ * We've seen a few fonts with out-of-range LanguageGroup values;
+ * if it weren't for this, the only legal values should be 0 or 1.
+ */
+ if ((code = dict_int_param(pprivate, "LanguageGroup", min_int, max_int, 0, &pdata1->LanguageGroup)) < 0)
+ return code;
+ if ((code = pdata1->OtherBlues.count = dict_float_array_param(mem, pprivate, "OtherBlues",
+ max_OtherBlues * 2, &pdata1->OtherBlues.values[0], NULL)) < 0)
+ return code;
+ if ((code = dict_bool_param(pprivate, "RndStemUp", true, &pdata1->RndStemUp)) < 0)
+ return code;
+ if ((code = pdata1->StdHW.count = dict_float_array_check_param(mem, pprivate, "StdHW",
+ 1, &pdata1->StdHW.values[0], NULL, 0, gs_error_rangecheck)) < 0)
+ return code;
+ if ((code = pdata1->StdVW.count = dict_float_array_check_param(mem, pprivate, "StdVW",
+ 1, &pdata1->StdVW.values[0], NULL, 0, gs_error_rangecheck)) < 0)
+ return code;
+ if ((code = pdata1->StemSnapH.count = dict_float_array_param(mem, pprivate, "StemSnapH",
+ max_StemSnap, &pdata1->StemSnapH.values[0], NULL)) < 0)
+ return code;
+ if ((code = pdata1->StemSnapV.count = dict_float_array_param(mem, pprivate, "StemSnapV",
+ max_StemSnap, &pdata1->StemSnapV.values[0], NULL)) < 0)
+ return code;
+ /* The WeightVector is in the font dictionary, not Private. */
+ if ((code = pdata1->WeightVector.count = dict_float_array_param(mem, op, "WeightVector",
+ max_WeightVector, pdata1->WeightVector.values, NULL)) < 0)
+ return code;
+ /*
+ * According to section 5.6 of the "Adobe Type 1 Font Format",
+ * there is a requirement that BlueScale times the maximum
+ * alignment zone height must be less than 1. Some fonts
+ * produced by Fontographer have ridiculously large BlueScale
+ * values, so we force BlueScale back into range here.
+ */
+ {
+ float max_zone_height = 1.0;
+
+ find_zone_height(&max_zone_height, pdata1->BlueValues.count, pdata1->BlueValues.values);
+ find_zone_height(&max_zone_height, pdata1->OtherBlues.count, pdata1->OtherBlues.values);
+ find_zone_height(&max_zone_height, pdata1->FamilyBlues.count, pdata1->FamilyBlues.values);
+ find_zone_height(&max_zone_height, pdata1->FamilyOtherBlues.count, pdata1->FamilyOtherBlues.values);
+ if (pdata1->BlueScale * max_zone_height > 1.0)
+ pdata1->BlueScale = 1.0 / max_zone_height;
+ }
+ /*
+ * According to the same Adobe book, section 5.11, only values
+ * 0 and 1 are allowed for LanguageGroup and we have encountered
+ * fonts with other values. If the value is anything else, map it to 0
+ * so that the remainder of the graphics library won't see an
+ * unexpected value.
+ */
+ if (pdata1->LanguageGroup > 1 || pdata1->LanguageGroup < 0)
+ pdata1->LanguageGroup = 0;
+ /* This is used only when determining if its possible to copy glyphs
+ * between fonts. Currenly only by pdfwrite and friends. Rather than
+ * check all the subrs (which we used to do) we hash tehm, store it
+ * and check the hashes. Zero except when in use by pdfwrite..
+ */
+ memset(&pdata1->hash_subrs, 0x00, 16);
+ return 0;
+}
+
+/* Fill in a newly built CharString-based font or FDArray entry. */
+int
+charstring_font_init(gs_font_type1 *pfont, const charstring_font_refs_t *pfr,
+ const gs_type1_data *pdata1)
+{
+ font_data *pdata;
+
+ pdata = pfont_data(pfont);
+ pfont->data = *pdata1;
+ pfont->data.parent = NULL;
+ ref_assign(&pdata->u.type1.OtherSubrs, pfr->OtherSubrs);
+ ref_assign(&pdata->u.type1.Subrs, pfr->Subrs);
+ ref_assign(&pdata->u.type1.GlobalSubrs, pfr->GlobalSubrs);
+ pfont->data.procs = z1_data_procs;
+ pfont->data.proc_data = (char *)pdata;
+ pfont->procs.same_font = z1_same_font;
+ pfont->procs.glyph_info = z1_glyph_info;
+ pfont->procs.enumerate_glyph = z1_enumerate_glyph;
+ pfont->procs.glyph_outline = zchar1_glyph_outline;
+ return 0;
+}
+
+/* Build a Type 1, Type 2, or Type 4 font. */
+int
+build_charstring_font(i_ctx_t *i_ctx_p, os_ptr op, build_proc_refs *pbuild,
+ font_type ftype, charstring_font_refs_t *pfr,
+ gs_type1_data *pdata1, build_font_options_t options)
+{
+ int code = charstring_font_params(imemory, op, pfr, pdata1);
+ gs_font_type1 *pfont;
+
+ if (code < 0)
+ return code;
+ code = build_gs_primitive_font(i_ctx_p, op, (gs_font_base **)&pfont, ftype,
+ &st_gs_font_type1, pbuild, options);
+ if (code != 0)
+ return code;
+ /* This is a new font, fill it in. */
+ charstring_font_init(pfont, pfr, pdata1);
+ return define_gs_font(i_ctx_p, (gs_font *)pfont);
+}
+
+/* ------ Operators ------ */
+
+/* Build a Type 1 or Type 4 font. */
+static int
+buildfont1or4(i_ctx_t *i_ctx_p, os_ptr op, build_proc_refs * pbuild,
+ font_type ftype, build_font_options_t options)
+{
+ charstring_font_refs_t refs;
+ int code = charstring_font_get_refs(op, &refs);
+ gs_type1_data data1;
+
+ if (code < 0)
+ return code;
+ data1.interpret = gs_type1_interpret;
+ data1.subroutineNumberBias = 0;
+ data1.lenIV = DEFAULT_LENIV_1;
+ return build_charstring_font(i_ctx_p, op, pbuild, ftype, &refs, &data1,
+ options);
+}
+
+/* <string|name> <font_dict> .buildfont1 <string|name> <font> */
+/* Build a type 1 (Adobe encrypted) font. */
+static int
+zbuildfont1(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ build_proc_refs build;
+ int code = build_proc_name_refs(imemory, &build,
+ "%Type1BuildChar", "%Type1BuildGlyph");
+
+ if (code < 0)
+ return code;
+ return buildfont1or4(i_ctx_p, op, &build, ft_encrypted,
+ bf_notdef_required);
+}
+
+/* <string|name> <font_dict> .buildfont4 <string|name> <font> */
+/* Build a type 4 (disk-based Adobe encrypted) font. */
+static int
+zbuildfont4(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ build_proc_refs build;
+ int code = build_gs_font_procs(op, &build);
+
+ if (code < 0)
+ return code;
+ return buildfont1or4(i_ctx_p, op, &build, ft_disk_based, bf_options_none);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfont1_op_defs[] =
+{
+ {"2.buildfont1", zbuildfont1},
+ {"2.buildfont4", zbuildfont4},
+ op_def_end(0)
+};
+
+/* ------ Font procedures for Type 1 fonts ------ */
+
+/* same_font procedure */
+static bool
+same_font_dict(const font_data *pdata, const font_data *podata,
+ const char *key)
+{
+ ref *pvalue;
+ bool present = dict_find_string(&pdata->dict, key, &pvalue) > 0;
+ ref *povalue;
+ bool opresent = dict_find_string(&podata->dict, key, &povalue) > 0;
+ dict *pdict = (&(podata->dict))->value.pdict;
+
+ return (present == opresent &&
+ (present <= 0 || obj_eq(dict_mem(pdict), pvalue, povalue)));
+}
+static int
+z1_same_font(const gs_font *font, const gs_font *ofont, int mask)
+{
+ if (ofont->FontType != font->FontType)
+ return 0;
+ while (font->base != font)
+ font = font->base;
+ while (ofont->base != ofont)
+ ofont = ofont->base;
+ if (ofont == font)
+ return mask;
+ {
+ int same = gs_base_same_font(font, ofont, mask);
+ int check = mask & ~same;
+ const gs_font_type1 *const pfont1 = (const gs_font_type1 *)font;
+ const font_data *const pdata = pfont_data(pfont1);
+ const gs_font_type1 *pofont1 = (const gs_font_type1 *)ofont;
+ const font_data *const podata = pfont_data(pofont1);
+
+ if ((check & (FONT_SAME_OUTLINES | FONT_SAME_METRICS)) &&
+ !memcmp(&pofont1->data.procs, &z1_data_procs, sizeof(z1_data_procs)) &&
+ obj_eq(font->memory, &pdata->CharStrings, &podata->CharStrings) &&
+ /*
+ * We use same_font_dict for convenience: we know that
+ * both fonts do have Private dictionaries.
+ */
+ same_font_dict(pdata, podata, "Private")
+ )
+ same |= FONT_SAME_OUTLINES;
+
+ if ((check & FONT_SAME_METRICS) && (same & FONT_SAME_OUTLINES) &&
+ !memcmp(&pofont1->data.procs, &z1_data_procs, sizeof(z1_data_procs)) &&
+ /* Metrics may be affected by CDevProc, Metrics, Metrics2. */
+ same_font_dict(pdata, podata, "Metrics") &&
+ same_font_dict(pdata, podata, "Metrics2") &&
+ same_font_dict(pdata, podata, "CDevProc")
+ )
+ same |= FONT_SAME_METRICS;
+
+ if ((check & FONT_SAME_ENCODING) &&
+ pofont1->procs.same_font == z1_same_font &&
+ obj_eq(font->memory, &pdata->Encoding, &podata->Encoding)
+ )
+ same |= FONT_SAME_ENCODING;
+
+ return same & mask;
+ }
+}
diff --git a/psi/zfont2.c b/psi/zfont2.c
new file mode 100644
index 000000000..7be41a82a
--- /dev/null
+++ b/psi/zfont2.c
@@ -0,0 +1,2841 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Type 2 font creation operators */
+#include "ghost.h"
+#include "string_.h" /* for CFF parser */
+#include "oper.h"
+#include "gxfixed.h"
+#include "gsmatrix.h"
+#include "gxfont.h"
+#include "gxfont1.h"
+#include "bfont.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifont1.h"
+#include "ifont2.h"
+#include "ialloc.h"
+#include "iname.h" /* for CFF parser */
+#include "iddict.h" /* for CFF parser */
+#include "store.h" /* for CFF parser */
+#include "iscannum.h"
+
+/* Private utilities */
+static uint
+subr_bias(const ref * psubrs)
+{
+ uint size = r_size(psubrs);
+
+ return (size < 1240 ? 107 : size < 33900 ? 1131 : 32768);
+}
+
+/*
+ * Get the additional parameters for a Type 2 font (or FontType 2 FDArray
+ * entry in a CIDFontType 0 font), beyond those common to Type 1 and Type 2
+ * fonts.
+ */
+int
+type2_font_params(const_os_ptr op, charstring_font_refs_t *pfr,
+ gs_type1_data *pdata1)
+{
+ int code;
+ float dwx, nwx;
+ ref *temp;
+
+ pdata1->interpret = gs_type2_interpret;
+ pdata1->lenIV = DEFAULT_LENIV_2;
+ pdata1->subroutineNumberBias = subr_bias(pfr->Subrs);
+ /* Get information specific to Type 2 fonts. */
+ if (dict_find_string(pfr->Private, "GlobalSubrs", &temp) > 0) {
+ if (!r_is_array(temp))
+ return_error(gs_error_typecheck);
+ pfr->GlobalSubrs = temp;
+ }
+ pdata1->gsubrNumberBias = subr_bias(pfr->GlobalSubrs);
+ if ((code = dict_uint_param(pfr->Private, "gsubrNumberBias",
+ 0, max_uint, pdata1->gsubrNumberBias,
+ &pdata1->gsubrNumberBias)) < 0 ||
+ (code = dict_float_param(pfr->Private, "defaultWidthX", 0.0,
+ &dwx)) < 0 ||
+ (code = dict_float_param(pfr->Private, "nominalWidthX", 0.0,
+ &nwx)) < 0
+ )
+ return code;
+ pdata1->defaultWidthX = float2fixed(dwx);
+ pdata1->nominalWidthX = float2fixed(nwx);
+ {
+ ref *pirs;
+
+ if (dict_find_string(pfr->Private, "initialRandomSeed", &pirs) <= 0)
+ pdata1->initialRandomSeed = 0;
+ else if (!r_has_type(pirs, t_integer))
+ return_error(gs_error_typecheck);
+ else
+ pdata1->initialRandomSeed = pirs->value.intval;
+ }
+ return 0;
+}
+
+/* <string|name> <font_dict> .buildfont2 <string|name> <font> */
+/* Build a type 2 (compact Adobe encrypted) font. */
+static int
+zbuildfont2(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ charstring_font_refs_t refs;
+ build_proc_refs build;
+ int code = build_proc_name_refs(imemory, &build,
+ "%Type2BuildChar", "%Type2BuildGlyph");
+ gs_type1_data data1;
+
+ if (code < 0)
+ return code;
+ code = charstring_font_get_refs(op, &refs);
+ if (code < 0)
+ return code;
+ code = type2_font_params(op, &refs, &data1);
+ if (code < 0)
+ return code;
+ return build_charstring_font(i_ctx_p, op, &build, ft_encrypted2, &refs,
+ &data1, bf_notdef_required);
+}
+
+/* CFF parser */
+
+#define STR2MEM(s) s, (sizeof(s) - 1)
+#define MAXOP 50 /* Max value according to TN 5176 is 48 */
+
+static const char * const standard_strings[] = {
+ /* 0 */ ".notdef",
+ /* 1 */ "space",
+ /* 2 */ "exclam",
+ /* 3 */ "quotedbl",
+ /* 4 */ "numbersign",
+ /* 5 */ "dollar",
+ /* 6 */ "percent",
+ /* 7 */ "ampersand",
+ /* 8 */ "quoteright",
+ /* 9 */ "parenleft",
+ /* 10 */ "parenright",
+ /* 11 */ "asterisk",
+ /* 12 */ "plus",
+ /* 13 */ "comma",
+ /* 14 */ "hyphen",
+ /* 15 */ "period",
+ /* 16 */ "slash",
+ /* 17 */ "zero",
+ /* 18 */ "one",
+ /* 19 */ "two",
+ /* 20 */ "three",
+ /* 21 */ "four",
+ /* 22 */ "five",
+ /* 23 */ "six",
+ /* 24 */ "seven",
+ /* 25 */ "eight",
+ /* 26 */ "nine",
+ /* 27 */ "colon",
+ /* 28 */ "semicolon",
+ /* 29 */ "less",
+ /* 30 */ "equal",
+ /* 31 */ "greater",
+ /* 32 */ "question",
+ /* 33 */ "at",
+ /* 34 */ "A",
+ /* 35 */ "B",
+ /* 36 */ "C",
+ /* 37 */ "D",
+ /* 38 */ "E",
+ /* 39 */ "F",
+ /* 40 */ "G",
+ /* 41 */ "H",
+ /* 42 */ "I",
+ /* 43 */ "J",
+ /* 44 */ "K",
+ /* 45 */ "L",
+ /* 46 */ "M",
+ /* 47 */ "N",
+ /* 48 */ "O",
+ /* 49 */ "P",
+ /* 50 */ "Q",
+ /* 51 */ "R",
+ /* 52 */ "S",
+ /* 53 */ "T",
+ /* 54 */ "U",
+ /* 55 */ "V",
+ /* 56 */ "W",
+ /* 57 */ "X",
+ /* 58 */ "Y",
+ /* 59 */ "Z",
+ /* 60 */ "bracketleft",
+ /* 61 */ "backslash",
+ /* 62 */ "bracketright",
+ /* 63 */ "asciicircum",
+ /* 64 */ "underscore",
+ /* 65 */ "quoteleft",
+ /* 66 */ "a",
+ /* 67 */ "b",
+ /* 68 */ "c",
+ /* 69 */ "d",
+ /* 70 */ "e",
+ /* 71 */ "f",
+ /* 72 */ "g",
+ /* 73 */ "h",
+ /* 74 */ "i",
+ /* 75 */ "j",
+ /* 76 */ "k",
+ /* 77 */ "l",
+ /* 78 */ "m",
+ /* 79 */ "n",
+ /* 80 */ "o",
+ /* 81 */ "p",
+ /* 82 */ "q",
+ /* 83 */ "r",
+ /* 84 */ "s",
+ /* 85 */ "t",
+ /* 86 */ "u",
+ /* 87 */ "v",
+ /* 88 */ "w",
+ /* 89 */ "x",
+ /* 90 */ "y",
+ /* 91 */ "z",
+ /* 92 */ "braceleft",
+ /* 93 */ "bar",
+ /* 94 */ "braceright",
+ /* 95 */ "asciitilde",
+ /* 96 */ "exclamdown",
+ /* 97 */ "cent",
+ /* 98 */ "sterling",
+ /* 99 */ "fraction",
+ /* 100 */ "yen",
+ /* 101 */ "florin",
+ /* 102 */ "section",
+ /* 103 */ "currency",
+ /* 104 */ "quotesingle",
+ /* 105 */ "quotedblleft",
+ /* 106 */ "guillemotleft",
+ /* 107 */ "guilsinglleft",
+ /* 108 */ "guilsinglright",
+ /* 109 */ "fi",
+ /* 110 */ "fl",
+ /* 111 */ "endash",
+ /* 112 */ "dagger",
+ /* 113 */ "daggerdbl",
+ /* 114 */ "periodcentered",
+ /* 115 */ "paragraph",
+ /* 116 */ "bullet",
+ /* 117 */ "quotesinglbase",
+ /* 118 */ "quotedblbase",
+ /* 119 */ "quotedblright",
+ /* 120 */ "guillemotright",
+ /* 121 */ "ellipsis",
+ /* 122 */ "perthousand",
+ /* 123 */ "questiondown",
+ /* 124 */ "grave",
+ /* 125 */ "acute",
+ /* 126 */ "circumflex",
+ /* 127 */ "tilde",
+ /* 128 */ "macron",
+ /* 129 */ "breve",
+ /* 130 */ "dotaccent",
+ /* 131 */ "dieresis",
+ /* 132 */ "ring",
+ /* 133 */ "cedilla",
+ /* 134 */ "hungarumlaut",
+ /* 135 */ "ogonek",
+ /* 136 */ "caron",
+ /* 137 */ "emdash",
+ /* 138 */ "AE",
+ /* 139 */ "ordfeminine",
+ /* 140 */ "Lslash",
+ /* 141 */ "Oslash",
+ /* 142 */ "OE",
+ /* 143 */ "ordmasculine",
+ /* 144 */ "ae",
+ /* 145 */ "dotlessi",
+ /* 146 */ "lslash",
+ /* 147 */ "oslash",
+ /* 148 */ "oe",
+ /* 149 */ "germandbls",
+ /* 150 */ "onesuperior",
+ /* 151 */ "logicalnot",
+ /* 152 */ "mu",
+ /* 153 */ "trademark",
+ /* 154 */ "Eth",
+ /* 155 */ "onehalf",
+ /* 156 */ "plusminus",
+ /* 157 */ "Thorn",
+ /* 158 */ "onequarter",
+ /* 159 */ "divide",
+ /* 160 */ "brokenbar",
+ /* 161 */ "degree",
+ /* 162 */ "thorn",
+ /* 163 */ "threequarters",
+ /* 164 */ "twosuperior",
+ /* 165 */ "registered",
+ /* 166 */ "minus",
+ /* 167 */ "eth",
+ /* 168 */ "multiply",
+ /* 169 */ "threesuperior",
+ /* 170 */ "copyright",
+ /* 171 */ "Aacute",
+ /* 172 */ "Acircumflex",
+ /* 173 */ "Adieresis",
+ /* 174 */ "Agrave",
+ /* 175 */ "Aring",
+ /* 176 */ "Atilde",
+ /* 177 */ "Ccedilla",
+ /* 178 */ "Eacute",
+ /* 179 */ "Ecircumflex",
+ /* 180 */ "Edieresis",
+ /* 181 */ "Egrave",
+ /* 182 */ "Iacute",
+ /* 183 */ "Icircumflex",
+ /* 184 */ "Idieresis",
+ /* 185 */ "Igrave",
+ /* 186 */ "Ntilde",
+ /* 187 */ "Oacute",
+ /* 188 */ "Ocircumflex",
+ /* 189 */ "Odieresis",
+ /* 190 */ "Ograve",
+ /* 191 */ "Otilde",
+ /* 192 */ "Scaron",
+ /* 193 */ "Uacute",
+ /* 194 */ "Ucircumflex",
+ /* 195 */ "Udieresis",
+ /* 196 */ "Ugrave",
+ /* 197 */ "Yacute",
+ /* 198 */ "Ydieresis",
+ /* 199 */ "Zcaron",
+ /* 200 */ "aacute",
+ /* 201 */ "acircumflex",
+ /* 202 */ "adieresis",
+ /* 203 */ "agrave",
+ /* 204 */ "aring",
+ /* 205 */ "atilde",
+ /* 206 */ "ccedilla",
+ /* 207 */ "eacute",
+ /* 208 */ "ecircumflex",
+ /* 209 */ "edieresis",
+ /* 210 */ "egrave",
+ /* 211 */ "iacute",
+ /* 212 */ "icircumflex",
+ /* 213 */ "idieresis",
+ /* 214 */ "igrave",
+ /* 215 */ "ntilde",
+ /* 216 */ "oacute",
+ /* 217 */ "ocircumflex",
+ /* 218 */ "odieresis",
+ /* 219 */ "ograve",
+ /* 220 */ "otilde",
+ /* 221 */ "scaron",
+ /* 222 */ "uacute",
+ /* 223 */ "ucircumflex",
+ /* 224 */ "udieresis",
+ /* 225 */ "ugrave",
+ /* 226 */ "yacute",
+ /* 227 */ "ydieresis",
+ /* 228 */ "zcaron",
+ /* 229 */ "exclamsmall",
+ /* 230 */ "Hungarumlautsmall",
+ /* 231 */ "dollaroldstyle",
+ /* 232 */ "dollarsuperior",
+ /* 233 */ "ampersandsmall",
+ /* 234 */ "Acutesmall",
+ /* 235 */ "parenleftsuperior",
+ /* 236 */ "parenrightsuperior",
+ /* 237 */ "twodotenleader",
+ /* 238 */ "onedotenleader",
+ /* 239 */ "zerooldstyle",
+ /* 240 */ "oneoldstyle",
+ /* 241 */ "twooldstyle",
+ /* 242 */ "threeoldstyle",
+ /* 243 */ "fouroldstyle",
+ /* 244 */ "fiveoldstyle",
+ /* 245 */ "sixoldstyle",
+ /* 246 */ "sevenoldstyle",
+ /* 247 */ "eightoldstyle",
+ /* 248 */ "nineoldstyle",
+ /* 249 */ "commasuperior",
+ /* 250 */ "threequartersemdash",
+ /* 251 */ "periodsuperior",
+ /* 252 */ "questionsmall",
+ /* 253 */ "asuperior",
+ /* 254 */ "bsuperior",
+ /* 255 */ "centsuperior",
+ /* 256 */ "dsuperior",
+ /* 257 */ "esuperior",
+ /* 258 */ "isuperior",
+ /* 259 */ "lsuperior",
+ /* 260 */ "msuperior",
+ /* 261 */ "nsuperior",
+ /* 262 */ "osuperior",
+ /* 263 */ "rsuperior",
+ /* 264 */ "ssuperior",
+ /* 265 */ "tsuperior",
+ /* 266 */ "ff",
+ /* 267 */ "ffi",
+ /* 268 */ "ffl",
+ /* 269 */ "parenleftinferior",
+ /* 270 */ "parenrightinferior",
+ /* 271 */ "Circumflexsmall",
+ /* 272 */ "hyphensuperior",
+ /* 273 */ "Gravesmall",
+ /* 274 */ "Asmall",
+ /* 275 */ "Bsmall",
+ /* 276 */ "Csmall",
+ /* 277 */ "Dsmall",
+ /* 278 */ "Esmall",
+ /* 279 */ "Fsmall",
+ /* 280 */ "Gsmall",
+ /* 281 */ "Hsmall",
+ /* 282 */ "Ismall",
+ /* 283 */ "Jsmall",
+ /* 284 */ "Ksmall",
+ /* 285 */ "Lsmall",
+ /* 286 */ "Msmall",
+ /* 287 */ "Nsmall",
+ /* 288 */ "Osmall",
+ /* 289 */ "Psmall",
+ /* 290 */ "Qsmall",
+ /* 291 */ "Rsmall",
+ /* 292 */ "Ssmall",
+ /* 293 */ "Tsmall",
+ /* 294 */ "Usmall",
+ /* 295 */ "Vsmall",
+ /* 296 */ "Wsmall",
+ /* 297 */ "Xsmall",
+ /* 298 */ "Ysmall",
+ /* 299 */ "Zsmall",
+ /* 300 */ "colonmonetary",
+ /* 301 */ "onefitted",
+ /* 302 */ "rupiah",
+ /* 303 */ "Tildesmall",
+ /* 304 */ "exclamdownsmall",
+ /* 305 */ "centoldstyle",
+ /* 306 */ "Lslashsmall",
+ /* 307 */ "Scaronsmall",
+ /* 308 */ "Zcaronsmall",
+ /* 309 */ "Dieresissmall",
+ /* 310 */ "Brevesmall",
+ /* 311 */ "Caronsmall",
+ /* 312 */ "Dotaccentsmall",
+ /* 313 */ "Macronsmall",
+ /* 314 */ "figuredash",
+ /* 315 */ "hypheninferior",
+ /* 316 */ "Ogoneksmall",
+ /* 317 */ "Ringsmall",
+ /* 318 */ "Cedillasmall",
+ /* 319 */ "questiondownsmall",
+ /* 320 */ "oneeighth",
+ /* 321 */ "threeeighths",
+ /* 322 */ "fiveeighths",
+ /* 323 */ "seveneighths",
+ /* 324 */ "onethird",
+ /* 325 */ "twothirds",
+ /* 326 */ "zerosuperior",
+ /* 327 */ "foursuperior",
+ /* 328 */ "fivesuperior",
+ /* 329 */ "sixsuperior",
+ /* 330 */ "sevensuperior",
+ /* 331 */ "eightsuperior",
+ /* 332 */ "ninesuperior",
+ /* 333 */ "zeroinferior",
+ /* 334 */ "oneinferior",
+ /* 335 */ "twoinferior",
+ /* 336 */ "threeinferior",
+ /* 337 */ "fourinferior",
+ /* 338 */ "fiveinferior",
+ /* 339 */ "sixinferior",
+ /* 340 */ "seveninferior",
+ /* 341 */ "eightinferior",
+ /* 342 */ "nineinferior",
+ /* 343 */ "centinferior",
+ /* 344 */ "dollarinferior",
+ /* 345 */ "periodinferior",
+ /* 346 */ "commainferior",
+ /* 347 */ "Agravesmall",
+ /* 348 */ "Aacutesmall",
+ /* 349 */ "Acircumflexsmall",
+ /* 350 */ "Atildesmall",
+ /* 351 */ "Adieresissmall",
+ /* 352 */ "Aringsmall",
+ /* 353 */ "AEsmall",
+ /* 354 */ "Ccedillasmall",
+ /* 355 */ "Egravesmall",
+ /* 356 */ "Eacutesmall",
+ /* 357 */ "Ecircumflexsmall",
+ /* 358 */ "Edieresissmall",
+ /* 359 */ "Igravesmall",
+ /* 360 */ "Iacutesmall",
+ /* 361 */ "Icircumflexsmall",
+ /* 362 */ "Idieresissmall",
+ /* 363 */ "Ethsmall",
+ /* 364 */ "Ntildesmall",
+ /* 365 */ "Ogravesmall",
+ /* 366 */ "Oacutesmall",
+ /* 367 */ "Ocircumflexsmall",
+ /* 368 */ "Otildesmall",
+ /* 369 */ "Odieresissmall",
+ /* 370 */ "OEsmall",
+ /* 371 */ "Oslashsmall",
+ /* 372 */ "Ugravesmall",
+ /* 373 */ "Uacutesmall",
+ /* 374 */ "Ucircumflexsmall",
+ /* 375 */ "Udieresissmall",
+ /* 376 */ "Yacutesmall",
+ /* 377 */ "Thornsmall",
+ /* 378 */ "Ydieresissmall",
+ /* 379 */ "001.000",
+ /* 380 */ "001.001",
+ /* 381 */ "001.002",
+ /* 382 */ "001.003",
+ /* 383 */ "Black",
+ /* 384 */ "Bold",
+ /* 385 */ "Book",
+ /* 386 */ "Light",
+ /* 387 */ "Medium",
+ /* 388 */ "Regular",
+ /* 389 */ "Roman",
+ /* 390 */ "Semibold"
+};
+
+const static unsigned short expert_charset[] = {
+ 1, /* space */
+ 229, /* exclamsmall */
+ 230, /* Hungarumlautsmall */
+ 231, /* dollaroldstyle */
+ 232, /* dollarsuperior */
+ 233, /* ampersandsmall */
+ 234, /* Acutesmall */
+ 235, /* parenleftsuperior */
+ 236, /* parenrightsuperior */
+ 237, /* twodotenleader */
+ 238, /* onedotenleader */
+ 13, /* comma */
+ 14, /* hyphen */
+ 15, /* period */
+ 99, /* fraction */
+ 239, /* zerooldstyle */
+ 240, /* oneoldstyle */
+ 241, /* twooldstyle */
+ 242, /* threeoldstyle */
+ 243, /* fouroldstyle */
+ 244, /* fiveoldstyle */
+ 245, /* sixoldstyle */
+ 246, /* sevenoldstyle */
+ 247, /* eightoldstyle */
+ 248, /* nineoldstyle */
+ 27, /* colon */
+ 28, /* semicolon */
+ 249, /* commasuperior */
+ 250, /* threequartersemdash */
+ 251, /* periodsuperior */
+ 252, /* questionsmall */
+ 253, /* asuperior */
+ 254, /* bsuperior */
+ 255, /* centsuperior */
+ 256, /* dsuperior */
+ 257, /* esuperior */
+ 258, /* isuperior */
+ 259, /* lsuperior */
+ 260, /* msuperior */
+ 261, /* nsuperior */
+ 262, /* osuperior */
+ 263, /* rsuperior */
+ 264, /* ssuperior */
+ 265, /* tsuperior */
+ 266, /* ff */
+ 109, /* fi */
+ 110, /* fl */
+ 267, /* ffi */
+ 268, /* ffl */
+ 269, /* parenleftinferior */
+ 270, /* parenrightinferior */
+ 271, /* Circumflexsmall */
+ 272, /* hyphensuperior */
+ 273, /* Gravesmall */
+ 274, /* Asmall */
+ 275, /* Bsmall */
+ 276, /* Csmall */
+ 277, /* Dsmall */
+ 278, /* Esmall */
+ 279, /* Fsmall */
+ 280, /* Gsmall */
+ 281, /* Hsmall */
+ 282, /* Ismall */
+ 283, /* Jsmall */
+ 284, /* Ksmall */
+ 285, /* Lsmall */
+ 286, /* Msmall */
+ 287, /* Nsmall */
+ 288, /* Osmall */
+ 289, /* Psmall */
+ 290, /* Qsmall */
+ 291, /* Rsmall */
+ 292, /* Ssmall */
+ 293, /* Tsmall */
+ 294, /* Usmall */
+ 295, /* Vsmall */
+ 296, /* Wsmall */
+ 297, /* Xsmall */
+ 298, /* Ysmall */
+ 299, /* Zsmall */
+ 300, /* colonmonetary */
+ 301, /* onefitted */
+ 302, /* rupiah */
+ 303, /* Tildesmall */
+ 304, /* exclamdownsmall */
+ 305, /* centoldstyle */
+ 306, /* Lslashsmall */
+ 307, /* Scaronsmall */
+ 308, /* Zcaronsmall */
+ 309, /* Dieresissmall */
+ 310, /* Brevesmall */
+ 311, /* Caronsmall */
+ 312, /* Dotaccentsmall */
+ 313, /* Macronsmall */
+ 314, /* figuredash */
+ 315, /* hypheninferior */
+ 316, /* Ogoneksmall */
+ 317, /* Ringsmall */
+ 318, /* Cedillasmall */
+ 158, /* onequarter */
+ 155, /* onehalf */
+ 163, /* threequarters */
+ 319, /* questiondownsmall */
+ 320, /* oneeighth */
+ 321, /* threeeighths */
+ 322, /* fiveeighths */
+ 323, /* seveneighths */
+ 324, /* onethird */
+ 325, /* twothirds */
+ 326, /* zerosuperior */
+ 150, /* onesuperior */
+ 164, /* twosuperior */
+ 169, /* threesuperior */
+ 327, /* foursuperior */
+ 328, /* fivesuperior */
+ 329, /* sixsuperior */
+ 330, /* sevensuperior */
+ 331, /* eightsuperior */
+ 332, /* ninesuperior */
+ 333, /* zeroinferior */
+ 334, /* oneinferior */
+ 335, /* twoinferior */
+ 336, /* threeinferior */
+ 337, /* fourinferior */
+ 338, /* fiveinferior */
+ 339, /* sixinferior */
+ 340, /* seveninferior */
+ 341, /* eightinferior */
+ 342, /* nineinferior */
+ 343, /* centinferior */
+ 344, /* dollarinferior */
+ 345, /* periodinferior */
+ 346, /* commainferior */
+ 347, /* Agravesmall */
+ 348, /* Aacutesmall */
+ 349, /* Acircumflexsmall */
+ 350, /* Atildesmall */
+ 351, /* Adieresissmall */
+ 352, /* Aringsmall */
+ 353, /* AEsmall */
+ 354, /* Ccedillasmall */
+ 355, /* Egravesmall */
+ 356, /* Eacutesmall */
+ 357, /* Ecircumflexsmall */
+ 358, /* Edieresissmall */
+ 359, /* Igravesmall */
+ 360, /* Iacutesmall */
+ 361, /* Icircumflexsmall */
+ 362, /* Idieresissmall */
+ 363, /* Ethsmall */
+ 364, /* Ntildesmall */
+ 365, /* Ogravesmall */
+ 366, /* Oacutesmall */
+ 367, /* Ocircumflexsmall */
+ 368, /* Otildesmall */
+ 369, /* Odieresissmall */
+ 370, /* OEsmall */
+ 371, /* Oslashsmall */
+ 372, /* Ugravesmall */
+ 373, /* Uacutesmall */
+ 374, /* Ucircumflexsmall */
+ 375, /* Udieresissmall */
+ 376, /* Yacutesmall */
+ 377, /* Thornsmall */
+ 378 /* Ydieresissmall */
+};
+
+const static unsigned short expert_subset_charset[] = {
+ 1, /*space */
+ 231, /*dollaroldstyle */
+ 232, /*dollarsuperior */
+ 235, /*parenleftsuperior */
+ 236, /*parenrightsuperior */
+ 237, /*twodotenleader */
+ 238, /*onedotenleader */
+ 13, /*comma */
+ 14, /*hyphen */
+ 15, /*period */
+ 99, /*fraction */
+ 239, /*zerooldstyle */
+ 240, /*oneoldstyle */
+ 241, /*twooldstyle */
+ 242, /*threeoldstyle */
+ 243, /*fouroldstyle */
+ 244, /*fiveoldstyle */
+ 245, /*sixoldstyle */
+ 246, /*sevenoldstyle */
+ 247, /*eightoldstyle */
+ 248, /*nineoldstyle */
+ 27, /*colon */
+ 28, /*semicolon */
+ 249, /*commasuperior */
+ 250, /*threequartersemdash */
+ 251, /*periodsuperior */
+ 253, /*asuperior */
+ 254, /*bsuperior */
+ 255, /*centsuperior */
+ 256, /*dsuperior */
+ 257, /*esuperior */
+ 258, /*isuperior */
+ 259, /*lsuperior */
+ 260, /*msuperior */
+ 261, /*nsuperior */
+ 262, /*osuperior */
+ 263, /*rsuperior */
+ 264, /*ssuperior */
+ 265, /*tsuperior */
+ 266, /*ff */
+ 109, /*fi */
+ 110, /*fl */
+ 267, /*ffi */
+ 268, /*ffl */
+ 269, /*parenleftinferior */
+ 270, /*parenrightinferior */
+ 272, /*hyphensuperior */
+ 300, /*colonmonetary */
+ 301, /*onefitted */
+ 302, /*rupiah */
+ 305, /*centoldstyle */
+ 314, /*figuredash */
+ 315, /*hypheninferior */
+ 158, /*onequarter */
+ 155, /*onehalf */
+ 163, /*threequarters */
+ 320, /*oneeighth */
+ 321, /*threeeighths */
+ 322, /*fiveeighths */
+ 323, /*seveneighths */
+ 324, /*onethird */
+ 325, /*twothirds */
+ 326, /*zerosuperior */
+ 150, /*onesuperior */
+ 164, /*twosuperior */
+ 169, /*threesuperior */
+ 327, /*foursuperior */
+ 328, /*fivesuperior */
+ 329, /*sixsuperior */
+ 330, /*sevensuperior */
+ 331, /*eightsuperior */
+ 332, /*ninesuperior */
+ 333, /*zeroinferior */
+ 334, /*oneinferior */
+ 335, /*twoinferior */
+ 336, /*threeinferior */
+ 337, /*fourinferior */
+ 338, /*fiveinferior */
+ 339, /*sixinferior */
+ 340, /*seveninferior */
+ 341, /*eightinferior */
+ 342, /*nineinferior */
+ 343, /*centinferior */
+ 344, /*dollarinferior */
+ 345, /*periodinferior */
+ 346 /*commainferior */
+};
+
+static const unsigned short standard_encoding[] = {
+ /* 0 */ 0, /* .notdef */
+ /* 1 */ 0, /* .notdef */
+ /* 2 */ 0, /* .notdef */
+ /* 3 */ 0, /* .notdef */
+ /* 4 */ 0, /* .notdef */
+ /* 5 */ 0, /* .notdef */
+ /* 6 */ 0, /* .notdef */
+ /* 7 */ 0, /* .notdef */
+ /* 8 */ 0, /* .notdef */
+ /* 9 */ 0, /* .notdef */
+ /* 10 */ 0, /* .notdef */
+ /* 11 */ 0, /* .notdef */
+ /* 12 */ 0, /* .notdef */
+ /* 13 */ 0, /* .notdef */
+ /* 14 */ 0, /* .notdef */
+ /* 15 */ 0, /* .notdef */
+ /* 16 */ 0, /* .notdef */
+ /* 17 */ 0, /* .notdef */
+ /* 18 */ 0, /* .notdef */
+ /* 19 */ 0, /* .notdef */
+ /* 20 */ 0, /* .notdef */
+ /* 21 */ 0, /* .notdef */
+ /* 22 */ 0, /* .notdef */
+ /* 23 */ 0, /* .notdef */
+ /* 24 */ 0, /* .notdef */
+ /* 25 */ 0, /* .notdef */
+ /* 26 */ 0, /* .notdef */
+ /* 27 */ 0, /* .notdef */
+ /* 28 */ 0, /* .notdef */
+ /* 29 */ 0, /* .notdef */
+ /* 30 */ 0, /* .notdef */
+ /* 31 */ 0, /* .notdef */
+ /* 32 */ 1, /* space */
+ /* 33 */ 2, /* exclam */
+ /* 34 */ 3, /* quotedbl */
+ /* 35 */ 4, /* numbersign */
+ /* 36 */ 5, /* dollar */
+ /* 37 */ 6, /* percent */
+ /* 38 */ 7, /* ampersand */
+ /* 39 */ 8, /* quoteright */
+ /* 40 */ 9, /* parenleft */
+ /* 41 */ 10, /* parenright */
+ /* 42 */ 11, /* asterisk */
+ /* 43 */ 12, /* plus */
+ /* 44 */ 13, /* comma */
+ /* 45 */ 14, /* hyphen */
+ /* 46 */ 15, /* period */
+ /* 47 */ 16, /* slash */
+ /* 48 */ 17, /* zero */
+ /* 49 */ 18, /* one */
+ /* 50 */ 19, /* two */
+ /* 51 */ 20, /* three */
+ /* 52 */ 21, /* four */
+ /* 53 */ 22, /* five */
+ /* 54 */ 23, /* six */
+ /* 55 */ 24, /* seven */
+ /* 56 */ 25, /* eight */
+ /* 57 */ 26, /* nine */
+ /* 58 */ 27, /* colon */
+ /* 59 */ 28, /* semicolon */
+ /* 60 */ 29, /* less */
+ /* 61 */ 30, /* equal */
+ /* 62 */ 31, /* greater */
+ /* 63 */ 32, /* question */
+ /* 64 */ 33, /* at */
+ /* 65 */ 34, /* A */
+ /* 66 */ 35, /* B */
+ /* 67 */ 36, /* C */
+ /* 68 */ 37, /* D */
+ /* 69 */ 38, /* E */
+ /* 70 */ 39, /* F */
+ /* 71 */ 40, /* G */
+ /* 72 */ 41, /* H */
+ /* 73 */ 42, /* I */
+ /* 74 */ 43, /* J */
+ /* 75 */ 44, /* K */
+ /* 76 */ 45, /* L */
+ /* 77 */ 46, /* M */
+ /* 78 */ 47, /* N */
+ /* 79 */ 48, /* O */
+ /* 80 */ 49, /* P */
+ /* 81 */ 50, /* Q */
+ /* 82 */ 51, /* R */
+ /* 83 */ 52, /* S */
+ /* 84 */ 53, /* T */
+ /* 85 */ 54, /* U */
+ /* 86 */ 55, /* V */
+ /* 87 */ 56, /* W */
+ /* 88 */ 57, /* X */
+ /* 89 */ 58, /* Y */
+ /* 90 */ 59, /* Z */
+ /* 91 */ 60, /* bracketleft */
+ /* 92 */ 61, /* backslash */
+ /* 93 */ 62, /* bracketright */
+ /* 94 */ 63, /* asciicircum */
+ /* 95 */ 64, /* underscore */
+ /* 96 */ 65, /* quoteleft */
+ /* 97 */ 66, /* a */
+ /* 98 */ 67, /* b */
+ /* 99 */ 68, /* c */
+ /* 100 */ 69, /* d */
+ /* 101 */ 70, /* e */
+ /* 102 */ 71, /* f */
+ /* 103 */ 72, /* g */
+ /* 104 */ 73, /* h */
+ /* 105 */ 74, /* i */
+ /* 106 */ 75, /* j */
+ /* 107 */ 76, /* k */
+ /* 108 */ 77, /* l */
+ /* 109 */ 78, /* m */
+ /* 110 */ 79, /* n */
+ /* 111 */ 80, /* o */
+ /* 112 */ 81, /* p */
+ /* 113 */ 82, /* q */
+ /* 114 */ 83, /* r */
+ /* 115 */ 84, /* s */
+ /* 116 */ 85, /* t */
+ /* 117 */ 86, /* u */
+ /* 118 */ 87, /* v */
+ /* 119 */ 88, /* w */
+ /* 120 */ 89, /* x */
+ /* 121 */ 90, /* y */
+ /* 122 */ 91, /* z */
+ /* 123 */ 92, /* braceleft */
+ /* 124 */ 93, /* bar */
+ /* 125 */ 94, /* braceright */
+ /* 126 */ 95, /* asciitilde */
+ /* 127 */ 0, /* .notdef */
+ /* 128 */ 0, /* .notdef */
+ /* 129 */ 0, /* .notdef */
+ /* 130 */ 0, /* .notdef */
+ /* 131 */ 0, /* .notdef */
+ /* 132 */ 0, /* .notdef */
+ /* 133 */ 0, /* .notdef */
+ /* 134 */ 0, /* .notdef */
+ /* 135 */ 0, /* .notdef */
+ /* 136 */ 0, /* .notdef */
+ /* 137 */ 0, /* .notdef */
+ /* 138 */ 0, /* .notdef */
+ /* 139 */ 0, /* .notdef */
+ /* 140 */ 0, /* .notdef */
+ /* 141 */ 0, /* .notdef */
+ /* 142 */ 0, /* .notdef */
+ /* 143 */ 0, /* .notdef */
+ /* 144 */ 0, /* .notdef */
+ /* 145 */ 0, /* .notdef */
+ /* 146 */ 0, /* .notdef */
+ /* 147 */ 0, /* .notdef */
+ /* 148 */ 0, /* .notdef */
+ /* 149 */ 0, /* .notdef */
+ /* 150 */ 0, /* .notdef */
+ /* 151 */ 0, /* .notdef */
+ /* 152 */ 0, /* .notdef */
+ /* 153 */ 0, /* .notdef */
+ /* 154 */ 0, /* .notdef */
+ /* 155 */ 0, /* .notdef */
+ /* 156 */ 0, /* .notdef */
+ /* 157 */ 0, /* .notdef */
+ /* 158 */ 0, /* .notdef */
+ /* 159 */ 0, /* .notdef */
+ /* 160 */ 0, /* .notdef */
+ /* 161 */ 96, /* exclamdown */
+ /* 162 */ 97, /* cent */
+ /* 163 */ 98, /* sterling */
+ /* 164 */ 99, /* fraction */
+ /* 165 */ 100, /* yen */
+ /* 166 */ 101, /* florin */
+ /* 167 */ 102, /* section */
+ /* 168 */ 103, /* currency */
+ /* 169 */ 104, /* quotesingle */
+ /* 170 */ 105, /* quotedblleft */
+ /* 171 */ 106, /* guillemotleft */
+ /* 172 */ 107, /* guilsinglleft */
+ /* 173 */ 108, /* guilsinglright */
+ /* 174 */ 109, /* fi */
+ /* 175 */ 110, /* fl */
+ /* 176 */ 0, /* .notdef */
+ /* 177 */ 111, /* endash */
+ /* 178 */ 112, /* dagger */
+ /* 179 */ 113, /* daggerdbl */
+ /* 180 */ 114, /* periodcentered */
+ /* 181 */ 0, /* .notdef */
+ /* 182 */ 115, /* paragraph */
+ /* 183 */ 116, /* bullet */
+ /* 184 */ 117, /* quotesinglbase */
+ /* 185 */ 118, /* quotedblbase */
+ /* 186 */ 119, /* quotedblright */
+ /* 187 */ 120, /* guillemotright */
+ /* 188 */ 121, /* ellipsis */
+ /* 189 */ 122, /* perthousand */
+ /* 190 */ 0, /* .notdef */
+ /* 191 */ 123, /* questiondown */
+ /* 192 */ 0, /* .notdef */
+ /* 193 */ 124, /* grave */
+ /* 194 */ 125, /* acute */
+ /* 195 */ 126, /* circumflex */
+ /* 196 */ 127, /* tilde */
+ /* 197 */ 128, /* macron */
+ /* 198 */ 129, /* breve */
+ /* 199 */ 130, /* dotaccent */
+ /* 200 */ 131, /* dieresis */
+ /* 201 */ 0, /* .notdef */
+ /* 202 */ 132, /* ring */
+ /* 203 */ 133, /* cedilla */
+ /* 204 */ 0, /* .notdef */
+ /* 205 */ 134, /* hungarumlaut */
+ /* 206 */ 135, /* ogonek */
+ /* 207 */ 136, /* caron */
+ /* 208 */ 137, /* emdash */
+ /* 209 */ 0, /* .notdef */
+ /* 210 */ 0, /* .notdef */
+ /* 211 */ 0, /* .notdef */
+ /* 212 */ 0, /* .notdef */
+ /* 213 */ 0, /* .notdef */
+ /* 214 */ 0, /* .notdef */
+ /* 215 */ 0, /* .notdef */
+ /* 216 */ 0, /* .notdef */
+ /* 217 */ 0, /* .notdef */
+ /* 218 */ 0, /* .notdef */
+ /* 219 */ 0, /* .notdef */
+ /* 220 */ 0, /* .notdef */
+ /* 221 */ 0, /* .notdef */
+ /* 222 */ 0, /* .notdef */
+ /* 223 */ 0, /* .notdef */
+ /* 224 */ 0, /* .notdef */
+ /* 225 */ 138, /* AE */
+ /* 226 */ 0, /* .notdef */
+ /* 227 */ 139, /* ordfeminine */
+ /* 228 */ 0, /* .notdef */
+ /* 229 */ 0, /* .notdef */
+ /* 230 */ 0, /* .notdef */
+ /* 231 */ 0, /* .notdef */
+ /* 232 */ 140, /* Lslash */
+ /* 233 */ 141, /* Oslash */
+ /* 234 */ 142, /* OE */
+ /* 235 */ 143, /* ordmasculine */
+ /* 236 */ 0, /* .notdef */
+ /* 237 */ 0, /* .notdef */
+ /* 238 */ 0, /* .notdef */
+ /* 239 */ 0, /* .notdef */
+ /* 240 */ 0, /* .notdef */
+ /* 241 */ 144, /* ae */
+ /* 242 */ 0, /* .notdef */
+ /* 243 */ 0, /* .notdef */
+ /* 244 */ 0, /* .notdef */
+ /* 245 */ 145, /* dotlessi */
+ /* 246 */ 0, /* .notdef */
+ /* 247 */ 0, /* .notdef */
+ /* 248 */ 146, /* lslash */
+ /* 249 */ 147, /* oslash */
+ /* 250 */ 148, /* oe */
+ /* 251 */ 149, /* germandbls */
+ /* 252 */ 0, /* .notdef */
+ /* 253 */ 0, /* .notdef */
+ /* 254 */ 0, /* .notdef */
+ /* 255 */ 0 /* .notdef */
+};
+
+static const unsigned short expert_encoding[] = {
+ /* 0 */ 0, /* .notdef */
+ /* 1 */ 0, /* .notdef */
+ /* 2 */ 0, /* .notdef */
+ /* 3 */ 0, /* .notdef */
+ /* 4 */ 0, /* .notdef */
+ /* 5 */ 0, /* .notdef */
+ /* 6 */ 0, /* .notdef */
+ /* 7 */ 0, /* .notdef */
+ /* 8 */ 0, /* .notdef */
+ /* 9 */ 0, /* .notdef */
+ /* 10 */ 0, /* .notdef */
+ /* 11 */ 0, /* .notdef */
+ /* 12 */ 0, /* .notdef */
+ /* 13 */ 0, /* .notdef */
+ /* 14 */ 0, /* .notdef */
+ /* 15 */ 0, /* .notdef */
+ /* 16 */ 0, /* .notdef */
+ /* 17 */ 0, /* .notdef */
+ /* 18 */ 0, /* .notdef */
+ /* 19 */ 0, /* .notdef */
+ /* 20 */ 0, /* .notdef */
+ /* 21 */ 0, /* .notdef */
+ /* 22 */ 0, /* .notdef */
+ /* 23 */ 0, /* .notdef */
+ /* 24 */ 0, /* .notdef */
+ /* 25 */ 0, /* .notdef */
+ /* 26 */ 0, /* .notdef */
+ /* 27 */ 0, /* .notdef */
+ /* 28 */ 0, /* .notdef */
+ /* 29 */ 0, /* .notdef */
+ /* 30 */ 0, /* .notdef */
+ /* 31 */ 0, /* .notdef */
+ /* 32 */ 1, /* space */
+ /* 33 */ 229, /* exclamsmall */
+ /* 34 */ 230, /* Hungarumlautsmall */
+ /* 35 */ 0, /* .notdef */
+ /* 36 */ 231, /* dollaroldstyle */
+ /* 37 */ 232, /* dollarsuperior */
+ /* 38 */ 233, /* ampersandsmall */
+ /* 39 */ 234, /* Acutesmall */
+ /* 40 */ 235, /* parenleftsuperior */
+ /* 41 */ 236, /* parenrightsuperior */
+ /* 42 */ 237, /* twodotenleader */
+ /* 43 */ 238, /* onedotenleader */
+ /* 44 */ 13, /* comma */
+ /* 45 */ 14, /* hyphen */
+ /* 46 */ 15, /* period */
+ /* 47 */ 99, /* fraction */
+ /* 48 */ 239, /* zerooldstyle */
+ /* 49 */ 240, /* oneoldstyle */
+ /* 50 */ 241, /* twooldstyle */
+ /* 51 */ 242, /* threeoldstyle */
+ /* 52 */ 243, /* fouroldstyle */
+ /* 53 */ 244, /* fiveoldstyle */
+ /* 54 */ 245, /* sixoldstyle */
+ /* 55 */ 246, /* sevenoldstyle */
+ /* 56 */ 247, /* eightoldstyle */
+ /* 57 */ 248, /* nineoldstyle */
+ /* 58 */ 27, /* colon */
+ /* 59 */ 28, /* semicolon */
+ /* 60 */ 249, /* commasuperior */
+ /* 61 */ 250, /* threequartersemdash */
+ /* 62 */ 251, /* periodsuperior */
+ /* 63 */ 252, /* questionsmall */
+ /* 64 */ 0, /* .notdef */
+ /* 65 */ 253, /* asuperior */
+ /* 66 */ 254, /* bsuperior */
+ /* 67 */ 255, /* centsuperior */
+ /* 68 */ 256, /* dsuperior */
+ /* 69 */ 257, /* esuperior */
+ /* 70 */ 0, /* .notdef */
+ /* 71 */ 0, /* .notdef */
+ /* 72 */ 0, /* .notdef */
+ /* 73 */ 258, /* isuperior */
+ /* 74 */ 0, /* .notdef */
+ /* 75 */ 0, /* .notdef */
+ /* 76 */ 259, /* lsuperior */
+ /* 77 */ 260, /* msuperior */
+ /* 78 */ 261, /* nsuperior */
+ /* 79 */ 262, /* osuperior */
+ /* 80 */ 0, /* .notdef */
+ /* 81 */ 0, /* .notdef */
+ /* 82 */ 263, /* rsuperior */
+ /* 83 */ 264, /* ssuperior */
+ /* 84 */ 265, /* tsuperior */
+ /* 85 */ 0, /* .notdef */
+ /* 86 */ 266, /* ff */
+ /* 87 */ 109, /* fi */
+ /* 88 */ 110, /* fl */
+ /* 89 */ 267, /* ffi */
+ /* 90 */ 268, /* ffl */
+ /* 91 */ 269, /* parenleftinferior */
+ /* 92 */ 0, /* .notdef */
+ /* 93 */ 270, /* parenrightinferior */
+ /* 94 */ 271, /* Circumflexsmall */
+ /* 95 */ 272, /* hyphensuperior */
+ /* 96 */ 273, /* Gravesmall */
+ /* 97 */ 274, /* Asmall */
+ /* 98 */ 275, /* Bsmall */
+ /* 99 */ 276, /* Csmall */
+ /* 100 */ 277, /* Dsmall */
+ /* 101 */ 278, /* Esmall */
+ /* 102 */ 279, /* Fsmall */
+ /* 103 */ 280, /* Gsmall */
+ /* 104 */ 281, /* Hsmall */
+ /* 105 */ 282, /* Ismall */
+ /* 106 */ 283, /* Jsmall */
+ /* 107 */ 284, /* Ksmall */
+ /* 108 */ 285, /* Lsmall */
+ /* 109 */ 286, /* Msmall */
+ /* 110 */ 287, /* Nsmall */
+ /* 111 */ 288, /* Osmall */
+ /* 112 */ 289, /* Psmall */
+ /* 113 */ 290, /* Qsmall */
+ /* 114 */ 291, /* Rsmall */
+ /* 115 */ 292, /* Ssmall */
+ /* 116 */ 293, /* Tsmall */
+ /* 117 */ 294, /* Usmall */
+ /* 118 */ 295, /* Vsmall */
+ /* 119 */ 296, /* Wsmall */
+ /* 120 */ 297, /* Xsmall */
+ /* 121 */ 298, /* Ysmall */
+ /* 122 */ 299, /* Zsmall */
+ /* 123 */ 300, /* colonmonetary */
+ /* 124 */ 301, /* onefitted */
+ /* 125 */ 302, /* rupiah */
+ /* 126 */ 303, /* Tildesmall */
+ /* 127 */ 0, /* .notdef */
+ /* 128 */ 0, /* .notdef */
+ /* 129 */ 0, /* .notdef */
+ /* 130 */ 0, /* .notdef */
+ /* 131 */ 0, /* .notdef */
+ /* 132 */ 0, /* .notdef */
+ /* 133 */ 0, /* .notdef */
+ /* 134 */ 0, /* .notdef */
+ /* 135 */ 0, /* .notdef */
+ /* 136 */ 0, /* .notdef */
+ /* 137 */ 0, /* .notdef */
+ /* 138 */ 0, /* .notdef */
+ /* 139 */ 0, /* .notdef */
+ /* 140 */ 0, /* .notdef */
+ /* 141 */ 0, /* .notdef */
+ /* 142 */ 0, /* .notdef */
+ /* 143 */ 0, /* .notdef */
+ /* 144 */ 0, /* .notdef */
+ /* 145 */ 0, /* .notdef */
+ /* 146 */ 0, /* .notdef */
+ /* 147 */ 0, /* .notdef */
+ /* 148 */ 0, /* .notdef */
+ /* 149 */ 0, /* .notdef */
+ /* 150 */ 0, /* .notdef */
+ /* 151 */ 0, /* .notdef */
+ /* 152 */ 0, /* .notdef */
+ /* 153 */ 0, /* .notdef */
+ /* 154 */ 0, /* .notdef */
+ /* 155 */ 0, /* .notdef */
+ /* 156 */ 0, /* .notdef */
+ /* 157 */ 0, /* .notdef */
+ /* 158 */ 0, /* .notdef */
+ /* 159 */ 0, /* .notdef */
+ /* 160 */ 0, /* .notdef */
+ /* 161 */ 304, /* exclamdownsmall */
+ /* 162 */ 305, /* centoldstyle */
+ /* 163 */ 306, /* Lslashsmall */
+ /* 164 */ 0, /* .notdef */
+ /* 165 */ 0, /* .notdef */
+ /* 166 */ 307, /* Scaronsmall */
+ /* 167 */ 308, /* Zcaronsmall */
+ /* 168 */ 309, /* Dieresissmall */
+ /* 169 */ 310, /* Brevesmall */
+ /* 170 */ 311, /* Caronsmall */
+ /* 171 */ 0, /* .notdef */
+ /* 172 */ 312, /* Dotaccentsmall */
+ /* 173 */ 0, /* .notdef */
+ /* 174 */ 0, /* .notdef */
+ /* 175 */ 313, /* Macronsmall */
+ /* 176 */ 0, /* .notdef */
+ /* 177 */ 0, /* .notdef */
+ /* 178 */ 314, /* figuredash */
+ /* 179 */ 315, /* hypheninferior */
+ /* 180 */ 0, /* .notdef */
+ /* 181 */ 0, /* .notdef */
+ /* 182 */ 316, /* Ogoneksmall */
+ /* 183 */ 317, /* Ringsmall */
+ /* 184 */ 318, /* Cedillasmall */
+ /* 185 */ 0, /* .notdef */
+ /* 186 */ 0, /* .notdef */
+ /* 187 */ 0, /* .notdef */
+ /* 188 */ 158, /* onequarter */
+ /* 189 */ 155, /* onehalf */
+ /* 190 */ 163, /* threequarters */
+ /* 191 */ 319, /* questiondownsmall */
+ /* 192 */ 320, /* oneeighth */
+ /* 193 */ 321, /* threeeighths */
+ /* 194 */ 322, /* fiveeighths */
+ /* 195 */ 323, /* seveneighths */
+ /* 196 */ 324, /* onethird */
+ /* 197 */ 325, /* twothirds */
+ /* 198 */ 0, /* .notdef */
+ /* 199 */ 0, /* .notdef */
+ /* 200 */ 326, /* zerosuperior */
+ /* 201 */ 150, /* onesuperior */
+ /* 202 */ 164, /* twosuperior */
+ /* 203 */ 169, /* threesuperior */
+ /* 204 */ 327, /* foursuperior */
+ /* 205 */ 328, /* fivesuperior */
+ /* 206 */ 329, /* sixsuperior */
+ /* 207 */ 330, /* sevensuperior */
+ /* 208 */ 331, /* eightsuperior */
+ /* 209 */ 332, /* ninesuperior */
+ /* 210 */ 333, /* zeroinferior */
+ /* 211 */ 334, /* oneinferior */
+ /* 212 */ 335, /* twoinferior */
+ /* 213 */ 336, /* threeinferior */
+ /* 214 */ 337, /* fourinferior */
+ /* 215 */ 338, /* fiveinferior */
+ /* 216 */ 339, /* sixinferior */
+ /* 217 */ 340, /* seveninferior */
+ /* 218 */ 341, /* eightinferior */
+ /* 219 */ 342, /* nineinferior */
+ /* 220 */ 343, /* centinferior */
+ /* 221 */ 344, /* dollarinferior */
+ /* 222 */ 345, /* periodinferior */
+ /* 223 */ 346, /* commainferior */
+ /* 224 */ 347, /* Agravesmall */
+ /* 225 */ 348, /* Aacutesmall */
+ /* 226 */ 349, /* Acircumflexsmall */
+ /* 227 */ 350, /* Atildesmall */
+ /* 228 */ 351, /* Adieresissmall */
+ /* 229 */ 352, /* Aringsmall */
+ /* 230 */ 353, /* AEsmall */
+ /* 231 */ 354, /* Ccedillasmall */
+ /* 232 */ 355, /* Egravesmall */
+ /* 233 */ 356, /* Eacutesmall */
+ /* 234 */ 357, /* Ecircumflexsmall */
+ /* 235 */ 358, /* Edieresissmall */
+ /* 236 */ 359, /* Igravesmall */
+ /* 237 */ 360, /* Iacutesmall */
+ /* 238 */ 361, /* Icircumflexsmall */
+ /* 239 */ 362, /* Idieresissmall */
+ /* 240 */ 363, /* Ethsmall */
+ /* 241 */ 364, /* Ntildesmall */
+ /* 242 */ 365, /* Ogravesmall */
+ /* 243 */ 366, /* Oacutesmall */
+ /* 244 */ 367, /* Ocircumflexsmall */
+ /* 245 */ 368, /* Otildesmall */
+ /* 246 */ 369, /* Odieresissmall */
+ /* 247 */ 370, /* OEsmall */
+ /* 248 */ 371, /* Oslashsmall */
+ /* 249 */ 372, /* Ugravesmall */
+ /* 250 */ 373, /* Uacutesmall */
+ /* 251 */ 374, /* Ucircumflexsmall */
+ /* 252 */ 375, /* Udieresissmall */
+ /* 253 */ 376, /* Yacutesmall */
+ /* 254 */ 377, /* Thornsmall */
+ /* 255 */ 378 /* Ydieresissmall */
+};
+
+typedef struct tag_cff_data_t {
+ ref *blk_ref;
+ unsigned int length;
+ unsigned int shift;
+ unsigned int mask;
+} cff_data_t;
+
+typedef struct tag_cff_index_t {
+ unsigned int start, end, data;
+ unsigned int offsize, count;
+} cff_index_t;
+
+static int
+card8(unsigned int *u, const cff_data_t *o, unsigned p, unsigned pe)
+{
+ if (pe > o->length || p > pe - 1)
+ return_error(gs_error_rangecheck); /* out of range access */
+ *u = o->blk_ref[p >> o->shift].value.bytes[p & o->mask];
+ return 0;
+}
+
+static int
+card16(unsigned int *u, const cff_data_t *o, unsigned p, unsigned pe)
+{
+ if (pe > o->length || p > pe - 2)
+ return_error(gs_error_rangecheck); /* out of range access */
+ *u = (o->blk_ref[ p >> o->shift].value.bytes[ p & o->mask]) << 8 |
+ o->blk_ref[(p + 1) >> o->shift].value.bytes[(p + 1) & o->mask];
+ return 0;
+}
+
+static int
+card24(unsigned int *u, const cff_data_t *o, unsigned p, unsigned pe)
+{
+ if (pe > o->length || p > pe - 3)
+ return_error(gs_error_rangecheck); /* out of range access */
+ *u = (o->blk_ref[ p >> o->shift].value.bytes[ p & o->mask]) << 16 |
+ (o->blk_ref[(p + 1) >> o->shift].value.bytes[(p + 1) & o->mask]) << 8 |
+ o->blk_ref[(p + 2) >> o->shift].value.bytes[(p + 2) & o->mask];
+ return 0;
+}
+static int
+card32(unsigned int *u, const cff_data_t *o, unsigned p, unsigned pe)
+{
+ if (pe > o->length || p > pe - 4)
+ return_error(gs_error_rangecheck); /* out of range access */
+ *u = (o->blk_ref[ p >> o->shift].value.bytes[ p & o->mask]) << 24 |
+ (o->blk_ref[(p + 1) >> o->shift].value.bytes[(p + 1) & o->mask]) << 16 |
+ (o->blk_ref[(p + 2) >> o->shift].value.bytes[(p + 2) & o->mask]) << 8 |
+ o->blk_ref[(p + 3) >> o->shift].value.bytes[(p + 3) & o->mask];
+ return 0;
+}
+
+static int (* const offset_procs[])(unsigned int *, const cff_data_t *, unsigned, unsigned) = {
+ 0, card8, card16, card24, card32
+};
+
+static int
+get_cff_string(unsigned char *dst, const cff_data_t *o, unsigned p, unsigned len)
+{
+ if (p + len > o->length)
+ return_error(gs_error_rangecheck); /* out of range access */
+ while (len) {
+ unsigned chunk_len = o->mask + 1 - (p & o->mask);
+ unsigned char *pos = o->blk_ref[p >> o->shift].value.bytes + (p & o->mask);
+ if (chunk_len > len)
+ chunk_len = len;
+ memcpy(dst, pos, chunk_len);
+ dst += chunk_len;
+ len -= chunk_len;
+ p += chunk_len;
+ }
+ return 0;
+}
+
+static int
+parse_index(cff_index_t *x, const cff_data_t *data, unsigned p, unsigned pe)
+{ int code;
+
+ if (p == 0) {
+ /* Make an empty index when the offset to the index is not defined. */
+ memset(x, 0, sizeof(*x));
+ return 0;
+ }
+ x->start = p;
+ if ((code = card16(&x->count, data, p, pe)) < 0)
+ return code;
+ if (x->count) {
+ unsigned int eod;
+
+ if ((code = card8(&x->offsize, data, p + 2, pe)) < 0)
+ return code;
+ if (x->offsize == 0) { /* skip incorrect index, bug 689854 */
+ x->count = 0;
+ x->data = 0;
+ x->end = p + 3;
+ } else if( x->offsize > 4) {
+ return_error(gs_error_rangecheck); /* Invalid offest size */
+ } else {
+ x->data = p + 2 + x->offsize*(x->count+1);
+ code = (*offset_procs[x->offsize])(&eod, data, p + 3 + x->offsize*x->count, pe);
+ if (code < 0)
+ return code;
+ x->end = x->data + eod;
+ }
+ } else {
+ x->offsize = 0;
+ x->data = 0;
+ x->end = p + 2;
+ }
+ return 0;
+}
+
+static int
+peek_index(unsigned *pp, unsigned int *len, const cff_index_t *x, const cff_data_t *data, unsigned int i)
+{
+ int code;
+ unsigned int off1, off2;
+
+ if (i >= x->count)
+ return_error(gs_error_rangecheck); /* wrong index */
+ if((code = (*offset_procs[x->offsize])(&off1, data, x->start + 3 + x->offsize*i, x->end)) < 0)
+ return code;
+ if((code = (*offset_procs[x->offsize])(&off2, data, x->start + 3 + x->offsize*(i + 1), x->end)) < 0)
+ return code;
+ if (off2 < off1)
+ return_error(gs_error_rangecheck); /* Decreasing offsets */
+ if (x->data + off2 > x->end)
+ return_error(gs_error_rangecheck); /* Element exceeds index size */
+ *len = off2 - off1;
+ *pp = x->data + off1;
+ return 0;
+}
+
+static int
+get_int(int *v, const cff_data_t *data, unsigned p, unsigned pe)
+{
+ int code;
+ unsigned int c, u;
+ const int ext16 = ~0 << 15; /* sign extension constant */
+ const int ext32 = ~0 << 31; /* sign extension constant */
+
+ if ((code = card8(&c, data, p, pe)) < 0)
+ return code;
+
+ if (c == 28) {
+ if ((code = card16(&u, data, p + 1, pe)) < 0)
+ return code;
+ *v = ((int)u + ext16) ^ ext16;
+ return 3;
+ }
+ if (c == 29) {
+ if ((code = card32(&u, data, p + 1, pe)) < 0)
+ return code;
+ *v = ((int)u + ext32) ^ ext32;
+ return 5;
+ }
+ if (c < 32)
+ return_error(gs_error_rangecheck); /* out of range */
+ if (c < 247) {
+ *v = ((int)c - 139);
+ return 1;
+ }
+ if (c < 251) {
+ if ((code = card8(&u, data, p + 1, pe)) < 0)
+ return code;
+ *v = ((int)c - 247)*256 + (int)u + 108;
+ return 2;
+ }
+ if (c < 255) {
+ if ((code = card8(&u, data, p + 1, pe)) < 0)
+ return code;
+ *v = -((int)c - 251)*256 - (int)u - 108;
+ return 2;
+ }
+ return_error(gs_error_rangecheck); /* out of range */
+}
+
+static int
+get_float(ref *fnum, const cff_data_t *data, unsigned p, unsigned pe)
+{
+ int code;
+ unsigned int c, i;
+ char buf[80];
+ const unsigned p0 = p;
+ char *q = buf;
+
+ for(;;) {
+ unsigned int u;
+
+ if ((code = card8(&u, data, p, pe)) < 0)
+ return code;
+ for (i=0; i<2 && q < buf + sizeof(buf) - 1; i++) {
+ c = i ? u & 15 : u >> 4 ;
+ switch(c) {
+ case 0: case 1: case 2: case 3: case 4: /* 0 .. 9 */
+ case 5: case 6: case 7: case 8: case 9:
+ *q++ = '0' + c;
+ break;
+ case 10: /* . */
+ *q++ = '.';
+ break;
+ case 11: /* E */
+ *q++ = 'e';
+ break;
+ case 12: /* E- */
+ *q++ = 'e';
+ *q++ = '-';
+ break;
+ case 13: /* invalid, skip */
+ break;
+ case 14: /* - */
+ *q++ = '-';
+ break;
+ case 15: { /* end */
+ int sign = 0;
+ char *eptr, *bptr = buf;
+
+ if (buf[0] == '-'){
+ sign = -1;
+ bptr = &(buf[1]);
+ }
+
+ code = scan_number ((const byte *)bptr, (const byte *)q, sign, fnum, (const byte **)&eptr, 0);
+ if (code < 0) {
+ return(code);
+ }
+ else {
+ return p - p0 + 1;
+ }
+ break;
+ }
+ }
+ }
+ p++;
+ }
+}
+
+static int
+idict_put_c_name(i_ctx_t *i_ctx_p, ref *dst, const char *c_name, unsigned int len, const ref *src)
+{
+ int code;
+ ref ps_name;
+
+ if ((code = name_ref(imemory, (const unsigned char *)c_name, len, &ps_name, 0)) < 0)
+ return code;
+ return idict_put(dst, &ps_name, src);
+}
+
+static int
+idict_undef_c_name(i_ctx_t *i_ctx_p, ref *dst, const char *c_name, unsigned int len)
+{
+ int code;
+ ref ps_name;
+
+ if ((code = name_ref(imemory, (const unsigned char *)c_name, len, &ps_name, 0)) < 0)
+ return code;
+ code = idict_undef(dst, &ps_name);
+ if (code < 0 && code != gs_error_undefined) /* ignore undefined error */
+ return code;
+ return 0;
+}
+
+static int
+idict_move_c_name(i_ctx_t *i_ctx_p, ref *dst, ref *src, const char *c_name, unsigned int len)
+{
+ int code;
+ ref ps_name, *pvalue;
+
+ if ((code = name_ref(imemory, (const unsigned char *)c_name, len, &ps_name, 0)) < 0)
+ return code;
+ if (dict_find(src, &ps_name, &pvalue) > 0) {
+ if ((code = idict_put(dst, &ps_name, pvalue)) < 0)
+ return code;
+ if ((code = idict_undef(src, &ps_name)) < 0)
+ return code;
+ }
+ return 0;
+}
+
+static int
+make_string_from_index(i_ctx_t *i_ctx_p, ref *dst, const cff_index_t *index, const cff_data_t *data, unsigned int id, int fd_num)
+{
+ int code;
+ unsigned int len, doff;
+ byte *sbody;
+ int fdoff = fd_num >= 0;
+
+ if ((code = peek_index(&doff, &len, index, data, id)) < 0)
+ return code;
+ if (len + fdoff> 65535)
+ return_error(gs_error_limitcheck);
+ if ((sbody = ialloc_string(len + fdoff, "make_string_from_index")) == 0)
+ return_error(gs_error_VMerror);
+ make_string(dst, icurrent_space | a_readonly, len + fdoff, sbody);
+ if ((code = get_cff_string(sbody + fdoff, data, doff, len)) < 0)
+ return code;
+ if (fdoff)
+ sbody[0] = fd_num;
+ return 0;
+}
+
+static int
+make_string_from_sid(i_ctx_t *i_ctx_p, ref *dst, const cff_index_t *strings, const cff_data_t *data, unsigned int sid)
+{
+ if (sid < count_of(standard_strings)) {
+ make_string(dst, avm_foreign | a_readonly,
+ strlen(standard_strings[sid]), (unsigned char *)standard_strings[sid]); /* break const */
+ return 0;
+ } else
+ return make_string_from_index(i_ctx_p, dst, strings, data, sid - count_of(standard_strings), -1);
+}
+
+static int
+make_name_from_sid(i_ctx_t *i_ctx_p, ref *dst, const cff_index_t *strings, const cff_data_t *data, unsigned int sid)
+{
+ if (sid < count_of(standard_strings)) {
+ return name_ref(imemory, (const unsigned char *)standard_strings[sid],
+ strlen(standard_strings[sid]), dst, 0);
+ } else {
+ int code;
+ unsigned int off, len;
+ byte buf[200];
+
+ if ((code = peek_index(&off, &len, strings, data, sid - count_of(standard_strings))) < 0)
+ return code;
+ if (len > sizeof(buf))
+ return_error(gs_error_limitcheck);
+ if ((code = get_cff_string(buf, data, off, len)) < 0)
+ return code;
+ return name_ref(imemory, buf, len, dst, 1);
+ }
+}
+
+static int
+make_stringarray_from_index(i_ctx_t *i_ctx_p, ref *dst, const cff_index_t *index, const cff_data_t *data)
+{
+ int code;
+ unsigned int i;
+
+ if ((code = ialloc_ref_array(dst, a_readonly, index->count, "make_stringarray_from_index")) < 0)
+ return code;
+ for (i = 0; i < index->count; i++) {
+ unsigned int subr, len;
+
+ if ((code = peek_index(&subr, &len, index, data, i)) < 0)
+ return code;
+ if ((code = make_string_from_index(i_ctx_p, dst->value.refs + i, index, data, i, -1)) < 0)
+ return code;
+ }
+ return 0;
+}
+
+static void
+undelta(ref *ops, unsigned int cnt)
+{
+ unsigned int i;
+
+ for (i = 0; i < cnt; i++) {
+ if (!r_has_type(&ops[i], t_real))
+ make_real(&ops[i], (float)ops[i].value.intval);
+ }
+ for (i = 1; i < cnt; i++) {
+ make_real(&ops[i], ops[i].value.realval + ops[i - 1].value.realval);
+ }
+}
+
+static int
+iso_adobe_charset_proc(const cff_data_t *data, unsigned p, unsigned pe, unsigned i)
+{
+ if (i < 228)
+ return i + 1;
+ else
+ return_error(gs_error_rangecheck);
+}
+
+static int
+expert_charset_proc(const cff_data_t *data, unsigned p, unsigned pe, unsigned i)
+{
+ if (i < sizeof(expert_charset)/sizeof(*expert_charset))
+ return expert_charset[i];
+ else
+ return_error(gs_error_rangecheck);
+}
+
+static int
+expert_subset_charset_proc(const cff_data_t *data, unsigned p, unsigned pe, unsigned int i)
+{
+ if (i < sizeof(expert_subset_charset)/sizeof(*expert_subset_charset))
+ return expert_subset_charset[i];
+ else
+ return_error(gs_error_rangecheck);
+}
+
+static int
+format0_charset_proc(const cff_data_t *data, unsigned p, unsigned pe, unsigned int i)
+{
+ int code;
+ unsigned u;
+
+ if ((code = card16(&u, data, p + 2*i, pe)) < 0)
+ return code;
+ return (int)u;
+}
+
+static int
+format1_charset_proc(const cff_data_t *data, unsigned p, unsigned pe, unsigned int i)
+{
+ int code;
+ unsigned int cid = 0;
+
+ while( p < pe - 3) {
+ unsigned int first, count;
+
+ if ((code = card16(&first, data, p, pe)) < 0)
+ return code;
+ if ((code = card8(&count, data, p + 2, pe)) < 0)
+ return code;
+ ++count;
+ if (i < cid + count)
+ return first + i - cid;
+ p += 3;
+ cid += count;
+ }
+ return_error(gs_error_rangecheck);
+}
+
+static int
+format2_charset_proc(const cff_data_t *data, unsigned p, unsigned pe, unsigned int i)
+{
+ int code;
+ unsigned int cid = 0;
+
+ while( p < pe - 4) {
+ unsigned int first, count;
+
+ if ((code = card16(&first, data, p, pe)) < 0)
+ return code;
+ if ((code = card16(&count, data, p + 2, pe)) < 0)
+ return code;
+ ++count;
+
+ if (i < cid + count)
+ return first + i - cid;
+ p += 4;
+ cid += count;
+ }
+ return_error(gs_error_rangecheck);
+}
+
+static int
+format0_fdselect_proc(const cff_data_t *data, unsigned p, unsigned pe, unsigned int i)
+{
+ int code;
+ unsigned u;
+
+ if ((code = card8(&u, data, p + i, pe)) < 0)
+ return code;
+ return (int)u;
+}
+
+static int
+format3_fdselect_proc(const cff_data_t *data, unsigned p, unsigned pe, unsigned int i)
+{
+ int code;
+ unsigned int u, n_ranges;
+
+ if ((code = card16(&n_ranges, data, p, pe)) < 0)
+ return code;
+ p += 2;
+
+ while (n_ranges-- && p + 5 <= pe) {
+ unsigned int first, last;
+
+ if ((code = card16(&first, data, p, pe)) < 0)
+ return code;
+ if ((code = card16(&last, data, p + 3, pe)) < 0)
+ return code;
+ if (i >= first && i < last) {
+ if ((code = card8(&u, data, p + 2, pe)) < 0)
+ return code;
+ return (int)u;
+ }
+ p += 3;
+ }
+ return_error(gs_error_rangecheck);
+}
+
+static int
+find_font_dict(i_ctx_t *i_ctx_p, ref *topdict, ref **fontdict, const char *key)
+{
+ int code;
+ ref val;
+
+ if (*fontdict)
+ return 0;
+ if (dict_find_string(topdict, key, fontdict) > 0)
+ return 0;
+ if ((code = dict_create(8, &val)) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, topdict, key, strlen(key), &val)) < 0)
+ return code;
+ if ((code = dict_find_string(topdict, key, fontdict)) == 0)
+ return_error(gs_error_undefined); /* can't happen */
+ return code;
+}
+
+#define DEFINE_KEYS(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z, \
+ A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) \
+static const char * const font_keys[] = { \
+ #a,#b,#c,#d,#e,#f,#g,#h,#i,#j,#k,#l,#m,#n,#o,#p,#q,#r,#s,#t,#u,#v,#w,#x,#y,#z, \
+ #A,#B,#C,#D,#E,#F,#G,#H,#I,#J,#K,#L,#M,#N,#O,#P,#Q,#R,#S,#T,#U,#V \
+}; \
+static const short font_keys_sz[] = { \
+ sizeof(#a)-1, sizeof(#b)-1, sizeof(#c)-1, sizeof(#d)-1, sizeof(#e)-1, \
+ sizeof(#f)-1, sizeof(#g)-1, sizeof(#h)-1, sizeof(#i)-1, sizeof(#j)-1, \
+ sizeof(#k)-1, sizeof(#l)-1, sizeof(#m)-1, sizeof(#n)-1, sizeof(#o)-1, \
+ sizeof(#p)-1, sizeof(#q)-1, sizeof(#r)-1, sizeof(#s)-1, sizeof(#t)-1, \
+ sizeof(#u)-1, sizeof(#v)-1, sizeof(#w)-1, sizeof(#x)-1, sizeof(#y)-1, \
+ sizeof(#z)-1, sizeof(#A)-1, sizeof(#B)-1, sizeof(#C)-1, sizeof(#D)-1, \
+ sizeof(#E)-1, sizeof(#F)-1, sizeof(#G)-1, sizeof(#H)-1, sizeof(#I)-1, \
+ sizeof(#J)-1, sizeof(#K)-1, sizeof(#L)-1, sizeof(#M)-1, sizeof(#N)-1, \
+ sizeof(#O)-1, sizeof(#P)-1, sizeof(#Q)-1, sizeof(#R)-1, sizeof(#S)-1, \
+ sizeof(#T)-1, sizeof(#U)-1, sizeof(#V)-1 \
+}; \
+typedef enum { \
+ k_##a = 0, k_##b, k_##c, k_##d, k_##e, k_##f, k_##g, k_##h, k_##i, k_##j, k_##k, k_##l, k_##m, \
+ k_##n, k_##o, k_##p, k_##q, k_##r, k_##s, k_##t, k_##u, k_##v, k_##w, k_##x, k_##y, k_##z, \
+ k_##A, k_##B, k_##C, k_##D, k_##E, k_##F, k_##G, k_##H, k_##I, k_##J, k_##K, k_##L, k_##M, \
+ k_##N, k_##O, k_##P, k_##Q, k_##R, k_##S, k_##T, k_##U, k_##V\
+} font_keys_dummy_t;
+
+DEFINE_KEYS(
+ version,
+ Notice,
+ FullName,
+ FamilyName,
+ Weight,
+ FontBBox,
+ BlueValues,
+ OtherBlues,
+ FamilyBlues,
+ FamilyOtherBlues,
+ StdHW,
+ StdVW,
+ Copyright,
+ isFixedPitch,
+ ItalicAngle,
+ UnderlinePosition,
+ UnderlineThickness,
+ PaintType,
+ CharstringType,
+ FontMatrix,
+ StrokeWidth,
+ BlueScale,
+ BlueShift,
+ BlueFuzz,
+ StemSnapH,
+ StemSnapV,
+ ForceBold,
+ LanguageGroup,
+ ExpansionFactor,
+ initialRandomSeed,
+ SyntheticBase,
+ PostScript,
+ BaseFontName,
+ BaseFontBlend,
+ CIDFontVersion,
+ CIDFontRevision,
+ CIDFontType,
+ CIDCount,
+ UIDBase,
+ FDArray,
+ FDSelect,
+ FontName,
+ UniqueID,
+ XUID,
+ defaultWidthX,
+ nominalWidthX,
+ FontType,
+ Private
+)
+#undef DEFINE_KEYS
+
+typedef enum {
+ k_topdict = 0, k_fontinfodict = 0x10000, k_privatedict = 0x20000, k_sid = 0x40000,
+ k_array = 0x80000, k_delta = 0x100000, k_int = 0x200000, k_bool=0x400000
+} font_flags_dummy_t;
+
+#define CONTROL(id, n_op, flags) ((flags) | (n_op) | ((k_##id) << 8))
+
+typedef enum {
+ k_0, k_1, k_2, k_7, k_50, k_neg_100, k_8720, k_0_039625, k_0_06, k_false, k_box,
+ k_matrix, k_matrix_1, k_emptydict
+} font_defaults_dummy_t;
+
+typedef struct tag_font_defaults {
+ unsigned short key;
+ unsigned short value;
+} font_defaults_t;
+
+static const font_defaults_t fontinfo_font_defaults[] = {
+ { k_isFixedPitch, k_false },
+ { k_ItalicAngle, k_0 },
+ { k_UnderlinePosition, k_neg_100 },
+ { k_UnderlineThickness, k_50 }
+};
+
+static const font_defaults_t simple_font_defaults[] = {
+ { k_PaintType, k_0 },
+ { k_CharstringType, k_2 },
+ { k_FontMatrix, k_matrix }, /* 0.001 0 0 0.001 0 0 */
+ { k_FontBBox, k_box }, /* 0 0 0 0 */
+ { k_StrokeWidth, k_0 }
+};
+
+static const font_defaults_t cid_font_defaults[] = {
+ { k_CIDFontVersion, k_0 },
+ { k_CIDFontRevision, k_0 },
+ { k_CIDFontType, k_0 },
+ { k_CIDCount, k_8720 }
+};
+
+static const font_defaults_t fd_font_defaults[] = {
+ { k_FontMatrix, k_matrix }, /* 0.001 0 0 0.001 0 0 */
+ { k_PaintType, k_0 }, /* gs needs this */
+ { k_FontType, k_2 }, /* gs needs this */
+ { k_Private, k_emptydict} /* following gs implementation */
+};
+
+static const font_defaults_t set_unit_matrix[] = {
+ { k_FontMatrix, k_matrix_1 } /* 1 0 0 1 0 0 */
+};
+
+static const font_defaults_t private_font_defaults[] = {
+ { k_BlueScale, k_0_039625 },
+ { k_BlueShift, k_7 },
+ { k_BlueFuzz, k_1 },
+ { k_ForceBold, k_false },
+ { k_LanguageGroup, k_0 },
+ { k_ExpansionFactor, k_0_06 },
+ { k_initialRandomSeed, k_0 },
+ { k_defaultWidthX, k_0 },
+ { k_nominalWidthX, k_0 }
+};
+
+static int
+set_defaults(i_ctx_t *i_ctx_p, ref *dest, const font_defaults_t *def, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ ref name, *dummy, value;
+ int code;
+ float xx;
+
+ if ((code = name_ref(imemory, (const unsigned char *)font_keys[def[i].key],
+ font_keys_sz[def[i].key], &name, 0)) < 0)
+ return code;
+ if (dict_find(dest, &name, &dummy) <= 0) {
+ switch (def[i].value) {
+ default:
+ case k_0:
+ make_int(&value, 0);
+ break;
+ case k_1:
+ make_int(&value, 1);
+ break;
+ case k_2:
+ make_int(&value, 2);
+ break;
+ case k_7:
+ make_int(&value, 7);
+ break;
+ case k_50:
+ make_int(&value, 50);
+ break;
+ case k_neg_100:
+ make_int(&value, -100);
+ break;
+ case k_8720:
+ make_int(&value, 8720);
+ break;
+ case k_0_039625:
+ make_real(&value, (float)0.039625);
+ break;
+ case k_0_06:
+ make_real(&value, (float)0.06);
+ break;
+ case k_false:
+ make_bool(&value, 0);
+ break;
+ case k_box:
+ if ((code = ialloc_ref_array(&value, a_readonly, 4, "parsecff.default_bbox")) < 0)
+ return code;
+ make_int(&value.value.refs[0], 0);
+ value.value.refs[1] = value.value.refs[2] = value.value.refs[3] = value.value.refs[0];
+ break;
+ case k_matrix:
+ xx = (float)0.001;
+ goto make_matrix;
+ case k_matrix_1:
+ xx = (float)1.;
+ make_matrix:;
+ if ((code = ialloc_ref_array(&value, a_readonly, 6, "parsecff.default_bbox")) < 0)
+ return code;
+ make_real(&value.value.refs[0], xx);
+ value.value.refs[3] = value.value.refs[0];
+ make_real(&value.value.refs[1], (float)0.);
+ value.value.refs[2] = value.value.refs[4] = value.value.refs[5] = value.value.refs[1];
+ break;
+ case k_emptydict:
+ if ((code = dict_create(0, &value)) < 0)
+ return code;
+ }
+ if ((code = idict_put(dest, &name, &value)) < 0)
+ return code;
+ }
+ }
+ return 0;
+}
+
+typedef struct tag_font_offsets_t {
+ unsigned int fdarray_off;
+ unsigned int fdselect_off;
+ unsigned int charset_off;
+ unsigned int encoding_off;
+ unsigned int private_off;
+ unsigned int charstrings_off;
+ unsigned int local_subrs_off;
+ unsigned int private_size;
+ bool have_ros;
+} font_offsets_t;
+
+static int
+parse_dict(i_ctx_t *i_ctx_p, ref *topdict, font_offsets_t *offsets,
+ const cff_index_t *strings, const cff_data_t *data,
+ const unsigned p0, const unsigned pe)
+{
+ ref *fontinfodict = 0, *privatedict = 0, arg, ops[MAXOP];
+ int op_i = 0;
+
+ unsigned c;
+ unsigned p = p0;
+ unsigned control;
+ int code;
+
+ while(p < pe) {
+ if ((code = card8(&c, data, p++, pe)) < 0)
+ return code;
+ switch(c) {
+ case 0: /* version 0 SID -, FontInfo */
+ control = CONTROL(version, 1, k_fontinfodict | k_sid);
+ break;
+ case 1: /* Notice 1 SID -, FontInfo */
+ control = CONTROL(Notice, 1, k_fontinfodict | k_sid);
+ break;
+ case 2: /* FullName 2 SID -, FontInfo */
+ control = CONTROL(FullName, 1, k_fontinfodict | k_sid);
+ break;
+ case 3: /* FamilyName 3 SID -, FontInfo */
+ control = CONTROL(FamilyName, 1, k_fontinfodict | k_sid);
+ break;
+ case 4: /* Weight 4 SID -, FontInfo */
+ control = CONTROL(FamilyName, 1, k_fontinfodict | k_sid);
+ break;
+ case 5: /* FontBBox 5 array 0 0 0 0 */
+ control = CONTROL(FontBBox, 4, k_topdict | k_array);
+ break;
+ case 6: /* BlueValues 6 delta -, Private */
+ control = CONTROL(BlueValues, 0, k_privatedict | k_array | k_delta);
+ break;
+ case 7: /* OtherBlues 7 delta -, Private */
+ control = CONTROL(OtherBlues, 0, k_privatedict | k_array | k_delta);
+ break;
+ case 8: /* FamilyBlues 8 delta -, Private */
+ control = CONTROL(FamilyBlues, 0, k_privatedict | k_array | k_delta);
+ break;
+ case 9: /* FamilyOtherBlues 9 delta -, Private */
+ control = CONTROL(FamilyOtherBlues, 0, k_privatedict | k_array | k_delta);
+ break;
+ case 10: /* StdHW 10 number -, Private */ /* converts to array */
+ control = CONTROL(StdHW, 1, k_privatedict | k_array);
+ break;
+ case 11: /* StdVW 11 number -, Private */ /* converts to array */
+ control = CONTROL(StdVW, 1, k_privatedict | k_array);
+ break;
+ case 12:
+ if (p >= pe)
+ return_error(gs_error_rangecheck);
+ if ((code = card8(&c, data, p++, pe)) < 0)
+ return code;
+ switch(c) {
+ case 0: /* Copyright 12 0 SID -, FontInfo */
+ control = CONTROL(Copyright, 1, k_fontinfodict | k_sid);
+ break;
+ case 1: /* isFixedPitch 12 1 boolean 0 (false), FontInfo */
+ control = CONTROL(isFixedPitch, 1, k_fontinfodict | k_int | k_bool);
+ break;
+ case 2: /* ItalicAngle 12 2 number 0, FontInfo */
+ control = CONTROL(ItalicAngle, 1, k_fontinfodict);
+ break;
+ case 3: /* UnderlinePosition 12 3 number -100, FontInfo */
+ control = CONTROL(UnderlinePosition, 1, k_fontinfodict);
+ break;
+ case 4: /* UnderlineThickness 12 4 number 50, FontInfo */
+ control = CONTROL(UnderlineThickness, 1, k_fontinfodict);
+ break;
+ case 5: /* PaintType 12 5 number 0 */
+ control = CONTROL(PaintType, 1, k_fontinfodict);
+ break;
+ case 6: /* CharstringType 12 6 number 2 */
+ control = CONTROL(CharstringType, 1, k_fontinfodict | k_int);
+ break;
+ case 7: /* FontMatrix 12 7 array 0.001 0 0 0.001 0 0 */
+ control = CONTROL(FontMatrix, 6, k_topdict | k_array);
+ break;
+ case 8: /* StrokeWidth 12 8 number 0 */
+ control = CONTROL(StrokeWidth, 1, k_topdict);
+ break;
+ case 9: /* BlueScale 12 9 number 0.039625, Private */
+ control = CONTROL(BlueScale, 1, k_privatedict);
+ break;
+ case 10: /* BlueShift 12 10 number 7, Private */
+ control = CONTROL(BlueShift, 1, k_privatedict);
+ break;
+ case 11: /* BlueFuzz 12 11 number 1, Private */
+ control = CONTROL(BlueFuzz, 1, k_privatedict);
+ break;
+ case 12: /* StemSnapH 12 12 delta -, Private */
+ control = CONTROL(StemSnapH, 0, k_privatedict | k_array | k_delta);
+ break;
+ case 13: /* StemSnapV 12 13 delta -, Private */
+ control = CONTROL(StemSnapV, 0, k_privatedict | k_array | k_delta);
+ break;
+ case 14: /* ForceBold 12 14 boolean false, Private */
+ control = CONTROL(ForceBold, 1, k_privatedict | k_int | k_bool);
+ break;
+ case 17: /* LanguageGroup 12 17 number 0, Private */
+ control = CONTROL(LanguageGroup, 1, k_privatedict | k_int);
+ break;
+ case 18: /* ExpansionFactor 12 18 number 0.06, Private */
+ control = CONTROL(ExpansionFactor, 1, k_privatedict);
+ break;
+ case 19: /* initialRandomSeed 12 19 number 0, Private */
+ control = CONTROL(initialRandomSeed, 1, k_privatedict | k_int);
+ break;
+ case 20: /* SyntheticBase 12 20 number -, synthetic base font index */
+ control = CONTROL(SyntheticBase, 1, k_privatedict | k_int);
+ break;
+ case 21: /* PostScript 12 21 SID -, embedded PostScript language code */
+ control = CONTROL(PostScript, 1, k_topdict | k_int | k_sid);
+ break;
+ case 22: /* BaseFontName 12 22 SID -, (added as needed by Adobe-based technology) */
+ control = CONTROL(BaseFontName, 1, k_topdict | k_int | k_sid);
+ break;
+ case 23: /* BaseFontBlend 12 23 delta -, (added as needed by Adobe-based technology) */
+ control = CONTROL(BaseFontBlend, 0, k_topdict | k_array | k_delta);
+ break;
+
+ /* CIDFont */
+ case 30: {/* ROS 12 30 SID SID number -, Registry Ordering Supplement */
+ ref ros;
+
+ if ((op_i -= 3) < 0)
+ return_error(gs_error_stackunderflow);
+ if ((code = dict_create(3, &ros)) < 0)
+ return code;
+ check_type(ops[op_i], t_integer);
+ if ((code = make_string_from_sid(i_ctx_p, &arg, strings, data, ops[op_i].value.intval)) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, &ros, STR2MEM("Registry"), &arg)) < 0)
+ return code;
+ if ((code = make_string_from_sid(i_ctx_p, &arg, strings, data, ops[op_i + 1].value.intval)) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, &ros, STR2MEM("Ordering"), &arg)) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, &ros, STR2MEM("Supplement"), &ops[op_i + 2])) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, topdict, STR2MEM("CIDSystemInfo"), &ros)) < 0)
+ return code;
+ offsets->have_ros = true;
+ }
+ continue;
+ case 31: /* CIDFontVersion 12 31 number 0 */
+ control = CONTROL(CIDFontVersion, 1, k_topdict);
+ break;
+ case 32: /* CIDFontRevision 12 32 number 0 */
+ control = CONTROL(CIDFontRevision, 1, k_topdict);
+ break;
+ case 33: /* CIDFontType 12 33 number 0 */
+ control = CONTROL(CIDFontType, 1, k_topdict | k_int);
+ break;
+ case 34: /* CIDCount 12 34 number 8720 */
+ control = CONTROL(CIDCount, 1, k_topdict | k_int);
+ break;
+ case 35: /* UIDBase 12 35 number - */
+ control = CONTROL(UIDBase, 1, k_topdict | k_int);
+ break;
+ case 36: /* FDArray 12 36 number -, Font DICT (FD) INDEX offset (0) */
+ control = 0;
+ if (--op_i < 0)
+ return_error(gs_error_stackunderflow);
+ check_type(ops[op_i], t_integer);
+ offsets->fdarray_off = ops[op_i].value.intval;
+ continue;
+ case 37: /* FDSelect 12 37 number -, FDSelect offset (0) */
+ control = 0;
+ if (--op_i < 0)
+ return_error(gs_error_stackunderflow);
+ check_type(ops[op_i], t_integer);
+ offsets->fdselect_off = ops[op_i].value.intval;
+ continue;
+ case 38: /* FontName 12 38 SID -, FD FontName */
+ control = CONTROL(FontName, 1, k_topdict | k_int | k_sid);
+ break;
+ default:
+ continue;
+ }
+ break;
+ case 13: /* UniqueID 13 number - */
+ control = CONTROL(UniqueID, 1, k_topdict | k_int);
+ break;
+ case 14: /* XUID 14 array - */
+ control = CONTROL(XUID, 0, k_topdict | k_array);
+ break;
+ case 15: /* charset 15 number 0, charset offset (0) */
+ if (--op_i < 0)
+ return_error(gs_error_stackunderflow);
+ check_type(ops[op_i], t_integer);
+ offsets->charset_off = ops[op_i].value.intval;
+ continue;
+ case 16: /* Encoding 16 number 0, encoding offset (0) */
+ if (--op_i < 0)
+ return_error(gs_error_stackunderflow);
+ check_type(ops[op_i], t_integer);
+ offsets->encoding_off = ops[op_i].value.intval;
+ continue;
+ case 17: /* CharStrings 17 number -, CharStrings offset (0) */
+ if (--op_i < 0)
+ return_error(gs_error_stackunderflow);
+ check_type(ops[op_i], t_integer);
+ offsets->charstrings_off = ops[op_i].value.intval;
+ continue;
+ case 18: /* Private 18 number, number -, Private DICT size and offset (0)*/
+ if ((op_i -= 2) < 0)
+ return_error(gs_error_stackunderflow);
+ check_type(ops[op_i], t_integer);
+ check_type(ops[op_i + 1], t_integer);
+ offsets->private_size = ops[op_i].value.intval;
+ offsets->private_off = ops[op_i + 1].value.intval;
+ continue;
+ case 19: /* Local Subrs 19 number -, Offset (self) to local subrs */
+ if (--op_i < 0)
+ return_error(gs_error_stackunderflow);
+ check_type(ops[op_i], t_integer);
+ offsets->local_subrs_off = ops[op_i].value.intval;
+ continue;
+ case 20: /* defaultWidthX 20 number 0 */
+ control = CONTROL(defaultWidthX, 1, k_privatedict);
+ break;
+ case 21: /* nominalWidthX 21 number 0 */
+ control = CONTROL(nominalWidthX, 1, k_privatedict);
+ break;
+ case 22: case 23: case 24: case 25: case 26: case 27: /* reserved */
+ continue;
+ case 30: { /* float operand */
+ if (op_i >= MAXOP)
+ return_error(gs_error_limitcheck);
+
+ if ((code = get_float(&ops[op_i], data, p, pe)) < 0)
+ return code;
+
+ op_i++;
+ p += code;
+ }
+ continue;
+ case 31: case 255: /* reserved */
+ continue;
+ default:
+ { int n; /* int operand */
+
+ if (op_i >= MAXOP)
+ return_error(gs_error_limitcheck);
+ if ((code = get_int(&n, data, p - 1, pe)) < 0)
+ return code;
+ make_int(&ops[op_i], n);
+ op_i++;
+ p += code - 1;
+ }
+ continue;
+ }
+ {
+ unsigned int n_op = control & 255;
+ unsigned int string_id = (control >> 8) & 255;
+ ref arg, *dict;
+
+ if (n_op == 0)
+ n_op = op_i;
+ else if (op_i < n_op)
+ return_error(gs_error_stackunderflow);
+ if (control & k_delta)
+ undelta(ops + op_i - n_op, n_op);
+ op_i -= n_op;
+ if (control & (k_sid | k_int | k_bool)) {
+ if (!r_has_type(&ops[op_i], t_integer))
+ return_error(gs_error_typecheck);
+ }
+ if (control & k_bool)
+ make_bool(&arg, !!ops[op_i].value.intval);
+ if (control & k_sid) {
+ if ((code = make_string_from_sid(i_ctx_p, &arg, strings, data, ops[op_i].value.intval)) < 0)
+ return code;
+ }
+ if (control & k_array) {
+ if ((code = ialloc_ref_array(&arg, a_readonly, n_op, "parsecff.array")) < 0)
+ return code;
+ memcpy(arg.value.refs, ops + op_i, n_op*sizeof(ref));
+ }
+ if (control & k_privatedict) {
+ if ((code = find_font_dict(i_ctx_p, topdict, &privatedict, "Private")) < 0)
+ return code;
+ dict = privatedict;
+ } else if (control & k_fontinfodict) {
+ if ((code = find_font_dict(i_ctx_p, topdict, &fontinfodict, "FontInfo")) < 0)
+ return code;
+ dict = fontinfodict;
+ } else
+ dict = topdict;
+ code = idict_put_c_name(i_ctx_p, dict, font_keys[string_id], font_keys_sz[string_id],
+ ((control & (k_bool | k_sid | k_array)) == 0 ? &ops[op_i] : &arg));
+ if (code < 0)
+ return code;
+ }
+ }
+ return 0;
+}
+
+static int
+parse_font(i_ctx_t *i_ctx_p, ref *topdict,
+ const cff_index_t *strings, const ref *global_subrs,
+ const cff_data_t *data,
+ const unsigned p_top, const unsigned pe_top,
+ const unsigned p_all, const unsigned pe_all,
+ const bool force_cidfont)
+{
+ int code = 0;
+ ref *privatedict = 0, *fontinfodict = 0;
+ cff_index_t local_subrs, charstrings_index;
+ int (*peek_charset_proc)(const cff_data_t *, unsigned p, unsigned pe, unsigned i);
+ font_offsets_t offsets;
+
+ memset(&offsets, 0, sizeof(offsets)); /* set defaults for charset_off, encoding_off */
+
+ if ((code = parse_dict(i_ctx_p, topdict, &offsets, strings, data, p_top, pe_top) < 0))
+ return code;
+ if ((code = parse_dict(i_ctx_p, topdict, &offsets, strings, data,
+ p_all + offsets.private_off,
+ p_all + offsets.private_off + offsets.private_size) < 0))
+ return code;
+ if ((code = parse_index(&local_subrs, data,
+ (offsets.local_subrs_off ? p_all + offsets.private_off + offsets.local_subrs_off : 0),
+ pe_all)) < 0)
+ return code;
+
+ if ((code = find_font_dict(i_ctx_p, topdict, &privatedict, "Private")) < 0)
+ return code;
+ if ((code = find_font_dict(i_ctx_p, topdict, &fontinfodict, "FontInfo")) < 0)
+ return code;
+ if (local_subrs.count) {
+ ref r;
+
+ if (( code = make_stringarray_from_index(i_ctx_p, &r, &local_subrs, data)) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, privatedict, STR2MEM("Subrs"), &r)) < 0)
+ return code;
+ }
+ if (global_subrs && !offsets.have_ros) {
+ if ((code = idict_put_c_name(i_ctx_p, privatedict, STR2MEM("GlobalSubrs"), global_subrs)) < 0)
+ return code;
+ }
+ if (offsets.charstrings_off <= 0)
+ return_error(gs_error_invalidfont);
+ if ((code = parse_index(&charstrings_index, data, p_all + offsets.charstrings_off, pe_all)) < 0)
+ return code;
+
+ switch (offsets.charset_off) {
+ case 0:
+ peek_charset_proc = iso_adobe_charset_proc;
+ break;
+ case 1:
+ peek_charset_proc = expert_charset_proc;
+ break;
+ case 2:
+ peek_charset_proc = expert_subset_charset_proc;
+ break;
+ default: {
+ unsigned u;
+
+ if ((code = card8(&u, data, p_all + offsets.charset_off, pe_all)) < 0)
+ return code;
+ switch (u) {
+ case 0:
+ peek_charset_proc = format0_charset_proc;
+ break;
+ case 1:
+ peek_charset_proc = format1_charset_proc;
+ break;
+ case 2:
+ peek_charset_proc = format2_charset_proc;
+ break;
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ }
+ }
+
+ if (offsets.have_ros) { /* CIDFont */
+ unsigned int i;
+ cff_index_t fdarray;
+ ref int_ref, cstr_ref, fdarray_ref, glyph_directory_ref;
+ unsigned int fdselect_off = offsets.fdselect_off;
+ unsigned int fdselect_code;
+ int (*fdselect_proc)(const cff_data_t *, unsigned p, unsigned pe, unsigned int i);
+ ref *array_FontMatrix, name_FontMatrix;
+ bool top_has_FontMatrix;
+
+ if (offsets.fdarray_off == 0 || fdselect_off == 0)
+ return_error(gs_error_invalidfont);
+ if ((code = parse_index(&fdarray, data, p_all + offsets.fdarray_off, pe_all)) < 0)
+ return code;
+ if ((code = ialloc_ref_array(&fdarray_ref, a_readonly, fdarray.count, "parsecff.fdarray")) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, topdict, STR2MEM("FDArray"), &fdarray_ref)) < 0)
+ return code;
+ if ((code = dict_create(charstrings_index.count + 1, &glyph_directory_ref)) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, topdict, STR2MEM("GlyphDirectory"), &glyph_directory_ref)) < 0)
+ return code;
+ if ((code = name_ref(imemory, (const unsigned char *)font_keys[k_FontMatrix], font_keys_sz[k_FontMatrix], &name_FontMatrix, 0)) < 0)
+ return code;
+ top_has_FontMatrix = dict_find(topdict, &name_FontMatrix, &array_FontMatrix) > 0;
+
+ for (i = 0; i < fdarray.count; i++) {
+ unsigned int len;
+ unsigned doff;
+ cff_index_t fd_subrs;
+ ref *fdprivate = 0;
+ ref *fdfont = &fdarray_ref.value.refs[i];
+
+ offsets.local_subrs_off = offsets.private_off = 0;
+ if ((code = peek_index(&doff, &len, &fdarray, data, i)) < 0)
+ return code;
+ if ((code = dict_create(5, fdfont)) < 0)
+ return code;
+ if ((code = parse_dict(i_ctx_p, fdfont, &offsets, strings, data, doff, doff + len) < 0))
+ return code;
+ if ((code = parse_dict(i_ctx_p, fdfont, &offsets, strings, data,
+ p_all + offsets.private_off, p_all + offsets.private_off + offsets.private_size) < 0))
+ return code;
+ if ((code = find_font_dict(i_ctx_p, fdfont, &fdprivate, "Private")) < 0)
+ return code;
+ if (offsets.local_subrs_off) {
+ if ((code = parse_index(&fd_subrs, data,
+ p_all + offsets.private_off + offsets.local_subrs_off, pe_all)) < 0)
+ return code;
+ if (fd_subrs.count) {
+ ref r;
+
+ if (( code = make_stringarray_from_index(i_ctx_p, &r, &fd_subrs, data)) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, fdprivate, STR2MEM("Subrs"), &r)) < 0)
+ return code;
+ }
+ }
+ if (global_subrs) {
+ if ((code = idict_put_c_name(i_ctx_p, fdprivate, STR2MEM("GlobalSubrs"), global_subrs)) < 0)
+ return code;
+ }
+ /* There is no explicit description what to do with FontMatrix of CFF CIDFont in
+ * Adobe tech note #5176 (CFF Spec), Ken and Masaki have figured out this is what
+ * it should be:
+ *
+ * 1) If both Top DICT and Font DICT does _not_ have FontMatrix, then Top DICT =
+ * [0.001 0 0 0.001 0 0], Font DICT= [1 0 0 1 0 0]. (Or, Top DICT = (absent),
+ * Font DICT = [0.001 0 0 0.001 0 0] then let '/CIDFont defineresource'
+ * make Top DICT = [0.001 0 0 0.001 0 0], Font DICT = [1 0 0 1 0 0].)
+ *
+ * 2) If Top DICT has FontMatrix and Font DICT doesn't, then Top DICT = (supplied
+ * matrix), Font DICT = [1 0 0 1 0 0].
+ *
+ * 3) If Top DICT does not have FontMatrix but Font DICT does, then Top DICT = [1
+ * 0 0 1 0 0], Font DICT = (supplied matrix). (Or, Top DICT = (absent),
+ * Font DICT = (supplied matrix) then let '/CIDFont defineresource'
+ * make Top DICT = [0.001 0 0 0.001 0 0], Font DICT = (supplied matrix 1000 times
+ * larger).)
+ *
+ * 4) If both Top DICT and Font DICT _does_ have FontMatrix, then Top DICT =
+ * (supplied matrix), Font DICT = (supplied matrix).
+ */
+ if (top_has_FontMatrix) {
+ if (dict_find(fdfont, &name_FontMatrix, &array_FontMatrix) <= 0) {
+ if ((code = set_defaults(i_ctx_p, fdfont, set_unit_matrix, count_of(set_unit_matrix))) < 0)
+ return code;
+ }
+ }
+ if ((code = set_defaults(i_ctx_p, fdfont, fd_font_defaults, count_of(fd_font_defaults))) < 0)
+ return code;
+ }
+
+ if ((code = card8(&fdselect_code, data, p_all + fdselect_off, pe_all)) < 0)
+ return code;
+ if (fdselect_code == 0)
+ fdselect_proc = format0_fdselect_proc;
+ else if (fdselect_code == 3)
+ fdselect_proc = format3_fdselect_proc;
+ else
+ return_error(gs_error_rangecheck);
+
+ make_int(&int_ref, 0);
+ for (i = 0; i < charstrings_index.count; i++) {
+ int fd;
+
+ if (fdarray.count <= 1)
+ fd = -1;
+ else {
+ fd = (*fdselect_proc)(data, p_all + fdselect_off + 1, pe_all, i);
+ if (fd < 0)
+ return fd;
+ }
+ if ((code = make_string_from_index(i_ctx_p, &cstr_ref, &charstrings_index, data, i, fd)) < 0)
+ return code;
+ if (i > 0)
+ int_ref.value.intval = (*peek_charset_proc)(data, p_all + offsets.charset_off + 1, pe_all, i - 1);
+ if ( int_ref.value.intval < 0)
+ return int_ref.value.intval;
+ if ((code = idict_put(&glyph_directory_ref, &int_ref, &cstr_ref)) < 0)
+ return code;
+ }
+ int_ref.value.intval = fdarray.count > 1;
+ if ((code = idict_put_c_name(i_ctx_p, topdict, STR2MEM("FDBytes"), &int_ref)) < 0)
+ return code;
+ if ((code = set_defaults(i_ctx_p, topdict, cid_font_defaults, count_of(cid_font_defaults))) < 0)
+ return code;
+ } else if (force_cidfont) {
+ /* Convert simple font to CIDFont */
+ ref int_ref, str_ref, glyph_directory_ref, fdarray_ref, cidsysteminfo_ref;
+
+ if ((code = idict_undef_c_name(i_ctx_p, topdict, STR2MEM("UniqueID"))) < 0)
+ return code;
+ if ((code = idict_undef_c_name(i_ctx_p, topdict, STR2MEM("XUID"))) < 0)
+ return code;
+ if ((code = ialloc_ref_array(&fdarray_ref, a_readonly, 1, "force_cid.fdarray")) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, topdict, STR2MEM("FDArray"), &fdarray_ref)) < 0)
+ return code;
+ if ((code = dict_create(4, &fdarray_ref.value.refs[0])) < 0)
+ return code;
+ if ((code = idict_move_c_name(i_ctx_p, &fdarray_ref.value.refs[0], topdict, STR2MEM("FontMatrix"))) < 0)
+ return code;
+ if ((code = idict_move_c_name(i_ctx_p, &fdarray_ref.value.refs[0], topdict, STR2MEM("Private"))) < 0)
+ return code;
+ if ((code = idict_move_c_name(i_ctx_p, &fdarray_ref.value.refs[0], topdict, STR2MEM("FontType"))) < 0)
+ return code;
+ if ((code = idict_move_c_name(i_ctx_p, &fdarray_ref.value.refs[0], topdict, STR2MEM("PaintType"))) < 0)
+ return code;
+ if ((code = set_defaults(i_ctx_p, &fdarray_ref.value.refs[0], fd_font_defaults, count_of(fd_font_defaults))) < 0)
+ return code;
+ if ((code = dict_create(3, &cidsysteminfo_ref)) < 0)
+ return code;
+ make_string(&str_ref, avm_foreign | a_readonly, 5, (unsigned char *)"Adobe");
+ if ((code = idict_put_c_name(i_ctx_p, &cidsysteminfo_ref, STR2MEM("Registry"), &str_ref)) < 0)
+ return code;
+ make_string(&str_ref, avm_foreign | a_readonly, 8, (unsigned char *)"Identity");
+ if ((code = idict_put_c_name(i_ctx_p, &cidsysteminfo_ref, STR2MEM("Ordering"), &str_ref)) < 0)
+ return code;
+ make_int(&int_ref, 0);
+ if ((code = idict_put_c_name(i_ctx_p, &cidsysteminfo_ref, STR2MEM("Supplement"), &int_ref)) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, topdict, STR2MEM("CIDSystemInfo"), &cidsysteminfo_ref)) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, topdict, STR2MEM("FDBytes"), &int_ref)) < 0)
+ return code;
+
+ if ((code = dict_create(charstrings_index.count + 1, &glyph_directory_ref)) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, topdict, STR2MEM("GlyphDirectory"), &glyph_directory_ref)) < 0)
+ return code;
+ make_int(&int_ref, 0);
+ for ( ; int_ref.value.intval < (int)charstrings_index.count; int_ref.value.intval++) {
+ ref cstr_ref;
+
+ if ((code = make_string_from_index(i_ctx_p, &cstr_ref, &charstrings_index, data, int_ref.value.intval, -1)) < 0)
+ return code;
+ if ((code = idict_put(&glyph_directory_ref, &int_ref, &cstr_ref)) < 0)
+ return code;
+ }
+ make_int(&int_ref, charstrings_index.count);
+ if ((code = idict_put_c_name(i_ctx_p, topdict, STR2MEM("CIDCount"), &int_ref)) < 0)
+ return code;
+ if ((code = set_defaults(i_ctx_p, topdict, cid_font_defaults, count_of(cid_font_defaults))) < 0)
+ return code;
+ } else {
+ /* Simple font */
+ unsigned int i, gid, enc_format;
+ int sid;
+ ref name, cstr, charstrings_dict, encoding, notdef;
+ unsigned char gid2char[256];
+ unsigned supp_enc_offset;
+
+ if ((code = name_ref(imemory, (unsigned char *)".notdef", 7, &notdef, 0)) < 0)
+ return code;
+ if ((code = make_string_from_index(i_ctx_p, &cstr, &charstrings_index, data, 0, -1)) < 0)
+ return code;
+ if ((code = dict_create(charstrings_index.count + 1, &charstrings_dict)) < 0)
+ return code;
+ if ((code = idict_put_c_name(i_ctx_p, topdict, STR2MEM("CharStrings"), &charstrings_dict)) < 0)
+ return code;
+ if ((code = idict_put(&charstrings_dict, &notdef, &cstr)) < 0)
+ return code;
+ if (offsets.encoding_off <= 1) {
+ if ((code = ialloc_ref_array(&encoding, a_readonly, 256, "cff_parser.encoding")) < 0)
+ return code;
+ for (i = 0; i < 256; i++) {
+ if ((code = make_name_from_sid(i_ctx_p, &encoding.value.refs[i],
+ strings, data, (offsets.encoding_off ? expert_encoding : standard_encoding)[i])) < 0)
+ return code;
+ }
+ } else {
+ if ((code = ialloc_ref_array(&encoding, a_readonly, 256, "cff_parser.encoding")) < 0)
+ return code;
+ for (i = 0; i < 256; i++)
+ encoding.value.refs[i] = notdef;
+ if ((code = card8(&enc_format, data, p_all + offsets.encoding_off, pe_all)) < 0)
+ return code;
+ if ((enc_format & 0x7f) == 0) {
+ unsigned int n_codes;
+
+ if ((code = card8(&n_codes, data, p_all + offsets.encoding_off + 1, pe_all)) < 0)
+ return code;
+ gid2char[0] = 0;
+ for (i = 0; i < n_codes; i++) {
+ unsigned int charcode;
+
+ if ((code = card8(&charcode, data, p_all + offsets.encoding_off + 2 + i, pe_all)) < 0)
+ return code;
+ gid2char[i + 1] = charcode;
+ }
+ memset(gid2char + n_codes + 1, 0, sizeof(gid2char) - n_codes - 1);
+ supp_enc_offset = p_all + offsets.encoding_off + 2 + n_codes;
+ } else if ((enc_format & 0x7f) == 1) {
+ unsigned int n_ranges, first, left, j, k = 1;
+
+ if ((code = card8(&n_ranges, data, p_all + offsets.encoding_off + 1, pe_all)) < 0)
+ return code;
+ gid2char[0] = 0;
+ for (i = 0; i < n_ranges; i++) {
+ if ((code = card8(&first, data, p_all + offsets.encoding_off + 2 + 2*i, pe_all)) < 0)
+ return code;
+ if ((code = card8(&left, data, p_all + offsets.encoding_off + 3 + 2*i, pe_all)) < 0)
+ return code;
+ for (j = 0; j <= left && k < 256; j++)
+ gid2char[k++] = first + j;
+ }
+ memset(gid2char + k, 0, sizeof(gid2char) - k);
+ supp_enc_offset = p_all + offsets.encoding_off + 2*n_ranges + 2;
+ } else
+ return_error(gs_error_rangecheck);
+ }
+ if ((code = idict_put_c_name(i_ctx_p, topdict, STR2MEM("Encoding"), &encoding)) < 0)
+ return code;
+ for (gid = 1; gid < charstrings_index.count; gid++) {
+ if ((code = make_string_from_index(i_ctx_p, &cstr, &charstrings_index, data, gid, -1)) < 0)
+ return code;
+ sid = (*peek_charset_proc)(data, p_all + offsets.charset_off + 1, pe_all, gid - 1);
+ if (sid < 0)
+ return sid;
+ if ((code = make_name_from_sid(i_ctx_p, &name, strings, data, sid)) < 0) {
+ char buf[40];
+ int len = gs_sprintf(buf, "sid-%d", sid);
+
+ if ((code = name_ref(imemory, (unsigned char *)buf, len, &name, 1)) < 0)
+ return code;
+ }
+ if ((code = idict_put(&charstrings_dict, &name, &cstr)) < 0)
+ return code;
+ if (offsets.encoding_off > 1 && gid < 256) {
+ encoding.value.refs[gid2char[gid]] = name;
+ }
+ }
+ if (offsets.encoding_off > 1 && (enc_format & 0x80)) {
+ unsigned int n_supp, charcode, sid;
+
+ if ((code = card8(&n_supp, data, supp_enc_offset, pe_all)) < 0)
+ return code;
+ for (i = 0; i < n_supp; i++) {
+ if ((code = card8(&charcode, data, supp_enc_offset + 1 + 3*i, pe_all)) < 0)
+ return code;
+ if ((code = card16(&sid, data, supp_enc_offset + 2 + 3*i, pe_all)) < 0)
+ return code;
+ if ((code = make_name_from_sid(i_ctx_p, &encoding.value.refs[charcode], strings, data, sid)) < 0)
+ return code;
+ }
+ }
+ if ((code = set_defaults(i_ctx_p, fontinfodict, fontinfo_font_defaults, count_of(fontinfo_font_defaults))) < 0)
+ return code;
+ if ((code = set_defaults(i_ctx_p, privatedict, private_font_defaults, count_of(private_font_defaults))) < 0)
+ return code;
+ if ((code = set_defaults(i_ctx_p, topdict, simple_font_defaults, count_of(simple_font_defaults))) < 0)
+ return code;
+ }
+ return 0;
+}
+
+/* <force_cidfont> <string> .parsecff <dict> */
+/* Parse CFF font stored in a single string */
+static int
+zparsecff(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ unsigned p, pe;
+ unsigned int hdr_ver, hdr_size, off_size;
+ cff_index_t names, topdicts, strings, gsubrs;
+ int code;
+ unsigned int i_font;
+ ref gsubrs_array, fontset;
+ bool force_cidfont;
+ cff_data_t data;
+ ref blk_wrap[1];
+
+ check_read(*op);
+ if (r_has_type(op, t_array)) { /* no packedarrays */
+ int i, blk_sz, blk_cnt;
+
+ data.blk_ref = op->value.refs;
+ blk_cnt = r_size(op);
+ blk_sz = r_size(data.blk_ref);
+ data.length = 0;
+ for (i = 0; i < blk_cnt; i++) {
+ int len;
+
+ check_read_type(data.blk_ref[i], t_string);
+ len = r_size(&data.blk_ref[i]);
+ if (len > blk_sz || (len < blk_sz && i < blk_cnt - 1))
+ return_error(gs_error_rangecheck); /* last block can be smaller */
+ data.length += len;
+ }
+ if (data.length == 0)
+ return_error(gs_error_rangecheck);
+
+ if (blk_cnt == 1) {
+ data.mask = 0xffff; /* stub */
+ data.shift = 16; /* stub */
+ } else {
+ static const int mod2shift[] = {
+ 0, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4, 7, 17,
+ 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5, 20, 8, 19, 18
+ };
+ data.mask = blk_sz - 1;
+ if (data.mask & blk_sz)
+ return_error(gs_error_rangecheck); /* not a power of 2 */
+ data.shift = mod2shift[blk_sz % 37];
+ }
+ } else if (r_has_type(op, t_string)) {
+ data.blk_ref = blk_wrap;
+ blk_wrap[0] = *op;
+ data.length = r_size(op);
+ data.mask = 0xffff; /* stub */
+ data.shift = 16; /* stub */
+ } else
+ return_error(gs_error_typecheck);
+
+ check_type(op[-1], t_boolean);
+ force_cidfont = op[-1].value.boolval;
+ p = 0;
+ pe = p + data.length;
+
+ if (pe < 8) /* arbitrary, to avoid underflow in pe - 4 */
+ return_error(gs_error_rangecheck);
+ if ((code = card8(&hdr_ver, &data, p, pe)) < 0)
+ return code;
+ if (hdr_ver != 1)
+ return_error(gs_error_rangecheck); /* Unsupported version. */
+ if ((code = card8(&hdr_size, &data, p + 2, pe)) < 0)
+ return code;
+ if ((code = card8(&off_size, &data, p + 3, pe)) < 0)
+ return code;
+
+ if ((code = parse_index(&names, &data, p + hdr_size, pe)) < 0)
+ return code;
+ if ((code = parse_index(&topdicts, &data, names.end, pe)) < 0)
+ return code;
+ if ((code = parse_index(&strings, &data, topdicts.end, pe)) < 0)
+ return code;
+ if ((code = parse_index(&gsubrs, &data, strings.end, pe)) < 0)
+ return code;
+ if (gsubrs.count) {
+ if (( code = make_stringarray_from_index(i_ctx_p, &gsubrs_array, &gsubrs, &data)) < 0)
+ return code;
+ }
+ if (names.count >= 65535)
+ return_error(gs_error_limitcheck);
+ if ((code = dict_create(names.count, &fontset)) < 0)
+ return code;
+
+ for (i_font = 0; i_font < names.count; i_font++) {
+ unsigned int name_data;
+ unsigned int name_len;
+ ref name;
+ unsigned int topdict_data;
+ unsigned int topdict_len;
+ ref topdict;
+ unsigned char buf[200];
+
+ if ((code = peek_index(&name_data, &name_len, &names, &data, i_font)) < 0)
+ return code;
+ if (name_len == 0) /* deleted entry */
+ continue;
+ if (name_len > sizeof(buf))
+ return_error(gs_error_limitcheck);
+ if ((code = get_cff_string(buf, &data, name_data, name_len)) < 0)
+ return code;
+ if (buf[0] == 0) /* deleted entry */
+ continue;
+ if ((code = name_ref(imemory, buf, name_len, &name, 1)) < 0)
+ return code;
+ if ((code = peek_index(&topdict_data, &topdict_len, &topdicts, &data, i_font)) < 0)
+ return code;
+
+ if ((code = dict_create(20, &topdict)) < 0)
+ return code;
+
+ if ((code = idict_put(&fontset, &name, &topdict)) < 0)
+ return code;
+
+ if ((code = parse_font(i_ctx_p, &topdict, &strings, (gsubrs.count ? &gsubrs_array : 0),
+ &data, topdict_data, topdict_data + topdict_len, p, pe, force_cidfont)) < 0)
+ return code;
+ }
+ ref_assign(op - 1, &fontset);
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfont2_op_defs[] =
+{
+ {"2.buildfont2", zbuildfont2},
+ {"2.parsecff", zparsecff},
+ op_def_end(0)
+};
diff --git a/psi/zfont32.c b/psi/zfont32.c
new file mode 100644
index 000000000..313c75d97
--- /dev/null
+++ b/psi/zfont32.c
@@ -0,0 +1,92 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Type 32 font operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gsccode.h" /* for gxfont.h */
+#include "gsmatrix.h"
+#include "gsutil.h"
+#include "gxfont.h"
+#include "gxtext.h"
+#include "bfont.h"
+#include "store.h"
+#include "ichar.h"
+
+/* The encode_char procedure of a Type 32 font should never be called. */
+static gs_glyph
+zfont_no_encode_char(gs_font *pfont, gs_char chr, gs_glyph_space_t ignored)
+{
+ return gs_no_glyph;
+}
+
+/* <string|name> <font_dict> .buildfont32 <string|name> <font> */
+/* Build a type 32 (bitmap) font. */
+static int
+zbuildfont32(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ build_proc_refs build;
+ gs_font_base *pfont;
+
+ check_type(*op, t_dictionary);
+ code = build_proc_name_refs(imemory, &build, NULL, "%Type32BuildGlyph");
+ if (code < 0)
+ return code;
+ code = build_gs_simple_font(i_ctx_p, op, &pfont, ft_CID_bitmap,
+ &st_gs_font_base, &build,
+ bf_Encoding_optional);
+ if (code < 0)
+ return code;
+ /* Always transform cached bitmaps. */
+ pfont->BitmapWidths = true;
+ pfont->ExactSize = fbit_transform_bitmaps;
+ pfont->InBetweenSize = fbit_transform_bitmaps;
+ pfont->TransformedChar = fbit_transform_bitmaps;
+ /* The encode_char procedure of a Type 32 font */
+ /* should never be called. */
+ pfont->procs.encode_char = zfont_no_encode_char;
+ return define_gs_font(i_ctx_p, (gs_font *) pfont);
+}
+
+/* - .getshowoperator <oper|null> */
+/* Get the calling operator for error reporting in %Type32BuildGlyph */
+static int
+zgetshowoperator(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_text_enum_t *osenum = op_show_find(i_ctx_p);
+
+ push(1);
+ if (osenum == NULL)
+ make_null(op);
+ else {
+ op_proc_t proc;
+ *(void **)&proc = osenum->enum_client_data;
+ make_oper(op, 0, proc);
+ }
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfont32_op_defs[] =
+{
+ {"2.buildfont32", zbuildfont32},
+ {"0.getshowoperator", zgetshowoperator},
+ op_def_end(0)
+};
diff --git a/psi/zfont42.c b/psi/zfont42.c
new file mode 100644
index 000000000..21d518f60
--- /dev/null
+++ b/psi/zfont42.c
@@ -0,0 +1,420 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Type 42 font creation operator */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsccode.h"
+#include "gsmatrix.h"
+#include "gxfont.h"
+#include "gxfont42.h"
+#include "bfont.h"
+#include "icharout.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifont42.h"
+#include "ichar1.h"
+#include "iname.h"
+#include "store.h"
+
+/* Forward references */
+static int z42_string_proc(gs_font_type42 *, ulong, uint, const byte **);
+static uint z42_get_glyph_index(gs_font_type42 *, gs_glyph);
+static int z42_gdir_get_outline(gs_font_type42 *, uint, gs_glyph_data_t *);
+static font_proc_enumerate_glyph(z42_enumerate_glyph);
+static font_proc_enumerate_glyph(z42_gdir_enumerate_glyph);
+static font_proc_encode_char(z42_encode_char);
+static font_proc_glyph_info(z42_glyph_info);
+static font_proc_glyph_outline(z42_glyph_outline);
+static font_proc_font_info(z42_font_info);
+
+/* <string|name> <font_dict> .buildfont11/42 <string|name> <font> */
+/* Build a type 11 (TrueType CID-keyed) or 42 (TrueType) font. */
+int
+build_gs_TrueType_font(i_ctx_t *i_ctx_p, os_ptr op, gs_font_type42 **ppfont,
+ font_type ftype, gs_memory_type_ptr_t pstype,
+ const char *bcstr, const char *bgstr,
+ build_font_options_t options)
+{
+ build_proc_refs build;
+ ref sfnts, GlyphDirectory;
+ gs_font_type42 *pfont;
+ font_data *pdata;
+ int code;
+
+ code = build_proc_name_refs(imemory, &build, bcstr, bgstr);
+ if (code < 0)
+ return code;
+ check_type(*op, t_dictionary);
+ /*
+ * Since build_gs_primitive_font may resize the dictionary and cause
+ * pointers to become invalid, we save sfnts and GlyphDirectory.
+ */
+ if ((code = font_string_array_param(imemory, op, "sfnts", &sfnts)) < 0 ||
+ (code = font_GlyphDirectory_param(op, &GlyphDirectory)) < 0
+ )
+ return code;
+ code = build_gs_primitive_font(i_ctx_p, op, (gs_font_base **)ppfont,
+ ftype, pstype, &build, options);
+ if (code != 0)
+ return code;
+ pfont = *ppfont;
+ pdata = pfont_data(pfont);
+ ref_assign(&pdata->u.type42.sfnts, &sfnts);
+ pdata->u.type42.mru_sfnts_index = 0;
+ pdata->u.type42.mru_sfnts_pos = 0;
+ make_null_new(&pdata->u.type42.CIDMap);
+ ref_assign(&pdata->u.type42.GlyphDirectory, &GlyphDirectory);
+ pfont->data.string_proc = z42_string_proc;
+ pfont->data.proc_data = (char *)pdata;
+ pfont->is_resource = (options & bf_has_font_file ? true : false);
+ code = gs_type42_font_init(pfont, 0);
+ if (code < 0)
+ return code;
+ pfont->procs.font_info = z42_font_info;
+ /*
+ * If the font has a GlyphDictionary, this replaces loca and glyf for
+ * accessing character outlines. In this case, we use alternate
+ * get_outline and enumerate_glyph procedures.
+ */
+ if (!r_has_type(&GlyphDirectory, t_null)) {
+ pfont->data.get_outline = z42_gdir_get_outline;
+ pfont->procs.enumerate_glyph = z42_gdir_enumerate_glyph;
+ } else
+ pfont->procs.enumerate_glyph = z42_enumerate_glyph;
+ /*
+ * The procedures that access glyph information must accept either
+ * glyph names or glyph indexes.
+ */
+ pfont->data.get_glyph_index = z42_get_glyph_index;
+ pfont->data.substitute_glyph_index_vertical = gs_type42_substitute_glyph_index_vertical;
+ pfont->procs.encode_char = z42_encode_char;
+ pfont->procs.glyph_info = z42_glyph_info;
+ pfont->procs.glyph_outline = z42_glyph_outline;
+ return 0;
+}
+static int
+zbuildfont42(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_font_type42 *pfont;
+ int code = build_gs_TrueType_font(i_ctx_p, op, &pfont, ft_TrueType,
+ &st_gs_font_type42, "%Type42BuildChar",
+ "%Type42BuildGlyph", bf_options_none);
+
+ if (code < 0)
+ return code;
+ return define_gs_font(i_ctx_p, (gs_font *)pfont);
+}
+
+/*
+ * Check a parameter for being an array of strings. Return the parameter
+ * value even if it is of the wrong type.
+ */
+int
+font_string_array_param(const gs_memory_t *mem, os_ptr op, const char *kstr, ref *psa)
+{
+ ref *pvsa;
+ ref rstr0;
+ int code;
+
+ if (dict_find_string(op, kstr, &pvsa) <= 0)
+ return_error(gs_error_invalidfont);
+ *psa = *pvsa;
+ /*
+ * We only check the first element of the array now, as a sanity test;
+ * elements are checked as needed by string_array_access_proc.
+ */
+ if ((code = array_get(mem, pvsa, 0L, &rstr0)) < 0)
+ return code;
+ if (!r_has_type(&rstr0, t_string))
+ return_error(gs_error_typecheck);
+ return 0;
+}
+
+/*
+ * Get a GlyphDirectory if present. Return 0 if present, 1 if absent,
+ * or an error code.
+ */
+int
+font_GlyphDirectory_param(os_ptr op, ref *pGlyphDirectory)
+{
+ ref *pgdir;
+
+ if (dict_find_string(op, "GlyphDirectory", &pgdir) <= 0)
+ make_null(pGlyphDirectory);
+ else if (!r_has_type(pgdir, t_dictionary) && !r_is_array(pgdir))
+ return_error(gs_error_typecheck);
+ else
+ *pGlyphDirectory = *pgdir;
+ return 0;
+}
+
+/*
+ * Access a given byte offset and length in an array of strings.
+ * This is used for sfnts and for CIDMap. The int argument is 2 for sfnts
+ * (because of the strange behavior of odd-length strings), 1 for CIDMap.
+ * Return code : 0 - success, <0 - error,
+ * >0 - number of accessible bytes (client must cycle).
+ */
+int
+string_array_access_proc(const gs_memory_t *mem,
+ const ref *psa, int modulus, ulong offset, uint length,
+ uint *mru_index, ulong *mru_pos,
+ const byte **pdata)
+{
+ ulong left;
+ uint index;
+ bool backwards;
+
+ if (length == 0)
+ return 0;
+ if (mru_index && mru_pos && offset >= (*mru_pos >> 1)) {
+ /* offset in or after mru string */
+ /* OR offset in 2nd half of the fragment before the mru string */
+ backwards = (*mru_pos > offset);
+ if (backwards) {
+ index = *mru_index - 1; /* 1st string to examine */
+ left = *mru_pos - offset; /* how many bytes to seek backwards */
+ } else {
+ index = *mru_index; /* 1st string to examine */
+ left = offset - *mru_pos; /* how many bytes to seek forward */
+ }
+ } else {
+ /* no mru */
+ /* OR offset in 1st half of the fragment before the mru string */
+ backwards = false;
+ index = 0;
+ left = offset;
+ }
+ for (;;) {
+ ref rstr;
+ int code = array_get(mem, psa, index, &rstr);
+ uint size;
+
+ if (code < 0)
+ return code;
+ if (!r_has_type(&rstr, t_string))
+ return_error(gs_error_typecheck);
+ /*
+ * NOTE: According to the Adobe documentation, each sfnts
+ * string should have even length. If the length is odd,
+ * the additional byte is padding and should be ignored.
+ */
+ size = r_size(&rstr) & -modulus;
+ if (backwards) {
+ if (left <= size) {
+ left = size - left;
+ backwards = false;
+ /* "index" does not change */
+ } else {
+ left -= size;
+ --index;
+ continue;
+ }
+ }
+ if (left < size) {
+ *pdata = rstr.value.const_bytes + left;
+ if (mru_index)
+ *mru_index = index;
+ if (mru_pos)
+ *mru_pos = offset - left;
+ if (left + length > size)
+ return size - left;
+ return 0;
+ }
+ left -= size;
+ ++index;
+ }
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfont42_op_defs[] =
+{
+ {"2.buildfont42", zbuildfont42},
+ op_def_end(0)
+};
+
+/* Reduce a glyph name to a glyph index if needed. */
+static gs_glyph
+glyph_to_index(const gs_font *font, gs_glyph glyph)
+{
+ ref gref;
+ ref *pcstr;
+
+ if (glyph >= GS_MIN_GLYPH_INDEX)
+ return glyph;
+ name_index_ref(font->memory, glyph, &gref);
+ if (dict_find(&pfont_data(font)->CharStrings, &gref, &pcstr) > 0 &&
+ r_has_type(pcstr, t_integer)
+ ) {
+ gs_glyph index_glyph = pcstr->value.intval + GS_MIN_GLYPH_INDEX;
+
+ if (index_glyph >= GS_MIN_GLYPH_INDEX && index_glyph <= gs_max_glyph)
+ return index_glyph;
+ }
+ return GS_MIN_GLYPH_INDEX; /* glyph 0 is notdef */
+}
+static uint
+z42_get_glyph_index(gs_font_type42 *pfont, gs_glyph glyph)
+{
+ gs_glyph gid = glyph_to_index((gs_font *)pfont, glyph);
+
+ return gid - GS_MIN_GLYPH_INDEX;
+}
+
+/*
+ * Get a glyph outline from GlyphDirectory. Return an empty string if
+ * the glyph is missing or out of range.
+ */
+int
+font_gdir_get_outline(const gs_memory_t *mem,
+ const ref *pgdir,
+ long glyph_index,
+ gs_glyph_data_t *pgd)
+{
+ ref iglyph;
+ ref gdef;
+ ref *pgdef;
+ int code;
+
+ if (r_has_type(pgdir, t_dictionary)) {
+ make_int(&iglyph, glyph_index);
+ code = dict_find(pgdir, &iglyph, &pgdef) - 1; /* 0 => not found */
+ } else {
+ code = array_get(mem, pgdir, glyph_index, &gdef);
+ pgdef = &gdef;
+ }
+ if (code < 0) {
+ gs_glyph_data_from_null(pgd);
+ } else if (!r_has_type(pgdef, t_string)) {
+ return_error(gs_error_typecheck);
+ } else {
+ gs_glyph_data_from_string(pgd, pgdef->value.const_bytes, r_size(pgdef),
+ NULL);
+ }
+ return 0;
+}
+static int
+z42_gdir_get_outline(gs_font_type42 * pfont, uint glyph_index,
+ gs_glyph_data_t *pgd)
+{
+ const font_data *pfdata = pfont_data(pfont);
+ const ref *pgdir = &pfdata->u.type42.GlyphDirectory;
+
+ return font_gdir_get_outline(pfont->memory, pgdir, (long)glyph_index, pgd);
+}
+
+/* Enumerate glyphs from CharStrings or loca / glyf. */
+static int
+z42_enumerate_glyph(gs_font *font, int *pindex, gs_glyph_space_t glyph_space,
+ gs_glyph *pglyph)
+{
+ if (glyph_space == GLYPH_SPACE_INDEX)
+ return gs_type42_enumerate_glyph(font, pindex, glyph_space, pglyph);
+ else {
+ const ref *pcsdict = &pfont_data(font)->CharStrings;
+
+ return zchar_enumerate_glyph(font->memory, pcsdict, pindex, pglyph);
+ }
+}
+
+/* Enumerate glyphs (keys) from GlyphDirectory instead of loca / glyf. */
+static int
+z42_gdir_enumerate_glyph(gs_font *font, int *pindex,
+ gs_glyph_space_t glyph_space, gs_glyph *pglyph)
+{
+ const ref *pgdict;
+ int code;
+
+ if (glyph_space == GLYPH_SPACE_INDEX) {
+ pgdict = &pfont_data(font)->u.type42.GlyphDirectory;
+ if (!r_has_type(pgdict, t_dictionary)) {
+ ref gdef;
+
+ for (;; (*pindex)++) {
+ if (array_get(font->memory, pgdict, (long)*pindex, &gdef) < 0) {
+ *pindex = 0;
+ return 0;
+ }
+ if (!r_has_type(&gdef, t_null)) {
+ *pglyph = GS_MIN_GLYPH_INDEX + (*pindex)++;
+ return 0;
+ }
+ }
+ }
+ } else
+ pgdict = &pfont_data(font)->CharStrings;
+ /* A trick : use zchar_enumerate_glyph to enumerate GIDs : */
+ code = zchar_enumerate_glyph(font->memory, pgdict, pindex, pglyph);
+ if (*pindex != 0 && *pglyph >= gs_min_cid_glyph)
+ *pglyph = *pglyph - gs_min_cid_glyph + GS_MIN_GLYPH_INDEX;
+ return code;
+}
+
+/*
+ * Define font procedures that accept either a character name or a glyph
+ * index as the glyph.
+ */
+static gs_glyph
+z42_encode_char(gs_font *font, gs_char chr, gs_glyph_space_t glyph_space)
+{
+ gs_glyph glyph = zfont_encode_char(font, chr, glyph_space);
+
+ return (glyph_space == GLYPH_SPACE_INDEX && glyph != gs_no_glyph ?
+ glyph_to_index(font, glyph) : glyph);
+}
+static int
+z42_glyph_outline(gs_font *font, int WMode, gs_glyph glyph, const gs_matrix *pmat,
+ gx_path *ppath, double sbw[4])
+{
+ return gs_type42_glyph_outline(font, WMode, glyph_to_index(font, glyph),
+ pmat, ppath, sbw);
+}
+static int
+z42_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
+ int members, gs_glyph_info_t *info)
+{ /* fixme : same as z1_glyph_info. */
+ int wmode = font->WMode;
+
+ return z1_glyph_info_generic(font, glyph, pmat, members, info, gs_type42_glyph_info, wmode);
+}
+
+/* Procedure for accessing the sfnts array.
+ * Return code : 0 - success, <0 - error,
+ * >0 - number of accessible bytes (client must cycle).
+ */
+static int
+z42_string_proc(gs_font_type42 * pfont, ulong offset, uint length,
+ const byte ** pdata)
+{
+ return string_array_access_proc(pfont->memory, &pfont_data(pfont)->u.type42.sfnts, 2,
+ offset, length, &pfont_data(pfont)->u.type42.mru_sfnts_index,
+ &pfont_data(pfont)->u.type42.mru_sfnts_pos, pdata);
+}
+
+static int
+z42_font_info(gs_font *font, const gs_point *pscale, int members,
+ gs_font_info_t *info)
+{
+ int code = zfont_info(font, pscale, members, info);
+
+ if (code < 0)
+ return code;
+ return gs_truetype_font_info(font, pscale, members, info);
+}
diff --git a/psi/zfontenum.c b/psi/zfontenum.c
new file mode 100644
index 000000000..52c9078b3
--- /dev/null
+++ b/psi/zfontenum.c
@@ -0,0 +1,133 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+
+/* this is the ps interpreter interface to the native font
+ enumeration code. it calls the platform-specific routines
+ to obtain an additional set of entries that can be added
+ to the Fontmap to reference fonts stored on the system.
+ */
+
+#include "memory_.h"
+#include "string_.h"
+#include <stdlib.h>
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gsmalloc.h"
+#include "ialloc.h"
+#include "iname.h"
+#include "iutil.h"
+#include "store.h"
+#include "gp.h"
+
+typedef struct fontenum_s {
+ char *fontname, *path;
+ struct fontenum_s *next;
+} fontenum_t;
+
+/* .getnativefonts [ [<name> <path>] ... ] */
+static int
+z_fontenum(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ void *enum_state;
+ int code = 0;
+ int e,elements;
+ char *fontname, *path;
+ fontenum_t *r, *results;
+ ref array;
+ uint length;
+ byte *string;
+
+ enum_state = gp_enumerate_fonts_init(imemory);
+ if (enum_state == NULL) {
+ /* put false on the stack and return */
+ push(1);
+ make_bool(op, false);
+ return code;
+ }
+
+ r = results = gs_malloc(imemory->non_gc_memory, 1, sizeof(fontenum_t), "fontenum list");
+ elements = 0;
+ while((code = gp_enumerate_fonts_next(enum_state, &fontname, &path )) > 0) {
+ if (fontname == NULL || path == NULL) {
+ gp_enumerate_fonts_free(enum_state);
+ return_error(gs_error_ioerror);
+ }
+
+ length = strlen(fontname) + 1;
+ r->fontname = gs_malloc(imemory->non_gc_memory, length, 1, "native font name");
+ memcpy(r->fontname, fontname, length);
+
+ length = strlen(path) + 1;
+ r->path = gs_malloc(imemory->non_gc_memory, length, 1, "native font path");
+ memcpy(r->path, path, length);
+
+ r->next = gs_malloc(imemory->non_gc_memory, 1, sizeof(fontenum_t), "fontenum list");
+ r = r->next;
+ elements += 1;
+ }
+
+ gp_enumerate_fonts_free(enum_state);
+
+ code = ialloc_ref_array(&array, a_all | icurrent_space, elements, "native fontmap");
+
+ r = results;
+ for (e = 0; e < elements; e++) {
+ ref mapping;
+
+ code = ialloc_ref_array(&mapping, a_all | icurrent_space, 2, "native font mapping");
+
+ length = strlen(r->fontname);
+ string = ialloc_string(length, "native font name");
+ if (string == NULL)
+ return_error(gs_error_VMerror);
+ memcpy(string, r->fontname, length);
+ make_string(&(mapping.value.refs[0]), a_all | icurrent_space, length, string);
+
+ length = strlen(r->path);
+ string = ialloc_string(length, "native font path");
+ if (string == NULL)
+ return_error(gs_error_VMerror);
+ memcpy(string, r->path, length);
+ make_string(&(mapping.value.refs[1]), a_all | icurrent_space, length, string);
+
+ ref_assign(&(array.value.refs[e]), &mapping);
+ results = r;
+ r = r->next;
+
+ gs_free(imemory->non_gc_memory,
+ results->fontname, strlen(results->fontname) + 1, 1, "native font name");
+ gs_free(imemory->non_gc_memory,
+ results->path, strlen(results->path) + 1, 1, "native font path");
+ gs_free(imemory->non_gc_memory,
+ results, 1, sizeof(fontenum_t), "fontenum list");
+ }
+
+ push(2);
+ ref_assign(op-1, &array);
+ make_bool(op, true);
+
+ return code;
+}
+
+/* Match the above routines to their postscript filter names.
+ This is how our static routines get called externally. */
+const op_def zfontenum_op_defs[] = {
+ {"0.getnativefonts", z_fontenum},
+ op_def_end(0)
+};
diff --git a/psi/zform.c b/psi/zform.c
new file mode 100644
index 000000000..618b81c45
--- /dev/null
+++ b/psi/zform.c
@@ -0,0 +1,205 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Simple high level forms */
+#include "ghost.h"
+#include "oper.h"
+#include "gxdevice.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "gxdevsop.h"
+#include "gscoord.h"
+#include "gsform1.h"
+#include "gspath.h"
+#include "gxpath.h"
+#include "gzstate.h"
+#include "store.h"
+
+/* support for high level formss */
+
+/* [CTM before Form Matrix applied] <<Form dictionary>> .beginform -
+ */
+static int zbeginform(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_device *cdev = gs_currentdevice_inline(igs);
+ int code;
+ float BBox[4], Matrix[6];
+ gs_form_template_t tmplate;
+ gs_point pt;
+ gs_fixed_rect box;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+
+ code = read_matrix(imemory, op - 1, &tmplate.CTM);
+ if (code < 0)
+ return code;
+
+ code = dict_floats_param(imemory, op, "BBox", 4, BBox, NULL);
+ if (code < 0)
+ return code;
+ if (code == 0)
+ return_error(gs_error_undefined);
+ tmplate.FormID = -1;
+ tmplate.BBox.p.x = BBox[0];
+ tmplate.BBox.p.y = BBox[1];
+ pt.x = tmplate.BBox.q.x = BBox[2];
+ pt.y = tmplate.BBox.q.y = BBox[3];
+
+ code = dict_floats_param(imemory, op, "Matrix", 6, Matrix, NULL);
+ if (code < 0)
+ return code;
+ if (code == 0)
+ return_error(gs_error_undefined);
+
+ tmplate.form_matrix.xx = Matrix[0];
+ tmplate.form_matrix.xy = Matrix[1];
+ tmplate.form_matrix.yx = Matrix[2];
+ tmplate.form_matrix.yy = Matrix[3];
+ tmplate.form_matrix.tx = Matrix[4];
+ tmplate.form_matrix.ty = Matrix[5];
+
+ tmplate.pcpath = igs->clip_path;
+ code = dev_proc(cdev, dev_spec_op)(cdev, gxdso_form_begin,
+ &tmplate, 0);
+
+ /* return value > 0 means the device sent us back a matrix
+ * and wants the CTM set to that.
+ */
+ if (code > 0)
+ {
+ gs_setmatrix(igs, &tmplate.CTM);
+ gs_distance_transform(tmplate.BBox.q.x, tmplate.BBox.q.y, &tmplate.CTM, &pt);
+
+ /* A form can legitimately have negative co-ordinates in paths
+ * because it can be translated. But we always clip paths to the
+ * page which (clearly) can't have negative co-ordinates. NB this
+ * wouldn't be a problem if we didn't reset the CTM, but that would
+ * break the form capture.
+ * So here we temporarily set the clip to permit negative values,
+ * fortunately this works.....
+ */
+ /* We choose to permit negative values of the same magnitude as the
+ * positive ones.
+ */
+ box.p.x = float2fixed(pt.x * -1);
+ box.p.y = float2fixed(pt.y * -1);
+ box.q.x = float2fixed(pt.x);
+ box.q.y = float2fixed(pt.y);
+
+ /* This gets undone when we grestore after the form is executed */
+ code = gx_clip_to_rectangle(igs, &box);
+ }
+
+ pop(2);
+ return code;
+}
+
+/* - .endform -
+ */
+static int zendform(i_ctx_t *i_ctx_p)
+{
+ gx_device *cdev = gs_currentdevice_inline(igs);
+ int code;
+
+ code = dev_proc(cdev, dev_spec_op)(cdev, gxdso_form_end,
+ 0, 0);
+ return code;
+}
+
+/*
+ * - .get_form_id <int>
+ */
+static int zget_form_id(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_device *cdev = gs_currentdevice_inline(igs);
+ int code, ID;
+
+ code = dev_proc(cdev, dev_spec_op)(cdev, gxdso_get_form_ID,
+ &ID, sizeof(int));
+
+ if (code < 0){
+ ID = -1;
+ code = 0;
+ }
+
+ push(1);
+ make_int(op, ID);
+ return code;
+}
+
+/*
+ * <int> .repeatform -
+ */
+static int zrepeatform(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_device *cdev = gs_currentdevice_inline(igs);
+ int code;
+ gs_form_template_t tmplate;
+ float BBox[4], Matrix[6];
+
+ check_type(*op, t_integer);
+
+ code = read_matrix(imemory, op - 2, &tmplate.CTM);
+ if (code < 0)
+ return code;
+
+ code = dict_floats_param(imemory, op - 1, "BBox", 4, BBox, NULL);
+ if (code < 0)
+ return code;
+ if (code == 0)
+ return_error(gs_error_undefined);
+
+ tmplate.BBox.p.x = BBox[0];
+ tmplate.BBox.p.y = BBox[1];
+
+ code = dict_floats_param(imemory, op - 1, "Matrix", 6, Matrix, NULL);
+ if (code < 0)
+ return code;
+ if (code == 0)
+ return_error(gs_error_undefined);
+
+ tmplate.form_matrix.xx = Matrix[0];
+ tmplate.form_matrix.xy = Matrix[1];
+ tmplate.form_matrix.yx = Matrix[2];
+ tmplate.form_matrix.yy = Matrix[3];
+ tmplate.form_matrix.tx = Matrix[4];
+ tmplate.form_matrix.ty = Matrix[5];
+
+ tmplate.pcpath = igs->clip_path;
+
+ tmplate.FormID = op->value.intval;
+
+ code = dev_proc(cdev, dev_spec_op)(cdev, gxdso_repeat_form,
+ &tmplate, sizeof(gs_form_template_t));
+
+ pop(3);
+ return code;
+}
+
+const op_def zform_op_defs[] =
+{
+ {"0.beginform", zbeginform},
+ {"0.endform", zendform},
+ {"0.get_form_id", zget_form_id},
+ {"1.repeatform", zrepeatform},
+op_def_end(0)
+};
diff --git a/psi/zfproc.c b/psi/zfproc.c
new file mode 100644
index 000000000..b12d13b03
--- /dev/null
+++ b/psi/zfproc.c
@@ -0,0 +1,387 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Procedure-based filter stream support */
+#include "memory_.h"
+#include "stat_.h" /* get system header early to avoid name clash on Cygwin */
+#include "ghost.h"
+#include "oper.h" /* for ifilter.h */
+#include "estack.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "istruct.h" /* for RELOC_REF_VAR */
+#include "stream.h"
+#include "strimpl.h"
+#include "ifilter.h"
+#include "files.h"
+#include "store.h"
+
+/* ---------------- Generic ---------------- */
+
+/* GC procedures */
+static
+CLEAR_MARKS_PROC(sproc_clear_marks)
+{
+ stream_proc_state *const pptr = vptr;
+
+ r_clear_attrs(&pptr->proc, l_mark);
+ r_clear_attrs(&pptr->data, l_mark);
+}
+static
+ENUM_PTRS_WITH(sproc_enum_ptrs, stream_proc_state *pptr) return 0;
+case 0:
+ENUM_RETURN_REF(&pptr->proc);
+case 1:
+ENUM_RETURN_REF(&pptr->data);
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(sproc_reloc_ptrs, stream_proc_state *pptr);
+RELOC_REF_VAR(pptr->proc);
+r_clear_attrs(&pptr->proc, l_mark);
+RELOC_REF_VAR(pptr->data);
+r_clear_attrs(&pptr->data, l_mark);
+RELOC_PTRS_END
+
+/* Structure type for procedure-based streams. */
+private_st_stream_proc_state();
+
+/* Allocate and open a procedure-based filter. */
+/* The caller must have checked that *sop is a procedure. */
+static int
+s_proc_init(ref * sop, stream ** psstrm, uint mode,
+ const stream_template * temp, const stream_procs * procs,
+ gs_ref_memory_t *imem)
+{
+ gs_memory_t *const mem = (gs_memory_t *)imem;
+ stream *sstrm = file_alloc_stream(mem, "s_proc_init(stream)");
+ stream_proc_state *state = (stream_proc_state *)
+ s_alloc_state(mem, &st_sproc_state, "s_proc_init(state)");
+
+ if (sstrm == 0 || state == 0) {
+ gs_free_object(mem, state, "s_proc_init(state)");
+ /*gs_free_object(mem, sstrm, "s_proc_init(stream)"); *//* just leave it on the file list */
+ return_error(gs_error_VMerror);
+ }
+ s_std_init(sstrm, NULL, 0, procs, mode);
+ sstrm->procs.process = temp->process;
+ state->templat = temp;
+ state->memory = mem;
+ state->eof = 0;
+ state->proc = *sop;
+ make_empty_string(&state->data, a_all);
+ state->index = 0;
+ sstrm->state = (stream_state *) state;
+ *psstrm = sstrm;
+ return 0;
+}
+
+/* Handle an interrupt during a stream operation. */
+/* This is logically unrelated to procedure streams, */
+/* but it is also associated with the interpreter stream machinery. */
+static int
+s_handle_intc(i_ctx_t *i_ctx_p, const ref *pstate, int nstate,
+ op_proc_t cont)
+{
+ int npush = nstate + 2;
+
+ check_estack(npush);
+ if (nstate)
+ memcpy(esp + 2, pstate, nstate * sizeof(ref));
+#if 0 /* **************** */
+ {
+ int code = gs_interpret_error(gs_error_interrupt, (ref *) (esp + npush));
+
+ if (code < 0)
+ return code;
+ }
+#else /* **************** */
+ npush--;
+#endif /* **************** */
+ make_op_estack(esp + 1, cont);
+ esp += npush;
+ return o_push_estack;
+}
+
+/* Set default parameter values (actually, just clear pointers). */
+static void
+s_proc_set_defaults(stream_state * st)
+{
+ stream_proc_state *const ss = (stream_proc_state *) st;
+
+ make_null(&ss->proc);
+ make_null(&ss->data);
+}
+
+/* ---------------- Read streams ---------------- */
+
+/* Forward references */
+static stream_proc_process(s_proc_read_process);
+static int s_proc_read_continue(i_ctx_t *);
+
+/* Stream templates */
+static const stream_template s_proc_read_template = {
+ &st_sproc_state, NULL, s_proc_read_process, 1, 1,
+ NULL, s_proc_set_defaults
+};
+static const stream_procs s_proc_read_procs = {
+ s_std_noavailable, s_std_noseek, s_std_read_reset,
+ s_std_read_flush, s_std_null, NULL
+};
+
+/* Allocate and open a procedure-based read stream. */
+/* The caller must have checked that *sop is a procedure. */
+int
+sread_proc(ref * sop, stream ** psstrm, gs_ref_memory_t *imem)
+{
+ int code =
+ s_proc_init(sop, psstrm, s_mode_read, &s_proc_read_template,
+ &s_proc_read_procs, imem);
+
+ if (code < 0)
+ return code;
+ (*psstrm)->end_status = CALLC;
+ return code;
+}
+
+/* Handle an input request. */
+static int
+s_proc_read_process(stream_state * st, stream_cursor_read * ignore_pr,
+ stream_cursor_write * pw, bool last)
+{
+ /* Move data from the string returned by the procedure */
+ /* into the stream buffer, or ask for a callback. */
+ stream_proc_state *const ss = (stream_proc_state *) st;
+ uint count = r_size(&ss->data) - ss->index;
+
+ if (count > 0) {
+ uint wcount = pw->limit - pw->ptr;
+
+ if (wcount < count)
+ count = wcount;
+ memcpy(pw->ptr + 1, ss->data.value.bytes + ss->index, count);
+ pw->ptr += count;
+ ss->index += count;
+ return 1;
+ }
+ return (ss->eof ? EOFC : CALLC);
+}
+
+/* Handle an exception (INTC or CALLC) from a read stream */
+/* whose buffer is empty. */
+int
+s_handle_read_exception(i_ctx_t *i_ctx_p, int status, const ref * fop,
+ const ref * pstate, int nstate, op_proc_t cont)
+{
+ int npush = nstate + 4;
+ stream *ps;
+
+ switch (status) {
+ case INTC:
+ return s_handle_intc(i_ctx_p, pstate, nstate, cont);
+ case CALLC:
+ break;
+ default:
+ return_error(gs_error_ioerror);
+ }
+ /* Find the stream whose buffer needs refilling. */
+ for (ps = fptr(fop); ps->strm != 0;)
+ ps = ps->strm;
+ check_estack(npush);
+ if (nstate)
+ memcpy(esp + 2, pstate, nstate * sizeof(ref));
+ make_op_estack(esp + 1, cont);
+ esp += npush;
+ make_op_estack(esp - 2, s_proc_read_continue);
+ esp[-1] = *fop;
+ r_clear_attrs(esp - 1, a_executable);
+ *esp = ((stream_proc_state *) ps->state)->proc;
+ return o_push_estack;
+}
+/* Continue a read operation after returning from a procedure callout. */
+/* osp[0] contains the file (pushed on the e-stack by handle_read_status); */
+/* osp[-1] contains the new data string (pushed by the procedure). */
+/* The top of the e-stack contains the real continuation. */
+static int
+s_proc_read_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr opbuf = op - 1;
+ stream *ps;
+ stream_proc_state *ss;
+
+ check_file(ps, op);
+ check_read_type(*opbuf, t_string);
+ while ((ps->end_status = 0, ps->strm) != 0)
+ ps = ps->strm;
+ ss = (stream_proc_state *) ps->state;
+ ss->data = *opbuf;
+ ss->index = 0;
+ if (r_size(opbuf) == 0)
+ ss->eof = true;
+ pop(2);
+ return 0;
+}
+
+/* ---------------- Write streams ---------------- */
+
+/* Forward references */
+static stream_proc_flush(s_proc_write_flush);
+static stream_proc_process(s_proc_write_process);
+static int s_proc_write_continue(i_ctx_t *);
+
+/* Stream templates */
+static const stream_template s_proc_write_template = {
+ &st_sproc_state, NULL, s_proc_write_process, 1, 1,
+ NULL, s_proc_set_defaults
+};
+static const stream_procs s_proc_write_procs = {
+ s_std_noavailable, s_std_noseek, s_std_write_reset,
+ s_proc_write_flush, s_std_null, NULL
+};
+
+/* Allocate and open a procedure-based write stream. */
+/* The caller must have checked that *sop is a procedure. */
+int
+swrite_proc(ref * sop, stream ** psstrm, gs_ref_memory_t *imem)
+{
+ return s_proc_init(sop, psstrm, s_mode_write, &s_proc_write_template,
+ &s_proc_write_procs, imem);
+}
+
+/* Handle an output request. */
+static int
+s_proc_write_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * ignore_pw, bool last)
+{
+ /* Move data from the stream buffer to the string */
+ /* returned by the procedure, or ask for a callback. */
+ stream_proc_state *const ss = (stream_proc_state *) st;
+ uint rcount = pr->limit - pr->ptr;
+
+ /* if 'last' return CALLC even when rcount == 0. ss->eof terminates */
+ if (rcount > 0 || (last && !ss->eof)) {
+ uint wcount = r_size(&ss->data) - ss->index;
+ uint count = min(rcount, wcount);
+
+ memcpy(ss->data.value.bytes + ss->index, pr->ptr + 1, count);
+ pr->ptr += count;
+ ss->index += count;
+ if (rcount > wcount)
+ return CALLC;
+ else if (last) {
+ ss->eof = true;
+ return CALLC;
+ } else
+ return 0;
+ }
+ return ((ss->eof = last) ? EOFC : 0);
+}
+
+/* Flush the output. This is non-standard because it must call the */
+/* procedure. */
+static int
+s_proc_write_flush(stream *s)
+{
+ int result = s_process_write_buf(s, false);
+ stream_proc_state *const ss = (stream_proc_state *)s->state;
+
+ return (result < 0 || ss->index == 0 ? result : CALLC);
+}
+
+/* Handle an exception (INTC or CALLC) from a write stream */
+/* whose buffer is full. */
+int
+s_handle_write_exception(i_ctx_t *i_ctx_p, int status, const ref * fop,
+ const ref * pstate, int nstate, op_proc_t cont)
+{
+ stream *ps;
+ stream_proc_state *psst;
+
+ switch (status) {
+ case INTC:
+ return s_handle_intc(i_ctx_p, pstate, nstate, cont);
+ case CALLC:
+ break;
+ default:
+ return_error(gs_error_ioerror);
+ }
+ /* Find the stream whose buffer needs emptying. */
+ for (ps = fptr(fop); ps->strm != 0;)
+ ps = ps->strm;
+ psst = (stream_proc_state *) ps->state;
+ {
+ int npush = nstate + 6;
+
+ check_estack(npush);
+ if (nstate)
+ memcpy(esp + 2, pstate, nstate * sizeof(ref));
+ make_op_estack(esp + 1, cont);
+ esp += npush;
+ make_op_estack(esp - 4, s_proc_write_continue);
+ esp[-3] = *fop;
+ r_clear_attrs(esp - 3, a_executable);
+ make_bool(esp - 1, !psst->eof);
+ }
+ esp[-2] = psst->proc;
+ *esp = psst->data;
+ r_set_size(esp, psst->index);
+ return o_push_estack;
+}
+/* Continue a write operation after returning from a procedure callout. */
+/* osp[0] contains the file (pushed on the e-stack by handle_write_status); */
+/* osp[-1] contains the new buffer string (pushed by the procedure). */
+/* The top of the e-stack contains the real continuation. */
+static int
+s_proc_write_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr opbuf = op - 1;
+ stream *ps;
+ stream_proc_state *ss;
+
+ check_file(ps, op);
+ check_write_type(*opbuf, t_string);
+ while (ps->strm != 0) {
+ if (ps->end_status == CALLC)
+ ps->end_status = 0;
+ ps = ps->strm;
+ }
+ ps->end_status = 0;
+ ss = (stream_proc_state *) ps->state;
+ ss->data = *opbuf;
+ ss->index = 0;
+ pop(2);
+ return 0;
+}
+
+/* ------ More generic ------ */
+
+/* Test whether a stream is procedure-based. */
+bool
+s_is_proc(const stream *s)
+{
+ return (s->procs.process == s_proc_read_process ||
+ s->procs.process == s_proc_write_process);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfproc_op_defs[] =
+{
+ /* Internal operators */
+ {"2%s_proc_read_continue", s_proc_read_continue},
+ {"2%s_proc_write_continue", s_proc_write_continue},
+ op_def_end(0)
+};
diff --git a/psi/zfrsd.c b/psi/zfrsd.c
new file mode 100644
index 000000000..69aa0cc95
--- /dev/null
+++ b/psi/zfrsd.c
@@ -0,0 +1,474 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* ReusableStreamDecode filter support */
+#include "memory_.h"
+#include "ghost.h"
+#include "gsfname.h" /* for gs_parse_file_name */
+#include "gxiodev.h"
+#include "oper.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "sfilter.h" /* for SubFileDecode */
+#include "files.h"
+#include "idict.h"
+#include "idparam.h"
+#include "iname.h"
+#include "istruct.h"
+#include "store.h"
+#include "zfile.h"
+#include "zfrsd.h"
+
+/* ---------------- Reusable streams ---------------- */
+
+/*
+ * The actual work of constructing the filter is done in PostScript code.
+ * The operators in this file are internal ones that handle the dirty work.
+ */
+
+/* <dict|null> .rsdparams <filters> <decodeparms|null> */
+/* filters is always an array; decodeparms is always either an array */
+/* of the same length as filters, or null. */
+static int
+zrsdparams(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *pFilter;
+ ref *pDecodeParms;
+ int Intent = 0;
+ bool AsyncRead;
+ ref empty_array, filter1_array, parms1_array;
+ uint i;
+ int code;
+
+ make_empty_array(&empty_array, a_readonly);
+ if (dict_find_string(op, "Filter", &pFilter) > 0) {
+ if (!r_is_array(pFilter)) {
+ if (!r_has_type(pFilter, t_name))
+ return_error(gs_error_typecheck);
+ make_array(&filter1_array, a_readonly, 1, pFilter);
+ pFilter = &filter1_array;
+ }
+ } else
+ pFilter = &empty_array;
+ /* If Filter is undefined, ignore DecodeParms. */
+ if (pFilter != &empty_array &&
+ dict_find_string(op, "DecodeParms", &pDecodeParms) > 0
+ ) {
+ if (pFilter == &filter1_array) {
+ make_array(&parms1_array, a_readonly, 1, pDecodeParms);
+ pDecodeParms = &parms1_array;
+ } else if (!r_is_array(pDecodeParms))
+ return_error(gs_error_typecheck);
+ else if (r_size(pFilter) != r_size(pDecodeParms))
+ return_error(gs_error_rangecheck);
+ } else
+ pDecodeParms = 0;
+ for (i = 0; i < r_size(pFilter); ++i) {
+ ref f, fname, dp;
+
+ array_get(imemory, pFilter, (long)i, &f);
+ if (!r_has_type(&f, t_name))
+ return_error(gs_error_typecheck);
+ name_string_ref(imemory, &f, &fname);
+ if (r_size(&fname) < 6 ||
+ memcmp(fname.value.bytes + r_size(&fname) - 6, "Decode", 6)
+ )
+ return_error(gs_error_rangecheck);
+ if (pDecodeParms) {
+ array_get(imemory, pDecodeParms, (long)i, &dp);
+ if (!(r_has_type(&dp, t_dictionary) || r_has_type(&dp, t_null)))
+ return_error(gs_error_typecheck);
+ }
+ }
+ code = dict_int_param(op, "Intent", 0, 3, 0, &Intent);
+ if (code < 0 && code != gs_error_rangecheck) /* out-of-range int is ok, use 0 */
+ return code;
+ if ((code = dict_bool_param(op, "AsyncRead", false, &AsyncRead)) < 0
+ )
+ return code;
+ push(1);
+ op[-1] = *pFilter;
+ if (pDecodeParms)
+ *op = *pDecodeParms;
+ else
+ make_null(op);
+ return 0;
+}
+
+/* <file|string> <CloseSource> .reusablestream <filter> */
+/*
+ * The file|string operand must be a "reusable source", either:
+ * - A string or bytestring;
+ * - An array of strings;
+ * - A readable, positionable file stream;
+ * - A readable string stream;
+ * - A SubFileDecode filter with an empty EODString and a reusable
+ * source.
+ * Reusable streams are also reusable sources, but they look just like
+ * ordinary file or string streams.
+ */
+static int make_rfs(i_ctx_t *i_ctx_p, os_ptr op, stream *fs,
+ long offset, long length);
+
+static int make_aos(i_ctx_t *i_ctx_p, os_ptr op,
+ int blk_sz, int blk_sz_last, unsigned int file_sz);
+
+static int
+zreusablestream(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr source_op = op - 1;
+ long length = max_long;
+ bool close_source;
+ int code;
+
+ check_type(*op, t_boolean);
+ close_source = op->value.boolval;
+ if (r_has_type(source_op, t_string)) {
+ uint size = r_size(source_op);
+
+ check_read(*source_op);
+ code = make_rss(i_ctx_p, source_op, source_op->value.const_bytes,
+ size, r_space(source_op), 0L, size, false);
+ } else if (r_has_type(source_op, t_astruct)) {
+ uint size = gs_object_size(imemory, source_op->value.pstruct);
+
+ if (gs_object_type(imemory, source_op->value.pstruct) != &st_bytes)
+ return_error(gs_error_rangecheck);
+ check_read(*source_op);
+ code = make_rss(i_ctx_p, source_op,
+ (const byte *)source_op->value.pstruct, size,
+ r_space(source_op), 0L, size, true);
+ } else if (r_has_type(source_op, t_array)) { /* no packedarrays */
+ int i, blk_cnt, blk_sz;
+ ref *blk_ref;
+ ulong filelen = 0;
+
+ check_read(*source_op);
+ blk_cnt = r_size(source_op);
+ blk_ref = source_op->value.refs;
+ if (blk_cnt > 0) {
+ blk_sz = r_size(blk_ref);
+ for (i = 0; i < blk_cnt; i++) {
+ int len;
+
+ check_read_type(blk_ref[i], t_string);
+ len = r_size(&blk_ref[i]);
+ if (len > blk_sz || (len < blk_sz && i < blk_cnt - 1))
+ return_error(gs_error_rangecheck); /* last block can be smaller */
+ filelen += len;
+ }
+ }
+ if (filelen == 0) {
+ code = make_rss(i_ctx_p, source_op, (unsigned char *)"", 0,
+ r_space(source_op), 0, 0, false);
+ } else {
+ code = make_aos(i_ctx_p, source_op, blk_sz, r_size(&blk_ref[blk_cnt - 1]), filelen);
+ }
+ } else {
+ long offset = 0;
+ stream *source;
+ stream *s;
+
+ check_read_file(i_ctx_p, source, source_op);
+ s = source;
+rs:
+ if (s->cbuf_string.data != 0) { /* string stream */
+ long pos = stell(s);
+ long avail = sbufavailable(s) + pos;
+
+ offset += pos;
+ code = make_rss(i_ctx_p, source_op, s->cbuf_string.data,
+ s->cbuf_string.size,
+ imemory_space((const gs_ref_memory_t *)s->memory),
+ offset, min(avail, length), false);
+ } else if (s->file != 0) { /* file stream */
+ if (~s->modes & (s_mode_read | s_mode_seek))
+ return_error(gs_error_ioerror);
+ code = make_rfs(i_ctx_p, source_op, s, offset + stell(s), length);
+ } else if (s->state->templat == &s_SFD_template) {
+ /* SubFileDecode filter */
+ const stream_SFD_state *const sfd_state =
+ (const stream_SFD_state *)s->state;
+
+ if (sfd_state->eod.size != 0)
+ return_error(gs_error_rangecheck);
+ offset += sfd_state->skip_count - sbufavailable(s);
+ if (sfd_state->count != 0) {
+ long left = max(sfd_state->count, 0) + sbufavailable(s);
+
+ if (left < length)
+ length = left;
+ }
+ s = s->strm;
+ goto rs;
+ }
+ else /* some other kind of stream */
+ return_error(gs_error_rangecheck);
+ if (close_source) {
+ stream *rs = fptr(source_op);
+
+ rs->strm = source; /* only for close_source */
+ rs->close_strm = true;
+ }
+ }
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+/* Make a reusable string stream. */
+int
+make_rss(i_ctx_t *i_ctx_p, os_ptr op, const byte * data, uint size,
+ uint string_space, long offset, long length, bool is_bytestring)
+{
+ uint save_space = icurrent_space;
+ stream *s;
+ long left = min(length, size - offset);
+
+ ialloc_set_space(idmemory, string_space);
+ s = file_alloc_stream(imemory, "make_rss");
+ ialloc_set_space(idmemory, save_space);
+ if (s == 0)
+ return_error(gs_error_VMerror);
+ sread_string_reusable(s, data + offset, max(left, 0));
+ if (is_bytestring)
+ s->cbuf_string.data = 0; /* byte array, not string */
+ make_stream_file(op, s, "r");
+ return 0;
+}
+
+/* Make a reusable file stream. */
+static int
+make_rfs(i_ctx_t *i_ctx_p, os_ptr op, stream *fs, long offset, long length)
+{
+ uint save_space = icurrent_space;
+ uint stream_space = imemory_space((const gs_ref_memory_t *)fs->memory);
+ gs_const_string fname;
+ gs_parsed_file_name_t pname;
+ stream *s;
+ int code;
+
+ if (sfilename(fs, &fname) < 0)
+ return_error(gs_error_ioerror);
+ code = gs_parse_file_name(&pname, (const char *)fname.data, fname.size,
+ imemory);
+ if (code < 0)
+ return code;
+ if (pname.len == 0) /* %stdin% etc. won't have a filename */
+ return_error(gs_error_invalidfileaccess); /* can't reopen */
+ if (pname.iodev == NULL)
+ pname.iodev = iodev_default(imemory);
+ /* Open the file again, to be independent of the source. */
+ ialloc_set_space(idmemory, stream_space);
+ code = zopen_file(i_ctx_p, &pname, "r", &s, imemory);
+ ialloc_set_space(idmemory, save_space);
+ if (code < 0)
+ return code;
+ if (sread_subfile(s, offset, length) < 0) {
+ sclose(s);
+ return_error(gs_error_ioerror);
+ }
+ s->close_at_eod = false;
+ make_stream_file(op, s, "r");
+ return 0;
+}
+/* ----------- Reusable array-of-strings stream ------------- */
+
+static int s_aos_available(stream *, gs_offset_t *);
+static int s_aos_seek(stream *, gs_offset_t);
+static void s_aos_reset(stream *s);
+static int s_aos_flush(stream *s);
+static int s_aos_close(stream *);
+static int s_aos_process(stream_state *, stream_cursor_read *,
+ stream_cursor_write *, bool);
+
+/* Stream state */
+typedef struct aos_state_s {
+ stream_state_common;
+ ref blocks;
+ stream *s;
+ int blk_sz;
+ int blk_sz_last;
+ uint file_sz;
+} aos_state_t;
+
+/* GC procedures */
+static
+CLEAR_MARKS_PROC(aos_clear_marks)
+{ aos_state_t *const pptr = vptr;
+
+ r_clear_attrs(&pptr->blocks, l_mark);
+}
+static
+ENUM_PTRS_WITH(aos_enum_ptrs, aos_state_t *pptr) return 0;
+ENUM_PTR(0, aos_state_t, s);
+case 1:
+ENUM_RETURN_REF(&pptr->blocks);
+ENUM_PTRS_END
+static RELOC_PTRS_WITH(aos_reloc_ptrs, aos_state_t *pptr);
+RELOC_PTR(aos_state_t, s);
+RELOC_REF_VAR(pptr->blocks);
+r_clear_attrs(&pptr->blocks, l_mark);
+RELOC_PTRS_END
+
+gs_private_st_complex_only(st_aos_state, aos_state_t,
+ "aos_state", aos_clear_marks, aos_enum_ptrs, aos_reloc_ptrs, 0);
+
+/* Stream template */
+static const stream_template s_aos_template = {
+ &st_aos_state, 0, s_aos_process, 1, 1, 0, 0 };
+
+/* Stream procs */
+static const stream_procs s_aos_procs = {
+ s_aos_available, s_aos_seek, s_aos_reset,
+ s_aos_flush, s_aos_close, s_aos_process,
+ NULL /* no s_aos_switch */
+};
+
+static int
+make_aos(i_ctx_t *i_ctx_p, os_ptr op, int blk_sz, int blk_sz_last, uint file_sz)
+{
+ stream *s;
+ aos_state_t *ss;
+ byte *buf;
+ const int aos_buf_size = 1024; /* arbitrary */
+ uint save_space = icurrent_space;
+ ialloc_set_space(idmemory, r_space(op));
+
+ s = s_alloc(imemory, "aos_stream");
+ ss = (aos_state_t *)s_alloc_state(imemory, &st_aos_state, "st_aos_state");
+ buf = gs_alloc_bytes(imemory, aos_buf_size, "aos_stream_buf");
+ if (s == 0 || ss == 0 || buf == 0) {
+ gs_free_object(imemory, buf, "aos_stream_buf");
+ gs_free_object(imemory, ss, "st_aos_state");
+ gs_free_object(imemory, s, "aos_stream");
+ ialloc_set_space(idmemory, save_space);
+ return_error(gs_error_VMerror);
+ }
+ ialloc_set_space(idmemory, save_space);
+ ss->templat = &s_aos_template;
+ ss->blocks = *op;
+ ss->s = s;
+ ss->blk_sz = blk_sz;
+ ss->blk_sz_last = blk_sz_last;
+ ss->file_sz = file_sz;
+ s_std_init(s, buf, aos_buf_size, &s_aos_procs, s_mode_read + s_mode_seek);
+ s->state = (stream_state *)ss;
+ s->file_offset = 0;
+ s->file_limit = max_long;
+ s->close_at_eod = false;
+ s->read_id = 1;
+ make_stream_file(op, s, "r");
+ return 0;
+}
+
+/* Return the number of available bytes */
+static int
+s_aos_available(stream *s, gs_offset_t *pl)
+{
+ *pl = ((aos_state_t *)s->state)->file_sz - stell(s);
+ return 0;
+}
+
+/* Seek in a string being read. Return 0 if OK, ERRC if not. */
+static int
+s_aos_seek(register stream * s, gs_offset_t pos)
+{
+ uint end = s->srlimit - s->cbuf + 1;
+ long offset = pos - s->position;
+
+ if (offset >= 0 && offset <= end) { /* Staying within the same buffer */
+ s->srptr = s->cbuf + offset - 1;
+ return 0;
+ }
+ if (pos < 0 || pos > s->file_limit)
+ return ERRC;
+ s->srptr = s->srlimit = s->cbuf - 1;
+ s->end_status = 0;
+ s->position = pos;
+ return 0;
+}
+
+static void
+s_aos_reset(stream *s)
+{
+ /* PLRM definition of reset operator is strange. */
+ /* Rewind the file and discard the buffer. */
+ s->position = 0;
+ s->srptr = s->srlimit = s->cbuf - 1;
+ s->end_status = 0;
+}
+
+static int
+s_aos_flush(stream *s)
+{
+ s->position = ((aos_state_t *)s->state)->file_sz;
+ s->srptr = s->srlimit = s->cbuf - 1;
+ return 0;
+}
+
+static int
+s_aos_close(stream * s)
+{
+ gs_free_object(s->memory, s->cbuf, "s_aos_close(buffer)");
+ s->cbuf = 0;
+ /* Increment the IDs to prevent further access. */
+ s->read_id = s->write_id = (s->read_id | s->write_id) + 1;
+ return 0;
+}
+
+static int
+s_aos_process(stream_state * st, stream_cursor_read * ignore_pr,
+ stream_cursor_write * pw, bool last)
+{
+ int blk_i, blk_off, blk_cnt, status = 1;
+ uint count;
+ aos_state_t *ss = (aos_state_t *)st;
+ uint max_count = pw->limit - pw->ptr;
+ uint pos = stell(ss->s);
+ unsigned const char *data;
+ ref *blk_ref;
+
+ pos += sbufavailable(ss->s);
+ if (pos >= ss->file_sz)
+ return EOFC;
+ blk_i = pos / ss->blk_sz;
+ blk_off = pos % ss->blk_sz;
+ blk_cnt = r_size(&ss->blocks);
+ count = blk_i < blk_cnt - 1 ? ss->blk_sz : ss->blk_sz_last;
+ blk_ref = ss->blocks.value.refs;
+ data = blk_ref[blk_i].value.bytes;
+
+ if (max_count > count - blk_off) {
+ max_count = count - blk_off;
+ if (blk_i == blk_cnt - 1)
+ status = EOFC;
+ }
+ memcpy(pw->ptr+1, data + blk_off, max_count);
+ pw->ptr += max_count;
+ return status;
+}
+
+/* ---------------- Initialization procedure ---------------- */
+
+const op_def zfrsd_op_defs[] =
+{
+ {"2.reusablestream", zreusablestream},
+ {"2.rsdparams", zrsdparams},
+ op_def_end(0)
+};
diff --git a/psi/zfrsd.h b/psi/zfrsd.h
new file mode 100644
index 000000000..7fa632bd7
--- /dev/null
+++ b/psi/zfrsd.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Reusable straem implementation */
+
+#ifndef zfrsd_INCLUDED
+# define zfrsd_INCLUDED
+
+/* Make a reusable string stream. */
+int
+make_rss(i_ctx_t *i_ctx_p, os_ptr op, const byte * data, uint size,
+ uint string_space, long offset, long length, bool is_bytestring);
+
+#endif /* zfrsd_INCLUDED */
+
+/* Reusable straem implementation */
+
+#ifndef zfrsd_INCLUDED
+# define zfrsd_INCLUDED
+
+/* Make a reusable string stream. */
+int
+make_rss(i_ctx_t *i_ctx_p, os_ptr op, const byte * data, uint size,
+ uint string_space, long offset, long length, bool is_bytestring);
+
+#endif /* zfrsd_INCLUDED */
diff --git a/psi/zfsample.c b/psi/zfsample.c
new file mode 100644
index 000000000..d2aeeed47
--- /dev/null
+++ b/psi/zfsample.c
@@ -0,0 +1,718 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Sample data to create a type 0 function */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gxcspace.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifunc.h"
+#include "ostack.h"
+#include "store.h"
+#include "gsfunc0.h"
+#include "gscdevn.h"
+#include "zfunc.h"
+#include "zcolor.h"
+
+/*
+ * We store the data in a string. Since the max size for a string is 64k,
+ * we use that as our max data size.
+ */
+#define MAX_DATA_SIZE 0x10000
+/*
+ * We cannot handle more than 16 inputs. Otherwise the the data will not
+ * fit within MAX_DATA_SIZE.
+ */
+#define MAX_NUM_INPUTS 16
+/*
+ * This value is rather arbitrary.
+ */
+#define MAX_NUM_OUTPUTS 128
+
+/* --- Build sampled data function --- */
+
+/*
+ * This structure is used to hold data required while collecting samples
+ * for a type 0 function (sampled data).
+ */
+struct gs_sampled_data_enum_s {
+ int indexes[MAX_NUM_INPUTS];
+ int o_stack_depth; /* used to verify stack while sampling */
+ gs_function_t * pfn;
+};
+
+typedef struct gs_sampled_data_enum_s gs_sampled_data_enum;
+
+gs_private_st_ptrs1(st_gs_sampled_data_enum, gs_sampled_data_enum,
+ "gs_sampled_data_enum", gs_sampled_data_enum_enum_ptrs,
+ gs_sampled_data_enum_reloc_ptrs, pfn);
+
+/* Forward references */
+
+static int cube_build_func0(const ref * pdict,
+ gs_function_Sd_params_t * params, gs_memory_t *mem);
+static int sampled_data_setup(i_ctx_t *i_ctx_p, gs_function_t *pfn,
+ const ref * pproc, int (*finish_proc)(i_ctx_t *),
+ gs_memory_t * mem);
+static int sampled_data_sample(i_ctx_t *i_ctx_p);
+static int sampled_data_continue(i_ctx_t *i_ctx_p);
+static int sampled_data_finish(i_ctx_t *i_ctx_p);
+
+static gs_sampled_data_enum * gs_sampled_data_enum_alloc
+ (gs_memory_t * mem, client_name_t cname);
+
+/*
+ * Collect data for a type 0 (sampled data) function
+ * <dict> .buildsampledfunction <function_struct>
+ *
+ * The following keys are used from the dictionary:
+ * Function (required)
+ * Domain (required)
+ * Range (required)
+ * Size (optional) If Size is not specified then a default value is determined
+ * based upon the number of inputs and outputs.
+ * BitsPerSample (required) Only 8, 16, 24, and 32 accepted,
+ * The remaining keys are ignored.
+ */
+static int
+zbuildsampledfunction(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const ref * pdict = op;
+ ref * pfunc;
+ int code = 0;
+ gs_function_t *pfn;
+ gs_function_Sd_params_t params = {0};
+
+ check_type(*pdict, t_dictionary);
+ /*
+ * Check procedure to be sampled.
+ */
+ if (dict_find_string(pdict, "Function", &pfunc) <= 0)
+ return_error(gs_error_rangecheck);
+ check_proc(*pfunc);
+ /*
+ * Set up the hyper cube function data structure.
+ */
+ code = cube_build_func0(pdict, &params, imemory);
+ if (code < 0)
+ return code;
+ /*
+ * This is temporary. We will call gs_function_Sd_init again after
+ * we have collected the cube data. We are doing it now because we need
+ * a function structure created (along with its GC enumeration stuff)
+ * that we can use while collecting the cube data. We will call
+ * the routine again after the cube data is collected to correctly
+ * initialize the function.
+ */
+ code = gs_function_Sd_init(&pfn, &params, imemory);
+ if (code < 0)
+ return code;
+ /*
+ * Now setup to collect the sample data.
+ */
+ return sampled_data_setup(i_ctx_p, pfn, pfunc, sampled_data_finish, imemory);
+}
+
+/* ------- Internal procedures ------- */
+
+#define bits2bytes(x) ((x) >> 3) /* Convert bit count to byte count */
+
+/*
+ * This routine will verify that the requested data hypercube parameters will require
+ * a data storage size less than or equal to the MAX_DATA_SIZE.
+ */
+static bool
+valid_cube_size(int num_inputs, int num_outputs, int sample_size, const int Size[])
+{
+ int i, total_size = num_outputs * sample_size;
+
+ for (i = 0; i < num_inputs; i++) {
+ if (Size[i] <= 0 || Size[i] > MAX_DATA_SIZE / total_size)
+ return false;
+ total_size *= Size[i];
+ }
+ return true;
+}
+
+/*
+ * This routine is used to determine a default value for the sampled data size.
+ * As a default, we will build a hyper cube with each side having the same
+ * size. The space requirements for a hypercube grow exponentially with the
+ * number of dimensions. Thus we must use fewer points if our functions has
+ * many inputs. The values returned were chosen simply to given a reasonable
+ * tradeoff between keeping storage requirements low but still having enough
+ * points per side to minimize loss of information.
+ *
+ * We do check to see if the data will fit using our initial guess. If not
+ * then we decrement the size of each edge until it fits. We will return a
+ * gs_error_rangecheck error if the cube can not fit into the maximum size.
+ * On exit the Size array contains the cube size (if a valid size was found).
+ */
+static int
+determine_sampled_data_size(int num_inputs, int num_outputs,
+ int sample_size, int Size[])
+{
+ static const int size_list[] = {512, 50, 20, 10, 7, 5, 4, 3};
+ int i, size;
+
+ /* Start with initial guess at cube size */
+ if (num_inputs > 0 && num_inputs <= 8)
+ size = size_list[num_inputs - 1];
+ else
+ size = 2;
+ /*
+ * Verify that the cube will fit into MAX_DATA_SIZE. If not then
+ * decrement the cube size until it will fit.
+ */
+ while (true) {
+ /* Fill Size array with value. */
+ for (i = 0; i < num_inputs; i++)
+ Size[i] = size;
+
+ /* If we have reached the minimum size (2), don't bother checking if its 'valid'
+ * as there is nothing we cna do now if it isn't.
+ */
+ if (size > 2) {
+ if (valid_cube_size(num_inputs, num_outputs, sample_size, Size))
+ return 0; /* We have a valid size */
+ size--;
+ } else {
+ return 0;
+ }
+ }
+}
+
+/*
+ * Allocate the enumerator used while collecting sampled data. This enumerator
+ * is used to hold the various state data required while sampling.
+ */
+static gs_sampled_data_enum *
+gs_sampled_data_enum_alloc(gs_memory_t * mem, client_name_t cname)
+{
+ return gs_alloc_struct(mem, gs_sampled_data_enum,
+ &st_gs_sampled_data_enum, cname);
+}
+
+/*
+ * This routine will determine the location of a block of data
+ * in the hyper cube. Basically this does an index calculation
+ * for an n dimensional cube.
+ */
+static byte *
+cube_ptr_from_index(gs_function_Sd_params_t * params, int indexes[])
+{
+ int i, sum = indexes[params->m - 1];
+
+ for (i = params->m - 2; i >= 0; i--) {
+ sum *= params->Size[i];
+ sum += indexes[i];
+ }
+ return (byte *)(params->DataSource.data.str.data) +
+ sum * params->n * bits2bytes(params->BitsPerSample);
+}
+
+/*
+ * This routine will increment the index values for the hypercube. This
+ * is used for collecting the data. If we have incremented the
+ * last index beyond its last value then we return a true, else false;
+ */
+static bool
+increment_cube_indexes(gs_function_Sd_params_t * params, int indexes[])
+{
+ int i = 0;
+
+ while (true) {
+ /*
+ * Increment an index value for an edge and test if we have
+ * gone past the final value for the edge.
+ */
+ indexes[i]++;
+ if (indexes[i] < params->Size[i])
+ /*
+ * We have not reached the end of the edge. Exit but
+ * indicate that we are not done with the hypercube.
+ */
+ return false;
+ /*
+ * We have reached the end of one edge of the hypercube and we
+ * need to increment the next index.
+ */
+ indexes[i] = 0;
+ i++;
+ if (i == params->m)
+ /*
+ * We have finished the last edge of the hyper cube.
+ * We are done.
+ */
+ return true;
+ }
+}
+
+/*
+ * Fill in the data for a function type 0 parameter object to be used while
+ * we collect the data for the data cube. At the end of the process, we
+ * will create a function type 0 object to be used to calculate values
+ * as a replacement for the original function.
+ */
+static int
+cube_build_func0(const ref * pdict, gs_function_Sd_params_t * params,
+ gs_memory_t *mem)
+{
+ byte * bytes = 0;
+ int code, i;
+ int total_size;
+
+ if ((code = dict_int_param(pdict, "Order", 1, 3, 1, &params->Order)) < 0 ||
+ (code = dict_int_param(pdict, "BitsPerSample", 1, 32, 0,
+ &params->BitsPerSample)) < 0 ||
+ ((code = params->m =
+ fn_build_float_array(pdict, "Domain", false, true,
+ &params->Domain, mem)) < 0 ) ||
+ ((code = params->n =
+ fn_build_float_array(pdict, "Range", false, true,
+ &params->Range, mem)) < 0)
+ ) {
+ goto fail;
+ }
+ /*
+ * The previous logic set the size of m and n to the size of the Domain
+ * and Range arrays. This is twice the actual size. Correct this and
+ * check for valid values.
+ */
+ params->m >>= 1;
+ params->n >>= 1;
+ if (params->m == 0 || params->n == 0 ||
+ params->m > MAX_NUM_INPUTS || params->n > MAX_NUM_OUTPUTS) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto fail;
+ }
+ /*
+ * The Size array may or not be specified. If it is not specified then
+ * we need to determine a set of default values for the Size array.
+ */
+ {
+ int *ptr = (int *)
+ gs_alloc_byte_array(mem, params->m, sizeof(int), "Size");
+
+ if (ptr == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ params->Size = ptr;
+ code = dict_ints_param(mem, pdict, "Size", params->m, ptr);
+ if (code < 0)
+ goto fail;
+ if (code == 0) {
+ /*
+ * The Size array has not been specified. Determine a default
+ * set of values.
+ */
+ code = determine_sampled_data_size(params->m, params->n,
+ params->BitsPerSample, (int *)params->Size);
+ if (code < 0)
+ goto fail;
+ }
+ else { /* Size array specified - verify valid */
+ if (code != params->m || !valid_cube_size(params->m, params->n,
+ params->BitsPerSample, params->Size))
+ code = gs_note_error(gs_error_rangecheck);
+ goto fail;
+ }
+ }
+ /*
+ * Determine space required for the sample data storage.
+ */
+ total_size = params->n * bits2bytes(params->BitsPerSample);
+ for (i = 0; i < params->m; i++)
+ total_size *= params->Size[i];
+ /*
+ * Allocate space for the data cube itself.
+ */
+ bytes = gs_alloc_byte_array(mem, total_size, 1, "cube_build_func0(bytes)");
+ if (!bytes) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ data_source_init_bytes(&params->DataSource,
+ (const unsigned char *)bytes, total_size);
+
+ return 0;
+
+fail:
+ gs_function_Sd_free_params(params, mem);
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+}
+
+/*
+ * Layout of stuff pushed on estack while collecting the sampled data.
+ * The data is saved there since it is safe from attack by the procedure
+ * being sampled and is convient.
+ *
+ * finishing procedure (or 0)
+ * procedure being sampled
+ * enumeration structure (as bytes)
+ */
+#define estack_storage 3
+#define esp_finish_proc (*real_opproc(esp - 2))
+#define sample_proc esp[-1]
+#define senum r_ptr(esp, gs_sampled_data_enum)
+/*
+ * Sone invalid tint transform functions pop more items off of the stack
+ * then they are supposed to use. This is a violation of the PLRM however
+ * this is done by Adobe and we have to handle the situation. This is
+ * a kludge but we set aside some unused stack space below the input
+ * variables. The tint transform can trash this without causing any
+ * real problems.
+ */
+#define O_STACK_PAD 3
+
+/*
+ * Set up to collect the data for the sampled function. This is used for
+ * those alternate tint transforms that cannot be converted into a
+ * type 4 function.
+ */
+static int
+sampled_data_setup(i_ctx_t *i_ctx_p, gs_function_t *pfn,
+ const ref * pproc, int (*finish_proc)(i_ctx_t *), gs_memory_t * mem)
+{
+ os_ptr op = osp;
+ gs_sampled_data_enum *penum;
+ int i;
+ gs_function_Sd_params_t * params = (gs_function_Sd_params_t *)&pfn->params;
+
+ check_estack(estack_storage + 1); /* Verify space on estack */
+ check_ostack(params->m + O_STACK_PAD); /* and the operand stack */
+ check_ostack(params->n + O_STACK_PAD);
+
+ /*
+ * Allocate space for the enumerator data structure.
+ */
+ penum = gs_sampled_data_enum_alloc(imemory, "zbuildsampledfuntion(params)");
+ if (penum == NULL)
+ return_error(gs_error_VMerror);
+
+ /* Initialize data in the enumeration structure */
+
+ penum->pfn = pfn;
+ for(i=0; i< params->m; i++)
+ penum->indexes[i] = 0;
+ /*
+ * Save stack depth for checking the correct number of values on stack
+ * after the function, which is being sampled, is called.
+ */
+ penum->o_stack_depth = ref_stack_count(&o_stack);
+ /*
+ * Note: As previously mentioned, we are putting some spare (unused) stack
+ * space under the input values in case the function unbalances the stack.
+ * It is possible for the function to pop or change values on the stack
+ * outside of the input values. (This has been found to happen with some
+ * proc sets from Adobe.)
+ */
+ push(O_STACK_PAD);
+ for (i = 0; i < O_STACK_PAD; i++) /* Set space = null */
+ make_null(op - i);
+
+ /* Push everything on the estack */
+
+ esp += estack_storage;
+ make_op_estack(esp - 2, finish_proc); /* Finish proc onto estack */
+ sample_proc = *pproc; /* Save function to be sampled */
+ make_istruct(esp, 0, penum); /* Color cube enumeration structure */
+ push_op_estack(sampled_data_sample); /* Start sampling data */
+ return o_push_estack;
+}
+
+/*
+ * Set up to collect the next sampled data value.
+ */
+static int
+sampled_data_sample(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_sampled_data_enum *penum = senum;
+ ref proc;
+ gs_function_Sd_params_t * params =
+ (gs_function_Sd_params_t *)&penum->pfn->params;
+ int num_inputs = params->m;
+ int i;
+
+ /* Put set of input values onto the stack. */
+ push(num_inputs);
+ for (i = 0; i < num_inputs; i++) {
+ double dmin = params->Domain[2 * i];
+ double dmax = params->Domain[2 * i + 1];
+
+ make_real(op - num_inputs + i + 1, (float) (
+ penum->indexes[i] * (dmax - dmin)/(params->Size[i] - 1) + dmin));
+ }
+
+ proc = sample_proc; /* Get procedure from storage */
+ push_op_estack(sampled_data_continue); /* Put 'save' routine on estack, after sample proc */
+ *++esp = proc; /* Put procedure to be executed */
+ return o_push_estack;
+}
+
+/*
+ * Continuation procedure for processing sampled values.
+ */
+static int
+sampled_data_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_sampled_data_enum *penum = senum;
+ gs_function_Sd_params_t * params =
+ (gs_function_Sd_params_t *)&penum->pfn->params;
+ int i, j, num_out = params->n;
+ int code = 0;
+ byte * data_ptr;
+ double sampled_data_value_max = (double)((1 << params->BitsPerSample) - 1);
+ int bps = bits2bytes(params->BitsPerSample), stack_depth_adjust = 0;
+
+ /*
+ * Check to make sure that the procedure produced the correct number of
+ * values. If not, move the stack back to where it belongs and abort
+ */
+ if (num_out + O_STACK_PAD + penum->o_stack_depth != ref_stack_count(&o_stack)) {
+ stack_depth_adjust = ref_stack_count(&o_stack) - penum->o_stack_depth;
+
+ if (stack_depth_adjust < 0) {
+ /*
+ * If we get to here then there were major problems. The function
+ * removed too many items off of the stack. We had placed extra
+ * (unused) stack stack space to allow for this but the function
+ * exceeded even that. Data on the stack may have been lost.
+ * The only thing that we can do is move the stack pointer back and
+ * hope. (We have not seen real Postscript files that have this
+ * problem.)
+ */
+ push(-stack_depth_adjust);
+ ifree_object(penum->pfn, "sampled_data_continue(pfn)");
+ ifree_object(penum, "sampled_data_continue((enum)");
+ return_error(gs_error_undefinedresult);
+ }
+ }
+
+ /* Save data from the given function */
+ data_ptr = cube_ptr_from_index(params, penum->indexes);
+ for (i=0; i < num_out; i++) {
+ ulong cv;
+ double value;
+ double rmin = params->Range[2 * i];
+ double rmax = params->Range[2 * i + 1];
+
+ code = real_param(op + i - num_out + 1, &value);
+ if (code < 0)
+ return code;
+ if (value < rmin)
+ value = rmin;
+ else if (value > rmax)
+ value = rmax;
+ value = (value - rmin) / (rmax - rmin); /* Convert to 0 to 1.0 */
+ cv = (int) (value * sampled_data_value_max + 0.5);
+ for (j = 0; j < bps; j++)
+ data_ptr[bps * i + j] = (byte)(cv >> ((bps - 1 - j) * 8)); /* MSB first */
+ }
+ pop(num_out); /* Move op to base of result values */
+
+ /* Check if we are done collecting data. */
+
+ if (increment_cube_indexes(params, penum->indexes)) {
+ if (stack_depth_adjust == 0)
+ pop(O_STACK_PAD); /* Remove spare stack space */
+ else
+ pop(stack_depth_adjust - num_out);
+ /* Execute the closing procedure, if given */
+ code = 0;
+ if (esp_finish_proc != 0)
+ code = esp_finish_proc(i_ctx_p);
+
+ return code;
+ } else {
+ if (stack_depth_adjust) {
+ stack_depth_adjust -= num_out;
+ push(O_STACK_PAD - stack_depth_adjust);
+ for (i=0;i<O_STACK_PAD - stack_depth_adjust;i++)
+ make_null(op - i);
+ }
+ }
+
+ /* Now get the data for the next location */
+
+ return sampled_data_sample(i_ctx_p);
+}
+
+/*
+ * We have collected all of the sample data. Create a type 0 function stucture.
+ */
+static int
+sampled_data_finish(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_sampled_data_enum *penum = senum;
+ /* Build a type 0 function using the given parameters */
+ gs_function_Sd_params_t * params =
+ (gs_function_Sd_params_t *)&penum->pfn->params;
+ gs_function_t * pfn;
+ ref cref; /* closure */
+ int code = gs_function_Sd_init(&pfn, params, imemory);
+
+ if (code < 0)
+ return code;
+
+ code = ialloc_ref_array(&cref, a_executable | a_execute, 2,
+ "sampled_data_finish(cref)");
+ if (code < 0)
+ return code;
+
+ make_istruct_new(cref.value.refs, a_executable | a_execute, pfn);
+ make_oper_new(cref.value.refs + 1, 0, zexecfunction);
+ ref_assign(op, &cref);
+
+ esp -= estack_storage;
+ ifree_object(penum->pfn, "sampled_data_finish(pfn)");
+ ifree_object(penum, "sampled_data_finish(enum)");
+ return o_pop_estack;
+}
+
+int make_sampled_function(i_ctx_t * i_ctx_p, ref *arr, ref *pproc, gs_function_t **func)
+{
+ int code = 0, *ptr, i, total_size, num_components, CIESubst;
+ byte * bytes = 0;
+ float *fptr;
+ gs_function_t *pfn = *func;
+ gs_function_Sd_params_t params = {0};
+ ref alternatespace, *palternatespace = &alternatespace;
+ PS_colour_space_t *space, *altspace;
+
+ code = get_space_object(i_ctx_p, arr, &space);
+ if (code < 0)
+ return code;
+ if (!space->alternateproc)
+ return gs_error_typecheck;
+ code = space->alternateproc(i_ctx_p, arr, &palternatespace, &CIESubst);
+ if (code < 0)
+ return code;
+ code = get_space_object(i_ctx_p, palternatespace, &altspace);
+ if (code < 0)
+ return code;
+ /*
+ * Set up the hyper cube function data structure.
+ */
+ params.Order = 3;
+ params.BitsPerSample = 16;
+
+ code = space->numcomponents(i_ctx_p, arr, &num_components);
+ if (code < 0)
+ return code;
+ fptr = (float *)gs_alloc_byte_array(imemory, num_components * 2, sizeof(float), "make_sampled_function(Domain)");
+ if (!fptr)
+ return gs_error_VMerror;
+ code = space->domain(i_ctx_p, arr, fptr);
+ if (code < 0) {
+ gs_free_const_object(imemory, fptr, "make_sampled_function(Domain)");
+ return code;
+ }
+ params.Domain = fptr;
+ params.m = num_components;
+
+ code = altspace->numcomponents(i_ctx_p, palternatespace, &num_components);
+ if (code < 0) {
+ gs_free_const_object(imemory, params.Domain, "make_type4_function(Domain)");
+ return code;
+ }
+ fptr = (float *)gs_alloc_byte_array(imemory, num_components * 2, sizeof(float), "make_sampled_function(Range)");
+ if (!fptr) {
+ gs_free_const_object(imemory, params.Domain, "make_sampled_function(Domain)");
+ return gs_error_VMerror;
+ }
+ code = altspace->range(i_ctx_p, palternatespace, fptr);
+ if (code < 0) {
+ gs_free_const_object(imemory, params.Domain, "make_sampled_function(Domain)");
+ gs_free_const_object(imemory, fptr, "make_sampled_function(Range)");
+ return code;
+ }
+ params.Range = fptr;
+ params.n = num_components;
+
+ /*
+ * The Size array may or not be specified. If it is not specified then
+ * we need to determine a set of default values for the Size array.
+ */
+ ptr = (int *)gs_alloc_byte_array(imemory, params.m, sizeof(int), "Size");
+ if (ptr == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ params.Size = ptr;
+ /*
+ * Determine a default
+ * set of values.
+ */
+ code = determine_sampled_data_size(params.m, params.n,
+ params.BitsPerSample, (int *)params.Size);
+ if (code < 0)
+ goto fail;
+ /*
+ * Determine space required for the sample data storage.
+ */
+ total_size = params.n * bits2bytes(params.BitsPerSample);
+ for (i = 0; i < params.m; i++)
+ total_size *= params.Size[i];
+ /*
+ * Allocate space for the data cube itself.
+ */
+ bytes = gs_alloc_byte_array(imemory, total_size, 1, "cube_build_func0(bytes)");
+ if (!bytes) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ data_source_init_bytes(&params.DataSource,
+ (const unsigned char *)bytes, total_size);
+
+ /*
+ * This is temporary. We will call gs_function_Sd_init again after
+ * we have collected the cube data. We are doing it now because we need
+ * a function structure created (along with its GC enumeration stuff)
+ * that we can use while collecting the cube data. We will call
+ * the routine again after the cube data is collected to correctly
+ * initialize the function.
+ */
+ code = gs_function_Sd_init(&pfn, &params, imemory);
+ if (code < 0)
+ return code;
+ /*
+ * Now setup to collect the sample data.
+ */
+ return sampled_data_setup(i_ctx_p, pfn, pproc, sampled_data_finish, imemory);
+
+fail:
+ gs_function_Sd_free_params(&params, imemory);
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfsample_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"1.buildsampledfunction", zbuildsampledfunction},
+ op_def_end(0)
+};
diff --git a/psi/zfsha2.c b/psi/zfsha2.c
new file mode 100644
index 000000000..5f22100e6
--- /dev/null
+++ b/psi/zfsha2.c
@@ -0,0 +1,43 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* SHA256Encode filter creation */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "stream.h"
+#include "strimpl.h"
+#include "ialloc.h"
+#include "ifilter.h"
+#include "ssha2.h"
+
+/* <source> SHA256Encode/filter <file> */
+/* <source> <dict> SHA256Encode/filter <file> */
+static int
+zSHA256E(i_ctx_t *i_ctx_p)
+{
+ return filter_write_simple(i_ctx_p, &s_SHA256E_template);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfsha2_op_defs[] =
+{
+ op_def_begin_filter(),
+ {"1SHA256Encode", zSHA256E},
+ op_def_end(0)
+};
diff --git a/psi/zfunc.c b/psi/zfunc.c
new file mode 100644
index 000000000..bb22e6f39
--- /dev/null
+++ b/psi/zfunc.c
@@ -0,0 +1,417 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Generic PostScript language interface to Functions */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gscdefs.h"
+#include "gsfunc.h"
+#include "gsstruct.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifunc.h"
+#include "store.h"
+#include "zfunc.h"
+
+/*#define TEST*/
+
+/* Define the maximum depth of nesting of subsidiary functions. */
+#define MAX_SUB_FUNCTION_DEPTH 3
+
+/* ------ Operators ------ */
+
+/* Create a function procedure from a function structure. */
+static int
+make_function_proc(i_ctx_t *i_ctx_p, ref *op, gs_function_t *pfn)
+{
+ ref cref; /* closure */
+ int code;
+
+ code = ialloc_ref_array(&cref, a_executable | a_execute, 2,
+ ".buildfunction");
+ if (code < 0)
+ return code;
+ make_istruct_new(cref.value.refs, a_executable | a_execute, pfn);
+ make_oper_new(cref.value.refs + 1, 0, zexecfunction);
+ ref_assign(op, &cref);
+ return 0;
+}
+
+/* <dict> .buildfunction <function_proc> */
+static int
+zbuildfunction(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_function_t *pfn;
+ int code = fn_build_function(i_ctx_p, op, &pfn, imemory, 0, 0);
+
+ if (code < 0)
+ return code;
+ code = make_function_proc(i_ctx_p, op, pfn);
+ if (code < 0)
+ gs_function_free(pfn, true, imemory);
+ return 0;
+}
+
+int buildfunction(i_ctx_t * i_ctx_p, ref *arr, ref *pproc, int type)
+{
+ os_ptr op = osp;
+ gs_function_t *pfn=NULL;
+ int code=0;
+
+ switch(type) {
+ case 0:
+ code = make_sampled_function(i_ctx_p, arr, pproc, &pfn);
+ break;
+ case 4:
+ code = make_type4_function(i_ctx_p, arr, pproc, &pfn);
+ if (code == 0) {
+ code = make_function_proc(i_ctx_p, op, pfn);
+ if (code < 0) {
+ gs_function_free(pfn, true, imemory);
+ }
+ }
+ break;
+ }
+ return code;
+}
+
+#ifdef TEST
+
+/* <function_proc> <array> .scalefunction <function_proc> */
+static int
+zscalefunction(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_function_t *pfn;
+ gs_function_t *psfn;
+ gs_range_t *ranges;
+ int code;
+ uint i;
+
+ check_proc(op[-1]);
+ pfn = ref_function(op - 1);
+ if (pfn == 0 || !r_is_array(op))
+ return_error(gs_error_typecheck);
+ if (r_size(op) != 2 * pfn->params.n)
+ return_error(gs_error_rangecheck);
+ ranges = (gs_range_t *)
+ gs_alloc_byte_array(imemory, pfn->params.n, sizeof(gs_range_t),
+ "zscalefunction");
+ if (ranges == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0; i < pfn->params.n; ++i) {
+ ref rval[2];
+ float val[2];
+
+ if ((code = array_get(op, 2 * i, &rval[0])) < 0 ||
+ (code = array_get(op, 2 * i + 1, &rval[1])) < 0 ||
+ (code = float_params(rval + 1, 2, val)) < 0)
+ return code;
+ ranges[i].rmin = val[0];
+ ranges[i].rmax = val[1];
+ }
+ code = gs_function_make_scaled(pfn, &psfn, ranges, imemory);
+ gs_free_object(imemory, ranges, "zscalefunction");
+ if (code < 0 ||
+ (code = make_function_proc(i_ctx_p, op - 1, psfn)) < 0) {
+ gs_function_free(psfn, true, imemory);
+ return code;
+ }
+ pop(1);
+ return 0;
+}
+
+#endif /* TEST */
+
+/* <in1> ... <function_struct> %execfunction <out1> ... */
+int
+zexecfunction(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ /*
+ * Since this operator's name begins with %, the name is not defined
+ * in systemdict. The only place this operator can ever appear is
+ * in the execute-only closure created by .buildfunction.
+ * Therefore, in principle it is unnecessary to check the argument.
+ * However, we do a little checking anyway just on general
+ * principles. Note that since the argument may be an instance of
+ * any subclass of gs_function_t, we currently have no way to check
+ * its type.
+ */
+ if (!r_is_struct(op) ||
+ !r_has_masked_attrs(op, a_executable | a_execute, a_executable | a_all)
+ )
+ return_error(gs_error_typecheck);
+ {
+ gs_function_t *pfn = (gs_function_t *) op->value.pstruct;
+ int m = pfn->params.m, n = pfn->params.n;
+ int diff = n - (m + 1);
+
+ if (diff > 0)
+ check_ostack(diff);
+ {
+ float params[20]; /* arbitrary size, just to avoid allocs */
+ float *in;
+ float *out;
+ int code = 0;
+
+ if (m + n <= countof(params)) {
+ in = params;
+ } else {
+ in = (float *)ialloc_byte_array(m + n, sizeof(float),
+ "%execfunction(in/out)");
+ if (in == 0)
+ code = gs_note_error(gs_error_VMerror);
+ }
+ out = in + m;
+ if (code < 0 ||
+ (code = float_params(op - 1, m, in)) < 0 ||
+ (code = gs_function_evaluate(pfn, in, out)) < 0
+ )
+ DO_NOTHING;
+ else {
+ if (diff > 0)
+ push(diff); /* can't fail */
+ else if (diff < 0) {
+ pop(-diff);
+ op = osp;
+ }
+ code = make_floats(op + 1 - n, out, n);
+ }
+ if (in != params)
+ ifree_object(in, "%execfunction(in)");
+ return code;
+ }
+ }
+}
+
+/*
+ * <proc> .isencapfunction <bool>
+ *
+ * This routine checks if a given Postscript procedure is an "encapsulated"
+ * function of the type made by .buildfunction. These functions can then
+ * be executed without executing the interpreter. These functions can be
+ * executed directly from within C code inside the graphics library.
+ */
+static int
+zisencapfunction(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_function_t *pfn;
+
+ check_proc(*op);
+ pfn = ref_function(op);
+ make_bool(op, pfn != NULL);
+ return 0;
+}
+
+/* ------ Procedures ------ */
+
+/* Build a function structure from a PostScript dictionary. */
+int
+fn_build_function(i_ctx_t *i_ctx_p, const ref * op, gs_function_t ** ppfn, gs_memory_t *mem,
+ const float *shading_domain, const int num_inputs)
+{
+ return fn_build_sub_function(i_ctx_p, op, ppfn, 0, mem, shading_domain, num_inputs);
+}
+int
+fn_build_sub_function(i_ctx_t *i_ctx_p, const ref * op, gs_function_t ** ppfn,
+ int depth, gs_memory_t *mem, const float *shading_domain, const int num_inputs)
+{
+ int j, code, type;
+ uint i;
+ gs_function_params_t params;
+
+ if (depth > MAX_SUB_FUNCTION_DEPTH)
+ return_error(gs_error_limitcheck);
+ check_type(*op, t_dictionary);
+ code = dict_int_param(op, "FunctionType", 0, max_int, -1, &type);
+ if (code < 0)
+ return code;
+ for (i = 0; i < build_function_type_table_count; ++i)
+ if (build_function_type_table[i].type == type)
+ break;
+ if (i == build_function_type_table_count)
+ return_error(gs_error_rangecheck);
+ /* Collect parameters common to all function types. */
+ params.Domain = 0;
+ params.Range = 0;
+ code = fn_build_float_array(op, "Domain", true, true, &params.Domain, mem);
+ if (code < 0) {
+ gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "Domain");
+ goto fail;
+ }
+ params.m = code >> 1;
+ for (j = 0; j < params.m << 1; j += 2) {
+ if (params.Domain[j] >= params.Domain[j + 1]) {
+ code = gs_note_error(gs_error_rangecheck);
+ gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "Domain");
+ goto fail;
+ }
+ }
+ if (shading_domain) {
+ /* Each function dictionary's domain must be a superset of that of
+ * the shading dictionary. PLRM3 p.265. CET 12-14c. We do this check
+ * here because Adobe checks Domain before checking other parameters.
+ */
+ if (num_inputs != params.m)
+ code = gs_note_error(gs_error_rangecheck);
+ for (j = 0; j < 2*num_inputs && code >= 0; j += 2) {
+ if (params.Domain[j] > shading_domain[j] ||
+ params.Domain[j+1] < shading_domain[j+1]
+ ) {
+ code = gs_note_error(gs_error_rangecheck);
+ }
+ }
+ if (code < 0) {
+ gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "Domain");
+ goto fail;
+ }
+ }
+ code = fn_build_float_array(op, "Range", false, true, &params.Range, mem);
+ if (code < 0)
+ goto fail;
+ params.n = code >> 1;
+ /* Finish building the function. */
+ /* If this fails, it will free all the parameters. */
+ return (*build_function_type_table[i].proc)
+ (i_ctx_p, op, &params, depth + 1, ppfn, mem);
+fail:
+ gs_free_const_object(mem, params.Range, "Range");
+ gs_free_const_object(mem, params.Domain, "Domain");
+ return code;
+}
+
+/*
+ * Collect a heap-allocated array of floats. If the key is missing, set
+ * *pparray = 0 and return 0; otherwise set *pparray and return the number
+ * of elements. Note that 0-length arrays are acceptable, so if the value
+ * returned is 0, the caller must check whether *pparray == 0.
+ */
+int
+fn_build_float_array(const ref * op, const char *kstr, bool required,
+ bool even, const float **pparray, gs_memory_t *mem)
+{
+ ref *par;
+ int code;
+
+ *pparray = 0;
+ if (dict_find_string(op, kstr, &par) <= 0)
+ return (required ? gs_note_error(gs_error_rangecheck) : 0);
+ if (!r_is_array(par))
+ return_error(gs_error_typecheck);
+ {
+ uint size = r_size(par);
+ float *ptr = (float *)
+ gs_alloc_byte_array(mem, size, sizeof(float), kstr);
+
+ if (ptr == 0)
+ return_error(gs_error_VMerror);
+ code = dict_float_array_check_param(mem, op, kstr, size,
+ ptr, NULL,
+ 0, gs_error_rangecheck);
+ if (code < 0 || (even && (code & 1) != 0)) {
+ gs_free_object(mem, ptr, kstr);
+ return(code < 0 ? code : gs_note_error(gs_error_rangecheck));
+ }
+ *pparray = ptr;
+ }
+ return code;
+}
+
+/*
+ * Similar to fn_build_float_array() except
+ * - numeric parameter is accepted and converted to 1-element array
+ * - number of elements is not checked for even/odd
+ */
+int
+fn_build_float_array_forced(const ref * op, const char *kstr, bool required,
+ const float **pparray, gs_memory_t *mem)
+{
+ ref *par;
+ int code;
+ uint size;
+ float *ptr;
+
+ *pparray = 0;
+ if (dict_find_string(op, kstr, &par) <= 0)
+ return (required ? gs_note_error(gs_error_rangecheck) : 0);
+
+ if( r_is_array(par) )
+ size = r_size(par);
+ else if(r_type(par) == t_integer || r_type(par) == t_real)
+ size = 1;
+ else
+ return_error(gs_error_typecheck);
+ ptr = (float *)gs_alloc_byte_array(mem, size, sizeof(float), kstr);
+
+ if (ptr == 0)
+ return_error(gs_error_VMerror);
+ if(r_is_array(par) )
+ code = dict_float_array_check_param(mem, op, kstr,
+ size, ptr, NULL,
+ 0, gs_error_rangecheck);
+ else {
+ code = dict_float_param(op, kstr, 0., ptr); /* defailt cannot happen */
+ if( code == 0 )
+ code = 1;
+ }
+
+ if (code < 0 ) {
+ gs_free_object(mem, ptr, kstr);
+ return code;
+ }
+ *pparray = ptr;
+ return code;
+}
+
+/*
+ * If a PostScript object is a Function procedure, return the function
+ * object, otherwise return 0.
+ */
+gs_function_t *
+ref_function(const ref *op)
+{
+ if (r_has_type(op, t_array) &&
+ r_has_masked_attrs(op, a_executable | a_execute,
+ a_executable | a_all) &&
+ r_size(op) == 2 &&
+ r_has_type_attrs(op->value.refs + 1, t_operator, a_executable) &&
+ op->value.refs[1].value.opproc == zexecfunction &&
+ r_is_struct(op->value.refs) &&
+ r_has_masked_attrs(op->value.refs, a_executable | a_execute,
+ a_executable | a_all)
+ )
+ return (gs_function_t *)op->value.refs->value.pstruct;
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfunc_op_defs[] =
+{
+ {"1.buildfunction", zbuildfunction},
+#ifdef TEST
+ {"2.scalefunction", zscalefunction},
+#endif /* TEST */
+ {"1%execfunction", zexecfunction},
+ {"1.isencapfunction", zisencapfunction},
+ op_def_end(0)
+};
diff --git a/psi/zfunc.h b/psi/zfunc.h
new file mode 100644
index 000000000..5e5789a82
--- /dev/null
+++ b/psi/zfunc.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 2 sethalftone support */
+
+#ifndef zfunc_INCLUDED
+# define zfunc_INCLUDED
+
+/* imported from zfsample.c */
+int make_sampled_function(i_ctx_t * i_ctx_p, ref *arr, ref *pproc, gs_function_t **func);
+/* imported from zfunc4.c */
+int make_type4_function(i_ctx_t * i_ctx_p, ref *arr, ref *pproc, gs_function_t **func);
+
+#endif /* zfunc_INCLUDED */
diff --git a/psi/zfunc0.c b/psi/zfunc0.c
new file mode 100644
index 000000000..15fda3248
--- /dev/null
+++ b/psi/zfunc0.c
@@ -0,0 +1,94 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PostScript language interface to FunctionType 0 (Sampled) Functions */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsdsrc.h"
+#include "gsfunc.h"
+#include "gsfunc0.h"
+#include "stream.h" /* for files.h */
+#include "files.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifunc.h"
+
+/* Check prototype */
+build_function_proc(gs_build_function_0);
+
+/* Finish building a FunctionType 0 (Sampled) function. */
+int
+gs_build_function_0(i_ctx_t *i_ctx_p, const ref *op, const gs_function_params_t * mnDR,
+ int depth, gs_function_t ** ppfn, gs_memory_t *mem)
+{
+ gs_function_Sd_params_t params;
+ ref *pDataSource;
+ int code;
+
+ *(gs_function_params_t *) & params = *mnDR;
+ params.Encode = params.Decode = NULL;
+ params.pole = NULL;
+ params.Size = params.array_step = params.stream_step = NULL;
+ if ((code = dict_find_string(op, "DataSource", &pDataSource)) <= 0)
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+ switch (r_type(pDataSource)) {
+ case t_string:
+ data_source_init_string2(&params.DataSource,
+ pDataSource->value.const_bytes,
+ r_size(pDataSource));
+ break;
+ case t_file: {
+ stream *s;
+
+ check_read_known_file_else(s, pDataSource, return_error,
+ return_error(gs_error_invalidfileaccess));
+ if (!(s->modes & s_mode_seek))
+ return_error(gs_error_ioerror);
+ data_source_init_stream(&params.DataSource, s);
+ break;
+ }
+ default:
+ return_error(gs_error_rangecheck);
+ }
+ if ((code = dict_int_param(op, "Order", 1, 3, 1, &params.Order)) < 0 ||
+ (code = dict_int_param(op, "BitsPerSample", 1, 32, 0,
+ &params.BitsPerSample)) < 0 ||
+ ((code = fn_build_float_array(op, "Encode", false, true, &params.Encode, mem)) != 2 * params.m && (code != 0 || params.Encode != 0)) ||
+ ((code = fn_build_float_array(op, "Decode", false, true, &params.Decode, mem)) != 2 * params.n && (code != 0 || params.Decode != 0))
+ ) {
+ goto fail;
+ } {
+ int *ptr = (int *)
+ gs_alloc_byte_array(mem, params.m, sizeof(int), "Size");
+
+ if (ptr == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ params.Size = ptr;
+ code = dict_ints_param(mem, op, "Size", params.m, ptr);
+ if (code != params.m)
+ goto fail;
+ }
+ code = gs_function_Sd_init(ppfn, &params, mem);
+ if (code >= 0)
+ return 0;
+fail:
+ gs_function_Sd_free_params(&params, mem);
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+}
diff --git a/psi/zfunc3.c b/psi/zfunc3.c
new file mode 100644
index 000000000..59de11a07
--- /dev/null
+++ b/psi/zfunc3.c
@@ -0,0 +1,141 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PostScript language interface to LL3 Functions */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsfunc3.h"
+#include "gsstruct.h"
+#include "stream.h" /* for files.h */
+#include "files.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifunc.h"
+#include "store.h"
+#include "igstate.h"
+
+/* Check prototypes */
+build_function_proc(gs_build_function_2);
+build_function_proc(gs_build_function_3);
+
+/* Finish building a FunctionType 2 (ExponentialInterpolation) function. */
+int
+gs_build_function_2(i_ctx_t *i_ctx_p, const ref *op, const gs_function_params_t * mnDR,
+ int depth, gs_function_t ** ppfn, gs_memory_t *mem)
+{
+ gs_function_ElIn_params_t params;
+ int code, n0, n1;
+
+ *(gs_function_params_t *)&params = *mnDR;
+ params.C0 = 0;
+ params.C1 = 0;
+ if ((code = dict_float_param(op, "N", 0.0, &params.N)) != 0 ||
+ (code = n0 = fn_build_float_array_forced(op, "C0", false, &params.C0, mem)) < 0 ||
+ (code = n1 = fn_build_float_array_forced(op, "C1", false, &params.C1, mem)) < 0
+ )
+ goto fail;
+ if (params.C0 == 0)
+ n0 = 1; /* C0 defaulted */
+ if (params.C1 == 0)
+ n1 = 1; /* C1 defaulted */
+ if (params.Range == 0)
+ params.n = n0; /* either one will do */
+ if (n0 != n1 || n0 != params.n)
+ goto fail;
+ code = gs_function_ElIn_init(ppfn, &params, mem);
+ if (code >= 0)
+ return 0;
+fail:
+ gs_function_ElIn_free_params(&params, mem);
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+}
+
+/* Finish building a FunctionType 3 (1-Input Stitching) function. */
+int
+gs_build_function_3(i_ctx_t *i_ctx_p, const ref *op, const gs_function_params_t * mnDR,
+ int depth, gs_function_t ** ppfn, gs_memory_t *mem)
+{
+ gs_function_1ItSg_params_t params;
+ int code;
+
+ *(gs_function_params_t *) & params = *mnDR;
+ params.Functions = 0;
+ params.Bounds = 0;
+ params.Encode = 0;
+ {
+ ref *pFunctions;
+ gs_function_t **ptr;
+ int i;
+
+ if ((code = dict_find_string(op, "Functions", &pFunctions)) <= 0)
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+ check_array_only(*pFunctions);
+ params.k = r_size(pFunctions);
+ code = alloc_function_array(params.k, &ptr, mem);
+ if (code < 0)
+ return code;
+ params.Functions = (const gs_function_t * const *)ptr;
+ for (i = 0; i < params.k; ++i) {
+ ref subfn;
+
+ array_get(mem, pFunctions, (long)i, &subfn);
+ code = fn_build_sub_function(i_ctx_p, &subfn, &ptr[i], depth, mem, 0, 0);
+ if (code < 0)
+ goto fail;
+ }
+ }
+ if ((code = fn_build_float_array(op, "Bounds", true, false, &params.Bounds, mem)) != params.k - 1)
+ goto fail;
+ if (gs_currentcpsimode(imemory)) {
+ /* Adobe implementation doesn't check the Encode length. */
+ /* Extra elements are ignored; missing elements are filled with 0. */
+ /* CET 12-14m.ps depends on this bug */
+ uint sz, k2 = 2 * params.k;
+ ref *encode;
+ float *p = (float *)gs_alloc_byte_array(mem, k2, sizeof(float), "Encode");
+
+ params.Encode = p;
+ if (p == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ if (dict_find_string(op, "Encode", &encode) <= 0) {
+ code = gs_note_error(gs_error_undefined);
+ goto fail;
+ }
+ if (!r_is_array(encode)) {
+ code = gs_note_error(gs_error_typecheck);
+ goto fail;
+ }
+ sz = min(k2, r_size(encode));
+ code = process_float_array(mem, encode, sz, p);
+ if (code < 0)
+ goto fail;
+ while (sz < k2)
+ p[sz++] = 0.0;
+ } else if ((code = fn_build_float_array(op, "Encode", true, true, &params.Encode, mem)) != 2 * params.k)
+ goto fail;
+ if (params.Range == 0)
+ params.n = params.Functions[0]->params.n;
+ code = gs_function_1ItSg_init(ppfn, &params, mem);
+ if (code >= 0)
+ return 0;
+fail:
+ gs_function_1ItSg_free_params(&params, mem);
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+}
diff --git a/psi/zfunc4.c b/psi/zfunc4.c
new file mode 100644
index 000000000..a5fbca5df
--- /dev/null
+++ b/psi/zfunc4.c
@@ -0,0 +1,599 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PostScript language support for FunctionType 4 (PS Calculator) Functions */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "opextern.h"
+#include "gsfunc.h"
+#include "gsfunc4.h"
+#include "gsutil.h"
+#include "idict.h"
+#include "ifunc.h"
+#include "iname.h"
+#include "dstack.h"
+#include "ialloc.h"
+#include "gzstate.h" /* these are needed to check device parameters */
+#include "gsparam.h" /* these are needed to check device parameters */
+#include "zfunc.h"
+#include "zcolor.h"
+#include "gxdevsop.h"
+
+/*
+ * FunctionType 4 functions are not defined in the PostScript language. We
+ * provide support for them because they are needed for PDF 1.3. In
+ * addition to the standard FunctionType, Domain, and Range keys, they have
+ * a Function key whose value is a procedure in a restricted subset of the
+ * PostScript language. Specifically, the procedure must (recursively)
+ * contain only integer, real, Boolean, and procedure constants (only as
+ * literal operands of if and and ifelse), and operators chosen from the set
+ * given below. Note that names other than true and false are not allowed:
+ * the procedure must be 'bound'.
+ *
+ * The following list is taken directly from the PDF 1.3 documentation.
+ */
+#define XOP(zfn) int zfn(i_ctx_t *)
+XOP(zabs); XOP(zand); XOP(zatan); XOP(zbitshift);
+XOP(zceiling); XOP(zcos); XOP(zcvi); XOP(zcvr);
+XOP(zdiv); XOP(zexp); XOP(zfloor); XOP(zidiv);
+XOP(zln); XOP(zlog); XOP(zmod); XOP(zmul);
+XOP(zneg); XOP(znot); XOP(zor); XOP(zround);
+XOP(zsin); XOP(zsqrt); XOP(ztruncate); XOP(zxor);
+XOP(zeq); XOP(zge); XOP(zgt); XOP(zle); XOP(zlt); XOP(zne);
+XOP(z2copy);
+#undef XOP
+typedef struct calc_op_s {
+ op_proc_t proc;
+ gs_PtCr_opcode_t opcode;
+} calc_op_t;
+static const calc_op_t calc_ops[] = {
+
+ /* Arithmetic operators */
+
+ {zabs, PtCr_abs},
+ {zadd, PtCr_add},
+ {zand, PtCr_and},
+ {zatan, PtCr_atan},
+ {zbitshift, PtCr_bitshift},
+ {zceiling, PtCr_ceiling},
+ {zcos, PtCr_cos},
+ {zcvi, PtCr_cvi},
+ {zcvr, PtCr_cvr},
+ {zdiv, PtCr_div},
+ {zexp, PtCr_exp},
+ {zfloor, PtCr_floor},
+ {zidiv, PtCr_idiv},
+ {zln, PtCr_ln},
+ {zlog, PtCr_log},
+ {zmod, PtCr_mod},
+ {zmul, PtCr_mul},
+ {zneg, PtCr_neg},
+ {znot, PtCr_not},
+ {zor, PtCr_or},
+ {zround, PtCr_round},
+ {zsin, PtCr_sin},
+ {zsqrt, PtCr_sqrt},
+ {zsub, PtCr_sub},
+ {ztruncate, PtCr_truncate},
+ {zxor, PtCr_xor},
+
+ /* Comparison operators */
+
+ {zeq, PtCr_eq},
+ {zge, PtCr_ge},
+ {zgt, PtCr_gt},
+ {zle, PtCr_le},
+ {zlt, PtCr_lt},
+ {zne, PtCr_ne},
+
+ /* Stack operators */
+
+ {zcopy, PtCr_copy},
+ {z2copy, PtCr_copy},
+ {zdup, PtCr_dup},
+ {zexch, PtCr_exch},
+ {zindex, PtCr_index},
+ {zpop, PtCr_pop},
+ {zroll, PtCr_roll}
+
+ /* Special operators */
+
+ /*{zif, PtCr_if},*/
+ /*{zifelse, PtCr_ifelse},*/
+ /*{ztrue, PtCr_true},*/
+ /*{zfalse, PtCr_false}*/
+};
+
+/* Fix up an if or ifelse forward reference. */
+static void
+psc_fixup(byte *p, byte *to)
+{
+ int skip = to - (p + 3);
+
+ p[1] = (byte)(skip >> 8);
+ p[2] = (byte)skip;
+}
+
+/* Check whether the ref is a given operator or resolves to it */
+static bool
+resolves_to_oper(i_ctx_t *i_ctx_p, const ref *pref, const op_proc_t proc)
+{
+ if (!r_has_attr(pref, a_executable))
+ return false;
+ if (r_btype(pref) == t_operator) {
+ return pref->value.opproc == proc;
+ } else if (r_btype(pref) == t_name) {
+ ref * val;
+ if (dict_find(systemdict, pref, &val) <= 0)
+ return false;
+ if (r_btype(val) != t_operator)
+ return false;
+ if (!r_has_attr(val, a_executable))
+ return false;
+ return val->value.opproc == proc;
+ }
+ else
+ return false;
+}
+
+/* Store an int in the buffer */
+static int
+put_int(byte **p, int n) {
+ if (n == (byte)n) {
+ if (*p) {
+ (*p)[0] = PtCr_byte;
+ (*p)[1] = (byte)n;
+ *p += 2;
+ }
+ return 2;
+ } else {
+ if (*p) {
+ **p = PtCr_int;
+ memcpy(*p + 1, &n, sizeof(int));
+ *p += sizeof(int) + 1;
+ }
+ return (sizeof(int) + 1);
+ }
+}
+
+/* Store a float in the buffer */
+static int
+put_float(byte **p, float n) {
+ if (*p) {
+ **p = PtCr_float;
+ memcpy(*p + 1, &n, sizeof(float));
+ *p += sizeof(float) + 1;
+ }
+ return (sizeof(float) + 1);
+}
+
+/* Store an op code in the buffer */
+static int
+put_op(byte **p, byte op) {
+ if (*p)
+ *(*p)++ = op;
+ return 1;
+}
+
+/*
+ * Check a calculator function for validity, optionally storing its encoded
+ * representation and add the size of the encoded representation to *psize.
+ * Note that we arbitrarily limit the depth of procedure nesting. pref is
+ * known to be a procedure.
+ */
+static int
+check_psc_function(i_ctx_t *i_ctx_p, const ref *pref, int depth, byte *ops, int *psize, bool AllowRepeat)
+{
+ int code;
+ uint i, j;
+ uint size = r_size(pref);
+ byte *p;
+
+ if (size == 2 && depth == 0) {
+ /* Bug 690986. Recognize and replace Corel tint transform function.
+ { tint_params CorelTintTransformFunction }
+
+ Where tint_params resolves to an arrey like:
+ [ 1.0 0.0 0.0 0.0
+ 0.0 1.0 0.0 0.0
+ 0.0 0.0 1.0 0.0
+ 0.0 0.0 0.0 1.0
+ 0.2 0.81 0.76 0.61
+ ]
+ And CorelTintTransformFunction is:
+ { /colorantSpecArray exch def
+ /nColorants colorantSpecArray length 4 idiv def
+ /inColor nColorants 1 add 1 roll nColorants array astore def
+ /outColor 4 array def
+ 0 1 3 {
+ /nOutInk exch def
+ 1
+ 0 1 nColorants 1 sub {
+ dup inColor exch get
+ exch 4 mul nOutInk add colorantSpecArray exch get mul
+ 1 exch sub mul
+ } for
+ 1 exch sub
+ outColor nOutInk 3 -1 roll put
+ } for
+ outColor aload pop
+ }
+ */
+ ref r_tp, r_cttf; /* original references */
+ ref n_tp, n_cttf; /* names */
+ ref *v_tp, *v_cttf; /* values */
+ int sz;
+
+ p = ops;
+ sz = *psize;
+ if(array_get(imemory, pref, 0, &r_tp) < 0)
+ goto idiom_failed;
+ if (array_get(imemory, pref, 1, &r_cttf) < 0)
+ goto idiom_failed;
+ if (r_has_type(&r_tp, t_name) && r_has_type(&r_cttf, t_name)) {
+ if ((code = name_enter_string(imemory, "tint_params", &n_tp)) < 0)
+ return code;
+ if (r_tp.value.pname == n_tp.value.pname) {
+ if ((code = name_enter_string(imemory, "CorelTintTransformFunction", &n_cttf)) < 0)
+ return code;
+ if (r_cttf.value.pname == n_cttf.value.pname) {
+ v_tp = dict_find_name(&n_tp);
+ v_cttf = dict_find_name(&n_cttf);
+ if (v_tp && v_cttf && r_is_array(v_tp) && r_is_array(v_cttf)) {
+ uint n_elem = r_size(v_tp);
+
+ if ((n_elem & 3) == 0 && r_size(v_cttf) == 31) {
+ /* Enough testing, idiom recognition tests less. */
+ uint n_col = n_elem/4;
+
+ for (i = 0; i < 4; i ++) {
+ ref v;
+ float fv;
+ bool first = true;
+
+ for (j = 0; j < n_col; j++) {
+ if (array_get(imemory, v_tp, j*4 + i, &v) < 0)
+ goto idiom_failed;
+ if (r_type(&v) == t_integer)
+ fv = (float)v.value.intval;
+ else if (r_type(&v) == t_real)
+ fv = v.value.realval;
+ else
+ goto idiom_failed;
+
+ if (fv != 0.) {
+ if (first)
+ sz += put_int(&p, 1);
+ sz += put_int(&p, 1);
+ sz += put_int(&p, n_col + 1 - j + i + !first);
+ sz += put_op(&p, PtCr_index);
+ if (fv != 1.) {
+ sz += put_float(&p, fv);
+ sz += put_op(&p, PtCr_mul);
+ }
+ sz += put_op(&p, PtCr_sub);
+ if (first)
+ first = false;
+ else
+ sz += put_op(&p, PtCr_mul);
+ }
+ }
+ if (first)
+ sz += put_int(&p, 0);
+ else
+ sz += put_op(&p, PtCr_sub);
+ }
+ /* n_col+4 4 roll pop ... pop */
+ sz += put_int(&p, n_col + 4);
+ sz += put_int(&p, 4);
+ sz += put_op(&p, PtCr_roll);
+ for (j = 0; j < n_col; j++)
+ sz += put_op(&p, PtCr_pop);
+ *psize = sz;
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ idiom_failed:;
+ for (i = 0; i < size; ++i) {
+ byte no_ops[1 + max(sizeof(int), sizeof(float))];
+ ref elt, elt2, elt3;
+ ref * delp;
+
+ p = (ops ? ops + *psize : no_ops);
+ array_get(imemory, pref, i, &elt);
+ switch (r_btype(&elt)) {
+ case t_integer:
+ *psize += put_int(&p, elt.value.intval);
+ break;
+ case t_real:
+ *psize += put_float(&p, elt.value.realval);
+ break;
+ case t_boolean:
+ *p = (elt.value.boolval ? PtCr_true : PtCr_false);
+ ++*psize;
+ break;
+ case t_name:
+ if (!r_has_attr(&elt, a_executable))
+ return_error(gs_error_rangecheck);
+ name_string_ref(imemory, &elt, &elt);
+ if (!bytes_compare(elt.value.bytes, r_size(&elt),
+ (const byte *)"true", 4)) {
+ *p = PtCr_true;
+ ++*psize;
+ break;
+ }
+ if (!bytes_compare(elt.value.bytes, r_size(&elt),
+ (const byte *)"false", 5)) {
+ *p = PtCr_false;
+ ++*psize;
+ break;
+ }
+ /* Check if the name is a valid operator in systemdict */
+ if (dict_find(systemdict, &elt, &delp) <= 0)
+ return_error(gs_error_undefined);
+ if (r_btype(delp) != t_operator)
+ return_error(gs_error_typecheck);
+ if (!r_has_attr(delp, a_executable))
+ return_error(gs_error_rangecheck);
+ elt = *delp;
+ /* Fall into the operator case */
+ case t_operator: {
+ int j;
+
+ for (j = 0; j < countof(calc_ops); ++j)
+ if (elt.value.opproc == calc_ops[j].proc) {
+ *p = calc_ops[j].opcode;
+ ++*psize;
+ goto next;
+ }
+ return_error(gs_error_rangecheck);
+ }
+ default: {
+ if (!r_is_proc(&elt))
+ return_error(gs_error_typecheck);
+ if (depth == MAX_PSC_FUNCTION_NESTING)
+ return_error(gs_error_limitcheck);
+ if ((code = array_get(imemory, pref, ++i, &elt2)) < 0)
+ return code;
+ *psize += 3;
+ code = check_psc_function(i_ctx_p, &elt, depth + 1, ops, psize, AllowRepeat);
+ if (code < 0)
+ return code;
+ /* Check for { proc } repeat | {proc} if | {proc1} {proc2} ifelse */
+ if (resolves_to_oper(i_ctx_p, &elt2, zrepeat)) {
+ if (!AllowRepeat)
+ return_error(gs_error_rangecheck);
+ if (ops) {
+ *p = PtCr_repeat;
+ psc_fixup(p, ops + *psize);
+ p = ops + *psize;
+ *p++ = PtCr_repeat_end;
+ }
+ *psize += 1; /* extra room for repeat_end */
+ } else if (resolves_to_oper(i_ctx_p, &elt2, zif)) {
+ if (ops) {
+ *p = PtCr_if;
+ psc_fixup(p, ops + *psize);
+ }
+ } else if (!r_is_proc(&elt2))
+ return_error(gs_error_rangecheck);
+ else if ((code = array_get(imemory, pref, ++i, &elt3)) < 0)
+ return code;
+ else if (resolves_to_oper(i_ctx_p, &elt3, zifelse)) {
+ if (ops) {
+ *p = PtCr_if;
+ psc_fixup(p, ops + *psize + 3);
+ p = ops + *psize;
+ *p = PtCr_else;
+ }
+ *psize += 3;
+ code = check_psc_function(i_ctx_p, &elt2, depth + 1, ops, psize, AllowRepeat);
+ if (code < 0)
+ return code;
+ if (ops)
+ psc_fixup(p, ops + *psize);
+ } else
+ return_error(gs_error_rangecheck);
+ } /* end 'default' */
+ }
+ next:
+ DO_NOTHING;
+ }
+ return 0;
+}
+#undef MAX_PSC_FUNCTION_NESTING
+
+/* Check prototype */
+build_function_proc(gs_build_function_4);
+
+/* Finish building a FunctionType 4 (PostScript Calculator) function. */
+int
+gs_build_function_4(i_ctx_t *i_ctx_p, const ref *op, const gs_function_params_t * mnDR,
+ int depth, gs_function_t ** ppfn, gs_memory_t *mem)
+{
+ gs_function_PtCr_params_t params;
+ ref *proc;
+ int code;
+ byte *ops;
+ int size;
+ int AllowRepeat = 1; /* Default to permitting Repeat, devices which can't handle it implement the spec_op */
+
+ *(gs_function_params_t *)&params = *mnDR;
+ params.ops.data = 0; /* in case of failure */
+ params.ops.size = 0; /* ditto */
+ if (dict_find_string(op, "Function", &proc) <= 0) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto fail;
+ }
+ if (!r_is_proc(proc)) {
+ code = gs_note_error(gs_error_typecheck);
+ goto fail;
+ }
+ size = 0;
+
+ /* Check if the device allows the use of repeat in functions */
+ /* We can't handle 'repeat' with pdfwrite since it emits FunctionType 4 */
+ {
+ char data[] = {"AllowPSRepeatFunctions"};
+ dev_param_req_t request;
+ gs_c_param_list list;
+
+ gs_c_param_list_write(&list, i_ctx_p->pgs->device->memory);
+ /* Stuff the data into a structure for passing to the spec_op */
+ request.Param = data;
+ request.list = &list;
+ code = dev_proc(i_ctx_p->pgs->device, dev_spec_op)(i_ctx_p->pgs->device, gxdso_get_dev_param, &request, sizeof(dev_param_req_t));
+ if (code < 0 && code != gs_error_undefined) {
+ gs_c_param_list_release(&list);
+ return code;
+ }
+ gs_c_param_list_read(&list);
+ code = param_read_bool((gs_param_list *)&list,
+ "AllowPSRepeatFunctions",
+ &AllowRepeat);
+ gs_c_param_list_release(&list);
+ if (code < 0)
+ return code;
+ }
+
+ code = check_psc_function(i_ctx_p, proc, 0, NULL, &size, AllowRepeat);
+ if (code < 0)
+ goto fail;
+ ops = gs_alloc_string(mem, size + 1, "gs_build_function_4(ops)");
+ if (ops == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ size = 0;
+ check_psc_function(i_ctx_p, proc, 0, ops, &size, AllowRepeat); /* can't fail */
+ ops[size] = PtCr_return;
+ params.ops.data = ops;
+ params.ops.size = size + 1;
+ code = gs_function_PtCr_init(ppfn, &params, mem);
+ if (code >= 0)
+ return 0;
+ /* free_params will free the ops string */
+fail:
+ gs_function_PtCr_free_params(&params, mem);
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+}
+
+int make_type4_function(i_ctx_t * i_ctx_p, ref *arr, ref *pproc, gs_function_t **func)
+{
+ int code, size, num_components, CIESubst;
+ byte *ops;
+ gs_function_PtCr_params_t params;
+ float *ptr;
+ ref alternatespace, *palternatespace = &alternatespace;
+ PS_colour_space_t *space, *altspace;
+ int AllowRepeat = 1; /* Default to permitting Repeat, devices which can't handle it implement the spec_op */
+
+ code = get_space_object(i_ctx_p, arr, &space);
+ if (code < 0)
+ return code;
+ if (!space->alternateproc)
+ return gs_error_typecheck;
+ code = space->alternateproc(i_ctx_p, arr, &palternatespace, &CIESubst);
+ if (code < 0)
+ return code;
+ code = get_space_object(i_ctx_p, palternatespace, &altspace);
+ if (code < 0)
+ return code;
+
+ code = space->numcomponents(i_ctx_p, arr, &num_components);
+ if (code < 0)
+ return code;
+ ptr = (float *)gs_alloc_byte_array(imemory, num_components * 2, sizeof(float), "make_type4_function(Domain)");
+ if (!ptr)
+ return gs_error_VMerror;
+ code = space->domain(i_ctx_p, arr, ptr);
+ if (code < 0) {
+ gs_free_const_object(imemory, ptr, "make_type4_function(Domain)");
+ return code;
+ }
+ params.Domain = ptr;
+ params.m = num_components;
+
+ code = altspace->numcomponents(i_ctx_p, &alternatespace, &num_components);
+ if (code < 0) {
+ gs_free_const_object(imemory, params.Domain, "make_type4_function(Domain)");
+ return code;
+ }
+ ptr = (float *)gs_alloc_byte_array(imemory, num_components * 2, sizeof(float), "make_type4_function(Range)");
+ if (!ptr) {
+ gs_free_const_object(imemory, params.Domain, "make_type4_function(Domain)");
+ return gs_error_VMerror;
+ }
+ code = altspace->range(i_ctx_p, &alternatespace, ptr);
+ if (code < 0) {
+ gs_free_const_object(imemory, ptr, "make_type4_function(Domain)");
+ gs_free_const_object(imemory, params.Domain, "make_type4_function(Range)");
+ return code;
+ }
+ params.Range = ptr;
+ params.n = num_components;
+
+ params.ops.data = 0; /* in case of failure, see gs_function_PtCr_free_params */
+ params.ops.size = 0; /* ditto */
+ size = 0;
+
+ /* Check if the device allows the use of repeat in functions */
+ /* We can't handle 'repeat' with pdfwrite since it emits FunctionType 4 */
+ {
+ char data[] = {"AllowPSRepeatFunctions"};
+ dev_param_req_t request;
+ gs_c_param_list list;
+
+ gs_c_param_list_write(&list, i_ctx_p->pgs->device->memory);
+ /* Stuff the data into a structure for passing to the spec_op */
+ request.Param = data;
+ request.list = &list;
+ code = dev_proc(i_ctx_p->pgs->device, dev_spec_op)(i_ctx_p->pgs->device, gxdso_get_dev_param, &request, sizeof(dev_param_req_t));
+ if (code < 0 && code != gs_error_undefined) {
+ gs_c_param_list_release(&list);
+ return code;
+ }
+ gs_c_param_list_read(&list);
+ code = param_read_bool((gs_param_list *)&list,
+ "AllowPSRepeatFunctions",
+ &AllowRepeat);
+ gs_c_param_list_release(&list);
+ if (code < 0)
+ return code;
+ }
+
+ code = check_psc_function(i_ctx_p, (const ref *)pproc, 0, NULL, &size, AllowRepeat);
+ if (code < 0) {
+ gs_function_PtCr_free_params(&params, imemory);
+ return code;
+ }
+ ops = gs_alloc_string(imemory, size + 1, "make_type4_function(ops)");
+ size = 0;
+ check_psc_function(i_ctx_p, (const ref *)pproc, 0, ops, &size, AllowRepeat); /* can't fail */
+ ops[size] = PtCr_return;
+ params.ops.data = ops;
+ params.ops.size = size + 1;
+ code = gs_function_PtCr_init(func, &params, imemory);
+ if (code < 0)
+ gs_function_PtCr_free_params(&params, imemory);
+
+ return code;
+}
diff --git a/psi/zfzlib.c b/psi/zfzlib.c
new file mode 100644
index 000000000..89cbfd6b7
--- /dev/null
+++ b/psi/zfzlib.c
@@ -0,0 +1,103 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* zlib and Flate filter creation */
+#include "ghost.h"
+#include "oper.h"
+#include "idict.h"
+#include "strimpl.h"
+#include "spdiffx.h"
+#include "spngpx.h"
+#include "szlibx.h"
+#include "idparam.h"
+#include "ifilter.h"
+#include "ifrpred.h"
+#include "ifwpred.h"
+
+/* Common setup for zlib (Flate) filter */
+static int
+filter_zlib(i_ctx_t *i_ctx_p, stream_zlib_state *pzls)
+{
+ os_ptr op = osp;
+ int code = 0;
+
+ (*s_zlibE_template.set_defaults)((stream_state *)pzls);
+ if (r_has_type(op, t_dictionary))
+ code = dict_int_param(op, "Effort", -1, 9, -1, &pzls->level);
+ return code;
+}
+
+/* <source> zlibEncode/filter <file> */
+/* <source> <dict> zlibEncode/filter <file> */
+static int
+zzlibE(i_ctx_t *i_ctx_p)
+{
+ stream_zlib_state zls;
+ int code = filter_zlib(i_ctx_p, &zls);
+
+ if (code < 0)
+ return code;
+ return filter_write(i_ctx_p, 0, &s_zlibE_template, (stream_state *)&zls, 0);
+}
+
+/* <target> zlibDecode/filter <file> */
+/* <target> <dict> zlibDecode/filter <file> */
+static int
+zzlibD(i_ctx_t *i_ctx_p)
+{
+ stream_zlib_state zls;
+
+ (*s_zlibD_template.set_defaults)((stream_state *)&zls);
+ return filter_read(i_ctx_p, 0, &s_zlibD_template, (stream_state *)&zls, 0);
+}
+
+/* <source> FlateEncode/filter <file> */
+/* <source> <dict> FlateEncode/filter <file> */
+static int
+zFlateE(i_ctx_t *i_ctx_p)
+{
+ stream_zlib_state zls;
+ int code = filter_zlib(i_ctx_p, &zls);
+
+ if (code < 0)
+ return code;
+ return filter_write_predictor(i_ctx_p, 0, &s_zlibE_template,
+ (stream_state *)&zls);
+}
+
+/* <target> FlateDecode/filter <file> */
+/* <target> <dict> FlateDecode/filter <file> */
+static int
+zFlateD(i_ctx_t *i_ctx_p)
+{
+ stream_zlib_state zls;
+
+ (*s_zlibD_template.set_defaults)((stream_state *)&zls);
+ return filter_read_predictor(i_ctx_p, 0, &s_zlibD_template,
+ (stream_state *)&zls);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zfzlib_op_defs[] =
+{
+ op_def_begin_filter(),
+ {"1zlibEncode", zzlibE},
+ {"1zlibDecode", zzlibD},
+ {"1FlateEncode", zFlateE},
+ {"1FlateDecode", zFlateD},
+ op_def_end(0)
+};
diff --git a/psi/zgeneric.c b/psi/zgeneric.c
new file mode 100644
index 000000000..359aebcfa
--- /dev/null
+++ b/psi/zgeneric.c
@@ -0,0 +1,621 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Array/string/dictionary generic operators for PostScript */
+#include "memory_.h"
+#include "ghost.h"
+#include "gsstruct.h" /* for st_bytes */
+#include "oper.h"
+#include "dstack.h" /* for systemdict */
+#include "estack.h" /* for forall */
+#include "iddict.h"
+#include "iname.h"
+#include "ipacked.h"
+#include "ivmspace.h"
+#include "store.h"
+
+/* This file implements copy, get, put, getinterval, putinterval, */
+/* length, and forall, which apply generically to */
+/* arrays, strings, and dictionaries. (Copy also has a special */
+/* meaning for copying the top N elements of the stack.) */
+
+/* See the comment in opdef.h for an invariant which allows */
+/* more efficient implementation of forall. */
+
+/* Forward references */
+static int zcopy_integer(i_ctx_t *);
+static int zcopy_interval(i_ctx_t *);
+static int copy_interval(i_ctx_t *, os_ptr, uint, os_ptr, client_name_t);
+
+/* <various1> <various2> copy <various> */
+/* <obj1> ... <objn> <int> copy <obj1> ... <objn> <obj1> ... <objn> */
+/* Note that this implements copy for arrays and strings, */
+/* but not for dictionaries (see zcopy_dict in zdict.c). */
+int
+zcopy(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int type = r_type(op);
+
+ if (type == t_integer)
+ return zcopy_integer(i_ctx_p);
+ check_op(2);
+ switch (type) {
+ case t_array:
+ case t_string:
+ return zcopy_interval(i_ctx_p);
+ case t_dictionary:
+ return zcopy_dict(i_ctx_p);
+ default:
+ return_op_typecheck(op);
+ }
+}
+
+/* <obj1> ... <objn> <int> copy <obj1> ... <objn> <obj1> ... <objn> */
+static int
+zcopy_integer(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ int count, i;
+ int code;
+
+ if ((uint) op->value.intval > (uint)(op - osbot)) {
+ /* There might be enough elements in other blocks. */
+ check_type(*op, t_integer);
+ if (op->value.intval >= (int)ref_stack_count(&o_stack))
+ return_error(gs_error_stackunderflow);
+ if (op->value.intval < 0)
+ return_error(gs_error_rangecheck);
+ check_int_ltu(*op, ref_stack_count(&o_stack));
+ count = op->value.intval;
+ } else if (op1 + (count = op->value.intval) <= ostop) {
+ /* Fast case. */
+ memcpy((char *)op, (char *)(op - count), count * sizeof(ref));
+ push(count - 1);
+ return 0;
+ }
+ /* Do it the slow, general way. */
+ code = ref_stack_push(&o_stack, count - 1);
+ if (code < 0)
+ return code;
+ for (i = 0; i < count; i++)
+ *ref_stack_index(&o_stack, i) =
+ *ref_stack_index(&o_stack, i + count);
+ return 0;
+}
+
+/* <array1> <array2> copy <subarray2> */
+/* <string1> <string2> copy <substring2> */
+static int
+zcopy_interval(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ int code = copy_interval(i_ctx_p, op, 0, op1, "copy");
+
+ if (code < 0)
+ return code;
+ r_set_size(op, r_size(op1));
+ *op1 = *op;
+ pop(1);
+ return 0;
+}
+
+/* <array|dict|name|packedarray|string> length <int> */
+static int
+zlength(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ switch (r_type(op)) {
+ case t_array:
+ case t_string:
+ case t_mixedarray:
+ case t_shortarray:
+ check_read(*op);
+ make_int(op, r_size(op));
+ return 0;
+ case t_dictionary:
+ check_dict_read(*op);
+ make_int(op, dict_length(op));
+ return 0;
+ case t_name: {
+ ref str;
+
+ name_string_ref(imemory, op, &str);
+ make_int(op, r_size(&str));
+ return 0;
+ }
+ case t_astruct:
+ if (gs_object_type(imemory, op->value.pstruct) != &st_bytes)
+ return_error(gs_error_typecheck);
+ check_read(*op);
+ make_int(op, gs_object_size(imemory, op->value.pstruct));
+ return 0;
+ default:
+ return_op_typecheck(op);
+ }
+}
+
+/* <array|packedarray|string> <index> get <obj> */
+/* <dict> <key> get <obj> */
+static int
+zget(i_ctx_t *i_ctx_p)
+{
+ int code;
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ ref *pvalue;
+
+ switch (r_type(op1)) {
+ case t_dictionary:
+ check_dict_read(*op1);
+ if (dict_find(op1, op, &pvalue) <= 0)
+ return_error(gs_error_undefined);
+ op[-1] = *pvalue;
+ break;
+ case t_string:
+ check_read(*op1);
+ check_int_ltu(*op, r_size(op1));
+ make_int(op1, op1->value.bytes[(uint) op->value.intval]);
+ break;
+ case t_array:
+ case t_mixedarray:
+ case t_shortarray:
+ check_type(*op, t_integer);
+ check_read(*op1);
+ code = array_get(imemory, op1, op->value.intval, op1);
+ if (code < 0)
+ return code;
+ break;
+ case t__invalid:
+ return_error(gs_error_stackunderflow);
+ default:
+ return_error(gs_error_typecheck);
+ }
+ pop(1);
+ return 0;
+}
+
+/* <array> <index> <obj> put - */
+/* <dict> <key> <value> put - */
+/* <string> <index> <int> put - */
+static int
+zput(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ os_ptr op2 = op1 - 1;
+ byte *sdata;
+ uint ssize;
+
+ switch (r_type(op2)) {
+ case t_dictionary:
+ if (i_ctx_p->in_superexec == 0)
+ check_dict_write(*op2);
+ {
+ int code = idict_put(op2, op1, op);
+
+ if (code < 0)
+ return code; /* error */
+ }
+ break;
+ case t_array:
+ check_write(*op2);
+ check_int_ltu(*op1, r_size(op2));
+ store_check_dest(op2, op);
+ {
+ ref *eltp = op2->value.refs + (uint) op1->value.intval;
+
+ ref_assign_old(op2, eltp, op, "put");
+ }
+ break;
+ case t_mixedarray: /* packed arrays are read-only */
+ case t_shortarray:
+ return_error(gs_error_invalidaccess);
+ case t_string:
+ sdata = op2->value.bytes;
+ ssize = r_size(op2);
+str: check_write(*op2);
+ check_int_ltu(*op1, ssize);
+ check_int_leu(*op, 0xff);
+ sdata[(uint)op1->value.intval] = (byte)op->value.intval;
+ break;
+ case t_astruct:
+ if (gs_object_type(imemory, op2->value.pstruct) != &st_bytes)
+ return_error(gs_error_typecheck);
+ sdata = r_ptr(op2, byte);
+ ssize = gs_object_size(imemory, op2->value.pstruct);
+ goto str;
+ default:
+ return_op_typecheck(op2);
+ }
+ pop(3);
+ return 0;
+}
+
+/* <array> <index> <obj> .forceput - */
+/* <dict> <key> <value> .forceput - */
+/*
+ * This forces a "put" even if the object is not writable, and (if the
+ * object is systemdict or the save level is 0) even if the value is in
+ * local VM. It is meant to be used only for replacing the value of
+ * FontDirectory in systemdict when switching between local and global VM,
+ * and a few similar applications. After initialization, this operator
+ * should no longer be accessible by name.
+ */
+static int
+zforceput(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ os_ptr op2 = op - 2;
+ int code;
+
+ switch (r_type(op2)) {
+ case t_array:
+ check_int_ltu(*op1, r_size(op2));
+ if (r_space(op2) > r_space(op)) {
+ if (imemory_save_level(iimemory))
+ return_error(gs_error_invalidaccess);
+ }
+ {
+ ref *eltp = op2->value.refs + (uint) op1->value.intval;
+
+ ref_assign_old(op2, eltp, op, "put");
+ }
+ break;
+ case t_dictionary:
+ if (op2->value.pdict == systemdict->value.pdict ||
+ !imemory_save_level(iimemory)
+ ) {
+ uint space = r_space(op2);
+
+ r_set_space(op2, avm_local);
+ code = idict_put(op2, op1, op);
+ r_set_space(op2, space);
+ } else
+ code = idict_put(op2, op1, op);
+ if (code < 0)
+ return code;
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ pop(3);
+ return 0;
+}
+
+/* <seq:array|packedarray|string> <index> <count> getinterval <subseq> */
+static int
+zgetinterval(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ os_ptr op2 = op1 - 1;
+ uint index;
+ uint count;
+
+ switch (r_type(op2)) {
+ default:
+ return_op_typecheck(op2);
+ case t_array:
+ case t_string:
+ case t_mixedarray:
+ case t_shortarray:;
+ }
+ check_read(*op2);
+ check_int_leu(*op1, r_size(op2));
+ index = op1->value.intval;
+ check_int_leu(*op, r_size(op2) - index);
+ count = op->value.intval;
+ switch (r_type(op2)) {
+ case t_array:
+ op2->value.refs += index;
+ break;
+ case t_string:
+ op2->value.bytes += index;
+ break;
+ case t_mixedarray: {
+ const ref_packed *packed = op2->value.packed;
+
+ for (; index--;)
+ packed = packed_next(packed);
+ op2->value.packed = packed;
+ break;
+ }
+ case t_shortarray:
+ op2->value.packed += index;
+ break;
+ }
+ r_set_size(op2, count);
+ pop(2);
+ return 0;
+}
+
+/* <array1> <index> <array2|packedarray2> putinterval - */
+/* <string1> <index> <string2> putinterval - */
+/* <bytestring1> <index> <string2> putinterval - */
+static int
+zputinterval(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr opindex = op - 1;
+ os_ptr opto = opindex - 1;
+ int code;
+
+ switch (r_type(opto)) {
+ default:
+ return_error(gs_error_typecheck);
+ case t__invalid:
+ if (r_type(op) != t_array && r_type(op) != t_string && r_type(op) != t__invalid)
+ return_error(gs_error_typecheck); /* to match Distiller */
+ else
+ return_error(gs_error_stackunderflow);
+ case t_mixedarray:
+ case t_shortarray:
+ return_error(gs_error_invalidaccess);
+ case t_array:
+ case t_string:
+ check_write(*opto);
+ check_int_leu(*opindex, r_size(opto));
+ code = copy_interval(i_ctx_p, opto, (uint)(opindex->value.intval),
+ op, "putinterval");
+ break;
+ case t_astruct: {
+ uint dsize, ssize, index;
+
+ check_write(*opto);
+ if (gs_object_type(imemory, opto->value.pstruct) != &st_bytes)
+ return_error(gs_error_typecheck);
+ dsize = gs_object_size(imemory, opto->value.pstruct);
+ check_int_leu(*opindex, dsize);
+ index = (uint)opindex->value.intval;
+ check_read_type(*op, t_string);
+ ssize = r_size(op);
+ if (ssize > dsize - index)
+ return_error(gs_error_rangecheck);
+ memcpy(r_ptr(opto, byte) + index, op->value.const_bytes, ssize);
+ code = 0;
+ break;
+ }
+ }
+ if (code >= 0)
+ pop(3);
+ return code;
+}
+
+/* <array|packedarray|string> <<element> proc> forall - */
+/* <dict> <<key> <value> proc> forall - */
+static int
+ array_continue(i_ctx_t *),
+ dict_continue(i_ctx_t *),
+ string_continue(i_ctx_t *),
+ packedarray_continue(i_ctx_t *);
+static int forall_cleanup(i_ctx_t *);
+static int
+zforall(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr obj = op - 1;
+ es_ptr ep = esp;
+ es_ptr cproc = ep + 4;
+
+ check_estack(6);
+ check_proc(*op);
+ switch (r_type(obj)) {
+ default:
+ return_op_typecheck(obj);
+ case t_array:
+ check_read(*obj);
+ make_op_estack(cproc, array_continue);
+ break;
+ case t_dictionary:
+ check_dict_read(*obj);
+ make_int(cproc, dict_first(obj));
+ ++cproc;
+ make_op_estack(cproc, dict_continue);
+ break;
+ case t_string:
+ check_read(*obj);
+ make_op_estack(cproc, string_continue);
+ break;
+ case t_mixedarray:
+ case t_shortarray:
+ check_read(*obj);
+ make_op_estack(cproc, packedarray_continue);
+ break;
+ }
+ /*
+ * Push:
+ * - a mark;
+ * - the composite object;
+ * - the procedure;
+ * - the iteration index (only for dictionaries, done above);
+ * and invoke the continuation operator.
+ */
+ make_mark_estack(ep + 1, es_for, forall_cleanup);
+ ep[2] = *obj;
+ ep[3] = *op;
+ esp = cproc - 1;
+ pop(2);
+ return (*real_opproc(cproc))(i_ctx_p);
+}
+/* Continuation operator for arrays */
+static int
+array_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr obj = esp - 1;
+
+ if (r_size(obj)) { /* continue */
+ push(1);
+ r_dec_size(obj, 1);
+ *op = *obj->value.refs;
+ obj->value.refs++;
+ esp += 2;
+ *esp = obj[1];
+ return o_push_estack;
+ } else { /* done */
+ esp -= 3; /* pop mark, object, proc */
+ return o_pop_estack;
+ }
+}
+/* Continuation operator for dictionaries */
+static int
+dict_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr obj = esp - 2;
+ int index = esp->value.intval;
+
+ push(2); /* make room for key and value */
+ if ((index = dict_next(obj, index, op - 1)) >= 0) { /* continue */
+ esp->value.intval = index;
+ esp += 2;
+ *esp = obj[1];
+ return o_push_estack;
+ } else { /* done */
+ pop(2); /* undo push */
+ esp -= 4; /* pop mark, object, proc, index */
+ return o_pop_estack;
+ }
+}
+/* Continuation operator for strings */
+static int
+string_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr obj = esp - 1;
+
+ if (r_size(obj)) { /* continue */
+ r_dec_size(obj, 1);
+ push(1);
+ make_int(op, *obj->value.bytes);
+ obj->value.bytes++;
+ esp += 2;
+ *esp = obj[1];
+ return o_push_estack;
+ } else { /* done */
+ esp -= 3; /* pop mark, object, proc */
+ return o_pop_estack;
+ }
+}
+/* Continuation operator for packed arrays */
+static int
+packedarray_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ es_ptr obj = esp - 1;
+
+ if (r_size(obj)) { /* continue */
+ const ref_packed *packed = obj->value.packed;
+
+ r_dec_size(obj, 1);
+ push(1);
+ packed_get(imemory, packed, op);
+ obj->value.packed = packed_next(packed);
+ esp += 2;
+ *esp = obj[1];
+ return o_push_estack;
+ } else { /* done */
+ esp -= 3; /* pop mark, object, proc */
+ return o_pop_estack;
+ }
+}
+/* Vacuous cleanup procedure */
+static int
+forall_cleanup(i_ctx_t *i_ctx_p)
+{
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zgeneric_op_defs[] =
+{
+ {"1copy", zcopy},
+ {"2forall", zforall},
+ {"3.forceput", zforceput},
+ {"2get", zget},
+ {"3getinterval", zgetinterval},
+ {"1length", zlength},
+ {"3put", zput},
+ {"3putinterval", zputinterval},
+ /* Internal operators */
+ {"0%array_continue", array_continue},
+ {"0%dict_continue", dict_continue},
+ {"0%packedarray_continue", packedarray_continue},
+ {"0%string_continue", string_continue},
+ op_def_end(0)
+};
+
+/* ------ Shared routines ------ */
+
+/* Copy an interval from one operand to another. */
+/* This is used by both putinterval and string/array copy. */
+/* The destination is known to be an array or string, */
+/* and the starting index is known to be less than or equal to */
+/* its length; nothing else has been checked. */
+static int
+copy_interval(i_ctx_t *i_ctx_p /* for ref_assign_old */, os_ptr prto,
+ uint index, os_ptr prfrom, client_name_t cname)
+{
+ int fromtype = r_type(prfrom);
+ uint fromsize = r_size(prfrom);
+
+ if (!(fromtype == r_type(prto) ||
+ ((fromtype == t_shortarray || fromtype == t_mixedarray) &&
+ r_type(prto) == t_array))
+ )
+ return_op_typecheck(prfrom);
+ check_read(*prfrom);
+ check_write(*prto);
+ if (fromsize > r_size(prto) - index)
+ return_error(gs_error_rangecheck);
+ switch (fromtype) {
+ case t_array:
+ { /* We have to worry about aliasing, */
+ /* but refcpy_to_old takes care of it for us. */
+ return refcpy_to_old(prto, index, prfrom->value.refs,
+ fromsize, idmemory, cname);
+ }
+ case t_string:
+ { /* memmove takes care of aliasing. */
+ memmove(prto->value.bytes + index, prfrom->value.bytes,
+ fromsize);
+ }
+ break;
+ case t_mixedarray:
+ case t_shortarray:
+ { /* We don't have to worry about aliasing, because */
+ /* packed arrays are read-only and hence the destination */
+ /* can't be a packed array. */
+ uint i;
+ const ref_packed *packed = prfrom->value.packed;
+ ref *pdest = prto->value.refs + index;
+ ref elt;
+
+ for (i = 0; i < fromsize; i++, pdest++) {
+ packed_get(imemory, packed, &elt);
+ ref_assign_old(prto, pdest, &elt, cname);
+ packed = packed_next(packed);
+ }
+ }
+ break;
+ }
+ return 0;
+}
diff --git a/psi/zgstate.c b/psi/zgstate.c
new file mode 100644
index 000000000..bb9693169
--- /dev/null
+++ b/psi/zgstate.c
@@ -0,0 +1,605 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Graphics state operators */
+#include "math_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "icremap.h"
+#include "idict.h"
+#include "istruct.h"
+#include "igstate.h"
+#include "gsmatrix.h"
+#include "store.h"
+#include "gscspace.h"
+#include "iname.h"
+
+/* Structure descriptors */
+private_st_int_gstate();
+private_st_int_remap_color_info();
+
+/* ------ Utilities ------ */
+
+static int
+zset_real(i_ctx_t *i_ctx_p, int (*set_proc)(gs_state *, double))
+{
+ os_ptr op = osp;
+ double param;
+ int code = real_param(op, &param);
+
+ if (code < 0)
+ return_op_typecheck(op);
+ code = set_proc(igs, param);
+ if (!code)
+ pop(1);
+ return code;
+}
+
+static int
+zset_bool(i_ctx_t *i_ctx_p, void (*set_proc)(gs_state *, bool))
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_boolean);
+ set_proc(igs, op->value.boolval);
+ pop(1);
+ return 0;
+}
+
+static int
+zcurrent_bool(i_ctx_t *i_ctx_p, bool (*current_proc)(const gs_state *))
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_bool(op, current_proc(igs));
+ return 0;
+}
+
+static int
+zset_uint(i_ctx_t *i_ctx_p, void (*set_proc)(gs_state *, uint))
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ set_proc(igs, op->value.intval);
+ pop(1);
+ return 0;
+}
+
+static int
+zcurrent_uint(i_ctx_t *i_ctx_p, uint (*current_proc)(const gs_state *))
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, current_proc(igs));
+ return 0;
+}
+
+/* ------ Operations on the entire graphics state ------ */
+
+/* "Client" procedures */
+static void *gs_istate_alloc(gs_memory_t * mem);
+static int gs_istate_copy(void *to, const void *from);
+static void gs_istate_free(void *old, gs_memory_t * mem);
+static const gs_state_client_procs istate_procs = {
+ gs_istate_alloc,
+ gs_istate_copy,
+ gs_istate_free,
+ 0, /* copy_for */
+};
+
+/* Initialize the graphics stack. */
+gs_state *
+int_gstate_alloc(const gs_dual_memory_t * dmem)
+{
+ int_gstate *iigs;
+ ref proc0;
+ int_remap_color_info_t *prci;
+ gs_ref_memory_t *lmem = dmem->space_local;
+ gs_ref_memory_t *gmem = dmem->space_global;
+ gs_state *pgs = gs_state_alloc((gs_memory_t *)lmem);
+
+ iigs = gs_alloc_struct((gs_memory_t *)lmem, int_gstate, &st_int_gstate,
+ "int_gstate_alloc(int_gstate)");
+ if (iigs == NULL)
+ return NULL;
+ int_gstate_map_refs(iigs, make_null);
+ make_empty_array(&iigs->dash_pattern_array, a_all);
+ gs_alloc_ref_array(lmem, &proc0, a_readonly + a_executable, 2,
+ "int_gstate_alloc(proc0)");
+ make_oper(proc0.value.refs, 0, zpop);
+ make_real(proc0.value.refs + 1, 0.0);
+ iigs->black_generation = proc0;
+ iigs->undercolor_removal = proc0;
+ make_false(&iigs->use_cie_color);
+ /*
+ * Even though the gstate itself is allocated in local VM, the
+ * container for the color remapping procedure must be allocated in
+ * global VM so that the gstate can be copied into global VM.
+ */
+ prci = gs_alloc_struct((gs_memory_t *)gmem, int_remap_color_info_t,
+ &st_int_remap_color_info,
+ "int_gstate_alloc(remap color info)");
+ if (prci == NULL)
+ return NULL;
+ make_struct(&iigs->remap_color_info, imemory_space(gmem), prci);
+ clear_pagedevice(iigs);
+ gs_state_set_client(pgs, iigs, &istate_procs, true);
+ /* PostScript code wants limit clamping enabled. */
+ gs_setlimitclamp(pgs, true);
+ /*
+ * gsave and grestore only work properly
+ * if there are always at least 2 entries on the stack.
+ * We count on the PostScript initialization code to do a gsave.
+ */
+ return pgs;
+}
+
+/* - gsave - */
+int
+zgsave(i_ctx_t *i_ctx_p)
+{
+ return gs_gsave(igs);
+}
+
+/* - grestore - */
+int
+zgrestore(i_ctx_t *i_ctx_p)
+{
+ return gs_grestore(igs);
+}
+
+/* - grestoreall - */
+int
+zgrestoreall(i_ctx_t *i_ctx_p)
+{
+ return gs_grestoreall(igs);
+}
+
+/* - initgraphics - */
+static int
+zinitgraphics(i_ctx_t *i_ctx_p)
+{
+ /*
+ * gs_initigraphics does not reset the colorspace;
+ * this is now handled in the PostScript code.
+ */
+ make_empty_array(&istate->dash_pattern_array, a_all);
+ return gs_initgraphics(igs);
+}
+
+/* ------ Operations on graphics state elements ------ */
+
+/* <num> setlinewidth - */
+static int
+zsetlinewidth(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ /*
+ * The Red Book doesn't say anything about this, but Adobe
+ * interpreters return (or perhaps store) the absolute value
+ * of the width.
+ */
+ double width;
+ int code = real_param(op, &width);
+
+ if (code < 0)
+ return_op_typecheck(op);
+ code = gs_setlinewidth(igs, fabs(width));
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+/* - currentlinewidth <num> */
+static int
+zcurrentlinewidth(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_real(op, gs_currentlinewidth(igs));
+ return 0;
+}
+
+/* <cap_int> .setlinecap - */
+static int
+zsetlinecap(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int param;
+ int code = int_param(op, max_int, &param);
+
+ if (code < 0 || (code = gs_setlinecap(igs, (gs_line_cap) param)) < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* - currentlinecap <cap_int> */
+static int
+zcurrentlinecap(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, (int)gs_currentlinecap(igs));
+ return 0;
+}
+
+/* <join_int> .setlinejoin - */
+static int
+zsetlinejoin(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int param;
+ int code = int_param(op, max_int, &param);
+
+ if (code < 0 || (code = gs_setlinejoin(igs, (gs_line_join) param)) < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* - currentlinejoin <join_int> */
+static int
+zcurrentlinejoin(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, (int)gs_currentlinejoin(igs));
+ return 0;
+}
+
+/* <num> setmiterlimit - */
+static int
+zsetmiterlimit(i_ctx_t *i_ctx_p)
+{
+ return zset_real(i_ctx_p, gs_setmiterlimit);
+}
+
+/* - currentmiterlimit <num> */
+static int
+zcurrentmiterlimit(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_real(op, gs_currentmiterlimit(igs));
+ return 0;
+}
+
+/* <array> <offset> setdash - */
+static int
+zsetdash(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ double offset;
+ int code = real_param(op, &offset);
+ uint i, n;
+ gs_memory_t *mem = imemory;
+ float *pattern;
+
+ if (code < 0)
+ return_op_typecheck(op);
+ if (!r_is_array(op1))
+ return_op_typecheck(op1);
+ /* Adobe interpreters apparently don't check the array for */
+ /* read access, so we won't either. */
+ /*check_read(*op1); */
+ /* Unpack the dash pattern and check it */
+ n = r_size(op1);
+ pattern =
+ (float *)gs_alloc_byte_array(mem, n, sizeof(float), "setdash");
+
+ if (pattern == 0)
+ return_error(gs_error_VMerror);
+ for (i = 0, code = 0; i < n && code >= 0; ++i) {
+ ref element;
+
+ array_get(mem, op1, (long)i, &element);
+ code = float_param(&element, &pattern[i]);
+ }
+ if (code >= 0)
+ code = gs_setdash(igs, pattern, n, offset);
+ gs_free_object(mem, pattern, "setdash"); /* gs_setdash copies this */
+ if (code < 0)
+ return code;
+ ref_assign(&istate->dash_pattern_array, op1);
+ pop(2);
+ return code;
+}
+
+/* - currentdash <array> <offset> */
+static int
+zcurrentdash(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(2);
+ ref_assign(op - 1, &istate->dash_pattern_array);
+ make_real(op, gs_currentdash_offset(igs));
+ return 0;
+}
+
+/* <num> setflat - */
+static int
+zsetflat(i_ctx_t *i_ctx_p)
+{
+ return zset_real(i_ctx_p, gs_setflat);
+}
+
+/* - currentflat <num> */
+static int
+zcurrentflat(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_real(op, gs_currentflat(igs));
+ return 0;
+}
+
+/* ------ Extensions ------ */
+
+/* <bool> .setaccuratecurves - */
+static int
+zsetaccuratecurves(i_ctx_t *i_ctx_p)
+{
+ return zset_bool(i_ctx_p, gs_setaccuratecurves);
+}
+
+/* - .currentaccuratecurves <bool> */
+static int
+zcurrentaccuratecurves(i_ctx_t *i_ctx_p)
+{
+ return zcurrent_bool(i_ctx_p, gs_currentaccuratecurves);
+}
+
+/* <join_int|-1> .setcurvejoin - */
+static int
+zsetcurvejoin(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_type(*op, t_integer);
+ if (op->value.intval < -1 || op->value.intval > max_int)
+ return_error(gs_error_rangecheck);
+ code = gs_setcurvejoin(igs, (int)op->value.intval);
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* - .currentcurvejoin <join_int|-1> */
+static int
+zcurrentcurvejoin(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, gs_currentcurvejoin(igs));
+ return 0;
+}
+
+/* <adjust.x> <adjust.y> .setfilladjust2 - */
+static int
+zsetfilladjust2(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double adjust[2];
+ int code = num_params(op, 2, adjust);
+
+ if (code < 0)
+ return code;
+ code = gs_setfilladjust(igs, adjust[0], adjust[1]);
+ if (code < 0)
+ return code;
+ pop(2);
+ return 0;
+}
+
+/* - .currentfilladjust2 <adjust.x> <adjust.y> */
+static int
+zcurrentfilladjust2(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_point adjust;
+
+ push(2);
+ gs_currentfilladjust(igs, &adjust);
+ make_real(op - 1, adjust.x);
+ make_real(op, adjust.y);
+ return 0;
+}
+
+/* <bool> .setdashadapt - */
+static int
+zsetdashadapt(i_ctx_t *i_ctx_p)
+{
+ return zset_bool(i_ctx_p, gs_setdashadapt);
+}
+
+/* - .currentdashadapt <bool> */
+static int
+zcurrentdashadapt(i_ctx_t *i_ctx_p)
+{
+ return zcurrent_bool(i_ctx_p, gs_currentdashadapt);
+}
+
+/* <num> <bool> .setdotlength - */
+static int
+zsetdotlength(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double length;
+ int code = real_param(op - 1, &length);
+
+ if (code < 0)
+ return code;
+ check_type(*op, t_boolean);
+ code = gs_setdotlength(igs, length, op->value.boolval);
+ if (code < 0)
+ return code;
+ pop(2);
+ return 0;
+}
+
+/* - .currentdotlength <num> <bool> */
+static int
+zcurrentdotlength(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(2);
+ make_real(op - 1, gs_currentdotlength(igs));
+ make_bool(op, gs_currentdotlength_absolute(igs));
+ return 0;
+}
+
+/* - .setdotorientation - */
+static int
+zsetdotorientation(i_ctx_t *i_ctx_p)
+{
+ return gs_setdotorientation(igs);
+}
+
+/* - .dotorientation - */
+static int
+zdotorientation(i_ctx_t *i_ctx_p)
+{
+ return gs_dotorientation(igs);
+}
+
+/* <bool> .setlimitclamp - */
+static int
+zsetlimitclamp(i_ctx_t *i_ctx_p)
+{
+ return zset_bool(i_ctx_p, gs_setlimitclamp);
+}
+
+/* - .currentlimitclamp <bool> */
+static int
+zcurrentlimitclamp(i_ctx_t *i_ctx_p)
+{
+ return zcurrent_bool(i_ctx_p, gs_currentlimitclamp);
+}
+
+/* <int> .settextrenderingmode - */
+static int
+zsettextrenderingmode(i_ctx_t *i_ctx_p)
+{
+ return zset_uint(i_ctx_p, gs_settextrenderingmode);
+}
+
+/* - .currenttextrenderingmode <int> */
+static int
+zcurrenttextrenderingmode(i_ctx_t *i_ctx_p)
+{
+ return zcurrent_uint(i_ctx_p, gs_currenttextrenderingmode);
+}
+
+/* <bool> .sethpglpathmode - */
+static int
+zsethpglpathmode(i_ctx_t *i_ctx_p)
+{
+ return zset_bool(i_ctx_p, gs_sethpglpathmode);
+}
+
+/* - .currenthpglpathmode <int> */
+static int
+zcurrenthpglpathmode(i_ctx_t *i_ctx_p)
+{
+ return zcurrent_bool(i_ctx_p, gs_currenthpglpathmode);
+}
+
+/* ------ Initialization procedure ------ */
+
+/* We need to split the table because of the 16-element limit. */
+const op_def zgstate1_op_defs[] = {
+ {"0.currentaccuratecurves", zcurrentaccuratecurves},
+ {"0.currentcurvejoin", zcurrentcurvejoin},
+ {"0currentdash", zcurrentdash},
+ {"0.currentdashadapt", zcurrentdashadapt},
+ {"0.currentdotlength", zcurrentdotlength},
+ {"0.currentfilladjust2", zcurrentfilladjust2},
+ {"0currentflat", zcurrentflat},
+ {"0.currentlimitclamp", zcurrentlimitclamp},
+ {"0currentlinecap", zcurrentlinecap},
+ {"0currentlinejoin", zcurrentlinejoin},
+ {"0currentlinewidth", zcurrentlinewidth},
+ {"0currentmiterlimit", zcurrentmiterlimit},
+ {"0.dotorientation", zdotorientation},
+ {"0grestore", zgrestore},
+ {"0grestoreall", zgrestoreall},
+ op_def_end(0)
+};
+const op_def zgstate2_op_defs[] = {
+ {"0gsave", zgsave},
+ {"0initgraphics", zinitgraphics},
+ {"1.setaccuratecurves", zsetaccuratecurves},
+ {"1.setcurvejoin", zsetcurvejoin},
+ {"2setdash", zsetdash},
+ {"1.setdashadapt", zsetdashadapt},
+ {"2.setdotlength", zsetdotlength},
+ {"0.setdotorientation", zsetdotorientation},
+ {"2.setfilladjust2", zsetfilladjust2},
+ {"1.setlimitclamp", zsetlimitclamp},
+ {"1setflat", zsetflat},
+ {"1.setlinecap", zsetlinecap},
+ {"1.setlinejoin", zsetlinejoin},
+ {"1setlinewidth", zsetlinewidth},
+ {"1setmiterlimit", zsetmiterlimit},
+ op_def_end(0)
+};
+const op_def zgstate3_op_defs[] = {
+ {"0.settextrenderingmode", zsettextrenderingmode},
+ {"0.currenttextrenderingmode", zcurrenttextrenderingmode},
+ {"0.sethpglpathmode", zsethpglpathmode},
+ {"0.currenthpglpathmode", zcurrenthpglpathmode},
+ op_def_end(0)
+};
+
+/* ------ Internal routines ------ */
+
+/* Allocate the interpreter's part of a graphics state. */
+static void *
+gs_istate_alloc(gs_memory_t * mem)
+{
+ return gs_alloc_struct(mem, int_gstate, &st_int_gstate, "int_gsave");
+}
+
+/* Copy the interpreter's part of a graphics state. */
+static int
+gs_istate_copy(void *to, const void *from)
+{
+ *(int_gstate *) to = *(const int_gstate *)from;
+ return 0;
+}
+
+/* Free the interpreter's part of a graphics state. */
+static void
+gs_istate_free(void *old, gs_memory_t * mem)
+{
+ gs_free_object(mem, old, "int_grestore");
+}
diff --git a/psi/zhsb.c b/psi/zhsb.c
new file mode 100644
index 000000000..0963b8baf
--- /dev/null
+++ b/psi/zhsb.c
@@ -0,0 +1,61 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* HSB color operators */
+#include "ghost.h"
+#include "oper.h"
+#include "igstate.h"
+#include "store.h"
+#include "gshsb.h"
+
+/* - currenthsbcolor <hue> <saturation> <brightness> */
+static int
+zcurrenthsbcolor(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ float par[3];
+
+ gs_currenthsbcolor(igs, par);
+ push(3);
+ make_floats(op - 2, par, 3);
+ return 0;
+}
+
+/* <hue> <saturation> <brightness> sethsbcolor - */
+static int
+zsethsbcolor(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double par[3];
+ int code;
+
+ if ((code = num_params(op, 3, par)) < 0 ||
+ (code = gs_sethsbcolor(igs, par[0], par[1], par[2])) < 0
+ )
+ return code;
+ make_null(&istate->colorspace.array);
+ pop(3);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zhsb_op_defs[] =
+{
+ {"0currenthsbcolor", zcurrenthsbcolor},
+ {"3sethsbcolor", zsethsbcolor},
+ op_def_end(0)
+};
diff --git a/psi/zht.c b/psi/zht.c
new file mode 100644
index 000000000..e57fb0797
--- /dev/null
+++ b/psi/zht.c
@@ -0,0 +1,278 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Halftone definition operators */
+#include "ghost.h"
+#include "memory_.h"
+#include "oper.h"
+#include "estack.h"
+#include "gsstruct.h" /* must precede igstate.h, */
+ /* because of #ifdef in gsht.h */
+#include "ialloc.h"
+#include "igstate.h"
+#include "gsmatrix.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gzht.h"
+#include "gsstate.h"
+#include "iht.h" /* prototypes */
+#include "store.h"
+
+/* Forward references */
+static int screen_sample(i_ctx_t *);
+static int set_screen_continue(i_ctx_t *);
+static int screen_cleanup(i_ctx_t *);
+
+/* - .currenthalftone <dict> 0 */
+/* - .currenthalftone <frequency> <angle> <proc> 1 */
+/* - .currenthalftone <red_freq> ... <gray_proc> 2 */
+static int
+zcurrenthalftone(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_halftone ht;
+
+ gs_currenthalftone(igs, &ht);
+ switch (ht.type) {
+ case ht_type_screen:
+ push(4);
+ make_real(op - 3, ht.params.screen.frequency);
+ make_real(op - 2, ht.params.screen.angle);
+ op[-1] = istate->screen_procs.gray;
+ make_int(op, 1);
+ break;
+ case ht_type_colorscreen:
+ push(13);
+ {
+ os_ptr opc = op - 12;
+ gs_screen_halftone *pht =
+ &ht.params.colorscreen.screens.colored.red;
+
+ make_real(opc, pht->frequency);
+ make_real(opc + 1, pht->angle);
+ opc[2] = istate->screen_procs.red;
+
+ opc = op - 9;
+ pht = &ht.params.colorscreen.screens.colored.green;
+ make_real(opc, pht->frequency);
+ make_real(opc + 1, pht->angle);
+ opc[2] = istate->screen_procs.green;
+
+ opc = op - 6;
+ pht = &ht.params.colorscreen.screens.colored.blue;
+ make_real(opc, pht->frequency);
+ make_real(opc + 1, pht->angle);
+ opc[2] = istate->screen_procs.blue;
+
+ opc = op - 3;
+ pht = &ht.params.colorscreen.screens.colored.gray;
+ make_real(opc, pht->frequency);
+ make_real(opc + 1, pht->angle);
+ opc[2] = istate->screen_procs.gray;
+ }
+ make_int(op, 2);
+ break;
+ default: /* Screen was set by sethalftone. */
+ push(2);
+ op[-1] = istate->halftone;
+ make_int(op, 0);
+ break;
+ }
+ return 0;
+}
+
+/* - .currentscreenlevels <int> */
+static int
+zcurrentscreenlevels(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, gs_currentscreenlevels(igs));
+ return 0;
+}
+
+/* The setscreen operator is complex because it has to sample */
+/* each pixel in the pattern cell, calling a procedure, and then */
+/* sort the result into a whitening order. */
+
+/* Layout of stuff pushed on estack: */
+/* Control mark, */
+/* [other stuff for other screen-setting operators], */
+/* finishing procedure (or 0), */
+/* spot procedure, */
+/* enumeration structure (as bytes). */
+#define snumpush 4
+#define sproc esp[-1]
+#define senum r_ptr(esp, gs_screen_enum)
+
+/* Forward references */
+static int setscreen_finish(i_ctx_t *);
+
+/* <frequency> <angle> <proc> setscreen - */
+static int
+zsetscreen(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_screen_halftone screen;
+ gx_ht_order order;
+ int code = zscreen_params(op, &screen);
+ gs_memory_t *mem;
+ int space_index = r_space_index(op);
+
+ if (code < 0)
+ return code;
+ mem = (gs_memory_t *)idmemory->spaces_indexed[space_index];
+ /*
+ * Allocate the halftone in the same VM space as the procedure.
+ * This keeps the space relationships consistent.
+ */
+ code = gs_screen_order_init_memory(&order, igs, &screen,
+ gs_currentaccuratescreens(mem), mem);
+ if (code < 0)
+ return code;
+ return zscreen_enum_init(i_ctx_p, &order, &screen, op, 3,
+ setscreen_finish, space_index);
+}
+
+/* We break out the body of this operator so it can be shared with */
+/* the code for Type 1 halftones in sethalftone. */
+int
+zscreen_enum_init(i_ctx_t *i_ctx_p, const gx_ht_order * porder,
+ gs_screen_halftone * psp, ref * pproc, int npop,
+ int (*finish_proc)(i_ctx_t *), int space_index)
+{
+ gs_screen_enum *penum;
+ gs_memory_t * mem = (gs_memory_t *)idmemory->spaces_indexed[space_index];
+ int code;
+
+ check_estack(snumpush + 1);
+ penum = gs_screen_enum_alloc(mem, "setscreen");
+ if (penum == 0)
+ return_error(gs_error_VMerror);
+ make_struct(esp + snumpush, space_index << r_space_shift, penum); /* do early for screen_cleanup in case of error */
+ code = gs_screen_enum_init_memory(penum, porder, igs, psp, mem);
+ if (code < 0) {
+ screen_cleanup(i_ctx_p);
+ return code;
+ }
+ /* Push everything on the estack */
+ make_mark_estack(esp + 1, es_other, screen_cleanup);
+ esp += snumpush;
+ make_op_estack(esp - 2, finish_proc);
+ sproc = *pproc;
+ push_op_estack(screen_sample);
+ pop(npop);
+ return o_push_estack;
+}
+/* Set up the next sample */
+static int
+screen_sample(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_screen_enum *penum = senum;
+ gs_point pt;
+ int code = gs_screen_currentpoint(penum, &pt);
+ ref proc;
+
+ switch (code) {
+ default:
+ return code;
+ case 1:
+ /* All done */
+ if (real_opproc(esp - 2) != 0)
+ code = (*real_opproc(esp - 2)) (i_ctx_p);
+ esp -= snumpush;
+ screen_cleanup(i_ctx_p);
+ return (code < 0 ? code : o_pop_estack);
+ case 0:
+ ;
+ }
+ push(2);
+ make_real(op - 1, pt.x);
+ make_real(op, pt.y);
+ proc = sproc;
+ push_op_estack(set_screen_continue);
+ *++esp = proc;
+ return o_push_estack;
+}
+/* Continuation procedure for processing sampled pixels. */
+static int
+set_screen_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double value;
+ int code = real_param(op, &value);
+
+ if (code < 0)
+ return code;
+ code = gs_screen_next(senum, value);
+ if (code < 0)
+ return code;
+ pop(1);
+ return screen_sample(i_ctx_p);
+}
+/* Finish setscreen. */
+static int
+setscreen_finish(i_ctx_t *i_ctx_p)
+{
+ gs_screen_install(senum);
+ istate->screen_procs.red = sproc;
+ istate->screen_procs.green = sproc;
+ istate->screen_procs.blue = sproc;
+ istate->screen_procs.gray = sproc;
+ make_null(&istate->halftone);
+ return 0;
+}
+/* Clean up after screen enumeration */
+static int
+screen_cleanup(i_ctx_t *i_ctx_p)
+{
+ gs_screen_enum *penum = r_ptr(esp + snumpush, gs_screen_enum);
+
+ gs_free_object(penum->halftone.rc.memory, penum, "screen_cleanup");
+ return 0;
+}
+
+/* ------ Utility procedures ------ */
+
+/* Get parameters for a single screen. */
+int
+zscreen_params(os_ptr op, gs_screen_halftone * phs)
+{
+ double fa[2];
+ int code = num_params(op - 1, 2, fa);
+
+ if (code < 0)
+ return code;
+ check_proc(*op);
+ phs->frequency = fa[0];
+ phs->angle = fa[1];
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zht_op_defs[] =
+{
+ {"0.currenthalftone", zcurrenthalftone},
+ {"0.currentscreenlevels", zcurrentscreenlevels},
+ {"3setscreen", zsetscreen},
+ /* Internal operators */
+ {"0%screen_sample", screen_sample},
+ {"1%set_screen_continue", set_screen_continue},
+ {"0%setscreen_finish", setscreen_finish},
+ op_def_end(0)
+};
diff --git a/psi/zht1.c b/psi/zht1.c
new file mode 100644
index 000000000..699edbd57
--- /dev/null
+++ b/psi/zht1.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* setcolorscreen operator */
+#include "ghost.h"
+#include "memory_.h"
+#include "oper.h"
+#include "estack.h"
+#include "gsstruct.h" /* must precede igstate.h, */
+ /* because of #ifdef in gsht.h */
+#include "ialloc.h"
+#include "igstate.h"
+#include "gsmatrix.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gzht.h"
+#include "gsstate.h"
+#include "iht.h"
+#include "store.h"
+
+/* Dummy spot function */
+static float
+spot_dummy(double x, double y)
+{
+ return (x + y) / 2;
+}
+
+/* <red_freq> ... <gray_proc> setcolorscreen - */
+static int setcolorscreen_finish(i_ctx_t *);
+static int setcolorscreen_cleanup(i_ctx_t *);
+static int
+zsetcolorscreen(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_colorscreen_halftone cscreen;
+ ref sprocs[4];
+ gs_halftone *pht;
+ gx_device_halftone *pdht;
+ int i;
+ int code = 0;
+ int space = 0;
+ gs_memory_t *mem;
+
+ for (i = 0; i < 4; i++) {
+ os_ptr op1 = op - 9 + i * 3;
+ int code = zscreen_params(op1, &cscreen.screens.indexed[i]);
+
+ if (code < 0)
+ return code;
+ cscreen.screens.indexed[i].spot_function = spot_dummy;
+ sprocs[i] = *op1;
+ space = max(space, r_space_index(op1));
+ }
+ mem = (gs_memory_t *)idmemory->spaces_indexed[space];
+ check_estack(8); /* for sampling screens */
+ rc_alloc_struct_0(pht, gs_halftone, &st_halftone,
+ mem, pht = 0, "setcolorscreen(halftone)");
+ rc_alloc_struct_0(pdht, gx_device_halftone, &st_device_halftone,
+ mem, pdht = 0, "setcolorscreen(device halftone)");
+ if (pht == 0 || pdht == 0)
+ code = gs_note_error(gs_error_VMerror);
+ else {
+ pht->type = ht_type_colorscreen;
+ pht->params.colorscreen = cscreen;
+ code = gs_sethalftone_prepare(igs, pht, pdht);
+ }
+ if (code >= 0) { /* Schedule the sampling of the screens. */
+ es_ptr esp0 = esp; /* for backing out */
+
+ esp += 8;
+ make_mark_estack(esp - 7, es_other, setcolorscreen_cleanup);
+ memcpy(esp - 6, sprocs, sizeof(ref) * 4); /* procs */
+ make_istruct(esp - 2, 0, pht);
+ make_istruct(esp - 1, 0, pdht);
+ make_op_estack(esp, setcolorscreen_finish);
+ for (i = 0; i < 4; i++) {
+ /* Shuffle the indices to correspond to */
+ /* the component order. */
+ code = zscreen_enum_init(i_ctx_p,
+ &pdht->components[(i + 1) & 3].corder,
+ &pht->params.colorscreen.screens.indexed[i],
+ &sprocs[i], 0, 0, space);
+ if (code < 0) {
+ esp = esp0;
+ break;
+ }
+ }
+ }
+ if (code < 0) {
+ gs_free_object(mem, pdht, "setcolorscreen(device halftone)");
+ gs_free_object(mem, pht, "setcolorscreen(halftone)");
+ return code;
+ }
+ pop(12);
+ return o_push_estack;
+}
+/* Install the color screen after sampling. */
+static int
+setcolorscreen_finish(i_ctx_t *i_ctx_p)
+{
+ gx_device_halftone *pdht = r_ptr(esp, gx_device_halftone);
+ int code;
+
+ pdht->order = pdht->components[0].corder;
+ code = gx_ht_install(igs, r_ptr(esp - 1, gs_halftone), pdht);
+ if (code < 0)
+ return code;
+ istate->screen_procs.red = esp[-5];
+ istate->screen_procs.green = esp[-4];
+ istate->screen_procs.blue = esp[-3];
+ istate->screen_procs.gray = esp[-2];
+ make_null(&istate->halftone);
+ esp -= 7;
+ setcolorscreen_cleanup(i_ctx_p);
+ return o_pop_estack;
+}
+/* Clean up after installing the color screen. */
+static int
+setcolorscreen_cleanup(i_ctx_t *i_ctx_p)
+{
+ gs_halftone *pht = r_ptr(esp + 6, gs_halftone);
+ gx_device_halftone *pdht = r_ptr(esp + 7, gx_device_halftone);
+
+ gs_free_object(pdht->rc.memory, pdht,
+ "setcolorscreen_cleanup(device halftone)");
+ gs_free_object(pht->rc.memory, pht,
+ "setcolorscreen_cleanup(halftone)");
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zht1_op_defs[] =
+{
+ {"<setcolorscreen", zsetcolorscreen},
+ /* Internal operators */
+ {"0%setcolorscreen_finish", setcolorscreen_finish},
+ op_def_end(0)
+};
diff --git a/psi/zht2.c b/psi/zht2.c
new file mode 100644
index 000000000..dc31f50d2
--- /dev/null
+++ b/psi/zht2.c
@@ -0,0 +1,554 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 2 sethalftone operator */
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gxdevice.h" /* for gzht.h */
+#include "gzht.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "iddict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "icolor.h"
+#include "iht.h"
+#include "store.h"
+#include "iname.h"
+#include "zht2.h"
+
+/* Forward references */
+static int dict_spot_params(const ref *, gs_spot_halftone *, ref *, ref *,
+ gs_memory_t *);
+static int dict_spot_results(i_ctx_t *, ref *, const gs_spot_halftone *);
+static int dict_threshold_params(const ref *, gs_threshold_halftone *,
+ ref *);
+static int dict_threshold2_params(const ref *, gs_threshold2_halftone *,
+ ref *, gs_memory_t *);
+
+/*
+ * This routine translates a gs_separation_name value into a character string
+ * pointer and a string length.
+ */
+int
+gs_get_colorname_string(const gs_memory_t *mem, gs_separation_name colorname_index,
+ unsigned char **ppstr, unsigned int *pname_size)
+{
+ ref nref;
+
+ name_index_ref(mem, colorname_index, &nref);
+ name_string_ref(mem, &nref, &nref);
+ return obj_string_data(mem, &nref, (const unsigned char**) ppstr, pname_size);
+}
+
+/* Dummy spot function */
+static float
+spot1_dummy(double x, double y)
+{
+ return (x + y) / 2;
+}
+
+/* <dict> <dict5> .sethalftone5 - */
+static int sethalftone_finish(i_ctx_t *);
+static int sethalftone_cleanup(i_ctx_t *);
+static int
+zsethalftone5(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint count;
+ gs_halftone_component *phtc = 0;
+ gs_halftone_component *pc;
+ int code = 0;
+ int j;
+ bool have_default;
+ gs_halftone *pht = 0;
+ gx_device_halftone *pdht = 0;
+ ref sprocs[GS_CLIENT_COLOR_MAX_COMPONENTS + 1];
+ ref tprocs[GS_CLIENT_COLOR_MAX_COMPONENTS + 1];
+ gs_memory_t *mem;
+ uint edepth = ref_stack_count(&e_stack);
+ int npop = 2;
+ int dict_enum = dict_first(op);
+ ref rvalue[2];
+ int cname, colorant_number;
+ byte * pname;
+ uint name_size;
+ int halftonetype, type = 0;
+ gs_state *pgs = igs;
+ int space_index = r_space_index(op - 1);
+
+ mem = (gs_memory_t *) idmemory->spaces_indexed[space_index];
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ check_type(op[-1], t_dictionary);
+ check_dict_read(op[-1]);
+
+ /*
+ * We think that Type 2 and Type 4 halftones, like
+ * screens set by setcolorscreen, adapt automatically to
+ * the device color space, so we need to mark them
+ * with a different internal halftone type.
+ */
+ code = dict_int_param(op - 1, "HalftoneType", 1, 100, 0, &type);
+ if (code < 0)
+ return code;
+ halftonetype = (type == 2 || type == 4)
+ ? ht_type_multiple_colorscreen
+ : ht_type_multiple;
+
+ /* Count how many components that we will actually use. */
+
+ have_default = false;
+ for (count = 0; ;) {
+
+ /* Move to next element in the dictionary */
+ if ((dict_enum = dict_next(op, dict_enum, rvalue)) == -1)
+ break;
+ /*
+ * Verify that we have a valid component. We may have a
+ * /HalfToneType entry.
+ */
+ if (!r_has_type(&rvalue[0], t_name))
+ continue;
+ if (!r_has_type(&rvalue[1], t_dictionary))
+ continue;
+
+ /* Get the name of the component verify that we will use it. */
+ cname = name_index(mem, &rvalue[0]);
+ code = gs_get_colorname_string(mem, cname, &pname, &name_size);
+ if (code < 0)
+ break;
+ colorant_number = gs_cname_to_colorant_number(pgs, pname, name_size,
+ halftonetype);
+ if (colorant_number < 0)
+ continue;
+ else if (colorant_number == GX_DEVICE_COLOR_MAX_COMPONENTS) {
+ /* If here then we have the "Default" component */
+ if (have_default)
+ return_error(gs_error_rangecheck);
+ have_default = true;
+ }
+
+ count++;
+ /*
+ * Check to see if we have already reached the legal number of
+ * components.
+ */
+ if (count > GS_CLIENT_COLOR_MAX_COMPONENTS + 1) {
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ }
+ }
+ if (count == 0 || (halftonetype == ht_type_multiple && ! have_default))
+ code = gs_note_error(gs_error_rangecheck);
+
+ if (code >= 0) {
+ check_estack(5); /* for sampling Type 1 screens */
+ refset_null(sprocs, count);
+ refset_null(tprocs, count);
+ rc_alloc_struct_0(pht, gs_halftone, &st_halftone,
+ imemory, pht = 0, ".sethalftone5");
+ phtc = gs_alloc_struct_array(mem, count, gs_halftone_component,
+ &st_ht_component_element,
+ ".sethalftone5");
+ rc_alloc_struct_0(pdht, gx_device_halftone, &st_device_halftone,
+ imemory, pdht = 0, ".sethalftone5");
+ if (pht == 0 || phtc == 0 || pdht == 0) {
+ j = 0; /* Quiet the compiler:
+ gs_note_error isn't necessarily identity,
+ so j could be left ununitialized. */
+ code = gs_note_error(gs_error_VMerror);
+ }
+ }
+ if (code >= 0) {
+ dict_enum = dict_first(op);
+ for (j = 0, pc = phtc; ;) {
+ int type;
+
+ /* Move to next element in the dictionary */
+ if ((dict_enum = dict_next(op, dict_enum, rvalue)) == -1)
+ break;
+ /*
+ * Verify that we have a valid component. We may have a
+ * /HalfToneType entry.
+ */
+ if (!r_has_type(&rvalue[0], t_name))
+ continue;
+ if (!r_has_type(&rvalue[1], t_dictionary))
+ continue;
+
+ /* Get the name of the component */
+ cname = name_index(mem, &rvalue[0]);
+ code = gs_get_colorname_string(mem, cname, &pname, &name_size);
+ if (code < 0)
+ break;
+ colorant_number = gs_cname_to_colorant_number(pgs, pname, name_size,
+ halftonetype);
+ if (colorant_number < 0)
+ continue; /* Do not use this component */
+ pc->cname = cname;
+ pc->comp_number = colorant_number;
+
+ /* Now process the component dictionary */
+ check_dict_read(rvalue[1]);
+ if (dict_int_param(&rvalue[1], "HalftoneType", 1, 7, 0, &type) < 0) {
+ code = gs_note_error(gs_error_typecheck);
+ break;
+ }
+ switch (type) {
+ default:
+ code = gs_note_error(gs_error_rangecheck);
+ break;
+ case 1:
+ code = dict_spot_params(&rvalue[1], &pc->params.spot,
+ sprocs + j, tprocs + j, mem);
+ pc->params.spot.screen.spot_function = spot1_dummy;
+ pc->type = ht_type_spot;
+ break;
+ case 3:
+ code = dict_threshold_params(&rvalue[1], &pc->params.threshold,
+ tprocs + j);
+ pc->type = ht_type_threshold;
+ break;
+ case 7:
+ code = dict_threshold2_params(&rvalue[1], &pc->params.threshold2,
+ tprocs + j, imemory);
+ pc->type = ht_type_threshold2;
+ break;
+ }
+ if (code < 0)
+ break;
+ pc++;
+ j++;
+ }
+ }
+ if (code >= 0) {
+ pht->type = halftonetype;
+ pht->params.multiple.components = phtc;
+ pht->params.multiple.num_comp = j;
+ pht->params.multiple.get_colorname_string = gs_get_colorname_string;
+ code = gs_sethalftone_prepare(igs, pht, pdht);
+ }
+ if (code >= 0) {
+ /*
+ * Put the actual frequency and angle in the spot function component dictionaries.
+ */
+ dict_enum = dict_first(op);
+ for (pc = phtc; ; ) {
+ /* Move to next element in the dictionary */
+ if ((dict_enum = dict_next(op, dict_enum, rvalue)) == -1)
+ break;
+
+ /* Verify that we have a valid component */
+ if (!r_has_type(&rvalue[0], t_name))
+ continue;
+ if (!r_has_type(&rvalue[1], t_dictionary))
+ continue;
+
+ /* Get the name of the component and verify that we will use it. */
+ cname = name_index(mem, &rvalue[0]);
+ code = gs_get_colorname_string(mem, cname, &pname, &name_size);
+ if (code < 0)
+ break;
+ colorant_number = gs_cname_to_colorant_number(pgs, pname, name_size,
+ halftonetype);
+ if (colorant_number < 0)
+ continue;
+
+ if (pc->type == ht_type_spot) {
+ code = dict_spot_results(i_ctx_p, &rvalue[1], &pc->params.spot);
+ if (code < 0)
+ break;
+ }
+ pc++;
+ }
+ }
+ if (code >= 0) {
+ /*
+ * Schedule the sampling of any Type 1 screens,
+ * and any (Type 1 or Type 3) TransferFunctions.
+ * Save the stack depths in case we have to back out.
+ */
+ uint odepth = ref_stack_count(&o_stack);
+ ref odict, odict5;
+
+ odict = op[-1];
+ odict5 = *op;
+ pop(2);
+ op = osp;
+ esp += 5;
+ make_mark_estack(esp - 4, es_other, sethalftone_cleanup);
+ esp[-3] = odict;
+ make_istruct(esp - 2, 0, pht);
+ make_istruct(esp - 1, 0, pdht);
+ make_op_estack(esp, sethalftone_finish);
+ for (j = 0; j < count; j++) {
+ gx_ht_order *porder = NULL;
+
+ if (pdht->components == 0)
+ porder = &pdht->order;
+ else {
+ /* Find the component in pdht that matches component j in
+ the pht; gs_sethalftone_prepare() may permute these. */
+ int k;
+ int comp_number = phtc[j].comp_number;
+ for (k = 0; k < count; k++) {
+ if (pdht->components[k].comp_number == comp_number) {
+ porder = &pdht->components[k].corder;
+ break;
+ }
+ }
+ }
+ switch (phtc[j].type) {
+ case ht_type_spot:
+ code = zscreen_enum_init(i_ctx_p, porder,
+ &phtc[j].params.spot.screen,
+ &sprocs[j], 0, 0, space_index);
+ if (code < 0)
+ break;
+ /* falls through */
+ case ht_type_threshold:
+ if (!r_has_type(tprocs + j, t__invalid)) {
+ /* Schedule TransferFunction sampling. */
+ /****** check_xstack IS WRONG ******/
+ check_ostack(zcolor_remap_one_ostack);
+ check_estack(zcolor_remap_one_estack);
+ code = zcolor_remap_one(i_ctx_p, tprocs + j,
+ porder->transfer, igs,
+ zcolor_remap_one_finish);
+ op = osp;
+ }
+ break;
+ default: /* not possible here, but to keep */
+ /* the compilers happy.... */
+ ;
+ }
+ if (code < 0) { /* Restore the stack. */
+ ref_stack_pop_to(&o_stack, odepth);
+ ref_stack_pop_to(&e_stack, edepth);
+ op = osp;
+ op[-1] = odict;
+ *op = odict5;
+ break;
+ }
+ npop = 0;
+ }
+ }
+ if (code < 0) {
+ gs_free_object(mem, pdht, ".sethalftone5");
+ gs_free_object(mem, phtc, ".sethalftone5");
+ gs_free_object(mem, pht, ".sethalftone5");
+ return code;
+ }
+ pop(npop);
+ return (ref_stack_count(&e_stack) > edepth ? o_push_estack : 0);
+}
+
+/* Install the halftone after sampling. */
+static int
+sethalftone_finish(i_ctx_t *i_ctx_p)
+{
+ gx_device_halftone *pdht = r_ptr(esp, gx_device_halftone);
+ int code;
+
+ if (pdht->components)
+ pdht->order = pdht->components[0].corder;
+ code = gx_ht_install(igs, r_ptr(esp - 1, gs_halftone), pdht);
+ if (code < 0)
+ return code;
+ istate->halftone = esp[-2];
+ esp -= 4;
+ sethalftone_cleanup(i_ctx_p);
+ return o_pop_estack;
+}
+/* Clean up after installing the halftone. */
+static int
+sethalftone_cleanup(i_ctx_t *i_ctx_p)
+{
+ gx_device_halftone *pdht = r_ptr(&esp[4], gx_device_halftone);
+ gs_halftone *pht = r_ptr(&esp[3], gs_halftone);
+
+ gs_free_object(pdht->rc.memory, pdht,
+ "sethalftone_cleanup(device halftone)");
+ gs_free_object(pht->rc.memory, pht,
+ "sethalftone_cleanup(halftone)");
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zht2_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"2.sethalftone5", zsethalftone5},
+ /* Internal operators */
+ {"0%sethalftone_finish", sethalftone_finish},
+ op_def_end(0)
+};
+
+/* ------ Internal routines ------ */
+
+/* Extract frequency, angle, spot function, and accurate screens flag */
+/* from a dictionary. */
+static int
+dict_spot_params(const ref * pdict, gs_spot_halftone * psp,
+ ref * psproc, ref * ptproc, gs_memory_t *mem)
+{
+ int code;
+
+ check_dict_read(*pdict);
+ if ((code = dict_float_param(pdict, "Frequency", 0.0,
+ &psp->screen.frequency)) != 0 ||
+ (code = dict_float_param(pdict, "Angle", 0.0,
+ &psp->screen.angle)) != 0 ||
+ (code = dict_proc_param(pdict, "SpotFunction", psproc, false)) != 0 ||
+ (code = dict_bool_param(pdict, "AccurateScreens",
+ gs_currentaccuratescreens(mem),
+ &psp->accurate_screens)) < 0 ||
+ (code = dict_proc_param(pdict, "TransferFunction", ptproc, false)) < 0
+ )
+ return (code < 0 ? code : gs_error_undefined);
+ psp->transfer = (code > 0 ? (gs_mapping_proc) 0 : gs_mapped_transfer);
+ psp->transfer_closure.proc = 0;
+ psp->transfer_closure.data = 0;
+ return 0;
+}
+
+/* Set actual frequency and angle in a dictionary. */
+static int
+dict_real_result(i_ctx_t *i_ctx_p, ref * pdict, const char *kstr, double val)
+{
+ int code = 0;
+ ref *ignore;
+
+ if (dict_find_string(pdict, kstr, &ignore) > 0) {
+ ref rval;
+
+ check_dict_write(*pdict);
+ make_real(&rval, val);
+ code = idict_put_string(pdict, kstr, &rval);
+ }
+ return code;
+}
+static int
+dict_spot_results(i_ctx_t *i_ctx_p, ref * pdict, const gs_spot_halftone * psp)
+{
+ int code;
+
+ code = dict_real_result(i_ctx_p, pdict, "ActualFrequency",
+ psp->screen.actual_frequency);
+ if (code < 0)
+ return code;
+ return dict_real_result(i_ctx_p, pdict, "ActualAngle",
+ psp->screen.actual_angle);
+}
+
+/* Extract Width, Height, and TransferFunction from a dictionary. */
+static int
+dict_threshold_common_params(const ref * pdict,
+ gs_threshold_halftone_common * ptp,
+ ref **pptstring, ref *ptproc)
+{
+ int code;
+
+ check_dict_read(*pdict);
+ if ((code = dict_int_param(pdict, "Width", 1, 0x7fff, -1,
+ &ptp->width)) < 0 ||
+ (code = dict_int_param(pdict, "Height", 1, 0x7fff, -1,
+ &ptp->height)) < 0 ||
+ (code = dict_find_string(pdict, "Thresholds", pptstring)) <= 0 ||
+ (code = dict_proc_param(pdict, "TransferFunction", ptproc, false)) < 0
+ )
+ return (code < 0 ? code : gs_error_undefined);
+ ptp->transfer_closure.proc = 0;
+ ptp->transfer_closure.data = 0;
+ return code;
+}
+
+/* Extract threshold common parameters + Thresholds. */
+static int
+dict_threshold_params(const ref * pdict, gs_threshold_halftone * ptp,
+ ref * ptproc)
+{
+ ref *tstring;
+ int code =
+ dict_threshold_common_params(pdict,
+ (gs_threshold_halftone_common *)ptp,
+ &tstring, ptproc);
+
+ if (code < 0)
+ return code;
+ check_read_type_only(*tstring, t_string);
+ if (r_size(tstring) != (long)ptp->width * ptp->height)
+ return_error(gs_error_rangecheck);
+ ptp->thresholds.data = tstring->value.const_bytes;
+ ptp->thresholds.size = r_size(tstring);
+ ptp->transfer = (code > 0 ? (gs_mapping_proc) 0 : gs_mapped_transfer);
+ return 0;
+}
+
+/* Extract threshold common parameters + Thresholds, Width2, Height2, */
+/* BitsPerSample. */
+static int
+dict_threshold2_params(const ref * pdict, gs_threshold2_halftone * ptp,
+ ref * ptproc, gs_memory_t *mem)
+{
+ ref *tstring;
+ int code =
+ dict_threshold_common_params(pdict,
+ (gs_threshold_halftone_common *)ptp,
+ &tstring, ptproc);
+ int bps;
+ uint size;
+ int cw2, ch2;
+
+ if (code < 0 ||
+ (code = cw2 = dict_int_param(pdict, "Width2", 0, 0x7fff, 0,
+ &ptp->width2)) < 0 ||
+ (code = ch2 = dict_int_param(pdict, "Height2", 0, 0x7fff, 0,
+ &ptp->height2)) < 0 ||
+ (code = dict_int_param(pdict, "BitsPerSample", 8, 16, -1, &bps)) < 0
+ )
+ return code;
+ if ((bps != 8 && bps != 16) || cw2 != ch2 ||
+ (!cw2 && (ptp->width2 == 0 || ptp->height2 == 0))
+ )
+ return_error(gs_error_rangecheck);
+ ptp->bytes_per_sample = bps / 8;
+ switch (r_type(tstring)) {
+ case t_string:
+ size = r_size(tstring);
+ gs_bytestring_from_string(&ptp->thresholds, tstring->value.const_bytes,
+ size);
+ break;
+ case t_astruct:
+ if (gs_object_type(mem, tstring->value.pstruct) != &st_bytes)
+ return_error(gs_error_typecheck);
+ size = gs_object_size(mem, tstring->value.pstruct);
+ gs_bytestring_from_bytes(&ptp->thresholds, r_ptr(tstring, byte),
+ 0, size);
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ check_read(*tstring);
+ if (size != (ptp->width * ptp->height + ptp->width2 * ptp->height2) *
+ ptp->bytes_per_sample)
+ return_error(gs_error_rangecheck);
+ return 0;
+}
diff --git a/psi/zht2.h b/psi/zht2.h
new file mode 100644
index 000000000..20a06d617
--- /dev/null
+++ b/psi/zht2.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 2 sethalftone support */
+
+#ifndef zht2_INCLUDED
+# define zht2_INCLUDED
+
+#include "gscspace.h" /* for gs_separation_name */
+
+/*
+ * This routine translates a gs_separation_name value into a character string
+ * pointer and a string length.
+ */
+int gs_get_colorname_string(const gs_memory_t *mem,
+ gs_separation_name colorname_index,
+ unsigned char **ppstr,
+ unsigned int *pname_size);
+
+#endif /* zht2_INCLUDED */
diff --git a/psi/zicc.c b/psi/zicc.c
new file mode 100644
index 000000000..00e97b268
--- /dev/null
+++ b/psi/zicc.c
@@ -0,0 +1,575 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* ICCBased color operators */
+
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsstruct.h"
+#include "gxcspace.h" /* gscolor2.h requires gscspace.h */
+#include "stream.h"
+#include "files.h"
+#include "gscolor2.h"
+#include "gsicc.h"
+#include "estack.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "icie.h"
+#include "ialloc.h"
+#include "zicc.h"
+#include "gsicc_manage.h"
+#include "gx.h"
+#include "gxistate.h"
+#include "gsicc_create.h"
+#include "gsicc_profilecache.h"
+#include "gxdevice.h" /* for output intent setting */
+#include "gsicc_cache.h"
+
+int seticc(i_ctx_t * i_ctx_p, int ncomps, ref *ICCdict, float *range_buff)
+{
+ int code, k;
+ gs_color_space * pcs;
+ gs_color_space * palt_cs;
+ ref * pstrmval;
+ stream * s = 0L;
+ cmm_profile_t *picc_profile;
+ gs_imager_state * pis = (gs_imager_state *)igs;
+ int i, expected = 0;
+ ref * pnameval;
+ static const char *const icc_std_profile_names[] = {
+ GSICC_STANDARD_PROFILES
+ };
+ static const char *const icc_std_profile_keys[] = {
+ GSICC_STANDARD_PROFILES_KEYS
+ };
+
+ palt_cs = gs_currentcolorspace(igs);
+
+ /* verify the DataSource entry */
+ if (dict_find_string(ICCdict, "DataSource", &pstrmval) <= 0)
+ return_error(gs_error_undefined);
+ check_read_file(i_ctx_p, s, pstrmval);
+
+ /* build the color space object */
+ code = gs_cspace_build_ICC(&pcs, NULL, gs_state_memory(igs));
+ if (code < 0)
+ return gs_rethrow(code, "building color space object");
+ /* For now, dump the profile into a buffer
+ and obtain handle from the buffer when we need it.
+ We may want to change this later.
+ This depends to some degree on what the CMS is capable of doing.
+ I don't want to get bogged down on stream I/O at this point.
+ Note also, if we are going to be putting these into the clist we will
+ want to have this buffer. */
+ /* Check if we have the /Name entry. This is used to associate with
+ specs that have enumerated types to indicate sRGB sGray etc */
+ if (dict_find_string(ICCdict, "Name", &pnameval) > 0){
+ uint size = r_size(pnameval);
+ char *str = (char *)gs_alloc_bytes(gs_state_memory(igs), size+1, "seticc");
+ memcpy(str, (const char *)pnameval->value.bytes, size);
+ str[size] = 0;
+
+ /* Compare this to the standard profile names */
+ picc_profile = NULL;
+ for (k = 0; k < GSICC_NUMBER_STANDARD_PROFILES; k++) {
+ if ( strcmp( str, icc_std_profile_keys[k] ) == 0 ) {
+ picc_profile = gsicc_get_profile_handle_file(icc_std_profile_names[k],
+ strlen(icc_std_profile_names[k]), gs_state_memory(igs));
+ break;
+ }
+ }
+ } else {
+ picc_profile = gsicc_profile_new(s, gs_state_memory(igs), NULL, 0);
+ }
+ if (picc_profile == NULL) {
+ rc_decrement(picc_profile,"seticc");
+ rc_decrement(pcs,"seticc");
+ return -1;
+ }
+ code = gsicc_set_gscs_profile(pcs, picc_profile, gs_state_memory(igs));
+ if (code < 0) {
+ rc_decrement(picc_profile,"seticc");
+ rc_decrement(pcs,"seticc");
+ return code;
+ }
+ picc_profile->num_comps = ncomps;
+
+ /* We have to get the profile handle due to the fact that we need to know
+ if it has a data space that is CIELAB */
+ picc_profile->profile_handle =
+ gsicc_get_profile_handle_buffer(picc_profile->buffer,
+ picc_profile->buffer_size,
+ gs_state_memory(igs));
+ if (picc_profile->profile_handle == NULL) {
+ /* Free up everything, the profile is not valid. We will end up going
+ ahead and using a default based upon the number of components */
+ rc_decrement(picc_profile,"seticc");
+ rc_decrement(pcs,"seticc");
+ return -1;
+ }
+ picc_profile->data_cs = gscms_get_profile_data_space(picc_profile->profile_handle);
+ switch( picc_profile->data_cs ) {
+ case gsCIEXYZ:
+ case gsCIELAB:
+ case gsRGB:
+ expected = 3;
+ break;
+ case gsGRAY:
+ expected = 1;
+ break;
+ case gsCMYK:
+ expected = 4;
+ break;
+ case gsNCHANNEL:
+ case gsNAMED: /* Silence warnings */
+ case gsUNDEFINED: /* Silence warnings */
+ break;
+ }
+ if (!expected || ncomps != expected) {
+ rc_decrement(picc_profile,"seticc");
+ rc_decrement(pcs,"seticc");
+ return_error(gs_error_rangecheck);
+ }
+
+ /* Lets go ahead and get the hash code and check if we match one of the default spaces */
+ /* Later we may want to delay this, but for now lets go ahead and do it */
+ gsicc_init_hash_cs(picc_profile, pis);
+
+ /* Set the range according to the data type that is associated with the
+ ICC input color type. Occasionally, we will run into CIELAB to CIELAB
+ profiles for spot colors in PDF documents. These spot colors are typically described
+ as separation colors with tint transforms that go from a tint value
+ to a linear mapping between the CIELAB white point and the CIELAB tint
+ color. This results in a CIELAB value that we need to use to fill. We
+ need to detect this to make sure we do the proper scaling of the data. For
+ CIELAB images in PDF, the source is always normal 8 or 16 bit encoded data
+ in the range from 0 to 255 or 0 to 65535. In that case, there should not
+ be any encoding and decoding to CIELAB. The PDF content will not include
+ an ICC profile but will set the color space to \Lab. In this case, we use
+ our seticc_lab operation to install the LAB to LAB profile, but we detect
+ that we did that through the use of the is_lab flag in the profile descriptor.
+ When then avoid the CIELAB encode and decode */
+ if (picc_profile->data_cs == gsCIELAB) {
+ /* If the input space to this profile is CIELAB, then we need to adjust the limits */
+ /* See ICC spec ICC.1:2004-10 Section 6.3.4.2 and 6.4. I don't believe we need to
+ worry about CIEXYZ profiles or any of the other odds ones. Need to check that though
+ at some point. */
+ picc_profile->Range.ranges[0].rmin = 0.0;
+ picc_profile->Range.ranges[0].rmax = 100.0;
+ picc_profile->Range.ranges[1].rmin = -128.0;
+ picc_profile->Range.ranges[1].rmax = 127.0;
+ picc_profile->Range.ranges[2].rmin = -128.0;
+ picc_profile->Range.ranges[2].rmax = 127.0;
+ picc_profile->islab = true;
+ } else {
+ for (i = 0; i < ncomps; i++) {
+ picc_profile->Range.ranges[i].rmin = range_buff[2 * i];
+ picc_profile->Range.ranges[i].rmax = range_buff[2 * i + 1];
+ }
+ }
+ /* Now see if we are in an overide situation. We have to wait until now
+ in case this is an LAB profile which we will not overide */
+ if (gs_currentoverrideicc(pis) && picc_profile->data_cs != gsCIELAB) {
+ /* Free up the profile structure */
+ switch( picc_profile->data_cs ) {
+ case gsRGB:
+ pcs->cmm_icc_profile_data = pis->icc_manager->default_rgb;
+ break;
+ case gsGRAY:
+ pcs->cmm_icc_profile_data = pis->icc_manager->default_gray;
+ break;
+ case gsCMYK:
+ pcs->cmm_icc_profile_data = pis->icc_manager->default_cmyk;
+ break;
+ default:
+ break;
+ }
+ /* Have one increment from the color space. Having these tied
+ together is not really correct. Need to fix that. ToDo. MJV */
+ rc_adjust(picc_profile, -2, "seticc");
+ rc_increment(pcs->cmm_icc_profile_data);
+ }
+ /* Set the color space. We are done. No joint cache here... */
+ code = gs_setcolorspace(igs, pcs);
+ /* The context has taken a reference to the colorspace. We no longer need
+ * ours, so drop it. */
+ rc_decrement_only(pcs, "seticc");
+ /* In this case, we already have a ref count of 2 on the icc profile
+ one for when it was created and one for when it was set. We really
+ only want one here so adjust */
+ rc_decrement(picc_profile,"seticc");
+ /* Remove the ICC dict from the stack */
+ pop(1);
+ return code;
+}
+
+/*
+ * <dict> .set_outputintent -
+ *
+ * Set and use the specified output intent.
+ *
+ */
+static int
+zset_outputintent(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = 0;
+ gx_device *dev = gs_currentdevice(igs);
+ gs_imager_state *pis = (gs_imager_state *)igs;
+ cmm_dev_profile_t *dev_profile;
+ stream * s = 0L;
+ ref * pnval;
+ ref * pstrmval;
+ int ncomps, dev_comps;
+ cmm_profile_t *picc_profile;
+ int expected = 0;
+ gs_color_space_index index;
+ gsicc_manager_t *icc_manager = pis->icc_manager;
+ cmm_profile_t *source_profile = NULL;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if_debug0m(gs_debug_flag_icc, imemory, "[icc] Using OutputIntent\n");
+
+ /* Get the device structure */
+ code = dev_proc(dev, get_profile)(dev, &dev_profile);
+ if (dev_profile == NULL) {
+ code = gsicc_init_device_profile_struct(dev, NULL, 0);
+ code = dev_proc(dev, get_profile)(dev, &dev_profile);
+ }
+ if (dev_profile->oi_profile != NULL) {
+ return 0; /* Allow only one setting of this object */
+ }
+ code = dict_find_string(op, "N", &pnval);
+ if (code < 0)
+ return code;
+ ncomps = pnval->value.intval;
+
+ /* verify the DataSource entry. Creat profile from stream */
+ if (dict_find_string(op, "DataSource", &pstrmval) <= 0)
+ return_error(gs_error_undefined);
+ check_read_file(i_ctx_p, s, pstrmval);
+
+ picc_profile = gsicc_profile_new(s, gs_state_memory(igs), NULL, 0);
+ picc_profile->num_comps = ncomps;
+ picc_profile->profile_handle =
+ gsicc_get_profile_handle_buffer(picc_profile->buffer,
+ picc_profile->buffer_size,
+ gs_state_memory(igs));
+ if (picc_profile->profile_handle == NULL) {
+ rc_decrement(picc_profile,"zset_outputintent");
+ return -1;
+ }
+ picc_profile->data_cs = gscms_get_profile_data_space(picc_profile->profile_handle);
+ switch (picc_profile->data_cs) {
+ case gsCIEXYZ:
+ case gsCIELAB:
+ case gsRGB:
+ expected = 3;
+ source_profile = icc_manager->default_rgb;
+ break;
+ case gsGRAY:
+ expected = 1;
+ source_profile = icc_manager->default_gray;
+ break;
+ case gsCMYK:
+ expected = 4;
+ source_profile = icc_manager->default_cmyk;
+ break;
+ case gsNCHANNEL:
+ expected = 0;
+ break;
+ case gsNAMED:
+ case gsUNDEFINED:
+ break;
+ }
+ if (expected && ncomps != expected) {
+ rc_decrement(picc_profile,"zset_outputintent");
+ return_error(gs_error_rangecheck);
+ }
+ gsicc_init_hash_cs(picc_profile, pis);
+
+ /* All is well with the profile. Lets set the stuff that needs to be set */
+ dev_profile->oi_profile = picc_profile;
+ picc_profile->name = (char *) gs_alloc_bytes(picc_profile->memory,
+ MAX_DEFAULT_ICC_LENGTH,
+ "zset_outputintent");
+ strncpy(picc_profile->name, OI_PROFILE, strlen(OI_PROFILE));
+ picc_profile->name[strlen(OI_PROFILE)] = 0;
+ picc_profile->name_length = strlen(OI_PROFILE);
+ /* Set the range of the profile */
+ gscms_set_icc_range(&picc_profile);
+
+ /* If the output device has a different number of componenets, then we are
+ going to set the output intent as the proofing profile, unless the
+ proofing profile has already been set.
+
+ If the device has the same number of components (and color model) then as
+ the profile we will use this as the output profile, unless someone has
+ explicitly set the output profile.
+
+ Finally, we will use the output intent profile for the default profile
+ of the proper Device profile in the icc manager, again, unless someone
+ has explicitly set this default profile. */
+
+ dev_comps = dev_profile->device_profile[0]->num_comps;
+ index = gsicc_get_default_type(dev_profile->device_profile[0]);
+ if (ncomps == dev_comps && index < gs_color_space_index_DevicePixel) {
+ /* The OI profile is the same type as the profile for the device and a
+ "default" profile for the device was not externally set. So we go
+ ahead and use the OI profile as the device profile. Care needs to be
+ taken here to keep from screwing up any device parameters. We will
+ use a keyword of OIProfile for the user/device parameter to indicate
+ its usage. Also, note conflicts if one is setting object dependent
+ color management */
+ rc_assign(dev_profile->device_profile[0], picc_profile,
+ "zset_outputintent");
+ if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used for device profile\n");
+ } else {
+ if (dev_profile->proof_profile == NULL) {
+ /* This means that we should use the OI profile as the proofing
+ profile. Note that if someone already has specified a
+ proofing profile it is unclear what they are trying to do
+ with the output intent. In this case, we will use it
+ just for the source data below */
+ dev_profile->proof_profile = picc_profile;
+ rc_increment(picc_profile);
+ if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used for proof profile\n");
+ }
+ }
+ /* Now the source colors. See which source color space needs to use the
+ output intent ICC profile */
+ index = gsicc_get_default_type(source_profile);
+ if (index < gs_color_space_index_DevicePixel) {
+ /* source_profile is currently the default. Set it to the OI profile */
+ switch (picc_profile->data_cs) {
+ case gsGRAY:
+ if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used source Gray\n");
+ rc_assign(icc_manager->default_gray, picc_profile,
+ "zset_outputintent");
+ break;
+ case gsRGB:
+ if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used source RGB\n");
+ rc_assign(icc_manager->default_rgb, picc_profile,
+ "zset_outputintent");
+ break;
+ case gsCMYK:
+ if_debug0m(gs_debug_flag_icc, imemory, "[icc] OutputIntent used source CMYK\n");
+ rc_assign(icc_manager->default_cmyk, picc_profile,
+ "zset_outputintent");
+ break;
+ default:
+ break;
+ }
+ }
+ /* Remove the output intent dict from the stack */
+ pop(1);
+ return code;
+}
+
+/*
+ * <dict> .seticcspace -
+ *
+ * Create an ICCBased color space and set it to be the current color space.
+ *
+ * The PostScript structure of an ICCBased color space is that same as that
+ * for a CIEBased* color space:
+ *
+ * [ /ICCBased <dictionary> ]
+ *
+ * As is the for other .setcie*space operators, the operand dictionary rather
+ * than the complete color space array is on the stack when this operator
+ * is inovked.
+ *
+ * At the time this procedure is called, the alternative color space for
+ * the ICCBased color space is expected to be the current color space,
+ * whether that space was explicitly specified or implied by the number
+ * of components in the ICCBased color space dictionary. This is consistent
+ * with the handling of alternative spaces in Separation, DeviceN, and
+ * Indexed color spaces. Unlike the "zset*space" routines for those spaces,
+ * however, the current code does not attempt to build the color space
+ * "in place" in the graphic state.
+ *
+ * The procedure that invokes this operator will already have checked that
+ * the operand is a dictionary, is readable, and defines the key /N
+ * (number of components).
+ */
+static int
+zseticcspace(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ gs_color_space * palt_cs;
+ ref * pnval;
+ ref * pstrmval;
+ stream * s;
+ int i, ncomps;
+ float range_buff[8];
+ static const float dflt_range[8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
+
+ code = dict_find_string(op, "N", &pnval);
+ if (code < 0)
+ return code;
+ ncomps = pnval->value.intval;
+ if (2*ncomps > sizeof(range_buff)/sizeof(range_buff[0]))
+ return_error(gs_error_rangecheck);
+ /* verify the DataSource entry */
+ if (dict_find_string(op, "DataSource", &pstrmval) <= 0)
+ return_error(gs_error_undefined);
+ check_read_file(i_ctx_p, s, pstrmval);
+ /*
+ * Verify that the current color space can be a alternative color space.
+ * The check for ICCBased color space is a hack to avoid introducing yet
+ * another category indicator into the gs_color_space_type structur.
+ */
+ palt_cs = gs_currentcolorspace(igs);
+ if ( !palt_cs->type->can_be_alt_space ||
+ gs_color_space_get_index(palt_cs) == gs_color_space_index_ICC )
+ return_error(gs_error_rangecheck);
+ /*
+ * Fetch and verify the Range array.
+ *
+ * The PDF documentation is unclear as to the purpose of this array.
+ * Essentially all that is stated is that "These values must match the
+ * information in the ICC profile" (PDF Reference, 2nd ed., p. 174).
+ * If that is the case, why not use the information in the profile?
+ * The only reason we can think of is range specification is intended
+ * to be used to limit the range of values passed to the alternate
+ * color space (the range may be smaller than the native range of values
+ * provided by that color space).
+ *
+ * Because the cms code will perform normalization based on color
+ * space, we use the range values only to restrict the set of input
+ * values; they are not used for normalization.
+ */
+ code = dict_floats_param( imemory,
+ op,
+ "Range",
+ 2 * ncomps,
+ range_buff,
+ dflt_range );
+ for (i = 0; i < 2 * ncomps && range_buff[i + 1] >= range_buff[i]; i += 2)
+ ;
+ if (i != 2 * ncomps)
+ return_error(gs_error_rangecheck);
+ return seticc(i_ctx_p, ncomps, op, range_buff);
+}
+
+/* Install a ICC type color space and use the ICC LABLUT profile. */
+int
+seticc_lab(i_ctx_t * i_ctx_p, float *white, float *black, float *range_buff)
+{
+ int code;
+ gs_color_space * pcs;
+ gs_color_space * palt_cs;
+ gs_imager_state * pis = (gs_imager_state *)igs;
+ int i;
+ static const char *const rfs = LAB_ICC;
+ gs_param_string val, *pval;
+
+ val.data = (const byte *)rfs;
+ val.size = strlen(rfs);
+ val.persistent = true;
+ pval = &val;
+
+ palt_cs = gs_currentcolorspace(igs);
+ /* build the color space object */
+ code = gs_cspace_build_ICC(&pcs, NULL, gs_state_memory(igs));
+ if (code < 0)
+ return gs_rethrow(code, "building color space object");
+ /* record the current space as the alternative color space */
+ /* pcs->base_space = palt_cs;
+ rc_increment_cs(palt_cs); */
+ /* Get the lab profile. It may already be set in the icc manager.
+ If not then lets populate it. */
+ if (pis->icc_manager->lab_profile == NULL ) {
+ /* This can't happen as the profile
+ should be initialized during the
+ setting of the user params */
+ return gs_rethrow(code, "cannot find lab icc profile");
+ }
+ /* Assign the LAB to LAB profile to this color space */
+ code = gsicc_set_gscs_profile(pcs, pis->icc_manager->lab_profile, gs_state_memory(igs));
+ rc_increment(pis->icc_manager->lab_profile);
+ if (code < 0)
+ return gs_rethrow(code, "installing the lab profile");
+ pcs->cmm_icc_profile_data->Range.ranges[0].rmin = 0.0;
+ pcs->cmm_icc_profile_data->Range.ranges[0].rmax = 100.0;
+ for (i = 1; i < 3; i++) {
+ pcs->cmm_icc_profile_data->Range.ranges[i].rmin =
+ range_buff[2 * (i-1)];
+ pcs->cmm_icc_profile_data->Range.ranges[i].rmax =
+ range_buff[2 * (i-1) + 1];
+ }
+ /* Set the color space. We are done. */
+ code = gs_setcolorspace(igs, pcs);
+ return code;
+}
+
+/* Install an ICC space from the PDF CalRGB or CalGray types */
+int
+seticc_cal(i_ctx_t * i_ctx_p, float *white, float *black, float *gamma,
+ float *matrix, int num_colorants, ulong dictkey)
+{
+ int code;
+ gs_color_space * pcs;
+ gs_imager_state * pis = (gs_imager_state *)igs;
+ gs_memory_t *mem = pis->memory;
+ int i;
+ cmm_profile_t *cal_profile;
+
+ /* See if the color space is in the profile cache */
+ pcs = gsicc_find_cs(dictkey, igs);
+ if (pcs == NULL ) {
+ /* build the color space object. Since this is cached
+ in the profile cache which is a member variable
+ of the graphic state, we will want to use stable
+ memory here */
+ code = gs_cspace_build_ICC(&pcs, NULL, mem->stable_memory);
+ if (code < 0)
+ return gs_rethrow(code, "building color space object");
+ /* There is no alternate for this. Perhaps we should set DeviceRGB? */
+ pcs->base_space = NULL;
+ /* Create the ICC profile from the CalRGB or CalGray parameters */
+ cal_profile = gsicc_create_from_cal(white, black, gamma, matrix,
+ mem->stable_memory, num_colorants);
+ if (cal_profile == NULL)
+ return gs_rethrow(-1, "creating the cal profile");
+ /* Assign the profile to this color space */
+ code = gsicc_set_gscs_profile(pcs, cal_profile, mem->stable_memory);
+ if (code < 0)
+ return gs_rethrow(code, "installing the cal profile");
+ for (i = 0; i < num_colorants; i++) {
+ pcs->cmm_icc_profile_data->Range.ranges[i].rmin = 0;
+ pcs->cmm_icc_profile_data->Range.ranges[i].rmax = 1;
+ }
+ /* Add the color space to the profile cache */
+ gsicc_add_cs(igs, pcs,dictkey);
+ }
+ /* Set the color space. We are done. */
+ code = gs_setcolorspace(igs, pcs);
+ return code;
+}
+
+const op_def zicc_ll3_op_defs[] = {
+ op_def_begin_ll3(),
+ { "1.seticcspace", zseticcspace },
+ { "1.set_outputintent", zset_outputintent },
+ op_def_end(0)
+};
diff --git a/psi/zicc.h b/psi/zicc.h
new file mode 100644
index 000000000..f0e532263
--- /dev/null
+++ b/psi/zicc.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Definitions for setcolorspace */
+
+#ifndef zicc_INCLUDED
+# define zicc_INCLUDED
+
+int seticc(i_ctx_t * i_ctx_p, int ncomps, ref *ICCdict, float *range_buff);
+int seticc_lab(i_ctx_t * i_ctx_p, float *white, float *black, float *range_buff);
+int seticc_cal(i_ctx_t * i_ctx_p, float *white, float *black, float *gamma,
+ float *matrix, int num_colorants,ulong dictkey);
+#endif /* zicc_INCLUDED */
diff --git a/psi/zimage.c b/psi/zimage.c
new file mode 100644
index 000000000..c886f5711
--- /dev/null
+++ b/psi/zimage.c
@@ -0,0 +1,655 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Image operators */
+#include "math_.h"
+#include "memory_.h"
+#include "stat_.h" /* get system header early to avoid name clash on Cygwin */
+#include "ghost.h"
+#include "oper.h"
+#include "gscolor.h"
+#include "gscspace.h"
+#include "gscolor2.h"
+#include "gsmatrix.h"
+#include "gsimage.h"
+#include "gxfixed.h"
+#include "gsstruct.h"
+#include "gxiparam.h"
+#include "idict.h"
+#include "idparam.h"
+#include "estack.h" /* for image[mask] */
+#include "ialloc.h"
+#include "igstate.h"
+#include "ilevel.h"
+#include "store.h"
+#include "stream.h"
+#include "ifilter.h" /* for stream exception handling */
+#include "iimage.h"
+#include "gxcspace.h"
+
+/* Forward references */
+static int zimage_data_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
+ gx_image_enum_common_t * pie,
+ const ref * sources, int npop);
+static int image_proc_process(i_ctx_t *);
+static int image_file_continue(i_ctx_t *);
+static int image_string_continue(i_ctx_t *);
+static int image_cleanup(i_ctx_t *);
+
+/* Extract and check the parameters for a gs_data_image_t. */
+int
+data_image_params(const gs_memory_t *mem,
+ const ref *op, gs_data_image_t *pim,
+ image_params *pip, bool require_DataSource,
+ int num_components, int max_bits_per_component,
+ bool has_alpha, bool islab)
+{
+ int code;
+ ref *pds;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ code = dict_int_param(op, "Width", 0, max_int_in_fixed/2, -1, &pim->Width);
+ if (code < 0)
+ return code;
+ code = dict_int_param(op, "Height", 0, max_int_in_fixed/2, -1, &pim->Height);
+ if (code < 0)
+ return code;
+ code = dict_matrix_param(mem, op, "ImageMatrix", &pim->ImageMatrix);
+ if (code < 0)
+ return code;
+ code = dict_bool_param(op, "MultipleDataSources", false, &pip->MultipleDataSources);
+ if (code < 0)
+ return code;
+ code = dict_int_param(op, "BitsPerComponent", 1, max_bits_per_component, -1, &pim->BitsPerComponent);
+ if (code < 0)
+ return code;
+ code = dict_bool_param(op, "Interpolate", false, &pim->Interpolate);
+ if (code < 0)
+ return code;
+
+ /* Decode size pulled out of here to catch case of Lab color space which
+ has a 4 entry range. We also do NOT want to do Lab decoding IF range
+ is the common -128 127 for a and b. Otherwise we end up doing multiple
+ decode operations, since ICC flow will expect encoded data.
+ That is resolved later. Also discovered that PDF write will stick
+ 6 entry range in wich appears to be OK as far as AR is concerned so
+ we have to handle that too. */
+ if (islab) {
+ /* Note that it is possible that only the ab range values are there
+ or the lab values. I have seen both cases.... */
+ code = dict_floats_param(mem, op, "Decode", 4,
+ &pim->Decode[2], NULL);
+ if (code < 0) {
+ /* Try for all three pairs. Ignore more than 6 elements */
+ code = dict_float_array_check_param(mem, op, "Decode", 6,
+ &pim->Decode[0], NULL, gs_error_rangecheck, 0); /* over_error = 0 */
+ } else {
+ /* Set the range on the L */
+ pim->Decode[0] = 0;
+ pim->Decode[1] = 100.0;
+ }
+ if (code < 0)
+ return code;
+ } else {
+ /* more elements than we need is OK */
+ code = dict_float_array_check_param(mem, op, "Decode", 2 * num_components,
+ &pim->Decode[0], NULL, gs_error_rangecheck, 0); /* over_error = 0 */
+ if (code < 0)
+ return code;
+ }
+ pip->pDecode = &pim->Decode[0];
+ /* Extract and check the data sources. */
+ if ((code = dict_find_string(op, "DataSource", &pds)) <= 0) {
+ if (require_DataSource)
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+ return 1; /* no data source */
+ }
+ if (pip->MultipleDataSources) {
+ ref *ds = pip->DataSource;
+ long i, n = num_components + (has_alpha ? 1 : 0);
+ if (!r_is_array(pds))
+ return_error(gs_error_typecheck);
+ if (r_size(pds) != n)
+ return_error(gs_error_rangecheck);
+ for (i = 0; i < n; ++i)
+ array_get(mem, pds, i, &ds[i]);
+ if (r_type(&ds[0]) == t_string) {
+ /* We don't have a problem with the strings of different length
+ * but Adobe does and CET tast 12-02.ps reports this as an error.
+ */
+ if (has_alpha)
+ n--;
+ for (i = 1; i < n; ++i) {
+ if (r_type(&ds[i]) == t_string && r_size(&ds[i]) != r_size(&ds[0])) {
+ return_error(gs_error_rangecheck);
+ }
+ }
+ }
+ } else
+ pip->DataSource[0] = *pds;
+ return 0;
+}
+
+/* Extract and check the parameters for a gs_pixel_image_t. */
+int
+pixel_image_params(i_ctx_t *i_ctx_p, const ref *op, gs_pixel_image_t *pim,
+ image_params *pip, int max_bits_per_component,
+ bool has_alpha, gs_color_space *csp)
+{
+ bool islab = false;
+ int num_components =
+ gs_color_space_num_components(csp);
+ int code;
+
+ if (num_components < 1)
+ return_error(gs_error_rangecheck); /* Pattern space not allowed */
+ pim->ColorSpace = csp;
+
+ if (pim->ColorSpace->cmm_icc_profile_data != NULL)
+ islab = pim->ColorSpace->cmm_icc_profile_data->islab;
+
+ code = data_image_params(imemory, op, (gs_data_image_t *) pim, pip, true,
+ num_components, max_bits_per_component,
+ has_alpha, islab);
+ if (code < 0)
+ return code;
+ pim->format =
+ (pip->MultipleDataSources ? gs_image_format_component_planar :
+ gs_image_format_chunky);
+ return dict_bool_param(op, "CombineWithColor", false,
+ &pim->CombineWithColor);
+}
+
+/* Common setup for all Level 1 and 2 images, and ImageType 4 images. */
+int
+zimage_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
+ const ref * sources, bool uses_color, int npop)
+{
+ gx_image_enum_common_t *pie;
+ int code =
+ gs_image_begin_typed((const gs_image_common_t *)pim, igs,
+ uses_color, &pie);
+
+ if (code < 0)
+ return code;
+ return zimage_data_setup(i_ctx_p, (const gs_pixel_image_t *)pim, pie,
+ sources, npop);
+}
+
+/* Common code for .image1 and .alphaimage operators */
+int
+image1_setup(i_ctx_t * i_ctx_p, bool has_alpha)
+{
+ os_ptr op = osp;
+ gs_image_t image;
+ image_params ip;
+ int code;
+ gs_color_space *csp = gs_currentcolorspace(igs);
+
+ /* Adobe interpreters accept sampled images when the current color
+ * space is a pattern color space using the base color space instead
+ * of the pattern space. CET 12-07a-12
+ * If all conditions are not met the pattern color space goes through
+ * triggering a rangecheck error.
+ */
+ if (gs_currentcpsimode(imemory) && gs_color_space_num_components(csp) < 1) {
+ gs_color_space *bsp = csp->base_space;
+ if (bsp)
+ csp = bsp;
+ }
+
+ gs_image_t_init(&image, csp);
+ code = pixel_image_params( i_ctx_p,
+ op,
+ (gs_pixel_image_t *)&image,
+ &ip,
+ (level2_enabled ? 16 : 8),
+ has_alpha, csp);
+ if (code < 0)
+ return code;
+
+ image.Alpha = (has_alpha ? gs_image_alpha_last : gs_image_alpha_none);
+ /* swap Width, Height, and ImageMatrix so that it comes out the same */
+ /* This is only for performance, so only do it for non-skew cases */
+ if (image.Width == 1 && image.Height > 1 && image.BitsPerComponent == 8 &&
+ image.ImageMatrix.xy == 0.0 && image.ImageMatrix.yx == 0.0 &&
+ image.ImageMatrix.tx == 0.0) {
+ float ftmp;
+ int itemp;
+
+ itemp = image.Width;
+ image.Width = image.Height;
+ image.Height = itemp;
+
+ image.ImageMatrix.xy = image.ImageMatrix.xx;
+ image.ImageMatrix.yx = image.ImageMatrix.yy;
+ image.ImageMatrix.xx = 0.0;
+ image.ImageMatrix.yy = 0.0;
+ ftmp = image.ImageMatrix.tx;
+ image.ImageMatrix.tx = image.ImageMatrix.ty;
+ image.ImageMatrix.ty = ftmp;
+ }
+ return zimage_setup( i_ctx_p,
+ (gs_pixel_image_t *)&image,
+ &ip.DataSource[0],
+ image.CombineWithColor,
+ 1 );
+}
+
+/* <dict> .image1 - */
+static int
+zimage1(i_ctx_t *i_ctx_p)
+{
+ return image1_setup(i_ctx_p, false);
+}
+
+/* <dict> .imagemask1 - */
+static int
+zimagemask1(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_image_t image;
+ image_params ip;
+ int code;
+
+ gs_image_t_init_mask_adjust(&image, false,
+ gs_incachedevice(igs) != CACHE_DEVICE_NONE);
+ code = data_image_params(imemory, op, (gs_data_image_t *) & image,
+ &ip, true, 1, 1, false, false);
+ if (code < 0)
+ return code;
+ return zimage_setup(i_ctx_p, (gs_pixel_image_t *)&image, &ip.DataSource[0],
+ true, 1);
+}
+
+/* Common setup for all Level 1 and 2 images, and ImageType 3 and 4 images. */
+/*
+ * We push the following on the estack.
+ * control mark,
+ * num_sources,
+ * for I = num_sources-1 ... 0:
+ * data source I,
+ * aliasing information:
+ * if source is not file, 1, except that the topmost value
+ * is used for bookkeeping in the procedure case (see below);
+ * if file is referenced by a total of M different sources and
+ * this is the occurrence with the lowest I, M;
+ * otherwise, -J, where J is the lowest I of the same file as
+ * this one;
+ * current plane index,
+ * num_sources,
+ * enumeration structure.
+ */
+#define NUM_PUSH(nsource) ((nsource) * 2 + 5)
+/*
+ * We can access these values either from the bottom (esp at control mark - 1,
+ * EBOT macros) or the top (esp = enumeration structure, ETOP macros).
+ * Note that all macros return pointers.
+ */
+#define EBOT_NUM_SOURCES(ep) ((ep) + 2)
+#define EBOT_SOURCE(ep, i)\
+ ((ep) + 3 + (EBOT_NUM_SOURCES(ep)->value.intval - 1 - (i)) * 2)
+#define ETOP_SOURCE(ep, i)\
+ ((ep) - 4 - (i) * 2)
+#define ETOP_PLANE_INDEX(ep) ((ep) - 2)
+#define ETOP_NUM_SOURCES(ep) ((ep) - 1)
+static int
+zimage_data_setup(i_ctx_t *i_ctx_p, const gs_pixel_image_t * pim,
+ gx_image_enum_common_t * pie, const ref * sources, int npop)
+{
+ int num_sources = pie->num_planes;
+ int inumpush = NUM_PUSH(num_sources);
+ int code;
+ gs_image_enum *penum;
+ int px;
+ const ref *pp;
+ bool string_sources = true;
+
+ check_estack(inumpush + 2); /* stuff above, + continuation + proc */
+ make_int(EBOT_NUM_SOURCES(esp), num_sources);
+ /*
+ * Note that the data sources may be procedures, strings, or (Level
+ * 2 only) files. (The Level 1 reference manual says that Level 1
+ * requires procedures, but Adobe Level 1 interpreters also accept
+ * strings.) The sources must all be of the same type.
+ *
+ * The Adobe documentation explicitly says that if two or more of the
+ * data sources are the same or inter-dependent files, the result is not
+ * defined. We don't have a problem with the bookkeeping for
+ * inter-dependent files, since each one has its own buffer, but we do
+ * have to be careful if two or more sources are actually the same file.
+ * That is the reason for the aliasing information described above.
+ */
+ for (px = 0, pp = sources; px < num_sources; px++, pp++) {
+ es_ptr ep = EBOT_SOURCE(esp, px);
+
+ make_int(ep + 1, 1); /* default is no aliasing */
+ switch (r_type(pp)) {
+ case t_file:
+ if (!level2_enabled)
+ return_error(gs_error_typecheck);
+ /* Check for aliasing. */
+ {
+ int pi;
+
+ for (pi = 0; pi < px; ++pi)
+ if (sources[pi].value.pfile == pp->value.pfile) {
+ /* Record aliasing */
+ make_int(ep + 1, -pi);
+ EBOT_SOURCE(esp, pi)[1].value.intval++;
+ break;
+ }
+ }
+ string_sources = false;
+ /* falls through */
+ case t_string:
+ if (r_type(pp) != r_type(sources)) {
+ gx_image_end(pie, false); /* Clean up pie */
+ return_error(gs_error_typecheck);
+ }
+ check_read(*pp);
+ break;
+ default:
+ if (!r_is_proc(sources)) {
+ static const char ds[] = "DataSource";
+ gx_image_end(pie, false); /* Clean up pie */
+ gs_errorinfo_put_pair(i_ctx_p, ds, sizeof(ds) - 1, pp);
+ return_error(gs_error_typecheck);
+ }
+ check_proc(*pp);
+ string_sources = false;
+ }
+ *ep = *pp;
+ }
+ /* Always place the image enumerator into local memory,
+ because pie may have local objects inherited from igs,
+ which may be local when the current allocation mode is global.
+ Bug 688140. */
+ if ((penum = gs_image_enum_alloc(imemory_local, "image_setup")) == 0)
+ return_error(gs_error_VMerror);
+ code = gs_image_enum_init(penum, pie, (const gs_data_image_t *)pim, igs);
+ if (code != 0 || (pie->skipping && string_sources)) { /* error, or empty image */
+ int code1 = gs_image_cleanup_and_free_enum(penum, igs);
+
+ if (code >= 0) /* empty image */
+ pop(npop);
+ if (code >= 0 && code1 < 0)
+ code = code1;
+ return code;
+ }
+ push_mark_estack(es_other, image_cleanup);
+ esp += inumpush - 1;
+ make_int(ETOP_PLANE_INDEX(esp), 0);
+ make_int(ETOP_NUM_SOURCES(esp), num_sources);
+ make_struct(esp, avm_local, penum);
+ switch (r_type(sources)) {
+ case t_file:
+ push_op_estack(image_file_continue);
+ break;
+ case t_string:
+ push_op_estack(image_string_continue);
+ break;
+ default: /* procedure */
+ push_op_estack(image_proc_process);
+ break;
+ }
+ pop(npop);
+ return o_push_estack;
+}
+/* Pop all the control information off the e-stack. */
+static es_ptr
+zimage_pop_estack(es_ptr tep)
+{
+ return tep - NUM_PUSH(ETOP_NUM_SOURCES(tep)->value.intval);
+}
+
+/*
+ * Continuation for procedure data source. We use the topmost aliasing slot
+ * to remember whether we've just called the procedure (1) or whether we're
+ * returning from a RemapColor callout (0).
+ */
+static int
+image_proc_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_image_enum *penum = r_ptr(esp, gs_image_enum);
+ int px = ETOP_PLANE_INDEX(esp)->value.intval;
+ int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
+ uint size, used[GS_IMAGE_MAX_COMPONENTS];
+ gs_const_string plane_data[GS_IMAGE_MAX_COMPONENTS];
+ const byte *wanted;
+ int i, code;
+
+ if (!r_has_type_attrs(op, t_string, a_read)) {
+ check_op(1);
+ /* Procedure didn't return a (readable) string. Quit. */
+ esp = zimage_pop_estack(esp);
+ image_cleanup(i_ctx_p);
+ return_error(!r_has_type(op, t_string) ? gs_error_typecheck : gs_error_invalidaccess);
+ }
+ size = r_size(op);
+ if (size == 0 && ETOP_SOURCE(esp, 0)[1].value.intval == 0)
+ code = 1;
+ else {
+ for (i = 0; i < num_sources; i++)
+ plane_data[i].size = 0;
+ plane_data[px].data = op->value.bytes;
+ plane_data[px].size = size;
+ code = gs_image_next_planes(penum, plane_data, used);
+ if (code == gs_error_Remap_Color) {
+ op->value.bytes += used[px]; /* skip used data */
+ r_dec_size(op, used[px]);
+ ETOP_SOURCE(esp, 0)[1].value.intval = 0; /* RemapColor callout */
+ return code;
+ }
+ }
+ if (code) { /* Stop now. */
+ esp = zimage_pop_estack(esp);
+ pop(1);
+ image_cleanup(i_ctx_p);
+ return (code < 0 ? code : o_pop_estack);
+ }
+ pop(1);
+ wanted = gs_image_planes_wanted(penum);
+ do {
+ if (++px == num_sources)
+ px = 0;
+ } while (!wanted[px]);
+ ETOP_PLANE_INDEX(esp)->value.intval = px;
+ return image_proc_process(i_ctx_p);
+}
+static int
+image_proc_process(i_ctx_t *i_ctx_p)
+{
+ int px = ETOP_PLANE_INDEX(esp)->value.intval;
+ gs_image_enum *penum = r_ptr(esp, gs_image_enum);
+ const byte *wanted = gs_image_planes_wanted(penum);
+ int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
+ const ref *pp;
+
+ ETOP_SOURCE(esp, 0)[1].value.intval = 0; /* procedure callout */
+ while (!wanted[px]) {
+ if (++px == num_sources)
+ px = 0;
+ ETOP_PLANE_INDEX(esp)->value.intval = px;
+ }
+ pp = ETOP_SOURCE(esp, px);
+ push_op_estack(image_proc_continue);
+ *++esp = *pp;
+ return o_push_estack;
+}
+
+/* Continue processing data from an image with file data sources. */
+static int
+image_file_continue(i_ctx_t *i_ctx_p)
+{
+ gs_image_enum *penum = r_ptr(esp, gs_image_enum);
+ int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
+
+ for (;;) {
+ uint min_avail = max_int;
+ gs_const_string plane_data[GS_IMAGE_MAX_COMPONENTS];
+ int code;
+ int px;
+ const ref *pp;
+ int at_eof_count = 0;
+ int total_used;
+
+ /*
+ * Do a first pass through the files to ensure that at least
+ * one has data available in its buffer.
+ */
+
+ for (px = 0, pp = ETOP_SOURCE(esp, 0); px < num_sources;
+ ++px, pp -= 2
+ ) {
+ int num_aliases = pp[1].value.intval;
+ stream *s = pp->value.pfile;
+ int min_left;
+ uint avail;
+
+ if (num_aliases <= 0)
+ num_aliases = ETOP_SOURCE(esp, -num_aliases)[1].value.intval;
+ while ((avail = sbufavailable(s)) <=
+ (min_left = sbuf_min_left(s)) + num_aliases - 1) {
+ int next = s->end_status;
+
+ switch (next) {
+ case 0:
+ s_process_read_buf(s);
+ continue;
+ case EOFC:
+ at_eof_count++;
+ break; /* with no data available */
+ case INTC:
+ case CALLC:
+ return
+ s_handle_read_exception(i_ctx_p, next, pp,
+ NULL, 0, image_file_continue);
+ default:
+ /* case ERRC: */
+ return_error(gs_error_ioerror);
+ }
+ break; /* for EOFC */
+ }
+ /*
+ * Note that in the EOF case, we can get here with no data
+ * available.
+ */
+ if (avail >= min_left)
+ avail = (avail - min_left) / num_aliases; /* may be 0 */
+ if (avail < min_avail)
+ min_avail = avail;
+ plane_data[px].data = sbufptr(s);
+ plane_data[px].size = avail;
+ }
+
+ /*
+ * Now pass the available buffered data to the image processor.
+ * Even if there is no available data, we must call
+ * gs_image_next_planes one more time to finish processing any
+ * retained data.
+ */
+
+ {
+ int pi;
+ uint used[GS_IMAGE_MAX_COMPONENTS];
+
+ code = gs_image_next_planes(penum, plane_data, used);
+ /* Now that used has been set, update the streams. */
+ total_used = 0;
+ for (pi = 0, pp = ETOP_SOURCE(esp, 0); pi < num_sources;
+ ++pi, pp -= 2 ) {
+ sbufskip(pp->value.pfile, used[pi]);
+ total_used += used[pi];
+ }
+ if (code == gs_error_Remap_Color)
+ return code;
+ }
+ if (at_eof_count >= num_sources || (at_eof_count && total_used == 0))
+ code = 1;
+ if (code) {
+ int code1;
+
+ esp = zimage_pop_estack(esp);
+ code1 = image_cleanup(i_ctx_p);
+ return (code < 0 ? code : code1 < 0 ? code1 : o_pop_estack);
+ }
+ }
+}
+
+/* Process data from an image with string data sources. */
+/* This may still encounter a RemapColor callback. */
+static int
+image_string_continue(i_ctx_t *i_ctx_p)
+{
+ gs_image_enum *penum = r_ptr(esp, gs_image_enum);
+ int num_sources = ETOP_NUM_SOURCES(esp)->value.intval;
+ gs_const_string sources[GS_IMAGE_MAX_COMPONENTS];
+ uint used[GS_IMAGE_MAX_COMPONENTS];
+
+ /* Pass no data initially, to find out how much is retained. */
+ memset(sources, 0, sizeof(sources[0]) * num_sources);
+ for (;;) {
+ int px;
+ int code = gs_image_next_planes(penum, sources, used);
+
+ if (code == gs_error_Remap_Color)
+ return code;
+ stop_now:
+ if (code) { /* Stop now. */
+ esp -= NUM_PUSH(num_sources);
+ image_cleanup(i_ctx_p);
+ return (code < 0 ? code : o_pop_estack);
+ }
+ for (px = 0; px < num_sources; ++px)
+ if (sources[px].size == 0) {
+ const ref *psrc = ETOP_SOURCE(esp, px);
+ uint size = r_size(psrc);
+
+ if (size == 0) { /* empty source */
+ code = 1;
+ goto stop_now;
+ }
+ sources[px].data = psrc->value.bytes;
+ sources[px].size = size;
+ }
+ }
+}
+
+/* Clean up after enumerating an image */
+static int
+image_cleanup(i_ctx_t *i_ctx_p)
+{
+ es_ptr ep_top = esp + NUM_PUSH(EBOT_NUM_SOURCES(esp)->value.intval);
+ gs_image_enum *penum = r_ptr(ep_top, gs_image_enum);
+
+ return gs_image_cleanup_and_free_enum(penum, igs);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zimage_op_defs[] =
+{
+ {"1.image1", zimage1},
+ {"1.imagemask1", zimagemask1},
+ /* Internal operators */
+ {"1%image_proc_continue", image_proc_continue},
+ {"0%image_file_continue", image_file_continue},
+ {"0%image_string_continue", image_string_continue},
+ op_def_end(0)
+};
diff --git a/psi/zimage2.c b/psi/zimage2.c
new file mode 100644
index 000000000..8bbf8a83c
--- /dev/null
+++ b/psi/zimage2.c
@@ -0,0 +1,41 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* image operator extensions for Level 2 PostScript */
+#include "memory_.h"
+#include "ghost.h"
+#include "gsimage.h"
+#include "gxiparam.h"
+#include "icstate.h"
+#include "iimage2.h"
+#include "igstate.h" /* for igs */
+
+/*
+ * Process an image that has no explicit source data. This isn't used by
+ * standard Level 2, but it's a very small procedure and is needed by
+ * both zdps.c and zdpnext.c.
+ */
+int
+process_non_source_image(i_ctx_t *i_ctx_p, const gs_image_common_t * pic,
+ client_name_t cname)
+{
+ gx_image_enum_common_t *pie;
+ int code = gs_image_begin_typed(pic, igs, false /****** WRONG ******/ ,
+ &pie);
+
+ /* We didn't pass any data, so there's nothing to clean up. */
+ return code;
+}
diff --git a/psi/zimage3.c b/psi/zimage3.c
new file mode 100644
index 000000000..9384ed58f
--- /dev/null
+++ b/psi/zimage3.c
@@ -0,0 +1,137 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* LanguageLevel 3 ImageTypes (3 & 4 - masked images) */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gscspace.h" /* for gscolor2.h */
+#include "gscolor2.h"
+#include "gsiparm3.h"
+#include "gsiparm4.h"
+#include "gxiparam.h" /* for image enumerator */
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "iimage.h"
+#include "ialloc.h"
+
+/* <dict> .image3 - */
+static int
+zimage3(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_image3_t image;
+ int interleave_type;
+ ref *pDataDict;
+ ref *pMaskDict;
+ image_params ip_data, ip_mask;
+ int ignored;
+ int code, mcode;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ if ((code = dict_int_param(op, "InterleaveType", 1, 3, -1,
+ &interleave_type)) < 0
+ )
+ return code;
+ gs_image3_t_init(&image, NULL, interleave_type);
+ if (dict_find_string(op, "DataDict", &pDataDict) <= 0 ||
+ dict_find_string(op, "MaskDict", &pMaskDict) <= 0
+ )
+ return_error(gs_error_rangecheck);
+ if ((code = pixel_image_params(i_ctx_p, pDataDict,
+ (gs_pixel_image_t *)&image, &ip_data,
+ 12, false, gs_currentcolorspace(igs))) < 0 ||
+ (mcode = code = data_image_params(imemory, pMaskDict, &image.MaskDict,
+ &ip_mask, false, 1, 12, false, false)) < 0 ||
+ (code = dict_int_param(pDataDict, "ImageType", 1, 1, 0, &ignored)) < 0 ||
+ (code = dict_int_param(pMaskDict, "ImageType", 1, 1, 0, &ignored)) < 0
+ )
+ return code;
+ /*
+ * MaskDict must have a DataSource iff InterleaveType == 3.
+ */
+ if ((ip_data.MultipleDataSources && interleave_type != 3) ||
+ ip_mask.MultipleDataSources ||
+ mcode != (image.InterleaveType != 3)
+ )
+ return_error(gs_error_rangecheck);
+ if (image.InterleaveType == 3) {
+ /* Insert the mask DataSource before the data DataSources. */
+ memmove(&ip_data.DataSource[1], &ip_data.DataSource[0],
+ (countof(ip_data.DataSource) - 1) *
+ sizeof(ip_data.DataSource[0]));
+ ip_data.DataSource[0] = ip_mask.DataSource[0];
+ }
+ /* We never interpolate images with masks */
+ image.Interpolate = 0;
+ return zimage_setup(i_ctx_p, (gs_pixel_image_t *)&image,
+ &ip_data.DataSource[0],
+ image.CombineWithColor, 1);
+}
+
+/* <dict> .image4 - */
+static int
+zimage4(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_image4_t image;
+ image_params ip;
+ int num_components =
+ gs_color_space_num_components(gs_currentcolorspace(igs));
+ int colors[countof(image.MaskColor)];
+ int code;
+ int i;
+
+ gs_image4_t_init(&image, NULL);
+ code = pixel_image_params(i_ctx_p, op, (gs_pixel_image_t *)&image, &ip,
+ 12, false, gs_currentcolorspace(igs));
+ if (code < 0)
+ return code;
+ code = dict_int_array_check_param(imemory, op, "MaskColor",
+ num_components * 2, colors, 0, gs_error_rangecheck);
+ /* Clamp the color values to the unsigned range. */
+ if (code == num_components) {
+ image.MaskColor_is_range = false;
+ for (i = 0; i < code; ++i)
+ image.MaskColor[i] = (colors[i] < 0 ? ~(uint)0 : colors[i]);
+ }
+ else if (code == num_components * 2) {
+ image.MaskColor_is_range = true;
+ for (i = 0; i < code; i += 2) {
+ if (colors[i+1] < 0) /* no match possible */
+ image.MaskColor[i] = 1, image.MaskColor[i+1] = 0;
+ else {
+ image.MaskColor[i+1] = colors[i+1];
+ image.MaskColor[i] = max(colors[i], 0);
+ }
+ }
+ } else
+ return_error(code < 0 ? code : gs_note_error(gs_error_rangecheck));
+ return zimage_setup(i_ctx_p, (gs_pixel_image_t *)&image, &ip.DataSource[0],
+ image.CombineWithColor, 1);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zimage3_op_defs[] =
+{
+ op_def_begin_ll3(),
+ {"1.image3", zimage3},
+ {"1.image4", zimage4},
+ op_def_end(0)
+};
diff --git a/psi/ziodev.c b/psi/ziodev.c
new file mode 100644
index 000000000..9947f033e
--- /dev/null
+++ b/psi/ziodev.c
@@ -0,0 +1,301 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Standard IODevice implementation */
+#include "memory_.h"
+#include "stdio_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "gpcheck.h"
+#include "oper.h"
+#include "stream.h"
+#include "istream.h"
+#include "ialloc.h"
+#include "iscan.h"
+#include "ivmspace.h"
+#include "gxiodev.h" /* must come after stream.h */
+ /* and before files.h */
+#include "files.h"
+#include "scanchar.h" /* for char_EOL */
+#include "store.h"
+#include "ierrors.h"
+
+/* Import the dtype of the stdio IODevices. */
+extern const char iodev_dtype_stdio[];
+
+/* Define the special devices. */
+#define iodev_special(dname, init, open) {\
+ dname, iodev_dtype_stdio,\
+ { init, open, iodev_no_open_file, iodev_no_fopen, iodev_no_fclose,\
+ iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,\
+ iodev_no_enumerate_files, NULL, NULL,\
+ iodev_no_get_params, iodev_no_put_params\
+ }\
+}
+
+/*
+ * We need the current context pointer for accessing / opening the %std
+ * IODevices. However, this is not available to the open routine.
+ * Therefore, we use the hack of storing this pointer in the IODevice state
+ * pointer just before calling the open routines. We clear the pointer
+ * immediately afterwards so as not to wind up with dangling references.
+ */
+
+#define LINEEDIT_BUF_SIZE 20 /* initial size, not fixed size */
+/*static iodev_proc_open_device(lineedit_open);*/ /* no longer used */
+const gx_io_device gs_iodev_lineedit =
+ iodev_special("%lineedit%", iodev_no_init, iodev_no_open_device);
+
+#define STATEMENTEDIT_BUF_SIZE 50 /* initial size, not fixed size */
+/*static iodev_proc_open_device(statementedit_open);*/ /* no longer used */
+const gx_io_device gs_iodev_statementedit =
+ iodev_special("%statementedit%", iodev_no_init, iodev_no_open_device);
+
+/* ------ Operators ------ */
+
+/* <int> .getiodevice <string|null> */
+static int
+zgetiodevice(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_io_device *iodev;
+ const byte *dname;
+
+ check_type(*op, t_integer);
+ iodev = gs_getiodevice(imemory, (int)(op->value.intval));
+ if (iodev == 0) /* index out of range */
+ return_error(gs_error_rangecheck);
+ dname = (const byte *)iodev->dname;
+ if (dname == 0)
+ make_null(op);
+ else
+ make_const_string(op, a_readonly | avm_foreign,
+ strlen((const char *)dname), dname);
+ return 0;
+}
+
+/* ------ %lineedit and %statementedit ------ */
+
+/* <file> <bool> <int> <string> .filelineedit <file> */
+/* This opens %statementedit% or %lineedit% and is also the
+ * continuation proc for callouts.
+ * Input:
+ * string is the statement/line buffer,
+ * int is the write index into string
+ * bool is true if %statementedit%
+ * file is stdin
+ * Output:
+ * file is a string based stream
+ * We store the line being read in a PostScript string.
+ * This limits the size to max_string_size (64k).
+ * This could be increased by storing the input line in something
+ * other than a PostScript string.
+ */
+int
+zfilelineedit(i_ctx_t *i_ctx_p)
+{
+ uint count = 0;
+ bool in_eol = false;
+ int code;
+ os_ptr op = osp;
+ bool statement;
+ stream *s;
+ stream *ins;
+ gs_string str;
+ uint initial_buf_size;
+ const char *filename;
+ /*
+ * buf exists only for stylistic parallelism: all occurrences of
+ * buf-> could just as well be str. .
+ */
+ gs_string *const buf = &str;
+
+ check_type(*op, t_string); /* line assembled so far */
+ buf->data = op->value.bytes;
+ buf->size = op->tas.rsize;
+ check_type(*(op-1), t_integer); /* index */
+ count = (op-1)->value.intval;
+ check_type(*(op-2), t_boolean); /* statementedit/lineedit */
+ statement = (op-2)->value.boolval;
+ check_read_file(i_ctx_p, ins, op - 3); /* %stdin */
+
+ /* extend string */
+ initial_buf_size = statement ? STATEMENTEDIT_BUF_SIZE : LINEEDIT_BUF_SIZE;
+ if (initial_buf_size > max_string_size)
+ return_error(gs_error_limitcheck);
+ if (!buf->data || (buf->size < initial_buf_size)) {
+ count = 0;
+ buf->data = gs_alloc_string(imemory_system, initial_buf_size,
+ "zfilelineedit(buffer)");
+ if (buf->data == 0)
+ return_error(gs_error_VMerror);
+ op->value.bytes = buf->data;
+ op->tas.rsize = buf->size = initial_buf_size;
+ }
+
+rd:
+ code = zreadline_from(ins, buf, imemory_system, &count, &in_eol);
+ if (buf->size > max_string_size) {
+ /* zreadline_from reallocated the buffer larger than
+ * is valid for a PostScript string.
+ * Return an error, but first realloc the buffer
+ * back to a legal size.
+ */
+ byte *nbuf = gs_resize_string(imemory_system, buf->data, buf->size,
+ max_string_size, "zfilelineedit(shrink buffer)");
+ if (nbuf == 0)
+ return_error(gs_error_VMerror);
+ op->value.bytes = buf->data = nbuf;
+ op->tas.rsize = buf->size = max_string_size;
+ return_error(gs_error_limitcheck);
+ }
+
+ op->value.bytes = buf->data; /* zreadline_from sometimes resizes the buffer. */
+ op->tas.rsize = buf->size;
+
+ switch (code) {
+ case EOFC:
+ code = gs_note_error(gs_error_undefinedfilename);
+ /* falls through */
+ case 0:
+ break;
+ default:
+ code = gs_note_error(gs_error_ioerror);
+ break;
+ case CALLC:
+ {
+ ref rfile;
+ (op-1)->value.intval = count;
+ /* callout is for stdin */
+ make_file(&rfile, a_readonly | avm_system, ins->read_id, ins);
+ code = s_handle_read_exception(i_ctx_p, code, &rfile,
+ NULL, 0, zfilelineedit);
+ }
+ break;
+ case 1: /* filled buffer */
+ {
+ uint nsize = buf->size;
+ byte *nbuf;
+
+ if (nsize >= max_string_size) {
+ code = gs_note_error(gs_error_limitcheck);
+ break;
+ }
+ else if (nsize >= max_string_size / 2)
+ nsize= max_string_size;
+ else
+ nsize = buf->size * 2;
+ nbuf = gs_resize_string(imemory_system, buf->data, buf->size, nsize,
+ "zfilelineedit(grow buffer)");
+ if (nbuf == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ break;
+ }
+ op->value.bytes = buf->data = nbuf;
+ op->tas.rsize = buf->size = nsize;
+ goto rd;
+ }
+ }
+ if (code != 0)
+ return code;
+ if (statement) {
+ /* If we don't have a complete token, keep going. */
+ stream st;
+ stream *ts = &st;
+ scanner_state state;
+ ref ignore_value;
+ uint depth = ref_stack_count(&o_stack);
+ int code;
+
+ /* Add a terminating EOL. */
+ if (count + 1 > buf->size) {
+ uint nsize;
+ byte *nbuf;
+
+ nsize = buf->size + 1;
+ if (nsize > max_string_size) {
+ return_error(gs_note_error(gs_error_limitcheck));
+ }
+ else {
+ nbuf = gs_resize_string(imemory_system, buf->data, buf->size, nsize,
+ "zfilelineedit(grow buffer)");
+ if (nbuf == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ return_error(code);
+ }
+ op->value.bytes = buf->data = nbuf;
+ op->tas.rsize = buf->size = nsize;
+ }
+ }
+ buf->data[count++] = char_EOL;
+ s_init(ts, NULL);
+ sread_string(ts, buf->data, count);
+sc:
+ gs_scanner_init_stream_options(&state, ts, SCAN_CHECK_ONLY);
+ ialloc_set_space(idmemory, avm_local);
+ code = gs_scan_token(i_ctx_p, &ignore_value, &state);
+ ref_stack_pop_to(&o_stack, depth);
+ if (code < 0)
+ code = scan_EOF; /* stop on scanner error */
+ switch (code) {
+ case 0: /* read a token */
+ case scan_BOS:
+ goto sc; /* keep going until we run out of data */
+ case scan_Refill:
+ goto rd;
+ case scan_EOF:
+ break;
+ default: /* error */
+ return code;
+ }
+ }
+ buf->data = gs_resize_string(imemory_system, buf->data, buf->size, count,
+ "zfilelineedit(resize buffer)");
+ if (buf->data == 0)
+ return_error(gs_error_VMerror);
+ op->value.bytes = buf->data;
+ op->tas.rsize = buf->size;
+
+ s = file_alloc_stream(imemory_system, "zfilelineedit(stream)");
+ if (s == 0)
+ return_error(gs_error_VMerror);
+
+ sread_string(s, buf->data, count);
+ s->save_close = s->procs.close;
+ s->procs.close = file_close_disable;
+
+ filename = statement ? gs_iodev_statementedit.dname
+ : gs_iodev_lineedit.dname;
+ code = ssetfilename(s, (const byte *)filename, strlen(filename)+1);
+ if (code < 0) {
+ sclose(s);
+ return_error(gs_error_VMerror);
+ }
+
+ pop(3);
+ make_stream_file(osp, s, "r");
+
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def ziodev_op_defs[] =
+{
+ {"1.getiodevice", zgetiodevice},
+ op_def_end(0)
+};
diff --git a/psi/ziodev2.c b/psi/ziodev2.c
new file mode 100644
index 000000000..4e5729d98
--- /dev/null
+++ b/psi/ziodev2.c
@@ -0,0 +1,127 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* (Level 2) IODevice operators */
+#include "string_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "oper.h"
+#include "stream.h"
+#include "gxiodev.h"
+#include "dstack.h" /* for systemdict */
+#include "files.h" /* for file_open_stream */
+#include "iparam.h"
+#include "iutil2.h"
+#include "store.h"
+
+/* ------ %null% ------ */
+
+/* This represents the null output file. */
+static iodev_proc_open_device(null_open);
+const gx_io_device gs_iodev_null = {
+ "%null%", "Special",
+ {
+ iodev_no_init, null_open, iodev_no_open_file,
+ iodev_os_gp_fopen, iodev_os_fclose,
+ iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,
+ iodev_no_enumerate_files, NULL, NULL,
+ iodev_no_get_params, iodev_no_put_params
+ }
+};
+
+static int
+null_open(gx_io_device * iodev, const char *access, stream ** ps,
+ gs_memory_t * mem)
+{
+ if (!streq1(access, 'w'))
+ return_error(gs_error_invalidfileaccess);
+ return file_open_stream(gp_null_file_name,
+ strlen(gp_null_file_name),
+ access, 256 /* arbitrary */ , ps,
+ iodev, iodev->procs.gp_fopen, mem);
+}
+
+/* ------ Operators ------ */
+
+/* <iodevice> .getdevparams <mark> <name> <value> ... */
+static int
+zgetdevparams(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_io_device *iodev;
+ stack_param_list list;
+ gs_param_list *const plist = (gs_param_list *) & list;
+ int code;
+ ref *pmark;
+
+ check_read_type(*op, t_string);
+ iodev = gs_findiodevice(imemory, op->value.bytes, r_size(op));
+ if (iodev == 0)
+ return_error(gs_error_undefined);
+ stack_param_list_write(&list, &o_stack, NULL, iimemory);
+ if ((code = gs_getdevparams(iodev, plist)) < 0) {
+ ref_stack_pop(&o_stack, list.count * 2);
+ return code;
+ }
+ pmark = ref_stack_index(&o_stack, list.count * 2);
+ make_mark(pmark);
+ return 0;
+}
+
+/* <mark> <name> <value> ... <iodevice> .putdevparams */
+static int
+zputdevparams(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gx_io_device *iodev;
+ stack_param_list list;
+ gs_param_list *const plist = (gs_param_list *) & list;
+ int code;
+ password system_params_password;
+
+ check_read_type(*op, t_string);
+ iodev = gs_findiodevice(imemory, op->value.bytes, r_size(op));
+ if (iodev == 0)
+ return_error(gs_error_undefined);
+ code = stack_param_list_read(&list, &o_stack, 1, NULL, false, iimemory);
+ if (code < 0)
+ return code;
+ code = dict_read_password(&system_params_password, systemdict,
+ "SystemParamsPassword");
+ if (code < 0)
+ return code;
+ code = param_check_password(plist, &system_params_password);
+ if (code != 0) {
+ iparam_list_release(&list);
+ return_error(code < 0 ? code : gs_error_invalidaccess);
+ }
+ code = gs_putdevparams(iodev, plist);
+ iparam_list_release(&list);
+ if (code < 0)
+ return code;
+ ref_stack_pop(&o_stack, list.count * 2 + 2);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def ziodev2_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"1.getdevparams", zgetdevparams},
+ {"2.putdevparams", zputdevparams},
+ op_def_end(0)
+};
diff --git a/psi/ziodevs.c b/psi/ziodevs.c
new file mode 100644
index 000000000..980f3d2fb
--- /dev/null
+++ b/psi/ziodevs.c
@@ -0,0 +1,273 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* %stdxxx IODevice implementation for PostScript interpreter */
+#include "stdio_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "gpcheck.h"
+#include "oper.h"
+#include "stream.h"
+#include "gxiodev.h" /* must come after stream.h */
+ /* and before files.h */
+#include "files.h"
+#include "store.h"
+
+/* Define the special devices. */
+const char iodev_dtype_stdio[] = "Special";
+#define iodev_special(dname, init, open) {\
+ dname, iodev_dtype_stdio,\
+ { init, open, iodev_no_open_file, iodev_no_fopen, iodev_no_fclose,\
+ iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,\
+ iodev_no_enumerate_files, NULL, NULL,\
+ iodev_no_get_params, iodev_no_put_params\
+ }\
+}
+
+/*
+ * We need the current context pointer for accessing / opening the %std
+ * IODevices. However, this is not available to the open routine.
+ * Therefore, we use the hack of storing this pointer in the IODevice state
+ * pointer just before calling the open routines. We clear the pointer
+ * immediately afterwards so as not to wind up with dangling references.
+ */
+
+#define STDIN_BUF_SIZE 1024
+
+static iodev_proc_init(stdin_init);
+static iodev_proc_open_device(stdin_open);
+const gx_io_device gs_iodev_stdin =
+ iodev_special("%stdin%", stdin_init, stdin_open);
+
+#define STDOUT_BUF_SIZE 128
+static iodev_proc_open_device(stdout_open);
+const gx_io_device gs_iodev_stdout =
+ iodev_special("%stdout%", iodev_no_init, stdout_open);
+
+#define STDERR_BUF_SIZE 128
+static iodev_proc_open_device(stderr_open);
+const gx_io_device gs_iodev_stderr =
+ iodev_special("%stderr%", iodev_no_init, stderr_open);
+
+/* ------- %stdin, %stdout, and %stderr ------ */
+
+/*
+ * According to Adobe, it is legal to close the %std... files and then
+ * re-open them later. However, the re-opened file object is not 'eq' to
+ * the original file object (in our implementation, it has a different
+ * read_id or write_id).
+ */
+
+static int
+ s_stdin_read_process(stream_state *, stream_cursor_read *,
+ stream_cursor_write *, bool);
+
+static int
+stdin_init(gx_io_device * iodev, gs_memory_t * mem)
+{
+ mem->gs_lib_ctx->stdin_is_interactive = true;
+ return 0;
+}
+
+/* Read from stdin into the buffer. */
+/* If interactive, only read one character. */
+static int
+s_stdin_read_process(stream_state * st, stream_cursor_read * ignore_pr,
+ stream_cursor_write * pw, bool last)
+{
+ FILE *file = ((stream *) st)->file; /* hack for file streams */
+ int wcount = (int)(pw->limit - pw->ptr);
+ int count;
+
+ if (wcount <= 0)
+ return 0;
+ count = gp_stdin_read( (char*) pw->ptr + 1, wcount,
+ st->memory->gs_lib_ctx->stdin_is_interactive, file);
+ pw->ptr += (count < 0) ? 0 : count;
+ return ((count < 0) ? ERRC : (count == 0) ? EOFC : count);
+}
+
+static int
+stdin_open(gx_io_device * iodev, const char *access, stream ** ps,
+ gs_memory_t * mem)
+{
+ i_ctx_t *i_ctx_p = (i_ctx_t *)iodev->state; /* see above */
+ stream *s;
+
+ if (!streq1(access, 'r'))
+ return_error(gs_error_invalidfileaccess);
+ if (file_is_invalid(s, &ref_stdin)) {
+ /****** stdin SHOULD NOT LINE-BUFFER ******/
+ gs_memory_t *mem = imemory_system;
+ byte *buf;
+
+ s = file_alloc_stream(mem, "stdin_open(stream)");
+ /* We want stdin to read only one character at a time, */
+ /* but it must have a substantial buffer, in case it is used */
+ /* by a stream that requires more than one input byte */
+ /* to make progress. */
+ buf = gs_alloc_bytes(mem, STDIN_BUF_SIZE, "stdin_open(buffer)");
+ if (s == 0 || buf == 0)
+ return_error(gs_error_VMerror);
+ sread_file(s, gs_stdin, buf, STDIN_BUF_SIZE);
+ s->procs.process = s_stdin_read_process;
+ s->save_close = s_std_null;
+ s->procs.close = file_close_file;
+ make_file(&ref_stdin, a_readonly | avm_system, s->read_id, s);
+ *ps = s;
+ return 1;
+ }
+ *ps = s;
+ return 0;
+}
+/* This is the public routine for getting the stdin stream. */
+int
+zget_stdin(i_ctx_t *i_ctx_p, stream ** ps)
+{
+ stream *s;
+ gx_io_device *iodev;
+ int code;
+
+ if (file_is_valid(s, &ref_stdin)) {
+ *ps = s;
+ return 0;
+ }
+ iodev = gs_findiodevice((const byte *)"%stdin", 6);
+ iodev->state = i_ctx_p;
+ code = (*iodev->procs.open_device)(iodev, "r", ps, imemory_system);
+ iodev->state = NULL;
+ return min(code, 0);
+}
+/* Test whether a stream is stdin. */
+bool
+zis_stdin(const stream *s)
+{
+ return (s_is_valid(s) && s->procs.process == s_stdin_read_process);
+}
+
+static int
+ s_stdout_swrite_process(stream_state *, stream_cursor_read *,
+ stream_cursor_write *, bool);
+
+/* Write a buffer to stdout, potentially writing to callback */
+static int
+s_stdout_write_process(stream_state * st, stream_cursor_read * ignore_pr,
+ stream_cursor_write * pw, bool last)
+{
+ uint count = pr->limit - pr->ptr;
+ int written;
+
+ if (count == 0)
+ return 0;
+ written = outwrite(st->memory, pr->ptr + 1, count);
+ if (written < count) {
+ return ERRC;
+ pr->ptr += written;
+ return 0;
+}
+
+static int
+stdout_open(gx_io_device * iodev, const char *access, stream ** ps,
+ gs_memory_t * mem)
+{
+ i_ctx_t *i_ctx_p = (i_ctx_t *)iodev->state; /* see above */
+ stream *s;
+
+ if (!streq1(access, 'w'))
+ return_error(gs_error_invalidfileaccess);
+ if (file_is_invalid(s, &ref_stdout)) {
+ gs_memory_t *mem = imemory_system;
+ byte *buf;
+
+ s = file_alloc_stream(mem, "stdout_open(stream)");
+ buf = gs_alloc_bytes(mem, STDOUT_BUF_SIZE, "stdout_open(buffer)");
+ if (s == 0 || buf == 0)
+ return_error(gs_error_VMerror);
+ swrite_file(s, gs_stdout, buf, STDOUT_BUF_SIZE);
+ s->save_close = s->procs.flush;
+ s->procs.close = file_close_file;
+ s->procs.process = s_stdout_write_process;
+ make_file(&ref_stdout, a_write | avm_system, s->write_id, s);
+ *ps = s;
+ return 1;
+ }
+ *ps = s;
+ return 0;
+}
+/* This is the public routine for getting the stdout stream. */
+int
+zget_stdout(i_ctx_t *i_ctx_p, stream ** ps)
+{
+ stream *s;
+ gx_io_device *iodev;
+ int code;
+
+ if (file_is_valid(s, &ref_stdout)) {
+ *ps = s;
+ return 0;
+ }
+ iodev = gs_findiodevice((const byte *)"%stdout", 7);
+ iodev->state = i_ctx_p;
+ code = (*iodev->procs.open_device)(iodev, "w", ps, imemory_system);
+ iodev->state = NULL;
+ return min(code, 0);
+}
+
+static int
+stderr_open(gx_io_device * iodev, const char *access, stream ** ps,
+ gs_memory_t * mem)
+{
+ i_ctx_t *i_ctx_p = (i_ctx_t *)iodev->state; /* see above */
+ stream *s;
+
+ if (!streq1(access, 'w'))
+ return_error(gs_error_invalidfileaccess);
+ if (file_is_invalid(s, &ref_stderr)) {
+ gs_memory_t *mem = imemory_system;
+ byte *buf;
+
+ s = file_alloc_stream(mem, "stderr_open(stream)");
+ buf = gs_alloc_bytes(mem, STDERR_BUF_SIZE, "stderr_open(buffer)");
+ if (s == 0 || buf == 0)
+ return_error(gs_error_VMerror);
+ swrite_file(s, gs_stderr, buf, STDERR_BUF_SIZE);
+ s->save_close = s->procs.flush;
+ s->procs.close = file_close_file;
+ make_file(&ref_stderr, a_write | avm_system, s->write_id, s);
+ *ps = s;
+ return 1;
+ }
+ *ps = s;
+ return 0;
+}
+/* This is the public routine for getting the stderr stream. */
+int
+zget_stderr(i_ctx_t *i_ctx_p, stream ** ps)
+{
+ stream *s;
+ gx_io_device *iodev;
+ int code;
+
+ if (file_is_valid(s, &ref_stderr)) {
+ *ps = s;
+ return 0;
+ }
+ iodev = gs_findiodevice((const byte *)"%stderr", 7);
+ iodev->state = i_ctx_p;
+ code = (*iodev->procs.open_device)(iodev, "w", ps, imemory_system);
+ iodev->state = NULL;
+ return min(code, 0);
+}
diff --git a/psi/ziodevsc.c b/psi/ziodevsc.c
new file mode 100644
index 000000000..835848f29
--- /dev/null
+++ b/psi/ziodevsc.c
@@ -0,0 +1,321 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* %stdxxx IODevice implementation using callouts for PostScript interpreter */
+#include "stdio_.h"
+#include "ghost.h"
+#include "gpcheck.h"
+#include "gp.h"
+#include "oper.h"
+#include "stream.h"
+#include "gxiodev.h" /* must come after stream.h */
+ /* and before files.h */
+#include "istream.h"
+#include "files.h"
+#include "ifilter.h"
+#include "store.h"
+
+/* Define the special devices. */
+const char iodev_dtype_stdio[] = "Special";
+#define iodev_special(dname, init, open) {\
+ dname, iodev_dtype_stdio,\
+ { init, open, iodev_no_open_file, iodev_no_fopen, iodev_no_fclose,\
+ iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,\
+ iodev_no_enumerate_files, NULL, NULL,\
+ iodev_no_get_params, iodev_no_put_params\
+ }\
+}
+
+/*
+ * We need the current context pointer for accessing / opening the %std
+ * IODevices. However, this is not available to the open routine.
+ * Therefore, we use the hack of storing this pointer in the IODevice state
+ * pointer just before calling the open routines. We clear the pointer
+ * immediately afterwards so as not to wind up with dangling references.
+ */
+
+#define STDIN_BUF_SIZE 1024
+static iodev_proc_init(stdin_init);
+static iodev_proc_open_device(stdin_open);
+const gx_io_device gs_iodev_stdin =
+ iodev_special("%stdin%", stdin_init, stdin_open);
+
+#define STDOUT_BUF_SIZE 128
+static iodev_proc_open_device(stdout_open);
+const gx_io_device gs_iodev_stdout =
+ iodev_special("%stdout%", iodev_no_init, stdout_open);
+
+#define STDERR_BUF_SIZE 128
+static iodev_proc_open_device(stderr_open);
+const gx_io_device gs_iodev_stderr =
+ iodev_special("%stderr%", iodev_no_init, stderr_open);
+
+/* ------- %stdin, %stdout, and %stderr ------ */
+
+/*
+ * According to Adobe, it is legal to close the %std... files and then
+ * re-open them later. However, the re-opened file object is not 'eq' to
+ * the original file object (in our implementation, it has a different
+ * read_id or write_id). This is performed in 'file_close_file' by the
+ * call to file_close_disable.
+ */
+
+static int
+ s_stdin_read_process(stream_state *, stream_cursor_read *,
+ stream_cursor_write *, bool);
+
+static int
+stdin_init(gx_io_device * iodev, gs_memory_t * mem)
+{
+ mem->gs_lib_ctx->stdin_is_interactive = true;
+ return 0;
+}
+
+/* Read from stdin into the buffer. */
+/* If interactive, only read one character. */
+static int
+s_stdin_read_process(stream_state * st, stream_cursor_read * ignore_pr,
+ stream_cursor_write * pw, bool last)
+{
+ int wcount = (int)(pw->limit - pw->ptr);
+ int count;
+ gs_memory_t *mem = st->memory;
+
+ if (wcount <= 0)
+ return 0;
+
+ /* do the callout */
+ if (mem->gs_lib_ctx->stdin_fn)
+ count = (*mem->gs_lib_ctx->stdin_fn)
+ (mem->gs_lib_ctx->caller_handle, (char *)pw->ptr + 1,
+ mem->gs_lib_ctx->stdin_is_interactive ? 1 : wcount);
+ else
+ count = gp_stdin_read((char *)pw->ptr + 1, wcount,
+ mem->gs_lib_ctx->stdin_is_interactive,
+ mem->gs_lib_ctx->fstdin);
+
+ pw->ptr += (count < 0) ? 0 : count;
+ return ((count < 0) ? ERRC : (count == 0) ? EOFC : count);
+}
+
+static int
+stdin_open(gx_io_device * iodev, const char *access, stream ** ps,
+ gs_memory_t * mem)
+{
+ i_ctx_t *i_ctx_p = (i_ctx_t *)iodev->state; /* see above */
+ stream *s;
+
+ if (!streq1(access, 'r'))
+ return_error(gs_error_invalidfileaccess);
+ if (file_is_invalid(s, &ref_stdin)) {
+ /****** stdin SHOULD NOT LINE-BUFFER ******/
+ gs_memory_t *sysmem = imemory_system;
+ byte *buf;
+ static const stream_procs p = {
+ s_std_noavailable, s_std_noseek, s_std_read_reset,
+ s_std_read_flush, file_close_file, s_stdin_read_process
+ };
+
+ s = file_alloc_stream(sysmem, "stdin_open(stream)");
+
+ /* We want stdin to read only one character at a time, */
+ /* but it must have a substantial buffer, in case it is used */
+ /* by a stream that requires more than one input byte */
+ /* to make progress. */
+ buf = gs_alloc_bytes(sysmem, STDIN_BUF_SIZE, "stdin_open(buffer)");
+ if (s == 0 || buf == 0)
+ return_error(gs_error_VMerror);
+
+ s_std_init(s, buf, STDIN_BUF_SIZE, &p, s_mode_read);
+ s->file = 0;
+ s->file_modes = s->modes;
+ s->file_offset = 0;
+ s->file_limit = max_long;
+ s->save_close = s_std_null;
+ make_file(&ref_stdin, a_readonly | avm_system, s->read_id, s);
+ *ps = s;
+ return 1;
+ }
+ *ps = s;
+ return 0;
+}
+/* This is the public routine for getting the stdin stream. */
+int
+zget_stdin(i_ctx_t *i_ctx_p, stream ** ps)
+{
+ stream *s;
+ gx_io_device *iodev;
+ int code;
+
+ if (file_is_valid(s, &ref_stdin)) {
+ *ps = s;
+ return 0;
+ }
+ iodev = gs_findiodevice(imemory, (const byte *)"%stdin", 6);
+ iodev->state = i_ctx_p;
+ code = (*iodev->procs.open_device)(iodev, "r", ps, imemory_system);
+ iodev->state = NULL;
+ return min(code, 0);
+}
+
+/* Test whether a stream is stdin. */
+bool
+zis_stdin(const stream *s)
+{
+ return (s_is_valid(s) && s->procs.process == s_stdin_read_process);
+}
+
+/* Write a buffer to stdout, potentially writing to callback */
+static int
+s_stdout_write_process(stream_state * st, stream_cursor_read *pr,
+ stream_cursor_write *ignore_pw, bool last)
+{
+ uint count = pr->limit - pr->ptr;
+ int written;
+
+ if (count == 0)
+ return 0;
+ written = outwrite(st->memory, (const char *)pr->ptr + 1, count);
+ if (written < count)
+ return ERRC;
+ pr->ptr += written;
+ return 0;
+}
+
+static int
+stdout_open(gx_io_device * iodev, const char *access, stream ** ps,
+ gs_memory_t * mem)
+{
+ i_ctx_t *i_ctx_p = (i_ctx_t *)iodev->state; /* see above */
+ stream *s;
+
+ if (!streq1(access, 'w'))
+ return_error(gs_error_invalidfileaccess);
+ if (file_is_invalid(s, &ref_stdout)) {
+ gs_memory_t *sysmem = imemory_system;
+ byte *buf;
+ static const stream_procs p = {
+ s_std_noavailable, s_std_noseek, s_std_write_reset,
+ s_std_write_flush, file_close_file, s_stdout_write_process
+ };
+
+ s = file_alloc_stream(sysmem, "stdout_open(stream)");
+ buf = gs_alloc_bytes(sysmem, STDOUT_BUF_SIZE, "stdout_open(buffer)");
+ if (s == 0 || buf == 0)
+ return_error(gs_error_VMerror);
+ s_std_init(s, buf, STDOUT_BUF_SIZE, &p, s_mode_write);
+ s->file = 0;
+ s->file_modes = s->modes;
+ s->file_offset = 0; /* in case we switch to reading later */
+ s->file_limit = max_long; /* ibid. */
+ s->save_close = s->procs.flush;
+ make_file(&ref_stdout, a_write | avm_system, s->write_id, s);
+ *ps = s;
+ return 1;
+ }
+ *ps = s;
+ return 0;
+}
+
+/* This is the public routine for getting the stdout stream. */
+int
+zget_stdout(i_ctx_t *i_ctx_p, stream ** ps)
+{
+ stream *s;
+ gx_io_device *iodev;
+ int code;
+
+ if (file_is_valid(s, &ref_stdout)) {
+ *ps = s;
+ return 0;
+ }
+ iodev = gs_findiodevice(imemory, (const byte *)"%stdout", 7);
+ iodev->state = i_ctx_p;
+ code = (*iodev->procs.open_device)(iodev, "w", ps, imemory_system);
+ iodev->state = NULL;
+ return min(code, 0);
+}
+
+/* Write a buffer to stderr, potentially writing to callback */
+static int
+s_stderr_write_process(stream_state * st, stream_cursor_read *pr,
+ stream_cursor_write *ignore_pw, bool last)
+{
+ uint count = pr->limit - pr->ptr;
+ int written;
+
+ if (count == 0)
+ return 0;
+ written = errwrite(st->memory, (const char *)(pr->ptr + 1), count);
+ if (written < count)
+ return ERRC;
+ pr->ptr += written;
+ return 0;
+}
+
+static int
+stderr_open(gx_io_device * iodev, const char *access, stream ** ps,
+ gs_memory_t * mem)
+{
+ i_ctx_t *i_ctx_p = (i_ctx_t *)iodev->state; /* see above */
+ stream *s;
+
+ if (!streq1(access, 'w'))
+ return_error(gs_error_invalidfileaccess);
+ if (file_is_invalid(s, &ref_stderr)) {
+ gs_memory_t *sysmem = imemory_system;
+ byte *buf;
+ static const stream_procs p = {
+ s_std_noavailable, s_std_noseek, s_std_write_reset,
+ s_std_write_flush, file_close_file, s_stderr_write_process
+ };
+
+ s = file_alloc_stream(sysmem, "stderr_open(stream)");
+ buf = gs_alloc_bytes(sysmem, STDERR_BUF_SIZE, "stderr_open(buffer)");
+ if (s == 0 || buf == 0)
+ return_error(gs_error_VMerror);
+ s_std_init(s, buf, STDERR_BUF_SIZE, &p, s_mode_write);
+ s->file = 0;
+ s->file_modes = s->modes;
+ s->file_offset = 0; /* in case we switch to reading later */
+ s->file_limit = max_long; /* ibid. */
+ s->save_close = s->procs.flush;
+ make_file(&ref_stderr, a_write | avm_system, s->write_id, s);
+ *ps = s;
+ return 1;
+ }
+ *ps = s;
+ return 0;
+}
+
+/* This is the public routine for getting the stderr stream. */
+int
+zget_stderr(i_ctx_t *i_ctx_p, stream ** ps)
+{
+ stream *s;
+ gx_io_device *iodev;
+ int code;
+
+ if (file_is_valid(s, &ref_stderr)) {
+ *ps = s;
+ return 0;
+ }
+ iodev = gs_findiodevice(imemory, (const byte *)"%stderr", 7);
+ iodev->state = i_ctx_p;
+ code = (*iodev->procs.open_device)(iodev, "w", ps, imemory_system);
+ iodev->state = NULL;
+ return min(code, 0);
+}
diff --git a/psi/zmath.c b/psi/zmath.c
new file mode 100644
index 000000000..05d33b4ca
--- /dev/null
+++ b/psi/zmath.c
@@ -0,0 +1,275 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Mathematical operators */
+#include "math_.h"
+#include "ghost.h"
+#include "gxfarith.h"
+#include "oper.h"
+#include "store.h"
+
+/*
+ * Many of the procedures in this file are public only so they can be
+ * called from the FunctionType 4 interpreter (zfunc4.c).
+ */
+
+/*
+ * Define the current state of random number generator for operators. We
+ * have to implement this ourselves because the Unix rand doesn't provide
+ * anything equivalent to rrand. Note that the value always lies in the
+ * range [0..0x7ffffffe], even if longs are longer than 32 bits.
+ *
+ * The state must be public so that context switching can save and
+ * restore it. (Even though the Red Book doesn't mention this,
+ * we verified with Adobe that this is the case.)
+ */
+#define zrand_state (i_ctx_p->rand_state)
+
+/* Initialize the random number generator. */
+const long rand_state_initial = 1;
+
+/****** NOTE: none of these operators currently ******/
+/****** check for floating over- or underflow. ******/
+
+/* <num> sqrt <real> */
+int
+zsqrt(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double num;
+ int code = real_param(op, &num);
+
+ if (code < 0)
+ return code;
+ if (num < 0.0)
+ return_error(gs_error_rangecheck);
+ make_real(op, sqrt(num));
+ return 0;
+}
+
+/* <num> arccos <real> */
+static int
+zarccos(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double num, result;
+ int code = real_param(op, &num);
+
+ if (code < 0)
+ return code;
+ result = acos(num) * radians_to_degrees;
+ make_real(op, result);
+ return 0;
+}
+
+/* <num> arcsin <real> */
+static int
+zarcsin(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double num, result;
+ int code = real_param(op, &num);
+
+ if (code < 0)
+ return code;
+ result = asin(num) * radians_to_degrees;
+ make_real(op, result);
+ return 0;
+}
+
+/* <num> <denom> atan <real> */
+int
+zatan(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double args[2];
+ double result;
+ int code = num_params(op, 2, args);
+
+ if (code < 0)
+ return code;
+ code = gs_atan2_degrees(args[0], args[1], &result);
+ if (code < 0)
+ return code;
+ make_real(op - 1, result);
+ pop(1);
+ return 0;
+}
+
+/* <num> cos <real> */
+int
+zcos(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double angle;
+ int code = real_param(op, &angle);
+
+ if (code < 0)
+ return code;
+ make_real(op, gs_cos_degrees(angle));
+ return 0;
+}
+
+/* <num> sin <real> */
+int
+zsin(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double angle;
+ int code = real_param(op, &angle);
+
+ if (code < 0)
+ return code;
+ make_real(op, gs_sin_degrees(angle));
+ return 0;
+}
+
+/* <base> <exponent> exp <real> */
+int
+zexp(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double args[2];
+ double result;
+ double ipart;
+ int code = num_params(op, 2, args);
+
+ if (code < 0)
+ return code;
+ if (args[0] < 0.0 && modf(args[1], &ipart) != 0.0)
+ return_error(gs_error_undefinedresult);
+ if (args[0] == 0.0 && args[1] == 0.0)
+ result = 1.0; /* match Adobe; can't rely on C library */
+ else
+ result = pow(args[0], args[1]);
+ make_real(op - 1, result);
+ pop(1);
+ return 0;
+}
+
+/* <posnum> ln <real> */
+int
+zln(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double num;
+ int code = real_param(op, &num);
+
+ if (code < 0)
+ return code;
+ if (num <= 0.0)
+ return_error(gs_error_rangecheck);
+ make_real(op, log(num));
+ return 0;
+}
+
+/* <posnum> log <real> */
+int
+zlog(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double num;
+ int code = real_param(op, &num);
+
+ if (code < 0)
+ return code;
+ if (num <= 0.0)
+ return_error(gs_error_rangecheck);
+ make_real(op, log10(num));
+ return 0;
+}
+
+/* - rand <int> */
+static int
+zrand(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ /*
+ * We use an algorithm from CACM 31 no. 10, pp. 1192-1201,
+ * October 1988. According to a posting by Ed Taft on
+ * comp.lang.postscript, Level 2 (Adobe) PostScript interpreters
+ * use this algorithm too:
+ * x[n+1] = (16807 * x[n]) mod (2^31 - 1)
+ */
+#define A 16807
+#define M 0x7fffffff
+#define Q 127773 /* M / A */
+#define R 2836 /* M % A */
+ zrand_state = A * (zrand_state % Q) - R * (zrand_state / Q);
+ /* Note that zrand_state cannot be 0 here. */
+ if (zrand_state <= 0)
+ zrand_state += M;
+#undef A
+#undef M
+#undef Q
+#undef R
+ push(1);
+ make_int(op, zrand_state);
+ return 0;
+}
+
+/* <int> srand - */
+static int
+zsrand(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int state;
+
+ check_type(*op, t_integer);
+ state = op->value.intval;
+ /*
+ * The following somewhat bizarre adjustments are according to
+ * public information from Adobe describing their implementation.
+ */
+ if (state < 1)
+ state = -(state % 0x7ffffffe) + 1;
+ else if (state > 0x7ffffffe)
+ state = 0x7ffffffe;
+ zrand_state = state;
+ pop(1);
+ return 0;
+}
+
+/* - rrand <int> */
+static int
+zrrand(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, zrand_state);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zmath_op_defs[] =
+{
+ {"1arccos", zarccos}, /* extension */
+ {"1arcsin", zarcsin}, /* extension */
+ {"2atan", zatan},
+ {"1cos", zcos},
+ {"2exp", zexp},
+ {"1ln", zln},
+ {"1log", zlog},
+ {"0rand", zrand},
+ {"0rrand", zrrand},
+ {"1sin", zsin},
+ {"1sqrt", zsqrt},
+ {"1srand", zsrand},
+ op_def_end(0)
+};
diff --git a/psi/zmatrix.c b/psi/zmatrix.c
new file mode 100644
index 000000000..bb0a628aa
--- /dev/null
+++ b/psi/zmatrix.c
@@ -0,0 +1,420 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Matrix operators */
+#include "ghost.h"
+#include "oper.h"
+#include "igstate.h"
+#include "gsmatrix.h"
+#include "gscoord.h"
+#include "store.h"
+
+/* Forward references */
+static int common_transform(i_ctx_t *,
+ int (*)(gs_state *, double, double, gs_point *),
+ int (*)(double, double, const gs_matrix *, gs_point *));
+
+/* - initmatrix - */
+static int
+zinitmatrix(i_ctx_t *i_ctx_p)
+{
+ return gs_initmatrix(igs);
+}
+
+/* <matrix> defaultmatrix <matrix> */
+static int
+zdefaultmatrix(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_matrix mat;
+
+ gs_defaultmatrix(igs, &mat);
+ return write_matrix(op, &mat);
+}
+
+/* - .currentmatrix <xx> <xy> <yx> <yy> <tx> <ty> */
+static int
+zcurrentmatrix(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_matrix mat;
+ int code = gs_currentmatrix(igs, &mat);
+
+ if (code < 0)
+ return code;
+ push(6);
+ code = make_floats(op - 5, &mat.xx, 6);
+ if (code < 0)
+ pop(6);
+ return code;
+}
+
+/* <xx> <xy> <yx> <yy> <tx> <ty> .setmatrix - */
+static int
+zsetmatrix(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_matrix mat;
+ int code = float_params(op, 6, &mat.xx);
+
+ if (code < 0)
+ return code;
+ if ((code = gs_setmatrix(igs, &mat)) < 0)
+ return code;
+ pop(6);
+ return 0;
+}
+
+/* <matrix|null> .setdefaultmatrix - */
+static int
+zsetdefaultmatrix(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ if (r_has_type(op, t_null))
+ code = gs_setdefaultmatrix(igs, NULL);
+ else {
+ gs_matrix mat;
+
+ code = read_matrix(imemory, op, &mat);
+ if (code < 0)
+ return code;
+ code = gs_setdefaultmatrix(igs, &mat);
+ }
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* <tx> <ty> translate - */
+/* <tx> <ty> <matrix> translate <matrix> */
+static int
+ztranslate(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ double trans[2];
+
+ if ((code = num_params(op, 2, trans)) >= 0) {
+ code = gs_translate(igs, trans[0], trans[1]);
+ if (code < 0)
+ return code;
+ } else { /* matrix operand */
+ gs_matrix mat;
+
+ /* The num_params failure might be a stack underflow. */
+ check_op(2);
+ if ((code = num_params(op - 1, 2, trans)) < 0 ||
+ (code = gs_make_translation(trans[0], trans[1], &mat)) < 0 ||
+ (code = write_matrix(op, &mat)) < 0
+ ) { /* Might be a stack underflow. */
+ check_op(3);
+ return code;
+ }
+ op[-2] = *op;
+ }
+ pop(2);
+ return code;
+}
+
+/* <sx> <sy> scale - */
+/* <sx> <sy> <matrix> scale <matrix> */
+static int
+zscale(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ double scale[2];
+
+ if ((code = num_params(op, 2, scale)) >= 0) {
+ code = gs_scale(igs, scale[0], scale[1]);
+ if (code < 0)
+ return code;
+ } else { /* matrix operand */
+ gs_matrix mat;
+
+ /* The num_params failure might be a stack underflow. */
+ check_op(2);
+ if ((code = num_params(op - 1, 2, scale)) < 0 ||
+ (code = gs_make_scaling(scale[0], scale[1], &mat)) < 0 ||
+ (code = write_matrix(op, &mat)) < 0
+ ) { /* Might be a stack underflow. */
+ check_op(3);
+ return code;
+ }
+ op[-2] = *op;
+ }
+ pop(2);
+ return code;
+}
+
+/* <angle> rotate - */
+/* <angle> <matrix> rotate <matrix> */
+static int
+zrotate(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ double ang;
+
+ if ((code = real_param(op, &ang)) >= 0) {
+ code = gs_rotate(igs, ang);
+ if (code < 0)
+ return code;
+ } else { /* matrix operand */
+ gs_matrix mat;
+
+ /* The num_params failure might be a stack underflow. */
+ check_op(1);
+ if ((code = num_params(op - 1, 1, &ang)) < 0 ||
+ (code = gs_make_rotation(ang, &mat)) < 0 ||
+ (code = write_matrix(op, &mat)) < 0
+ ) { /* Might be a stack underflow. */
+ check_op(2);
+ return code;
+ }
+ op[-1] = *op;
+ }
+ pop(1);
+ return code;
+}
+
+/* <matrix> concat - */
+static int
+zconcat(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_matrix mat;
+ int code = read_matrix(imemory, op, &mat);
+
+ if (code < 0)
+ return code;
+ code = gs_concat(igs, &mat);
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* <matrix1> <matrix2> <matrix> concatmatrix <matrix> */
+static int
+zconcatmatrix(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_matrix m1, m2, mp;
+ int code;
+
+ if ((code = read_matrix(imemory, op - 2, &m1)) < 0 ||
+ (code = read_matrix(imemory, op - 1, &m2)) < 0 ||
+ (code = gs_matrix_multiply(&m1, &m2, &mp)) < 0 ||
+ (code = write_matrix(op, &mp)) < 0
+ )
+ return code;
+ op[-2] = *op;
+ pop(2);
+ return code;
+}
+
+/* <x> <y> transform <xt> <yt> */
+/* <x> <y> <matrix> transform <xt> <yt> */
+static int
+ztransform(i_ctx_t *i_ctx_p)
+{
+ return common_transform(i_ctx_p, gs_transform, gs_point_transform);
+}
+
+/* <dx> <dy> dtransform <dxt> <dyt> */
+/* <dx> <dy> <matrix> dtransform <dxt> <dyt> */
+static int
+zdtransform(i_ctx_t *i_ctx_p)
+{
+ return common_transform(i_ctx_p, gs_dtransform, gs_distance_transform);
+}
+
+/* <xt> <yt> itransform <x> <y> */
+/* <xt> <yt> <matrix> itransform <x> <y> */
+static int
+zitransform(i_ctx_t *i_ctx_p)
+{
+ return common_transform(i_ctx_p, gs_itransform, gs_point_transform_inverse);
+}
+
+/* <dxt> <dyt> idtransform <dx> <dy> */
+/* <dxt> <dyt> <matrix> idtransform <dx> <dy> */
+static int
+zidtransform(i_ctx_t *i_ctx_p)
+{
+ return common_transform(i_ctx_p, gs_idtransform, gs_distance_transform_inverse);
+}
+
+/* Common logic for [i][d]transform */
+static int
+common_transform(i_ctx_t *i_ctx_p,
+ int (*ptproc)(gs_state *, double, double, gs_point *),
+ int (*matproc)(double, double, const gs_matrix *, gs_point *))
+{
+ os_ptr op = osp;
+ double opxy[2];
+ gs_point pt;
+ int code;
+
+ /* Optimize for the non-matrix case */
+ switch (r_type(op)) {
+ case t_real:
+ opxy[1] = op->value.realval;
+ break;
+ case t_integer:
+ opxy[1] = op->value.intval;
+ break;
+ case t_array: /* might be a matrix */
+ case t_shortarray:
+ case t_mixedarray: {
+ gs_matrix mat;
+ gs_matrix *pmat = &mat;
+
+ if ((code = read_matrix(imemory, op, pmat)) < 0 ||
+ (code = num_params(op - 1, 2, opxy)) < 0 ||
+ (code = (*matproc) (opxy[0], opxy[1], pmat, &pt)) < 0
+ ) { /* Might be a stack underflow. */
+ check_op(3);
+ return code;
+ }
+ op--;
+ pop(1);
+ goto out;
+ }
+ default:
+ return_op_typecheck(op);
+ }
+ switch (r_type(op - 1)) {
+ case t_real:
+ opxy[0] = (op - 1)->value.realval;
+ break;
+ case t_integer:
+ opxy[0] = (op - 1)->value.intval;
+ break;
+ default:
+ return_op_typecheck(op - 1);
+ }
+ if ((code = (*ptproc) (igs, opxy[0], opxy[1], &pt)) < 0)
+ return code;
+out:
+ make_real(op - 1, pt.x);
+ make_real(op, pt.y);
+ return 0;
+}
+
+/* <matrix> <inv_matrix> invertmatrix <inv_matrix> */
+static int
+zinvertmatrix(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_matrix m;
+ int code;
+
+ if ((code = read_matrix(imemory, op - 1, &m)) < 0 ||
+ (code = gs_matrix_invert(&m, &m)) < 0 ||
+ (code = write_matrix(op, &m)) < 0
+ )
+ return code;
+ op[-1] = *op;
+ pop(1);
+ return code;
+}
+
+/* <bbox> <matrix> .bbox_transform <x0> <y0> <x1> <y1> */
+/* Calculate bonding box of a box transformed by a matrix. */
+static int
+zbbox_transform(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_matrix m;
+ float bbox[4];
+ gs_point aa, az, za, zz;
+ double temp;
+ int code;
+
+ if ((code = read_matrix(imemory, op, &m)) < 0)
+ return code;
+
+ if (!r_is_array(op - 1))
+ return_op_typecheck(op - 1);
+ check_read(op[-1]);
+ if (r_size(op - 1) != 4)
+ return_error(gs_error_rangecheck);
+ if ((code = process_float_array(imemory, op - 1, 4, bbox) < 0))
+ return code;
+
+ gs_point_transform(bbox[0], bbox[1], &m, &aa);
+ gs_point_transform(bbox[0], bbox[3], &m, &az);
+ gs_point_transform(bbox[2], bbox[1], &m, &za);
+ gs_point_transform(bbox[2], bbox[3], &m, &zz);
+
+ if ( aa.x > az.x)
+ temp = aa.x, aa.x = az.x, az.x = temp;
+ if ( za.x > zz.x)
+ temp = za.x, za.x = zz.x, zz.x = temp;
+ if ( za.x < aa.x)
+ aa.x = za.x; /* min */
+ if ( az.x > zz.x)
+ zz.x = az.x; /* max */
+
+ if ( aa.y > az.y)
+ temp = aa.y, aa.y = az.y, az.y = temp;
+ if ( za.y > zz.y)
+ temp = za.y, za.y = zz.y, zz.y = temp;
+ if ( za.y < aa.y)
+ aa.y = za.y; /* min */
+ if ( az.y > zz.y)
+ zz.y = az.y; /* max */
+
+ push(2);
+ make_real(op - 3, (float)aa.x);
+ make_real(op - 2, (float)aa.y);
+ make_real(op - 1, (float)zz.x);
+ make_real(op , (float)zz.y);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zmatrix_op_defs[] =
+{
+ {"1concat", zconcat},
+ {"2dtransform", zdtransform},
+ {"3concatmatrix", zconcatmatrix},
+ {"0.currentmatrix", zcurrentmatrix},
+ {"1defaultmatrix", zdefaultmatrix},
+ {"2idtransform", zidtransform},
+ {"0initmatrix", zinitmatrix},
+ {"2invertmatrix", zinvertmatrix},
+ {"2itransform", zitransform},
+ {"1rotate", zrotate},
+ {"2scale", zscale},
+ {"6.setmatrix", zsetmatrix},
+ {"1.setdefaultmatrix", zsetdefaultmatrix},
+ {"2transform", ztransform},
+ {"2translate", ztranslate},
+ op_def_end(0)
+};
+
+const op_def zmatrix2_op_defs[] =
+{
+ {"2.bbox_transform", zbbox_transform},
+ op_def_end(0)
+};
diff --git a/psi/zmedia2.c b/psi/zmedia2.c
new file mode 100644
index 000000000..9192f6b9e
--- /dev/null
+++ b/psi/zmedia2.c
@@ -0,0 +1,518 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Media matching for setpagedevice */
+#include "math_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "gsmatrix.h"
+#include "oper.h"
+#include "idict.h"
+#include "idparam.h"
+#include "iname.h"
+#include "store.h"
+
+/* <pagedict> <attrdict> <policydict> <keys> .matchmedia <key> true */
+/* <pagedict> <attrdict> <policydict> <keys> .matchmedia false */
+/* <pagedict> null <policydict> <keys> .matchmedia null true */
+static int zmatch_page_size(const gs_memory_t *mem,
+ const ref * pvreq, const ref * pvmed,
+ int policy, int orient, bool roll,
+ float *best_mismatch, gs_matrix * pmat,
+ gs_point * pmsize);
+typedef struct match_record_s {
+ ref best_key, match_key;
+ uint priority, no_match_priority;
+} match_record_t;
+static void
+reset_match(match_record_t *match)
+{
+ make_null(&match->best_key);
+ make_null(&match->match_key);
+ match->priority = match->no_match_priority;
+}
+static int
+zmatchmedia(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr preq = op - 3;
+ os_ptr pattr = op - 2;
+ os_ptr ppol = op - 1;
+ os_ptr pkeys = op; /* *const */
+ int policy_default;
+ float best_mismatch = (float)max_long; /* adhoc */
+ float mepos_penalty;
+ float mbest = best_mismatch;
+ match_record_t match;
+ ref no_priority;
+ ref *ppriority;
+ int mepos, orient;
+ bool roll;
+ int code;
+ int ai;
+ struct mkd_ {
+ ref key, dict;
+ } aelt;
+ if (r_has_type(pattr, t_null)) {
+ check_op(4);
+ make_null(op - 3);
+ make_true(op - 2);
+ pop(2);
+ return 0;
+ }
+ check_type(*preq, t_dictionary);
+ check_dict_read(*preq);
+ check_type(*pattr, t_dictionary);
+ check_dict_read(*pattr);
+ check_type(*ppol, t_dictionary);
+ check_dict_read(*ppol);
+ check_array(*pkeys);
+ check_read(*pkeys);
+ switch (code = dict_int_null_param(preq, "MediaPosition", 0, 0x7fff,
+ 0, &mepos)) {
+ default:
+ return code;
+ case 2:
+ case 1:
+ mepos = -1;
+ case 0:;
+ }
+ switch (code = dict_int_null_param(preq, "Orientation", 0, 3,
+ 0, &orient)) {
+ default:
+ return code;
+ case 2:
+ case 1:
+ orient = -1;
+ case 0:;
+ }
+ code = dict_bool_param(preq, "RollFedMedia", false, &roll);
+ if (code < 0)
+ return code;
+ code = dict_int_param(ppol, "PolicyNotFound", 0, 7, 0,
+ &policy_default);
+ if (code < 0)
+ return code;
+ if (dict_find_string(pattr, "Priority", &ppriority) > 0) {
+ check_array_only(*ppriority);
+ check_read(*ppriority);
+ } else {
+ make_empty_array(&no_priority, a_readonly);
+ ppriority = &no_priority;
+ }
+ match.no_match_priority = r_size(ppriority);
+ reset_match(&match);
+ for (ai = dict_first(pattr);
+ (ai = dict_next(pattr, ai, (ref * /*[2]*/)&aelt)) >= 0;
+ ) {
+ if (r_has_type(&aelt.dict, t_dictionary) &&
+ r_has_attr(dict_access_ref(&aelt.dict), a_read) &&
+ r_has_type(&aelt.key, t_integer)
+ ) {
+ bool match_all;
+ uint ki, pi;
+
+ code = dict_bool_param(&aelt.dict, "MatchAll", false,
+ &match_all);
+ if (code < 0)
+ return code;
+ for (ki = 0; ki < r_size(pkeys); ki++) {
+ ref key;
+ ref kstr;
+ ref *prvalue;
+ ref *pmvalue;
+ ref *ppvalue;
+ int policy;
+
+ array_get(imemory, pkeys, ki, &key);
+ if (dict_find(&aelt.dict, &key, &pmvalue) <= 0)
+ continue;
+ if (dict_find(preq, &key, &prvalue) <= 0 ||
+ r_has_type(prvalue, t_null)
+ ) {
+ if (match_all)
+ goto no;
+ else
+ continue;
+ }
+ /* Look for the Policies entry for this key. */
+ if (dict_find(ppol, &key, &ppvalue) > 0) {
+ check_type_only(*ppvalue, t_integer);
+ policy = ppvalue->value.intval;
+ } else
+ policy = policy_default;
+ /*
+ * Match a requested attribute value with the attribute value in the
+ * description of a medium. For all attributes except PageSize,
+ * matching means equality. PageSize is special; see match_page_size
+ * below.
+ */
+ if (r_has_type(&key, t_name) &&
+ (name_string_ref(imemory, &key, &kstr),
+ r_size(&kstr) == 8 &&
+ !memcmp(kstr.value.bytes, "PageSize", 8))
+ ) {
+ gs_matrix ignore_mat;
+ gs_point ignore_msize;
+
+ if (zmatch_page_size(imemory, prvalue, pmvalue,
+ policy, orient, roll,
+ &best_mismatch,
+ &ignore_mat,
+ &ignore_msize)
+ <= 0)
+ goto no;
+ } else if (!obj_eq(imemory, prvalue, pmvalue))
+ goto no;
+ }
+
+ mepos_penalty = (mepos < 0 || aelt.key.value.intval == mepos) ?
+ 0 : .001;
+
+ /* We have a match. Save the match in case no better match is found */
+ if (r_has_type(&match.match_key, t_null))
+ match.match_key = aelt.key;
+ /*
+ * If it is a better match than the current best it supersedes it
+ * regardless of priority. If the match is the same, then update
+ * to the current only if the key value is lower.
+ */
+ if (best_mismatch + mepos_penalty <= mbest) {
+ if (best_mismatch + mepos_penalty < mbest ||
+ (r_has_type(&match.match_key, t_integer) &&
+ match.match_key.value.intval > aelt.key.value.intval)) {
+ reset_match(&match);
+ match.match_key = aelt.key;
+ mbest = best_mismatch + mepos_penalty;
+ }
+ }
+ /* In case of a tie, see if the new match has priority. */
+ for (pi = match.priority; pi > 0;) {
+ ref pri;
+
+ pi--;
+ array_get(imemory, ppriority, pi, &pri);
+ if (obj_eq(imemory, &aelt.key, &pri)) { /* Yes, higher priority. */
+ match.best_key = aelt.key;
+ match.priority = pi;
+ break;
+ }
+ }
+no:;
+ }
+ }
+ if (r_has_type(&match.match_key, t_null)) {
+ make_false(op - 3);
+ pop(3);
+ } else {
+ if (r_has_type(&match.best_key, t_null))
+ op[-3] = match.match_key;
+ else
+ op[-3] = match.best_key;
+ make_true(op - 2);
+ pop(2);
+ }
+ return 0;
+}
+
+/* [<req_x> <req_y>] [<med_x0> <med_y0> (<med_x1> <med_y1> | )]
+ * <policy> <orient|null> <roll> <matrix|null> .matchpagesize
+ * <matrix|null> <med_x> <med_y> true -or- false
+ */
+static int
+zmatchpagesize(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_matrix mat;
+ float ignore_mismatch = (float)max_long;
+ gs_point media_size;
+ int orient;
+ bool roll;
+ int code;
+
+ check_type(op[-3], t_integer);
+ if (r_has_type(op - 2, t_null))
+ orient = -1;
+ else {
+ check_int_leu(op[-2], 3);
+ orient = (int)op[-2].value.intval;
+ }
+ check_type(op[-1], t_boolean);
+ roll = op[-1].value.boolval;
+ code = zmatch_page_size(imemory,
+ op - 5, op - 4, (int)op[-3].value.intval,
+ orient, roll,
+ &ignore_mismatch, &mat, &media_size);
+ switch (code) {
+ default:
+ return code;
+ case 0:
+ make_false(op - 5);
+ pop(5);
+ break;
+ case 1:
+ code = write_matrix(op, &mat);
+ if (code < 0 && !r_has_type(op, t_null))
+ return code;
+ op[-5] = *op;
+ make_real(op - 4, media_size.x);
+ make_real(op - 3, media_size.y);
+ make_true(op - 2);
+ pop(2);
+ break;
+ }
+ return 0;
+}
+/* Match the PageSize. See below for details. */
+static int
+match_page_size(const gs_point * request,
+ const gs_rect * medium,
+ int policy, int orient, bool roll,
+ float *best_mismatch, gs_matrix * pmat,
+ gs_point * pmsize);
+static int
+zmatch_page_size(const gs_memory_t *mem, const ref * pvreq, const ref * pvmed,
+ int policy, int orient, bool roll,
+ float *best_mismatch, gs_matrix * pmat, gs_point * pmsize)
+{
+ uint nr, nm;
+ int code;
+ ref rv[6];
+
+ /* array_get checks array types and size. */
+ /* This allows normal or packed arrays to be used */
+ if ((code = array_get(mem, pvreq, 1, &rv[1])) < 0)
+ return_error(code);
+ nr = r_size(pvreq);
+ if ((code = array_get(mem, pvmed, 1, &rv[3])) < 0)
+ return_error(code);
+ nm = r_size(pvmed);
+ if (!((nm == 2 || nm == 4) && (nr == 2 || nr == nm)))
+ return_error(gs_error_rangecheck);
+ {
+ uint i;
+ double v[6];
+ int code;
+
+ array_get(mem, pvreq, 0, &rv[0]);
+ for (i = 0; i < 4; ++i)
+ array_get(mem,pvmed, i % nm, &rv[i + 2]);
+ if ((code = num_params(rv + 5, 6, v)) < 0)
+ return code;
+ {
+ gs_point request;
+ gs_rect medium;
+
+ request.x = v[0], request.y = v[1];
+ medium.p.x = v[2], medium.p.y = v[3],
+ medium.q.x = v[4], medium.q.y = v[5];
+ return match_page_size(&request, &medium, policy, orient,
+ roll, best_mismatch, pmat, pmsize);
+ }
+ }
+}
+/*
+ * Match a requested PageSize with the PageSize of a medium. The medium
+ * may specify either a single value [mx my] or a range
+ * [mxmin mymin mxmax mymax]; matching means equality or inclusion
+ * to within a tolerance of 5, possibly swapping the requested X and Y.
+ * Take the Policies value into account, keeping track of the discrepancy
+ * if needed. When a match is found, also return the matrix to be
+ * concatenated after setting up the default matrix, and the actual
+ * media size.
+ *
+ * NOTE: The algorithm here doesn't work properly for variable-size media
+ * when the match isn't exact. We'll fix it if we ever need to.
+ */
+static void make_adjustment_matrix(const gs_point * request,
+ const gs_rect * medium,
+ gs_matrix * pmat,
+ bool scale, int rotate);
+static int
+match_page_size(const gs_point * request, const gs_rect * medium, int policy,
+ int orient, bool roll, float *best_mismatch, gs_matrix * pmat,
+ gs_point * pmsize)
+{
+ double rx = request->x, ry = request->y;
+
+ if ((rx <= 0) || (ry <= 0))
+ return_error(gs_error_rangecheck);
+ if (policy == 7) {
+ /* (Adobe) hack: just impose requested values */
+ *best_mismatch = 0;
+ gs_make_identity(pmat);
+ *pmsize = *request;
+ } else {
+ int fit_direct = rx - medium->p.x >= -5 && rx - medium->q.x <= 5
+ && ry - medium->p.y >= -5 && ry - medium->q.y <= 5;
+ int fit_rotated = rx - medium->p.y >= -5 && rx - medium->q.y <= 5
+ && ry - medium->p.x >= -5 && ry - medium->q.x <= 5;
+
+ /* Fudge matches from a non-standard page size match (4 element array) */
+ /* as worse than an exact match from a standard (2 element array), but */
+ /* better than for a rotated match to a standard pagesize. This should */
+ /* prevent rotation unless we have to (particularly for raster file */
+ /* formats like TIFF, JPEG, PNG, PCX, BMP, etc. and also should allow */
+ /* exact page size specification when there is a range PageSize entry. */
+ /* As the comment in gs_setpd.ps says "Devices that care will provide */
+ /* a real InputAttributes dictionary (most without a range pagesize) */
+ if ( fit_direct && fit_rotated) {
+ make_adjustment_matrix(request, medium, pmat, false, orient < 0 ? 0 : orient);
+ if (medium->p.x < medium->q.x || medium->p.y < medium->q.y)
+ *best_mismatch = (float)0.001; /* fudge a match to a range as a small number */
+ else /* should be 0 for an exact match */
+ *best_mismatch = fabs((rx - medium->p.x) * (medium->q.x - rx)) +
+ fabs((ry - medium->p.y) * (medium->q.y - ry));
+ } else if ( fit_direct ) {
+ int rotate = orient < 0 ? 0 : orient;
+
+ make_adjustment_matrix(request, medium, pmat, false, (rotate + 1) & 2);
+ *best_mismatch = fabs((medium->p.x - rx) * (medium->q.x - rx)) +
+ fabs((medium->p.y - ry) * (medium->q.y - ry)) +
+ (pmat->xx == 0.0 || (rotate & 1) == 1 ? 0.01 : 0); /* rotated */
+ } else if ( fit_rotated ) {
+ int rotate = (orient < 0 ? 1 : orient);
+
+ make_adjustment_matrix(request, medium, pmat, false, rotate | 1);
+ *best_mismatch = fabs((medium->p.y - rx) * (medium->q.y - rx)) +
+ fabs((medium->p.x - ry) * (medium->q.x - ry)) +
+ (pmat->xx == 0.0 || (rotate & 1) == 1 ? 0.01 : 0); /* rotated */
+ } else {
+ int rotate =
+ (orient >= 0 ? orient :
+ (rx < ry) ^ (medium->q.x < medium->q.y));
+ bool larger = (policy == 13) ? 0 :
+ (rotate & 1 ? medium->q.y >= rx && medium->q.x >= ry :
+ medium->q.x >= rx && medium->q.y >= ry);
+ bool adjust = false;
+ float mismatch = medium->q.x * medium->q.y - rx * ry;
+
+ switch (policy) {
+ default: /* exact match only */
+ return 0;
+ case 3: /* nearest match, adjust */
+ case 13: /* non-standard, nearest match, scale down OR up */
+ adjust = true;
+ case 5: /* nearest match, don't adjust */
+ if (fabs(mismatch) >= fabs(*best_mismatch))
+ return 0;
+ break;
+ case 4: /* next larger match, adjust */
+ adjust = true;
+ case 6: /* next larger match, don't adjust */
+ if (!larger || mismatch >= *best_mismatch)
+ return 0;
+ break;
+ }
+ if (adjust)
+ make_adjustment_matrix(request, medium, pmat, !larger, rotate);
+ else {
+ gs_rect req_rect;
+ if(rotate & 1) {
+ req_rect.p.x = ry;
+ req_rect.p.y = rx;
+ } else {
+ req_rect.p.x = rx;
+ req_rect.p.y = ry;
+ }
+ req_rect.q = req_rect.p;
+ make_adjustment_matrix(request, &req_rect, pmat, false, rotate);
+ }
+ *best_mismatch = fabs(mismatch);
+ }
+ if (pmat->xx == 0) { /* Swap request X and Y. */
+ double temp = rx;
+
+ rx = ry, ry = temp;
+ }
+#define ADJUST_INTO(req, mmin, mmax)\
+ (req < mmin ? mmin : req > mmax ? mmax : req)
+ pmsize->x = ADJUST_INTO(rx, medium->p.x, medium->q.x);
+ pmsize->y = ADJUST_INTO(ry, medium->p.y, medium->q.y);
+#undef ADJUST_INTO
+ }
+ return 1;
+}
+/*
+ * Compute the adjustment matrix for scaling and/or rotating the page
+ * to match the medium. If the medium is completely flexible in a given
+ * dimension (e.g., roll media in one dimension, or displays in both),
+ * we must adjust its size in that dimension to match the request.
+ * We recognize this by an unreasonably small medium->p.{x,y}.
+ * The PageSize Policy 3 only scales down, so 'scale' will be false if
+ * the medium is larger than the request. Policy 13 scales up OR down.
+ */
+static void
+make_adjustment_matrix(const gs_point * request, const gs_rect * medium,
+ gs_matrix * pmat, bool scale, int rotate)
+{
+ double rx = request->x, ry = request->y;
+ double mx = medium->q.x, my = medium->q.y;
+
+ /* Rotate the request if necessary. */
+ if (rotate & 1) {
+ double temp = rx;
+
+ rx = ry, ry = temp;
+ }
+ /* If 'medium' is flexible, adjust 'mx' and 'my' towards 'rx' and 'ry',
+ respectively. Note that 'mx' and 'my' have just acquired the largest
+ permissible value, medium->q. */
+ if (medium->p.x < mx) { /* non-empty width range */
+ if (rx < medium->p.x)
+ mx = medium->p.x; /* use minimum of the range */
+ else if (rx < mx)
+ mx = rx; /* fits */
+ /* else leave mx == medium->q.x, i.e., the maximum */
+ }
+ if (medium->p.y < my) { /* non-empty height range */
+ if (ry < medium->p.y)
+ my = medium->p.y; /* use minimum of the range */
+ else if (ry < my)
+ my = ry; /* fits */
+ /* else leave my == medium->q.y, i.e., the maximum */
+ }
+
+ /* Translate to align the centers. */
+ gs_make_translation(mx / 2, my / 2, pmat);
+
+ /* Rotate if needed. */
+ if (rotate)
+ gs_matrix_rotate(pmat, 90.0 * rotate, pmat);
+
+ /* Scale if needed. */
+ if (scale) {
+ double xfactor = mx / rx;
+ double yfactor = my / ry;
+ double factor = min(xfactor, yfactor);
+
+ gs_matrix_scale(pmat, factor, factor, pmat);
+ }
+ /* Now translate the origin back, */
+ /* using the original, unswapped request. */
+ gs_matrix_translate(pmat, -request->x / 2, -request->y / 2, pmat);
+}
+#undef MIN_MEDIA_SIZE
+
+/* ------ Initialization procedure ------ */
+
+const op_def zmedia2_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"4.matchmedia", zmatchmedia},
+ {"6.matchpagesize", zmatchpagesize},
+ op_def_end(0)
+};
diff --git a/psi/zmisc.c b/psi/zmisc.c
new file mode 100644
index 000000000..acd566b5d
--- /dev/null
+++ b/psi/zmisc.c
@@ -0,0 +1,528 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Miscellaneous operators */
+
+#include "errno_.h"
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "gp.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "dstack.h" /* for name lookup in bind */
+#include "iname.h"
+#include "ipacked.h"
+#include "ivmspace.h"
+#include "store.h"
+#include "igstate.h" /* for gs_currentcpsimode */
+#include "memento.h"
+#include "iscan.h"
+
+/**********************************************************************/
+
+/* <proc> bind <proc> */
+static inline bool
+r_is_ex_oper(const ref *rp)
+{
+ return (r_has_attr(rp, a_executable) &&
+ (r_btype(rp) == t_operator || r_type(rp) == t_oparray));
+}
+static int
+zbind(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint depth = 1;
+ ref defn;
+ register os_ptr bsp;
+
+ switch (r_type(op)) {
+ case t_array:
+ if (!r_has_attr(op, a_write)) {
+ return 0; /* per PLRM3 */
+ }
+ case t_mixedarray:
+ case t_shortarray:
+ defn = *op;
+ break;
+ case t_oparray:
+ defn = *op->value.const_refs;
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ push(1);
+ *op = defn;
+ bsp = op;
+ /*
+ * We must not make the top-level procedure read-only,
+ * but we must bind it even if it is read-only already.
+ *
+ * Here are the invariants for the following loop:
+ * `depth' elements have been pushed on the ostack;
+ * For i < depth, p = ref_stack_index(&o_stack, i):
+ * *p is an array (or packedarray) ref.
+ */
+ while (depth) {
+ while (r_size(bsp)) {
+ ref_packed *const tpp = (ref_packed *)bsp->value.packed; /* break const */
+
+ r_dec_size(bsp, 1);
+ if (r_is_packed(tpp)) {
+ /* Check for a packed executable name */
+ ushort elt = *tpp;
+
+ if (r_packed_is_exec_name(&elt)) {
+ ref nref;
+ ref *pvalue;
+
+ name_index_ref(imemory, packed_name_index(&elt),
+ &nref);
+ if ((pvalue = dict_find_name(&nref)) != 0 &&
+ r_is_ex_oper(pvalue)
+ ) {
+ store_check_dest(bsp, pvalue);
+ /*
+ * Always save the change, since this can only
+ * happen once.
+ */
+ ref_do_save(bsp, tpp, "bind");
+ *tpp = pt_tag(pt_executable_operator) +
+ op_index(pvalue);
+ }
+ }
+ bsp->value.packed = tpp + 1;
+ } else {
+ ref *const tp = bsp->value.refs++;
+
+ switch (r_type(tp)) {
+ case t_name: /* bind the name if an operator */
+ if (r_has_attr(tp, a_executable)) {
+ ref *pvalue;
+
+ if ((pvalue = dict_find_name(tp)) != 0 &&
+ r_is_ex_oper(pvalue)
+ ) {
+ store_check_dest(bsp, pvalue);
+ ref_assign_old(bsp, tp, pvalue, "bind");
+ }
+ }
+ break;
+ case t_array: /* push into array if writable */
+ if (!r_has_attr(tp, a_write))
+ break;
+ case t_mixedarray:
+ case t_shortarray:
+ if (r_has_attr(tp, a_executable)) {
+ /* Make reference read-only */
+ r_clear_attrs(tp, a_write);
+ if (bsp >= ostop) {
+ /* Push a new stack block. */
+ ref temp;
+ int code;
+
+ temp = *tp;
+ osp = bsp;
+ code = ref_stack_push(&o_stack, 1);
+ if (code < 0) {
+ ref_stack_pop(&o_stack, depth);
+ return_error(code);
+ }
+ bsp = osp;
+ *bsp = temp;
+ } else
+ *++bsp = *tp;
+ depth++;
+ }
+ }
+ }
+ }
+ bsp--;
+ depth--;
+ if (bsp < osbot) { /* Pop back to the previous stack block. */
+ osp = bsp;
+ ref_stack_pop_block(&o_stack);
+ bsp = osp;
+ }
+ }
+ osp = bsp;
+ return 0;
+}
+
+/* - serialnumber <int> */
+static int
+zserialnumber(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, gp_serialnumber());
+ return 0;
+}
+
+/* - realtime <int> */
+static int
+zrealtime(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ long secs_ns[2];
+ gs_lib_ctx_t *libctx = gs_lib_ctx_get_interp_instance(imemory);
+
+ gp_get_realtime(secs_ns);
+ secs_ns[1] -= libctx->real_time_0[1];
+ secs_ns[0] -= libctx->real_time_0[0];
+ push(1);
+ make_int(op, secs_ns[0] * 1000 + secs_ns[1] / 1000000);
+ return 0;
+}
+
+/* - usertime <int> */
+static int
+zusertime(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ long secs_ns[2];
+
+ gp_get_usertime(secs_ns);
+ push(1);
+ make_int(op, secs_ns[0] * 1000 + secs_ns[1] / 1000000);
+ return 0;
+}
+
+/* ---------------- Non-standard operators ---------------- */
+
+/* <string> getenv <value_string> true */
+/* <string> getenv false */
+static int
+zgetenv(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ char *str;
+ byte *value;
+ int len = 0;
+
+ check_read_type(*op, t_string);
+ str = ref_to_string(op, imemory, "getenv key");
+ if (str == 0)
+ return_error(gs_error_VMerror);
+ if (gp_getenv(str, (char *)0, &len) > 0) { /* key missing */
+ ifree_string((byte *) str, r_size(op) + 1, "getenv key");
+ make_false(op);
+ return 0;
+ }
+ value = ialloc_string(len, "getenv value");
+ if (value == 0) {
+ ifree_string((byte *) str, r_size(op) + 1, "getenv key");
+ return_error(gs_error_VMerror);
+ }
+ DISCARD(gp_getenv(str, (char *)value, &len)); /* can't fail */
+ ifree_string((byte *) str, r_size(op) + 1, "getenv key");
+ /* Delete the stupid C string terminator. */
+ value = iresize_string(value, len, len - 1,
+ "getenv value"); /* can't fail */
+ push(1);
+ make_string(op - 1, a_all | icurrent_space, len - 1, value);
+ make_true(op);
+ return 0;
+}
+
+/* - .defaultpapersize <string> true */
+/* - .defaultpapersize false */
+static int
+zdefaultpapersize(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ byte *value;
+ int len = 0;
+
+ if (gp_defaultpapersize((char *)0, &len) > 0) {
+ /* no default paper size */
+ push(1);
+ make_false(op);
+ return 0;
+ }
+
+ value = ialloc_string(len, "defaultpapersize value");
+ if (value == 0) {
+ return_error(gs_error_VMerror);
+ }
+ DISCARD(gp_defaultpapersize((char *)value, &len)); /* can't fail */
+ /* Delete the stupid C string terminator. */
+ value = iresize_string(value, len, len - 1,
+ "defaultpapersize value"); /* can't fail */
+ push(2);
+ make_string(op - 1, a_all | icurrent_space, len - 1, value);
+ make_true(op);
+ return 0;
+}
+
+/* <name> <proc> .makeoperator <oper> */
+static int
+zmakeoperator(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ op_array_table *opt;
+ uint count;
+ ref *tab;
+
+ check_type(op[-1], t_name);
+ check_proc(*op);
+ switch (r_space(op)) {
+ case avm_global:
+ opt = &i_ctx_p->op_array_table_global;
+ break;
+ case avm_local:
+ opt = &i_ctx_p->op_array_table_local;
+ break;
+ default:
+ return_error(gs_error_invalidaccess);
+ }
+ count = opt->count;
+ tab = opt->table.value.refs;
+ /*
+ * restore doesn't reset op_array_table.count, but it does
+ * remove entries from op_array_table.table. Since we fill
+ * the table in order, we can detect that a restore has occurred
+ * by checking whether what should be the most recent entry
+ * is occupied. If not, we scan backwards over the vacated entries
+ * to find the true end of the table.
+ */
+ while (count > 0 && r_has_type(&tab[count - 1], t_null))
+ --count;
+ if (count == r_size(&opt->table))
+ return_error(gs_error_limitcheck);
+ ref_assign_old(&opt->table, &tab[count], op, "makeoperator");
+ opt->nx_table[count] = name_index(imemory, op - 1);
+ op_index_ref(imemory, opt->base_index + count, op - 1);
+ opt->count = count + 1;
+ pop(1);
+ return 0;
+}
+
+/* - .oserrno <int> */
+static int
+zoserrno(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, errno);
+ return 0;
+}
+
+/* <int> .setoserrno - */
+static int
+zsetoserrno(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ errno = op->value.intval;
+ pop(1);
+ return 0;
+}
+
+/* <int> .oserrorstring <string> true */
+/* <int> .oserrorstring false */
+static int
+zoserrorstring(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const char *str;
+ int code;
+ uint len;
+ byte ch;
+
+ check_type(*op, t_integer);
+ str = gp_strerror((int)op->value.intval);
+ if (str == 0 || (len = strlen(str)) == 0) {
+ make_false(op);
+ return 0;
+ }
+ check_ostack(1);
+ code = string_to_ref(str, op, iimemory, ".oserrorstring");
+ if (code < 0)
+ return code;
+ /* Strip trailing end-of-line characters. */
+ while ((len = r_size(op)) != 0 &&
+ ((ch = op->value.bytes[--len]) == '\r' || ch == '\n')
+ )
+ r_dec_size(op, 1);
+ push(1);
+ make_true(op);
+ return 0;
+}
+
+/* <string> <bool> .setdebug - */
+static int
+zsetdebug(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ check_read_type(op[-1], t_string);
+ check_type(*op, t_boolean);
+ {
+ int i;
+
+ for (i = 0; i < r_size(op - 1); i++)
+ gs_debug[op[-1].value.bytes[i] & 127] =
+ op->value.boolval;
+ }
+ pop(2);
+ return 0;
+}
+
+/* .mementolistnew - */
+static int
+zmementolistnewblocks(i_ctx_t *i_ctx_p)
+{
+ Memento_listNewBlocks();
+ return 0;
+}
+
+/* There are a few cases where a customer/user might want CPSI behavior
+ * instead of the GS default behavior. cmyk_to_rgb and Type 1 char fill
+ * method are two that have come up so far. This operator allows a PS
+ * program to control the behavior without needing to recompile.
+ */
+/* <bool> .setCPSImode - */
+static int
+zsetCPSImode(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ check_type(*op, t_boolean);
+ gs_setcpsimode(imemory, op->value.boolval);
+ if (op->value.boolval) {
+ i_ctx_p->scanner_options |= SCAN_CPSI_MODE;
+ }
+ else {
+ i_ctx_p->scanner_options &= ~(int)SCAN_CPSI_MODE;
+ }
+ pop(1);
+ return 0;
+}
+
+/* - .getCPSImode <bool> */
+static int
+zgetCPSImode(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_bool(op, gs_currentcpsimode(imemory));
+ return 0;
+}
+
+/* ------ gs persistent cache operators ------ */
+/* these are for testing only. they're disabled in the normal build
+ * to prevent access to the cache by malicious postscript files
+ *
+ * use something like this:
+ * (value) (key) .pcacheinsert
+ * (key) .pcachequery { (\n) concatstrings print } if
+ */
+
+#ifdef DEBUG_CACHE
+
+/* <string> <string> .pcacheinsert */
+static int
+zpcacheinsert(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ char *key, *buffer;
+ int keylen, buflen;
+ int code = 0;
+
+ check_read_type(*op, t_string);
+ keylen = r_size(op);
+ key = op->value.bytes;
+ check_read_type(*(op - 1), t_string);
+ buflen = r_size(op - 1);
+ buffer = (op - 1)->value.bytes;
+
+ code = gp_cache_insert(0, key, keylen, buffer, buflen);
+ if (code < 0)
+ return code;
+
+ pop(2);
+
+ return code;
+}
+
+/* allocation callback for query result */
+static void *
+pcache_alloc_callback(void *userdata, int bytes)
+{
+ i_ctx_t *i_ctx_p = (i_ctx_t*)userdata;
+ return ialloc_string(bytes, "pcache buffer");
+}
+
+/* <string> .pcachequery <string> true */
+/* <string> .pcachequery false */
+static int
+zpcachequery(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int len;
+ char *key;
+ byte *string;
+ int code = 0;
+
+ check_read_type(*op, t_string);
+ len = r_size(op);
+ key = op->value.bytes;
+ len = gp_cache_query(GP_CACHE_TYPE_TEST, key, len, (void**)&string, &pcache_alloc_callback, i_ctx_p);
+ if (len < 0) {
+ make_false(op);
+ return 0;
+ }
+ if (string == NULL)
+ return_error(gs_error_VMerror);
+ make_string(op, a_all | icurrent_space, len, string);
+
+ push(1);
+ make_true(op);
+
+ return code;
+}
+
+#endif /* DEBUG_CACHE */
+
+/* ------ Initialization procedure ------ */
+
+const op_def zmisc_op_defs[] =
+{
+ {"1bind", zbind},
+ {"1getenv", zgetenv},
+ {"0.defaultpapersize", zdefaultpapersize},
+ {"2.makeoperator", zmakeoperator},
+ {"0.oserrno", zoserrno},
+ {"1.oserrorstring", zoserrorstring},
+ {"0realtime", zrealtime},
+ {"1serialnumber", zserialnumber},
+ {"2.setdebug", zsetdebug},
+ {"0.mementolistnewblocks", zmementolistnewblocks},
+ {"1.setoserrno", zsetoserrno},
+ {"0usertime", zusertime},
+ {"1.setCPSImode", zsetCPSImode},
+ {"0.getCPSImode", zgetCPSImode},
+#ifdef DEBUG_CACHE
+ /* pcache test */
+ {"2.pcacheinsert", zpcacheinsert},
+ {"1.pcachequery", zpcachequery},
+#endif
+ op_def_end(0)
+};
diff --git a/psi/zmisc1.c b/psi/zmisc1.c
new file mode 100644
index 000000000..35379150c
--- /dev/null
+++ b/psi/zmisc1.c
@@ -0,0 +1,176 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Miscellaneous Type 1 font operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gscrypt1.h"
+#include "stream.h" /* for getting state of PFBD stream */
+#include "strimpl.h"
+#include "sfilter.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifilter.h"
+
+/* <state> <from_string> <to_string> .type1encrypt <new_state> <substring> */
+/* <state> <from_string> <to_string> .type1decrypt <new_state> <substring> */
+static int type1crypt(i_ctx_t *,
+ int (*)(byte *, const byte *, uint, ushort *));
+static int
+ztype1encrypt(i_ctx_t *i_ctx_p)
+{
+ return type1crypt(i_ctx_p, gs_type1_encrypt);
+}
+static int
+ztype1decrypt(i_ctx_t *i_ctx_p)
+{
+ return type1crypt(i_ctx_p, gs_type1_decrypt);
+}
+static int
+type1crypt(i_ctx_t *i_ctx_p,
+ int (*proc)(byte *, const byte *, uint, ushort *))
+{
+ os_ptr op = osp;
+ crypt_state state;
+ uint ssize;
+
+ check_type(op[-2], t_integer);
+ state = op[-2].value.intval;
+ if (op[-2].value.intval != state)
+ return_error(gs_error_rangecheck); /* state value was truncated */
+ check_read_type(op[-1], t_string);
+ check_write_type(*op, t_string);
+ ssize = r_size(op - 1);
+ if (r_size(op) < ssize)
+ return_error(gs_error_rangecheck);
+ discard((*proc)(op->value.bytes, op[-1].value.const_bytes, ssize,
+ &state)); /* can't fail */
+ op[-2].value.intval = state;
+ op[-1] = *op;
+ r_set_size(op - 1, ssize);
+ pop(1);
+ return 0;
+}
+
+/* Get the seed parameter for eexecEncode/Decode. */
+/* Return npop if OK. */
+static int
+eexec_param(os_ptr op, ushort * pcstate)
+{
+ int npop = 1;
+
+ if (r_has_type(op, t_dictionary))
+ ++npop, --op;
+ check_type(*op, t_integer);
+ *pcstate = op->value.intval;
+ if (op->value.intval != *pcstate)
+ return_error(gs_error_rangecheck); /* state value was truncated */
+ return npop;
+}
+
+/* <target> <seed> eexecEncode/filter <file> */
+/* <target> <seed> <dict_ignored> eexecEncode/filter <file> */
+static int
+zexE(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_exE_state state;
+ int code = eexec_param(op, &state.cstate);
+
+ if (code < 0)
+ return code;
+ return filter_write(i_ctx_p, code, &s_exE_template, (stream_state *)&state, 0);
+}
+
+/* <source> <seed> eexecDecode/filter <file> */
+/* <source> <dict> eexecDecode/filter <file> */
+static int
+zexD(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream_exD_state state;
+ int code;
+
+ (*s_exD_template.set_defaults)((stream_state *)&state);
+ if (r_has_type(op, t_dictionary)) {
+ uint cstate;
+ bool is_eexec;
+
+ check_dict_read(*op);
+ if ((code = dict_uint_param(op, "seed", 0, 0xffff, 0x10000,
+ &cstate)) < 0 ||
+ (code = dict_int_param(op, "lenIV", 0, max_int, 4,
+ &state.lenIV)) < 0 ||
+ (code = dict_bool_param(op, "eexec", false,
+ &is_eexec)) < 0 ||
+ (code = dict_bool_param(op, "keep_spaces", false,
+ &state.keep_spaces)) < 0
+ )
+ return code;
+ state.cstate = cstate;
+ state.binary = (is_eexec ? -1 : 1);
+ code = 1;
+ } else {
+ state.binary = 1;
+ code = eexec_param(op, &state.cstate);
+ }
+ if (code < 0)
+ return code;
+ /*
+ * If we're reading a .PFB file, let the filter know about it,
+ * so it can read recklessly to the end of the binary section.
+ */
+ if (r_has_type(op - 1, t_file)) {
+ stream *s = (op - 1)->value.pfile;
+
+ if (s->state != 0 && s->state->templat == &s_PFBD_template) {
+ stream_PFBD_state *pss = (stream_PFBD_state *)s->state;
+
+ state.pfb_state = pss;
+ /*
+ * If we're reading the binary section of a PFB stream,
+ * avoid the conversion from binary to hex and back again.
+ */
+ if (pss->record_type == 2) {
+ /*
+ * The PFB decoder may have converted some data to hex
+ * already. Convert it back if necessary.
+ */
+ if (pss->binary_to_hex && sbufavailable(s) > 0) {
+ state.binary = 0; /* start as hex */
+ state.hex_left = sbufavailable(s);
+ } else {
+ state.binary = 1;
+ }
+ pss->binary_to_hex = 0;
+ }
+ }
+ }
+ return filter_read(i_ctx_p, code, &s_exD_template, (stream_state *)&state, 0);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zmisc1_op_defs[] =
+{
+ {"3.type1encrypt", ztype1encrypt},
+ {"3.type1decrypt", ztype1decrypt},
+ op_def_begin_filter(),
+ {"2eexecEncode", zexE},
+ {"2eexecDecode", zexD},
+ op_def_end(0)
+};
diff --git a/psi/zmisc2.c b/psi/zmisc2.c
new file mode 100644
index 000000000..3560cc602
--- /dev/null
+++ b/psi/zmisc2.c
@@ -0,0 +1,287 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Miscellaneous Level 2 operators */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "estack.h"
+#include "iddict.h"
+#include "idparam.h"
+#include "iparam.h"
+#include "dstack.h"
+#include "ilevel.h"
+#include "iname.h"
+#include "iutil2.h"
+#include "ivmspace.h"
+#include "store.h"
+
+/* Forward references */
+static int set_language_level(i_ctx_t *, int);
+
+/* ------ Language level operators ------ */
+
+/* - .languagelevel <int> */
+static int
+zlanguagelevel(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, LANGUAGE_LEVEL);
+ return 0;
+}
+
+/* <int> .setlanguagelevel - */
+static int
+zsetlanguagelevel(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = 0;
+
+ check_type(*op, t_integer);
+ if (op->value.intval != LANGUAGE_LEVEL) {
+ code = set_language_level(i_ctx_p, (int)op->value.intval);
+ if (code < 0)
+ return code;
+ }
+ LANGUAGE_LEVEL = op->value.intval;
+ pop(1);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+/* The level setting ops are recognized even in Level 1 mode. */
+const op_def zmisc2_op_defs[] =
+{
+ {"0.languagelevel", zlanguagelevel},
+ {"1.setlanguagelevel", zsetlanguagelevel},
+ op_def_end(0)
+};
+
+/* ------ Internal procedures ------ */
+
+/*
+ * Adjust the interpreter for a change in language level.
+ * This is used for the .setlanguagelevel operator,
+ * and (perhaps someday) after a restore.
+ */
+static int swap_level_dict(i_ctx_t *i_ctx_p, const char *dict_name);
+static int swap_entry(i_ctx_t *i_ctx_p, ref elt[2], ref * pdict,
+ ref * pdict2);
+static int
+set_language_level(i_ctx_t *i_ctx_p, int new_level)
+{
+ int old_level = LANGUAGE_LEVEL;
+ ref *pgdict = /* globaldict, if present */
+ ref_stack_index(&d_stack, ref_stack_count(&d_stack) - 2);
+ ref *level2dict;
+ int code = 0;
+
+ if (new_level < 1 ||
+ new_level >
+ (dict_find_string(systemdict, "ll3dict", &level2dict) > 0 ? 3 : 2)
+ )
+ return_error(gs_error_rangecheck);
+ if (dict_find_string(systemdict, "level2dict", &level2dict) <= 0)
+ return_error(gs_error_undefined);
+ /*
+ * As noted in dstack.h, we allocate the extra d-stack entry for
+ * globaldict even in Level 1 mode; in Level 1 mode, this entry
+ * holds an extra copy of systemdict, and [count]dictstack omit the
+ * very bottommost entry.
+ */
+ while (new_level != old_level) {
+ switch (old_level) {
+ case 1: { /* 1 => 2 or 3 */
+ /* Put globaldict in the dictionary stack. */
+ ref *pdict;
+
+ /*
+ * This might be called so early in initialization that
+ * globaldict hasn't been defined yet. If so, just skip
+ * this step.
+ */
+ code = dict_find_string(level2dict, "globaldict", &pdict);
+ if (code > 0) {
+ if (!r_has_type(pdict, t_dictionary))
+ return_error(gs_error_typecheck);
+ *pgdict = *pdict;
+ }
+ /* Set other flags for Level 2 operation. */
+ imemory->gs_lib_ctx->dict_auto_expand = true;
+ }
+ code = swap_level_dict(i_ctx_p, "level2dict");
+ if (code < 0)
+ return code;
+ ++old_level;
+ continue;
+ case 3: /* 3 => 1 or 2 */
+ code = swap_level_dict(i_ctx_p, "ll3dict");
+ if (code < 0)
+ return code;
+ --old_level;
+ continue;
+ default: /* 2 => 1 or 3 */
+ break;
+ }
+ switch (new_level) {
+ case 1: { /* 2 => 1 */
+ /*
+ * Clear the cached definition pointers of all names defined
+ * in globaldict. This will slow down future lookups, but
+ * we don't care.
+ */
+ int index = dict_first(pgdict);
+ ref elt[2];
+
+ while ((index = dict_next(pgdict, index, &elt[0])) >= 0)
+ if (r_has_type(&elt[0], t_name))
+ name_invalidate_value_cache(imemory, &elt[0]);
+ /* Overwrite globaldict in the dictionary stack. */
+ *pgdict = *systemdict;
+ /* Set other flags for Level 1 operation. */
+ imemory->gs_lib_ctx->dict_auto_expand = false;
+ }
+ code = swap_level_dict(i_ctx_p, "level2dict");
+ break;
+ case 3: /* 2 => 3 */
+ code = swap_level_dict(i_ctx_p, "ll3dict");
+ break;
+ default: /* not possible */
+ return_error(gs_error_Fatal);
+ }
+ break;
+ }
+ dict_set_top(); /* reload dict stack cache */
+ return code;
+}
+
+/*
+ * Swap the contents of a level dictionary (level2dict or ll3dict) and
+ * systemdict. If a value in the level dictionary is itself a dictionary,
+ * and it contains a key/value pair referring to itself, swap its contents
+ * with the contents of the same dictionary in systemdict. (This is a hack
+ * to swap the contents of statusdict.)
+ */
+static int
+swap_level_dict(i_ctx_t *i_ctx_p, const char *dict_name)
+{
+ ref *pleveldict;
+ ref rleveldict;
+ int index;
+ ref elt[2]; /* key, value */
+ ref *psubdict;
+
+ /*
+ * We have to copy the refs for leveldict and subdict, since they may
+ * move if their containing dictionary is resized.
+ */
+ if (dict_find_string(systemdict, dict_name, &pleveldict) <= 0)
+ return_error(gs_error_undefined);
+ rleveldict = *pleveldict;
+ index = dict_first(&rleveldict);
+ while ((index = dict_next(&rleveldict, index, &elt[0])) >= 0)
+ if (r_has_type(&elt[1], t_dictionary) &&
+ dict_find(&elt[1], &elt[0], &psubdict) > 0 &&
+ obj_eq(imemory, &elt[1], psubdict)
+ ) {
+ /* elt[1] is the 2nd-level sub-dictionary */
+ int isub = dict_first(&elt[1]);
+ ref subelt[2];
+ int found = dict_find(systemdict, &elt[0], &psubdict);
+ ref rsubdict;
+
+ if (found <= 0)
+ continue;
+ rsubdict = *psubdict;
+ while ((isub = dict_next(&elt[1], isub, &subelt[0])) >= 0)
+ if (!obj_eq(imemory, &subelt[0], &elt[0])) {
+ /* don't swap dict itself */
+ int code = swap_entry(i_ctx_p, subelt, &rsubdict, &elt[1]);
+
+ if (code < 0)
+ return code;
+ }
+ } else {
+ int code = swap_entry(i_ctx_p, elt, systemdict, &rleveldict);
+
+ if (code < 0)
+ return code;
+ }
+ return 0;
+}
+
+/*
+ * Swap an entry from a higher level dictionary into a base dictionary.
+ * elt[0] is the key, elt[1] is the current value in the Level 2 dictionary
+ * (*pdict2).
+ */
+static int
+swap_entry(i_ctx_t *i_ctx_p, ref elt[2], ref * pdict, ref * pdict2)
+{
+ ref *pvalue;
+#ifdef PACIFY_VALGRIND
+ ref old_value = { 0 }; /* current value in *pdict */
+#else
+ ref old_value; /* current value in *pdict */
+#endif
+ int found = dict_find(pdict, &elt[0], &pvalue);
+
+ switch (found) {
+ default: /* <0, error */
+ /*
+ * The only possible error here is a dictfull error, which is
+ * harmless.
+ */
+ /* fall through */
+ case 0: /* missing */
+ make_null(&old_value);
+ break;
+ case 1: /* present */
+ old_value = *pvalue;
+ }
+ /*
+ * Temporarily flag the dictionaries as local, so that we don't
+ * get invalidaccess errors. (We know that they are both
+ * referenced from systemdict, so they are allowed to reference
+ * local objects even if they are global.)
+ */
+ {
+ uint space2 = r_space(pdict2);
+ int code;
+
+ r_set_space(pdict2, avm_local);
+ idict_put(pdict2, &elt[0], &old_value);
+ if (r_has_type(&elt[1], t_null)) {
+ code = idict_undef(pdict, &elt[0]);
+ if (code == gs_error_undefined &&
+ r_has_type(&old_value, t_null)
+ )
+ code = 0;
+ } else {
+ uint space = r_space(pdict);
+
+ r_set_space(pdict, avm_local);
+ code = idict_put(pdict, &elt[0], &elt[1]);
+ r_set_space(pdict, space);
+ }
+ r_set_space(pdict2, space2);
+ return code;
+ }
+}
diff --git a/psi/zmisc3.c b/psi/zmisc3.c
new file mode 100644
index 000000000..54b304246
--- /dev/null
+++ b/psi/zmisc3.c
@@ -0,0 +1,126 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Miscellaneous LanguageLevel 3 operators */
+#include "ghost.h"
+#include "gscspace.h" /* for gscolor2.h */
+#include "gsmatrix.h" /* ditto */
+#include "gsclipsr.h"
+#include "gscolor2.h"
+#include "oper.h"
+#include "igstate.h"
+#include "store.h"
+
+/* - clipsave - */
+static int
+zclipsave(i_ctx_t *i_ctx_p)
+{
+ return gs_clipsave(igs);
+}
+
+/* - cliprestore - */
+static int
+zcliprestore(i_ctx_t *i_ctx_p)
+{
+ return gs_cliprestore(igs);
+}
+
+/* <proc1> <proc2> .eqproc <bool> */
+/*
+ * Test whether two procedures are equal to depth 10.
+ * This is the equality test used by idiom recognition in 'bind'.
+ */
+/* Adobe specifies maximum depth of 10 but 12 is needed */
+/* to reproduce the observed behavior. CET 31-01-05 */
+#define MAX_DEPTH 12
+typedef struct ref2_s {
+ ref proc1, proc2;
+} ref2_t;
+static int
+zeqproc(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref2_t stack[MAX_DEPTH + 1];
+ ref2_t *top = stack;
+
+ make_array(&stack[0].proc1, 0, 1, op - 1);
+ make_array(&stack[0].proc2, 0, 1, op);
+ for (;;) {
+ long i;
+
+ if (r_size(&top->proc1) == 0) {
+ /* Finished these arrays, go up to next level. */
+ if (top == stack) {
+ /* We're done matching: it succeeded. */
+ make_true(op - 1);
+ pop(1);
+ return 0;
+ }
+ --top;
+ continue;
+ }
+ /* Look at the next elements of the arrays. */
+ i = r_size(&top->proc1) - 1;
+ array_get(imemory, &top->proc1, i, &top[1].proc1);
+ array_get(imemory, &top->proc2, i, &top[1].proc2);
+ r_dec_size(&top->proc1, 1);
+ ++top;
+ /*
+ * Amazingly enough, the objects' executable attributes are not
+ * required to match. This means { x load } will match { /x load },
+ * even though this is clearly wrong.
+ */
+#if 0
+ if (r_has_attr(&top->proc1, a_executable) !=
+ r_has_attr(&top->proc2, a_executable)
+ )
+ break;
+#endif
+ if (obj_eq(imemory, &top->proc1, &top->proc2)) {
+ /* Names don't match strings. */
+ if (r_type(&top->proc1) != r_type(&top->proc2) &&
+ (r_type(&top->proc1) == t_name ||
+ r_type(&top->proc2) == t_name)
+ )
+ break;
+ --top; /* no recursion */
+ continue;
+ }
+ if (r_is_array(&top->proc1) && r_is_array(&top->proc2) &&
+ r_size(&top->proc1) == r_size(&top->proc2) &&
+ top < stack + (MAX_DEPTH - 1)
+ ) {
+ /* Descend into the arrays. */
+ continue;
+ }
+ break;
+ }
+ /* An exit from the loop indicates that matching failed. */
+ make_false(op - 1);
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zmisc3_op_defs[] =
+{
+ op_def_begin_ll3(),
+ {"0cliprestore", zcliprestore},
+ {"0clipsave", zclipsave},
+ {"2.eqproc", zeqproc},
+ op_def_end(0)
+};
diff --git a/psi/zncdummy.c b/psi/zncdummy.c
new file mode 100644
index 000000000..12abe3ac3
--- /dev/null
+++ b/psi/zncdummy.c
@@ -0,0 +1,62 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* Sample implementation for client custom processing of color spaces. */
+
+/*
+ * This module has been created to demonstrate how to add support for the use
+ * of custom color handling to the Ghostscript graphics library via a custom color
+ * callback mechanism.
+ *
+ * See the comments at the start of src/gsncdummy.c for more information.
+ */
+
+#include "math_.h"
+#include "memory_.h"
+#include "gx.h"
+#include "gscdefs.h"
+#include "gsnamecl.h"
+#include "malloc_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsncdummy.h"
+
+/*
+ * This procedure is here to simplify debugging. Normally one would expect the
+ * custom color callback structure to be set up by a calling application.
+ * Since I do not have a calling application, I need a simple way to setup the
+ * callback parameter. The callback parameter is passed as a string value.
+ * This routine puts the address of our demo callback structure into the
+ * provided string.
+ *
+ * This routine allows the demo version of the PANTONE logic to be enabled
+ * by adding the following to the command line:
+ * -c "<< /CustomColorCallback 32 string .pantonecallback >> setsystemparams" -f
+ */
+
+/* <string> .pantonecallback <string> */
+static int
+zpantonecallback(i_ctx_t *i_ctx_p)
+{
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def pantone_op_defs[] =
+{
+ {"1.pantonecallback", zpantonecallback},
+ op_def_end(0)
+};
diff --git a/psi/zpacked.c b/psi/zpacked.c
new file mode 100644
index 000000000..d37954c86
--- /dev/null
+++ b/psi/zpacked.c
@@ -0,0 +1,256 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Packed array operators */
+#include "ghost.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "iname.h"
+#include "istack.h" /* for iparray.h */
+#include "ipacked.h"
+#include "iparray.h"
+#include "ivmspace.h"
+#include "oper.h"
+#include "store.h"
+#include "gxalloc.h"
+
+/* - currentpacking <bool> */
+static int
+zcurrentpacking(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ ref_assign(op, &ref_array_packing);
+ return 0;
+}
+
+/* <obj_0> ... <obj_n-1> <n> packedarray <packedarray> */
+int
+zpackedarray(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ ref parr;
+
+ check_type(*op, t_integer);
+ if (op->value.intval < 0)
+ return_error(gs_error_rangecheck);
+ if (op->value.intval > op - osbot &&
+ op->value.intval >= ref_stack_count(&o_stack)
+ )
+ return_error(gs_error_stackunderflow);
+ osp--;
+ code = make_packed_array(&parr, &o_stack, (uint) op->value.intval,
+ idmemory, "packedarray");
+ osp++;
+ if (code >= 0)
+ *osp = parr;
+ return code;
+}
+
+/* <bool> setpacking - */
+static int
+zsetpacking(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref cont;
+ check_type(*op, t_boolean);
+ make_struct(&cont, avm_local, ref_array_packing_container);
+ ref_assign_old(&cont, &ref_array_packing, op, "setpacking");
+ pop(1);
+ return 0;
+}
+
+/* ------ Non-operator routines ------ */
+
+/* Make a packed array. See the comment in packed.h about */
+/* ensuring that refs in mixed arrays are properly aligned. */
+#undef idmemory /****** NOTA BENE ******/
+int
+make_packed_array(ref * parr, ref_stack_t * pstack, uint size,
+ gs_dual_memory_t *idmemory, client_name_t cname)
+{
+ uint i;
+ const ref *pref;
+ uint idest = 0, ishort = 0;
+ ref_packed *pbody;
+ ref_packed *pdest;
+ ref_packed *pshort; /* points to start of */
+ /* last run of short elements */
+ gs_ref_memory_t *imem = idmemory->current;
+ uint space = imemory_space(imem);
+ int skip = 0, pad;
+ ref rtemp;
+ int code;
+
+ /* Do a first pass to calculate the size of the array, */
+ /* and to detect local-into-global stores. */
+
+ for (i = size; i != 0; i--) {
+ pref = ref_stack_index(pstack, i - 1);
+ switch (r_btype(pref)) { /* not r_type, opers are special */
+ case t_name:
+ if (name_index(imem, pref) >= packed_name_max_index)
+ break; /* can't pack */
+ idest++;
+ continue;
+ case t_integer:
+ if (pref->value.intval < packed_min_intval ||
+ pref->value.intval > packed_max_intval
+ )
+ break;
+ idest++;
+ continue;
+ case t_oparray:
+ /* Check for local-into-global store. */
+ store_check_space(space, pref);
+ /* falls through */
+ case t_operator:
+ {
+ uint oidx;
+
+ if (!r_has_attr(pref, a_executable))
+ break;
+ oidx = op_index(pref);
+ if (oidx == 0 || oidx > packed_int_mask)
+ break;
+ }
+ idest++;
+ continue;
+ default:
+ /* Check for local-into-global store. */
+ store_check_space(space, pref);
+ }
+ /* Can't pack this element, use a full ref. */
+ /* We may have to unpack up to align_packed_per_ref - 1 */
+ /* preceding short elements. */
+ /* If we are at the beginning of the array, however, */
+ /* we can just move the elements up. */
+ {
+ int i = (idest - ishort) & (align_packed_per_ref - 1);
+
+ if (ishort == 0) /* first time */
+ idest += skip = -i & (align_packed_per_ref - 1);
+ else
+ idest += (packed_per_ref - 1) * i;
+ }
+ ishort = idest += packed_per_ref;
+ }
+ pad = (packed_per_ref - idest % packed_per_ref) % packed_per_ref; /* padding at end */
+
+ /* Now we can allocate the array. */
+
+ code = gs_alloc_ref_array(imem, &rtemp, 0, (idest + pad) / packed_per_ref,
+ cname);
+ if (code < 0)
+ return code;
+ pbody = (ref_packed *) rtemp.value.refs;
+
+ /* Make sure any initial skipped elements contain legal packed */
+ /* refs, so that the garbage collector can scan storage. */
+
+ pshort = pbody;
+ for (; skip; skip--)
+ *pbody++ = pt_tag(pt_integer);
+ pdest = pbody;
+
+ for (i = size; i != 0; i--) {
+ pref = ref_stack_index(pstack, i - 1);
+ switch (r_btype(pref)) { /* not r_type, opers are special */
+ case t_name:
+ {
+ uint nidx = name_index(imem, pref);
+
+ if (nidx >= packed_name_max_index)
+ break; /* can't pack */
+ *pdest++ = nidx +
+ (r_has_attr(pref, a_executable) ?
+ pt_tag(pt_executable_name) :
+ pt_tag(pt_literal_name));
+ }
+ continue;
+ case t_integer:
+ if (pref->value.intval < packed_min_intval ||
+ pref->value.intval > packed_max_intval
+ )
+ break;
+ *pdest++ = pt_tag(pt_integer) +
+ ((short)pref->value.intval - packed_min_intval);
+ continue;
+ case t_oparray:
+ case t_operator:
+ {
+ uint oidx;
+
+ if (!r_has_attr(pref, a_executable))
+ break;
+ oidx = op_index(pref);
+ if (oidx == 0 || oidx > packed_int_mask)
+ break;
+ *pdest++ = pt_tag(pt_executable_operator) + oidx;
+ }
+ continue;
+ }
+ /* Can't pack this element, use a full ref. */
+ /* We may have to unpack up to align_packed_per_ref - 1 */
+ /* preceding short elements. */
+ /* Note that if we are at the beginning of the array, */
+ /* 'skip' already ensures that we don't need to do this. */
+ {
+ int i = (pdest - pshort) & (align_packed_per_ref - 1);
+ const ref_packed *psrc = pdest;
+ ref *pmove =
+ (ref *) (pdest += (packed_per_ref - 1) * i);
+
+ ref_assign_new(pmove, pref);
+ while (--i >= 0) {
+ --psrc;
+ --pmove;
+ packed_get(imem->non_gc_memory, psrc, pmove);
+ }
+ }
+ pshort = pdest += packed_per_ref;
+ }
+
+ {
+ int atype =
+ (pdest == pbody + size ? t_shortarray : t_mixedarray);
+
+ /* Pad with legal packed refs so that the garbage collector */
+ /* can scan storage. */
+
+ for (; pad; pad--)
+ *pdest++ = pt_tag(pt_integer);
+
+ /* Finally, make the array. */
+
+ ref_stack_pop(pstack, size);
+ make_tasv_new(parr, atype, a_readonly | space, size,
+ packed, pbody + skip);
+ }
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zpacked_op_defs[] =
+{
+ {"0currentpacking", zcurrentpacking},
+ {"1packedarray", zpackedarray},
+ {"1setpacking", zsetpacking},
+ op_def_end(0)
+};
diff --git a/psi/zpaint.c b/psi/zpaint.c
new file mode 100644
index 000000000..6a8065522
--- /dev/null
+++ b/psi/zpaint.c
@@ -0,0 +1,84 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Painting operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gspaint.h"
+#include "igstate.h"
+
+/* - fill - */
+static int
+zfill(i_ctx_t *i_ctx_p)
+{
+ return gs_fill(igs);
+}
+
+/* - eofill - */
+static int
+zeofill(i_ctx_t *i_ctx_p)
+{
+ return gs_eofill(igs);
+}
+
+/* - stroke - */
+static int
+zstroke(i_ctx_t *i_ctx_p)
+{
+ return gs_stroke(igs);
+}
+
+/* ------ Non-standard operators ------ */
+
+/* - .fillpage - */
+static int
+zfillpage(i_ctx_t *i_ctx_p)
+{
+ return gs_fillpage(igs);
+}
+
+/* <width> <height> <data> .imagepath - */
+static int
+zimagepath(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_type(op[-2], t_integer);
+ check_type(op[-1], t_integer);
+ check_read_type(*op, t_string);
+ if (r_size(op) < ((op[-2].value.intval + 7) >> 3) * op[-1].value.intval)
+ return_error(gs_error_rangecheck);
+ code = gs_imagepath(igs,
+ (int)op[-2].value.intval, (int)op[-1].value.intval,
+ op->value.const_bytes);
+ if (code >= 0)
+ pop(3);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zpaint_op_defs[] =
+{
+ {"0eofill", zeofill},
+ {"0fill", zfill},
+ {"0stroke", zstroke},
+ /* Non-standard operators */
+ {"0.fillpage", zfillpage},
+ {"3.imagepath", zimagepath},
+ op_def_end(0)
+};
diff --git a/psi/zpath.c b/psi/zpath.c
new file mode 100644
index 000000000..446ecad40
--- /dev/null
+++ b/psi/zpath.c
@@ -0,0 +1,176 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Basic path operators */
+#include "math_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "igstate.h"
+#include "gsmatrix.h"
+#include "gspath.h"
+#include "store.h"
+
+/* Forward references */
+static int common_to(i_ctx_t *,
+ int (*)(gs_state *, double, double));
+static int common_curve(i_ctx_t *,
+ int (*)(gs_state *, double, double, double, double, double, double));
+
+/* - newpath - */
+static int
+znewpath(i_ctx_t *i_ctx_p)
+{
+ return gs_newpath(igs);
+}
+
+/* - currentpoint <x> <y> */
+static int
+zcurrentpoint(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_point pt;
+ int code = gs_currentpoint(igs, &pt);
+
+ if (code < 0)
+ return code;
+ push(2);
+ make_real(op - 1, pt.x);
+ make_real(op, pt.y);
+ return 0;
+}
+
+/* <x> <y> moveto - */
+int
+zmoveto(i_ctx_t *i_ctx_p)
+{
+ return common_to(i_ctx_p, gs_moveto);
+}
+
+/* <dx> <dy> rmoveto - */
+int
+zrmoveto(i_ctx_t *i_ctx_p)
+{
+ return common_to(i_ctx_p, gs_rmoveto);
+}
+
+/* <x> <y> lineto - */
+int
+zlineto(i_ctx_t *i_ctx_p)
+{
+ return common_to(i_ctx_p, gs_lineto);
+}
+
+/* <dx> <dy> rlineto - */
+int
+zrlineto(i_ctx_t *i_ctx_p)
+{
+ return common_to(i_ctx_p, gs_rlineto);
+}
+
+/* Common code for [r](move/line)to */
+static int
+common_to(i_ctx_t *i_ctx_p,
+ int (*add_proc)(gs_state *, double, double))
+{
+ os_ptr op = osp;
+ double opxy[2];
+ int code;
+
+ if ((code = num_params(op, 2, opxy)) < 0 ||
+ (code = (*add_proc)(igs, opxy[0], opxy[1])) < 0
+ )
+ return code;
+ pop(2);
+ return 0;
+}
+
+/* <x1> <y1> <x2> <y2> <x3> <y3> curveto - */
+int
+zcurveto(i_ctx_t *i_ctx_p)
+{
+ return common_curve(i_ctx_p, gs_curveto);
+}
+
+/* <dx1> <dy1> <dx2> <dy2> <dx3> <dy3> rcurveto - */
+int
+zrcurveto(i_ctx_t *i_ctx_p)
+{
+ return common_curve(i_ctx_p, gs_rcurveto);
+}
+
+/* Common code for [r]curveto */
+static int
+common_curve(i_ctx_t *i_ctx_p,
+ int (*add_proc)(gs_state *, double, double, double, double, double, double))
+{
+ os_ptr op = osp;
+ double opxy[6];
+ int code;
+
+ if ((code = num_params(op, 6, opxy)) < 0)
+ return code;
+ code = (*add_proc)(igs, opxy[0], opxy[1], opxy[2], opxy[3], opxy[4], opxy[5]);
+ if (code >= 0)
+ pop(6);
+ return code;
+}
+
+/* - closepath - */
+int
+zclosepath(i_ctx_t *i_ctx_p)
+{
+ return gs_closepath(igs);
+}
+
+/* - initclip - */
+static int
+zinitclip(i_ctx_t *i_ctx_p)
+{
+ return gs_initclip(igs);
+}
+
+/* - clip - */
+static int
+zclip(i_ctx_t *i_ctx_p)
+{
+ return gs_clip(igs);
+}
+
+/* - eoclip - */
+static int
+zeoclip(i_ctx_t *i_ctx_p)
+{
+ return gs_eoclip(igs);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zpath_op_defs[] =
+{
+ {"0clip", zclip},
+ {"0closepath", zclosepath},
+ {"0currentpoint", zcurrentpoint},
+ {"6curveto", zcurveto},
+ {"0eoclip", zeoclip},
+ {"0initclip", zinitclip},
+ {"2lineto", zlineto},
+ {"2moveto", zmoveto},
+ {"0newpath", znewpath},
+ {"6rcurveto", zrcurveto},
+ {"2rlineto", zrlineto},
+ {"2rmoveto", zrmoveto},
+ op_def_end(0)
+};
diff --git a/psi/zpath1.c b/psi/zpath1.c
new file mode 100644
index 000000000..e4f8863fb
--- /dev/null
+++ b/psi/zpath1.c
@@ -0,0 +1,300 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PostScript Level 1 additional path operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "oparc.h" /* for prototypes */
+#include "estack.h" /* for pathforall */
+#include "ialloc.h"
+#include "igstate.h"
+#include "gsstruct.h"
+#include "gspath.h"
+#include "store.h"
+
+/* Forward references */
+static int common_arc(i_ctx_t *,
+ int (*)(gs_state *, double, double, double, double, double));
+static int common_arct(i_ctx_t *, float *);
+
+/* <x> <y> <r> <ang1> <ang2> arc - */
+int
+zarc(i_ctx_t *i_ctx_p)
+{
+ return common_arc(i_ctx_p, gs_arc);
+}
+
+/* <x> <y> <r> <ang1> <ang2> arcn - */
+int
+zarcn(i_ctx_t *i_ctx_p)
+{
+ return common_arc(i_ctx_p, gs_arcn);
+}
+
+/* Common code for arc[n] */
+static int
+common_arc(i_ctx_t *i_ctx_p,
+ int (*aproc)(gs_state *, double, double, double, double, double))
+{
+ os_ptr op = osp;
+ double xyra[5]; /* x, y, r, ang1, ang2 */
+ int code = num_params(op, 5, xyra);
+
+ if (code < 0)
+ return code;
+ code = (*aproc)(igs, xyra[0], xyra[1], xyra[2], xyra[3], xyra[4]);
+ if (code >= 0)
+ pop(5);
+ return code;
+}
+
+/* <x1> <y1> <x2> <y2> <r> arct - */
+int
+zarct(i_ctx_t *i_ctx_p)
+{
+ int code = common_arct(i_ctx_p, (float *)0);
+
+ if (code < 0)
+ return code;
+ pop(5);
+ return 0;
+}
+
+/* <x1> <y1> <x2> <y2> <r> arcto <xt1> <yt1> <xt2> <yt2> */
+static int
+zarcto(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ float tanxy[4]; /* xt1, yt1, xt2, yt2 */
+ int code = common_arct(i_ctx_p, tanxy);
+
+ if (code < 0)
+ return code;
+ make_real(op - 4, tanxy[0]);
+ make_real(op - 3, tanxy[1]);
+ make_real(op - 2, tanxy[2]);
+ make_real(op - 1, tanxy[3]);
+ pop(1);
+ return 0;
+}
+
+/* Common code for arct[o] */
+static int
+common_arct(i_ctx_t *i_ctx_p, float *tanxy)
+{
+ os_ptr op = osp;
+ double args[5]; /* x1, y1, x2, y2, r */
+ int code = num_params(op, 5, args);
+
+ if (code < 0)
+ return code;
+ return gs_arcto(igs, args[0], args[1], args[2], args[3], args[4], tanxy);
+}
+
+/* - .dashpath - */
+static int
+zdashpath(i_ctx_t *i_ctx_p)
+{
+ return gs_dashpath(igs);
+}
+
+/* - flattenpath - */
+static int
+zflattenpath(i_ctx_t *i_ctx_p)
+{
+ return gs_flattenpath(igs);
+}
+
+/* - reversepath - */
+static int
+zreversepath(i_ctx_t *i_ctx_p)
+{
+ return gs_reversepath(igs);
+}
+
+/* - strokepath - */
+static int
+zstrokepath(i_ctx_t *i_ctx_p)
+{
+ return gs_strokepath(igs);
+}
+
+/* - clippath - */
+static int
+zclippath(i_ctx_t *i_ctx_p)
+{
+ return gs_clippath(igs);
+}
+
+/* <bool> .pathbbox <llx> <lly> <urx> <ury> */
+static int
+z1pathbbox(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_rect box;
+ int code;
+
+ check_type(*op, t_boolean);
+ code = gs_upathbbox(igs, &box, op->value.boolval);
+ if (code < 0)
+ return code;
+ push(3);
+ make_real(op - 3, box.p.x);
+ make_real(op - 2, box.p.y);
+ make_real(op - 1, box.q.x);
+ make_real(op, box.q.y);
+ return 0;
+}
+
+/*
+ * In order to match Adobe output on a Genoa test, pathbbox must be an
+ * operator, not an operator procedure, even though it has a trivial
+ * definition as a procedure.
+ */
+static int
+zpathbbox(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ push(1);
+ make_false(op);
+ code = z1pathbbox(i_ctx_p);
+ if (code < 0) {
+ pop(1); /* remove the Boolean */
+ }
+ return code;
+}
+
+/* <moveproc> <lineproc> <curveproc> <closeproc> pathforall - */
+static int path_continue(i_ctx_t *);
+static int path_cleanup(i_ctx_t *);
+static int
+zpathforall(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_path_enum *penum;
+ int code;
+
+ check_proc(op[-3]);
+ check_proc(op[-2]);
+ check_proc(op[-1]);
+ check_proc(*op);
+ check_estack(8);
+
+ if ((penum = gs_path_enum_alloc(imemory, "pathforall")) == 0)
+ return_error(gs_error_VMerror);
+ code = gs_path_enum_init(imemory, penum, igs);
+ if (code < 0) {
+ ifree_object(penum, "path_cleanup");
+ return code;
+ }
+ /* Push a mark, the four procedures, and the path enumerator. */
+ push_mark_estack(es_for, path_cleanup); /* iterator */
+ memcpy(esp + 1, op - 3, 4 * sizeof(ref)); /* 4 procs */
+ esp += 5;
+ make_istruct(esp, 0, penum);
+ push_op_estack(path_continue);
+ pop(4);
+ op -= 4;
+ return o_push_estack;
+}
+/* Continuation procedure for pathforall */
+static void pf_push(i_ctx_t *, gs_point *, int);
+static int
+path_continue(i_ctx_t *i_ctx_p)
+{
+ gs_path_enum *penum = r_ptr(esp, gs_path_enum);
+ gs_point ppts[3];
+ int code;
+
+ /* Make sure we have room on the o-stack for the worst case */
+ /* before we enumerate the next path element. */
+ check_ostack(6); /* 3 points for curveto */
+ code = gs_path_enum_next(penum, ppts);
+ switch (code) {
+ case 0: /* all done */
+ esp -= 6;
+ path_cleanup(i_ctx_p);
+ return o_pop_estack;
+ default: /* error */
+ return code;
+ case gs_pe_moveto:
+ esp[2] = esp[-4]; /* moveto proc */
+ pf_push(i_ctx_p, ppts, 1);
+ break;
+ case gs_pe_lineto:
+ esp[2] = esp[-3]; /* lineto proc */
+ pf_push(i_ctx_p, ppts, 1);
+ break;
+ case gs_pe_curveto:
+ esp[2] = esp[-2]; /* curveto proc */
+ pf_push(i_ctx_p, ppts, 3);
+ break;
+ case gs_pe_closepath:
+ esp[2] = esp[-1]; /* closepath proc */
+ break;
+ }
+ push_op_estack(path_continue);
+ ++esp; /* include pushed procedure */
+ return o_push_estack;
+}
+/* Internal procedure to push one or more points */
+static void
+pf_push(i_ctx_t *i_ctx_p, gs_point * ppts, int n)
+{
+ os_ptr op = osp;
+
+ while (n--) {
+ op += 2;
+ make_real(op - 1, ppts->x);
+ make_real(op, ppts->y);
+ ppts++;
+ }
+ osp = op;
+}
+/* Clean up after a pathforall */
+static int
+path_cleanup(i_ctx_t *i_ctx_p)
+{
+ gs_path_enum *penum = r_ptr(esp + 6, gs_path_enum);
+
+ gs_path_enum_cleanup(penum);
+ ifree_object(penum, "path_cleanup");
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zpath1_op_defs[] =
+{
+ {"5arc", zarc},
+ {"5arcn", zarcn},
+ {"5arct", zarct},
+ {"5arcto", zarcto},
+ {"0clippath", zclippath},
+ {"0.dashpath", zdashpath},
+ {"0flattenpath", zflattenpath},
+ {"4pathforall", zpathforall},
+ {"0reversepath", zreversepath},
+ {"0strokepath", zstrokepath},
+ {"1.pathbbox", z1pathbbox},
+ {"0pathbbox", zpathbbox},
+ /* Internal operators */
+ {"0%path_continue", path_continue},
+ op_def_end(0)
+};
diff --git a/psi/zpcolor.c b/psi/zpcolor.c
new file mode 100644
index 000000000..e5fc29e16
--- /dev/null
+++ b/psi/zpcolor.c
@@ -0,0 +1,378 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Pattern color */
+#include "ghost.h"
+#include "oper.h"
+#include "gscolor.h"
+#include "gsmatrix.h"
+#include "gsstruct.h"
+#include "gscoord.h"
+#include "gxcspace.h"
+#include "gxfixed.h" /* for gxcolor2.h */
+#include "gxcolor2.h"
+#include "gxdcolor.h" /* for gxpcolor.h */
+#include "gxdevice.h"
+#include "gxdevmem.h" /* for gxpcolor.h */
+#include "gxpcolor.h"
+#include "gxpath.h"
+#include "estack.h"
+#include "ialloc.h"
+#include "icremap.h"
+#include "istruct.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "ipcolor.h"
+#include "store.h"
+#include "gzstate.h"
+#include "memory_.h"
+#include "gdevp14.h"
+#include "gxdevsop.h"
+
+/* Imported from gspcolor.c */
+extern const gs_color_space_type gs_color_space_type_Pattern;
+
+/* Forward references */
+static int zPaintProc(const gs_client_color *, gs_state *);
+static int pattern_paint_prepare(i_ctx_t *);
+static int pattern_paint_finish(i_ctx_t *);
+
+/* GC descriptors */
+private_st_int_pattern();
+
+/* Initialize the Pattern cache. */
+static int
+zpcolor_init(i_ctx_t *i_ctx_p)
+{
+ gx_pattern_cache *pc = gx_pattern_alloc_cache(imemory_system,
+ gx_pat_cache_default_tiles(),
+ gx_pat_cache_default_bits());
+ if (pc == NULL)
+ return_error(gs_error_VMerror);
+ gstate_set_pattern_cache(igs, pc);
+ return 0;
+}
+
+/* Create an interpreter pattern structure. */
+int
+int_pattern_alloc(int_pattern **ppdata, const ref *op, gs_memory_t *mem)
+{
+ int_pattern *pdata =
+ gs_alloc_struct(mem, int_pattern, &st_int_pattern, "int_pattern");
+
+ if (pdata == 0)
+ return_error(gs_error_VMerror);
+ pdata->dict = *op;
+ *ppdata = pdata;
+ return 0;
+}
+
+/* <pattern> <matrix> .buildpattern1 <pattern> <instance> */
+static int
+zbuildpattern1(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ int code;
+ gs_matrix mat;
+ float BBox[4];
+ gs_client_pattern templat;
+ int_pattern *pdata;
+ gs_client_color cc_instance;
+ ref *pPaintProc;
+
+ code = read_matrix(imemory, op, &mat);
+ if (code < 0)
+ return code;
+ check_type(*op1, t_dictionary);
+ check_dict_read(*op1);
+ gs_pattern1_init(&templat);
+
+ code = dict_uid_param(op1, &templat.uid, 1, imemory, i_ctx_p);
+ if (code < 0)
+ return code;
+ if (code != 1)
+ return_error(gs_error_rangecheck);
+
+ code = dict_int_param(op1, "PaintType", 1, 2, 0, &templat.PaintType);
+ if (code < 0)
+ return code;
+
+ code = dict_int_param(op1, "TilingType", 1, 3, 0, &templat.TilingType);
+ if (code < 0)
+ return code;
+
+ code = dict_bool_param(op1, ".pattern_uses_transparency", 0, &templat.uses_transparency);
+ if (code < 0)
+ return code;
+
+ code = dict_floats_param(imemory, op1, "BBox", 4, BBox, NULL);
+ if (code < 0)
+ return code;
+ if (code == 0)
+ return_error(gs_error_undefined);
+
+ code = dict_float_param(op1, "XStep", 0.0, &templat.XStep);
+ if (code < 0)
+ return code;
+ if (code == 1)
+ return_error(gs_error_undefined);
+
+ code = dict_float_param(op1, "YStep", 0.0, &templat.YStep);
+ if (code < 0)
+ return code;
+ if (code == 1)
+ return_error(gs_error_undefined);
+
+ code = dict_find_string(op1, "PaintProc", &pPaintProc);
+ if (code < 0)
+ return code;
+ if (code == 0)
+ return_error(gs_error_undefined);
+
+ check_proc(*pPaintProc);
+
+ if (mat.xx * mat.yy == mat.xy * mat.yx)
+ return_error(gs_error_undefinedresult);
+ if (BBox[0] >= BBox[2] || BBox[1] >= BBox[3])
+ return_error(gs_error_rangecheck);
+
+ templat.BBox.p.x = BBox[0];
+ templat.BBox.p.y = BBox[1];
+ templat.BBox.q.x = BBox[2];
+ templat.BBox.q.y = BBox[3];
+ templat.PaintProc = zPaintProc;
+ code = int_pattern_alloc(&pdata, op1, imemory);
+ if (code < 0)
+ return code;
+ templat.client_data = pdata;
+ code = gs_makepattern(&cc_instance, &templat, &mat, igs, imemory);
+ if (code < 0) {
+ ifree_object(pdata, "int_pattern");
+ return code;
+ }
+ make_istruct(op, a_readonly, cc_instance.pattern);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zpcolor_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ {"2.buildpattern1", zbuildpattern1},
+ {"0%pattern_paint_prepare", pattern_paint_prepare},
+ {"0%pattern_paint_finish", pattern_paint_finish},
+ op_def_end(zpcolor_init)
+};
+
+/* ------ Internal procedures ------ */
+
+/* Render the pattern by calling the PaintProc. */
+static int pattern_paint_cleanup(i_ctx_t *);
+static int
+zPaintProc(const gs_client_color * pcc, gs_state * pgs)
+{
+ /* Just schedule a call on the real PaintProc. */
+ r_ptr(&gs_int_gstate(pgs)->remap_color_info,
+ int_remap_color_info_t)->proc =
+ pattern_paint_prepare;
+ return_error(gs_error_Remap_Color);
+}
+/* Prepare to run the PaintProc. */
+static int
+pattern_paint_prepare(i_ctx_t *i_ctx_p)
+{
+ gs_state *pgs = igs;
+ gs_pattern1_instance_t *pinst =
+ (gs_pattern1_instance_t *)gs_currentcolor(pgs)->pattern;
+ ref *pdict = &((int_pattern *) pinst->templat.client_data)->dict;
+ gx_device_forward *pdev = NULL;
+ gx_device *cdev = gs_currentdevice_inline(igs);
+ int code;
+ ref *ppp;
+ bool internal_accum = true;
+
+ check_estack(6);
+ if (pgs->have_pattern_streams) {
+ code = dev_proc(cdev, dev_spec_op)(cdev, gxdso_pattern_can_accum,
+ pinst, pinst->id);
+ if (code < 0)
+ return code;
+ internal_accum = (code == 0);
+ }
+ if (internal_accum) {
+ gs_memory_t *storage_memory = gstate_pattern_cache(pgs)->memory;
+
+ pdev = gx_pattern_accum_alloc(imemory, storage_memory, pinst, "pattern_paint_prepare");
+ if (pdev == 0)
+ return_error(gs_error_VMerror);
+ code = (*dev_proc(pdev, open_device)) ((gx_device *) pdev);
+ if (code < 0) {
+ ifree_object(pdev, "pattern_paint_prepare");
+ return code;
+ }
+ } else {
+ code = gx_pattern_cache_add_dummy_entry((gs_imager_state *)igs,
+ pinst, cdev->color_info.depth);
+ if (code < 0)
+ return code;
+ }
+ code = gs_gsave(pgs);
+ if (code < 0)
+ return code;
+ code = gs_setgstate(pgs, pinst->saved);
+ if (code < 0) {
+ gs_grestore(pgs);
+ return code;
+ }
+ /* gx_set_device_only(pgs, (gx_device *) pdev); */
+ if (internal_accum) {
+ gs_setdevice_no_init(pgs, (gx_device *)pdev);
+ if (pinst->templat.uses_transparency) {
+ if_debug0m('v', imemory, " pushing the pdf14 compositor device into this graphics state\n");
+ if ((code = gs_push_pdf14trans_device(pgs, true)) < 0)
+ return code;
+ } else { /* not transparent */
+ if (pinst->templat.PaintType == 1 && !(pinst->is_clist))
+ if ((code = gx_erase_colored_pattern(pgs)) < 0)
+ return code;
+ }
+ } else {
+ gs_matrix m;
+ gs_rect bbox;
+ gs_fixed_rect clip_box;
+
+ dev_proc(pgs->device, get_initial_matrix)(pgs->device, &m);
+ gs_setmatrix(igs, &m);
+ code = gs_bbox_transform(&pinst->templat.BBox, &ctm_only(pgs), &bbox);
+ if (code < 0) {
+ gs_grestore(pgs);
+ return code;
+ }
+ clip_box.p.x = float2fixed(bbox.p.x);
+ clip_box.p.y = float2fixed(bbox.p.y);
+ clip_box.q.x = float2fixed(bbox.q.x);
+ clip_box.q.y = float2fixed(bbox.q.y);
+ code = gx_clip_to_rectangle(igs, &clip_box);
+ if (code < 0) {
+ gs_grestore(pgs);
+ return code;
+ }
+
+ {
+ pattern_accum_param_s param;
+ param.pinst = (void *)pinst;
+ param.graphics_state = (void *)pgs;
+ param.pinst_id = pinst->id;
+
+ code = (*dev_proc(pgs->device, dev_spec_op))((gx_device *)pgs->device,
+ gxdso_pattern_start_accum, &param, sizeof(pattern_accum_param_s));
+ }
+
+ if (code < 0) {
+ gs_grestore(pgs);
+ return code;
+ }
+ }
+ push_mark_estack(es_other, pattern_paint_cleanup);
+ ++esp;
+ make_istruct(esp, 0, pdev);
+ ++esp;
+ /* Save operator stack depth in case PaintProc leaves junk on ostack. */
+ make_int(esp, ref_stack_count(&o_stack));
+ push_op_estack(pattern_paint_finish);
+ dict_find_string(pdict, "PaintProc", &ppp); /* can't fail */
+ *++esp = *ppp;
+ *++esp = *pdict; /* (push on ostack) */
+ return o_push_estack;
+}
+/* Save the rendered pattern. */
+static int
+pattern_paint_finish(i_ctx_t *i_ctx_p)
+{
+ int o_stack_adjust = ref_stack_count(&o_stack) - esp->value.intval;
+ gx_device_forward *pdev = r_ptr(esp - 1, gx_device_forward);
+ gs_pattern1_instance_t *pinst =
+ (gs_pattern1_instance_t *)gs_currentcolor(igs->saved)->pattern;
+ gx_device_pattern_accum const *padev = (const gx_device_pattern_accum *) pdev;
+
+ if (pdev != NULL) {
+ gx_color_tile *ctile;
+ int code;
+ gs_state *pgs = igs;
+
+ if (pinst->templat.uses_transparency) {
+ if (pinst->is_clist) {
+ /* Send the compositor command to close the PDF14 device */
+ code = (gs_pop_pdf14trans_device(pgs, true) < 0);
+ if (code < 0)
+ return code;
+ } else {
+ /* Not a clist, get PDF14 buffer information */
+ code = pdf14_get_buffer_information(pgs->device,
+ padev->transbuff, pgs->memory,
+ true);
+ /* PDF14 device (and buffer) is destroyed when pattern cache
+ entry is removed */
+ if (code < 0)
+ return code;
+ }
+ }
+ code = gx_pattern_cache_add_entry((gs_imager_state *)igs, pdev, &ctile);
+ if (code < 0)
+ return code;
+ }
+ if (o_stack_adjust > 0) {
+#if 0
+ dmlprintf1(imemory, "PaintProc left %d extra on operator stack!\n", o_stack_adjust);
+#endif
+ pop(o_stack_adjust);
+ }
+ esp -= 3;
+ pattern_paint_cleanup(i_ctx_p);
+ return o_pop_estack;
+}
+/* Clean up after rendering a pattern. Note that iff the rendering */
+/* succeeded, closing the accumulator won't free the bits. */
+static int
+pattern_paint_cleanup(i_ctx_t *i_ctx_p)
+{
+ gx_device_pattern_accum *const pdev =
+ r_ptr(esp + 2, gx_device_pattern_accum);
+ gs_pattern1_instance_t *pinst = (gs_pattern1_instance_t *)gs_currentcolor(igs->saved)->pattern;
+ int code;
+
+ if (pdev != NULL) {
+ /* grestore will free the device, so close it first. */
+ (*dev_proc(pdev, close_device)) ((gx_device *) pdev);
+ }
+ if (pdev == NULL) {
+ gx_device *cdev = gs_currentdevice_inline(igs);
+ pattern_accum_param_s param;
+
+ param.pinst = (void *)pinst;
+ param.graphics_state = (void *)igs;
+ param.pinst_id = pinst->id;
+
+ code = dev_proc(cdev, dev_spec_op)(cdev,
+ gxdso_pattern_finish_accum, &param, sizeof(pattern_accum_param_s));
+ }
+ code = gs_grestore(igs);
+ gx_unset_dev_color(igs); /* dev_color may need updating if GC ran */
+ return code;
+}
diff --git a/psi/zpdf_r6.c b/psi/zpdf_r6.c
new file mode 100644
index 000000000..5f209c712
--- /dev/null
+++ b/psi/zpdf_r6.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "dstack.h" /* for systemdict */
+#include "estack.h"
+#include "ialloc.h"
+#include "iutil.h"
+#include "idict.h"
+#include "iname.h"
+#include "string_.h" /* memcmp() */
+#include "store.h"
+#include "aes.h"
+#include "sha2.h"
+
+/* Implementation of the PDF security handler revision6 (PDF 1.7 ExtensionLevel 8 algorithm)
+ *
+ * Adobe/ISO has not yet released the details, so the algorithm reference is:
+ * http://esec-lab.sogeti.com/post/The-undocumented-password-validation-algorithm-of-Adobe-Reader-X
+ *
+ * The code below is the same as (and copied from) the MuPDF implementation.
+ */
+
+static void
+pdf_compute_hardened_hash_r6(unsigned char *password, int pwlen, unsigned char salt[16], unsigned char *ownerkey, unsigned char hash[32])
+{
+ unsigned char data[(128 + 64 + 48) * 64];
+ unsigned char block[64];
+ int block_size = 32;
+ int data_len = 0;
+ int i, j, sum;
+
+ SHA256_CTX sha256;
+ SHA384_CTX sha384;
+ SHA512_CTX sha512;
+ aes_context aes;
+
+ pSHA256_Init(&sha256);
+ pSHA256_Update(&sha256, password, pwlen);
+ pSHA256_Update(&sha256, salt, 8);
+ if (ownerkey)
+ pSHA256_Update(&sha256, ownerkey, 48);
+ pSHA256_Final((uint8_t *)block, &sha256);
+
+ for (i = 0; i < 64 || i < data[data_len * 64 - 1] + 32; i++)
+ {
+ /* Step 2: repeat password and data block 64 times */
+ memcpy(data, password, pwlen);
+ memcpy(data + pwlen, block, block_size);
+ if (ownerkey)
+ memcpy(data + pwlen + block_size, ownerkey, 48);
+ data_len = pwlen + block_size + (ownerkey ? 48 : 0);
+ for (j = 1; j < 64; j++)
+ memcpy(data + j * data_len, data, data_len);
+
+ /* Step 3: encrypt data using data block as key and iv */
+ aes_setkey_enc(&aes, block, 128);
+ aes_crypt_cbc(&aes, AES_ENCRYPT, data_len * 64, block + 16, data, data);
+
+ /* Step 4: determine SHA-2 hash size for this round */
+ for (j = 0, sum = 0; j < 16; j++)
+ sum += data[j];
+
+ /* Step 5: calculate data block for next round */
+ block_size = 32 + (sum % 3) * 16;
+ switch (block_size)
+ {
+ case 32:
+ pSHA256_Init(&sha256);
+ pSHA256_Update(&sha256, data, data_len * 64);
+ pSHA256_Final((uint8_t *)block, &sha256);
+ break;
+ case 48:
+ pSHA384_Init(&sha384);
+ pSHA384_Update(&sha384, data, data_len * 64);
+ pSHA384_Final((uint8_t *)block, &sha384);
+ break;
+ case 64:
+ pSHA512_Init(&sha512);
+ pSHA512_Update(&sha512, data, data_len * 64);
+ pSHA512_Final((uint8_t *)block, &sha512);
+ break;
+ }
+ }
+
+ memset(data, 0, sizeof(data));
+ memcpy(hash, block, 32);
+}
+
+static void
+pdf_compute_encryption_key_r6(unsigned char *password, int pwlen, unsigned char *O, unsigned char *OE, unsigned char *U, unsigned char *UE, int ownerkey, unsigned char *validationkey, unsigned char *output)
+{
+ unsigned char hash[32];
+ unsigned char iv[16];
+ aes_context aes;
+
+ if (pwlen > 127)
+ pwlen = 127;
+
+ pdf_compute_hardened_hash_r6(password, pwlen,
+ (ownerkey ? O : U) + 32,
+ ownerkey ? U : NULL, validationkey);
+ pdf_compute_hardened_hash_r6(password, pwlen,
+ U + 40, NULL, hash);
+
+ memset(iv, 0, sizeof(iv));
+ aes_setkey_dec(&aes, hash, 256);
+ aes_crypt_cbc(&aes, AES_DECRYPT, 32, iv,
+ ownerkey ? OE : UE, output);
+}
+
+/* (password) <encryption dict> check_r6_password (key) true|false */
+static int
+zcheck_r6_password(i_ctx_t * i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *CryptDict, *Oref, *OEref, *Uref, *UEref, *Pref;
+ int code, PWlen;
+ unsigned char validation[32];
+ unsigned char output[32];
+ ref stref;
+ byte *body;
+
+ check_op(2);
+
+ CryptDict = op--;
+ Pref = op;
+ if (!r_has_type(CryptDict, t_dictionary))
+ return_error(gs_error_typecheck);
+ if (!r_has_type(Pref, t_string))
+ return_error(gs_error_typecheck);
+
+ code = dict_find_string(CryptDict, "O", &Oref);
+ if (code < 0 || !r_has_type(Oref, t_string)) {
+ return_error(gs_error_typecheck);
+ }
+ code = dict_find_string(CryptDict, "OE", &OEref);
+ if (code < 0 || !r_has_type(OEref, t_string)) {
+ return_error(gs_error_typecheck);
+ }
+ code = dict_find_string(CryptDict, "U", &Uref);
+ if (code < 0 || !r_has_type(Uref, t_string)) {
+ return_error(gs_error_typecheck);
+ }
+ code = dict_find_string(CryptDict, "UE", &UEref);
+ if (code < 0 || !r_has_type(UEref, t_string)) {
+ return_error(gs_error_typecheck);
+ }
+
+ pop(2);
+ op = osp;
+
+ PWlen = r_size(Pref);
+
+ /* First, try the password as the user password */
+ pdf_compute_encryption_key_r6((unsigned char *)Pref->value.const_bytes, PWlen, (unsigned char *)Oref->value.const_bytes,
+ (unsigned char *)OEref->value.const_bytes, (unsigned char *)Uref->value.const_bytes, (unsigned char *)UEref->value.const_bytes, 0, validation, output);
+
+ if (memcmp(validation, Uref->value.const_bytes, 32) != 0){
+ /* It wasn't the user password, maybe its the owner password */
+ pdf_compute_encryption_key_r6((unsigned char *)Pref->value.const_bytes, PWlen, (unsigned char *)Oref->value.const_bytes,
+ (unsigned char *)OEref->value.const_bytes, (unsigned char *)Uref->value.const_bytes, (unsigned char *)UEref->value.const_bytes, 1, validation, output);
+
+ if (memcmp(validation, Oref->value.const_bytes, 32) != 0){
+ /* Doesn't seem to be a valid password.... */
+ push(1);
+ make_bool(op, 0);
+ return 0;
+ }
+ }
+
+ body = ialloc_string(32, "r6 encryption key");
+ if (body == 0)
+ return_error(gs_error_VMerror);
+ push(1);
+ memcpy(body, output, 32);
+ make_string(&stref, a_all | icurrent_space, 32, body);
+ ref_assign(op, &stref);
+ push(1);
+ make_bool(op, 1);
+
+ return 0;
+}
+
+const op_def zpdf_r6_op_defs[] =
+{
+ { "2check_r6_password", zcheck_r6_password },
+ op_def_end(0)
+};
diff --git a/psi/zpdfops.c b/psi/zpdfops.c
new file mode 100644
index 000000000..59a4ea3fb
--- /dev/null
+++ b/psi/zpdfops.c
@@ -0,0 +1,205 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Custom operators for PDF interpreter */
+
+#include "ghost.h"
+#include "oper.h"
+#include "igstate.h"
+#include "istack.h"
+#include "iutil.h"
+#include "gspath.h"
+#include "math_.h"
+#include "ialloc.h"
+#include "malloc_.h"
+#include "string_.h"
+#include "store.h"
+
+#ifdef HAVE_LIBIDN
+# include <stringprep.h>
+#endif
+
+/* Construct a smooth path passing though a number of points on the stack */
+/* for PDF ink annotations. The program is based on a very simple method of */
+/* smoothing polygons by Maxim Shemanarev. */
+/* http://www.antigrain.com/research/bezier_interpolation/ */
+/* <mark> <x0> <y0> ... <xn> <yn> .pdfinkpath - */
+static int
+zpdfinkpath(i_ctx_t *i_ctx_p)
+{
+ os_ptr optr, op = osp;
+ uint count = ref_stack_counttomark(&o_stack);
+
+ uint i, ocount;
+ int code;
+ double x0, y0, x1, y1, x2, y2, x3, y3, xc1, yc1, xc2, yc2, xc3, yc3;
+ double len1, len2, len3, k1, k2, xm1, ym1, xm2, ym2;
+ double ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y;
+ const double smooth_value = 1; /* from 0..1 range */
+
+ if (count == 0)
+ return_error(gs_error_unmatchedmark);
+ if ((count & 1) == 0 || count < 3)
+ return_error(gs_error_rangecheck);
+
+ ocount = count - 1;
+ optr = op - ocount + 1;
+
+ if ((code = real_param(optr, &x1)) < 0)
+ return code;
+ if ((code = real_param(optr + 1, &y1)) < 0)
+ return code;
+ if ((code = gs_moveto(igs, x1, y1)) < 0)
+ return code;
+ if (ocount == 2)
+ goto pop;
+
+ if ((code = real_param(optr + 2, &x2)) < 0)
+ return code;
+ if ((code = real_param(optr + 3, &y2)) < 0)
+ return code;
+ if (ocount == 4) {
+ if((code = gs_lineto(igs, x2, y2)) < 0)
+ return code;
+ goto pop;
+ }
+ x0 = 2*x1 - x2;
+ y0 = 2*y1 - y2;
+
+ for (i = 4; i <= ocount; i += 2) {
+ if (i < ocount) {
+ if ((code = real_param(optr + i, &x3)) < 0)
+ return code;
+ if ((code = real_param(optr + i + 1, &y3)) < 0)
+ return code;
+ } else {
+ x3 = 2*x2 - x1;
+ y3 = 2*y2 - y1;
+ }
+
+ xc1 = (x0 + x1) / 2.0;
+ yc1 = (y0 + y1) / 2.0;
+ xc2 = (x1 + x2) / 2.0;
+ yc2 = (y1 + y2) / 2.0;
+ xc3 = (x2 + x3) / 2.0;
+ yc3 = (y2 + y3) / 2.0;
+
+ len1 = hypot(x1 - x0, y1 - y0);
+ len2 = hypot(x2 - x1, y2 - y1);
+ len3 = hypot(x3 - x2, y3 - y2);
+
+ k1 = len1 / (len1 + len2);
+ k2 = len2 / (len2 + len3);
+
+ xm1 = xc1 + (xc2 - xc1) * k1;
+ ym1 = yc1 + (yc2 - yc1) * k1;
+
+ xm2 = xc2 + (xc3 - xc2) * k2;
+ ym2 = yc2 + (yc3 - yc2) * k2;
+
+ ctrl1_x = xm1 + (xc2 - xm1) * smooth_value + x1 - xm1;
+ ctrl1_y = ym1 + (yc2 - ym1) * smooth_value + y1 - ym1;
+
+ ctrl2_x = xm2 + (xc2 - xm2) * smooth_value + x2 - xm2;
+ ctrl2_y = ym2 + (yc2 - ym2) * smooth_value + y2 - ym2;
+
+ code = gs_curveto(igs, ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x2, y2);
+ if (code < 0)
+ return code;
+ x0 = x1, x1 = x2, x2 = x3;
+ y0 = y1, y1 = y2, y2 = y3;
+ }
+ pop:
+ ref_stack_pop(&o_stack, count);
+ return 0;
+}
+
+#ifdef HAVE_LIBIDN
+/* Given a UTF-8 password string, convert it to the canonical form
+ * defined by SASLprep (RFC 4013). This is a permissive implementation,
+ * suitable for verifying existing passwords but not for creating new
+ * ones -- if you want to create a new password, you'll need to add a
+ * strict mode that returns stringprep errors to the user, and uses the
+ * STRINGPREP_NO_UNASSIGNED flag to disallow unassigned characters.
+ * <string> .saslprep <string> */
+static int
+zsaslprep(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint input_size = r_size(op);
+ byte *buffer;
+ uint buffer_size;
+ uint output_size;
+ Stringprep_rc err;
+
+ check_read_type(*op, t_string);
+
+ /* According to http://unicode.org/faq/normalization.html, converting
+ * a UTF-8 string to normalization form KC has a worst-case expansion
+ * factor of 11, so we allocate 11 times the length of the string plus
+ * 1 for the NUL terminator. If somehow that's still not big enough,
+ * stringprep will return STRINGPREP_TOO_SMALL_BUFFER; there's no
+ * danger of corrupting memory. */
+ buffer_size = input_size * 11 + 1;
+ buffer = ialloc_string(buffer_size, "saslprep result");
+ if (buffer == 0)
+ return_error(gs_error_VMerror);
+
+ memcpy(buffer, op->value.bytes, input_size);
+ buffer[input_size] = '\0';
+
+ err = stringprep((char *)buffer, buffer_size, 0, stringprep_saslprep);
+ if (err != STRINGPREP_OK) {
+ ifree_string(buffer, buffer_size, "saslprep result");
+
+ /* Since we're just verifying the password to an existing
+ * document here, we don't care about "invalid input" errors
+ * like STRINGPREP_CONTAINS_PROHIBITED. In these cases, we
+ * ignore the error and return the original string unchanged --
+ * chances are it's not the right password anyway, and if it
+ * is we shouldn't gratuitously fail to decrypt the document.
+ *
+ * On the other hand, errors like STRINGPREP_NFKC_FAILED are
+ * real errors, and should be returned to the user.
+ *
+ * Fortunately, the stringprep error codes are sorted to make
+ * this easy: the errors we want to ignore are the ones with
+ * codes less than 100. */
+ if ((int)err < 100)
+ return 0;
+
+ return_error(gs_error_ioerror);
+ }
+
+ output_size = strlen((char *)buffer);
+ buffer = iresize_string(buffer, buffer_size, output_size,
+ "saslprep result"); /* can't fail */
+ make_string(op, a_all | icurrent_space, output_size, buffer);
+
+ return 0;
+}
+#endif
+
+/* ------ Initialization procedure ------ */
+
+const op_def zpdfops_op_defs[] =
+{
+ {"0.pdfinkpath", zpdfinkpath},
+#ifdef HAVE_LIBIDN
+ {"1.saslprep", zsaslprep},
+#endif
+ op_def_end(0)
+};
diff --git a/psi/zrelbit.c b/psi/zrelbit.c
new file mode 100644
index 000000000..5b9c16980
--- /dev/null
+++ b/psi/zrelbit.c
@@ -0,0 +1,374 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Relational, boolean, and bit operators */
+#include "ghost.h"
+#include "oper.h"
+#include "gsutil.h"
+#include "idict.h"
+#include "store.h"
+#include "gsstate.h"
+
+/*
+ * Many of the procedures in this file are public only so they can be
+ * called from the FunctionType 4 interpreter (zfunc4.c).
+ */
+
+/* ------ Standard operators ------ */
+
+/* Define the type test for eq and its relatives. */
+#define EQ_CHECK_READ(opp, dflt)\
+ switch ( r_type(opp) ) {\
+ case t_string:\
+ check_read(*(opp));\
+ break;\
+ default:\
+ dflt;\
+ }
+
+/* Forward references */
+static int obj_le(os_ptr, os_ptr);
+
+/* <obj1> <obj2> eq <bool> */
+int
+zeq(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ EQ_CHECK_READ(op - 1, check_op(2));
+ EQ_CHECK_READ(op, DO_NOTHING);
+ make_bool(op - 1, (obj_eq(imemory, op - 1, op) ? 1 : 0));
+ pop(1);
+ return 0;
+}
+
+/* <obj1> <obj2> ne <bool> */
+int
+zne(i_ctx_t *i_ctx_p)
+{ /* We'll just be lazy and use eq. */
+ int code = zeq(i_ctx_p);
+
+ if (!code)
+ osp->value.boolval ^= 1;
+ return code;
+}
+
+/* <num1> <num2> ge <bool> */
+/* <str1> <str2> ge <bool> */
+int
+zge(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = obj_le(op, op - 1);
+
+ if (code < 0)
+ return code;
+ make_bool(op - 1, code);
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> gt <bool> */
+/* <str1> <str2> gt <bool> */
+int
+zgt(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = obj_le(op - 1, op);
+
+ if (code < 0)
+ return code;
+ make_bool(op - 1, code ^ 1);
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> le <bool> */
+/* <str1> <str2> le <bool> */
+int
+zle(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = obj_le(op - 1, op);
+
+ if (code < 0)
+ return code;
+ make_bool(op - 1, code);
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> lt <bool> */
+/* <str1> <str2> lt <bool> */
+int
+zlt(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = obj_le(op, op - 1);
+
+ if (code < 0)
+ return code;
+ make_bool(op - 1, code ^ 1);
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> .max <num> */
+/* <str1> <str2> .max <str> */
+static int
+zmax(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = obj_le(op - 1, op);
+
+ if (code < 0)
+ return code;
+ if (code) {
+ ref_assign(op - 1, op);
+ }
+ pop(1);
+ return 0;
+}
+
+/* <num1> <num2> .min <num> */
+/* <str1> <str2> .min <str> */
+static int
+zmin(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = obj_le(op - 1, op);
+
+ if (code < 0)
+ return code;
+ if (!code) {
+ ref_assign(op - 1, op);
+ }
+ pop(1);
+ return 0;
+}
+
+/* <bool1> <bool2> and <bool> */
+/* <int1> <int2> and <int> */
+int
+zand(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ case t_boolean:
+ check_type(op[-1], t_boolean);
+ op[-1].value.boolval &= op->value.boolval;
+ break;
+ case t_integer:
+ check_type(op[-1], t_integer);
+ op[-1].value.intval &= op->value.intval;
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ pop(1);
+ return 0;
+}
+
+/* <bool> not <bool> */
+/* <int> not <int> */
+int
+znot(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ case t_boolean:
+ op->value.boolval = !op->value.boolval;
+ break;
+ case t_integer:
+ op->value.intval = ~op->value.intval;
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ return 0;
+}
+
+/* <bool1> <bool2> or <bool> */
+/* <int1> <int2> or <int> */
+int
+zor(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ case t_boolean:
+ check_type(op[-1], t_boolean);
+ op[-1].value.boolval |= op->value.boolval;
+ break;
+ case t_integer:
+ check_type(op[-1], t_integer);
+ op[-1].value.intval |= op->value.intval;
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ pop(1);
+ return 0;
+}
+
+/* <bool1> <bool2> xor <bool> */
+/* <int1> <int2> xor <int> */
+int
+zxor(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ case t_boolean:
+ check_type(op[-1], t_boolean);
+ op[-1].value.boolval ^= op->value.boolval;
+ break;
+ case t_integer:
+ check_type(op[-1], t_integer);
+ op[-1].value.intval ^= op->value.intval;
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ pop(1);
+ return 0;
+}
+
+/* <int> <shift> bitshift <int> */
+int
+zbitshift(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int shift;
+ short max_shift = (sizeof(ps_int) * 8) - 1;
+ short max_shift32 = (sizeof(ps_int32) * 8) - 1;
+
+ check_type(*op, t_integer);
+ check_type(op[-1], t_integer);
+ if ((op->value.intval < -max_shift) || (op->value.intval > max_shift))
+ op[-1].value.intval = 0;
+ else if (sizeof(ps_int) != 4 && gs_currentcpsimode(imemory) && (op->value.intval < -max_shift32 || op->value.intval > max_shift32))
+ op[-1].value.intval = 0;
+ else if ((shift = op->value.intval) < 0) {
+ if (sizeof(ps_int) != 4 && gs_currentcpsimode(imemory)) {
+ ps_int32 val = (ps_int32)(op[-1].value.intval);
+ op[-1].value.intval = (ps_int)((uint)(val)) >> -shift;
+ }
+ else {
+ op[-1].value.intval = ((ps_int)(op[-1].value.intval)) >> -shift;
+ }
+ }
+ else {
+ if (sizeof(ps_int) != 4 && gs_currentcpsimode(imemory)) {
+ ps_int32 val = (ps_int32)(op[-1].value.intval);
+
+ op[-1].value.intval = (ps_int)(val << shift);
+ }
+ else
+ op[-1].value.intval <<= shift;
+ }
+ pop(1);
+ return 0;
+}
+
+/* ------ Extensions ------ */
+
+/* <obj1> <obj2> .identeq <bool> */
+static int
+zidenteq(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ EQ_CHECK_READ(op - 1, check_op(2));
+ EQ_CHECK_READ(op, DO_NOTHING);
+ make_bool(op - 1, (obj_ident_eq(imemory, op - 1, op) ? 1 : 0));
+ pop(1);
+ return 0;
+
+}
+
+/* <obj1> <obj2> .identne <bool> */
+static int
+zidentne(i_ctx_t *i_ctx_p)
+{
+ /* We'll just be lazy and use .identeq. */
+ int code = zidenteq(i_ctx_p);
+
+ if (!code)
+ osp->value.boolval ^= 1;
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zrelbit_op_defs[] =
+{
+ {"2and", zand},
+ {"2bitshift", zbitshift},
+ {"2eq", zeq},
+ {"2ge", zge},
+ {"2gt", zgt},
+ {"2le", zle},
+ {"2lt", zlt},
+ {"2.max", zmax},
+ {"2.min", zmin},
+ {"2ne", zne},
+ {"1not", znot},
+ {"2or", zor},
+ {"2xor", zxor},
+ /* Extensions */
+ {"2.identeq", zidenteq},
+ {"2.identne", zidentne},
+ op_def_end(0)
+};
+
+/* ------ Internal routines ------ */
+
+/* Compare two operands (both numeric, or both strings). */
+/* Return 1 if op[-1] <= op[0], 0 if op[-1] > op[0], */
+/* or a (negative) error code. */
+static int
+obj_le(register os_ptr op1, register os_ptr op)
+{
+ switch (r_type(op1)) {
+ case t_integer:
+ switch (r_type(op)) {
+ case t_integer:
+ return (op1->value.intval <= op->value.intval);
+ case t_real:
+ return ((double)op1->value.intval <= op->value.realval);
+ default:
+ return_op_typecheck(op);
+ }
+ case t_real:
+ switch (r_type(op)) {
+ case t_real:
+ return (op1->value.realval <= op->value.realval);
+ case t_integer:
+ return (op1->value.realval <= (double)op->value.intval);
+ default:
+ return_op_typecheck(op);
+ }
+ case t_string:
+ check_read(*op1);
+ check_read_type(*op, t_string);
+ return (bytes_compare(op1->value.bytes, r_size(op1),
+ op->value.bytes, r_size(op)) <= 0);
+ default:
+ return_op_typecheck(op1);
+ }
+}
diff --git a/psi/zrop.c b/psi/zrop.c
new file mode 100644
index 000000000..916883d20
--- /dev/null
+++ b/psi/zrop.c
@@ -0,0 +1,112 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* RasterOp control operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gsrop.h"
+#include "gsutil.h"
+#include "gxdevice.h"
+#include "idict.h"
+#include "idparam.h"
+#include "igstate.h"
+#include "store.h"
+
+/* <int8> .setrasterop - */
+static int
+zsetrasterop(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int param;
+ int code = int_param(op, 0xff, &param);
+
+ if (code < 0)
+ return code;
+ gs_setrasterop(igs, (gs_rop3_t)param);
+ pop(1);
+ return 0;
+}
+
+/* - .currentrasterop <int8> */
+static int
+zcurrentrasterop(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, (int)gs_currentrasterop(igs));
+ return 0;
+}
+
+/* <bool> .setsourcetransparent - */
+static int
+zsetsourcetransparent(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_boolean);
+ gs_setsourcetransparent(igs, op->value.boolval);
+ pop(1);
+ return 0;
+}
+
+/* - .currentsourcetransparent <bool> */
+static int
+zcurrentsourcetransparent(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_bool(op, gs_currentsourcetransparent(igs));
+ return 0;
+}
+
+/* <bool> .settexturetransparent - */
+static int
+zsettexturetransparent(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_boolean);
+ gs_settexturetransparent(igs, op->value.boolval);
+ pop(1);
+ return 0;
+}
+
+/* - .currenttexturetransparent <bool> */
+static int
+zcurrenttexturetransparent(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_bool(op, gs_currenttexturetransparent(igs));
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zrop_op_defs[] =
+{
+ {"0.currentrasterop", zcurrentrasterop},
+ {"0.currentsourcetransparent", zcurrentsourcetransparent},
+ {"0.currenttexturetransparent", zcurrenttexturetransparent},
+ {"1.setrasterop", zsetrasterop},
+ {"1.setsourcetransparent", zsetsourcetransparent},
+ {"1.settexturetransparent", zsettexturetransparent},
+ op_def_end(0)
+};
diff --git a/psi/zshade.c b/psi/zshade.c
new file mode 100644
index 000000000..b9088c271
--- /dev/null
+++ b/psi/zshade.c
@@ -0,0 +1,700 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* PostScript language interface to shading */
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gscolor3.h"
+#include "gscspace.h"
+#include "gscolor2.h" /* requires gscspace.h */
+#include "gsfunc3.h"
+#include "gsptype2.h"
+#include "gsstruct.h" /* must precede gsshade.h */
+#include "gsshade.h"
+#include "gsuid.h"
+#include "gscie.h"
+#include "stream.h" /* for files.h */
+#include "files.h"
+#include "ialloc.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifunc.h"
+#include "igstate.h"
+#include "ipcolor.h"
+#include "store.h"
+
+/* Forward references */
+static int shading_param(const_os_ptr op, const gs_shading_t ** ppsh);
+
+/* ---------------- Standard operators ---------------- */
+
+/* - currentsmoothness <smoothness> */
+static int
+zcurrentsmoothness(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_real(op, gs_currentsmoothness(igs));
+ return 0;
+}
+
+/* <smoothness> setsmoothness - */
+static int
+zsetsmoothness(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ double smoothness;
+ int code;
+
+ if (real_param(op, &smoothness) < 0)
+ return_op_typecheck(op);
+ if ((code = gs_setsmoothness(igs, smoothness)) < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* <shading> .shfill - */
+static int
+zshfill(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const gs_shading_t *psh;
+ int code = shading_param(op, &psh);
+
+ if (code < 0 || (code = gs_shfill(igs, psh)) < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* ------ Non-standard operators ------ */
+
+/* <pattern> <matrix> <shading> .buildshadingpattern <pattern> <instance> */
+static int
+zbuildshadingpattern(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op2 = op - 2;
+ gs_matrix mat;
+ gs_pattern2_template_t templat;
+ int_pattern *pdata;
+ gs_client_color cc_instance;
+ int code;
+
+ check_type(*op2, t_dictionary);
+ check_dict_read(*op2);
+ gs_pattern2_init(&templat);
+ if ((code = read_matrix(imemory, op - 1, &mat)) < 0 ||
+ (code = dict_uid_param(op2, &templat.uid, 1, imemory, i_ctx_p)) != 1 ||
+ (code = shading_param(op, &templat.Shading)) < 0 ||
+ (code = int_pattern_alloc(&pdata, op2, imemory)) < 0
+ )
+ return_error((code < 0 ? code : gs_error_rangecheck));
+ templat.client_data = pdata;
+ code = gs_make_pattern(&cc_instance,
+ (const gs_pattern_template_t *)&templat,
+ &mat, igs, imemory);
+ if (code < 0) {
+ ifree_object(pdata, "int_pattern");
+ return code;
+ }
+ make_istruct(op - 1, a_readonly, cc_instance.pattern);
+ pop(1);
+ return code;
+}
+
+/* ------ Internal procedures ------ */
+
+/* Get a shading parameter. */
+static int
+shading_param(const_os_ptr op, const gs_shading_t ** ppsh)
+{ /*
+ * Since shadings form a subclass hierarchy, we currently have
+ * no way to check whether a structure is actually a shading.
+ */
+ if (!r_is_struct(op) ||
+ r_has_masked_attrs(op, a_executable | a_execute, a_all)
+ )
+ return_error(gs_error_typecheck);
+ *ppsh = (gs_shading_t *) op->value.pstruct;
+ return 0;
+}
+
+/* ---------------- Shading dictionaries ---------------- */
+
+/* ------ Common code ------ */
+
+extern_st(st_color_space);
+
+typedef int (*build_shading_proc_t)
+ (i_ctx_t *i_ctx_p, const ref *op, const gs_shading_params_t *params,
+ gs_shading_t **ppsh, gs_memory_t *mem);
+
+/* Operators */
+
+/* Common framework for building shadings. */
+static int
+build_shading(i_ctx_t *i_ctx_p, build_shading_proc_t proc)
+{
+ os_ptr op = osp;
+ int code;
+ float box[4];
+ gs_shading_params_t params;
+ gs_shading_t *psh;
+ ref *pvalue;
+
+ check_type(*op, t_dictionary);
+ params.ColorSpace = 0;
+ params.cie_joint_caches = 0;
+ params.Background = 0;
+ /* Collect parameters common to all shading types. */
+ {
+ gs_color_space *pcs = gs_currentcolorspace(igs);
+ int num_comp = gs_color_space_num_components(pcs);
+
+ if (num_comp < 0) { /* Pattern color space */
+ gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "ColorSpace");
+ return_error(gs_error_typecheck);
+ }
+ params.ColorSpace = pcs;
+ rc_increment_cs(pcs);
+ if (dict_find_string(op, "Background", &pvalue) > 0) {
+ gs_client_color *pcc =
+ ialloc_struct(gs_client_color, &st_client_color,
+ "build_shading");
+
+ if (pcc == 0) {
+ code = gs_note_error(gs_error_VMerror);
+ goto fail;
+ }
+ pcc->pattern = 0;
+ params.Background = pcc;
+ code = dict_floats_param(imemory, op, "Background",
+ gs_color_space_num_components(pcs),
+ pcc->paint.values, NULL);
+ if (code < 0) {
+ gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "Background");
+ goto fail;
+ }
+ }
+ }
+ if (dict_find_string(op, "BBox", &pvalue) <= 0)
+ params.have_BBox = false;
+ else if ((code = dict_floats_param(imemory, op, "BBox",
+ 4, box, NULL)) == 4) {
+ /* Adobe Interpreters accept denormalised BBox - bug 688937 */
+ if (box[0] <= box[2]) {
+ params.BBox.p.x = box[0];
+ params.BBox.q.x = box[2];
+ } else {
+ params.BBox.p.x = box[2];
+ params.BBox.q.x = box[0];
+ }
+ if (box[1] <= box[3]) {
+ params.BBox.p.y = box[1];
+ params.BBox.q.y = box[3];
+ } else {
+ params.BBox.p.y = box[3];
+ params.BBox.q.y = box[1];
+ }
+ params.have_BBox = true;
+ } else {
+ gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "BBox");
+ goto fail;
+ }
+ code = dict_bool_param(op, "AntiAlias", false, &params.AntiAlias);
+ if (code < 0) {
+ gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "AntiAlias");
+ goto fail;
+ }
+ /* Finish building the shading. */
+ code = (*proc)(i_ctx_p, op, &params, &psh, imemory);
+ if (code < 0)
+ goto fail;
+ if (gx_color_space_needs_cie_caches(psh->params.ColorSpace)) {
+ rc_decrement(psh->params.cie_joint_caches, "build_shading");
+ psh->params.cie_joint_caches = gx_currentciecaches(igs);
+ rc_increment(psh->params.cie_joint_caches);
+ }
+ make_istruct_new(op, 0, psh);
+ return code;
+fail:
+ gs_free_object(imemory, params.Background, "Background");
+ if (params.ColorSpace) {
+ rc_decrement_only_cs(params.ColorSpace, "build_shading");
+ }
+ return (code < 0 ? code : gs_note_error(gs_error_rangecheck));
+}
+
+/* Collect a Function value. */
+static int
+build_shading_function(i_ctx_t *i_ctx_p, const ref * op, gs_function_t ** ppfn,
+ int num_inputs, gs_memory_t *mem, const float *shading_domain)
+{
+ ref *pFunction;
+ int code;
+
+ *ppfn = 0;
+ if (dict_find_string(op, "Function", &pFunction) <= 0)
+ return 0;
+ if (r_is_array(pFunction)) {
+ uint size = r_size(pFunction);
+ gs_function_t **Functions;
+ uint i;
+ gs_function_AdOt_params_t params;
+
+ check_read(*pFunction);
+ if (size == 0)
+ return_error(gs_error_rangecheck);
+ code = alloc_function_array(size, &Functions, mem);
+ if (code < 0)
+ return code;
+ for (i = 0; i < size; ++i) {
+ ref rsubfn;
+
+ array_get(imemory, pFunction, (long)i, &rsubfn);
+ code = fn_build_function(i_ctx_p, &rsubfn, &Functions[i], mem,
+ shading_domain, num_inputs);
+ if (code < 0)
+ break;
+ }
+ params.m = num_inputs;
+ params.Domain = 0;
+ params.n = size;
+ params.Range = 0;
+ params.Functions = (const gs_function_t * const *)Functions;
+ if (code >= 0)
+ code = gs_function_AdOt_init(ppfn, &params, mem);
+ if (code < 0)
+ gs_function_AdOt_free_params(&params, mem);
+ } else {
+ code = fn_build_function(i_ctx_p, pFunction, ppfn, mem,
+ shading_domain, num_inputs);
+ if (code < 0)
+ return code;
+ if ((*ppfn)->params.m != num_inputs) {
+ gs_function_free(*ppfn, true, mem);
+ return_error(gs_error_rangecheck);
+ }
+ }
+ return code;
+}
+
+/* According to PLRM 3rd ed, p. 264 "indexed color space is not
+ * allowed in any shading whose color values are generated by a function;
+ * this applies to any shading dictionary that contains a Function entry."
+ * Adobe interpreters follow PLRM in this respect and we follow them.
+ */
+static int
+check_indexed_vs_function(i_ctx_t *i_ctx_p, const ref *op,
+ const gs_color_space *pcs, const gs_function_t *funct)
+{ if (funct && gs_color_space_get_index(pcs) == gs_color_space_index_Indexed) {
+ static const char fn[] = "Function";
+ ref *f;
+ if (dict_find_string(op, fn, &f) > 0)
+ gs_errorinfo_put_pair(i_ctx_p, fn, sizeof(fn) - 1, f);
+ return_error(gs_error_typecheck); /* CET 12-14a */
+ }
+ return 0;
+}
+
+/* ------ Build shadings ------ */
+
+/* Build a ShadingType 1 (Function-based) shading. */
+static int
+build_shading_1(i_ctx_t *i_ctx_p, const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh, gs_memory_t *mem)
+{
+ gs_shading_Fb_params_t params;
+ int code;
+ static const float default_Domain[4] = {0, 1, 0, 1};
+ ref *pmatrix;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ gs_make_identity(&params.Matrix);
+ params.Function = 0;
+ code = dict_floats_param_errorinfo(i_ctx_p, op, "Domain",
+ 4, params.Domain, default_Domain);
+ if (code < 0)
+ goto out;
+ if (params.Domain[0] > params.Domain[1] || params.Domain[2] > params.Domain[3]) {
+ gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "Domain");
+ code = gs_note_error(gs_error_rangecheck);
+ goto out; /* CPSI 3017 and CET 12-14b reject un-normalised domain */
+ }
+ if (dict_find_string(op, "Matrix", &pmatrix) > 0 ) {
+ code = read_matrix(imemory, pmatrix, &params.Matrix);
+ if (code < 0) {
+ gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "Matrix");
+ goto out;
+ }
+ }
+ code = build_shading_function(i_ctx_p, op, &params.Function, 2, mem, params.Domain);
+ if (code < 0)
+ goto out;
+ if (params.Function == 0) { /* Function is required */
+ code = gs_note_error(gs_error_undefined);
+ gs_errorinfo_put_pair_from_dict(i_ctx_p, op, "Function");
+ goto out;
+ }
+ code = check_indexed_vs_function(i_ctx_p, op, params.ColorSpace, params.Function);
+ if (code < 0)
+ goto out;
+ code = gs_shading_Fb_init(ppsh, &params, mem);
+ out:;
+ if (code < 0 && params.Function)
+ gs_free_object(mem, params.Function, "Function");
+ return code;
+}
+/* <dict> .buildshading1 <shading_struct> */
+static int
+zbuildshading1(i_ctx_t *i_ctx_p)
+{
+ return build_shading(i_ctx_p, build_shading_1);
+}
+
+/* Collect parameters for an Axial or Radial shading. */
+static int
+build_directional_shading(i_ctx_t *i_ctx_p, const ref * op, float *Coords, int num_Coords,
+ float Domain[2], gs_function_t ** pFunction,
+ bool Extend[2], gs_memory_t *mem)
+{
+ int code = dict_floats_param(imemory, op, "Coords",
+ num_Coords, Coords, NULL);
+ static const float default_Domain[2] = {0, 1};
+ ref *pExtend;
+
+ *pFunction = 0;
+ if (code < 0 ||
+ (code = dict_floats_param_errorinfo(i_ctx_p, op, "Domain", 2, Domain,
+ default_Domain)) < 0 ||
+ (code = build_shading_function(i_ctx_p, op, pFunction, 1, mem, Domain)) < 0
+ )
+ return code;
+ if (!*pFunction)
+ return_error(gs_error_undefined);
+ if (dict_find_string(op, "Extend", &pExtend) <= 0)
+ Extend[0] = Extend[1] = false;
+ else {
+ ref E0, E1;
+
+ if (!r_is_array(pExtend))
+ return_error(gs_error_typecheck);
+ else if (r_size(pExtend) != 2)
+ return_error(gs_error_rangecheck);
+ else if ((array_get(imemory, pExtend, 0L, &E0),
+ !r_has_type(&E0, t_boolean)) ||
+ (array_get(imemory, pExtend, 1L, &E1),
+ !r_has_type(&E1, t_boolean))
+ )
+ return_error(gs_error_typecheck);
+ Extend[0] = E0.value.boolval, Extend[1] = E1.value.boolval;
+ }
+ return 0;
+}
+
+/* Build a ShadingType 2 (Axial) shading. */
+static int
+build_shading_2(i_ctx_t *i_ctx_p, const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh, gs_memory_t *mem)
+{
+ gs_shading_A_params_t params;
+ int code;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ if ((code = build_directional_shading(i_ctx_p, op, params.Coords, 4,
+ params.Domain, &params.Function,
+ params.Extend, mem)) < 0 ||
+ (code = check_indexed_vs_function(i_ctx_p, op, params.ColorSpace, params.Function)) < 0 ||
+ (code = gs_shading_A_init(ppsh, &params, mem)) < 0
+ ) {
+ gs_free_object(mem, params.Function, "Function");
+ }
+ return code;
+}
+/* <dict> .buildshading2 <shading_struct> */
+static int
+zbuildshading2(i_ctx_t *i_ctx_p)
+{
+ return build_shading(i_ctx_p, build_shading_2);
+}
+
+/* Build a ShadingType 3 (Radial) shading. */
+static int
+build_shading_3(i_ctx_t *i_ctx_p, const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh, gs_memory_t *mem)
+{
+ gs_shading_R_params_t params;
+ int code;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ if ((code = build_directional_shading(i_ctx_p, op, params.Coords, 6,
+ params.Domain, &params.Function,
+ params.Extend, mem)) < 0 ||
+ (code = check_indexed_vs_function(i_ctx_p, op, params.ColorSpace, params.Function)) < 0 ||
+ (code = gs_shading_R_init(ppsh, &params, mem)) < 0
+ ) {
+ gs_free_object(mem, params.Function, "Function");
+ }
+ if (params.Function == 0) /* Function is required */
+ return_error(gs_error_undefined);
+ return code;
+}
+/* <dict> .buildshading3 <shading_struct> */
+static int
+zbuildshading3(i_ctx_t *i_ctx_p)
+{
+ return build_shading(i_ctx_p, build_shading_3);
+}
+
+/* Collect parameters for a mesh shading. */
+static int
+build_mesh_shading(i_ctx_t *i_ctx_p, const ref * op,
+ gs_shading_mesh_params_t * params,
+ float **pDecode, gs_function_t ** pFunction,
+ gs_memory_t *mem)
+{
+ int code;
+ float *data = 0;
+ ref *pDataSource;
+
+ *pDecode = 0;
+ *pFunction = 0;
+ if (dict_find_string(op, "DataSource", &pDataSource) <= 0)
+ return_error(gs_error_rangecheck);
+ if (r_is_array(pDataSource)) {
+ uint size = r_size(pDataSource);
+
+ data = (float *)gs_alloc_byte_array(mem, size, sizeof(float),
+ "build_mesh_shading");
+ if (data == 0)
+ return_error(gs_error_VMerror);
+ code = process_float_array(mem, pDataSource, size, data);
+ if (code < 0) {
+ gs_free_object(mem, data, "build_mesh_shading");
+ return code;
+ }
+ data_source_init_floats(&params->DataSource, data, size);
+ } else
+ switch (r_type(pDataSource)) {
+ case t_file: {
+ stream *s;
+
+ check_read_file(i_ctx_p, s, pDataSource);
+ data_source_init_stream(&params->DataSource, s);
+ break;
+ }
+ case t_string:
+ check_read(*pDataSource);
+ data_source_init_string2(&params->DataSource,
+ pDataSource->value.bytes,
+ r_size(pDataSource));
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ code = build_shading_function(i_ctx_p, op, pFunction, 1, mem, NULL);
+ if (code < 0) {
+ gs_free_object(mem, data, "build_mesh_shading");
+ return code;
+ }
+ if (data_source_is_array(params->DataSource)) {
+ params->BitsPerCoordinate = 0;
+ params->BitsPerComponent = 0;
+ } else {
+ int num_decode = 4 +
+ (*pFunction != 0 ? 1 :
+ gs_color_space_num_components(params->ColorSpace)) * 2;
+
+ if ((code = dict_int_param(op, "BitsPerCoordinate", 1, 32, 0,
+ &params->BitsPerCoordinate)) >= 0 &&
+ (code = dict_int_param(op, "BitsPerComponent", 1, 16, 0,
+ &params->BitsPerComponent)) >= 0
+ ) {
+ *pDecode = (float *)
+ gs_alloc_byte_array(mem, num_decode, sizeof(float),
+ "build_mesh_shading");
+ if (*pDecode == 0)
+ code = gs_note_error(gs_error_VMerror);
+ else {
+ code = dict_floats_param(mem, op, "Decode", num_decode, *pDecode, NULL);
+ if (code < 0) {
+ gs_free_object(mem, *pDecode, "build_mesh_shading");
+ *pDecode = 0;
+ }
+ }
+ }
+ }
+ if (code < 0) {
+ if (*pFunction != 0) {
+ gs_function_free(*pFunction, true, mem);
+ *pFunction = 0;
+ }
+ gs_free_object(mem, data, "build_mesh_shading");
+ }
+ return code;
+}
+
+/* Collect the BitsPerFlag parameter, if relevant. */
+static int
+flag_bits_param(const ref * op, const gs_shading_mesh_params_t * params,
+ int *pBitsPerFlag)
+{
+ if (data_source_is_array(params->DataSource)) {
+ *pBitsPerFlag = 0;
+ return 0;
+ } else {
+ return dict_int_param(op, "BitsPerFlag", 2, 8, 0, pBitsPerFlag);
+ }
+}
+
+/* Build a ShadingType 4 (Free-form Gouraud triangle mesh) shading. */
+static int
+build_shading_4(i_ctx_t *i_ctx_p, const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh, gs_memory_t *mem)
+{
+ gs_shading_FfGt_params_t params;
+ int code;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ if ((code =
+ build_mesh_shading(i_ctx_p, op, (gs_shading_mesh_params_t *)&params,
+ &params.Decode, &params.Function, mem)) < 0 ||
+ (code = check_indexed_vs_function(i_ctx_p, op, params.ColorSpace, params.Function)) < 0 ||
+ (code = flag_bits_param(op, (gs_shading_mesh_params_t *)&params,
+ &params.BitsPerFlag)) < 0 ||
+ (code = gs_shading_FfGt_init(ppsh, &params, mem)) < 0
+ ) {
+ gs_free_object(mem, params.Function, "Function");
+ gs_free_object(mem, params.Decode, "Decode");
+ }
+ return code;
+}
+/* <dict> .buildshading4 <shading_struct> */
+static int
+zbuildshading4(i_ctx_t *i_ctx_p)
+{
+ return build_shading(i_ctx_p, build_shading_4);
+}
+
+/* Build a ShadingType 5 (Lattice-form Gouraud triangle mesh) shading. */
+static int
+build_shading_5(i_ctx_t *i_ctx_p, const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh, gs_memory_t *mem)
+{
+ gs_shading_LfGt_params_t params;
+ int code;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ if ((code =
+ build_mesh_shading(i_ctx_p, op, (gs_shading_mesh_params_t *)&params,
+ &params.Decode, &params.Function, mem)) < 0 ||
+ (code = check_indexed_vs_function(i_ctx_p, op, params.ColorSpace, params.Function)) < 0 ||
+ (code = dict_int_param(op, "VerticesPerRow", 2, max_int, 0,
+ &params.VerticesPerRow)) < 0 ||
+ (code = gs_shading_LfGt_init(ppsh, &params, mem)) < 0
+ ) {
+ gs_free_object(mem, params.Function, "Function");
+ gs_free_object(mem, params.Decode, "Decode");
+ }
+ return code;
+}
+/* <dict> .buildshading5 <shading_struct> */
+static int
+zbuildshading5(i_ctx_t *i_ctx_p)
+{
+ return build_shading(i_ctx_p, build_shading_5);
+}
+
+/* Build a ShadingType 6 (Coons patch mesh) shading. */
+static int
+build_shading_6(i_ctx_t *i_ctx_p, const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh, gs_memory_t *mem)
+{
+ gs_shading_Cp_params_t params;
+ int code;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ if ((code =
+ build_mesh_shading(i_ctx_p, op, (gs_shading_mesh_params_t *)&params,
+ &params.Decode, &params.Function, mem)) < 0 ||
+ (code = check_indexed_vs_function(i_ctx_p, op, params.ColorSpace, params.Function)) < 0 ||
+ (code = flag_bits_param(op, (gs_shading_mesh_params_t *)&params,
+ &params.BitsPerFlag)) < 0 ||
+ (code = gs_shading_Cp_init(ppsh, &params, mem)) < 0
+ ) {
+ gs_free_object(mem, params.Function, "Function");
+ gs_free_object(mem, params.Decode, "Decode");
+ }
+ return code;
+}
+/* <dict> .buildshading6 <shading_struct> */
+static int
+zbuildshading6(i_ctx_t *i_ctx_p)
+{
+ return build_shading(i_ctx_p, build_shading_6);
+}
+
+/* Build a ShadingType 7 (Tensor product patch mesh) shading. */
+static int
+build_shading_7(i_ctx_t *i_ctx_p, const ref * op, const gs_shading_params_t * pcommon,
+ gs_shading_t ** ppsh, gs_memory_t *mem)
+{
+ gs_shading_Tpp_params_t params;
+ int code;
+
+ *(gs_shading_params_t *)&params = *pcommon;
+ if ((code =
+ build_mesh_shading(i_ctx_p, op, (gs_shading_mesh_params_t *)&params,
+ &params.Decode, &params.Function, mem)) < 0 ||
+ (code = check_indexed_vs_function(i_ctx_p, op, params.ColorSpace, params.Function)) < 0 ||
+ (code = flag_bits_param(op, (gs_shading_mesh_params_t *)&params,
+ &params.BitsPerFlag)) < 0 ||
+ (code = gs_shading_Tpp_init(ppsh, &params, mem)) < 0
+ ) {
+ gs_free_object(mem, params.Function, "Function");
+ gs_free_object(mem, params.Decode, "Decode");
+ }
+ return code;
+}
+/* <dict> .buildshading7 <shading_struct> */
+static int
+zbuildshading7(i_ctx_t *i_ctx_p)
+{
+ return build_shading(i_ctx_p, build_shading_7);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zshade_op_defs[] =
+{
+ op_def_begin_ll3(),
+ {"0currentsmoothness", zcurrentsmoothness},
+ {"1setsmoothness", zsetsmoothness},
+ {"1.shfill", zshfill},
+ {"1.buildshading1", zbuildshading1},
+ {"1.buildshading2", zbuildshading2},
+ {"1.buildshading3", zbuildshading3},
+ {"1.buildshading4", zbuildshading4},
+ {"1.buildshading5", zbuildshading5},
+ {"1.buildshading6", zbuildshading6},
+ {"1.buildshading7", zbuildshading7},
+ {"3.buildshadingpattern", zbuildshadingpattern},
+ op_def_end(0)
+};
diff --git a/psi/zstack.c b/psi/zstack.c
new file mode 100644
index 000000000..3031cc42f
--- /dev/null
+++ b/psi/zstack.c
@@ -0,0 +1,316 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Operand stack operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "ialloc.h"
+#include "istack.h"
+#include "oper.h"
+#include "store.h"
+
+/* <obj> pop - */
+int
+zpop(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_op(1);
+ pop(1);
+ return 0;
+}
+
+/* <obj1> <obj2> exch <obj2> <obj1> */
+int
+zexch(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref next;
+
+ check_op(2);
+ ref_assign_inline(&next, op - 1);
+ ref_assign_inline(op - 1, op);
+ ref_assign_inline(op, &next);
+ return 0;
+}
+
+/* <obj> dup <obj> <obj> */
+int
+zdup(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_op(1);
+ push(1);
+ ref_assign_inline(op, op - 1);
+ return 0;
+}
+
+/* <obj_n> ... <obj_0> <n> index <obj_n> ... <obj_0> <obj_n> */
+int
+zindex(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ register os_ptr opn;
+
+ check_type(*op, t_integer);
+ if ((ulong)op->value.intval >= (ulong)(op - osbot)) {
+ /* Might be in an older stack block. */
+ ref *elt;
+
+ if (op->value.intval < 0)
+ return_error(gs_error_rangecheck);
+ elt = ref_stack_index(&o_stack, op->value.intval + 1);
+ if (elt == 0)
+ return_error(gs_error_stackunderflow);
+ ref_assign(op, elt);
+ return 0;
+ }
+ opn = op + ~(int)op->value.intval;
+ ref_assign_inline(op, opn);
+ return 0;
+}
+
+/* <obj_n> ... <obj_0> <n> .argindex <obj_n> ... <obj_0> <obj_n> */
+static int
+zargindex(i_ctx_t *i_ctx_p)
+{
+ int code = zindex(i_ctx_p);
+
+ /*
+ * Pseudo-operators should use .argindex rather than index to access
+ * their arguments on the stack, so that if there aren't enough, the
+ * result will be a stackunderflow rather than a rangecheck. (This is,
+ * in fact, the only reason this operator exists.)
+ */
+ if (code == gs_error_rangecheck && osp->value.intval >= 0)
+ code = gs_note_error(gs_error_stackunderflow);
+ return code;
+}
+
+/* <obj_n-1> ... <obj_0> <n> <i> roll */
+/* <obj_(i-1)_mod_ n> ... <obj_0> <obj_n-1> ... <obj_i_mod_n> */
+int
+zroll(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ int count, mod;
+ register os_ptr from, to;
+ register int n;
+
+ check_type(*op1, t_integer);
+ check_type(*op, t_integer);
+ if ((uint) op1->value.intval > (uint)(op1 - osbot)) {
+ /*
+ * The data might span multiple stack blocks.
+ * There are efficient ways to handle this situation,
+ * but they're more complicated than seems worth implementing;
+ * for now, do something very simple and inefficient.
+ */
+ int left, i;
+
+ if (op1->value.intval < 0)
+ return_error(gs_error_rangecheck);
+ if (op1->value.intval + 2 > (int)ref_stack_count(&o_stack))
+ return_error(gs_error_stackunderflow);
+ count = op1->value.intval;
+ if (count <= 1) {
+ pop(2);
+ return 0;
+ }
+ mod = op->value.intval;
+ if (mod >= count)
+ mod %= count;
+ else if (mod < 0) {
+ mod %= count;
+ if (mod < 0)
+ mod += count; /* can't assume % means mod! */
+ }
+ /* Use the chain rotation algorithm mentioned below. */
+ for (i = 0, left = count; left; i++) {
+ ref *elt = ref_stack_index(&o_stack, i + 2);
+ ref save;
+ int j, k;
+ ref *next;
+
+ save = *elt;
+ for (j = i, left--;; j = k, elt = next, left--) {
+ k = (j + mod) % count;
+ if (k == i)
+ break;
+ next = ref_stack_index(&o_stack, k + 2);
+ ref_assign(elt, next);
+ }
+ *elt = save;
+ }
+ pop(2);
+ return 0;
+ }
+ count = op1->value.intval;
+ if (count <= 1) {
+ pop(2);
+ return 0;
+ }
+ mod = op->value.intval;
+ /*
+ * The elegant approach, requiring no extra space, would be to
+ * rotate the elements in chains separated by mod elements.
+ * Instead, we simply check to make sure there is enough space
+ * above op to do the roll in two block moves.
+ * Unfortunately, we can't count on memcpy doing the right thing
+ * in *either* direction.
+ */
+ switch (mod) {
+ case 1: /* common special case */
+ pop(2);
+ op -= 2;
+ {
+ ref top;
+
+ ref_assign_inline(&top, op);
+ for (from = op, n = count; --n; from--)
+ ref_assign_inline(from, from - 1);
+ ref_assign_inline(from, &top);
+ }
+ return 0;
+ case -1: /* common special case */
+ pop(2);
+ op -= 2;
+ {
+ ref bot;
+
+ to = op - count + 1;
+ ref_assign_inline(&bot, to);
+ for (n = count; --n; to++)
+ ref_assign(to, to + 1);
+ ref_assign_inline(to, &bot);
+ }
+ return 0;
+ }
+ if (mod < 0) {
+ mod += count;
+ if (mod < 0) {
+ mod %= count;
+ if (mod < 0)
+ mod += count; /* can't assume % means mod! */
+ }
+ } else if (mod >= count)
+ mod %= count;
+ if (mod <= count >> 1) {
+ /* Move everything up, then top elements down. */
+ if (mod >= ostop - op) {
+ o_stack.requested = mod;
+ return_error(gs_error_stackoverflow);
+ }
+ pop(2);
+ op -= 2;
+ for (to = op + mod, from = op, n = count; n--; to--, from--)
+ ref_assign(to, from);
+ memcpy((char *)(from + 1), (char *)(op + 1), mod * sizeof(ref));
+ } else {
+ /* Move bottom elements up, then everything down. */
+ mod = count - mod;
+ if (mod >= ostop - op) {
+ o_stack.requested = mod;
+ return_error(gs_error_stackoverflow);
+ }
+ pop(2);
+ op -= 2;
+ to = op - count + 1;
+ memcpy((char *)(op + 1), (char *)to, mod * sizeof(ref));
+ for (from = to + mod, n = count; n--; to++, from++)
+ ref_assign(to, from);
+ }
+ return 0;
+}
+
+/* |- ... clear |- */
+/* The function name is changed, because the IRIS library has */
+/* a function called zclear. */
+static int
+zclear_stack(i_ctx_t *i_ctx_p)
+{
+ ref_stack_clear(&o_stack);
+ return 0;
+}
+
+/* |- <obj_n-1> ... <obj_0> count <obj_n-1> ... <obj_0> <n> */
+static int
+zcount(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_int(op, ref_stack_count(&o_stack) - 1);
+ return 0;
+}
+
+/* - mark <mark> */
+static int
+zmark(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_mark(op);
+ return 0;
+}
+
+/* <mark> ... cleartomark */
+int
+zcleartomark(i_ctx_t *i_ctx_p)
+{
+ uint count = ref_stack_counttomark(&o_stack);
+
+ if (count == 0)
+ return_error(gs_error_unmatchedmark);
+ ref_stack_pop(&o_stack, count);
+ return 0;
+}
+
+/* <mark> <obj_n-1> ... <obj_0> counttomark */
+/* <mark> <obj_n-1> ... <obj_0> <n> */
+static int
+zcounttomark(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint count = ref_stack_counttomark(&o_stack);
+
+ if (count == 0)
+ return_error(gs_error_unmatchedmark);
+ push(1);
+ make_int(op, count - 1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zstack_op_defs[] =
+{
+ {"2.argindex", zargindex},
+ {"0clear", zclear_stack},
+ {"0cleartomark", zcleartomark},
+ {"0count", zcount},
+ {"0counttomark", zcounttomark},
+ {"1dup", zdup},
+ {"2exch", zexch},
+ {"2index", zindex},
+ {"0mark", zmark},
+ {"1pop", zpop},
+ {"2roll", zroll},
+ op_def_end(0)
+};
diff --git a/psi/zstring.c b/psi/zstring.c
new file mode 100644
index 000000000..df9593907
--- /dev/null
+++ b/psi/zstring.c
@@ -0,0 +1,215 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* String operators */
+#include "memory_.h"
+#include "ghost.h"
+#include "gsutil.h"
+#include "ialloc.h"
+#include "iname.h"
+#include "ivmspace.h"
+#include "oper.h"
+#include "store.h"
+
+/* The generic operators (copy, get, put, getinterval, putinterval, */
+/* length, and forall) are implemented in zgeneric.c. */
+
+/* <int> .bytestring <bytestring> */
+static int
+zbytestring(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ byte *sbody;
+ uint size;
+
+ check_int_leu(*op, max_int);
+ size = (uint)op->value.intval;
+ sbody = ialloc_bytes(size, ".bytestring");
+ if (sbody == 0)
+ return_error(gs_error_VMerror);
+ make_astruct(op, a_all | icurrent_space, sbody);
+ memset(sbody, 0, size);
+ return 0;
+}
+
+/* <int> string <string> */
+int
+zstring(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ byte *sbody;
+ uint size;
+
+ check_type(*op, t_integer);
+ if (op->value.intval < 0 )
+ return_error(gs_error_rangecheck);
+ if (op->value.intval > max_string_size )
+ return_error(gs_error_limitcheck); /* to match Distiller */
+ size = op->value.intval;
+ sbody = ialloc_string(size, "string");
+ if (sbody == 0)
+ return_error(gs_error_VMerror);
+ make_string(op, a_all | icurrent_space, size, sbody);
+ memset(sbody, 0, size);
+ return 0;
+}
+
+/* <name> .namestring <string> */
+static int
+znamestring(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_name);
+ name_string_ref(imemory, op, op);
+ return 0;
+}
+
+/* <string> <pattern> anchorsearch <post> <match> -true- */
+/* <string> <pattern> anchorsearch <string> -false- */
+static int
+zanchorsearch(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ uint size = r_size(op);
+
+ check_read_type(*op, t_string);
+ check_read_type(*op1, t_string);
+ if (size <= r_size(op1) && !memcmp(op1->value.bytes, op->value.bytes, size)) {
+ os_ptr op0 = op;
+
+ push(1);
+ *op0 = *op1;
+ r_set_size(op0, size);
+ op1->value.bytes += size;
+ r_dec_size(op1, size);
+ make_true(op);
+ } else
+ make_false(op);
+ return 0;
+}
+
+/* <string> <pattern> search <post> <match> <pre> -true- */
+/* <string> <pattern> search <string> -false- */
+static int
+zsearch(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ uint size = r_size(op);
+ uint count;
+ byte *pat;
+ byte *ptr;
+ byte ch;
+
+ check_read_type(*op1, t_string);
+ check_read_type(*op, t_string);
+ if (size > r_size(op1)) { /* can't match */
+ make_false(op);
+ return 0;
+ }
+ count = r_size(op1) - size;
+ ptr = op1->value.bytes;
+ if (size == 0)
+ goto found;
+ pat = op->value.bytes;
+ ch = pat[0];
+ do {
+ if (*ptr == ch && (size == 1 || !memcmp(ptr, pat, size)))
+ goto found;
+ ptr++;
+ }
+ while (count--);
+ /* No match */
+ make_false(op);
+ return 0;
+found:
+ op->tas.type_attrs = op1->tas.type_attrs;
+ op->value.bytes = ptr;
+ r_set_size(op, size);
+ push(2);
+ op[-1] = *op1;
+ r_set_size(op - 1, ptr - op[-1].value.bytes);
+ op1->value.bytes = ptr + size;
+ r_set_size(op1, count);
+ make_true(op);
+ return 0;
+}
+
+/* <string> <charstring> .stringbreak <int|null> */
+static int
+zstringbreak(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint i, j;
+
+ check_read_type(op[-1], t_string);
+ check_read_type(*op, t_string);
+ /* We can't use strpbrk here, because C doesn't allow nulls in strings. */
+ for (i = 0; i < r_size(op - 1); ++i)
+ for (j = 0; j < r_size(op); ++j)
+ if (op[-1].value.const_bytes[i] == op->value.const_bytes[j]) {
+ make_int(op - 1, i);
+ goto done;
+ }
+ make_null(op - 1);
+ done:
+ pop(1);
+ return 0;
+}
+
+/* <obj> <pattern> .stringmatch <bool> */
+static int
+zstringmatch(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr op1 = op - 1;
+ bool result;
+
+ check_read_type(*op, t_string);
+ switch (r_type(op1)) {
+ case t_string:
+ check_read(*op1);
+ goto cmp;
+ case t_name:
+ name_string_ref(imemory, op1, op1); /* can't fail */
+cmp:
+ result = string_match(op1->value.const_bytes, r_size(op1),
+ op->value.const_bytes, r_size(op),
+ NULL);
+ break;
+ default:
+ result = (r_size(op) == 1 && *op->value.bytes == '*');
+ }
+ make_bool(op1, result);
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zstring_op_defs[] =
+{
+ {"1.bytestring", zbytestring},
+ {"2anchorsearch", zanchorsearch},
+ {"1.namestring", znamestring},
+ {"2search", zsearch},
+ {"1string", zstring},
+ {"2.stringbreak", zstringbreak},
+ {"2.stringmatch", zstringmatch},
+ op_def_end(0)
+};
diff --git a/psi/zsysvm.c b/psi/zsysvm.c
new file mode 100644
index 000000000..4db555579
--- /dev/null
+++ b/psi/zsysvm.c
@@ -0,0 +1,184 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* System VM and VM-specific operators */
+#include "ghost.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "ivmspace.h"
+#include "store.h" /* for make_bool */
+
+/*
+ * These operators allow creation of objects in a specific VM --
+ * local, global, or system. System VM, which is not a standard PostScript
+ * facility, is not subject to save and restore; objects in system VM
+ * may only refer to simple objects or to other (composite) objects
+ * in system VM.
+ */
+
+/* Execute an operator with a specific VM selected as current VM. */
+static int
+specific_vm_op(i_ctx_t *i_ctx_p, op_proc_t opproc, uint space)
+{
+ uint save_space = icurrent_space;
+ int code;
+
+ ialloc_set_space(idmemory, space);
+ code = opproc(i_ctx_p);
+ ialloc_set_space(idmemory, save_space);
+ return code;
+}
+
+/* <int> .globalvmarray <array> */
+static int
+zglobalvmarray(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zarray, avm_global);
+}
+
+/* <int> .globalvmdict <dict> */
+static int
+zglobalvmdict(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zdict, avm_global);
+}
+
+/* <obj_0> ... <obj_n-1> <n> .globalvmpackedarray <packedarray> */
+static int
+zglobalvmpackedarray(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zpackedarray, avm_global);
+}
+
+/* <int> .globalvmstring <string> */
+static int
+zglobalvmstring(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zstring, avm_global);
+}
+
+/* <int> .localvmarray <array> */
+static int
+zlocalvmarray(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zarray, avm_local);
+}
+
+/* <int> .localvmdict <dict> */
+static int
+zlocalvmdict(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zdict, avm_local);
+}
+
+/* <obj_0> ... <obj_n-1> <n> .localvmpackedarray <packedarray> */
+static int
+zlocalvmpackedarray(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zpackedarray, avm_local);
+}
+
+/* <int> .localvmstring <string> */
+static int
+zlocalvmstring(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zstring, avm_local);
+}
+
+/* <int> .systemvmarray <array> */
+static int
+zsystemvmarray(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zarray, avm_system);
+}
+
+/* <int> .systemvmdict <dict> */
+static int
+zsystemvmdict(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zdict, avm_system);
+}
+
+/* <obj_0> ... <obj_n-1> <n> .systemvmpackedarray <packedarray> */
+static int
+zsystemvmpackedarray(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zpackedarray, avm_system);
+}
+
+/* <int> .systemvmstring <string> */
+static int
+zsystemvmstring(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zstring, avm_system);
+}
+
+/* <name_string> <access_string> .systemvmfile <file> */
+static int
+zsystemvmfile(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zfile, avm_system);
+}
+
+/* <string> .systemvmlibfile <file> true */
+/* <string> .systemvmlibfile <string> false */
+static int
+zsystemvmlibfile(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zlibfile, avm_system);
+}
+
+/* <source> <EODcount> <EODstring> .systemvmSFD <file> */
+/* <source> <dict> <EODcount> <EODstring> .systemvmSFD <file> */
+/* <source> <dict> .systemvmSFD <file> *//* (LL3 only) */
+static int
+zsystemvmSFD(i_ctx_t *i_ctx_p)
+{
+ return specific_vm_op(i_ctx_p, zSFD, avm_system);
+}
+
+/* <any> .systemvmcheck <bool> */
+static int
+zsystemvmcheck(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ make_bool(op, (r_space(op) == avm_system ? true : false));
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zsysvm_op_defs[] =
+{
+ {"1.globalvmarray", zglobalvmarray},
+ {"1.globalvmdict", zglobalvmdict},
+ {"1.globalvmpackedarray", zglobalvmpackedarray},
+ {"1.globalvmstring", zglobalvmstring},
+ {"1.localvmarray", zlocalvmarray},
+ {"1.localvmdict", zlocalvmdict},
+ {"1.localvmpackedarray", zlocalvmpackedarray},
+ {"1.localvmstring", zlocalvmstring},
+ {"1.systemvmarray", zsystemvmarray},
+ {"1.systemvmcheck", zsystemvmcheck},
+ {"1.systemvmdict", zsystemvmdict},
+ {"1.systemvmpackedarray", zsystemvmpackedarray},
+ {"1.systemvmstring", zsystemvmstring},
+ {"1.systemvmfile", zsystemvmfile},
+ {"1.systemvmlibfile", zsystemvmlibfile},
+ {"2.systemvmSFD", zsystemvmSFD},
+ op_def_end(0)
+};
diff --git a/psi/ztoken.c b/psi/ztoken.c
new file mode 100644
index 000000000..4dba7c5bd
--- /dev/null
+++ b/psi/ztoken.c
@@ -0,0 +1,385 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Token reading operators */
+#include "string_.h"
+#include "stat_.h" /* get system header early to avoid name clash on Cygwin */
+#include "ghost.h"
+#include "oper.h"
+#include "dstack.h" /* for dict_find_name */
+#include "estack.h"
+#include "gsstruct.h" /* for iscan.h */
+#include "gsutil.h"
+#include "stream.h"
+#include "files.h"
+#include "store.h"
+#include "strimpl.h" /* for sfilter.h */
+#include "sfilter.h" /* for iscan.h */
+#include "idict.h"
+#include "iname.h"
+#include "iscan.h"
+#include "itoken.h" /* for prototypes */
+
+/* <file> token <obj> -true- */
+/* <string> token <post> <obj> -true- */
+/* <string|file> token -false- */
+static int ztoken_continue(i_ctx_t *);
+static int token_continue(i_ctx_t *, scanner_state *, bool);
+int
+ztoken(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ default:
+ return_op_typecheck(op);
+ case t_file: {
+ stream *s;
+ scanner_state state;
+
+ check_read_file(i_ctx_p, s, op);
+ check_ostack(1);
+ gs_scanner_init(&state, op);
+ return token_continue(i_ctx_p, &state, true);
+ }
+ case t_string: {
+ ref token;
+ /* -1 is to remove the string operand in case of error. */
+ int orig_ostack_depth = ref_stack_count(&o_stack) - 1;
+ int code;
+
+ /* Don't pop the operand in case of invalidaccess. */
+ if (!r_has_attr(op, a_read))
+ return_error(gs_error_invalidaccess);
+ code = gs_scan_string_token(i_ctx_p, op, &token);
+ switch (code) {
+ case scan_EOF: /* no tokens */
+ make_false(op);
+ return 0;
+ default:
+ if (code < 0) {
+ /*
+ * Clear anything that may have been left on the ostack,
+ * including the string operand.
+ */
+ if (orig_ostack_depth < ref_stack_count(&o_stack))
+ pop(ref_stack_count(&o_stack)- orig_ostack_depth);
+ return code;
+ }
+ }
+ push(2);
+ op[-1] = token;
+ make_true(op);
+ return 0;
+ }
+ }
+}
+/* Continue reading a token after an interrupt or callout. */
+/* *op is the scanner state. */
+static int
+ztoken_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ scanner_state *pstate;
+
+ check_stype(*op, st_scanner_state_dynamic);
+ pstate = r_ptr(op, scanner_state);
+ return token_continue(i_ctx_p, pstate, false);
+}
+/* Common code for token reading. */
+static int
+token_continue(i_ctx_t *i_ctx_p, scanner_state * pstate, bool save)
+{
+ os_ptr op = osp;
+ int code;
+ ref token;
+
+ /* Note that gs_scan_token may change osp! */
+ pop(1); /* remove the file or scanner state */
+again:
+ code = gs_scan_token(i_ctx_p, &token, pstate);
+ op = osp;
+ switch (code) {
+ default: /* error */
+ if (code > 0) /* comment, not possible */
+ code = gs_note_error(gs_error_syntaxerror);
+ gs_scanner_error_object(i_ctx_p, pstate, &i_ctx_p->error_object);
+ break;
+ case scan_BOS:
+ code = 0;
+ case 0: /* read a token */
+ push(2);
+ ref_assign(op - 1, &token);
+ make_true(op);
+ break;
+ case scan_EOF: /* no tokens */
+ push(1);
+ make_false(op);
+ code = 0;
+ break;
+ case scan_Refill: /* need more data */
+ code = gs_scan_handle_refill(i_ctx_p, pstate, save,
+ ztoken_continue);
+ switch (code) {
+ case 0: /* state is not copied to the heap */
+ goto again;
+ case o_push_estack:
+ return code;
+ }
+ break; /* error */
+ }
+ if (code <= 0 && !save) { /* Deallocate the scanner state record. */
+ ifree_object(pstate, "token_continue");
+ }
+ return code;
+}
+
+/* <file> .tokenexec - */
+/* Read a token and do what the interpreter would do with it. */
+/* This is different from token + exec because literal procedures */
+/* are not executed (although binary object sequences ARE executed). */
+int ztokenexec_continue(i_ctx_t *); /* export for interpreter */
+static int tokenexec_continue(i_ctx_t *, scanner_state *, bool);
+int
+ztokenexec(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ stream *s;
+ scanner_state state;
+
+ check_read_file(i_ctx_p, s, op);
+ check_estack(1);
+ gs_scanner_init(&state, op);
+ return tokenexec_continue(i_ctx_p, &state, true);
+}
+/* Continue reading a token for execution after an interrupt or callout. */
+/* *op is the scanner state. */
+/* We export this because this is how the interpreter handles a */
+/* scan_Refill for an executable file. */
+int
+ztokenexec_continue(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ scanner_state *pstate;
+
+ check_stype(*op, st_scanner_state_dynamic);
+ pstate = r_ptr(op, scanner_state);
+ return tokenexec_continue(i_ctx_p, pstate, false);
+}
+/* Common code for token reading + execution. */
+static int
+tokenexec_continue(i_ctx_t *i_ctx_p, scanner_state * pstate, bool save)
+{
+ os_ptr op;
+ int code;
+ /* Note that gs_scan_token may change osp! */
+ pop(1);
+again:
+ check_estack(1);
+ code = gs_scan_token(i_ctx_p, (ref *) (esp + 1), pstate);
+ op = osp;
+ switch (code) {
+ case 0:
+ if (r_is_proc(esp + 1)) { /* Treat procedure as a literal. */
+ push(1);
+ ref_assign(op, esp + 1);
+ code = 0;
+ break;
+ }
+ /* falls through */
+ case scan_BOS:
+ ++esp;
+ code = o_push_estack;
+ break;
+ case scan_EOF: /* no tokens */
+ code = 0;
+ break;
+ case scan_Refill: /* need more data */
+ code = gs_scan_handle_refill(i_ctx_p, pstate, save,
+ ztokenexec_continue);
+ switch (code) {
+ case 0: /* state is not copied to the heap */
+ goto again;
+ case o_push_estack:
+ return code;
+ }
+ break; /* error */
+ case scan_Comment:
+ case scan_DSC_Comment:
+ return ztoken_handle_comment(i_ctx_p, pstate, esp + 1, code,
+ save, true, ztokenexec_continue);
+ default: /* error */
+ gs_scanner_error_object(i_ctx_p, pstate, &i_ctx_p->error_object);
+ break;
+ }
+ if (!save) { /* Deallocate the scanner state record. */
+ gs_free_object(((scanner_state_dynamic *)pstate)->mem, pstate, "token_continue");
+ }
+ return code;
+}
+
+/*
+ * Handle a scan_Comment or scan_DSC_Comment return from gs_scan_token
+ * (scan_code) by calling out to %Process[DSC]Comment. The continuation
+ * procedure expects the scanner state on the o-stack.
+ */
+int
+ztoken_handle_comment(i_ctx_t *i_ctx_p, scanner_state *sstate,
+ const ref *ptoken, int scan_code,
+ bool save, bool push_file, op_proc_t cont)
+{
+ const char *proc_name;
+ scanner_state *pstate;
+ os_ptr op;
+ ref *ppcproc;
+ int code;
+
+ switch (scan_code) {
+ case scan_Comment:
+ proc_name = "%ProcessComment";
+ break;
+ case scan_DSC_Comment:
+ proc_name = "%ProcessDSCComment";
+ break;
+ default:
+ return_error(gs_error_Fatal); /* can't happen */
+ }
+ /*
+ * We can't use check_ostack here, because it returns on overflow.
+ */
+ /*check_ostack(2);*/
+ if (ostop - osp < 2) {
+ code = ref_stack_extend(&o_stack, 2);
+ if (code < 0)
+ return code;
+ }
+ check_estack(3);
+ code = name_enter_string(imemory, proc_name, esp + 3);
+ if (code < 0)
+ return code;
+ if (save) {
+ pstate = (scanner_state *)ialloc_struct(scanner_state_dynamic, &st_scanner_state_dynamic,
+ "ztoken_handle_comment");
+ if (pstate == 0)
+ return_error(gs_error_VMerror);
+ ((scanner_state_dynamic *)pstate)->mem = imemory;
+ *pstate = *sstate;
+ } else
+ pstate = sstate;
+ /* Save the token now -- it might be on the e-stack. */
+ if (!pstate->s_pstack)
+ osp[2] = *ptoken;
+ /*
+ * Push the continuation, scanner state, file, and callout procedure
+ * on the e-stack.
+ */
+ make_op_estack(esp + 1, cont);
+ make_istruct(esp + 2, 0, pstate);
+ ppcproc = dict_find_name(esp + 3);
+ if (ppcproc == 0) {
+ /*
+ * This can only happen during initialization.
+ * Pop the comment string from the o-stack if needed (see below).
+ */
+ if (pstate->s_pstack)
+ --osp;
+ esp += 2; /* do run the continuation */
+ } else {
+ /*
+ * Push the file and comment string on the o-stack.
+ * If we were inside { }, the comment string is already on the stack.
+ */
+ if (pstate->s_pstack) {
+ op = ++osp;
+ *op = op[-1];
+ } else {
+ op = osp += 2;
+ /* *op = *ptoken; */ /* saved above */
+ }
+ op[-1] = pstate->s_file;
+ esp[3] = *ppcproc;
+ esp += 3;
+ }
+ return o_push_estack;
+}
+
+typedef struct named_scanner_option_s {
+ const char *pname;
+ int option;
+} named_scanner_option_t;
+static const named_scanner_option_t named_options[] = {
+ {"PDFScanRules", SCAN_PDF_RULES},
+ {"ProcessComment", SCAN_PROCESS_COMMENTS},
+ {"ProcessDSCComment", SCAN_PROCESS_DSC_COMMENTS},
+ {"PDFScanInvNum", SCAN_PDF_INV_NUM},
+ {"PDFScanUnsigned", SCAN_PDF_UNSIGNED}
+};
+
+/*
+ * Update the cached scanner_options in the context state after doing a
+ * setuserparams. (We might move this procedure somewhere else eventually.)
+ */
+int
+ztoken_scanner_options(const ref *upref, int old_options)
+{
+ int options = old_options;
+ int i;
+
+ for (i = 0; i < countof(named_options); ++i) {
+ const named_scanner_option_t *pnso = &named_options[i];
+ ref *ppcproc;
+ int code = dict_find_string(upref, pnso->pname, &ppcproc);
+
+ /* Update the options only if the parameter has changed. */
+ if (code >= 0) {
+ if (r_has_type(ppcproc, t_null))
+ options &= ~pnso->option;
+ else
+ options |= pnso->option;
+ }
+ }
+ return options;
+}
+/*
+ * Get the value for a scanner option.
+ * return -1 if no such option, 1/0 for on/off and option's name in *pname as a C string
+ */
+int
+ztoken_get_scanner_option(const ref *psref, int options, const char **pname)
+{
+ const named_scanner_option_t *pnso;
+
+ for (pnso = named_options + countof(named_options); pnso-- != named_options;) {
+ if (!bytes_compare((const byte *)pnso->pname, strlen(pnso->pname),
+ psref->value.const_bytes, r_size(psref))) {
+ *pname = pnso->pname;
+ return (options & pnso->option ? 1 : 0);
+ }
+ }
+ return -1;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def ztoken_op_defs[] =
+{
+ {"1token", ztoken},
+ {"1.tokenexec", ztokenexec},
+ /* Internal operators */
+ {"2%ztoken_continue", ztoken_continue},
+ {"2%ztokenexec_continue", ztokenexec_continue},
+ op_def_end(0)
+};
diff --git a/psi/ztrans.c b/psi/ztrans.c
new file mode 100644
index 000000000..1133cc076
--- /dev/null
+++ b/psi/ztrans.c
@@ -0,0 +1,537 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Transparency operators */
+#include "string_.h"
+#include "memory_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gscspace.h" /* for gscolor2.h */
+#include "gscolor2.h"
+#include "gsipar3x.h"
+#include "gstrans.h"
+#include "gxiparam.h" /* for image enumerator */
+#include "gxcspace.h"
+#include "idict.h"
+#include "idparam.h"
+#include "ifunc.h"
+#include "igstate.h"
+#include "iimage.h"
+#include "iname.h"
+#include "store.h"
+#include "gsdfilt.h"
+#include "gdevdevn.h"
+#include "gxblend.h"
+#include "gdevp14.h"
+#include "gsicc_cms.h"
+
+/* ------ Utilities ------ */
+
+static int
+set_float_value(i_ctx_t *i_ctx_p, int (*set_value)(gs_state *, double))
+{
+ os_ptr op = osp;
+ double value;
+ int code;
+
+ if (real_param(op, &value) < 0)
+ return_op_typecheck(op);
+ if ((code = set_value(igs, value)) < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+static int
+current_float_value(i_ctx_t *i_ctx_p,
+ float (*current_value)(const gs_state *))
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_real(op, current_value(igs));
+ return 0;
+}
+
+static int
+enum_param(const gs_memory_t *mem, const ref *pnref,
+ const char *const names[])
+{
+ const char *const *p;
+ ref nsref;
+
+ name_string_ref(mem, pnref, &nsref);
+ for (p = names; *p; ++p)
+ if (r_size(&nsref) == strlen(*p) &&
+ !memcmp(*p, nsref.value.const_bytes, r_size(&nsref))
+ )
+ return p - names;
+ return_error(gs_error_rangecheck);
+}
+
+/* ------ Graphics state operators ------ */
+
+static const char *const blend_mode_names[] = {
+ GS_BLEND_MODE_NAMES, 0
+};
+
+/* <modename> .setblendmode - */
+static int
+zsetblendmode(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_type(*op, t_name);
+ if ((code = enum_param(imemory, op, blend_mode_names)) < 0 ||
+ (code = gs_setblendmode(igs, code)) < 0
+ )
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* - .currentblendmode <modename> */
+static int
+zcurrentblendmode(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ const char *mode_name = blend_mode_names[gs_currentblendmode(igs)];
+ ref nref;
+ int code = name_enter_string(imemory, mode_name, &nref);
+
+ if (code < 0)
+ return code;
+ push(1);
+ *op = nref;
+ return 0;
+}
+
+/* <0..1> .setopacityalpha - */
+static int
+zsetopacityalpha(i_ctx_t *i_ctx_p)
+{
+ return set_float_value(i_ctx_p, gs_setopacityalpha);
+}
+
+/* - .currentopacityalpha <0..1> */
+static int
+zcurrentopacityalpha(i_ctx_t *i_ctx_p)
+{
+ return current_float_value(i_ctx_p, gs_currentopacityalpha);
+}
+
+/* <0..1> .setshapealpha - */
+static int
+zsetshapealpha(i_ctx_t *i_ctx_p)
+{
+ return set_float_value(i_ctx_p, gs_setshapealpha);
+}
+
+/* - .currentshapealpha <0..1> */
+static int
+zcurrentshapealpha(i_ctx_t *i_ctx_p)
+{
+ return current_float_value(i_ctx_p, gs_currentshapealpha);
+}
+
+/* <bool> .settextknockout - */
+static int
+zsettextknockout(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_boolean);
+ gs_settextknockout(igs, op->value.boolval);
+ pop(1);
+ return 0;
+}
+
+/* - .currenttextknockout <bool> */
+static int
+zcurrenttextknockout(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_bool(op, gs_currenttextknockout(igs));
+ return 0;
+}
+
+/* ------ Rendering stack operators ------ */
+
+static int
+rect_param(gs_rect *prect, os_ptr op)
+{
+ double coords[4];
+ int code = num_params(op, 4, coords);
+
+ if (code < 0)
+ return code;
+ prect->p.x = coords[0], prect->p.y = coords[1];
+ prect->q.x = coords[2], prect->q.y = coords[3];
+ return 0;
+}
+
+static int
+mask_op(i_ctx_t *i_ctx_p,
+ int (*mask_proc)(gs_state *, gs_transparency_channel_selector_t))
+{
+ int csel;
+ int code = int_param(osp, 1, &csel);
+
+ if (code < 0)
+ return code;
+ code = mask_proc(igs, csel);
+ if (code >= 0)
+ pop(1);
+ return code;
+
+}
+
+/* <paramdict> <llx> <lly> <urx> <ury> .begintransparencygroup - */
+static int
+zbegintransparencygroup(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr dop = op - 4;
+ gs_transparency_group_params_t params;
+ gs_rect bbox;
+ ref *dummy;
+ int code;
+
+ check_type(*dop, t_dictionary);
+ check_dict_read(*dop);
+ gs_trans_group_params_init(&params);
+ if ((code = dict_bool_param(dop, "Isolated", false, &params.Isolated)) < 0 ||
+ (code = dict_bool_param(dop, "Knockout", false, &params.Knockout)) < 0 ||
+ (code = dict_bool_param(dop, ".image_with_SMask", false, &params.image_with_SMask)) < 0
+ )
+ return code;
+ code = rect_param(&bbox, op);
+ if (code < 0)
+ return code;
+ /* If the CS is not given in the transparency group dict, set to NULL */
+ /* so that the transparency code knows to inherit from the parent layer */
+ if (dict_find_string(dop, "CS", &dummy) <= 0) {
+ params.ColorSpace = NULL;
+ } else {
+ /* the PDF interpreter sets the colorspace, so use it */
+ params.ColorSpace = gs_currentcolorspace(igs);
+ /* Lets make sure that it is not an ICC color space that came from
+ a PS CIE color space or a PS color space. These are 1-way color
+ spaces and cannot be used for group color spaces */
+ if (gs_color_space_is_PSCIE(params.ColorSpace))
+ params.ColorSpace = NULL;
+ else if (gs_color_space_is_ICC(params.ColorSpace) &&
+ params.ColorSpace->cmm_icc_profile_data != NULL &&
+ params.ColorSpace->cmm_icc_profile_data->profile_handle != NULL) {
+ if (gscms_is_input(params.ColorSpace->cmm_icc_profile_data->profile_handle))
+ params.ColorSpace = NULL;
+ }
+ }
+ code = gs_begin_transparency_group(igs, &params, &bbox);
+ if (code < 0)
+ return code;
+ pop(5);
+ return code;
+}
+
+/* - .endtransparencygroup - */
+static int
+zendtransparencygroup(i_ctx_t *i_ctx_p)
+{
+ return gs_end_transparency_group(igs);
+}
+
+/* <cs_set?> <paramdict> <llx> <lly> <urx> <ury> .begintransparencymaskgroup - */
+/* cs_set == false if we are inheriting the colorspace */
+static int tf_using_function(double, float *, void *);
+static int
+zbegintransparencymaskgroup(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ os_ptr dop = op - 4;
+ gs_transparency_mask_params_t params;
+ ref *pparam;
+ gs_rect bbox;
+ int code;
+ static const char *const subtype_names[] = {
+ GS_TRANSPARENCY_MASK_SUBTYPE_NAMES, 0
+ };
+
+ check_type(*dop, t_dictionary);
+ check_dict_read(*dop);
+ if (dict_find_string(dop, "Subtype", &pparam) <= 0)
+ return_error(gs_error_rangecheck);
+ if ((code = enum_param(imemory, pparam, subtype_names)) < 0)
+ return code;
+ gs_trans_mask_params_init(&params, code);
+ params.replacing = true;
+ if ((code = dict_floats_param(imemory, dop, "Background",
+ cs_num_components(gs_currentcolorspace(i_ctx_p->pgs)),
+ params.Background, NULL)) < 0)
+ return code;
+ else if (code > 0)
+ params.Background_components = code;
+ if ((code = dict_floats_param(imemory, dop, "GrayBackground",
+ 1, &params.GrayBackground, NULL)) < 0)
+ return code;
+ if (dict_find_string(dop, "TransferFunction", &pparam) > 0) {
+ gs_function_t *pfn = ref_function(pparam);
+
+ if (pfn == 0 || pfn->params.m != 1 || pfn->params.n != 1)
+ return_error(gs_error_rangecheck);
+ params.TransferFunction = tf_using_function;
+ params.TransferFunction_data = pfn;
+ }
+ code = rect_param(&bbox, op);
+ if (code < 0)
+ return code;
+ /* Is the colorspace set for this mask ? */
+ if (op[-5].value.boolval) {
+ params.ColorSpace = gs_currentcolorspace(igs);
+ /* Lets make sure that it is not an ICC color space that came from
+ a PS CIE color space or a PS color space. These are 1-way color
+ spaces and cannot be used for group color spaces */
+ if (gs_color_space_is_PSCIE(params.ColorSpace))
+ params.ColorSpace = NULL;
+ else if (gs_color_space_is_ICC(params.ColorSpace) &&
+ params.ColorSpace->cmm_icc_profile_data != NULL &&
+ params.ColorSpace->cmm_icc_profile_data->profile_handle != NULL) {
+ if (gscms_is_input(params.ColorSpace->cmm_icc_profile_data->profile_handle))
+ params.ColorSpace = NULL;
+ }
+ } else {
+ params.ColorSpace = NULL;
+ }
+ code = gs_begin_transparency_mask(igs, &params, &bbox, false);
+ if (code < 0)
+ return code;
+ pop(6);
+ return code;
+}
+
+/* - .begintransparencymaskimage - */
+static int
+zbegintransparencymaskimage(i_ctx_t *i_ctx_p)
+{
+ gs_transparency_mask_params_t params;
+ gs_rect bbox = { { 0, 0} , { 1, 1} };
+ int code;
+ gs_color_space *gray_cs = gs_cspace_new_DeviceGray(imemory);
+
+ if (!gray_cs)
+ return_error(gs_error_VMerror);
+ gs_trans_mask_params_init(&params, TRANSPARENCY_MASK_Luminosity);
+ code = gs_begin_transparency_mask(igs, &params, &bbox, true);
+ if (code < 0)
+ return code;
+ rc_decrement_cs(gray_cs, "zbegintransparencymaskimage");
+ return code;
+}
+
+/* Implement the TransferFunction using a Function. */
+static int
+tf_using_function(double in_val, float *out, void *proc_data)
+{
+ float in = in_val;
+ gs_function_t *const pfn = proc_data;
+
+ return gs_function_evaluate(pfn, &in, out);
+}
+
+/* <mask#> .endtransparencymask - */
+static int
+zendtransparencymask(i_ctx_t *i_ctx_p)
+{
+ return mask_op(i_ctx_p, gs_end_transparency_mask);
+}
+
+/* ------ Soft-mask images ------ */
+
+/* <dict> .image3x - */
+static int mask_dict_param(const gs_memory_t *mem, os_ptr,
+ image_params *, const char *, int,
+ gs_image3x_mask_t *);
+static int
+zimage3x(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_image3x_t image;
+ ref *pDataDict;
+ image_params ip_data;
+ int num_components =
+ gs_color_space_num_components(gs_currentcolorspace(igs));
+ int ignored;
+ int code;
+
+ check_type(*op, t_dictionary);
+ check_dict_read(*op);
+ memset(&image, 0, sizeof(gs_image3x_t));
+ gs_image3x_t_init(&image, NULL);
+ if (dict_find_string(op, "DataDict", &pDataDict) <= 0)
+ return_error(gs_error_rangecheck);
+ if ((code = pixel_image_params(i_ctx_p, pDataDict,
+ (gs_pixel_image_t *)&image, &ip_data,
+ 16, false, gs_currentcolorspace(igs))) < 0 ||
+ (code = dict_int_param(pDataDict, "ImageType", 1, 1, 0, &ignored)) < 0
+ )
+ return code;
+ /*
+ * We have to process the masks in the reverse order, because they
+ * insert their DataSource before the one(s) for the DataDict.
+ */
+ if ((code = mask_dict_param(imemory, op, &ip_data,
+ "ShapeMaskDict", num_components,
+ &image.Shape)) < 0 ||
+ (code = mask_dict_param(imemory, op, &ip_data,
+ "OpacityMaskDict", num_components,
+ &image.Opacity)) < 0
+ )
+ return code;
+ return zimage_setup(i_ctx_p, (gs_pixel_image_t *)&image,
+ &ip_data.DataSource[0],
+ image.CombineWithColor, 1);
+}
+
+/* Get one soft-mask dictionary parameter. */
+static int
+mask_dict_param(const gs_memory_t *mem, os_ptr op,
+image_params *pip_data, const char *dict_name,
+ int num_components, gs_image3x_mask_t *pixm)
+{
+ ref *pMaskDict;
+ image_params ip_mask;
+ int ignored;
+ int code, mcode;
+
+ if (dict_find_string(op, dict_name, &pMaskDict) <= 0)
+ return 1;
+ if ((mcode = code = data_image_params(mem, pMaskDict, &pixm->MaskDict,
+ &ip_mask, false, 1, 16, false, false)) < 0 ||
+ (code = dict_int_param(pMaskDict, "ImageType", 1, 1, 0, &ignored)) < 0 ||
+ (code = dict_int_param(pMaskDict, "InterleaveType", 1, 3, -1,
+ &pixm->InterleaveType)) < 0 ||
+ (code = dict_floats_param(mem, op, "Matte", num_components,
+ pixm->Matte, NULL)) < 0
+ )
+ return code;
+ pixm->has_Matte = code > 0;
+ /*
+ * The MaskDict must have a DataSource iff InterleaveType == 3.
+ */
+ if ((pip_data->MultipleDataSources && pixm->InterleaveType != 3) ||
+ ip_mask.MultipleDataSources ||
+ mcode != (pixm->InterleaveType != 3)
+ )
+ return_error(gs_error_rangecheck);
+ if (pixm->InterleaveType == 3) {
+ /* Insert the mask DataSource before the data DataSources. */
+ memmove(&pip_data->DataSource[1], &pip_data->DataSource[0],
+ (countof(pip_data->DataSource) - 1) *
+ sizeof(pip_data->DataSource[0]));
+ pip_data->DataSource[0] = ip_mask.DataSource[0];
+ }
+ return 0;
+}
+
+/* depth .pushpdf14devicefilter - */
+/* this is a filter operator, but we include it here to maintain
+ modularity of the pdf14 transparency support */
+static int
+zpushpdf14devicefilter(i_ctx_t *i_ctx_p)
+{
+ int code;
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ code = gs_push_pdf14trans_device(igs, false);
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* this is a filter operator, but we include it here to maintain
+ modularity of the pdf14 transparency support */
+static int
+zpoppdf14devicefilter(i_ctx_t *i_ctx_p)
+{
+ return gs_pop_pdf14trans_device(igs, false);
+}
+
+/* Something has gone terribly wrong */
+static int
+zabortpdf14devicefilter(i_ctx_t *i_ctx_p)
+{
+ return gs_abort_pdf14trans_device(igs);
+}
+
+/* This is used to communicate to the transparency compositor
+ when a q (save extended graphic state) occurs. Since
+ the softmask is part of the graphic state we need to know
+ this to handle clist processing properly */
+
+static int
+zpushextendedgstate(i_ctx_t *i_ctx_p)
+{
+ int code;
+ code = gs_push_transparency_state(igs);
+ return(code);
+}
+
+/* This is used to communicate to the transparency compositor
+ when a Q (restore extended graphic state) occurs. Since
+ the softmask is part of the graphic state we need to know
+ this to handle clist processing properly */
+
+static int
+zpopextendedgstate(i_ctx_t *i_ctx_p)
+{
+ int code;
+ code = gs_pop_transparency_state(igs, false);
+ return(code);
+}
+
+/* ------ Initialization procedure ------ */
+
+/* We need to split the table because of the 16-element limit. */
+const op_def ztrans1_op_defs[] = {
+ {"1.setblendmode", zsetblendmode},
+ {"0.currentblendmode", zcurrentblendmode},
+ {"1.setopacityalpha", zsetopacityalpha},
+ {"0.currentopacityalpha", zcurrentopacityalpha},
+ {"1.setshapealpha", zsetshapealpha},
+ {"0.currentshapealpha", zcurrentshapealpha},
+ {"1.settextknockout", zsettextknockout},
+ {"0.currenttextknockout", zcurrenttextknockout},
+ {"0.pushextendedgstate", zpushextendedgstate},
+ {"0.popextendedgstate", zpopextendedgstate},
+ op_def_end(0)
+};
+const op_def ztrans2_op_defs[] = {
+ {"5.begintransparencygroup", zbegintransparencygroup},
+ {"0.endtransparencygroup", zendtransparencygroup},
+ {"5.begintransparencymaskgroup", zbegintransparencymaskgroup},
+ {"5.begintransparencymaskimage", zbegintransparencymaskimage},
+ {"1.endtransparencymask", zendtransparencymask},
+ {"1.image3x", zimage3x},
+ {"1.pushpdf14devicefilter", zpushpdf14devicefilter},
+ {"0.poppdf14devicefilter", zpoppdf14devicefilter},
+ {"0.abortpdf14devicefilter", zabortpdf14devicefilter},
+ op_def_end(0)
+};
diff --git a/psi/ztrap.c b/psi/ztrap.c
new file mode 100644
index 000000000..314b74d71
--- /dev/null
+++ b/psi/ztrap.c
@@ -0,0 +1,64 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Operators for setting trapping parameters and zones */
+#include "ghost.h"
+#include "oper.h"
+#include "ialloc.h"
+#include "iparam.h"
+#include "gstrap.h"
+
+/* Define the current trap parameters. */
+/****** THIS IS BOGUS ******/
+gs_trap_params_t i_trap_params;
+
+/* <dict> .settrapparams - */
+static int
+zsettrapparams(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ dict_param_list list;
+ int code;
+
+ check_type(*op, t_dictionary);
+ code = dict_param_list_read(&list, op, NULL, false, iimemory);
+ if (code < 0)
+ return code;
+ code = gs_settrapparams(&i_trap_params, (gs_param_list *) & list);
+ iparam_list_release(&list);
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* - settrapzone - */
+static int
+zsettrapzone(i_ctx_t *i_ctx_p)
+{
+/****** NYI ******/
+ return_error(gs_error_undefined);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def ztrap_op_defs[] =
+{
+ op_def_begin_ll3(),
+ {"1.settrapparams", zsettrapparams},
+ {"0settrapzone", zsettrapzone},
+ op_def_end(0)
+};
diff --git a/psi/ztype.c b/psi/ztype.c
new file mode 100644
index 000000000..6a646322d
--- /dev/null
+++ b/psi/ztype.c
@@ -0,0 +1,528 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Type, attribute, and conversion operators */
+#include "math_.h"
+#include "memory_.h"
+#include "string_.h"
+#include "gsexit.h"
+#include "ghost.h"
+#include "oper.h"
+#include "imemory.h" /* for register_ref_root */
+#include "idict.h"
+#include "iname.h"
+#include "stream.h" /* for iscan.h */
+#include "strimpl.h" /* for sfilter.h for picky compilers */
+#include "sfilter.h" /* ditto */
+#include "iscan.h"
+#include "iutil.h"
+#include "dstack.h" /* for dict_set_top */
+#include "store.h"
+
+/*
+ * Some of the procedures in this file are public only so they can be
+ * called from the FunctionType 4 interpreter (zfunc4.c).
+ */
+
+/* Forward references */
+static int access_check(i_ctx_t *, int, bool);
+static int convert_to_string(const gs_memory_t *mem, os_ptr, os_ptr);
+
+/*
+ * Max and min integer values expressed as reals.
+ * Note that these are biased by 1 to allow for truncation.
+ * They should be #defines rather than static consts, but
+ * several of the SCO Unix compilers apparently can't handle this.
+ * On the other hand, the DEC compiler can't handle casts in
+ * constant expressions, so we can't use min_long and max_long.
+ * What a nuisance!
+ */
+#define ALT_MIN_INT (((ps_int)-1) << ((sizeof(ps_int) * 8) - 1))
+#define ALT_MAX_INT (~(ALT_MIN_INT))
+static const double min_int_real = (ALT_MIN_INT * 1.0 - 1);
+static const double max_int_real = (ALT_MAX_INT * 1.0 + 1);
+
+#define REAL_CAN_BE_INT(v)\
+ ((v) > min_int_real && (v) < max_int_real)
+
+/* Get the pointer to the access flags for a ref. */
+#define ACCESS_REF(opp)\
+ (r_has_type(opp, t_dictionary) ? dict_access_ref(opp) : opp)
+
+/* <obj> <typenames> .type <name> */
+static int
+ztype(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref tnref;
+ int code = array_get(imemory, op, (long)r_btype(op - 1), &tnref);
+
+ if (code < 0)
+ return code;
+ if (!r_has_type(&tnref, t_name)) {
+ /* Must be either a stack underflow or a t_[a]struct. */
+ check_op(2);
+ { /* Get the type name from the structure. */
+ const char *sname =
+ gs_struct_type_name_string(gs_object_type(imemory,
+ op[-1].value.pstruct));
+ int code = name_ref(imemory, (const byte *)sname, strlen(sname),
+ (ref *) (op - 1), 0);
+
+ if (code < 0)
+ return code;
+ }
+ r_set_attrs(op - 1, a_executable);
+ } else {
+ ref_assign(op - 1, &tnref);
+ }
+ pop(1);
+ return 0;
+}
+
+/* - .typenames <name1|null> ... <nameN|null> */
+static int
+ztypenames(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ static const char *const tnames[] = { REF_TYPE_NAME_STRINGS };
+ int i;
+
+ check_ostack(t_next_index);
+ for (i = 0; i < t_next_index; i++) {
+ ref *const rtnp = op + 1 + i;
+
+ if (i >= countof(tnames) || tnames[i] == 0)
+ make_null(rtnp);
+ else {
+ int code = name_enter_string(imemory, tnames[i], rtnp);
+
+ if (code < 0)
+ return code;
+ r_set_attrs(rtnp, a_executable);
+ }
+ }
+ osp += t_next_index;
+ return 0;
+}
+
+/* <obj> cvlit <obj> */
+static int
+zcvlit(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *aop;
+
+ check_op(1);
+ aop = ACCESS_REF(op);
+ r_clear_attrs(aop, a_executable);
+ return 0;
+}
+
+/* <obj> cvx <obj> */
+int
+zcvx(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref *aop;
+ uint opidx;
+
+ check_op(1);
+ /*
+ * If the object is an internal operator, we can't allow it to
+ * exist in executable form anywhere outside the e-stack.
+ */
+ if (r_has_type(op, t_operator) &&
+ ((opidx = op_index(op)) == 0 ||
+ op_def_is_internal(op_index_def(opidx)))
+ )
+ return_error(gs_error_rangecheck);
+ aop = ACCESS_REF(op);
+ r_set_attrs(aop, a_executable);
+ return 0;
+}
+
+/* <obj> xcheck <bool> */
+static int
+zxcheck(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_op(1);
+ make_bool(op, (r_has_attr(ACCESS_REF(op), a_executable) ? 1 : 0));
+ return 0;
+}
+
+/* <obj:array|packedarray|file|string> executeonly <obj> */
+static int
+zexecuteonly(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_op(1);
+ if (r_has_type(op, t_dictionary))
+ return_error(gs_error_typecheck);
+ return access_check(i_ctx_p, a_execute, true);
+}
+
+/* <obj:array|packedarray|dict|file|string> noaccess <obj> */
+static int
+znoaccess(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_op(1);
+ if (r_has_type(op, t_dictionary)) {
+ ref *aop = dict_access_ref(op);
+
+ /* CPSI throws invalidaccess when seting noaccess to a readonly dictionary (CET 13-13-6) : */
+ if (!r_has_attrs(aop, a_write)) {
+ if (!r_has_attrs(aop, a_read) && !r_has_attrs(aop, a_execute)) {
+ /* Already noaccess - do nothing (CET 24-09-1). */
+ return 0;
+ }
+ return_error(gs_error_invalidaccess);
+ }
+
+ /* Don't allow removing read access to permanent dictionaries. */
+ if (dict_is_permanent_on_dstack(op))
+ return_error(gs_error_invalidaccess);
+ }
+ return access_check(i_ctx_p, 0, true);
+}
+
+/* <obj:array|packedarray|dict|file|string> readonly <obj> */
+int
+zreadonly(i_ctx_t *i_ctx_p)
+{
+ return access_check(i_ctx_p, a_readonly, true);
+}
+
+/* <array|packedarray|dict|file|string> rcheck <bool> */
+static int
+zrcheck(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = access_check(i_ctx_p, a_read, false);
+
+ if (code >= 0)
+ make_bool(op, code), code = 0;
+ return code;
+}
+
+/* <array|packedarray|dict|file|string> wcheck <bool> */
+static int
+zwcheck(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = access_check(i_ctx_p, a_write, false);
+
+ if (code >= 0)
+ make_bool(op, code), code = 0;
+ return code;
+}
+
+/* <num> cvi <int> */
+/* <string> cvi <int> */
+int
+zcvi(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ float fval;
+
+ switch (r_type(op)) {
+ case t_integer:
+ return 0;
+ case t_real:
+ fval = op->value.realval;
+ break;
+ default:
+ return_op_typecheck(op);
+ case t_string:
+ {
+ ref str, token;
+ int code;
+
+ ref_assign(&str, op);
+ code = gs_scan_string_token(i_ctx_p, &str, &token);
+ if (code > 0) /* anything other than a plain token */
+ code = gs_note_error(gs_error_syntaxerror);
+ if (code < 0)
+ return code;
+ switch (r_type(&token)) {
+ case t_integer:
+ *op = token;
+ return 0;
+ case t_real:
+ fval = token.value.realval;
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ }
+ }
+ if (!REAL_CAN_BE_INT(fval))
+ return_error(gs_error_rangecheck);
+ make_int(op, (long)fval); /* truncates towards 0 */
+ return 0;
+}
+
+/* <string> cvn <name> */
+static int
+zcvn(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_read_type(*op, t_string);
+ return name_from_string(imemory, op, op);
+}
+
+/* <num> cvr <real> */
+/* <string> cvr <real> */
+int
+zcvr(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ switch (r_type(op)) {
+ case t_integer:
+ make_real(op, (float)op->value.intval);
+ case t_real:
+ return 0;
+ default:
+ return_op_typecheck(op);
+ case t_string:
+ {
+ ref str, token;
+ int code;
+
+ ref_assign(&str, op);
+ code = gs_scan_string_token(i_ctx_p, &str, &token);
+ if (code > 0) /* anything other than a plain token */
+ code = gs_note_error(gs_error_syntaxerror);
+ if (code < 0)
+ return code;
+ switch (r_type(&token)) {
+ case t_integer:
+ make_real(op, (float)token.value.intval);
+ return 0;
+ case t_real:
+ *op = token;
+ return 0;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ }
+ }
+}
+
+/* <num> <radix_int> <string> cvrs <substring> */
+static int
+zcvrs(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int radix;
+
+ check_type(op[-1], t_integer);
+ if (op[-1].value.intval < 2 || op[-1].value.intval > 36)
+ return_error(gs_error_rangecheck);
+ radix = op[-1].value.intval;
+ check_write_type(*op, t_string);
+ if (radix == 10) {
+ switch (r_type(op - 2)) {
+ case t_integer:
+ case t_real:
+ {
+ int code = convert_to_string(imemory, op - 2, op);
+
+ if (code < 0)
+ return code;
+ pop(2);
+ return 0;
+ }
+ case t__invalid:
+ return_error(gs_error_stackunderflow);
+ default:
+ return_error(gs_error_rangecheck); /* CET 24-05 wants rangecheck */
+ }
+ } else {
+ uint ival;
+ byte digits[sizeof(ulong) * 8];
+ byte *endp = &digits[countof(digits)];
+ byte *dp = endp;
+
+ switch (r_type(op - 2)) {
+ case t_integer:
+ ival = (uint) op[-2].value.intval;
+ break;
+ case t_real:
+ {
+ float fval = op[-2].value.realval;
+
+ if (!REAL_CAN_BE_INT(fval))
+ return_error(gs_error_rangecheck);
+ ival = (ulong) (long)fval;
+ } break;
+ case t__invalid:
+ return_error(gs_error_stackunderflow);
+ default:
+ return_error(gs_error_rangecheck); /* CET 24-05 wants rangecheck */
+ }
+ do {
+ int dit = ival % radix;
+
+ *--dp = dit + (dit < 10 ? '0' : ('A' - 10));
+ ival /= radix;
+ }
+ while (ival);
+ if (endp - dp > r_size(op))
+ return_error(gs_error_rangecheck);
+ memcpy(op->value.bytes, dp, (uint) (endp - dp));
+ r_set_size(op, endp - dp);
+ }
+ op[-2] = *op;
+ pop(2);
+ return 0;
+}
+
+/* <any> <string> cvs <substring> */
+static int
+zcvs(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+
+ check_write_type(*op, t_string);
+ check_op(2);
+ code = convert_to_string(imemory, op - 1, op);
+ if (code >= 0)
+ pop(1);
+ return code;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def ztype_op_defs[] =
+{
+ {"1cvi", zcvi},
+ {"1cvlit", zcvlit},
+ {"1cvn", zcvn},
+ {"1cvr", zcvr},
+ {"3cvrs", zcvrs},
+ {"2cvs", zcvs},
+ {"1cvx", zcvx},
+ {"1executeonly", zexecuteonly},
+ {"1noaccess", znoaccess},
+ {"1rcheck", zrcheck},
+ {"1readonly", zreadonly},
+ {"2.type", ztype},
+ {"0.typenames", ztypenames},
+ {"1wcheck", zwcheck},
+ {"1xcheck", zxcheck},
+ op_def_end(0)
+};
+
+/* ------ Internal routines ------ */
+
+/* Test or modify the access of an object. */
+/* If modify = true, restrict to the selected access and return 0; */
+/* if modify = false, do not change the access, and return 1 */
+/* if the object had the access. */
+/* Return an error code if the object is not of appropriate type, */
+/* or if the object did not have the access already when modify=1. */
+static int
+access_check(i_ctx_t *i_ctx_p,
+ int access, /* mask for attrs */
+ bool modify) /* if true, reduce access */
+{
+ os_ptr op = osp;
+ ref *aop;
+
+ switch (r_type(op)) {
+ case t_dictionary:
+ aop = dict_access_ref(op);
+ if (modify) {
+ if (!r_has_attrs(aop, access))
+ return_error(gs_error_invalidaccess);
+ ref_save(op, aop, "access_check(modify)");
+ r_clear_attrs(aop, a_all);
+ r_set_attrs(aop, access);
+ dict_set_top();
+ return 0;
+ }
+ break;
+ case t_array:
+ case t_file:
+ case t_string:
+ case t_mixedarray:
+ case t_shortarray:
+ case t_astruct:
+ case t_device:;
+ if (modify) {
+ if (!r_has_attrs(op, access))
+ return_error(gs_error_invalidaccess);
+ r_clear_attrs(op, a_all);
+ r_set_attrs(op, access);
+ return 0;
+ }
+ aop = op;
+ break;
+ default:
+ return_op_typecheck(op);
+ }
+ return (r_has_attrs(aop, access) ? 1 : 0);
+}
+
+/* Do all the work of cvs. The destination has been checked, but not */
+/* the source. This is a separate procedure so that */
+/* cvrs can use it when the radix is 10. */
+static int
+convert_to_string(const gs_memory_t *mem, os_ptr op1, os_ptr op)
+{
+ uint len;
+ const byte *pstr = 0;
+ int code = obj_cvs(mem, op1, op->value.bytes, r_size(op), &len, &pstr);
+
+ if (code < 0) {
+ /*
+ * Some common downloaded error handlers assume that
+ * operator names don't exceed a certain fixed size.
+ * To work around this bit of bad design, we implement
+ * a special hack here: if we got a rangecheck, and
+ * the object is an operator whose name begins with
+ * %, ., or @, we just truncate the name.
+ */
+ if (code == gs_error_rangecheck)
+ switch (r_btype(op1)) {
+ case t_oparray:
+ case t_operator:
+ if (pstr != 0)
+ switch (*pstr) {
+ case '%':
+ case '.':
+ case '@':
+ len = r_size(op);
+ memcpy(op->value.bytes, pstr, len);
+ goto ok;
+ }
+ }
+ return code;
+ }
+ok:
+ *op1 = *op;
+ r_set_size(op1, len);
+ return 0;
+}
diff --git a/psi/zupath.c b/psi/zupath.c
new file mode 100644
index 000000000..7f4ec2a89
--- /dev/null
+++ b/psi/zupath.c
@@ -0,0 +1,892 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Operators related to user paths */
+#include "ghost.h"
+#include "oper.h"
+#include "oparc.h"
+#include "idict.h"
+#include "dstack.h"
+#include "igstate.h"
+#include "iname.h"
+#include "iutil.h"
+#include "store.h"
+#include "stream.h"
+#include "ibnum.h"
+#include "gsmatrix.h"
+#include "gsstate.h"
+#include "gscoord.h"
+#include "gspaint.h"
+#include "gxfixed.h"
+#include "gxdevice.h"
+#include "gspath.h"
+#include "gzpath.h" /* for saving path */
+#include "gzstate.h" /* for accessing path */
+
+/* Imported data */
+extern const gx_device gs_hit_device;
+extern const int gs_hit_detected;
+
+/*
+ * CPSI mode affects two algorithms in this file:
+ * - CPSI allows ucache to appear anywhere in user paths, even though the
+ * PLRM says ucache must appear (if at all) at the beginning
+ * (PLRM3 p, 199);
+ * - After appending an empty user path, in CPSI the current point is
+ * defined, even though the PLRM strongly implies this is incorrect
+ * (PLRM3 p. 712).
+ * The 'upath_compat' Boolean controls this behavior.
+ */
+
+/* Forward references */
+static int upath_append(os_ptr, i_ctx_t *, bool);
+static int upath_stroke(i_ctx_t *, gs_matrix *, bool);
+
+/* ---------------- Insideness testing ---------------- */
+
+/* Forward references */
+static int in_test(i_ctx_t *, int (*)(gs_state *));
+static int in_path(os_ptr, i_ctx_t *, gx_device *);
+static int in_path_result(i_ctx_t *, int, int);
+static int in_utest(i_ctx_t *, int (*)(gs_state *));
+static int in_upath(i_ctx_t *, gx_device *);
+static int in_upath_result(i_ctx_t *, int, int);
+
+/* <x> <y> ineofill <bool> */
+/* <userpath> ineofill <bool> */
+static int
+zineofill(i_ctx_t *i_ctx_p)
+{
+ return in_test(i_ctx_p, gs_eofill);
+}
+
+/* <x> <y> infill <bool> */
+/* <userpath> infill <bool> */
+static int
+zinfill(i_ctx_t *i_ctx_p)
+{
+ return in_test(i_ctx_p, gs_fill);
+}
+
+/* <x> <y> instroke <bool> */
+/* <userpath> instroke <bool> */
+static int
+zinstroke(i_ctx_t *i_ctx_p)
+{
+ return in_test(i_ctx_p, gs_stroke);
+}
+
+/* <x> <y> <userpath> inueofill <bool> */
+/* <userpath1> <userpath2> inueofill <bool> */
+static int
+zinueofill(i_ctx_t *i_ctx_p)
+{
+ return in_utest(i_ctx_p, gs_eofill);
+}
+
+/* <x> <y> <userpath> inufill <bool> */
+/* <userpath1> <userpath2> inufill <bool> */
+static int
+zinufill(i_ctx_t *i_ctx_p)
+{
+ return in_utest(i_ctx_p, gs_fill);
+}
+
+/* <x> <y> <userpath> inustroke <bool> */
+/* <x> <y> <userpath> <matrix> inustroke <bool> */
+/* <userpath1> <userpath2> inustroke <bool> */
+/* <userpath1> <userpath2> <matrix> inustroke <bool> */
+static int
+zinustroke(i_ctx_t *i_ctx_p)
+{ /* This is different because of the optional matrix operand. */
+ os_ptr op = osp;
+ int code = gs_gsave(igs);
+ int spop, npop;
+ gs_matrix mat;
+ gx_device hdev;
+
+ if (code < 0)
+ return code;
+ if ((spop = upath_stroke(i_ctx_p, &mat, false)) < 0) {
+ gs_grestore(igs);
+ return spop;
+ }
+ if ((npop = in_path(op - spop, i_ctx_p, &hdev)) < 0) {
+ gs_grestore(igs);
+ return npop;
+ }
+ if (npop > 1) /* matrix was supplied */
+ code = gs_concat(igs, &mat);
+ if (code >= 0)
+ code = gs_stroke(igs);
+ return in_upath_result(i_ctx_p, npop + spop, code);
+}
+
+/* ------ Internal routines ------ */
+
+/* Do the work of the non-user-path insideness operators. */
+static int
+in_test(i_ctx_t *i_ctx_p, int (*paintproc)(gs_state *))
+{
+ os_ptr op = osp;
+ gx_device hdev;
+ int npop = in_path(op, i_ctx_p, &hdev);
+ int code;
+
+ if (npop < 0)
+ return npop;
+ code = (*paintproc)(igs);
+ return in_path_result(i_ctx_p, npop, code);
+}
+
+/* Set up a clipping path and device for insideness testing. */
+static int
+in_path(os_ptr oppath, i_ctx_t *i_ctx_p, gx_device * phdev)
+{
+ int code = gs_gsave(igs);
+ int npop;
+ double uxy[2];
+
+ if (code < 0)
+ return code;
+ code = num_params(oppath, 2, uxy);
+ if (code >= 0) { /* Aperture is a single pixel. */
+ gs_point dxy;
+ gs_fixed_rect fr;
+
+ gs_transform(igs, uxy[0], uxy[1], &dxy);
+ fr.p.x = fixed_floor(float2fixed(dxy.x));
+ fr.p.y = fixed_floor(float2fixed(dxy.y));
+ fr.q.x = fr.p.x + fixed_1;
+ fr.q.y = fr.p.y + fixed_1;
+ code = gx_clip_to_rectangle(igs, &fr);
+ npop = 2;
+ } else if (code == gs_error_stackunderflow) {
+ /* If 0 elements, definitely a stackunderflow; otherwise, */
+ /* only 1 number, also a stackunderflow. */
+ npop = code;
+ } else { /* Aperture is a user path. */
+ /* We have to set the clipping path without disturbing */
+ /* the current path. */
+ gx_path *ipath = igs->path;
+ gx_path save;
+
+ gx_path_init_local(&save, imemory);
+ gx_path_assign_preserve(&save, ipath);
+ gs_newpath(igs);
+ code = upath_append(oppath, i_ctx_p, false);
+ if (code >= 0)
+ code = gx_clip_to_path(igs);
+ gx_path_assign_free(igs->path, &save);
+ npop = 1;
+ }
+ if (code < 0) {
+ gs_grestore(igs);
+ return code;
+ }
+ /* Install the hit detection device. */
+ gx_set_device_color_1(igs);
+ gx_device_init_on_stack((gx_device *) phdev, (const gx_device *)&gs_hit_device,
+ imemory);
+ phdev->width = phdev->height = max_int;
+ gx_device_fill_in_procs(phdev);
+ gx_set_device_only(igs, phdev);
+ return npop;
+}
+
+/* Finish an insideness test. */
+static int
+in_path_result(i_ctx_t *i_ctx_p, int npop, int code)
+{
+ os_ptr op = osp;
+ bool result;
+
+ gs_grestore(igs); /* matches gsave in in_path */
+ if (code == gs_hit_detected)
+ result = true;
+ else if (code == 0) /* completed painting without a hit */
+ result = false;
+ else /* error */
+ return code;
+ npop--;
+ pop(npop);
+ op -= npop;
+ make_bool(op, result);
+ return 0;
+
+}
+
+/* Do the work of the user-path insideness operators. */
+static int
+in_utest(i_ctx_t *i_ctx_p, int (*paintproc)(gs_state *))
+{
+ gx_device hdev;
+ int npop = in_upath(i_ctx_p, &hdev);
+ int code;
+
+ if (npop < 0)
+ return npop;
+ code = (*paintproc)(igs);
+ return in_upath_result(i_ctx_p, npop, code);
+}
+
+/* Set up a clipping path and device for insideness testing */
+/* with a user path. */
+static int
+in_upath(i_ctx_t *i_ctx_p, gx_device * phdev)
+{
+ os_ptr op = osp;
+ int code = gs_gsave(igs);
+ int npop;
+
+ if (code < 0)
+ return code;
+ if ((code = upath_append(op, i_ctx_p, false)) < 0 ||
+ (code = npop = in_path(op - 1, i_ctx_p, phdev)) < 0
+ ) {
+ gs_grestore(igs);
+ return code;
+ }
+ return npop + 1;
+}
+
+/* Finish an insideness test with a user path. */
+static int
+in_upath_result(i_ctx_t *i_ctx_p, int npop, int code)
+{
+ gs_grestore(igs); /* matches gsave in in_upath */
+ return in_path_result(i_ctx_p, npop, code);
+}
+
+/* ---------------- User paths ---------------- */
+
+/* User path operator codes */
+typedef enum {
+ upath_op_setbbox = 0,
+ upath_op_moveto = 1,
+ upath_op_rmoveto = 2,
+ upath_op_lineto = 3,
+ upath_op_rlineto = 4,
+ upath_op_curveto = 5,
+ upath_op_rcurveto = 6,
+ upath_op_arc = 7,
+ upath_op_arcn = 8,
+ upath_op_arct = 9,
+ upath_op_closepath = 10,
+ upath_op_ucache = 11
+} upath_op;
+
+/* User path interpretation states */
+typedef enum {
+ UPS_INITIAL = 1, /* (no ops yet) */
+ UPS_UCACHE = 2, /* ucache */
+ UPS_SETBBOX = 4, /* [ucache] setbbox */
+ UPS_PATH = 8 /* (within path) */
+} upath_state;
+
+typedef struct up_data_s {
+ byte num_args;
+ byte states_before;
+ byte state_after;
+} up_data_t;
+#define UP_DATA_PATH(n) {n, UPS_SETBBOX | UPS_PATH, UPS_PATH}
+
+#define UPATH_MAX_OP 11
+#define UPATH_REPEAT 32
+static const up_data_t up_data[UPATH_MAX_OP + 1] = {
+ {4, UPS_INITIAL | UPS_UCACHE, UPS_SETBBOX}, /* setbbox */
+ UP_DATA_PATH(2),
+ UP_DATA_PATH(2),
+ UP_DATA_PATH(2),
+ UP_DATA_PATH(2),
+ UP_DATA_PATH(6),
+ UP_DATA_PATH(6),
+ UP_DATA_PATH(5),
+ UP_DATA_PATH(5),
+ UP_DATA_PATH(5),
+ UP_DATA_PATH(0),
+ {0, UPS_INITIAL, UPS_UCACHE} /* ucache */
+};
+
+/* Declare operator procedures not declared in opextern.h. */
+int zsetbbox(i_ctx_t *);
+static int zucache(i_ctx_t *);
+
+#undef zp
+static const op_proc_t up_ops[UPATH_MAX_OP + 1] = {
+ zsetbbox, zmoveto, zrmoveto, zlineto, zrlineto,
+ zcurveto, zrcurveto, zarc, zarcn, zarct,
+ zclosepath, zucache
+};
+
+/* - ucache - */
+static int
+zucache(i_ctx_t *i_ctx_p)
+{
+ /* A no-op for now. */
+ return 0;
+}
+
+/* <userpath> uappend - */
+static int
+zuappend(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = gs_gsave(igs);
+
+ if (code < 0)
+ return code;
+ if ((code = upath_append(op, i_ctx_p, false)) >= 0)
+ code = gs_upmergepath(igs);
+ gs_grestore(igs);
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* <userpath> ueofill - */
+static int
+zueofill(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = gs_gsave(igs);
+
+ if (code < 0)
+ return code;
+ if ((code = upath_append(op, i_ctx_p, gs_currentcpsimode(imemory))) >= 0)
+ code = gs_eofill(igs);
+ gs_grestore(igs);
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* <userpath> ufill - */
+static int
+zufill(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = gs_gsave(igs);
+
+ if (code < 0)
+ return code;
+ if ((code = upath_append(op, i_ctx_p, gs_currentcpsimode(imemory))) >= 0)
+ code = gs_fill(igs);
+ gs_grestore(igs);
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* <userpath> ustroke - */
+/* <userpath> <matrix> ustroke - */
+static int
+zustroke(i_ctx_t *i_ctx_p)
+{
+ int code = gs_gsave(igs);
+ int npop;
+
+ if (code < 0)
+ return code;
+ if ((code = npop = upath_stroke(i_ctx_p, NULL, gs_currentcpsimode(imemory))) >= 0)
+ code = gs_stroke(igs);
+ gs_grestore(igs);
+ if (code < 0)
+ return code;
+ pop(npop);
+ return 0;
+}
+
+/* <userpath> ustrokepath - */
+/* <userpath> <matrix> ustrokepath - */
+static int
+zustrokepath(i_ctx_t *i_ctx_p)
+{
+ gx_path save;
+ gs_matrix saved_matrix;
+ int npop, code = gs_currentmatrix(igs, &saved_matrix);
+
+ if (code < 0)
+ return code;
+ /* Save and reset the path. */
+ gx_path_init_local(&save, imemory);
+ gx_path_assign_preserve(&save, igs->path);
+ if ((code = npop = upath_stroke(i_ctx_p, NULL, false)) < 0 ||
+ (code = gs_strokepath(igs)) < 0
+ ) {
+ gx_path_assign_free(igs->path, &save);
+ return code;
+ }
+ /*
+ * If a matrix was specified then restore the previous matrix.
+ */
+ if (npop > 1) {
+ if ((code = gs_setmatrix(igs, &saved_matrix)) < 0) {
+ gx_path_assign_free(igs->path, &save);
+ return code;
+ }
+ }
+ gx_path_free(&save, "ustrokepath");
+ pop(npop);
+ return 0;
+}
+
+/* <with_ucache> upath <userpath> */
+/* We do all the work in a procedure that is also used to construct */
+/* the UnpaintedPath user path for ImageType 2 images. */
+int make_upath(i_ctx_t *i_ctx_p, ref *rupath, gs_state *pgs, gx_path *ppath,
+ bool with_ucache);
+static int
+zupath(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_boolean);
+ return make_upath(i_ctx_p, op, igs, igs->path, op->value.boolval);
+}
+
+/* Compute the path length for user path purposes. */
+static int
+path_length_for_upath(const gx_path *ppath)
+{
+ gs_path_enum penum;
+ int op, size = 0;
+ gs_fixed_point pts[3];
+
+ gx_path_enum_init(&penum, ppath);
+ while ((op = gx_path_enum_next(&penum, pts)) != 0) {
+ switch (op) {
+ case gs_pe_moveto:
+ case gs_pe_lineto:
+ size += 3;
+ continue;
+ case gs_pe_curveto:
+ size += 7;
+ continue;
+ case gs_pe_closepath:
+ size += 1;
+ continue;
+ default:
+ return_error(gs_error_unregistered);
+ }
+ }
+ return size;
+}
+
+int
+make_upath(i_ctx_t *i_ctx_p, ref *rupath, gs_state *pgs, gx_path *ppath,
+ bool with_ucache)
+{
+ int size = (with_ucache ? 6 : 5);
+ gs_path_enum penum;
+ gs_rect bbox;
+ int op;
+ ref *next;
+ int code;
+
+ /* Compute the bounding box. */
+ if ((code = gs_upathbbox(pgs, &bbox, true)) < 0) {
+ /*
+ * Note: Adobe throws 'nocurrentpoint' error, but the PLRM does
+ * not list this as a possible error from 'upath', so if we are
+ * not in CPSI compatibility mode, we set a reasonable default
+ * bbox instead.
+ */
+ if (code != gs_error_nocurrentpoint || gs_currentcpsimode(imemory))
+ return code;
+ bbox.p.x = bbox.p.y = bbox.q.x = bbox.q.y = 0;
+ }
+
+ code = path_length_for_upath(ppath);
+ if (code < 0)
+ return code;
+ size += code;
+ if (size >= 65536)
+ return_error(gs_error_limitcheck);
+
+ code = ialloc_ref_array(rupath, a_all | a_executable, size,
+ "make_upath");
+ if (code < 0)
+ return code;
+ /* Construct the path. */
+ next = rupath->value.refs;
+ if (with_ucache) {
+ if ((code = name_enter_string(pgs->memory, "ucache", next)) < 0)
+ return code;
+ r_set_attrs(next, a_executable | l_new);
+ ++next;
+ }
+ make_real_new(next, bbox.p.x);
+ make_real_new(next + 1, bbox.p.y);
+ make_real_new(next + 2, bbox.q.x);
+ make_real_new(next + 3, bbox.q.y);
+ next += 4;
+ if ((code = name_enter_string(pgs->memory, "setbbox", next)) < 0)
+ return code;
+ r_set_attrs(next, a_executable | l_new);
+ ++next;
+ {
+ gs_point pts[3];
+
+ /* Patch the path in the gstate to set up the enumerator. */
+ gx_path *save_path = pgs->path;
+
+ pgs->path = ppath;
+ gs_path_enum_copy_init(pgs->memory, &penum, pgs, false);
+ pgs->path = save_path;
+ while ((op = gs_path_enum_next(&penum, pts)) != 0) {
+ const char *opstr;
+
+ switch (op) {
+ case gs_pe_moveto:
+ opstr = "moveto";
+ goto ml;
+ case gs_pe_lineto:
+ opstr = "lineto";
+ ml:make_real_new(next, pts[0].x);
+ make_real_new(next + 1, pts[0].y);
+ next += 2;
+ break;
+ case gs_pe_curveto:
+ opstr = "curveto";
+ make_real_new(next, pts[0].x);
+ make_real_new(next + 1, pts[0].y);
+ make_real_new(next + 2, pts[1].x);
+ make_real_new(next + 3, pts[1].y);
+ make_real_new(next + 4, pts[2].x);
+ make_real_new(next + 5, pts[2].y);
+ next += 6;
+ break;
+ case gs_pe_closepath:
+ opstr = "closepath";
+ break;
+ default:
+ return_error(gs_error_unregistered);
+ }
+ if ((code = name_enter_string(pgs->memory, opstr, next)) < 0)
+ return code;
+ r_set_attrs(next, a_executable);
+ ++next;
+ }
+ }
+ return 0;
+}
+
+static int
+zgetpath(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int i, code, path_size, leaf_count;
+ ref *main_ref, *operators[5];
+
+ push(1);
+ path_size = code = path_length_for_upath(igs->path);
+ if (code < 0)
+ return code;
+ leaf_count = (path_size + max_array_size - 1) / max_array_size;
+ code = ialloc_ref_array(op, a_all, leaf_count, "zgetpath_master");
+ if (code < 0)
+ return code;
+ if (path_size == 0)
+ return 0;
+
+ if (dict_find_string(systemdict, "moveto", &operators[1]) <= 0 ||
+ dict_find_string(systemdict, "lineto", &operators[2]) <= 0 ||
+ dict_find_string(systemdict, "curveto", &operators[3]) <= 0 ||
+ dict_find_string(systemdict, "closepath", &operators[4]) <= 0)
+ return_error(gs_error_undefined);
+
+ main_ref = op->value.refs;
+ for (i = 0; i < leaf_count; i++) {
+ int leaf_size = ( i == leaf_count - 1) ? path_size - i * max_array_size : max_array_size;
+ code = ialloc_ref_array(&main_ref[i], a_all | a_executable, leaf_size, "zgetpath_leaf");
+ if (code < 0)
+ return code;
+ }
+
+ {
+ int pe, j, k;
+ gs_path_enum penum;
+ static const int oper_count[5] = { 0, 2, 2, 6, 0 };
+ gs_point pts[3];
+ const double *fts[6];
+
+ fts[0] = &pts[0].x;
+ fts[1] = &pts[0].y;
+ fts[2] = &pts[1].x;
+ fts[3] = &pts[1].y;
+ fts[4] = &pts[2].x;
+ fts[5] = &pts[2].y;
+
+ main_ref = op->value.refs;
+ gs_path_enum_copy_init(igs->memory, &penum, igs, false);
+ pe = gs_path_enum_next(&penum, pts);
+ if (pe < 0)
+ return pe;
+ k = 0;
+
+ for (i = 0; i < leaf_count; i++) {
+ int leaf_size = ( i == leaf_count - 1) ? path_size - i * max_array_size : max_array_size;
+ ref *leaf_ref = main_ref[i].value.refs;
+
+ for (j = 0; j < leaf_size; j++) {
+ if (k < oper_count[pe])
+ make_real_new(&leaf_ref[j], (float)*fts[k++]);
+ else {
+ k = 0;
+ ref_assign(&leaf_ref[j], operators[pe]);
+ pe = gs_path_enum_next(&penum, pts);
+ if (pe <= 0)
+ return pe;
+ if (pe >= 5)
+ return_error(gs_error_unregistered);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* ------ Internal routines ------ */
+
+/* Append a user path to the current path. */
+static inline int
+upath_append_aux(os_ptr oppath, i_ctx_t *i_ctx_p, int *pnargs, bool upath_compat)
+{
+ upath_state ups = UPS_INITIAL;
+ ref opcodes;
+
+ if (r_has_type(oppath, t__invalid))
+ return_error(gs_error_stackunderflow);
+ if (!r_is_array(oppath))
+ return_error(gs_error_typecheck);
+ check_read(*oppath);
+ gs_newpath(igs);
+ /****** ROUND tx AND ty ******/
+
+ if ( r_size(oppath) == 2 &&
+ array_get(imemory, oppath, 1, &opcodes) >= 0 &&
+ r_has_type(&opcodes, t_string)
+ ) { /* 1st element is operands, 2nd is operators */
+ ref operands;
+ int code, format;
+ int repcount = 1;
+ const byte *opp;
+ uint ocount, i = 0;
+
+ array_get(imemory, oppath, 0, &operands);
+ code = num_array_format(&operands);
+ if (code < 0)
+ return code;
+ format = code;
+ check_read(opcodes);
+ opp = opcodes.value.bytes;
+ ocount = r_size(&opcodes);
+ while (ocount--) {
+ byte opx = *opp++;
+
+ if (opx > UPATH_REPEAT)
+ repcount = opx - UPATH_REPEAT;
+ else if (opx > UPATH_MAX_OP)
+ return_error(gs_error_rangecheck);
+ else { /* operator */
+ const up_data_t data = up_data[opx];
+
+ *pnargs = 0; /* in case of error */
+ if (upath_compat && opx == upath_op_ucache) {
+ /* CPSI does not complain about incorrect ucache
+ placement, even though PLRM3 says it's illegal. */
+ ups = ups > UPS_UCACHE ? ups : data.state_after;
+ } else {
+ if (!(ups & data.states_before))
+ return_error(gs_error_typecheck);
+ ups = data.state_after;
+ }
+ do {
+ os_ptr op = osp;
+ byte opargs = data.num_args;
+
+ while (opargs--) {
+ push(1);
+ (*pnargs)++; /* in case of error */
+ code = num_array_get(imemory, &operands, format, i++, op);
+ switch (code) {
+ case t_integer:
+ r_set_type_attrs(op, t_integer, 0);
+ break;
+ case t_real:
+ r_set_type_attrs(op, t_real, 0);
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ }
+ code = (*up_ops[opx])(i_ctx_p);
+ if (code < 0)
+ return code;
+ }
+ while (--repcount);
+ repcount = 1;
+ }
+ }
+ } else { /* Ordinary executable array. */
+ const ref *arp = oppath;
+ uint ocount = r_size(oppath);
+ long index = 0;
+ int argcount = 0;
+ op_proc_t oproc;
+ int opx, code;
+
+ for (; index < ocount; index++) {
+ ref rup;
+ ref *defp;
+ os_ptr op = osp;
+ up_data_t data;
+
+ *pnargs = argcount;
+ array_get(imemory, arp, index, &rup);
+ switch (r_type(&rup)) {
+ case t_integer:
+ case t_real:
+ argcount++;
+ push(1);
+ *op = rup;
+ break;
+ case t_name:
+ if (!r_has_attr(&rup, a_executable) ||
+ dict_find(systemdict, &rup, &defp) <= 0 ||
+ r_btype(defp) != t_operator)
+ return_error(gs_error_typecheck); /* all errors = typecheck */
+ goto xop;
+ case t_operator:
+ defp = &rup;
+ xop:if (!r_has_attr(defp, a_executable))
+ return_error(gs_error_typecheck);
+ oproc = real_opproc(defp);
+ for (opx = 0; opx <= UPATH_MAX_OP; opx++)
+ if (oproc == up_ops[opx])
+ break;
+ if (opx > UPATH_MAX_OP)
+ return_error(gs_error_typecheck);
+ data = up_data[opx];
+ if (argcount != data.num_args)
+ return_error(gs_error_typecheck);
+ if (upath_compat && opx == upath_op_ucache) {
+ /* CPSI does not complain about incorrect ucache
+ placement, even though PLRM3 says it's illegal. */
+ ups = ups > UPS_UCACHE ? ups : data.state_after;
+ } else {
+ if (!(ups & data.states_before))
+ return_error(gs_error_typecheck);
+ ups = data.state_after;
+ }
+ code = (*up_ops[opx])(i_ctx_p);
+ if (code < 0) {
+ if (code == gs_error_nocurrentpoint)
+ return_error(gs_error_rangecheck); /* CET 11-22 */
+ return code;
+ }
+ argcount = 0;
+ break;
+ default:
+ return_error(gs_error_typecheck);
+ }
+ }
+ if (argcount) {
+ *pnargs = argcount;
+ return_error(gs_error_typecheck); /* leftover args */
+ }
+ }
+ if (ups < UPS_SETBBOX)
+ return_error(gs_error_typecheck); /* no setbbox */
+ if (ups == UPS_SETBBOX && upath_compat) {
+ /*
+ * In CPSI compatibility mode, an empty path with a setbbox also
+ * does a moveto (but only if the path is empty). Since setbbox
+ * was the last operator, its operands are still on the o-stack.
+ */
+ osp += 2;
+ return zmoveto(i_ctx_p);
+ }
+ return 0;
+}
+static int
+upath_append(os_ptr oppath, i_ctx_t *i_ctx_p, bool upath_compat)
+{
+ int nargs = 0;
+ int code = upath_append_aux(oppath, i_ctx_p, &nargs, upath_compat);
+
+ if (code < 0) {
+ /* Pop args on error, to match Adobe interpreters. */
+ pop(nargs);
+ return code;
+ }
+ return 0;
+}
+
+/* Append a user path to the current path, and then apply or return */
+/* a transformation if one is supplied. */
+static int
+upath_stroke(i_ctx_t *i_ctx_p, gs_matrix *pmat, bool upath_compat)
+{
+ os_ptr op = osp;
+ int code, npop;
+ gs_matrix mat;
+
+ if ((code = read_matrix(imemory, op, &mat)) >= 0) {
+ if ((code = upath_append(op - 1, i_ctx_p, upath_compat)) >= 0) {
+ if (pmat)
+ *pmat = mat;
+ else
+ code = gs_concat(igs, &mat);
+ }
+ npop = 2;
+ } else {
+ if ((code = upath_append(op, i_ctx_p, upath_compat)) >= 0)
+ if (pmat)
+ gs_make_identity(pmat);
+ npop = 1;
+ }
+ return (code < 0 ? code : npop);
+}
+
+/* ---------------- Initialization procedure ---------------- */
+
+const op_def zupath_l2_op_defs[] =
+{
+ op_def_begin_level2(),
+ /* Insideness testing */
+ {"1ineofill", zineofill},
+ {"1infill", zinfill},
+ {"1instroke", zinstroke},
+ {"2inueofill", zinueofill},
+ {"2inufill", zinufill},
+ {"2inustroke", zinustroke},
+ /* User paths */
+ {"1uappend", zuappend},
+ {"0ucache", zucache},
+ {"1ueofill", zueofill},
+ {"1ufill", zufill},
+ {"1upath", zupath},
+ {"1ustroke", zustroke},
+ {"1ustrokepath", zustrokepath},
+ /* Path access for PDF */
+ {"0.getpath", zgetpath},
+ op_def_end(0)
+};
diff --git a/psi/zusparam.c b/psi/zusparam.c
new file mode 100644
index 000000000..4e8daa55c
--- /dev/null
+++ b/psi/zusparam.c
@@ -0,0 +1,924 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* User and system parameter operators */
+#include "memory_.h"
+#include "string_.h"
+#include "ghost.h"
+#include "oper.h"
+#include "gscdefs.h"
+#include "gsstruct.h" /* for gxht.h */
+#include "gsfont.h" /* for user params */
+#include "gxht.h" /* for user params */
+#include "gsutil.h"
+#include "estack.h"
+#include "ialloc.h" /* for imemory for status */
+#include "icontext.h" /* for set_user_params prototype */
+#include "idict.h"
+#include "idparam.h"
+#include "iparam.h"
+#include "dstack.h"
+#include "iname.h"
+#include "itoken.h"
+#include "iutil2.h"
+#include "ivmem2.h"
+#include "store.h"
+#include "gsnamecl.h"
+#include "igstate.h"
+#include "gscms.h"
+#include "gsicc_manage.h"
+#include "gsparamx.h"
+#include "gx.h"
+#include "gxistate.h"
+#include "gslibctx.h"
+
+
+/* The (global) font directory */
+extern gs_font_dir *ifont_dir; /* in zfont.c */
+
+/* Define an individual user or system parameter. */
+/* Eventually this will be made public. */
+#define param_def_common\
+ const char *pname
+
+typedef struct param_def_s {
+ param_def_common;
+} param_def_t;
+
+typedef struct long_param_def_s {
+ param_def_common;
+ long min_value, max_value;
+ long (*current)(i_ctx_t *);
+ int (*set)(i_ctx_t *, long);
+} long_param_def_t;
+
+#if arch_sizeof_long > arch_sizeof_int
+# define MAX_UINT_PARAM max_uint
+# define MIN_INT_PARAM min_int
+#else
+# define MAX_UINT_PARAM max_long
+# define MIN_INT_PARAM min_long
+#endif
+
+typedef struct bool_param_def_s {
+ param_def_common;
+ bool (*current)(i_ctx_t *);
+ int (*set)(i_ctx_t *, bool);
+} bool_param_def_t;
+
+typedef struct string_param_def_s {
+ param_def_common;
+ void (*current)(i_ctx_t *, gs_param_string *);
+ int (*set)(i_ctx_t *, gs_param_string *);
+} string_param_def_t;
+
+/* Define a parameter set (user or system). */
+typedef struct param_set_s {
+ const long_param_def_t *long_defs;
+ uint long_count;
+ const bool_param_def_t *bool_defs;
+ uint bool_count;
+ const string_param_def_t *string_defs;
+ uint string_count;
+} param_set;
+
+/* Forward references */
+static int setparams(i_ctx_t *, gs_param_list *, const param_set *);
+static int currentparams(i_ctx_t *, const param_set *);
+static int currentparam1(i_ctx_t *, const param_set *);
+
+/* ------ Passwords ------ */
+
+/* <string|int> .checkpassword <0|1|2> */
+static int
+zcheckpassword(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ ref params[2];
+ array_param_list list;
+ gs_param_list *const plist = (gs_param_list *)&list;
+ int result = 0;
+ int code = name_ref(imemory, (const byte *)"Password", 8, &params[0], 0);
+ password pass;
+
+ if (code < 0)
+ return code;
+ params[1] = *op;
+ array_param_list_read(&list, params, 2, NULL, false, iimemory);
+ if (dict_read_password(&pass, systemdict, "StartJobPassword") >= 0 &&
+ param_check_password(plist, &pass) == 0
+ )
+ result = 1;
+ if (dict_read_password(&pass, systemdict, "SystemParamsPassword") >= 0 &&
+ param_check_password(plist, &pass) == 0
+ )
+ result = 2;
+ iparam_list_release(&list);
+ make_int(op, result);
+ return 0;
+}
+
+/* ------ System parameters ------ */
+
+/* Integer values */
+static long
+current_BuildTime(i_ctx_t *i_ctx_p)
+{
+ return gs_buildtime;
+}
+
+/* we duplicate this definition here instead of including bfont.h and
+ all its dependencies */
+
+#define ifont_dir (gs_lib_ctx_get_interp_instance(imemory)->font_dir)
+
+static long
+current_MaxFontCache(i_ctx_t *i_ctx_p)
+{
+ return gs_currentcachesize(ifont_dir);
+}
+static int
+set_MaxFontCache(i_ctx_t *i_ctx_p, long val)
+{
+ return gs_setcachesize(igs, ifont_dir,
+ (uint)(val < 0 ? 0 : val > max_uint ? max_uint :
+ val));
+}
+static long
+current_CurFontCache(i_ctx_t *i_ctx_p)
+{
+ uint cstat[7];
+
+ gs_cachestatus(ifont_dir, cstat);
+ return cstat[0];
+}
+static long
+current_MaxGlobalVM(i_ctx_t *i_ctx_p)
+{
+ gs_memory_gc_status_t stat;
+
+ gs_memory_gc_status(iimemory_global, &stat);
+ return stat.max_vm;
+}
+static int
+set_MaxGlobalVM(i_ctx_t *i_ctx_p, long val)
+{
+ gs_memory_gc_status_t stat;
+
+ gs_memory_gc_status(iimemory_global, &stat);
+ stat.max_vm = max(val, 0);
+ gs_memory_set_gc_status(iimemory_global, &stat);
+ return 0;
+}
+static long
+current_Revision(i_ctx_t *i_ctx_p)
+{
+ return gs_revision;
+}
+
+static long
+current_PageCount(i_ctx_t *i_ctx_p)
+{
+ gx_device *dev = gs_currentdevice(igs);
+
+ if ((*dev_proc(dev, get_page_device))(dev) != 0)
+ if (dev->ShowpageCount > i_ctx_p->nv_page_count)
+ i_ctx_p->nv_page_count = dev->ShowpageCount;
+ return 1000 + i_ctx_p->nv_page_count; /* Add 1000 to imitate NV memory */
+}
+
+static const long_param_def_t system_long_params[] =
+{
+ {"BuildTime", min_long, max_long, current_BuildTime, NULL},
+{"MaxFontCache", 0, MAX_UINT_PARAM, current_MaxFontCache, set_MaxFontCache},
+ {"CurFontCache", 0, MAX_UINT_PARAM, current_CurFontCache, NULL},
+ {"Revision", min_long, max_long, current_Revision, NULL},
+ {"PageCount", min_long, max_long, current_PageCount, NULL},
+
+ /* Extensions */
+ {"MaxGlobalVM", 0, max_long, current_MaxGlobalVM, set_MaxGlobalVM}
+};
+
+/* Boolean values */
+static bool
+current_ByteOrder(i_ctx_t *i_ctx_p)
+{
+ return !arch_is_big_endian;
+}
+static const bool_param_def_t system_bool_params[] =
+{
+ {"ByteOrder", current_ByteOrder, NULL}
+};
+
+/* String values */
+static void
+current_RealFormat(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+#if ARCH_FLOATS_ARE_IEEE
+ static const char *const rfs = "IEEE";
+#else
+ static const char *const rfs = "not IEEE";
+#endif
+
+ pval->data = (const byte *)rfs;
+ pval->size = strlen(rfs);
+ pval->persistent = true;
+}
+
+static const string_param_def_t system_string_params[] =
+{
+ {"RealFormat", current_RealFormat, NULL},
+};
+
+/* The system parameter set */
+static const param_set system_param_set =
+{
+ system_long_params, countof(system_long_params),
+ system_bool_params, countof(system_bool_params),
+ system_string_params, countof(system_string_params)
+};
+
+/* <dict> .setsystemparams - */
+static int
+zsetsystemparams(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code;
+ dict_param_list list;
+ gs_param_list *const plist = (gs_param_list *)&list;
+ password pass;
+
+ check_type(*op, t_dictionary);
+ code = dict_param_list_read(&list, op, NULL, false, iimemory);
+ if (code < 0)
+ return code;
+ code = dict_read_password(&pass, systemdict, "SystemParamsPassword");
+ if (code < 0)
+ return code;
+ code = param_check_password(plist, &pass);
+ if (code != 0) {
+ if (code > 0)
+ code = gs_note_error(gs_error_invalidaccess);
+ goto out;
+ }
+ code = param_read_password(plist, "StartJobPassword", &pass);
+ switch (code) {
+ default: /* invalid */
+ goto out;
+ case 1: /* missing */
+ break;
+ case 0:
+ code = dict_write_password(&pass, systemdict,
+ "StartJobPassword",
+ ! i_ctx_p->LockFilePermissions);
+ if (code < 0)
+ goto out;
+ }
+ code = param_read_password(plist, "SystemParamsPassword", &pass);
+ switch (code) {
+ default: /* invalid */
+ goto out;
+ case 1: /* missing */
+ break;
+ case 0:
+ code = dict_write_password(&pass, systemdict,
+ "SystemParamsPassword",
+ ! i_ctx_p->LockFilePermissions);
+ if (code < 0)
+ goto out;
+ }
+
+ code = setparams(i_ctx_p, plist, &system_param_set);
+ out:
+ iparam_list_release(&list);
+ if (code < 0)
+ return code;
+ pop(1);
+ return 0;
+}
+
+/* - .currentsystemparams <name1> <value1> ... */
+static int
+zcurrentsystemparams(i_ctx_t *i_ctx_p)
+{
+ return currentparams(i_ctx_p, &system_param_set);
+}
+
+/* <name> .getsystemparam <value> */
+static int
+zgetsystemparam(i_ctx_t *i_ctx_p)
+{
+ return currentparam1(i_ctx_p, &system_param_set);
+}
+
+/* ------ User parameters ------ */
+
+/* Integer values */
+static long
+current_JobTimeout(i_ctx_t *i_ctx_p)
+{
+ return 0;
+}
+static int
+set_JobTimeout(i_ctx_t *i_ctx_p, long val)
+{
+ return 0;
+}
+static long
+current_MaxFontItem(i_ctx_t *i_ctx_p)
+{
+ return gs_currentcacheupper(ifont_dir);
+}
+static int
+set_MaxFontItem(i_ctx_t *i_ctx_p, long val)
+{
+ return gs_setcacheupper(ifont_dir, val);
+}
+static long
+current_MinFontCompress(i_ctx_t *i_ctx_p)
+{
+ return gs_currentcachelower(ifont_dir);
+}
+static int
+set_MinFontCompress(i_ctx_t *i_ctx_p, long val)
+{
+ return gs_setcachelower(ifont_dir, val);
+}
+static long
+current_MaxOpStack(i_ctx_t *i_ctx_p)
+{
+ return ref_stack_max_count(&o_stack);
+}
+static int
+set_MaxOpStack(i_ctx_t *i_ctx_p, long val)
+{
+ return ref_stack_set_max_count(&o_stack, val);
+}
+static long
+current_MaxDictStack(i_ctx_t *i_ctx_p)
+{
+ return ref_stack_max_count(&d_stack);
+}
+static int
+set_MaxDictStack(i_ctx_t *i_ctx_p, long val)
+{
+ return ref_stack_set_max_count(&d_stack, val);
+}
+static long
+current_MaxExecStack(i_ctx_t *i_ctx_p)
+{
+ return ref_stack_max_count(&e_stack);
+}
+static int
+set_MaxExecStack(i_ctx_t *i_ctx_p, long val)
+{
+ return ref_stack_set_max_count(&e_stack, val);
+}
+static long
+current_MaxLocalVM(i_ctx_t *i_ctx_p)
+{
+ gs_memory_gc_status_t stat;
+
+ gs_memory_gc_status(iimemory_local, &stat);
+ return stat.max_vm;
+}
+static int
+set_MaxLocalVM(i_ctx_t *i_ctx_p, long val)
+{
+ gs_memory_gc_status_t stat;
+
+ gs_memory_gc_status(iimemory_local, &stat);
+ stat.max_vm = max(val, 0);
+ gs_memory_set_gc_status(iimemory_local, &stat);
+ return 0;
+}
+static long
+current_VMReclaim(i_ctx_t *i_ctx_p)
+{
+ gs_memory_gc_status_t gstat, lstat;
+
+ gs_memory_gc_status(iimemory_global, &gstat);
+ gs_memory_gc_status(iimemory_local, &lstat);
+ return (!gstat.enabled ? -2 : !lstat.enabled ? -1 : 0);
+}
+static long
+current_VMThreshold(i_ctx_t *i_ctx_p)
+{
+ gs_memory_gc_status_t stat;
+
+ gs_memory_gc_status(iimemory_local, &stat);
+ return stat.vm_threshold;
+}
+static long
+current_WaitTimeout(i_ctx_t *i_ctx_p)
+{
+ return 0;
+}
+static int
+set_WaitTimeout(i_ctx_t *i_ctx_p, long val)
+{
+ return 0;
+}
+static long
+current_MinScreenLevels(i_ctx_t *i_ctx_p)
+{
+ return gs_currentminscreenlevels(imemory);
+}
+static int
+set_MinScreenLevels(i_ctx_t *i_ctx_p, long val)
+{
+ gs_setminscreenlevels(imemory, (uint) val);
+ return 0;
+}
+static long
+current_AlignToPixels(i_ctx_t *i_ctx_p)
+{
+ return gs_currentaligntopixels(ifont_dir);
+}
+static int
+set_AlignToPixels(i_ctx_t *i_ctx_p, long val)
+{
+ gs_setaligntopixels(ifont_dir, (uint)val);
+ return 0;
+}
+static long
+current_GridFitTT(i_ctx_t *i_ctx_p)
+{
+ return gs_currentgridfittt(ifont_dir);
+}
+static int
+set_GridFitTT(i_ctx_t *i_ctx_p, long val)
+{
+ gs_setgridfittt(ifont_dir, (uint)val);
+ return 0;
+}
+
+#undef ifont_dir
+
+static void
+current_devicen_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ gs_currentdevicenicc(igs, pval);
+}
+
+static int
+set_devicen_profile_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ return gs_setdevicenprofileicc(igs, pval);
+}
+
+static void
+current_default_gray_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ gs_currentdefaultgrayicc(igs, pval);
+}
+
+static int
+set_default_gray_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ return gs_setdefaultgrayicc(igs, pval);
+}
+
+static void
+current_icc_directory(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ gs_currenticcdirectory(igs, pval);
+}
+
+static int
+set_icc_directory(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ return gs_seticcdirectory(igs, pval);
+}
+
+static void
+current_srcgtag_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ gs_currentsrcgtagicc(igs, pval);
+}
+
+static int
+set_srcgtag_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ return gs_setsrcgtagicc(igs, pval);
+}
+
+static void
+current_default_rgb_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ gs_currentdefaultrgbicc(igs, pval);
+}
+
+static int
+set_default_rgb_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ return gs_setdefaultrgbicc(igs, pval);
+}
+
+static void
+current_named_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ gs_currentnamedicc(igs, pval);
+}
+
+static int
+set_named_profile_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ return gs_setnamedprofileicc(igs, pval);
+}
+
+static void
+current_default_cmyk_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ gs_currentdefaultcmykicc(igs, pval);
+}
+
+static int
+set_default_cmyk_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ return gs_setdefaultcmykicc(igs, pval);
+}
+
+static void
+current_lab_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ gs_currentlabicc(igs, pval);
+}
+
+static int
+set_lab_icc(i_ctx_t *i_ctx_p, gs_param_string * pval)
+{
+ return gs_setlabicc(igs, pval);
+}
+
+static const long_param_def_t user_long_params[] =
+{
+ {"JobTimeout", 0, MAX_UINT_PARAM,
+ current_JobTimeout, set_JobTimeout},
+ {"MaxFontItem", MIN_INT_PARAM, MAX_UINT_PARAM,
+ current_MaxFontItem, set_MaxFontItem},
+ {"MinFontCompress", MIN_INT_PARAM, MAX_UINT_PARAM,
+ current_MinFontCompress, set_MinFontCompress},
+ {"MaxOpStack", 0, MAX_UINT_PARAM,
+ current_MaxOpStack, set_MaxOpStack},
+ {"MaxDictStack", 0, MAX_UINT_PARAM,
+ current_MaxDictStack, set_MaxDictStack},
+ {"MaxExecStack", 0, MAX_UINT_PARAM,
+ current_MaxExecStack, set_MaxExecStack},
+ {"MaxLocalVM", 0, max_long,
+ current_MaxLocalVM, set_MaxLocalVM},
+ {"VMReclaim", -2, 0,
+ current_VMReclaim, set_vm_reclaim},
+ {"VMThreshold", -1, max_long,
+ current_VMThreshold, set_vm_threshold},
+ {"WaitTimeout", 0, MAX_UINT_PARAM,
+ current_WaitTimeout, set_WaitTimeout},
+ /* Extensions */
+ {"MinScreenLevels", 0, MAX_UINT_PARAM,
+ current_MinScreenLevels, set_MinScreenLevels},
+ {"AlignToPixels", 0, 1,
+ current_AlignToPixels, set_AlignToPixels},
+ {"GridFitTT", 0, 3,
+ current_GridFitTT, set_GridFitTT}
+};
+
+/* Note that string objects that are maintained as user params must be
+ either allocated in non-gc memory or be a constant in the executable.
+ The problem stems from the way userparams are retained during garbage
+ collection in a param_list (collected by currentuserparams). For
+ some reason this param_list does not get the pointers to strings relocated
+ during the GC. Note that the param_dict itself is correctly updated by reloc,
+ it is just the pointers to the strings in the param_list that are not traced
+ and updated. An example of this includes the ICCProfilesDir, which sets a
+ string in the icc_manager. When a reclaim occurs, the string is relocated
+ (when in non-gc memory and when it is noted to the gc with the proper object
+ descriptor). Then if a set_icc_directory occurs, the user params pointer has
+ NOT been updated and validation problems will occur. */
+static const string_param_def_t user_string_params[] =
+{
+ {"DefaultGrayProfile", current_default_gray_icc, set_default_gray_icc},
+ {"DefaultRGBProfile", current_default_rgb_icc, set_default_rgb_icc},
+ {"DefaultCMYKProfile", current_default_cmyk_icc, set_default_cmyk_icc},
+ {"NamedProfile", current_named_icc, set_named_profile_icc},
+ {"ICCProfilesDir", current_icc_directory, set_icc_directory},
+ {"LabProfile", current_lab_icc, set_lab_icc},
+ {"DeviceNProfile", current_devicen_icc, set_devicen_profile_icc},
+ {"SourceObjectICC", current_srcgtag_icc, set_srcgtag_icc}
+};
+
+/* Boolean values */
+static bool
+current_AccurateScreens(i_ctx_t *i_ctx_p)
+{
+ return gs_currentaccuratescreens(imemory);
+}
+static int
+set_AccurateScreens(i_ctx_t *i_ctx_p, bool val)
+{
+ gs_setaccuratescreens(imemory, val);
+ return 0;
+}
+/* Boolean values */
+static bool
+current_OverrideICC(i_ctx_t *i_ctx_p)
+{
+ const gs_imager_state * pis = (gs_imager_state *) igs;
+ return gs_currentoverrideicc(pis);
+}
+static int
+set_OverrideICC(i_ctx_t *i_ctx_p, bool val)
+{
+ gs_imager_state * pis = (gs_imager_state *) igs;
+ gs_setoverrideicc(pis, val);
+ return 0;
+}
+static bool
+current_LockFilePermissions(i_ctx_t *i_ctx_p)
+{
+ return i_ctx_p->LockFilePermissions;
+}
+static int
+set_LockFilePermissions(i_ctx_t *i_ctx_p, bool val)
+{
+ /* allow locking even if already locked */
+ if (i_ctx_p->LockFilePermissions && !val)
+ return_error(gs_error_invalidaccess);
+ i_ctx_p->LockFilePermissions = val;
+ return 0;
+}
+static bool
+current_RenderTTNotdef(i_ctx_t *i_ctx_p)
+{
+ return i_ctx_p->RenderTTNotdef;
+}
+static int
+set_RenderTTNotdef(i_ctx_t *i_ctx_p, bool val)
+{
+ i_ctx_p->RenderTTNotdef = val;
+ return 0;
+}
+static const bool_param_def_t user_bool_params[] =
+{
+ {"AccurateScreens", current_AccurateScreens, set_AccurateScreens},
+ {"LockFilePermissions", current_LockFilePermissions, set_LockFilePermissions},
+ {"RenderTTNotdef", current_RenderTTNotdef, set_RenderTTNotdef},
+ {"OverrideICC", current_OverrideICC, set_OverrideICC}
+};
+
+/* The user parameter set */
+static const param_set user_param_set =
+{
+ user_long_params, countof(user_long_params),
+ user_bool_params, countof(user_bool_params),
+ user_string_params, countof(user_string_params)
+};
+
+/* <dict> .setuserparams - */
+/* We break this out for use when switching contexts. */
+int
+set_user_params(i_ctx_t *i_ctx_p, const ref *paramdict)
+{
+ dict_param_list list;
+ int code;
+
+ check_type(*paramdict, t_dictionary);
+ code = dict_param_list_read(&list, paramdict, NULL, false, iimemory);
+ if (code < 0)
+ return code;
+ code = setparams(i_ctx_p, (gs_param_list *)&list, &user_param_set);
+ iparam_list_release(&list);
+ return code;
+}
+static int
+zsetuserparams(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ int code = set_user_params(i_ctx_p, op);
+
+ if (code >= 0) {
+ /* Update cached scanner options. */
+ i_ctx_p->scanner_options =
+ ztoken_scanner_options(op, i_ctx_p->scanner_options);
+ pop(1);
+ }
+ return code;
+}
+
+/* - .currentuserparams <name1> <value1> ... */
+static int
+zcurrentuserparams(i_ctx_t *i_ctx_p)
+{
+ return currentparams(i_ctx_p, &user_param_set);
+}
+
+/* <name> .getuserparam <value> */
+static int
+zgetuserparam(i_ctx_t *i_ctx_p)
+{
+ return currentparam1(i_ctx_p, &user_param_set);
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zusparam_op_defs[] =
+{
+ /* User and system parameters are accessible even in Level 1 */
+ /* (if this is a Level 2 system). */
+ {"0.currentsystemparams", zcurrentsystemparams},
+ {"0.currentuserparams", zcurrentuserparams},
+ {"1.getsystemparam", zgetsystemparam},
+ {"1.getuserparam", zgetuserparam},
+ {"1.setsystemparams", zsetsystemparams},
+ {"1.setuserparams", zsetuserparams},
+ /* The rest of the operators are defined only in Level 2. */
+ op_def_begin_level2(),
+ {"1.checkpassword", zcheckpassword},
+ op_def_end(0)
+};
+
+/* ------ Internal procedures ------ */
+
+/* Set the values of a parameter set from a parameter list. */
+/* We don't attempt to back out if anything fails. */
+static int
+setparams(i_ctx_t *i_ctx_p, gs_param_list * plist, const param_set * pset)
+{
+ int code;
+ unsigned int i;
+
+ for (i = 0; i < pset->long_count; i++) {
+ const long_param_def_t *pdef = &pset->long_defs[i];
+ long val;
+
+ if (pdef->set == NULL)
+ continue;
+ code = param_read_long(plist, pdef->pname, &val);
+ switch (code) {
+ default: /* invalid */
+ return code;
+ case 1: /* missing */
+ break;
+ case 0:
+ if (val < pdef->min_value || val > pdef->max_value)
+ return_error(gs_error_rangecheck);
+ code = (*pdef->set)(i_ctx_p, val);
+ if (code < 0)
+ return code;
+ }
+ }
+ for (i = 0; i < pset->bool_count; i++) {
+ const bool_param_def_t *pdef = &pset->bool_defs[i];
+ bool val;
+
+ if (pdef->set == NULL)
+ continue;
+ code = param_read_bool(plist, pdef->pname, &val);
+ if (code == 0)
+ code = (*pdef->set)(i_ctx_p, val);
+ if (code < 0)
+ return code;
+ }
+
+ for (i = 0; i < pset->string_count; i++) {
+ const string_param_def_t *pdef = &pset->string_defs[i];
+ gs_param_string val;
+
+ if (pdef->set == NULL)
+ continue;
+ code = param_read_string(plist, pdef->pname, &val);
+ switch (code) {
+ default: /* invalid */
+ return code;
+ case 1: /* missing */
+ break;
+ case 0:
+ code = (*pdef->set)(i_ctx_p, &val);
+ if (code < 0)
+ return code;
+ }
+ }
+
+ return 0;
+}
+
+/* Get the current values of a parameter set to the stack. */
+static bool
+pname_matches(const char *pname, const ref * psref)
+{
+ return
+ (psref == 0 ||
+ !bytes_compare((const byte *)pname, strlen(pname),
+ psref->value.const_bytes, r_size(psref)));
+}
+static int
+current_param_list(i_ctx_t *i_ctx_p, const param_set * pset,
+ const ref * psref /*t_string */ )
+{
+ stack_param_list list;
+ gs_param_list *const plist = (gs_param_list *)&list;
+ int code = 0;
+ unsigned int i;
+
+ stack_param_list_write(&list, &o_stack, NULL, iimemory);
+ for (i = 0; i < pset->long_count; i++) {
+ const char *pname = pset->long_defs[i].pname;
+
+ if (pname_matches(pname, psref)) {
+ long val = (*pset->long_defs[i].current)(i_ctx_p);
+
+ code = param_write_long(plist, pname, &val);
+ if (code < 0)
+ return code;
+ }
+ }
+ for (i = 0; i < pset->bool_count; i++) {
+ const char *pname = pset->bool_defs[i].pname;
+
+ if (pname_matches(pname, psref)) {
+ bool val = (*pset->bool_defs[i].current)(i_ctx_p);
+
+ code = param_write_bool(plist, pname, &val);
+ if (code < 0)
+ return code;
+ }
+ }
+ for (i = 0; i < pset->string_count; i++) {
+ const char *pname = pset->string_defs[i].pname;
+
+ if (pname_matches(pname, psref)) {
+ gs_param_string val;
+
+ (*pset->string_defs[i].current)(i_ctx_p, &val);
+ code = param_write_string(plist, pname, &val);
+ if (code < 0)
+ return code;
+ }
+ }
+ if (psref) {
+ /*
+ * Scanner options can be read, but only individually by .getuserparam.
+ * This avoids putting them into userparams, and being affected by save/restore.
+ */
+ const char *pname;
+ bool val;
+ int code;
+
+ switch (ztoken_get_scanner_option(psref, i_ctx_p->scanner_options, &pname)) {
+ case 0:
+ code = param_write_null(plist, pname);
+ break;
+ case 1:
+ val = true;
+ code = param_write_bool(plist, pname, &val);
+ break;
+ default:
+ code = 0;
+ break;
+ }
+ if (code < 0)
+ return code;
+ }
+ return code;
+}
+
+/* Get the current values of a parameter set to the stack. */
+static int
+currentparams(i_ctx_t *i_ctx_p, const param_set * pset)
+{
+ return current_param_list(i_ctx_p, pset, NULL);
+}
+
+/* Get the value of a single parameter to the stack, or signal an error. */
+static int
+currentparam1(i_ctx_t *i_ctx_p, const param_set * pset)
+{
+ os_ptr op = osp;
+ ref sref;
+ int code;
+
+ check_type(*op, t_name);
+ check_ostack(2);
+ name_string_ref(imemory, (const ref *)op, &sref);
+ code = current_param_list(i_ctx_p, pset, &sref);
+ if (code < 0)
+ return code;
+ if (osp == op)
+ return_error(gs_error_undefined);
+ /* We know osp == op + 2. */
+ ref_assign(op, op + 2);
+ pop(2);
+ return code;
+}
diff --git a/psi/zutf8.c b/psi/zutf8.c
new file mode 100644
index 000000000..c195cb706
--- /dev/null
+++ b/psi/zutf8.c
@@ -0,0 +1,82 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* The .locale_to_utf8 operator, for converting text from the locale
+ * charset to UTF-8. This code is not used on Windows; there's a
+ * separate Windows implementation in zwinutf8.c. */
+
+#include "ghost.h"
+#include "oper.h"
+#include "iutil.h"
+#include "ialloc.h"
+#include "malloc_.h"
+#include "errno_.h"
+#include "string_.h"
+#include "store.h"
+#include <stringprep.h>
+
+/* Convert a string from the current locale's character set to UTF-8.
+ * <string> .locale_to_utf8 <string> */
+static int
+zlocale_to_utf8(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ char *input;
+ char *output;
+ int code;
+
+ check_read_type(*op, t_string);
+ input = ref_to_string(op, imemory, "locale_to_utf8 input");
+ if (input == 0)
+ return_error(gs_error_VMerror);
+
+ output = stringprep_locale_to_utf8(input);
+ ifree_string((byte *)input, r_size(op) + 1, "locale_to_utf8 input");
+ if (output == 0) {
+ /* This function is intended to be used on strings whose
+ * character set is unknown, so it's not an error if the
+ * input contains invalid characters. Just return the input
+ * string unchanged.
+ *
+ * Sadly, EINVAL from stringprep_locale_to_utf8 can mean
+ * either an invalid character set conversion (which we care
+ * about), or an incomplete input string (which we don't).
+ * For now, we ignore EINVAL; the right solution is probably
+ * to not use stringprep_locale_to_utf8, and just call iconv
+ * by hand. */
+ if (errno == EILSEQ || errno == EINVAL)
+ return 0;
+
+ /* Other errors (like ENFILE) are real errors, which we
+ * want to return to the user. */
+ return_error(gs_error_ioerror);
+ }
+
+ code = string_to_ref(output, op, iimemory, "locale_to_utf8 output");
+ free(output);
+ if (code < 0)
+ return code;
+
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zutf8_op_defs[] =
+{
+ {"1.locale_to_utf8", zlocale_to_utf8},
+ op_def_end(0)
+};
diff --git a/psi/zvmem.c b/psi/zvmem.c
new file mode 100644
index 000000000..a00beb1f1
--- /dev/null
+++ b/psi/zvmem.c
@@ -0,0 +1,439 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* "Virtual memory" operators */
+#include "stat_.h" /* get system header early to avoid name clash on Cygwin */
+#include "ghost.h"
+#include "gsstruct.h"
+#include "oper.h"
+#include "estack.h" /* for checking in restore */
+#include "ialloc.h"
+#include "idict.h" /* ditto */
+#include "igstate.h"
+#include "isave.h"
+#include "dstack.h"
+#include "stream.h" /* for files.h */
+#include "files.h" /* for e-stack processing */
+#include "store.h"
+#include "gsmatrix.h" /* for gsstate.h */
+#include "gsstate.h"
+
+/* Define whether we validate memory before/after save/restore. */
+/* Note that we only actually do this if DEBUG is set and -Z? is selected. */
+static const bool I_VALIDATE_BEFORE_SAVE = true;
+static const bool I_VALIDATE_AFTER_SAVE = true;
+static const bool I_VALIDATE_BEFORE_RESTORE = true;
+static const bool I_VALIDATE_AFTER_RESTORE = true;
+
+/* 'Save' structure */
+typedef struct vm_save_s vm_save_t;
+struct vm_save_s {
+ gs_state *gsave; /* old graphics state */
+};
+
+gs_private_st_ptrs1(st_vm_save, vm_save_t, "savetype",
+ vm_save_enum_ptrs, vm_save_reloc_ptrs, gsave);
+
+/* Clean up the stacks and validate storage. */
+void
+ivalidate_clean_spaces(i_ctx_t *i_ctx_p)
+{
+ if (gs_debug_c('?')) {
+ ref_stack_cleanup(&d_stack);
+ ref_stack_cleanup(&e_stack);
+ ref_stack_cleanup(&o_stack);
+ ivalidate_spaces();
+ }
+}
+
+/* - save <save> */
+int
+zsave(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ uint space = icurrent_space;
+ vm_save_t *vmsave;
+ ulong sid;
+ int code;
+ gs_state *prev;
+
+ if (I_VALIDATE_BEFORE_SAVE)
+ ivalidate_clean_spaces(i_ctx_p);
+ ialloc_set_space(idmemory, avm_local);
+ vmsave = ialloc_struct(vm_save_t, &st_vm_save, "zsave");
+ ialloc_set_space(idmemory, space);
+ if (vmsave == 0)
+ return_error(gs_error_VMerror);
+ vmsave->gsave = NULL; /* Ensure constructed enough to destroy safely */
+ code = alloc_save_state(idmemory, vmsave, &sid);
+ if (code < 0)
+ return code;
+ if (sid == 0) {
+ ifree_object(vmsave, "zsave");
+ return_error(gs_error_VMerror);
+ }
+ if_debug2m('u', imemory, "[u]vmsave 0x%lx, id = %lu\n",
+ (ulong) vmsave, (ulong) sid);
+ code = gs_gsave_for_save(igs, &prev);
+ if (code < 0)
+ return code;
+ code = gs_gsave(igs);
+ if (code < 0)
+ return code;
+ vmsave->gsave = prev;
+ push(1);
+ make_tav(op, t_save, 0, saveid, sid);
+ if (I_VALIDATE_AFTER_SAVE)
+ ivalidate_clean_spaces(i_ctx_p);
+ return 0;
+}
+
+/* <save> restore - */
+static int restore_check_operand(os_ptr, alloc_save_t **, gs_dual_memory_t *);
+static int restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t *, const alloc_save_t *, bool);
+static void restore_fix_stack(i_ctx_t *i_ctx_p, ref_stack_t *, const alloc_save_t *, bool);
+int
+zrestore(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ alloc_save_t *asave;
+ bool last;
+ vm_save_t *vmsave;
+ int code = restore_check_operand(op, &asave, idmemory);
+
+ if (code < 0)
+ return code;
+ if_debug2m('u', imemory, "[u]vmrestore 0x%lx, id = %lu\n",
+ (ulong) alloc_save_client_data(asave),
+ (ulong) op->value.saveid);
+ if (I_VALIDATE_BEFORE_RESTORE)
+ ivalidate_clean_spaces(i_ctx_p);
+ /* Check the contents of the stacks. */
+ osp--;
+ {
+ int code;
+
+ if ((code = restore_check_stack(i_ctx_p, &o_stack, asave, false)) < 0 ||
+ (code = restore_check_stack(i_ctx_p, &e_stack, asave, true)) < 0 ||
+ (code = restore_check_stack(i_ctx_p, &d_stack, asave, false)) < 0
+ ) {
+ osp++;
+ return code;
+ }
+ }
+ /* Reset l_new in all stack entries if the new save level is zero. */
+ /* Also do some special fixing on the e-stack. */
+ restore_fix_stack(i_ctx_p, &o_stack, asave, false);
+ restore_fix_stack(i_ctx_p, &e_stack, asave, true);
+ restore_fix_stack(i_ctx_p, &d_stack, asave, false);
+ /* Iteratively restore the state of memory, */
+ /* also doing a grestoreall at each step. */
+ do {
+ vmsave = alloc_save_client_data(alloc_save_current(idmemory));
+ /* Restore the graphics state. */
+ gs_grestoreall_for_restore(igs, vmsave->gsave);
+ /*
+ * If alloc_save_space decided to do a second save, the vmsave
+ * object was allocated one save level less deep than the
+ * current level, so ifree_object won't actually free it;
+ * however, it points to a gsave object that definitely
+ * *has* been freed. In order not to trip up the garbage
+ * collector, we clear the gsave pointer now.
+ */
+ vmsave->gsave = 0;
+ /* Now it's safe to restore the state of memory. */
+ code = alloc_restore_step_in(idmemory, asave);
+ if (code < 0)
+ return code;
+ last = code;
+ }
+ while (!last);
+ {
+ uint space = icurrent_space;
+
+ ialloc_set_space(idmemory, avm_local);
+ ifree_object(vmsave, "zrestore");
+ ialloc_set_space(idmemory, space);
+ }
+ dict_set_top(); /* reload dict stack cache */
+ if (I_VALIDATE_AFTER_RESTORE)
+ ivalidate_clean_spaces(i_ctx_p);
+ /* If the i_ctx_p LockFilePermissions is true, but the userparams */
+ /* we just restored is false, we need to make sure that we do not */
+ /* cause an 'invalidaccess' in setuserparams. Temporarily set */
+ /* LockFilePermissions false until the gs_lev2.ps can do a */
+ /* setuserparams from the restored userparam dictionary. */
+ i_ctx_p->LockFilePermissions = false;
+ return 0;
+}
+/* Check the operand of a restore. */
+static int
+restore_check_operand(os_ptr op, alloc_save_t ** pasave,
+ gs_dual_memory_t *idmem)
+{
+ vm_save_t *vmsave;
+ ulong sid;
+ alloc_save_t *asave;
+
+ check_type(*op, t_save);
+ vmsave = r_ptr(op, vm_save_t);
+ if (vmsave == 0) /* invalidated save */
+ return_error(gs_error_invalidrestore);
+ sid = op->value.saveid;
+ asave = alloc_find_save(idmem, sid);
+ if (asave == 0)
+ return_error(gs_error_invalidrestore);
+ *pasave = asave;
+ return 0;
+}
+/* Check a stack to make sure all its elements are older than a save. */
+static int
+restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t * pstack,
+ const alloc_save_t * asave, bool is_estack)
+{
+ ref_stack_enum_t rsenum;
+
+ ref_stack_enum_begin(&rsenum, pstack);
+ do {
+ const ref *stkp = rsenum.ptr;
+ uint size = rsenum.size;
+
+ for (; size; stkp++, size--) {
+ const void *ptr;
+
+ switch (r_type(stkp)) {
+ case t_array:
+ /*
+ * Zero-length arrays are a special case: see the
+ * t_*array case (label rr:) in igc.c:gc_trace.
+ */
+ if (r_size(stkp) == 0) {
+ /*stkp->value.refs = (void *)0;*/
+ continue;
+ }
+ ptr = stkp->value.refs;
+ break;
+ case t_dictionary:
+ ptr = stkp->value.pdict;
+ break;
+ case t_file:
+ /* Don't check executable or closed literal */
+ /* files on the e-stack. */
+ {
+ stream *s;
+
+ if (is_estack &&
+ (r_has_attr(stkp, a_executable) ||
+ file_is_invalid(s, stkp))
+ )
+ continue;
+ }
+ ptr = stkp->value.pfile;
+ break;
+ case t_name:
+ /* Names are special because of how they are allocated. */
+ if (alloc_name_is_since_save((const gs_memory_t *)pstack->memory,
+ stkp, asave))
+ return_error(gs_error_invalidrestore);
+ continue;
+ case t_string:
+ /* Don't check empty executable strings */
+ /* on the e-stack. */
+ if (r_size(stkp) == 0 &&
+ r_has_attr(stkp, a_executable) && is_estack
+ )
+ continue;
+ ptr = stkp->value.bytes;
+ break;
+ case t_mixedarray:
+ case t_shortarray:
+ /* See the t_array case above. */
+ if (r_size(stkp) == 0) {
+ /*stkp->value.packed = (void *)0;*/
+ continue;
+ }
+ ptr = stkp->value.packed;
+ break;
+ case t_device:
+ ptr = stkp->value.pdevice;
+ break;
+ case t_fontID:
+ case t_struct:
+ case t_astruct:
+ ptr = stkp->value.pstruct;
+ break;
+ case t_save:
+ /* See the comment in isave.h regarding the following. */
+ if (i_ctx_p->language_level <= 2)
+ continue;
+ ptr = alloc_find_save(&gs_imemory, stkp->value.saveid);
+ /*
+ * Invalid save objects aren't supposed to be possible
+ * in LL3, but just in case....
+ */
+ if (ptr == 0)
+ return_error(gs_error_invalidrestore);
+ if (ptr == asave)
+ continue;
+ break;
+ default:
+ continue;
+ }
+ if (alloc_is_since_save(ptr, asave))
+ return_error(gs_error_invalidrestore);
+ }
+ } while (ref_stack_enum_next(&rsenum));
+ return 0; /* OK */
+}
+/*
+ * If the new save level is zero, fix up the contents of a stack
+ * by clearing the l_new bit in all the entries (since we can't tolerate
+ * values with l_new set if the save level is zero).
+ * Also, in any case, fix up the e-stack by replacing empty executable
+ * strings and closed executable files that are newer than the save
+ * with canonical ones that aren't.
+ *
+ * Note that this procedure is only called if restore_check_stack succeeded.
+ */
+static void
+restore_fix_stack(i_ctx_t *i_ctx_p, ref_stack_t * pstack,
+ const alloc_save_t * asave, bool is_estack)
+{
+ ref_stack_enum_t rsenum;
+
+ ref_stack_enum_begin(&rsenum, pstack);
+ do {
+ ref *stkp = rsenum.ptr;
+ uint size = rsenum.size;
+
+ for (; size; stkp++, size--) {
+ r_clear_attrs(stkp, l_new); /* always do it, no harm */
+ if (is_estack) {
+ ref ofile;
+
+ ref_assign(&ofile, stkp);
+ switch (r_type(stkp)) {
+ case t_string:
+ if (r_size(stkp) == 0 &&
+ alloc_is_since_save(stkp->value.bytes,
+ asave)
+ ) {
+ make_empty_const_string(stkp,
+ avm_foreign);
+ break;
+ }
+ continue;
+ case t_file:
+ if (alloc_is_since_save(stkp->value.pfile,
+ asave)
+ ) {
+ make_invalid_file(i_ctx_p, stkp);
+ break;
+ }
+ continue;
+ default:
+ continue;
+ }
+ r_copy_attrs(stkp, a_all | a_executable,
+ &ofile);
+ }
+ }
+ } while (ref_stack_enum_next(&rsenum));
+}
+
+/* - vmstatus <save_level> <vm_used> <vm_maximum> */
+static int
+zvmstatus(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ gs_memory_status_t mstat, dstat;
+
+ gs_memory_status(imemory, &mstat);
+ if (imemory == imemory_global) {
+ gs_memory_status_t sstat;
+
+ gs_memory_status(imemory_system, &sstat);
+ mstat.allocated += sstat.allocated;
+ mstat.used += sstat.used;
+ }
+ gs_memory_status(imemory->non_gc_memory, &dstat);
+ push(3);
+ make_int(op - 2, imemory_save_level(iimemory_local));
+ make_int(op - 1, mstat.used);
+ make_int(op, mstat.allocated + dstat.allocated - dstat.used);
+ return 0;
+}
+
+/* ------ Non-standard extensions ------ */
+
+/* <save> .forgetsave - */
+static int
+zforgetsave(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ alloc_save_t *asave;
+ vm_save_t *vmsave;
+ int code = restore_check_operand(op, &asave, idmemory);
+
+ if (code < 0)
+ return 0;
+ vmsave = alloc_save_client_data(asave);
+ /* Reset l_new in all stack entries if the new save level is zero. */
+ restore_fix_stack(i_ctx_p, &o_stack, asave, false);
+ restore_fix_stack(i_ctx_p, &e_stack, asave, false);
+ restore_fix_stack(i_ctx_p, &d_stack, asave, false);
+ /*
+ * Forget the gsaves, by deleting the bottom gstate on
+ * the current stack and the top one on the saved stack and then
+ * concatenating the stacks together.
+ */
+ {
+ gs_state *pgs = igs;
+ gs_state *last;
+
+ while (gs_state_saved(last = gs_state_saved(pgs)) != 0)
+ pgs = last;
+ gs_state_swap_saved(last, vmsave->gsave);
+ gs_grestore(last);
+ gs_grestore(last);
+ }
+ /* Forget the save in the memory manager. */
+ code = alloc_forget_save_in(idmemory, asave);
+ if (code < 0)
+ return code;
+ {
+ uint space = icurrent_space;
+
+ ialloc_set_space(idmemory, avm_local);
+ /* See above for why we clear the gsave pointer here. */
+ vmsave->gsave = 0;
+ ifree_object(vmsave, "zrestore");
+ ialloc_set_space(idmemory, space);
+ }
+ pop(1);
+ return 0;
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zvmem_op_defs[] =
+{
+ {"1.forgetsave", zforgetsave},
+ {"1restore", zrestore},
+ {"0save", zsave},
+ {"0vmstatus", zvmstatus},
+ op_def_end(0)
+};
diff --git a/psi/zvmem2.c b/psi/zvmem2.c
new file mode 100644
index 000000000..0edfd5756
--- /dev/null
+++ b/psi/zvmem2.c
@@ -0,0 +1,139 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Level 2 "Virtual memory" operators */
+#include "ghost.h"
+#include "oper.h"
+#include "estack.h"
+#include "ialloc.h" /* for ivmspace.h */
+#include "ivmspace.h"
+#include "ivmem2.h"
+#include "store.h"
+
+/* Garbage collector control parameters. */
+#define DEFAULT_VM_THRESHOLD_SMALL 100000
+#define DEFAULT_VM_THRESHOLD_LARGE 8000000
+#define MIN_VM_THRESHOLD 1
+#define MAX_VM_THRESHOLD max_long
+
+/* ------ Local/global VM control ------ */
+
+/* <bool> .setglobal - */
+static int
+zsetglobal(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+ check_type(*op, t_boolean);
+ ialloc_set_space(idmemory,
+ (op->value.boolval ? avm_global : avm_local));
+ pop(1);
+ return 0;
+}
+
+/* <bool> .currentglobal - */
+static int
+zcurrentglobal(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ push(1);
+ make_bool(op, ialloc_space(idmemory) != avm_local);
+ return 0;
+}
+
+/* <any> gcheck/scheck <bool> */
+static int
+zgcheck(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_op(1);
+ make_bool(op, (r_is_local(op) ? false : true));
+ return 0;
+}
+
+/* ------ Garbage collector control ------ */
+
+/* These routines are exported for setuserparams. */
+
+/*
+ * <int> setvmthreshold -
+ *
+ * This is implemented as a PostScript procedure that calls setuserparams.
+ */
+int
+set_vm_threshold(i_ctx_t *i_ctx_p, long val)
+{
+ if (val < -1)
+ return_error(gs_error_rangecheck);
+ else if (val == -1)
+ val = (gs_debug_c('.') ? DEFAULT_VM_THRESHOLD_SMALL :
+ DEFAULT_VM_THRESHOLD_LARGE);
+ else if (val < MIN_VM_THRESHOLD)
+ val = MIN_VM_THRESHOLD;
+ else if (val > MAX_VM_THRESHOLD)
+ val = MAX_VM_THRESHOLD;
+ gs_memory_set_vm_threshold(idmemory->space_global, val);
+ gs_memory_set_vm_threshold(idmemory->space_local, val);
+ return 0;
+}
+
+int
+set_vm_reclaim(i_ctx_t *i_ctx_p, long val)
+{
+ if (val >= -2 && val <= 0) {
+ gs_memory_set_vm_reclaim(idmemory->space_system, (val >= -1));
+ gs_memory_set_vm_reclaim(idmemory->space_global, (val >= -1));
+ gs_memory_set_vm_reclaim(idmemory->space_local, (val == 0));
+ return 0;
+ } else
+ return_error(gs_error_rangecheck);
+}
+
+/*
+ * <int> .vmreclaim -
+ *
+ * This implements only immediate garbage collection: enabling and
+ * disabling GC is implemented by calling setuserparams.
+ */
+static int
+zvmreclaim(i_ctx_t *i_ctx_p)
+{
+ os_ptr op = osp;
+
+ check_type(*op, t_integer);
+ if (op->value.intval == 1 || op->value.intval == 2) {
+ /* Force the interpreter to store its state and exit. */
+ /* The interpreter's caller will do the actual GC. */
+ return_error(gs_error_VMreclaim);
+ }
+ return_error(gs_error_rangecheck);
+}
+
+/* ------ Initialization procedure ------ */
+
+/* The VM operators are defined even if the initial language level is 1, */
+/* because we need them during initialization. */
+const op_def zvmem2_op_defs[] =
+{
+ {"0.currentglobal", zcurrentglobal},
+ {"1.gcheck", zgcheck},
+ {"1.setglobal", zsetglobal},
+ /* The rest of the operators are defined only in Level 2. */
+ op_def_begin_level2(),
+ {"1.vmreclaim", zvmreclaim},
+ op_def_end(0)
+};
diff --git a/psi/zwinutf8.c b/psi/zwinutf8.c
new file mode 100644
index 000000000..fe6865ef2
--- /dev/null
+++ b/psi/zwinutf8.c
@@ -0,0 +1,75 @@
+/* Copyright (C) 2001-2012 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., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* The Windows implementation of the .locale_to_utf8 operator. See
+ * also zutf8.c, which is the non-Windows implementation. */
+
+#include "ghost.h"
+#include "oper.h"
+#include "iutil.h"
+#include "store.h"
+#include "windows_.h"
+
+/* Convert a string from the current locale's character set to UTF-8.
+ * Unfortunately, "current locale" can mean a few different things on
+ * Windows -- we use the default ANSI code page, which does the right
+ * thing for command-line arguments (like "-sPDFPassword=foo") and
+ * for strings typed as input to gswin32.exe. It doesn't work for
+ * strings typed as input to gswin32c.exe, which are normally in the
+ * default OEM code page instead.
+ * <string> .locale_to_utf8 <string> */
+static int
+zlocale_to_utf8(i_ctx_t *i_ctx_p)
+{
+#define LOCALE_TO_UTF8_BUFFER_SIZE 1024
+ os_ptr op = osp;
+ char *input;
+ WCHAR wide_buffer[LOCALE_TO_UTF8_BUFFER_SIZE];
+ char utf8_buffer[LOCALE_TO_UTF8_BUFFER_SIZE];
+ int success;
+ int code;
+
+ check_read_type(*op, t_string);
+ input = ref_to_string(op, imemory, "locale_to_utf8 input");
+ if (input == 0)
+ return_error(gs_error_VMerror);
+
+ success = MultiByteToWideChar(CP_ACP, 0, input, -1,
+ wide_buffer, LOCALE_TO_UTF8_BUFFER_SIZE);
+ ifree_string((byte *)input, r_size(op) + 1, "locale_to_utf8 input");
+ if (success == 0)
+ return_error(gs_error_ioerror);
+
+ success = WideCharToMultiByte(CP_UTF8, 0, wide_buffer, -1,
+ utf8_buffer, LOCALE_TO_UTF8_BUFFER_SIZE, NULL, NULL);
+ if (success == 0)
+ return_error(gs_error_ioerror);
+
+ code = string_to_ref(utf8_buffer, op, iimemory, "locale_to_utf8 output");
+ if (code < 0)
+ return code;
+
+ return 0;
+#undef LOCALE_TO_UTF8_BUFFER_SIZE
+}
+
+/* ------ Initialization procedure ------ */
+
+const op_def zwinutf8_op_defs[] =
+{
+ {"1.locale_to_utf8", zlocale_to_utf8},
+ op_def_end(0)
+};