xref: /plan9/sys/src/cmd/gs/src/gdevokii.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989, 1992, 1995 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: gdevokii.c,v 1.7 2004/08/10 13:02:36 stefan Exp $*/
18 /*
19  * Okidata IBM compatible dot-matrix printer driver for Ghostscript.
20  *
21  * This device is for the Okidata Microline IBM compatible 9 pin dot
22  * matrix printers.  It is derived from the Epson 9 pin printer driver
23  * using the standard 1/72" vertical pin spacing and the 60/120/240
24  * dpi horizontal resolutions.  The vertical feed resolution however
25  * is 1/144" and the Okidata implements the standard 1/216" requests
26  * through "scaling":
27  *
28  *   (power on)
29  *   "\033J\001" (vertical feed 1/216")  => Nothing happens
30  *   "\033J\001" (vertical feed 1/216")  => Advance 1/144"
31  *   "\033J\001" (vertical feed 1/216")  => Advance 1/144"
32  *   "\033J\001" (vertical feed 1/216")  => Nothing happens
33  *   (and so on)
34  *
35  * The simple minded accounting used here keep track of when the
36  * page actually advances assumes the printer starts in a "power on"
37  * state.
38  *
39  * Supported resolutions are:
40  *
41  *    60x72      60x144
42  *   120x72     120x144
43  *   240x72     240x144
44  *
45  */
46 #include "gdevprn.h"
47 
48 /*
49  * Valid values for X_DPI:
50  *
51  *     60, 120, 240
52  *
53  * The value specified at compile time is the default value used if the
54  * user does not specify a resolution at runtime.
55  */
56 
57 #ifndef X_DPI
58 #  define X_DPI 120
59 #endif
60 
61 /*
62  * Valid values for Y_DPI:
63  *
64  *     72, 144
65  *
66  * The value specified at compile time is the default value used if the
67  * user does not specify a resolution at runtime.
68  */
69 
70 #ifndef Y_DPI
71 #  define Y_DPI 72
72 #endif
73 
74 /* The device descriptor */
75 private dev_proc_print_page(okiibm_print_page);
76 
77 /* Okidata IBM device */
78 const gx_device_printer far_data gs_okiibm_device =
79   prn_device(prn_std_procs, "okiibm",
80 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
81 	X_DPI, Y_DPI,
82 	0.25, 0.0, 0.25, 0.0,			/* margins */
83 	1, okiibm_print_page);
84 
85 /* ------ Internal routines ------ */
86 
87 /* Forward references */
88 private void okiibm_output_run(byte *, int, int, char, FILE *, int);
89 
90 /* Send the page to the printer. */
91 private int
okiibm_print_page1(gx_device_printer * pdev,FILE * prn_stream,int y_9pin_high,const char * init_string,int init_length,const char * end_string,int end_length)92 okiibm_print_page1(gx_device_printer *pdev, FILE *prn_stream, int y_9pin_high,
93   const char *init_string, int init_length,
94   const char *end_string, int end_length)
95 {
96 	static const char graphics_modes_9[5] =
97 	{
98 	-1, 0 /*60*/, 1 /*120*/, -1, 3 /*240*/
99 	};
100 
101 	int in_y_mult = (y_9pin_high ? 2 : 1);
102 	int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
103 	/* Note that in_size is a multiple of 8. */
104 	int in_size = line_size * (8 * in_y_mult);
105 	byte *buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "okiibm_print_page(buf1)");
106 	byte *buf2 = (byte *)gs_malloc(pdev->memory, in_size, 1, "okiibm_print_page(buf2)");
107 	byte *in = buf1;
108 	byte *out = buf2;
109 	int out_y_mult = 1;
110 	int x_dpi = pdev->x_pixels_per_inch;
111 	char start_graphics = graphics_modes_9[x_dpi / 60];
112 	int first_pass = (start_graphics == 3 ? 1 : 0);
113 	int last_pass = first_pass * 2;
114 	int y_passes = (y_9pin_high ? 2 : 1);
115 	int skip = 0, lnum = 0, pass, ypass;
116 	int y_step = 0;
117 
118 	/* Check allocations */
119 	if ( buf1 == 0 || buf2 == 0 )
120 	{	if ( buf1 )
121 		  gs_free(pdev->memory, (char *)buf1, in_size, 1, "okiibm_print_page(buf1)");
122 		if ( buf2 )
123 		  gs_free(pdev->memory, (char *)buf2, in_size, 1, "okiibm_print_page(buf2)");
124 		return_error(gs_error_VMerror);
125 	}
126 
127 	/* Initialize the printer. */
128 	fwrite(init_string, 1, init_length, prn_stream);
129 
130 	/* Print lines of graphics */
131 	while ( lnum < pdev->height )
132 	{
133 		byte *in_data;
134 		byte *inp;
135 		byte *in_end;
136 		byte *out_end;
137 		int lcnt;
138 
139 		/* Copy 1 scan line and test for all zero. */
140 		gdev_prn_get_bits(pdev, lnum, in, &in_data);
141 		if ( in_data[0] == 0 &&
142 		     !memcmp((char *)in_data, (char *)in_data + 1, line_size - 1)
143 		   )
144 	    	{
145 			lnum++;
146 			skip += 2 / in_y_mult;
147 			continue;
148 		}
149 
150 		/*
151 		 * Vertical tab to the appropriate position.
152 		 * The skip count is in 1/144" steps.  If total
153 		 * vertical request is not a multiple od 1/72"
154 		 * we need to make sure the page is actually
155 		 * going to advance.
156 		 */
157 		if ( skip & 1 )
158 		{
159 			int n = 1 + (y_step == 0 ? 1 : 0);
160 			fprintf(prn_stream, "\033J%c", n);
161 			y_step = (y_step + n) % 3;
162 			skip -= 1;
163 		}
164 		skip = skip / 2 * 3;
165 		while ( skip > 255 )
166 		{
167 			fputs("\033J\377", prn_stream);
168 			skip -= 255;
169 		}
170 		if ( skip )
171 		{
172 			fprintf(prn_stream, "\033J%c", skip);
173 		}
174 
175 		/* Copy the the scan lines. */
176 	    	lcnt = gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
177 		if ( lcnt < 8 * in_y_mult )
178 		{	/* Pad with lines of zeros. */
179 			memset(in + lcnt * line_size, 0,
180 			       in_size - lcnt * line_size);
181 		}
182 
183 		if ( y_9pin_high )
184 		{	/* Shuffle the scan lines */
185 			byte *p;
186 			int i;
187 			static const char index[] =
188 			{  0, 2, 4, 6, 8, 10, 12, 14,
189 			   1, 3, 5, 7, 9, 11, 13, 15
190 			};
191 			for ( i = 0; i < 16; i++ )
192 			{
193 				memcpy( out + (i * line_size),
194 				        in + (index[i] * line_size),
195 				        line_size);
196 			}
197 			p = in;
198 			in = out;
199 			out = p;
200 		}
201 
202 	for ( ypass = 0; ypass < y_passes; ypass++ )
203 	{
204 	    for ( pass = first_pass; pass <= last_pass; pass++ )
205 	    {
206 		/* We have to 'transpose' blocks of 8 pixels x 8 lines, */
207 		/* because that's how the printer wants the data. */
208 
209 	        if ( pass == first_pass )
210 	        {
211 		    out_end = out;
212 		    inp = in;
213 		    in_end = inp + line_size;
214 
215     	            for ( ; inp < in_end; inp++, out_end += 8 )
216     	            {
217     		        gdev_prn_transpose_8x8(inp + (ypass * 8 * line_size),
218 					       line_size, out_end, 1);
219 		    }
220 		    /* Remove trailing 0s. */
221 		    while ( out_end > out && out_end[-1] == 0 )
222 	            {
223 		       	out_end--;
224 		    }
225 		}
226 
227 		/* Transfer whatever is left and print. */
228 		if ( out_end > out )
229 	        {
230 		    okiibm_output_run(out, (int)(out_end - out),
231 			           out_y_mult, start_graphics,
232 				   prn_stream, pass);
233 	        }
234 	    	fputc('\r', prn_stream);
235 	    }
236 	    if ( ypass < y_passes - 1 )
237 	    {
238 		int n = 1 + (y_step == 0 ? 1 : 0);
239 		fprintf(prn_stream, "\033J%c", n);
240 		y_step = (y_step + n) % 3;
241 	    }
242 	}
243 	skip = 16 - y_passes + 1;		/* no skip on last Y pass */
244 	lnum += 8 * in_y_mult;
245 	}
246 
247 	/* Reinitialize the printer. */
248 	fwrite(end_string, 1, end_length, prn_stream);
249 	fflush(prn_stream);
250 
251 	gs_free(pdev->memory, (char *)buf2, in_size, 1, "okiibm_print_page(buf2)");
252 	gs_free(pdev->memory, (char *)buf1, in_size, 1, "okiibm_print_page(buf1)");
253 	return 0;
254 }
255 
256 /* Output a single graphics command. */
257 /* pass=0 for all columns, 1 for even columns, 2 for odd columns. */
258 private void
okiibm_output_run(byte * data,int count,int y_mult,char start_graphics,FILE * prn_stream,int pass)259 okiibm_output_run(byte *data, int count, int y_mult,
260   char start_graphics, FILE *prn_stream, int pass)
261 {
262 	int xcount = count / y_mult;
263 
264 	fputc(033, prn_stream);
265 	fputc("KLYZ"[start_graphics], prn_stream);
266 	fputc(xcount & 0xff, prn_stream);
267 	fputc(xcount >> 8, prn_stream);
268 	if ( !pass )
269 	{
270 		fwrite(data, 1, count, prn_stream);
271 	}
272 	else
273 	{
274 		/* Only write every other column of y_mult bytes. */
275 		int which = pass;
276 		register byte *dp = data;
277 		register int i, j;
278 
279 		for ( i = 0; i < xcount; i++, which++ )
280 		{
281 			for ( j = 0; j < y_mult; j++, dp++ )
282 			{
283 				putc(((which & 1) ? *dp : 0), prn_stream);
284 			}
285 		}
286 	}
287 }
288 
289 /* The print_page procedures are here, to avoid a forward reference. */
290 
291 private const char okiibm_init_string[]	= { 0x18 };
292 private const char okiibm_end_string[]	= { 0x0c };
293 private const char okiibm_one_direct[]	= { 0x1b, 0x55, 0x01 };
294 private const char okiibm_two_direct[]	= { 0x1b, 0x55, 0x00 };
295 
296 private int
okiibm_print_page(gx_device_printer * pdev,FILE * prn_stream)297 okiibm_print_page(gx_device_printer *pdev, FILE *prn_stream)
298 {
299 	char init_string[16], end_string[16];
300 	int init_length, end_length;
301 
302 	init_length = sizeof(okiibm_init_string);
303 	memcpy(init_string, okiibm_init_string, init_length);
304 
305 	end_length = sizeof(okiibm_end_string);
306 	memcpy(end_string, okiibm_end_string, end_length);
307 
308 	if ( pdev->y_pixels_per_inch > 72 &&
309 	     pdev->x_pixels_per_inch > 60 )
310 	{
311 		/* Unidirectional printing for the higher resolutions. */
312 		memcpy( init_string + init_length, okiibm_one_direct,
313 		        sizeof(okiibm_one_direct) );
314 		init_length += sizeof(okiibm_one_direct);
315 
316 		memcpy( end_string + end_length, okiibm_two_direct,
317 		        sizeof(okiibm_two_direct) );
318 		end_length += sizeof(okiibm_two_direct);
319 	}
320 
321 	return okiibm_print_page1( pdev, prn_stream,
322 				   pdev->y_pixels_per_inch > 72 ? 1 : 0,
323 				   init_string, init_length,
324 				   end_string, end_length );
325 }
326