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 <vector> 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 33 PSDriver::PSDriver(BMessage* message, PrinterData* printerData, 34 const PrinterCap* printerCap) 35 : 36 GraphicsDriver(message, printerData, printerCap), 37 fPrintedPages(0), 38 fCompressionMethod(0), 39 fHalftone(NULL), 40 fFilterIO(NULL) 41 { 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::StartDocument() 111 { 112 try { 113 _StartFilterIfNeeded(); 114 115 _JobStart(); 116 fHalftone = new Halftone(GetJobData()->GetSurfaceType(), 117 GetJobData()->GetGamma(), GetJobData()->GetInkDensity(), 118 GetJobData()->GetDitherType()); 119 return true; 120 } 121 catch (TransportException& err) { 122 return false; 123 } 124 } 125 126 127 bool 128 PSDriver::StartPage(int page) 129 { 130 page ++; 131 _WritePSString("%%%%Page: %d %d\n", page, page); 132 _WritePSString("gsave\n"); 133 _SetupCTM(); 134 return true; 135 } 136 137 138 bool 139 PSDriver::EndPage(int) 140 { 141 try { 142 fPrintedPages ++; 143 _WritePSString("grestore\n"); 144 _WritePSString("showpage\n"); 145 146 _FlushFilterIfNeeded(); 147 148 return true; 149 } 150 catch (TransportException& err) { 151 return false; 152 } 153 } 154 155 156 void 157 PSDriver::_SetupCTM() 158 { 159 const float leftMargin = GetJobData()->GetPrintableRect().left; 160 const float topMargin = GetJobData()->GetPrintableRect().top; 161 if (GetJobData()->GetOrientation() == JobData::kPortrait) { 162 // move origin from bottom left to top left 163 // and set margin 164 _WritePSString("%f %f translate\n", leftMargin, 165 GetJobData()->GetPaperRect().Height()-topMargin); 166 } else { 167 // landscape: 168 // move origin from bottom left to margin top and left 169 // and rotate page contents 170 _WritePSString("%f %f translate\n", topMargin, leftMargin); 171 _WritePSString("90 rotate\n"); 172 } 173 // y values increase from top to bottom 174 // units of measure is dpi 175 _WritePSString("72 %d div 72 -%d div scale\n", GetJobData()->GetXres(), 176 GetJobData()->GetYres()); 177 } 178 179 180 bool 181 PSDriver::EndDocument(bool) 182 { 183 try { 184 if (fHalftone) { 185 delete fHalftone; 186 fHalftone = NULL; 187 } 188 _JobEnd(); 189 return true; 190 } 191 catch (TransportException& err) { 192 return false; 193 } 194 } 195 196 197 static inline uchar 198 ToHexDigit(uchar value) 199 { 200 if (value <= 9) return '0' + value; 201 else return 'a' + (value - 10); 202 } 203 204 205 bool 206 PSDriver::NextBand(BBitmap* bitmap, BPoint* offset) 207 { 208 DBGMSG(("> nextBand\n")); 209 210 try { 211 BRect bounds = bitmap->Bounds(); 212 213 RECT rc; 214 rc.left = (int)bounds.left; 215 rc.top = (int)bounds.top; 216 rc.right = (int)bounds.right; 217 rc.bottom = (int)bounds.bottom; 218 219 int height = rc.bottom - rc.top + 1; 220 221 int x = (int)offset->x; 222 int y = (int)offset->y; 223 224 int page_height = GetPageHeight(); 225 226 if (y + height > page_height) { 227 height = page_height - y; 228 } 229 230 rc.bottom = height - 1; 231 232 DBGMSG(("height = %d\n", height)); 233 DBGMSG(("x = %d\n", x)); 234 DBGMSG(("y = %d\n", y)); 235 236 if (get_valid_rect(bitmap, &rc)) { 237 238 DBGMSG(("validate rect = %d, %d, %d, %d\n", 239 rc.left, rc.top, rc.right, rc.bottom)); 240 241 x = rc.left; 242 y += rc.top; 243 244 bool color = GetJobData()->GetColor() == JobData::kColor; 245 int width = rc.right - rc.left + 1; 246 int widthByte = (width + 7) / 8; 247 // byte boundary 248 int height = rc.bottom - rc.top + 1; 249 int in_size = color ? width : widthByte; 250 int out_size = color ? width * 6: widthByte * 2; 251 int delta = bitmap->BytesPerRow(); 252 253 DBGMSG(("width = %d\n", width)); 254 DBGMSG(("widthByte = %d\n", widthByte)); 255 DBGMSG(("height = %d\n", height)); 256 DBGMSG(("out_size = %d\n", out_size)); 257 DBGMSG(("delta = %d\n", delta)); 258 DBGMSG(("renderobj->Get_pixel_depth() = %d\n", 259 fHalftone->GetPixelDepth())); 260 261 uchar* ptr = static_cast<uchar*>(bitmap->Bits()) 262 + rc.top * delta 263 + (rc.left * fHalftone->GetPixelDepth()) / 8; 264 265 int compression_method; 266 int compressed_size; 267 const uchar* buffer; 268 269 std::vector<uchar> in_buffer(in_size); 270 // gray values 271 std::vector<uchar> out_buffer(out_size); 272 // gray values in hexadecimal 273 274 DBGMSG(("move\n")); 275 276 int size = color ? width * 3 : in_size; 277 _StartRasterGraphics(x, y, width, height, size); 278 279 for (int i = rc.top; i <= rc.bottom; i++) { 280 if (color) { 281 uchar* out = &out_buffer[0]; 282 uchar* in = ptr; 283 for (int w = width; w > 0; w --) { 284 *out++ = ToHexDigit((in[2]) >> 4); 285 *out++ = ToHexDigit((in[2]) & 15); 286 *out++ = ToHexDigit((in[1]) >> 4); 287 *out++ = ToHexDigit((in[1]) & 15); 288 *out++ = ToHexDigit((in[0]) >> 4); 289 *out++ = ToHexDigit((in[0]) & 15); 290 in += 4; 291 } 292 } else { 293 fHalftone->Dither(&in_buffer[0], ptr, x, y, width); 294 295 uchar* in = &in_buffer[0]; 296 uchar* out = &out_buffer[0]; 297 298 for (int w = in_size; w > 0; w --, in ++) { 299 *in = ~*in; // invert pixels 300 *out++ = ToHexDigit((*in) >> 4); 301 *out++ = ToHexDigit((*in) & 15); 302 } 303 } 304 305 { 306 compression_method = 0; // uncompressed 307 buffer = &out_buffer[0]; 308 compressed_size = out_size; 309 } 310 311 _RasterGraphics( 312 compression_method, 313 buffer, 314 compressed_size); 315 316 ptr += delta; 317 y++; 318 } 319 320 _EndRasterGraphics(); 321 322 } else 323 DBGMSG(("band bitmap is clean.\n")); 324 325 if (y >= page_height) { 326 offset->x = -1.0; 327 offset->y = -1.0; 328 } else 329 offset->y += height; 330 331 DBGMSG(("< nextBand\n")); 332 return true; 333 } 334 catch (TransportException& err) { 335 BAlert* alert = new BAlert("", err.What(), "OK"); 336 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 337 alert->Go(); 338 return false; 339 } 340 } 341 342 343 void 344 PSDriver::_JobStart() 345 { 346 // PostScript header 347 _WritePSString("%%!PS-Adobe-3.0\n"); 348 _WritePSString("%%%%LanguageLevel: 1\n"); 349 _WritePSString("%%%%Title: %s\n", 350 GetSpoolMetaData()->GetDescription().c_str()); 351 _WritePSString("%%%%Creator: %s\n", 352 GetSpoolMetaData()->GetMimeType().c_str()); 353 _WritePSString("%%%%CreationDate: %s", 354 GetSpoolMetaData()->GetCreationTime().c_str()); 355 _WritePSString("%%%%DocumentMedia: Plain %d %d white 0 ( )\n", 356 GetJobData()->GetPaperRect().IntegerWidth(), 357 GetJobData()->GetPaperRect().IntegerHeight()); 358 _WritePSString("%%%%Pages: (atend)\n"); 359 _WritePSString("%%%%EndComments\n"); 360 361 _WritePSString("%%%%BeginDefaults\n"); 362 _WritePSString("%%%%PageMedia: Plain\n"); 363 _WritePSString("%%%%EndDefaults\n"); 364 } 365 366 367 void 368 PSDriver::_StartRasterGraphics(int x, int y, int width, int height, 369 int widthByte) 370 { 371 bool color = GetJobData()->GetColor() == JobData::kColor; 372 fCompressionMethod = -1; 373 _WritePSString("gsave\n"); 374 _WritePSString("/s %d string def\n", widthByte); 375 _WritePSString("%d %d translate\n", x, y); 376 _WritePSString("%d %d scale\n", width, height); 377 if (color) 378 _WritePSString("%d %d 8\n", width, height); // 8 bpp 379 else 380 _WritePSString("%d %d 1\n", width, height); // 1 bpp 381 _WritePSString("[%d 0 0 %d 0 0]\n", width, height); 382 _WritePSString("{ currentfile s readhexstring pop }\n"); 383 if (color) { 384 _WritePSString("false 3\n"); // single data source, 3 color components 385 _WritePSString("colorimage\n"); 386 } else 387 _WritePSString("image\n\n"); 388 } 389 390 391 void 392 PSDriver::_EndRasterGraphics() 393 { 394 _WritePSString("grestore\n"); 395 } 396 397 398 void 399 PSDriver::_RasterGraphics(int compression_method, const uchar* buffer, 400 int size) 401 { 402 if (fCompressionMethod != compression_method) { 403 fCompressionMethod = compression_method; 404 } 405 _WritePSData(buffer, size); 406 _WritePSString("\n"); 407 } 408 409 410 void 411 PSDriver::_JobEnd() 412 { 413 _WritePSString("%%%%Pages: %d\n", fPrintedPages); 414 _WritePSString("%%%%EOF\n"); 415 416 _FlushFilterIfNeeded(); 417 } 418