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