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.
31class DeltaRowStreamCompressor : public AbstractDeltaRowCompressor
32{
33public:
34				DeltaRowStreamCompressor(int rowSize, uchar initialSeed,
35					PCL6Writer* writer)
36				:
37				AbstractDeltaRowCompressor(rowSize, initialSeed),
38				fWriter(writer)
39				{}
40
41protected:
42	void		AppendByteToDeltaRow(uchar byte)
43				{
44					fWriter->Append(byte);
45				}
46
47private:
48	PCL6Writer*	fWriter;
49};
50
51
52PCL6Driver::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
63void
64PCL6Driver::Write(const uint8* data, uint32 size)
65{
66	WriteSpoolData(data, size);
67}
68
69
70bool
71PCL6Driver::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
86bool
87PCL6Driver::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
101bool
102PCL6Driver::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
194void
195PCL6Driver::_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
245void
246PCL6Driver::_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
261bool
262PCL6Driver::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
300void
301PCL6Driver::_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
322void
323PCL6Driver::_EndRasterGraphics()
324{
325	fWriter->EndImage();
326}
327
328
329void
330PCL6Driver::_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
371bool
372PCL6Driver::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
384void
385PCL6Driver::_JobEnd()
386{
387	fWriter->CloseDataSource();
388	fWriter->EndSession();
389	fWriter->PJLFooter();
390	fWriter->Flush();
391	delete fWriter;
392	fWriter = NULL;
393}
394
395
396void
397PCL6Driver::_Move(int x, int y)
398{
399	fWriter->SetCursor(x, y);
400}
401
402
403bool
404PCL6Driver::_SupportsRLECompression()
405{
406	return GetJobData()->GetColor() != JobData::kColorCompressionDisabled;
407}
408
409
410bool
411PCL6Driver::_SupportsDeltaRowCompression()
412{
413	return GetProtocolClass() >= PCL6Writer::kProtocolClass2_1
414		&& GetJobData()->GetColor() != JobData::kColorCompressionDisabled;
415}
416
417
418bool
419PCL6Driver::_UseColorMode()
420{
421	return GetJobData()->GetColor() != JobData::kMonochrome;
422}
423
424
425PCL6Writer::MediaSize
426PCL6Driver::_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
472PCL6Writer::MediaSource
473PCL6Driver::_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