Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4680 | right-hear | 1 | /* |
2 | * pdfdraw -- command line tool for drawing pdf documents |
||
3 | */ |
||
4 | |||
5 | #include "fitz.h" |
||
6 | #include "mupdf.h" |
||
7 | |||
8 | #ifdef _MSC_VER |
||
9 | #include |
||
10 | #else |
||
11 | #include |
||
12 | #endif |
||
13 | |||
14 | char *output = NULL; |
||
15 | float resolution = 72; |
||
16 | float rotation = 0; |
||
17 | |||
18 | int showxml = 0; |
||
19 | int showtext = 0; |
||
20 | int showtime = 0; |
||
21 | int showmd5 = 0; |
||
22 | int savealpha = 0; |
||
23 | int uselist = 1; |
||
24 | int alphabits = 8; |
||
25 | float gamma_value = 1; |
||
26 | int invert = 0; |
||
27 | |||
28 | fz_colorspace *colorspace; |
||
29 | fz_glyph_cache *glyphcache; |
||
30 | char *filename; |
||
31 | |||
32 | struct { |
||
33 | int count, total; |
||
34 | int min, max; |
||
35 | int minpage, maxpage; |
||
36 | } timing; |
||
37 | |||
38 | static void die(fz_error error) |
||
39 | { |
||
40 | fz_catch(error, "aborting"); |
||
41 | exit(1); |
||
42 | } |
||
43 | |||
44 | static void usage(void) |
||
45 | { |
||
46 | fprintf(stderr, |
||
47 | "usage: pdfdraw [options] input.pdf [pages]\n" |
||
48 | "\t-o -\toutput filename (%%d for page number)\n" |
||
49 | "\t\tsupported formats: pgm, ppm, pam, png, pbm\n" |
||
50 | "\t-p -\tpassword\n" |
||
51 | "\t-r -\tresolution in dpi (default: 72)\n" |
||
52 | "\t-A\tdisable accelerated functions\n" |
||
53 | "\t-a\tsave alpha channel (only pam and png)\n" |
||
54 | "\t-b -\tnumber of bits of antialiasing (0 to 8)\n" |
||
55 | "\t-g\trender in grayscale\n" |
||
56 | "\t-m\tshow timing information\n" |
||
57 | "\t-t\tshow text (-tt for xml)\n" |
||
58 | "\t-x\tshow display list\n" |
||
59 | "\t-d\tdisable use of display list\n" |
||
60 | "\t-5\tshow md5 checksums\n" |
||
61 | "\t-R -\trotate clockwise by given number of degrees\n" |
||
62 | "\t-G gamma\tgamma correct output\n" |
||
63 | "\t-I\tinvert output\n" |
||
64 | "\tpages\tcomma separated list of ranges\n"); |
||
65 | exit(1); |
||
66 | } |
||
67 | |||
68 | static int gettime(void) |
||
69 | { |
||
70 | static struct timeval first; |
||
71 | static int once = 1; |
||
72 | struct timeval now; |
||
73 | if (once) |
||
74 | { |
||
75 | gettimeofday(&first, NULL); |
||
76 | once = 0; |
||
77 | } |
||
78 | gettimeofday(&now, NULL); |
||
79 | return (now.tv_sec - first.tv_sec) * 1000 + (now.tv_usec - first.tv_usec) / 1000; |
||
80 | } |
||
81 | |||
82 | static int isrange(char *s) |
||
83 | { |
||
84 | while (*s) |
||
85 | { |
||
86 | if ((*s < '0' || *s > '9') && *s != '-' && *s != ',') |
||
87 | return 0; |
||
88 | s++; |
||
89 | } |
||
90 | return 1; |
||
91 | } |
||
92 | |||
93 | static void drawpage(pdf_xref *xref, int pagenum) |
||
94 | { |
||
95 | fz_error error; |
||
96 | pdf_page *page; |
||
97 | fz_display_list *list; |
||
98 | fz_device *dev; |
||
99 | int start; |
||
100 | |||
101 | if (showtime) |
||
102 | { |
||
103 | start = gettime(); |
||
104 | } |
||
105 | |||
106 | error = pdf_load_page(&page, xref, pagenum - 1); |
||
107 | if (error) |
||
108 | die(fz_rethrow(error, "cannot load page %d in file '%s'", pagenum, filename)); |
||
109 | |||
110 | list = NULL; |
||
111 | |||
112 | if (uselist) |
||
113 | { |
||
114 | list = fz_new_display_list(); |
||
115 | dev = fz_new_list_device(list); |
||
116 | error = pdf_run_page(xref, page, dev, fz_identity); |
||
117 | if (error) |
||
118 | die(fz_rethrow(error, "cannot draw page %d in file '%s'", pagenum, filename)); |
||
119 | fz_free_device(dev); |
||
120 | } |
||
121 | |||
122 | if (showxml) |
||
123 | { |
||
124 | dev = fz_new_trace_device(); |
||
125 | printf(" |
||
126 | if (list) |
||
127 | fz_execute_display_list(list, dev, fz_identity, fz_infinite_bbox); |
||
128 | else |
||
129 | pdf_run_page(xref, page, dev, fz_identity); |
||
130 | printf("\n"); |
||
131 | fz_free_device(dev); |
||
132 | } |
||
133 | |||
134 | if (showtext) |
||
135 | { |
||
136 | fz_text_span *text = fz_new_text_span(); |
||
137 | dev = fz_new_text_device(text); |
||
138 | if (list) |
||
139 | fz_execute_display_list(list, dev, fz_identity, fz_infinite_bbox); |
||
140 | else |
||
141 | pdf_run_page(xref, page, dev, fz_identity); |
||
142 | fz_free_device(dev); |
||
143 | printf("[Page %d]\n", pagenum); |
||
144 | if (showtext > 1) |
||
145 | fz_debug_text_span_xml(text); |
||
146 | else |
||
147 | fz_debug_text_span(text); |
||
148 | printf("\n"); |
||
149 | fz_free_text_span(text); |
||
150 | } |
||
151 | |||
152 | if (showmd5 || showtime) |
||
153 | printf("page %s %d", filename, pagenum); |
||
154 | |||
155 | if (output || showmd5 || showtime) |
||
156 | { |
||
157 | float zoom; |
||
158 | fz_matrix ctm; |
||
159 | fz_bbox bbox; |
||
160 | fz_pixmap *pix; |
||
161 | |||
162 | zoom = resolution / 72; |
||
163 | ctm = fz_translate(0, -page->mediabox.y1); |
||
164 | ctm = fz_concat(ctm, fz_scale(zoom, -zoom)); |
||
165 | ctm = fz_concat(ctm, fz_rotate(page->rotate)); |
||
166 | ctm = fz_concat(ctm, fz_rotate(rotation)); |
||
167 | bbox = fz_round_rect(fz_transform_rect(ctm, page->mediabox)); |
||
168 | |||
169 | /* TODO: banded rendering and multi-page ppm */ |
||
170 | |||
171 | pix = fz_new_pixmap_with_rect(colorspace, bbox); |
||
172 | |||
173 | if (savealpha) |
||
174 | fz_clear_pixmap(pix); |
||
175 | else |
||
176 | fz_clear_pixmap_with_color(pix, 255); |
||
177 | |||
178 | dev = fz_new_draw_device(glyphcache, pix); |
||
179 | if (list) |
||
180 | fz_execute_display_list(list, dev, ctm, bbox); |
||
181 | else |
||
182 | pdf_run_page(xref, page, dev, ctm); |
||
183 | fz_free_device(dev); |
||
184 | |||
185 | if (invert) |
||
186 | fz_invert_pixmap(pix); |
||
187 | if (gamma_value != 1) |
||
188 | fz_gamma_pixmap(pix, gamma_value); |
||
189 | |||
190 | if (output) |
||
191 | { |
||
192 | char buf[512]; |
||
193 | sprintf(buf, output, pagenum); |
||
194 | if (strstr(output, ".pgm") || strstr(output, ".ppm") || strstr(output, ".pnm")) |
||
195 | fz_write_pnm(pix, buf); |
||
196 | else if (strstr(output, ".pam")) |
||
197 | fz_write_pam(pix, buf, savealpha); |
||
198 | else if (strstr(output, ".png")) |
||
199 | fz_write_png(pix, buf, savealpha); |
||
200 | else if (strstr(output, ".pbm")) { |
||
201 | fz_halftone *ht = fz_get_default_halftone(1); |
||
202 | fz_bitmap *bit = fz_halftone_pixmap(pix, ht); |
||
203 | fz_write_pbm(bit, buf); |
||
204 | fz_drop_bitmap(bit); |
||
205 | fz_drop_halftone(ht); |
||
206 | } |
||
207 | } |
||
208 | |||
209 | if (showmd5) |
||
210 | { |
||
211 | fz_md5 md5; |
||
212 | unsigned char digest[16]; |
||
213 | int i; |
||
214 | |||
215 | fz_md5_init(&md5); |
||
216 | fz_md5_update(&md5, pix->samples, pix->w * pix->h * pix->n); |
||
217 | fz_md5_final(&md5, digest); |
||
218 | |||
219 | printf(" "); |
||
220 | for (i = 0; i < 16; i++) |
||
221 | printf("%02x", digest[i]); |
||
222 | } |
||
223 | |||
224 | fz_drop_pixmap(pix); |
||
225 | } |
||
226 | |||
227 | if (list) |
||
228 | fz_free_display_list(list); |
||
229 | |||
230 | pdf_free_page(page); |
||
231 | |||
232 | if (showtime) |
||
233 | { |
||
234 | int end = gettime(); |
||
235 | int diff = end - start; |
||
236 | |||
237 | if (diff < timing.min) |
||
238 | { |
||
239 | timing.min = diff; |
||
240 | timing.minpage = pagenum; |
||
241 | } |
||
242 | if (diff > timing.max) |
||
243 | { |
||
244 | timing.max = diff; |
||
245 | timing.maxpage = pagenum; |
||
246 | } |
||
247 | timing.total += diff; |
||
248 | timing.count ++; |
||
249 | |||
250 | printf(" %dms", diff); |
||
251 | } |
||
252 | |||
253 | if (showmd5 || showtime) |
||
254 | printf("\n"); |
||
255 | |||
256 | pdf_age_store(xref->store, 3); |
||
257 | |||
258 | fz_flush_warnings(); |
||
259 | } |
||
260 | |||
261 | static void drawrange(pdf_xref *xref, char *range) |
||
262 | { |
||
263 | int page, spage, epage; |
||
264 | char *spec, *dash; |
||
265 | |||
266 | spec = fz_strsep(&range, ","); |
||
267 | while (spec) |
||
268 | { |
||
269 | dash = strchr(spec, '-'); |
||
270 | |||
271 | if (dash == spec) |
||
272 | spage = epage = pdf_count_pages(xref); |
||
273 | else |
||
274 | spage = epage = atoi(spec); |
||
275 | |||
276 | if (dash) |
||
277 | { |
||
278 | if (strlen(dash) > 1) |
||
279 | epage = atoi(dash + 1); |
||
280 | else |
||
281 | epage = pdf_count_pages(xref); |
||
282 | } |
||
283 | |||
284 | spage = CLAMP(spage, 1, pdf_count_pages(xref)); |
||
285 | epage = CLAMP(epage, 1, pdf_count_pages(xref)); |
||
286 | |||
287 | if (spage < epage) |
||
288 | for (page = spage; page <= epage; page++) |
||
289 | drawpage(xref, page); |
||
290 | else |
||
291 | for (page = spage; page >= epage; page--) |
||
292 | drawpage(xref, page); |
||
293 | |||
294 | spec = fz_strsep(&range, ","); |
||
295 | } |
||
296 | } |
||
297 | |||
298 | int main(int argc, char **argv) |
||
299 | { |
||
300 | char *password = ""; |
||
301 | int grayscale = 0; |
||
302 | int accelerate = 1; |
||
303 | pdf_xref *xref; |
||
304 | fz_error error; |
||
305 | int c; |
||
306 | |||
307 | while ((c = fz_getopt(argc, argv, "o:p:r:R:Aab:dgmtx5G:I")) != -1) |
||
308 | { |
||
309 | switch (c) |
||
310 | { |
||
311 | case 'o': output = fz_optarg; break; |
||
312 | case 'p': password = fz_optarg; break; |
||
313 | case 'r': resolution = atof(fz_optarg); break; |
||
314 | case 'R': rotation = atof(fz_optarg); break; |
||
315 | case 'A': accelerate = 0; break; |
||
316 | case 'a': savealpha = 1; break; |
||
317 | case 'b': alphabits = atoi(fz_optarg); break; |
||
318 | case 'm': showtime++; break; |
||
319 | case 't': showtext++; break; |
||
320 | case 'x': showxml++; break; |
||
321 | case '5': showmd5++; break; |
||
322 | case 'g': grayscale++; break; |
||
323 | case 'd': uselist = 0; break; |
||
324 | case 'G': gamma_value = atof(fz_optarg); break; |
||
325 | case 'I': invert++; break; |
||
326 | default: usage(); break; |
||
327 | } |
||
328 | } |
||
329 | |||
330 | fz_set_aa_level(alphabits); |
||
331 | |||
332 | if (fz_optind == argc) |
||
333 | usage(); |
||
334 | |||
335 | if (!showtext && !showxml && !showtime && !showmd5 && !output) |
||
336 | { |
||
337 | printf("nothing to do\n"); |
||
338 | exit(0); |
||
339 | } |
||
340 | |||
341 | if (accelerate) |
||
342 | fz_accelerate(); |
||
343 | |||
344 | glyphcache = fz_new_glyph_cache(); |
||
345 | |||
346 | colorspace = fz_device_rgb; |
||
347 | if (grayscale) |
||
348 | colorspace = fz_device_gray; |
||
349 | if (output && strstr(output, ".pgm")) |
||
350 | colorspace = fz_device_gray; |
||
351 | if (output && strstr(output, ".ppm")) |
||
352 | colorspace = fz_device_rgb; |
||
353 | if (output && strstr(output, ".pbm")) |
||
354 | colorspace = fz_device_gray; |
||
355 | |||
356 | timing.count = 0; |
||
357 | timing.total = 0; |
||
358 | timing.min = 1 << 30; |
||
359 | timing.max = 0; |
||
360 | timing.minpage = 0; |
||
361 | timing.maxpage = 0; |
||
362 | |||
363 | if (showxml) |
||
364 | printf("\n"); |
||
365 | |||
366 | while (fz_optind < argc) |
||
367 | { |
||
368 | filename = argv[fz_optind++]; |
||
369 | |||
370 | error = pdf_open_xref(&xref, filename, password); |
||
371 | if (error) |
||
372 | die(fz_rethrow(error, "cannot open document: %s", filename)); |
||
373 | |||
374 | error = pdf_load_page_tree(xref); |
||
375 | if (error) |
||
376 | die(fz_rethrow(error, "cannot load page tree: %s", filename)); |
||
377 | |||
378 | if (showxml) |
||
379 | printf(" |
||
380 | |||
381 | if (fz_optind == argc || !isrange(argv[fz_optind])) |
||
382 | drawrange(xref, "1-"); |
||
383 | if (fz_optind < argc && isrange(argv[fz_optind])) |
||
384 | drawrange(xref, argv[fz_optind++]); |
||
385 | |||
386 | if (showxml) |
||
387 | printf("\n"); |
||
388 | |||
389 | pdf_free_xref(xref); |
||
390 | } |
||
391 | |||
392 | if (showtime) |
||
393 | { |
||
394 | printf("total %dms / %d pages for an average of %dms\n", |
||
395 | timing.total, timing.count, timing.total / timing.count); |
||
396 | printf("fastest page %d: %dms\n", timing.minpage, timing.min); |
||
397 | printf("slowest page %d: %dms\n", timing.maxpage, timing.max); |
||
398 | } |
||
399 | |||
400 | fz_free_glyph_cache(glyphcache); |
||
401 | |||
402 | fz_flush_warnings(); |
||
403 | |||
404 | return 0; |
||
405 | }>>?xml>><>=>>>>> |