1 /* Copyright (C) 2001-2012 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134, San Rafael,
13    CA  94903, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 
17 /*
18  * Epson (and similar) dot-matrix printer driver for Ghostscript.
19  *
20  * Four devices are defined here: 'epson', 'eps9mid', 'eps9high', and 'ibmpro'.
21  * The 'epson' device is the generic device, for 9-pin and 24-pin printers.
22  * 'eps9high' is a special mode for 9-pin printers where scan lines are
23  * interleaved in multiple passes to produce high vertical resolution at
24  * the expense of several passes of the print head.  'eps9mid' is a special
25  * mode for 9 pin printers too, scan lines are interleaved but with next
26  * vertical line. 'ibmpro' is for the IBM ProPrinter, which has slightly
27  * (but only slightly) different control codes.
28  *
29  * Thanks to:
30  *	David Wexelblat (dwex@mtgzfs3.att.com) for the 'eps9high' code;
31  *	Guenther Thomsen (thomsen@cs.tu-berlin.de) for the 'eps9mid' code;
32  *	James W. Birdsall (jwbirdsa@picarefy.picarefy.com) for the
33  *	  'ibmpro' modifications;
34  *	Russell J. Lang (rjl@aladdin.com) for the 180x60 and 240x180 dpi
35  *	  enhancements.
36  */
37 #include "gdevprn.h"
38 
39 /*
40  * Define whether the printer is archaic -- so old that it doesn't
41  * support settable tabs, pitch, or left margin.  (This should be a
42  * run-time property....)  Note: the IBM ProPrinter is archaic.
43  */
44 /*#define ARCHAIC 1*/
45 
46 /*
47  * Define whether the printer is a Panasonic 9-pin printer,
48  * which sometimes doesn't recognize a horizontal tab command
49  * when a line contains a lot of graphics commands,
50  * requiring a "backspace, space" sequence before a tab.
51  */
52 /*#define TAB_HICCUP 1*/
53 
54 /*
55  * Define the minimum distance for which it's worth converting white space
56  * into a tab.  This can be specified in pixels (to save transmission time),
57  * in tenths of an inch (for printers where tabs provoke actual head motion),
58  * or both.  The distance must meet BOTH criteria for the driver to tab,
59  * so an irrelevant criterion should be set to 0 rather than infinite.
60  */
61 #define MIN_TAB_PIXELS 10
62 #define MIN_TAB_10THS 15
63 
64 /*
65  * Valid values for X_DPI:
66  *
67  *    For 9-pin printers: 60, 120, 240
68  *    For 24-pin printers: 60, 120, 180, 240, 360
69  *
70  * The value specified at compile time is the default value used if the
71  * user does not specify a resolution at runtime.
72  */
73 #ifndef X_DPI
74 #  define X_DPI 240
75 #endif
76 
77 /*
78  * For Y_DPI, a given printer will support a base resolution of 60 or 72;
79  * check the printer manual.  The Y_DPI value must be a multiple of this
80  * base resolution.  Valid values for Y_DPI:
81  *
82  *    For 9-pin printers: 1*base_res
83  *    For 24-pin printers: 1*base_res, 3*base_res
84  *
85  * The value specified at compile time is the default value used if the
86  * user does not specify a resolution at runtime.
87  */
88 
89 #ifndef Y_BASERES
90 #  define Y_BASERES 72
91 #endif
92 #ifndef Y_DPI
93 #  define Y_DPI (1*Y_BASERES)
94 #endif
95 
96 /* The device descriptors */
97 static dev_proc_print_page(epson_print_page);
98 static dev_proc_print_page(eps9mid_print_page);
99 static dev_proc_print_page(eps9high_print_page);
100 static dev_proc_print_page(ibmpro_print_page);
101 
102 /* Standard Epson device */
103 const gx_device_printer far_data gs_epson_device =
104   prn_device(prn_std_procs, "epson",
105         DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
106         X_DPI, Y_DPI,
107         0.25, 0.02, 0.25, 0.4,			/* margins */
108         1, epson_print_page);
109 
110 /* Mid-res (interleaved, 1 pass per line) 9-pin device */
111 const gx_device_printer far_data gs_eps9mid_device =
112   prn_device(prn_std_procs, "eps9mid",
113         DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
114         X_DPI, 3*Y_BASERES,
115         0.2, 0.0, 0, 0.0,			/* margins */
116         1, eps9mid_print_page);
117 
118 /* High-res (interleaved) 9-pin device */
119 const gx_device_printer far_data gs_eps9high_device =
120   prn_device(prn_std_procs, "eps9high",
121         DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
122         X_DPI, 3*Y_BASERES,
123         0.2, 0.0, 0.0, 0.0,			/* margins */
124         1, eps9high_print_page);
125 
126 /* IBM ProPrinter device */
127 const gx_device_printer far_data gs_ibmpro_device =
128   prn_device(prn_std_procs, "ibmpro",
129         DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
130         X_DPI, Y_DPI,
131         0.2, 0.0, 0.0, 0.0,			/* margins */
132         1, ibmpro_print_page);
133 
134 /* ------ Driver procedures ------ */
135 
136 /* Forward references */
137 static void eps_output_run(byte *, int, int, char, FILE *, int);
138 
139 /* Send the page to the printer. */
140 #define DD 0x40				/* double density flag */
141 static int
eps_print_page(gx_device_printer * pdev,FILE * prn_stream,int y_9pin_high,const char * init_string,int init_length,const char * end_string,int archaic,int tab_hiccup)142 eps_print_page(gx_device_printer *pdev, FILE *prn_stream, int y_9pin_high,
143   const char *init_string, int init_length, const char *end_string,
144   int archaic, int tab_hiccup)
145 {
146         static const char graphics_modes_9[5] =
147         {
148         -1, 0 /*60*/, 1	/*120*/, 7 /*180*/, DD+3 /*240*/
149         };
150 
151         static const char graphics_modes_24[7] =
152         {
153         -1, 32 /*60*/, 33 /*120*/, 39 /*180*/,
154         DD+35 /*240*/, -1, DD+40 /*360*/
155         };
156 
157         int y_24pin = (y_9pin_high ? 0 : pdev->y_pixels_per_inch > 72);
158         int in_y_mult = ((y_24pin | y_9pin_high) ? 3 : 1);
159         int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
160         /* Note that in_size is a multiple of 8. */
161         int in_size = line_size * (8 * in_y_mult);
162         byte *buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "eps_print_page(buf1)");
163         byte *buf2 = (byte *)gs_malloc(pdev->memory, in_size, 1, "eps_print_page(buf2)");
164         byte *in = buf1;
165         byte *out = buf2;
166         int out_y_mult = (y_24pin ? 3 : 1);
167         int x_dpi = (int)pdev->x_pixels_per_inch;
168         char start_graphics =
169                 (y_24pin ? graphics_modes_24 : graphics_modes_9)[x_dpi / 60];
170         int first_pass = (start_graphics & DD ? 1 : 0);
171         int last_pass = first_pass * (y_9pin_high == 2 ? 1 : 2);
172         int y_passes = (y_9pin_high ? 3 : 1);
173         int dots_per_space = x_dpi / 10;	/* pica space = 1/10" */
174         int bytes_per_space = dots_per_space * out_y_mult;
175         int tab_min_pixels = x_dpi * MIN_TAB_10THS / 10;
176         int skip = 0, lnum = 0, pass, ypass;
177 
178         /* Check allocations */
179         if ( buf1 == 0 || buf2 == 0 )
180         {	if ( buf1 )
181                   gs_free(pdev->memory, (char *)buf1, in_size, 1, "eps_print_page(buf1)");
182                 if ( buf2 )
183                   gs_free(pdev->memory, (char *)buf2, in_size, 1, "eps_print_page(buf2)");
184                 return_error(gs_error_VMerror);
185         }
186 
187         /* Initialize the printer and reset the margins. */
188         fwrite(init_string, 1, init_length, prn_stream);
189         if ( init_string[init_length - 1] == 'Q' )
190                 fputc((int)(pdev->width / pdev->x_pixels_per_inch * 10) + 2,
191                       prn_stream);
192 
193         /* Calculate the minimum tab distance. */
194         if ( tab_min_pixels < max(MIN_TAB_PIXELS, 3) )
195                 tab_min_pixels = max(MIN_TAB_PIXELS, 3);
196         tab_min_pixels -= tab_min_pixels % 3;	/* simplify life */
197 
198         /* Print lines of graphics */
199         while ( lnum < pdev->height )
200         {
201                 byte *in_data;
202                 byte *inp;
203                 byte *in_end;
204                 byte *out_end;
205                 byte *out_blk;
206                 register byte *outp;
207                 int lcnt;
208 
209                 /* Copy 1 scan line and test for all zero. */
210                 gdev_prn_get_bits(pdev, lnum, in, &in_data);
211                 if ( in_data[0] == 0 &&
212                      !memcmp((char *)in_data, (char *)in_data + 1, line_size - 1)
213                    )
214                 {
215                         lnum++;
216                         skip += 3 / in_y_mult;
217                         continue;
218                 }
219 
220                 /* Vertical tab to the appropriate position. */
221                 while ( skip > 255 )
222                 {
223                         fputs("\033J\377", prn_stream);
224                         skip -= 255;
225                 }
226                 if ( skip )
227                 {
228                         fprintf(prn_stream, "\033J%c", skip);
229                 }
230 
231                 /* Copy the the scan lines. */
232                 lcnt = gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
233                 if ( lcnt < 8 * in_y_mult )
234                 {	/* Pad with lines of zeros. */
235                         memset(in + lcnt * line_size, 0,
236                                in_size - lcnt * line_size);
237                 }
238 
239                 if ( y_9pin_high == 2 )
240                 {	/* Force printing of every dot in one pass */
241                         /* by reducing vertical resolution */
242                         /* (ORing with the next line of data). */
243                         /* This is necessary because some Epson compatibles */
244                         /* can't print neighboring dots. */
245                         int i;
246                         for ( i = 0; i < line_size * in_y_mult; ++i )
247                                 in_data[i] |= in_data[i + line_size];
248                 }
249 
250                 if ( y_9pin_high )
251                 {	/* Shuffle the scan lines */
252                         byte *p;
253                         int i;
254                         static const char index[] =
255                         {  0,  8, 16,  1,  9, 17,
256                            2, 10, 18,  3, 11, 19,
257                            4, 12, 20,  5, 13, 21,
258                            6, 14, 22,  7, 15, 23
259                         };
260 
261                         for ( i = 0; i < 24; i++ )
262                         {
263                                 memcpy(out+(index[i]*line_size),
264                                        in+(i*line_size), line_size);
265                         }
266                         p = in;
267                         in = out;
268                         out = p;
269                 }
270 
271         for ( ypass = 0; ypass < y_passes; ypass++ )
272         {
273             for ( pass = first_pass; pass <= last_pass; pass++ )
274             {
275                 /* We have to 'transpose' blocks of 8 pixels x 8 lines, */
276                 /* because that's how the printer wants the data. */
277                 /* If we are in a 24-pin mode, we have to transpose */
278                 /* groups of 3 lines at a time. */
279 
280                 if ( pass == first_pass )
281                 {
282                     out_end = out;
283                     inp = in;
284                     in_end = inp + line_size;
285 
286                     if ( y_24pin )
287                     {
288                         for ( ; inp < in_end; inp++, out_end += 24 )
289                         {
290                             gdev_prn_transpose_8x8(inp, line_size, out_end, 3);
291                             gdev_prn_transpose_8x8(inp + line_size * 8,
292                                                    line_size, out_end + 1, 3);
293                             gdev_prn_transpose_8x8(inp + line_size * 16,
294                                                    line_size, out_end + 2, 3);
295                         }
296                         /* Remove trailing 0s. */
297                         while ( out_end > out && out_end[-1] == 0 &&
298                                 out_end[-2] == 0 && out_end[-3] == 0)
299                         {
300                              out_end -= 3;
301                         }
302                     }
303                     else
304                     {
305                         for ( ; inp < in_end; inp++, out_end += 8 )
306                         {
307                             gdev_prn_transpose_8x8(inp + (ypass * 8*line_size),
308                                                    line_size, out_end, 1);
309                         }
310                         /* Remove trailing 0s. */
311                         while ( out_end > out && out_end[-1] == 0 )
312                         {
313                             out_end--;
314                         }
315                     }
316                 }
317 
318                 for ( out_blk = outp = out; outp < out_end; )
319                 {
320                     /* Skip a run of leading 0s.  At least */
321                     /* tab_min_pixels are needed to make tabbing */
322                     /* worth it.  We do everything by 3's to */
323                     /* avoid having to make different cases */
324                     /* for 9- and 24-pin. */
325                    if ( !archaic &&
326                         *outp == 0 && out_end - outp >= tab_min_pixels &&
327                         (outp[1] | outp[2]) == 0 &&
328                         !memcmp((char *)outp, (char *)outp + 3,
329                                 tab_min_pixels - 3)
330                       )
331                     {
332                         byte *zp = outp;
333                         int tpos;
334                         byte *newp;
335 
336                         outp += tab_min_pixels;
337                         while ( outp + 3 <= out_end &&
338                                 *outp == 0 &&
339                                 outp[1] == 0 && outp[2] == 0 )
340                         {
341                             outp += 3;
342                         }
343                         tpos = (outp - out) / bytes_per_space;
344                         newp = out + tpos * bytes_per_space;
345                         if ( newp > zp + 10 )
346                         {
347                             /* Output preceding bit data.*/
348                             if ( zp > out_blk )
349                             {
350                                 /* only false at beginning of line */
351                                 eps_output_run(out_blk, (int)(zp - out_blk),
352                                                out_y_mult, start_graphics,
353                                                prn_stream,
354                                                (y_9pin_high == 2 ?
355                                                 (1 + ypass) & 1 : pass));
356                             }
357                             /* Tab over to the appropriate position. */
358                             if ( tab_hiccup )
359                               fputs("\010 ", prn_stream); /* bksp, space */
360                             /* The following statement is broken up */
361                             /* to work around a bug in emx/gcc. */
362                             fprintf(prn_stream, "\033D%c", tpos);
363                             fputc(0, prn_stream);
364                             fputc('\t', prn_stream);
365                             out_blk = outp = newp;
366                         }
367                     }
368                     else
369                     {
370                         outp += out_y_mult;
371                     }
372                 }
373                 if ( outp > out_blk )
374                 {
375                     eps_output_run(out_blk, (int)(outp - out_blk),
376                                    out_y_mult, start_graphics,
377                                    prn_stream,
378                                    (y_9pin_high == 2 ? (1 + ypass) & 1 : pass));
379                 }
380 
381                 fputc('\r', prn_stream);
382             }
383             if ( ypass < y_passes - 1 )
384                 fputs("\033J\001", prn_stream);
385         }
386         skip = 24 - y_passes + 1;		/* no skip on last Y pass */
387         lnum += 8 * in_y_mult;
388         }
389 
390         /* Eject the page and reinitialize the printer */
391         fputs(end_string, prn_stream);
392         fflush(prn_stream);
393 
394         gs_free(pdev->memory, (char *)buf2, in_size, 1, "eps_print_page(buf2)");
395         gs_free(pdev->memory, (char *)buf1, in_size, 1, "eps_print_page(buf1)");
396         return 0;
397 }
398 
399 /* Output a single graphics command. */
400 /* pass=0 for all columns, 1 for even columns, 2 for odd columns. */
401 static void
eps_output_run(byte * data,int count,int y_mult,char start_graphics,FILE * prn_stream,int pass)402 eps_output_run(byte *data, int count, int y_mult,
403   char start_graphics, FILE *prn_stream, int pass)
404 {
405         int xcount = count / y_mult;
406 
407         fputc(033, prn_stream);
408         if ( !(start_graphics & ~3) )
409         {
410                 fputc("KLYZ"[(int)start_graphics], prn_stream);
411         }
412         else
413         {
414                 fputc('*', prn_stream);
415                 fputc(start_graphics & ~DD, prn_stream);
416         }
417         fputc(xcount & 0xff, prn_stream);
418         fputc(xcount >> 8, prn_stream);
419         if ( !pass )
420         {
421                 fwrite(data, 1, count, prn_stream);
422         }
423         else
424         {
425                 /* Only write every other column of y_mult bytes. */
426                 int which = pass;
427                 register byte *dp = data;
428                 register int i, j;
429 
430                 for ( i = 0; i < xcount; i++, which++ )
431                 {
432                         for ( j = 0; j < y_mult; j++, dp++ )
433                         {
434                                 putc(((which & 1) ? *dp : 0), prn_stream);
435                         }
436                 }
437         }
438 }
439 
440 /* The print_page procedures are here, to avoid a forward reference. */
441 #ifndef ARCHAIC
442 #  define ARCHAIC 0
443 #endif
444 #ifndef TAB_HICCUP
445 #  define TAB_HICCUP 0
446 #endif
447 
448 #define ESC 0x1b
449 static const char eps_init_string[] = {
450 #if ARCHAIC
451         ESC, '@', 022 /*^R*/, ESC, 'Q'
452 #else
453         ESC, '@', ESC, 'P', ESC, 'l', 0, '\r', ESC, 'Q'
454 #endif
455 };
456 
457 static int
epson_print_page(gx_device_printer * pdev,FILE * prn_stream)458 epson_print_page(gx_device_printer *pdev, FILE *prn_stream)
459 {
460         return eps_print_page(pdev, prn_stream, 0, eps_init_string,
461                               sizeof(eps_init_string), "\f\033@",
462                               ARCHAIC, TAB_HICCUP);
463 }
464 
465 static int
eps9high_print_page(gx_device_printer * pdev,FILE * prn_stream)466 eps9high_print_page(gx_device_printer *pdev, FILE *prn_stream)
467 {
468         return eps_print_page(pdev, prn_stream, 1, eps_init_string,
469                               sizeof(eps_init_string), "\f\033@",
470                               ARCHAIC, TAB_HICCUP);
471 }
472 
473 static int
eps9mid_print_page(gx_device_printer * pdev,FILE * prn_stream)474 eps9mid_print_page(gx_device_printer *pdev, FILE *prn_stream)
475 {
476         return eps_print_page(pdev, prn_stream, 2, eps_init_string,
477                               sizeof(eps_init_string), "\f\033@",
478                               ARCHAIC, TAB_HICCUP);
479 }
480 
481 static int
ibmpro_print_page(gx_device_printer * pdev,FILE * prn_stream)482 ibmpro_print_page(gx_device_printer *pdev, FILE *prn_stream)
483 {
484     /*
485      * IBM Proprinter Guide to Operations, p. 4-5: "DC1: Select Printer: Sets
486      * the printer to accept data from your computer."  Prevents printer from
487      * interpreting first characters as literal text.
488      */
489 #define DC1 0x11
490         static const char ibmpro_init_string[] = {
491                 DC1, ESC, '3', 0x30
492         };
493 #undef DC1
494         return eps_print_page(pdev, prn_stream, 0, ibmpro_init_string,
495                               sizeof(ibmpro_init_string), "\f", 1, 0);
496 }
497