1 /* min12xxw.c driver for minolta pagepro 1[234]xxW printers
2 *
3 * It converts pages in pbmraw format read from stdin to the printer
4 * language used by Minolta PagePro 1[234]00W. The output goes to stdout.
5 *
6 * Copyright (C) 2004-2006 Manuel Tobias Schiller <mala@hinterbergen.de>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22 /* Note: This program was written without any documentation from the
23 * manufacturer. The description of the printer language may be
24 * inaccurate or even wrong because it was obtained by looking at the
25 * output of the windoof driver only. The code dealing with the
26 * compression of raster data is in its spirit heavily based on an
27 * initial driver by Adam Bocim <beetman@seznam.cz> who managed to
28 * find out how things are done (thanks, Adam, you did a great job!).
29 * This new version is considerably faster, implemented more cleanly
30 * and (hopefully) well documented.
31 * It also features querying the printer status and page counter, a
32 * suggestion made by Bruno Schoedlbauer <bruno.schoedlbauer@gmx.de>,
33 * who also pointed me to a nice USB sniffer by Benoit Papillault for
34 * Windoof 98 and up, see http://benoit.papillault.free.fr/usbsnoop.
35 * (This little program may prove very helpful on similar occasions.
36 * Happy USB sniffing...)
37 * */
38 /* see the file CHANGELOG for a list of changes between revisions.
39 */
40
41 #include "config.h"
42
43 #define _GNU_SOURCE /* we might need this because we use getline */
44 #include <assert.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <ctype.h>
48 #include <stdio.h>
49 #include <unistd.h> /* usleep */
50 #include <sys/stat.h> /* fstat */
51 #include <getopt.h>
52
53 /* include the inttypes.h for uint8_t and similar types */
54 #ifdef HAVE_INTTYPES_H
55 #include <inttypes.h>
56 #else
57 /* don't know yet what to do in this case. just put some typedefs there
58 * and hope for the best? until someone complains we just make the
59 * compiler throw an error... */
60 #error "You'll need either stdint.h or inttypes.h"
61 #endif
62
63 /* make sure we get getline even if the system libc does not provide it */
64 #ifndef HAVE_GETLINE
65 #include <getline.h>
66 #endif
67
68 #ifndef MIN
69 #define MIN(x, y) (((x) < (y))?(x):(y))
70 #endif
71 #ifndef MAX /* not used now - but might come handy in the future */
72 #define MAX(x, y) (((x) > (y))?(x):(y))
73 #endif
74
75 /* we wish to know what version we are ;) */
76 static char *versionstr = "version " PACKAGE_VERSION " ($Id: min12xxw.c 115 2005-12-31 13:50:48Z mala $)";
77
78 /* defaults used for the output */
79 static int ptype = 0x00; /* normal paper */
80 static int pformat = 0x04; /* a4 */
81 static int res = 0x0001; /* 600 dpi, horizontal resolution not doubled */
82 static int tray = 0xff; /* auto select paper tray */
83 static int nomargins = 0; /* if flag is set, margins are not enforced */
84 static int ecomode = 0; /* if set, toner-saving mode is enabled */
85 static int model = 0x81; /* 12xxW series printers are the default */
86 static char *device = "/dev/lp0"; /* default device used for queries */
87
88 /* try to exit gracefully in case of fatal errors */
fatal(char * str)89 static void fatal(char *str)
90 {
91 perror(str);
92 exit(1);
93 }
94
95 /**********************************************************************
96 * minolta esc command utility
97 *
98 * this is used to send printer commands and read printer response
99 * packets
100 * see the file format.txt for an overview of what has been found out
101 * about the format used
102 **********************************************************************/
do_cmd(FILE * out,uint8_t cmd,uint32_t len,uint8_t * data)103 static void do_cmd(FILE *out, uint8_t cmd, uint32_t len, uint8_t *data)
104 {
105 /* command sequence number, incremented with each command */
106 static uint8_t sq = 0;
107 uint8_t cksum = 0; /* every command is transmitted with a checksum */
108 uint8_t buf[6]; /* buffer to build up command header */
109 uint8_t *end = &data[len], *p = buf;
110
111 assert(len < 0x10000); /* do not transmit way too much data */
112
113 /* fill in command header */
114 *p++ = 0x1b; *p++ = cmd; *p++ = sq++;
115 *p++ = len & 0xff; *p++ = len >> 8; *p++ = ~cmd;
116
117 /* calculate the checksum */
118 p = data;
119 while (p < end) cksum += *p++;
120 p = buf;
121 end = &buf[6];
122 while (p < end) cksum += *p++;
123
124 /* ok, write the command to the output file */
125 if (fwrite(buf, sizeof(uint8_t), 6, out) != 6)
126 fatal("min12xxw: error writing command to output file.");
127 if (fwrite(data, sizeof(uint8_t), len, out) != len)
128 fatal("min12xxw: error writing command to output file.");
129 if (fputc(cksum, out) == EOF)
130 fatal("min12xxw: error writing command to output file.");
131 }
132
133 /* this will read data from the printer
134 * note: when reading data from the printer over the USB port, we must
135 * check what we get back because the printer will give back the wrong
136 * register when it is not ready - in this case we sleep a bit and try
137 * again */
do_read(FILE * in,uint8_t reg,int * len)138 static uint8_t *do_read(FILE *in, uint8_t reg, int *len)
139 {
140 int c, d, i, j = 0;
141 uint8_t *retVal = NULL;
142
143 /* try to read the desired register in a loop */
144 do {
145 usleep(100000);
146 if (retVal != NULL) free(retVal);
147 if ((d = fgetc(in)) == EOF)
148 fatal("min12xxw: error reading data from printer");
149 if ((c = fgetc(in)) == EOF)
150 fatal("min12xxw: error reading data from printer");
151
152 if ((retVal = (uint8_t *) malloc(c * sizeof(uint8_t)))==NULL)
153 fatal("min12xxw: error allocating buffer");
154 for (i = 0; i < c; i++) {
155 int cc;
156 if ((cc = fgetc(in)) == EOF)
157 fatal("min12xxw: error reading data from " \
158 "printer");
159 retVal[i] = (uint8_t) cc;
160 }
161 /* break if we have the desired register or we have waited
162 * long enough for the printer to reply and have not seen
163 * a decent answer */
164 } while ((d != reg) && (j++ < 10));
165
166 if (d != reg) {
167 /* we didn't manage to read the desired register */
168 free(retVal);
169 *len = 0;
170 return NULL;
171 }
172
173 /* we have read the desired register */
174 *len = c;
175 return retVal;
176 }
177
178 /* sends the start signal to the printer (make printer pay attention) */
do_start(FILE * out)179 static void do_start(FILE *out)
180 {
181 uint8_t buf[2];
182
183 buf[0] = (uint8_t) model;
184 buf[1] = 0;
185 do_cmd(out, 0x40, 2, buf);
186 }
187
188 /* sends the stop signal to the printer (terminate a command sequence) */
do_stop(FILE * out)189 static void do_stop(FILE *out)
190 {
191 uint8_t buf = 0;
192
193 do_cmd(out, 0x41, 1, &buf);
194 }
195
196 /* send read register command */
do_readreg(FILE * out,uint8_t reg)197 static void do_readreg(FILE *out, uint8_t reg)
198 {
199 uint8_t buf[2];
200
201 buf[0] = reg;
202 buf[1] = 0;
203 do_cmd(out, 0x60, 2, buf);
204 }
205
206 /* send register enabler command - not sure if this is what this command
207 * does */
do_enreg(FILE * out)208 static void do_enreg(FILE *out)
209 {
210 uint8_t buf[3];
211
212 /* FIXME: until we know better, we'll just treat the 1400W as a
213 * better 13xxW here - testers report that this seems to work
214 * well */
215 buf[0] = (((model == 0x83) || (model == 0x86))?(0x1c):(0x78));
216 buf[1] = 0;
217 buf[2] = 0x04;
218 do_cmd(out, 0x6a, 3, buf);
219 }
220
221 /* produce a quick and dirty register dump from the printer registers
222 * this routine was mainly used for debugging purposes and is therefore
223 * disabled by default - it was never meant to be used by a wider
224 * audience */
225 #if 0
226 static void regdump(FILE *f)
227 {
228 int i, len, state;
229 uint8_t *buf = NULL, regs[] = { 0x81, 0x53, 0x05, 0x04, 0x03, 0x02 };
230
231 do_start(f);
232 buf = do_read(f, 0x4a, &len);
233 printf("\nread start sequence state 0x%02x, %3d bytes:\n", 0x4a, len);
234 for (state = 0; state < len; state++)
235 putchar(buf[state]);
236 free(buf);
237 do_enreg(f);
238 buf = do_read(f, 0x45, &len);
239 printf("\nread reg enabler sequence state 0x%02x, %3d bytes:\n", \
240 0x45, len);
241 for (state = 0; state < len; state++)
242 putchar(buf[state]);
243 free(buf);
244 for (i = 0; i < 6; i++) {
245 do_readreg(f, regs[i]);
246 buf = do_read(f, regs[i], &len);
247 printf("\nread register 0x%02x, %3d bytes:\n", regs[i], len);
248 if (len > 0) {
249 for (state = 0; state < len; state++)
250 putchar(buf[state]);
251 }
252 free(buf);
253 }
254 do_stop(f);
255 }
256 #endif
257
258 /* this prints whatever information we can read from the hardware */
printhwstate(FILE * f)259 static void printhwstate(FILE *f)
260 {
261 uint8_t *bufcfw, *bufefw = NULL, *bufpcnt, *bufst;
262 int len;
263
264 do_start(f);
265 /* the printers appear to have a sort of enabler command - I'm
266 * not sure if this is what this does - we send it anyway,
267 * because the windoof driver does it as well */
268 do_enreg(f);
269 /* get printer state */
270 do_readreg(f, 0x04);
271 bufst = do_read(f, 0x04, &len);
272 if (len != 0) {
273 memmove(bufst, &bufst[1], len - 1);
274 bufst[len - 1] = 0;
275 }
276 /* get controller firmware version */
277 do_readreg(f, 0x02);
278 bufcfw = do_read(f, 0x02, &len);
279 if (len != 14)
280 goto readerr;
281 /* get engine firmware version for models that support it */
282 do_readreg(f, 0x81);
283 bufefw = do_read(f, 0x81, &len);
284 /* if len != 0 the printer appears not to have register 0x81 */
285 if ((len != 30) && (len != 0))
286 goto readerr;
287 /* ok, read page counter */
288 do_readreg(f, 0x53);
289 bufpcnt = do_read(f, 0x53, &len);
290 if (len != 38)
291 goto readerr;
292 do_stop(f);
293
294 printf("printer status: %s\n", bufst);
295 free(bufst);
296 printf("controller firmware version: %c%c%c%c\n", \
297 bufcfw[3], bufcfw[2], bufcfw[1], bufcfw[0]);
298 free(bufcfw);
299 if (bufefw != NULL) {
300 /* print engine firmware version if the model supports it */
301 memmove(bufefw, &bufefw[18], 12);
302 bufefw[12] = 0;
303 printf("engine firmware version: %s\n", bufefw);
304 free(bufefw);
305 }
306 len = bufpcnt[30] | (bufpcnt[31] << 8) | (bufpcnt[32] << 16) | \
307 (bufpcnt[33] << 24);
308 printf("page counter: %lu pages\n", (unsigned long) len);
309 free(bufpcnt);
310 return;
311
312 readerr:
313 do_stop(f); /* try not to hang the printer */
314 fatal("min12xxw: read unexpected data from printer");
315 }
316
317 /* this procedure sends raster data to the printer */
send_raster_data(FILE * out,uint32_t nlines,uint32_t len,uint8_t * data)318 static void send_raster_data(FILE *out, uint32_t nlines, \
319 uint32_t len, uint8_t *data)
320 {
321 uint8_t cmdbuf[6], *p = cmdbuf;
322
323 assert(nlines < (1 << 16)); /* just to make sure */
324 *p++ = len & 0xff; *p++ = (len >> 8) & 0xff;
325 *p++ = (len >> 16) & 0xff; *p++ = len >> 24;
326 *p++ = nlines & 0xff;
327 *p++ = (nlines >> 8) & 0xff;
328 /* we start with sending the command */
329 do_cmd(out, 0x52, 6, cmdbuf);
330 /* the data is appended immediately */
331 if (fwrite(data, sizeof(uint8_t), len, out) != len)
332 fatal("min12xxw: couldn't send raster data to output file");
333 }
334
335 /* this procedure sends a start-of-job/select-resolution-and-papertype
336 * sequence to the printer */
send_start_job(FILE * out)337 static void send_start_job(FILE *out)
338 {
339 uint8_t cmdbuf[8];
340
341 memset(cmdbuf, 0x00, 8 * sizeof(uint8_t)); /* zero our buffer */
342
343
344 do_start(out); /* start-of-printer-commands command */
345
346 cmdbuf[0] = (uint8_t) (res & 0xff); /* set resolution and paper type */
347 /* check if we are to use doubled horizontal resolution */
348 cmdbuf[1] = (uint8_t) (res >> 8);
349 cmdbuf[3] = (uint8_t) ptype;
350 cmdbuf[4] = 0x04;
351 /* 1[34]xxW series models might expect an 0x04 in cmdbuf[6] as well */
352 if ((model == 0x83) || (model == 0x86)) cmdbuf[6] = 0x04;
353 do_cmd(out, 0x50, 8, cmdbuf); /* issue it */
354 }
355
356 /* this procedure sends an end-of-job sequence to the printer */
send_end_job(FILE * out)357 static void send_end_job(FILE *out)
358 {
359 uint8_t cmdbuf = 0;
360
361 do_cmd(out, 0x55, 1, &cmdbuf);
362 do_stop(out);
363 }
364
365 /* the following procedure sends a new page command */
send_new_page(FILE * out,uint32_t x,uint32_t y)366 static void send_new_page(FILE *out, uint32_t x, uint32_t y)
367 {
368 uint8_t cmdbuf[22], *p = &cmdbuf[1];
369
370 memset(cmdbuf, 0x00, 22 * sizeof(uint8_t)); /* zero our buffer */
371 *p++ = 0x01;
372 *p++ = (x >> 16) & 0xff; *p++ = x >> 24;
373 *p++ = x & 0xff; *p++ = (x >> 8) & 0xff;
374 *p++ = (y >> 16) & 0xff; *p++ = y >> 24;
375 *p++ = y & 0xff; *p++ = (y >> 8) & 0xff;
376 *p++ = 0x08; p++; *p++ = 0x08; p++;
377 *p++ = tray; *p++ = pformat;
378 if ((res & 0xff) == 0) {
379 /* apparently, 300 dpi needs special flags set here */
380 cmdbuf[20] = 0xc0;
381 }
382 do_cmd(out, 0x51, 22, cmdbuf); /* issue our command */
383 }
384
385 /**********************************************************************
386 * raster data compression
387 **********************************************************************/
388 /* the following table keeps sixteen bytes which can be sent to the
389 * printer by referring to their table index (4 bit only)
390 * this table is filled anew for every scanline */
391 static int tbllen = -1; /* all tables uninitialized by default */
392 static uint8_t tbl[16];
393 static uint8_t invtbl[256]; /* this is for inverse lookups */
394
395 /* return the number of repetitions of a byte at position pointed to by p */
get_len(uint8_t * p,uint8_t * end)396 static uint32_t get_len(uint8_t *p, uint8_t *end)
397 {
398 uint8_t c = *p++;
399 uint32_t len = 0;
400
401 while ((p < end) && (c == *p++)) len++;
402
403 return ++len;
404 }
405
406 /* add a byte to the table and return the table index to it */
add_tbl(uint8_t b)407 static uint8_t add_tbl(uint8_t b)
408 {
409 /* is there space in the table or is the byte already there?
410 * if the table is full, returning invtbl[b] is still correct
411 * doing it this way has the advantage of saving a constant load
412 * because we have the value already at hand (from the test) */
413 if ((invtbl[b] < 16) || (tbllen >= 16)) return invtbl[b];
414 /* ok, add it to the table */
415 tbl[tbllen] = b;
416 invtbl[b] = tbllen;
417 return tbllen++;
418 }
419
420 /* check if the next n bytes are in the table, or if we have space for them */
next_n_in_tbl(uint8_t * p,long len,uint8_t * end)421 static int next_n_in_tbl(uint8_t *p, long len, uint8_t *end)
422 {
423 long i = 0, j = len;
424
425 /* make sure we don't read past the end of our data */
426 if ((p + len) >= end) return 0;
427
428 /* count how many bytes are in the table */
429 while (j--)
430 if (invtbl[*p++] < 16) i++;
431
432 /* check if we have space for those which are not */
433 if (tbllen < (17 - len + i)) return 1;
434
435 return 0; /* sorry - no space in table left... */
436 }
437
438 /* compress a scanline of data and write the result to an output buffer */
compress_scanline(uint8_t * p,uint8_t * end,uint8_t * obuf)439 static uint32_t compress_scanline(uint8_t *p, uint8_t *end, uint8_t *obuf)
440 {
441 uint32_t olen = 0;
442 uint8_t *q; /* temporary pointer */
443
444 /* clear the table and inverse lookup table - we try to do
445 * partial clearing of the inverse lookup table if possible,
446 * because reading and writing 16 or fewer bytes should be faster
447 * than writing 256 bytes... Can't do that the first time, though */
448 if (tbllen == -1) memset(invtbl, 0xff, 256 * sizeof(uint8_t));
449 else for (q = tbl; q < &tbl[tbllen]; q++) invtbl[*q] = 0xff;
450 tbllen = 0;
451
452 while (p < end) {
453 uint32_t n = get_len(p, end);
454 if (n > 2) {
455 /* RLE compression pays off */
456 /* make sure n is small enough - we're paranoid */
457 assert(n < (63 * 64 + 63));
458 if (n > 63) {
459 /* how to encode multiples of 64 bytes */
460 *obuf++ = 0xc0 | (n >> 6);
461 *obuf++ = *p;
462 olen += 2;
463 p += n & (~0x3f);
464 n &= 0x3f;
465 }
466 if (n) {
467 /* encode the remainder */
468 *obuf++ = 0x80 | n;
469 *obuf++ = *p;
470 olen += 2;
471 p += n;
472 }
473 } else if (next_n_in_tbl(p, 4, end)) {
474 /* ok - try to do table compression */
475 /* the next four bytes are in the table or there
476 * is enough space to put them into the table
477 * we issue a 0x41 code to indicate that we send
478 * table indices - the 0x41 means that we send
479 * a quartet of table indices - we might even
480 * send more, see below */
481 q = obuf; /* back up current position */
482 *obuf++= 0x41;
483 *obuf = add_tbl(*p++) << 4;
484 *obuf++ |= add_tbl(*p++);
485 *obuf = add_tbl(*p++) << 4;
486 *obuf++ |= add_tbl(*p++);
487 olen += 3;
488 /* check if we can send even more table indices */
489 while (next_n_in_tbl(p, 2, end) && (*q < 0x7f)) {
490 /* break if we can RLE-code the next bytes */
491 if (get_len(p, MIN(p + 3, end)) >= 3) break;
492 /* we send one more pair of table indices */
493 (*q)++;
494 *obuf = add_tbl(*p++) << 4;
495 *obuf++ |= add_tbl(*p++);
496 olen++;
497 }
498 } else {
499 /* can't do table compression - fall back to send
500 * up to 10 bytes in uncompressed plaintext */
501 q = obuf; /* back up current output position */
502 *obuf++ = 0xff;
503 olen++;
504 do {
505 *obuf++ = *p++;
506 olen++;
507 (*q)++; /* update # of plaintext bytes */
508 /* three repeating bytes ahead? if so,
509 * do RLE compression instead */
510 if (get_len(p, MIN(p + 3, end)) >= 3) break;
511 /* see if we can do table compression for
512 * the bytes ahead - comment the next line
513 * of code out if you absolutely need the
514 * speed and can live with things compressing
515 * a little worse (on the order of a percent
516 * or so) */
517 if (next_n_in_tbl(p, 4, end)) break;
518 } while ((p < end) && (*q < 9));
519 }
520 }
521
522 return olen;
523 }
524
525 /**********************************************************************
526 * process all the pages in the rawpbm input job
527 **********************************************************************/
528 /* printer has non-printable margins of 17/100" on all sides of a sheet
529 * of paper which are present in ghostscript output and need to be
530 * removed - these are in units of 8 pixels; note - this is not exact
531 * to the last decimal place but it has to be good enough
532 * update this if new printers allow for new resolutions codes or equal
533 * codes with different meanings!!! */
534 static int skiptbl[3] = { 6, 13, 25 };
535
536 /* processes a single page */
dopage(FILE * in,FILE * out,uint32_t x,uint32_t y)537 static void dopage(FILE *in, FILE *out, uint32_t x, uint32_t y)
538 {
539 uint32_t sclbytes = x / 8; /* # of bytes per scanline */
540 /* # of scanlines per block (rounded up) */
541 uint32_t sclperbl = y / 8 + ((y & 7)?1:0);
542 /* size of buffer for processed data - note: the estimate given
543 * below is conservative - the buffer can not become larger... */
544 uint32_t blblen = sclperbl * (17 + sclbytes + sclbytes / 10 + 1);
545 uint8_t *scl, *blbuf; /* buffers for scanline and output block */
546 uint32_t bllen; /* block length so far */
547 uint32_t sclen; /* length of scanline excluding table */
548 uint32_t yy, yc; /* counters */
549 /* this controls how many pixels are skipped on each side of a
550 * sheet of paper to keep the margins that the printer expects */
551 int skip = ((nomargins == 0)?skiptbl[res & 0xff]:0);
552 int i; /* counter */
553 int ecofl = 0; /* flag used for toner-saving mode */
554
555 /* allocate buffers */
556 if ((scl = (uint8_t *) malloc(sclbytes * sizeof(uint8_t))) == NULL)
557 fatal("min12xxw: couldn't allocate memory");
558 if ((blbuf = (uint8_t *) malloc(blblen * sizeof(uint8_t))) == NULL)
559 fatal("min12xxw: couldn't allocate memory");
560
561 yc = 0;
562 for (yc = 0; yc < skip * 8; yc++) {
563 /* skip the first few scanlines in case we enforce margins */
564 if (fread(scl, sizeof(uint8_t), sclbytes, in) != sclbytes)
565 fatal("min12xxw: couldn't read scanline");
566 }
567 /* we will always build eight blocks per page - the windows driver
568 * seems to do the same thing */
569 for (i = 0; i < 8; i++) {
570 bllen = 0;
571 /* process the scanlines in the block */
572 for (yy = 0; (yy < sclperbl) && (yc < y); yy++, yc++) {
573 /* read in the next scanline */
574 if (fread(scl, sizeof(uint8_t), sclbytes, in) != \
575 sclbytes)
576 fatal("min12xxw: couldn't read scanline");
577 /* check if we need to do this line (we don't work
578 * for the margins...) */
579 if ((yc + skip * 8) > y) continue;
580 /* check if in toner-saving mode */
581 if (ecomode) {
582 /* clear every other scanline */
583 if (ecofl) memset(scl, 0,
584 sclbytes * sizeof(uint8_t));
585 ecofl = ~ecofl;
586 }
587 /* compress a scanline - leave some space for the
588 * table (we'll probably have to remove a hole) */
589 sclen = compress_scanline(&scl[skip], \
590 &scl[sclbytes-skip], \
591 blbuf + 17);
592 blbuf[0] = 0x80 + tbllen; /* copy table */
593 if (tbllen) memcpy(blbuf + 1, tbl, tbllen);
594 if (tbllen < 16) /* remove the hole */
595 memmove(blbuf + 1 + tbllen, blbuf + 17, sclen);
596 blbuf += sclen + 1 + tbllen;
597 bllen += sclen + 1 + tbllen;
598 }
599 blbuf -= bllen;
600 /* account for skipped scanlines in the last block */
601 if (i == 7) yy -= 8 * skip;
602 /* send the raster data to the printer */
603 send_raster_data(out, yy, bllen, blbuf);
604 }
605 free(scl); /* avoid memory leaks */
606 free(blbuf);
607 }
608
609 /* processes all the pages in a pbmraw job file */
dojob(FILE * in,FILE * out)610 static void dojob(FILE *in, FILE *out)
611 {
612 uint32_t x, y; /* page dimesions in dots at the current resolution */
613 char *line = NULL; /* buffer for reading whole lines from the input */
614 size_t llen = 0; /* length of line buffer */
615 /* how much to skip because of margins */
616 int skip = ((nomargins == 0)?(16 * skiptbl[res & 0xff]):0);
617
618 /* send the printer a start-of-job/set-resolution-and-papertype
619 * sequence */
620 send_start_job(out);
621
622 /* process pages */
623 while (!feof(in) && !ferror(in)) {
624 /* read the png header for x and y dimensions */
625 if (getline(&line, &llen, in) == -1) break;
626 /* check for valid pbmraw signature */
627 if ((line[0] != 'P') || (line[1] != '4'))
628 fatal("min12xxw: input is not valid pbmraw "
629 "(no valid signature)");
630 /* read in comment lines and the line containing the
631 * resolution */
632 do {
633 if (getline(&line, &llen, in) == -1)
634 fatal("min12xxw: input is not valid pbmraw "
635 "(premature end of file)");
636 } while (line[0] == '#');
637 /* parse resolution */
638 {
639 /* this ugly hack is needed to make things work
640 * on 64 bit platforms like sparc64 or amd64 */
641 unsigned long lx, ly;
642 if (sscanf(line, "%lu %lu", &lx, &ly) != 2)
643 fatal("min12xxw: input is not valid pbmraw "
644 "(ill formatted bitmap dimensions)");
645 x = lx; y = ly;
646 }
647 /* scanlines are byte-aligned - adjust x accordingly */
648 if (x & 0x7) x = 8 + (x & ~0x7);
649 /* check if the page dimensions are so small that we need
650 * switch of the margins
651 * many thanks go to Ben Cooper who alerted me to the
652 * problem */
653 if ((((y - skip) / 8) <= skip) || (x <= (2 * skip))) {
654 if (!nomargins)
655 fprintf(stderr, "min12xxw: page dimensions"
656 " are so small that I won't "
657 "enforce page margins for this and"
658 "all subsequent pages!\n");
659 nomargins = 1;
660 skip = 0;
661 }
662
663 /* tell the printer to start a new page - note the page
664 * dimensions are reduced because we need to take the
665 * margins into account - see above */
666 send_new_page(out, x - skip, y - skip);
667 /* read, process and send the raster data for the next
668 * page - this needs to know the real dimensions because
669 * it needs to know how much data to read... */
670 dopage(in, out, x, y);
671 }
672
673 /* tell the printer we got our job finished... */
674 send_end_job(out);
675
676 free(line); /* free memory to avoid leaks... */
677 }
678
679 /**********************************************************************
680 * option handling
681 **********************************************************************/
682 /* the next few tables contain the mappings from human-readable strings
683 * to id numbers the printer understands (or our routines, for that
684 * matter) */
685 struct map {
686 char *str;
687 int id;
688 };
689 static struct map ptypes[] = {
690 { "normal", 0x00 }, { "thick", 0x01 }, { "transparency", 0x02 },
691 { "postcard", 0x03 }, { "envelope", 0x03 }, { NULL, -1 }
692 };
693 static struct map trays[] = {
694 { "auto", 0xff }, { "tray1", 0x00 }, { "tray2", 0x01 },
695 { "manual", 0x80 }, { NULL, -1 }
696 };
697 static struct map pformats[] = {
698 { "a4", 0x04 }, { "b5", 0x06 }, { "a5", 0x08 }, { "jpost", 0x0c },
699 { "corpost", 0x0d }, { "jisy6", 0x10 }, { "jisy0", 0x11 },
700 { "chinese16k", 0x13 }, { "chinese32k", 0x15 }, { "legal", 0x19 },
701 { "glegal", 0x1a }, { "letter", 0x1b }, { "gletter", 0x1d },
702 { "executive", 0x1f }, { "halfletter", 0x21 }, { "envmonarch", 0x24 },
703 { "env10", 0x25 }, { "envdl", 0x26 }, { "envc5", 0x27 },
704 { "envc6", 0x28 }, { "envb5", 0x29 }, { "choukei3gou", 0x2d },
705 { "choukei5gou", 0x2e }, { "custom", 0x31 }, { "envb6", 0x31 },
706 { "folio", 0x31 }, { "jisy1", 0x31 }, { "jisy2", 0x31 },
707 { "quadpost", 0x31 }, { NULL, -1 }
708 };
709 static struct map models[] = {
710 { "1200W", 0x81 }, { "1250W", 0x81 },
711 { "1300W", 0x83 }, { "1350W", 0x83 },
712 { "1400W", 0x86 }, { NULL, -1 }
713 };
714 static struct map resolutions[] = {
715 { "300", 0x0000 }, { "300x300", 0x0000 },
716 { "600", 0x0001 }, { "600x600", 0x0001 },
717 { "1200", 0x0002 }, { "1200x1200", 0x0002 },
718 { "1200x600", 0x0101 }, { NULL, -1 }
719 };
720
721 /* this procedure is used by help() below to print available somethings
722 * in a nice and painless way from the maps above
723 * see help() below and everything will become clearer */
printav(char * msg,struct map * m,int defid)724 static void printav(char *msg, struct map *m, int defid)
725 {
726 int i, n;
727
728 n = printf(msg);
729 /* print all available somethings from our map */
730 for (i = 0; m[i].str != NULL; i++) {
731 /* in case we had to start a new line below, we need to
732 * print a tab here */
733 if (n == 0) {
734 printf("\t");
735 n = 8;
736 }
737 /* print the next something */
738 n += printf("%s", m[i].str);
739 /* is it the default? if so, print "*" */
740 if (m[i].id == defid)
741 n += printf("*");
742 /* print ", " unless it's the last something */
743 if (m[i + 1].str != NULL)
744 n += printf(", ");
745 /* check if we have to start a new line */
746 if (n > 72) {
747 printf("\n");
748 n = 0;
749 }
750 }
751 /* unless the last thing we did was to start a new line, we append
752 * a newline here */
753 if (n != 0) printf("\n");
754 }
755
756 /* this procedure gives help */
help()757 static void help()
758 {
759 printf("min12xxw - a pbmraw to Minolta PagePro 1[234]xx W "
760 "filter\n\noptions: (defaults are marked with an"
761 " asterisk)\n"
762 "\t-h\t--help\t\t\tthis help\n"
763 "\t-v\t--version\t\tshow version number\n"
764 "\t-d dev\t--device dev\t\tset device to use for "
765 "queries (%s by default)\n"
766 "\t-s\t--status\t\tquery printer status\n"
767 "\t-e\t--ecomode\t\teconomic (toner saving) mode\n"
768 "\t-n\t--nomargins\t\tdisable enforcement of margins\n"
769 "\t-m mod\t--model mod\t\tset the printer model to "
770 "produce output for\n"
771 "\t-r res\t--res res\t\tset resolution\n"
772 "\t-t tray\t--tray tray\t\tset paper tray\n"
773 "\t-p type\t--papertype type\tset paper type"
774 "\n\t-f fmt\t--paperformat fmt\tset paper format\n"
775 "\n", device);
776 /* print the available somethings */
777 printav("available models: ", models, model);
778 printav("available resolutions: ", resolutions, res);
779 printav("available paper trays: ", trays, tray);
780 printav("available paper types: ", ptypes, ptype);
781 printav("available paper formats: ", pformats, pformat);
782 printf("\n");
783 exit(0);
784 }
785
786 /* this procedure prints out version information */
version()787 static void version()
788 {
789 printf("min12xxw: %s\n", versionstr);
790 printf("\nCopyright (C) 2004-2006 Manuel Tobias Schiller\n"
791 "This is free software; see the source for copying "
792 "conditions. There is NO\nwarranty; not even for "
793 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
794 exit(0);
795 }
796
797 /* convert string to id number */
getid(struct map * m,char * thing,char * str,int deflt)798 static int getid(struct map *m, char *thing, char *str, int deflt)
799 {
800 while (m->str != NULL) {
801 if (strcasecmp(str, m->str) == 0) return m->id;
802 m++;
803 }
804 fprintf(stderr, "min12xxw: unknown %s: %s\n", thing, str);
805 return deflt; /* return the default */
806 }
807
808 /* parse the options given to the filter */
parseopts(int argc,char ** argv)809 static void parseopts(int argc, char **argv)
810 {
811 /* this structure is needed by getopt_long */
812 static struct option opts[] = {
813 { "res", required_argument, 0, 'r' },
814 { "papertype", required_argument, 0, 'p' },
815 { "paperformat", required_argument, 0, 'f' },
816 { "tray", required_argument, 0, 't' },
817 { "nomargins", no_argument, 0, 'n' },
818 { "ecomode", no_argument, 0, 'e' },
819 { "version", no_argument, 0, 'v' },
820 { "device", required_argument, 0, 'd' },
821 { "status", no_argument, 0, 's' },
822 { "model", required_argument, 0, 'm' },
823 { "help", no_argument, 0, 'h' },
824 { NULL, 0, 0, 0 }
825 };
826 int optidx = 0, dostate = 0;
827 int c;
828 int lres = res, lptype = ptype, lpformat = pformat, lmodel = model;
829 int ltray = tray, lnomargins = nomargins, lecomode = ecomode;
830 char *ldevice = device;
831
832 opterr = 1; /* let getopt_long do the error reporting */
833 while (42) { /* this line is for all hitch-hikers out there */
834 c = getopt_long(argc, argv, "hsevnd:r:p:f:t:m:", opts, &optidx);
835 if (c == -1) break;
836 switch (c) {
837 case 'h':
838 help();
839 case 'v':
840 version();
841 case 'r':
842 lres = getid(resolutions, "resolution", optarg, 0x0001);
843 break;
844 case 'p':
845 lptype = getid(ptypes, "paper type", optarg, 0);
846 break;
847 case 'f':
848 lpformat = getid(pformats, "paper format", optarg, 4);
849 break;
850 case 'm':
851 lmodel = getid(models, "printer model", optarg, 0x81);
852 break;
853 case 't':
854 ltray = getid(trays, "tray", optarg, 0xff);
855 break;
856 case 'n':
857 lnomargins = 1;
858 break;
859 case 'd':
860 ldevice = optarg;
861 break;
862 case 's':
863 dostate = 1;
864 break;
865 case 'e':
866 lecomode = 1;
867 break;
868 default:
869 /* getopt_long told the user about the unknown option
870 * character, all we have to do now is bail out... */
871 exit(1);
872 }
873 }
874 /* copy the local copies of options to the global ones
875 * we need to do this copying to make sure that help sees the
876 * global defaults, not what the users sets with options */
877 res = lres; ptype = lptype; pformat = lpformat; model = lmodel;
878 tray = ltray; nomargins = lnomargins; device = ldevice;
879 ecomode = lecomode;
880 if (dostate) {
881 /* process printer queries right away and exit */
882 FILE *d;
883 if ((d = fopen(device, "r+b")) == NULL)
884 fatal("min12xxw: couldn't fdopen printer");
885 printhwstate(d);
886 if (fclose(d) != 0) /* close the printer */
887 fatal("min12xxw: couldn't close printer");
888 exit(0); /* exit */
889 }
890 }
891
892 /* adjust the model defaults to the name of the executable
893 * do some magic for the users to spare them from having to type model
894 * selection options again and again and again */
modeladj(char * str)895 static void modeladj(char *str)
896 {
897 int len = strlen(str);
898
899 if (len < 8) return; /* don't do anything on short strings */
900 /* we are only interested in the end of the string */
901 str = &str[len - 8];
902 /* if it is a model name, set the defaults accordingly */
903 if (strcmp(str, "min1200w") == 0) model = 0x81;
904 if (strcmp(str, "min1250w") == 0) model = 0x81;
905 if (strcmp(str, "min1300w") == 0) model = 0x83;
906 if (strcmp(str, "min1350w") == 0) model = 0x83;
907 if (strcmp(str, "min1400w") == 0) model = 0x86;
908 }
909
910 /**********************************************************************
911 * main program
912 **********************************************************************/
main(int argc,char ** argv)913 int main(int argc, char **argv)
914 {
915 FILE *tmp;
916 size_t i;
917 char *buf;
918 int fd;
919 struct stat st;
920
921 /* adjust the default model based on the name of the executable */
922 modeladj(argv[0]);
923 /* this is really boring now... */
924 parseopts(argc, argv); /* parse any options given */
925 /* if we write to a file or a pipe, we won't need a temp file so
926 * we'll use fstat to check for this */
927 fd = fileno(stdout);
928 if (fstat(fd, &st) != 0)
929 fatal("min12xxw: couldn't examine stdout via fstat");
930 if (S_ISREG(st.st_mode) || S_ISFIFO(st.st_mode)) {
931 tmp = stdout;
932 } else {
933 /* create temp file to hold the output until we're done */
934 if ((tmp = tmpfile()) == NULL)
935 fatal("min12xxw: couldn't create temporary file");
936 }
937
938 /* filter our input */
939 dojob(stdin, tmp);
940
941 if (stdout != tmp) {
942 /* if we used a temp file we need to copy output from temp
943 * file to stdout */
944 rewind(tmp);
945 if ((buf = (char*) malloc(16384 * sizeof(char))) == NULL)
946 fatal("min12xxw: couldn't allocate memory");
947 while ((!feof(tmp)) && (!ferror(tmp))) {
948 i = fread(buf, sizeof(char), 16384, tmp);
949 if (i <= 0) continue;
950 if (fwrite(buf, 1, i, stdout) != i)
951 fatal("min12xxw: couldn't write to stdout");
952 }
953 free(buf);
954 }
955
956 /* all went well :) */
957 return 0;
958 }
959