diff options
author | Chris Liddell <chris.liddell@artifex.com> | 2017-11-17 15:17:03 +0000 |
---|---|---|
committer | Robin Watts <Robin.Watts@artifex.com> | 2018-12-07 14:17:55 -0500 |
commit | abb742af3affbe00da625a1272d682ac5fd4c964 (patch) | |
tree | a201eae99bee574f5cf0fa2ffa208d9928c07e37 | |
parent | eab61428b3fa1e4078e71febefe406dad499814e (diff) | |
download | ghostpdl-abb742af3affbe00da625a1272d682ac5fd4c964.tar.gz |
Commit of gpdl-shared-device branch.
This commit is a squashed version of the gpdl-shared-device
branch. Essentially this is a first version of the new
language switching mechanism.
This does not build as part as "all", but rather as "experimental"
or "gpdl".
67 files changed, 3891 insertions, 1000 deletions
diff --git a/Resource/Init/gs_cet.ps b/Resource/Init/gs_cet.ps index 4261e7956..3faa18e3d 100644 --- a/Resource/Init/gs_cet.ps +++ b/Resource/Init/gs_cet.ps @@ -73,6 +73,53 @@ dup 7 -1 put % and GS_CHAR_FILL are now dynamic. Force CPSI compatibility. //true .setCPSImode +% Some QL jobs (/23-12B.PS for example) do: +% '(%stdin) (r) file' +% expecting that they are being read from stdin. +% if that is not the case, they block waiting for input +% from stdin. +% This hooks the 'file' operator', so if a job tries to +% open %stdin, and we're *not* reading the job from stdin +% it'll return currentfile instead. +/ofile /file load def +/file +{ + 1 index (%stdin) eq 2 index (%stdin%) eq or + { + ofile dup currentfile eq not + { + pop currentfile + }if + } + { + ofile + } ifelse +} bind odef +currentdict /ofile undef + +% One QL job (23-12B.PS) effectively does: +% (%stdin) (r) file 0 setfileposition +% expecting it to throw an ioerror (stdin not being a positionable +% stream. +% With the above 'file' hook, when running from a file +% rather than stdin, setfileposition will succeed, and we hit +% an infinite loop - '0 setfileposition' meaning we restart intepreting +% from the beginning of the file. +% Hook setfileposition to throw an error attempting to reposition +% the current file. +/osetfileposition /setfileposition load def +/setfileposition +{ + 1 index currentfile eq + { + /setfileposition cvx /ioerror signalerror + } + { + osetfileposition + } ifelse +} bind odef +currentdict /osetfileposition undef + setglobal % Remove all but the default page size (the 0 entry in the InputAttributes diff --git a/Resource/Init/gs_init.ps b/Resource/Init/gs_init.ps index dbd7f4ce4..124a65960 100644 --- a/Resource/Init/gs_init.ps +++ b/Resource/Init/gs_init.ps @@ -1239,33 +1239,46 @@ end /=== { ===only (\n) print } bind def % Create the initialization queue. - -/.delayed_init_queue 10 dict def +1183615869 internaldict dup +/.delayed_init_queue 10 dict put /.schedule_init % <priority> <proc> .schedule_init - { - //.delayed_init_queue 2 index known { + 1183615869 internaldict begin + .delayed_init_queue 2 index known { + end (.delayed_init_queue priority conflict with ) print 1 index = /.schedule_init cvx /configurationerror signalerror } if - //.delayed_init_queue 3 1 roll .growput + .delayed_init_queue 3 1 roll .growput + end } bind def +begin /.execute_scheduled_inits % - .execute_scheduled_inits - { - { 0 //null //.delayed_init_queue { % maxp {} p {} + 2 dict begin + /.newdelayed_init_queue + mark + 1183615869 internaldict /.delayed_init_queue get {} forall + .dicttomark def + { + 0 //null .newdelayed_init_queue { % maxp {} p {} 3 index 2 index lt { 4 2 roll } if pop pop } forall - exch //.delayed_init_queue exch undef + exch .newdelayed_init_queue exch undef dup //null eq { pop exit } if exec } loop + currentdict /.newdelayed_init_queue undef + end } bind def +end (END PROCS) VMDEBUG @@ -2025,9 +2038,21 @@ gsave } { { } } ifelse def -<1b> cvn { % UEL is <esc>%-12345X and acts the same as ^D - currentfile (%-12345X) .peekstring pop (%-12345X) eq <04> cvn load if -} bind def +<1b> cvn +.actonuel % UEL is <esc>%-12345X and acts the same as ^D +{systemdict (.forceinterp_exit) cvn known {//true} {//false} ifelse} +{//false} ifelse + +{ + { + currentfile (%-12345X) .peekstring pop (%-12345X) eq + { currentfile .forceinterp_exit } if + } +} +{ + {currentfile (%-12345X) .peekstring pop (%-12345X) eq <04> cvn load if} +} ifelse bind def + <1b45> cvn { } def % PJL reset prologue (ESC E) <1b451b> cvn <1b> cvn load def % PJL reset epilogue (ESC E + UEL) (\001M) cvn % TBCP initiator @@ -2265,6 +2290,7 @@ SAFER { .setsafeglobal } if /.patterntypes /.shadingtypes /.wheredict /.renderingintentdict /.currentmatrix /.setmatrix /.setlinecap /.setlinejoin /.sizeimagebox /.systemvmcheck + /.forceinterp_exit /.actonuel % Used by a free user in the Library of Congress. Apparently this is used to % draw a partial page, which is then filled in by the results of a barcode @@ -2454,9 +2480,10 @@ $error /.nosetlocal //false put } ifelse % Execute scheduled inits : -//.execute_scheduled_inits exec -currentdict /.execute_scheduled_inits undef -currentdict /.delayed_init_queue undef +1183615869 internaldict /.execute_scheduled_inits get exec +currentdict /.schedule_init undef +%currentdict /.execute_scheduled_inits undef +%currentdict /.delayed_init_queue undef % Make global and local FontDirectory read-only. FontDirectory readonly pop diff --git a/base/gp_mspol.c b/base/gp_mspol.c index fc5303b38..ad0dfa300 100644 --- a/base/gp_mspol.c +++ b/base/gp_mspol.c @@ -38,8 +38,8 @@ gp_check_interrupts(const gs_memory_t *mem) mem = gs_lib_ctx_get_non_gc_memory_t(); } #endif - if (mem && mem->gs_lib_ctx && mem->gs_lib_ctx->poll_fn) - return (*mem->gs_lib_ctx->poll_fn)(mem->gs_lib_ctx->caller_handle); + if (mem && mem->gs_lib_ctx && mem->gs_lib_ctx->core->poll_fn) + return (*mem->gs_lib_ctx->core->poll_fn)(mem->gs_lib_ctx->core->caller_handle); return 0; } #endif diff --git a/base/gs.mak b/base/gs.mak index 40c3a9dd6..39939a2a1 100644 --- a/base/gs.mak +++ b/base/gs.mak @@ -397,6 +397,7 @@ DEVICE_DEVS_ALL=$(DEVICE_DEVS) $(DEVICE_DEVS1) \ PSI_DEVS_ALL=$(GSPLAT_DEVS_ALL) \ $(PSI_FEATURE_DEVS) \ + $(PSD)iapi.dev \ $(FEATURE_DEVS) \ $(FEATURE_DEVS_EXTRA) \ $(DEVICE_DEVS_ALL) @@ -473,18 +474,26 @@ $(ld_tr) : \ $(EXP)$(GENCONF_XE) $(devs_tr) -h $(gconfxx_h) $(CONFILES) $(CONFLDTR) $(ld_tr) $(EXP)$(ECHOGS_XE) -a $(gconfxx_h) $(GCONFIG_EXTRAS) +gsnoapi_tr=$(GLGENDIR)$(D)gsnoapi.tr gs_tr=$(GLGENDIR)$(D)gs.tr igs_tr=$(GLGENDIR)$(D)igs.tr gsld_tr=$(GLGENDIR)$(D)gsld.tr -$(gs_tr): $(GS_MAK) $(GLSRCDIR)$(D)version.mak $(GENCONF_XE) $(ECHOGS_XE) $(ld_tr) $(devs_tr) $(PSI_DEVS_ALL) \ - $(GLGENDIR)$(D)libcore.dev $(MAKEDIRS) +gsnoapild_tr=$(GLGENDIR)$(D)gsnoapild.tr +$(gsnoapi_tr): $(GS_MAK) $(GLSRCDIR)$(D)version.mak $(GENCONF_XE) $(ECHOGS_XE) $(ld_tr) $(devs_tr) $(PSI_DEVS_ALL)\ + $(GLGENDIR)$(D)libcore.dev $(MAKEDIRS) $(EXP)$(ECHOGS_XE) -w $(igs_tr) - -include $(PSI_FEATURE_DEVS) $(EXP)$(GENCONF_XE) $(igs_tr) -h $(iconfxx_h) $(CONFILES) $(CONFLDTR) $(gsld_tr) $(EXP)$(ECHOGS_XE) -w $(iconfig_h) -R $(iconfxx_h) - $(EXP)$(ECHOGS_XE) -w $(gs_tr) -R $(devs_tr) - $(EXP)$(ECHOGS_XE) -a $(gs_tr) -R $(igs_tr) + $(EXP)$(ECHOGS_XE) -w $(gsnoapi_tr) -R $(devs_tr) + $(EXP)$(ECHOGS_XE) -a $(gsnoapi_tr) -R $(igs_tr) + $(EXP)$(GENCONF_XE) $(gsnoapi_tr) -h $(GLGENDIR)$(D)unused.h $(CONFILES) $(CONFLDTR) $(gsnoapild_tr) + +$(gs_tr): $(gsnoapi_tr) + $(EXP)$(ECHOGS_XE) -w $(gs_tr) -R $(gsnoapi_tr) + $(EXP)$(ECHOGS_XE) -a $(gs_tr) - -include $(PSD)iapi.dev $(EXP)$(GENCONF_XE) $(gs_tr) -h $(GLGENDIR)$(D)unused.h $(CONFILES) $(CONFLDTR) $(gsld_tr) + pcl_tr=$(GLGENDIR)$(D)pcl.tr ipcl_tr=$(GLGENDIR)$(D)ipcl.tr pclld_tr=$(GLGENDIR)$(D)pclld.tr diff --git a/base/gsalloc.c b/base/gsalloc.c index 110ad36e0..a83f9c270 100644 --- a/base/gsalloc.c +++ b/base/gsalloc.c @@ -2340,19 +2340,23 @@ trim_obj(gs_ref_memory_t *mem, obj_header_t *obj, uint size, clump_t *cp) /* Register a root. */ static int -i_register_root(gs_memory_t * mem, gs_gc_root_t * rp, gs_ptr_type_t ptype, +i_register_root(gs_memory_t * mem, gs_gc_root_t ** rpp, gs_ptr_type_t ptype, void **up, client_name_t cname) { gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem; + gs_gc_root_t *rp; - if (rp == NULL) { + if (rpp == NULL || *rpp == NULL) { rp = gs_raw_alloc_struct_immovable(imem->non_gc_memory, &st_gc_root_t, "i_register_root"); if (rp == 0) return_error(gs_error_VMerror); rp->free_on_unregister = true; - } else + *rpp = rp; + } else { + rp = *rpp; rp->free_on_unregister = false; + } if_debug3m('8', mem, "[8]register root(%s) 0x%lx -> 0x%lx\n", client_name_string(cname), (ulong)rp, (ulong)up); rp->ptype = ptype; diff --git a/base/gsdevice.c b/base/gsdevice.c index 5f298be16..4c58e35ee 100644 --- a/base/gsdevice.c +++ b/base/gsdevice.c @@ -1219,7 +1219,7 @@ gx_device_open_output_file(const gx_device * dev, char *fname, code = gs_note_error(gs_error_undefinedfilename); goto done; } - *pfile = dev->memory->gs_lib_ctx->fstdout; + *pfile = dev->memory->gs_lib_ctx->core->fstdout; /* Force stdout to binary. */ code = gp_setmode_binary(*pfile, true); goto done; diff --git a/base/gsiodev.c b/base/gsiodev.c index d05526cc0..536d478ad 100644 --- a/base/gsiodev.c +++ b/base/gsiodev.c @@ -91,7 +91,7 @@ gs_iodev_init(gs_memory_t * mem) table[i] = NULL; } - code = gs_register_struct_root(mem, mem->gs_lib_ctx->io_device_table_root, + code = gs_register_struct_root(mem, &mem->gs_lib_ctx->io_device_table_root, (void **)&libctx->io_device_table, "io_device_table"); if (code < 0) diff --git a/base/gsiodevs.c b/base/gsiodevs.c index f98a489aa..b13d21218 100644 --- a/base/gsiodevs.c +++ b/base/gsiodevs.c @@ -71,7 +71,7 @@ stdin_open(gx_io_device * iodev, const char *access, stream ** ps, gs_memory_t * mem) { return stdio_open(iodev, access, ps, mem, 'r', - mem->gs_lib_ctx->fstdin, sread_file); + mem->gs_lib_ctx->core->fstdin, sread_file); } const gx_io_device gs_iodev_stdin = iodev_stdio("%stdin%", stdin_open); @@ -80,7 +80,7 @@ stdout_open(gx_io_device * iodev, const char *access, stream ** ps, gs_memory_t * mem) { return stdio_open(iodev, access, ps, mem, 'w', - mem->gs_lib_ctx->fstdout, swrite_file); + mem->gs_lib_ctx->core->fstdout, swrite_file); } const gx_io_device gs_iodev_stdout = iodev_stdio("%stdout%", stdout_open); @@ -89,6 +89,6 @@ stderr_open(gx_io_device * iodev, const char *access, stream ** ps, gs_memory_t * mem) { return stdio_open(iodev, access, ps, mem, 'w', - mem->gs_lib_ctx->fstderr, swrite_file); + mem->gs_lib_ctx->core->fstderr, swrite_file); } const gx_io_device gs_iodev_stderr = iodev_stdio("%stderr%", stderr_open); diff --git a/base/gslibctx.c b/base/gslibctx.c index a72011aec..63c3ae489 100644 --- a/base/gslibctx.c +++ b/base/gslibctx.c @@ -95,7 +95,7 @@ gs_lib_ctx_set_default_device_list(const gs_memory_t *mem, const char* dev_list_ gs_lib_ctx_t *p_ctx = mem->gs_lib_ctx; gs_memory_t *ctx_mem = p_ctx->memory; int code = 0; - + result = (char *)gs_alloc_bytes(ctx_mem, list_str_len + 1, "gs_lib_ctx_set_default_device_list"); @@ -142,9 +142,9 @@ gs_lib_ctx_alloc_root_structure(gs_memory_t *mem, gs_gc_root_ptr *rp) return code; } -int gs_lib_ctx_init( gs_memory_t *mem ) +int gs_lib_ctx_init(gs_lib_ctx_t *ctx, gs_memory_t *mem) { - gs_lib_ctx_t *pio = 0; + gs_lib_ctx_t *pio = NULL; /* Check the non gc allocator is being passed in */ if (mem == 0 || mem != mem->non_gc_memory) @@ -160,18 +160,48 @@ int gs_lib_ctx_init( gs_memory_t *mem ) pio = (gs_lib_ctx_t*)gs_alloc_bytes_immovable(mem, sizeof(gs_lib_ctx_t), "gs_lib_ctx_init"); - if( pio == 0 ) + if(pio == NULL) return -1; /* Wholesale blanking is cheaper than retail, and scales better when new * fields are added. */ memset(pio, 0, sizeof(*pio)); + + if (ctx != NULL) { + pio->core = ctx->core; + gx_monitor_enter((gx_monitor_t *)(pio->core->monitor)); + pio->core->refs++; + gx_monitor_leave((gx_monitor_t *)(pio->core->monitor)); + } else { + pio->core = (gs_lib_ctx_core_t *)gs_alloc_bytes_immovable(mem, + sizeof(gs_lib_ctx_core_t), + "gs_lib_ctx_init(core)"); + if (pio->core == NULL) { + gs_free_object(mem, pio, "gs_lib_ctx_init"); + return -1; + } + memset(pio->core, 0, sizeof(*pio->core)); + + pio->core->monitor = gx_monitor_alloc(mem); + if (pio->core->monitor == NULL) { + gs_free_object(mem, pio->core, "gs_lib_ctx_init"); + gs_free_object(mem, pio, "gs_lib_ctx_init"); + return -1; + } + pio->core->refs = 1; + pio->core->memory = mem; + + /* Set the non-zero "shared" things */ + gs_lib_ctx_get_real_stdio(&pio->core->fstdin, &pio->core->fstdout, &pio->core->fstderr ); + pio->core->stdin_is_interactive = true; + /* id's 1 through 4 are reserved for Device color spaces; see gscspace.h */ + pio->core->gs_next_id = 5; /* Cloned contexts share the state */ + /* Set scanconverter to 1 (default) */ + pio->core->scanconverter = GS_SCANCONVERTER_DEFAULT; + } + /* Now set the non zero/false/NULL things */ pio->memory = mem; - gs_lib_ctx_get_real_stdio(&pio->fstdin, &pio->fstdout, &pio->fstderr ); - pio->stdin_is_interactive = true; - /* id's 1 through 4 are reserved for Device color spaces; see gscspace.h */ - pio->gs_next_id = 5; /* this implies that each thread has its own complete state */ /* Need to set this before calling gs_lib_ctx_set_icc_directory. */ mem->gs_lib_ctx = pio; @@ -197,9 +227,6 @@ int gs_lib_ctx_init( gs_memory_t *mem ) pio->client_check_file_permission = NULL; gp_get_realtime(pio->real_time_0); - /* Set scanconverter to 1 (default) */ - pio->scanconverter = GS_SCANCONVERTER_DEFAULT; - if (gs_lib_ctx_alloc_root_structure(mem, &pio->name_table_root)) goto Failure; @@ -231,10 +258,11 @@ void gs_lib_ctx_fin(gs_memory_t *mem) { gs_lib_ctx_t *ctx; gs_memory_t *ctx_mem; + int refs; if (!mem || !mem->gs_lib_ctx) return; - + ctx = mem->gs_lib_ctx; ctx_mem = ctx->memory; @@ -242,7 +270,7 @@ void gs_lib_ctx_fin(gs_memory_t *mem) gscms_destroy(ctx_mem); gs_free_object(ctx_mem, ctx->profiledir, "gs_lib_ctx_fin"); - + gs_free_object(ctx_mem, ctx->default_device_list, "gs_lib_ctx_fin"); @@ -254,6 +282,15 @@ void gs_lib_ctx_fin(gs_memory_t *mem) mem_err_print = NULL; #endif remove_ctx_pointers(ctx_mem); + + gx_monitor_enter((gx_monitor_t *)(ctx->core->monitor)); + refs = --ctx->core->refs; + gx_monitor_leave((gx_monitor_t *)(ctx->core->monitor)); + if (refs == 0) { + gx_monitor_free((gx_monitor_t *)(ctx->core->monitor)); + gs_free_object(ctx->core->memory, ctx->core, "gs_lib_ctx_fin"); + } + gs_free_object(ctx_mem, ctx, "gs_lib_ctx_init"); } @@ -278,6 +315,13 @@ void gs_lib_ctx_set_cms_context( const gs_memory_t *mem, void *cms_context ) mem->gs_lib_ctx->cms_context = cms_context; } +int gs_lib_ctx_get_act_on_uel( const gs_memory_t *mem ) +{ + if (mem == NULL) + return 0; + return mem->gs_lib_ctx->core->act_on_uel; +} + /* Provide a single point for all "C" stdout and stderr. */ @@ -286,19 +330,20 @@ int outwrite(const gs_memory_t *mem, const char *str, int len) int code; FILE *fout; gs_lib_ctx_t *pio = mem->gs_lib_ctx; + gs_lib_ctx_core_t *core = pio->core; if (len == 0) return 0; - if (pio->stdout_is_redirected) { - if (pio->stdout_to_stderr) + if (core->stdout_is_redirected) { + if (core->stdout_to_stderr) return errwrite(mem, str, len); - fout = pio->fstdout2; + fout = core->fstdout2; } - else if (pio->stdout_fn) { - return (*pio->stdout_fn)(pio->caller_handle, str, len); + else if (core->stdout_fn) { + return (*core->stdout_fn)(core->caller_handle, str, len); } else { - fout = pio->fstdout; + fout = core->fstdout; } code = fwrite(str, 1, len, fout); fflush(fout); @@ -316,6 +361,7 @@ int errwrite(const gs_memory_t *mem, const char *str, int len) { int code; gs_lib_ctx_t *ctx; + gs_lib_ctx_core_t *core; if (len == 0) return 0; if (mem == NULL) { @@ -330,26 +376,28 @@ int errwrite(const gs_memory_t *mem, const char *str, int len) ctx = mem->gs_lib_ctx; if (ctx == NULL) return 0; - if (ctx->stderr_fn) - return (*ctx->stderr_fn)(ctx->caller_handle, str, len); + core = ctx->core; + if (core->stderr_fn) + return (*core->stderr_fn)(core->caller_handle, str, len); - code = fwrite(str, 1, len, ctx->fstderr); - fflush(ctx->fstderr); + code = fwrite(str, 1, len, core->fstderr); + fflush(core->fstderr); return code; } void outflush(const gs_memory_t *mem) { - if (mem->gs_lib_ctx->stdout_is_redirected) { - if (mem->gs_lib_ctx->stdout_to_stderr) { - if (!mem->gs_lib_ctx->stderr_fn) - fflush(mem->gs_lib_ctx->fstderr); + gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core; + if (core->stdout_is_redirected) { + if (core->stdout_to_stderr) { + if (!core->stderr_fn) + fflush(core->fstderr); } else - fflush(mem->gs_lib_ctx->fstdout2); + fflush(core->fstdout2); } - else if (!mem->gs_lib_ctx->stdout_fn) - fflush(mem->gs_lib_ctx->fstdout); + else if (!core->stdout_fn) + fflush(core->fstdout); } #ifndef GS_THREADSAFE @@ -361,8 +409,8 @@ void errflush_nomem(void) void errflush(const gs_memory_t *mem) { - if (!mem->gs_lib_ctx->stderr_fn) - fflush(mem->gs_lib_ctx->fstderr); + if (!mem->gs_lib_ctx->core->stderr_fn) + fflush(mem->gs_lib_ctx->core->fstderr); /* else nothing to flush */ } diff --git a/base/gslibctx.h b/base/gslibctx.h index 348bde00e..137abe32b 100644 --- a/base/gslibctx.h +++ b/base/gslibctx.h @@ -39,9 +39,10 @@ typedef struct gs_font_dir_s gs_font_dir; typedef int (*client_check_file_permission_t) (gs_memory_t *mem, const char *fname, const int len, const char *permission); -typedef struct gs_lib_ctx_s -{ - gs_memory_t *memory; /* mem->gs_lib_ctx->memory == mem */ +typedef struct { + void *monitor; + int refs; + gs_memory_t *memory; FILE *fstdin; FILE *fstdout; FILE *fstderr; @@ -56,6 +57,18 @@ typedef struct gs_lib_ctx_s int (GSDLLCALL *stderr_fn)(void *caller_handle, const char *str, int len); int (GSDLLCALL *poll_fn)(void *caller_handle); ulong gs_next_id; /* gs_id initialized here, private variable of gs_next_ids() */ + /* True if we are emulating CPSI. Ideally this would be in the imager + * state, but this can't be done due to problems detecting changes in it + * for the clist based devices. */ + bool CPSI_mode; + int scanconverter; + int act_on_uel; +} gs_lib_ctx_core_t; + +typedef struct gs_lib_ctx_s +{ + gs_memory_t *memory; /* mem->gs_lib_ctx->memory == mem */ + gs_lib_ctx_core_t *core; void *top_of_system; /* use accessor functions to walk down the system * to the desired structure gs_lib_ctx_get_*() */ @@ -84,10 +97,6 @@ typedef struct gs_lib_ctx_s /* font directory - see gsfont.h */ gs_font_dir *font_dir; gs_gc_root_ptr font_dir_root; - /* True if we are emulating CPSI. Ideally this would be in the imager - * state, but this can't be done due to problems detecting changes in it - * for the clist based devices. */ - bool CPSI_mode; /* Keep the path for the ICCProfiles here so devices and the icc_manager * can get to it. Prevents needing two copies, one in the icc_manager * and one in the device */ @@ -97,7 +106,6 @@ typedef struct gs_lib_ctx_s gs_fapi_server **fapi_servers; char *default_device_list; int gcsignal; - int scanconverter; void *sjpxd_private; /* optional for use of jpx codec */ } gs_lib_ctx_t; @@ -114,7 +122,7 @@ enum { * it is the responsibility of the gs_memory_t objects to copy * the pointer to subsequent memory objects. */ -int gs_lib_ctx_init( gs_memory_t *mem ); +int gs_lib_ctx_init( gs_lib_ctx_t *ctx, gs_memory_t *mem ); /** Called when the lowest level allocator (the one which the lib_ctx was * initialised under) is about to be destroyed. The lib_ctx should tidy up @@ -125,6 +133,7 @@ gs_lib_ctx_t *gs_lib_ctx_get_interp_instance( const gs_memory_t *mem ); void *gs_lib_ctx_get_cms_context( const gs_memory_t *mem ); void gs_lib_ctx_set_cms_context( const gs_memory_t *mem, void *cms_context ); +int gs_lib_ctx_get_act_on_uel( const gs_memory_t *mem ); #ifndef GS_THREADSAFE /* HACK to get at non garbage collection memory pointer @@ -152,8 +161,8 @@ gs_lib_ctx_get_default_device_list(const gs_memory_t *mem, char** dev_list_str, int gs_check_file_permission (gs_memory_t *mem, const char *fname, const int len, const char *permission); -#define IS_LIBCTX_STDOUT(mem, f) (f == mem->gs_lib_ctx->fstdout) -#define IS_LIBCTX_STDERR(mem, f) (f == mem->gs_lib_ctx->fstderr) +#define IS_LIBCTX_STDOUT(mem, f) (f == mem->gs_lib_ctx->core->fstdout) +#define IS_LIBCTX_STDERR(mem, f) (f == mem->gs_lib_ctx->core->fstderr) /* Functions to init/fin JPX decoder libctx entry */ int sjpxd_create(gs_memory_t *mem); diff --git a/base/gsmalloc.c b/base/gsmalloc.c index d17647a05..4c2e2161f 100644 --- a/base/gsmalloc.c +++ b/base/gsmalloc.c @@ -424,7 +424,7 @@ gs_heap_free_string(gs_memory_t * mem, byte * data, uint nbytes, gs_heap_free_object(mem, data, cname); } static int -gs_heap_register_root(gs_memory_t * mem, gs_gc_root_t * rp, +gs_heap_register_root(gs_memory_t * mem, gs_gc_root_t ** rp, gs_ptr_type_t ptype, void **up, client_name_t cname) { return 0; @@ -582,13 +582,19 @@ gs_malloc_unwrap(gs_memory_t *wrapped) gs_memory_t * gs_malloc_init(void) { + return gs_malloc_init_with_context(NULL); +} + +gs_memory_t * +gs_malloc_init_with_context(gs_lib_ctx_t *ctx) +{ gs_malloc_memory_t *malloc_memory_default = gs_malloc_memory_init(); gs_memory_t *memory_t_default; if (malloc_memory_default == NULL) return NULL; - if (gs_lib_ctx_init((gs_memory_t *)malloc_memory_default) != 0) { + if (gs_lib_ctx_init(ctx, (gs_memory_t *)malloc_memory_default) != 0) { gs_malloc_release((gs_memory_t *)malloc_memory_default); return NULL; } diff --git a/base/gsmalloc.h b/base/gsmalloc.h index 76bd322ef..32e44c869 100644 --- a/base/gsmalloc.h +++ b/base/gsmalloc.h @@ -42,7 +42,14 @@ gs_malloc_memory_t *gs_malloc_memory_init(void); gs_memory_free_all((gs_memory_t *)mem, FREE_ALL_EVERYTHING,\ "gs_malloc_memory_release") +/* Get a basic malloc based allocator, built on a new + * gs_lib_ctx instance. */ gs_memory_t * gs_malloc_init(void); +/* Get a basic malloc based allocator, built on a + * gs_lib_ctx instance, cloned from this supplied one. + * If ctx == NULL, this behaves as gs_malloc_init. + */ +gs_memory_t * gs_malloc_init_with_context(gs_lib_ctx_t *ctx); void gs_malloc_release(gs_memory_t *mem); #define gs_malloc(mem, nelts, esize, cname)\ diff --git a/base/gsmchunk.c b/base/gsmchunk.c index 151935f63..3a4ff131f 100644 --- a/base/gsmchunk.c +++ b/base/gsmchunk.c @@ -1409,7 +1409,7 @@ chunk_object_type(const gs_memory_t * mem, const void *ptr) } static int -chunk_register_root(gs_memory_t * mem, gs_gc_root_t * rp, gs_ptr_type_t ptype, +chunk_register_root(gs_memory_t * mem, gs_gc_root_t ** rp, gs_ptr_type_t ptype, void **up, client_name_t cname) { return 0; diff --git a/base/gsmemory.c b/base/gsmemory.c index f56f26f8f..b88361023 100644 --- a/base/gsmemory.c +++ b/base/gsmemory.c @@ -212,7 +212,7 @@ gs_struct_type_name(gs_memory_type_ptr_t pstype) /* Register a structure root. */ int -gs_register_struct_root(gs_memory_t *mem, gs_gc_root_t *root, +gs_register_struct_root(gs_memory_t *mem, gs_gc_root_t **root, void **pp, client_name_t cname) { return gs_register_root(mem, root, ptr_struct_type, pp, cname); diff --git a/base/gsmemory.h b/base/gsmemory.h index 4683e5aca..343d1666a 100644 --- a/base/gsmemory.h +++ b/base/gsmemory.h @@ -355,7 +355,7 @@ typedef struct gs_memory_procs_s { */ #define gs_memory_proc_register_root(proc)\ - int proc(gs_memory_t *mem, gs_gc_root_t *root, gs_ptr_type_t ptype,\ + int proc(gs_memory_t *mem, gs_gc_root_t **root, gs_ptr_type_t ptype,\ void **pp, client_name_t cname) #define gs_register_root(mem, root, ptype, pp, cname)\ (*(mem)->procs.register_root)(mem, root, ptype, pp, cname) @@ -431,7 +431,7 @@ void *gs_resize_struct_array(gs_memory_t *mem, void *obj, uint num_elements, client_name_t cname); /* Register a structure root. This just calls gs_register_root. */ -int gs_register_struct_root(gs_memory_t *mem, gs_gc_root_t *root, +int gs_register_struct_root(gs_memory_t *mem, gs_gc_root_t **root, void **pp, client_name_t cname); /* Define no-op freeing procedures for use by enable_free. */ diff --git a/base/gsmemret.c b/base/gsmemret.c index 77f398470..489195f74 100644 --- a/base/gsmemret.c +++ b/base/gsmemret.c @@ -314,7 +314,7 @@ gs_forward_free_string(gs_memory_t * mem, byte * data, uint nbytes, DO_FORWARD(target->procs.free_string(target, data, nbytes, cname)); } static int -gs_retrying_register_root(gs_memory_t * mem, gs_gc_root_t * rp, +gs_retrying_register_root(gs_memory_t * mem, gs_gc_root_t ** rp, gs_ptr_type_t ptype, void **up, client_name_t cname) { RETURN_RETRYING( diff --git a/base/gsstate.c b/base/gsstate.c index 1386acc8d..6eaf369c9 100644 --- a/base/gsstate.c +++ b/base/gsstate.c @@ -715,7 +715,7 @@ gs_setcpsimode(gs_memory_t *mem, bool mode) { gs_lib_ctx_t *libctx = gs_lib_ctx_get_interp_instance(mem); - libctx->CPSI_mode = mode; + libctx->core->CPSI_mode = mode; } /* currentcpsimode */ @@ -724,7 +724,7 @@ gs_currentcpsimode(const gs_memory_t * mem) { gs_lib_ctx_t *libctx = gs_lib_ctx_get_interp_instance(mem); - return libctx->CPSI_mode; + return libctx->core->CPSI_mode; } /* The edgebuffer based scanconverter can only cope with values of 0 @@ -748,7 +748,7 @@ gs_setscanconverter(gs_gstate * gs, int converter) { gs_lib_ctx_t *libctx = gs_lib_ctx_get_interp_instance(gs->memory); - libctx->scanconverter = converter; + libctx->core->scanconverter = converter; sanitize_fill_adjust(gs); } @@ -759,7 +759,7 @@ gs_getscanconverter(const gs_memory_t * mem) { gs_lib_ctx_t *libctx = gs_lib_ctx_get_interp_instance(mem); - return libctx->scanconverter; + return libctx->core->scanconverter; } /* setrenderingintent diff --git a/base/gsutil.c b/base/gsutil.c index d17da4001..f9706a80d 100644 --- a/base/gsutil.c +++ b/base/gsutil.c @@ -32,9 +32,10 @@ ulong gs_next_ids(const gs_memory_t *mem, uint count) { - ulong id = mem->gs_lib_ctx->gs_next_id; + gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core; + ulong id = core->gs_next_id; - mem->gs_lib_ctx->gs_next_id += count; + core->gs_next_id += count; return id; } diff --git a/base/gxdownscale.c b/base/gxdownscale.c index 2a7b4347f..113e38d3d 100644 --- a/base/gxdownscale.c +++ b/base/gxdownscale.c @@ -21,6 +21,12 @@ #include "assert_.h" #include "ets.h" +/* Nasty inline declaration, as gxht_thresh.h requires penum */ +void gx_ht_threshold_row_bit_sub(byte *contone, byte *threshold_strip, + int contone_stride, byte *halftone, + int dithered_stride, int width, int num_rows, + int offset_bits); + enum { MAX_ETS_PLANES = 8 @@ -874,6 +880,81 @@ static void down_core4(gx_downscaler_t *ds, pack_8to1(out_buffer, outp, awidth*4); } +static void down_core4_ht(gx_downscaler_t *ds, + byte *out_buffer, /* Guaranteed aligned */ + byte *in_buffer, /* Not guaranteed aligned */ + int row, + int plane /* unused */, + int span) +{ + int pad_white, y; + int factor = ds->factor; + int i; + int nc = ds->early_cm ? ds->post_cm_num_comps : ds->num_comps; + byte *downscaled_data = ds->inbuf; + + pad_white = (ds->awidth - ds->width) * factor * 4; + if (pad_white < 0) + pad_white = 0; + + if (pad_white) + { + unsigned char *inp = in_buffer + ds->width * factor * 4; + for (y = factor; y > 0; y--) + { + memset(inp, 0xFF, pad_white); + inp += span; + } + } + + /* Color conversion has already happened. Do any downscale required. */ + if (ds->ets_downscale) + ds->ets_downscale(ds, downscaled_data, in_buffer, row, plane, span); + else if ((31 & (intptr_t)in_buffer) == 0) + downscaled_data = in_buffer; /* Already aligned! Yay! */ + else + memcpy(downscaled_data, in_buffer, nc*ds->width); /* Copy to align */ + + /* Do the halftone */ + for (i = 0; i < nc; i++) + { + /* Make the expanded threshold row */ + byte *d = ds->htrow + i; + int len = ds->width; + const byte *srow = ds->ht[i].data + ds->ht[i].stride * ((row + ds->ht[i].y_phase) % ds->ht[i].h); + { + int o = ds->ht[i].x_phase; + int run = ds->ht[i].w - o; + const byte *s = &srow[o]; + if (run > len) + run = len; + len -= run; + do { + *d = *s++; + d += nc; + } while (--run); + } + while (len) + { + const byte *s = srow; + int run = ds->ht[i].w; + if (run > len) + run = len; + len -= run; + do { + *d = *s++; + d += nc; + } + while (--run); + } + } + + /* Do the halftone */ + gx_ht_threshold_row_bit_sub(downscaled_data, ds->htrow, 0, + out_buffer, 0, + ds->width * nc, 1, 0); +} + static void down_core4_ets(gx_downscaler_t *ds, byte *out_buffer, byte *in_buffer, @@ -1769,11 +1850,40 @@ static int init_ets(gx_downscaler_t *ds, int num_planes, gx_downscale_core *down ds->ets_config = ets_create(ds->dev->memory, ¶ms); if (ds->ets_config == NULL) - return gs_error_VMerror; + return gs_note_error(gs_error_VMerror); return 0; } +static int init_ht(gx_downscaler_t *ds, int num_planes, gx_downscale_core *downscale_core) +{ + int nc = ds->early_cm ? ds->post_cm_num_comps : ds->num_comps; + + ds->ets_downscale = downscale_core; + + /* Allocate us a row (with padding for alignment) so we can hold the + * expanded threshold array. */ + ds->htrow_alloc = gs_alloc_bytes(ds->dev->memory, ds->width * nc + 64, + "gx_downscaler(htrow)"); + if (ds->htrow_alloc == NULL) + return gs_error_VMerror; + /* Make an aligned version */ + ds->htrow = ds->htrow_alloc + ((32-(intptr_t)ds->htrow_alloc) & 31); + + /* Allocate us a row (with padding for alignment) for the downscaled data. */ + ds->inbuf_alloc = gs_alloc_bytes(ds->dev->memory, ds->width * nc + 64, + "gx_downscaler(inbuf)"); + if (ds->inbuf_alloc == NULL) + { + gs_free_object(ds->dev->memory, ds->htrow_alloc, "gx_downscaler(htrow)"); + ds->htrow_alloc = ds->htrow = NULL; + return gs_error_VMerror; + } + /* Make an aligned version */ + ds->inbuf = ds->inbuf_alloc + ((32-(intptr_t)ds->inbuf_alloc) & 31); + + return 0; +} int gx_downscaler_init_planar(gx_downscaler_t *ds, gx_device *dev, @@ -2102,6 +2212,8 @@ int gx_downscaler_init_trapped_cm(gx_downscaler_t *ds, 0); } +static gx_downscaler_ht_t bogus_ets_halftone; + int gx_downscaler_init_trapped_cm_ets(gx_downscaler_t *ds, gx_device *dev, int src_bpc, @@ -2112,13 +2224,74 @@ int gx_downscaler_init_trapped_cm_ets(gx_downscaler_t *ds, int (*adjust_width_proc)(int, int), int adjust_width, int trap_w, - int trap_h, + int trap_h, const int *comp_order, gx_downscale_cm_fn *apply_cm, void *apply_cm_arg, int post_cm_num_comps, int ets) { + return gx_downscaler_init_trapped_cm_halftone(ds, + dev, + src_bpc, + dst_bpc, + num_comps, + factor, + mfs, + adjust_width_proc, + adjust_width, + trap_w, + trap_h, + comp_order, + apply_cm, + apply_cm_arg, + post_cm_num_comps, + &bogus_ets_halftone); +} + + +static gx_downscale_core * +select_8_to_8_core(int nc, int factor) +{ + if (factor == 1) + return NULL; /* No sense doing anything */ + if (nc == 1) + { + if (factor == 4) + return &down_core8_4; + else if (factor == 3) + return &down_core8_3; + else if (factor == 2) + return &down_core8_2; + else + return &down_core8; + } + else if (nc == 3) + return &down_core24; + else if (nc == 4) + return &down_core32; + + return NULL; +} + +int +gx_downscaler_init_trapped_cm_halftone(gx_downscaler_t *ds, + gx_device *dev, + int src_bpc, + int dst_bpc, + int num_comps, + int factor, + int mfs, + int (*adjust_width_proc)(int, int), + int adjust_width, + int trap_w, + int trap_h, + const int *comp_order, + gx_downscale_cm_fn *apply_cm, + void *apply_cm_arg, + int post_cm_num_comps, + gx_downscaler_ht_t *ht) +{ int size; int post_size; int span; @@ -2159,6 +2332,9 @@ int gx_downscaler_init_trapped_cm_ets(gx_downscaler_t *ds, ds->apply_cm_arg = apply_cm_arg; ds->early_cm = dst_bpc < src_bpc; ds->post_cm_num_comps = post_cm_num_comps; + ds->ht = ht; + ds->dst_bpc = dst_bpc; + ds->num_comps = num_comps; code = check_trapping(dev->memory, trap_w, trap_h, num_comps, comp_order); if (code < 0) @@ -2193,13 +2369,20 @@ int gx_downscaler_init_trapped_cm_ets(gx_downscaler_t *ds, { if (mfs > 1) core = &down_core4_mfs; - else if (ets) + else if (ht == &bogus_ets_halftone) { - code = init_ets(ds, 4, core); + code = init_ets(ds, 4, select_8_to_8_core(nc, factor)); if (code) goto cleanup; core = &down_core4_ets; } + else if (ht != NULL) + { + code = init_ht(ds, 4, select_8_to_8_core(nc, factor)); + if (code) + goto cleanup; + core = &down_core4_ht; + } else core = &down_core4; } @@ -2207,7 +2390,7 @@ int gx_downscaler_init_trapped_cm_ets(gx_downscaler_t *ds, { if (mfs > 1) core = &down_core_mfs; - else if (ets) + else if (ht == &bogus_ets_halftone) { code = init_ets(ds, 1, core); if (code) @@ -2227,21 +2410,8 @@ int gx_downscaler_init_trapped_cm_ets(gx_downscaler_t *ds, } else if ((factor == 1) && (src_bpc == dst_bpc)) break; - else if ((src_bpc == 8) && (dst_bpc == 8) && (nc == 1)) - { - if (factor == 4) - core = &down_core8_4; - else if (factor == 3) - core = &down_core8_3; - else if (factor == 2) - core = &down_core8_2; - else - core = &down_core8; - } - else if ((src_bpc == 8) && (dst_bpc == 8) && (nc == 3)) - core = &down_core24; - else if ((src_bpc == 8) && (dst_bpc == 8) && (nc == 4)) - core = &down_core32; + else if (src_bpc == 8 && dst_bpc == 8) + core = select_8_to_8_core(nc, factor); /* If we found one, or we have nothing to fallback to, exit */ if (core || !ds->early_cm) @@ -2290,13 +2460,13 @@ int gx_downscaler_init_trapped_cm_ets(gx_downscaler_t *ds, } if (dst_bpc == 1) { ds->errors = (int *)gs_alloc_bytes(dev->memory, - num_comps*(awidth+3)*sizeof(int), + nc*(awidth+3)*sizeof(int), "gx_downscaler(errors)"); if (ds->errors == NULL) { code = gs_note_error(gs_error_VMerror); goto cleanup; } - memset(ds->errors, 0, num_comps * (awidth+3) * sizeof(int)); + memset(ds->errors, 0, nc * (awidth+3) * sizeof(int)); } } @@ -2324,6 +2494,9 @@ void gx_downscaler_fin(gx_downscaler_t *ds) ds->errors = NULL; gs_free_object(ds->dev->memory, ds->scaled_data, "gx_downscaler(scaled_data)"); ds->scaled_data = NULL; + gs_free_object(ds->dev->memory, ds->htrow_alloc, "gx_downscaler(htrow)"); + ds->htrow = NULL; + ds->htrow_alloc = NULL; if (ds->claptrap) ClapTrap_Fin(ds->dev->memory, ds->claptrap); diff --git a/base/gxdownscale.h b/base/gxdownscale.h index a436816e6..cea2cfabf 100644 --- a/base/gxdownscale.h +++ b/base/gxdownscale.h @@ -44,6 +44,16 @@ typedef int (gx_downscale_cm_fn)(void *arg, int h, int raster); +typedef struct gx_downscaler_ht_s +{ + int w; + int h; + int stride; + int x_phase; + int y_phase; + byte *data; +} gx_downscaler_ht_t; + /* The following structure definitions should really be considered * private, and are exposed here only because it enables us to define * gx_downscaler_t's on the stack, thus avoiding mallocs. @@ -67,12 +77,14 @@ struct gx_downscaler_s { int factor; /* Factor to downscale */ byte *mfs_data; /* MinFeatureSize data */ int src_bpc; /* Source bpc */ + int dst_bpc; /* Destination bpc */ int *errors; /* Error diffusion table */ byte *scaled_data;/* Downscaled data (only used for non * integer downscales). */ int scaled_span;/* Num bytes in scaled scanline */ gx_downscale_core *down_core; /* Core downscaling function */ gs_get_bits_params_t params; /* Params if in planar mode */ + int num_comps; /* Number of components as rendered */ int num_planes; /* Number of planes if planar, 0 otherwise */ ClapTrap *claptrap; /* ClapTrap pointer (if trapping) */ @@ -89,6 +101,12 @@ struct gx_downscaler_s { byte *pre_cm[GS_CLIENT_COLOR_MAX_COMPONENTS]; byte *post_cm[GS_CLIENT_COLOR_MAX_COMPONENTS]; + + gx_downscaler_ht_t *ht; + byte *htrow; + byte *htrow_alloc; + byte *inbuf; + byte *inbuf_alloc; }; /* To use the downscaler: @@ -221,6 +239,23 @@ int gx_downscaler_init_trapped_cm_ets(gx_downscaler_t *ds, int post_cm_num_comps, int ets); +int gx_downscaler_init_trapped_cm_halftone(gx_downscaler_t *ds, + gx_device *dev, + int src_bpc, + int dst_bpc, + int num_comps, + int factor, + int mfs, + int (*adjust_width_proc)(int, int), + int adjust_width, + int trap_w, + int trap_h, + const int *comp_order, + gx_downscale_cm_fn *apply_cm, + void *apply_cm_arg, + int post_cm_num_comps, + gx_downscaler_ht_t *ht); + int gx_downscaler_init_planar(gx_downscaler_t *ds, gx_device *dev, gs_get_bits_params_t *params, diff --git a/configure.ac b/configure.ac index 8ecaccc53..d78e20f02 100644 --- a/configure.ac +++ b/configure.ac @@ -2007,7 +2007,7 @@ if test "x$XPS_TARGET" != "x" && test "x$PCL_TARGET" != "x" ; then [name of the GhostPDL executible (if the source is available, ignored otherwise) [[gpdl]]]), [GPDL="$with_gpdl"],[GPDL='gpdl']) -# GPDL_TARGET=gpdl + GPDL_TARGET=gpdl GPDL_MAK="\$(GPDLSRCDIR)\$(D)gpdl.mak" fi fi diff --git a/devices/devs.mak b/devices/devs.mak index e8654e566..99823eea8 100644 --- a/devices/devs.mak +++ b/devices/devs.mak @@ -131,6 +131,7 @@ DEVGEN=$(DEVGENDIR)$(D) # bmp256 8-bit (256-color) .BMP file format # bmp16m 24-bit .BMP file format # bmp32b 32-bit pseudo-.BMP file format +# chameleon Plain bits, rgb/mono/cmyk runtime configurable. # jpeg JPEG format, RGB output # jpeggray JPEG format, gray output # jpegcmyk JPEG format, cmyk output @@ -1126,6 +1127,20 @@ $(DEVOBJ)gdevbit.$(OBJ) : $(DEVSRC)gdevbit.c $(PDEVH)\ $(gsutil_h) $(DEVS_MAK) $(MAKEDIRS) $(DEVCC) $(DEVO_)gdevbit.$(OBJ) $(C_) $(DEVSRC)gdevbit.c +### --------------------- The chameleon device ---------------------- ### + +chameleon_=$(DEVOBJ)gdevchameleon.$(OBJ) $(DEVOBJ)gdevdcrd.$(OBJ) + +$(DD)chameleon.dev : $(chameleon_) $(GLD)page.dev $(GLD)cielib.dev $(GDEV) \ + $(DEVS_MAK) $(MAKEDIRS) + $(SETPDEV2) $(DD)chameleon $(chameleon_) + $(ADDMOD) $(DD)chameleon -include $(GLD)cielib + +$(DEVOBJ)gdevchameleon.$(OBJ) : $(DEVSRC)gdevchameleon.c $(PDEVH)\ + $(gsparam_h) $(gdevdcrd_h) $(gscrd_h) $(gscrdp_h) $(gxlum_h) $(gxdcconv_h)\ + $(gsutil_h) $(DEVS_MAK) $(MAKEDIRS) + $(DEVCC) $(DEVO_)gdevchameleon.$(OBJ) $(C_) $(DEVSRC)gdevchameleon.c + ### ------------------------- .BMP file formats ------------------------- ### gdevbmp_h=$(DEVSRC)gdevbmp.h diff --git a/devices/gdevchameleon.c b/devices/gdevchameleon.c new file mode 100644 index 000000000..c944b92a8 --- /dev/null +++ b/devices/gdevchameleon.c @@ -0,0 +1,672 @@ +/* Copyright (C) 2001-2018 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + + +/* Chameleon devices to allow for changing bit depths/colors at runtime. */ + +#include "gdevprn.h" +#include "gsparam.h" +#include "gscrd.h" +#include "gscrdp.h" +#include "gxlum.h" +#include "gxdcconv.h" +#include "gdevdcrd.h" +#include "gxdownscale.h" +#include "gxdevsop.h" + +/* Define the device parameters. */ +#ifndef X_DPI +# define X_DPI 72 +#endif +#ifndef Y_DPI +# define Y_DPI 72 +#endif + +/* The device descriptor */ +static dev_proc_encode_color(chameleon_mono_encode_color); +static dev_proc_encode_color(chameleon_rgb_encode_color); +static dev_proc_encode_color(chameleon_cmyk_encode_color); +static dev_proc_decode_color(chameleon_mono_decode_color); +static dev_proc_decode_color(chameleon_rgb_decode_color); +static dev_proc_decode_color(chameleon_cmyk_decode_color); +static dev_proc_get_params(chameleon_get_params); +static dev_proc_put_params(chameleon_put_params); +static dev_proc_print_page(chameleon_print_page); +static dev_proc_dev_spec_op(chameleon_spec_op); + +struct gx_device_chameleon_s { + gx_device_common; + gx_prn_device_common; + int num_components; + int bpc; + int dst_num_components; + int dst_bpc; + int output_as_pxm; + bool language_uses_rops; + gx_downscaler_params downscale; +}; +typedef struct gx_device_chameleon_s gx_device_chameleon; + +static const gx_device_procs chameleon_procs = +{ gdev_prn_open, + gx_default_get_initial_matrix, + NULL, /* sync_output */ + /* Since the print_page doesn't alter the device, this device can print in the background */ + gdev_prn_bg_output_page, + gdev_prn_close, + chameleon_rgb_encode_color, /* map_rgb_color */ + chameleon_rgb_decode_color, /* map_color_rgb */ + NULL, /* fill_rectangle */ + NULL, /* tile_rectangle */ + NULL, /* copy_mono */ + NULL, /* copy_color */ + NULL, /* draw_line */ + NULL, /* get_bits */ + chameleon_get_params, + chameleon_put_params, + chameleon_rgb_encode_color, /* map_cmyk_color */ + NULL, /* get_xfont_procs */ + NULL, /* get_xfont_device */ + NULL, /* map_rgb_alpha_color */ + gx_page_device_get_page_device, /* get_page_device */ + NULL, /* get_alpha_bits */ + NULL, /* copy_alpha */ + NULL, /* get_band */ + NULL, /* copy_rop */ + NULL, /* fill_path */ + NULL, /* stroke_path */ + NULL, /* fill_mask */ + NULL, /* fill_trapezoid */ + NULL, /* fill_parallelogram */ + NULL, /* fill_triangle */ + NULL, /* draw_thin_line */ + NULL, /* begin_image */ + NULL, /* image_data */ + NULL, /* end_image */ + NULL, /* strip_tile_rectangle */ + NULL, /* strip_copy_rop */ + NULL, /* get_clipping_box */ + NULL, /* begin_typed_image */ + NULL, /* get_bits_rectangle */ + NULL, /* map_color_rgb_alpha */ + NULL, /* create_compositor */ + NULL, /* get_hardware_params */ + NULL, /* text_begin */ + NULL, /* finish_copydevice */ + NULL, /* begin_transparency_group */ + NULL, /* end_transparency_group */ + NULL, /* begin_transparency_mask */ + NULL, /* end_transparency_mask */ + NULL, /* discard_transparency_layer */ + NULL, /* get_color_mapping_procs */ + NULL, /* get_color_comp_index */ + chameleon_rgb_encode_color,/* encode_color */ + chameleon_rgb_decode_color, /* decode_color */ + NULL, /* pattern_manage */ + NULL, /* fill_rectangle_hl_color */ + NULL, /* include_color_space */ + NULL, /* fill_linear_color_scanline */ + NULL, /* fill_linear_color_trapezoid */ + NULL, /* fill_linear_color_triangle */ + NULL, /* update_spot_equivalent_colors */ + NULL, /* ret_devn_params */ + NULL, /* fillpage */ + NULL, /* push_transparency_state */ + NULL, /* pop_transparency_state */ + NULL, /* put_image */ + chameleon_spec_op /* dev_spec_op */ +}; + +const gx_device_chameleon gs_chameleon_device = +{prn_device_body(gx_device_chameleon, chameleon_procs, "chameleon", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + X_DPI, Y_DPI, + 0, 0, 0, 0, /* margins */ + 4, /* 3 colors (by default) */ + 4, /* depth */ + 15, /* max grey */ + 15, /* max color */ + 16, /* dither greys */ + 16, /* dither colors */ + chameleon_print_page), + 4, /* Default to CMYK output */ + 1, /* Default to 1bpc output */ + 4, /* Default to CMYK output */ + 1, /* Default to 1bpc output */ + 0, /* Default to outputting headerless data */ + 0, /* Default to not ropping */ + GX_DOWNSCALER_PARAMS_DEFAULTS +}; + +/* Map gray to color. */ +/* Note that 1-bit monochrome is a special case. */ +static gx_color_index +chameleon_mono_encode_color(gx_device * dev, const gx_color_value cv[]) +{ + int bpc = dev->color_info.depth; + int drop = sizeof(gx_color_value) * 8 - bpc; + gx_color_value gray = cv[0]; + + return (bpc == 1 ? gx_max_color_value - gray : gray) >> drop; +} + +static gx_color_index +chameleon_rgb_encode_color(gx_device * dev, const gx_color_value cv[]) +{ + if (dev->color_info.depth == 24) + return gx_color_value_to_byte(cv[2]) + + ((uint) gx_color_value_to_byte(cv[1]) << 8) + + ((ulong) gx_color_value_to_byte(cv[0]) << 16); + else { + COLROUND_VARS; + /* The following needs special handling to avoid bpc=5 when depth=16 */ + int bpc = dev->color_info.depth == 16 ? 4 : dev->color_info.depth / 3; + COLROUND_SETUP(bpc); + + return (((COLROUND_ROUND(cv[0]) << bpc) + + COLROUND_ROUND(cv[1])) << bpc) + + COLROUND_ROUND(cv[2]); + } +} + +/* Map CMYK to color. */ +static gx_color_index +chameleon_cmyk_encode_color(gx_device * dev, const gx_color_value cv[]) +{ + int bpc = dev->color_info.depth / 4; + int drop = sizeof(gx_color_value) * 8 - bpc; + gx_color_index color = + (((((((gx_color_index) cv[0] >> drop) << bpc) + + (cv[1] >> drop)) << bpc) + + (cv[2] >> drop)) << bpc) + + (cv[3] >> drop); + + return (color == gx_no_color_index ? color ^ 1 : color); +} + +/* Map color to RGB. This has 3 separate cases, but since it is rarely */ +/* used, we do a case test rather than providing 3 separate routines. */ +static int +chameleon_mono_decode_color(gx_device * dev, gx_color_index color, gx_color_value cv[4]) +{ + int depth = dev->color_info.depth; + int ncomp = dev->color_info.num_components; + int bpc = depth / ncomp; + uint mask = (1 << bpc) - 1; + +#define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / mask)) + + cv[0] = cv[1] = cv[2] = + (depth == 1 ? (color ? 0 : gx_max_color_value) : + cvalue(color)); + return 0; +#undef cvalue +} + +static int +chameleon_rgb_decode_color(gx_device * dev, gx_color_index color, gx_color_value cv[4]) +{ + int depth = dev->color_info.depth; + int ncomp = dev->color_info.num_components; + int bpc = depth / ncomp; + uint mask = (1 << bpc) - 1; + +#define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / mask)) + + gx_color_index cshift = color; + + cv[2] = cvalue(cshift & mask); + cshift >>= bpc; + cv[1] = cvalue(cshift & mask); + cv[0] = cvalue(cshift >> bpc); + return 0; +#undef cvalue +} + +static int +chameleon_cmyk_decode_color(gx_device * dev, gx_color_index color, gx_color_value cv[4]) +{ + int depth = dev->color_info.depth; + int ncomp = dev->color_info.num_components; + int bpc = depth / ncomp; + uint mask = (1 << bpc) - 1; + +#define cvalue(c) ((gx_color_value)((ulong)(c) * gx_max_color_value / mask)) + + gx_color_index cshift = color; + uint c, m, y, k; + + k = cshift & mask; + cshift >>= bpc; + y = cshift & mask; + cshift >>= bpc; + m = cshift & mask; + c = cshift >> bpc; + /* We use our improved conversion rule.... */ + cv[0] = cvalue((mask - c) * (mask - k) / mask); + cv[1] = cvalue((mask - m) * (mask - k) / mask); + cv[2] = cvalue((mask - y) * (mask - k) / mask); + return 0; +#undef cvalue +} + +/* # So long, oh, I hate to see you go */ +static void +reconfigure_baby(gx_device_chameleon *pcdev) +{ + int bpc = pcdev->dst_bpc; + int num_comps = pcdev->dst_num_components; + + if (pcdev->language_uses_rops) { + bpc = 8; + num_comps = 3; + } + + if (pcdev->bpc != bpc || + pcdev->num_components != num_comps) { + + gs_closedevice((gx_device *)pcdev); + + pcdev->bpc = bpc; + pcdev->num_components = num_comps; + + switch (num_comps * bpc) { + case 1*1: case 1*2: case 1*4: case 1*8: + case 4*4: case 4*8: + pcdev->color_info.depth = bpc * num_comps; + break; + case 3*1: + pcdev->color_info.depth = 4; + break; + case 3*2: + pcdev->color_info.depth = 8; + break; + case 3*4: + pcdev->color_info.depth = 16; + break; + } + pcdev->color_info.max_gray = pcdev->color_info.max_color = + (pcdev->color_info.dither_grays = pcdev->color_info.dither_colors = 1<<bpc) - 1; + + set_dev_proc(pcdev, map_cmyk_color, NULL); + set_dev_proc(pcdev, map_rgb_color, NULL); + set_dev_proc(pcdev, map_color_rgb, NULL); + set_dev_proc(pcdev, encode_color, + num_comps == 1 ? chameleon_mono_encode_color : + num_comps == 4 ? chameleon_cmyk_encode_color : + chameleon_rgb_encode_color); + set_dev_proc(pcdev, decode_color, + num_comps == 1 ? chameleon_mono_decode_color : + num_comps == 4 ? chameleon_cmyk_decode_color : + chameleon_rgb_decode_color); + + /* Reset the separable and linear shift, masks, bits. */ + set_linear_color_bits_mask_shift((gx_device *)pcdev); + pcdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN; + pcdev->bpc = bpc; + pcdev->num_components = num_comps; + } +} + +/* Get parameters. We provide a default CRD. */ +static int +chameleon_get_params(gx_device * pdev, gs_param_list * plist) +{ + int code, ecode; + gx_device_chameleon *pcdev = (gx_device_chameleon *)pdev; + + ecode = gdev_prn_get_params(pdev, plist); + code = sample_device_crd_get_params(pdev, plist, "CRDDefault"); + if (code < 0) + ecode = code; + + if ((code = param_write_int(plist, "DstBitDepth", &pcdev->dst_bpc)) < 0) + ecode = code; + if ((code = param_write_int(plist, "DstComponents", &pcdev->dst_num_components)) < 0) + ecode = code; + if ((code = gx_downscaler_write_params(plist, &pcdev->downscale, 0)) < 0) + ecode = code; + if ((code = param_write_int(plist, "OutputAsPXM", &pcdev->output_as_pxm)) < 0) + ecode = code; + if ((code = param_write_bool(plist, "LanguageUsesROPs", &pcdev->language_uses_rops)) < 0) + ecode = code; + + return ecode; +} + +/* Set parameters. We allow setting the number of bits per component, and number of components. */ +static int +chameleon_put_params(gx_device * pdev, gs_param_list * plist) +{ + gx_device_chameleon *pcdev = (gx_device_chameleon *)pdev; + gx_device_color_info save_info; + int pxm = pcdev->output_as_pxm; + int dst_num_comps = pcdev->dst_num_components; + int dst_bpc = pcdev->dst_bpc; + bool language_uses_rops = pcdev->language_uses_rops; + int ecode; + int code; + const char *vname; + + ecode = gx_downscaler_read_params(plist, &pcdev->downscale, 0); + + if ((code = param_read_int(plist, (vname = "DstBitDepth"), &dst_bpc)) != 1) { + if (code < 0) + ecode = code; + else + switch (dst_bpc) { + case 1: case 2: case 4: case 8: break; + default: + param_signal_error(plist, vname, + ecode = gs_error_rangecheck); + } + } + + if ((code = param_read_int(plist, (vname = "DstComponents"), &dst_num_comps)) != 1) { + if (code < 0) + ecode = code; + else + switch (dst_num_comps) { + case 1: case 3: case 4: break; + default: + param_signal_error(plist, vname, + ecode = gs_error_rangecheck); + } + } + + if ((code = param_read_int(plist, (vname = "OutputAsPXM"), &pxm)) != 1) { + if (code == gs_error_typecheck) + pxm = 1; + else if (code < 0) + ecode = code; + else + pxm = !!pxm; + } + + if ((code = param_read_bool(plist, (vname = "LanguageUsesROPs"), &language_uses_rops)) != 1) { + if (code == gs_error_typecheck) + language_uses_rops = false; + else if (code < 0) + ecode = code; + } + + ecode = gdev_prn_put_params(pdev, plist); + if (ecode < 0) + return ecode; + + pcdev->dst_bpc = dst_bpc; + pcdev->dst_num_components = dst_num_comps; + pcdev->output_as_pxm = pxm; + pcdev->language_uses_rops = language_uses_rops; + + reconfigure_baby(pcdev); + + return 0; +} + +static int +chameleon_spec_op(gx_device *dev_, int op, void *data, int datasize) +{ + gx_device_chameleon *dev = (gx_device_chameleon *)dev_; + + if (op == gxdso_supports_iccpostrender) { + return true; + } + return gdev_prn_dev_spec_op(dev_, op, data, datasize); +} + +static int +craprgbtocmyk(void *arg, + byte **dst, + byte **src, + int w, + int h, + int raster) +{ + byte *d = *dst; + byte *s = *src; + + while (w--) { + int c = 255-*s++; + int m = 255-*s++; + int y = 255-*s++; + int k = c; + if (k > m) + k = m; + if (k > y) + k = y; + *d++ = c - k; + *d++ = m - k; + *d++ = y - k; + *d++ = k; + } + + return 0; +} + +static int +header_4x1(FILE *file, gx_device_chameleon *pcdev) +{ + fprintf(file, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\nTUPLTYPE CMYK\nENDHDR\n", + pcdev->width, pcdev->height); + return 0; +} + +static int +write_4x1(const byte *data, int n, FILE *file) +{ + byte b[8]; + n -= 4; + while (n > 0) { + byte d = *data++; + b[0] = (d & 128) ? 255 : 0; + b[1] = (d & 64) ? 255 : 0; + b[2] = (d & 32) ? 255 : 0; + b[3] = (d & 16) ? 255 : 0; + b[4] = (d & 8) ? 255 : 0; + b[5] = (d & 4) ? 255 : 0; + b[6] = (d & 2) ? 255 : 0; + b[7] = (d & 1) ? 255 : 0; + fwrite(b, 1, 8, file); + n -= 8; + } + if (n == 0) { + byte d = *data; + b[0] = (d & 128) ? 255 : 0; + b[1] = (d & 64) ? 255 : 0; + b[2] = (d & 32) ? 255 : 0; + b[3] = (d & 16) ? 255 : 0; + fwrite(b, 1, 4, file); + } + return 0; +} + +static int +header_3x8(FILE *file, gx_device_chameleon *pcdev) +{ + fprintf(file, "P6\n%d %d 255\n", + pcdev->width, pcdev->height); + return 0; +} + +static int +do_fwrite(const byte *data, int n, FILE *file) +{ + return fwrite(data, 1, (n+7)>>3, file); +} + +static int tiff_chunky_post_cm(void *arg, byte **dst, byte **src, int w, int h, + int raster) +{ + gsicc_bufferdesc_t input_buffer_desc, output_buffer_desc; + gsicc_link_t *icclink = (gsicc_link_t*)arg; + + gsicc_init_buffer(&input_buffer_desc, icclink->num_input, 1, false, + false, false, 0, raster, h, w); + gsicc_init_buffer(&output_buffer_desc, icclink->num_output, 1, false, + false, false, 0, raster, h, w); + icclink->procs.map_buffer(NULL, icclink, &input_buffer_desc, &output_buffer_desc, + src[0], dst[0]); + return 0; +} + +static byte ht_data[256] = +{ + 0x0E, 0x8E, 0x2E, 0xAE, 0x06, 0x86, 0x26, 0xA6, 0x0C, 0x8C, 0x2C, 0xAC, 0x04, 0x84, 0x24, 0xA4, + 0xCE, 0x4E, 0xEE, 0x6E, 0xC6, 0x46, 0xE6, 0x66, 0xCC, 0x4C, 0xEC, 0x6C, 0xC4, 0x44, 0xE4, 0x64, + 0x3E, 0xBE, 0x1E, 0x9E, 0x36, 0xB6, 0x16, 0x96, 0x3C, 0xBC, 0x1C, 0x9C, 0x34, 0xB4, 0x14, 0x94, + 0xFE, 0x7E, 0xDE, 0x5E, 0xF6, 0x76, 0xD6, 0x56, 0xFC, 0x7C, 0xDC, 0x5C, 0xF4, 0x74, 0xD4, 0x54, + 0x01, 0x81, 0x21, 0xA1, 0x09, 0x89, 0x29, 0xA9, 0x03, 0x83, 0x23, 0xA3, 0x0B, 0x8B, 0x2B, 0xAB, + 0xC1, 0x41, 0xE1, 0x61, 0xC9, 0x49, 0xE9, 0x69, 0xC3, 0x43, 0xE3, 0x63, 0xCB, 0x4B, 0xEB, 0x6B, + 0x31, 0xB1, 0x11, 0x91, 0x39, 0xB9, 0x19, 0x99, 0x33, 0xB3, 0x13, 0x93, 0x3B, 0xBB, 0x1B, 0x9B, + 0xF1, 0x71, 0xD1, 0x51, 0xF9, 0x79, 0xD9, 0x59, 0xF3, 0x73, 0xD3, 0x53, 0xFB, 0x7B, 0xDB, 0x5B, + 0x0D, 0x8D, 0x2D, 0xAD, 0x05, 0x85, 0x25, 0xA5, 0x0F, 0x8F, 0x2F, 0xAF, 0x07, 0x87, 0x27, 0xA7, + 0xCD, 0x4D, 0xED, 0x6D, 0xC5, 0x45, 0xE5, 0x65, 0xCF, 0x4F, 0xEF, 0x6F, 0xC7, 0x47, 0xE7, 0x67, + 0x3D, 0xBD, 0x1D, 0x9D, 0x35, 0xB5, 0x15, 0x95, 0x3F, 0xBF, 0x1F, 0x9F, 0x37, 0xB7, 0x17, 0x97, + 0xFD, 0x7D, 0xDD, 0x5D, 0xF5, 0x75, 0xD5, 0x55, 0xFF, 0x7F, 0xDF, 0x5F, 0xF7, 0x77, 0xD7, 0x57, + 0x02, 0x82, 0x22, 0xA2, 0x0A, 0x8A, 0x2A, 0xAA, 0x01 /*0x00*/, 0x80, 0x20, 0xA0, 0x08, 0x88, 0x28, 0xA8, + 0xC2, 0x42, 0xE2, 0x62, 0xCA, 0x4A, 0xEA, 0x6A, 0xC0, 0x40, 0xE0, 0x60, 0xC8, 0x48, 0xE8, 0x68, + 0x32, 0xB2, 0x12, 0x92, 0x3A, 0xBA, 0x1A, 0x9A, 0x30, 0xB0, 0x10, 0x90, 0x38, 0xB8, 0x18, 0x98, + 0xF2, 0x72, 0xD2, 0x52, 0xFA, 0x7A, 0xDA, 0x5A, 0xF0, 0x70, 0xD0, 0x50, 0xF8, 0x78, 0xD8, 0x58 +}; + +static gx_downscaler_ht_t default_ht[4] = +{ + { 16, 16, 16, 0, 0, ht_data }, + { 16, 16, 16, 0, 0, ht_data }, + { 16, 16, 16, 0, 0, ht_data }, + { 16, 16, 16, 0, 0, ht_data } +}; + +/* Send the page to the printer. */ +static int +chameleon_print_page(gx_device_printer * pdev, FILE * prn_stream) +{ /* Just dump the bits on the file. */ + gx_device_chameleon *pcdev = (gx_device_chameleon *)pdev; + /* If the file is 'nul', don't even do the writes. */ + int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev); + byte *in, *in_alloc; + byte *data; + int nul; + int line_count = pdev->height; + int i, code; + gx_downscaler_t ds = { NULL }; + int depth = pdev->color_info.depth; + int factor = pcdev->downscale.downscale_factor; + int mfs = pcdev->downscale.min_feature_size; + int pxm = pcdev->output_as_pxm; + int dst_bpp; + int (*write)(const byte *, int, FILE *) = do_fwrite; + int (*header)(FILE *, gx_device_chameleon *) = NULL; + int bitwidth; + gx_downscale_cm_fn *col_convert = NULL; + void *col_convert_arg = NULL; + + switch (pcdev->dst_num_components * pcdev->dst_bpc) { + case 1*1: case 1*2: case 1*4: case 1*8: + case 4*4: case 4*8: case 3*8: + dst_bpp = pcdev->dst_num_components * pcdev->dst_bpc; + break; + case 3*1: + dst_bpp = 4; + break; + case 3*2: + dst_bpp = 8; + break; + case 3*4: + dst_bpp = 12; + break; + default: + return gs_error_rangecheck; + } + + bitwidth = pdev->width * dst_bpp; + line_size = (bitwidth+7)>>3; + in_alloc = gs_alloc_bytes(pdev->memory, line_size+32, "chameleon_print_page(in)"); + if (in_alloc == 0) + return_error(gs_error_VMerror); + in = in_alloc + ((32-(intptr_t)in_alloc) & 31); + + if (!strcmp(pdev->fname, "nul") || !strcmp(pdev->fname, "/dev/null")) + write = NULL; + else if (pxm) { + switch (pcdev->dst_num_components) { + case 4: + if (pcdev->dst_bpc == 1) + header = header_4x1, write = write_4x1; + break; + case 3: + if (pcdev->dst_bpc == 8) + header = header_3x8; + break; + } + } + + /* If we have a post render profile, and the number of components + * of that is compatible with the desired destination number of + * components, then use that. */ + if (pcdev->icc_struct->postren_profile && + pcdev->icc_struct->postren_profile->num_comps == pcdev->dst_num_components) { + col_convert = tiff_chunky_post_cm; + col_convert_arg = pcdev->icc_struct->postren_profile; + } + /* Can we get away with no conversion? */ + else if (pcdev->num_components == pcdev->dst_num_components && + pcdev->bpc == pcdev->dst_bpc) { + /* Nothing to do */ + } + /* Otherwise, use some inbuilt crap conversions */ + else if (pcdev->num_components == 3 && pcdev->bpc == 8 && pcdev->dst_num_components == 4) { + col_convert = craprgbtocmyk; + col_convert_arg = NULL; + } else { + emprintf(pdev->memory, "Chameleon device doesn't support this color conversion.\n"); + return gs_error_rangecheck; + } + + code = gx_downscaler_init_trapped_cm_halftone + (&ds, + (gx_device *)pdev, + pcdev->bpc, + pcdev->dst_bpc, + pcdev->num_components, + pcdev->downscale.downscale_factor, + pcdev->downscale.min_feature_size, + NULL, 0, /* Adjust width */ + 0, 0, NULL, /* Trapping w/h/comp_order */ + col_convert, col_convert_arg, /* Color Management */ + pcdev->dst_num_components, + default_ht); + if (code < 0) + goto cleanup; + + if (header) { + code = header(prn_stream, pcdev); + if (code < 0) + goto cleanup; + } + + for (i = 0; i < line_count; i++) { + code = gx_downscaler_getbits(&ds, in, i); + if (code < 0) + break; + if (write != NULL) + write(in, bitwidth, prn_stream); + } + gx_downscaler_fin(&ds); +cleanup: + gs_free_object(pdev->memory, in_alloc, "chameleon_print_page(in)"); + return code; +} diff --git a/gpdl/psi/psitop.c b/gpdl/psi/psitop.c index 778837ffd..2a5e6b59e 100644 --- a/gpdl/psi/psitop.c +++ b/gpdl/psi/psitop.c @@ -21,6 +21,7 @@ #include "imain.h" #include "imainarg.h" #include "iapi.h" +#include "psapi.h" #include "string_.h" #include "gdebug.h" #include "gp.h" @@ -39,6 +40,7 @@ #include "gxstate.h" #include "plparse.h" #include "pltop.h" +#include "plmain.h" #include "gzstate.h" #include "gsicc_manage.h" @@ -55,17 +57,112 @@ extern int zflush(i_ctx_t *); * PS interpreter instance: derived from pl_interp_implementation_t */ typedef struct ps_interp_instance_s { - pl_interp_implementation_t pl; + gs_memory_t *memory; + uint bytes_fed; + gs_lib_ctx_t *psapi_instance; } ps_interp_instance_t; static int +check_token(int token_type, const char *s, const char *e, int *score) +{ + /* Empty tokens are always OK */ + if (s == e) + return 0; + + switch (token_type) { + case 'a': + /* Angle bracket - mainly to catch << */ + break; + case 'n': + /* Name */ + /* FIXME: Check it's a valid name */ + break; + case 'd': + /* Dictionary */ + break; + case 'i': + /* int - ok by construction. */ + return 0; + case 'f': + /* float - ok by construction. */ + return 0; + } + +#define TOKEN_CHECK(n) else if (e-s == strlen(n) && memcmp(s, n, e-s) == 0) { score[0] += e-s; score[1]++; } + + if (0) {} + TOKEN_CHECK("dup") + TOKEN_CHECK("exch") + TOKEN_CHECK("grestore") + TOKEN_CHECK("gsave") + TOKEN_CHECK("idiv") + TOKEN_CHECK("lineto") + TOKEN_CHECK("mod") + TOKEN_CHECK("mul") + TOKEN_CHECK("moveto") + TOKEN_CHECK("setflat") + TOKEN_CHECK("setlinecap") + TOKEN_CHECK("setlinejoin") + TOKEN_CHECK("showpage") + TOKEN_CHECK("stroke") + TOKEN_CHECK("translate") + TOKEN_CHECK("systemdict") + + if (score[0] > 1024 && score[2] >= 3) + return 1; + if (score[0] < -1024) + return 1; + + return 0; +} + +static int +score_comment(const char *s, const char *e, int *score) +{ +#define COMMENT_CHECK(n) else if (e-s >= strlen(n) && memcmp(s, n, strlen(n)) == 0) { score[0] += 100; score[1]++; } + + if (0) {} + COMMENT_CHECK("!PS") + COMMENT_CHECK("%Title:") + COMMENT_CHECK("%Version:") + COMMENT_CHECK("%Creator:") + COMMENT_CHECK("%CreationDate:") + COMMENT_CHECK("%Document") + COMMENT_CHECK("%BoundingBox:") + COMMENT_CHECK("%HiResBoundingBox:") + COMMENT_CHECK("%Pages:") + COMMENT_CHECK("%+ procset") + COMMENT_CHECK("%End") + COMMENT_CHECK("%Begin") + COMMENT_CHECK("%Copyright") + else { + score[0]++; score[1]++; + } + + if (score[0] > 1024 && score[1] >= 3) + return 1; + + return 0; +} + +static int ps_detect_language(const char *s, int len) { /* For postscript, we look for %! */ if (len >= 2) { - /* Be careful to avoid shell scripts (e.g. #!/bin/bash) if possible */ - if (s[0] == '%' && s[1] == '!' && (len < 3 || s[2] != '/')) + if (s[0] != '%' || s[1] != '!') { + /* Not what we were looking for */ + } else if (len >= 12 && memcmp(s+2, "Postscript", 10) == 0) { + return 100; + } else if (len >= 4 && memcmp(s+2, "PS", 2) == 0) { + return 100; + } else if (len >= 3 && s[2] == '/') { + /* Looks like a shell script. Don't want that. */ return 0; + } else { + /* If it begins %!, then it's PROBABLY postscript */ + return 80; + } } /* For PDF, we allow for leading crap, then a postscript version marker */ { @@ -79,7 +176,7 @@ ps_detect_language(const char *s, int len) t[5] >= '1' && t[5] <= '9' && t[6] == '.' && t[7] >= '0' && t[7] <= '9') { - return 0; + return 100; } if (memcmp(t, "%!PS-Adobe-", 11) == 0 && t[11] >= '0' && t[11] <= '9' && @@ -89,14 +186,115 @@ ps_detect_language(const char *s, int len) t[19] >= '0' && t[19] <= '9' && t[20] == '.' && t[21] >= '0' && t[21] <= '9') { - return 0; + return 100; } t++; left--; } } - return 1; + /* Now we do some seriously hairy stuff */ + { + const char *t = s; + const char *token = t; + int left = len; + int token_type = 0; + int score[2] = { 0, 0 }; + + while (left--) { + if (*t == '%') { + if (check_token(token_type, token, t, score)) + break; + /* Skip to end of line */ + left--; + token = ++t; + while (left && *t != '\r' && *t != '\n') { + left--; t++; + } + if (score_comment(token, t, score)) + break; + /* Skip any combination of newlines */ + while (left && (*t == '\r' || *t == '\n')) { + left--; t++; + } + token_type = 0; + continue; + } else if (*t == 27) { + /* Give up if we meet an ESC. It could be a UEL. */ + break; + } else if (*t <= 32 || *t > 127) { + if (check_token(token_type, token, t, score)) + break; + if (*t != 9 && *t != 10 && *t != 12 && *t != 13 && *t != 32) + score[0]--; + token = t+1; + token_type = 0; + } else if (*t == '/') { + if (check_token(token_type, token, t, score)) + break; + token = t+1; + token_type = 'n'; + } else if (*t == '[' || *t == ']' || *t == '{' || *t == '}') { + if (check_token(token_type, token, t, score)) + break; + token = t+1; + token_type = 0; + } else if (*t == '<') { + if (token_type == 'a') { + /* << */ + token = t+1; + token_type = 'd'; + } else if (token_type == 'd') { + /* <<< ???!? */ + score[0] -= 10; + } else { + if (check_token(token_type, token, t, score)) + break; + token = t+1; + token_type = 'a'; + } + } else if (*t == '>') { + if (check_token(token_type, token, t, score)) + break; + token = t+1; + token_type = 0; + } else if (*t >= '0' && *t <= '9') { + if (token_type == 'i') { + /* Keep going */ + } else if (token_type == 'f') { + /* Keep going */ + } else { + if (check_token(token_type, token, t, score)) + break; + token = t; + token_type = 'i'; + } + } else if (*t == '.') { + if (token_type == 'f') { + /* seems unlikely */ + score[0]--; + break; + } else if (token_type == 'i') { + token = t; + token_type = 'f'; + } else { + if (check_token(token_type, token, t, score)) + break; + token = t; + token_type = 'f'; + } + } else { + /* Assume anything else goes into the token */ + } + t++; + } + if (score[0] < 0 || score[1] < 3) + return 0; /* Unlikely to be PS */ + else if (score[0] > 0) + return 75; /* Could be PS */ + } + + return 0; } /* Get implementation's characteristics */ @@ -130,30 +328,98 @@ ps_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t * "ps_impl_allocate_interp_instance"); int code; - +#define GS_MAX_NUM_ARGS 10 + const char *gsargs[GS_MAX_NUM_ARGS] = {0}; + int nargs = 0; + if (!psi) return gs_error_VMerror; - - code = gsapi_new_instance(&impl->interp_client_data, NULL); + + gsargs[nargs++] = "gpdl"; + /* We start gs with the nullpage device, and replace the device with the + * set_device call from the language independent code. + */ + gsargs[nargs++] = "-dNODISPLAY"; + /* As we're "printer targetted, use a jobserver */ + gsargs[nargs++] = "-dJOBSERVER"; + + psi->memory = mem; + psi->bytes_fed = 0; + psi->psapi_instance = gs_lib_ctx_get_interp_instance(mem); + code = psapi_new_instance(&psi->psapi_instance, NULL); if (code < 0) gs_free_object(mem, psi, "ps_impl_allocate_interp_instance"); + + impl->interp_client_data = psi; + + /* Tell gs not to ignore a UEL, but do an interpreter exit */ + psapi_act_on_uel(psi->psapi_instance); + + code = psapi_init_with_args01(psi->psapi_instance, nargs, (char **)gsargs); + if (code < 0) { + psapi_delete_instance(psi->psapi_instance); + gs_free_object(mem, psi, "ps_impl_allocate_interp_instance"); + } return code; } -/* Set a device into an interpreter instance */ +/* + * Get the allocator with which to allocate a device + */ +static gs_memory_t * +ps_impl_get_device_memory(pl_interp_implementation_t *impl) +{ + ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; + + return psapi_get_device_memory(psi->psapi_instance); +} + static int -ps_impl_set_device(pl_interp_implementation_t *impl, gx_device *device) +ps_impl_set_param(pl_interp_implementation_t *impl, + pl_set_param_type type, + const char *param, + const void *val) { - /* Nothing to PS/PDF manages it's own device */ - return 0; + ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; + + return psapi_set_param(psi->psapi_instance, type, param, val); } +static int +ps_impl_post_args_init(pl_interp_implementation_t *impl) +{ + ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; + const float *resolutions; + const long *page_sizes; + + pl_main_get_forced_geometry(psi->memory, &resolutions, &page_sizes); + psapi_force_geometry(psi->psapi_instance, resolutions, page_sizes); + + return psapi_init_with_args2(psi->psapi_instance); +} /* Prepare interp instance for the next "job" */ static int -ps_impl_init_job(pl_interp_implementation_t *impl) +ps_impl_init_job(pl_interp_implementation_t *impl, + gx_device *device) { - return gsapi_init_with_args(impl->interp_client_data, 0, NULL); + ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; + int exit_code; + int code = 0; + + /* Any error after here *must* reset the device to null */ + if (code >= 0) + code = psapi_set_device(psi->psapi_instance, device); + + if (code >= 0) + code = psapi_run_string(psi->psapi_instance, "erasepage", 0, &exit_code); + + if (code < 0) { + int code1 = psapi_set_device(psi->psapi_instance, NULL); + (void)code1; + } + + return code; } /* Not complete. */ @@ -161,15 +427,92 @@ static int ps_impl_process_file(pl_interp_implementation_t *impl, char *filename) { /* NB incomplete */ + ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; + int exit_code; + + return psapi_run_file(psi->psapi_instance, filename, 0, &exit_code); +} + +/* Do any setup for parser per-cursor */ +static int /* ret 0 or +ve if ok, else -ve error code */ +ps_impl_process_begin(pl_interp_implementation_t * impl) +{ + ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; int exit_code; - return gsapi_run_file(impl->interp_client_data, filename, 0, &exit_code); + + psi->bytes_fed = 0; + return psapi_run_string_begin(psi->psapi_instance, 0, &exit_code); +} + +/* TODO: in some fashion have gs pass back how far into the input buffer it + * had read, so we don't need to explicitly search the buffer for the UEL + */ +static int +ps_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr) +{ + ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; + const unsigned int len = pr->limit - pr->ptr; + int code, exit_code = 0; + + code = psapi_run_string_continue(psi->psapi_instance, (const char *)pr->ptr + 1, len, 0, &exit_code); + if (exit_code == gs_error_InterpreterExit) { + int64_t offset; + + offset = psapi_get_uel_offset(psi->psapi_instance) - psi->bytes_fed; + pr->ptr += offset; + psi->bytes_fed += offset + 1; + +#ifdef SEND_CTRLD_AFTER_UEL + { + const char eot[1] = {4}; + code = psapi_run_string_continue(psi->psapi_instance, eot, 1, 0, &exit_code); + (void)code; /* Ignoring code here */ + } +#endif + return gs_error_InterpreterExit; + } + else { + pr->ptr = pr->limit; + psi->bytes_fed += len; + } + return code; +} + +static int /* ret 0 or +ve if ok, else -ve error code */ +ps_impl_process_end(pl_interp_implementation_t * impl) +{ + ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; + int exit_code, code; + + code = psapi_run_string_end(psi->psapi_instance, 0, &exit_code); + + if (exit_code == gs_error_InterpreterExit || code == gs_error_NeedInput) + code = 0; + + return code; } /* Not implemented */ static int ps_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor) { - return 0; + 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 */ @@ -194,23 +537,24 @@ ps_impl_report_errors(pl_interp_implementation_t *impl, /* interp instance static int ps_impl_dnit_job(pl_interp_implementation_t *impl) { - return 0; -} + ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; -/* Remove a device from an interpreter instance */ -static int -ps_impl_remove_device(pl_interp_implementation_t *impl) -{ - return 0; + return psapi_set_device(psi->psapi_instance, NULL); } /* Deallocate a interpreter instance */ static int ps_impl_deallocate_interp_instance(pl_interp_implementation_t *impl) { + ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data; + int code; - int code = gsapi_exit(impl->interp_client_data); - gsapi_delete_instance(impl->interp_client_data); + if (psi == NULL) + return 0; + code = psapi_exit(psi->psapi_instance); + psapi_delete_instance(psi->psapi_instance); + gs_free_object(psi->memory, psi, "ps_impl_allocate_interp_instance"); + impl->interp_client_data = NULL; return code; } @@ -218,15 +562,18 @@ ps_impl_deallocate_interp_instance(pl_interp_implementation_t *impl) const pl_interp_implementation_t ps_implementation = { ps_impl_characteristics, ps_impl_allocate_interp_instance, - ps_impl_set_device, + ps_impl_get_device_memory, + ps_impl_set_param, + ps_impl_post_args_init, ps_impl_init_job, ps_impl_process_file, - NULL, /* process */ + ps_impl_process_begin, + ps_impl_process, + ps_impl_process_end, 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, NULL }; diff --git a/pcl/pcl/pctop.c b/pcl/pcl/pctop.c index f0d981dea..383f3fcfc 100644 --- a/pcl/pcl/pctop.c +++ b/pcl/pcl/pctop.c @@ -40,6 +40,7 @@ #include "pcpalet.h" #include "rtgmode.h" #include "gsicc_manage.h" +#include "pcparam.h" /* Configuration table for modules */ extern const pcl_init_t pcparse_init; @@ -168,9 +169,30 @@ typedef struct pcl_interp_instance_s static int pcl_detect_language(const char *s, int length) { + int count; + int len; + if (length < 2) - return 1; - return memcmp(s, "\033E", 2); + return 0; + if (s[0] == 27) { + if (s[1] == 'E') + return 100; + return 80; + } + + /* Count the number of ESC's */ + for (count = 0, len = length; len > 0; len--, s++) + { + if (*s == 27) + count++; + } + + if (count > 10 || count > length/20) + return 80; + if (count > 0) + return 20; + + return 0; } /* Get implementation's characteristics */ @@ -252,7 +274,7 @@ pcl_impl_allocate_interp_instance(pl_interp_implementation_t *impl, } pcli->pcs.pjls = pl_main_get_pjl_instance(mem); - + /* Return success */ impl->interp_client_data = pcli; return 0; @@ -265,12 +287,12 @@ pcl_get_personality(pl_interp_implementation_t * impl, gx_device * device) { pcl_interp_instance_t *pcli = impl->interp_client_data; char *personality = pl_main_get_pcl_personality(pcli->memory); - + if (!strcmp(personality, "PCL5C")) return pcl5c; else if (!strcmp(personality, "PCL5E")) return pcl5e; - /* + /* * match RTL or any string containing "GL" we see many variants in * test files: HPGL/2, HPGL2 etc. */ @@ -291,21 +313,20 @@ pcl_set_icc_params(pl_interp_implementation_t * impl, gs_gstate * pgs) return pl_set_icc_params(pcli->memory, pgs); } -/* Set a device into an interperter instance */ +/* Prepare interp instance for the next "job" */ static int /* ret 0 ok, else -ve error code */ -pcl_impl_set_device(pl_interp_implementation_t * impl, /* interp instance to use */ - gx_device * device /* device to set (open or closed) */ - ) +pcl_impl_init_job(pl_interp_implementation_t * impl, /* interp instance to start job in */ + gx_device * device) { - - int code; + int code = 0; pcl_interp_instance_t *pcli = impl->interp_client_data; gs_memory_t *mem = pcli->memory; - enum { Sbegin, Ssetdevice, Sinitg, Sgsave1, Spclgsave, Sreset, Serase, Sdone } stage; + pcl_process_init(&pcli->pst); + stage = Sbegin; /* Set parameters from the main instance */ @@ -385,19 +406,22 @@ pcl_impl_set_device(pl_interp_implementation_t * impl, /* interp instance to case Sbegin: /* nothing left to undo */ break; } + + /* Warn the device we use ROPs */ + if (code == 0) { + code = put_param1_bool(&pcli->pcs, "LanguageUsesROPs", true); + if (!device->is_open) + code = gs_opendevice(device); + } + return code; } -/* Prepare interp instance for the next "job" */ -static int /* ret 0 ok, else -ve error code */ -pcl_impl_init_job(pl_interp_implementation_t * impl /* interp instance to start job in */ - ) +/* Do any setup for parser per-cursor */ +static int /* ret 0 or +ve if ok, else -ve error code */ +pcl_impl_process_begin(pl_interp_implementation_t * impl) { - int code = 0; - pcl_interp_instance_t *pcli = impl->interp_client_data; - - pcl_process_init(&pcli->pst); - return code; + return 0; } /* Parse a cursor-full of data */ @@ -412,6 +436,12 @@ pcl_impl_process(pl_interp_implementation_t * impl, /* interp instance to return code; } +static int /* ret 0 or +ve if ok, else -ve error code */ +pcl_impl_process_end(pl_interp_implementation_t * impl) +{ + return 0; +} + /* Skip to end of job ret 1 if done, 0 ok but EOJ not found, else -ve error code */ static int pcl_impl_flush_to_eoj(pl_interp_implementation_t * impl, /* interp instance to flush for */ @@ -469,24 +499,12 @@ pcl_impl_report_errors(pl_interp_implementation_t * impl, /* interp instance to /* Wrap up interp instance after a "job" */ static int /* ret 0 ok, else -ve error code */ -pcl_impl_dnit_job(pl_interp_implementation_t * impl /* interp instance to wrap up job in */ - ) +pcl_impl_dnit_job(pl_interp_implementation_t * impl) /* interp instance to wrap up job in */ { pcl_interp_instance_t *pcli = impl->interp_client_data; pcl_state_t *pcs = &pcli->pcs; - - if (pcs->raster_state.graphics_mode) - return pcl_end_graphics_mode(pcs); - return 0; -} - -/* Remove a device from an interperter instance */ -static int /* ret 0 ok, else -ve error code */ -pcl_impl_remove_device(pl_interp_implementation_t * impl /* interp instance to use */ - ) -{ int code; - pcl_interp_instance_t *pcli = impl->interp_client_data; + gx_device *device = gs_currentdevice(pcs->pgs); /* Note: "PCL" grestore. */ code = pcl_grestore(&pcli->pcs); @@ -498,7 +516,22 @@ pcl_impl_remove_device(pl_interp_implementation_t * impl /* interp instance to if (code < 0) return code; - return pcl_do_resets(&pcli->pcs, pcl_reset_permanent); + code = pcl_do_resets(&pcli->pcs, pcl_reset_permanent); + if (code < 0) + return code; + + if (pcs->raster_state.graphics_mode) + code = pcl_end_graphics_mode(pcs); + + if (code >= 0) { + /* Warn the device that ROP usage has come to an end */ + code = put_param1_bool(&pcli->pcs, "LanguageUsesROPs", false); + + if (!device->is_open) + code = gs_opendevice(device); + } + + return code; } /* Deallocate a interpreter instance */ @@ -545,15 +578,18 @@ pcl_end_page_top(pcl_state_t * pcs, int num_copies, int flush) pl_interp_implementation_t pcl_implementation = { pcl_impl_characteristics, pcl_impl_allocate_interp_instance, - pcl_impl_set_device, + NULL, + NULL, + NULL, pcl_impl_init_job, NULL, /* process_file */ + pcl_impl_process_begin, pcl_impl_process, + pcl_impl_process_end, pcl_impl_flush_to_eoj, pcl_impl_process_eof, pcl_impl_report_errors, pcl_impl_dnit_job, - pcl_impl_remove_device, pcl_impl_deallocate_interp_instance, NULL }; diff --git a/pcl/pl/pjparsei.c b/pcl/pl/pjparsei.c index dc060edf5..63ef55960 100644 --- a/pcl/pl/pjparsei.c +++ b/pcl/pl/pjparsei.c @@ -35,8 +35,10 @@ pjl_detect_language(const char *s, int len) if (len && *s == '\n') s++, len--; if (len < 4) - return 1; - return memcmp(s, "@PJL", 4); + return 0; + if (memcmp(s, "@PJL", 4) == 0) + return 100; + return 0; } /* Get implementation's characteristics */ @@ -71,19 +73,10 @@ pjl_impl_allocate_interp_instance(pl_interp_implementation_t *impl, return 0; } -/* Set a device into an interperter instance */ -static int /* ret 0 ok, else -ve error code */ -pjl_impl_set_device(pl_interp_implementation_t *impl, /* interp instance to use */ - gx_device * device /* device to set (open or closed) */ - ) -{ - return 0; -} - /* Prepare interp instance for the next "job" */ static int /* ret 0 ok, else -ve error code */ -pjl_impl_init_job(pl_interp_implementation_t *impl /* interp instance to start job in */ - ) +pjl_impl_init_job(pl_interp_implementation_t *impl, /* interp instance to start job in */ + gx_device *device) { pjl_parser_state *pjls = impl->interp_client_data; if (pjls == NULL) @@ -93,6 +86,12 @@ pjl_impl_init_job(pl_interp_implementation_t *impl /* interp instance to s return 0; } +static int +pjl_impl_process_begin(pl_interp_implementation_t *impl /* interp instance to process data job in */) +{ + return 0; +} + /* Parse a cursor-full of data */ /* The parser reads data from the input @@ -113,6 +112,12 @@ pjl_impl_process(pl_interp_implementation_t *impl, /* interp instance to p return code == 1 ? e_ExitLanguage : code; } +static int +pjl_impl_process_end(pl_interp_implementation_t *impl /* interp instance to process data job in */) +{ + return 0; +} + /* Skip to end of job ret 1 if done, 0 ok but EOJ not found, else -ve error code */ static int pjl_impl_flush_to_eoj(pl_interp_implementation_t * impl, /* interp impl to flush for */ @@ -149,14 +154,6 @@ pjl_impl_dnit_job(pl_interp_implementation_t * impl /* interp impl to wrap return 0; } -/* Remove a device from an interperter impl */ -static int /* ret 0 ok, else -ve error code */ -pjl_impl_remove_device(pl_interp_implementation_t * impl /* interp impl to use */ - ) -{ - return 0; -} - /* Deallocate a interpreter impl */ static int /* ret 0 ok, else -ve error code */ pjl_impl_deallocate_interp_instance(pl_interp_implementation_t * impl /* impl to dealloc */ @@ -171,15 +168,18 @@ pjl_impl_deallocate_interp_instance(pl_interp_implementation_t * impl /* imp pl_interp_implementation_t pjl_implementation = { pjl_impl_characteristics, pjl_impl_allocate_interp_instance, - pjl_impl_set_device, + NULL, + NULL, + NULL, pjl_impl_init_job, NULL, /* process_file */ + pjl_impl_process_begin, pjl_impl_process, + pjl_impl_process_end, pjl_impl_flush_to_eoj, pjl_impl_process_eof, pjl_impl_report_errors, pjl_impl_dnit_job, - pjl_impl_remove_device, pjl_impl_deallocate_interp_instance, NULL, /* instance */ }; diff --git a/pcl/pl/pl.mak b/pcl/pl/pl.mak index f7432f2c2..1dd061059 100644 --- a/pcl/pl/pl.mak +++ b/pcl/pl/pl.mak @@ -352,7 +352,8 @@ $(PLOBJ)plmain.$(OBJ): $(PLSRC)plmain.c $(AK) $(string__h)\ $(gsalloc_h) $(gsargs_h) $(gp_h) $(gsdevice_h) $(gslib_h) $(gslibctx_h)\ $(gxdevice_h) $(gsparam_h) $(pjtop_h) $(plapi_h) $(plparse_h)\ $(plmain_h) $(pltop_h) $(stream_h) $(strmio_h) $(gsargs_h) $(dwtrace_h) $(vdtrace_h)\ - $(gxclpage_h) $(gdevprn_h) $(gxiodev_h) $(PL_MAK) $(MAKEDIRS) + $(gxclpage_h) $(gdevprn_h) $(gxiodev_h) $(assert__h)\ + $(PL_MAK) $(MAKEDIRS) $(PLCCC) $(PLSRC)plmain.c $(PLO_)plmain.$(OBJ) # Real top level; provides main that just calls pl_main diff --git a/pcl/pl/plapi.c b/pcl/pl/plapi.c index f6673f45b..177b78d76 100644 --- a/pcl/pl/plapi.c +++ b/pcl/pl/plapi.c @@ -20,8 +20,27 @@ #include "gserrors.h" #include "gsexit.h" +#include "gp.h" +#include "gscdefs.h" +#include "gsmemory.h" + +/* 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; +} + GSDLLEXPORT int GSDLLAPI -plapi_new_instance(void **lib, void *caller_handle) +gsapi_new_instance(void **lib, void *caller_handle) { gs_memory_t *heap_mem = gs_malloc_init(); gs_memory_t *chunk_mem; @@ -49,7 +68,24 @@ plapi_new_instance(void **lib, void *caller_handle) } GSDLLEXPORT int GSDLLAPI -plapi_init_with_args(void *lib, int argc, char **argv) +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)) +{ + gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; + + if (ctx == NULL) + return gs_error_Fatal; + ctx->core->stdin_fn = stdin_fn; + ctx->core->stdout_fn = stdout_fn; + ctx->core->stderr_fn = stderr_fn; + + return 0; +} + +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) @@ -59,17 +95,17 @@ plapi_init_with_args(void *lib, int argc, char **argv) } GSDLLEXPORT int GSDLLAPI -plapi_run_file(void *lib, const char *file_name) +gsapi_run_file(void *lib, const char *file_name) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib; if (lib == NULL) return gs_error_Fatal; - + return pl_main_run_file(pl_main_get_instance(ctx->memory), file_name); } GSDLLEXPORT int GSDLLAPI -plapi_exit(void *lib) +gsapi_exit(void *lib) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib; if (lib == NULL) @@ -79,7 +115,7 @@ plapi_exit(void *lib) } GSDLLEXPORT int GSDLLAPI -plapi_delete_instance(void *lib) +gsapi_delete_instance(void *lib) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib; if (lib == NULL) @@ -88,9 +124,18 @@ plapi_delete_instance(void *lib) return pl_main_delete_instance(pl_main_get_instance(ctx->memory)); } -/* Set the display callback structure */ +GSDLLEXPORT int GSDLLAPI gsapi_set_poll(void *instance, + int (GSDLLCALLPTR poll_fn)(void *caller_handle)) +{ + gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; + if (instance == NULL) + return gs_error_Fatal; + ctx->core->poll_fn = poll_fn; + return 0; +} + GSDLLEXPORT int GSDLLAPI -plapi_set_display_callback(void *lib, void *callback) +gsapi_set_display_callback(void *lib, display_callback *callback) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib; if (lib == NULL) @@ -100,7 +145,133 @@ plapi_set_display_callback(void *lib, void *callback) } GSDLLEXPORT int GSDLLAPI -plapi_run_string_begin(void *lib) +gsapi_set_default_device_list(void *instance, char *list, int listlen) +{ + gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; + if (instance == 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 *instance, char **list, int *listlen) +{ + gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; + if (instance == 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; +} + +/* Initialise the interpreter */ +GSDLLEXPORT int GSDLLAPI +gsapi_set_arg_encoding(void *instance, int encoding) +{ + gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; + if (instance == NULL) + return gs_error_Fatal; + + if (encoding == PL_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. + */ + pl_main_set_arg_decode(pl_main_get_instance(ctx->memory), gp_local_arg_encoding_get_codepoint); +#else + pl_main_set_arg_decode(pl_main_get_instance(ctx->memory), NULL); +#endif /* WIN32 */ + return 0; + } + if (encoding == PL_ARG_ENCODING_UTF8) { + pl_main_set_arg_decode(pl_main_get_instance(ctx->memory), NULL); + return 0; + } + if (encoding == PL_ARG_ENCODING_UTF16LE) { + pl_main_set_arg_decode(pl_main_get_instance(ctx->memory), utf16le_get_codepoint); + return 0; + } + return gs_error_Fatal; +} + +GSDLLEXPORT int GSDLLAPI +gsapi_run_string_begin(void *lib) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib; if (lib == NULL) @@ -109,7 +280,7 @@ plapi_run_string_begin(void *lib) } GSDLLEXPORT int GSDLLAPI -plapi_run_string_continue(void *lib, const char *str, unsigned int length) +gsapi_run_string_continue(void *lib, const char *str, unsigned int length) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib; if (lib == NULL) @@ -118,7 +289,7 @@ plapi_run_string_continue(void *lib, const char *str, unsigned int length) } GSDLLEXPORT int GSDLLAPI -plapi_run_string_end(void *lib) +gsapi_run_string_end(void *lib) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)lib; if (lib == NULL) diff --git a/pcl/pl/plapi.h b/pcl/pl/plapi.h index 48dc66bb8..7ab7a2814 100644 --- a/pcl/pl/plapi.h +++ b/pcl/pl/plapi.h @@ -17,8 +17,8 @@ /* plapi.h */ /* pcl6 as a library or dll api */ -#ifndef plapi_INCLUDED -# define plapi_INCLUDED +#ifndef gsapi_INCLUDED +# define gsapi_INCLUDED /* * This API level is intended to hide everything behind @@ -46,12 +46,18 @@ # 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 __MACINTOSH__ @@ -64,11 +70,21 @@ #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 #ifdef _Windows @@ -76,22 +92,131 @@ GSDLLEXPORT int GSDLLAPI pl_wchar_to_utf8(char *out, const void *in); #endif -GSDLLEXPORT int GSDLLAPI plapi_run_file(void *instance, const char *file_name); +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 GhostPDL 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); + +/* Create a new instance of GhostPDL. + * This instance is passed to most other API functions. + * The caller_handle will be provided to callback functions. + * + * On success: Returns 0, with *instance set to the newly created + * instance handle. + * On error: (such as the maximum number of instances being exceeded) + * this will return <0 and set *instance=NULL. + */ +GSDLLEXPORT int GSDLLAPI gsapi_new_instance(void **instance, void *caller_handle); -GSDLLEXPORT int GSDLLAPI plapi_exit(void *instance); +/* Destroy an instance of GhostPDL + * Before you call this, GhostPDL must have finished. + * If GhostPDL has been initialised, you must call gsapi_exit() + * before gsapi_delete_instance. + */ +GSDLLEXPORT int 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)); -GSDLLEXPORT int GSDLLAPI plapi_init_with_args(void *instance, int argc, char **argv); +/* 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 + * + * If this is to be called, it must be called after + * gsapi_new_instance() and before gsapi_init_with_args(). + */ +GSDLLEXPORT int GSDLLAPI +gsapi_set_default_device_list(void *instance, char *list, int listlen); -GSDLLEXPORT int GSDLLAPI plapi_new_instance(void **instance, void *caller_handle); +/* Returns a pointer to the current default device string. + */ +GSDLLEXPORT int GSDLLAPI +gsapi_get_default_device_list(void *instance, 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 { + PL_ARG_ENCODING_LOCAL = 0, + PL_ARG_ENCODING_UTF8 = 1, + PL_ARG_ENCODING_UTF16LE = 2 +}; + +/* Initialise the interpreter. + * If this returns gs_error_Quit, then the interpreter quit due to + * an explicit .quit. This is not an error. You must call gsapi_exit() + * and gsapi_destroy_instance(). You may not call any other plapi + * functions. + * If this returns gs_error_Info, then usage info should be displayed. + * This is not an error. You must call gsapi_exit() and + * gsapi_destroy_instance(). You may not call any other plapi functions. + * If this returns another negative value, this is an error. + * Normal return is 0 or greater. Callers can then call other + * gsapi_run... functions if required, ending with gsapi_exit and + * gsapi_destroy_instance. + */ +GSDLLEXPORT int GSDLLAPI gsapi_init_with_args(void *instance, int argc, char **argv); -GSDLLEXPORT int GSDLLAPI plapi_delete_instance(void *instance); +GSDLLEXPORT int GSDLLAPI gsapi_run_file(void *instance, const char *file_name); -GSDLLEXPORT int GSDLLAPI plapi_set_display_callback(void *lib, void *callback); +GSDLLEXPORT int GSDLLAPI gsapi_exit(void *instance); -GSDLLEXPORT int GSDLLAPI plapi_run_string_begin(void *instance); +GSDLLEXPORT int GSDLLAPI gsapi_run_string_begin(void *instance); -GSDLLEXPORT int GSDLLAPI plapi_run_string_continue(void *instance, const char *str, unsigned int length); +GSDLLEXPORT int GSDLLAPI gsapi_run_string_continue(void *instance, const char *str, unsigned int length); -GSDLLEXPORT int GSDLLAPI plapi_run_string_end(void *instance); +GSDLLEXPORT int GSDLLAPI gsapi_run_string_end(void *instance); -#endif /* plapi_INCLUDED */ +#endif /* gsapi_INCLUDED */ diff --git a/pcl/pl/plmain.c b/pcl/pl/plmain.c index 043ab92bf..bdcb66d3c 100644 --- a/pcl/pl/plmain.c +++ b/pcl/pl/plmain.c @@ -18,6 +18,7 @@ /* Main program command-line interpreter for PCL interpreters */ #include "string_.h" #include <stdlib.h> /* atof */ +#include "assert_.h" #include "gdebug.h" #include "gscdefs.h" #include "gsio.h" @@ -87,12 +88,16 @@ struct pl_main_instance_s { /* The following are set at initialization time. */ gs_memory_t *memory; + gs_memory_t *device_memory; long base_time[2]; /* starting time */ int error_report; /* -E# */ bool pause; /* -dNOPAUSE => false */ int first_page; /* -dFirstPage= */ int last_page; /* -dLastPage= */ gx_device *device; + gs_gc_root_t *device_root; + pl_main_get_codepoint_t *get_codepoint; + /* Get next 'unicode' codepoint */ pl_interp_implementation_t *implementation; /*-L<Language>*/ @@ -102,7 +107,9 @@ struct pl_main_instance_s bool interpolate; bool nocache; bool page_set_on_command_line; + long page_size[2]; bool res_set_on_command_line; + float res[2]; bool high_level_device; #ifndef OMIT_SAVED_PAGES_TEST bool saved_pages_test_mode; @@ -119,17 +126,11 @@ struct pl_main_instance_s arg_list args; pl_interp_implementation_t **implementations; pl_interp_implementation_t *curr_implementation; - pl_interp_implementation_t *desired_implementation; byte buf[8192]; /* languages read buffer */ void *disp; /* display device pointer NB wrong - remove */ }; -/* ---------------- Static data for memory management ------------------ */ - -static gs_gc_root_t device_root; - - /* ---------------- Forward decls ------------------ */ static int pl_main_languages_init(gs_memory_t * mem, pl_main_instance_t * inst); @@ -209,7 +210,7 @@ pl_main_init_with_args(pl_main_instance_t *inst, int argc, char *argv[]) gs_param_list_set_persistent_keys((gs_param_list *)&inst->params, false); arg_init(&inst->args, (const char **)argv, argc, pl_main_arg_fopen, NULL, - NULL, mem); + inst->get_codepoint, mem); /* Create PDL instances, etc */ if (pl_main_languages_init(mem, inst) < 0) { @@ -219,7 +220,7 @@ pl_main_init_with_args(pl_main_instance_t *inst, int argc, char *argv[]) inst->curr_implementation = pjli = inst->implementations[0]; /* initialize pjl, needed for option processing. */ - if (pl_init_job(pjli) < 0) { + if (pl_init_job(pjli, inst->device) < 0) { return gs_error_Fatal; } @@ -256,25 +257,53 @@ pl_main_init_with_args(pl_main_instance_t *inst, int argc, char *argv[]) int -pl_main_run_string_begin(void *instance) +pl_main_run_string_begin(pl_main_instance_t *minst) { - return 0; + return pl_process_begin(minst->curr_implementation); } int -pl_main_run_string_continue(void *instance, const char *str, unsigned int length) +pl_main_run_string_continue(pl_main_instance_t *minst, const char *str, unsigned int length) { - return 0; + stream_cursor_read cursor; + + cursor.ptr = (const byte *)str-1; /* -1 because of gs's stupid stream convention */ + cursor.limit = cursor.ptr + length; + return pl_process(minst->curr_implementation, &cursor); } int -pl_main_run_string_end(void *instance) +pl_main_run_string_end(pl_main_instance_t *minst) { - return 0; + return pl_process_end(minst->curr_implementation); } -int -pl_main_run_file(pl_main_instance_t *minst, const char *filename) +static int +revert_to_pjli(pl_main_instance_t *minst) +{ + pl_interp_implementation_t *pjli = + minst->implementations[0]; + int code; + + /* If we're already in PJL, don't clear the state. */ + if (minst->curr_implementation == pjli) + return 0; + + if (minst->curr_implementation) { + code = pl_dnit_job(minst->curr_implementation); + if (code < 0) { + minst->curr_implementation = NULL; + return code; + } + } + minst->curr_implementation = pjli; + code = pl_init_job(minst->curr_implementation, minst->device); + + return code; +} + +static int +pl_main_run_file_utf8(pl_main_instance_t *minst, const char *filename) { bool new_job = true; pl_interp_implementation_t *pjli = @@ -283,20 +312,40 @@ pl_main_run_file(pl_main_instance_t *minst, const char *filename) stream *s; int code = 0; bool is_stdin = filename[0] == '-' && filename[1] == 0; + bool use_process_file = false; + bool first_job = true; + pl_interp_implementation_t *desired_implementation = NULL; s = sfopen(filename, "r", mem); if (s == NULL) - return gs_error_Fatal; + return gs_error_undefinedfilename; + + /* This function can run in 2 modes. Either it can run a file directly + * using the run_file mechanism, or it can feed the data piecemeal + * using the run_string mechanism. Which one depends on several things: + * + * If we're being piped data, then we have to use run_string. + * If we are entered (as is usually the case) with PJL as the selected + * interpreter, then we do a quick assessment of the file contents to + * pick an interpreter. If the first interpreter has a run_file method + * then we'll use that. + * + * This means that files that start with PJL data will always be run + * using run_string. + */ for (;;) { if_debug1m('I', mem, "[i][file pos=%ld]\n", sftell(s)); + /* Check for EOF and prepare the next block of data. */ if (s->cursor.r.ptr == s->cursor.r.limit && sfeof(s)) { if_debug0m('I', mem, "End of of data\n"); + if (pl_process_end(minst->curr_implementation) < 0) + goto error_fatal; pl_process_eof(minst->curr_implementation); - if (pl_dnit_job(minst->curr_implementation) < 0) - return gs_error_Fatal; + if (revert_to_pjli(minst) < 0) + goto error_fatal_reverted; break; } code = s_process_read_buf(s); @@ -304,102 +353,163 @@ pl_main_run_file(pl_main_instance_t *minst, const char *filename) break; if (new_job) { - if_debug0m('I', mem, "Selecting PDL\n"); - minst->desired_implementation = pl_select_implementation(pjli, minst, s); - if (minst->curr_implementation != minst->desired_implementation) { - code = pl_remove_device(minst->curr_implementation); - if (code >= 0) - code = pl_set_device(minst->desired_implementation, minst->device); - if (code < 0) { - minst->curr_implementation = minst->desired_implementation; - return gs_error_Fatal; + /* The only time the current implementation won't be PJL, + * is if someone has preselected a particular language + * before calling this function. */ + if (minst->curr_implementation == pjli) { + /* Autodetect the language based on the content. */ + desired_implementation = pl_select_implementation(pjli, minst, s); + + /* Possibly this never happens? But attempt to cope anyway. */ + if (desired_implementation == NULL) + goto flush_to_end_of_job; + if (gs_debug_c('I') || gs_debug_c(':')) + dmlprintf1(mem, "PDL detected as %s\n", + pl_characteristics(desired_implementation)->language); + + /* If the language implementation needs changing, change it. */ + if (desired_implementation != pjli) { + code = pl_dnit_job(pjli); + minst->curr_implementation = NULL; + if (code >= 0) + code = pl_init_job(desired_implementation, minst->device); + if (code < 0) + goto error_fatal; + minst->curr_implementation = desired_implementation; } } - minst->curr_implementation = minst->desired_implementation; - - /* Don't reset PJL if there is PJL state from the command line arguments. */ - if (minst->curr_implementation == pjli) { - if (minst->pjl_from_args == false) { - if (pl_init_job(pjli) < 0) - return gs_error_Fatal; - } else - minst->pjl_from_args = false; - } else { - if (pl_init_job(minst->curr_implementation) < 0) - return gs_error_Fatal; + if (minst->curr_implementation != pjli) { + if_debug1m('I', mem, "initialised (%s)\n", + pl_characteristics(minst->curr_implementation)->language); + if (first_job && + !is_stdin && + minst->curr_implementation->proc_process_file) { + /* If we aren't being piped data, and this interpreter + * is capable of coping with running a file directly, + * let's do that. */ + use_process_file = true; + break; + } } - if_debug1m('I', mem, "selected and initializing (%s)\n", - pl_characteristics(minst->curr_implementation)->language); + first_job = false; new_job = false; - } - if (minst->curr_implementation) { - /* Special case when the job resides in a seekable file and - the implementation has a function to process a file at a - time. */ - if (minst->curr_implementation->proc_process_file - && !is_stdin) { - if_debug1m('I', mem, "processing job from file (%s)\n", - filename); - - code = pl_process_file(minst->curr_implementation, (char *)filename); - if (code < 0) { - errprintf(mem, "Warning interpreter exited with error code %d\n", - code); - } - if (pl_dnit_job(minst->curr_implementation) < 0) - return gs_error_Fatal; - - break; /* break out of the loop to process the next file */ - } + if (pl_process_begin(minst->curr_implementation) < 0) + goto error_fatal; + } - code = pl_process(minst->curr_implementation, &s->cursor.r); - if_debug1m('I', mem, "processing (%s) job\n", - pl_characteristics(minst->curr_implementation)->language); - if (code == e_ExitLanguage) { - if (pl_dnit_job(minst->curr_implementation) < 0) - return gs_error_Fatal; - - new_job = true; - - if (minst->curr_implementation != pjli) - if (pl_init_job(pjli) < 0) - return gs_error_Fatal; - - } else if (code < 0) { /* error and not exit language */ - dmprintf1(mem, - "Warning interpreter exited with error code %d\n", - code); - dmprintf(mem, "Flushing to end of job\n"); - /* flush eoj may require more data */ - while ((pl_flush_to_eoj(minst->curr_implementation, &s->cursor.r)) == 0) { - int code2; - if (s->cursor.r.ptr == s->cursor.r.limit && sfeof(s)) { - if_debug0m('I', mem, - "end of data found while flushing\n"); - break; - } - code2 = s_process_read_buf(s); - if (code2 < 0) - break; + if_debug2m('I', mem, "processing (%s) job from offset %ld\n", + pl_characteristics(minst->curr_implementation)->language, + sftell(s)); + code = pl_process(minst->curr_implementation, &s->cursor.r); + if_debug2m('I', mem, "processed (%s) job to offset %ld\n", + pl_characteristics(minst->curr_implementation)->language, + sftell(s)); + if (code == gs_error_NeedInput || code >= 0) { + continue; + } + if (code != e_ExitLanguage) { + /* error and not exit language */ + dmprintf1(mem, + "Warning interpreter exited with error code %d\n", + code); +flush_to_end_of_job: + dmprintf(mem, "Flushing to end of job\n"); + /* flush eoj may require more data */ + while ((pl_flush_to_eoj(minst->curr_implementation, &s->cursor.r)) == 0) { + int code2; + if (s->cursor.r.ptr == s->cursor.r.limit && sfeof(s)) { + if_debug0m('I', mem, + "end of data found while flushing\n"); + break; } - pl_report_errors(minst->curr_implementation, code, - sftell(s), - minst->error_report > 0); - if (pl_dnit_job(minst->curr_implementation) < 0) - return gs_error_Fatal; - if (pl_init_job(pjli) < 0) - return gs_error_Fatal; - - code = 0; - new_job = true; + code2 = s_process_read_buf(s); + if (code2 < 0) + break; } + pl_report_errors(minst->curr_implementation, code, + sftell(s), + minst->error_report > 0); } + + if (pl_process_end(minst->curr_implementation) < 0) + goto error_fatal; + new_job = true; + /* Always revert to PJL after each job. We avoid reinitialising PJL + * if we are already in PJL to avoid clearing the state. */ + if (revert_to_pjli(minst)) + goto error_fatal_reverted; } sfclose(s); + s = NULL; + if (use_process_file) + { + if_debug1m('I', mem, "processing job from file (%s)\n", + filename); + + code = pl_process_file(minst->curr_implementation, (char *)filename); + if (code == gs_error_InterpreterExit) + code = 0; + if (code < 0) { + errprintf(mem, "Warning interpreter exited with error code %d\n", + code); + } + } + if (revert_to_pjli(minst) < 0) + goto error_fatal_reverted; return 0; + +error_fatal: + revert_to_pjli(minst); +error_fatal_reverted: + sfclose(s); + return gs_error_Fatal; +} + +void +pl_main_set_arg_decode(pl_main_instance_t *minst, + pl_main_get_codepoint_t *get_codepoint) +{ + if (minst == NULL) + return; + + minst->get_codepoint = get_codepoint; +} + +int +pl_main_run_file(pl_main_instance_t *minst, const char *file_name) +{ + char *d, *temp; + const char *c = file_name; + char dummy[6]; + int rune, code, len; + + if (minst == NULL) + return 0; + + /* 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(minst->memory, len, "gsapi_run_file"); + if (temp == NULL) + return gs_error_VMerror; + 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 = pl_main_run_file_utf8(minst, temp); + if (temp != file_name) + gs_free_object(minst->memory, temp, "gsapi_run_file"); + return code; } int @@ -412,6 +522,15 @@ pl_main_delete_instance(pl_main_instance_t *minst) if (minst == NULL) return 0; + /* close and deallocate the device */ + if (minst->device) { + gs_closedevice(minst->device); + gs_unregister_root(minst->device->memory, minst->device_root, + "pl_main_languages_delete_instance"); + minst->device_root = NULL; + gx_device_retain(minst->device, false); + minst->device = NULL; + } mem = minst->memory; impl = minst->implementations; if (impl != NULL) { @@ -427,14 +546,6 @@ pl_main_delete_instance(pl_main_instance_t *minst) gs_free_object(mem, impl, "pl_main_languages_delete_instance()"); } - /* close and deallocate the device */ - if (minst->device) { - gs_closedevice(minst->device); - gs_unregister_root(minst->device->memory, &device_root, - "pl_main_languages_delete_instance"); - gx_device_retain(minst->device, false); - minst->device = NULL; - } gs_iodev_finit(mem); gs_lib_finit(0, 0, mem); @@ -452,16 +563,17 @@ pl_main_delete_instance(pl_main_instance_t *minst) int pl_to_exit(gs_memory_t *mem) { + int ret = 0; pl_main_instance_t *minst = mem->gs_lib_ctx->top_of_system; /* Deselect last-selected device */ if (minst->curr_implementation - && pl_remove_device(minst->curr_implementation) < 0) { - return -1; + && pl_dnit_job(minst->curr_implementation) < 0) { + ret = -1; } gs_c_param_list_release(&minst->params); arg_finit(&minst->args); - return 0; + return ret; } static int /* 0 ok, else -1 error */ @@ -486,7 +598,7 @@ pl_main_languages_init(gs_memory_t * mem, /* deallocator for devices */ goto pmui_err; minst->implementations = impls; - minst->curr_implementation = minst->desired_implementation = NULL; + minst->curr_implementation = NULL; memset(impls, 0, sz); /* Create & init PDL all instances. Could do this lazily to save memory, */ @@ -555,7 +667,7 @@ pl_main_alloc_instance(gs_memory_t * mem) memset(minst, 0, sizeof(*minst)); - minst->memory = mem; + minst->memory = minst->device_memory = mem; minst->pjl_from_args = false; minst->error_report = -1; @@ -587,12 +699,14 @@ pl_main_alloc_instance(gs_memory_t * mem) /* Create a default device if not already defined. */ static int -pl_top_create_device(pl_main_instance_t * pti, int index, bool is_default) +pl_top_create_device(pl_main_instance_t * pti, int index) { int code = 0; - if (!is_default || !pti->device) { + if (!pti->device) { const gx_device *dev; + pl_interp_implementation_t **impl; + gs_memory_t *mem = pti->device_memory; /* We assume that nobody else changes pti->device, and this function is called from this module only. Due to that device_root is always consistent with pti->device, @@ -609,7 +723,20 @@ pl_top_create_device(pl_main_instance_t * pti, int index, bool is_default) gs_lib_device_list((const gx_device * const **)&list, NULL); dev = list[index]; } - code = gs_copydevice(&pti->device, dev, pti->memory); + for (impl = pti->implementations; *impl != 0; ++impl) { + mem = pl_get_device_memory(*impl); + if (mem) + break; + } + if (mem) + pti->device_memory = mem; +#ifdef DEBUG + for (; *impl != 0; ++impl) { + mem = pl_get_device_memory(*impl); + assert(mem == NULL || mem == pti->device_memory); + } +#endif + code = gs_copydevice(&pti->device, dev, pti->device_memory); if (code < 0) return code; @@ -617,7 +744,8 @@ pl_top_create_device(pl_main_instance_t * pti, int index, bool is_default) if (pti->device == NULL) return gs_error_VMerror; - gs_register_struct_root(pti->memory, &device_root, + pti->device_root = NULL; + gs_register_struct_root(pti->device_memory, &pti->device_root, (void **)&pti->device, "pl_top_create_device"); @@ -696,23 +824,26 @@ parse_floats(gs_memory_t * mem, uint arg_count, char *arg, float *f) return float_index; } +#define argcmp(A, S, L) \ + (!strncmp(A, S, L) && (A[L] == 0 || A[L] == '=')) + static int check_for_special_int(pl_main_instance_t * pmi, const char *arg, int b) { - if (!strncmp(arg, "BATCH", 5)) - return (b == 0) ? 0 : gs_note_error(gs_error_rangecheck); - if (!strncmp(arg, "NOPAUSE", 6)) { + if (argcmp(arg, "BATCH", 5)) + return (b == 1) ? 0 : gs_note_error(gs_error_rangecheck); + if (argcmp(arg, "NOPAUSE", 7)) { pmi->pause = !b; return 0; } - if (!strncmp(arg, "DOINTERPOLATE", 13)) { + if (argcmp(arg, "DOINTERPOLATE", 13)) { pmi->interpolate = !!b; return 0; } - if (!strncmp(arg, "NOCACHE", 7)) { + if (argcmp(arg, "NOCACHE", 7)) { pmi->nocache = !!b; return 0; } - if (!strncmp(arg, "SCANCONVERTERTYPE", 17)) { + if (argcmp(arg, "SCANCONVERTERTYPE", 17)) { pmi->scanconverter = b; return 0; } @@ -721,11 +852,11 @@ static int check_for_special_int(pl_main_instance_t * pmi, const char *arg, int static int check_for_special_float(pl_main_instance_t * pmi, const char *arg, float f) { - if (!strncmp(arg, "BATCH", 5) || - !strncmp(arg, "NOPAUSE", 6) || - !strncmp(arg, "DOINTERPOLATE", 13) || - !strncmp(arg, "NOCACHE", 7) || - !strncmp(arg, "SCANCONVERTERTYPE", 17)) { + if (argcmp(arg, "BATCH", 5) || + argcmp(arg, "NOPAUSE", 7) || + argcmp(arg, "DOINTERPOLATE", 13) || + argcmp(arg, "NOCACHE", 7) || + argcmp(arg, "SCANCONVERTERTYPE", 17)) { return gs_note_error(gs_error_rangecheck); } return 1; @@ -733,17 +864,50 @@ static int check_for_special_float(pl_main_instance_t * pmi, const char *arg, fl static int check_for_special_str(pl_main_instance_t * pmi, const char *arg, gs_param_string *f) { - if (!strncmp(arg, "BATCH", 5) || - !strncmp(arg, "NOPAUSE", 6) || - !strncmp(arg, "DOINTERPOLATE", 13) || - !strncmp(arg, "NOCACHE", 7) || - !strncmp(arg, "SCANCONVERTERTYPE", 17)) { + if (argcmp(arg, "BATCH", 5) || + argcmp(arg, "NOPAUSE", 7) || + argcmp(arg, "DOINTERPOLATE", 13) || + argcmp(arg, "NOCACHE", 7) || + argcmp(arg, "SCANCONVERTERTYPE", 17)) { return gs_note_error(gs_error_rangecheck); } return 1; } static int +pass_param_to_languages(pl_main_instance_t *pmi, + pl_set_param_type type, + const char *param, + const void *value) +{ + pl_interp_implementation_t **imp; + int code = 0; + + for (imp = pmi->implementations; *imp != NULL; imp++) { + code = pl_set_param(*imp, type, param, value); + if (code != 0) + break; + } + + return code; +} + +static int +pl_main_post_args_init(pl_main_instance_t * pmi) +{ + pl_interp_implementation_t **imp; + int code = 0; + + for (imp = pmi->implementations; *imp != NULL; imp++) { + code = pl_post_args_init(*imp); + if (code != 0) + break; + } + + return code; +} + +static int pl_main_process_options(pl_main_instance_t * pmi, arg_list * pal, pl_interp_implementation_t * pjli) { @@ -751,6 +915,7 @@ pl_main_process_options(pl_main_instance_t * pmi, arg_list * pal, bool help = false; char *arg; gs_c_param_list *params = &pmi->params; + int device_index = -1; gs_c_param_list_write_more(params); while ((code = arg_next(pal, (const char **)&arg, pmi->memory)) > 0 && *arg == '-') { @@ -843,73 +1008,86 @@ pl_main_process_options(pl_main_instance_t * pmi, arg_list * pal, float vf; bool bval = true; char buffer[128]; + pl_set_param_type spt_type = pl_spt_invalid; + const void *spt_val = NULL; + static const char const_true_string[] = "true"; if (eqp || (eqp = strchr(arg, '#'))) value = eqp + 1; else { /* -dDefaultBooleanIs_TRUE */ - code = check_for_special_int(pmi, arg, (int)bval); - if (code < 0) code = 0; - if (code == 1) - code = - param_write_bool((gs_param_list *) params, - arg, &bval); - break; + value = const_true_string; + eqp = arg + strlen(arg); } + /* Arrange for a null terminated copy of the key name in buffer. */ + if (eqp-arg >= sizeof(buffer)-1) { + dmprintf1(pmi->memory, "Command line key is too long: %s\n", arg); + return -1; + } + strncpy(buffer, arg, eqp - arg); + buffer[eqp - arg] = '\0'; + code = 0; if (value && value[0] == '/') { + /* We have a name! */ gs_param_string str; - strncpy(buffer, arg, eqp - arg); - buffer[eqp - arg] = '\0'; - param_string_from_transient_string(str, value + 1); code = check_for_special_str(pmi, arg, &str); - if (code == 1) - code = param_write_name((gs_param_list *) params, - buffer, &str); - break; - } - /* Search for a non-decimal 'radix' number */ - else if (strchr(value, '#')) { - int base, number = 0; - char *val = strchr(value, '#'); + if (code <= 0) + break; + + param_string_from_transient_string(str, value + 1); + code = param_write_name((gs_param_list *) params, + buffer, &str); + spt_type = pl_spt_name; + spt_val = value+1; + } else if (strchr(value, '#')) { + /* We have a non-decimal 'radix' number */ + int base = 0; + const char *val = strchr(value, '#'); + const char *v = value; + char c; + + while ((c = *v++) >= '0' && c <= '9') + base = base*10 + (c - '0'); + if (*v != '#') { + dmprintf1(pmi->memory, "Malformed base value for radix. %s", + value); + return -1; + } - *val++ = 0x00; - sscanf(value, "%d", &base); if (base < 2 || base > 36) { - dmprintf1(pmi->memory, "Value out of range %s", + dmprintf1(pmi->memory, "Base out of range %s", value); return -1; } + vi = 0; while (*val) { - if (*val >= '0' && *val <= '9') { - number = number * base + (*val - '0'); - } else { - if (*val >= 'A' && *val <= 'Z') { - number = number * base + (*val - 'A'); + if (*val >= '0' && *val < ('0'+(base<=10?base:10))) { + vi = vi * base + (*val - '0'); + } else if (base > 10) { + if (*val >= 'A' && *val < 'A'+base-10) { + vi = vi * base + (*val - 'A' + 10); + } else if (*val >= 'a' && *val < 'a'+base-10) { + vi = vi * base + (*val - 'a' + 10); } else { - if (*val >= 'a' && *val <= 'z') { - number = number * base + (*val - 'a'); - } else { - dmprintf1(pmi->memory, - "Value out of range %s", - val); - return -1; - } + dmprintf1(pmi->memory, + "Value out of range %s\n", + val); + return -1; } } val++; } - strncpy(buffer, arg, eqp - arg); - buffer[eqp - arg] = '\0'; - code = check_for_special_int(pmi, arg, number); + code = check_for_special_int(pmi, arg, vi); if (code < 0) code = 0; - if (code == 1) - code = - param_write_int((gs_param_list *) params, - buffer, &number); + if (code <= 0) + break; + code = param_write_int((gs_param_list *) params, + buffer, &vi); + spt_type = pl_spt_int; + spt_val = &vi; } else if ((!strchr(value, '.')) && - /* search for an int (no decimal), if fail try a float */ (sscanf(value, "%d", &vi) == 1)) { /* Here we have an int -- check for a scaling suffix */ char suffix = eqp[strlen(eqp) - 1]; @@ -932,51 +1110,63 @@ pl_main_process_options(pl_main_instance_t * pmi, arg_list * pal, default: break; /* not a valid suffix or last char was digit */ } - /* create a null terminated string for the key */ - strncpy(buffer, arg, eqp - arg); - buffer[eqp - arg] = '\0'; code = check_for_special_int(pmi, arg, vi); if (code < 0) code = 0; - if (code == 1) - code = - param_write_int((gs_param_list *) params, - buffer, &vi); + if (code <= 0) + break; + code = param_write_int((gs_param_list *) params, + buffer, &vi); + spt_type = pl_spt_int; + spt_val = &vi; } else if (sscanf(value, "%f", &vf) == 1) { - /* create a null terminated string. NB duplicated code. */ - strncpy(buffer, arg, eqp - arg); - buffer[eqp - arg] = '\0'; + /* We have a float */ code = check_for_special_float(pmi, arg, vf); - if (code == 1) - code = - param_write_float((gs_param_list *) params, + if (code <= 0) + break; + code = param_write_float((gs_param_list *) params, buffer, &vf); + spt_type = pl_spt_float; + spt_val = &vf; + } else if (!strcmp(value, "null")) { + code = check_for_special_int(pmi, arg, (int)bval); + if (code < 0) code = 0; + if (code <= 0) + break; + code = param_write_null((gs_param_list *) params, + buffer); + spt_type = pl_spt_null; + spt_val = NULL; } else if (!strcmp(value, "true")) { /* bval = true; */ - strncpy(buffer, arg, eqp - arg); - buffer[eqp - arg] = '\0'; code = check_for_special_int(pmi, arg, (int)bval); if (code < 0) code = 0; - if (code == 1) - code = - param_write_bool((gs_param_list *) params, - buffer, &bval); + if (code <= 0) + break; + code = param_write_bool((gs_param_list *) params, + buffer, &bval); + spt_type = pl_spt_bool; + spt_val = (void*)1; } else if (!strcmp(value, "false")) { bval = false; - strncpy(buffer, arg, eqp - arg); - buffer[eqp - arg] = '\0'; code = check_for_special_int(pmi, arg, (int)bval); if (code < 0) code = 0; - if (code == 1) - code = - param_write_bool((gs_param_list *) params, - buffer, &bval); + if (code <= 0) + break; + code = param_write_bool((gs_param_list *) params, + buffer, &bval); + spt_type = pl_spt_bool; + spt_val = NULL; } else { dmprintf(pmi->memory, - "Usage for -d is -d<option>=[<integer>|<float>|true|false]\n"); + "Usage for -d is -d<option>=[<integer>|<float>|null|true|false|name]\n"); continue; } + if (code < 0) + return code; + code = pass_param_to_languages(pmi, spt_type, buffer, spt_val); + if (code < 0) + return code; } - break; case 'E': if (*arg == 0) gs_debug['#'] = 1; @@ -999,8 +1189,11 @@ pl_main_process_options(pl_main_instance_t * pmi, arg_list * pal, code = param_write_int_array((gs_param_list *) params, "HWSize", &ia); - if (code >= 0) + if (code >= 0) { pmi->page_set_on_command_line = true; + pmi->page_size[0] = geom[0]; + pmi->page_size[1] = geom[1]; + } } break; case 'H': @@ -1190,8 +1383,11 @@ pl_main_process_options(pl_main_instance_t * pmi, arg_list * pal, code = param_write_float_array((gs_param_list *) params, "HWResolution", &fa); - if (code == 0) + if (code == 0) { pmi->res_set_on_command_line = true; + pmi->res[0] = res[0]; + pmi->res[1] = res[1]; + } } break; case 's': @@ -1209,12 +1405,13 @@ pl_main_process_options(pl_main_instance_t * pmi, arg_list * pal, } value = eqp + 1; if (!strncmp(arg, "DEVICE", 6)) { - code = pl_top_create_device(pmi, - get_device_index(pmi-> - memory, - value), - false); - + if (device_index != -1) { + dmprintf(pmi->memory, "DEVICE can only be set once!\n"); + return -1; + } + device_index = get_device_index(pmi->memory, value); + if (device_index == -1) + return -1; /* check for icc settings */ } else if (!strncmp @@ -1238,12 +1435,22 @@ pl_main_process_options(pl_main_instance_t * pmi, arg_list * pal, } else { char buffer[128]; + if (eqp-arg >= sizeof(buffer)-1) { + dmprintf1(pmi->memory, "Command line key is too long: %s\n", arg); + return -1; + } strncpy(buffer, arg, eqp - arg); buffer[eqp - arg] = '\0'; + param_string_from_transient_string(str, value); code = param_write_string((gs_param_list *) params, buffer, &str); + if (code < 0) + return code; + code = pass_param_to_languages(pmi, pl_spt_string, buffer, value); + if (code < 0) + return code; } } break; @@ -1271,8 +1478,14 @@ pl_main_process_options(pl_main_instance_t * pmi, arg_list * pal, return code; } + /* Do any last minute language specific device initialisation + * (i.e. let gs_init.ps do its worst). */ + code = pl_main_post_args_init(pmi); + if (code < 0) + return code; + gs_c_param_list_read(params); - code = pl_top_create_device(pmi, -1, true); /* create default device if needed */ + code = pl_top_create_device(pmi, device_index); /* create default device if needed */ if (code < 0) return code; @@ -1285,7 +1498,9 @@ pl_main_process_options(pl_main_instance_t * pmi, arg_list * pal, return 0; do { - code = pl_main_run_file(pmi, arg); + code = pl_main_run_file_utf8(pmi, arg); + if (code == gs_error_undefinedfilename) + errprintf(pmi->memory, "Failed to open file '%s'\n", arg); if (code < 0) return code; } while ((code = arg_next(pal, (const char **)&arg, pmi->memory)) > 0); @@ -1303,6 +1518,8 @@ pl_auto_sense(pl_main_instance_t *minst, const char *name, int buffer_length) pl_interp_implementation_t **impls = minst->implementations; pl_interp_implementation_t **impl; size_t uel_len = strlen(PJL_UEL); + pl_interp_implementation_t *best = NULL; + int max_score = 0; /* first check for a UEL */ if (buffer_length >= uel_len) { @@ -1310,12 +1527,16 @@ pl_auto_sense(pl_main_instance_t *minst, const char *name, int buffer_length) return impls[0]; } + /* Defaults to language 1 (if there is one): PJL is language 0, PCL is language 1. */ + best = impls[1] ? impls[1] : impls[0]; for (impl = impls; *impl != NULL; ++impl) { - if (pl_characteristics(*impl)->auto_sense(name, buffer_length) == 0) - return *impl; + int score = pl_characteristics(*impl)->auto_sense(name, buffer_length); + if (score > max_score) { + best = *impl; + max_score = score; + } } - /* Defaults to language 1 (if there is one): PJL is language 0, PCL is language 1. */ - return impls[1] ? impls[1] : impls[0]; + return best; } /* either the (1) implementation has been selected on the command line or @@ -1374,7 +1595,7 @@ pl_log_string(const gs_memory_t * mem, const char *str, int wait_for_key) { errwrite(mem, str, strlen(str)); if (wait_for_key) - (void)fgetc(mem->gs_lib_ctx->fstdin); + (void)fgetc(mem->gs_lib_ctx->core->fstdin); } pl_interp_implementation_t * @@ -1409,6 +1630,24 @@ bool pl_main_get_res_set_on_command_line(const gs_memory_t *mem) return pl_main_get_instance(mem)->res_set_on_command_line; } +void pl_main_get_forced_geometry(const gs_memory_t *mem, const float **resolutions, const long **dimensions) +{ + pl_main_instance_t *minst = pl_main_get_instance(mem); + + if (resolutions) { + if (minst->res_set_on_command_line) + *resolutions = minst->res; + else + *resolutions = NULL; + } + if (dimensions) { + if (minst->page_set_on_command_line) + *dimensions = minst->page_size; + else + *dimensions = NULL; + } +} + bool pl_main_get_high_level_device(const gs_memory_t *mem) { return pl_main_get_instance(mem)->high_level_device; diff --git a/pcl/pl/plmain.h b/pcl/pl/plmain.h index 61b871fed..ad29edd68 100644 --- a/pcl/pl/plmain.h +++ b/pcl/pl/plmain.h @@ -55,9 +55,9 @@ int pl_main_set_display_callback(pl_main_instance_t *inst, void *callback); int pl_main_run_file(pl_main_instance_t *minst, const char *filename); int pl_main_init_with_args(pl_main_instance_t *inst, int argc, char *argv[]); int pl_main_delete_instance(pl_main_instance_t *minst); -int pl_main_run_string_begin(void *instance); -int pl_main_run_string_continue(void *instance, const char *str, unsigned int length); -int pl_main_run_string_end(void *instance); +int pl_main_run_string_begin(pl_main_instance_t *minst); +int pl_main_run_string_continue(pl_main_instance_t *minst, const char *str, unsigned int length); +int pl_main_run_string_end(pl_main_instance_t *minst); int pl_to_exit(gs_memory_t *mem); /* instance accessors */ @@ -66,9 +66,14 @@ bool pl_main_get_nocache(const gs_memory_t *mem); bool pl_main_get_page_set_on_command_line(const gs_memory_t *mem); bool pl_main_get_res_set_on_command_line(const gs_memory_t *mem); bool pl_main_get_high_level_device(const gs_memory_t *mem); +void pl_main_get_forced_geometry(const gs_memory_t *mem, const float **resolutions, const long **dimensions); int pl_main_get_scanconverter(const gs_memory_t *mem); pl_main_instance_t *pl_main_get_instance(const gs_memory_t *mem); +typedef int pl_main_get_codepoint_t(FILE *, const char **); +void pl_main_set_arg_decode(pl_main_instance_t *minst, + pl_main_get_codepoint_t *get_codepoint); + /* retrieve the PJL instance so languages can query PJL. */ bool pl_main_get_pjl_from_args(const gs_memory_t *mem); /* pjl was passed on the command line */ diff --git a/pcl/pl/pltop.c b/pcl/pl/pltop.c index a27d70821..5b8669416 100644 --- a/pcl/pl/pltop.c +++ b/pcl/pl/pltop.c @@ -24,6 +24,9 @@ #include "gsstruct.h" #include "gsdevice.h" #include "pltop.h" +#include "gserrors.h" +#include "stream.h" +#include "strmio.h" /* Get implementation's characteristics */ const pl_interp_characteristics_t * /* always returns a descriptor */ @@ -35,27 +38,48 @@ pl_characteristics(const pl_interp_implementation_t * impl) /* implementati /* Do instance interpreter allocation/init. No device is set yet */ int /* ret 0 ok, else -ve error code */ pl_allocate_interp_instance(pl_interp_implementation_t * impl, - gs_memory_t * mem /* allocator to allocate instance from */ - ) + gs_memory_t * mem) /* allocator to allocate instance from */ { return impl->proc_allocate_interp_instance(impl, mem); } -/* Get and interpreter prefered device memory allocator if any */ -int /* ret 0 ok, else -ve error code */ -pl_set_device(pl_interp_implementation_t * impl, /* interp instance to use */ - gx_device * device /* device to set (open or closed) */ - ) +/* + * Get the allocator with which to allocate a device + */ +gs_memory_t * +pl_get_device_memory(pl_interp_implementation_t *impl) { - return impl->proc_set_device(impl, device); + if (impl->proc_get_device_memory == NULL) + return NULL; + return impl->proc_get_device_memory(impl); +} + +int +pl_set_param(pl_interp_implementation_t *impl, + pl_set_param_type type, + const char *param, + const void *value) +{ + if (impl->proc_set_param == NULL) + return 0; + + return impl->proc_set_param(impl, type, param, value); +} + +int pl_post_args_init(pl_interp_implementation_t *impl) +{ + if (impl->proc_post_args_init == NULL) + return 0; + + return impl->proc_post_args_init(impl); } /* Prepare interp instance for the next "job" */ int /* ret 0 ok, else -ve error code */ -pl_init_job(pl_interp_implementation_t * impl /* interp instance to start job in */ - ) +pl_init_job(pl_interp_implementation_t * impl, /* interp instance to start job in */ + gx_device * device) /* device to set (open or closed) */ { - return impl->proc_init_job(impl); + return impl->proc_init_job(impl, device); } /* Parse a random access seekable file. @@ -66,7 +90,50 @@ pl_init_job(pl_interp_implementation_t * impl /* interp instance to start jo int pl_process_file(pl_interp_implementation_t * impl, char *filename) { - return impl->proc_process_file(impl, filename); + gs_memory_t *mem; + int code, code1; + stream *s; + + if (impl->proc_process_file != NULL) + return impl->proc_process_file(impl, filename); + + /* We have to process the file in chunks. */ + mem = pl_get_device_memory(impl); + code = 0; + + s = sfopen(filename, "r", mem); + if (s == NULL) + return gs_error_undefinedfilename; + + code = pl_process_begin(impl); + + while (code == gs_error_NeedInput || code >= 0) { + if (s->cursor.r.ptr == s->cursor.r.limit && sfeof(s)) + break; + code = s_process_read_buf(s); + if (code < 0) + break; + + code = pl_process(impl, &s->cursor.r); + if_debug2m('I', mem, "processed (%s) job to offset %ld\n", + pl_characteristics(impl)->language, + sftell(s)); + } + + code1 = pl_process_end(impl); + if (code >= 0 && code1 < 0) + code = code1; + + sfclose(s); + + return code; +} + +/* Do setup to for parsing cursor-fulls of data */ +int +pl_process_begin(pl_interp_implementation_t * impl) /* interp instance to process data job in */ +{ + return impl->proc_process_begin(impl); } /* Parse a cursor-full of data */ @@ -78,37 +145,39 @@ pl_process_file(pl_interp_implementation_t * impl, char *filename) * other <0 value - an error was detected. */ int -pl_process(pl_interp_implementation_t * impl, /* interp instance to process data job in */ - stream_cursor_read * cursor /* data to process */ - ) +pl_process(pl_interp_implementation_t * impl, /* interp instance to process data job in */ + stream_cursor_read * cursor) /* data to process */ { return impl->proc_process(impl, cursor); } +int +pl_process_end(pl_interp_implementation_t * impl) /* interp instance to process data job in */ +{ + return impl->proc_process_end(impl); +} + /* Skip to end of job ret 1 if done, 0 ok but EOJ not found, else -ve error code */ int -pl_flush_to_eoj(pl_interp_implementation_t * impl, /* interp instance to flush for */ - stream_cursor_read * cursor /* data to process */ - ) +pl_flush_to_eoj(pl_interp_implementation_t * impl, /* interp instance to flush for */ + stream_cursor_read * cursor)/* data to process */ { return impl->proc_flush_to_eoj(impl, cursor); } /* Parser action for end-of-file (also resets after unexpected EOF) */ int /* ret 0 or +ve if ok, else -ve error code */ -pl_process_eof(pl_interp_implementation_t * impl /* interp instance to process data job in */ - ) +pl_process_eof(pl_interp_implementation_t * impl) /* interp instance to process data job in */ { return impl->proc_process_eof(impl); } /* Report any errors after running a job */ int /* ret 0 ok, else -ve error code */ -pl_report_errors(pl_interp_implementation_t * impl, /* interp instance to wrap up job in */ - int code, /* prev termination status */ - long file_position, /* file position of error, -1 if unknown */ - bool force_to_cout /* force errors to cout */ - ) +pl_report_errors(pl_interp_implementation_t * impl, /* interp instance to wrap up job in */ + int code, /* prev termination status */ + long file_position, /* file position of error, -1 if unknown */ + bool force_to_cout) /* force errors to cout */ { return impl->proc_report_errors (impl, code, file_position, force_to_cout); @@ -116,24 +185,14 @@ pl_report_errors(pl_interp_implementation_t * impl, /* interp instance to /* Wrap up interp instance after a "job" */ int /* ret 0 ok, else -ve error code */ -pl_dnit_job(pl_interp_implementation_t * impl /* interp instance to wrap up job in */ - ) +pl_dnit_job(pl_interp_implementation_t * impl) /* interp instance to wrap up job in */ { return impl->proc_dnit_job(impl); } -/* Remove a device from an interperter instance */ -int /* ret 0 ok, else -ve error code */ -pl_remove_device(pl_interp_implementation_t * impl /* interp instance to use */ - ) -{ - return impl->proc_remove_device(impl); -} - /* Deallocate a interpreter instance */ int /* ret 0 ok, else -ve error code */ -pl_deallocate_interp_instance(pl_interp_implementation_t * impl /* instance to dealloc */ - ) +pl_deallocate_interp_instance(pl_interp_implementation_t * impl) /* instance to dealloc */ { if (impl->interp_client_data == NULL) return 0; diff --git a/pcl/pl/pltop.h b/pcl/pl/pltop.h index e2ae33171..a109cd717 100644 --- a/pcl/pl/pltop.h +++ b/pcl/pl/pltop.h @@ -41,7 +41,7 @@ typedef struct pl_interp_characteristics_s { const char *language; /* generic language should correspond with HP documented PJL name */ - int (*auto_sense)(const char *string, int length); /* routine used to detect language - 0 is detected, non-zero not detected */ + int (*auto_sense)(const char *string, int length); /* routine used to detect language - returns a score: 0 is definitely not, 100 is definitely yes. */ const char *manufacturer; /* manuf str */ const char *version; /* version str */ const char *build_date; /* build date str */ @@ -63,27 +63,59 @@ int pl_allocate_interp_instance(pl_interp_implementation_t *, gs_memory_t *); typedef int (*pl_interp_proc_allocate_interp_instance_t) (pl_interp_implementation_t *, gs_memory_t *); + +/* + * Get the allocator with which to allocate a device + * NOTE: only one interpreter is permitted to return a + * allocator. + */ +gs_memory_t * +pl_get_device_memory(pl_interp_implementation_t *); +typedef gs_memory_t * (*pl_interp_proc_get_device_memory_t) (pl_interp_implementation_t *); + /* - * Set a device, possibly shared, into the graphics state of the language. + * Pass a parameter/value to a language. */ +typedef enum { + pl_spt_invalid = -1, + pl_spt_null = 0, /* void * is NULL */ + pl_spt_bool = 1, /* void * is NULL (false) or non-NULL (true) */ + pl_spt_int = 2, /* void * is a pointer to an int */ + pl_spt_float = 3, /* void * is a float * */ + pl_spt_name = 4, /* void * is a char * */ + pl_spt_string = 5 /* void * is a char * */ +} pl_set_param_type; +int pl_set_param(pl_interp_implementation_t *, pl_set_param_type type, const char *param, const void *value); +typedef int (*pl_interp_proc_set_param_t) (pl_interp_implementation_t *, + pl_set_param_type, + const char *, + const void *); -int pl_set_device(pl_interp_implementation_t *, gx_device *); -typedef int (*pl_interp_proc_set_device_t) (pl_interp_implementation_t *, - gx_device *); +/* + * Do any language specific init required after the args have been sent. + */ +int pl_post_args_init(pl_interp_implementation_t *); +typedef int (*pl_interp_proc_post_args_init_t) (pl_interp_implementation_t *); /* * Work to be done when a job begins. */ -int pl_init_job(pl_interp_implementation_t *); -typedef int (*pl_interp_proc_init_job_t) (pl_interp_implementation_t *); +int pl_init_job(pl_interp_implementation_t *, gx_device *); +typedef int (*pl_interp_proc_init_job_t) (pl_interp_implementation_t *, gx_device *); /* * Process a stream of PDL data. */ +int pl_process_begin(pl_interp_implementation_t *); +typedef int (*pl_interp_proc_process_begin_t) (pl_interp_implementation_t *); + int pl_process(pl_interp_implementation_t *, stream_cursor_read *); typedef int (*pl_interp_proc_process_t) (pl_interp_implementation_t *, stream_cursor_read *); +int pl_process_end(pl_interp_implementation_t *); +typedef int (*pl_interp_proc_process_end_t) (pl_interp_implementation_t *); + /* * The process_file function is an optional optimized path for * languages that want to use a random access file. If this function @@ -121,12 +153,6 @@ int pl_dnit_job(pl_interp_implementation_t *); typedef int (*pl_interp_proc_dnit_job_t) (pl_interp_implementation_t *); /* - * Remove the device from the graphics state and reset. - */ -int pl_remove_device(pl_interp_implementation_t *); -typedef int (*pl_interp_proc_remove_device_t) (pl_interp_implementation_t *); - -/* * Free everything. */ int pl_deallocate_interp_instance(pl_interp_implementation_t *); @@ -140,15 +166,18 @@ struct pl_interp_implementation_s /* Procedure vector */ pl_interp_proc_characteristics_t proc_characteristics; pl_interp_proc_allocate_interp_instance_t proc_allocate_interp_instance; - pl_interp_proc_set_device_t proc_set_device; + pl_interp_proc_get_device_memory_t proc_get_device_memory; + pl_interp_proc_set_param_t proc_set_param; + pl_interp_proc_post_args_init_t proc_post_args_init; pl_interp_proc_init_job_t proc_init_job; pl_interp_proc_process_file_t proc_process_file; + pl_interp_proc_process_begin_t proc_process_begin; pl_interp_proc_process_t proc_process; + pl_interp_proc_process_end_t proc_process_end; pl_interp_proc_flush_to_eoj_t proc_flush_to_eoj; pl_interp_proc_process_eof_t proc_process_eof; pl_interp_proc_report_errors_t proc_report_errors; pl_interp_proc_dnit_job_t proc_dnit_job; - pl_interp_proc_remove_device_t proc_remove_device; pl_interp_proc_deallocate_interp_instance_t proc_deallocate_interp_instance; void *interp_client_data; diff --git a/pcl/pl/plwmainc.c b/pcl/pl/plwmainc.c index 26d238be2..d6c6148c4 100644 --- a/pcl/pl/plwmainc.c +++ b/pcl/pl/plwmainc.c @@ -353,7 +353,7 @@ main_utf8(int argc, char *argv[]) hwndforeground = GetForegroundWindow(); /* assume this is ours */ #endif - if (plapi_new_instance(&instance, NULL) < 0) { + if (gsapi_new_instance(&instance, NULL) < 0) { fprintf(stderr, "Cannot create instance\n"); return 1; } @@ -384,7 +384,7 @@ main_utf8(int argc, char *argv[]) nargc = argc; nargv = argv; #else - plapi_set_display_callback(instance, &display); + gsapi_set_display_callback(instance, &display); { int format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | DISPLAY_DEPTH_1 | DISPLAY_BIGENDIAN | DISPLAY_BOTTOMFIRST; @@ -420,11 +420,11 @@ main_utf8(int argc, char *argv[]) nargv[2] = ddpi; memcpy(&nargv[3], &argv[1], argc * sizeof(char *)); #endif - code = plapi_init_with_args(instance, nargc, nargv); - code1 = plapi_exit(instance); + code = gsapi_init_with_args(instance, nargc, nargv); + code1 = gsapi_exit(instance); if (code == 0 || (code == gs_error_Quit && code1 != 0)) code = code1; - plapi_delete_instance(instance); + gsapi_delete_instance(instance); #ifndef METRO free(nargv); diff --git a/pcl/pl/realmain.c b/pcl/pl/realmain.c index 2f261f549..3fc48edc2 100644 --- a/pcl/pl/realmain.c +++ b/pcl/pl/realmain.c @@ -25,31 +25,31 @@ main(int argc, char *argv[]) { int code, code1; void *minst; + size_t uel_len = strlen(PJL_UEL); - code = plapi_new_instance(&minst, (void *)0); + code = gsapi_new_instance(&minst, (void *)0); if (code < 0) - return EXIT_FAILURE; - - if (code == 0) - code = plapi_init_with_args(minst, argc, argv); - - if (code == 0) { - size_t uel_len = strlen(PJL_UEL); - if ( - (code = plapi_run_string_begin(minst) < 0) || - (code = plapi_run_string_continue(minst, PJL_UEL, uel_len) < 0) || - (code = plapi_run_string_end(minst) < 0) - ) - ; /* nothing */ - } - - code1 = plapi_exit(minst); + return EXIT_FAILURE; + + if (code >= 0) + code = gsapi_init_with_args(minst, argc, argv); + + if (code >= 0) + code = gsapi_run_string_begin(minst); + if (code >= 0) + code = gsapi_run_string_continue(minst, PJL_UEL, uel_len); + if (code >= 0) + code = gsapi_run_string_end(minst); + if (code == gs_error_InterpreterExit) + code = 0; + + code1 = gsapi_exit(minst); if ((code == 0) || (code == gs_error_Quit)) - code = code1; + code = code1; - plapi_delete_instance(minst); + gsapi_delete_instance(minst); if ((code == 0) || (code == gs_error_Quit)) - return EXIT_SUCCESS; + return EXIT_SUCCESS; return EXIT_FAILURE; } diff --git a/pcl/pxl/pxtop.c b/pcl/pxl/pxtop.c index 050d9fa64..944403fea 100644 --- a/pcl/pxl/pxtop.c +++ b/pcl/pxl/pxtop.c @@ -44,6 +44,7 @@ #include "pltop.h" #include "plmain.h" #include "gsicc_manage.h" +#include "gxdevsop.h" /* Imported operators */ px_operator_proc(pxEndPage); @@ -141,8 +142,10 @@ static int pxl_detect_language(const char *s, int len) { if (len < 11) - return 1; - return memcmp(s, ") HP-PCL XL", 11); + return 0; + if (memcmp(s, ") HP-PCL XL", 11) == 0) + return 100; + return 0; } /* Get implementation's characteristics */ @@ -221,19 +224,49 @@ pxl_set_icc_params(pl_interp_implementation_t * impl, gs_gstate * pgs) return pl_set_icc_params(pxli->memory, pgs); } -/* Set a device into an interperter instance */ -/* ret 0 ok, else -ve error code */ -static int -pxl_impl_set_device(pl_interp_implementation_t * impl, - gx_device * device) +/* + * Set a Boolean parameter. + */ +static int +put_param_bool(pxl_interp_instance_t *pxli, gs_param_name pkey, bool value) { + gs_c_param_list list; int code; + + gs_c_param_list_write(&list, pxli->memory); + code = param_write_bool((gs_param_list *) & list, pkey, &value); + if (code >= 0) { + gs_c_param_list_read(&list); + + code = gs_gstate_putdeviceparams(pxli->pgs, + gs_currentdevice(pxli->pgs), + (gs_param_list *) &list); + } + + gs_c_param_list_release(&list); + return code; +} + +/* Prepare interp instance for the next "job" */ +/* ret 0 ok, else -ve error code */ +static int +pxl_impl_init_job(pl_interp_implementation_t * impl, + gx_device * device) +{ + int code = 0; pxl_interp_instance_t *pxli = impl->interp_client_data; px_state_t *pxs = pxli->pxs; gs_memory_t *mem = pxli->memory; enum { Sbegin, Ssetdevice, Sinitg, Sgsave, Serase, Sdone } stage; + px_reset_errors(pxli->pxs); + px_process_init(pxli->st, true); + + /* set input status to: expecting stream header */ + px_stream_header_init(&pxli->headerState, pxli->st, pxli->pxs); + pxli->processState = PSHeader; + stage = Sbegin; pxs->interpolate = pl_main_get_interpolate(mem); @@ -295,25 +328,23 @@ pxl_impl_set_device(pl_interp_implementation_t * impl, case Sbegin: /* nothing left to undo */ break; } + + /* Warn the device that PXL uses ROPs. */ + if (code == 0) { + code = put_param_bool(pxli, "LanguageUsesROPs", true); + + if (!device->is_open) + code = gs_opendevice(device); + } + return code; } -/* Prepare interp instance for the next "job" */ -/* ret 0 ok, else -ve error code */ +/* Do any setup for parser per-cursor */ static int -pxl_impl_init_job(pl_interp_implementation_t * impl) +pxl_impl_process_begin(pl_interp_implementation_t * impl) { - int code = 0; - pxl_interp_instance_t *pxli = impl->interp_client_data; - - px_reset_errors(pxli->pxs); - px_process_init(pxli->st, true); - - /* set input status to: expecting stream header */ - px_stream_header_init(&pxli->headerState, pxli->st, pxli->pxs); - pxli->processState = PSHeader; - - return code; + return 0; } /* Parse a cursor-full of data */ @@ -372,6 +403,13 @@ pxl_impl_process(pl_interp_implementation_t * impl, return code; } +static int +pxl_impl_process_end(pl_interp_implementation_t * impl) +{ + return 0; +} + + /* Skip to end of job ret 1 if done, 0 ok but EOJ not found, else -ve error code */ static int pxl_impl_flush_to_eoj(pl_interp_implementation_t * impl, @@ -457,23 +495,25 @@ pxl_impl_report_errors(pl_interp_implementation_t * impl, static int pxl_impl_dnit_job(pl_interp_implementation_t * impl) { + int code; pxl_interp_instance_t *pxli = impl->interp_client_data; + gx_device *device = gs_currentdevice(pxli->pgs); px_stream_header_dnit(&pxli->headerState); px_state_cleanup(pxli->pxs); px_process_init(pxli->st, true); - return 0; -} + /* return to original gstate */ + code = gs_grestore_only(pxli->pgs); /* destroys gs_save stack */ -/* Remove a device from an interperter instance */ -/* ret 0 ok, else -ve error code */ -static int -pxl_impl_remove_device(pl_interp_implementation_t * impl) -{ - pxl_interp_instance_t *pxli = impl->interp_client_data; + /* Warn the device that ROP usage has come to an end */ + if (code >= 0) { + code = put_param_bool(pxli, "LanguageUsesROPs", false); - /* return to original gstate */ - return gs_grestore_only(pxli->pgs); /* destroys gs_save stack */ + if (!device->is_open) + code = gs_opendevice(device); + } + + return code; } /* Deallocate a interpreter instance */ @@ -511,15 +551,18 @@ pxl_end_page_top(px_state_t * pxls, int num_copies, int flush) pl_interp_implementation_t pxl_implementation = { pxl_impl_characteristics, pxl_impl_allocate_interp_instance, - pxl_impl_set_device, + NULL, + NULL, + NULL, pxl_impl_init_job, NULL, /* process_file */ + pxl_impl_process_begin, pxl_impl_process, + pxl_impl_process_end, pxl_impl_flush_to_eoj, pxl_impl_process_eof, pxl_impl_report_errors, pxl_impl_dnit_job, - pxl_impl_remove_device, pxl_impl_deallocate_interp_instance, NULL }; diff --git a/psi/dwdll.h b/psi/dwdll.h index e68012c0a..0926c5f0a 100644 --- a/psi/dwdll.h +++ b/psi/dwdll.h @@ -40,6 +40,8 @@ typedef struct GSDLL_S { 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; + PFN_gsapi_get_device_memory get_device_memory; + PFN_gsapi_set_device set_device; } GSDLL; /* Load the Ghostscript DLL. diff --git a/psi/dwnodll.c b/psi/dwnodll.c index b3987b820..800297b88 100644 --- a/psi/dwnodll.c +++ b/psi/dwnodll.c @@ -42,6 +42,8 @@ int load_dll(GSDLL *gsdll, char *last_error, int len) gsdll->exit = &gsapi_exit; gsdll->set_default_device_list = &gsapi_set_default_device_list; gsdll->get_default_device_list = &gsapi_get_default_device_list; + gsdll->get_device_memory = &gsapi_get_device_memory; + gsdll->set_device = &gsapi_set_device; return 0; } diff --git a/psi/gsdll2.def b/psi/gsdll2.def index b6e198ef0..088cc2a6d 100644 --- a/psi/gsdll2.def +++ b/psi/gsdll2.def @@ -19,3 +19,5 @@ EXPORTS gsapi_set_arg_encoding gsapi_set_default_device_list gsapi_get_default_device_list + gsapi_get_device_memory + gsapi_set_device diff --git a/psi/gsdll32.def b/psi/gsdll32.def index deccca22f..0d44d1726 100644 --- a/psi/gsdll32.def +++ b/psi/gsdll32.def @@ -31,3 +31,5 @@ EXPORTS gsapi_set_arg_encoding gsapi_set_default_device_list gsapi_get_default_device_list + gsapi_get_device_memory + gsapi_set_device diff --git a/psi/gsdll32metro.def b/psi/gsdll32metro.def index 391d9aba3..c1bdfcd27 100644 --- a/psi/gsdll32metro.def +++ b/psi/gsdll32metro.def @@ -31,3 +31,5 @@ EXPORTS gsapi_set_arg_encoding gsapi_set_default_device_list gsapi_get_default_device_list + gsapi_get_device_memory + gsapi_set_device diff --git a/psi/gsdll64.def b/psi/gsdll64.def index 0e1e4a56e..46e4a2995 100644 --- a/psi/gsdll64.def +++ b/psi/gsdll64.def @@ -31,3 +31,5 @@ EXPORTS gsapi_set_arg_encoding gsapi_set_default_device_list gsapi_get_default_device_list + gsapi_get_device_memory + gsapi_set_device diff --git a/psi/gsdll64metro.def b/psi/gsdll64metro.def index d2a0649ec..b1af06c1e 100644 --- a/psi/gsdll64metro.def +++ b/psi/gsdll64metro.def @@ -31,3 +31,5 @@ EXPORTS gsapi_set_arg_encoding gsapi_set_default_device_list gsapi_get_default_device_list + gsapi_get_device_memory + gsapi_set_device diff --git a/psi/gsdllARM32metro.def b/psi/gsdllARM32metro.def index 371d4cd5c..769cf6677 100644 --- a/psi/gsdllARM32metro.def +++ b/psi/gsdllARM32metro.def @@ -31,3 +31,5 @@ EXPORTS gsapi_set_arg_encoding gsapi_set_default_device_list gsapi_get_default_device_list + gsapi_get_device_memory + gsapi_set_device diff --git a/psi/ialloc.c b/psi/ialloc.c index 947759d6d..12af9b6c5 100644 --- a/psi/ialloc.c +++ b/psi/ialloc.c @@ -168,7 +168,7 @@ ialloc_trace_space(const gs_ref_memory_t *imem) /* Register a ref root. */ int -gs_register_ref_root(gs_memory_t *mem, gs_gc_root_t *root, +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); diff --git a/psi/iapi.c b/psi/iapi.c index 49116b449..914badcaa 100644 --- a/psi/iapi.c +++ b/psi/iapi.c @@ -23,6 +23,7 @@ #include "gstypes.h" #include "gdebug.h" #include "iapi.h" /* Public API */ +#include "psapi.h" #include "iref.h" #include "iminst.h" #include "imain.h" @@ -33,14 +34,9 @@ #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 - +typedef struct { int a[GS_ARG_ENCODING_LOCAL == PS_ARG_ENCODING_LOCAL ? 1 : -1]; } compile_time_assert_0; +typedef struct { int a[GS_ARG_ENCODING_UTF8 == PS_ARG_ENCODING_UTF8 ? 1 : -1]; } compile_time_assert_1; +typedef struct { int a[GS_ARG_ENCODING_UTF16LE == PS_ARG_ENCODING_UTF16LE ? 1 : -1]; } compile_time_assert_2; /* Return revision numbers and strings of Ghostscript. */ /* Used for determining if wrong GSDLL loaded. */ @@ -57,26 +53,6 @@ gsapi_revision(gsapi_revision_t *pr, int rvsize) 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 @@ -85,52 +61,7 @@ static int GSDLLCALL metro_stderr(void *v, const char *str, int len) 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); + return psapi_new_instance((gs_lib_ctx_t **)pinstance, caller_handle); } /* Destroy an instance of Ghostscript */ @@ -140,27 +71,7 @@ gsapi_new_instance(void **pinstance, void *caller_handle) GSDLLEXPORT void GSDLLAPI gsapi_delete_instance(void *instance) { - gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; - if ((ctx != NULL)) { - gs_memory_t *mem = (gs_memory_t *)(ctx->memory); - 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; - - gs_free_object(mem, minst, "init_main_instance"); - - /* Release the memory (frees up everything) */ - gs_malloc_release(mem); - -#ifndef GS_THREADSAFE - --gsapi_instance_counter; -#endif - } + psapi_delete_instance(instance); } /* Set the callback functions for stdio */ @@ -173,9 +84,9 @@ gsapi_set_stdio(void *instance, gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; if (instance == NULL) return gs_error_Fatal; - ctx->stdin_fn = stdin_fn; - ctx->stdout_fn = stdout_fn; - ctx->stderr_fn = stderr_fn; + ctx->core->stdin_fn = stdin_fn; + ctx->core->stdout_fn = stdout_fn; + ctx->core->stderr_fn = stderr_fn; return 0; } @@ -187,7 +98,7 @@ gsapi_set_poll(void *instance, gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; if (instance == NULL) return gs_error_Fatal; - ctx->poll_fn = poll_fn; + ctx->core->poll_fn = poll_fn; return 0; } @@ -222,122 +133,19 @@ gsapi_get_default_device_list(void *instance, char **list, int *listlen) 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; -} - /* Initialise the interpreter */ GSDLLEXPORT int GSDLLAPI gsapi_set_arg_encoding(void *instance, int encoding) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; - if (instance == NULL) - return gs_error_Fatal; - - 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; - } - return gs_error_Fatal; + return psapi_set_arg_encoding(ctx, encoding); } GSDLLEXPORT int GSDLLAPI gsapi_init_with_args(void *instance, int argc, char **argv) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; - if (instance == NULL) - return gs_error_Fatal; - - return gs_main_init_with_args(get_minst_from_memory(ctx->memory), argc, argv); + return psapi_init_with_args(ctx, argc, argv); } /* The gsapi_run_* functions are like gs_main_run_* except @@ -347,106 +155,74 @@ gsapi_init_with_args(void *instance, int argc, char **argv) /* Setup up a suspendable run_string */ GSDLLEXPORT int GSDLLAPI -gsapi_run_string_begin(void *instance, int user_errors, - int *pexit_code) +gsapi_run_string_begin(void *instance, + int user_errors, + int *pexit_code) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; - if (instance == 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)); + return psapi_run_string_begin(ctx, user_errors, pexit_code); } GSDLLEXPORT int GSDLLAPI -gsapi_run_string_continue(void *instance, - const char *str, uint length, int user_errors, int *pexit_code) +gsapi_run_string_continue(void *instance, + const char *str, + unsigned int length, + int user_errors, + int *pexit_code) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; - if (instance == 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)); + return psapi_run_string_continue(ctx, str, length, user_errors, pexit_code); } GSDLLEXPORT int GSDLLAPI gsapi_run_string_end(void *instance, - int user_errors, int *pexit_code) + int user_errors, + int *pexit_code) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; - if (instance == 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)); + return psapi_run_string_end(ctx, user_errors, pexit_code); } GSDLLEXPORT int GSDLLAPI -gsapi_run_string_with_length(void *instance, - const char *str, uint length, int user_errors, int *pexit_code) +gsapi_run_string_with_length(void *instance, + const char *str, + unsigned int length, + int user_errors, + int *pexit_code) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; - if (instance == 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)); + return psapi_run_string_with_length(ctx, str, length, user_errors, pexit_code); } GSDLLEXPORT int GSDLLAPI -gsapi_run_string(void *instance, - const char *str, int user_errors, int *pexit_code) +gsapi_run_string(void *instance, + const char *str, + int user_errors, + int *pexit_code) { - return gsapi_run_string_with_length(instance, - str, (uint)strlen(str), user_errors, pexit_code); + gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; + return psapi_run_string_with_length(ctx, + str, + (unsigned int)strlen(str), + user_errors, + pexit_code); } GSDLLEXPORT int GSDLLAPI -gsapi_run_file(void *instance, const char *file_name, - int user_errors, int *pexit_code) +gsapi_run_file(void *instance, + const char *file_name, + int user_errors, + int *pexit_code) { - char *d, *temp; - const char *c = file_name; - char dummy[6]; - int rune, code, len; gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; - gs_main_instance *minst; - if (instance == NULL) - return gs_error_Fatal; - minst = get_minst_from_memory(ctx->memory); - - /* 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; + return psapi_run_file(ctx, file_name, user_errors, pexit_code); } #ifdef __WIN32__ GSDLLEXPORT int GSDLLAPI -gsapi_init_with_argsW(void *instance, int argc, wchar_t **argv) +gsapi_init_with_argsW(void *instance, + int argc, + wchar_t **argv) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; int code; @@ -455,16 +231,18 @@ gsapi_init_with_argsW(void *instance, int argc, wchar_t **argv) return gs_error_Fatal; old = gs_main_inst_get_arg_decode(get_minst_from_memory(ctx->memory)); - code = gsapi_set_arg_encoding(instance, GS_ARG_ENCODING_UTF16LE); + code = psapi_set_arg_encoding(ctx, PS_ARG_ENCODING_UTF16LE); if (code != 0) return code; - code = gsapi_init_with_args(instance, 2*argc, (char **)argv); + code = psapi_init_with_args(ctx, 2*argc, (char **)argv); gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), old); return code; } GSDLLEXPORT int GSDLLAPI -gsapi_init_with_argsA(void *instance, int argc, char **argv) +gsapi_init_with_argsA(void *instance, + int argc, + char **argv) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; int code; @@ -473,17 +251,19 @@ gsapi_init_with_argsA(void *instance, int argc, char **argv) return gs_error_Fatal; old = gs_main_inst_get_arg_decode(get_minst_from_memory(ctx->memory)); - code = gsapi_set_arg_encoding(instance, GS_ARG_ENCODING_LOCAL); + code = psapi_set_arg_encoding(ctx, PS_ARG_ENCODING_LOCAL); if (code != 0) return code; - code = gsapi_init_with_args(instance, 2*argc, (char **)argv); + code = psapi_init_with_args(ctx, 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 *instance, const wchar_t *file_name, - int user_errors, int *pexit_code) +gsapi_run_fileW(void *instance, + const wchar_t *file_name, + int user_errors, + int *pexit_code) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; int code; @@ -492,17 +272,19 @@ gsapi_run_fileW(void *instance, const wchar_t *file_name, return gs_error_Fatal; old = gs_main_inst_get_arg_decode(get_minst_from_memory(ctx->memory)); - code = gsapi_set_arg_encoding(instance, GS_ARG_ENCODING_UTF16LE); + code = psapi_set_arg_encoding(ctx, PS_ARG_ENCODING_UTF16LE); if (code != 0) return code; - code = gsapi_run_file(instance, (const char *)file_name, user_errors, pexit_code); + code = psapi_run_file(ctx, (const char *)file_name, user_errors, pexit_code); gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), old); return code; } GSDLLEXPORT int GSDLLAPI -gsapi_run_fileA(void *instance, const char *file_name, - int user_errors, int *pexit_code) +gsapi_run_fileA(void *instance, + const char *file_name, + int user_errors, + int *pexit_code) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; int code; @@ -511,25 +293,37 @@ gsapi_run_fileA(void *instance, const char *file_name, return gs_error_Fatal; old = gs_main_inst_get_arg_decode(get_minst_from_memory(ctx->memory)); - code = gsapi_set_arg_encoding(instance, GS_ARG_ENCODING_LOCAL); + code = psapi_set_arg_encoding(ctx, PS_ARG_ENCODING_LOCAL); if (code != 0) return code; - code = gsapi_run_file(instance, (const char *)file_name, user_errors, pexit_code); + code = psapi_run_file(ctx, (const char *)file_name, user_errors, pexit_code); gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), old); return code; } #endif +/* Retrieve the memory allocator for the interpreter instance */ +GSDLLEXPORT gs_memory_t * GSDLLAPI +gsapi_get_device_memory(void *instance) +{ + gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; + return psapi_get_device_memory(ctx); +} + +/* Retrieve the memory allocator for the interpreter instance */ +GSDLLEXPORT int GSDLLAPI +gsapi_set_device(void *instance, gx_device *pdev) +{ + gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; + return psapi_set_device(ctx, pdev); +} + /* Exit the interpreter */ GSDLLEXPORT int GSDLLAPI gsapi_exit(void *instance) { gs_lib_ctx_t *ctx = (gs_lib_ctx_t *)instance; - if (instance == NULL) - return gs_error_Fatal; - - gs_to_exit(ctx->memory, 0); - return 0; + return psapi_exit(ctx); } /* end of iapi.c */ diff --git a/psi/iapi.h b/psi/iapi.h index 4155cbf03..f4120c546 100644 --- a/psi/iapi.h +++ b/psi/iapi.h @@ -112,6 +112,16 @@ extern "C" { typedef struct display_callback_s display_callback; #endif +#ifndef gs_memory_DEFINED +# define gs_memory_DEFINED +typedef struct gs_memory_s gs_memory_t; +#endif + +#ifndef gx_device_DEFINED +# define gx_device_DEFINED +typedef struct gx_device_s gx_device; +#endif + typedef struct gsapi_revision_s { const char *product; const char *copyright; @@ -296,6 +306,14 @@ gsapi_run_fileW(void *instance, const wchar_t *file_name, int user_errors, int *pexit_code); #endif +/* Retrieve the memory allocator for the interpreter instance */ +GSDLLEXPORT gs_memory_t * GSDLLAPI +gsapi_get_device_memory(void *instance); + +/* Set the device */ +GSDLLEXPORT int GSDLLAPI +gsapi_set_device(void *instance, gx_device *pdev); + /* Exit the interpreter. * This must be called on shutdown if gsapi_init_with_args() * has been called, and just before gsapi_delete_instance(). @@ -353,6 +371,8 @@ typedef int (GSDLLAPIPTR PFN_gsapi_run_fileA)(void *instance, typedef int (GSDLLAPIPTR PFN_gsapi_run_fileW)(void *instance, const wchar_t *file_name, int user_errors, int *pexit_code); #endif +typedef gs_memory_t * (GSDLLAPIPTR PFN_gsapi_get_device_memory)(void *instance); +typedef gs_memory_t * (GSDLLAPIPTR PFN_gsapi_set_device)(void *instance, gx_device *pdev); typedef int (GSDLLAPIPTR PFN_gsapi_exit)(void *instance); #ifdef __MACOS__ diff --git a/psi/icstate.h b/psi/icstate.h index 4c6a14d44..492800ed3 100644 --- a/psi/icstate.h +++ b/psi/icstate.h @@ -70,6 +70,7 @@ struct gs_context_state_s { 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 */ + gs_offset_t uel_position; /* The file position at which we last hit UEL */ /* Put the stacks at the end to minimize other offsets. */ dict_stack_t dict_stack; @@ -238,11 +238,13 @@ gs_gc_reclaim(vm_spaces * pspaces, bool global) /* Register the allocators themselves as roots, */ /* so we mark and relocate the change and save lists properly. */ - for_spaces(ispace, max_trace) + for_spaces(ispace, max_trace) { + rp = &space_roots[ispace]; gs_register_struct_root((gs_memory_t *)space_memories[ispace], - &space_roots[ispace], + &rp, (void **)&space_memories[ispace], "gc_top_level"); + } end_phase(state.heap,"register space roots"); diff --git a/psi/imain.c b/psi/imain.c index fc5bbc2e5..1b84604a9 100644 --- a/psi/imain.c +++ b/psi/imain.c @@ -38,7 +38,7 @@ #include "oper.h" #include "iconf.h" /* for gs_init_* imports */ #include "idebug.h" -#include "idict.h" +#include "iddict.h" #include "iname.h" /* for name_init */ #include "dstack.h" #include "estack.h" @@ -192,7 +192,7 @@ gs_main_init1(gs_main_instance * minst) goto fail; } mem->gs_lib_ctx->gs_name_table = nt; - code = gs_register_struct_root(mem, mem->gs_lib_ctx->name_table_root, + code = gs_register_struct_root(mem, &mem->gs_lib_ctx->name_table_root, (void **)&mem->gs_lib_ctx->gs_name_table, "the_gs_name_table"); if (code < 0) @@ -225,7 +225,7 @@ fail: */ static int gs_main_interpret(gs_main_instance *minst, ref * pref, int user_errors, - int *pexit_code, ref * perror_object) + int *pexit_code, ref * perror_object) { int code; @@ -233,7 +233,7 @@ gs_main_interpret(gs_main_instance *minst, ref * pref, int user_errors, minst->i_ctx_p->lib_path = &minst->lib_path; code = gs_interpret(&minst->i_ctx_p, pref, - user_errors, pexit_code, perror_object); + user_errors, pexit_code, perror_object); return code; } @@ -630,7 +630,7 @@ gs_main_run_string_begin(gs_main_instance * minst, int user_errors, 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); + perror_object); return (code == gs_error_NeedInput ? 0 : code == 0 ? gs_error_Fatal : code); } /* Continue running a string with the option of suspending. */ @@ -645,8 +645,16 @@ gs_main_run_string_continue(gs_main_instance * minst, const char *str, make_const_string(&rstr, avm_foreign | a_readonly, length, (const byte *)str); return gs_main_interpret(minst, &rstr, user_errors, pexit_code, - perror_object); + perror_object); } +uint +gs_main_get_uel_offset(gs_main_instance * minst) +{ + if (minst->i_ctx_p == NULL) + return 0; + return (uint)minst->i_ctx_p->uel_position; +} + /* Signal EOF when suspended. */ int gs_main_run_string_end(gs_main_instance * minst, int user_errors, @@ -656,7 +664,79 @@ gs_main_run_string_end(gs_main_instance * minst, int user_errors, make_empty_const_string(&rstr, avm_foreign | a_readonly); return gs_main_interpret(minst, &rstr, user_errors, pexit_code, - perror_object); + perror_object); +} + +gs_memory_t * +gs_main_get_device_memory(gs_main_instance * minst) +{ + gs_memory_t *dev_mem = NULL; + if (minst && minst->init_done >= 1) { + i_ctx_t * i_ctx_p = minst->i_ctx_p; + dev_mem = imemory_global->stable_memory; + } + return dev_mem; +} + +int +gs_main_set_device(gs_main_instance * minst, gx_device *pdev) +{ + i_ctx_t *i_ctx_p = minst->i_ctx_p; + ref error_object; + int code; + + if (pdev == NULL) { + /* Leave job encapsulation, restore the graphics state gsaved below (so back to the nullpage device) + and re-enter job encapsulation. + We rely on the end of job encapsulation restore to the put the gstate stack back how it was when + we entered job encapsulation below, so the grestore will pickup the correct gstate. + */ + code = gs_main_run_string(minst, + "true 0 startjob pop grestore false 0 startjob pop", + 0, &code, &error_object); + if (code < 0) goto done; + } + else { + /* Leave job encapsulation, and save the graphics state (including the device: nullpage) + Store the page size in a dictionary, which we'll use to configure the incoming device */ + code = gs_main_run_string(minst, + "true 0 startjob pop gsave " + /* /PageSize /GetDeviceParam .special_op will either return: + * /PageSize [ <width> <height> ] true (if it exists) or + * false (if it does not) */ + "<< /PageSize /GetDeviceParam .special_op " + /* If we wanted to force a default pagesize, we'd do: + * "not { /PageSize [595 842] } if " + * but for now we'll just leave the default as it is, and do: */ + "pop " + ">> " + , 0, &code, &error_object); + if (code < 0) goto done; + /* First call goes to the C directly to actually set the device. This + * avoids the SAFER checks. */ + code = zsetdevice_no_safer(i_ctx_p, pdev); + if (code < 0) goto done; + code = zcurrentoutputdevice(i_ctx_p); + if (code < 0) goto done; + code = gs_main_run_string(minst, + /* Set the device again to the same one. This determines + * whether to erase page or not, but passes the safer + * checks as the device is unchanged. */ + "setdevice " + "setpagedevice " + /* GS specifics: Force the cached copy of the params to be updated. */ + "currentpagedevice pop " + /* Setup the halftone */ + ".setdefaultscreen " + /* Re-run the scheduled initialisation procs, in case we've just set pdfwrite */ + "1183615869 internaldict /.execute_scheduled_inits get exec " + /* Re-enter job encapsulation */ + "false 0 startjob pop " + , 0, &code, &error_object); + if (code < 0) goto done; + } +done: + return code; } /* ------ Operand stack access ------ */ @@ -875,6 +955,7 @@ gs_main_finit(gs_main_instance * minst, int exit_status, int code) int exit_code; ref error_object; char *tempnames = NULL; + gs_lib_ctx_core_t *core; /* NB: need to free gs_name_table */ @@ -960,6 +1041,7 @@ gs_main_finit(gs_main_instance * minst, int exit_status, int code) gx_device *pdev = i_ctx_p->pgs->device; const char * dname = pdev->dname; gs_gc_root_t dev_root; + gs_gc_root_t *dev_root_ptr = &dev_root; /* There is a chance that, during the call to gs_main_run_string(), the interpreter may * decide to call the garbager - the device is in gc memory, and the only reference to it * (in the gstate) has been removed, thus it can be destroyed by the garbager. @@ -967,7 +1049,7 @@ gs_main_finit(gs_main_instance * minst, int exit_status, int code) * Register the device as a gc 'root' so it will be implicitely marked by garbager, and * and thus surive until control returns here. */ - if (gs_register_struct_root(pdev->memory, &dev_root, (void **)&pdev, "gs_main_finit") < 0) { + if (gs_register_struct_root(pdev->memory, &dev_root_ptr, (void **)&pdev, "gs_main_finit") < 0) { free(tempnames); return_error(gs_error_Fatal); } @@ -993,7 +1075,7 @@ gs_main_finit(gs_main_instance * minst, int exit_status, int code) emprintf2(imemory, "UNKNOWN ERROR %d closing %s device.\n", code, dname); } } - gs_unregister_root(pdev->memory, &dev_root, "gs_main_finit"); + gs_unregister_root(pdev->memory, dev_root_ptr, "gs_main_finit"); rc_decrement(pdev, "gs_main_finit"); /* device might be freed */ if (exit_status == 0 || exit_status == gs_error_Quit) exit_status = code; @@ -1029,15 +1111,16 @@ gs_main_finit(gs_main_instance * minst, int exit_status, int code) } /* 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; + core = minst->heap->gs_lib_ctx->core; + if (core->fstdout2 + && (core->fstdout2 != core->fstdout) + && (core->fstdout2 != core->fstderr)) { + fclose(core->fstdout2); + core->fstdout2 = (FILE *)NULL; } - minst->heap->gs_lib_ctx->stdout_is_redirected = 0; - minst->heap->gs_lib_ctx->stdout_to_stderr = 0; + minst->heap->gs_lib_ctx->core->stdout_is_redirected = 0; + minst->heap->gs_lib_ctx->core->stdout_to_stderr = 0; /* remove any temporary files, after ghostscript has closed files */ if (tempnames) { char *p = tempnames; @@ -1127,3 +1210,53 @@ gs_main_dump_stack(gs_main_instance *minst, int code, ref * perror_object) debug_dump_stack(minst->heap, &e_stack, "Execution stack"); debug_dump_stack(minst->heap, &d_stack, "Dictionary stack"); } + +int +gs_main_force_resolutions(gs_main_instance * minst, const float *resolutions) +{ + ref value; + int code; + + if (resolutions == NULL) + return 0; + + if (minst == NULL) + return gs_error_Fatal; + + make_true(&value); + code = i_initial_enter_name(minst->i_ctx_p, "FIXEDRESOLUTION", &value); + if (code < 0) + return code; + make_real(&value, resolutions[0]); + code = i_initial_enter_name(minst->i_ctx_p, "DEVICEXRESOLUTION", &value); + if (code < 0) + return code; + make_real(&value, resolutions[1]); + return i_initial_enter_name(minst->i_ctx_p, "DEVICEYRESOLUTION", &value); +} + +int +gs_main_force_dimensions(gs_main_instance *minst, const long *dimensions) +{ + i_ctx_t *i_ctx_p; + ref value; + int code = 0; + + if (dimensions == NULL) + return 0; + if (minst == NULL) + return gs_error_Fatal; + + i_ctx_p = minst->i_ctx_p; + + make_true(&value); + code = i_initial_enter_name(minst->i_ctx_p, "FIXEDMEDIA", &value); + if (code < 0) + return code; + make_int(&value, dimensions[0]); + code = i_initial_enter_name(minst->i_ctx_p, "DEVICEWIDTH", &value); + if (code < 0) + return code; + make_int(&value, dimensions[1]); + return i_initial_enter_name(minst->i_ctx_p, "DEVICEHEIGHT", &value); +} diff --git a/psi/imain.h b/psi/imain.h index 708d139e6..374ce2e2a 100644 --- a/psi/imain.h +++ b/psi/imain.h @@ -169,6 +169,13 @@ int gs_main_run_string_with_length(gs_main_instance * minst, int user_errors, int *pexit_code, ref * perror_object); +/* Version of gs_main_run_file that works for PDF files. */ +int gs_main_run_file2(gs_main_instance *minst, + const char *fname, + 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. @@ -198,6 +205,25 @@ int gs_main_run_string_continue(gs_main_instance * minst, int gs_main_run_string_end(gs_main_instance * minst, int user_errors, int *pexit_code, ref * perror_object); +/* This procedure returns the offset at which the last UEL was + * encountered during parsing. This is only defined after + * a gs_error_InterpreterExit has been returned (and in particular + * after a .forceinterp_exit has been called). Calling this + * in other circumstances will get undefined results. */ +uint gs_main_get_uel_offset(gs_main_instance * minst); + +gs_memory_t * +gs_main_get_device_memory(gs_main_instance * minst); + +int +gs_main_set_device(gs_main_instance * minst, gx_device *pdev); + +int +gs_main_force_resolutions(gs_main_instance * minst, const float *resolutions); + +int +gs_main_force_dimensions(gs_main_instance * minst, const long *resolutions); + /* ---------------- Operand stack access ---------------- */ /* diff --git a/psi/imainarg.c b/psi/imainarg.c index 5c8016a8c..6ccd839bf 100644 --- a/psi/imainarg.c +++ b/psi/imainarg.c @@ -95,8 +95,8 @@ 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 runarg(gs_main_instance *, const char *, const char *, const char *, int, int, int *, ref *); +static int run_string(gs_main_instance *, const char *, int, int, int *, ref *); 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); @@ -131,7 +131,7 @@ set_debug_flags(const char *arg, char *flags) } int -gs_main_init_with_args(gs_main_instance * minst, int argc, char *argv[]) +gs_main_init_with_args01(gs_main_instance * minst, int argc, char *argv[]) { const char *arg; arg_list args; @@ -256,24 +256,43 @@ gs_main_init_with_args(gs_main_instance * minst, int argc, char *argv[]) } } } - 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); + dmprintf1(minst->heap, "%% Init (Part 1/2) done, instance 0x%p, with args: ", minst); for (i=1; i<argc; i++) dmprintf1(minst->heap, "%s ", argv[i]); dmprintf(minst->heap, "\n"); } + return code; +} + +int +gs_main_init_with_args2(gs_main_instance * minst) +{ + int code; + + code = gs_main_init2(minst); + if (code < 0) + return code; + + if (gs_debug[':']) + dmprintf1(minst->heap, "%% Init (Part 2/2) done, instance 0x%p.", minst); + if (!minst->run_start) return gs_error_Quit; - return code ; + return code; +} + +int +gs_main_init_with_args(gs_main_instance * minst, int argc, char *argv[]) +{ + int code = gs_main_init_with_args01(minst, argc, argv); + + if (code < 0) + return code; + return gs_main_init_with_args2(minst); } /* @@ -296,7 +315,7 @@ gs_arg_get_codepoint *gs_main_inst_get_arg_decode(gs_main_instance * minst) int gs_main_run_start(gs_main_instance * minst) { - return run_string(minst, "systemdict /start get exec", runFlush); + return run_string(minst, "systemdict /start get exec", runFlush, minst->user_errors, NULL, NULL); } /* Process switches. Return 0 if processed, 1 for unknown switch, */ @@ -315,10 +334,10 @@ swproc(gs_main_instance * minst, const char *arg, arg_list * pal) 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; + minst->heap->gs_lib_ctx->core->stdin_is_interactive = true; goto run_stdin; case '_': /* read stdin with normal buffering */ - minst->heap->gs_lib_ctx->stdin_is_interactive = false; + minst->heap->gs_lib_ctx->core->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. */ @@ -329,7 +348,7 @@ run_stdin: if (code < 0) return code; - code = run_string(minst, ".runstdin", runFlush); + code = run_string(minst, ".runstdin", runFlush, minst->user_errors, NULL, NULL); if (code < 0) return code; /* If in saved_pages_test_mode, print and flush previous job before the next file */ @@ -432,19 +451,19 @@ run_stdin: else code = gs_main_init2(minst); if (code >= 0) - code = run_string(minst, "userdict/ARGUMENTS[", 0); + code = run_string(minst, "userdict/ARGUMENTS[", 0, minst->user_errors, NULL, NULL); if (code >= 0) while ((code = arg_next(pal, (const char **)&arg, minst->heap)) > 0) { - code = runarg(minst, "", arg, "", runInit); + code = runarg(minst, "", arg, "", runInit, minst->user_errors, NULL, NULL); if (code < 0) break; } if (code >= 0) - code = runarg(minst, "]put", psarg, ".runfile", runInit | runFlush); + code = runarg(minst, "]put", psarg, ".runfile", runInit | runFlush, minst->user_errors, NULL, NULL); arg_free((char *)psarg, minst->heap); if (code >= 0) code = gs_error_Quit; - + return code; } case 'A': /* trace allocator */ @@ -490,7 +509,7 @@ run_stdin: (arg[0] == '-' && !isdigit((unsigned char)arg[1])) ) break; - code = runarg(minst, "", arg, ".runstring", 0); + code = runarg(minst, "", arg, ".runstring", 0, minst->user_errors, NULL, NULL); if (code < 0) return code; } @@ -585,20 +604,15 @@ run_stdin: break; case 'g': /* define device geometry */ { - long width, height; - ref value; + long dimensions[2]; if ((code = gs_main_init1(minst)) < 0) return code; - if (sscanf((const char *)arg, "%ldx%ld", &width, &height) != 2) { + if (sscanf((const char *)arg, "%ldx%ld", &dimensions[0], &dimensions[1]) != 2) { puts(minst->heap, "-g must be followed by <width>x<height>"); return gs_error_Fatal; } - make_int(&value, width); - i_initial_enter_name(minst->i_ctx_p, "DEVICEWIDTH", &value); - make_int(&value, height); - i_initial_enter_name(minst->i_ctx_p, "DEVICEHEIGHT", &value); - i_initial_enter_name(minst->i_ctx_p, "FIXEDMEDIA", &vtrue); + gs_main_force_dimensions(minst, dimensions); break; } case 'h': /* print help */ @@ -719,24 +733,19 @@ run_stdin: break; case 'r': /* define device resolution */ { - float xres, yres; - ref value; + float res[2]; if ((code = gs_main_init1(minst)) < 0) return code; - switch (sscanf((const char *)arg, "%fx%f", &xres, &yres)) { + switch (sscanf((const char *)arg, "%fx%f", &res[0], &res[0])) { default: puts(minst->heap, "-r must be followed by <res> or <xres>x<yres>"); return gs_error_Fatal; case 1: /* -r<res> */ - yres = xres; + res[1] = res[0]; /* fall through */ case 2: /* -r<xres>x<yres> */ - make_real(&value, xres); - i_initial_enter_name(minst->i_ctx_p, "DEVICEXRESOLUTION", &value); - make_real(&value, yres); - i_initial_enter_name(minst->i_ctx_p, "DEVICEYRESOLUTION", &value); - i_initial_enter_name(minst->i_ctx_p, "FIXEDRESOLUTION", &vtrue); + gs_main_force_resolutions(minst, res); } break; } @@ -954,7 +963,7 @@ argproc(gs_main_instance * minst, const char *arg) return run_buffered(minst, arg); } else { /* Run file directly in the normal way. */ - return runarg(minst, "", arg, ".runfile", runInit | runFlush); + return runarg(minst, "", arg, ".runfile", runInit | runFlush, minst->user_errors, NULL, NULL); } } static int @@ -999,8 +1008,14 @@ run_buffered(gs_main_instance * minst, const char *arg) 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) +runarg(gs_main_instance *minst, + const char *pre, + const char *arg, + const char *post, + int options, + int user_errors, + int *pexit_code, + ref *perror_object) { int len = strlen(pre) + esc_strlen(arg) + strlen(post) + 1; int code; @@ -1021,24 +1036,45 @@ runarg(gs_main_instance * minst, const char *pre, const char *arg, esc_strcat(line, arg); strcat(line, post); minst->i_ctx_p->starting_arg_file = true; - code = run_string(minst, line, options); + code = run_string(minst, line, options, user_errors, pexit_code, perror_object); minst->i_ctx_p->starting_arg_file = false; gs_free_object(minst->heap, line, "runarg"); return code; } +int +gs_main_run_file2(gs_main_instance *minst, + const char *filename, + int user_errors, + int *pexit_code, + ref *perror_object) +{ + return runarg(minst, "", filename, ".runfile", runFlush, user_errors, pexit_code, perror_object); +} static int -run_string(gs_main_instance * minst, const char *str, int options) +run_string(gs_main_instance *minst, + const char *str, + int options, + int user_errors, + int *pexit_code, + ref *perror_object) { int exit_code; ref error_object; - int code = gs_main_run_string(minst, str, minst->user_errors, - &exit_code, &error_object); + int code; + + if (pexit_code == NULL) + pexit_code = &exit_code; + if (perror_object == NULL) + perror_object = &error_object; + + code = gs_main_run_string(minst, str, user_errors, + pexit_code, perror_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); + return run_finish(minst, code, *pexit_code, perror_object); } static int run_finish(gs_main_instance *minst, int code, int exit_code, @@ -1049,9 +1085,12 @@ run_finish(gs_main_instance *minst, int code, int exit_code, case 0: break; case gs_error_Fatal: - emprintf1(minst->heap, - "Unrecoverable error, exit code %d\n", - exit_code); + if (exit_code == gs_error_InterpreterExit) + code = exit_code; + else + emprintf1(minst->heap, + "Unrecoverable error, exit code %d\n", + exit_code); break; default: gs_main_dump_stack(minst, code, perror_object); @@ -1072,28 +1111,29 @@ static int try_stdout_redirect(gs_main_instance * minst, const char *command, const char *filename) { + gs_lib_ctx_core_t *core = minst->heap->gs_lib_ctx->core; if (strcmp(command, "stdout") == 0) { - minst->heap->gs_lib_ctx->stdout_to_stderr = 0; - minst->heap->gs_lib_ctx->stdout_is_redirected = 0; + core->stdout_to_stderr = 0; + core->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 (core->fstdout2 + && (core->fstdout2 != core->fstdout) + && (core->fstdout2 != core->fstderr)) { + fclose(core->fstdout2); + core->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; + core->stdout_to_stderr = 1; } - else if ((minst->heap->gs_lib_ctx->fstdout2 = + else if ((core->fstdout2 = gp_fopen(filename, "w")) == (FILE *)NULL) return_error(gs_error_invalidfileaccess); - minst->heap->gs_lib_ctx->stdout_is_redirected = 1; + core->stdout_is_redirected = 1; } return 0; } diff --git a/psi/imainarg.h b/psi/imainarg.h index 86a21ce28..7b6d278a1 100644 --- a/psi/imainarg.h +++ b/psi/imainarg.h @@ -35,6 +35,13 @@ typedef struct gs_main_instance_s gs_main_instance; int gs_main_init_with_args(gs_main_instance * minst, int argc, char *argv[]); /* + * Split init functions; gs_main_init_with_args01 and 2 together do + * the same as gs_main_init_with_args, but allow for additional + * options to be processed in between. */ +int gs_main_init_with_args01(gs_main_instance * minst, int argc, char *argv[]); +int gs_main_init_with_args2(gs_main_instance * minst); + +/* * Run the 'start' procedure (after processing the command line). */ int gs_main_run_start(gs_main_instance * minst); diff --git a/psi/imemory.h b/psi/imemory.h index 7d1ce1217..e1a5b676d 100644 --- a/psi/imemory.h +++ b/psi/imemory.h @@ -60,7 +60,7 @@ int gs_alloc_string_ref(gs_ref_memory_t * mem, ref * psref, /* 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, +int gs_register_ref_root(gs_memory_t *mem, gs_gc_root_t **root, void **pp, client_name_t cname); /* diff --git a/psi/iminst.h b/psi/iminst.h index e3c49687f..6a68e75ec 100644 --- a/psi/iminst.h +++ b/psi/iminst.h @@ -25,6 +25,11 @@ typedef struct gs_main_instance_s gs_main_instance; #endif +#ifndef display_callback_DEFINED +# define display_callback_DEFINED +typedef struct display_callback_s display_callback; +#endif + /* * Define the structure of a search path. Currently there is only one, * but there might be more someday. diff --git a/psi/int.mak b/psi/int.mak index 88ce38ad1..836eb165c 100644 --- a/psi/int.mak +++ b/psi/int.mak @@ -535,7 +535,8 @@ $(PSOBJ)zpath.$(OBJ) : $(PSSRC)zpath.c $(OP) $(math__h)\ # 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) +INTAPI=$(PSOBJ)iapi.$(OBJ) +INT1=$(PSOBJ)psapi.$(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) @@ -620,6 +621,12 @@ $(PSD)psbase.dev : $(ECHOGS_XE) $(INT_OBJS)\ $(ADDMOD) $(PSD)psbase -include $(GLD)fapi_ps $(ADDMOD) $(PSD)psbase -replace $(GLD)gsiodevs +$(PSD)iapi.dev : $(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 $(INTAPI) $(INT_MAK) $(MAKEDIRS) + $(SETMOD) $(PSD)iapi $(INTAPI) + # -------------------------- Feature definitions -------------------------- # # ---------------- Full Level 1 interpreter ---------------- # @@ -1919,6 +1926,12 @@ $(PSOBJ)iapi.$(OBJ) : $(PSSRC)iapi.c $(AK)\ $(INT_MAK) $(MAKEDIRS) $(PSCC) $(PSO_)iapi.$(OBJ) $(C_) $(PSSRC)iapi.c +$(PSOBJ)psapi.$(OBJ) : $(PSSRC)psapi.c $(AK)\ + $(string__h) $(ierrors_h) $(gscdefs_h) $(gstypes_h) $(iapi_h)\ + $(iref_h) $(imain_h) $(imainarg_h) $(iminst_h) $(gslibctx_h)\ + $(INT_MAK) $(MAKEDIRS) + $(PSCC) $(PSO_)psapi.$(OBJ) $(C_) $(PSSRC)psapi.c + $(PSOBJ)icontext.$(OBJ) : $(PSSRC)icontext.c $(GH)\ $(gsstruct_h) $(gxalloc_h)\ $(dstack_h) $(ierrors_h) $(estack_h) $(files_h)\ @@ -1953,7 +1966,7 @@ $(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)\ + $(ialloc_h) $(iconf_h) $(idebug_h) $(iddict_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) $(zfile_h)\ diff --git a/psi/interp.c b/psi/interp.c index 6dc0ddae1..281a20d83 100644 --- a/psi/interp.c +++ b/psi/interp.c @@ -136,6 +136,7 @@ struct stats_interp_s { static int estack_underflow(i_ctx_t *); static int interp(i_ctx_t **, const ref *, ref *); static int interp_exit(i_ctx_t *); +static int zforceinterp_exit(i_ctx_t *i_ctx_p); static void set_gc_signal(i_ctx_t *, int); static int copy_stack(i_ctx_t *, const ref_stack_t *, int skip, ref *); static int oparray_pop(i_ctx_t *); @@ -146,6 +147,7 @@ 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 *); +static int zactonuel(i_ctx_t *); /* Stack sizes */ @@ -279,8 +281,10 @@ const op_def interp2_op_defs[] = { {"2.errorexec", zerrorexec}, {"0.finderrorobject", zfinderrorobject}, {"0%interp_exit", interp_exit}, + {"0.forceinterp_exit", zforceinterp_exit}, {"0%oparray_pop", oparray_pop}, {"0%errorexec_pop", errorexec_pop}, + {"0.actonuel", zactonuel}, op_def_end(0) }; @@ -433,7 +437,7 @@ 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; + gs_gc_root_t ctx_root, *r = &ctx_root; int code; #ifdef DEBUG @@ -441,11 +445,11 @@ interp_reclaim(i_ctx_t **pi_ctx_p, int space) return 0; #endif - gs_register_struct_root(imemory_system, &ctx_root, + gs_register_struct_root(imemory_system, &r, (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)"); + gs_unregister_root(imemory_system, r, "interp_reclaim(pi_ctx_p)"); return code; } @@ -465,10 +469,10 @@ 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; + gs_gc_root_t error_root, *r = &error_root; int code; - gs_register_ref_root(imemory_system, &error_root, + gs_register_ref_root(imemory_system, &r, (void **)&perror_object, "gs_interpret"); code = gs_call_interp(pi_ctx_p, pref, user_errors, pexit_code, perror_object); @@ -499,12 +503,12 @@ again: 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; + gs_gc_root_t epref_root, *r = &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, + gs_register_ref_root(imemory_system, &r, (void **)&epref, "gs_call_interp(epref)"); code = interp_reclaim(pi_ctx_p, -1); i_ctx_p = *pi_ctx_p; @@ -727,6 +731,38 @@ interp_exit(i_ctx_t *i_ctx_p) return gs_error_InterpreterExit; } +/* Only used (currently) with language switching: + * allows the PS interpreter to co-exist with the + * PJL interpreter. + */ +static int +zforceinterp_exit(i_ctx_t *i_ctx_p) +{ + os_ptr op = osp; + stream *s; + + check_file(s, op); + i_ctx_p->uel_position = stell(s)-1; + /* resetfile */ + if (file_is_valid(s, op)) + sreset(s); + + if (!gs_lib_ctx_get_act_on_uel((gs_memory_t *)(i_ctx_p->memory.current))) + return 0; + + gs_interp_reset(i_ctx_p); + /* gs_interp_reset() actually leaves the op stack one entry below + * the bottom of the stack, and that can cause problems depending + * on the interpreter state at the end of the job. + * So push a null object, and the return code before continuing. + */ + push(2); + op = osp; + make_null(op - 1); + make_int(op, gs_error_InterpreterExit); + return_error(gs_error_Quit); +} + /* Set the GC signal for all VMs. */ static void set_gc_signal(i_ctx_t *i_ctx_p, int value) @@ -2017,3 +2053,13 @@ zcurrentstackprotect(i_ctx_t *i_ctx_p) make_bool(op, ep->value.opproc == oparray_cleanup); return 0; } + +static int +zactonuel(i_ctx_t *i_ctx_p) +{ + os_ptr op = osp; + + push(1); + make_bool(op, !!gs_lib_ctx_get_act_on_uel((gs_memory_t *)(i_ctx_p->memory.current))); + return 0; +} diff --git a/psi/ireclaim.c b/psi/ireclaim.c index 8c1562d94..653ab5f85 100644 --- a/psi/ireclaim.c +++ b/psi/ireclaim.c @@ -156,12 +156,12 @@ gs_vmreclaim(gs_dual_memory_t *dmem, bool global) { void *ctxp = i_ctx_p; - gs_gc_root_t context_root; + gs_gc_root_t context_root, *r = &context_root; - gs_register_struct_root((gs_memory_t *)lmem, &context_root, + gs_register_struct_root((gs_memory_t *)lmem, &r, &ctxp, "i_ctx_p root"); GS_RECLAIM(&dmem->spaces, global); - gs_unregister_root((gs_memory_t *)lmem, &context_root, "i_ctx_p root"); + gs_unregister_root((gs_memory_t *)lmem, r, "i_ctx_p root"); i_ctx_p = ctxp; dmem = &i_ctx_p->memory; } diff --git a/psi/msvc.mak b/psi/msvc.mak index 6a2ed1cdc..125b92a28 100644 --- a/psi/msvc.mak +++ b/psi/msvc.mak @@ -267,9 +267,9 @@ PCL_TARGET=gpcl6 XPS_TARGET=gxps !endif -# !if $(BUILD_GPDL) -# GPDL_TARGET=gpdl -# !endif +!if $(BUILD_GPDL) +GPDL_TARGET=gpdl +!endif PCL_XPS_TARGETS=$(PCL_TARGET) $(XPS_TARGET) @@ -1457,7 +1457,7 @@ DEVICE_DEVS8=$(DD)pcxmono.dev $(DD)pcxgray.dev $(DD)pcx16.dev $(DD)pcx256.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 $(DD)bitrgbtags.dev +DEVICE_DEVS12=$(DD)bit.dev $(DD)bitrgb.dev $(DD)bitcmyk.dev $(DD)bitrgbtags.dev $(DD)chameleon.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 $(DD)pdfimage8.dev $(DD)pdfimage24.dev $(DD)pdfimage32.dev $(DD)PCLm.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 diff --git a/psi/opextern.h b/psi/opextern.h index 543b1b3d8..f1c5b45df 100644 --- a/psi/opextern.h +++ b/psi/opextern.h @@ -148,6 +148,8 @@ int zcurrentdevice(i_ctx_t *); int ztoken(i_ctx_t *); int ztokenexec(i_ctx_t *); int zwrite(i_ctx_t *); +int zcurrentoutputdevice(i_ctx_t *i_ctx_p); +int zsetdevice_no_safer(i_ctx_t *i_ctx_p, gx_device *new_dev); int zspec_op(i_ctx_t *i_ctx_p); #endif /* opextern_INCLUDED */ diff --git a/psi/psapi.c b/psi/psapi.c new file mode 100644 index 000000000..90613e6a5 --- /dev/null +++ b/psi/psapi.c @@ -0,0 +1,512 @@ +/* Copyright (C) 2001-2018 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + + + +/* Private internal API to Ghostscript interpreter */ + +#include "string_.h" +#include "ierrors.h" +#include "gscdefs.h" +#include "gstypes.h" +#include "gdebug.h" +#include "psapi.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" +#include "ialloc.h" +#include "icstate.h" +#include "store.h" +#include "iname.h" +#include "interp.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 + + +#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. + */ +int +psapi_new_instance(gs_lib_ctx_t **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 + + mem = gs_malloc_init_with_context(*pinstance); + 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->core->caller_handle = caller_handle; + mem->gs_lib_ctx->core->custom_color_callback = NULL; +#ifdef METRO + mem->gs_lib_ctx->core->stdin_fn = metro_stdin; + mem->gs_lib_ctx->core->stdout_fn = metro_stdout; + mem->gs_lib_ctx->core->stderr_fn = metro_stderr; +#else + mem->gs_lib_ctx->core->stdin_fn = NULL; + mem->gs_lib_ctx->core->stdout_fn = NULL; + mem->gs_lib_ctx->core->stderr_fn = NULL; +#endif + mem->gs_lib_ctx->core->poll_fn = NULL; + + *pinstance = mem->gs_lib_ctx; + return psapi_set_arg_encoding(*pinstance, PS_ARG_ENCODING_LOCAL); +} + +/* Set an instance of Ghostscript to respond to UEL (universal + * exit language) strings in the input. */ +void +psapi_act_on_uel(gs_lib_ctx_t *ctx) +{ + ctx->core->act_on_uel = 1; +} + +/* Destroy an instance of Ghostscript */ +/* We do not support multiple instances, so make sure + * we use the default instance only once. + */ +void +psapi_delete_instance(gs_lib_ctx_t *ctx) +{ + gs_memory_t *mem; + gs_main_instance *minst; + + if (ctx == NULL) + return; + + mem = (gs_memory_t *)(ctx->memory); + minst = get_minst_from_memory(ctx->memory); + + ctx->core->caller_handle = NULL; + ctx->core->stdin_fn = NULL; + ctx->core->stdout_fn = NULL; + ctx->core->stderr_fn = NULL; + ctx->core->poll_fn = NULL; + minst->display = NULL; + + gs_free_object(mem, minst, "init_main_instance"); + + /* Release the memory (frees up everything) */ + gs_malloc_release(mem); + +#ifndef GS_THREADSAFE + --gsapi_instance_counter; +#endif +} + +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; +} + +/* Initialise the interpreter */ +int +psapi_set_arg_encoding(gs_lib_ctx_t *ctx, int encoding) +{ + if (ctx == NULL) + return gs_error_Fatal; + + if (encoding == PS_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 == PS_ARG_ENCODING_UTF8) { + gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), NULL); + return 0; + } + if (encoding == PS_ARG_ENCODING_UTF16LE) { + gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), utf16le_get_codepoint); + return 0; + } + return gs_error_Fatal; +} + +int +psapi_init_with_args(gs_lib_ctx_t *ctx, int argc, char **argv) +{ + if (ctx == NULL) + return gs_error_Fatal; + + return gs_main_init_with_args(get_minst_from_memory(ctx->memory), argc, argv); +} + +int +psapi_init_with_args01(gs_lib_ctx_t *ctx, int argc, char **argv) +{ + if (ctx == NULL) + return gs_error_Fatal; + + return gs_main_init_with_args01(get_minst_from_memory(ctx->memory), argc, argv); +} + +int +psapi_init_with_args2(gs_lib_ctx_t *ctx) +{ + if (ctx == NULL) + return gs_error_Fatal; + + return gs_main_init_with_args2(get_minst_from_memory(ctx->memory)); +} + +int +psapi_set_param(gs_lib_ctx_t *ctx, + psapi_sptype type, + const char *param, + const void *val) +{ + gs_main_instance *minst = get_minst_from_memory(ctx->memory); + ref value; + int code = 0; + i_ctx_t *i_ctx_p = minst->i_ctx_p; + uint space = icurrent_space; + + ialloc_set_space(idmemory, avm_system); + switch (type) { + case psapi_spt_null: + make_null(&value); + break; + case psapi_spt_bool: + if (val) + make_true(&value); + else + make_false(&value); + break; + case psapi_spt_int: + make_int(&value, *(int *)val); + break; + case psapi_spt_float: + make_real(&value, *(float *)val); + break; + case psapi_spt_string: + if (val == NULL) + make_empty_string(&value, a_readonly); + else { + size_t len = strlen(val); + byte *body = ialloc_string(len, "-s"); + + if (body == NULL) + return gs_error_Fatal; + memcpy(body, val, len); + make_const_string(&value, a_readonly | avm_system, len, body); + } + break; + case psapi_spt_name: + code = name_ref(ctx->memory, val, strlen(val), &value, 1); + break; + default: + break; + } + ialloc_set_space(idmemory, space); + /* Enter the name in systemdict. */ + i_initial_enter_name_copy(minst->i_ctx_p, param, &value); + + return code; +} + +/* 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 */ +int +psapi_run_string_begin(gs_lib_ctx_t *ctx, + int user_errors, + int *pexit_code) +{ + if (ctx == 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)); +} + +int +psapi_run_string_continue(gs_lib_ctx_t *ctx, + const char *str, + unsigned int length, + int user_errors, + int *pexit_code) +{ + if (ctx == 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)); +} + +uint +psapi_get_uel_offset(gs_lib_ctx_t *ctx) +{ + if (ctx == NULL) + return 0; + + return gs_main_get_uel_offset(get_minst_from_memory(ctx->memory)); +} + +int +psapi_run_string_end(gs_lib_ctx_t *ctx, + int user_errors, + int *pexit_code) +{ + if (ctx == 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)); +} + +int +psapi_run_string_with_length(gs_lib_ctx_t *ctx, + const char *str, + unsigned int length, + int user_errors, + int *pexit_code) +{ + if (ctx == 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)); +} + +int +psapi_run_string(gs_lib_ctx_t *ctx, + const char *str, + int user_errors, + int *pexit_code) +{ + if (ctx == NULL) + return gs_error_Fatal; + + return gs_main_run_string(get_minst_from_memory(ctx->memory), + str, user_errors, pexit_code, + &(get_minst_from_memory(ctx->memory)->error_object)); +} + +int +psapi_run_file(gs_lib_ctx_t *ctx, + const char *file_name, + int user_errors, + int *pexit_code) +{ + char *d, *temp; + const char *c = file_name; + char dummy[6]; + int rune, code, len; + gs_main_instance *minst; + if (ctx == NULL) + return gs_error_Fatal; + minst = get_minst_from_memory(ctx->memory); + + /* 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_file2(minst, temp, user_errors, pexit_code, + &(minst->error_object)); + if (temp != file_name) + gs_free_object(ctx->memory, temp, "gsapi_run_file"); + return code; +} + +/* Retrieve the memory allocator for the interpreter instance */ +gs_memory_t * +psapi_get_device_memory(gs_lib_ctx_t *ctx) +{ + if (ctx == NULL) + return NULL; + return gs_main_get_device_memory(get_minst_from_memory(ctx->memory)); +} + +/* Retrieve the memory allocator for the interpreter instance */ +int +psapi_set_device(gs_lib_ctx_t *ctx, gx_device *pdev) +{ + if (ctx == NULL) + return gs_error_Fatal; + return gs_main_set_device(get_minst_from_memory(ctx->memory), pdev); +} + +/* Exit the interpreter */ +int +psapi_exit(gs_lib_ctx_t *ctx) +{ + if (ctx == NULL) + return gs_error_Fatal; + + gs_to_exit(ctx->memory, 0); + return 0; +} + +int +psapi_force_geometry(gs_lib_ctx_t *ctx, const float *resolutions, const long *dimensions) +{ + int code; + + if (ctx == NULL) + return gs_error_Fatal; + code = gs_main_force_resolutions(get_minst_from_memory(ctx->memory), resolutions); + if (code < 0) + return code; + return gs_main_force_dimensions(get_minst_from_memory(ctx->memory), dimensions); +} diff --git a/psi/psapi.h b/psi/psapi.h new file mode 100644 index 000000000..d76fc33b4 --- /dev/null +++ b/psi/psapi.h @@ -0,0 +1,127 @@ +/* Copyright (C) 2001-2018 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + + + +/* + * Internal API for Ghostscript interpreter. + */ + +#ifndef psapi_INCLUDED +# define psapi_INCLUDED + +#include "gsmemory.h" +#include "gsdevice.h" + +int +psapi_set_arg_encoding(gs_lib_ctx_t *instance, + int encoding); + +/* Must match iapi.h and plapi.h */ +enum { + PS_ARG_ENCODING_LOCAL = 0, + PS_ARG_ENCODING_UTF8 = 1, + PS_ARG_ENCODING_UTF16LE = 2 +}; + +int +psapi_new_instance(gs_lib_ctx_t **pinstance, + void *caller_handle); + +void +psapi_delete_instance(gs_lib_ctx_t *instance); + +void +psapi_act_on_uel(gs_lib_ctx_t *instance); + +int +psapi_init_with_args(gs_lib_ctx_t *instance, + int argc, + char **argv); +int +psapi_init_with_args01(gs_lib_ctx_t *instance, + int argc, + char **argv); +int +psapi_init_with_args2(gs_lib_ctx_t *instance); + +typedef enum { + psapi_spt_invalid = -1, + psapi_spt_null = 0, /* void * is NULL */ + psapi_spt_bool = 1, /* void * is NULL (false) or non-NULL (true) */ + psapi_spt_int = 2, /* void * is a pointer to an int */ + psapi_spt_float = 3, /* void * is a float * */ + psapi_spt_name = 4, /* void * is a char * */ + psapi_spt_string = 5 /* void * is a char * */ +} psapi_sptype; +int +psapi_set_param(gs_lib_ctx_t *ctx, + psapi_sptype type, + const char *param, + const void *val); + +int +psapi_run_string_begin(gs_lib_ctx_t *instance, + int user_errors, + int *pexit_code); + +int +psapi_run_string_continue(gs_lib_ctx_t *instance, + const char *str, + unsigned int length, + int user_errors, + int *pexit_code); + +unsigned int +psapi_get_uel_offset(gs_lib_ctx_t *instance); + +int +psapi_run_string_end(gs_lib_ctx_t *instance, + int user_errors, + int *pexit_code); + +int +psapi_run_string_with_length(gs_lib_ctx_t *instance, + const char *str, + unsigned int length, + int user_errors, + int *pexit_code); + +int +psapi_run_string(gs_lib_ctx_t *ctx, + const char *str, + int user_errors, + int *pexit_code); + +int +psapi_run_file(gs_lib_ctx_t *instance, + const char *file_name, + int user_errors, + int *pexit_code); + +gs_memory_t * +psapi_get_device_memory(gs_lib_ctx_t *instance); + +int +psapi_set_device(gs_lib_ctx_t *instance, + gx_device *pdev); + +int +psapi_exit(gs_lib_ctx_t *instance); + +int +psapi_force_geometry(gs_lib_ctx_t *ctx, const float *resolutions, const long *dimensions); + +#endif /* psapi_INCLUDED */ diff --git a/psi/zdevice.c b/psi/zdevice.c index e0ce71892..65ae0811a 100644 --- a/psi/zdevice.c +++ b/psi/zdevice.c @@ -86,7 +86,7 @@ zcurrentdevice(i_ctx_t *i_ctx_p) the *device* object, rather than the dictionary describing the device and device state. */ -static int +int zcurrentoutputdevice(i_ctx_t *i_ctx_p) { os_ptr op = osp; @@ -503,6 +503,26 @@ zputdeviceparams(i_ctx_t *i_ctx_p) return 0; } +int +zsetdevice_no_safer(i_ctx_t *i_ctx_p, gx_device *new_dev) +{ + gx_device *dev = gs_currentdevice(igs); + int code; + + dev->ShowpageCount = 0; + + if (new_dev == NULL) + return gs_note_error(gs_error_undefined); + + code = gs_setdevice_no_erase(igs, new_dev); + if (code < 0) + return code; + + invalidate_stack_devices(i_ctx_p); + clear_pagedevice(istate); + return code; +} + /* <device> .setdevice <eraseflag> */ /* Note that .setdevice clears the current pagedevice. */ int @@ -538,15 +558,8 @@ zsetdevice(i_ctx_t *i_ctx_p) if(ndev != odev) /* don't allow a different device */ return_error(gs_error_invalidaccess); } - dev->ShowpageCount = 0; - - code = gs_setdevice_no_erase(igs, op->value.pdevice); - if (code < 0) - return code; - + code = zsetdevice_no_safer(i_ctx_p, op->value.pdevice); make_bool(op, code != 0); /* erase page if 1 */ - invalidate_stack_devices(i_ctx_p); - clear_pagedevice(istate); return code; } diff --git a/psi/zfont.c b/psi/zfont.c index f6c5ae113..0c6342ea1 100644 --- a/psi/zfont.c +++ b/psi/zfont.c @@ -67,7 +67,7 @@ zfont_init(i_ctx_t *i_ctx_p) 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, imemory->gs_lib_ctx->font_dir_root, + return gs_register_struct_root(imemory, &imemory->gs_lib_ctx->font_dir_root, (void **)&ifont_dir, "ifont_dir"); } diff --git a/psi/ziodevsc.c b/psi/ziodevsc.c index 99d379a04..20b4151fb 100644 --- a/psi/ziodevsc.c +++ b/psi/ziodevsc.c @@ -77,14 +77,14 @@ const gx_io_device gs_iodev_stderr = static int stdin_init(gx_io_device * iodev, gs_memory_t * mem) { - mem->gs_lib_ctx->stdin_is_interactive = true; + mem->gs_lib_ctx->core->stdin_is_interactive = true; return 0; } static void stdin_finit(gx_io_device * iodev, gs_memory_t * mem) { - mem->gs_lib_ctx->stdin_is_interactive = false; + mem->gs_lib_ctx->core->stdin_is_interactive = false; return; } @@ -97,19 +97,20 @@ s_stdin_read_process(stream_state * st, stream_cursor_read * ignore_pr, int wcount = (int)(pw->limit - pw->ptr); int count; gs_memory_t *mem = st->memory; + gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core; 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); + if (core->stdin_fn) + count = (*core->stdin_fn) + (core->caller_handle, (char *)pw->ptr + 1, + core->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); + core->stdin_is_interactive, + core->fstdin); pw->ptr += (count < 0) ? 0 : count; return ((count < 0) ? ERRC : (count == 0) ? EOFC : count); diff --git a/windows/ghostscript.vcproj b/windows/ghostscript.vcproj index 15f00cb62..dfbcb55fb 100644 --- a/windows/ghostscript.vcproj +++ b/windows/ghostscript.vcproj @@ -2321,6 +2321,10 @@ RelativePath="..\base\gstext.c" > </File> + <File + RelativePath="..\base\gstiffio.c" + > + </File> <File RelativePath="..\base\gstrap.c" > @@ -5693,6 +5697,10 @@ RelativePath="..\devices\gdevcfax.c" > </File> + <File + RelativePath="..\devices\gdevchameleon.c" + > + </File> <File RelativePath="..\devices\gdevcif.c" > @@ -8027,6 +8035,14 @@ RelativePath="..\psi\iutil2.c" > </File> + <File + RelativePath="..\psi\psapi.c" + > + </File> + <File + RelativePath="..\psi\psapi.h" + > + </File> <File RelativePath="..\psi\sfilter1.c" > diff --git a/xps/xpstop.c b/xps/xpstop.c index 7ca14e021..7c382d24c 100644 --- a/xps/xpstop.c +++ b/xps/xpstop.c @@ -59,8 +59,10 @@ static int xps_detect_language(const char *s, int len) { if (len < 2) - return 1; - return memcmp(s, "PK", 2); + return 0; + if (memcmp(s, "PK", 2) == 0) + return 100; + return 0; } static const pl_interp_characteristics_t * @@ -165,14 +167,33 @@ xps_imp_allocate_interp_instance(pl_interp_implementation_t *impl, return 0; } +/* Prepare interp instance for the next "job" */ static int -xps_imp_set_device(pl_interp_implementation_t *impl, gx_device *pdevice) +xps_imp_init_job(pl_interp_implementation_t *impl, + gx_device *pdevice) { xps_interp_instance_t *instance = impl->interp_client_data; xps_context_t *ctx = instance->ctx; gs_c_param_list list; int code; + if (gs_debug_c('|')) + xps_zip_trace = 1; + if (gs_debug_c('|')) + xps_doc_trace = 1; + + ctx->font_table = xps_hash_new(ctx); + ctx->colorspace_table = xps_hash_new(ctx); + + ctx->start_part = NULL; + + ctx->use_transparency = 1; + if (getenv("XPS_DISABLE_TRANSPARENCY")) + ctx->use_transparency = 0; + + ctx->opacity_only = 0; + ctx->fill_rule = 0; + code = gs_setdevice_no_erase(ctx->pgs, pdevice); if (code < 0) goto cleanup_setdevice; @@ -241,6 +262,13 @@ xps_imp_process_file(pl_interp_implementation_t *impl, char *filename) return code; } +/* Do any setup for parser per-cursor */ +static int /* ret 0 or +ve if ok, else -ve error code */ +xps_impl_process_begin(pl_interp_implementation_t * impl) +{ + return 0; +} + /* Parse a cursor-full of data */ static int xps_imp_process(pl_interp_implementation_t *impl, stream_cursor_read *cursor) @@ -273,6 +301,12 @@ xps_imp_process(pl_interp_implementation_t *impl, stream_cursor_read *cursor) return 0; } +static int /* ret 0 or +ve if ok, else -ve error code */ +xps_impl_process_end(pl_interp_implementation_t * impl) +{ + return 0; +} + /* Skip to end of job. * Return 1 if done, 0 ok but EOJ not found, else negative error code. */ @@ -320,33 +354,6 @@ xps_imp_report_errors(pl_interp_implementation_t *impl, return 0; } -/* Prepare interp instance for the next "job" */ -static int -xps_imp_init_job(pl_interp_implementation_t *impl) -{ - xps_interp_instance_t *instance = impl->interp_client_data; - xps_context_t *ctx = instance->ctx; - - if (gs_debug_c('|')) - xps_zip_trace = 1; - if (gs_debug_c('|')) - xps_doc_trace = 1; - - ctx->font_table = xps_hash_new(ctx); - ctx->colorspace_table = xps_hash_new(ctx); - - ctx->start_part = NULL; - - ctx->use_transparency = 1; - if (getenv("XPS_DISABLE_TRANSPARENCY")) - ctx->use_transparency = 0; - - ctx->opacity_only = 0; - ctx->fill_rule = 0; - - return 0; -} - static void xps_free_key_func(xps_context_t *ctx, void *ptr) { xps_free(ctx, ptr); @@ -363,7 +370,10 @@ xps_imp_dnit_job(pl_interp_implementation_t *impl) { xps_interp_instance_t *instance = impl->interp_client_data; xps_context_t *ctx = instance->ctx; - int i; + int i, code; + + /* return to original gstate */ + code = gs_grestore_only(ctx->pgs); /* destroys gs_save stack */ if (gs_debug_c('|')) xps_debug_fixdocseq(ctx); @@ -379,18 +389,7 @@ xps_imp_dnit_job(pl_interp_implementation_t *impl) xps_free_fixed_pages(ctx); xps_free_fixed_documents(ctx); - return 0; -} - -/* Remove a device from an interperter instance */ -static int -xps_imp_remove_device(pl_interp_implementation_t *impl) -{ - xps_interp_instance_t *instance = impl->interp_client_data; - xps_context_t *ctx = instance->ctx; - - /* return to original gstate */ - return gs_grestore_only(ctx->pgs); /* destroys gs_save stack */ + return code; } /* Deallocate a interpreter instance */ @@ -402,8 +401,15 @@ xps_imp_deallocate_interp_instance(pl_interp_implementation_t *impl) gs_memory_t *mem = ctx->memory; /* language clients don't free the font cache machinery */ + rc_decrement_cs(ctx->gray_lin, "xps_imp_deallocate_interp_instance"); + rc_decrement_cs(ctx->gray, "xps_imp_deallocate_interp_instance"); + rc_decrement_cs(ctx->cmyk, "xps_imp_deallocate_interp_instance"); + rc_decrement_cs(ctx->srgb, "xps_imp_deallocate_interp_instance"); + rc_decrement_cs(ctx->scrgb, "xps_imp_deallocate_interp_instance"); - // free gstate? + gs_gstate_free(ctx->pgs); + + gs_free_object(mem, ctx->fontdir, "xps_imp_deallocate_interp_instance"); gs_free_object(mem, ctx, "xps_imp_deallocate_interp_instance"); gs_free_object(mem, instance, "xps_imp_deallocate_interp_instance"); @@ -415,15 +421,18 @@ pl_interp_implementation_t xps_implementation = { xps_imp_characteristics, xps_imp_allocate_interp_instance, - xps_imp_set_device, + NULL, + NULL, + NULL, xps_imp_init_job, xps_imp_process_file, + xps_impl_process_begin, xps_imp_process, + xps_impl_process_end, xps_imp_flush_to_eoj, xps_imp_process_eof, xps_imp_report_errors, xps_imp_dnit_job, - xps_imp_remove_device, xps_imp_deallocate_interp_instance, NULL, }; |