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:
CoordinateSystem()19 CoordinateSystem()
20 :
21 fXDPI(0),
22 fYDPI(0)
23 {
24 }
25
26
SetDPI(int32 x,int32 y)27 void SetDPI(int32 x, int32 y) {
28 fXDPI = x;
29 fYDPI = y;
30 }
31
32
ToGutenprint(int32 fromX,int32 fromY,double & toX,double & toY)33 void ToGutenprint(int32 fromX, int32 fromY, double& toX, double& toY) {
34 toX = fromX * kGutenprintUnit / fXDPI;
35 toY = fromY * kGutenprintUnit / fYDPI;
36 }
37
38
ToGutenprintCeiling(int32 fromX,int32 fromY,double & toX,double & toY)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
FromGutenprint(double fromX,double fromY,int32 & toX,int32 & toY)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
FromGutenprintCeiling(double fromX,double fromY,int32 & toX,int32 & toY)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
SizeFromGutenprint(double fromWidth,double fromHeight,int32 & toWidth,int32 & toHeight)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
RoundUpToWholeInches(int32 & width,int32 & height)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
GPJob()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
~GPJob()99 GPJob::~GPJob()
100 {
101 }
102
103
104 void
SetApplicationName(const BString & applicationName)105 GPJob::SetApplicationName(const BString& applicationName)
106 {
107 fApplicationName = applicationName;
108 }
109
110
111 void
SetConfiguration(GPJobConfiguration * configuration)112 GPJob::SetConfiguration(GPJobConfiguration* configuration)
113 {
114 fConfiguration = configuration;
115 }
116
117
118 void
SetOutputStream(OutputStream * outputStream)119 GPJob::SetOutputStream(OutputStream* outputStream)
120 {
121 fOutputStream = outputStream;
122 }
123
124
125 status_t
Begin()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
End()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
PrintPage(list<GPBand * > & bands)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
GetErrorMessage(BString & message)359 GPJob::GetErrorMessage(BString& message)
360 {
361 message = fErrorMessage;
362 }
363
364
365 RectInt32
GetPrintRectangle(list<GPBand * > & bands)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
Init()383 GPJob::Init()
384 {
385
386 }
387
388
389 void
Reset()390 GPJob::Reset()
391 {
392
393 }
394
395
396 int
Width()397 GPJob::Width()
398 {
399 return fPrintRect.Width() + 1;
400 }
401
402
403 int
Height()404 GPJob::Height()
405 {
406 return fPrintRect.Height() + 1;
407 }
408
409
410 stp_image_status_t
GetRow(unsigned char * data,size_t size,int row)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*
FindBand(int line)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
FillRow(GPBand * band,unsigned char * data,size_t size,int line)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
FillWhite(unsigned char * data,size_t size)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*
GetAppname()503 GPJob::GetAppname()
504 {
505 return fApplicationName.String();
506 }
507
508
509 void
Conclude()510 GPJob::Conclude()
511 {
512 // nothing to do
513 }
514
515
516 void
Write(const char * data,size_t size)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
ReportError(const char * data,size_t size)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
ImageInit(stp_image_t * image)537 GPJob::ImageInit(stp_image_t* image)
538 {
539 GPJob* job = static_cast<GPJob*>(image->rep);
540 job->Init();
541 }
542
543
544 void
ImageReset(stp_image_t * image)545 GPJob::ImageReset(stp_image_t* image)
546 {
547 GPJob* job = static_cast<GPJob*>(image->rep);
548 job->Reset();
549 }
550
551
552 int
ImageWidth(stp_image_t * image)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
ImageHeight(stp_image_t * image)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
ImageGetRow(stp_image_t * image,unsigned char * data,size_t size,int row)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*
ImageGetAppname(stp_image_t * image)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
ImageConclude(stp_image_t * image)586 GPJob::ImageConclude(stp_image_t *image)
587 {
588 GPJob* job = static_cast<GPJob*>(image->rep);
589 job->Conclude();
590 }
591
592
593 void
OutputFunction(void * cookie,const char * data,size_t size)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
ErrorFunction(void * cookie,const char * data,size_t size)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