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