xref: /haiku/src/add-ons/print/drivers/postscript/PS.cpp (revision 97901ec593ec4dd50ac115c1c35a6d72f6e489a5)
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