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 <Message.h> 13 #include <PrintJob.h> 14 #include <Region.h> 15 #include <TextControl.h> 16 #include <TextControl.h> 17 #include <StopWatch.h> 18 #include <View.h> 19 #include <Directory.h> 20 #include <File.h> 21 22 #include "GraphicsDriver.h" 23 #include "PrintProcess.h" 24 #include "JobData.h" 25 #include "PrinterData.h" 26 #include "PrinterCap.h" 27 #include "Preview.h" 28 #include "Transport.h" 29 #include "ValidRect.h" 30 #include "DbgMsg.h" 31 32 33 using namespace std; 34 35 36 // Measure printJob() time. Either true or false. 37 #define MEASURE_PRINT_JOB_TIME false 38 39 enum { 40 kMaxMemorySize = (4 *1024 *1024) 41 }; 42 43 GraphicsDriver::GraphicsDriver(BMessage *msg, PrinterData *printer_data, const PrinterCap *printer_cap) 44 : fMsg(msg) 45 , fPrinterData(printer_data) 46 , fPrinterCap(printer_cap) 47 { 48 fView = NULL; 49 fBitmap = NULL; 50 fTransport = NULL; 51 fOrgJobData = NULL; 52 fRealJobData = NULL; 53 fSpoolMetaData = NULL; 54 } 55 56 GraphicsDriver::~GraphicsDriver() 57 { 58 } 59 60 61 static BRect RotateRect(BRect rect) 62 { 63 BRect rotated(rect.top, rect.left, rect.bottom, rect.right); 64 return rotated; 65 } 66 67 bool 68 GraphicsDriver::setupData(BFile *spoolFile) 69 { 70 if (fOrgJobData != NULL) { 71 // already initialized 72 return true; 73 } 74 75 #ifndef B_BEOS_VERSION_DANO 76 print_file_header pfh; 77 #else 78 BPrintJob::print_file_header pfh; 79 #endif 80 spoolFile->Seek(0, SEEK_SET); 81 spoolFile->Read(&pfh, sizeof(pfh)); 82 83 DBGMSG(("print_file_header::version = 0x%x\n", pfh.version)); 84 DBGMSG(("print_file_header::page_count = %d\n", pfh.page_count)); 85 DBGMSG(("print_file_header::first_page = 0x%x\n", (int)pfh.first_page)); 86 87 if (pfh.page_count <= 0) { 88 // nothing to print 89 return false; 90 } 91 92 fPageCount = (uint32) pfh.page_count; 93 BMessage *msg = new BMessage(); 94 msg->Unflatten(spoolFile); 95 fOrgJobData = new JobData(msg, fPrinterCap, JobData::kJobSettings); 96 DUMP_BMESSAGE(msg); 97 delete msg; 98 99 fRealJobData = new JobData(*fOrgJobData); 100 101 switch (fOrgJobData->getNup()) { 102 case 2: 103 case 8: 104 case 32: 105 case 128: 106 fRealJobData->setPrintableRect(RotateRect(fOrgJobData->getPrintableRect())); 107 fRealJobData->setScaledPrintableRect(RotateRect(fOrgJobData->getScaledPrintableRect())); 108 fRealJobData->setPhysicalRect(RotateRect(fOrgJobData->getPhysicalRect())); 109 fRealJobData->setScaledPhysicalRect(RotateRect(fOrgJobData->getScaledPhysicalRect())); 110 if (JobData::kPortrait == fOrgJobData->getOrientation()) 111 fRealJobData->setOrientation(JobData::kLandscape); 112 else 113 fRealJobData->setOrientation(JobData::kPortrait); 114 break; 115 } 116 117 if (fOrgJobData->getCollate() && fPageCount > 1) { 118 fRealJobData->setCopies(1); 119 fInternalCopies = fOrgJobData->getCopies(); 120 } else { 121 fInternalCopies = 1; 122 } 123 124 fSpoolMetaData = new SpoolMetaData(spoolFile); 125 return true; 126 } 127 128 void 129 GraphicsDriver::cleanupData() 130 { 131 delete fRealJobData; 132 delete fOrgJobData; 133 delete fSpoolMetaData; 134 fRealJobData = NULL; 135 fOrgJobData = NULL; 136 fSpoolMetaData = NULL; 137 } 138 139 void 140 GraphicsDriver::setupBitmap() 141 { 142 fPixelDepth = color_space2pixel_depth(fOrgJobData->getSurfaceType()); 143 144 fPageWidth = (fRealJobData->getPhysicalRect().IntegerWidth() * fOrgJobData->getXres() + 71) / 72; 145 fPageHeight = (fRealJobData->getPhysicalRect().IntegerHeight() * fOrgJobData->getYres() + 71) / 72; 146 147 int widthByte = (fPageWidth * fPixelDepth + 7) / 8; 148 int size = widthByte * fPageHeight; 149 #ifdef USE_PREVIEW_FOR_DEBUG 150 size = 0; 151 #endif 152 153 if (size < kMaxMemorySize) { 154 fBandCount = 0; 155 fBandWidth = fPageWidth; 156 fBandHeight = fPageHeight; 157 } else { 158 fBandCount = (size + kMaxMemorySize - 1) / kMaxMemorySize; 159 if ((JobData::kLandscape == fRealJobData->getOrientation()) && (fFlags & kGDFRotateBandBitmap)) { 160 fBandWidth = (fPageWidth + fBandCount - 1) / fBandCount; 161 fBandHeight = fPageHeight; 162 } else { 163 fBandWidth = fPageWidth; 164 fBandHeight = (fPageHeight + fBandCount - 1) / fBandCount; 165 } 166 } 167 168 DBGMSG(("****************\n")); 169 DBGMSG(("page_width = %d\n", fPageWidth)); 170 DBGMSG(("page_height = %d\n", fPageHeight)); 171 DBGMSG(("band_count = %d\n", fBandCount)); 172 DBGMSG(("band_height = %d\n", fBandHeight)); 173 DBGMSG(("****************\n")); 174 175 BRect rect; 176 rect.Set(0, 0, fBandWidth - 1, fBandHeight - 1); 177 fBitmap = new BBitmap(rect, fOrgJobData->getSurfaceType(), true); 178 fView = new BView(rect, "", B_FOLLOW_ALL, B_WILL_DRAW); 179 fBitmap->AddChild(fView); 180 } 181 182 void 183 GraphicsDriver::cleanupBitmap() 184 { 185 delete fBitmap; 186 fBitmap = NULL; 187 fView = NULL; 188 } 189 190 BPoint 191 GraphicsDriver::getScale(int32 nup, BRect physicalRect, float scaling) 192 { 193 float width; 194 float height; 195 BPoint scale; 196 197 scale.x = scale.y = 1.0f; 198 199 switch (nup) { 200 case 1: 201 scale.x = scale.y = 1.0f; 202 break; 203 case 2: /* 1x2 or 2x1 */ 204 width = physicalRect.Width(); 205 height = physicalRect.Height(); 206 if (width < height) { // portrait 207 scale.x = height / 2.0f / width; 208 scale.y = width / height; 209 } else { // landscape 210 scale.x = height / width; 211 scale.y = width / 2.0f / height; 212 } 213 break; 214 case 8: /* 2x4 or 4x2 */ 215 width = physicalRect.Width(); 216 height = physicalRect.Height(); 217 if (width < height) { 218 scale.x = height / 4.0f / width; 219 scale.y = width / height / 2.0f; 220 } else { 221 scale.x = height / width / 2.0f; 222 scale.y = width / 4.0f / height; 223 } 224 break; 225 case 32: /* 4x8 or 8x4 */ 226 width = physicalRect.Width(); 227 height = physicalRect.Height(); 228 if (width < height) { 229 scale.x = height / 8.0f / width; 230 scale.y = width / height / 4.0f; 231 } else { 232 scale.x = height / width / 4.0f; 233 scale.y = width / 8.0f / height; 234 } 235 break; 236 case 4: /* 2x2 */ 237 scale.x = scale.y = 1.0f / 2.0f; 238 break; 239 case 9: /* 3x3 */ 240 scale.x = scale.y = 1.0f / 3.0f; 241 break; 242 case 16: /* 4x4 */ 243 scale.x = scale.y = 1.0f / 4.0f; 244 break; 245 case 25: /* 5x5 */ 246 scale.x = scale.y = 1.0f / 5.0f; 247 break; 248 case 36: /* 6x6 */ 249 scale.x = scale.y = 1.0f / 6.0f; 250 break; 251 case 49: /* 7x7 */ 252 scale.x = scale.y = 1.0f / 7.0f; 253 break; 254 case 64: /* 8x8 */ 255 scale.x = scale.y = 1.0f / 8.0f; 256 break; 257 case 81: /* 9x9 */ 258 scale.x = scale.y = 1.0f / 9.0f; 259 break; 260 case 100: /* 10x10 */ 261 scale.x = scale.y = 1.0f / 10.0f; 262 break; 263 case 121: /* 11x11 */ 264 scale.x = scale.y = 1.0f / 11.0f; 265 break; 266 } 267 268 scale.x = scale.x * scaling / 100.0; 269 scale.y = scale.y * scaling / 100.0; 270 271 return scale; 272 } 273 274 BPoint 275 GraphicsDriver::getOffset(int32 nup, int index, JobData::Orientation orientation, 276 const BPoint *scale, BRect scaledPhysicalRect, BRect scaledPrintableRect, 277 BRect physicalRect) 278 { 279 BPoint offset; 280 offset.x = 0; 281 offset.y = 0; 282 283 float width = scaledPhysicalRect.Width(); 284 float height = scaledPhysicalRect.Height(); 285 286 switch (nup) { 287 case 1: 288 break; 289 case 2: 290 if (index == 1) { 291 if (JobData::kPortrait == orientation) { 292 offset.x = width; 293 } else { 294 offset.y = height; 295 } 296 } 297 break; 298 case 8: 299 if (JobData::kPortrait == orientation) { 300 offset.x = width * (index / 2); 301 offset.y = height * (index % 2); 302 } else { 303 offset.x = width * (index % 2); 304 offset.y = height * (index / 2); 305 } 306 break; 307 case 32: 308 if (JobData::kPortrait == orientation) { 309 offset.x = width * (index / 4); 310 offset.y = height * (index % 4); 311 } else { 312 offset.x = width * (index % 4); 313 offset.y = height * (index / 4); 314 } 315 break; 316 case 4: 317 offset.x = width * (index / 2); 318 offset.y = height * (index % 2); 319 break; 320 case 9: 321 offset.x = width * (index / 3); 322 offset.y = height * (index % 3); 323 break; 324 case 16: 325 offset.x = width * (index / 4); 326 offset.y = height * (index % 4); 327 break; 328 case 25: 329 offset.x = width * (index / 5); 330 offset.y = height * (index % 5); 331 break; 332 case 36: 333 offset.x = width * (index / 6); 334 offset.y = height * (index % 6); 335 break; 336 case 49: 337 offset.x = width * (index / 7); 338 offset.y = height * (index % 7); 339 break; 340 case 64: 341 offset.x = width * (index / 8); 342 offset.y = height * (index % 8); 343 break; 344 case 81: 345 offset.x = width * (index / 9); 346 offset.y = height * (index % 9); 347 break; 348 case 100: 349 offset.x = width * (index / 10); 350 offset.y = height * (index % 10); 351 break; 352 case 121: 353 offset.x = width * (index / 11); 354 offset.y = height * (index % 11); 355 break; 356 } 357 358 // adjust margin 359 offset.x += scaledPrintableRect.left - physicalRect.left; 360 offset.y += scaledPrintableRect.top - physicalRect.top; 361 362 float real_scale = min(scale->x, scale->y); 363 if (real_scale != scale->x) 364 offset.x *= scale->x / real_scale; 365 else 366 offset.y *= scale->y / real_scale; 367 368 return offset; 369 } 370 371 // print the specified pages on a physical page 372 bool 373 GraphicsDriver::printPage(PageDataList *pages) 374 { 375 BPoint offset; 376 offset.x = 0.0f; 377 offset.y = 0.0f; 378 379 if (pages == NULL) { 380 return true; 381 } 382 383 do { 384 // clear the physical page 385 fView->SetScale(1.0); 386 fView->SetHighColor(255, 255, 255); 387 fView->ConstrainClippingRegion(NULL); 388 fView->FillRect(fView->Bounds()); 389 390 BPoint scale = getScale(fOrgJobData->getNup(), fOrgJobData->getPhysicalRect(), fOrgJobData->getScaling()); 391 float real_scale = min(scale.x, scale.y) * fOrgJobData->getXres() / 72.0f; 392 fView->SetScale(real_scale); 393 float x = offset.x / real_scale; 394 float y = offset.y / real_scale; 395 int page_index = 0; 396 397 for (PageDataList::iterator it = pages->begin(); it != pages->end(); it++) { 398 BPoint left_top(getOffset(fOrgJobData->getNup(), page_index++, fOrgJobData->getOrientation(), &scale, fOrgJobData->getScaledPhysicalRect(), fOrgJobData->getScaledPrintableRect(), fOrgJobData->getPhysicalRect())); 399 left_top.x -= x; 400 left_top.y -= y; 401 BRect clip(fOrgJobData->getScaledPrintableRect()); 402 clip.OffsetTo(left_top); 403 BRegion *region = new BRegion(); 404 region->Set(clip); 405 fView->ConstrainClippingRegion(region); 406 delete region; 407 if ((*it)->startEnum()) { 408 bool more; 409 do { 410 PictureData *picture_data; 411 more = (*it)->enumObject(&picture_data); 412 BPoint real_offset = left_top + picture_data->point; 413 fView->DrawPicture(picture_data->picture, real_offset); 414 fView->Sync(); 415 delete picture_data; 416 } while (more); 417 } 418 } 419 if (!nextBand(fBitmap, &offset)) { 420 return false; 421 } 422 } while (offset.x >= 0.0f && offset.y >= 0.0f); 423 424 return true; 425 } 426 427 bool 428 GraphicsDriver::collectPages(SpoolData *spool_data, PageDataList *pages) 429 { 430 // collect the pages to be printed on the physical page 431 PageData *page_data; 432 int nup = fOrgJobData->getNup(); 433 bool more; 434 do { 435 more = spool_data->enumObject(&page_data); 436 if (pages != NULL) { 437 pages->push_back(page_data); 438 } 439 } while (more && --nup); 440 441 return more; 442 } 443 444 bool 445 GraphicsDriver::skipPages(SpoolData *spool_data) 446 { 447 return collectPages(spool_data, NULL); 448 } 449 450 bool 451 GraphicsDriver::printDocument(SpoolData *spool_data) 452 { 453 bool more; 454 bool success; 455 int page_index; 456 int copy; 457 int copies; 458 459 more = true; 460 success = true; 461 page_index = 0; 462 463 if (fPrinterCap->isSupport(PrinterCap::kCopyCommand)) { 464 // let the printer perform the copy operation 465 copies = 1; 466 } else { 467 // send the page multiple times to the printer 468 copies = fRealJobData->getCopies(); 469 } 470 fStatusWindow -> SetPageCopies(copies); // inform fStatusWindow about number of copies 471 472 // printing of even/odd numbered pages only is valid in simplex mode 473 bool simplex = fRealJobData->getPrintStyle() == JobData::kSimplex; 474 475 if (spool_data->startEnum()) { 476 do { 477 DBGMSG(("page index = %d\n", page_index)); 478 479 if (simplex && 480 fRealJobData->getPageSelection() == JobData::kEvenNumberedPages) { 481 // skip odd numbered page 482 more = skipPages(spool_data); 483 } 484 485 if (!more) { 486 // end reached 487 break; 488 } 489 490 PageDataList pages; 491 more = collectPages(spool_data, &pages); 492 493 if (more && simplex && 494 fRealJobData->getPageSelection() == JobData::kOddNumberedPages) { 495 // skip even numbered page 496 more = skipPages(spool_data); 497 } 498 499 // print each physical page "copies" of times 500 for (copy = 0; success && copy < copies; copy ++) { 501 502 // Update the status / cancel job 503 if (fStatusWindow->UpdateStatusBar(page_index, copy)) 504 return false; 505 506 success = startPage(page_index); 507 if (!success) 508 break; 509 510 // print the pages on the physical page 511 fView->Window()->Lock(); 512 success = printPage(&pages); 513 fView->Window()->Unlock(); 514 515 if (success) { 516 success = endPage(page_index); 517 } 518 } 519 520 page_index++; 521 } while (success && more); 522 } 523 524 #ifndef USE_PREVIEW_FOR_DEBUG 525 if (success 526 && fPrinterCap->isSupport(PrinterCap::kPrintStyle) 527 && (fOrgJobData->getPrintStyle() != JobData::kSimplex) 528 && (((page_index + fOrgJobData->getNup() - 1) / fOrgJobData->getNup()) % 2)) 529 { 530 // append an empty page on the back side of the page in duplex or booklet mode 531 success = 532 startPage(page_index) && 533 printPage(NULL) && 534 endPage(page_index); 535 } 536 #endif 537 538 return success; 539 } 540 541 const JobData* 542 GraphicsDriver::getJobData(BFile *spoolFile) 543 { 544 setupData(spoolFile); 545 return fOrgJobData; 546 } 547 548 bool 549 GraphicsDriver::printJob(BFile *spoolFile) 550 { 551 bool success = true; 552 if (!setupData(spoolFile)) { 553 // silently exit if there is nothing to print 554 return true; 555 } 556 557 fTransport = new Transport(fPrinterData); 558 559 if (fTransport->check_abort()) { 560 success = false; 561 } else if (!fTransport->is_print_to_file_canceled()) { 562 BStopWatch stopWatch("printJob", !MEASURE_PRINT_JOB_TIME); 563 setupBitmap(); 564 SpoolData spool_data(spoolFile, fPageCount, fOrgJobData->getNup(), fOrgJobData->getReverse()); 565 success = startDoc(); 566 if (success) { 567 fStatusWindow = new StatusWindow( 568 fRealJobData->getPageSelection() == JobData::kOddNumberedPages, 569 fRealJobData->getPageSelection() == JobData::kEvenNumberedPages, 570 fRealJobData->getFirstPage(), 571 fPageCount, 572 fInternalCopies,fRealJobData->getNup()); 573 574 while (fInternalCopies--) { 575 success = printDocument(&spool_data); 576 if (success == false) { 577 break; 578 } 579 } 580 endDoc(success); 581 582 fStatusWindow->Lock(); 583 fStatusWindow->Quit(); 584 } 585 cleanupBitmap(); 586 cleanupData(); 587 } 588 589 if (success == false) { 590 BAlert *alert; 591 if (fTransport->check_abort()) { 592 alert = new BAlert("", fTransport->last_error().c_str(), "OK"); 593 } else { 594 alert = new BAlert("", "Printer not responding.", "OK"); 595 } 596 alert->Go(); 597 } 598 599 delete fTransport; 600 fTransport = NULL; 601 602 return success; 603 } 604 605 BMessage * 606 GraphicsDriver::takeJob(BFile* spoolFile, uint32 flags) 607 { 608 fFlags = flags; 609 BMessage *msg; 610 if (printJob(spoolFile)) { 611 msg = new BMessage('okok'); 612 } else { 613 msg = new BMessage('baad'); 614 } 615 return msg; 616 } 617 618 bool 619 GraphicsDriver::startDoc() 620 { 621 return true; 622 } 623 624 bool 625 GraphicsDriver::startPage(int) 626 { 627 return true; 628 } 629 630 bool 631 GraphicsDriver::nextBand(BBitmap *, BPoint *) 632 { 633 return true; 634 } 635 636 bool 637 GraphicsDriver::endPage(int) 638 { 639 return true; 640 } 641 642 bool 643 GraphicsDriver::endDoc(bool) 644 { 645 return true; 646 } 647 648 void 649 GraphicsDriver::writeSpoolData(const void *buffer, size_t size) throw(TransportException) 650 { 651 if (fTransport) { 652 fTransport->write(buffer, size); 653 } 654 } 655 656 void 657 GraphicsDriver::writeSpoolString(const char *format, ...) throw(TransportException) 658 { 659 if (fTransport) { 660 char buffer[256]; 661 va_list ap; 662 va_start(ap, format); 663 vsprintf(buffer, format, ap); 664 fTransport->write(buffer, strlen(buffer)); 665 va_end(ap); 666 } 667 } 668 669 void 670 GraphicsDriver::writeSpoolChar(char c) throw(TransportException) 671 { 672 if (fTransport) { 673 fTransport->write(&c, 1); 674 } 675 } 676 677 678 void 679 GraphicsDriver::rgb32_to_rgb24(void* src, void* dst, int width) { 680 uint8* d = (uint8*)dst; 681 rgb_color* s = (rgb_color*)src; 682 for (int i = width; i > 0; i --) { 683 *d ++ = s->red; 684 *d ++ = s->green; 685 *d ++ = s->blue; 686 s++; 687 } 688 } 689 690 void 691 GraphicsDriver::cmap8_to_rgb24(void* src, void* dst, int width) { 692 uint8* d = (uint8*)dst; 693 uint8* s = (uint8*)src; 694 const color_map* cmap = system_colors(); 695 for (int i = width; i > 0; i --) { 696 const rgb_color* rgb = &cmap->color_list[*s]; 697 *d ++ = rgb->red; 698 *d ++ = rgb->green; 699 *d ++ = rgb->blue; 700 s ++; 701 } 702 } 703 704 void 705 GraphicsDriver::convert_to_rgb24(void* src, void* dst, int width, color_space cs) { 706 if (cs == B_RGB32) rgb32_to_rgb24(src, dst, width); 707 else if (cs == B_CMAP8) cmap8_to_rgb24(src, dst, width); 708 else { 709 DBGMSG(("color_space %d not supported", cs)); 710 } 711 } 712 713 uint8 714 GraphicsDriver::gray(uint8 r, uint8 g, uint8 b) { 715 if (r == g && g == b) { 716 return r; 717 } else { 718 return (r * 3 + g * 6 + b) / 10; 719 } 720 } 721 722 723 void 724 GraphicsDriver::rgb32_to_gray(void* src, void* dst, int width) { 725 uint8* d = (uint8*)dst; 726 rgb_color* s = (rgb_color*)src; 727 for (int i = width; i > 0; i --) { 728 *d ++ = gray(s->red, s->green, s->blue); 729 s++; 730 } 731 } 732 733 void 734 GraphicsDriver::cmap8_to_gray(void* src, void* dst, int width) { 735 uint8* d = (uint8*)dst; 736 uint8* s = (uint8*)src; 737 const color_map* cmap = system_colors(); 738 for (int i = width; i > 0; i --) { 739 const rgb_color* rgb = &cmap->color_list[*s]; 740 *d ++ = gray(rgb->red, rgb->green, rgb->blue); 741 s ++; 742 } 743 } 744 745 void 746 GraphicsDriver::convert_to_gray(void* src, void* dst, int width, color_space cs) { 747 if (cs == B_RGB32) rgb32_to_gray(src, dst, width); 748 else if (cs == B_CMAP8) cmap8_to_gray(src, dst, width); 749 else { 750 DBGMSG(("color_space %d not supported", cs)); 751 } 752 } 753 754