/* Copyright (C) 1990, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevprn.c,v 1.5.6.2.2.1 2003/01/17 00:49:01 giles Exp $ */ /* 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" /*#define DEBUGGING_HACKS*/ /* GC information */ #define PRINTER_IS_CLIST(pdev) ((pdev)->buffer_space != 0) private 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 private 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(P4(gx_device_printer *pdev, gdev_prn_space_params *old_space, int old_width, int old_height)); /* ------ 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; } private 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_params(pclist_dev, base, space, pdev, ppdev->printer_procs.buf_procs, space_params->band, ppdev->is_async_renderer, (ppdev->bandlist_memory == 0 ? &gs_memory_default : ppdev->bandlist_memory), ppdev->free_up_bandlist_memory, ppdev->clist_disable_mask); 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; } private 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; } 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; } private 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, save_height; bool is_command_list; bool save_is_command_list; int ecode = 0; int pass; gs_memory_t *buffer_memory = (ppdev->buffer_memory == 0 ? &gs_memory_default : 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; 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)); ppdev->printer_procs.buf_procs.size_buf_device (&buf_space, pdev, NULL, pdev->height, false); mem_space = buf_space.bits + buf_space.line_ptrs; /* 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 || mem_space != (uint)mem_space; /* 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 == &gs_memory_default) { /* 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; } 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, NULL, NULL, false)) < 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); #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 ? &gs_memory_default : 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_bool(plist, "OpenOutputFile", &ppdev->OpenOutputFile)) < 0 || (code = param_write_bool(plist, "ReopenPerPage", &ppdev->ReopenPerPage)) < 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. */ private 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 duplex; int duplex_set = -1; int width = pdev->width; int height = pdev->height; 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; } 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) 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"); 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; if (duplex_set >= 0) { ppdev->Duplex = duplex; ppdev->Duplex_set = duplex_set; } ppdev->space_params = sp; /* 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); 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, const gx_render_plane_t *render_plane, gs_memory_t *mem, bool for_band) { int code = cbd_proc(pbdev, target, render_plane, mem, for_band); 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, const gx_render_plane_t *render_plane, gs_memory_t *mem, bool for_band) { 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); } else gs_make_mem_device(mdev, mdproto, mem, (for_band ? 1 : 0), (target == (gx_device *)mdev ? NULL : target)); mdev->width = target->width; /* * 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 ******/ 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 for_band) { gx_device_memory mdev; 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; space->bits = gdev_mem_bits_size(&mdev, target->width, height); 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) { /* * 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) { 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 ) ) { 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; }