xref: /plan9/sys/src/cmd/gs/src/gdevepsc.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: gdevepsc.c,v 1.11 2004/08/04 19:36:12 stefan Exp $*/
18 /* Epson color dot-matrix printer driver by dave@exlog.com */
19 #include "gdevprn.h"
20 
21 /*
22  * For 9-pin printers, you may select
23  *   X_DPI = 60, 120, or 240
24  *   Y_DPI = 60 or 72
25  * For 24-pin printers, you may select
26  *   X_DPI = 60, 120, 180, 240, or 360
27  *   Y_DPI = 60, 72, 180, or 216
28  * Note that a given printer implements *either* Y_DPI = 60 | 180 *or*
29  * Y_DPI = 72 | 216; no attempt is made to check this here.
30  * Note that X_DPI = 180 or 360 requires Y_DPI > 100;
31  * this isn't checked either.  Finally, note that X_DPI=240 and
32  * X_DPI=360 are double-density modes requiring two passes to print.
33  *
34  * The values of X_DPI and Y_DPI may be set at compile time:
35  * see gdevs.mak.
36  *
37  * At some time in the future, we could simulate 24-bit output on
38  * 9-pin printers by using fractional vertical positioning;
39  * we could even implement an X_DPI=360 mode by using the
40  * ESC++ command that spaces vertically in units of 1/360"
41  * (not supported on many printers.)
42  */
43 
44 #ifndef X_DPI
45 #  define X_DPI 180			/* pixels per inch */
46 #endif
47 #ifndef Y_DPI
48 #  define Y_DPI 180			/* pixels per inch */
49 #endif
50 
51 /*
52 **	Colors for EPSON LQ-2550.
53 **
54 **	We map VIOLET to BLUE since this is the best we can do.
55 */
56 #define BLACK	0
57 #define MAGENTA 1
58 #define CYAN	2
59 #define VIOLET	3
60 #define YELLOW	4
61 #define RED		5
62 #define GREEN	6
63 #define WHITE	7
64 
65 /*
66 **	The offset in this array correspond to
67 **	the ESC-r n value
68 */
69 static char rgb_color[2][2][2] =	{
70 	{{BLACK, VIOLET}, {GREEN, CYAN}},
71 	{{RED, MAGENTA}, {YELLOW, WHITE}}
72 	};
73 
74 /* Map an RGB color to a printer color. */
75 #define cv_shift (sizeof(gx_color_value) * 8 - 1)
76 private gx_color_index
epson_map_rgb_color(gx_device * dev,const gx_color_value cv[])77 epson_map_rgb_color(gx_device *dev, const gx_color_value cv[])
78 {
79 
80     gx_color_value r = cv[0];
81     gx_color_value g = cv[1];
82     gx_color_value b = cv[2];
83 
84     if (gx_device_has_color(dev))
85 /* use ^7 so WHITE is 0 for internal calculations */
86         return (gx_color_index)rgb_color[r >> cv_shift][g >> cv_shift][b >> cv_shift] ^ 7;
87     else
88 	return gx_default_map_rgb_color(dev, cv);
89 }
90 
91 /* Map the printer color back to RGB. */
92 private int
epson_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])93 epson_map_color_rgb(gx_device *dev, gx_color_index color,
94   gx_color_value prgb[3])
95 {
96 #define c1 gx_max_color_value
97 if (gx_device_has_color(dev))
98 	switch ((ushort)color ^ 7)
99 		{
100 		case BLACK:
101 			prgb[0] = 0; prgb[1] = 0; prgb[2] = 0; break;
102 		case VIOLET:
103 			prgb[0] = 0; prgb[1] = 0; prgb[2] = c1; break;
104 		case GREEN:
105 			prgb[0] = 0; prgb[1] = c1; prgb[2] = 0; break;
106 		case CYAN:
107 			prgb[0] = 0; prgb[1] = c1; prgb[2] = c1; break;
108 		case  RED:
109 			prgb[0] = c1; prgb[1] = 0; prgb[2] = 0; break;
110 		case  MAGENTA:
111 			prgb[0] = c1; prgb[1] = 0; prgb[2] = c1; break;
112 		case YELLOW:
113 			prgb[0] = c1; prgb[1] = c1; prgb[2] = 0; break;
114 		case  WHITE:
115 			prgb[0] = c1; prgb[1] = c1; prgb[2] = c1; break;
116 		}
117 	else
118 		return gx_default_map_color_rgb(dev, color, prgb);
119 	return 0;
120 }
121 
122 /* The device descriptor */
123 private dev_proc_print_page(epsc_print_page);
124 
125 private gx_device_procs epson_procs =
126   prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
127     epson_map_rgb_color, epson_map_color_rgb);
128 
129 const gx_device_printer far_data gs_epsonc_device =
130   prn_device(epson_procs, "epsonc",
131 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
132 	X_DPI, Y_DPI,
133 	0, 0, 0.25, 0,		/* margins */
134 	3, epsc_print_page);
135 
136 /* ------ Internal routines ------ */
137 
138 /* Forward references */
139 private void epsc_output_run(byte *, int, int, char, FILE *, int);
140 
141 /* Send the page to the printer. */
142 #define DD 0x80				/* double density flag */
143 private int
epsc_print_page(gx_device_printer * pdev,FILE * prn_stream)144 epsc_print_page(gx_device_printer *pdev, FILE *prn_stream)
145 {	static int graphics_modes_9[5] =
146 	   {	-1, 0 /*60*/, 1	/*120*/, -1, DD+3 /*240*/
147 	   };
148 	static int graphics_modes_24[7] =
149 	   {	-1, 32 /*60*/, 33 /*120*/, 39 /*180*/,
150 		-1, -1, DD+40 /*360*/
151 	   };
152 	int y_24pin = pdev->y_pixels_per_inch > 72;
153 	int y_mult = (y_24pin ? 3 : 1);
154 	int line_size = (pdev->width + 7) >> 3;	/* always mono */
155 	int in_size = line_size * (8 * y_mult);
156 	byte *in = (byte *)gs_malloc(pdev->memory, in_size+1, 1, "epsc_print_page(in)");
157 	int out_size = ((pdev->width + 7) & -8) * y_mult;
158 	byte *out = (byte *)gs_malloc(pdev->memory, out_size+1, 1, "epsc_print_page(out)");
159 	int x_dpi = (int)pdev->x_pixels_per_inch;
160 	char start_graphics = (char)
161 		((y_24pin ? graphics_modes_24 : graphics_modes_9)[x_dpi / 60]);
162 	int first_pass = (start_graphics & DD ? 1 : 0);
163 	int last_pass = first_pass * 2;
164 	int dots_per_space = x_dpi / 10;	/* pica space = 1/10" */
165 	int bytes_per_space = dots_per_space * y_mult;
166 	int skip = 0, lnum = 0, pass;
167 /* declare color buffer and related vars */
168 	byte *color_in;
169 	int color_line_size, color_in_size;
170 	int spare_bits = (pdev->width % 8);	/* left over bits to go to margin */
171 	int whole_bits = pdev->width - spare_bits;
172 
173 	/* Check allocations */
174 	if ( in == 0 || out == 0 )
175 	    {	if ( in ) gs_free(pdev->memory, (char *)in, in_size+1, 1, "epsc_print_page(in)");
176 		if ( out ) gs_free(pdev->memory, (char *)out, out_size+1, 1, "epsc_print_page(out)");
177 		return -1;
178 	   }
179 
180 	/* Initialize the printer and reset the margins. */
181 	fwrite("\033@\033P\033l\000\033Q\377\033U\001\r", 1, 14, prn_stream);
182 
183 /*	Create color buffer */
184 	if (gx_device_has_color(pdev))
185 		{
186 		color_line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
187 		color_in_size = color_line_size * (8 * y_mult);
188 		if((color_in = (byte *)gs_malloc(pdev->memory, color_in_size+1, 1,
189 						 "epsc_print_page(color)")) == 0)
190 		    {
191 			gs_free(pdev->memory, (char *)in, in_size+1, 1, "epsc_print_page(in)");
192 			gs_free(pdev->memory, (char *)out, out_size+1, 1, "epsc_print_page(out)");
193 			return(-1);
194 		    }
195 		}
196 	else
197 		{
198 		color_in = in;
199 		color_in_size = in_size;
200 		color_line_size = line_size;
201 		}
202 
203 	/* Print lines of graphics */
204 	while ( lnum < pdev->height )
205 	   {
206 		int lcnt;
207 		byte *nextcolor = NULL;	/* position where next color appears */
208 		byte *nextmono = NULL;	/* position to map next color */
209 
210 		/* Copy 1 scan line and test for all zero. */
211 		gdev_prn_copy_scan_lines(pdev, lnum, color_in, color_line_size);
212 
213 		if ( color_in[0] == 0 &&
214 		     !memcmp((char *)color_in, (char *)color_in + 1, color_line_size - 1)
215 		   )
216 		   {	lnum++;
217 			skip += 3 / y_mult;
218 			continue;
219 		   }
220 
221 		/* Vertical tab to the appropriate position. */
222 		while ( skip > 255 )
223 		   {	fputs("\033J\377", prn_stream);
224 			skip -= 255;
225 		   }
226 		if ( skip )
227 			fprintf(prn_stream, "\033J%c", skip);
228 
229 		/* Copy the rest of the scan lines. */
230 		lcnt = 1 + gdev_prn_copy_scan_lines(pdev, lnum + 1,
231 			color_in + color_line_size, color_in_size - color_line_size);
232 
233 		if ( lcnt < 8 * y_mult )
234 			{
235 			memset((char *)(color_in + lcnt * color_line_size), 0,
236 				color_in_size - lcnt * color_line_size);
237 			if (gx_device_has_color(pdev))	/* clear the work buffer */
238 				memset((char *)(in + lcnt * line_size), 0,
239 					in_size - lcnt * line_size);
240 			}
241 
242 /*
243 **	We need to create a normal epson scan line from our color scan line
244 **	We do this by setting a bit in the "in" buffer if the pixel byte is set
245 **	to any color.  We then search for any more pixels of that color, setting
246 **	"in" accordingly.  If any other color is found, we save it for the next
247 **	pass.  There may be up to 7 passes.
248 **	In the future, we should make the passes so as to maximize the
249 **	life of the color ribbon (i.e. go lightest to darkest).
250 */
251 		do
252 		{
253 		byte *inp = in;
254 		byte *in_end = in + line_size;
255 		byte *out_end = out;
256 		byte *out_blk;
257 		register byte *outp;
258 
259 		if (gx_device_has_color(pdev))
260 			{
261 			register int i,j;
262 			register byte *outbuf, *realbuf;
263 			byte current_color;
264 			int end_next_bits = whole_bits;
265 			int lastbits;
266 
267 /*	Move to the point in the scanline that has a new color */
268 			if (nextcolor)
269 				{
270 				realbuf = nextcolor;
271 				outbuf = nextmono;
272 				memset((char *)in, 0, (nextmono - in));
273 				i = nextcolor - color_in;
274 				nextcolor = NULL;
275 				end_next_bits = (i / color_line_size) * color_line_size
276 					+ whole_bits;
277 				}
278 			else
279 				{
280 				i = 0;
281 				realbuf = color_in;
282 				outbuf = in;
283 				nextcolor = NULL;
284 				}
285 /*	move thru the color buffer, turning on the appropriate
286 **	bit in the "mono" buffer", setting pointers to the next
287 **	color and changing the color output of the epson
288 */
289 			for (current_color = 0; i <= color_in_size && outbuf < in + in_size; outbuf++)
290 				{
291 /*	Remember, line_size is rounded up to next whole byte
292 **	whereas color_line_size is the proper length
293 **	We only want to set the proper bits in the last line_size byte.
294 */
295 				if (spare_bits && i == end_next_bits)
296 					{
297 					end_next_bits = whole_bits + i + spare_bits;
298 					lastbits = 8 - spare_bits;
299 					}
300 				else
301 					lastbits = 0;
302 
303 				for (*outbuf = 0, j = 8; --j >= lastbits && i <= color_in_size;
304 					realbuf++,i++)
305 					{
306 					if (*realbuf)
307 						{
308 						if (current_color > 0)
309 							{
310 							if (*realbuf == current_color)
311 								{
312 								*outbuf |= 1 << j;
313 								*realbuf = 0;	/* throw this byte away */
314 								}
315 					/* save this location for next pass */
316 							else if (nextcolor == NULL)
317 								{
318 								nextcolor = realbuf - (7 - j);
319 								nextmono = outbuf;
320 								}
321 							}
322 						else
323 							{
324 							*outbuf |= 1 << j;
325 							current_color = *realbuf;	/* set color */
326 							*realbuf = 0;
327 							}
328 						}
329 					}
330 				}
331 			*outbuf = 0;		/* zero the end, for safe keeping */
332 /*	Change color on the EPSON, current_color must be set
333 **	but lets check anyway
334 */
335 			if (current_color)
336 				fprintf(prn_stream,"\033r%d",current_color ^ 7);
337 			}
338 
339 		/* We have to 'transpose' blocks of 8 pixels x 8 lines, */
340 		/* because that's how the printer wants the data. */
341 		/* If we are in a 24-pin mode, we have to transpose */
342 		/* groups of 3 lines at a time. */
343 
344 		if ( y_24pin )
345 		 { for ( ; inp < in_end; inp++, out_end += 24 )
346 		    { gdev_prn_transpose_8x8(inp, line_size, out_end, 3);
347 		      gdev_prn_transpose_8x8(inp + line_size * 8, line_size, out_end + 1, 3);
348 		      gdev_prn_transpose_8x8(inp + line_size * 16, line_size, out_end + 2, 3);
349 		    }
350 		   /* Remove trailing 0s. */
351 		   while ( out_end > out && out_end[-1] == 0 &&
352 			   out_end[-2] == 0 && out_end[-3] == 0
353 			 )
354 		     out_end -= 3;
355 		 }
356 		else
357 		 { for ( ; inp < in_end; inp++, out_end += 8 )
358 		    { gdev_prn_transpose_8x8(inp, line_size, out_end, 1);
359 		    }
360 		   /* Remove trailing 0s. */
361 		   while ( out_end > out && out_end[-1] == 0 )
362 		     out_end--;
363 		 }
364 
365 		for ( pass = first_pass; pass <= last_pass; pass++ )
366 		   {
367 		for ( out_blk = outp = out; outp < out_end; )
368 		 { /* Skip a run of leading 0s. */
369 		   /* At least 10 are needed to make tabbing worth it. */
370 		   /* We do everything by 3's to avoid having to make */
371 		   /* different cases for 9- and 24-pin. */
372 
373 		   if ( *outp == 0 && outp + 12 <= out_end &&
374 			outp[1] == 0 && outp[2] == 0 &&
375 			(outp[3] | outp[4] | outp[5]) == 0 &&
376 			(outp[6] | outp[7] | outp[8]) == 0 &&
377 			(outp[9] | outp[10] | outp[11]) == 0
378 		      )
379 		    {	byte *zp = outp;
380 			int tpos;
381 			byte *newp;
382 			outp += 12;
383 			while ( outp + 3 <= out_end && *outp == 0 &&
384 				outp[1] == 0 && outp[2] == 0
385 			      )
386 				outp += 3;
387 			tpos = (outp - out) / bytes_per_space;
388 			newp = out + tpos * bytes_per_space;
389 			if ( newp > zp + 10 )
390 			 { /* Output preceding bit data. */
391 			   if ( zp > out_blk )	/* only false at */
392 						/* beginning of line */
393 			     epsc_output_run(out_blk, (int)(zp - out_blk),
394 					    y_mult, start_graphics,
395 					    prn_stream, pass);
396 			   /* Tab over to the appropriate position. */
397 			   fprintf(prn_stream, "\033D%c%c\t", tpos, 0);
398 			   out_blk = outp = newp;
399 			 }
400 		   }
401 		  else
402 			outp += y_mult;
403 		 }
404 		if ( outp > out_blk )
405 			epsc_output_run(out_blk, (int)(outp - out_blk),
406 				       y_mult, start_graphics,
407 				       prn_stream, pass);
408 
409 			fputc('\r', prn_stream);
410 		   }
411 			} while (nextcolor);
412 		skip = 24;
413 		lnum += 8 * y_mult;
414 	   }
415 
416 	/* Eject the page and reinitialize the printer */
417 	fputs("\f\033@", prn_stream);
418 
419 
420 	gs_free(pdev->memory, (char *)out, out_size+1, 1, "epsc_print_page(out)");
421 	gs_free(pdev->memory, (char *)in, in_size+1, 1, "epsc_print_page(in)");
422 	if (gx_device_has_color(pdev))
423 	    gs_free(pdev->memory, (char *)color_in, color_in_size+1, 1, "epsc_print_page(rin)");
424 	return 0;
425 }
426 
427 /* Output a single graphics command. */
428 /* pass=0 for all columns, 1 for even columns, 2 for odd columns. */
429 private void
epsc_output_run(byte * data,int count,int y_mult,char start_graphics,FILE * prn_stream,int pass)430 epsc_output_run(byte *data, int count, int y_mult,
431   char start_graphics, FILE *prn_stream, int pass)
432 {	int xcount = count / y_mult;
433 	fputc(033, prn_stream);
434 	if ( !(start_graphics & ~3) )
435 	   {	fputc("KLYZ"[(int)start_graphics], prn_stream);
436 	   }
437 	else
438 	   {	fputc('*', prn_stream);
439 		fputc(start_graphics & ~DD, prn_stream);
440 	   }
441 	fputc(xcount & 0xff, prn_stream);
442 	fputc(xcount >> 8, prn_stream);
443 	if ( !pass )
444 		fwrite((char *)data, 1, count, prn_stream);
445 	else
446 	   {	/* Only write every other column of y_mult bytes. */
447 		int which = pass;
448 		byte *dp = data;
449 		register int i, j;
450 		for ( i = 0; i < xcount; i++, which++ )
451 		  for ( j = 0; j < y_mult; j++, dp++ )
452 		   {	putc(((which & 1) ? *dp : 0), prn_stream);
453 		   }
454 	   }
455 }
456