3 * Copyright 1999-2000 Y.Takagi. All Rights Reserved.
16 #include <TextControl.h>
17 #include <TextControl.h>
18 #include <StopWatch.h>
20 #include <Directory.h>
23 #include "GraphicsDriver.h"
24 #include "PrintProcess.h"
26 #include "PrinterData.h"
27 #include "PrinterCap.h"
29 #include "Transport.h"
30 #include "ValidRect.h"
37 // Measure printJob() time. Either true or false.
38 #define MEASURE_PRINT_JOB_TIME false
42 kMaxMemorySize
= 4 * 1024 * 1024
46 GraphicsDriver::GraphicsDriver(BMessage
* message
, PrinterData
* printerData
,
47 const PrinterCap
* printerCap
)
56 fPrinterData(printerData
),
57 fPrinterCap(printerCap
),
72 GraphicsDriver::~GraphicsDriver()
78 RotateRect(BRect rect
)
80 BRect
rotated(rect
.top
, rect
.left
, rect
.bottom
, rect
.right
);
86 GraphicsDriver::_SetupData(BFile
* spoolFile
)
88 if (fOrgJobData
!= NULL
) {
89 // already initialized
93 print_file_header pfh
;
94 spoolFile
->Seek(0, SEEK_SET
);
95 spoolFile
->Read(&pfh
, sizeof(pfh
));
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
));
101 if (pfh
.page_count
<= 0) {
106 fPageCount
= (uint32
) pfh
.page_count
;
107 BMessage
*msg
= new BMessage();
108 msg
->Unflatten(spoolFile
);
109 fOrgJobData
= new JobData(msg
, fPrinterCap
, JobData::kJobSettings
);
113 fRealJobData
= new JobData(*fOrgJobData
);
115 switch (fOrgJobData
->GetNup()) {
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()));
129 if (JobData::kPortrait
== fOrgJobData
->GetOrientation())
130 fRealJobData
->SetOrientation(JobData::kLandscape
);
132 fRealJobData
->SetOrientation(JobData::kPortrait
);
136 if (fOrgJobData
->GetCollate() && fPageCount
> 1) {
137 fRealJobData
->SetCopies(1);
138 fInternalCopies
= fOrgJobData
->GetCopies();
143 fSpoolMetaData
= new SpoolMetaData(spoolFile
);
149 GraphicsDriver::_CleanupData()
153 delete fSpoolMetaData
;
156 fSpoolMetaData
= NULL
;
161 GraphicsDriver::_SetupBitmap()
163 fPixelDepth
= color_space2pixel_depth(fOrgJobData
->GetSurfaceType());
165 fPageWidth
= (fRealJobData
->GetPhysicalRect().IntegerWidth()
166 * fOrgJobData
->GetXres() + 71) / 72;
167 fPageHeight
= (fRealJobData
->GetPhysicalRect().IntegerHeight()
168 * fOrgJobData
->GetYres() + 71) / 72;
170 int widthByte
= (fPageWidth
* fPixelDepth
+ 7) / 8;
171 int size
= widthByte
* fPageHeight
;
172 #ifdef USE_PREVIEW_FOR_DEBUG
176 if (size
< kMaxMemorySize
) {
178 fBandWidth
= fPageWidth
;
179 fBandHeight
= fPageHeight
;
181 fBandCount
= (size
+ kMaxMemorySize
- 1) / kMaxMemorySize
;
182 if (_NeedRotateBitmapBand()) {
183 fBandWidth
= (fPageWidth
+ fBandCount
- 1) / fBandCount
;
184 fBandHeight
= fPageHeight
;
186 fBandWidth
= fPageWidth
;
187 fBandHeight
= (fPageHeight
+ fBandCount
- 1) / fBandCount
;
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"));
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
);
204 if (_NeedRotateBitmapBand()) {
205 BRect
rotatedRect(0, 0, rect
.bottom
, rect
.right
);
206 fRotatedBitmap
= new BBitmap(rotatedRect
, fOrgJobData
->GetSurfaceType(),
213 GraphicsDriver::_CleanupBitmap()
219 delete fRotatedBitmap
;
220 fRotatedBitmap
= NULL
;
225 GraphicsDriver::GetScale(int32 nup
, BRect physicalRect
, float scaling
)
231 scale
.x
= scale
.y
= 1.0f
;
235 scale
.x
= scale
.y
= 1.0f
;
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
;
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
;
255 scale
.x
= height
/ width
/ 2.0f
;
256 scale
.y
= width
/ 4.0f
/ height
;
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
;
266 scale
.x
= height
/ width
/ 4.0f
;
267 scale
.y
= width
/ 8.0f
/ height
;
271 scale
.x
= scale
.y
= 1.0f
/ 2.0f
;
274 scale
.x
= scale
.y
= 1.0f
/ 3.0f
;
277 scale
.x
= scale
.y
= 1.0f
/ 4.0f
;
280 scale
.x
= scale
.y
= 1.0f
/ 5.0f
;
283 scale
.x
= scale
.y
= 1.0f
/ 6.0f
;
286 scale
.x
= scale
.y
= 1.0f
/ 7.0f
;
289 scale
.x
= scale
.y
= 1.0f
/ 8.0f
;
292 scale
.x
= scale
.y
= 1.0f
/ 9.0f
;
294 case 100: /* 10x10 */
295 scale
.x
= scale
.y
= 1.0f
/ 10.0f
;
297 case 121: /* 11x11 */
298 scale
.x
= scale
.y
= 1.0f
/ 11.0f
;
302 scale
.x
= scale
.x
* scaling
/ 100.0;
303 scale
.y
= scale
.y
* scaling
/ 100.0;
310 GraphicsDriver::GetOffset(int32 nup
, int index
,
311 JobData::Orientation orientation
, const BPoint
* scale
,
312 BRect scaledPhysicalRect
, BRect scaledPrintableRect
,
319 float width
= scaledPhysicalRect
.Width();
320 float height
= scaledPhysicalRect
.Height();
327 if (JobData::kPortrait
== orientation
) {
335 if (JobData::kPortrait
== orientation
) {
336 offset
.x
= width
* (index
/ 2);
337 offset
.y
= height
* (index
% 2);
339 offset
.x
= width
* (index
% 2);
340 offset
.y
= height
* (index
/ 2);
344 if (JobData::kPortrait
== orientation
) {
345 offset
.x
= width
* (index
/ 4);
346 offset
.y
= height
* (index
% 4);
348 offset
.x
= width
* (index
% 4);
349 offset
.y
= height
* (index
/ 4);
353 offset
.x
= width
* (index
/ 2);
354 offset
.y
= height
* (index
% 2);
357 offset
.x
= width
* (index
/ 3);
358 offset
.y
= height
* (index
% 3);
361 offset
.x
= width
* (index
/ 4);
362 offset
.y
= height
* (index
% 4);
365 offset
.x
= width
* (index
/ 5);
366 offset
.y
= height
* (index
% 5);
369 offset
.x
= width
* (index
/ 6);
370 offset
.y
= height
* (index
% 6);
373 offset
.x
= width
* (index
/ 7);
374 offset
.y
= height
* (index
% 7);
377 offset
.x
= width
* (index
/ 8);
378 offset
.y
= height
* (index
% 8);
381 offset
.x
= width
* (index
/ 9);
382 offset
.y
= height
* (index
% 9);
385 offset
.x
= width
* (index
/ 10);
386 offset
.y
= height
* (index
% 10);
389 offset
.x
= width
* (index
/ 11);
390 offset
.y
= height
* (index
% 11);
395 offset
.x
+= scaledPrintableRect
.left
- physicalRect
.left
;
396 offset
.y
+= scaledPrintableRect
.top
- physicalRect
.top
;
398 float real_scale
= min(scale
->x
, scale
->y
);
399 if (real_scale
!= scale
->x
)
400 offset
.x
*= scale
->x
/ real_scale
;
402 offset
.y
*= scale
->y
/ real_scale
;
408 // print the specified pages on a physical page
410 GraphicsDriver::_PrintPage(PageDataList
* pages
)
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());
427 BPoint scale
= GetScale(fOrgJobData
->GetNup(),
428 fOrgJobData
->GetPhysicalRect(), fOrgJobData
->GetScaling());
429 float real_scale
= min(scale
.x
, scale
.y
) * fOrgJobData
->GetXres()
431 fView
->SetScale(real_scale
);
432 float x
= offset
.x
/ real_scale
;
433 float y
= offset
.y
/ real_scale
;
436 for (PageDataList::iterator it
= pages
->begin(); it
!= pages
->end();
438 BPoint
left_top(GetOffset(fOrgJobData
->GetNup(), page_index
++,
439 fOrgJobData
->GetOrientation(), &scale
,
440 fOrgJobData
->GetScaledPhysicalRect(),
441 fOrgJobData
->GetScaledPrintableRect(),
442 fOrgJobData
->GetPhysicalRect()));
447 BRect
clip(fOrgJobData
->GetScaledPrintableRect());
448 clip
.OffsetTo(left_top
);
450 BRegion
*region
= new BRegion();
452 fView
->ConstrainClippingRegion(region
);
455 if ((*it
)->startEnum()) {
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
);
468 if (!_PrintBand(fBitmap
, &offset
))
471 } while (offset
.x
>= 0.0f
&& offset
.y
>= 0.0f
);
478 GraphicsDriver::_PrintBand(BBitmap
* band
, BPoint
* offset
)
480 if (!_NeedRotateBitmapBand())
481 return NextBand(band
, offset
);
483 _RotateInto(fRotatedBitmap
, band
);
485 BPoint
rotatedOffset(offset
->y
, offset
->x
);
486 bool success
= NextBand(fRotatedBitmap
, &rotatedOffset
);
487 offset
->x
= rotatedOffset
.y
;
488 offset
->y
= rotatedOffset
.x
;
495 GraphicsDriver::_RotateInto(BBitmap
* target
, const BBitmap
* source
)
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());
502 const int32 width
= source
->Bounds().IntegerWidth() + 1;
503 const int32 height
= source
->Bounds().IntegerHeight() + 1;
505 const int32 sourceBPR
= source
->BytesPerRow();
506 const int32 targetBPR
= target
->BytesPerRow();
508 const uint8_t* sourceBits
=
509 reinterpret_cast<const uint8_t*>(source
->Bits());
510 uint8_t* targetBits
= static_cast<uint8_t*>(target
->Bits());
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
);
518 int32 targetX
= (height
- y
- 1);
520 uint32_t* targetPixel
=
521 reinterpret_cast<uint32_t*>(targetBits
+ targetBPR
* targetY
522 + sizeof(uint32_t) * targetX
);
523 *targetPixel
= *sourcePixel
;
529 GraphicsDriver::_CollectPages(SpoolData
* spoolData
, PageDataList
* pages
)
531 // collect the pages to be printed on the physical page
533 int nup
= fOrgJobData
->GetNup();
536 more
= spoolData
->enumObject(&page_data
);
538 pages
->push_back(page_data
);
539 } while (more
&& --nup
);
546 GraphicsDriver::_SkipPages(SpoolData
* spoolData
)
548 return _CollectPages(spoolData
, NULL
);
553 GraphicsDriver::_PrintDocument(SpoolData
* spoolData
)
565 if (fPrinterCap
->Supports(PrinterCap::kCopyCommand
))
566 // let the printer perform the copy operation
569 // send the page multiple times to the printer
570 copies
= fRealJobData
->GetCopies();
572 fStatusWindow
-> SetPageCopies(copies
);
573 // inform fStatusWindow about number of copies
575 // printing of even/odd numbered pages only is valid in simplex mode
576 bool simplex
= fRealJobData
->GetPrintStyle() == JobData::kSimplex
;
578 if (spoolData
->startEnum()) {
580 DBGMSG(("page index = %d\n", page_index
));
583 && fRealJobData
->GetPageSelection()
584 == JobData::kEvenNumberedPages
)
585 // skip odd numbered page
586 more
= _SkipPages(spoolData
);
593 more
= _CollectPages(spoolData
, &pages
);
596 && fRealJobData
->GetPageSelection()
597 == JobData::kOddNumberedPages
)
598 // skip even numbered page
599 more
= _SkipPages(spoolData
);
601 // print each physical page "copies" of times
602 for (copy
= 0; success
&& copy
< copies
; copy
++) {
604 // Update the status / cancel job
605 if (fStatusWindow
->UpdateStatusBar(page_index
, copy
))
608 success
= StartPage(page_index
);
612 // print the pages on the physical page
613 fView
->Window()->Lock();
614 success
= _PrintPage(&pages
);
615 fView
->Window()->Unlock();
618 success
= EndPage(page_index
);
623 } while (success
&& more
);
626 #ifndef USE_PREVIEW_FOR_DEBUG
628 && fPrinterCap
->Supports(PrinterCap::kPrintStyle
)
629 && (fOrgJobData
->GetPrintStyle() != JobData::kSimplex
)
630 && (((page_index
+ fOrgJobData
->GetNup() - 1) / fOrgJobData
->GetNup())
632 // append an empty page on the back side of the page in duplex or
635 StartPage(page_index
) &&
646 GraphicsDriver::GetJobData(BFile
* spoolFile
)
648 _SetupData(spoolFile
);
654 GraphicsDriver::_PrintJob(BFile
* spoolFile
)
657 if (!_SetupData(spoolFile
)) {
658 // silently exit if there is nothing to print
662 fTransport
= new Transport(fPrinterData
);
664 if (fTransport
->CheckAbort()) {
666 } else if (!fTransport
->IsPrintToFileCanceled()) {
667 BStopWatch
stopWatch("printJob", !MEASURE_PRINT_JOB_TIME
);
669 SpoolData
spoolData(spoolFile
, fPageCount
, fOrgJobData
->GetNup(),
670 fOrgJobData
->GetReverse());
671 success
= StartDocument();
673 fStatusWindow
= new StatusWindow(
674 fRealJobData
->GetPageSelection() == JobData::kOddNumberedPages
,
675 fRealJobData
->GetPageSelection() == JobData::kEvenNumberedPages
,
676 fRealJobData
->GetFirstPage(),
678 fInternalCopies
,fRealJobData
->GetNup());
680 while (fInternalCopies
--) {
681 success
= _PrintDocument(&spoolData
);
682 if (success
== false) {
686 EndDocument(success
);
688 fStatusWindow
->Lock();
689 fStatusWindow
->Quit();
695 if (success
== false) {
697 if (fTransport
->CheckAbort())
698 alert
= new BAlert("", fTransport
->LastError().c_str(), "OK");
700 alert
= new BAlert("", "Printer not responding.", "OK");
701 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
713 GraphicsDriver::TakeJob(BFile
* spoolFile
)
716 if (_PrintJob(spoolFile
))
717 msg
= new BMessage('okok');
719 msg
= new BMessage('baad');
725 GraphicsDriver::StartDocument()
732 GraphicsDriver::StartPage(int)
739 GraphicsDriver::NextBand(BBitmap
*, BPoint
*)
746 GraphicsDriver::EndPage(int)
753 GraphicsDriver::EndDocument(bool)
760 GraphicsDriver::WriteSpoolData(const void* buffer
, size_t size
)
761 throw (TransportException
)
763 if (fTransport
== NULL
)
765 fTransport
->Write(buffer
, size
);
770 GraphicsDriver::WriteSpoolString(const char* format
, ...)
771 throw (TransportException
)
773 if (fTransport
== NULL
)
778 va_start(ap
, format
);
779 vsprintf(buffer
, format
, ap
);
780 fTransport
->Write(buffer
, strlen(buffer
));
786 GraphicsDriver::WriteSpoolChar(char c
)
787 throw (TransportException
)
789 if (fTransport
== NULL
)
792 fTransport
->Write(&c
, 1);
797 GraphicsDriver::_NeedRotateBitmapBand() const
799 return JobData::kLandscape
== fRealJobData
->GetOrientation()
800 && !fPrinterCap
->Supports(PrinterCap::kCanRotatePageInLandscape
);
805 GraphicsDriver::_ConvertRGB32ToRGB24(const void* src
, void* dst
, int width
) {
806 uint8
* d
= (uint8
*)dst
;
807 const rgb_color
* s
= static_cast<const rgb_color
*>(src
);
808 for (int i
= width
; i
> 0; i
--) {
818 GraphicsDriver::_ConvertCMAP8ToRGB24(const void* src
, void* dst
, int width
) {
819 uint8
* d
= (uint8
*)dst
;
820 const uint8
* s
= static_cast<const uint8
*>(src
);
821 const color_map
* cmap
= system_colors();
822 for (int i
= width
; i
> 0; i
--) {
823 const rgb_color
* rgb
= &cmap
->color_list
[*s
];
833 GraphicsDriver::ConvertToRGB24(const void* src
, void* dst
, int width
,
836 _ConvertRGB32ToRGB24(src
, dst
, width
);
837 else if (cs
== B_CMAP8
)
838 _ConvertCMAP8ToRGB24(src
, dst
, width
);
840 DBGMSG(("color_space %d not supported", cs
));
846 GraphicsDriver::_ConvertToGray(uint8 r
, uint8 g
, uint8 b
) {
847 if (r
== g
&& g
== b
)
850 return (r
* 3 + g
* 6 + b
) / 10;
855 GraphicsDriver::_ConvertRGB32ToGray(const void* src
, void* dst
, int width
) {
856 uint8
* d
= (uint8
*)dst
;
857 const rgb_color
* s
= static_cast<const rgb_color
*>(src
);
858 for (int i
= width
; i
> 0; i
--, s
++, d
++)
859 *d
= _ConvertToGray(s
->red
, s
->green
, s
->blue
);
864 GraphicsDriver::_ConvertCMAP8ToGray(const void* src
, void* dst
, int width
) {
865 uint8
* d
= (uint8
*)dst
;
866 const uint8
* s
= static_cast<const uint8
*>(src
);
867 const color_map
* cmap
= system_colors();
868 for (int i
= width
; i
> 0; i
--, s
++, d
++) {
869 const rgb_color
* rgb
= &cmap
->color_list
[*s
];
870 *d
= _ConvertToGray(rgb
->red
, rgb
->green
, rgb
->blue
);
876 GraphicsDriver::ConvertToGray(const void* src
, void* dst
, int width
,
879 _ConvertRGB32ToGray(src
, dst
, width
);
880 else if (cs
== B_CMAP8
)
881 _ConvertCMAP8ToGray(src
, dst
, width
);
883 DBGMSG(("color_space %d not supported", cs
));