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
PSDriver(BMessage * message,PrinterData * printerData,const PrinterCap * printerCap)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
_StartFilterIfNeeded()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
_FlushFilterIfNeeded()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
_WritePSString(const char * format,...)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
_WritePSData(const void * data,size_t size)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
StartDocument()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
StartPage(int page)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
EndPage(int)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
_SetupCTM()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
EndDocument(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
ToHexDigit(uchar value)198 ToHexDigit(uchar value)
199 {
200 if (value <= 9) return '0' + value;
201 else return 'a' + (value - 10);
202 }
203
204
205 bool
NextBand(BBitmap * bitmap,BPoint * offset)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
_JobStart()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
_StartRasterGraphics(int x,int y,int width,int height,int widthByte)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
_EndRasterGraphics()392 PSDriver::_EndRasterGraphics()
393 {
394 _WritePSString("grestore\n");
395 }
396
397
398 void
_RasterGraphics(int compression_method,const uchar * buffer,int size)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
_JobEnd()411 PSDriver::_JobEnd()
412 {
413 _WritePSString("%%%%Pages: %d\n", fPrintedPages);
414 _WritePSString("%%%%EOF\n");
415
416 _FlushFilterIfNeeded();
417 }
418