1/* 2 * PCL6.cpp 3 * Copyright 1999-2000 Y.Takagi. All Rights Reserved. 4 * Copyright 2003 Michael Pfeiffer. 5 */ 6 7 8#include "PCL6.h" 9 10#include <memory.h> 11 12#include <Alert.h> 13#include <Bitmap.h> 14#include <File.h> 15 16#include "DbgMsg.h" 17#include "DeltaRowCompression.h" 18#include "Halftone.h" 19#include "JobData.h" 20#include "PackBits.h" 21#include "PCL6Cap.h" 22#include "PCL6Config.h" 23#include "PCL6Rasterizer.h" 24#include "PrinterData.h" 25#include "UIDriver.h" 26#include "ValidRect.h" 27 28 29// DeltaRowStreamCompressor writes the delta row directly to the 30// in the contructor specified stream. 31class DeltaRowStreamCompressor : public AbstractDeltaRowCompressor 32{ 33public: 34 DeltaRowStreamCompressor(int rowSize, uchar initialSeed, 35 PCL6Writer* writer) 36 : 37 AbstractDeltaRowCompressor(rowSize, initialSeed), 38 fWriter(writer) 39 {} 40 41protected: 42 void AppendByteToDeltaRow(uchar byte) 43 { 44 fWriter->Append(byte); 45 } 46 47private: 48 PCL6Writer* fWriter; 49}; 50 51 52PCL6Driver::PCL6Driver(BMessage* message, PrinterData* printerData, 53 const PrinterCap* printerCap) 54 : 55 GraphicsDriver(message, printerData, printerCap), 56 fWriter(NULL), 57 fMediaSide(PCL6Writer::kFrontMediaSide), 58 fHalftone(NULL) 59{ 60} 61 62 63void 64PCL6Driver::Write(const uint8* data, uint32 size) 65{ 66 WriteSpoolData(data, size); 67} 68 69 70bool 71PCL6Driver::StartDocument() 72{ 73 try { 74 _JobStart(); 75 fHalftone = new Halftone(GetJobData()->GetSurfaceType(), 76 GetJobData()->GetGamma(), GetJobData()->GetInkDensity(), 77 GetJobData()->GetDitherType()); 78 return true; 79 } 80 catch (TransportException& err) { 81 return false; 82 } 83} 84 85 86bool 87PCL6Driver::EndDocument(bool) 88{ 89 try { 90 if (fHalftone) 91 delete fHalftone; 92 _JobEnd(); 93 return true; 94 } 95 catch (TransportException& err) { 96 return false; 97 } 98} 99 100 101bool 102PCL6Driver::NextBand(BBitmap* bitmap, BPoint* offset) 103{ 104 DBGMSG(("> nextBand\n")); 105 106 try { 107 int y = (int)offset->y; 108 109 PCL6Rasterizer* rasterizer; 110 if (_UseColorMode()) { 111 #if COLOR_DEPTH == 8 112 rasterizer = new ColorRGBRasterizer(fHalftone); 113 #elif COLOR_DEPTH == 1 114 rasterizer = new ColorRasterizer(fHalftone); 115 #else 116 #error COLOR_DEPTH must be either 1 or 8! 117 #endif 118 } else 119 rasterizer = new MonochromeRasterizer(fHalftone); 120 121 auto_ptr<Rasterizer> _rasterizer(rasterizer); 122 bool valid = rasterizer->SetBitmap((int)offset->x, (int)offset->y, 123 bitmap, GetPageHeight()); 124 125 if (valid) { 126 rasterizer->InitializeBuffer(); 127 128 // Use compressor to calculate delta row size 129 DeltaRowCompressor* deltaRowCompressor = NULL; 130 if (_SupportsDeltaRowCompression()) { 131 deltaRowCompressor = 132 new DeltaRowCompressor(rasterizer->GetOutRowSize(), 0); 133 if (deltaRowCompressor->InitCheck() != B_OK) { 134 delete deltaRowCompressor; 135 return false; 136 } 137 } 138 auto_ptr<DeltaRowCompressor>_deltaRowCompressor(deltaRowCompressor); 139 int deltaRowSize = 0; 140 141 // remember position 142 int xPage = rasterizer->GetX(); 143 int yPage = rasterizer->GetY(); 144 145 while (rasterizer->HasNextLine()) { 146 const uchar* rowBuffer = 147 static_cast<const uchar*>(rasterizer->RasterizeNextLine()); 148 149 if (deltaRowCompressor != NULL) { 150 int size = 151 deltaRowCompressor->CalculateSize(rowBuffer, true); 152 deltaRowSize += size + 2; 153 // two bytes for the row byte count 154 } 155 } 156 157 y = rasterizer->GetY(); 158 159 uchar* outBuffer = rasterizer->GetOutBuffer(); 160 int outBufferSize = rasterizer->GetOutBufferSize(); 161 int outRowSize = rasterizer->GetOutRowSize(); 162 int width = rasterizer->GetWidth(); 163 int height = rasterizer->GetHeight(); 164 _WriteBitmap(outBuffer, outBufferSize, outRowSize, xPage, yPage, 165 width, height, deltaRowSize); 166 } 167 168 if (y >= GetPageHeight()) { 169 offset->x = -1.0; 170 offset->y = -1.0; 171 } else { 172 offset->y += bitmap->Bounds().IntegerHeight()+1; 173 } 174 175 return true; 176 } 177 catch (TransportException& err) { 178 BAlert* alert = new BAlert("", err.What(), "OK"); 179 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 180 alert->Go(); 181 return false; 182 } 183} 184 185 186void 187PCL6Driver::_WriteBitmap(const uchar* buffer, int outSize, int rowSize, int x, 188 int y, int width, int height, int deltaRowSize) 189{ 190 // choose the best compression method 191 PCL6Writer::Compression compressionMethod = PCL6Writer::kNoCompression; 192 int dataSize = outSize; 193 194#if ENABLE_DELTA_ROW_COMPRESSION 195 if (_SupportsDeltaRowCompression() && deltaRowSize < dataSize) { 196 compressionMethod = PCL6Writer::kDeltaRowCompression; 197 dataSize = deltaRowSize; 198 } 199#endif 200 201#if ENABLE_RLE_COMPRESSION 202 if (_SupportsRLECompression()) { 203 int rleSize = pack_bits_size(buffer, outSize); 204 if (rleSize < dataSize) { 205 compressionMethod = PCL6Writer::kRLECompression; 206 dataSize = rleSize; 207 } 208 } 209#endif 210 211 // write bitmap 212 _Move(x, y); 213 214 _StartRasterGraphics(x, y, width, height, compressionMethod); 215 216 _RasterGraphics(buffer, outSize, dataSize, rowSize, height, 217 compressionMethod); 218 219 _EndRasterGraphics(); 220 221#if DISPLAY_COMPRESSION_STATISTICS 222 fprintf(stderr, "Out Size %d %2.2f\n", (int)outSize, 100.0); 223#if ENABLE_RLE_COMPRESSION 224 fprintf(stderr, "RLE Size %d %2.2f\n", (int)rleSize, 225 100.0 * rleSize / outSize); 226#endif 227#if ENABLE_DELTA_ROW_COMPRESSION 228 fprintf(stderr, "Delta Row Size %d %2.2f\n", (int)deltaRowSize, 229 100.0 * deltaRowSize / outSize); 230#endif 231 fprintf(stderr, "Data Size %d %2.2f\n", (int)dataSize, 232 100.0 * dataSize / outSize); 233#endif 234} 235 236 237void 238PCL6Driver::_JobStart() 239{ 240 // PCL6 begin 241 fWriter = new PCL6Writer(this); 242 PCL6Writer::ProtocolClass pc = 243 (PCL6Writer::ProtocolClass)GetProtocolClass(); 244 fWriter->PJLHeader(pc, GetJobData()->GetXres(), 245 "Copyright (c) 2003 - 2010 Haiku"); 246 fWriter->BeginSession(GetJobData()->GetXres(), GetJobData()->GetYres(), 247 PCL6Writer::kInch, PCL6Writer::kBackChAndErrPage); 248 fWriter->OpenDataSource(); 249 fMediaSide = PCL6Writer::kFrontMediaSide; 250} 251 252 253bool 254PCL6Driver::StartPage(int) 255{ 256 PCL6Writer::Orientation orientation = PCL6Writer::kPortrait; 257 if (GetJobData()->GetOrientation() == JobData::kLandscape) { 258 orientation = PCL6Writer::kLandscape; 259 } 260 261 PCL6Writer::MediaSize mediaSize = 262 _MediaSize(GetJobData()->GetPaper()); 263 PCL6Writer::MediaSource mediaSource = 264 _MediaSource(GetJobData()->GetPaperSource()); 265 if (GetJobData()->GetPrintStyle() == JobData::kSimplex) { 266 fWriter->BeginPage(orientation, mediaSize, mediaSource); 267 } else if (GetJobData()->GetPrintStyle() == JobData::kDuplex) { 268 // TODO move duplex binding option to UI 269 fWriter->BeginPage(orientation, mediaSize, mediaSource, 270 PCL6Writer::kDuplexVerticalBinding, fMediaSide); 271 272 if (fMediaSide == PCL6Writer::kFrontMediaSide) 273 fMediaSide = PCL6Writer::kBackMediaSide; 274 else 275 fMediaSide = PCL6Writer::kFrontMediaSide; 276 } else 277 return false; 278 279 // PageOrigin from Windows NT printer driver 280 int x = 142 * GetJobData()->GetXres() / 600; 281 int y = 100 * GetJobData()->GetYres() / 600; 282 fWriter->SetPageOrigin(x, y); 283 fWriter->SetColorSpace(_UseColorMode() ? PCL6Writer::kRGB 284 : PCL6Writer::kGray); 285 fWriter->SetPaintTxMode(PCL6Writer::kOpaque); 286 fWriter->SetSourceTxMode(PCL6Writer::kOpaque); 287 fWriter->SetROP(204); 288 return true; 289} 290 291 292void 293PCL6Driver::_StartRasterGraphics(int x, int y, int width, int height, 294 PCL6Writer::Compression compressionMethod) 295{ 296 PCL6Writer::ColorDepth colorDepth; 297 if (_UseColorMode()) { 298 #if COLOR_DEPTH == 8 299 colorDepth = PCL6Writer::k8Bit; 300 #elif COLOR_DEPTH == 1 301 colorDepth = PCL6Writer::k1Bit; 302 #else 303 #error COLOR_DEPTH must be either 1 or 8! 304 #endif 305 } else 306 colorDepth = PCL6Writer::k1Bit; 307 308 fWriter->BeginImage(PCL6Writer::kDirectPixel, colorDepth, width, height, 309 width, height); 310 fWriter->ReadImage(compressionMethod, 0, height); 311} 312 313 314void 315PCL6Driver::_EndRasterGraphics() 316{ 317 fWriter->EndImage(); 318} 319 320 321void 322PCL6Driver::_RasterGraphics(const uchar* buffer, int bufferSize, int dataSize, 323 int rowSize, int height, int compressionMethod) 324{ 325 // write bitmap byte size 326 fWriter->EmbeddedDataPrefix32(dataSize); 327 328 // write data 329 if (compressionMethod == PCL6Writer::kRLECompression) { 330 // use RLE compression 331 uchar* outBuffer = new uchar[dataSize]; 332 pack_bits(outBuffer, buffer, bufferSize); 333 fWriter->Append(outBuffer, dataSize); 334 delete[] outBuffer; 335 return; 336 } else if (compressionMethod == PCL6Writer::kDeltaRowCompression) { 337 // use delta row compression 338 DeltaRowStreamCompressor compressor(rowSize, 0, fWriter); 339 if (compressor.InitCheck() != B_OK) { 340 return; 341 } 342 343 const uint8* row = buffer; 344 for (int i = 0; i < height; i ++) { 345 // write row byte count 346 int32 size = compressor.CalculateSize(row); 347 fWriter->Append((uint16)size); 348 349 if (size > 0) { 350 // write delta row 351 compressor.Compress(row); 352 } 353 354 row += rowSize; 355 } 356 } else { 357 // write raw data 358 fWriter->Append(buffer, bufferSize); 359 } 360} 361 362 363bool 364PCL6Driver::EndPage(int) 365{ 366 try { 367 fWriter->EndPage(GetJobData()->GetCopies()); 368 return true; 369 } 370 catch (TransportException& err) { 371 return false; 372 } 373} 374 375 376void 377PCL6Driver::_JobEnd() 378{ 379 fWriter->CloseDataSource(); 380 fWriter->EndSession(); 381 fWriter->PJLFooter(); 382 fWriter->Flush(); 383 delete fWriter; 384 fWriter = NULL; 385} 386 387 388void 389PCL6Driver::_Move(int x, int y) 390{ 391 fWriter->SetCursor(x, y); 392} 393 394 395bool 396PCL6Driver::_SupportsRLECompression() 397{ 398 return GetJobData()->GetColor() != JobData::kColorCompressionDisabled; 399} 400 401 402bool 403PCL6Driver::_SupportsDeltaRowCompression() 404{ 405 return GetProtocolClass() >= PCL6Writer::kProtocolClass2_1 406 && GetJobData()->GetColor() != JobData::kColorCompressionDisabled; 407} 408 409 410bool 411PCL6Driver::_UseColorMode() 412{ 413 return GetJobData()->GetColor() != JobData::kMonochrome; 414} 415 416 417PCL6Writer::MediaSize 418PCL6Driver::_MediaSize(JobData::Paper paper) 419{ 420 switch (paper) { 421 case JobData::kLetter: 422 return PCL6Writer::kLetterPaper; 423 case JobData::kLegal: 424 return PCL6Writer::kLegalPaper; 425 case JobData::kA4: 426 return PCL6Writer::kA4Paper; 427 case JobData::kExecutive: 428 return PCL6Writer::kExecPaper; 429 case JobData::kLedger: 430 return PCL6Writer::kLedgerPaper; 431 case JobData::kA3: 432 return PCL6Writer::kA3Paper; 433 case JobData::kB5: 434 return PCL6Writer::kB5Paper; 435 case JobData::kJapanesePostcard: 436 return PCL6Writer::kJPostcard; 437 case JobData::kA5: 438 return PCL6Writer::kA5Paper; 439 case JobData::kB4: 440 return PCL6Writer::kJB4Paper; 441/* 442 case : return PCL6Writer::kCOM10Envelope; 443 case : return PCL6Writer::kMonarchEnvelope; 444 case : return PCL6Writer::kC5Envelope; 445 case : return PCL6Writer::kDLEnvelope; 446 case : return PCL6Writer::kJB4Paper; 447 case : return PCL6Writer::kJB5Paper; 448 case : return PCL6Writer::kB5Envelope; 449 case : return PCL6Writer::kJPostcard; 450 case : return PCL6Writer::kJDoublePostcard; 451 case : return PCL6Writer::kA5Paper; 452 case : return PCL6Writer::kA6Paper; 453 case : return PCL6Writer::kJB6Paper; 454 case : return PCL6Writer::kJIS8KPaper; 455 case : return PCL6Writer::kJIS16KPaper; 456 case : return PCL6Writer::kJISExecPaper; 457*/ 458 default: 459 return PCL6Writer::kLegalPaper; 460 } 461} 462 463 464PCL6Writer::MediaSource 465PCL6Driver::_MediaSource(JobData::PaperSource source) 466{ 467 switch (source) { 468 case JobData::kAuto: 469 return PCL6Writer::kAutoSelect; 470 case JobData::kCassette1: 471 return PCL6Writer::kDefaultSource; 472 case JobData::kCassette2: 473 return PCL6Writer::kEnvelopeTray; 474 case JobData::kLower: 475 return PCL6Writer::kLowerCassette; 476 case JobData::kUpper: 477 return PCL6Writer::kUpperCassette; 478 case JobData::kMiddle: 479 return PCL6Writer::kThirdCassette; 480 case JobData::kManual: 481 return PCL6Writer::kManualFeed; 482 case JobData::kCassette3: 483 return PCL6Writer::kMultiPurposeTray; 484 default: 485 return PCL6Writer::kAutoSelect; 486 } 487} 488