2 * Copyright 2010, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
14 static const int32 kGutenprintUnit
= 72;
16 class CoordinateSystem
27 void SetDPI(int32 x
, int32 y
) {
33 void ToGutenprint(int32 fromX
, int32 fromY
, int32
& toX
, int32
& toY
) {
34 toX
= fromX
* kGutenprintUnit
/ fXDPI
;
35 toY
= fromY
* kGutenprintUnit
/ fYDPI
;
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
;
45 void FromGutenprint(int32 fromX
, int32 fromY
, int32
& toX
, int32
& toY
) {
46 toX
= fromX
* fXDPI
/ kGutenprintUnit
;
47 toY
= fromY
* fYDPI
/ kGutenprintUnit
;
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
;
55 void SizeFromGutenprint(int32 fromWidth
, int32 fromHeight
,
56 int32
& toWidth
, int32
& toHeight
) {
57 toWidth
= fromWidth
* fXDPI
/ kGutenprintUnit
;
58 toHeight
= fromHeight
* fYDPI
/ kGutenprintUnit
;
61 void RoundUpToWholeInches(int32
& width
, int32
& height
) {
62 width
= ((width
+ kGutenprintUnit
- 1) / kGutenprintUnit
)
64 height
= ((height
+ kGutenprintUnit
- 1) / kGutenprintUnit
)
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
;
103 GPJob::SetApplicationName(const BString
& applicationName
)
105 fApplicationName
= applicationName
;
110 GPJob::SetConfiguration(GPJobConfiguration
* configuration
)
112 fConfiguration
= configuration
;
117 GPJob::SetOutputStream(OutputStream
* outputStream
)
119 fOutputStream
= outputStream
;
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());
137 fVariables
= stp_vars_create();
138 if (fVariables
== NULL
) {
139 fprintf(stderr
, "GPJob Begin: out of memory\n");
142 stp_set_printer_defaults(fVariables
, fPrinter
);
144 stp_set_outfunc(fVariables
, OutputFunction
);
145 stp_set_errfunc(fVariables
, ErrorFunction
);
146 stp_set_outdata(fVariables
, this);
147 stp_set_errdata(fVariables
, this);
149 stp_set_string_parameter(fVariables
, "PageSize",
150 fConfiguration
->fPageSize
);
152 if (fConfiguration
->fResolution
!= "")
153 stp_set_string_parameter(fVariables
, "Resolution",
154 fConfiguration
->fResolution
);
156 stp_set_string_parameter(fVariables
, "InputSlot",
157 fConfiguration
->fInputSlot
);
159 stp_set_string_parameter(fVariables
, "PrintingMode",
160 fConfiguration
->fPrintingMode
);
163 map
<string
, string
>::iterator it
= fConfiguration
->fStringSettings
.
165 for (; it
!= fConfiguration
->fStringSettings
.end(); it
++) {
166 stp_set_string_parameter(fVariables
, it
->first
.c_str(),
172 map
<string
, bool>::iterator it
= fConfiguration
->fBooleanSettings
.
174 for (; it
!= fConfiguration
->fBooleanSettings
.end(); it
++) {
175 stp_set_boolean_parameter(fVariables
, it
->first
.c_str(),
181 map
<string
, int32
>::iterator it
= fConfiguration
->fIntSettings
.
183 for (; it
!= fConfiguration
->fIntSettings
.end(); it
++) {
184 stp_set_int_parameter(fVariables
, it
->first
.c_str(),
190 map
<string
, int32
>::iterator it
= fConfiguration
->fDimensionSettings
.
192 for (; it
!= fConfiguration
->fDimensionSettings
.end(); it
++) {
193 stp_set_dimension_parameter(fVariables
, it
->first
.c_str(),
199 map
<string
, double>::iterator it
= fConfiguration
->fDoubleSettings
.
201 for (; it
!= fConfiguration
->fDoubleSettings
.end(); it
++) {
202 stp_set_float_parameter(fVariables
, it
->first
.c_str(),
207 stp_set_string_parameter(fVariables
, "InputImageType",
209 stp_set_string_parameter(fVariables
, "ChannelBitDepth",
211 stp_set_float_parameter(fVariables
, "Density",
213 stp_set_string_parameter(fVariables
, "JobMode", "Job");
215 stp_set_printer_defaults_soft(fVariables
, fPrinter
);
224 if (fVariables
== NULL
)
228 stp_end_job(fVariables
, &fImage
);
230 stp_vars_destroy(fVariables
);
235 GPJob::PrintPage(list
<GPBand
*>& bands
) {
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, "
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"
256 CoordinateSystem coordinateSystem
;
257 coordinateSystem
.SetDPI(fConfiguration
->fXDPI
, fConfiguration
->fYDPI
);
259 // GPBand offset is relative to imageable area left, top
260 // but it has to be absolute to left, top of page
263 coordinateSystem
.FromGutenprintCeiling(imageableArea
.left
,
264 imageableArea
.top
, offsetX
, offsetY
);
266 BPoint
offset(offsetX
, offsetY
);
267 list
<GPBand
*>::iterator it
= fBands
->begin();
268 for (; it
!= fBands
->end(); it
++) {
269 (*it
)->fWhere
+= offset
;
273 fPrintRect
= GetPrintRectangle(bands
);
276 int left
= (int)fPrintRect
.left
;
277 int top
= (int)fPrintRect
.top
;
278 int width
= fPrintRect
.Width() + 1;
279 int height
= fPrintRect
.Height() + 1;
281 fprintf(stderr
, "GPJob bitmap bands frame left %d, top %d, width %d, "
283 left
, top
, width
, height
);
286 // calculate the position and size of the image to be printed on the page
288 // constraints: the image must be inside the imageable area
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
;
299 coordinateSystem
.ToGutenprintCeiling(fPrintRect
.right
, fPrintRect
.bottom
,
301 if (right
> imageableArea
.right
)
302 right
= imageableArea
.right
;
303 if (bottom
> imageableArea
.bottom
)
304 bottom
= imageableArea
.bottom
;
306 int32 width
= right
- left
;
307 int32 height
= bottom
- top
;
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
,
316 fPrintRect
.right
= fPrintRect
.left
+ printRectWidth
- 1;
317 fPrintRect
.bottom
= fPrintRect
.top
+ printRectHeight
- 1;
319 int left
= fPrintRect
.left
;
320 int top
= fPrintRect
.top
;
321 int width
= fPrintRect
.Width() + 1;
322 int height
= fPrintRect
.Height() + 1;
324 fprintf(stderr
, "GPJob image dimensions left %d, top %d, width %d, "
326 left
, top
, width
, height
);
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
);
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
);
338 stp_merge_printvars(fVariables
, stp_printer_get_defaults(fPrinter
));
340 if (!stp_verify(fVariables
)) {
341 fprintf(stderr
, "GPJob PrintPage: invalid variables\n");
347 stp_start_job(fVariables
, &fImage
);
350 stp_print(fVariables
, &fImage
);
357 GPJob::GetErrorMessage(BString
& message
)
359 message
= fErrorMessage
;
364 GPJob::GetPrintRectangle(list
<GPBand
*>& bands
)
366 list
<GPBand
*>::iterator it
= bands
.begin();
367 if (it
== bands
.end())
368 return BRect(0, 0, 0, 0);
371 BRect rect
= first
->GetBoundingRectangle();
372 for (it
++; it
!= bands
.end(); it
++) {
374 rect
= rect
| band
->GetBoundingRectangle();
397 return fPrintRect
.Width() + 1;
404 return fPrintRect
.Height() + 1;
409 GPJob::GetRow(unsigned char* data
, size_t size
, int row
)
412 return STP_IMAGE_STATUS_ABORT
;
414 // row is relative to left, top of image
415 // convert it to absolute y coordinate value
416 int line
= fPrintRect
.top
+ row
;
418 FillWhite(data
, size
);
420 GPBand
* band
= FindBand(line
);
422 FillRow(band
, data
, size
, line
);
424 return STP_IMAGE_STATUS_OK
;
429 GPJob::FindBand(int line
)
431 if (fCachedBand
!= NULL
&& fCachedBand
->ContainsLine(line
))
434 list
<GPBand
*>::iterator it
= fBands
->begin();
435 for (; it
!= fBands
->end(); it
++) {
437 if (band
->ContainsLine(line
)) {
449 GPJob::FillRow(GPBand
* band
, unsigned char* data
, size_t size
, int line
)
451 int imageTop
= line
- static_cast<int>(band
->fWhere
.y
-
452 band
->fValidRect
.top
);
453 int imageLeft
= static_cast<int>(band
->fValidRect
.left
);
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
;
462 int dataLeft
= static_cast<int>(band
->fWhere
.x
- fPrintRect
.left
);
463 int sourcePixelsToSkip
= 0;
465 sourcePixelsToSkip
= -dataLeft
;
468 int width
= band
->fValidRect
.IntegerWidth() + 1 - sourcePixelsToSkip
;
469 source
+= sourcePixelsToSkip
* kSourceBytesPerPixel
;
473 const int kTargetBytesPerPixel
= 3; // RGB
474 unsigned char* target
= &data
[dataLeft
* kTargetBytesPerPixel
];
475 int maxWidth
= size
/ kTargetBytesPerPixel
- dataLeft
;
476 if (width
> maxWidth
)
479 ASSERT(0 <= imageTop
&& imageTop
<= band
->fValidRect
.IntegerHeight());
480 ASSERT((dataLeft
+ width
) * kTargetBytesPerPixel
<= size
);
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
;
493 GPJob::FillWhite(unsigned char* data
, size_t size
)
495 for (size_t i
= 0; i
< size
; i
++)
503 return fApplicationName
.String();
515 GPJob::Write(const char* data
, size_t size
)
518 fOutputStream
->Write(data
, size
);
519 } catch (TransportException e
) {
520 fStatus
= B_IO_ERROR
;
526 GPJob::ReportError(const char* data
, size_t size
)
530 fErrorMessage
.Append(data
, size
);
535 GPJob::ImageInit(stp_image_t
* image
)
537 GPJob
* job
= static_cast<GPJob
*>(image
->rep
);
543 GPJob::ImageReset(stp_image_t
* image
)
545 GPJob
* job
= static_cast<GPJob
*>(image
->rep
);
551 GPJob::ImageWidth(stp_image_t
* image
)
553 GPJob
* job
= static_cast<GPJob
*>(image
->rep
);
559 GPJob::ImageHeight(stp_image_t
*image
)
561 GPJob
* job
= static_cast<GPJob
*>(image
->rep
);
562 return job
->Height();
567 GPJob::ImageGetRow(stp_image_t
* image
, unsigned char* data
, size_t size
,
570 GPJob
* job
= static_cast<GPJob
*>(image
->rep
);
571 return job
->GetRow(data
, size
, row
);
576 GPJob::ImageGetAppname(stp_image_t
* image
)
578 GPJob
* job
= static_cast<GPJob
*>(image
->rep
);
579 return job
->GetAppname();
584 GPJob::ImageConclude(stp_image_t
*image
)
586 GPJob
* job
= static_cast<GPJob
*>(image
->rep
);
592 GPJob::OutputFunction(void *cookie
, const char *data
, size_t size
)
594 GPJob
* job
= static_cast<GPJob
*>(cookie
);
595 job
->Write(data
, size
);
600 GPJob::ErrorFunction(void *cookie
, const char *data
, size_t size
)
602 GPJob
* job
= static_cast<GPJob
*>(cookie
);
603 job
->ReportError(data
, size
);