2 |
- |
1 |
/* Copyright (C) 2000 Aladdin Enterprises. All rights reserved.
|
|
|
2 |
|
|
|
3 |
This software is provided AS-IS with no warranty, either express or
|
|
|
4 |
implied.
|
|
|
5 |
|
|
|
6 |
This software is distributed under license and may not be copied,
|
|
|
7 |
modified or distributed except as expressly authorized under the terms
|
|
|
8 |
of the license contained in the file LICENSE in this distribution.
|
|
|
9 |
|
|
|
10 |
For more information about licensing, please refer to
|
|
|
11 |
http://www.ghostscript.com/licensing/. For information on
|
|
|
12 |
commercial licensing, go to http://www.artifex.com/licensing/ or
|
|
|
13 |
contact Artifex Software, Inc., 101 Lucas Valley Road #110,
|
|
|
14 |
San Rafael, CA 94903, U.S.A., +1(415)492-9861.
|
|
|
15 |
*/
|
|
|
16 |
|
|
|
17 |
/* $Id: gdevatx.c,v 1.9 2004/01/29 18:19:41 ray Exp $ */
|
|
|
18 |
/* Practical Automation ATX-23, -24, and -38 driver */
|
|
|
19 |
#include "math_.h"
|
|
|
20 |
#include "gdevprn.h"
|
|
|
21 |
|
|
|
22 |
/*
|
|
|
23 |
* All of the ATX printers have an unprintable margin of 0.125" at the top
|
|
|
24 |
* and bottom of the page. They also have unprintable left/right margins:
|
|
|
25 |
* ATX-23 0.25"
|
|
|
26 |
* ATX-24 0.193"
|
|
|
27 |
* ATS-38 0.25"
|
|
|
28 |
* The code below assumes that coordinates refer only to the *printable*
|
|
|
29 |
* part of each page. This is wrong and must eventually be changed.
|
|
|
30 |
*/
|
|
|
31 |
|
|
|
32 |
/* Define the printer commands. */
|
|
|
33 |
#define ATX_SET_PAGE_LENGTH "\033f" /* + 2-byte length */
|
|
|
34 |
#define ATX_VERTICAL_TAB "\033L" /* + 2-byte count */
|
|
|
35 |
#define ATX_UNCOMPRESSED_DATA "\033d" /* + 2-byte count */
|
|
|
36 |
#define ATX_COMPRESSED_DATA "\033x" /* + 1-byte word count */
|
|
|
37 |
#define ATX_END_PAGE "\033e"
|
|
|
38 |
|
|
|
39 |
/* The device descriptors */
|
|
|
40 |
private dev_proc_print_page(atx23_print_page);
|
|
|
41 |
private dev_proc_print_page(atx24_print_page);
|
|
|
42 |
private dev_proc_print_page(atx38_print_page);
|
|
|
43 |
|
|
|
44 |
#define ATX_DEVICE(dname, w10, h10, dpi, lrm, btm, print_page)\
|
|
|
45 |
prn_device_margins(prn_std_procs, dname, w10, h10, dpi, dpi, 0, 0,\
|
|
|
46 |
lrm, btm, lrm, btm, 1, print_page)
|
|
|
47 |
|
|
|
48 |
const gx_device_printer gs_atx23_device = /* real width = 576 pixels */
|
|
|
49 |
ATX_DEVICE("atx23", 28 /* 2.84" */, 35 /* (minimum) */,
|
|
|
50 |
203, 0.25, 0.125, atx23_print_page);
|
|
|
51 |
|
|
|
52 |
const gx_device_printer gs_atx24_device = /* real width = 832 pixels */
|
|
|
53 |
ATX_DEVICE("atx24", 41 /* 4.1" */, 35 /* (minimum) */,
|
|
|
54 |
203, 0.193, 0.125, atx24_print_page);
|
|
|
55 |
|
|
|
56 |
const gx_device_printer gs_atx38_device = /* real width = 2400 pixels */
|
|
|
57 |
ATX_DEVICE("atx38", 80 /* 8.0" */, 35 /* (minimum) */,
|
|
|
58 |
300, 0.25, 0.125, atx38_print_page);
|
|
|
59 |
|
|
|
60 |
/* Output a printer command with a 2-byte, little-endian numeric argument. */
|
|
|
61 |
private void
|
|
|
62 |
fput_atx_command(FILE *f, const char *str, int value)
|
|
|
63 |
{
|
|
|
64 |
fputs(str, f);
|
|
|
65 |
fputc((byte)value, f);
|
|
|
66 |
fputc((byte)(value >> 8), f);
|
|
|
67 |
}
|
|
|
68 |
|
|
|
69 |
/*
|
|
|
70 |
* Attempt to compress a scan line of data. in_size and out_size are even.
|
|
|
71 |
* Return -1 if the compressed data would exceed out_size, otherwise the
|
|
|
72 |
* size of the compressed data (always even).
|
|
|
73 |
*/
|
|
|
74 |
#define MIN_IN_SIZE_TO_COMPRESS 50
|
|
|
75 |
#define MAX_COMPRESSED_SEGMENT_PAIRS 127
|
|
|
76 |
#define MAX_UNCOMPRESSED_SEGMENT_PAIRS 255
|
|
|
77 |
#define COMPRESSED_SEGMENT_COMMAND 0x80 /* + # of repeated pairs */
|
|
|
78 |
#define UNCOMPRESSED_SEGMENT_COMMAND 0x7f /* followed by # of pairs */
|
|
|
79 |
private int
|
|
|
80 |
atx_compress(const byte *in_buf, int in_size, byte *out_buf, int out_size)
|
|
|
81 |
{
|
|
|
82 |
const byte *const in_end = in_buf + in_size;
|
|
|
83 |
byte *const out_end = out_buf + out_size;
|
|
|
84 |
const byte *in = in_buf;
|
|
|
85 |
byte *out = out_buf;
|
|
|
86 |
byte *out_command;
|
|
|
87 |
int pair_count;
|
|
|
88 |
|
|
|
89 |
if (in_size < MIN_IN_SIZE_TO_COMPRESS)
|
|
|
90 |
return -1; /* not worth compressing */
|
|
|
91 |
|
|
|
92 |
/* Start a new segment. */
|
|
|
93 |
New_Segment:
|
|
|
94 |
if (in == in_end) /* end of input data */
|
|
|
95 |
return out - out_buf;
|
|
|
96 |
if (out == out_end) /* output buffer full */
|
|
|
97 |
return -1;
|
|
|
98 |
out_command = out;
|
|
|
99 |
out += 2;
|
|
|
100 |
if (in[1] == in[0]) { /* start compressed segment */
|
|
|
101 |
/* out[-2] will be compressed segment command */
|
|
|
102 |
out[-1] = in[0];
|
|
|
103 |
pair_count = 1;
|
|
|
104 |
goto Scan_Compressed_Pair;
|
|
|
105 |
} else { /* start uncompressed segment */
|
|
|
106 |
out[-2] = UNCOMPRESSED_SEGMENT_COMMAND;
|
|
|
107 |
/* out[-1] will be pair count */
|
|
|
108 |
pair_count = 0;
|
|
|
109 |
goto Scan_Uncompressed_Pair;
|
|
|
110 |
}
|
|
|
111 |
|
|
|
112 |
/* Scan compressed data. */
|
|
|
113 |
Scan_Compressed:
|
|
|
114 |
if (pair_count == MAX_COMPRESSED_SEGMENT_PAIRS ||
|
|
|
115 |
in == in_end || in[0] != in[-1] || in[1] != in[0]
|
|
|
116 |
) { /* end the segment */
|
|
|
117 |
out_command[0] = COMPRESSED_SEGMENT_COMMAND + pair_count;
|
|
|
118 |
goto New_Segment;
|
|
|
119 |
}
|
|
|
120 |
++pair_count;
|
|
|
121 |
Scan_Compressed_Pair:
|
|
|
122 |
in += 2;
|
|
|
123 |
goto Scan_Compressed;
|
|
|
124 |
|
|
|
125 |
/* Scan uncompressed data. */
|
|
|
126 |
Scan_Uncompressed:
|
|
|
127 |
if (pair_count == MAX_UNCOMPRESSED_SEGMENT_PAIRS ||
|
|
|
128 |
in == in_end || in[1] == in[0]
|
|
|
129 |
) { /* end the segment */
|
|
|
130 |
out_command[1] = pair_count;
|
|
|
131 |
goto New_Segment;
|
|
|
132 |
}
|
|
|
133 |
Scan_Uncompressed_Pair:
|
|
|
134 |
if (out == out_end) /* output buffer full */
|
|
|
135 |
return -1;
|
|
|
136 |
out[0] = in[0], out[1] = in[1];
|
|
|
137 |
in += 2;
|
|
|
138 |
out += 2;
|
|
|
139 |
++pair_count;
|
|
|
140 |
goto Scan_Uncompressed;
|
|
|
141 |
|
|
|
142 |
}
|
|
|
143 |
|
|
|
144 |
/* Send the page to the printer. */
|
|
|
145 |
private int
|
|
|
146 |
atx_print_page(gx_device_printer *pdev, FILE *f, int max_width_bytes)
|
|
|
147 |
{
|
|
|
148 |
/*
|
|
|
149 |
* The page length command uses 16 bits to represent the length in
|
|
|
150 |
* units of 0.01", so the maximum representable page length is
|
|
|
151 |
* 655.35", including the unprintable top and bottom margins.
|
|
|
152 |
* Compute the maximum height of the printable area in pixels.
|
|
|
153 |
*/
|
|
|
154 |
float top_bottom_skip = (pdev->HWMargins[1] + pdev->HWMargins[3]) / 72.0;
|
|
|
155 |
int max_height = (int)(pdev->HWResolution[1] * 655 - top_bottom_skip);
|
|
|
156 |
int height = min(pdev->height, max_height);
|
|
|
157 |
int page_length_100ths =
|
|
|
158 |
(int)ceil((height / pdev->HWResolution[1] + top_bottom_skip) * 100);
|
|
|
159 |
gs_memory_t *mem = pdev->memory;
|
|
|
160 |
int raster = gx_device_raster((gx_device *)pdev, true);
|
|
|
161 |
byte *buf;
|
|
|
162 |
/*
|
|
|
163 |
* ATX_COMPRESSED_DATA only takes a 1-byte (word) count.
|
|
|
164 |
* Thus no compressed scan line can take more than 510 bytes.
|
|
|
165 |
*/
|
|
|
166 |
int compressed_raster = min(raster / 2, 510); /* require 50% compression */
|
|
|
167 |
byte *compressed;
|
|
|
168 |
int blank_lines, lnum;
|
|
|
169 |
int code = 0;
|
|
|
170 |
|
|
|
171 |
/* Enforce a minimum 3" page length. */
|
|
|
172 |
if (page_length_100ths < 300)
|
|
|
173 |
page_length_100ths = 300;
|
|
|
174 |
buf = gs_alloc_bytes(mem, raster, "atx_print_page(buf)");
|
|
|
175 |
compressed = gs_alloc_bytes(mem, compressed_raster,
|
|
|
176 |
"atx_print_page(compressed)");
|
|
|
177 |
if (buf == 0 || compressed == 0) {
|
|
|
178 |
code = gs_note_error(gs_error_VMerror);
|
|
|
179 |
goto done;
|
|
|
180 |
}
|
|
|
181 |
fput_atx_command(f, ATX_SET_PAGE_LENGTH, page_length_100ths);
|
|
|
182 |
for (blank_lines = 0, lnum = 0; lnum < height; ++lnum) {
|
|
|
183 |
byte *row;
|
|
|
184 |
byte *end;
|
|
|
185 |
int count;
|
|
|
186 |
|
|
|
187 |
gdev_prn_get_bits(pdev, lnum, buf, &row);
|
|
|
188 |
/* Find the end of the non-blank data. */
|
|
|
189 |
for (end = row + raster; end > row && end[-1] == 0 && end[-2] == 0; )
|
|
|
190 |
end -= 2;
|
|
|
191 |
if (end == row) { /* blank line */
|
|
|
192 |
++blank_lines;
|
|
|
193 |
continue;
|
|
|
194 |
}
|
|
|
195 |
if (blank_lines) { /* skip vertically */
|
|
|
196 |
fput_atx_command(f, ATX_VERTICAL_TAB, blank_lines + 1);
|
|
|
197 |
blank_lines = 0;
|
|
|
198 |
}
|
|
|
199 |
/* Truncate the line to the maximum printable width. */
|
|
|
200 |
if (end - row > max_width_bytes)
|
|
|
201 |
end = row + max_width_bytes;
|
|
|
202 |
count = atx_compress(row, end - row, compressed, compressed_raster);
|
|
|
203 |
if (count >= 0) { /* compressed line */
|
|
|
204 |
/*
|
|
|
205 |
* Note that since compressed_raster can't exceed 510, count
|
|
|
206 |
* can't exceed 510 either.
|
|
|
207 |
*/
|
|
|
208 |
fputs(ATX_COMPRESSED_DATA, f);
|
|
|
209 |
fputc(count / 2, f);
|
|
|
210 |
fwrite(compressed, 1, count, f);
|
|
|
211 |
} else { /* uncompressed line */
|
|
|
212 |
int num_bytes = end - row;
|
|
|
213 |
|
|
|
214 |
fput_atx_command(f, ATX_UNCOMPRESSED_DATA, num_bytes);
|
|
|
215 |
fwrite(row, 1, num_bytes, f);
|
|
|
216 |
}
|
|
|
217 |
}
|
|
|
218 |
|
|
|
219 |
#if 0 /**************** MAY NOT BE NEEDED ****************/
|
|
|
220 |
/* Enforce the minimum page length, and skip any final blank lines. */
|
|
|
221 |
{
|
|
|
222 |
int paper_length = (int)(pdev->HWResolution[1] * 3 + 0.5);
|
|
|
223 |
int printed_length = height - blank_lines;
|
|
|
224 |
|
|
|
225 |
if (height > paper_length)
|
|
|
226 |
paper_length = height;
|
|
|
227 |
if (printed_length < paper_length)
|
|
|
228 |
fput_atx_command(f, ATX_VERTICAL_TAB,
|
|
|
229 |
paper_length - printed_length + 1);
|
|
|
230 |
}
|
|
|
231 |
#endif
|
|
|
232 |
|
|
|
233 |
/* End the page. */
|
|
|
234 |
fputs(ATX_END_PAGE, f);
|
|
|
235 |
|
|
|
236 |
done:
|
|
|
237 |
gs_free_object(mem, compressed, "atx_print_page(compressed)");
|
|
|
238 |
gs_free_object(mem, buf, "atx_print_page(buf)");
|
|
|
239 |
return code;
|
|
|
240 |
}
|
|
|
241 |
|
|
|
242 |
/* Print pages with specified maximum pixel widths. */
|
|
|
243 |
private int
|
|
|
244 |
atx23_print_page(gx_device_printer *pdev, FILE *f)
|
|
|
245 |
{
|
|
|
246 |
return atx_print_page(pdev, f, 576 / 8);
|
|
|
247 |
}
|
|
|
248 |
private int
|
|
|
249 |
atx24_print_page(gx_device_printer *pdev, FILE *f)
|
|
|
250 |
{
|
|
|
251 |
return atx_print_page(pdev, f, 832 / 8);
|
|
|
252 |
}
|
|
|
253 |
private int
|
|
|
254 |
atx38_print_page(gx_device_printer *pdev, FILE *f)
|
|
|
255 |
{
|
|
|
256 |
return atx_print_page(pdev, f, 2400 / 8);
|
|
|
257 |
}
|