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