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 /* High-res 24Dot-matrix printer driver */
17 
18 /* Supported printers
19  *  NEC P6 and similar, implemented by Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de)
20  *  Epson LQ850, implemented by Christian Felsch (felsch@tu-harburg.d400.de)
21  */
22 
23 #include "gdevprn.h"
24 
25 /* Driver for NEC P6 */
26 static dev_proc_print_page (necp6_print_page);
27 const gx_device_printer far_data gs_necp6_device =
28   prn_device (prn_std_procs, "necp6",
29               DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
30               360, 360,
31               0, 0, 0.5, 0,	/* margins */
32               1, necp6_print_page);
33 
34 /* Driver for Epson LQ850 */
35 /* I've tested this driver on a BJ300 with LQ850 emulation and there it produce correct 360x360dpi output. */
36 static dev_proc_print_page (lq850_print_page);
37 const gx_device_printer gs_lq850_device =
38   prn_device (prn_std_procs, "lq850",
39               DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
40               360, 360,
41               0, 0, 0.5, 0,	/* margins */
42               1, lq850_print_page);
43 
44 /* ------ Internal routines ------ */
45 
46 /* Forward references */
47 static void dot24_output_run (byte *, int, int, FILE *);
48 static void dot24_improve_bitmap (byte *, int);
49 
50 /* Send the page to the printer. */
51 static int
dot24_print_page(gx_device_printer * pdev,FILE * prn_stream,char * init_string,int init_len)52 dot24_print_page (gx_device_printer *pdev, FILE *prn_stream, char *init_string, int init_len)
53 {
54   int xres = (int)pdev->x_pixels_per_inch;
55   int yres = (int)pdev->y_pixels_per_inch;
56   int x_high = (xres == 360);
57   int y_high = (yres == 360);
58   int bits_per_column = (y_high ? 48 : 24);
59   uint line_size = gdev_prn_raster (pdev);
60   uint in_size = line_size * bits_per_column;
61   byte *in = (byte *) gs_malloc (pdev->memory, in_size, 1, "dot24_print_page (in)");
62   uint out_size = ((pdev->width + 7) & -8) * 3;
63   byte *out = (byte *) gs_malloc (pdev->memory, out_size, 1, "dot24_print_page (out)");
64   int y_passes = (y_high ? 2 : 1);
65   int dots_per_space = xres / 10;	/* pica space = 1/10" */
66   int bytes_per_space = dots_per_space * 3;
67   int skip = 0, lnum = 0, ypass;
68 
69   /* Check allocations */
70   if (in == 0 || out == 0)
71     {
72       if (out)
73         gs_free (pdev->memory, (char *) out, out_size, 1, "dot24_print_page (out)");
74       if (in)
75         gs_free (pdev->memory, (char *) in, in_size, 1, "dot24_print_page (in)");
76       return_error (gs_error_VMerror);
77     }
78 
79   /* Initialize the printer and reset the margins. */
80   fwrite (init_string, init_len - 1, sizeof (char), prn_stream);
81   fputc ((int) (pdev->width / pdev->x_pixels_per_inch * 10) + 2,
82          prn_stream);
83 
84   /* Print lines of graphics */
85   while (lnum < pdev->height)
86     {
87       byte *inp;
88       byte *in_end;
89       byte *out_end;
90       byte *out_blk;
91       register byte *outp;
92       int lcnt;
93 
94       /* Copy 1 scan line and test for all zero. */
95       gdev_prn_copy_scan_lines (pdev, lnum, in, line_size);
96       if (in[0] == 0
97           && !memcmp ((char *) in, (char *) in + 1, line_size - 1))
98         {
99           lnum++;
100           skip += 2 - y_high;
101           continue;
102         }
103 
104       /* Vertical tab to the appropriate position. */
105       while ((skip >> 1) > 255)
106         {
107           fputs ("\033J\377", prn_stream);
108           skip -= 255 * 2;
109         }
110 
111       if (skip)
112         {
113           if (skip >> 1)
114             fprintf (prn_stream, "\033J%c", skip >> 1);
115           if (skip & 1)
116             fputc ('\n', prn_stream);
117         }
118 
119       /* Copy the rest of the scan lines. */
120       if (y_high)
121         {
122           inp = in + line_size;
123           for (lcnt = 1; lcnt < 24; lcnt++, inp += line_size)
124             if (!gdev_prn_copy_scan_lines (pdev, lnum + lcnt * 2, inp,
125                                            line_size))
126               {
127                 memset (inp, 0, (24 - lcnt) * line_size);
128                 break;
129               }
130           inp = in + line_size * 24;
131           for (lcnt = 0; lcnt < 24; lcnt++, inp += line_size)
132             if (!gdev_prn_copy_scan_lines (pdev, lnum + lcnt * 2 + 1, inp,
133                                            line_size))
134               {
135                 memset (inp, 0, (24 - lcnt) * line_size);
136                 break;
137               }
138         }
139       else
140         {
141           lcnt = 1 + gdev_prn_copy_scan_lines (pdev, lnum + 1, in + line_size,
142                                                in_size - line_size);
143           if (lcnt < 24)
144             /* Pad with lines of zeros. */
145             memset (in + lcnt * line_size, 0, in_size - lcnt * line_size);
146         }
147 
148       for (ypass = 0; ypass < y_passes; ypass++)
149         {
150           out_end = out;
151           inp = in;
152           if (ypass)
153             inp += line_size * 24;
154           in_end = inp + line_size;
155 
156           for (; inp < in_end; inp++, out_end += 24)
157             {
158               memflip8x8 (inp, line_size, out_end, 3);
159               memflip8x8 (inp + line_size * 8, line_size, out_end + 1, 3);
160               memflip8x8 (inp + line_size * 16, line_size, out_end + 2, 3);
161             }
162           /* Remove trailing 0s. */
163           while (out_end - 3 >= out && out_end[-1] == 0
164                  && out_end[-2] == 0 && out_end[-3] == 0)
165             out_end -= 3;
166 
167           for (out_blk = outp = out; outp < out_end;)
168             {
169               /* Skip a run of leading 0s. */
170               /* At least 10 are needed to make tabbing worth it. */
171 
172               if (outp[0] == 0 && outp + 12 <= out_end
173                   && outp[1] == 0 && outp[2] == 0
174                   && outp[3] == 0 && outp[4] == 0 && outp[5] == 0
175                   && outp[6] == 0 && outp[7] == 0 && outp[8] == 0
176                   && outp[9] == 0 && outp[10] == 0 && outp[11] == 0)
177                 {
178                   byte *zp = outp;
179                   int tpos;
180                   byte *newp;
181                   outp += 12;
182                   while (outp + 3 <= out_end
183                          && outp[0] == 0 && outp[1] == 0 && outp[2] == 0)
184                     outp += 3;
185                   tpos = (outp - out) / bytes_per_space;
186                   newp = out + tpos * bytes_per_space;
187                   if (newp > zp + 10)
188                     {
189                       /* Output preceding bit data. */
190                       /* only false at beginning of line */
191                       if (zp > out_blk)
192                         {
193                           if (x_high)
194                             dot24_improve_bitmap (out_blk, (int) (zp - out_blk));
195                           dot24_output_run (out_blk, (int) (zp - out_blk),
196                                           x_high, prn_stream);
197                         }
198                       /* Tab over to the appropriate position. */
199                       fprintf (prn_stream, "\033D%c%c\t", tpos, 0);
200                       out_blk = outp = newp;
201                     }
202                 }
203               else
204                 outp += 3;
205             }
206           if (outp > out_blk)
207             {
208               if (x_high)
209                 dot24_improve_bitmap (out_blk, (int) (outp - out_blk));
210               dot24_output_run (out_blk, (int) (outp - out_blk), x_high,
211                               prn_stream);
212             }
213 
214           fputc ('\r', prn_stream);
215           if (ypass < y_passes - 1)
216             fputc ('\n', prn_stream);
217         }
218       skip = 48 - y_high;
219       lnum += bits_per_column;
220     }
221 
222   /* Eject the page and reinitialize the printer */
223   fputs ("\f\033@", prn_stream);
224   fflush (prn_stream);
225 
226   gs_free (pdev->memory, (char *) out, out_size, 1, "dot24_print_page (out)");
227   gs_free (pdev->memory, (char *) in, in_size, 1, "dot24_print_page (in)");
228 
229   return 0;
230 }
231 
232 /* Output a single graphics command. */
233 static void
dot24_output_run(byte * data,int count,int x_high,FILE * prn_stream)234 dot24_output_run (byte *data, int count, int x_high, FILE *prn_stream)
235 {
236   int xcount = count / 3;
237   fputc (033, prn_stream);
238   fputc ('*', prn_stream);
239   fputc ((x_high ? 40 : 39), prn_stream);
240   fputc (xcount & 0xff, prn_stream);
241   fputc (xcount >> 8, prn_stream);
242   fwrite (data, 1, count, prn_stream);
243 }
244 
245 /* If xdpi == 360, the P6 / LQ850 cannot print adjacent pixels.  Clear the
246    second last pixel of every run of set pixels, so that the last pixel
247    is always printed.  */
248 static void
dot24_improve_bitmap(byte * data,int count)249 dot24_improve_bitmap (byte *data, int count)
250 {
251   int i;
252   register byte *p = data + 6;
253 
254       for (i = 6; i < count; i += 3, p += 3)
255         {
256           p[-6] &= ~(~p[0] & p[-3]);
257           p[-5] &= ~(~p[1] & p[-2]);
258           p[-4] &= ~(~p[2] & p[-1]);
259         }
260       p[-6] &= ~p[-3];
261       p[-5] &= ~p[-2];
262       p[-4] &= ~p[-1];
263 
264 }
265 
266 static int
necp6_print_page(gx_device_printer * pdev,FILE * prn_stream)267 necp6_print_page(gx_device_printer *pdev, FILE *prn_stream)
268 {
269   char necp6_init_string [] = "\033@\033P\033l\000\r\034\063\001\033Q";
270 
271   return dot24_print_page(pdev, prn_stream, necp6_init_string, sizeof(necp6_init_string));
272 }
273 
274 static int
lq850_print_page(gx_device_printer * pdev,FILE * prn_stream)275 lq850_print_page(gx_device_printer *pdev, FILE *prn_stream)
276 {
277   char lq850_init_string [] = "\033@\033P\033l\000\r\033\053\001\033Q";
278 
279   return dot24_print_page(pdev, prn_stream, lq850_init_string, sizeof(lq850_init_string));
280 }
281