1 /* Copyright (C) 1999 Norihito Ohmori.
2
3 Ghostscript laser printer driver supporting Library.
4
5 This software is distributed in the hope that it will be useful, but
6 WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
7 to anyone for the consequences of using it or for whether it serves any
8 particular purpose or works at all, unless he says so in writing. Refer
9 to the GNU General Public License for full details.
10
11 Everyone is granted permission to copy, modify and redistribute
12 this software, but only under the conditions described in the GNU
13 General Public License. A copy of this license is supposed to have been
14 given to you along with this software so you can know your rights and
15 responsibilities. It should be in a file named COPYING. Among other
16 things, the copyright notice and this notice must be preserved on all
17 copies.
18 */
19
20 /*$Id: gdevlprn.c,v 1.3 2002/07/20 21:03:21 tillkamppeter Exp $ */
21 /* Library for Laser Printer Driver */
22
23 /*
24 Main part of this library was taked from Hiroshi Narimatsu's
25 Ghostscript driver, epag-3.08.
26 <ftp://ftp.humblesoft.com/pub/epag-3.08.tar.gz>
27 */
28
29 /*
30 ************************
31 * This Library Options *
32 ************************
33
34 -dTumble : for Duplex
35 true: short edge binding, false: long edge binding
36 the default is false.
37 -dBlockLine=number : number of line to make output area.
38 -dBlockWidth=number : rectangle width for output area.
39 -dBlockHeight=number : rectangle height for output area.
40 -dShowBubble : Draw rectangles, output area.
41 */
42
43 #include "gdevlprn.h"
44
45 /* ------ Get/put parameters ------ */
46
47 /* Get parameters. Laser Printer devices add several more parameters */
48 /* to the default Printer Device set. */
49 int
lprn_get_params(gx_device * dev,gs_param_list * plist)50 lprn_get_params(gx_device * dev, gs_param_list * plist)
51 {
52 gx_device_lprn *const lprn = (gx_device_lprn *) dev;
53 int code = gdev_prn_get_params(dev, plist);
54 int ncode;
55
56 if (code < 0)
57 return code;
58
59 if ((ncode = param_write_bool(plist, "ManualFeed", &lprn->ManualFeed)) < 0)
60 code = ncode;
61
62 if ((ncode = param_write_bool(plist, "NegativePrint", &lprn->NegativePrint)) < 0)
63 code = ncode;
64
65 if ((ncode = param_write_bool(plist, "Tumble", &lprn->Tumble)) < 0)
66 code = ncode;
67
68 if ((ncode = param_write_bool(plist, "RITOff", &lprn->RITOff)) < 0)
69 code = ncode;
70
71 if ((ncode = param_write_int(plist, "BlockLine", &lprn->BlockLine)) < 0)
72 code = ncode;
73
74 if ((ncode = param_write_int(plist, "BlockWidth", &lprn->nBw)) < 0)
75 code = ncode;
76
77 if ((ncode = param_write_int(plist, "BlockHeight", &lprn->nBh)) < 0)
78 code = ncode;
79
80 if ((ncode = param_write_bool(plist, "ShowBubble", &lprn->ShowBubble)) < 0)
81 code = ncode;
82
83 return code;
84 }
85
86 /* Put properties. */
87 int
lprn_put_params(gx_device * dev,gs_param_list * plist)88 lprn_put_params(gx_device * dev, gs_param_list * plist)
89 {
90 gx_device_lprn *const lprn = (gx_device_lprn *) dev;
91 int ecode = 0;
92 int code;
93 gs_param_name param_name;
94 bool ManualFeed = lprn->ManualFeed;
95 bool NegativePrint = lprn->NegativePrint;
96 bool Tumble = lprn->Tumble;
97 bool RITOff = lprn->RITOff;
98 int BlockLine = lprn->BlockLine;
99 int BlockWidth = lprn->nBw;
100 int BlockHeight = lprn->nBh;
101 bool ShowBubble = lprn->ShowBubble;
102
103 if ((code = param_read_bool(plist,
104 (param_name = "ManualFeed"),
105 &ManualFeed)) < 0) {
106 param_signal_error(plist, param_name, ecode = code);
107 }
108
109 if ((code = param_read_bool(plist,
110 (param_name = "NegativePrint"),
111 &NegativePrint)) < 0) {
112 param_signal_error(plist, param_name, ecode = code);
113 }
114 if ((code = param_read_bool(plist,
115 (param_name = "Tumble"),
116 &Tumble)) < 0) {
117 param_signal_error(plist, param_name, ecode = code);
118 }
119 if ((code = param_read_bool(plist,
120 (param_name = "RITOff"),
121 &RITOff)) < 0) {
122 param_signal_error(plist, param_name, ecode = code);
123 }
124 switch (code = param_read_int(plist,
125 (param_name = "BlockWidth"),
126 &BlockWidth)) {
127 case 0:
128 if (BlockWidth < 0)
129 ecode = gs_error_rangecheck;
130 else
131 break;
132 goto bwidthe;
133 default:
134 ecode = code;
135 bwidthe:param_signal_error(plist, param_name, ecode = code);
136 case 1:
137 break;
138 }
139
140 switch (code = param_read_int(plist,
141 (param_name = "BlockLine"),
142 &BlockLine)) {
143 case 0:
144 if (BlockLine < 0)
145 ecode = gs_error_rangecheck;
146 else
147 break;
148 goto crowe;
149 default:
150 ecode = code;
151 crowe:param_signal_error(plist, param_name, ecode = code);
152 case 1:
153 break;
154 }
155
156 switch (code = param_read_int(plist,
157 (param_name = "BlockHeight"),
158 &BlockHeight)) {
159 case 0:
160 if (BlockHeight < 0)
161 ecode = gs_error_rangecheck;
162 else
163 break;
164 goto bheighte;
165 default:
166 ecode = code;
167 bheighte:param_signal_error(plist, param_name, ecode = code);
168 case 1:
169 break;
170 }
171
172 if ((code = param_read_bool(plist,
173 (param_name = "ShowBubble"),
174 &ShowBubble)) < 0) {
175 param_signal_error(plist, param_name, ecode = code);
176 }
177 if (ecode < 0)
178 return ecode;
179 code = gdev_prn_put_params(dev, plist);
180 if (code < 0)
181 return code;
182
183 lprn->ManualFeed = ManualFeed;
184 lprn->NegativePrint = NegativePrint;
185 lprn->Tumble = Tumble;
186 lprn->RITOff = RITOff;
187 lprn->BlockLine = BlockLine;
188 lprn->nBw = BlockWidth;
189 lprn->nBh = BlockHeight;
190 lprn->ShowBubble = ShowBubble;
191
192 return 0;
193 }
194
195 static void lprn_bubble_flush_all(gx_device_printer * pdev, gp_file * fp);
196 static void lprn_process_line(gx_device_printer * pdev, gp_file * fp, int r, int h);
197 static void lprn_bubble_flush(gx_device_printer * pdev, gp_file * fp, Bubble * bbl);
198 static int lprn_is_black(gx_device_printer * pdev, int r, int h, int bx);
199 static void lprn_bubble_gen(gx_device_printer * pdev, int x0, int x1, int y0, int y1);
200 static void lprn_rect_add(gx_device_printer * pdev, gp_file * fp, int r, int h, int start, int end);
201
202 int
lprn_print_image(gx_device_printer * pdev,gp_file * fp)203 lprn_print_image(gx_device_printer * pdev, gp_file * fp)
204 {
205 gx_device_lprn *const lprn = (gx_device_lprn *) pdev;
206 int y;
207 int bpl = gdev_mem_bytes_per_scan_line(pdev);
208 Bubble *bbtbl;
209 Bubble *bbl;
210 int i;
211 int ri, rmin, read_y;
212 int code = 0;
213 Bubble *bubbleBuffer;
214 int maxBx, maxBy, maxY;
215 int start_y_block = 0; /* start of data in buffer */
216 int num_y_blocks = 0; /* number of data line [r:r+h-1] */
217
218 maxBx = (bpl + lprn->nBw - 1) / lprn->nBw;
219 maxBy = (pdev->height + lprn->nBh - 1) / lprn->nBh;
220 maxY = lprn->BlockLine / lprn->nBh * lprn->nBh;
221
222 if (!(lprn->ImageBuf = gs_malloc(pdev->memory->non_gc_memory, bpl, maxY, "lprn_print_image(ImageBuf)")))
223 return_error(gs_error_VMerror);
224 if (!(lprn->TmpBuf = gs_malloc(pdev->memory->non_gc_memory, bpl, maxY, "lprn_print_iamge(TmpBuf)")))
225 return_error(gs_error_VMerror);
226 if (!(lprn->bubbleTbl = gs_malloc(pdev->memory->non_gc_memory, sizeof(Bubble *), maxBx, "lprn_print_image(bubbleTbl)")))
227 return_error(gs_error_VMerror);
228 if (!(bubbleBuffer = gs_malloc(pdev->memory->non_gc_memory, sizeof(Bubble), maxBx, "lprn_print_image(bubbleBuffer)")))
229 return_error(gs_error_VMerror);
230
231 for (i = 0; i < maxBx; i++)
232 lprn->bubbleTbl[i] = NULL;
233 bbtbl = bubbleBuffer;
234
235 for (i = 0; i < maxBx - 1; i++)
236 bbtbl[i].next = &bbtbl[i + 1];
237 bbtbl[i].next = NULL;
238 lprn->freeBubbleList = &bbtbl[0];
239
240 for (y = 0; y < maxBy; y++) {
241 if (num_y_blocks + lprn->nBh > maxY) { /* bubble flush for next data */
242 rmin = start_y_block + lprn->nBh; /* process the data under rmin */
243 for (i = 0; i < maxBx; i++) {
244 bbl = lprn->bubbleTbl[i];
245 if (bbl != NULL && bbl->brect.p.y < rmin)
246 lprn_bubble_flush(pdev, fp, bbl);
247 }
248 num_y_blocks -= lprn->nBh; /* data if keeps in [r:r+h] */
249 start_y_block += lprn->nBh;
250
251 }
252 ri = start_y_block + num_y_blocks; /* read position */
253 read_y = ri % maxY; /* end of read position */
254 code = gdev_prn_copy_scan_lines(pdev, ri, lprn->ImageBuf + bpl * read_y, bpl * lprn->nBh);
255 if (code < 0)
256 return code;
257
258 num_y_blocks += lprn->nBh;
259
260 lprn_process_line(pdev, fp, start_y_block, num_y_blocks);
261 }
262 lprn_bubble_flush_all(pdev, fp); /* flush the rest of bubble */
263
264 gs_free(pdev->memory->non_gc_memory, lprn->ImageBuf, bpl, maxY, "lprn_print_image(ImageBuf)");
265 gs_free(pdev->memory->non_gc_memory, lprn->TmpBuf, bpl, maxY, "lprn_print_iamge(TmpBuf)");
266 gs_free(pdev->memory->non_gc_memory, lprn->bubbleTbl, sizeof(Bubble *), maxBx, "lprn_print_image(bubbleTbl)");
267 gs_free(pdev->memory->non_gc_memory, bubbleBuffer, sizeof(Bubble), maxBx, "lprn_print_image(bubbleBuffer)");
268
269 return code;
270 }
271
272 /*
273 * epag_bubble_flush_all: Output the rect of bubble.
274 */
275 static void
lprn_bubble_flush_all(gx_device_printer * pdev,gp_file * fp)276 lprn_bubble_flush_all(gx_device_printer * pdev, gp_file * fp)
277 {
278 gx_device_lprn *const lprn = (gx_device_lprn *) pdev;
279 int i = 0;
280 int bpl = gdev_mem_bytes_per_scan_line(pdev);
281 int maxBx = (bpl + lprn->nBw - 1) / lprn->nBw;
282
283 for (i = 0; i < maxBx; i++) {
284 if (lprn->bubbleTbl[i] != NULL) {
285 lprn_bubble_flush(pdev, fp, lprn->bubbleTbl[i]);
286 } else
287 break;
288 }
289 }
290
291 /*
292 * Process bh lines raster data.
293 */
294 static void
lprn_process_line(gx_device_printer * pdev,gp_file * fp,int r,int h)295 lprn_process_line(gx_device_printer * pdev, gp_file * fp, int r, int h)
296 {
297 gx_device_lprn *const lprn = (gx_device_lprn *) pdev;
298
299 int bx, bInBlack, bBlack, start;
300 int bpl = gdev_mem_bytes_per_scan_line(pdev);
301 int maxBx = (bpl + lprn->nBw - 1) / lprn->nBw;
302
303 bInBlack = 0;
304 for (bx = 0; bx < maxBx; bx++) {
305 bBlack = lprn_is_black(pdev, r, h, bx);
306 if (!bInBlack) {
307 if (bBlack) {
308 start = bx;
309 bInBlack = 1;
310 }
311 } else {
312 if (!bBlack) {
313 bInBlack = 0;
314 lprn_rect_add(pdev, fp, r, h, start, bx);
315 }
316 }
317 }
318 if (bInBlack)
319 lprn_rect_add(pdev, fp, r, h, start, bx - 1);
320 }
321
322 /* Search the bx line to make output */
323 static int
lprn_is_black(gx_device_printer * pdev,int r,int h,int bx)324 lprn_is_black(gx_device_printer * pdev, int r, int h, int bx)
325 {
326 gx_device_lprn *const lprn = (gx_device_lprn *) pdev;
327
328 int bh = lprn->nBh;
329 int bpl = gdev_mem_bytes_per_scan_line(pdev);
330 int x, y, y0;
331 byte *p;
332 int maxY = lprn->BlockLine / lprn->nBh * lprn->nBh;
333
334 y0 = (r + h - bh) % maxY;
335 for (y = 0; y < bh; y++) {
336 p = &lprn->ImageBuf[(y0 + y) * bpl + bx * lprn->nBw];
337 for (x = 0; x < lprn->nBw; x++) {
338 /* bpl isn't necessarily a multiple of lprn->nBw, so
339 we need to explicitly stop after the last byte in this
340 line to avoid accessing either the next line's data or
341 going off the end of our buffer completely. This avoids
342 https://bugs.ghostscript.com/show_bug.cgi?id=701785. */
343 if (bx * lprn->nBw + x >= bpl) break;
344 if (p[x] != 0)
345 return 1;
346 }
347 }
348 return 0;
349 }
350
351 static void
lprn_rect_add(gx_device_printer * pdev,gp_file * fp,int r,int h,int start,int end)352 lprn_rect_add(gx_device_printer * pdev, gp_file * fp, int r, int h, int start, int end)
353 {
354 gx_device_lprn *const lprn = (gx_device_lprn *) pdev;
355
356 int x0 = start * lprn->nBw;
357 int x1 = end * lprn->nBw - 1;
358 int y0 = r + h - lprn->nBh;
359 int y1 = y0 + lprn->nBh - 1;
360 int i;
361 Bubble *bbl;
362
363 if ((bbl = lprn->bubbleTbl[start]) != NULL &&
364 bbl->brect.q.y == y0 - 1 &&
365 bbl->brect.p.x == x0 &&
366 bbl->brect.q.x == x1) {
367 bbl->brect.q.y = y1;
368 } else {
369 for (i = start; i <= end; i++)
370 if (lprn->bubbleTbl[i] != NULL)
371 lprn_bubble_flush(pdev, fp, lprn->bubbleTbl[i]);
372 lprn_bubble_gen(pdev, x0, x1, y0, y1);
373 }
374 }
375
376 static void
lprn_bubble_gen(gx_device_printer * pdev,int x0,int x1,int y0,int y1)377 lprn_bubble_gen(gx_device_printer * pdev, int x0, int x1, int y0, int y1)
378 {
379 gx_device_lprn *const lprn = (gx_device_lprn *) pdev;
380
381 Bubble *bbl;
382 int bx0, bx1, bx;
383
384 assert(lprn->freeBubbleList != NULL);
385
386 bbl = lprn->freeBubbleList;
387 lprn->freeBubbleList = bbl->next;
388
389 bbl->brect.p.x = x0;
390 bbl->brect.q.x = x1;
391 bbl->brect.p.y = y0;
392 bbl->brect.q.y = y1;
393
394 bx0 = x0 / lprn->nBw;
395 bx1 = (x1 + lprn->nBw - 1) / lprn->nBw;
396
397 for (bx = bx0; bx <= bx1; bx++) {
398 assert(lprn->bubbleTbl[bx] == NULL);
399 lprn->bubbleTbl[bx] = bbl;
400 }
401 }
402
403 /* make output */
404 void
lprn_bubble_flush(gx_device_printer * pdev,gp_file * fp,Bubble * bbl)405 lprn_bubble_flush(gx_device_printer * pdev, gp_file * fp, Bubble * bbl)
406 {
407 gx_device_lprn *const lprn = (gx_device_lprn *) pdev;
408 int i, j, bx;
409 byte *p;
410 int bx0 = bbl->brect.p.x / lprn->nBw;
411 int bx1 = (bbl->brect.q.x + lprn->nBw - 1) / lprn->nBw;
412 int bpl = gdev_mem_bytes_per_scan_line(pdev);
413 int x = bbl->brect.p.x * 8;
414 int y = bbl->brect.p.y;
415 int width = (bbl->brect.q.x - bbl->brect.p.x + 1) * 8;
416 int height = bbl->brect.q.y - bbl->brect.p.y + 1;
417 int maxY = lprn->BlockLine / lprn->nBh * lprn->nBh;
418
419 for (i = 0; i < height; i++) {
420 p = lprn->ImageBuf + ((i + y) % maxY) * bpl;
421 for (j = 0; j < width / 8; j++) {
422 if (lprn->NegativePrint)
423 *(lprn->TmpBuf + i * width / 8 + j) = ~*(p + j + bbl->brect.p.x);
424 else
425 *(lprn->TmpBuf + i * width / 8 + j) = *(p + j + bbl->brect.p.x);
426 }
427 }
428
429 (*lprn->image_out) (pdev, fp, x, y, width, height);
430
431 /* Initialize bubbleTbl */
432 for (bx = bx0; bx <= bx1; bx++) {
433 assert(lprn->bubbleTbl[bx] == bbl);
434 lprn->bubbleTbl[bx] = NULL;
435 }
436
437 bbl->next = lprn->freeBubbleList;
438 lprn->freeBubbleList = bbl;
439 }
440