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