Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7639 views
1
/*
2
* mudraw -- command line tool for drawing and converting documents
3
*/
4
5
#include "mupdf/fitz.h"
6
#include "mupdf/pdf.h" /* for pdf output */
7
8
#ifdef _MSC_VER
9
#include <winsock2.h>
10
#define main main_utf8
11
#else
12
#include <sys/time.h>
13
#endif
14
15
enum {
16
OUT_NONE,
17
OUT_PNG, OUT_TGA, OUT_PNM, OUT_PGM, OUT_PPM, OUT_PAM,
18
OUT_PBM, OUT_PWG, OUT_PCL,
19
OUT_TEXT, OUT_HTML, OUT_STEXT,
20
OUT_TRACE, OUT_SVG, OUT_PDF,
21
};
22
23
enum { CS_INVALID, CS_UNSET, CS_MONO, CS_GRAY, CS_GRAY_ALPHA, CS_RGB, CS_RGB_ALPHA, CS_CMYK, CS_CMYK_ALPHA };
24
25
typedef struct
26
{
27
char *suffix;
28
int format;
29
} suffix_t;
30
31
static const suffix_t suffix_table[] =
32
{
33
{ ".png", OUT_PNG },
34
{ ".pgm", OUT_PGM },
35
{ ".ppm", OUT_PPM },
36
{ ".pnm", OUT_PNM },
37
{ ".pam", OUT_PAM },
38
{ ".pbm", OUT_PBM },
39
{ ".svg", OUT_SVG },
40
{ ".pwg", OUT_PWG },
41
{ ".pcl", OUT_PCL },
42
{ ".pdf", OUT_PDF },
43
{ ".tga", OUT_TGA },
44
45
{ ".txt", OUT_TEXT },
46
{ ".text", OUT_TEXT },
47
{ ".html", OUT_HTML },
48
{ ".stext", OUT_STEXT },
49
50
{ ".trace", OUT_TRACE },
51
};
52
53
typedef struct
54
{
55
char *name;
56
int colorspace;
57
} cs_name_t;
58
59
static const cs_name_t cs_name_table[] =
60
{
61
{ "m", CS_MONO },
62
{ "mono", CS_MONO },
63
{ "g", CS_GRAY },
64
{ "gray", CS_GRAY },
65
{ "grey", CS_GRAY },
66
{ "ga", CS_GRAY_ALPHA },
67
{ "grayalpha", CS_GRAY_ALPHA },
68
{ "greyalpha", CS_GRAY_ALPHA },
69
{ "rgb", CS_RGB },
70
{ "rgba", CS_RGB_ALPHA },
71
{ "rgbalpha", CS_RGB_ALPHA },
72
{ "cmyk", CS_CMYK },
73
{ "cmyka", CS_CMYK_ALPHA },
74
{ "cmykalpha", CS_CMYK_ALPHA },
75
};
76
77
typedef struct
78
{
79
int format;
80
int default_cs;
81
int permitted_cs[6];
82
} format_cs_table_t;
83
84
static const format_cs_table_t format_cs_table[] =
85
{
86
{ OUT_PNG, CS_RGB, { CS_GRAY, CS_GRAY_ALPHA, CS_RGB, CS_RGB_ALPHA } },
87
{ OUT_PPM, CS_RGB, { CS_GRAY, CS_RGB } },
88
{ OUT_PNM, CS_GRAY, { CS_GRAY, CS_RGB } },
89
{ OUT_PAM, CS_RGB_ALPHA, { CS_GRAY, CS_GRAY_ALPHA, CS_RGB, CS_RGB_ALPHA, CS_CMYK, CS_CMYK_ALPHA } },
90
{ OUT_PGM, CS_GRAY, { CS_GRAY, CS_RGB } },
91
{ OUT_PBM, CS_MONO, { CS_MONO } },
92
{ OUT_PWG, CS_RGB, { CS_MONO, CS_GRAY, CS_RGB, CS_CMYK } },
93
{ OUT_PCL, CS_MONO, { CS_MONO } },
94
{ OUT_TGA, CS_RGB, { CS_GRAY, CS_GRAY_ALPHA, CS_RGB, CS_RGB_ALPHA } },
95
96
{ OUT_TRACE, CS_RGB, { CS_RGB } },
97
{ OUT_SVG, CS_RGB, { CS_RGB } },
98
{ OUT_PDF, CS_RGB, { CS_RGB } },
99
100
{ OUT_TEXT, CS_RGB, { CS_RGB } },
101
{ OUT_HTML, CS_RGB, { CS_RGB } },
102
{ OUT_STEXT, CS_RGB, { CS_RGB } },
103
};
104
105
static char *output = NULL;
106
static char *format = NULL;
107
static int output_format = OUT_NONE;
108
109
static float rotation = 0;
110
static float resolution = 72;
111
static int res_specified = 0;
112
static int width = 0;
113
static int height = 0;
114
static int fit = 0;
115
116
static float layout_w = 450;
117
static float layout_h = 600;
118
static float layout_em = 12;
119
120
static int showfeatures = 0;
121
static int showtime = 0;
122
static size_t memtrace_current = 0;
123
static size_t memtrace_peak = 0;
124
static size_t memtrace_total = 0;
125
static int showmemory = 0;
126
static int showmd5 = 0;
127
128
static pdf_document *pdfout = NULL;
129
130
static int ignore_errors = 0;
131
static int uselist = 1;
132
static int alphabits = 8;
133
134
static int out_cs = CS_UNSET;
135
static float gamma_value = 1;
136
static int invert = 0;
137
static int bandheight = 0;
138
139
static int errored = 0;
140
static int append = 0;
141
static fz_text_sheet *sheet = NULL;
142
static fz_colorspace *colorspace;
143
static char *filename;
144
static int files = 0;
145
fz_output *out = NULL;
146
147
static struct {
148
int count, total;
149
int min, max;
150
int minpage, maxpage;
151
char *minfilename;
152
char *maxfilename;
153
} timing;
154
155
static void usage(void)
156
{
157
fprintf(stderr,
158
"mudraw version " FZ_VERSION "\n"
159
"Usage: mudraw [options] file [pages]\n"
160
"\t-p -\tpassword\n"
161
"\n"
162
"\t-o -\toutput file name (%%d for page number)\n"
163
"\t-F -\toutput format (default inferred from output file name)\n"
164
"\t\traster: png, tga, pnm, pam, pbm, pwg, pcl\n"
165
"\t\tvector: svg, pdf, trace\n"
166
"\t\ttext: txt, html, stext\n"
167
"\n"
168
"\t-s -\tshow extra information:\n"
169
"\t\tm - show memory use\n"
170
"\t\tt - show timings\n"
171
"\t\tf - show page features\n"
172
"\t\t5 - show md5 checksum of rendered image\n"
173
"\n"
174
"\t-R -\trotate clockwise (default: 0 degrees)\n"
175
"\t-r -\tresolution in dpi (default: 72)\n"
176
"\t-w -\twidth (in pixels) (maximum width if -r is specified)\n"
177
"\t-h -\theight (in pixels) (maximum height if -r is specified)\n"
178
"\t-f -\tfit width and/or height exactly; ignore original aspect ratio\n"
179
"\t-B -\tmaximum bandheight (pgm, ppm, pam, png output only)\n"
180
"\n"
181
"\t-W -\tpage width for EPUB layout\n"
182
"\t-H -\tpage height for EPUB layout\n"
183
"\t-S -\tfont size for EPUB layout\n"
184
"\n"
185
"\t-c -\tcolorspace (mono, gray, grayalpha, rgb, rgba, cmyk, cmykalpha)\n"
186
"\t-G -\tapply gamma correction\n"
187
"\t-I\tinvert colors\n"
188
"\n"
189
"\t-A -\tnumber of bits of antialiasing (0 to 8)\n"
190
"\t-D\tdisable use of display list\n"
191
"\t-i\tignore errors\n"
192
"\n"
193
"\tpages\tcomma separated list of page numbers and ranges\n"
194
);
195
exit(1);
196
}
197
198
static int gettime(void)
199
{
200
static struct timeval first;
201
static int once = 1;
202
struct timeval now;
203
if (once)
204
{
205
gettimeofday(&first, NULL);
206
once = 0;
207
}
208
gettimeofday(&now, NULL);
209
return (now.tv_sec - first.tv_sec) * 1000 + (now.tv_usec - first.tv_usec) / 1000;
210
}
211
212
static int isrange(char *s)
213
{
214
while (*s)
215
{
216
if ((*s < '0' || *s > '9') && *s != '-' && *s != ',')
217
return 0;
218
s++;
219
}
220
return 1;
221
}
222
223
static int has_percent_d(char *s)
224
{
225
/* find '%[0-9]*d' */
226
while (*s)
227
{
228
if (*s++ == '%')
229
{
230
while (*s >= '0' && *s <= '9')
231
++s;
232
if (*s == 'd')
233
return 1;
234
}
235
}
236
return 0;
237
}
238
239
static void drawpage(fz_context *ctx, fz_document *doc, int pagenum)
240
{
241
fz_page *page;
242
fz_display_list *list = NULL;
243
fz_device *dev = NULL;
244
int start;
245
fz_cookie cookie = { 0 };
246
247
fz_var(list);
248
fz_var(dev);
249
250
if (showtime)
251
start = gettime();
252
253
fz_try(ctx)
254
page = fz_load_page(ctx, doc, pagenum - 1);
255
fz_catch(ctx)
256
fz_rethrow_message(ctx, "cannot load page %d in file '%s'", pagenum, filename);
257
258
if (showmd5 || showtime || showfeatures)
259
printf("page %s %d", filename, pagenum);
260
261
if (uselist)
262
{
263
fz_try(ctx)
264
{
265
list = fz_new_display_list(ctx);
266
dev = fz_new_list_device(ctx, list);
267
fz_run_page(ctx, page, dev, &fz_identity, &cookie);
268
}
269
fz_always(ctx)
270
{
271
fz_drop_device(ctx, dev);
272
dev = NULL;
273
}
274
fz_catch(ctx)
275
{
276
fz_drop_display_list(ctx, list);
277
fz_drop_page(ctx, page);
278
fz_rethrow_message(ctx, "cannot draw page %d in file '%s'", pagenum, filename);
279
}
280
}
281
282
if (showfeatures)
283
{
284
int iscolor;
285
dev = fz_new_test_device(ctx, &iscolor, 0.02f);
286
fz_try(ctx)
287
{
288
if (list)
289
fz_run_display_list(ctx, list, dev, &fz_identity, &fz_infinite_rect, NULL);
290
else
291
fz_run_page(ctx, page, dev, &fz_identity, &cookie);
292
}
293
fz_always(ctx)
294
{
295
fz_drop_device(ctx, dev);
296
dev = NULL;
297
}
298
fz_catch(ctx)
299
{
300
fz_rethrow(ctx);
301
}
302
printf(" %s", iscolor ? "color" : "grayscale");
303
}
304
305
if (output_format == OUT_TRACE)
306
{
307
fz_try(ctx)
308
{
309
dev = fz_new_trace_device(ctx);
310
if (list)
311
fz_run_display_list(ctx, list, dev, &fz_identity, &fz_infinite_rect, &cookie);
312
else
313
fz_run_page(ctx, page, dev, &fz_identity, &cookie);
314
}
315
fz_always(ctx)
316
{
317
fz_drop_device(ctx, dev);
318
dev = NULL;
319
}
320
fz_catch(ctx)
321
{
322
fz_drop_display_list(ctx, list);
323
fz_drop_page(ctx, page);
324
fz_rethrow(ctx);
325
}
326
}
327
328
else if (output_format == OUT_TEXT || output_format == OUT_HTML || output_format == OUT_STEXT)
329
{
330
fz_text_page *text = NULL;
331
332
fz_var(text);
333
334
fz_try(ctx)
335
{
336
text = fz_new_text_page(ctx);
337
dev = fz_new_text_device(ctx, sheet, text);
338
if (output_format == OUT_HTML)
339
fz_disable_device_hints(ctx, dev, FZ_IGNORE_IMAGE);
340
if (list)
341
fz_run_display_list(ctx, list, dev, &fz_identity, &fz_infinite_rect, &cookie);
342
else
343
fz_run_page(ctx, page, dev, &fz_identity, &cookie);
344
fz_drop_device(ctx, dev);
345
dev = NULL;
346
if (output_format == OUT_STEXT)
347
{
348
fz_print_text_page_xml(ctx, out, text);
349
}
350
else if (output_format == OUT_HTML)
351
{
352
fz_analyze_text(ctx, sheet, text);
353
fz_print_text_page_html(ctx, out, text);
354
}
355
else if (output_format == OUT_TEXT)
356
{
357
fz_print_text_page(ctx, out, text);
358
fz_printf(ctx, out, "\f\n");
359
}
360
}
361
fz_always(ctx)
362
{
363
fz_drop_device(ctx, dev);
364
dev = NULL;
365
fz_drop_text_page(ctx, text);
366
}
367
fz_catch(ctx)
368
{
369
fz_drop_display_list(ctx, list);
370
fz_drop_page(ctx, page);
371
fz_rethrow(ctx);
372
}
373
}
374
375
else if (output_format == OUT_PDF)
376
{
377
fz_matrix ctm;
378
fz_rect bounds, tbounds;
379
pdf_page *newpage;
380
381
fz_bound_page(ctx, page, &bounds);
382
fz_rotate(&ctm, rotation);
383
tbounds = bounds;
384
fz_transform_rect(&tbounds, &ctm);
385
386
newpage = pdf_create_page(ctx, pdfout, bounds, 72, 0);
387
388
fz_try(ctx)
389
{
390
dev = pdf_page_write(ctx, pdfout, newpage);
391
if (list)
392
fz_run_display_list(ctx, list, dev, &ctm, &tbounds, &cookie);
393
else
394
fz_run_page(ctx, page, dev, &ctm, &cookie);
395
fz_drop_device(ctx, dev);
396
dev = NULL;
397
}
398
fz_always(ctx)
399
{
400
fz_drop_device(ctx, dev);
401
dev = NULL;
402
}
403
fz_catch(ctx)
404
{
405
fz_drop_display_list(ctx, list);
406
fz_drop_page(ctx, page);
407
fz_rethrow(ctx);
408
}
409
pdf_insert_page(ctx, pdfout, newpage, INT_MAX);
410
fz_drop_page(ctx, &newpage->super);
411
}
412
413
else if (output_format == OUT_SVG)
414
{
415
float zoom;
416
fz_matrix ctm;
417
fz_rect bounds, tbounds;
418
char buf[512];
419
FILE *file;
420
fz_output *out;
421
422
if (!strcmp(output, "-"))
423
file = stdout;
424
else
425
{
426
sprintf(buf, output, pagenum);
427
file = fopen(buf, "wb");
428
if (file == NULL)
429
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open file '%s': %s", buf, strerror(errno));
430
}
431
432
out = fz_new_output_with_file(ctx, file, 0);
433
434
fz_bound_page(ctx, page, &bounds);
435
zoom = resolution / 72;
436
fz_pre_rotate(fz_scale(&ctm, zoom, zoom), rotation);
437
tbounds = bounds;
438
fz_transform_rect(&tbounds, &ctm);
439
440
fz_try(ctx)
441
{
442
dev = fz_new_svg_device(ctx, out, tbounds.x1-tbounds.x0, tbounds.y1-tbounds.y0);
443
if (list)
444
fz_run_display_list(ctx, list, dev, &ctm, &tbounds, &cookie);
445
else
446
fz_run_page(ctx, page, dev, &ctm, &cookie);
447
fz_drop_device(ctx, dev);
448
dev = NULL;
449
}
450
fz_always(ctx)
451
{
452
fz_drop_device(ctx, dev);
453
dev = NULL;
454
fz_drop_output(ctx, out);
455
if (file != stdout)
456
fclose(file);
457
}
458
fz_catch(ctx)
459
{
460
fz_drop_display_list(ctx, list);
461
fz_drop_page(ctx, page);
462
fz_rethrow(ctx);
463
}
464
}
465
466
else
467
{
468
float zoom;
469
fz_matrix ctm;
470
fz_rect bounds, tbounds;
471
fz_irect ibounds;
472
fz_pixmap *pix = NULL;
473
int w, h;
474
fz_output *output_file = NULL;
475
fz_png_output_context *poc = NULL;
476
477
fz_var(pix);
478
fz_var(poc);
479
480
fz_bound_page(ctx, page, &bounds);
481
zoom = resolution / 72;
482
fz_pre_scale(fz_rotate(&ctm, rotation), zoom, zoom);
483
tbounds = bounds;
484
fz_round_rect(&ibounds, fz_transform_rect(&tbounds, &ctm));
485
486
/* Make local copies of our width/height */
487
w = width;
488
h = height;
489
490
/* If a resolution is specified, check to see whether w/h are
491
* exceeded; if not, unset them. */
492
if (res_specified)
493
{
494
int t;
495
t = ibounds.x1 - ibounds.x0;
496
if (w && t <= w)
497
w = 0;
498
t = ibounds.y1 - ibounds.y0;
499
if (h && t <= h)
500
h = 0;
501
}
502
503
/* Now w or h will be 0 unless they need to be enforced. */
504
if (w || h)
505
{
506
float scalex = w / (tbounds.x1 - tbounds.x0);
507
float scaley = h / (tbounds.y1 - tbounds.y0);
508
fz_matrix scale_mat;
509
510
if (fit)
511
{
512
if (w == 0)
513
scalex = 1.0f;
514
if (h == 0)
515
scaley = 1.0f;
516
}
517
else
518
{
519
if (w == 0)
520
scalex = scaley;
521
if (h == 0)
522
scaley = scalex;
523
}
524
if (!fit)
525
{
526
if (scalex > scaley)
527
scalex = scaley;
528
else
529
scaley = scalex;
530
}
531
fz_scale(&scale_mat, scalex, scaley);
532
fz_concat(&ctm, &ctm, &scale_mat);
533
tbounds = bounds;
534
fz_transform_rect(&tbounds, &ctm);
535
}
536
fz_round_rect(&ibounds, &tbounds);
537
fz_rect_from_irect(&tbounds, &ibounds);
538
539
/* TODO: banded rendering and multi-page ppm */
540
fz_try(ctx)
541
{
542
int savealpha = (out_cs == CS_GRAY_ALPHA || out_cs == CS_RGB_ALPHA || out_cs == CS_CMYK_ALPHA);
543
fz_irect band_ibounds = ibounds;
544
int band, bands = 1;
545
char filename_buf[512];
546
int totalheight = ibounds.y1 - ibounds.y0;
547
int drawheight = totalheight;
548
549
if (bandheight != 0)
550
{
551
/* Banded rendering; we'll only render to a
552
* given height at a time. */
553
drawheight = bandheight;
554
if (totalheight > bandheight)
555
band_ibounds.y1 = band_ibounds.y0 + bandheight;
556
bands = (totalheight + bandheight-1)/bandheight;
557
tbounds.y1 = tbounds.y0 + bandheight + 2;
558
}
559
560
pix = fz_new_pixmap_with_bbox(ctx, colorspace, &band_ibounds);
561
fz_pixmap_set_resolution(pix, resolution);
562
563
if (output)
564
{
565
if (!strcmp(output, "-"))
566
output_file = fz_new_output_with_file(ctx, stdout, 0);
567
else
568
{
569
sprintf(filename_buf, output, pagenum);
570
output_file = fz_new_output_to_filename(ctx, filename_buf);
571
}
572
573
if (output_format == OUT_PGM || output_format == OUT_PPM || output_format == OUT_PNM)
574
fz_output_pnm_header(ctx, output_file, pix->w, totalheight, pix->n);
575
else if (output_format == OUT_PAM)
576
fz_output_pam_header(ctx, output_file, pix->w, totalheight, pix->n, savealpha);
577
else if (output_format == OUT_PNG)
578
poc = fz_output_png_header(ctx, output_file, pix->w, totalheight, pix->n, savealpha);
579
}
580
581
for (band = 0; band < bands; band++)
582
{
583
if (savealpha)
584
fz_clear_pixmap(ctx, pix);
585
else
586
fz_clear_pixmap_with_value(ctx, pix, 255);
587
588
dev = fz_new_draw_device(ctx, pix);
589
if (alphabits == 0)
590
fz_enable_device_hints(ctx, dev, FZ_DONT_INTERPOLATE_IMAGES);
591
if (list)
592
fz_run_display_list(ctx, list, dev, &ctm, &tbounds, &cookie);
593
else
594
fz_run_page(ctx, page, dev, &ctm, &cookie);
595
fz_drop_device(ctx, dev);
596
dev = NULL;
597
598
if (invert)
599
fz_invert_pixmap(ctx, pix);
600
if (gamma_value != 1)
601
fz_gamma_pixmap(ctx, pix, gamma_value);
602
603
if (savealpha)
604
fz_unmultiply_pixmap(ctx, pix);
605
606
if (output)
607
{
608
if (output_format == OUT_PGM || output_format == OUT_PPM || output_format == OUT_PNM)
609
fz_output_pnm_band(ctx, output_file, pix->w, totalheight, pix->n, band, drawheight, pix->samples);
610
else if (output_format == OUT_PAM)
611
fz_output_pam_band(ctx, output_file, pix->w, totalheight, pix->n, band, drawheight, pix->samples, savealpha);
612
else if (output_format == OUT_PNG)
613
fz_output_png_band(ctx, output_file, pix->w, totalheight, pix->n, band, drawheight, pix->samples, savealpha, poc);
614
else if (output_format == OUT_PWG)
615
{
616
if (has_percent_d(output))
617
append = 0;
618
if (out_cs == CS_MONO)
619
{
620
fz_bitmap *bit = fz_halftone_pixmap(ctx, pix, NULL);
621
fz_write_pwg_bitmap(ctx, bit, filename_buf, append, NULL);
622
fz_drop_bitmap(ctx, bit);
623
}
624
else
625
fz_write_pwg(ctx, pix, filename_buf, append, NULL);
626
append = 1;
627
}
628
else if (output_format == OUT_PCL)
629
{
630
fz_pcl_options options;
631
632
fz_pcl_preset(ctx, &options, "ljet4");
633
634
if (has_percent_d(output))
635
append = 0;
636
if (out_cs == CS_MONO)
637
{
638
fz_bitmap *bit = fz_halftone_pixmap(ctx, pix, NULL);
639
fz_write_pcl_bitmap(ctx, bit, filename_buf, append, &options);
640
fz_drop_bitmap(ctx, bit);
641
}
642
else
643
fz_write_pcl(ctx, pix, filename_buf, append, &options);
644
append = 1;
645
}
646
else if (output_format == OUT_PBM) {
647
fz_bitmap *bit = fz_halftone_pixmap(ctx, pix, NULL);
648
fz_write_pbm(ctx, bit, filename_buf);
649
fz_drop_bitmap(ctx, bit);
650
}
651
else if (output_format == OUT_TGA)
652
{
653
fz_write_tga(ctx, pix, filename_buf, savealpha);
654
}
655
}
656
ctm.f -= drawheight;
657
}
658
659
if (showmd5)
660
{
661
unsigned char digest[16];
662
int i;
663
664
fz_md5_pixmap(ctx, pix, digest);
665
printf(" ");
666
for (i = 0; i < 16; i++)
667
printf("%02x", digest[i]);
668
}
669
}
670
fz_always(ctx)
671
{
672
if (output)
673
{
674
if (output_format == OUT_PNG)
675
fz_output_png_trailer(ctx, output_file, poc);
676
}
677
678
fz_drop_device(ctx, dev);
679
dev = NULL;
680
fz_drop_pixmap(ctx, pix);
681
if (output_file)
682
fz_drop_output(ctx, output_file);
683
}
684
fz_catch(ctx)
685
{
686
fz_drop_display_list(ctx, list);
687
fz_drop_page(ctx, page);
688
fz_rethrow(ctx);
689
}
690
}
691
692
if (list)
693
fz_drop_display_list(ctx, list);
694
695
fz_drop_page(ctx, page);
696
697
if (showtime)
698
{
699
int end = gettime();
700
int diff = end - start;
701
702
if (diff < timing.min)
703
{
704
timing.min = diff;
705
timing.minpage = pagenum;
706
timing.minfilename = filename;
707
}
708
if (diff > timing.max)
709
{
710
timing.max = diff;
711
timing.maxpage = pagenum;
712
timing.maxfilename = filename;
713
}
714
timing.total += diff;
715
timing.count ++;
716
717
printf(" %dms", diff);
718
}
719
720
if (showmd5 || showtime || showfeatures)
721
printf("\n");
722
723
if (showmemory)
724
{
725
fz_dump_glyph_cache_stats(ctx);
726
}
727
728
fz_flush_warnings(ctx);
729
730
if (cookie.errors)
731
errored = 1;
732
}
733
734
static void drawrange(fz_context *ctx, fz_document *doc, char *range)
735
{
736
int page, spage, epage, pagecount;
737
char *spec, *dash;
738
739
pagecount = fz_count_pages(ctx, doc);
740
spec = fz_strsep(&range, ",");
741
while (spec)
742
{
743
dash = strchr(spec, '-');
744
745
if (dash == spec)
746
spage = epage = pagecount;
747
else
748
spage = epage = atoi(spec);
749
750
if (dash)
751
{
752
if (strlen(dash) > 1)
753
epage = atoi(dash + 1);
754
else
755
epage = pagecount;
756
}
757
758
spage = fz_clampi(spage, 1, pagecount);
759
epage = fz_clampi(epage, 1, pagecount);
760
761
if (spage < epage)
762
for (page = spage; page <= epage; page++)
763
drawpage(ctx, doc, page);
764
else
765
for (page = spage; page >= epage; page--)
766
drawpage(ctx, doc, page);
767
768
spec = fz_strsep(&range, ",");
769
}
770
}
771
772
static int
773
parse_colorspace(const char *name)
774
{
775
int i;
776
777
for (i = 0; i < nelem(cs_name_table); i++)
778
{
779
if (!strcmp(name, cs_name_table[i].name))
780
return cs_name_table[i].colorspace;
781
}
782
fprintf(stderr, "Unknown colorspace \"%s\"\n", name);
783
exit(1);
784
}
785
786
typedef struct
787
{
788
size_t size;
789
#if defined(_M_IA64) || defined(_M_AMD64)
790
size_t align;
791
#endif
792
} trace_header;
793
794
static void *
795
trace_malloc(void *arg, unsigned int size)
796
{
797
trace_header *p;
798
if (size == 0)
799
return NULL;
800
p = malloc(size + sizeof(trace_header));
801
if (p == NULL)
802
return NULL;
803
p[0].size = size;
804
memtrace_current += size;
805
memtrace_total += size;
806
if (memtrace_current > memtrace_peak)
807
memtrace_peak = memtrace_current;
808
return (void *)&p[1];
809
}
810
811
static void
812
trace_free(void *arg, void *p_)
813
{
814
trace_header *p = (trace_header *)p_;
815
816
if (p == NULL)
817
return;
818
memtrace_current -= p[-1].size;
819
free(&p[-1]);
820
}
821
822
static void *
823
trace_realloc(void *arg, void *p_, unsigned int size)
824
{
825
trace_header *p = (trace_header *)p_;
826
size_t oldsize;
827
828
if (size == 0)
829
{
830
trace_free(arg, p_);
831
return NULL;
832
}
833
if (p == NULL)
834
return trace_malloc(arg, size);
835
oldsize = p[-1].size;
836
p = realloc(&p[-1], size + sizeof(trace_header));
837
if (p == NULL)
838
return NULL;
839
memtrace_current += size - oldsize;
840
if (size > oldsize)
841
memtrace_total += size - oldsize;
842
if (memtrace_current > memtrace_peak)
843
memtrace_peak = memtrace_current;
844
p[0].size = size;
845
return &p[1];
846
}
847
848
int main(int argc, char **argv)
849
{
850
char *password = "";
851
fz_document *doc = NULL;
852
int c;
853
fz_context *ctx;
854
fz_alloc_context alloc_ctx = { NULL, trace_malloc, trace_realloc, trace_free };
855
856
fz_var(doc);
857
858
while ((c = fz_getopt(argc, argv, "po:F:R:r:w:h:fB:c:G:I:s:A:DiW:H:S:v")) != -1)
859
{
860
switch (c)
861
{
862
default: usage(); break;
863
864
case 'p': password = fz_optarg; break;
865
866
case 'o': output = fz_optarg; break;
867
case 'F': format = fz_optarg; break;
868
869
case 'R': rotation = atof(fz_optarg); break;
870
case 'r': resolution = atof(fz_optarg); res_specified = 1; break;
871
case 'w': width = atof(fz_optarg); break;
872
case 'h': height = atof(fz_optarg); break;
873
case 'f': fit = 1; break;
874
case 'B': bandheight = atoi(fz_optarg); break;
875
876
case 'c': out_cs = parse_colorspace(fz_optarg); break;
877
case 'G': gamma_value = atof(fz_optarg); break;
878
case 'I': invert++; break;
879
880
case 'W': layout_w = atof(fz_optarg); break;
881
case 'H': layout_h = atof(fz_optarg); break;
882
case 'S': layout_em = atof(fz_optarg); break;
883
884
case 's':
885
if (strchr(fz_optarg, 't')) ++showtime;
886
if (strchr(fz_optarg, 'm')) ++showmemory;
887
if (strchr(fz_optarg, 'f')) ++showfeatures;
888
if (strchr(fz_optarg, '5')) ++showmd5;
889
break;
890
891
case 'A': alphabits = atoi(fz_optarg); break;
892
case 'D': uselist = 0; break;
893
case 'i': ignore_errors = 1; break;
894
895
case 'v': fprintf(stderr, "mudraw version %s\n", FZ_VERSION); return 1;
896
}
897
}
898
899
if (fz_optind == argc)
900
usage();
901
902
ctx = fz_new_context((showmemory == 0 ? NULL : &alloc_ctx), NULL, FZ_STORE_DEFAULT);
903
if (!ctx)
904
{
905
fprintf(stderr, "cannot initialise context\n");
906
exit(1);
907
}
908
909
fz_set_aa_level(ctx, alphabits);
910
911
/* Determine output type */
912
if (bandheight < 0)
913
{
914
fprintf(stderr, "Bandheight must be > 0\n");
915
exit(1);
916
}
917
918
output_format = OUT_PNG;
919
if (format)
920
{
921
int i;
922
923
for (i = 0; i < nelem(suffix_table); i++)
924
{
925
if (!strcmp(format, suffix_table[i].suffix+1))
926
{
927
output_format = suffix_table[i].format;
928
break;
929
}
930
}
931
if (i == nelem(suffix_table))
932
{
933
fprintf(stderr, "Unknown output format '%s'\n", format);
934
exit(1);
935
}
936
}
937
else if (output)
938
{
939
char *suffix = output;
940
int i;
941
942
for (i = 0; i < nelem(suffix_table); i++)
943
{
944
char *s = strstr(suffix, suffix_table[i].suffix);
945
946
if (s != NULL)
947
{
948
suffix = s+1;
949
output_format = suffix_table[i].format;
950
i = 0;
951
}
952
}
953
}
954
955
if (bandheight)
956
{
957
if (output_format != OUT_PAM && output_format != OUT_PGM && output_format != OUT_PPM && output_format != OUT_PNM && output_format != OUT_PNG)
958
{
959
fprintf(stderr, "Banded operation only possible with PAM, PGM, PPM, PNM and PNG outputs\n");
960
exit(1);
961
}
962
if (showmd5)
963
{
964
fprintf(stderr, "Banded operation not compatible with MD5\n");
965
exit(1);
966
}
967
}
968
969
{
970
int i, j;
971
972
for (i = 0; i < nelem(format_cs_table); i++)
973
{
974
if (format_cs_table[i].format == output_format)
975
{
976
if (out_cs == CS_UNSET)
977
out_cs = format_cs_table[i].default_cs;
978
for (j = 0; j < nelem(format_cs_table[i].permitted_cs); j++)
979
{
980
if (format_cs_table[i].permitted_cs[j] == out_cs)
981
break;
982
}
983
if (j == nelem(format_cs_table[i].permitted_cs))
984
{
985
fprintf(stderr, "Unsupported colorspace for this format\n");
986
exit(1);
987
}
988
}
989
}
990
}
991
992
switch (out_cs)
993
{
994
case CS_MONO:
995
case CS_GRAY:
996
case CS_GRAY_ALPHA:
997
colorspace = fz_device_gray(ctx);
998
break;
999
case CS_RGB:
1000
case CS_RGB_ALPHA:
1001
colorspace = fz_device_rgb(ctx);
1002
break;
1003
case CS_CMYK:
1004
case CS_CMYK_ALPHA:
1005
colorspace = fz_device_cmyk(ctx);
1006
break;
1007
default:
1008
fprintf(stderr, "Unknown colorspace!\n");
1009
exit(1);
1010
break;
1011
}
1012
1013
if (output_format == OUT_PDF)
1014
{
1015
pdfout = pdf_create_document(ctx);
1016
}
1017
1018
timing.count = 0;
1019
timing.total = 0;
1020
timing.min = 1 << 30;
1021
timing.max = 0;
1022
timing.minpage = 0;
1023
timing.maxpage = 0;
1024
timing.minfilename = "";
1025
timing.maxfilename = "";
1026
1027
if (output_format == OUT_TEXT || output_format == OUT_HTML || output_format == OUT_STEXT || output_format == OUT_TRACE)
1028
out = fz_new_output_with_file(ctx, stdout, 0);
1029
1030
if (output_format == OUT_STEXT || output_format == OUT_TRACE)
1031
fz_printf(ctx, out, "<?xml version=\"1.0\"?>\n");
1032
1033
if (output_format == OUT_TEXT || output_format == OUT_HTML || output_format == OUT_STEXT)
1034
sheet = fz_new_text_sheet(ctx);
1035
1036
if (output_format == OUT_HTML)
1037
{
1038
fz_printf(ctx, out, "<style>\n");
1039
fz_printf(ctx, out, "body{background-color:gray;margin:12pt;}\n");
1040
fz_printf(ctx, out, "div.page{background-color:white;margin:6pt;padding:6pt;}\n");
1041
fz_printf(ctx, out, "div.block{border:1px solid gray;margin:6pt;padding:6pt;}\n");
1042
fz_printf(ctx, out, "div.metaline{display:table;width:100%%}\n");
1043
fz_printf(ctx, out, "div.line{display:table-row;padding:6pt}\n");
1044
fz_printf(ctx, out, "div.cell{display:table-cell;padding-left:6pt;padding-right:6pt}\n");
1045
fz_printf(ctx, out, "p{margin:0pt;padding:0pt;}\n");
1046
fz_printf(ctx, out, "</style>\n");
1047
fz_printf(ctx, out, "<body>\n");
1048
}
1049
1050
fz_try(ctx)
1051
{
1052
fz_register_document_handlers(ctx);
1053
1054
while (fz_optind < argc)
1055
{
1056
fz_try(ctx)
1057
{
1058
filename = argv[fz_optind++];
1059
files++;
1060
1061
fz_try(ctx)
1062
{
1063
doc = fz_open_document(ctx, filename);
1064
}
1065
fz_catch(ctx)
1066
{
1067
fz_rethrow_message(ctx, "cannot open document: %s", filename);
1068
}
1069
1070
if (fz_needs_password(ctx, doc))
1071
{
1072
if (!fz_authenticate_password(ctx, doc, password))
1073
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot authenticate password: %s", filename);
1074
}
1075
1076
fz_layout_document(ctx, doc, layout_w, layout_h, layout_em);
1077
1078
if (output_format == OUT_STEXT || output_format == OUT_TRACE)
1079
fz_printf(ctx, out, "<document name=\"%s\">\n", filename);
1080
1081
if (fz_optind == argc || !isrange(argv[fz_optind]))
1082
drawrange(ctx, doc, "1-");
1083
if (fz_optind < argc && isrange(argv[fz_optind]))
1084
drawrange(ctx, doc, argv[fz_optind++]);
1085
1086
if (output_format == OUT_STEXT || output_format == OUT_TRACE)
1087
fz_printf(ctx, out, "</document>\n");
1088
1089
fz_drop_document(ctx, doc);
1090
doc = NULL;
1091
}
1092
fz_catch(ctx)
1093
{
1094
if (!ignore_errors)
1095
fz_rethrow(ctx);
1096
1097
fz_drop_document(ctx, doc);
1098
doc = NULL;
1099
fz_warn(ctx, "ignoring error in '%s'", filename);
1100
}
1101
}
1102
}
1103
fz_catch(ctx)
1104
{
1105
fz_drop_document(ctx, doc);
1106
fprintf(stderr, "error: cannot draw '%s'\n", filename);
1107
errored = 1;
1108
}
1109
1110
if (pdfout)
1111
{
1112
fz_write_options opts = { 0 };
1113
1114
pdf_write_document(ctx, pdfout, output, &opts);
1115
pdf_close_document(ctx, pdfout);
1116
}
1117
1118
if (output_format == OUT_HTML)
1119
{
1120
fz_printf(ctx, out, "</body>\n");
1121
fz_printf(ctx, out, "<style>\n");
1122
fz_print_text_sheet(ctx, out, sheet);
1123
fz_printf(ctx, out, "</style>\n");
1124
}
1125
1126
fz_drop_text_sheet(ctx, sheet);
1127
fz_drop_output(ctx, out);
1128
out = NULL;
1129
1130
if (showtime && timing.count > 0)
1131
{
1132
if (files == 1)
1133
{
1134
printf("total %dms / %d pages for an average of %dms\n",
1135
timing.total, timing.count, timing.total / timing.count);
1136
printf("fastest page %d: %dms\n", timing.minpage, timing.min);
1137
printf("slowest page %d: %dms\n", timing.maxpage, timing.max);
1138
}
1139
else
1140
{
1141
printf("total %dms / %d pages for an average of %dms in %d files\n",
1142
timing.total, timing.count, timing.total / timing.count, files);
1143
printf("fastest page %d: %dms (%s)\n", timing.minpage, timing.min, timing.minfilename);
1144
printf("slowest page %d: %dms (%s)\n", timing.maxpage, timing.max, timing.maxfilename);
1145
}
1146
}
1147
1148
fz_drop_context(ctx);
1149
1150
if (showmemory)
1151
{
1152
#if defined(_WIN64)
1153
#define FMT "%Iu"
1154
#elif defined(_WIN32)
1155
#define FMT "%u"
1156
#else
1157
#define FMT "%zu"
1158
#endif
1159
printf("Total memory use = " FMT " bytes\n", memtrace_total);
1160
printf("Peak memory use = " FMT " bytes\n", memtrace_peak);
1161
printf("Current memory use = " FMT " bytes\n", memtrace_current);
1162
}
1163
1164
return (errored != 0);
1165
}
1166
1167
#ifdef _MSC_VER
1168
int wmain(int argc, wchar_t *wargv[])
1169
{
1170
char **argv = fz_argv_from_wargv(argc, wargv);
1171
int ret = main(argc, argv);
1172
fz_free_argv(argc, argv);
1173
return ret;
1174
}
1175
#endif
1176
1177