xref: /haiku/src/libs/print/libprint/GraphicsDriver.cpp (revision 5b189b0e1e2f51f367bfcb126b2f00a3702f352d)
1 /*
2  * GraphicsDriver.cpp
3  * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
4  */
5 
6 #include <algorithm>
7 #include <cstdio>
8 #include <cstdarg>
9 
10 #include <Alert.h>
11 #include <Bitmap.h>
12 #include <Debug.h>
13 #include <Message.h>
14 #include <PrintJob.h>
15 #include <Region.h>
16 #include <TextControl.h>
17 #include <TextControl.h>
18 #include <StopWatch.h>
19 #include <View.h>
20 #include <Directory.h>
21 #include <File.h>
22 
23 #include "GraphicsDriver.h"
24 #include "PrintProcess.h"
25 #include "JobData.h"
26 #include "PrinterData.h"
27 #include "PrinterCap.h"
28 #include "Preview.h"
29 #include "Transport.h"
30 #include "ValidRect.h"
31 #include "DbgMsg.h"
32 
33 
34 using namespace std;
35 
36 
37 // Measure printJob() time. Either true or false.
38 #define MEASURE_PRINT_JOB_TIME false
39 
40 
41 enum {
42 	kMaxMemorySize = 4 * 1024 * 1024
43 };
44 
45 
46 GraphicsDriver::GraphicsDriver(BMessage* message, PrinterData* printerData,
47 	const PrinterCap* printerCap)
48 	:
49 	fMessage(message),
50 	fView(NULL),
51 	fBitmap(NULL),
52 	fRotatedBitmap(NULL),
53 	fTransport(NULL),
54 	fOrgJobData(NULL),
55 	fRealJobData(NULL),
56 	fPrinterData(printerData),
57 	fPrinterCap(printerCap),
58 	fSpoolMetaData(NULL),
59 	fPageWidth(0),
60 	fPageHeight(0),
61 	fBandWidth(0),
62 	fBandHeight(0),
63 	fPixelDepth(0),
64 	fBandCount(0),
65 	fInternalCopies(0),
66 	fPageCount(0),
67 	fStatusWindow(NULL)
68 {
69 }
70 
71 
72 GraphicsDriver::~GraphicsDriver()
73 {
74 }
75 
76 
77 static BRect
78 RotateRect(BRect rect)
79 {
80 	BRect rotated(rect.top, rect.left, rect.bottom, rect.right);
81 	return rotated;
82 }
83 
84 
85 bool
86 GraphicsDriver::_SetupData(BFile* spoolFile)
87 {
88 	if (fOrgJobData != NULL) {
89 		// already initialized
90 		return true;
91 	}
92 
93 	print_file_header pfh;
94 	spoolFile->Seek(0, SEEK_SET);
95 	spoolFile->Read(&pfh, sizeof(pfh));
96 
97 	DBGMSG(("print_file_header::version = 0x%x\n",  pfh.version));
98 	DBGMSG(("print_file_header::page_count = %d\n", pfh.page_count));
99 	DBGMSG(("print_file_header::first_page = 0x%x\n", (int)pfh.first_page));
100 
101 	if (pfh.page_count <= 0) {
102 		// nothing to print
103 		return false;
104 	}
105 
106 	fPageCount = (uint32) pfh.page_count;
107 	BMessage *msg = new BMessage();
108 	msg->Unflatten(spoolFile);
109 	fOrgJobData = new JobData(msg, fPrinterCap, JobData::kJobSettings);
110 	DUMP_BMESSAGE(msg);
111 	delete msg;
112 
113 	fRealJobData = new JobData(*fOrgJobData);
114 
115 	switch (fOrgJobData->GetNup()) {
116 	case 2:
117 	case 8:
118 	case 32:
119 	case 128:
120 		fRealJobData->SetPrintableRect(
121 			RotateRect(fOrgJobData->GetPrintableRect()));
122 		fRealJobData->SetScaledPrintableRect(
123 			RotateRect(fOrgJobData->GetScaledPrintableRect()));
124 		fRealJobData->SetPhysicalRect(
125 			RotateRect(fOrgJobData->GetPhysicalRect()));
126 		fRealJobData->SetScaledPhysicalRect(
127 			RotateRect(fOrgJobData->GetScaledPhysicalRect()));
128 
129 		if (JobData::kPortrait == fOrgJobData->GetOrientation())
130 			fRealJobData->SetOrientation(JobData::kLandscape);
131 		else
132 			fRealJobData->SetOrientation(JobData::kPortrait);
133 		break;
134 	}
135 
136 	if (fOrgJobData->GetCollate() && fPageCount > 1) {
137 		fRealJobData->SetCopies(1);
138 		fInternalCopies = fOrgJobData->GetCopies();
139 	} else {
140 		fInternalCopies = 1;
141 	}
142 
143 	fSpoolMetaData = new SpoolMetaData(spoolFile);
144 	return true;
145 }
146 
147 
148 void
149 GraphicsDriver::_CleanupData()
150 {
151 	delete fRealJobData;
152 	delete fOrgJobData;
153 	delete fSpoolMetaData;
154 	fRealJobData   = NULL;
155 	fOrgJobData    = NULL;
156 	fSpoolMetaData = NULL;
157 }
158 
159 
160 void
161 GraphicsDriver::_SetupBitmap()
162 {
163 	fPixelDepth = color_space2pixel_depth(fOrgJobData->GetSurfaceType());
164 
165 	fPageWidth  = (fRealJobData->GetPhysicalRect().IntegerWidth()
166 		* fOrgJobData->GetXres() + 71) / 72;
167 	fPageHeight = (fRealJobData->GetPhysicalRect().IntegerHeight()
168 		* fOrgJobData->GetYres() + 71) / 72;
169 
170 	int widthByte = (fPageWidth * fPixelDepth + 7) / 8;
171 	int size = widthByte * fPageHeight;
172 #ifdef USE_PREVIEW_FOR_DEBUG
173 	size = 0;
174 #endif
175 
176 	if (size < kMaxMemorySize) {
177 		fBandCount  = 0;
178 		fBandWidth  = fPageWidth;
179 		fBandHeight = fPageHeight;
180 	} else {
181 		fBandCount  = (size + kMaxMemorySize - 1) / kMaxMemorySize;
182 		if (_NeedRotateBitmapBand()) {
183 			fBandWidth  = (fPageWidth + fBandCount - 1) / fBandCount;
184 			fBandHeight = fPageHeight;
185 		} else {
186 			fBandWidth  = fPageWidth;
187 			fBandHeight = (fPageHeight + fBandCount - 1) / fBandCount;
188 		}
189 	}
190 
191 	DBGMSG(("****************\n"));
192 	DBGMSG(("page_width  = %d\n", fPageWidth));
193 	DBGMSG(("page_height = %d\n", fPageHeight));
194 	DBGMSG(("band_count  = %d\n", fBandCount));
195 	DBGMSG(("band_height = %d\n", fBandHeight));
196 	DBGMSG(("****************\n"));
197 
198 	BRect rect;
199 	rect.Set(0, 0, fBandWidth - 1, fBandHeight - 1);
200 	fBitmap = new BBitmap(rect, fOrgJobData->GetSurfaceType(), true);
201 	fView   = new BView(rect, "", B_FOLLOW_ALL, B_WILL_DRAW);
202 	fBitmap->AddChild(fView);
203 
204 	if (_NeedRotateBitmapBand()) {
205 		BRect rotatedRect(0, 0, rect.bottom, rect.right);
206 		fRotatedBitmap = new BBitmap(rotatedRect, fOrgJobData->GetSurfaceType(),
207 			false);
208 	}
209 }
210 
211 
212 void
213 GraphicsDriver::_CleanupBitmap()
214 {
215 	delete fBitmap;
216 	fBitmap = NULL;
217 	fView   = NULL;
218 
219 	delete fRotatedBitmap;
220 	fRotatedBitmap = NULL;
221 }
222 
223 
224 BPoint
225 GraphicsDriver::GetScale(int32 nup, BRect physicalRect, float scaling)
226 {
227 	float width;
228 	float height;
229 	BPoint scale;
230 
231 	scale.x = scale.y = 1.0f;
232 
233 	switch (nup) {
234 	case 1:
235 		scale.x = scale.y = 1.0f;
236 		break;
237 	case 2:	/* 1x2 or 2x1 */
238 		width  = physicalRect.Width();
239 		height = physicalRect.Height();
240 		if (width < height) {	// portrait
241 			scale.x = height / 2.0f / width;
242 			scale.y = width / height;
243 		} else {	// landscape
244 			scale.x = height / width;
245 			scale.y = width / 2.0f / height;
246 		}
247 		break;
248 	case 8:	/* 2x4 or 4x2 */
249 		width  = physicalRect.Width();
250 		height = physicalRect.Height();
251 		if (width < height) {
252 			scale.x = height / 4.0f / width;
253 			scale.y = width / height / 2.0f;
254 		} else {
255 			scale.x = height / width / 2.0f;
256 			scale.y = width / 4.0f / height;
257 		}
258 		break;
259 	case 32:	/* 4x8 or 8x4 */
260 		width  = physicalRect.Width();
261 		height = physicalRect.Height();
262 		if (width < height) {
263 			scale.x = height / 8.0f / width;
264 			scale.y = width / height / 4.0f;
265 		} else {
266 			scale.x = height / width / 4.0f;
267 			scale.y = width / 8.0f / height;
268 		}
269 		break;
270 	case 4:		/* 2x2 */
271 		scale.x = scale.y = 1.0f / 2.0f;
272 		break;
273 	case 9:		/* 3x3 */
274 		scale.x = scale.y = 1.0f / 3.0f;
275 		break;
276 	case 16:	/* 4x4 */
277 		scale.x = scale.y = 1.0f / 4.0f;
278 		break;
279 	case 25:	/* 5x5 */
280 		scale.x = scale.y = 1.0f / 5.0f;
281 		break;
282 	case 36:	/* 6x6 */
283 		scale.x = scale.y = 1.0f / 6.0f;
284 		break;
285 	case 49:	/* 7x7 */
286 		scale.x = scale.y = 1.0f / 7.0f;
287 		break;
288 	case 64:	/* 8x8 */
289 		scale.x = scale.y = 1.0f / 8.0f;
290 		break;
291 	case 81:	/* 9x9 */
292 		scale.x = scale.y = 1.0f / 9.0f;
293 		break;
294 	case 100:	/* 10x10 */
295 		scale.x = scale.y = 1.0f / 10.0f;
296 		break;
297 	case 121:	/* 11x11 */
298 		scale.x = scale.y = 1.0f / 11.0f;
299 		break;
300 	}
301 
302 	scale.x = scale.x * scaling / 100.0;
303 	scale.y = scale.y * scaling / 100.0;
304 
305 	return scale;
306 }
307 
308 
309 BPoint
310 GraphicsDriver::GetOffset(int32 nup, int index,
311 	JobData::Orientation orientation, const BPoint* scale,
312 	BRect scaledPhysicalRect, BRect scaledPrintableRect,
313 	BRect physicalRect)
314 {
315 	BPoint offset;
316 	offset.x = 0;
317 	offset.y = 0;
318 
319 	float width  = scaledPhysicalRect.Width();
320 	float height = scaledPhysicalRect.Height();
321 
322 	switch (nup) {
323 	case 1:
324 		break;
325 	case 2:
326 		if (index == 1) {
327 			if (JobData::kPortrait == orientation) {
328 				offset.x = width;
329 			} else {
330 				offset.y = height;
331 			}
332 		}
333 		break;
334 	case 8:
335 		if (JobData::kPortrait == orientation) {
336 			offset.x = width  * (index / 2);
337 			offset.y = height * (index % 2);
338 		} else {
339 			offset.x = width  * (index % 2);
340 			offset.y = height * (index / 2);
341 		}
342 		break;
343 	case 32:
344 		if (JobData::kPortrait == orientation) {
345 			offset.x = width  * (index / 4);
346 			offset.y = height * (index % 4);
347 		} else {
348 			offset.x = width  * (index % 4);
349 			offset.y = height * (index / 4);
350 		}
351 		break;
352 	case 4:
353 		offset.x = width  * (index / 2);
354 		offset.y = height * (index % 2);
355 		break;
356 	case 9:
357 		offset.x = width  * (index / 3);
358 		offset.y = height * (index % 3);
359 		break;
360 	case 16:
361 		offset.x = width  * (index / 4);
362 		offset.y = height * (index % 4);
363 		break;
364 	case 25:
365 		offset.x = width  * (index / 5);
366 		offset.y = height * (index % 5);
367 		break;
368 	case 36:
369 		offset.x = width  * (index / 6);
370 		offset.y = height * (index % 6);
371 		break;
372 	case 49:
373 		offset.x = width  * (index / 7);
374 		offset.y = height * (index % 7);
375 		break;
376 	case 64:
377 		offset.x = width  * (index / 8);
378 		offset.y = height * (index % 8);
379 		break;
380 	case 81:
381 		offset.x = width  * (index / 9);
382 		offset.y = height * (index % 9);
383 		break;
384 	case 100:
385 		offset.x = width  * (index / 10);
386 		offset.y = height * (index % 10);
387 		break;
388 	case 121:
389 		offset.x = width  * (index / 11);
390 		offset.y = height * (index % 11);
391 		break;
392 	}
393 
394 	// adjust margin
395 	offset.x += scaledPrintableRect.left - physicalRect.left;
396 	offset.y += scaledPrintableRect.top - physicalRect.top;
397 
398 	float real_scale = min(scale->x, scale->y);
399 	if (real_scale != scale->x)
400 		offset.x *= scale->x / real_scale;
401 	else
402 		offset.y *= scale->y / real_scale;
403 
404 	return offset;
405 }
406 
407 
408 // print the specified pages on a physical page
409 bool
410 GraphicsDriver::_PrintPage(PageDataList* pages)
411 {
412 	BPoint offset;
413 	offset.x = 0.0f;
414 	offset.y = 0.0f;
415 
416 	if (pages == NULL) {
417 		return true;
418 	}
419 
420 	do {
421 		// clear the physical page
422 		fView->SetScale(1.0);
423 		fView->SetHighColor(255, 255, 255);
424 		fView->ConstrainClippingRegion(NULL);
425 		fView->FillRect(fView->Bounds());
426 
427 		BPoint scale = GetScale(fOrgJobData->GetNup(),
428 			fOrgJobData->GetPhysicalRect(), fOrgJobData->GetScaling());
429 		float real_scale = min(scale.x, scale.y) * fOrgJobData->GetXres()
430 			/ 72.0f;
431 		fView->SetScale(real_scale);
432 		float x = offset.x / real_scale;
433 		float y = offset.y / real_scale;
434 		int page_index = 0;
435 
436 		for (PageDataList::iterator it = pages->begin(); it != pages->end();
437 			it++) {
438 			BPoint left_top(GetOffset(fOrgJobData->GetNup(), page_index++,
439 				fOrgJobData->GetOrientation(), &scale,
440 				fOrgJobData->GetScaledPhysicalRect(),
441 				fOrgJobData->GetScaledPrintableRect(),
442 				fOrgJobData->GetPhysicalRect()));
443 
444 			left_top.x -= x;
445 			left_top.y -= y;
446 
447 			BRect clip(fOrgJobData->GetScaledPrintableRect());
448 			clip.OffsetTo(left_top);
449 
450 			BRegion *region = new BRegion();
451 			region->Set(clip);
452 			fView->ConstrainClippingRegion(region);
453 			delete region;
454 
455 			if ((*it)->startEnum()) {
456 				bool more;
457 				do {
458 					PictureData	*picture_data;
459 					more = (*it)->enumObject(&picture_data);
460 					BPoint real_offset = left_top + picture_data->point;
461 					fView->DrawPicture(picture_data->picture, real_offset);
462 					fView->Sync();
463 					delete picture_data;
464 				} while (more);
465 			}
466 		}
467 
468 		if (!_PrintBand(fBitmap, &offset))
469 			return false;
470 
471 	} while (offset.x >= 0.0f && offset.y >= 0.0f);
472 
473 	return true;
474 }
475 
476 
477 bool
478 GraphicsDriver::_PrintBand(BBitmap* band, BPoint* offset)
479 {
480 	if (!_NeedRotateBitmapBand())
481 		return NextBand(band, offset);
482 
483 	_RotateInto(fRotatedBitmap, band);
484 
485 	BPoint rotatedOffset(offset->y, offset->x);
486 	bool success = NextBand(fRotatedBitmap, &rotatedOffset);
487 	offset->x = rotatedOffset.y;
488 	offset->y = rotatedOffset.x;
489 
490 	return success;
491 }
492 
493 
494 void
495 GraphicsDriver::_RotateInto(BBitmap* target, const BBitmap* source)
496 {
497 	ASSERT(target->ColorSpace() == B_RGB32);
498 	ASSERT(source->ColorSpace() == B_RGB32);
499 	ASSERT(target->Bounds().IntegerWidth() == source->Bounds().IntegerHeight());
500 	ASSERT(target->Bounds().IntegerHeight() == source->Bounds().IntegerWidth());
501 
502 	const int32 width = source->Bounds().IntegerWidth() + 1;
503 	const int32 height = source->Bounds().IntegerHeight() + 1;
504 
505 	const int32 sourceBPR = source->BytesPerRow();
506 	const int32 targetBPR = target->BytesPerRow();
507 
508 	const uint8_t* sourceBits =
509 		reinterpret_cast<const uint8_t*>(source->Bits());
510 	uint8_t* targetBits = static_cast<uint8_t*>(target->Bits());
511 
512 	for (int32 y = 0; y < height; y ++) {
513 		for (int32 x = 0; x < width; x ++) {
514 			const uint32_t* sourcePixel =
515 				reinterpret_cast<const uint32_t*>(sourceBits + sourceBPR * y
516 					+ sizeof(uint32_t) * x);
517 
518 			int32 targetX = (height - y - 1);
519 			int32 targetY = x;
520 			uint32_t* targetPixel =
521 				reinterpret_cast<uint32_t*>(targetBits + targetBPR * targetY
522 					+ sizeof(uint32_t) * targetX);
523 			*targetPixel = *sourcePixel;
524 		}
525 	}
526 }
527 
528 bool
529 GraphicsDriver::_CollectPages(SpoolData* spoolData, PageDataList* pages)
530 {
531 	// collect the pages to be printed on the physical page
532 	PageData *page_data;
533 	int nup = fOrgJobData->GetNup();
534 	bool more;
535 	do {
536 		more = spoolData->enumObject(&page_data);
537 		if (pages != NULL)
538 			pages->push_back(page_data);
539 	} while (more && --nup);
540 
541 	return more;
542 }
543 
544 
545 bool
546 GraphicsDriver::_SkipPages(SpoolData* spoolData)
547 {
548 	return _CollectPages(spoolData, NULL);
549 }
550 
551 
552 bool
553 GraphicsDriver::_PrintDocument(SpoolData* spoolData)
554 {
555 	bool more;
556 	bool success;
557 	int page_index;
558 	int copy;
559 	int copies;
560 
561 	more = true;
562 	success = true;
563 	page_index = 0;
564 
565 	if (fPrinterCap->Supports(PrinterCap::kCopyCommand))
566 		// let the printer perform the copy operation
567 		copies = 1;
568 	else
569 		// send the page multiple times to the printer
570 		copies = fRealJobData->GetCopies();
571 
572 	fStatusWindow -> SetPageCopies(copies);
573 		// inform fStatusWindow about number of copies
574 
575 	// printing of even/odd numbered pages only is valid in simplex mode
576 	bool simplex = fRealJobData->GetPrintStyle() == JobData::kSimplex;
577 
578 	if (spoolData->startEnum()) {
579 		do {
580 			DBGMSG(("page index = %d\n", page_index));
581 
582 			if (simplex
583 				&& fRealJobData->GetPageSelection()
584 					== JobData::kEvenNumberedPages)
585 				// skip odd numbered page
586 				more = _SkipPages(spoolData);
587 
588 			if (!more)
589 				// end reached
590 				break;
591 
592 			PageDataList pages;
593 			more = _CollectPages(spoolData, &pages);
594 
595 			if (more && simplex
596 				&& fRealJobData->GetPageSelection()
597 					== JobData::kOddNumberedPages)
598 				// skip even numbered page
599 				more = _SkipPages(spoolData);
600 
601 			// print each physical page "copies" of times
602 			for (copy = 0; success && copy < copies; copy ++) {
603 
604 				// Update the status / cancel job
605 				if (fStatusWindow->UpdateStatusBar(page_index, copy))
606 					return false;
607 
608 				success = StartPage(page_index);
609 				if (!success)
610 					break;
611 
612 				// print the pages on the physical page
613 				fView->Window()->Lock();
614 				success = _PrintPage(&pages);
615 				fView->Window()->Unlock();
616 
617 				if (success) {
618 					success = EndPage(page_index);
619 				}
620 			}
621 
622 			page_index++;
623 		} while (success && more);
624 	}
625 
626 #ifndef USE_PREVIEW_FOR_DEBUG
627 	if (success
628 		&& fPrinterCap->Supports(PrinterCap::kPrintStyle)
629 		&& (fOrgJobData->GetPrintStyle() != JobData::kSimplex)
630 		&& (((page_index + fOrgJobData->GetNup() - 1) / fOrgJobData->GetNup())
631 			% 2)) {
632 		// append an empty page on the back side of the page in duplex or
633 		// booklet mode
634 		success =
635 			StartPage(page_index) &&
636 			_PrintPage(NULL) &&
637 			EndPage(page_index);
638 	}
639 #endif
640 
641 	return success;
642 }
643 
644 
645 const JobData*
646 GraphicsDriver::GetJobData(BFile* spoolFile)
647 {
648 	_SetupData(spoolFile);
649 	return fOrgJobData;
650 }
651 
652 
653 bool
654 GraphicsDriver::_PrintJob(BFile* spoolFile)
655 {
656 	bool success = true;
657 	if (!_SetupData(spoolFile)) {
658 		// silently exit if there is nothing to print
659 		return true;
660 	}
661 
662 	fTransport = new Transport(fPrinterData);
663 
664 	if (fTransport->CheckAbort()) {
665 		success = false;
666 	} else if (!fTransport->IsPrintToFileCanceled()) {
667 		BStopWatch stopWatch("printJob", !MEASURE_PRINT_JOB_TIME);
668 		_SetupBitmap();
669 		SpoolData spoolData(spoolFile, fPageCount, fOrgJobData->GetNup(),
670 			fOrgJobData->GetReverse());
671 		success = StartDocument();
672 		if (success) {
673 			fStatusWindow = new StatusWindow(
674 				fRealJobData->GetPageSelection() == JobData::kOddNumberedPages,
675 				fRealJobData->GetPageSelection() == JobData::kEvenNumberedPages,
676 				fRealJobData->GetFirstPage(),
677 				fPageCount,
678 				fInternalCopies,fRealJobData->GetNup());
679 
680 			while (fInternalCopies--) {
681 				success = _PrintDocument(&spoolData);
682 				if (success == false) {
683 					break;
684 				}
685 			}
686 			EndDocument(success);
687 
688 			fStatusWindow->Lock();
689 			fStatusWindow->Quit();
690 		}
691 		_CleanupBitmap();
692 		_CleanupData();
693 	}
694 
695 	if (success == false) {
696 		BAlert *alert;
697 		if (fTransport->CheckAbort())
698 			alert = new BAlert("", fTransport->LastError().c_str(), "OK");
699 		else
700 			alert = new BAlert("", "Printer not responding.", "OK");
701 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
702 		alert->Go();
703 	}
704 
705 	delete fTransport;
706 	fTransport = NULL;
707 
708 	return success;
709 }
710 
711 
712 BMessage*
713 GraphicsDriver::TakeJob(BFile* spoolFile)
714 {
715 	BMessage *msg;
716 	if (_PrintJob(spoolFile))
717 		msg = new BMessage('okok');
718 	else
719 		msg = new BMessage('baad');
720 	return msg;
721 }
722 
723 
724 bool
725 GraphicsDriver::StartDocument()
726 {
727 	return true;
728 }
729 
730 
731 bool
732 GraphicsDriver::StartPage(int)
733 {
734 	return true;
735 }
736 
737 
738 bool
739 GraphicsDriver::NextBand(BBitmap*, BPoint*)
740 {
741 	return true;
742 }
743 
744 
745 bool
746 GraphicsDriver::EndPage(int)
747 {
748 	return true;
749 }
750 
751 
752 bool
753 GraphicsDriver::EndDocument(bool)
754 {
755 	return true;
756 }
757 
758 
759 void
760 GraphicsDriver::WriteSpoolData(const void* buffer, size_t size)
761 {
762 	if (fTransport == NULL)
763 		return;
764 	fTransport->Write(buffer, size);
765 }
766 
767 
768 void
769 GraphicsDriver::WriteSpoolString(const char* format, ...)
770 {
771 	if (fTransport == NULL)
772 		return;
773 
774 	char buffer[256];
775 	va_list	ap;
776 	va_start(ap, format);
777 	vsprintf(buffer, format, ap);
778 	fTransport->Write(buffer, strlen(buffer));
779 	va_end(ap);
780 }
781 
782 
783 void
784 GraphicsDriver::WriteSpoolChar(char c)
785 {
786 	if (fTransport == NULL)
787 		return;
788 
789 	fTransport->Write(&c, 1);
790 }
791 
792 
793 void
794 GraphicsDriver::ReadSpoolData(void* buffer, size_t size)
795 {
796 	if (fTransport == NULL)
797 		return;
798 	fTransport->Read(buffer, size);
799 }
800 
801 
802 int
803 GraphicsDriver::ReadSpoolChar()
804 {
805 	if (fTransport == NULL)
806 		return -1;
807 
808 	char c;
809 	fTransport->Read(&c, 1);
810 	return c;
811 }
812 
813 
814 bool
815 GraphicsDriver::_NeedRotateBitmapBand() const
816 {
817 	return JobData::kLandscape == fRealJobData->GetOrientation()
818 		&& !fPrinterCap->Supports(PrinterCap::kCanRotatePageInLandscape);
819 }
820 
821 
822 void
823 GraphicsDriver::_ConvertRGB32ToRGB24(const void* src, void* dst, int width) {
824 	uint8* d = (uint8*)dst;
825 	const rgb_color* s = static_cast<const rgb_color*>(src);
826 	for (int i = width; i > 0; i --) {
827 		*d ++ = s->red;
828 		*d ++ = s->green;
829 		*d ++ = s->blue;
830 		s++;
831 	}
832 }
833 
834 
835 void
836 GraphicsDriver::_ConvertCMAP8ToRGB24(const void* src, void* dst, int width) {
837 	uint8* d = (uint8*)dst;
838 	const uint8* s = static_cast<const uint8*>(src);
839 	const color_map* cmap = system_colors();
840 	for (int i = width; i > 0; i --) {
841 		const rgb_color* rgb = &cmap->color_list[*s];
842 		*d ++ = rgb->red;
843 		*d ++ = rgb->green;
844 		*d ++ = rgb->blue;
845 		s ++;
846 	}
847 }
848 
849 
850 void
851 GraphicsDriver::ConvertToRGB24(const void* src, void* dst, int width,
852 	color_space cs) {
853 	if (cs == B_RGB32)
854 		_ConvertRGB32ToRGB24(src, dst, width);
855 	else if (cs == B_CMAP8)
856 		_ConvertCMAP8ToRGB24(src, dst, width);
857 	else {
858 		DBGMSG(("color_space %d not supported", cs));
859 	}
860 }
861 
862 
863 uint8
864 GraphicsDriver::_ConvertToGray(uint8 r, uint8 g, uint8 b) {
865 	if (r == g && g == b)
866 		return r;
867 	else
868 		return (r * 3 + g * 6 + b) / 10;
869 }
870 
871 
872 void
873 GraphicsDriver::_ConvertRGB32ToGray(const void* src, void* dst, int width) {
874 	uint8* d = (uint8*)dst;
875 	const rgb_color* s = static_cast<const rgb_color*>(src);
876 	for (int i = width; i > 0; i--, s++, d++)
877 		*d = _ConvertToGray(s->red, s->green, s->blue);
878 }
879 
880 
881 void
882 GraphicsDriver::_ConvertCMAP8ToGray(const void* src, void* dst, int width) {
883 	uint8* d = (uint8*)dst;
884 	const uint8* s = static_cast<const uint8*>(src);
885 	const color_map* cmap = system_colors();
886 	for (int i = width; i > 0; i--, s++, d++) {
887 		const rgb_color* rgb = &cmap->color_list[*s];
888 		*d = _ConvertToGray(rgb->red, rgb->green, rgb->blue);
889 	}
890 }
891 
892 
893 void
894 GraphicsDriver::ConvertToGray(const void* src, void* dst, int width,
895 	color_space cs) {
896 	if (cs == B_RGB32)
897 		_ConvertRGB32ToGray(src, dst, width);
898 	else if (cs == B_CMAP8)
899 		_ConvertCMAP8ToGray(src, dst, width);
900 	else {
901 		DBGMSG(("color_space %d not supported", cs));
902 	}
903 }
904 
905