• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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