* Copyright 2010, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Pfeiffer
*/
#include "GPJob.h"
#include <Debug.h>
static const int32 kGutenprintUnit = 72;
class CoordinateSystem
{
public:
CoordinateSystem()
:
fXDPI(0),
fYDPI(0)
{
}
void SetDPI(int32 x, int32 y) {
fXDPI = x;
fYDPI = y;
}
void ToGutenprint(int32 fromX, int32 fromY, double& toX, double& toY) {
toX = fromX * kGutenprintUnit / fXDPI;
toY = fromY * kGutenprintUnit / fYDPI;
}
void ToGutenprintCeiling(int32 fromX, int32 fromY, double& toX,
double& toY) {
toX = (fromX * kGutenprintUnit + fXDPI - 1) / fXDPI;
toY = (fromY * kGutenprintUnit + fYDPI - 1) / fYDPI;
}
void FromGutenprint(double fromX, double fromY, int32& toX, int32& toY) {
toX = (int32)(fromX * fXDPI / kGutenprintUnit);
toY = (int32)(fromY * fYDPI / kGutenprintUnit);
}
void FromGutenprintCeiling(double fromX, double fromY, int32& toX,
int32& toY) {
toX = (int32)((fromX * fXDPI + kGutenprintUnit - 1) / kGutenprintUnit);
toY = (int32)((fromY * fYDPI + kGutenprintUnit - 1) / kGutenprintUnit);
}
void SizeFromGutenprint(double fromWidth, double fromHeight,
int32& toWidth, int32& toHeight) {
toWidth = (int32)(fromWidth * fXDPI / kGutenprintUnit);
toHeight = (int32)(fromHeight * fYDPI / kGutenprintUnit);
}
void RoundUpToWholeInches(int32& width, int32& height) {
width = ((width + kGutenprintUnit - 1) / kGutenprintUnit)
* kGutenprintUnit;
height = ((height + kGutenprintUnit - 1) / kGutenprintUnit)
* kGutenprintUnit;
}
private:
double fXDPI;
double fYDPI;
};
GPJob::GPJob()
:
fApplicationName(),
fOutputStream(NULL),
fConfiguration(NULL),
fHasPages(false),
fVariables(NULL),
fPrinter(NULL),
fBands(NULL),
fCachedBand(NULL),
fStatus(B_OK)
{
fImage.init = ImageInit;
fImage.reset = ImageReset;
fImage.width = ImageWidth;
fImage.height = ImageHeight;
fImage.get_row = ImageGetRow;
fImage.get_appname = ImageGetAppname;
fImage.conclude = ImageConclude;
fImage.rep = this;
}
GPJob::~GPJob()
{
}
void
GPJob::SetApplicationName(const BString& applicationName)
{
fApplicationName = applicationName;
}
void
GPJob::SetConfiguration(GPJobConfiguration* configuration)
{
fConfiguration = configuration;
}
void
GPJob::SetOutputStream(OutputStream* outputStream)
{
fOutputStream = outputStream;
}
status_t
GPJob::Begin()
{
fStatus = B_OK;
stp_init();
fPrinter = stp_get_printer_by_driver(fConfiguration->fDriver);
if (fPrinter == NULL) {
fprintf(stderr, "GPJob Begin: driver %s not found!\n",
fConfiguration->fDriver.String());
return B_ERROR;
}
fVariables = stp_vars_create();
if (fVariables == NULL) {
fprintf(stderr, "GPJob Begin: out of memory\n");
return B_NO_MEMORY;
}
stp_set_printer_defaults(fVariables, fPrinter);
stp_set_outfunc(fVariables, OutputFunction);
stp_set_errfunc(fVariables, ErrorFunction);
stp_set_outdata(fVariables, this);
stp_set_errdata(fVariables, this);
stp_set_string_parameter(fVariables, "PageSize",
fConfiguration->fPageSize);
if (fConfiguration->fResolution != "")
stp_set_string_parameter(fVariables, "Resolution",
fConfiguration->fResolution);
stp_set_string_parameter(fVariables, "InputSlot",
fConfiguration->fInputSlot);
stp_set_string_parameter(fVariables, "PrintingMode",
fConfiguration->fPrintingMode);
{
map<string, string>::iterator it = fConfiguration->fStringSettings.
begin();
for (; it != fConfiguration->fStringSettings.end(); it ++) {
stp_set_string_parameter(fVariables, it->first.c_str(),
it->second.c_str());
}
}
{
map<string, bool>::iterator it = fConfiguration->fBooleanSettings.
begin();
for (; it != fConfiguration->fBooleanSettings.end(); it ++) {
stp_set_boolean_parameter(fVariables, it->first.c_str(),
it->second);
}
}
{
map<string, int32>::iterator it = fConfiguration->fIntSettings.
begin();
for (; it != fConfiguration->fIntSettings.end(); it ++) {
stp_set_int_parameter(fVariables, it->first.c_str(),
it->second);
}
}
{
map<string, int32>::iterator it = fConfiguration->fDimensionSettings.
begin();
for (; it != fConfiguration->fDimensionSettings.end(); it ++) {
stp_set_dimension_parameter(fVariables, it->first.c_str(),
it->second);
}
}
{
map<string, double>::iterator it = fConfiguration->fDoubleSettings.
begin();
for (; it != fConfiguration->fDoubleSettings.end(); it ++) {
stp_set_float_parameter(fVariables, it->first.c_str(),
it->second);
}
}
stp_set_string_parameter(fVariables, "InputImageType",
"RGB");
stp_set_string_parameter(fVariables, "ChannelBitDepth",
"8");
stp_set_float_parameter(fVariables, "Density",
1.0f);
stp_set_string_parameter(fVariables, "JobMode", "Job");
stp_set_printer_defaults_soft(fVariables, fPrinter);
return B_OK;
}
void
GPJob::End()
{
if (fVariables == NULL)
return;
if (fHasPages)
stp_end_job(fVariables, &fImage);
stp_vars_destroy(fVariables);
fVariables = NULL;
}
status_t
GPJob::PrintPage(list<GPBand*>& bands) {
if (fStatus != B_OK)
return fStatus;
fBands = &bands;
fCachedBand = NULL;
Rectangle<stp_dimension_t> imageableArea;
stp_get_imageable_area(fVariables, &imageableArea.left,
&imageableArea.right, &imageableArea.bottom, &imageableArea.top);
fprintf(stderr, "GPJob imageable area left %f, top %f, right %f, "
"bottom %f\n",
imageableArea.left, imageableArea.top, imageableArea.right,
imageableArea.bottom);
fprintf(stderr, "GPJob width %f %s, height %f %s\n",
imageableArea.Width(),
std::fmod(imageableArea.Width(), 72.) == 0.0 ? "whole inches" : "not whole inches",
imageableArea.Height(),
std::fmod(imageableArea.Height(), 72.) == 0.0 ? "whole inches" : "not whole inches"
);
CoordinateSystem coordinateSystem;
coordinateSystem.SetDPI(fConfiguration->fXDPI, fConfiguration->fYDPI);
{
int32 offsetX;
int32 offsetY;
coordinateSystem.FromGutenprintCeiling(imageableArea.left,
imageableArea.top, offsetX, offsetY);
BPoint offset(offsetX, offsetY);
list<GPBand*>::iterator it = fBands->begin();
for (; it != fBands->end(); it++) {
(*it)->fWhere += offset;
}
}
fPrintRect = GetPrintRectangle(bands);
{
int left = (int)fPrintRect.left;
int top = (int)fPrintRect.top;
int width = fPrintRect.Width() + 1;
int height = fPrintRect.Height() + 1;
fprintf(stderr, "GPJob bitmap bands frame left %d, top %d, width %d, "
"height %d\n",
left, top, width, height);
}
double left;
double top;
coordinateSystem.ToGutenprint(fPrintRect.left, fPrintRect.top, left, top);
if (left < imageableArea.left)
left = imageableArea.left;
if (top < imageableArea.top)
top = imageableArea.top;
double right;
double bottom;
coordinateSystem.ToGutenprintCeiling(fPrintRect.right, fPrintRect.bottom,
right, bottom);
if (right > imageableArea.right)
right = imageableArea.right;
if (bottom > imageableArea.bottom)
bottom = imageableArea.bottom;
double width = right - left;
double height = bottom - top;
coordinateSystem.FromGutenprint(left, top, fPrintRect.left, fPrintRect.top);
int32 printRectWidth;
int32 printRectHeight;
coordinateSystem.SizeFromGutenprint(width, height, printRectWidth,
printRectHeight);
fPrintRect.right = fPrintRect.left + printRectWidth - 1;
fPrintRect.bottom = fPrintRect.top + printRectHeight - 1;
{
int left = fPrintRect.left;
int top = fPrintRect.top;
int width = fPrintRect.Width() + 1;
int height = fPrintRect.Height() + 1;
fprintf(stderr, "GPJob image dimensions left %d, top %d, width %d, "
"height %d\n",
left, top, width, height);
}
fprintf(stderr, "GPJob image dimensions in 1/72 Inches: "
"left %d, top %d, right %d, bottom %d\n",
(int)left, (int)top, (int)right, (int)bottom);
stp_set_width(fVariables, right - left);
stp_set_height(fVariables, bottom - top);
stp_set_left(fVariables, left);
stp_set_top(fVariables, top);
stp_merge_printvars(fVariables, stp_printer_get_defaults(fPrinter));
if (!stp_verify(fVariables)) {
fprintf(stderr, "GPJob PrintPage: invalid variables\n");
return B_ERROR;
}
if (!fHasPages) {
fHasPages = true;
stp_start_job(fVariables, &fImage);
}
stp_print(fVariables, &fImage);
return fStatus;
}
void
GPJob::GetErrorMessage(BString& message)
{
message = fErrorMessage;
}
RectInt32
GPJob::GetPrintRectangle(list<GPBand*>& bands)
{
list<GPBand*>::iterator it = bands.begin();
if (it == bands.end())
return BRect(0, 0, 0, 0);
GPBand* first = *it;
BRect rect = first->GetBoundingRectangle();
for (it ++; it != bands.end(); it ++) {
GPBand* band = *it;
rect = rect | band->GetBoundingRectangle();
}
return rect;
}
void
GPJob::Init()
{
}
void
GPJob::Reset()
{
}
int
GPJob::Width()
{
return fPrintRect.Width() + 1;
}
int
GPJob::Height()
{
return fPrintRect.Height() + 1;
}
stp_image_status_t
GPJob::GetRow(unsigned char* data, size_t size, int row)
{
if (fStatus != B_OK)
return STP_IMAGE_STATUS_ABORT;
int line = fPrintRect.top + row;
FillWhite(data, size);
GPBand* band = FindBand(line);
if (band != NULL)
FillRow(band, data, size, line);
return STP_IMAGE_STATUS_OK;
}
GPBand*
GPJob::FindBand(int line)
{
if (fCachedBand != NULL && fCachedBand->ContainsLine(line))
return fCachedBand;
list<GPBand*>::iterator it = fBands->begin();
for (; it != fBands->end(); it ++) {
GPBand* band = *it;
if (band->ContainsLine(line)) {
fCachedBand = band;
return band;
}
}
fCachedBand = NULL;
return NULL;
}
void
GPJob::FillRow(GPBand* band, unsigned char* data, size_t size, int line)
{
int imageTop = line - static_cast<int>(band->fWhere.y -
band->fValidRect.top);
int imageLeft = static_cast<int>(band->fValidRect.left);
const int sourceBytesPerRow = band->fBitmap.BytesPerRow();
const int kSourceBytesPerPixel = 4;
const unsigned char* source =
static_cast<unsigned char*>(band->fBitmap.Bits())
+ imageTop * sourceBytesPerRow
+ imageLeft * kSourceBytesPerPixel;
int dataLeft = static_cast<int>(band->fWhere.x - fPrintRect.left);
int sourcePixelsToSkip = 0;
if (dataLeft < 0) {
sourcePixelsToSkip = -dataLeft;
dataLeft = 0;
}
int width = band->fValidRect.IntegerWidth() + 1 - sourcePixelsToSkip;
source += sourcePixelsToSkip * kSourceBytesPerPixel;
if (width <= 0)
return;
const int kTargetBytesPerPixel = 3;
unsigned char* target = &data[dataLeft * kTargetBytesPerPixel];
int maxWidth = size / kTargetBytesPerPixel - dataLeft;
if (width > maxWidth)
width = maxWidth;
ASSERT(0 <= imageTop && imageTop <= band->fValidRect.IntegerHeight());
ASSERT((dataLeft + width) * kTargetBytesPerPixel <= size);
for (int i = 0; i < width; i ++) {
target[0] = source[2];
target[1] = source[1];
target[2] = source[0];
target += kTargetBytesPerPixel;
source += kSourceBytesPerPixel;
}
}
void
GPJob::FillWhite(unsigned char* data, size_t size)
{
for (size_t i = 0; i < size; i ++)
data[i] = 0xff;
}
const char*
GPJob::GetAppname()
{
return fApplicationName.String();
}
void
GPJob::Conclude()
{
}
void
GPJob::Write(const char* data, size_t size)
{
try {
fOutputStream->Write(data, size);
} catch (TransportException& e) {
fStatus = B_IO_ERROR;
}
}
void
GPJob::ReportError(const char* data, size_t size)
{
if (fStatus == B_OK)
fStatus = B_ERROR;
fErrorMessage.Append(data, size);
}
void
GPJob::ImageInit(stp_image_t* image)
{
GPJob* job = static_cast<GPJob*>(image->rep);
job->Init();
}
void
GPJob::ImageReset(stp_image_t* image)
{
GPJob* job = static_cast<GPJob*>(image->rep);
job->Reset();
}
int
GPJob::ImageWidth(stp_image_t* image)
{
GPJob* job = static_cast<GPJob*>(image->rep);
return job->Width();
}
int
GPJob::ImageHeight(stp_image_t *image)
{
GPJob* job = static_cast<GPJob*>(image->rep);
return job->Height();
}
stp_image_status_t
GPJob::ImageGetRow(stp_image_t* image, unsigned char* data, size_t size,
int row)
{
GPJob* job = static_cast<GPJob*>(image->rep);
return job->GetRow(data, size, row);
}
const char*
GPJob::ImageGetAppname(stp_image_t* image)
{
GPJob* job = static_cast<GPJob*>(image->rep);
return job->GetAppname();
}
void
GPJob::ImageConclude(stp_image_t *image)
{
GPJob* job = static_cast<GPJob*>(image->rep);
job->Conclude();
}
void
GPJob::OutputFunction(void *cookie, const char *data, size_t size)
{
GPJob* job = static_cast<GPJob*>(cookie);
job->Write(data, size);
}
void
GPJob::ErrorFunction(void *cookie, const char *data, size_t size)
{
GPJob* job = static_cast<GPJob*>(cookie);
job->ReportError(data, size);
}