vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / print / drivers / gutenprint / GPJob.cpp
blobc9d61ab3a600d547f12237dd6c91664ae7a718af
1 /*
2 * Copyright 2010, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Michael Pfeiffer
7 */
8 #include "GPJob.h"
10 #include <Debug.h>
13 // 72 DPI
14 static const int32 kGutenprintUnit = 72;
16 class CoordinateSystem
18 public:
19 CoordinateSystem()
21 fXDPI(0),
22 fYDPI(0)
27 void SetDPI(int32 x, int32 y) {
28 fXDPI = x;
29 fYDPI = 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)
63 * kGutenprintUnit;
64 height = ((height + kGutenprintUnit - 1) / kGutenprintUnit)
65 * kGutenprintUnit;
68 private:
69 int32 fXDPI;
70 int32 fYDPI;
74 GPJob::GPJob()
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)
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;
97 GPJob::~GPJob()
102 void
103 GPJob::SetApplicationName(const BString& applicationName)
105 fApplicationName = applicationName;
109 void
110 GPJob::SetConfiguration(GPJobConfiguration* configuration)
112 fConfiguration = configuration;
116 void
117 GPJob::SetOutputStream(OutputStream* outputStream)
119 fOutputStream = outputStream;
123 status_t
124 GPJob::Begin()
126 fStatus = B_OK;
128 stp_init();
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;
137 fVariables = stp_vars_create();
138 if (fVariables == NULL) {
139 fprintf(stderr, "GPJob Begin: out of memory\n");
140 return B_NO_MEMORY;
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.
164 begin();
165 for (; it != fConfiguration->fStringSettings.end(); it ++) {
166 stp_set_string_parameter(fVariables, it->first.c_str(),
167 it->second.c_str());
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);
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);
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);
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);
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");
215 stp_set_printer_defaults_soft(fVariables, fPrinter);
217 return B_OK;
221 void
222 GPJob::End()
224 if (fVariables == NULL)
225 return;
227 if (fHasPages)
228 stp_end_job(fVariables, &fImage);
230 stp_vars_destroy(fVariables);
231 fVariables = NULL;
234 status_t
235 GPJob::PrintPage(list<GPBand*>& bands) {
236 if (fStatus != B_OK)
237 return fStatus;
239 fBands = &bands;
240 fCachedBand = NULL;
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"
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
261 int32 offsetX;
262 int32 offsetY;
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, "
282 "height %d\n",
283 left, top, width, height);
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;
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;
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,
315 printRectHeight);
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, "
325 "height %d\n",
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");
342 return B_ERROR;
345 if (!fHasPages) {
346 fHasPages = true;
347 stp_start_job(fVariables, &fImage);
350 stp_print(fVariables, &fImage);
352 return fStatus;
356 void
357 GPJob::GetErrorMessage(BString& message)
359 message = fErrorMessage;
363 RectInt32
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);
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();
376 return rect;
380 void
381 GPJob::Init()
387 void
388 GPJob::Reset()
395 GPJob::Width()
397 return fPrintRect.Width() + 1;
402 GPJob::Height()
404 return fPrintRect.Height() + 1;
408 stp_image_status_t
409 GPJob::GetRow(unsigned char* data, size_t size, int row)
411 if (fStatus != B_OK)
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);
421 if (band != NULL)
422 FillRow(band, data, size, line);
424 return STP_IMAGE_STATUS_OK;
428 GPBand*
429 GPJob::FindBand(int line)
431 if (fCachedBand != NULL && fCachedBand->ContainsLine(line))
432 return fCachedBand;
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;
443 fCachedBand = NULL;
444 return NULL;
448 void
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;
464 if (dataLeft < 0) {
465 sourcePixelsToSkip = -dataLeft;
466 dataLeft = 0;
468 int width = band->fValidRect.IntegerWidth() + 1 - sourcePixelsToSkip;
469 source += sourcePixelsToSkip * kSourceBytesPerPixel;
470 if (width <= 0)
471 return;
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;
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;
492 void
493 GPJob::FillWhite(unsigned char* data, size_t size)
495 for (size_t i = 0; i < size; i ++)
496 data[i] = 0xff;
500 const char*
501 GPJob::GetAppname()
503 return fApplicationName.String();
507 void
508 GPJob::Conclude()
510 // nothing to do
514 void
515 GPJob::Write(const char* data, size_t size)
517 try {
518 fOutputStream->Write(data, size);
519 } catch (TransportException e) {
520 fStatus = B_IO_ERROR;
525 void
526 GPJob::ReportError(const char* data, size_t size)
528 if (fStatus == B_OK)
529 fStatus = B_ERROR;
530 fErrorMessage.Append(data, size);
534 void
535 GPJob::ImageInit(stp_image_t* image)
537 GPJob* job = static_cast<GPJob*>(image->rep);
538 job->Init();
542 void
543 GPJob::ImageReset(stp_image_t* image)
545 GPJob* job = static_cast<GPJob*>(image->rep);
546 job->Reset();
551 GPJob::ImageWidth(stp_image_t* image)
553 GPJob* job = static_cast<GPJob*>(image->rep);
554 return job->Width();
559 GPJob::ImageHeight(stp_image_t *image)
561 GPJob* job = static_cast<GPJob*>(image->rep);
562 return job->Height();
566 stp_image_status_t
567 GPJob::ImageGetRow(stp_image_t* image, unsigned char* data, size_t size,
568 int row)
570 GPJob* job = static_cast<GPJob*>(image->rep);
571 return job->GetRow(data, size, row);
575 const char*
576 GPJob::ImageGetAppname(stp_image_t* image)
578 GPJob* job = static_cast<GPJob*>(image->rep);
579 return job->GetAppname();
583 void
584 GPJob::ImageConclude(stp_image_t *image)
586 GPJob* job = static_cast<GPJob*>(image->rep);
587 job->Conclude();
591 void
592 GPJob::OutputFunction(void *cookie, const char *data, size_t size)
594 GPJob* job = static_cast<GPJob*>(cookie);
595 job->Write(data, size);
599 void
600 GPJob::ErrorFunction(void *cookie, const char *data, size_t size)
602 GPJob* job = static_cast<GPJob*>(cookie);
603 job->ReportError(data, size);