/* Copyright (C) 1994, 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: gdevpsim.c,v 1.6.2.1.2.1 2003/01/17 00:49:01 giles Exp $ */ /* PostScript image output device */ #include "gdevprn.h" #include "gdevpsu.h" #include "stream.h" #include "strimpl.h" #include "sa85x.h" #include "srlx.h" /* * There are two drivers in this file, both of which produce PostScript * output consisting of a single bitmap per page. The psmono/psgray * driver produces monochrome Level 1 images using home-grown run length * compression; the psrgb driver produces planar RGB Level 2 images * using the RunLengthEncode filter. */ /* ---------------- Shared code ---------------- */ /* Define the device parameters. */ #ifndef X_DPI # define X_DPI 300 #endif #ifndef Y_DPI # define Y_DPI 300 #endif /* Write the file (if necessary) and page headers. */ private void ps_image_write_headers(FILE *f, gx_device_printer *pdev, const char *const setup[], gx_device_pswrite_common_t *pdpc) { if (gdev_prn_file_is_new(pdev)) { gs_rect bbox; bbox.p.x = 0; bbox.p.y = 0; bbox.q.x = pdev->width / pdev->HWResolution[0] * 72.0; bbox.q.y = pdev->height / pdev->HWResolution[1] * 72.0; psw_begin_file_header(f, (gx_device *)pdev, &bbox, pdpc, false); psw_print_lines(f, setup); psw_end_file_header(f); } { byte buf[100]; /* arbitrary */ stream s; swrite_file(&s, f, buf, sizeof(buf)); psw_write_page_header(&s, (gx_device *)pdev, pdpc, true, pdev->PageCount + 1); sflush(&s); } } /* ---------------- Level 1 monochrome driver ---------------- */ /* * This driver produces a bitmap in the form of a PostScript file that can * be fed to any PostScript printer. It uses a run-length compression * method that executes quickly (unlike some produced by PostScript * drivers!). * * There are two devices here, one for 1-bit black-and-white and one * for 8-bit gray. In fact, the same code could also handle 2- and * 4-bit gray output. */ /* The device descriptor */ private dev_proc_print_page(psmono_print_page); private dev_proc_close_device(psmono_close); const gx_device_printer gs_psmono_device = prn_device(prn_std_procs, "psmono", DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, 0, 0, 0, 0, /* margins */ 1, psmono_print_page); private const gx_device_procs psgray_procs = prn_color_procs(gdev_prn_open, gdev_prn_output_page, psmono_close, gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb); const gx_device_printer gs_psgray_device = { prn_device_body(gx_device_printer, psgray_procs, "psgray", DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, 0, 0, 0, 0, /* margins */ 1, 8, 255, 0, 256, 1, psmono_print_page) }; private const char *const psmono_setup[] = { /* Initialize the strings for filling runs. */ "/.ImageFills [ 0 1 255 {", " 256 string dup 0 1 7 { 3 index put dup } for { 8 16 32 64 128 } {", " 2 copy 0 exch getinterval putinterval dup", " } forall pop exch pop", "} bind for ] def", /* Initialize the procedure table for input dispatching. */ "/.ImageProcs [", /* Stack: */ " 32 { { pop .ImageItem } } repeat", " 16 { {", /* 0x20-0x2f: (N-0x20) data bytes follow */ " 32 sub 3 -1 roll add 3 index exch 0 exch getinterval 2 index exch", " readhexstring pop exch pop 0 exch dup", " } bind } repeat", " 16 { {", /* 0x30-0x3f: prefix hex digit (N-0x30) to next count */ " 48 sub 3 -1 roll add 4 bitshift exch .ImageItem", " } bind } repeat", " 32 { {", /* 0x40-0x5f: repeat last data byte (N-0x40) times */ " 64 sub 3 -1 roll add .ImageFills 2 index dup length 1 sub get get", " exch 0 exch getinterval 0 3 1 roll", " } bind } repeat", " 160 { { pop .ImageItem } } repeat", "] readonly def", /* Read one item from a compressed image. */ /* Stack contents: */ "/.ImageItem {", " 2 index read pop dup .ImageProcs exch get exec", "} bind def", /* Read and print an entire compressed image. */ "/.ImageRead {" /* .ImageRead - */ " gsave [", /* Stack: xres yres width height bpc -mark- */ " 6 -2 roll exch 72 div 0 0 4 -1 roll -72 div 0 7 index", /* Stack: width height bpc -mark- xres/72 0 0 -yres/72 0 height */ " ] { .ImageItem }", /* Stack: width height bpc */ " 4 index 3 index mul 7 add 8 idiv string currentfile 0 ()", /* Stack: width height bpc 0 () */ " 9 4 roll", /* Stack: 0 () width height bpc */ " image pop pop pop pop grestore", "} def", 0 }; static const gx_device_pswrite_common_t psmono_values = PSWRITE_COMMON_VALUES(1, 0 /*false*/, 1); #define data_run_code 0x20 #define xdigit_code 0x30 #define max_data_per_line 35 #define repeat_run_code 0x40 #define max_repeat_run_code 31 #define max_repeat_run 255 /* Send the page to the printer. */ private void write_data_run(P4(const byte *, int, FILE *, byte)); private int psmono_print_page(gx_device_printer * pdev, FILE * prn_stream) { int line_size = gdev_mem_bytes_per_scan_line((gx_device *) pdev); int lnum; byte *line = gs_alloc_bytes(pdev->memory, line_size, "psmono_print_page"); byte invert = (pdev->color_info.depth == 1 ? 0xff : 0); gx_device_pswrite_common_t pswrite_common; if (line == 0) return_error(gs_error_VMerror); pswrite_common = psmono_values; /* If this is the first page of the file, */ /* write the setup code. */ ps_image_write_headers(prn_stream, pdev, psmono_setup, &pswrite_common); /* Write the .ImageRead command. */ fprintf(prn_stream, "%g %g %d %d %d .ImageRead\n", pdev->HWResolution[0], pdev->HWResolution[1], pdev->width, pdev->height, pdev->color_info.depth); /* Compress each scan line in turn. */ for (lnum = 0; lnum < pdev->height; lnum++) { const byte *p; int left = line_size; byte *data; gdev_prn_get_bits(pdev, lnum, line, &data); p = data; /* Loop invariant: p + left = data + line_size. */ #define min_repeat_run 10 while (left >= min_repeat_run) { /* Detect a maximal run of non-repeated data. */ const byte *p1 = p; int left1 = left; byte b; int count, count_left; while (left1 >= min_repeat_run && ((b = *p1) != p1[1] || b != p1[2] || b != p1[3] || b != p1[4] || b != p1[5] || b != p1[6] || b != p1[7] || b != p1[8] || b != p1[9]) ) ++p1, --left1; if (left1 < min_repeat_run) break; /* no repeated data left */ write_data_run(p, (int)(p1 - p + 1), prn_stream, invert); /* Detect a maximal run of repeated data. */ p = ++p1 + (min_repeat_run - 1); left = --left1 - (min_repeat_run - 1); while (left > 0 && *p == b) ++p, --left; for (count = p - p1; count > 0; count -= count_left ) { count_left = min(count, max_repeat_run); if (count_left > max_repeat_run_code) fputc(xdigit_code + (count_left >> 4), prn_stream), fputc(repeat_run_code + (count_left & 0xf), prn_stream); else putc(repeat_run_code + count_left, prn_stream); } } /* Write the remaining data, if any. */ write_data_run(p, left, prn_stream, invert); } /* Clean up and return. */ fputs("\n", prn_stream); psw_write_page_trailer(prn_stream, 1, true); gs_free_object(pdev->memory, line, "psmono_print_page"); return 0; } /* Close the file. */ private int psmono_close(gx_device *dev) { psw_end_file(((gx_device_printer *)dev)->file, dev, &psmono_values, NULL, dev->PageCount); return gdev_prn_close(dev); } /* Write a run of data on the file. */ private void write_data_run(const byte * data, int count, FILE * f, byte invert) { const byte *p = data; const char *const hex_digits = "0123456789abcdef"; int left = count; char line[sizeof(count) * 2 + max_data_per_line * 2 + 3]; char *q = line; /* Write the count. */ if (!count) return; { int shift = sizeof(count) * 8; while ((shift -= 4) > 0 && (count >> shift) == 0); for (; shift > 0; shift -= 4) *q++ = xdigit_code + ((count >> shift) & 0xf); *q++ = data_run_code + (count & 0xf); } /* Write the data. */ while (left > 0) { register int wcount = min(left, max_data_per_line); left -= wcount; for (; wcount > 0; ++p, --wcount) { byte b = *p ^ invert; *q++ = hex_digits[b >> 4]; *q++ = hex_digits[b & 0xf]; } *q++ = '\n'; fwrite(line, 1, q - line, f); q = line; } } /* ---------------- Level 2 RGB driver ---------------- */ /* * This driver produces plane-separated, run-length-encoded, 24-bit RGB * images suitable for a PostScript Level 2 printer. LZW compression would * be better, but Unisys' claim to own the compression algorithm and their * demand for licensing and payment even for freely distributed software * rule this out. */ /* The device descriptor */ private dev_proc_print_page(psrgb_print_page); private dev_proc_close_device(psrgb_close); private const gx_device_procs psrgb_procs = prn_color_procs(gdev_prn_open, gdev_prn_output_page, psrgb_close, gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb); const gx_device_printer gs_psrgb_device = { prn_device_body(gx_device_printer, psrgb_procs, "psrgb", DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, 0, 0, 0, 0, /* margins */ 3, 24, 255, 255, 256, 256, psrgb_print_page) }; private const char *const psrgb_setup[] = { "/rgbimage {", /* rgbimage - */ " gsave 2 copy scale /h exch def /w exch def", " /s1 w string def /s2 w string def /s3 w string def", " /f currentfile /ASCII85Decode filter /RunLengthDecode filter def", " w h 8 [w 0 0 h neg 0 h]", " {f s1 readstring pop} {f s2 readstring pop} {f s3 readstring pop}", " true 3 colorimage grestore", "} bind def", 0 }; static const gx_device_pswrite_common_t psrgb_values = PSWRITE_COMMON_VALUES(2, 0 /*false*/, 1); /* Send the page to the printer. */ private int psrgb_print_page(gx_device_printer * pdev, FILE * prn_stream) { gs_memory_t *mem = pdev->memory; int width = pdev->width; byte *lbuf = gs_alloc_bytes(mem, width * 3, "psrgb_print_page(lbuf)"); int lnum; stream fs, a85s, rls; stream_A85E_state a85state; stream_RLE_state rlstate; byte fsbuf[200]; /* arbitrary, must be >2 */ byte a85sbuf[100]; /* arbitrary, must be >=6 */ byte rlsbuf[200]; /* arbitrary, must be >128 */ gx_device_pswrite_common_t pswrite_common; pswrite_common = psrgb_values; if (lbuf == 0) return_error(gs_error_VMerror); ps_image_write_headers(prn_stream, pdev, psrgb_setup, &pswrite_common); fprintf(prn_stream, "%d %d rgbimage\n", width, pdev->height); swrite_file(&fs, prn_stream, fsbuf, sizeof(fsbuf)); fs.memory = 0; if (s_A85E_template.set_defaults) (*s_A85E_template.set_defaults) ((stream_state *) & a85state); s_std_init(&a85s, a85sbuf, sizeof(a85sbuf), &s_filter_write_procs, s_mode_write); a85s.memory = 0; a85state.memory = 0; a85state.template = &s_A85E_template; (*s_A85E_template.init) ((stream_state *) & a85state); a85s.state = (stream_state *) & a85state; a85s.procs.process = s_A85E_template.process; a85s.strm = &fs; (*s_RLE_template.set_defaults) ((stream_state *) & rlstate); s_std_init(&rls, rlsbuf, sizeof(rlsbuf), &s_filter_write_procs, s_mode_write); rls.memory = 0; rlstate.memory = 0; rlstate.template = &s_RLE_template; (*s_RLE_template.init) ((stream_state *) & rlstate); rls.state = (stream_state *) & rlstate; rls.procs.process = s_RLE_template.process; rls.strm = &a85s; for (lnum = 0; lnum < pdev->height; ++lnum) { byte *data; int i, c; gdev_prn_get_bits(pdev, lnum, lbuf, &data); for (c = 0; c < 3; ++c) { const byte *p; for (i = 0, p = data + c; i < width; ++i, p += 3) sputc(&rls, *p); } } sclose(&rls); sclose(&a85s); sflush(&fs); fputs("\n", prn_stream); psw_write_page_trailer(prn_stream, 1, true); gs_free_object(mem, lbuf, "psrgb_print_page(lbuf)"); return 0; } /* Close the file. */ private int psrgb_close(gx_device *dev) { psw_end_file(((gx_device_printer *)dev)->file, dev, &psrgb_values, NULL, dev->PageCount); return gdev_prn_close(dev); }