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