diff options
Diffstat (limited to 'gs/base/gdevprn.c')
-rw-r--r-- | gs/base/gdevprn.c | 1324 |
1 files changed, 1324 insertions, 0 deletions
diff --git a/gs/base/gdevprn.c b/gs/base/gdevprn.c new file mode 100644 index 000000000..5106278eb --- /dev/null +++ b/gs/base/gdevprn.c @@ -0,0 +1,1324 @@ +/* Copyright (C) 2001-2006 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, modified + or distributed except as expressly authorized under the terms of that + license. Refer to licensing information at http://www.artifex.com/ + or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, + San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information. +*/ + +/* $Id$ */ +/* Generic printer driver support */ +#include "ctype_.h" +#include "gdevprn.h" +#include "gp.h" +#include "gsdevice.h" /* for gs_deviceinitialmatrix */ +#include "gsfname.h" +#include "gsparam.h" +#include "gxclio.h" +#include "gxgetbit.h" +#include "gdevplnx.h" +#include "gstrans.h" + +/*#define DEBUGGING_HACKS*/ + +/* GC information */ +#define PRINTER_IS_CLIST(pdev) ((pdev)->buffer_space != 0) +static +ENUM_PTRS_WITH(device_printer_enum_ptrs, gx_device_printer *pdev) + if (PRINTER_IS_CLIST(pdev)) + ENUM_PREFIX(st_device_clist, 0); + else + ENUM_PREFIX(st_device_forward, 0); +ENUM_PTRS_END +static +RELOC_PTRS_WITH(device_printer_reloc_ptrs, gx_device_printer *pdev) +{ + if (PRINTER_IS_CLIST(pdev)) + RELOC_PREFIX(st_device_clist); + else + RELOC_PREFIX(st_device_forward); +} RELOC_PTRS_END +public_st_device_printer(); + +/* ---------------- Standard device procedures ---------------- */ + +/* Define the standard printer procedure vector. */ +const gx_device_procs prn_std_procs = + prn_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close); + +/* Forward references */ +int gdev_prn_maybe_realloc_memory(gx_device_printer *pdev, + gdev_prn_space_params *old_space, + int old_width, int old_height, + bool old_page_uses_transparency); + +extern dev_proc_open_device(pattern_clist_open_device); + +/* ------ Open/close ------ */ + +/* Open a generic printer device. */ +/* Specific devices may wish to extend this. */ +int +gdev_prn_open(gx_device * pdev) +{ + gx_device_printer * const ppdev = (gx_device_printer *)pdev; + int code; + + ppdev->file = NULL; + code = gdev_prn_allocate_memory(pdev, NULL, 0, 0); + if (code < 0) + return code; + if (ppdev->OpenOutputFile) + code = gdev_prn_open_printer(pdev, 1); + return code; +} + +/* Generic closing for the printer device. */ +/* Specific devices may wish to extend this. */ +int +gdev_prn_close(gx_device * pdev) +{ + gx_device_printer * const ppdev = (gx_device_printer *)pdev; + int code = 0; + + gdev_prn_free_memory(pdev); + if (ppdev->file != NULL) { + code = gx_device_close_output_file(pdev, ppdev->fname, ppdev->file); + ppdev->file = NULL; + } + return code; +} + +static int /* returns 0 ok, else -ve error cde */ +gdev_prn_setup_as_command_list(gx_device *pdev, gs_memory_t *buffer_memory, + byte **the_memory, + const gdev_prn_space_params *space_params, + bool bufferSpace_is_exact) +{ + gx_device_printer * const ppdev = (gx_device_printer *)pdev; + uint space; + int code; + gx_device_clist *const pclist_dev = (gx_device_clist *)pdev; + gx_device_clist_common * const pcldev = &pclist_dev->common; + bool reallocate = *the_memory != 0; + byte *base; + + /* Try to allocate based simply on param-requested buffer size */ +#ifdef DEBUGGING_HACKS +#define BACKTRACE(first_arg)\ + BEGIN\ + ulong *fp_ = (ulong *)&first_arg - 2;\ + for (; fp_ && (fp_[1] & 0xff000000) == 0x08000000; fp_ = (ulong *)*fp_)\ + dprintf2(" fp=0x%lx ip=0x%lx\n", (ulong)fp_, fp_[1]);\ + END +dputs("alloc buffer:\n"); +BACKTRACE(pdev); +#endif /*DEBUGGING_HACKS*/ + for ( space = space_params->BufferSpace; ; ) { + base = (reallocate ? + (byte *)gs_resize_object(buffer_memory, *the_memory, space, + "cmd list buffer") : + gs_alloc_bytes(buffer_memory, space, + "cmd list buffer")); + if (base != 0) + break; + if (bufferSpace_is_exact || (space >>= 1) < PRN_MIN_BUFFER_SPACE) + break; + } + if (base == 0) + return_error(gs_error_VMerror); + *the_memory = base; + + /* Try opening the command list, to see if we allocated */ + /* enough buffer space. */ +open_c: + ppdev->buf = base; + ppdev->buffer_space = space; + clist_init_io_procs(pclist_dev, false); + clist_init_params(pclist_dev, base, space, pdev, + ppdev->printer_procs.buf_procs, + space_params->band, ppdev->is_async_renderer, + (ppdev->bandlist_memory == 0 ? pdev->memory->non_gc_memory: + ppdev->bandlist_memory), + ppdev->free_up_bandlist_memory, + ppdev->clist_disable_mask, + ppdev->page_uses_transparency); + code = (*gs_clist_device_procs.open_device)( (gx_device *)pcldev ); + if (code < 0) { + /* If there wasn't enough room, and we haven't */ + /* already shrunk the buffer, try enlarging it. */ + if ( code == gs_error_limitcheck && + space >= space_params->BufferSpace && + !bufferSpace_is_exact + ) { + space <<= 1; + if (reallocate) { + base = gs_resize_object(buffer_memory, + *the_memory, space, + "cmd list buf(retry open)"); + if (base != 0) + *the_memory = base; + } else { + gs_free_object(buffer_memory, base, + "cmd list buf(retry open)"); + *the_memory = base = + gs_alloc_bytes(buffer_memory, space, + "cmd list buf(retry open)"); + } + ppdev->buf = *the_memory; + if (base != 0) + goto open_c; + } + /* Failure. */ + if (!reallocate) { + gs_free_object(buffer_memory, base, "cmd list buf"); + ppdev->buffer_space = 0; + *the_memory = 0; + } + } + return code; +} + +static bool /* ret true if device was cmd list, else false */ +gdev_prn_tear_down(gx_device *pdev, byte **the_memory) +{ + gx_device_printer * const ppdev = (gx_device_printer *)pdev; + gx_device_memory * const pmemdev = (gx_device_memory *)pdev; + gx_device_clist *const pclist_dev = (gx_device_clist *)pdev; + gx_device_clist_common * const pcldev = &pclist_dev->common; + bool is_command_list; + + if (ppdev->buffer_space != 0) { + /* Close cmd list device & point to the storage */ + (*gs_clist_device_procs.close_device)( (gx_device *)pcldev ); + *the_memory = ppdev->buf; + ppdev->buf = 0; + ppdev->buffer_space = 0; + is_command_list = true; + + /* If the clist is a reader clist, free any band_complexity_array + * memory used by same. + */ + if (!CLIST_IS_WRITER(pclist_dev)) + gx_clist_reader_free_band_complexity_array(pclist_dev); + + } else { + /* point at the device bitmap, no need to close mem dev */ + *the_memory = pmemdev->base; + pmemdev->base = 0; + is_command_list = false; + } + + /* Reset device proc vector to default */ + if (ppdev->orig_procs.open_device != 0) + pdev->procs = ppdev->orig_procs; + ppdev->orig_procs.open_device = 0; /* prevent uninit'd restore of procs */ + + return is_command_list; +} + +static int +gdev_prn_allocate(gx_device *pdev, gdev_prn_space_params *new_space_params, + int new_width, int new_height, bool reallocate) +{ + gx_device_printer * const ppdev = (gx_device_printer *)pdev; + gx_device_memory * const pmemdev = (gx_device_memory *)pdev; + byte *the_memory = 0; + gdev_prn_space_params save_params; + int save_width = 0x0badf00d; /* Quiet compiler */ + int save_height = 0x0badf00d; /* Quiet compiler */ + bool is_command_list = false; /* Quiet compiler */ + bool save_is_command_list = false; /* Quiet compiler */ + bool size_ok = 0; + int ecode = 0; + int pass; + gs_memory_t *buffer_memory = + (ppdev->buffer_memory == 0 ? pdev->memory->non_gc_memory : + ppdev->buffer_memory); + + /* If reallocate, find allocated memory & tear down buffer device */ + if (reallocate) + save_is_command_list = gdev_prn_tear_down(pdev, &the_memory); + + /* Re/allocate memory */ + ppdev->orig_procs = pdev->procs; + for ( pass = 1; pass <= (reallocate ? 2 : 1); ++pass ) { + ulong mem_space; + ulong pdf14_trans_buffer_size = 0; + byte *base = 0; + bool bufferSpace_is_default = false; + gdev_prn_space_params space_params; + gx_device_buf_space_t buf_space; + + if (reallocate) + switch (pass) + { + case 1: + /* Setup device to get reallocated */ + save_params = ppdev->space_params; + ppdev->space_params = *new_space_params; + save_width = ppdev->width; + ppdev->width = new_width; + save_height = ppdev->height; + ppdev->height = new_height; + break; + case 2: /* only comes here if reallocate */ + /* Restore device to previous contents */ + ppdev->space_params = save_params; + ppdev->width = save_width; + ppdev->height = save_height; + break; + } + + /* Init clist/mem device-specific fields */ + memset(ppdev->skip, 0, sizeof(ppdev->skip)); + size_ok = ppdev->printer_procs.buf_procs.size_buf_device + (&buf_space, pdev, NULL, pdev->height, false) >= 0; + mem_space = buf_space.bits + buf_space.line_ptrs; + if (ppdev->page_uses_transparency) { + pdf14_trans_buffer_size = (ESTIMATED_PDF14_ROW_SPACE(max(1, new_width)) >> 3); + if (new_height < (max_ulong - mem_space) / pdf14_trans_buffer_size) { + pdf14_trans_buffer_size *= new_height; + mem_space += pdf14_trans_buffer_size; + } else { + size_ok = 0; + } + } + + /* Compute desired space params: never use the space_params as-is. */ + /* Rather, give the dev-specific driver a chance to adjust them. */ + space_params = ppdev->space_params; + space_params.BufferSpace = 0; + (*ppdev->printer_procs.get_space_params)(ppdev, &space_params); + if (ppdev->is_async_renderer && space_params.band.BandBufferSpace != 0) + space_params.BufferSpace = space_params.band.BandBufferSpace; + else if (space_params.BufferSpace == 0) { + if (space_params.band.BandBufferSpace > 0) + space_params.BufferSpace = space_params.band.BandBufferSpace; + else { + space_params.BufferSpace = ppdev->space_params.BufferSpace; + bufferSpace_is_default = true; + } + } + + /* Determine if we can use a full bitmap buffer, or have to use banding */ + if (pass > 1) + is_command_list = save_is_command_list; + else { + is_command_list = space_params.banding_type == BandingAlways || + mem_space >= space_params.MaxBitmap || + !size_ok; /* too big to allocate */ + } + if (!is_command_list) { + /* Try to allocate memory for full memory buffer */ + base = + (reallocate ? + (byte *)gs_resize_object(buffer_memory, the_memory, + (uint)mem_space, "printer buffer") : + gs_alloc_bytes(buffer_memory, (uint)mem_space, + "printer_buffer")); + if (base == 0) + is_command_list = true; + else + the_memory = base; + } + if (!is_command_list && pass == 1 && PRN_MIN_MEMORY_LEFT != 0 + && buffer_memory == pdev->memory->non_gc_memory) { + /* before using full memory buffer, ensure enough working mem left */ + byte * left = gs_alloc_bytes( buffer_memory, + PRN_MIN_MEMORY_LEFT, "printer mem left"); + if (left == 0) + is_command_list = true; + else + gs_free_object(buffer_memory, left, "printer mem left"); + } + + if (is_command_list) { + /* Buffer the image in a command list. */ + /* Release the buffer if we allocated it. */ + int code; + if (!reallocate) { + gs_free_object(buffer_memory, the_memory, + "printer buffer(open)"); + the_memory = 0; + } + if (space_params.banding_type == BandingNever) { + ecode = gs_note_error(gs_error_VMerror); + continue; + } + code = gdev_prn_setup_as_command_list(pdev, buffer_memory, + &the_memory, &space_params, + !bufferSpace_is_default); + if (ecode == 0) + ecode = code; + + if ( code >= 0 || (reallocate && pass > 1) ) + ppdev->procs = gs_clist_device_procs; + /* + * Now the device is a clist device, we enable multi-threaded rendering. + * It will remain enabled, but that doesn't really cause any problems. + */ + clist_enable_multi_thread_render(pdev); + } else { + /* Render entirely in memory. */ + gx_device *bdev = (gx_device *)pmemdev; + int code; + + ppdev->buffer_space = 0; + if ((code = gdev_create_buf_device + (ppdev->printer_procs.buf_procs.create_buf_device, + &bdev, pdev, 0, NULL, NULL, NULL)) < 0 || + (code = ppdev->printer_procs.buf_procs.setup_buf_device + (bdev, base, buf_space.raster, + (byte **)(base + buf_space.bits), 0, pdev->height, + pdev->height)) < 0 + ) { + /* Catastrophic. Shouldn't ever happen */ + gs_free_object(buffer_memory, base, "printer buffer"); + pdev->procs = ppdev->orig_procs; + ppdev->orig_procs.open_device = 0; /* prevent uninit'd restore of procs */ + return_error(code); + } + pmemdev->base = base; + } + if (ecode == 0) + break; + } + + if (ecode >= 0 || reallocate) { /* even if realloc failed */ + /* Synthesize the procedure vector. */ + /* Rendering operations come from the memory or clist device, */ + /* non-rendering come from the printer device. */ +#define COPY_PROC(p) set_dev_proc(ppdev, p, ppdev->orig_procs.p) + COPY_PROC(get_initial_matrix); + COPY_PROC(output_page); + COPY_PROC(close_device); + COPY_PROC(map_rgb_color); + COPY_PROC(map_color_rgb); + COPY_PROC(get_params); + COPY_PROC(put_params); + COPY_PROC(map_cmyk_color); + COPY_PROC(get_xfont_procs); + COPY_PROC(get_xfont_device); + COPY_PROC(map_rgb_alpha_color); + /* All printers are page devices, even if they didn't use the */ + /* standard macros for generating their procedure vectors. */ + set_dev_proc(ppdev, get_page_device, gx_page_device_get_page_device); + COPY_PROC(get_clipping_box); + COPY_PROC(map_color_rgb_alpha); + COPY_PROC(get_hardware_params); + COPY_PROC(get_color_mapping_procs); + COPY_PROC(get_color_comp_index); + COPY_PROC(encode_color); + COPY_PROC(decode_color); + COPY_PROC(update_spot_equivalent_colors); + COPY_PROC(ret_devn_params); +#undef COPY_PROC + /* If using a command list, already opened the device. */ + if (is_command_list) + return ecode; + else + return (*dev_proc(pdev, open_device))(pdev); + } else { + pdev->procs = ppdev->orig_procs; + ppdev->orig_procs.open_device = 0; /* prevent uninit'd restore of procs */ + return ecode; + } +} + +int +gdev_prn_allocate_memory(gx_device *pdev, + gdev_prn_space_params *new_space_params, + int new_width, int new_height) +{ + return gdev_prn_allocate(pdev, new_space_params, + new_width, new_height, false); +} + +int +gdev_prn_reallocate_memory(gx_device *pdev, + gdev_prn_space_params *new_space_params, + int new_width, int new_height) +{ + return gdev_prn_allocate(pdev, new_space_params, + new_width, new_height, true); +} + +int +gdev_prn_free_memory(gx_device *pdev) +{ + gx_device_printer * const ppdev = (gx_device_printer *)pdev; + byte *the_memory = 0; + gs_memory_t *buffer_memory = + (ppdev->buffer_memory == 0 ? pdev->memory->non_gc_memory : + ppdev->buffer_memory); + + gdev_prn_tear_down(pdev, &the_memory); + gs_free_object(buffer_memory, the_memory, "gdev_prn_free_memory"); + return 0; +} + +/* ------------- Stubs related only to async rendering ------- */ + +int /* rets 0 ok, -ve error if couldn't start thread */ +gx_default_start_render_thread(gdev_prn_start_render_params *params) +{ + return gs_error_unknownerror; +} + +/* Open the renderer's copy of a device. */ +/* This is overriden in gdevprna.c */ +int +gx_default_open_render_device(gx_device_printer *pdev) +{ + return gs_error_unknownerror; +} + +/* Close the renderer's copy of a device. */ +int +gx_default_close_render_device(gx_device_printer *pdev) +{ + return gdev_prn_close( (gx_device *)pdev ); +} + +/* ------ Get/put parameters ------ */ + +/* Get parameters. Printer devices add several more parameters */ +/* to the default set. */ +int +gdev_prn_get_params(gx_device * pdev, gs_param_list * plist) +{ + gx_device_printer * const ppdev = (gx_device_printer *)pdev; + int code = gx_default_get_params(pdev, plist); + gs_param_string ofns; + + if (code < 0 || + (code = param_write_long(plist, "MaxBitmap", &ppdev->space_params.MaxBitmap)) < 0 || + (code = param_write_long(plist, "BufferSpace", &ppdev->space_params.BufferSpace)) < 0 || + (code = param_write_int(plist, "BandWidth", &ppdev->space_params.band.BandWidth)) < 0 || + (code = param_write_int(plist, "BandHeight", &ppdev->space_params.band.BandHeight)) < 0 || + (code = param_write_long(plist, "BandBufferSpace", &ppdev->space_params.band.BandBufferSpace)) < 0 || + (code = param_write_int(plist, "NumRenderingThreads", &ppdev->num_render_threads_requested)) < 0 || + (code = param_write_bool(plist, "OpenOutputFile", &ppdev->OpenOutputFile)) < 0 || + (code = param_write_bool(plist, "ReopenPerPage", &ppdev->ReopenPerPage)) < 0 || + (code = param_write_bool(plist, "PageUsesTransparency", + &ppdev->page_uses_transparency)) < 0 || + (ppdev->Duplex_set >= 0 && + (code = (ppdev->Duplex_set ? + param_write_bool(plist, "Duplex", &ppdev->Duplex) : + param_write_null(plist, "Duplex"))) < 0) + ) + return code; + + ofns.data = (const byte *)ppdev->fname, + ofns.size = strlen(ppdev->fname), + ofns.persistent = false; + return param_write_string(plist, "OutputFile", &ofns); +} + +/* Validate an OutputFile name by checking any %-formats. */ +static int +validate_output_file(const gs_param_string * ofs) +{ + gs_parsed_file_name_t parsed; + const char *fmt; + + return gx_parse_output_file_name(&parsed, &fmt, (const char *)ofs->data, + ofs->size) >= 0; +} + +/* Put parameters. */ +int +gdev_prn_put_params(gx_device * pdev, gs_param_list * plist) +{ + gx_device_printer * const ppdev = (gx_device_printer *)pdev; + int ecode = 0; + int code; + const char *param_name; + bool is_open = pdev->is_open; + bool oof = ppdev->OpenOutputFile; + bool rpp = ppdev->ReopenPerPage; + bool page_uses_transparency = ppdev->page_uses_transparency; + bool old_page_uses_transparency = ppdev->page_uses_transparency; + bool duplex; + int duplex_set = -1; + int width = pdev->width; + int height = pdev->height; + int nthreads = ppdev->num_render_threads_requested; + gdev_prn_space_params sp, save_sp; + gs_param_string ofs; + gs_param_dict mdict; + + sp = ppdev->space_params; + save_sp = sp; + + switch (code = param_read_bool(plist, (param_name = "OpenOutputFile"), &oof)) { + default: + ecode = code; + param_signal_error(plist, param_name, ecode); + case 0: + case 1: + break; + } + + switch (code = param_read_bool(plist, (param_name = "ReopenPerPage"), &rpp)) { + default: + ecode = code; + param_signal_error(plist, param_name, ecode); + case 0: + case 1: + break; + } + + switch (code = param_read_bool(plist, (param_name = "PageUsesTransparency"), + &page_uses_transparency)) { + default: + ecode = code; + param_signal_error(plist, param_name, ecode); + case 0: + case 1: + break; + } + + if (ppdev->Duplex_set >= 0) /* i.e., Duplex is supported */ + switch (code = param_read_bool(plist, (param_name = "Duplex"), + &duplex)) { + case 0: + duplex_set = 1; + break; + default: + if ((code = param_read_null(plist, param_name)) == 0) { + duplex_set = 0; + break; + } + ecode = code; + param_signal_error(plist, param_name, ecode); + case 1: + ; + } +#define CHECK_PARAM_CASES(member, bad, label)\ + case 0:\ + if ((sp.params_are_read_only ? sp.member != save_sp.member : bad))\ + ecode = gs_error_rangecheck;\ + else\ + break;\ + goto label;\ + default:\ + ecode = code;\ +label:\ + param_signal_error(plist, param_name, ecode);\ + case 1:\ + break + + switch (code = param_read_long(plist, (param_name = "MaxBitmap"), &sp.MaxBitmap)) { + CHECK_PARAM_CASES(MaxBitmap, sp.MaxBitmap < 10000, mbe); + } + + switch (code = param_read_long(plist, (param_name = "BufferSpace"), &sp.BufferSpace)) { + CHECK_PARAM_CASES(BufferSpace, sp.BufferSpace < 10000, bse); + } + + switch (code = param_read_int(plist, (param_name = "BandWidth"), &sp.band.BandWidth)) { + CHECK_PARAM_CASES(band.BandWidth, sp.band.BandWidth < 0, bwe); + } + + switch (code = param_read_int(plist, (param_name = "BandHeight"), &sp.band.BandHeight)) { + CHECK_PARAM_CASES(band.BandHeight, sp.band.BandHeight < 0, bhe); + } + + switch (code = param_read_long(plist, (param_name = "BandBufferSpace"), &sp.band.BandBufferSpace)) { + CHECK_PARAM_CASES(band.BandBufferSpace, sp.band.BandBufferSpace < 0, bbse); + } + + switch (code = param_read_string(plist, (param_name = "OutputFile"), &ofs)) { + case 0: + if (pdev->LockSafetyParams && + bytes_compare(ofs.data, ofs.size, + (const byte *)ppdev->fname, strlen(ppdev->fname))) { + code = gs_error_invalidaccess; + } + else + code = validate_output_file(&ofs); + if (code >= 0) + break; + /* falls through */ + default: + ecode = code; + param_signal_error(plist, param_name, ecode); + case 1: + ofs.data = 0; + break; + } + + /* Read InputAttributes and OutputAttributes just for the type */ + /* check and to indicate that they aren't undefined. */ +#define read_media(pname)\ + switch ( code = param_begin_read_dict(plist, (param_name = pname), &mdict, true) )\ + {\ + case 0:\ + param_end_read_dict(plist, pname, &mdict);\ + break;\ + default:\ + ecode = code;\ + param_signal_error(plist, param_name, ecode);\ + case 1:\ + ;\ + } + + read_media("InputAttributes"); + read_media("OutputAttributes"); + + switch (code = param_read_int(plist, (param_name = "NumRenderingThreads"), &nthreads)) { + case 0: + break; + default: + ecode = code; + param_signal_error(plist, param_name, ecode); + case 1: + ; + } + + if (ecode < 0) + return ecode; + /* Prevent gx_default_put_params from closing the printer. */ + pdev->is_open = false; + code = gx_default_put_params(pdev, plist); + pdev->is_open = is_open; + if (code < 0) + return code; + + ppdev->OpenOutputFile = oof; + ppdev->ReopenPerPage = rpp; + ppdev->page_uses_transparency = page_uses_transparency; + if (duplex_set >= 0) { + ppdev->Duplex = duplex; + ppdev->Duplex_set = duplex_set; + } + ppdev->space_params = sp; + ppdev->num_render_threads_requested = nthreads; + + /* If necessary, free and reallocate the printer memory. */ + /* Formerly, would not reallocate if device is not open: */ + /* we had to patch this out (see News for 5.50). */ + code = gdev_prn_maybe_realloc_memory(ppdev, &save_sp, width, height, + old_page_uses_transparency); + if (code < 0) + return code; + + /* If filename changed, close file. */ + if (ofs.data != 0 && + bytes_compare(ofs.data, ofs.size, + (const byte *)ppdev->fname, strlen(ppdev->fname)) + ) { + /* Close the file if it's open. */ + if (ppdev->file != NULL) + gx_device_close_output_file(pdev, ppdev->fname, ppdev->file); + ppdev->file = NULL; + memcpy(ppdev->fname, ofs.data, ofs.size); + ppdev->fname[ofs.size] = 0; + } + /* If the device is open and OpenOutputFile is true, */ + /* open the OutputFile now. (If the device isn't open, */ + /* this will happen when it is opened.) */ + if (pdev->is_open && oof) { + code = gdev_prn_open_printer(pdev, 1); + if (code < 0) + return code; + } + return 0; +} + +/* ------ Others ------ */ + +/* Default routine to (not) override current space_params. */ +void +gx_default_get_space_params(const gx_device_printer *printer_dev, + gdev_prn_space_params *space_params) +{ + return; +} + +/* Generic routine to send the page to the printer. */ +int /* 0 ok, -ve error, or 1 if successfully upgraded to buffer_page */ +gdev_prn_output_page(gx_device * pdev, int num_copies, int flush) +{ + gx_device_printer * const ppdev = (gx_device_printer *)pdev; + int outcode = 0, closecode = 0, errcode = 0, endcode; + bool upgraded_copypage = false; + + if (num_copies > 0 || !flush) { + int code = gdev_prn_open_printer(pdev, 1); + + if (code < 0) + return code; + + /* If copypage request, try to do it using buffer_page */ + if ( !flush && + (*ppdev->printer_procs.buffer_page) + (ppdev, ppdev->file, num_copies) >= 0 + ) { + upgraded_copypage = true; + flush = true; + } + else if (num_copies > 0) + /* Print the accumulated page description. */ + outcode = + (*ppdev->printer_procs.print_page_copies)(ppdev, ppdev->file, + num_copies); + fflush(ppdev->file); + errcode = + (ferror(ppdev->file) ? gs_note_error(gs_error_ioerror) : 0); + if (!upgraded_copypage) + closecode = gdev_prn_close_printer(pdev); + } + endcode = (ppdev->buffer_space && !ppdev->is_async_renderer ? + clist_finish_page(pdev, flush) : 0); + + if (outcode < 0) + return outcode; + if (errcode < 0) + return errcode; + if (closecode < 0) + return closecode; + if (endcode < 0) + return endcode; + endcode = gx_finish_output_page(pdev, num_copies, flush); + return (endcode < 0 ? endcode : upgraded_copypage ? 1 : 0); +} + +/* Print a single copy of a page by calling print_page_copies. */ +int +gx_print_page_single_copy(gx_device_printer * pdev, FILE * prn_stream) +{ + return pdev->printer_procs.print_page_copies(pdev, prn_stream, 1); +} + +/* Print multiple copies of a page by calling print_page multiple times. */ +int +gx_default_print_page_copies(gx_device_printer * pdev, FILE * prn_stream, + int num_copies) +{ + int i = 1; + int code = 0; + + for (; i < num_copies; ++i) { + int errcode, closecode; + + code = (*pdev->printer_procs.print_page) (pdev, prn_stream); + if (code < 0) + return code; + /* + * Close and re-open the printer, to reset is_new and do the + * right thing if we're producing multiple output files. + * Code is mostly copied from gdev_prn_output_page. + */ + fflush(pdev->file); + errcode = + (ferror(pdev->file) ? gs_note_error(gs_error_ioerror) : 0); + closecode = gdev_prn_close_printer((gx_device *)pdev); + pdev->PageCount++; + code = (errcode < 0 ? errcode : closecode < 0 ? closecode : + gdev_prn_open_printer((gx_device *)pdev, true)); + if (code < 0) { + pdev->PageCount -= i; + return code; + } + prn_stream = pdev->file; + } + /* Print the last (or only) page. */ + pdev->PageCount -= num_copies - 1; + return (*pdev->printer_procs.print_page) (pdev, prn_stream); +} + +/* + * Buffer a (partial) rasterized page & optionally print result multiple times. + * The default implementation returns error, since the driver needs to override + * this (in procedure vector) in configurations where this call may occur. + */ +int +gx_default_buffer_page(gx_device_printer *pdev, FILE *prn_stream, + int num_copies) +{ + return gs_error_unknownerror; +} + +/* ---------------- Driver services ---------------- */ + +/* Initialize a rendering plane specification. */ +int +gx_render_plane_init(gx_render_plane_t *render_plane, const gx_device *dev, + int index) +{ + /* + * Eventually the computation of shift and depth from dev and index + * will be made device-dependent. + */ + int num_planes = dev->color_info.num_components; + int plane_depth = dev->color_info.depth / num_planes; + + if (index < 0 || index >= num_planes) + return_error(gs_error_rangecheck); + render_plane->index = index; + render_plane->depth = plane_depth; + render_plane->shift = plane_depth * (num_planes - 1 - index); + return 0; +} + +/* Clear trailing bits in the last byte of (a) scan line(s). */ +void +gdev_prn_clear_trailing_bits(byte *data, uint raster, int height, + const gx_device *dev) +{ + int first_bit = dev->width * dev->color_info.depth; + + if (first_bit & 7) + bits_fill_rectangle(data, first_bit, raster, mono_fill_make_pattern(0), + -first_bit & 7, height); +} + +/* Return the number of scan lines that should actually be passed */ +/* to the device. */ +int +gdev_prn_print_scan_lines(gx_device * pdev) +{ + int height = pdev->height; + gs_matrix imat; + float yscale; + int top, bottom, offset, end; + + (*dev_proc(pdev, get_initial_matrix)) (pdev, &imat); + yscale = imat.yy * 72.0; /* Y dpi, may be negative */ + top = (int)(dev_t_margin(pdev) * yscale); + bottom = (int)(dev_b_margin(pdev) * yscale); + offset = (int)(dev_y_offset(pdev) * yscale); + if (yscale < 0) { /* Y=0 is top of page */ + end = -offset + height + bottom; + } else { /* Y=0 is bottom of page */ + end = offset + height - top; + } + return min(height, end); +} + +/* Open the current page for printing. */ +int +gdev_prn_open_printer_seekable(gx_device *pdev, bool binary_mode, + bool seekable) +{ + gx_device_printer * const ppdev = (gx_device_printer *)pdev; + + if (ppdev->file != 0) { + ppdev->file_is_new = false; + return 0; + } + { + int code = gx_device_open_output_file(pdev, ppdev->fname, + binary_mode, seekable, + &ppdev->file); + if (code < 0) + return code; + } + ppdev->file_is_new = true; + return 0; +} +int +gdev_prn_open_printer(gx_device *pdev, bool binary_mode) +{ + return gdev_prn_open_printer_seekable(pdev, binary_mode, false); +} + +/* + * Test whether the printer's output file was just opened, i.e., whether + * this is the first page being written to this file. This is only valid + * at the entry to a driver's print_page procedure. + */ +bool +gdev_prn_file_is_new(const gx_device_printer *pdev) +{ + return pdev->file_is_new; +} + +/* Determine the colors used in a range of lines. */ +int +gx_page_info_colors_used(const gx_device *dev, + const gx_band_page_info_t *page_info, + int y, int height, + gx_colors_used_t *colors_used, int *range_start) +{ + int start, end, i; + int num_lines = page_info->scan_lines_per_colors_used; + gx_color_index or = 0; + bool slow_rop = false; + + if (y < 0 || height < 0 || height > dev->height - y) + return -1; + start = y / num_lines; + end = (y + height + num_lines - 1) / num_lines; + for (i = start; i < end; ++i) { + or |= page_info->band_colors_used[i].or; + slow_rop |= page_info->band_colors_used[i].slow_rop; + } + colors_used->or = or; + colors_used->slow_rop = slow_rop; + *range_start = start * num_lines; + return min(end * num_lines, dev->height) - *range_start; +} +int +gdev_prn_colors_used(gx_device *dev, int y, int height, + gx_colors_used_t *colors_used, int *range_start) +{ + gx_device_clist_writer *cldev; + + /* If this isn't a banded device, return default values. */ + if (dev_proc(dev, get_bits_rectangle) != + gs_clist_device_procs.get_bits_rectangle + ) { + *range_start = 0; + colors_used->or = ((gx_color_index)1 << dev->color_info.depth) - 1; + return dev->height; + } + cldev = (gx_device_clist_writer *)dev; + if (cldev->page_info.scan_lines_per_colors_used == 0) /* not set yet */ + clist_compute_colors_used(cldev); + return + gx_page_info_colors_used(dev, &cldev->page_info, + y, height, colors_used, range_start); +} + +/* + * Create the buffer device for a printer device. Clients should always + * call this, and never call the device procedure directly. + */ +int +gdev_create_buf_device(create_buf_device_proc_t cbd_proc, gx_device **pbdev, + gx_device *target, int y, + const gx_render_plane_t *render_plane, + gs_memory_t *mem, gx_band_complexity_t *band_complexity) +{ + int code = cbd_proc(pbdev, target, y, render_plane, mem, band_complexity); + + if (code < 0) + return code; + /* Retain this device -- it will be freed explicitly. */ + gx_device_retain(*pbdev, true); + return code; +} + +/* + * Create an ordinary memory device for page or band buffering, + * possibly preceded by a plane extraction device. + */ +int +gx_default_create_buf_device(gx_device **pbdev, gx_device *target, int y, + const gx_render_plane_t *render_plane, gs_memory_t *mem, gx_band_complexity_t *band_complexity) +{ + int plane_index = (render_plane ? render_plane->index : -1); + int depth; + const gx_device_memory *mdproto; + gx_device_memory *mdev; + gx_device *bdev; + + if (plane_index >= 0) + depth = render_plane->depth; + else + depth = target->color_info.depth; + mdproto = gdev_mem_device_for_bits(depth); + if (mdproto == 0) + return_error(gs_error_rangecheck); + if (mem) { + mdev = gs_alloc_struct(mem, gx_device_memory, &st_device_memory, + "create_buf_device"); + if (mdev == 0) + return_error(gs_error_VMerror); + } else { + mdev = (gx_device_memory *)*pbdev; + } + if (target == (gx_device *)mdev) { + /* The following is a special hack for setting up printer devices. */ + assign_dev_procs(mdev, mdproto); + check_device_separable((gx_device *)mdev); + gx_device_fill_in_procs((gx_device *)mdev); + } else + gs_make_mem_device(mdev, mdproto, mem, (band_complexity == NULL ? 1 : 0), + (target == (gx_device *)mdev ? NULL : target)); + mdev->width = target->width; + mdev->band_y = y; + /* + * The matrix in the memory device is irrelevant, + * because all we do with the device is call the device-level + * output procedures, but we may as well set it to + * something halfway reasonable. + */ + gs_deviceinitialmatrix(target, &mdev->initial_matrix); + if (plane_index >= 0) { + gx_device_plane_extract *edev = + gs_alloc_struct(mem, gx_device_plane_extract, + &st_device_plane_extract, "create_buf_device"); + + if (edev == 0) { + gx_default_destroy_buf_device((gx_device *)mdev); + return_error(gs_error_VMerror); + } + edev->memory = mem; + plane_device_init(edev, target, (gx_device *)mdev, render_plane, + false); + bdev = (gx_device *)edev; + } else + bdev = (gx_device *)mdev; + /****** QUESTIONABLE, BUT BETTER THAN OMITTING ******/ + if (&bdev->color_info != &target->color_info) /* Pacify Valgrind */ + bdev->color_info = target->color_info; + *pbdev = bdev; + return 0; +} + +/* Determine the space needed by the buffer device. */ +int +gx_default_size_buf_device(gx_device_buf_space_t *space, gx_device *target, + const gx_render_plane_t *render_plane, + int height, bool not_used) +{ + gx_device_memory mdev; + + space->line_ptrs = 0; /* */ + space->bits = 0; /* clear in case of failure */ + space->raster = 0; /* */ + mdev.color_info.depth = + (render_plane && render_plane->index >= 0 ? render_plane->depth : + target->color_info.depth); + mdev.width = target->width; + mdev.num_planes = 0; + if (gdev_mem_bits_size(&mdev, target->width, height, &(space->bits)) < 0) + return_error(gs_error_VMerror); + space->line_ptrs = gdev_mem_line_ptrs_size(&mdev, target->width, height); + space->raster = gdev_mem_raster(&mdev); + return 0; +} + +/* Set up the buffer device. */ +int +gx_default_setup_buf_device(gx_device *bdev, byte *buffer, int bytes_per_line, + byte **line_ptrs, int y, int setup_height, + int full_height) +{ + gx_device_memory *mdev = + (gs_device_is_memory(bdev) ? (gx_device_memory *)bdev : + (gx_device_memory *)(((gx_device_plane_extract *)bdev)->plane_dev)); + byte **ptrs = line_ptrs; + int raster = bytes_per_line; + int code; + + /****** HACK ******/ + if ((gx_device *)mdev == bdev && mdev->num_planes) + raster = bitmap_raster(mdev->planes[0].depth * mdev->width); + if (ptrs == 0) { + /* + * Before allocating a new line pointer array, if there is a previous + * array, free it to prevent leaks. + */ + if (mdev->line_ptrs != NULL) + gs_free_object(mdev->line_pointer_memory, mdev->line_ptrs, + "mem_close"); + /* + * Allocate line pointers now; free them when we close the device. + * Note that for multi-planar devices, we have to allocate using + * full_height rather than setup_height. + */ + ptrs = (byte **) + gs_alloc_byte_array(mdev->memory, + (mdev->num_planes ? + full_height * mdev->num_planes : + setup_height), + sizeof(byte *), "setup_buf_device"); + if (ptrs == 0) + return_error(gs_error_VMerror); + mdev->line_pointer_memory = mdev->memory; + mdev->foreign_line_pointers = false; + } + mdev->height = full_height; + code = gdev_mem_set_line_ptrs(mdev, buffer + raster * y, bytes_per_line, + ptrs, setup_height); + mdev->height = setup_height; + bdev->height = setup_height; /* do here in case mdev == bdev */ + return code; +} + +/* Destroy the buffer device. */ +void +gx_default_destroy_buf_device(gx_device *bdev) +{ + gx_device *mdev = bdev; + + if (!gs_device_is_memory(bdev)) { + /* bdev must be a plane extraction device. */ + mdev = ((gx_device_plane_extract *)bdev)->plane_dev; + gs_free_object(bdev->memory, bdev, "destroy_buf_device"); + } + dev_proc(mdev, close_device)(mdev); + gs_free_object(mdev->memory, mdev, "destroy_buf_device"); +} + +/* + * Copy one or more rasterized scan lines to a buffer, or return a pointer + * to them. See gdevprn.h for detailed specifications. + */ +int +gdev_prn_get_lines(gx_device_printer *pdev, int y, int height, + byte *buffer, uint bytes_per_line, + byte **actual_buffer, uint *actual_bytes_per_line, + const gx_render_plane_t *render_plane) +{ + int code; + gs_int_rect rect; + gs_get_bits_params_t params; + int plane; + + if (y < 0 || height < 0 || y + height > pdev->height) + return_error(gs_error_rangecheck); + rect.p.x = 0, rect.p.y = y; + rect.q.x = pdev->width, rect.q.y = y + height; + params.options = + GB_RETURN_POINTER | GB_ALIGN_STANDARD | GB_OFFSET_0 | + GB_RASTER_ANY | + /* No depth specified, we always use native colors. */ + GB_COLORS_NATIVE | GB_ALPHA_NONE; + if (render_plane) { + params.options |= GB_PACKING_PLANAR | GB_SELECT_PLANES; + memset(params.data, 0, + sizeof(params.data[0]) * pdev->color_info.num_components); + plane = render_plane->index; + params.data[plane] = buffer; + } else { + params.options |= GB_PACKING_CHUNKY; + params.data[0] = buffer; + plane = 0; + } + params.x_offset = 0; + params.raster = bytes_per_line; + code = dev_proc(pdev, get_bits_rectangle) + ((gx_device *)pdev, &rect, ¶ms, NULL); + if (code < 0 && actual_buffer) { + /* + * RETURN_POINTER might not be implemented for this + * combination of parameters: try RETURN_COPY. + */ + params.options &= ~(GB_RETURN_POINTER | GB_RASTER_ALL); + params.options |= GB_RETURN_COPY | GB_RASTER_SPECIFIED; + code = dev_proc(pdev, get_bits_rectangle) + ((gx_device *)pdev, &rect, ¶ms, NULL); + } + if (code < 0) + return code; + if (actual_buffer) + *actual_buffer = params.data[plane]; + if (actual_bytes_per_line) + *actual_bytes_per_line = params.raster; + return code; +} + + +/* Copy a scan line from the buffer to the printer. */ +int +gdev_prn_get_bits(gx_device_printer * pdev, int y, byte * str, byte ** actual_data) +{ + int code = (*dev_proc(pdev, get_bits)) ((gx_device *) pdev, y, str, actual_data); + uint line_size = gdev_prn_raster(pdev); + int last_bits = -(pdev->width * pdev->color_info.depth) & 7; + + if (code < 0) + return code; + if (last_bits != 0) { + byte *dest = (actual_data != 0 ? *actual_data : str); + + dest[line_size - 1] &= 0xff << last_bits; + } + return 0; +} +/* Copy scan lines to a buffer. Return the number of scan lines, */ +/* or <0 if error. This procedure is DEPRECATED. */ +int +gdev_prn_copy_scan_lines(gx_device_printer * pdev, int y, byte * str, uint size) +{ + uint line_size = gdev_prn_raster(pdev); + int count = size / line_size; + int i; + byte *dest = str; + + count = min(count, pdev->height - y); + for (i = 0; i < count; i++, dest += line_size) { + int code = gdev_prn_get_bits(pdev, y + i, dest, NULL); + + if (code < 0) + return code; + } + return count; +} + +/* Close the current page. */ +int +gdev_prn_close_printer(gx_device * pdev) +{ + gx_device_printer * const ppdev = (gx_device_printer *)pdev; + gs_parsed_file_name_t parsed; + const char *fmt; + int code = gx_parse_output_file_name(&parsed, &fmt, ppdev->fname, + strlen(ppdev->fname)); + + if ((code >= 0 && fmt) /* file per page */ || + ppdev->ReopenPerPage /* close and reopen for each page */ + ) { + gx_device_close_output_file(pdev, ppdev->fname, ppdev->file); + ppdev->file = NULL; + } + return 0; +} + +/* If necessary, free and reallocate the printer memory after changing params */ +int +gdev_prn_maybe_realloc_memory(gx_device_printer *prdev, + gdev_prn_space_params *old_sp, + int old_width, int old_height, + bool old_page_uses_transparency) +{ + int code = 0; + gx_device *const pdev = (gx_device *)prdev; + /*gx_device_memory * const mdev = (gx_device_memory *)prdev;*/ + + /* + * The first test was changed to mdev->base != 0 in 5.50 (per Artifex). + * Not only was this test wrong logically, it was incorrect in that + * casting pdev to a (gx_device_memory *) is only meaningful if banding + * is not being used. The test was changed back to prdev->is_open in + * 5.67 (also per Artifex). For more information, see the News items + * for these filesets. + */ + if (prdev->is_open && + (memcmp(&prdev->space_params, old_sp, sizeof(*old_sp)) != 0 || + prdev->width != old_width || prdev->height != old_height || + prdev->page_uses_transparency != old_page_uses_transparency) + ) { + int new_width = prdev->width; + int new_height = prdev->height; + gdev_prn_space_params new_sp; + +#ifdef DEBUGGING_HACKS +debug_dump_bytes((const byte *)old_sp, (const byte *)(old_sp + 1), "old"); +debug_dump_bytes((const byte *)&prdev->space_params, + (const byte *)(&prdev->space_params + 1), "new"); +dprintf4("w=%d/%d, h=%d/%d\n", old_width, new_width, old_height, new_height); +#endif /*DEBUGGING_HACKS*/ + new_sp = prdev->space_params; + prdev->width = old_width; + prdev->height = old_height; + prdev->space_params = *old_sp; + code = gdev_prn_reallocate_memory(pdev, &new_sp, + new_width, new_height); + /* If this fails, device should be usable w/old params, but */ + /* band files may not be open. */ + } + return code; +} |