1 /*
2   Copyright (C)1998 Ivan Schreter
3 
4   This file is part of GNU Ghostscript.
5 
6   GNU Ghostscript is distributed in the hope that it will be useful, but
7   WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility to
8   anyone for the consequences of using it or for whether it serves any
9   particular purpose or works at all, unless he says so in writing.  Refer
10   to the GNU General Public License for full details.
11 
12   This source is partially based on deskjet device driver for Ghostscript.
13 */
14 
15 /* gdevop4w.c */
16 /* OkiPage 4w/4w+ LED printer driver for Ghostscript */
17 
18 #include "gdevprn.h"
19 #include "gdevpcl.h"	/* for mode 2 compression */
20 
21 /*
22  * Thanks for various improvements to:
23  *	(none so far)
24  */
25 
26 /*
27  * TODO: You may select a default resolution of 150, 300 or 600 DPI
28  * TODO: in the makefile, or an actual resolution on the gs command line.
29  *
30  * If the preprocessor symbol A4 is defined, the default paper size is
31  * the European A4 size; otherwise it is the U.S. letter size (8.5"x11").
32  *
33  * You may find the following test page useful in determining the exact
34  * margin settings on your printer.  It prints four big arrows which
35  * point exactly to the for corners of an A4 sized paper. Of course the
36  * arrows cannot appear in full on the paper, and they are truncated by
37  * the margins. The margins measured on the testpage must match those
38  * in gdevdjet.c.  So the testpage indicates two facts: 1) the page is
39  * not printed in the right position 2) the page is truncated too much
40  * because the margins are wrong. Setting wrong margins in gdevop4w.c
41  * will also move the page, so both facts should be matched with the
42  * real world.
43 
44 %!
45         newpath
46         0 0 moveto 144 72 lineto 72 144 lineto
47         closepath fill stroke 0 0 moveto 144 144 lineto stroke
48 
49         595.27 841.88 moveto 451.27 769.88 lineto 523.27 697.88 lineto
50         closepath fill stroke 595.27 841.88 moveto 451.27 697.88 lineto stroke
51 
52         0 841.88 moveto 144 769.88 lineto 72 697.88 lineto
53         closepath fill stroke 0 841.88 moveto 144 697.88 lineto stroke
54 
55         595.27 0 moveto 451.27 72 lineto 523.27 144 lineto
56         closepath fill stroke 595.27 0 moveto 451.27 144 lineto stroke
57 
58         /Helvetica findfont
59         14 scalefont setfont
60         100 600 moveto
61         (This is an A4 testpage. The arrows should point exactly to the) show
62         100 580 moveto
63         (corners and the margins should match those given in gdev*.c) show
64         showpage
65 
66  */
67 
68 /* Define the default, maximum resolutions. */
69 #ifndef X_DPI
70 #  define X_DPI 300
71 #endif
72 #ifndef Y_DPI
73 #  define Y_DPI 300
74 #endif
75 
76 /* Margins are left, bottom, right, top. */
77 /* from Frans van Hoesel hoesel@rugr86.rug.nl. */
78 /* A4 has a left margin of 1/8 inch and at a printing width of
79  * 8 inch this give a right margin of 0.143. The 0.09 top margin is
80  * not the actual margin - which is 0.07 - but compensates for the
81  * inexact paperlength which is set to 117 10ths.
82  * Somebody should check for letter sized paper. I left it at 0.07".
83  */
84 #define OKI4W_MARGINS_LETTER	0.125f, 0.25f, 0.125f, 0.07f
85 #define OKI4W_MARGINS_A4	0.125f, 0.25f, 0.125f, 0.07f
86 
87 /* We round up the LINE_SIZE to a multiple of a ulong for faster scanning. */
88 #define word ulong
89 #define W sizeof(word)
90 #define byte unsigned char
91 
92 /* The device descriptors */
93 static dev_proc_open_device(oki4w_open);
94 static dev_proc_close_device(oki4w_close);
95 static dev_proc_print_page(oki4w_print_page);
96 
97 static gx_device_procs prn_hp_procs =
98   prn_params_procs(oki4w_open, gdev_prn_output_page, oki4w_close,
99                    gdev_prn_get_params, gdev_prn_put_params);
100 
101 gx_device_printer far_data gs_oki4w_device =
102   prn_device(prn_hp_procs, "oki4w",
103         DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
104         X_DPI, Y_DPI,
105         0, 0, 0, 0,		/* margins filled in by oki4w_open */
106         1, oki4w_print_page);
107 
108 #define ppdev ((gx_device_printer *)pdev)
109 
110 /* Find out paper size code */
111 static int
oki_paper_size(gx_device * dev)112 oki_paper_size(gx_device *dev)
113 {
114         float height_inches = dev->height / dev->y_pixels_per_inch;
115         return
116                 height_inches >= 15.9 ? 0x1b/*PAPER_SIZE_A3*/ :
117                 height_inches >= 11.8 ? 0x03/*PAPER_SIZE_LEGAL*/ :
118                 height_inches >= 11.1 ? 0x1a /*PAPER_SIZE_A4*/ :
119                 height_inches >= 8.3 ? 0x02 /*PAPER_SIZE_LETTER*/ :
120                         0x19 /*PAPER_SIZE_A5*/;
121 }
122 
123 /* Open the printer, adjusting the margins if necessary. */
124 static int
oki4w_open(gx_device * pdev)125 oki4w_open(gx_device *pdev)
126 {	/* Change the margins if necessary. */
127         const float *m = 0;
128         static const float m_a4[4] = { OKI4W_MARGINS_A4 };
129         static const float m_letter[4] = { OKI4W_MARGINS_LETTER };
130         m = (oki_paper_size(pdev) == 0x1a /*PAPER_SIZE_A4*/ ? m_a4 : m_letter);
131         if ( m != 0 )
132           gx_device_set_margins(pdev, m, true);
133         return gdev_prn_open(pdev);
134 }
135 
136 /* oki4w_close is only here to eject odd numbered pages in duplex mode. */
137 static int
oki4w_close(gx_device * pdev)138 oki4w_close(gx_device *pdev)
139 {
140         /* RJW: We must call the close entry point for the class. */
141         return gdev_prn_close(pdev);
142 /*
143         if ( pdev->Duplex_set >= 0 && pdev->Duplex )
144           {	gdev_prn_open_printer(pdev, 1);
145                 gp_fputs("\033$B\033\177", ppdev->file);
146                 gp_fputc(0, ppdev->file);
147                 return gdev_prn_close_printer(pdev);
148           }
149 */
150         return 0;
151 }
152 
153 #undef ppdev
154 
155 /* ------ Internal routines ------ */
156 
157 /* Send the page to the printer.  For speed, compress each scan line, */
158 /* since computer-to-printer communication time is often a bottleneck. */
159 static int
oki4w_print_page(gx_device_printer * pdev,gp_file * prn_stream)160 oki4w_print_page(gx_device_printer *pdev, gp_file *prn_stream)
161 {
162         int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
163         int line_size_words = (line_size + W - 1) / W;
164         uint storage_size_words = line_size_words * 8; /* data, out_row, out_row_alt, prev_row */
165         word *storage = (ulong *)gs_malloc(pdev->memory->non_gc_memory, storage_size_words, W,
166                                            "oki4w_print_page");
167         word
168           *data_words,
169           *out_row_words;
170 #define data ((byte *)data_words)
171 #define out_row ((byte *)out_row_words)
172         byte *out_data;
173         int x_dpi = (int)pdev->x_pixels_per_inch;
174         int y_dpi = (int)pdev->y_pixels_per_inch;
175         int y_dots_per_pixel = x_dpi / y_dpi;
176         int dpi_code, compress_code;
177         int num_rows = dev_print_scan_lines(pdev);
178 
179         int out_count;
180         int paper_size = oki_paper_size((gx_device *)pdev);
181         int code = 0;
182         /* bool dup = pdev->Duplex;
183         bool dupset = pdev->Duplex_set >= 0; */
184 
185         if ( storage == 0 )	/* can't allocate working area */
186                 return_error(gs_error_VMerror);
187         data_words = storage;
188         out_row_words = data_words + (line_size_words * 2);
189         /* Clear temp storage */
190         memset(data, 0, storage_size_words * W);
191 
192         out_data = out_row;
193 
194         if (y_dpi == 150) {
195                 dpi_code = 3;
196         } else if (y_dpi == 300) {
197                 dpi_code = 5;
198         } else {
199                 dpi_code = 7;
200         }
201         compress_code = 2;
202 
203         /* Initialize printer. */
204 /*	if ( pdev->PageCount == 0 ) { */
205                 /* Put out init string before page. */
206                 gp_fprintf(prn_stream, "\x1B%%-98765X\x1C\x14\x03\x41i\x10\x1C"
207                         "\x14\x05\x41\x65%cf%c\x1C\x14\x09\x42\x61%cb\x02\x63"
208                         "\x01\x65%c\x1C\x7F\x39\x1B&B\x1B&A\x07%c\x01%c"
209                         "\x01%c%c%c%c\x1B$A",
210                         dpi_code, dpi_code, 0, 0, 0, paper_size,
211                         0, dpi_code, dpi_code, 0);
212 /*	} */
213 
214         /* Send each scan line in turn */
215            {	int lnum;
216                 int num_blank_lines = 0;
217                 word rmask = ~(word)0 << (-pdev->width & (W * 8 - 1));
218 
219                 /* Transfer raster graphics. */
220                 for ( lnum = 0; lnum < num_rows; lnum++ )
221                    {	register word *end_data =
222                                 data_words + line_size_words;
223                         int i;
224                         code = gdev_prn_copy_scan_lines(pdev, lnum,
225                                                  (byte *)data, line_size);
226                         if ( code < 0 )
227                                 break;
228                         /* Mask off 1-bits beyond the line width. */
229                         end_data[-1] &= rmask;
230                         /* Remove trailing 0s. */
231                         while ( end_data > data_words && end_data[-1] == 0 )
232                           end_data--;
233                         if ( end_data == data_words )
234                            {	/* Blank line */
235                                 num_blank_lines++;
236                                 continue;
237                            }
238 
239                         /* We've reached a non-blank line. */
240                         /* Put out a spacing command if necessary. */
241                         if ( num_blank_lines == lnum )
242                         {	/* We're at the top of a page. */
243                                 /* TODO: skip top_margin lines... */
244                                 /* num_blank_lines += xxx */
245                                 /* Skip blank lines if any */
246                                 if (num_blank_lines > 0) {
247                                         gp_fprintf(prn_stream, "\x1b*B%c%c",
248                                                    num_blank_lines & 0xff,
249                                                    num_blank_lines >> 8);
250                                 }
251                         }
252                         else if ( num_blank_lines != 0 )
253                         {
254                                 /* Skip blank lines if any */
255                                 gp_fprintf(prn_stream, "\x1b*B%c%c",
256                                            num_blank_lines & 0xff,
257                                            num_blank_lines >> 8);
258                         }
259                         num_blank_lines = 0;
260 
261                         /* Compress the data */
262                         out_count = gdev_pcl_mode2compress(data_words,
263                                 end_data, out_data);
264 
265                         /* Transfer the data */
266                         for (i = 0; i < y_dots_per_pixel; ++i) {
267                                 gp_fprintf(prn_stream, "\033*A%c%c%c",
268                                            compress_code,
269                                            out_count & 0xff, out_count >> 8);
270                                 gp_fwrite(out_data, sizeof(byte), out_count,
271                                            prn_stream);
272                         }
273                    }
274         }
275 
276         /* end raster graphics and eject page */
277         gp_fprintf(prn_stream, "\x1b$B\x1b\x7f%c", 0);
278 
279         /* free temporary storage */
280         gs_free(pdev->memory->non_gc_memory, (char *)storage, storage_size_words, W, "oki4w_print_page");
281 
282         return code;
283 }
284