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	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
186void
187PCL6Driver::_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
237void
238PCL6Driver::_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
253bool
254PCL6Driver::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
292void
293PCL6Driver::_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
314void
315PCL6Driver::_EndRasterGraphics()
316{
317	fWriter->EndImage();
318}
319
320
321void
322PCL6Driver::_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
363bool
364PCL6Driver::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
376void
377PCL6Driver::_JobEnd()
378{
379	fWriter->CloseDataSource();
380	fWriter->EndSession();
381	fWriter->PJLFooter();
382	fWriter->Flush();
383	delete fWriter;
384	fWriter = NULL;
385}
386
387
388void
389PCL6Driver::_Move(int x, int y)
390{
391	fWriter->SetCursor(x, y);
392}
393
394
395bool
396PCL6Driver::_SupportsRLECompression()
397{
398	return GetJobData()->GetColor() != JobData::kColorCompressionDisabled;
399}
400
401
402bool
403PCL6Driver::_SupportsDeltaRowCompression()
404{
405	return GetProtocolClass() >= PCL6Writer::kProtocolClass2_1
406		&& GetJobData()->GetColor() != JobData::kColorCompressionDisabled;
407}
408
409
410bool
411PCL6Driver::_UseColorMode()
412{
413	return GetJobData()->GetColor() != JobData::kMonochrome;
414}
415
416
417PCL6Writer::MediaSize
418PCL6Driver::_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
464PCL6Writer::MediaSource
465PCL6Driver::_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