1/*
2 * PCL5.cpp
3 * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
4 * Copyright 2003 Michael Pfeiffer.
5 */
6
7
8#include "PCL5.h"
9
10#include <vector>
11
12#include <Alert.h>
13#include <Bitmap.h>
14#include <File.h>
15
16#include "DbgMsg.h"
17#include "Halftone.h"
18#include "JobData.h"
19#include "PackBits.h"
20#include "PCL5Cap.h"
21#include "PrinterData.h"
22#include "UIDriver.h"
23#include "ValidRect.h"
24
25
26PCL5Driver::PCL5Driver(BMessage* message, PrinterData* printerData,
27	const PrinterCap* printerCap)
28	:
29	GraphicsDriver(message, printerData, printerCap),
30	fCompressionMethod(0),
31	fHalftone(NULL)
32{
33}
34
35
36bool
37PCL5Driver::StartDocument()
38{
39	try {
40		_JobStart();
41		fHalftone = new Halftone(GetJobData()->GetSurfaceType(),
42			GetJobData()->GetGamma(), GetJobData()->GetInkDensity(),
43			GetJobData()->GetDitherType());
44		return true;
45	}
46	catch (TransportException& err) {
47		return false;
48	}
49}
50
51
52bool
53PCL5Driver::StartPage(int)
54{
55	return true;
56}
57
58
59bool
60PCL5Driver::EndPage(int)
61{
62	try {
63		WriteSpoolChar('\014');
64		return true;
65	}
66	catch (TransportException& err) {
67		return false;
68	}
69}
70
71
72bool
73PCL5Driver::EndDocument(bool)
74{
75	try {
76		if (fHalftone != NULL) {
77			delete fHalftone;
78			fHalftone = NULL;
79		}
80		_JobEnd();
81		return true;
82	}
83	catch (TransportException& err) {
84		return false;
85	}
86}
87
88
89bool
90PCL5Driver::NextBand(BBitmap* bitmap, BPoint* offset)
91{
92	DBGMSG(("> nextBand\n"));
93
94	try {
95		BRect bounds = bitmap->Bounds();
96
97		RECT rc;
98		rc.left = (int)bounds.left;
99		rc.top = (int)bounds.top;
100		rc.right = (int)bounds.right;
101		rc.bottom = (int)bounds.bottom;
102
103		int height = rc.bottom - rc.top + 1;
104
105		int x = (int)offset->x;
106		int y = (int)offset->y;
107
108		int pageHeight = GetPageHeight();
109
110		if (y + height > pageHeight)
111			height = pageHeight - y;
112
113		rc.bottom = height - 1;
114
115		DBGMSG(("height = %d\n", height));
116		DBGMSG(("x = %d\n", x));
117		DBGMSG(("y = %d\n", y));
118
119		if (get_valid_rect(bitmap, &rc)) {
120
121			DBGMSG(("validate rect = %d, %d, %d, %d\n",
122				rc.left, rc.top, rc.right, rc.bottom));
123
124			x = rc.left;
125			y += rc.top;
126
127			int width = rc.right - rc.left + 1;
128			int widthByte = (width + 7) / 8;
129				// byte boundary
130			int height = rc.bottom - rc.top + 1;
131			int in_size = widthByte;
132			int out_size = (widthByte * 6 + 4) / 5;
133			int delta = bitmap->BytesPerRow();
134
135			DBGMSG(("width = %d\n", width));
136			DBGMSG(("widthByte = %d\n", widthByte));
137			DBGMSG(("height = %d\n", height));
138			DBGMSG(("in_size = %d\n", in_size));
139			DBGMSG(("out_size = %d\n", out_size));
140			DBGMSG(("delta = %d\n", delta));
141			DBGMSG(("renderobj->Get_pixel_depth() = %d\n", fHalftone->GetPixelDepth()));
142
143			uchar* ptr = static_cast<uchar*>(bitmap->Bits())
144						+ rc.top * delta
145						+ (rc.left * fHalftone->GetPixelDepth()) / 8;
146
147			int compressionMethod;
148			int compressedSize;
149			const uchar* buffer;
150
151			std::vector<uchar> in_buffer(in_size);
152			std::vector<uchar> out_buffer(out_size);
153
154			DBGMSG(("move\n"));
155
156			_Move(x, y);
157			_StartRasterGraphics(width, height);
158
159			const bool color = GetJobData()->GetColor() == JobData::kColor;
160			const int num_planes = color ? 3 : 1;
161
162			if (color) {
163				fHalftone->SetPlanes(Halftone::kPlaneRGB1);
164				fHalftone->SetBlackValue(Halftone::kLowValueMeansBlack);
165			}
166
167			for (int i = rc.top; i <= rc.bottom; i++) {
168
169				for (int plane = 0; plane < num_planes; plane ++) {
170
171					fHalftone->Dither(&in_buffer[0], ptr, x, y, width);
172
173					compressedSize = pack_bits(&out_buffer[0], &in_buffer[0], in_size);
174
175					if (compressedSize + _BytesToEnterCompressionMethod(2)
176						< in_size + _BytesToEnterCompressionMethod(0)) {
177						compressionMethod = 2; // back bits
178						buffer = &out_buffer[0];
179					} else {
180						compressionMethod = 0; // uncompressed
181						buffer = &in_buffer[0];
182						compressedSize = in_size;
183					}
184
185					_RasterGraphics(
186						compressionMethod,
187						buffer,
188						compressedSize,
189						plane == num_planes - 1);
190
191				}
192
193				ptr  += delta;
194				y++;
195			}
196
197			_EndRasterGraphics();
198
199		} else
200			DBGMSG(("band bitmap is clean.\n"));
201
202		if (y >= pageHeight) {
203			offset->x = -1.0;
204			offset->y = -1.0;
205		} else
206			offset->y += height;
207
208		DBGMSG(("< nextBand\n"));
209		return true;
210	}
211	catch (TransportException& err) {
212		BAlert* alert = new BAlert("", err.What(), "OK");
213		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
214		alert->Go();
215		return false;
216	}
217}
218
219
220void
221PCL5Driver::_JobStart()
222{
223	const bool color = GetJobData()->GetColor() == JobData::kColor;
224	// enter PCL5
225	WriteSpoolString("\033%%-12345X@PJL ENTER LANGUAGE=PCL\n");
226	// reset
227	WriteSpoolString("\033E");
228	// dpi
229	WriteSpoolString("\033*t%dR", GetJobData()->GetXres());
230	// unit of measure
231	WriteSpoolString("\033&u%dD", GetJobData()->GetXres());
232	// page size
233	WriteSpoolString("\033&l0A");
234	// page orientation
235	WriteSpoolString("\033&l0O");
236	if (color) {
237		// 3 color planes (red, green, blue)
238		WriteSpoolString("\033*r3U");
239	}
240	// raster presentation
241	WriteSpoolString("\033*r0F");
242	// top maring and perforation skip
243	WriteSpoolString("\033&l0e0L");
244	// clear horizontal margins
245	WriteSpoolString("\0339");
246	// number of copies
247	// WriteSpoolString("\033&l%ldL", GetJobData()->GetCopies());
248}
249
250
251void
252PCL5Driver::_StartRasterGraphics(int width, int height)
253{
254	// width
255	WriteSpoolString("\033*r%dS", width);
256	// height
257	WriteSpoolString("\033*r%dT", height);
258	// start raster graphics
259	WriteSpoolString("\033*r1A");
260	fCompressionMethod = -1;
261}
262
263
264void
265PCL5Driver::_EndRasterGraphics()
266{
267	WriteSpoolString("\033*rB");
268}
269
270
271void
272PCL5Driver::_RasterGraphics(int compressionMethod, const uchar* buffer,
273	int size, bool lastPlane)
274{
275	if (fCompressionMethod != compressionMethod) {
276		WriteSpoolString("\033*b%dM", compressionMethod);
277		fCompressionMethod = compressionMethod;
278	}
279	WriteSpoolString("\033*b%d", size);
280	if (lastPlane)
281		WriteSpoolString("W");
282	else
283		WriteSpoolString("V");
284
285	WriteSpoolData(buffer, size);
286}
287
288
289void
290PCL5Driver::_JobEnd()
291{
292	WriteSpoolString("\033&l1T");
293	WriteSpoolString("\033E");
294}
295
296
297void
298PCL5Driver::_Move(int x, int y)
299{
300	WriteSpoolString("\033*p%dx%dY", x, y + 75);
301}
302
303
304int
305PCL5Driver::_BytesToEnterCompressionMethod(int compressionMethod)
306{
307	if (fCompressionMethod == compressionMethod)
308		return 0;
309	else
310		return 5;
311}
312