xref: /plan9/sys/src/cmd/gs/src/gdevadmp.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989, 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: gdevadmp.c,v 1.6 2004/08/10 13:02:36 stefan Exp $*/
18 /*
19  * Apple DMP / Imagewriter driver
20  *
21  * This is a modification of Mark Wedel's Apple DMP and
22  * Jonathan Luckey's Imagewriter II driver to
23  * support the Imagewriter LQ's higher resolution (320x216):
24  *      appledmp:  120dpi x  72dpi is still supported (yuck)
25  *	iwlo:	   160dpi x  72dpi
26  *	iwhi:	   160dpi x 144dpi
27  *      iwlq:      320dpi x 216dpi
28  *
29  * This is also my first attempt to work with gs. I have not included the LQ's
30  * ability to print in colour. Perhaps at a later date I will tackle that.
31  *
32  * BTW, to get your Imagewriter LQ serial printer to work with a PC, attach it
33  * with a nullmodem serial cable.
34  *
35  * Scott Barker (barkers@cuug.ab.ca)
36  */
37 
38 /*
39  * This is a modification of Mark Wedel's Apple DMP driver to
40  * support 2 higher resolutions:
41  *      appledmp:  120dpi x  72dpi is still supported (yuck)
42  *	iwlo:	   160dpi x  72dpi
43  *	iwhi:	   160dpi x 144dpi
44  *
45  * The Imagewriter II is a bit odd.  In pinfeed mode, it thinks its
46  * First line is 1 inch from the top of the page. If you set the top
47  * form so that it starts printing at the top of the page, and print
48  * to near the bottom, it thinks it has run onto the next page and
49  * the formfeed will skip a whole page.  As a work around, I reverse
50  * the paper about a 1.5 inches at the end of the page before the
51  * formfeed to make it think its on the 'right' page.  bah. hack!
52  *
53  * This is  my first attempt to work with gs, so your milage may vary
54  *
55  * Jonathan Luckey (luckey@rtfm.mlb.fl.us)
56  */
57 
58 /* This is a bare bones driver I developed for my apple Dot Matrix Printer.
59  * This code originally was from the epson driver, but I removed a lot
60  * of stuff that was not needed.
61  *
62  * The Dot Matrix Printer was a predecessor to the apple Imagewriter.  Its
63  * main difference being that it was parallel.
64  *
65  * This code should work fine on Imagewriters, as they have a superset
66  * of commands compared to the DMP printer.
67  *
68  * This driver does not produce the smalles output files possible.  To
69  * do that, it should look through the output strings and find repeat
70  * occurances of characters, and use the escape sequence that allows
71  * printing repeat sequences.  However, as I see it, this the limiting
72  * factor in printing is not transmission speed to the printer itself,
73  * but rather, how fast the print head can move.  This is assuming the
74  * printer is set up with a reasonable speed (9600 bps)
75  *
76  * WHAT THE CODE DOES AND DOES NOT DO:
77  *
78  * To print out images, it sets the printer for unidirection printing
79  * and 15 cpi (120 dpi). IT sets line feed to 1/9 of an inch (72 dpi).
80  * When finished, it sets things back to bidirection print, 1/8" line
81  * feeds, and 12 cpi.  There does not appear to be a way to reset
82  * things to initial values.
83  *
84  * This code does not set for 8 bit characters (which is required). It
85  * also assumes that carriage return/newline is needed, and not just
86  * carriage return.  These are all switch settings on the DMP, and
87  * I have configured them for 8 bit data and cr only.
88  *
89  * You can search for the strings Init and Reset to find the strings
90  * that set up the printer and clear things when finished, and change
91  * them to meet your needs.
92  *
93  * Also, you need to make sure that the printer daemon (assuming unix)
94  * doesn't change the data as it is being printed.  I have set my
95  * printcap file (sunos 4.1.1) with the string:
96  * ms=pass8,-opost
97  * and it works fine.
98  *
99  * Feel free to improve this code if you want.  However, please make
100  * sure that the old DMP will still be supported by any changes.  This
101  * may mean making an imagewriter device, and just copying this file
102  * to something like gdevimage.c.
103  *
104  * The limiting factor of the DMP is the vertical resolution.  However, I
105  * see no way to do anything about this.  Horizontal resolution could
106  * be increased by using 17 cpi (136 dpi).  I believe the Imagewriter
107  * supports 24 cpi (192 dpi).  However, the higher dpi, the slower
108  * the printing.
109  *
110  * Dot Matrix Code by Mark Wedel (master@cats.ucsc.edu)
111  */
112 
113 
114 #include "gdevprn.h"
115 
116 /* The device descriptors */
117 private dev_proc_print_page(dmp_print_page);
118 
119 /* Standard DMP device */
120 const gx_device_printer far_data gs_appledmp_device =
121 prn_device(prn_std_procs, "appledmp",
122 	85,				/* width_10ths, 8.5" */
123 	110,				/* height_10ths, 11" */
124 	120, 72,			/* X_DPI, Y_DPI */
125 	0, 0.5, 0.5, 0,		/* margins */
126 	1, dmp_print_page);
127 
128 
129 /*  lowrez Imagewriter device */
130 const gx_device_printer far_data gs_iwlo_device =
131 prn_device(prn_std_procs, "iwlo",
132 	85,				/* width_10ths, 8.5" */
133 	110,				/* height_10ths, 11" */
134 	160, 72,			/* X_DPI, Y_DPI */
135 	0, 0.5, 0.5, 0,		/* margins */
136 	1, dmp_print_page);
137 
138 
139 /*  hirez Imagewriter device */
140 const gx_device_printer far_data gs_iwhi_device =
141 prn_device(prn_std_procs, "iwhi",
142 	85,				/* width_10ths, 8.5" */
143 	110,				/* height_10ths, 11" */
144 	160, 144,			/* X_DPI, Y_DPI */
145 	0, 0.5, 0.5, 0,		/* margins */
146 	1, dmp_print_page);
147 
148 
149 /* LQ hirez Imagewriter device */
150 const gx_device_printer far_data gs_iwlq_device =
151 prn_device(prn_std_procs, "iwlq",
152 	85,				/* width_10ths, 8.5" */
153 	110,				/* height_10ths, 11" */
154 	320, 216,
155 	0, 0, 0.5, 0,		/* margins */
156 	1, dmp_print_page);
157 
158 
159 /* ------ Internal routines ------ */
160 
161 #define DMP 1
162 #define IWLO 2
163 #define IWHI 3
164 #define IWLQ 4
165 
166 /* Send the page to the printer. */
167 private int
dmp_print_page(gx_device_printer * pdev,FILE * prn_stream)168 dmp_print_page(gx_device_printer *pdev, FILE *prn_stream)
169 {
170 	int dev_type;
171 
172 	int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
173 	/* Note that in_size is a multiple of 8. */
174 	int in_size = line_size * 8;
175 
176 	byte *buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "dmp_print_page(buf1)");
177 	byte *buf2 = (byte *)gs_malloc(pdev->memory, in_size, 1, "dmp_print_page(buf2)");
178 	byte *prn = (byte *)gs_malloc(pdev->memory, 3*in_size, 1, "dmp_print_page(prn)");
179 
180 	byte *in = buf1;
181 	byte *out = buf2;
182 	int lnum = 0;
183 
184 	/* Check allocations */
185 	if ( buf1 == 0 || buf2 == 0 || prn == 0 )
186 	{
187 		if ( buf1 )
188 			gs_free(pdev->memory, (char *)buf1, in_size, 1,
189 			"dmp_print_page(buf1)");
190 		if ( buf2 )
191 			gs_free(pdev->memory, (char *)buf2, in_size, 1,
192 			"dmp_print_page(buf2)");
193 		if ( prn )
194 			gs_free(pdev->memory, (char *)prn, in_size, 1,
195 			"dmp_print_page(prn)");
196 		return_error(gs_error_VMerror);
197 	}
198 
199 	if ( pdev->y_pixels_per_inch == 216 )
200 		dev_type = IWLQ;
201 	else if ( pdev->y_pixels_per_inch == 144 )
202 		dev_type = IWHI;
203 	else if ( pdev->x_pixels_per_inch == 160 )
204 		dev_type = IWLO;
205 	else
206 		dev_type = DMP;
207 
208 	/* Initialize the printer and reset the margins. */
209 
210 	fputs("\r\n\033>\033T16", prn_stream);
211 
212 	switch(dev_type)
213 	{
214 	case IWLQ:
215 		fputs("\033P\033a3", prn_stream);
216 		break;
217 	case IWHI:
218 	case IWLO:
219 		fputs("\033P", prn_stream);
220 		break;
221 	case DMP:
222 	default:
223 		fputs("\033q", prn_stream);
224 		break;
225 	}
226 
227 	/* Print lines of graphics */
228 	while ( lnum < pdev->height )
229 	{
230 		byte *inp;
231 		byte *in_end;
232 		byte *out_end;
233 		int lcnt,ltmp;
234 		int count, passes;
235 		byte *prn_blk, *prn_end, *prn_tmp;
236 
237 /* The apple DMP printer seems to be odd in that the bit order on
238  * each line is reverse what might be expected.  Meaning, an
239  * underscore would be done as a series of 0x80, while on overscore
240  * would be done as a series of 0x01.  So we get each
241  * scan line in reverse order.
242  */
243 
244 		switch (dev_type)
245 		{
246 		case IWLQ: passes = 3; break;
247 		case IWHI: passes = 2; break;
248 		case IWLO:
249 		case DMP:
250 		default: passes = 1; break;
251 		}
252 
253 		for (count = 0; count < passes; count++)
254 		{
255 			for (lcnt=0; lcnt<8; lcnt++)
256 			{
257 				switch(dev_type)
258 				{
259 				case IWLQ: ltmp = lcnt + 8*count; break;
260 				case IWHI: ltmp = 2*lcnt + count; break;
261 				case IWLO:
262 				case DMP:
263 				default: ltmp = lcnt; break;
264 				}
265 
266 				if ((lnum+ltmp)>pdev->height)
267 					memset(in+lcnt*line_size,0,line_size);
268 				else
269 					gdev_prn_copy_scan_lines(pdev,
270 					lnum+ltmp, in + line_size*(7 - lcnt),
271 					line_size);
272 			}
273 
274 			out_end = out;
275 			inp = in;
276 			in_end = inp + line_size;
277 			for ( ; inp < in_end; inp++, out_end += 8 )
278 			{
279 				gdev_prn_transpose_8x8(inp, line_size,
280 				out_end, 1);
281 			}
282 
283 			out_end = out;
284 
285 			switch (dev_type)
286 			{
287 			case IWLQ: prn_end = prn + count; break;
288 			case IWHI: prn_end = prn + in_size*count; break;
289 			case IWLO:
290 			case DMP:
291 			default: prn_end = prn; break;
292 			}
293 
294 			while ( (int)(out_end-out) < in_size)
295 			{
296 				*prn_end = *(out_end++);
297 				if ((dev_type) == IWLQ) prn_end += 3;
298 				else prn_end++;
299 			}
300 		}
301 
302 		switch (dev_type)
303 		{
304 		case IWLQ:
305 			prn_blk = prn;
306 			prn_end = prn_blk + in_size * 3;
307 			while (prn_end > prn && prn_end[-1] == 0 &&
308 				prn_end[-2] == 0 && prn_end[-3] == 0)
309 			{
310 				prn_end -= 3;
311 			}
312 			while (prn_blk < prn_end && prn_blk[0] == 0 &&
313 				prn_blk[1] == 0 && prn_blk[2] == 0)
314 			{
315 				prn_blk += 3;
316 			}
317 			if (prn_end != prn_blk)
318 			{
319 				if ((prn_blk - prn) > 7)
320 					fprintf(prn_stream,"\033U%04d%c%c%c",
321 						(int)((prn_blk - prn)/3),
322 						0, 0, 0);
323 				else
324 					prn_blk = prn;
325 				fprintf(prn_stream,"\033C%04d",
326 					(int)((prn_end - prn_blk)/3));
327 				fwrite(prn_blk, 1, (int)(prn_end - prn_blk),
328 					prn_stream);
329 		        }
330 			break;
331 		case IWHI:
332 			for (count = 0; count < 2; count++)
333 			{
334 				prn_blk = prn_tmp = prn + in_size*count;
335 				prn_end = prn_blk + in_size;
336 				while (prn_end > prn_blk && prn_end[-1] == 0)
337 					prn_end--;
338 				while (prn_blk < prn_end && prn_blk[0] == 0)
339 					prn_blk++;
340 				if (prn_end != prn_blk)
341 				{
342 					if ((prn_blk - prn_tmp) > 7)
343 						fprintf(prn_stream,
344 							"\033V%04d%c",
345 							(int)(prn_blk-prn_tmp),
346 							 0);
347 					else
348 						prn_blk = prn_tmp;
349 					fprintf(prn_stream,"\033G%04d",
350 						(int)(prn_end - prn_blk));
351 					fwrite(prn_blk, 1,
352 						(int)(prn_end - prn_blk),
353 						prn_stream);
354 				}
355 				if (!count) fputs("\033T01\r\n",prn_stream);
356 			}
357 			fputs("\033T15",prn_stream);
358 			break;
359 		case IWLO:
360 		case DMP:
361 		default:
362 			prn_blk = prn;
363 			prn_end = prn_blk + in_size;
364 			while (prn_end > prn_blk && prn_end[-1] == 0)
365 				prn_end--;
366 			while (prn_blk < prn_end && prn_blk[0] == 0)
367 				prn_blk++;
368 			if (prn_end != prn_blk)
369 			{
370 				if ((prn_blk - prn) > 7)
371 					fprintf(prn_stream,"\033V%04d%c",
372 						(int)(prn_blk - prn), 0);
373 				else
374 					prn_blk = prn;
375 				fprintf(prn_stream,"\033G%04d",
376 					(int)(prn_end - prn_blk));
377 				fwrite(prn_blk, 1, (int)(prn_end - prn_blk),
378 					prn_stream);
379 			}
380 			break;
381 		}
382 
383 		fputs("\r\n",prn_stream);
384 
385 		switch (dev_type)
386 		{
387 			case IWLQ: lnum += 24 ; break;
388 			case IWHI: lnum += 16 ; break;
389 			case IWLO:
390 			case DMP:
391 			default: lnum += 8 ; break;
392 		}
393 	}
394 
395 	/* ImageWriter will skip a whole page if too close to end */
396 	/* so skip back more than an inch */
397 	if ( !(dev_type == DMP) )
398 		fputs("\033T99\n\n\033r\n\n\n\n\033f", prn_stream);
399 
400 	/* Formfeed and Reset printer */
401 	fputs("\033T16\f\033<\033B\033E", prn_stream);
402 	fflush(prn_stream);
403 
404 	gs_free(pdev->memory, (char *)prn, in_size, 1, "dmp_print_page(prn)");
405 	gs_free(pdev->memory, (char *)buf2, in_size, 1, "dmp_print_page(buf2)");
406 	gs_free(pdev->memory, (char *)buf1, in_size, 1, "dmp_print_page(buf1)");
407 	return 0;
408 }
409