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