1 /*
2  * pbmtoibm23xx -- print pbm file on IBM 23XX printers
3  * Copyright (C) 2004  Jorrit Fahlke <jorrit@jorrit.de>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License version
7  * 2 or later as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  * USA
18  */
19 
20 /*
21  * This program is primarily based on the description of Brothers PPDS
22  * emulation (see
23  * http://www.brother.de/download/send_file.cfm?file_name=guide_ibmpro.pdf).
24  * However, there are some differences.  Their document states that
25  * ESC J does linefeed in terms of 1/216" -- my printer clearly does
26  * it in terms of 1/240".  Also, the quick and the slow mode for
27  * double density printing really makes a difference on my printer,
28  * the result of printing tiger.ps in quick double density mode was
29  * worse than printing it in single density mode.
30  *
31  * If anyone Knows of any better documentation of the language used by
32  * the IBM 23XX or PPDS in general, please send a mail to
33  * Jö Fahlke <jorrit@jorrit.de>.
34  *
35  * All the graphics modes of the printer differ only in the resolution
36  * in x they provide (and how quick they do their job).  They print a
37  * line of 8 pixels height and variable widths.  The bitlines within
38  * the line are 1/60" apart, so that is the resolution you can
39  * normally achieve in y.  But the printer is able to do line feeds in
40  * terms of 1/240", so the trick to print in higher resolutions is to
41  * print in several interleaved passes, and do a line feed of 1/240"
42  * or 1/120" in between.
43  */
44 
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 
49 #include "pm_c_util.h"
50 #include "pbm.h"
51 #include "shhopt.h"
52 #include "mallocvar.h"
53 
54 struct cmdlineInfo {
55     unsigned char graph_mode;
56     unsigned int passes;
57     unsigned int nFiles;
58     const char ** inputFile;
59 };
60 
61 
62 bool sent_xon; /* We have send x-on to enable the printer already */
63 
64 
65 
66 
67 static void
parseCommandLine(int argc,char ** const argv,struct cmdlineInfo * const cmdlineP)68 parseCommandLine(int argc, char ** const argv,
69                  struct cmdlineInfo * const cmdlineP) {
70 
71     optStruct3 opt;
72     optEntry option_def[100];
73 
74     unsigned int option_def_index = 0;
75     unsigned int xresSpec, yresSpec;
76     unsigned int xres, yres;
77     unsigned int slowMode;
78 
79     OPTENT3(0, "xres", OPT_UINT, &xres, &xresSpec, 0);
80     OPTENT3(0, "yres", OPT_UINT, &yres, &yresSpec, 0);
81     OPTENT3(0, "slow", OPT_FLAG, NULL,  &slowMode, 0);
82 
83     opt.opt_table = option_def;
84     opt.short_allowed = 0;
85     opt.allowNegNum = 0;
86 
87     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
88         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
89 
90     if (!xresSpec)
91         pm_error("You must specify the -xres option");
92     if (!yresSpec)
93         pm_error("You must specify the -yres option");
94 
95     switch (xres) {
96     case  60: cmdlineP->graph_mode = 'K';                  break;
97     case 120: cmdlineP->graph_mode = slowMode ? 'L' : 'Y'; break;
98     case 240: cmdlineP->graph_mode = 'Z';                  break;
99     default:
100         pm_error("Please specify 60, 120, or 240 for -xres");
101     }
102 
103     if (yres != 60 && yres != 120 && yres != 240)
104         pm_error("Please specify 60, 120, or 240 for -yres");
105 
106     cmdlineP->passes = yres / 60;
107 
108     cmdlineP->nFiles = MAX(argc-1, 1);
109     MALLOCARRAY_NOFAIL(cmdlineP->inputFile, cmdlineP->nFiles);
110 
111     if (argc-1 < 1)
112         cmdlineP->inputFile[0] = "-";
113     else {
114         unsigned int i;
115         for (i = 0; i < argc-1; ++i)
116             cmdlineP->inputFile[i] = argv[i+1];
117     }
118 }
119 
120 
121 
122 /* Read all pbm images from a filehandle and print them */
123 static void
process_handle(FILE * const fh,unsigned char const graph_mode,unsigned int const passes)124 process_handle(FILE *        const fh,
125                unsigned char const graph_mode,
126                unsigned int  const passes) {
127     int eof;
128 
129     while(pbm_nextimage(fh, &eof), eof == 0) {
130         /* pbm header dats */
131         int cols, rows, format;
132         /* iteration variables */
133         unsigned int x, y;
134         unsigned int bitline; /* pixel line within a single printing line */
135         unsigned int pass;
136         /* here we build the to-be-printed data */
137         unsigned char *output;  /* for reading one row from the file */
138         bit *row;
139 
140         /* Enable printer in case it is disabled, do it only once */
141         if(!sent_xon) {
142             putchar(0x11);
143             sent_xon = TRUE;
144         }
145 
146         pbm_readpbminit(fh, &cols, &rows, &format);
147 
148         output = malloc(sizeof(*output) * cols * passes);
149         if(output == NULL)
150             pm_error("Out of memory");
151         row = pbm_allocrow(cols);
152 
153         for(y = 0; y < rows; y += 8 * passes) {
154             memset(output, 0, sizeof(*output) * cols * passes);
155             for(bitline = 0; bitline < 8; ++bitline)
156                 for(pass = 0; pass < passes; ++pass)
157                     /* don't read beyond the end of the image if
158                        height is not a multiple of passes
159                     */
160                     if(y + bitline * passes + pass < rows) {
161                         pbm_readpbmrow(fh, row, cols, format);
162                         for(x = 0; x < cols; ++x)
163                             if(row[x] == PBM_BLACK)
164                                 output[cols * pass + x] |= 1 << (7 - bitline);
165                     }
166             for(pass = 0; pass < passes; ++pass){
167                 /* write graphics data */
168                 putchar(0x1b); putchar(graph_mode);
169                 putchar(cols & 0xff); putchar((cols >> 8) & 0xff);
170                 fwrite(output + pass * cols, sizeof(*output), cols, stdout);
171 
172                 /* Carriage return */
173                 putchar('\r');
174 
175                 /* move one pixel down */
176                 putchar(0x1b); putchar('J'); putchar(4 / passes);
177             }
178 
179             /* move one line - passes pixel down */
180             putchar(0x1b); putchar('J'); putchar(24 - 4);
181         }
182         putchar(0x0c); /* Form-feed */
183 
184         pbm_freerow(row);
185         free(output);
186     }
187 }
188 
189 
190 
191 int
main(int argc,char ** argv)192 main(int argc,char **argv) {
193 
194   struct cmdlineInfo cmdline;
195   unsigned int i;
196 
197   pbm_init(&argc, argv);
198 
199   parseCommandLine(argc, argv, &cmdline);
200 
201   sent_xon = FALSE;
202 
203   for (i = 0; i < cmdline.nFiles; ++i) {
204       FILE *ifP;
205       pm_message("opening '%s'", cmdline.inputFile[i]);
206       ifP = pm_openr(cmdline.inputFile[i]);
207       process_handle(ifP, cmdline.graph_mode, cmdline.passes);
208       pm_close(ifP);
209   }
210 
211   free(cmdline.inputFile);
212 
213   return 0;
214 }
215