xref: /haiku/src/add-ons/print/drivers/pcl6/PCL6.cpp (revision cbe0a0c436162d78cc3f92a305b64918c839d079)
1 /*
2  * PCL6.cpp
3  * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
4  * Copyright 2003 Michael Pfeiffer.
5  */
6 
7 
8 #include "PCL6.h"
9 
10 #include <memory.h>
11 
12 #include <Alert.h>
13 #include <Bitmap.h>
14 #include <File.h>
15 
16 #include "DbgMsg.h"
17 #include "DeltaRowCompression.h"
18 #include "Halftone.h"
19 #include "JobData.h"
20 #include "PackBits.h"
21 #include "PCL6Cap.h"
22 #include "PCL6Config.h"
23 #include "PCL6Rasterizer.h"
24 #include "PrinterData.h"
25 #include "UIDriver.h"
26 #include "ValidRect.h"
27 
28 
29 // DeltaRowStreamCompressor writes the delta row directly to the
30 // in the contructor specified stream.
31 class DeltaRowStreamCompressor : public AbstractDeltaRowCompressor
32 {
33 public:
34 				DeltaRowStreamCompressor(int rowSize, uchar initialSeed,
35 					PCL6Writer* writer)
36 				:
37 				AbstractDeltaRowCompressor(rowSize, initialSeed),
38 				fWriter(writer)
39 				{}
40 
41 protected:
42 	void		AppendByteToDeltaRow(uchar byte)
43 				{
44 					fWriter->Append(byte);
45 				}
46 
47 private:
48 	PCL6Writer*	fWriter;
49 };
50 
51 
52 PCL6Driver::PCL6Driver(BMessage* message, PrinterData* printerData,
53 	const PrinterCap* printerCap)
54 	:
55 	GraphicsDriver(message, printerData, printerCap),
56 	fWriter(NULL),
57 	fMediaSide(PCL6Writer::kFrontMediaSide),
58 	fHalftone(NULL)
59 {
60 }
61 
62 
63 void
64 PCL6Driver::Write(const uint8* data, uint32 size)
65 {
66 	WriteSpoolData(data, size);
67 }
68 
69 
70 bool
71 PCL6Driver::StartDocument()
72 {
73 	try {
74 		_JobStart();
75 		fHalftone = new Halftone(GetJobData()->GetSurfaceType(),
76 			GetJobData()->GetGamma(), GetJobData()->GetInkDensity(),
77 			GetJobData()->GetDitherType());
78 		return true;
79 	}
80 	catch (TransportException& err) {
81 		return false;
82 	}
83 }
84 
85 
86 bool
87 PCL6Driver::EndDocument(bool)
88 {
89 	try {
90 		if (fHalftone)
91 			delete fHalftone;
92 		_JobEnd();
93 		return true;
94 	}
95 	catch (TransportException& err) {
96 		return false;
97 	}
98 }
99 
100 
101 bool
102 PCL6Driver::NextBand(BBitmap* bitmap, BPoint* offset)
103 {
104 	DBGMSG(("> nextBand\n"));
105 
106 	try {
107 		int y = (int)offset->y;
108 
109 		PCL6Rasterizer* rasterizer;
110 		if (_UseColorMode()) {
111 			#if COLOR_DEPTH == 8
112 				rasterizer = new ColorRGBRasterizer(fHalftone);
113 			#elif COLOR_DEPTH == 1
114 				rasterizer = new ColorRasterizer(fHalftone);
115 			#else
116 				#error COLOR_DEPTH must be either 1 or 8!
117 			#endif
118 		} else
119 			rasterizer = new MonochromeRasterizer(fHalftone);
120 
121 		auto_ptr<Rasterizer> _rasterizer(rasterizer);
122 		bool valid = rasterizer->SetBitmap((int)offset->x, (int)offset->y,
123 			bitmap, GetPageHeight());
124 
125 		if (valid) {
126 			rasterizer->InitializeBuffer();
127 
128 			// Use compressor to calculate delta row size
129 			DeltaRowCompressor* deltaRowCompressor = NULL;
130 			if (_SupportsDeltaRowCompression()) {
131 				deltaRowCompressor =
132 					new DeltaRowCompressor(rasterizer->GetOutRowSize(), 0);
133 				if (deltaRowCompressor->InitCheck() != B_OK) {
134 					delete deltaRowCompressor;
135 					return false;
136 				}
137 			}
138 			auto_ptr<DeltaRowCompressor>_deltaRowCompressor(deltaRowCompressor);
139 			int deltaRowSize = 0;
140 
141 			// remember position
142 			int xPage = rasterizer->GetX();
143 			int yPage = rasterizer->GetY();
144 
145 			while (rasterizer->HasNextLine()) {
146 				const uchar* rowBuffer =
147 					static_cast<const uchar*>(rasterizer->RasterizeNextLine());
148 
149 				if (deltaRowCompressor != NULL) {
150 					int size =
151 						deltaRowCompressor->CalculateSize(rowBuffer, true);
152 					deltaRowSize += size + 2;
153 						// two bytes for the row byte count
154 				}
155 			}
156 
157 			y = rasterizer->GetY();
158 
159 			uchar* outBuffer = rasterizer->GetOutBuffer();
160 			int outBufferSize = rasterizer->GetOutBufferSize();
161 			int outRowSize = rasterizer->GetOutRowSize();
162 			int width = rasterizer->GetWidth();
163 			int height = rasterizer->GetHeight();
164 			_WriteBitmap(outBuffer, outBufferSize, outRowSize, xPage, yPage,
165 				width, height, deltaRowSize);
166 		}
167 
168 		if (y >= GetPageHeight()) {
169 			offset->x = -1.0;
170 			offset->y = -1.0;
171 		} else {
172 			offset->y += bitmap->Bounds().IntegerHeight()+1;
173 		}
174 
175 		return true;
176 	}
177 	catch (TransportException& err) {
178 		BAlert* alert = new BAlert("", err.What(), "OK");
179 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
180 		alert->Go();
181 		return false;
182 	}
183 }
184 
185 
186 void
187 PCL6Driver::_WriteBitmap(const uchar* buffer, int outSize, int rowSize, int x,
188 	int y, int width, int height, int deltaRowSize)
189 {
190 	// choose the best compression method
191 	PCL6Writer::Compression compressionMethod = PCL6Writer::kNoCompression;
192 	int dataSize = outSize;
193 
194 #if ENABLE_DELTA_ROW_COMPRESSION
195 	if (_SupportsDeltaRowCompression() && deltaRowSize < dataSize) {
196 		compressionMethod = PCL6Writer::kDeltaRowCompression;
197 		dataSize = deltaRowSize;
198 	}
199 #endif
200 
201 #if ENABLE_RLE_COMPRESSION
202 	if (_SupportsRLECompression()) {
203 		int rleSize = pack_bits_size(buffer, outSize);
204 		if (rleSize < dataSize) {
205 			compressionMethod = PCL6Writer::kRLECompression;
206 			dataSize = rleSize;
207 		}
208 	}
209 #endif
210 
211 	// write bitmap
212 	_Move(x, y);
213 
214 	_StartRasterGraphics(x, y, width, height, compressionMethod);
215 
216 	_RasterGraphics(buffer, outSize, dataSize, rowSize, height,
217 		compressionMethod);
218 
219 	_EndRasterGraphics();
220 
221 #if DISPLAY_COMPRESSION_STATISTICS
222 	fprintf(stderr, "Out Size       %d %2.2f\n", (int)outSize, 100.0);
223 #if ENABLE_RLE_COMPRESSION
224 	fprintf(stderr, "RLE Size       %d %2.2f\n", (int)rleSize,
225 		100.0 * rleSize / outSize);
226 #endif
227 #if ENABLE_DELTA_ROW_COMPRESSION
228 	fprintf(stderr, "Delta Row Size %d %2.2f\n", (int)deltaRowSize,
229 		100.0 * deltaRowSize / outSize);
230 #endif
231 	fprintf(stderr, "Data Size      %d %2.2f\n", (int)dataSize,
232 		100.0 * dataSize / outSize);
233 #endif
234 }
235 
236 
237 void
238 PCL6Driver::_JobStart()
239 {
240 	// PCL6 begin
241 	fWriter = new PCL6Writer(this);
242 	PCL6Writer::ProtocolClass pc =
243 		(PCL6Writer::ProtocolClass)GetProtocolClass();
244 	fWriter->PJLHeader(pc, GetJobData()->GetXres(),
245 		"Copyright (c) 2003 - 2010 Haiku");
246 	fWriter->BeginSession(GetJobData()->GetXres(), GetJobData()->GetYres(),
247 		PCL6Writer::kInch, PCL6Writer::kBackChAndErrPage);
248 	fWriter->OpenDataSource();
249 	fMediaSide = PCL6Writer::kFrontMediaSide;
250 }
251 
252 
253 bool
254 PCL6Driver::StartPage(int)
255 {
256 	PCL6Writer::Orientation orientation = PCL6Writer::kPortrait;
257 	if (GetJobData()->GetOrientation() == JobData::kLandscape) {
258 		orientation = PCL6Writer::kLandscape;
259 	}
260 
261 	PCL6Writer::MediaSize mediaSize =
262 		_MediaSize(GetJobData()->GetPaper());
263 	PCL6Writer::MediaSource mediaSource =
264 		_MediaSource(GetJobData()->GetPaperSource());
265 	if (GetJobData()->GetPrintStyle() == JobData::kSimplex) {
266 		fWriter->BeginPage(orientation, mediaSize, mediaSource);
267 	} else if (GetJobData()->GetPrintStyle() == JobData::kDuplex) {
268 		// TODO move duplex binding option to UI
269 		fWriter->BeginPage(orientation, mediaSize, mediaSource,
270 			PCL6Writer::kDuplexVerticalBinding, fMediaSide);
271 
272 		if (fMediaSide == PCL6Writer::kFrontMediaSide)
273 			fMediaSide = PCL6Writer::kBackMediaSide;
274 		else
275 			fMediaSide = PCL6Writer::kFrontMediaSide;
276 	} else
277 		return false;
278 
279 	// PageOrigin from Windows NT printer driver
280 	int x = 142 * GetJobData()->GetXres() / 600;
281 	int y = 100 * GetJobData()->GetYres() / 600;
282 	fWriter->SetPageOrigin(x, y);
283 	fWriter->SetColorSpace(_UseColorMode() ? PCL6Writer::kRGB
284 		: PCL6Writer::kGray);
285 	fWriter->SetPaintTxMode(PCL6Writer::kOpaque);
286 	fWriter->SetSourceTxMode(PCL6Writer::kOpaque);
287 	fWriter->SetROP(204);
288 	return true;
289 }
290 
291 
292 void
293 PCL6Driver::_StartRasterGraphics(int x, int y, int width, int height,
294 	PCL6Writer::Compression compressionMethod)
295 {
296 	PCL6Writer::ColorDepth colorDepth;
297 	if (_UseColorMode()) {
298 		#if COLOR_DEPTH == 8
299 			colorDepth = PCL6Writer::k8Bit;
300 		#elif COLOR_DEPTH == 1
301 			colorDepth = PCL6Writer::k1Bit;
302 		#else
303 			#error COLOR_DEPTH must be either 1 or 8!
304 		#endif
305 	} else
306 		colorDepth = PCL6Writer::k1Bit;
307 
308 	fWriter->BeginImage(PCL6Writer::kDirectPixel, colorDepth, width, height,
309 		width, height);
310 	fWriter->ReadImage(compressionMethod, 0, height);
311 }
312 
313 
314 void
315 PCL6Driver::_EndRasterGraphics()
316 {
317 	fWriter->EndImage();
318 }
319 
320 
321 void
322 PCL6Driver::_RasterGraphics(const uchar* buffer, int bufferSize, int dataSize,
323 	int rowSize, int height, int compressionMethod)
324 {
325 	// write bitmap byte size
326 	fWriter->EmbeddedDataPrefix32(dataSize);
327 
328 	// write data
329 	if (compressionMethod == PCL6Writer::kRLECompression) {
330 		// use RLE compression
331 		uchar* outBuffer = new uchar[dataSize];
332 		pack_bits(outBuffer, buffer, bufferSize);
333 		fWriter->Append(outBuffer, dataSize);
334 		delete[] outBuffer;
335 		return;
336 	} else if (compressionMethod == PCL6Writer::kDeltaRowCompression) {
337 		// use delta row compression
338 		DeltaRowStreamCompressor compressor(rowSize, 0, fWriter);
339 		if (compressor.InitCheck() != B_OK) {
340 			return;
341 		}
342 
343 		const uint8* row = buffer;
344 		for (int i = 0; i < height; i ++) {
345 			// write row byte count
346 			int32 size = compressor.CalculateSize(row);
347 			fWriter->Append((uint16)size);
348 
349 			if (size > 0) {
350 				// write delta row
351 				compressor.Compress(row);
352 			}
353 
354 			row += rowSize;
355 		}
356 	} else {
357 		// write raw data
358 		fWriter->Append(buffer, bufferSize);
359 	}
360 }
361 
362 
363 bool
364 PCL6Driver::EndPage(int)
365 {
366 	try {
367 		fWriter->EndPage(GetJobData()->GetCopies());
368 		return true;
369 	}
370 	catch (TransportException& err) {
371 		return false;
372 	}
373 }
374 
375 
376 void
377 PCL6Driver::_JobEnd()
378 {
379 	fWriter->CloseDataSource();
380 	fWriter->EndSession();
381 	fWriter->PJLFooter();
382 	fWriter->Flush();
383 	delete fWriter;
384 	fWriter = NULL;
385 }
386 
387 
388 void
389 PCL6Driver::_Move(int x, int y)
390 {
391 	fWriter->SetCursor(x, y);
392 }
393 
394 
395 bool
396 PCL6Driver::_SupportsRLECompression()
397 {
398 	return GetJobData()->GetColor() != JobData::kColorCompressionDisabled;
399 }
400 
401 
402 bool
403 PCL6Driver::_SupportsDeltaRowCompression()
404 {
405 	return GetProtocolClass() >= PCL6Writer::kProtocolClass2_1
406 		&& GetJobData()->GetColor() != JobData::kColorCompressionDisabled;
407 }
408 
409 
410 bool
411 PCL6Driver::_UseColorMode()
412 {
413 	return GetJobData()->GetColor() != JobData::kMonochrome;
414 }
415 
416 
417 PCL6Writer::MediaSize
418 PCL6Driver::_MediaSize(JobData::Paper paper)
419 {
420 	switch (paper) {
421 		case JobData::kLetter:
422 			return PCL6Writer::kLetterPaper;
423 		case JobData::kLegal:
424 			return PCL6Writer::kLegalPaper;
425 		case JobData::kA4:
426 			return PCL6Writer::kA4Paper;
427 		case JobData::kExecutive:
428 			return PCL6Writer::kExecPaper;
429 		case JobData::kLedger:
430 			return PCL6Writer::kLedgerPaper;
431 		case JobData::kA3:
432 			return PCL6Writer::kA3Paper;
433 		case JobData::kB5:
434 			return PCL6Writer::kB5Paper;
435 		case JobData::kJapanesePostcard:
436 			return PCL6Writer::kJPostcard;
437 		case JobData::kA5:
438 			return PCL6Writer::kA5Paper;
439 		case JobData::kB4:
440 			return PCL6Writer::kJB4Paper;
441 /*
442 		case : return PCL6Writer::kCOM10Envelope;
443 		case : return PCL6Writer::kMonarchEnvelope;
444 		case : return PCL6Writer::kC5Envelope;
445 		case : return PCL6Writer::kDLEnvelope;
446 		case : return PCL6Writer::kJB4Paper;
447 		case : return PCL6Writer::kJB5Paper;
448 		case : return PCL6Writer::kB5Envelope;
449 		case : return PCL6Writer::kJPostcard;
450 		case : return PCL6Writer::kJDoublePostcard;
451 		case : return PCL6Writer::kA5Paper;
452 		case : return PCL6Writer::kA6Paper;
453 		case : return PCL6Writer::kJB6Paper;
454 		case : return PCL6Writer::kJIS8KPaper;
455 		case : return PCL6Writer::kJIS16KPaper;
456 		case : return PCL6Writer::kJISExecPaper;
457 */
458 		default:
459 			return PCL6Writer::kLegalPaper;
460 	}
461 }
462 
463 
464 PCL6Writer::MediaSource
465 PCL6Driver::_MediaSource(JobData::PaperSource source)
466 {
467 	switch (source) {
468 		case JobData::kAuto:
469 			return PCL6Writer::kAutoSelect;
470 		case JobData::kCassette1:
471 			return PCL6Writer::kDefaultSource;
472 		case JobData::kCassette2:
473 			return PCL6Writer::kEnvelopeTray;
474 		case JobData::kLower:
475 			return PCL6Writer::kLowerCassette;
476 		case JobData::kUpper:
477 			return PCL6Writer::kUpperCassette;
478 		case JobData::kMiddle:
479 			return PCL6Writer::kThirdCassette;
480 		case JobData::kManual:
481 			return PCL6Writer::kManualFeed;
482 		case JobData::kCassette3:
483 			return PCL6Writer::kMultiPurposeTray;
484 		default:
485 			return PCL6Writer::kAutoSelect;
486 	}
487 }
488