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 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 uchar* in_buffer = new uchar[in_size]; 270 // gray values 271 uchar* out_buffer = new uchar[out_size]; 272 // gray values in hexadecimal 273 274 auto_ptr<uchar> _in_buffer(in_buffer); 275 auto_ptr<uchar> _out_buffer(out_buffer); 276 277 DBGMSG(("move\n")); 278 279 int size = color ? width * 3 : in_size; 280 _StartRasterGraphics(x, y, width, height, size); 281 282 for (int i = rc.top; i <= rc.bottom; i++) { 283 if (color) { 284 uchar* out = out_buffer; 285 uchar* in = ptr; 286 for (int w = width; w > 0; w --) { 287 *out++ = ToHexDigit((in[2]) >> 4); 288 *out++ = ToHexDigit((in[2]) & 15); 289 *out++ = ToHexDigit((in[1]) >> 4); 290 *out++ = ToHexDigit((in[1]) & 15); 291 *out++ = ToHexDigit((in[0]) >> 4); 292 *out++ = ToHexDigit((in[0]) & 15); 293 in += 4; 294 } 295 } else { 296 fHalftone->Dither(in_buffer, ptr, x, y, width); 297 298 uchar* in = in_buffer; 299 uchar* out = out_buffer; 300 301 for (int w = in_size; w > 0; w --, in ++) { 302 *in = ~*in; // invert pixels 303 *out++ = ToHexDigit((*in) >> 4); 304 *out++ = ToHexDigit((*in) & 15); 305 } 306 } 307 308 { 309 compression_method = 0; // uncompressed 310 buffer = out_buffer; 311 compressed_size = out_size; 312 } 313 314 _RasterGraphics( 315 compression_method, 316 buffer, 317 compressed_size); 318 319 ptr += delta; 320 y++; 321 } 322 323 _EndRasterGraphics(); 324 325 } else 326 DBGMSG(("band bitmap is clean.\n")); 327 328 if (y >= page_height) { 329 offset->x = -1.0; 330 offset->y = -1.0; 331 } else 332 offset->y += height; 333 334 DBGMSG(("< nextBand\n")); 335 return true; 336 } 337 catch (TransportException& err) { 338 BAlert* alert = new BAlert("", err.What(), "OK"); 339 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 340 alert->Go(); 341 return false; 342 } 343 } 344 345 346 void 347 PSDriver::_JobStart() 348 { 349 // PostScript header 350 _WritePSString("%%!PS-Adobe-3.0\n"); 351 _WritePSString("%%%%LanguageLevel: 1\n"); 352 _WritePSString("%%%%Title: %s\n", 353 GetSpoolMetaData()->GetDescription().c_str()); 354 _WritePSString("%%%%Creator: %s\n", 355 GetSpoolMetaData()->GetMimeType().c_str()); 356 _WritePSString("%%%%CreationDate: %s", 357 GetSpoolMetaData()->GetCreationTime().c_str()); 358 _WritePSString("%%%%DocumentMedia: Plain %d %d white 0 ( )\n", 359 GetJobData()->GetPaperRect().IntegerWidth(), 360 GetJobData()->GetPaperRect().IntegerHeight()); 361 _WritePSString("%%%%Pages: (atend)\n"); 362 _WritePSString("%%%%EndComments\n"); 363 364 _WritePSString("%%%%BeginDefaults\n"); 365 _WritePSString("%%%%PageMedia: Plain\n"); 366 _WritePSString("%%%%EndDefaults\n"); 367 } 368 369 370 void 371 PSDriver::_StartRasterGraphics(int x, int y, int width, int height, 372 int widthByte) 373 { 374 bool color = GetJobData()->GetColor() == JobData::kColor; 375 fCompressionMethod = -1; 376 _WritePSString("gsave\n"); 377 _WritePSString("/s %d string def\n", widthByte); 378 _WritePSString("%d %d translate\n", x, y); 379 _WritePSString("%d %d scale\n", width, height); 380 if (color) 381 _WritePSString("%d %d 8\n", width, height); // 8 bpp 382 else 383 _WritePSString("%d %d 1\n", width, height); // 1 bpp 384 _WritePSString("[%d 0 0 %d 0 0]\n", width, height); 385 _WritePSString("{ currentfile s readhexstring pop }\n"); 386 if (color) { 387 _WritePSString("false 3\n"); // single data source, 3 color components 388 _WritePSString("colorimage\n"); 389 } else 390 _WritePSString("image\n\n"); 391 } 392 393 394 void 395 PSDriver::_EndRasterGraphics() 396 { 397 _WritePSString("grestore\n"); 398 } 399 400 401 void 402 PSDriver::_RasterGraphics(int compression_method, const uchar* buffer, 403 int size) 404 { 405 if (fCompressionMethod != compression_method) { 406 fCompressionMethod = compression_method; 407 } 408 _WritePSData(buffer, size); 409 _WritePSString("\n"); 410 } 411 412 413 void 414 PSDriver::_JobEnd() 415 { 416 _WritePSString("%%%%Pages: %d\n", fPrintedPages); 417 _WritePSString("%%%%EOF\n"); 418 419 _FlushFilterIfNeeded(); 420 } 421