1 /* 2 * PS.cpp 3 * Copyright 1999-2000 Y.Takagi. All Rights Reserved. 4 * Copyright 2003 Michael Pfeiffer. 5 * Copyright 2010 Ithamar Adema. 6 */ 7 8 9 #include "PS.h" 10 11 #include <memory.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 15 #include <Alert.h> 16 #include <Bitmap.h> 17 #include <File.h> 18 #include <Path.h> 19 20 #include "DbgMsg.h" 21 #include "FilterIO.h" 22 #include "Halftone.h" 23 #include "JobData.h" 24 #include "PackBits.h" 25 #include "PPDParser.h" 26 #include "PrinterData.h" 27 #include "PSCap.h" 28 #include "PSData.h" 29 #include "UIDriver.h" 30 #include "ValidRect.h" 31 32 #if (!__MWERKS__ || defined(MSIPL_USING_NAMESPACE)) 33 using namespace std; 34 #else 35 #define std 36 #endif 37 38 39 PSDriver::PSDriver(BMessage* msg, PrinterData* printer_data, 40 const PrinterCap* printer_cap) 41 : 42 GraphicsDriver(msg, printer_data, printer_cap) 43 { 44 fPrintedPages = 0; 45 fHalftone = NULL; 46 fFilterIO = NULL; 47 } 48 49 50 void 51 PSDriver::StartFilterIfNeeded() 52 { 53 const PSData* data = dynamic_cast<const PSData*>(getPrinterData()); 54 PPDParser parser(BPath(data->fPPD.String())); 55 if (parser.InitCheck() == B_OK) { 56 BString param = parser.GetParameter("FoomaticRIPCommandLine"); 57 char str[3] = "%?"; 58 // for now, we don't have any extra substitutions to do... 59 // (will be added once we support configuration options from the PPD) 60 for (str[1] = 'A'; str[1] <= 'Z'; str[1]++) 61 param.ReplaceAll(str, ""); 62 63 if (param.Length()) 64 fFilterIO = new FilterIO(param); 65 66 if (!fFilterIO || fFilterIO->InitCheck() != B_OK) { 67 delete fFilterIO; 68 fFilterIO = NULL; 69 } 70 } 71 } 72 73 74 void 75 PSDriver::FlushFilterIfNeeded() 76 { 77 if (fFilterIO) { 78 char buffer[1024]; 79 ssize_t len; 80 81 while ((len = fFilterIO->Read(buffer, sizeof(buffer))) > 0) 82 writeSpoolData(buffer, len); 83 } 84 } 85 86 87 void 88 PSDriver::writePSString(const char* format, ...) 89 { 90 char str[256]; 91 va_list ap; 92 va_start(ap, format); 93 vsprintf(str, format, ap); 94 95 if (fFilterIO) 96 fFilterIO->Write(str, strlen(str)); 97 else 98 writeSpoolData(str, strlen(str)); 99 100 va_end(ap); 101 } 102 103 104 void 105 PSDriver::writePSData(const void* data, size_t size) 106 { 107 if (fFilterIO) 108 fFilterIO->Write(data, size); 109 else 110 writeSpoolData(data, size); 111 } 112 113 114 bool 115 PSDriver::startDoc() 116 { 117 try { 118 StartFilterIfNeeded(); 119 120 jobStart(); 121 fHalftone = new Halftone(getJobData()->getSurfaceType(), 122 getJobData()->getGamma(), getJobData()->getInkDensity(), 123 getJobData()->getDitherType()); 124 return true; 125 } 126 catch (TransportException& err) { 127 return false; 128 } 129 } 130 131 132 bool 133 PSDriver::startPage(int page) 134 { 135 page ++; 136 writePSString("%%%%Page: %d %d\n", page, page); 137 writePSString("gsave\n"); 138 setupCTM(); 139 return true; 140 } 141 142 143 bool 144 PSDriver::endPage(int) 145 { 146 try { 147 fPrintedPages ++; 148 writePSString("grestore\n"); 149 writePSString("showpage\n"); 150 151 FlushFilterIfNeeded(); 152 153 return true; 154 } 155 catch (TransportException& err) { 156 return false; 157 } 158 } 159 160 161 void 162 PSDriver::setupCTM() 163 { 164 const float leftMargin = getJobData()->getPrintableRect().left; 165 const float topMargin = getJobData()->getPrintableRect().top; 166 if (getJobData()->getOrientation() == JobData::kPortrait) { 167 // move origin from bottom left to top left 168 // and set margin 169 writePSString("%f %f translate\n", leftMargin, 170 getJobData()->getPaperRect().Height()-topMargin); 171 } else { 172 // landscape: 173 // move origin from bottom left to margin top and left 174 // and rotate page contents 175 writePSString("%f %f translate\n", topMargin, leftMargin); 176 writePSString("90 rotate\n"); 177 } 178 // y values increase from top to bottom 179 // units of measure is dpi 180 writePSString("72 %d div 72 -%d div scale\n", getJobData()->getXres(), 181 getJobData()->getYres()); 182 } 183 184 185 bool 186 PSDriver::endDoc(bool) 187 { 188 try { 189 if (fHalftone) { 190 delete fHalftone; 191 } 192 jobEnd(); 193 return true; 194 } 195 catch (TransportException& err) { 196 return false; 197 } 198 } 199 200 201 inline uchar 202 hex_digit(uchar value) 203 { 204 if (value <= 9) return '0' + value; 205 else return 'a' + (value - 10); 206 } 207 208 209 bool 210 PSDriver::nextBand(BBitmap* bitmap, BPoint* offset) 211 { 212 DBGMSG(("> nextBand\n")); 213 214 try { 215 BRect bounds = bitmap->Bounds(); 216 217 RECT rc; 218 rc.left = (int)bounds.left; 219 rc.top = (int)bounds.top; 220 rc.right = (int)bounds.right; 221 rc.bottom = (int)bounds.bottom; 222 223 int height = rc.bottom - rc.top + 1; 224 225 int x = (int)offset->x; 226 int y = (int)offset->y; 227 228 int page_height = getPageHeight(); 229 230 if (y + height > page_height) { 231 height = page_height - y; 232 } 233 234 rc.bottom = height - 1; 235 236 DBGMSG(("height = %d\n", height)); 237 DBGMSG(("x = %d\n", x)); 238 DBGMSG(("y = %d\n", y)); 239 240 if (get_valid_rect(bitmap, &rc)) { 241 242 DBGMSG(("validate rect = %d, %d, %d, %d\n", 243 rc.left, rc.top, rc.right, rc.bottom)); 244 245 x = rc.left; 246 y += rc.top; 247 248 bool color = getJobData()->getColor() == JobData::kColor; 249 int width = rc.right - rc.left + 1; 250 int widthByte = (width + 7) / 8; 251 // byte boundary 252 int height = rc.bottom - rc.top + 1; 253 int in_size = color ? width : widthByte; 254 int out_size = color ? width * 6: widthByte * 2; 255 int delta = bitmap->BytesPerRow(); 256 257 DBGMSG(("width = %d\n", width)); 258 DBGMSG(("widthByte = %d\n", widthByte)); 259 DBGMSG(("height = %d\n", height)); 260 DBGMSG(("out_size = %d\n", out_size)); 261 DBGMSG(("delta = %d\n", delta)); 262 DBGMSG(("renderobj->get_pixel_depth() = %d\n", 263 fHalftone->getPixelDepth())); 264 265 uchar* ptr = static_cast<uchar*>(bitmap->Bits()) 266 + rc.top * delta 267 + (rc.left * fHalftone->getPixelDepth()) / 8; 268 269 int compression_method; 270 int compressed_size; 271 const uchar* buffer; 272 273 uchar* in_buffer = new uchar[in_size]; 274 // gray values 275 uchar* out_buffer = new uchar[out_size]; 276 // gray values in hexadecimal 277 278 auto_ptr<uchar> _in_buffer(in_buffer); 279 auto_ptr<uchar> _out_buffer(out_buffer); 280 281 DBGMSG(("move\n")); 282 283 int size = color ? width * 3 : in_size; 284 startRasterGraphics(x, y, width, height, size); 285 286 for (int i = rc.top; i <= rc.bottom; i++) { 287 if (color) { 288 uchar* out = out_buffer; 289 uchar* in = ptr; 290 for (int w = width; w > 0; w --) { 291 *out++ = hex_digit((in[2]) >> 4); 292 *out++ = hex_digit((in[2]) & 15); 293 *out++ = hex_digit((in[1]) >> 4); 294 *out++ = hex_digit((in[1]) & 15); 295 *out++ = hex_digit((in[0]) >> 4); 296 *out++ = hex_digit((in[0]) & 15); 297 in += 4; 298 } 299 } else { 300 fHalftone->dither(in_buffer, ptr, x, y, width); 301 302 uchar* in = in_buffer; 303 uchar* out = out_buffer; 304 305 for (int w = in_size; w > 0; w --, in ++) { 306 *in = ~*in; // invert pixels 307 *out++ = hex_digit((*in) >> 4); 308 *out++ = hex_digit((*in) & 15); 309 } 310 } 311 312 { 313 compression_method = 0; // uncompressed 314 buffer = out_buffer; 315 compressed_size = out_size; 316 } 317 318 rasterGraphics( 319 compression_method, 320 buffer, 321 compressed_size); 322 323 ptr += delta; 324 y++; 325 } 326 327 endRasterGraphics(); 328 329 } else 330 DBGMSG(("band bitmap is clean.\n")); 331 332 if (y >= page_height) { 333 offset->x = -1.0; 334 offset->y = -1.0; 335 } else 336 offset->y += height; 337 338 DBGMSG(("< nextBand\n")); 339 return true; 340 } 341 catch (TransportException& err) { 342 BAlert* alert = new BAlert("", err.what(), "OK"); 343 alert->Go(); 344 return false; 345 } 346 } 347 348 349 void 350 PSDriver::jobStart() 351 { 352 // PostScript header 353 writePSString("%%!PS-Adobe-3.0\n"); 354 writePSString("%%%%LanguageLevel: 1\n"); 355 writePSString("%%%%Title: %s\n", 356 getSpoolMetaData()->getDescription().c_str()); 357 writePSString("%%%%Creator: %s\n", 358 getSpoolMetaData()->getMimeType().c_str()); 359 writePSString("%%%%CreationDate: %s", 360 getSpoolMetaData()->getCreationTime().c_str()); 361 writePSString("%%%%DocumentMedia: Plain %d %d white 0 ( )\n", 362 getJobData()->getPaperRect().IntegerWidth(), 363 getJobData()->getPaperRect().IntegerHeight()); 364 writePSString("%%%%Pages: (atend)\n"); 365 writePSString("%%%%EndComments\n"); 366 367 writePSString("%%%%BeginDefaults\n"); 368 writePSString("%%%%PageMedia: Plain\n"); 369 writePSString("%%%%EndDefaults\n"); 370 } 371 372 373 void 374 PSDriver::startRasterGraphics(int x, int y, int width, int height, 375 int widthByte) 376 { 377 bool color = getJobData()->getColor() == JobData::kColor; 378 fCompressionMethod = -1; 379 writePSString("gsave\n"); 380 writePSString("/s %d string def\n", widthByte); 381 writePSString("%d %d translate\n", x, y); 382 writePSString("%d %d scale\n", width, height); 383 if (color) 384 writePSString("%d %d 8\n", width, height); // 8 bpp 385 else 386 writePSString("%d %d 1\n", width, height); // 1 bpp 387 writePSString("[%d 0 0 %d 0 0]\n", width, height); 388 writePSString("{ currentfile s readhexstring pop }\n"); 389 if (color) { 390 writePSString("false 3\n"); // single data source, 3 color components 391 writePSString("colorimage\n"); 392 } else 393 writePSString("image\n\n"); 394 } 395 396 397 void 398 PSDriver::endRasterGraphics() 399 { 400 writePSString("grestore\n"); 401 } 402 403 404 void 405 PSDriver::rasterGraphics( 406 int compression_method, 407 const uchar* buffer, 408 int size) 409 { 410 if (fCompressionMethod != compression_method) { 411 fCompressionMethod = compression_method; 412 } 413 writePSData(buffer, size); 414 writePSString("\n"); 415 } 416 417 418 void 419 PSDriver::jobEnd() 420 { 421 writePSString("%%%%Pages: %d\n", fPrintedPages); 422 writePSString("%%%%EOF\n"); 423 424 FlushFilterIfNeeded(); 425 } 426