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