xref: /haiku/src/add-ons/print/drivers/gutenprint/GPJob.cpp (revision 992ae400ec160a3e2ef6ad71c011cd688c9f2476)
1 /*
2 * Copyright 2010, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Michael Pfeiffer
7 */
8 #include "GPJob.h"
9 
10 #include <Debug.h>
11 
12 
13 // 72 DPI
14 static const int32 kGutenprintUnit = 72;
15 
16 class CoordinateSystem
17 {
18 public:
CoordinateSystem()19 	CoordinateSystem()
20 	:
21 	fXDPI(0),
22 	fYDPI(0)
23 	{
24 	}
25 
26 
SetDPI(int32 x,int32 y)27 	void SetDPI(int32 x, int32 y) {
28 		fXDPI = x;
29 		fYDPI = y;
30 	}
31 
32 
ToGutenprint(int32 fromX,int32 fromY,double & toX,double & toY)33 	void ToGutenprint(int32 fromX, int32 fromY, double& toX, double& toY) {
34 		toX = fromX * kGutenprintUnit / fXDPI;
35 		toY = fromY * kGutenprintUnit / fYDPI;
36 	}
37 
38 
ToGutenprintCeiling(int32 fromX,int32 fromY,double & toX,double & toY)39 	void ToGutenprintCeiling(int32 fromX, int32 fromY, double& toX,
40 		double& toY) {
41 		toX = (fromX * kGutenprintUnit + fXDPI - 1) / fXDPI;
42 		toY = (fromY * kGutenprintUnit + fYDPI - 1) / fYDPI;
43 	}
44 
45 
FromGutenprint(double fromX,double fromY,int32 & toX,int32 & toY)46 	void FromGutenprint(double fromX, double fromY, int32& toX, int32& toY) {
47 		toX = (int32)(fromX * fXDPI / kGutenprintUnit);
48 		toY = (int32)(fromY * fYDPI / kGutenprintUnit);
49 	}
50 
FromGutenprintCeiling(double fromX,double fromY,int32 & toX,int32 & toY)51 	void FromGutenprintCeiling(double fromX, double fromY, int32& toX,
52 		int32& toY) {
53 		toX = (int32)((fromX * fXDPI + kGutenprintUnit - 1) / kGutenprintUnit);
54 		toY = (int32)((fromY * fYDPI + kGutenprintUnit - 1) / kGutenprintUnit);
55 	}
56 
SizeFromGutenprint(double fromWidth,double fromHeight,int32 & toWidth,int32 & toHeight)57 	void SizeFromGutenprint(double fromWidth, double fromHeight,
58 		int32& toWidth, int32& toHeight) {
59 		toWidth = (int32)(fromWidth * fXDPI / kGutenprintUnit);
60 		toHeight = (int32)(fromHeight * fYDPI / kGutenprintUnit);
61 	}
62 
RoundUpToWholeInches(int32 & width,int32 & height)63 	void RoundUpToWholeInches(int32& width, int32& height) {
64 		width = ((width + kGutenprintUnit - 1) / kGutenprintUnit)
65 			* kGutenprintUnit;
66 		height = ((height + kGutenprintUnit - 1) / kGutenprintUnit)
67 			* kGutenprintUnit;
68 	}
69 
70 private:
71 	double fXDPI;
72 	double fYDPI;
73 };
74 
75 
GPJob()76 GPJob::GPJob()
77 	:
78 	fApplicationName(),
79 	fOutputStream(NULL),
80 	fConfiguration(NULL),
81 	fHasPages(false),
82 	fVariables(NULL),
83 	fPrinter(NULL),
84 	fBands(NULL),
85 	fCachedBand(NULL),
86 	fStatus(B_OK)
87 {
88 	fImage.init = ImageInit;
89 	fImage.reset = ImageReset;
90 	fImage.width = ImageWidth;
91 	fImage.height = ImageHeight;
92 	fImage.get_row = ImageGetRow;
93 	fImage.get_appname = ImageGetAppname;
94 	fImage.conclude = ImageConclude;
95 	fImage.rep = this;
96 }
97 
98 
~GPJob()99 GPJob::~GPJob()
100 {
101 }
102 
103 
104 void
SetApplicationName(const BString & applicationName)105 GPJob::SetApplicationName(const BString& applicationName)
106 {
107 	fApplicationName = applicationName;
108 }
109 
110 
111 void
SetConfiguration(GPJobConfiguration * configuration)112 GPJob::SetConfiguration(GPJobConfiguration* configuration)
113 {
114 	fConfiguration = configuration;
115 }
116 
117 
118 void
SetOutputStream(OutputStream * outputStream)119 GPJob::SetOutputStream(OutputStream* outputStream)
120 {
121 	fOutputStream = outputStream;
122 }
123 
124 
125 status_t
Begin()126 GPJob::Begin()
127 {
128 	fStatus = B_OK;
129 
130 	stp_init();
131 
132 	fPrinter = stp_get_printer_by_driver(fConfiguration->fDriver);
133 	if (fPrinter == NULL) {
134 		fprintf(stderr, "GPJob Begin: driver %s not found!\n",
135 			fConfiguration->fDriver.String());
136 		return B_ERROR;
137 	}
138 
139 	fVariables = stp_vars_create();
140 	if (fVariables == NULL) {
141 		fprintf(stderr, "GPJob Begin: out of memory\n");
142 		return B_NO_MEMORY;
143 	}
144 	stp_set_printer_defaults(fVariables, fPrinter);
145 
146 	stp_set_outfunc(fVariables, OutputFunction);
147 	stp_set_errfunc(fVariables, ErrorFunction);
148 	stp_set_outdata(fVariables, this);
149 	stp_set_errdata(fVariables, this);
150 
151 	stp_set_string_parameter(fVariables, "PageSize",
152 		fConfiguration->fPageSize);
153 
154 	if (fConfiguration->fResolution != "")
155 		stp_set_string_parameter(fVariables, "Resolution",
156 			fConfiguration->fResolution);
157 
158 	stp_set_string_parameter(fVariables, "InputSlot",
159 		fConfiguration->fInputSlot);
160 
161 	stp_set_string_parameter(fVariables, "PrintingMode",
162 		fConfiguration->fPrintingMode);
163 
164 	{
165 		map<string, string>::iterator it = fConfiguration->fStringSettings.
166 			begin();
167 		for (; it != fConfiguration->fStringSettings.end(); it ++) {
168 			stp_set_string_parameter(fVariables, it->first.c_str(),
169 				it->second.c_str());
170 		}
171 	}
172 
173 	{
174 		map<string, bool>::iterator it = fConfiguration->fBooleanSettings.
175 			begin();
176 		for (; it != fConfiguration->fBooleanSettings.end(); it ++) {
177 			stp_set_boolean_parameter(fVariables, it->first.c_str(),
178 				it->second);
179 		}
180 	}
181 
182 	{
183 		map<string, int32>::iterator it = fConfiguration->fIntSettings.
184 			begin();
185 		for (; it != fConfiguration->fIntSettings.end(); it ++) {
186 			stp_set_int_parameter(fVariables, it->first.c_str(),
187 				it->second);
188 		}
189 	}
190 
191 	{
192 		map<string, int32>::iterator it = fConfiguration->fDimensionSettings.
193 			begin();
194 		for (; it != fConfiguration->fDimensionSettings.end(); it ++) {
195 			stp_set_dimension_parameter(fVariables, it->first.c_str(),
196 				it->second);
197 		}
198 	}
199 
200 	{
201 		map<string, double>::iterator it = fConfiguration->fDoubleSettings.
202 			begin();
203 		for (; it != fConfiguration->fDoubleSettings.end(); it ++) {
204 			stp_set_float_parameter(fVariables, it->first.c_str(),
205 				it->second);
206 		}
207 	}
208 
209 	stp_set_string_parameter(fVariables, "InputImageType",
210 		"RGB");
211 	stp_set_string_parameter(fVariables, "ChannelBitDepth",
212 		"8");
213 	stp_set_float_parameter(fVariables, "Density",
214 		1.0f);
215 	stp_set_string_parameter(fVariables, "JobMode", "Job");
216 
217 	stp_set_printer_defaults_soft(fVariables, fPrinter);
218 
219 	return B_OK;
220 }
221 
222 
223 void
End()224 GPJob::End()
225 {
226 	if (fVariables == NULL)
227 		return;
228 
229 	if (fHasPages)
230 		stp_end_job(fVariables, &fImage);
231 
232 	stp_vars_destroy(fVariables);
233 	fVariables = NULL;
234 }
235 
236 status_t
PrintPage(list<GPBand * > & bands)237 GPJob::PrintPage(list<GPBand*>& bands) {
238 	if (fStatus != B_OK)
239 		return fStatus;
240 
241 	fBands = &bands;
242 	fCachedBand = NULL;
243 
244 	Rectangle<stp_dimension_t> imageableArea;
245 	stp_get_imageable_area(fVariables, &imageableArea.left,
246 		&imageableArea.right, &imageableArea.bottom, &imageableArea.top);
247 	fprintf(stderr, "GPJob imageable area left %f, top %f, right %f, "
248 		"bottom %f\n",
249 		imageableArea.left, imageableArea.top, imageableArea.right,
250 		imageableArea.bottom);
251 	fprintf(stderr, "GPJob width %f %s, height %f %s\n",
252 		imageableArea.Width(),
253 		std::fmod(imageableArea.Width(), 72.) == 0.0 ? "whole inches" : "not whole inches",
254 		imageableArea.Height(),
255 		std::fmod(imageableArea.Height(), 72.) == 0.0 ? "whole inches" : "not whole inches"
256 		);
257 
258 	CoordinateSystem coordinateSystem;
259 	coordinateSystem.SetDPI(fConfiguration->fXDPI, fConfiguration->fYDPI);
260 	{
261 		// GPBand offset is relative to imageable area left, top
262 		// but it has to be absolute to left, top of page
263 		int32 offsetX;
264 		int32 offsetY;
265 		coordinateSystem.FromGutenprintCeiling(imageableArea.left,
266 			imageableArea.top, offsetX, offsetY);
267 
268 		BPoint offset(offsetX, offsetY);
269 		list<GPBand*>::iterator it = fBands->begin();
270 		for (; it != fBands->end(); it++) {
271 			(*it)->fWhere += offset;
272 		}
273 	}
274 
275 	fPrintRect = GetPrintRectangle(bands);
276 
277 	{
278 		int left = (int)fPrintRect.left;
279 		int top = (int)fPrintRect.top;
280 		int width = fPrintRect.Width() + 1;
281 		int height = fPrintRect.Height() + 1;
282 
283 		fprintf(stderr, "GPJob bitmap bands frame left %d, top %d, width %d, "
284 			"height %d\n",
285 			left, top, width, height);
286 	}
287 
288 	// calculate the position and size of the image to be printed on the page
289 	// unit: 1/72 Inches
290 	// constraints: the image must be inside the imageable area
291 	double left;
292 	double top;
293 	coordinateSystem.ToGutenprint(fPrintRect.left, fPrintRect.top, left, top);
294 	if (left < imageableArea.left)
295 		left = imageableArea.left;
296 	if (top < imageableArea.top)
297 		top = imageableArea.top;
298 
299 	double right;
300 	double bottom;
301 	coordinateSystem.ToGutenprintCeiling(fPrintRect.right, fPrintRect.bottom,
302 		right, bottom);
303 	if (right > imageableArea.right)
304 		right = imageableArea.right;
305 	if (bottom > imageableArea.bottom)
306 		bottom = imageableArea.bottom;
307 
308 	double width = right - left;
309 	double height = bottom - top;
310 
311 	// because of rounding and clipping in the previous step,
312 	// now the image frame has to be synchronized
313 	coordinateSystem.FromGutenprint(left, top, fPrintRect.left, fPrintRect.top);
314 	int32 printRectWidth;
315 	int32 printRectHeight;
316 	coordinateSystem.SizeFromGutenprint(width, height, printRectWidth,
317 		printRectHeight);
318 	fPrintRect.right = fPrintRect.left + printRectWidth - 1;
319 	fPrintRect.bottom = fPrintRect.top + printRectHeight - 1;
320 	{
321 		int left = fPrintRect.left;
322 		int top = fPrintRect.top;
323 		int width = fPrintRect.Width() + 1;
324 		int height = fPrintRect.Height() + 1;
325 
326 		fprintf(stderr, "GPJob image dimensions left %d, top %d, width %d, "
327 			"height %d\n",
328 			left, top, width, height);
329 	}
330 
331 	fprintf(stderr, "GPJob image dimensions in 1/72 Inches: "
332 		"left %d, top %d, right %d, bottom %d\n",
333 		(int)left, (int)top, (int)right, (int)bottom);
334 
335 	stp_set_width(fVariables, right - left);
336 	stp_set_height(fVariables, bottom - top);
337 	stp_set_left(fVariables, left);
338 	stp_set_top(fVariables, top);
339 
340 	stp_merge_printvars(fVariables, stp_printer_get_defaults(fPrinter));
341 
342 	if (!stp_verify(fVariables)) {
343 		fprintf(stderr, "GPJob PrintPage: invalid variables\n");
344 		return B_ERROR;
345 	}
346 
347 	if (!fHasPages) {
348 		fHasPages = true;
349 		stp_start_job(fVariables, &fImage);
350 	}
351 
352 	stp_print(fVariables, &fImage);
353 
354 	return fStatus;
355 }
356 
357 
358 void
GetErrorMessage(BString & message)359 GPJob::GetErrorMessage(BString& message)
360 {
361 	message = fErrorMessage;
362 }
363 
364 
365 RectInt32
GetPrintRectangle(list<GPBand * > & bands)366 GPJob::GetPrintRectangle(list<GPBand*>& bands)
367 {
368 	list<GPBand*>::iterator it = bands.begin();
369 	if (it == bands.end())
370 		return BRect(0, 0, 0, 0);
371 
372 	GPBand* first = *it;
373 	BRect rect = first->GetBoundingRectangle();
374 	for (it ++; it != bands.end(); it ++) {
375 		GPBand* band = *it;
376 		rect = rect | band->GetBoundingRectangle();
377 	}
378 	return rect;
379 }
380 
381 
382 void
Init()383 GPJob::Init()
384 {
385 
386 }
387 
388 
389 void
Reset()390 GPJob::Reset()
391 {
392 
393 }
394 
395 
396 int
Width()397 GPJob::Width()
398 {
399 	return fPrintRect.Width() + 1;
400 }
401 
402 
403 int
Height()404 GPJob::Height()
405 {
406 	return fPrintRect.Height() + 1;
407 }
408 
409 
410 stp_image_status_t
GetRow(unsigned char * data,size_t size,int row)411 GPJob::GetRow(unsigned char* data, size_t size, int row)
412 {
413 	if (fStatus != B_OK)
414 		return STP_IMAGE_STATUS_ABORT;
415 
416 	// row is relative to left, top of image
417 	// convert it to absolute y coordinate value
418 	int line = fPrintRect.top + row;
419 
420 	FillWhite(data, size);
421 
422 	GPBand* band = FindBand(line);
423 	if (band != NULL)
424 		FillRow(band, data, size, line);
425 
426 	return STP_IMAGE_STATUS_OK;
427 }
428 
429 
430 GPBand*
FindBand(int line)431 GPJob::FindBand(int line)
432 {
433 	if (fCachedBand != NULL && fCachedBand->ContainsLine(line))
434 		return fCachedBand;
435 
436 	list<GPBand*>::iterator it = fBands->begin();
437 	for (; it != fBands->end(); it ++) {
438 		GPBand* band = *it;
439 		if (band->ContainsLine(line)) {
440 			fCachedBand = band;
441 			return band;
442 		}
443 	}
444 
445 	fCachedBand = NULL;
446 	return NULL;
447 }
448 
449 
450 void
FillRow(GPBand * band,unsigned char * data,size_t size,int line)451 GPJob::FillRow(GPBand* band, unsigned char* data, size_t size, int line)
452 {
453 	int imageTop = line - static_cast<int>(band->fWhere.y -
454 		band->fValidRect.top);
455 	int imageLeft = static_cast<int>(band->fValidRect.left);
456 
457 	const int sourceBytesPerRow = band->fBitmap.BytesPerRow();
458 	const int kSourceBytesPerPixel = 4; // BGRA
459 	const unsigned char* source =
460 		static_cast<unsigned char*>(band->fBitmap.Bits())
461 		+ imageTop * sourceBytesPerRow
462 		+ imageLeft * kSourceBytesPerPixel;
463 
464 	int dataLeft = static_cast<int>(band->fWhere.x - fPrintRect.left);
465 	int sourcePixelsToSkip = 0;
466 	if (dataLeft < 0) {
467 		sourcePixelsToSkip = -dataLeft;
468 		dataLeft = 0;
469 	}
470 	int width = band->fValidRect.IntegerWidth() + 1 - sourcePixelsToSkip;
471 	source += sourcePixelsToSkip * kSourceBytesPerPixel;
472 	if (width <= 0)
473 		return;
474 
475 	const int kTargetBytesPerPixel = 3; // RGB
476 	unsigned char* target = &data[dataLeft * kTargetBytesPerPixel];
477 	int maxWidth = size / kTargetBytesPerPixel - dataLeft;
478 	if (width > maxWidth)
479 		width = maxWidth;
480 
481 	ASSERT(0 <= imageTop && imageTop <= band->fValidRect.IntegerHeight());
482 	ASSERT((dataLeft + width) * kTargetBytesPerPixel <= size);
483 
484 	for (int i = 0; i < width; i ++) {
485 		target[0] = source[2];
486 		target[1] = source[1];
487 		target[2] = source[0];
488 		target += kTargetBytesPerPixel;
489 		source += kSourceBytesPerPixel;
490 	}
491 }
492 
493 
494 void
FillWhite(unsigned char * data,size_t size)495 GPJob::FillWhite(unsigned char* data, size_t size)
496 {
497 	for (size_t i = 0; i < size; i ++)
498 		data[i] = 0xff;
499 }
500 
501 
502 const char*
GetAppname()503 GPJob::GetAppname()
504 {
505 	return fApplicationName.String();
506 }
507 
508 
509 void
Conclude()510 GPJob::Conclude()
511 {
512 	// nothing to do
513 }
514 
515 
516 void
Write(const char * data,size_t size)517 GPJob::Write(const char* data, size_t size)
518 {
519 	try {
520 		fOutputStream->Write(data, size);
521 	} catch (TransportException& e) {
522 		fStatus = B_IO_ERROR;
523 	}
524 }
525 
526 
527 void
ReportError(const char * data,size_t size)528 GPJob::ReportError(const char* data, size_t size)
529 {
530 	if (fStatus == B_OK)
531 		fStatus = B_ERROR;
532 	fErrorMessage.Append(data, size);
533 }
534 
535 
536 void
ImageInit(stp_image_t * image)537 GPJob::ImageInit(stp_image_t* image)
538 {
539 	GPJob* job = static_cast<GPJob*>(image->rep);
540 	job->Init();
541 }
542 
543 
544 void
ImageReset(stp_image_t * image)545 GPJob::ImageReset(stp_image_t* image)
546 {
547 	GPJob* job = static_cast<GPJob*>(image->rep);
548 	job->Reset();
549 }
550 
551 
552 int
ImageWidth(stp_image_t * image)553 GPJob::ImageWidth(stp_image_t* image)
554 {
555 	GPJob* job = static_cast<GPJob*>(image->rep);
556 	return job->Width();
557 }
558 
559 
560 int
ImageHeight(stp_image_t * image)561 GPJob::ImageHeight(stp_image_t *image)
562 {
563 	GPJob* job = static_cast<GPJob*>(image->rep);
564 	return job->Height();
565 }
566 
567 
568 stp_image_status_t
ImageGetRow(stp_image_t * image,unsigned char * data,size_t size,int row)569 GPJob::ImageGetRow(stp_image_t* image, unsigned char* data, size_t size,
570 	int row)
571 {
572 	GPJob* job = static_cast<GPJob*>(image->rep);
573 	return job->GetRow(data, size, row);
574 }
575 
576 
577 const char*
ImageGetAppname(stp_image_t * image)578 GPJob::ImageGetAppname(stp_image_t* image)
579 {
580 	GPJob* job = static_cast<GPJob*>(image->rep);
581 	return job->GetAppname();
582 }
583 
584 
585 void
ImageConclude(stp_image_t * image)586 GPJob::ImageConclude(stp_image_t *image)
587 {
588 	GPJob* job = static_cast<GPJob*>(image->rep);
589 	job->Conclude();
590 }
591 
592 
593 void
OutputFunction(void * cookie,const char * data,size_t size)594 GPJob::OutputFunction(void *cookie, const char *data, size_t size)
595 {
596 	GPJob* job = static_cast<GPJob*>(cookie);
597 	job->Write(data, size);
598 }
599 
600 
601 void
ErrorFunction(void * cookie,const char * data,size_t size)602 GPJob::ErrorFunction(void *cookie, const char *data, size_t size)
603 {
604 	GPJob* job = static_cast<GPJob*>(cookie);
605 	job->ReportError(data, size);
606 }
607 
608