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