xref: /plan9/sys/src/cmd/gs/src/gdevatx.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
1 /* Copyright (C) 2000 Aladdin Enterprises.  All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2 of the License, or
6    (at your option) any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 U.S.A.
16 
17    This program may also be distributed as part of AFPL Ghostscript,
18    under the terms of the Aladdin Free Public License (the "License").
19 
20    AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
21    or distributor accepts any responsibility for the consequences of using
22    it, or for whether it serves any particular purpose or works at all,
23    unless he or she says so in writing.  Refer to the Aladdin Free Public
24    License (the "License") for full details.
25 
26    Every copy of AFPL Ghostscript must include a copy of the License,
27    normally in a plain ASCII text file named PUBLIC.  The License grants you
28    the right to copy, modify and redistribute AFPL Ghostscript, but only
29    under certain conditions described in the License.  Among other things, the
30    License requires that the copyright notice and this notice be preserved on
31    all copies.
32 */
33 
34 /*$Id: gdevatx.c,v 1.6 2000/09/19 19:00:11 lpd Exp $ */
35 /* Practical Automation ATX-23, -24, and -38 driver */
36 #include "math_.h"
37 #include "gdevprn.h"
38 
39 /*
40  * All of the ATX printers have an unprintable margin of 0.125" at the top
41  * and bottom of the page.  They also have unprintable left/right margins:
42  *	ATX-23	0.25"
43  *	ATX-24	0.193"
44  *	ATS-38	0.25"
45  * The code below assumes that coordinates refer only to the *printable*
46  * part of each page.  This is wrong and must eventually be changed.
47  */
48 
49 /* Define the printer commands. */
50 #define ATX_SET_PAGE_LENGTH "\033f"  /* + 2-byte length */
51 #define ATX_VERTICAL_TAB "\033L"  /* + 2-byte count */
52 #define ATX_UNCOMPRESSED_DATA "\033d"  /* + 2-byte count */
53 #define ATX_COMPRESSED_DATA "\033x"  /* + 1-byte word count */
54 #define ATX_END_PAGE "\033e"
55 
56 /* The device descriptors */
57 private dev_proc_print_page(atx23_print_page);
58 private dev_proc_print_page(atx24_print_page);
59 private dev_proc_print_page(atx38_print_page);
60 
61 #define ATX_DEVICE(dname, w10, h10, dpi, lrm, btm, print_page)\
62   prn_device_margins(prn_std_procs, dname, w10, h10, dpi, dpi, 0, 0,\
63 		     lrm, btm, lrm, btm, 1, print_page)
64 
65 const gx_device_printer gs_atx23_device = /* real width = 576 pixels */
66 ATX_DEVICE("atx23", 28 /* 2.84" */, 35 /* (minimum) */,
67 	   203, 0.25, 0.125, atx23_print_page);
68 
69 const gx_device_printer gs_atx24_device = /* real width = 832 pixels */
70 ATX_DEVICE("atx24", 41 /* 4.1" */, 35 /* (minimum) */,
71 	   203, 0.193, 0.125, atx24_print_page);
72 
73 const gx_device_printer gs_atx38_device = /* real width = 2400 pixels */
74 ATX_DEVICE("atx38", 80 /* 8.0" */, 35 /* (minimum) */,
75 	   300, 0.25, 0.125, atx38_print_page);
76 
77 /* Output a printer command with a 2-byte, little-endian numeric argument. */
78 private void
79 fput_atx_command(FILE *f, const char *str, int value)
80 {
81     fputs(str, f);
82     fputc((byte)value, f);
83     fputc((byte)(value >> 8), f);
84 }
85 
86 /*
87  * Attempt to compress a scan line of data.  in_size and out_size are even.
88  * Return -1 if the compressed data would exceed out_size, otherwise the
89  * size of the compressed data (always even).
90  */
91 #define MIN_IN_SIZE_TO_COMPRESS 50
92 #define MAX_COMPRESSED_SEGMENT_PAIRS 127
93 #define MAX_UNCOMPRESSED_SEGMENT_PAIRS 255
94 #define COMPRESSED_SEGMENT_COMMAND 0x80	/* + # of repeated pairs */
95 #define UNCOMPRESSED_SEGMENT_COMMAND 0x7f /* followed by # of pairs */
96 private int
97 atx_compress(const byte *in_buf, int in_size, byte *out_buf, int out_size)
98 {
99     const byte *const in_end = in_buf + in_size;
100     byte *const out_end = out_buf + out_size;
101     const byte *in = in_buf;
102     byte *out = out_buf;
103     byte *out_command;
104     int pair_count;
105 
106     if (in_size < MIN_IN_SIZE_TO_COMPRESS)
107 	return -1;			/* not worth compressing */
108 
109     /* Start a new segment. */
110  New_Segment:
111     if (in == in_end)		/* end of input data */
112 	return out - out_buf;
113     if (out == out_end)		/* output buffer full */
114 	return -1;
115     out_command = out;
116     out += 2;
117     if (in[1] == in[0]) {		/* start compressed segment */
118 	/* out[-2] will be compressed segment command */
119 	out[-1] = in[0];
120 	pair_count = 1;
121 	goto Scan_Compressed_Pair;
122     } else {			/* start uncompressed segment */
123 	out[-2] = UNCOMPRESSED_SEGMENT_COMMAND;
124 	/* out[-1] will be pair count */
125 	pair_count = 0;
126 	goto Scan_Uncompressed_Pair;
127     }
128 
129     /* Scan compressed data. */
130  Scan_Compressed:
131     if (pair_count == MAX_COMPRESSED_SEGMENT_PAIRS ||
132 	in == in_end || in[0] != in[-1] || in[1] != in[0]
133 	) {			/* end the segment */
134 	out_command[0] = COMPRESSED_SEGMENT_COMMAND + pair_count;
135 	goto New_Segment;
136     }
137     ++pair_count;
138  Scan_Compressed_Pair:
139     in += 2;
140     goto Scan_Compressed;
141 
142     /* Scan uncompressed data. */
143  Scan_Uncompressed:
144     if (pair_count == MAX_UNCOMPRESSED_SEGMENT_PAIRS ||
145 	in == in_end || in[1] == in[0]
146 	) {			/* end the segment */
147 	out_command[1] = pair_count;
148 	goto New_Segment;
149     }
150  Scan_Uncompressed_Pair:
151     if (out == out_end)		/* output buffer full */
152 	return -1;
153     out[0] = in[0], out[1] = in[1];
154     in += 2;
155     out += 2;
156     ++pair_count;
157     goto Scan_Uncompressed;
158 
159 }
160 
161 /* Send the page to the printer. */
162 private int
163 atx_print_page(gx_device_printer *pdev, FILE *f, int max_width_bytes)
164 {
165     /*
166      * The page length command uses 16 bits to represent the length in
167      * units of 0.01", so the maximum representable page length is
168      * 655.35", including the unprintable top and bottom margins.
169      * Compute the maximum height of the printable area in pixels.
170      */
171     float top_bottom_skip = (pdev->HWMargins[1] + pdev->HWMargins[3]) / 72.0;
172     int max_height = (int)(pdev->HWResolution[1] * 655 - top_bottom_skip);
173     int height = min(pdev->height, max_height);
174     int page_length_100ths =
175 	(int)ceil((height / pdev->HWResolution[1] + top_bottom_skip) * 100);
176     gs_memory_t *mem = pdev->memory;
177     int raster = gx_device_raster((gx_device *)pdev, true);
178     byte *buf;
179     /*
180      * ATX_COMPRESSED_DATA only takes a 1-byte (word) count.
181      * Thus no compressed scan line can take more than 510 bytes.
182      */
183     int compressed_raster = min(raster / 2, 510); /* require 50% compression */
184     byte *compressed;
185     int blank_lines, lnum;
186     int code = 0;
187 
188     /* Enforce a minimum 3" page length. */
189     if (page_length_100ths < 300)
190 	page_length_100ths = 300;
191     buf = gs_alloc_bytes(mem, raster, "atx_print_page(buf)");
192     compressed = gs_alloc_bytes(mem, compressed_raster,
193 				"atx_print_page(compressed)");
194     if (buf == 0 || compressed == 0) {
195 	code = gs_note_error(gs_error_VMerror);
196 	goto done;
197     }
198     fput_atx_command(f, ATX_SET_PAGE_LENGTH, page_length_100ths);
199     for (blank_lines = 0, lnum = 0; lnum < height; ++lnum) {
200 	byte *row;
201 	byte *end;
202 	int count;
203 
204 	gdev_prn_get_bits(pdev, lnum, buf, &row);
205 	/* Find the end of the non-blank data. */
206 	for (end = row + raster; end > row && end[-1] == 0 && end[-2] == 0; )
207 	    end -= 2;
208 	if (end == row) {		/* blank line */
209 	    ++blank_lines;
210 	    continue;
211 	}
212 	if (blank_lines) {		/* skip vertically */
213 	    fput_atx_command(f, ATX_VERTICAL_TAB, blank_lines + 1);
214 	    blank_lines = 0;
215 	}
216 	/* Truncate the line to the maximum printable width. */
217 	if (end - row > max_width_bytes)
218 	    end = row + max_width_bytes;
219 	count = atx_compress(row, end - row, compressed, compressed_raster);
220 	if (count >= 0) {		/* compressed line */
221 	    /*
222 	     * Note that since compressed_raster can't exceed 510, count
223 	     * can't exceed 510 either.
224 	     */
225 	    fputs(ATX_COMPRESSED_DATA, f);
226 	    fputc(count / 2, f);
227 	    fwrite(compressed, 1, count, f);
228 	} else {			/* uncompressed line */
229 	    int num_bytes = end - row;
230 
231 	    fput_atx_command(f, ATX_UNCOMPRESSED_DATA, num_bytes);
232 	    fwrite(row, 1, num_bytes, f);
233 	}
234     }
235 
236 #if 0	/**************** MAY NOT BE NEEDED ****************/
237     /* Enforce the minimum page length, and skip any final blank lines. */
238     {
239 	int paper_length = (int)(pdev->HWResolution[1] * 3 + 0.5);
240 	int printed_length = height - blank_lines;
241 
242 	if (height > paper_length)
243 	    paper_length = height;
244 	if (printed_length < paper_length)
245 	    fput_atx_command(f, ATX_VERTICAL_TAB,
246 			     paper_length - printed_length + 1);
247     }
248 #endif
249 
250     /* End the page. */
251     fputs(ATX_END_PAGE, f);
252 
253  done:
254     gs_free_object(mem, compressed, "atx_print_page(compressed)");
255     gs_free_object(mem, buf, "atx_print_page(buf)");
256     return code;
257 }
258 
259 /* Print pages with specified maximum pixel widths. */
260 private int
261 atx23_print_page(gx_device_printer *pdev, FILE *f)
262 {
263     return atx_print_page(pdev, f, 576 / 8);
264 }
265 private int
266 atx24_print_page(gx_device_printer *pdev, FILE *f)
267 {
268     return atx_print_page(pdev, f, 832 / 8);
269 }
270 private int
271 atx38_print_page(gx_device_printer *pdev, FILE *f)
272 {
273     return atx_print_page(pdev, f, 2400 / 8);
274 }
275