1 /**
2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation, either version 3 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 * @brief Decode URF to a PDF file
16 * @file urftopdf.cpp
17 * @author Neil 'Superna' Armstrong <superna9999@gmail.com> (C) 2010
18 * @author Tobias Hoffmann <smilingthax@gmail.com> (c) 2012
19 */
20
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdint.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <limits>
30 #include <errno.h>
31
32 #include <arpa/inet.h> // ntohl
33
34 #include <vector>
35 #include <qpdf/QPDF.hh>
36 #include <qpdf/QPDFWriter.hh>
37 #include <qpdf/QUtil.hh>
38
39 #include <qpdf/Pl_Flate.hh>
40 #include <qpdf/Pl_Buffer.hh>
41
42 #include "unirast.h"
43
44 #define DEFAULT_PDF_UNIT 72 // 1/72 inch
45
46 #define PROGRAM "urftopdf"
47
48 #ifdef URF_DEBUG
49 #define dprintf(format, ...) fprintf(stderr, "DEBUG: (" PROGRAM ") " format, __VA_ARGS__)
50 #else
51 #define dprintf(format, ...)
52 #endif
53
54 #define iprintf(format, ...) fprintf(stderr, "INFO: (" PROGRAM ") " format, __VA_ARGS__)
55
die(const char * str)56 void die(const char * str)
57 {
58 fprintf(stderr, "CRIT: (" PROGRAM ") die(%s) [%s]\n", str, strerror(errno));
59 exit(1);
60 }
61
62 //------------- PDF ---------------
63
64 struct pdf_info
65 {
pdf_infopdf_info66 pdf_info()
67 : pagecount(0),
68 width(0),height(0),
69 pixel_bytes(0),line_bytes(0),
70 bpp(0),
71 page_width(0),page_height(0)
72 {
73 }
74
75 QPDF pdf;
76 QPDFObjectHandle page;
77 unsigned pagecount;
78 unsigned width;
79 unsigned height;
80 unsigned pixel_bytes;
81 unsigned line_bytes;
82 unsigned bpp;
83 PointerHolder<Buffer> page_data;
84 double page_width,page_height;
85 };
86
create_pdf_file(struct pdf_info * info,unsigned pagecount)87 int create_pdf_file(struct pdf_info * info, unsigned pagecount)
88 {
89 try {
90 info->pdf.emptyPDF();
91 } catch (...) {
92 return 1;
93 }
94
95 info->pagecount = pagecount;
96
97 return 0;
98 }
99
makeBox(double x1,double y1,double x2,double y2)100 QPDFObjectHandle makeBox(double x1, double y1, double x2, double y2)
101 {
102 QPDFObjectHandle ret=QPDFObjectHandle::newArray();
103 ret.appendItem(QPDFObjectHandle::newReal(x1));
104 ret.appendItem(QPDFObjectHandle::newReal(y1));
105 ret.appendItem(QPDFObjectHandle::newReal(x2));
106 ret.appendItem(QPDFObjectHandle::newReal(y2));
107 return ret;
108 }
109
110 enum ColorSpace {
111 DEVICE_GRAY,
112 DEVICE_RGB,
113 DEVICE_CMYK
114 };
115
116 #define PRE_COMPRESS
117 /* or temporarily store images?
118 if(cupsTempFile2(tempfile_name, 255) == NULL) die("Unable to create a temporary pdf file");
119 iprintf("Created temporary file '%s'\n", tempfile_name);
120 */
121
makeImage(QPDF & pdf,PointerHolder<Buffer> page_data,unsigned width,unsigned height,ColorSpace cs,unsigned bpc)122 QPDFObjectHandle makeImage(QPDF &pdf, PointerHolder<Buffer> page_data, unsigned width, unsigned height, ColorSpace cs, unsigned bpc)
123 {
124 QPDFObjectHandle ret = QPDFObjectHandle::newStream(&pdf);
125
126 std::map<std::string,QPDFObjectHandle> dict;
127
128 dict["/Type"]=QPDFObjectHandle::newName("/XObject");
129 dict["/Subtype"]=QPDFObjectHandle::newName("/Image");
130 dict["/Width"]=QPDFObjectHandle::newInteger(width);
131 dict["/Height"]=QPDFObjectHandle::newInteger(height);
132 dict["/BitsPerComponent"]=QPDFObjectHandle::newInteger(bpc);
133
134 if (cs==DEVICE_GRAY) {
135 dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceGray");
136 } else if (cs==DEVICE_RGB) {
137 dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceRGB");
138 } else if (cs==DEVICE_CMYK) {
139 dict["/ColorSpace"]=QPDFObjectHandle::newName("/DeviceCMYK");
140 } else {
141 return QPDFObjectHandle();
142 }
143
144 ret.replaceDict(QPDFObjectHandle::newDictionary(dict));
145
146 #ifdef PRE_COMPRESS
147 // we deliver already compressed content (instead of letting QPDFWriter do it), to avoid using excessive memory
148 Pl_Buffer psink("psink");
149 Pl_Flate pflate("pflate",&psink,Pl_Flate::a_deflate);
150
151 pflate.write(page_data->getBuffer(),page_data->getSize());
152 pflate.finish();
153
154 // /Filter /FlateDecode
155 // /DecodeParms [<</Predictor 1 /Colors 1[3] /BitsPerComponent $bits /Columns $x>>] ??
156 ret.replaceStreamData(PointerHolder<Buffer>(psink.getBuffer()),
157 QPDFObjectHandle::newName("/FlateDecode"),QPDFObjectHandle::newNull());
158 #else
159 ret.replaceStreamData(page_data,QPDFObjectHandle::newNull(),QPDFObjectHandle::newNull());
160 #endif
161
162 return ret;
163 }
164
finish_page(struct pdf_info * info)165 void finish_page(struct pdf_info * info)
166 {
167 //Finish previous Page
168 if(!info->page_data.getPointer())
169 return;
170
171 QPDFObjectHandle image = makeImage(info->pdf, info->page_data, info->width, info->height, DEVICE_RGB, 8);
172 if(!image.isInitialized()) die("Unable to load image data");
173
174 // add it
175 info->page.getKey("/Resources").getKey("/XObject").replaceKey("/I",image);
176
177 // draw it
178 std::string content;
179 content.append(QUtil::double_to_string(info->page_width) + " 0 0 " +
180 QUtil::double_to_string(info->page_height) + " 0 0 cm\n");
181 content.append("/I Do\n");
182 info->page.getKey("/Contents").replaceStreamData(content,QPDFObjectHandle::newNull(),QPDFObjectHandle::newNull());
183
184 // bookkeeping
185 info->page_data = PointerHolder<Buffer>();
186 }
187
add_pdf_page(struct pdf_info * info,int pagen,unsigned width,unsigned height,int bpp,unsigned dpi)188 int add_pdf_page(struct pdf_info * info, int pagen, unsigned width, unsigned height, int bpp, unsigned dpi)
189 {
190 try {
191 finish_page(info); // any active
192
193 info->width = width;
194 info->height = height;
195 info->pixel_bytes = bpp/8;
196 info->line_bytes = (width*info->pixel_bytes);
197 info->bpp = bpp;
198
199 if (info->height > (std::numeric_limits<unsigned>::max() / info->line_bytes)) {
200 die("Page too big");
201 }
202 info->page_data = PointerHolder<Buffer>(new Buffer(info->line_bytes*info->height));
203
204 QPDFObjectHandle page = QPDFObjectHandle::parse(
205 "<<"
206 " /Type /Page"
207 " /Resources <<"
208 " /XObject << >> "
209 " >>"
210 " /MediaBox null "
211 " /Contents null "
212 ">>");
213 page.replaceKey("/Contents",QPDFObjectHandle::newStream(&info->pdf)); // data will be provided later
214
215 // Convert to pdf units
216 info->page_width=((double)info->width/dpi)*DEFAULT_PDF_UNIT;
217 info->page_height=((double)info->height/dpi)*DEFAULT_PDF_UNIT;
218 page.replaceKey("/MediaBox",makeBox(0,0,info->page_width,info->page_height));
219
220 info->page = info->pdf.makeIndirectObject(page); // we want to keep a reference
221 info->pdf.addPage(info->page, false);
222 } catch (std::bad_alloc &ex) {
223 die("Unable to allocate page data");
224 } catch (...) {
225 return 1;
226 }
227
228 return 0;
229 }
230
close_pdf_file(struct pdf_info * info)231 int close_pdf_file(struct pdf_info * info)
232 {
233 try {
234 finish_page(info); // any active
235
236 QPDFWriter output(info->pdf,NULL);
237 output.write();
238 } catch (...) {
239 return 1;
240 }
241
242 return 0;
243 }
244
pdf_set_line(struct pdf_info * info,unsigned line_n,uint8_t line[])245 void pdf_set_line(struct pdf_info * info, unsigned line_n, uint8_t line[])
246 {
247 dprintf("pdf_set_line(%d)\n", line_n);
248
249 if(line_n > info->height)
250 {
251 dprintf("Bad line %d\n", line_n);
252 return;
253 }
254
255 memcpy((info->page_data->getBuffer()+(line_n*info->line_bytes)), line, info->line_bytes);
256 }
257
258 // Data are in network endianness
259 struct urf_file_header {
260 char unirast[8];
261 uint32_t page_count;
262 } __attribute__((__packed__));
263
264 struct urf_page_header {
265 uint8_t bpp;
266 uint8_t colorspace;
267 uint8_t duplex;
268 uint8_t quality;
269 uint32_t unknown0;
270 uint32_t unknown1;
271 uint32_t width;
272 uint32_t height;
273 uint32_t dot_per_inch;
274 uint32_t unknown2;
275 uint32_t unknown3;
276 } __attribute__((__packed__));
277
decode_raster(int fd,unsigned width,unsigned height,int bpp,struct pdf_info * info)278 int decode_raster(int fd, unsigned width, unsigned height, int bpp, struct pdf_info * info)
279 {
280 // We should be at raster start
281 int i, j;
282 unsigned cur_line = 0;
283 unsigned pos = 0;
284 uint8_t line_repeat_byte = 0;
285 unsigned line_repeat = 0;
286 int8_t packbit_code = 0;
287 int pixel_size = (bpp/8);
288 std::vector<uint8_t> pixel_container;
289 std::vector<uint8_t> line_container;
290
291 if (width > (std::numeric_limits<unsigned>::max() / pixel_size)) {
292 die("Line too big");
293 }
294 try {
295 pixel_container.resize(pixel_size);
296 line_container.resize(pixel_size*width);
297 } catch (...) {
298 die("Unable to allocate temporary storage");
299 }
300
301 do
302 {
303 if(read(fd, &line_repeat_byte, 1) < 1)
304 {
305 dprintf("l%06d : line_repeat EOF at %lu\n", cur_line, lseek(fd, 0, SEEK_CUR));
306 return 1;
307 }
308
309 line_repeat = (unsigned)line_repeat_byte + 1;
310
311 dprintf("l%06d : next actions for %d lines\n", cur_line, line_repeat);
312
313 // Start of line
314 pos = 0;
315
316 do
317 {
318 if(read(fd, &packbit_code, 1) < 1)
319 {
320 dprintf("p%06dl%06d : packbit_code EOF at %lu\n", pos, cur_line, lseek(fd, 0, SEEK_CUR));
321 return 1;
322 }
323
324 dprintf("p%06dl%06d: Raster code %02X='%d'.\n", pos, cur_line, (uint8_t)packbit_code, packbit_code);
325
326 if(packbit_code == -128)
327 {
328 dprintf("\tp%06dl%06d : blank rest of line.\n", pos, cur_line);
329 memset((&line_container[pos*pixel_size]), 0xFF, (pixel_size*(width-pos)));
330 pos = width;
331 break;
332 }
333 else if(packbit_code >= 0 && packbit_code <= 127)
334 {
335 int n = (packbit_code+1);
336
337 //Read pixel
338 if(read(fd, &pixel_container[0], pixel_size) < pixel_size)
339 {
340 dprintf("p%06dl%06d : pixel repeat EOF at %lu\n", pos, cur_line, lseek(fd, 0, SEEK_CUR));
341 return 1;
342 }
343
344 dprintf("\tp%06dl%06d : Repeat pixel '", pos, cur_line);
345 for(j = 0 ; j < pixel_size ; ++j)
346 dprintf("%02X ", pixel_container[j]);
347 dprintf("' for %d times.\n", n);
348
349 for(i = 0 ; i < n ; ++i)
350 {
351 //for(j = pixel_size-1 ; j >= 0 ; --j)
352 for(j = 0 ; j < pixel_size ; ++j)
353 line_container[pixel_size*pos + j] = pixel_container[j];
354 ++pos;
355 if(pos >= width)
356 break;
357 }
358
359 if(i < n && pos >= width)
360 {
361 dprintf("\tp%06dl%06d : Forced end of line for pixel repeat.\n", pos, cur_line);
362 }
363
364 if(pos >= width)
365 break;
366 }
367 else if(packbit_code > -128 && packbit_code < 0)
368 {
369 int n = (-(int)packbit_code)+1;
370
371 dprintf("\tp%06dl%06d : Copy %d verbatim pixels.\n", pos, cur_line, n);
372
373 for(i = 0 ; i < n ; ++i)
374 {
375 if(read(fd, &pixel_container[0], pixel_size) < pixel_size)
376 {
377 dprintf("p%06dl%06d : literal_pixel EOF at %lu\n", pos, cur_line, lseek(fd, 0, SEEK_CUR));
378 return 1;
379 }
380 //Invert pixels, should be programmable
381 for(j = 0 ; j < pixel_size ; ++j)
382 line_container[pixel_size*pos + j] = pixel_container[j];
383 ++pos;
384 if(pos >= width)
385 break;
386 }
387
388 if(i < n && pos >= width)
389 {
390 dprintf("\tp%06dl%06d : Forced end of line for pixel copy.\n", pos, cur_line);
391 }
392
393 if(pos >= width)
394 break;
395 }
396 }
397 while(pos < width);
398
399 dprintf("\tl%06d : End Of line, drawing %d times.\n", cur_line, line_repeat);
400
401 // write lines
402 for(i = 0 ; i < (int)line_repeat ; ++i)
403 {
404 pdf_set_line(info, cur_line, &line_container[0]);
405 ++cur_line;
406 }
407 }
408 while(cur_line < height);
409
410 return 0;
411 }
412
main(int argc,char ** argv)413 int main(int argc, char **argv)
414 {
415 int fd, page;
416 struct urf_file_header head, head_orig;
417 struct urf_page_header page_header, page_header_orig;
418 struct pdf_info pdf;
419
420 FILE * input = NULL;
421
422 if(argc < 6)
423 {
424 fprintf(stderr, "Usage: %s <job> <user> <job name> <copies> <option> [file]\n", argv[0]);
425 return 1;
426 }
427
428 if(argc > 6)
429 {
430 input = fopen(argv[6], "rb");
431 if(input == NULL) die("Unable to open unirast file");
432 }
433 else
434 input = stdin;
435
436 // Get fd from file
437 fd = fileno(input);
438
439 if(read(fd, &head_orig, sizeof(head)) == -1) die("Unable to read file header");
440
441 //Transform
442 memcpy(head.unirast, head_orig.unirast, sizeof(head.unirast));
443 head.page_count = ntohl(head_orig.page_count);
444
445 if(head.unirast[7])
446 head.unirast[7] = 0;
447
448 if(strncmp(head.unirast, "UNIRAST", 7) != 0) die("Bad File Header");
449
450 iprintf("%s file, with %d page(s).\n", head.unirast, head.page_count);
451
452 if(create_pdf_file(&pdf, head.page_count) != 0) die("Unable to create PDF file");
453
454 for(page = 0 ; page < (int)head.page_count ; ++page)
455 {
456 if(read(fd, &page_header_orig, sizeof(page_header_orig)) == -1) die("Unable to read page header");
457
458 //Transform
459 page_header.bpp = page_header_orig.bpp;
460 page_header.colorspace = page_header_orig.colorspace;
461 page_header.duplex = page_header_orig.duplex;
462 page_header.quality = page_header_orig.quality;
463 page_header.unknown0 = 0;
464 page_header.unknown1 = 0;
465 page_header.width = ntohl(page_header_orig.width);
466 page_header.height = ntohl(page_header_orig.height);
467 page_header.dot_per_inch = ntohl(page_header_orig.dot_per_inch);
468 page_header.unknown2 = 0;
469 page_header.unknown3 = 0;
470
471 iprintf("Page %d :\n", page);
472 iprintf("Bits Per Pixel : %d\n", page_header.bpp);
473 iprintf("Colorspace : %d\n", page_header.colorspace);
474 iprintf("Duplex Mode : %d\n", page_header.duplex);
475 iprintf("Quality : %d\n", page_header.quality);
476 iprintf("Size : %dx%d pixels\n", page_header.width, page_header.height);
477 iprintf("Dots per Inches : %d\n", page_header.dot_per_inch);
478
479 if(page_header.colorspace != UNIRAST_COLOR_SPACE_SRGB_24BIT_1)
480 {
481 die("Invalid ColorSpace, only RGB 24BIT type 1 is supported");
482 }
483
484 if(page_header.bpp != UNIRAST_BPP_24BIT)
485 {
486 die("Invalid Bit Per Pixel value, only 24bit is supported");
487 }
488
489 if(add_pdf_page(&pdf, page, page_header.width, page_header.height, page_header.bpp, page_header.dot_per_inch) != 0) die("Unable to create PDF file");
490
491 if(decode_raster(fd, page_header.width, page_header.height, page_header.bpp, &pdf) != 0)
492 die("Failed to decode Page");
493 }
494
495 close_pdf_file(&pdf); // will output to stdout
496
497 return 0;
498 }
499