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