summaryrefslogtreecommitdiff
blob: cbb9c469290bad81d39fe3ae10cf0f7ded24be07 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
/* Copyright (C) 2001-2020 Artifex Software, Inc.
   All Rights Reserved.

   This software is provided AS-IS with no warranty, either express or
   implied.

   This software is distributed under license and may not be copied,
   modified or distributed except as expressly authorized under the terms
   of the license contained in the file LICENSE in this distribution.

   Refer to licensing information at http://www.artifex.com or contact
   Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
   CA 94945, U.S.A., +1(415)492-9861, for further information.
*/

/* Canon Bubble Jet BJ-10e, BJ200, and BJ300 printer driver */
#include "gdevprn.h"

/*
 * The following is taken from the BJ200 Programmer's manual.  The top
 * margin is 3mm (0.12"), and the bottom margin is 6.4mm (0.25").  The
 * left and right margin depend on the type of paper -- US letter or
 * A4 -- but ultimately rest on a print width of 203.2mm (8").  For letter
 * paper, the left margin (and hence the right) is 6.4mm (0.25"), while
 * for A4 paper, both are 3.4mm (0.13").
 *
 * The bottom margin requires a bit of care.  The image is printed
 * as strips, each about 3.4mm wide.  We can only attain the bottom
 * margin if the final strip coincides with it.  Note that each strip
 * is generated using only 48 of the available 64 jets, and the absence
 * of those bottom 16 jets makes our bottom margin, in effect, about
 * 1.1mm (0.04") larger.
 *
 * The bj200 behaves, in effect, as though the origin were at the first
 * printable position, rather than the top left corner of the page, so
 * we add a translation to the initial matrix to compensate for this.
 *
 * Except for the details of getting the margins correct, the bj200 is
 * no different from the bj10e, and uses the same routine to print each
 * page.
 *
 * NOTE:  The bj200 has a DIP switch called "Text scale mode" and if
 * set, it allows the printer to get 66 lines on a letter-sized page
 * by reducing the line spacing by a factor of 14/15.  If this DIP
 * switch is set, the page image printed by ghostscript will also be
 * similarly squeezed.  Thus text scale mode is something ghostscript
 * would like to disable.
 *
 * According to the bj200 manual, which describes the bj10 commands,
 * the printer can be reset either to the settings determined by the
 * DIP switches, or to the factory defaults, and then some of those
 * settings can be specifically overriden.  Unfortunately, the text
 * scale mode and horizontal print position (for letter vs A4 paper)
 * can not be overriden.  On my bj200, the factory settings are for
 * no text scaling and letter paper, thus using the factory defaults
 * also implies letter paper.  I don't know if this is necessarily
 * true for bj200's sold elsewhere, or for other printers that use
 * the same command set.
 *
 * If your factory defaults are in fact the same, you can compile
 * the driver with USE_FACTORY_DEFAULTS defined, in which case the
 * printer will be reset to the factory defaults for letter paper,
 * and reset to the DIP switch settings for A4 paper.  In this case,
 * with letter-sized paper, the text scale mode will be disabled.
 * Further, by leaving the horizontal print position DIP switch set
 * for A4 paper, gs will be able to print on either A4 or letter
 * paper without changing the DIP switch.  Since it's not clear that
 * the factory defaults are universal, the default behaviour is not
 * to define USE_FACTORY_DEFAULTS, and the printer will always be
 * reset to the DIP switch defaults.
 */

/*
 * According to md@duesti.fido.de (Matthias Duesterhoeft):

It is possible to use the printer Canon BJ-300 (and 330) with Ghostscript if
you use the driver for the Canon BJ-200. The Printer has to be set to
Proprinter Mode. Although it is possible to set the print quality with a DIP
switch, you should add the following to the already existing init-string:
1B 5B 64 01 00 80  (all numbers in hex)
This sets the print quality to letter quality.

The minimum margins are the following:

Portrait:
B5/A4: min. left and right margin: 3.4 mm (0.13")
Letter: min. left and right margin: 6.4 mm (0.25")

Landscape:
B4: min. left and right margin: 9.3 mm (0.37")
A3: min. left and right margin: 37.3 mm (1.47")

The recommended top margin is 12.7 mm (0.5"), although the printer is capable
to start at 0 mm. The recommended bottom margin is 25.4 mm (1"), but 12.7 mm
(0.5") are possible, too. If you ask me, don't use the recommended top and
bottom margins, use 0" and 0.5".

 */

#define BJ200_TOP_MARGIN		0.12
#define BJ200_BOTTOM_MARGIN		0.29
#define BJ200_LETTER_SIDE_MARGIN	0.25
#define BJ200_A4_SIDE_MARGIN		0.13

static dev_proc_open_device(bj200_open);

static dev_proc_print_page(bj10e_print_page);

static gx_device_procs prn_bj200_procs =
/* Since the print_page doesn't alter the device, this device can print in the background */
  prn_procs(bj200_open, gdev_prn_bg_output_page, gdev_prn_close);

const gx_device_printer far_data gs_bj200_device =
  prn_device(prn_bj200_procs, "bj200",
        DEFAULT_WIDTH_10THS,
        DEFAULT_HEIGHT_10THS,
        360,				/* x_dpi */
        360,				/* y_dpi */
        0, 0, 0, 0,			/* margins filled in by bj200_open */
        1, bj10e_print_page);

/*
 * (<simon@pogner.demon.co.uk>, aka <sjwright@cix.compulink.co.uk>):
 * My bj10ex, which as far as I can tell is just like a bj10e, needs a
 * bottom margin of 0.4" (actually, you must not print within 0.5" of
 * the bottom; somewhere, an extra 0.1" is creeping in).
 *
 * (<jim.hague@acm.org>):
 * I have a BJ10sx and the BJ10sx manual. This states that the top and
 * bottom margins for the BJ10sx are 0.33" and 0.5". The latter may
 * explain Simon's finding. The manual also instructs Win31 users to
 * select 'BJ10e' as their driver, so presumably the margins will be
 * identical and thus also correct for BJ10e. The values for the side
 * margins given are identical to those above.
 *
 * As of 2nd Nov 2001 the BJ10 sx manual is at
 * http://www.precision.com/Printer%20Manuals/Canon%20BJ-10sx%20Manual.pdf.
 */

#define BJ10E_TOP_MARGIN		0.33
#define BJ10E_BOTTOM_MARGIN		(0.50 + 0.04)

static dev_proc_open_device(bj10e_open);

static gx_device_procs prn_bj10e_procs =
  prn_procs(bj10e_open, gdev_prn_output_page, gdev_prn_close);

const gx_device_printer far_data gs_bj10e_device =
  prn_device(prn_bj10e_procs, "bj10e",
        DEFAULT_WIDTH_10THS,
        DEFAULT_HEIGHT_10THS,
        360,				/* x_dpi */
        360,				/* y_dpi */
        0,0,0,0,			/* margins */
        1, bj10e_print_page);

/*
 * Notes on the BJ10e/BJ200 command set.
 *

According to the BJ200 manual, the "set initial condition" sequence (ESC [
K) has 2 bytes which can override the DIP switches -- these are the last 2
bytes.  Several bits are listed as "reserved" -- one or more may possibly
affect the sheet feeder.  The first is referred to as <P1>, with the
following meaning:
                                1		0
bit 7	ignore/process P1	ignore		process
bit 6	reserved
bit 5	alarm			disabled	enabled
bit 4	automatic CR		CR+LF		CR
bit 3	automatic LF		CR+LF		LF
bit 2	page length		12 inches	11 inches
bit 1	style for zero		slashed		not slashed
bit 0	character set		set 2		set 1

The last byte is <P2>, with the following meaning:
                                1		0
bit 7	ignore/process P2	ignore		process
bit 6	code page		850		437
bit 5	reserved
bit 4	reserved
bit 3	reserved
bit 2	reserved
bit 1	reserved
bit 0	reserved

The automatic CR setting is important to gs, but the rest shouldn't matter
(gs doesn't print characters or send LF, and it explicitly sets the page
length).  The sequence ESC 5 <n> controls automatic CR -- if <n> is 0x00,
it is turned off (CR only) and if <n> is 0x01, it is turned on (CR + LF).
So we do following: Change the initialization string to so that the last 2
of the 9 bytes are \200 rather than \000.  Then add
        |* Turn off automatic carriage return, otherwise we get line feeds. *|
        fwrite("\0335\000", 1, 3, prn_stream);
after the initialization.  (Actually, instead of setting the last 2 bytes
to \200, we suppress them altogether by changing the byte count from \004
to \002 (the byte count is the 4th (low 8 bits) and 5th (high 8 bits) bytes
in the initialization sequence).)

*/

/* ------ Internal routines ------ */

/* Open the printer, and set the margins. */
static int
bj200_open(gx_device *pdev)
{
        /* Change the margins according to the paper size.
           The top and bottom margins don't seem to depend on the
           page length, but on the paper handling mechanism;
           The side margins do depend on the paper width, as the
           printer centres the 8" print line on the page. */

        static const float a4_margins[4] =
         {	(float)BJ200_A4_SIDE_MARGIN, (float)BJ200_BOTTOM_MARGIN,
                (float)BJ200_A4_SIDE_MARGIN, (float)BJ200_TOP_MARGIN
         };
        static const float letter_margins[4] =
         {	(float)BJ200_LETTER_SIDE_MARGIN, (float)BJ200_BOTTOM_MARGIN,
                (float)BJ200_LETTER_SIDE_MARGIN, (float)BJ200_TOP_MARGIN
         };

        gx_device_set_margins(pdev,
                (pdev->width / pdev->x_pixels_per_inch <= 8.4 ?
                 a4_margins : letter_margins),
                true);
        return gdev_prn_open(pdev);
}

static int
bj10e_open(gx_device *pdev)
{
        /* See bj200_open() */
        static const float a4_margins[4] =
         {	(float)BJ200_A4_SIDE_MARGIN, (float)BJ10E_BOTTOM_MARGIN,
                (float)BJ200_A4_SIDE_MARGIN, (float)BJ10E_TOP_MARGIN
         };
        static const float letter_margins[4] =
         {	(float)BJ200_LETTER_SIDE_MARGIN, (float)BJ10E_BOTTOM_MARGIN,
                (float)BJ200_LETTER_SIDE_MARGIN, (float)BJ10E_TOP_MARGIN
         };

        gx_device_set_margins(pdev,
                (pdev->width / pdev->x_pixels_per_inch <= 8.4 ?
                 a4_margins : letter_margins),
                true);
        return gdev_prn_open(pdev);
}

/* Send the page to the printer. */
static int
bj10e_print_page(gx_device_printer *pdev, gp_file *prn_stream)
{	int line_size = gx_device_raster((gx_device *)pdev, 0);
        int xres = (int)pdev->x_pixels_per_inch;
        int yres = (int)pdev->y_pixels_per_inch;
        int mode = (yres == 180 ?
                        (xres == 180 ? 11 : 12) :
                        (xres == 180 ? 14 : 16));
        int bytes_per_column = (yres == 180) ? 3 : 6;
        int bits_per_column = bytes_per_column * 8;
        int skip_unit = bytes_per_column * 3;
        byte *in = (byte *)gs_malloc(pdev->memory, 8, line_size, "bj10e_print_page(in)");
        byte *out = (byte *)gs_malloc(pdev->memory, bits_per_column, line_size, "bj10e_print_page(out)");
        int lnum = 0;
        int skip = 0;
        int code = 0;
        int last_row = dev_print_scan_lines(pdev);
        int limit = last_row - bits_per_column;

        if ( in == 0 || out == 0 )
        {	code = gs_note_error(gs_error_VMerror);
                goto fin;
        }

        /* Initialize the printer. */
#ifdef USE_FACTORY_DEFAULTS
        /* Check for U.S. letter vs. A4 paper. */
        gp_fwrite(( pdev->width / pdev->x_pixels_per_inch <= 8.4 ?
                "\033[K\002\000\000\044"	/*A4--DIP switch defaults*/ :
                "\033[K\002\000\004\044"	/*letter--factory defaults*/ ),
               1, 7, prn_stream);
#else
        gp_fwrite("\033[K\002\000\000\044", 1, 7, prn_stream);
#endif

        /* Turn off automatic carriage return, otherwise we get line feeds. */
        gp_fwrite("\0335\000", 1, 3, prn_stream);

        /* Set vertical spacing. */
        gp_fwrite("\033[\\\004\000\000\000", 1, 7, prn_stream);
        gp_fputc(yres & 0xff, prn_stream);
        gp_fputc(yres >> 8, prn_stream);

        /* Set the page length.  This is the printable length, in inches. */
        gp_fwrite("\033C\000", 1, 3, prn_stream);
        gp_fputc((last_row + yres - 1)/yres, prn_stream);

        /* Transfer pixels to printer.  The last row we can print is defined
           by "last_row".  Only the bottom of the print head can print at the
           bottom margin, and so we align the final printing pass.  The print
           head is kept from moving below "limit", which is exactly one pass
           above the bottom margin.  Once it reaches this limit, we make our
           final printing pass of a full "bits_per_column" rows. */
        while ( lnum < last_row )
           {
                byte *in_data;
                byte *in_end = in + line_size;
                byte *out_beg = out;
                byte *out_end = out + bytes_per_column * pdev->width;
                byte *outl = out;
                int bnum;

                /* Copy 1 scan line and test for all zero. */
                code = gdev_prn_get_bits(pdev, lnum, in, &in_data);
                if ( code < 0 )
                    goto fin;
                /* The mem... or str... functions should be faster than */
                /* the following code, but all systems seem to implement */
                /* them so badly that this code is faster. */
                   {	register const long *zip = (const long *)in_data;
                        register int zcnt = line_size;
                        register const byte *zipb;
                        for ( ; zcnt >= 4 * sizeof(long); zip += 4, zcnt -= 4 * sizeof(long) )
                           {	if ( zip[0] | zip[1] | zip[2] | zip[3] )
                                        goto notz;
                           }
                        zipb = (const byte *)zip;
                        while ( --zcnt >= 0 )
                           {
                                if ( *zipb++ )
                                        goto notz;
                           }
                        /* Line is all zero, skip */
                        lnum++;
                        skip++;
                        continue;
notz:			;
                   }

                /* Vertical tab to the appropriate position.  Note here that
                   we make sure we don't move below limit. */
                if ( lnum > limit )
                    {	skip -= (lnum - limit);
                        lnum = limit;
                    }
                while ( skip > 255 )
                   {	gp_fputs("\033J\377", prn_stream);
                        skip -= 255;
                   }
                if ( skip )
                        gp_fprintf(prn_stream, "\033J%c", skip);

                /* If we've printed as far as "limit", then reset "limit"
                   to "last_row" for the final printing pass. */
                if ( lnum == limit )
                        limit = last_row;
                skip = 0;

                /* Transpose in blocks of 8 scan lines. */
                for ( bnum = 0; bnum < bits_per_column; bnum += 8 )
                   {	int lcnt = min(8, limit - lnum);
                        byte *inp = in;
                        byte *outp = outl;
                        lcnt = gdev_prn_copy_scan_lines(pdev,
                                lnum, in, lcnt * line_size);
                        if ( lcnt < 0 )
                           {	code = lcnt;
                                goto xit;
                           }
                        if ( lcnt < 8 )
                                memset(in + lcnt * line_size, 0,
                                       (8 - lcnt) * line_size);
                        for ( ; inp < in_end; inp++, outp += bits_per_column )
                           {	gdev_prn_transpose_8x8(inp, line_size,
                                        outp, bytes_per_column);
                           }
                        outl++;
                        lnum += lcnt;
                        skip += lcnt;
                   }

                /* Send the bits to the printer.  We alternate horizontal
                   skips with the data.  The horizontal skips are in units
                   of 1/120 inches, so we look at the data in groups of
                   3 columns, since 3/360 = 1/120, and 3/180 = 2/120.  */
                outl = out;
                do
                   {	int count;
                        int n;
                        byte *out_ptr;

                        /* First look for blank groups of columns. */
                        while(outl < out_end)
                           {	n = count = min(out_end - outl, skip_unit);
                                out_ptr = outl;
                                while ( --count >= 0 )
                                   {	if ( *out_ptr++ )
                                                break;
                                   }
                                if ( count >= 0 )
                                        break;
                                else
                                        outl = out_ptr;
                           }
                        if (outl >= out_end)
                                break;
                        if (outl > out_beg)
                           {	count = (outl - out_beg) / skip_unit;
                                if ( xres == 180 ) count <<= 1;
                                gp_fprintf(prn_stream, "\033d%c%c",
                                        count & 0xff, count >> 8);
                           }

                        /* Next look for non-blank groups of columns. */
                        out_beg = outl;
                        outl += n;
                        while(outl < out_end)
                           {	n = count = min(out_end - outl, skip_unit);
                                out_ptr = outl;
                                while ( --count >= 0 )
                                   {	if ( *out_ptr++ )
                                                break;
                                   }
                                if ( count < 0 )
                                        break;
                                else
                                        outl += n;
                           }
                        count = outl - out_beg + 1;
                        gp_fprintf(prn_stream, "\033[g%c%c%c",
                                count & 0xff, count >> 8, mode);
                        gp_fwrite(out_beg, 1, count - 1, prn_stream);
                        out_beg = outl;
                        outl += n;
                   }
                while ( out_beg < out_end );

                gp_fputc('\r', prn_stream);
           }

        /* Eject the page */
xit:	gp_fputc(014, prn_stream);	/* form feed */
        gp_fflush(prn_stream);
fin:	if ( out != 0 )
                gs_free(pdev->memory, (char *)out, bits_per_column, line_size,
                        "bj10e_print_page(out)");
        if ( in != 0 )
                gs_free(pdev->memory, (char *)in, 8, line_size, "bj10e_print_page(in)");
        return code;
}