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