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 14static const int32 kGutenprintUnit = 72; 15 16class CoordinateSystem 17{ 18public: 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 70private: 71 double fXDPI; 72 double fYDPI; 73}; 74 75 76GPJob::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 99GPJob::~GPJob() 100{ 101} 102 103 104void 105GPJob::SetApplicationName(const BString& applicationName) 106{ 107 fApplicationName = applicationName; 108} 109 110 111void 112GPJob::SetConfiguration(GPJobConfiguration* configuration) 113{ 114 fConfiguration = configuration; 115} 116 117 118void 119GPJob::SetOutputStream(OutputStream* outputStream) 120{ 121 fOutputStream = outputStream; 122} 123 124 125status_t 126GPJob::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 223void 224GPJob::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 236status_t 237GPJob::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 358void 359GPJob::GetErrorMessage(BString& message) 360{ 361 message = fErrorMessage; 362} 363 364 365RectInt32 366GPJob::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 382void 383GPJob::Init() 384{ 385 386} 387 388 389void 390GPJob::Reset() 391{ 392 393} 394 395 396int 397GPJob::Width() 398{ 399 return fPrintRect.Width() + 1; 400} 401 402 403int 404GPJob::Height() 405{ 406 return fPrintRect.Height() + 1; 407} 408 409 410stp_image_status_t 411GPJob::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 430GPBand* 431GPJob::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 450void 451GPJob::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 494void 495GPJob::FillWhite(unsigned char* data, size_t size) 496{ 497 for (size_t i = 0; i < size; i ++) 498 data[i] = 0xff; 499} 500 501 502const char* 503GPJob::GetAppname() 504{ 505 return fApplicationName.String(); 506} 507 508 509void 510GPJob::Conclude() 511{ 512 // nothing to do 513} 514 515 516void 517GPJob::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 527void 528GPJob::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 536void 537GPJob::ImageInit(stp_image_t* image) 538{ 539 GPJob* job = static_cast<GPJob*>(image->rep); 540 job->Init(); 541} 542 543 544void 545GPJob::ImageReset(stp_image_t* image) 546{ 547 GPJob* job = static_cast<GPJob*>(image->rep); 548 job->Reset(); 549} 550 551 552int 553GPJob::ImageWidth(stp_image_t* image) 554{ 555 GPJob* job = static_cast<GPJob*>(image->rep); 556 return job->Width(); 557} 558 559 560int 561GPJob::ImageHeight(stp_image_t *image) 562{ 563 GPJob* job = static_cast<GPJob*>(image->rep); 564 return job->Height(); 565} 566 567 568stp_image_status_t 569GPJob::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 577const char* 578GPJob::ImageGetAppname(stp_image_t* image) 579{ 580 GPJob* job = static_cast<GPJob*>(image->rep); 581 return job->GetAppname(); 582} 583 584 585void 586GPJob::ImageConclude(stp_image_t *image) 587{ 588 GPJob* job = static_cast<GPJob*>(image->rep); 589 job->Conclude(); 590} 591 592 593void 594GPJob::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 601void 602GPJob::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