Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7643 views
1
/*
2
* Information tool.
3
* Print information about the input pdf.
4
*/
5
6
#include "mupdf/pdf.h"
7
8
enum
9
{
10
DIMENSIONS = 0x01,
11
FONTS = 0x02,
12
IMAGES = 0x04,
13
SHADINGS = 0x08,
14
PATTERNS = 0x10,
15
XOBJS = 0x20,
16
ALL = DIMENSIONS | FONTS | IMAGES | SHADINGS | PATTERNS | XOBJS
17
};
18
19
struct info
20
{
21
int page;
22
pdf_obj *pageref;
23
pdf_obj *pageobj;
24
union {
25
struct {
26
pdf_obj *obj;
27
} info;
28
struct {
29
pdf_obj *obj;
30
} crypt;
31
struct {
32
pdf_obj *obj;
33
fz_rect *bbox;
34
} dim;
35
struct {
36
pdf_obj *obj;
37
pdf_obj *subtype;
38
pdf_obj *name;
39
} font;
40
struct {
41
pdf_obj *obj;
42
pdf_obj *width;
43
pdf_obj *height;
44
pdf_obj *bpc;
45
pdf_obj *filter;
46
pdf_obj *cs;
47
pdf_obj *altcs;
48
} image;
49
struct {
50
pdf_obj *obj;
51
pdf_obj *type;
52
} shading;
53
struct {
54
pdf_obj *obj;
55
pdf_obj *type;
56
pdf_obj *paint;
57
pdf_obj *tiling;
58
pdf_obj *shading;
59
} pattern;
60
struct {
61
pdf_obj *obj;
62
pdf_obj *groupsubtype;
63
pdf_obj *reference;
64
} form;
65
} u;
66
};
67
68
typedef struct globals_s
69
{
70
pdf_document *doc;
71
fz_context *ctx;
72
fz_output *out;
73
int pagecount;
74
struct info *dim;
75
int dims;
76
struct info *font;
77
int fonts;
78
struct info *image;
79
int images;
80
struct info *shading;
81
int shadings;
82
struct info *pattern;
83
int patterns;
84
struct info *form;
85
int forms;
86
struct info *psobj;
87
int psobjs;
88
} globals;
89
90
static void clearinfo(fz_context *ctx, globals *glo)
91
{
92
int i;
93
94
if (glo->dim)
95
{
96
for (i = 0; i < glo->dims; i++)
97
fz_free(ctx, glo->dim[i].u.dim.bbox);
98
fz_free(ctx, glo->dim);
99
glo->dim = NULL;
100
glo->dims = 0;
101
}
102
103
if (glo->font)
104
{
105
fz_free(ctx, glo->font);
106
glo->font = NULL;
107
glo->fonts = 0;
108
}
109
110
if (glo->image)
111
{
112
fz_free(ctx, glo->image);
113
glo->image = NULL;
114
glo->images = 0;
115
}
116
117
if (glo->shading)
118
{
119
fz_free(ctx, glo->shading);
120
glo->shading = NULL;
121
glo->shadings = 0;
122
}
123
124
if (glo->pattern)
125
{
126
fz_free(ctx, glo->pattern);
127
glo->pattern = NULL;
128
glo->patterns = 0;
129
}
130
131
if (glo->form)
132
{
133
fz_free(ctx, glo->form);
134
glo->form = NULL;
135
glo->forms = 0;
136
}
137
138
if (glo->psobj)
139
{
140
fz_free(ctx, glo->psobj);
141
glo->psobj = NULL;
142
glo->psobjs = 0;
143
}
144
}
145
146
static void closexref(fz_context *ctx, globals *glo)
147
{
148
if (glo->doc)
149
{
150
pdf_close_document(ctx, glo->doc);
151
glo->doc = NULL;
152
}
153
154
clearinfo(ctx, glo);
155
}
156
157
static void
158
infousage(void)
159
{
160
fprintf(stderr,
161
"usage: mutool info [options] file.pdf [pages]\n"
162
"\t-p -\tpassword for decryption\n"
163
"\t-F\tlist fonts\n"
164
"\t-I\tlist images\n"
165
"\t-M\tlist dimensions\n"
166
"\t-P\tlist patterns\n"
167
"\t-S\tlist shadings\n"
168
"\t-X\tlist form and postscript xobjects\n"
169
"\tpages\tcomma separated list of page numbers and ranges\n"
170
);
171
exit(1);
172
}
173
174
static void
175
showglobalinfo(fz_context *ctx, globals *glo)
176
{
177
pdf_obj *obj;
178
fz_output *out = glo->out;
179
pdf_document *doc = glo->doc;
180
181
fz_printf(ctx, out, "\nPDF-%d.%d\n", doc->version / 10, doc->version % 10);
182
183
obj = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME_Info);
184
if (obj)
185
{
186
fz_printf(ctx, out, "Info object (%d %d R):\n", pdf_to_num(ctx, obj), pdf_to_gen(ctx, obj));
187
pdf_output_obj(ctx, out, pdf_resolve_indirect(ctx, obj), 1);
188
}
189
190
obj = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME_Encrypt);
191
if (obj)
192
{
193
fz_printf(ctx, out, "\nEncryption object (%d %d R):\n", pdf_to_num(ctx, obj), pdf_to_gen(ctx, obj));
194
pdf_output_obj(ctx, out, pdf_resolve_indirect(ctx, obj), 1);
195
}
196
197
fz_printf(ctx, out, "\nPages: %d\n\n", glo->pagecount);
198
}
199
200
static void
201
gatherdimensions(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *pageobj)
202
{
203
fz_rect bbox;
204
pdf_obj *obj;
205
int j;
206
207
obj = pdf_dict_get(ctx, pageobj, PDF_NAME_MediaBox);
208
if (!pdf_is_array(ctx, obj))
209
return;
210
211
pdf_to_rect(ctx, obj, &bbox);
212
213
obj = pdf_dict_get(ctx, pageobj, PDF_NAME_UserUnit);
214
if (pdf_is_real(ctx, obj))
215
{
216
float unit = pdf_to_real(ctx, obj);
217
bbox.x0 *= unit;
218
bbox.y0 *= unit;
219
bbox.x1 *= unit;
220
bbox.y1 *= unit;
221
}
222
223
for (j = 0; j < glo->dims; j++)
224
if (!memcmp(glo->dim[j].u.dim.bbox, &bbox, sizeof (fz_rect)))
225
break;
226
227
if (j < glo->dims)
228
return;
229
230
glo->dim = fz_resize_array(ctx, glo->dim, glo->dims+1, sizeof(struct info));
231
glo->dims++;
232
233
glo->dim[glo->dims - 1].page = page;
234
glo->dim[glo->dims - 1].pageref = pageref;
235
glo->dim[glo->dims - 1].pageobj = pageobj;
236
glo->dim[glo->dims - 1].u.dim.bbox = fz_malloc(ctx, sizeof(fz_rect));
237
memcpy(glo->dim[glo->dims - 1].u.dim.bbox, &bbox, sizeof (fz_rect));
238
239
return;
240
}
241
242
static void
243
gatherfonts(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *pageobj, pdf_obj *dict)
244
{
245
int i, n;
246
247
n = pdf_dict_len(ctx, dict);
248
for (i = 0; i < n; i++)
249
{
250
pdf_obj *fontdict = NULL;
251
pdf_obj *subtype = NULL;
252
pdf_obj *basefont = NULL;
253
pdf_obj *name = NULL;
254
int k;
255
256
fontdict = pdf_dict_get_val(ctx, dict, i);
257
if (!pdf_is_dict(ctx, fontdict))
258
{
259
fz_warn(ctx, "not a font dict (%d %d R)", pdf_to_num(ctx, fontdict), pdf_to_gen(ctx, fontdict));
260
continue;
261
}
262
263
subtype = pdf_dict_get(ctx, fontdict, PDF_NAME_Subtype);
264
basefont = pdf_dict_get(ctx, fontdict, PDF_NAME_BaseFont);
265
if (!basefont || pdf_is_null(ctx, basefont))
266
name = pdf_dict_get(ctx, fontdict, PDF_NAME_Name);
267
268
for (k = 0; k < glo->fonts; k++)
269
if (!pdf_objcmp(ctx, glo->font[k].u.font.obj, fontdict))
270
break;
271
272
if (k < glo->fonts)
273
continue;
274
275
glo->font = fz_resize_array(ctx, glo->font, glo->fonts+1, sizeof(struct info));
276
glo->fonts++;
277
278
glo->font[glo->fonts - 1].page = page;
279
glo->font[glo->fonts - 1].pageref = pageref;
280
glo->font[glo->fonts - 1].pageobj = pageobj;
281
glo->font[glo->fonts - 1].u.font.obj = fontdict;
282
glo->font[glo->fonts - 1].u.font.subtype = subtype;
283
glo->font[glo->fonts - 1].u.font.name = basefont ? basefont : name;
284
}
285
}
286
287
static void
288
gatherimages(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *pageobj, pdf_obj *dict)
289
{
290
int i, n;
291
292
n = pdf_dict_len(ctx, dict);
293
for (i = 0; i < n; i++)
294
{
295
pdf_obj *imagedict;
296
pdf_obj *type;
297
pdf_obj *width;
298
pdf_obj *height;
299
pdf_obj *bpc = NULL;
300
pdf_obj *filter = NULL;
301
pdf_obj *cs = NULL;
302
pdf_obj *altcs;
303
int k;
304
305
imagedict = pdf_dict_get_val(ctx, dict, i);
306
if (!pdf_is_dict(ctx, imagedict))
307
{
308
fz_warn(ctx, "not an image dict (%d %d R)", pdf_to_num(ctx, imagedict), pdf_to_gen(ctx, imagedict));
309
continue;
310
}
311
312
type = pdf_dict_get(ctx, imagedict, PDF_NAME_Subtype);
313
if (!pdf_name_eq(ctx, type, PDF_NAME_Image))
314
continue;
315
316
filter = pdf_dict_get(ctx, imagedict, PDF_NAME_Filter);
317
318
altcs = NULL;
319
cs = pdf_dict_get(ctx, imagedict, PDF_NAME_ColorSpace);
320
if (pdf_is_array(ctx, cs))
321
{
322
pdf_obj *cses = cs;
323
324
cs = pdf_array_get(ctx, cses, 0);
325
if (pdf_name_eq(ctx, cs, PDF_NAME_DeviceN) || pdf_name_eq(ctx, cs, PDF_NAME_Separation))
326
{
327
altcs = pdf_array_get(ctx, cses, 2);
328
if (pdf_is_array(ctx, altcs))
329
altcs = pdf_array_get(ctx, altcs, 0);
330
}
331
}
332
333
width = pdf_dict_get(ctx, imagedict, PDF_NAME_Width);
334
height = pdf_dict_get(ctx, imagedict, PDF_NAME_Height);
335
bpc = pdf_dict_get(ctx, imagedict, PDF_NAME_BitsPerComponent);
336
337
for (k = 0; k < glo->images; k++)
338
if (!pdf_objcmp(ctx, glo->image[k].u.image.obj, imagedict))
339
break;
340
341
if (k < glo->images)
342
continue;
343
344
glo->image = fz_resize_array(ctx, glo->image, glo->images+1, sizeof(struct info));
345
glo->images++;
346
347
glo->image[glo->images - 1].page = page;
348
glo->image[glo->images - 1].pageref = pageref;
349
glo->image[glo->images - 1].pageobj = pageobj;
350
glo->image[glo->images - 1].u.image.obj = imagedict;
351
glo->image[glo->images - 1].u.image.width = width;
352
glo->image[glo->images - 1].u.image.height = height;
353
glo->image[glo->images - 1].u.image.bpc = bpc;
354
glo->image[glo->images - 1].u.image.filter = filter;
355
glo->image[glo->images - 1].u.image.cs = cs;
356
glo->image[glo->images - 1].u.image.altcs = altcs;
357
}
358
}
359
360
static void
361
gatherforms(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *pageobj, pdf_obj *dict)
362
{
363
int i, n;
364
365
n = pdf_dict_len(ctx, dict);
366
for (i = 0; i < n; i++)
367
{
368
pdf_obj *xobjdict;
369
pdf_obj *type;
370
pdf_obj *subtype;
371
pdf_obj *group;
372
pdf_obj *groupsubtype;
373
pdf_obj *reference;
374
int k;
375
376
xobjdict = pdf_dict_get_val(ctx, dict, i);
377
if (!pdf_is_dict(ctx, xobjdict))
378
{
379
fz_warn(ctx, "not a xobject dict (%d %d R)", pdf_to_num(ctx, xobjdict), pdf_to_gen(ctx, xobjdict));
380
continue;
381
}
382
383
type = pdf_dict_get(ctx, xobjdict, PDF_NAME_Subtype);
384
if (!pdf_name_eq(ctx, type, PDF_NAME_Form))
385
continue;
386
387
subtype = pdf_dict_get(ctx, xobjdict, PDF_NAME_Subtype2);
388
if (!pdf_name_eq(ctx, subtype, PDF_NAME_PS))
389
continue;
390
391
group = pdf_dict_get(ctx, xobjdict, PDF_NAME_Group);
392
groupsubtype = pdf_dict_get(ctx, group, PDF_NAME_S);
393
reference = pdf_dict_get(ctx, xobjdict, PDF_NAME_Ref);
394
395
for (k = 0; k < glo->forms; k++)
396
if (!pdf_objcmp(ctx, glo->form[k].u.form.obj, xobjdict))
397
break;
398
399
if (k < glo->forms)
400
continue;
401
402
glo->form = fz_resize_array(ctx, glo->form, glo->forms+1, sizeof(struct info));
403
glo->forms++;
404
405
glo->form[glo->forms - 1].page = page;
406
glo->form[glo->forms - 1].pageref = pageref;
407
glo->form[glo->forms - 1].pageobj = pageobj;
408
glo->form[glo->forms - 1].u.form.obj = xobjdict;
409
glo->form[glo->forms - 1].u.form.groupsubtype = groupsubtype;
410
glo->form[glo->forms - 1].u.form.reference = reference;
411
}
412
}
413
414
static void
415
gatherpsobjs(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *pageobj, pdf_obj *dict)
416
{
417
int i, n;
418
419
n = pdf_dict_len(ctx, dict);
420
for (i = 0; i < n; i++)
421
{
422
pdf_obj *xobjdict;
423
pdf_obj *type;
424
pdf_obj *subtype;
425
int k;
426
427
xobjdict = pdf_dict_get_val(ctx, dict, i);
428
if (!pdf_is_dict(ctx, xobjdict))
429
{
430
fz_warn(ctx, "not a xobject dict (%d %d R)", pdf_to_num(ctx, xobjdict), pdf_to_gen(ctx, xobjdict));
431
continue;
432
}
433
434
type = pdf_dict_get(ctx, xobjdict, PDF_NAME_Subtype);
435
subtype = pdf_dict_get(ctx, xobjdict, PDF_NAME_Subtype2);
436
if (!pdf_name_eq(ctx, type, PDF_NAME_PS) &&
437
(!pdf_name_eq(ctx, type, PDF_NAME_Form) || !pdf_name_eq(ctx, subtype, PDF_NAME_PS)))
438
continue;
439
440
for (k = 0; k < glo->psobjs; k++)
441
if (!pdf_objcmp(ctx, glo->psobj[k].u.form.obj, xobjdict))
442
break;
443
444
if (k < glo->psobjs)
445
continue;
446
447
glo->psobj = fz_resize_array(ctx, glo->psobj, glo->psobjs+1, sizeof(struct info));
448
glo->psobjs++;
449
450
glo->psobj[glo->psobjs - 1].page = page;
451
glo->psobj[glo->psobjs - 1].pageref = pageref;
452
glo->psobj[glo->psobjs - 1].pageobj = pageobj;
453
glo->psobj[glo->psobjs - 1].u.form.obj = xobjdict;
454
}
455
}
456
457
static void
458
gathershadings(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *pageobj, pdf_obj *dict)
459
{
460
int i, n;
461
462
n = pdf_dict_len(ctx, dict);
463
for (i = 0; i < n; i++)
464
{
465
pdf_obj *shade;
466
pdf_obj *type;
467
int k;
468
469
shade = pdf_dict_get_val(ctx, dict, i);
470
if (!pdf_is_dict(ctx, shade))
471
{
472
fz_warn(ctx, "not a shading dict (%d %d R)", pdf_to_num(ctx, shade), pdf_to_gen(ctx, shade));
473
continue;
474
}
475
476
type = pdf_dict_get(ctx, shade, PDF_NAME_ShadingType);
477
if (!pdf_is_int(ctx, type) || pdf_to_int(ctx, type) < 1 || pdf_to_int(ctx, type) > 7)
478
{
479
fz_warn(ctx, "not a shading type (%d %d R)", pdf_to_num(ctx, shade), pdf_to_gen(ctx, shade));
480
type = NULL;
481
}
482
483
for (k = 0; k < glo->shadings; k++)
484
if (!pdf_objcmp(ctx, glo->shading[k].u.shading.obj, shade))
485
break;
486
487
if (k < glo->shadings)
488
continue;
489
490
glo->shading = fz_resize_array(ctx, glo->shading, glo->shadings+1, sizeof(struct info));
491
glo->shadings++;
492
493
glo->shading[glo->shadings - 1].page = page;
494
glo->shading[glo->shadings - 1].pageref = pageref;
495
glo->shading[glo->shadings - 1].pageobj = pageobj;
496
glo->shading[glo->shadings - 1].u.shading.obj = shade;
497
glo->shading[glo->shadings - 1].u.shading.type = type;
498
}
499
}
500
501
static void
502
gatherpatterns(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *pageobj, pdf_obj *dict)
503
{
504
int i, n;
505
506
n = pdf_dict_len(ctx, dict);
507
for (i = 0; i < n; i++)
508
{
509
pdf_obj *patterndict;
510
pdf_obj *type;
511
pdf_obj *paint = NULL;
512
pdf_obj *tiling = NULL;
513
pdf_obj *shading = NULL;
514
int k;
515
516
patterndict = pdf_dict_get_val(ctx, dict, i);
517
if (!pdf_is_dict(ctx, patterndict))
518
{
519
fz_warn(ctx, "not a pattern dict (%d %d R)", pdf_to_num(ctx, patterndict), pdf_to_gen(ctx, patterndict));
520
continue;
521
}
522
523
type = pdf_dict_get(ctx, patterndict, PDF_NAME_PatternType);
524
if (!pdf_is_int(ctx, type) || pdf_to_int(ctx, type) < 1 || pdf_to_int(ctx, type) > 2)
525
{
526
fz_warn(ctx, "not a pattern type (%d %d R)", pdf_to_num(ctx, patterndict), pdf_to_gen(ctx, patterndict));
527
type = NULL;
528
}
529
530
if (pdf_to_int(ctx, type) == 1)
531
{
532
paint = pdf_dict_get(ctx, patterndict, PDF_NAME_PaintType);
533
if (!pdf_is_int(ctx, paint) || pdf_to_int(ctx, paint) < 1 || pdf_to_int(ctx, paint) > 2)
534
{
535
fz_warn(ctx, "not a pattern paint type (%d %d R)", pdf_to_num(ctx, patterndict), pdf_to_gen(ctx, patterndict));
536
paint = NULL;
537
}
538
539
tiling = pdf_dict_get(ctx, patterndict, PDF_NAME_TilingType);
540
if (!pdf_is_int(ctx, tiling) || pdf_to_int(ctx, tiling) < 1 || pdf_to_int(ctx, tiling) > 3)
541
{
542
fz_warn(ctx, "not a pattern tiling type (%d %d R)", pdf_to_num(ctx, patterndict), pdf_to_gen(ctx, patterndict));
543
tiling = NULL;
544
}
545
}
546
else
547
{
548
shading = pdf_dict_get(ctx, patterndict, PDF_NAME_Shading);
549
}
550
551
for (k = 0; k < glo->patterns; k++)
552
if (!pdf_objcmp(ctx, glo->pattern[k].u.pattern.obj, patterndict))
553
break;
554
555
if (k < glo->patterns)
556
continue;
557
558
glo->pattern = fz_resize_array(ctx, glo->pattern, glo->patterns+1, sizeof(struct info));
559
glo->patterns++;
560
561
glo->pattern[glo->patterns - 1].page = page;
562
glo->pattern[glo->patterns - 1].pageref = pageref;
563
glo->pattern[glo->patterns - 1].pageobj = pageobj;
564
glo->pattern[glo->patterns - 1].u.pattern.obj = patterndict;
565
glo->pattern[glo->patterns - 1].u.pattern.type = type;
566
glo->pattern[glo->patterns - 1].u.pattern.paint = paint;
567
glo->pattern[glo->patterns - 1].u.pattern.tiling = tiling;
568
glo->pattern[glo->patterns - 1].u.pattern.shading = shading;
569
}
570
}
571
572
static void
573
gatherresourceinfo(fz_context *ctx, globals *glo, int page, pdf_obj *rsrc, int show)
574
{
575
pdf_obj *pageobj;
576
pdf_obj *pageref;
577
pdf_obj *font;
578
pdf_obj *xobj;
579
pdf_obj *shade;
580
pdf_obj *pattern;
581
pdf_obj *subrsrc;
582
int i;
583
584
pageref = pdf_lookup_page_obj(ctx, glo->doc, page-1);
585
pageobj = pdf_resolve_indirect(ctx, pageref);
586
587
if (!pageobj)
588
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot retrieve info from page %d", page);
589
590
font = pdf_dict_get(ctx, rsrc, PDF_NAME_Font);
591
if (show & FONTS && font)
592
{
593
int n;
594
595
gatherfonts(ctx, glo, page, pageref, pageobj, font);
596
n = pdf_dict_len(ctx, font);
597
for (i = 0; i < n; i++)
598
{
599
pdf_obj *obj = pdf_dict_get_val(ctx, font, i);
600
601
subrsrc = pdf_dict_get(ctx, obj, PDF_NAME_Resources);
602
if (subrsrc && pdf_objcmp(ctx, rsrc, subrsrc))
603
gatherresourceinfo(ctx, glo, page, subrsrc, show);
604
}
605
}
606
607
xobj = pdf_dict_get(ctx, rsrc, PDF_NAME_XObject);
608
if (show & XOBJS && xobj)
609
{
610
int n;
611
612
gatherimages(ctx, glo, page, pageref, pageobj, xobj);
613
gatherforms(ctx, glo, page, pageref, pageobj, xobj);
614
gatherpsobjs(ctx, glo, page, pageref, pageobj, xobj);
615
n = pdf_dict_len(ctx, xobj);
616
for (i = 0; i < n; i++)
617
{
618
pdf_obj *obj = pdf_dict_get_val(ctx, xobj, i);
619
subrsrc = pdf_dict_get(ctx, obj, PDF_NAME_Resources);
620
if (subrsrc && pdf_objcmp(ctx, rsrc, subrsrc))
621
gatherresourceinfo(ctx, glo, page, subrsrc, show);
622
}
623
}
624
625
shade = pdf_dict_get(ctx, rsrc, PDF_NAME_Shading);
626
if (show & SHADINGS && shade)
627
gathershadings(ctx, glo, page, pageref, pageobj, shade);
628
629
pattern = pdf_dict_get(ctx, rsrc, PDF_NAME_Pattern);
630
if (show & PATTERNS && pattern)
631
{
632
int n;
633
gatherpatterns(ctx, glo, page, pageref, pageobj, pattern);
634
n = pdf_dict_len(ctx, pattern);
635
for (i = 0; i < n; i++)
636
{
637
pdf_obj *obj = pdf_dict_get_val(ctx, pattern, i);
638
subrsrc = pdf_dict_get(ctx, obj, PDF_NAME_Resources);
639
if (subrsrc && pdf_objcmp(ctx, rsrc, subrsrc))
640
gatherresourceinfo(ctx, glo, page, subrsrc, show);
641
}
642
}
643
}
644
645
static void
646
gatherpageinfo(fz_context *ctx, globals *glo, int page, int show)
647
{
648
pdf_obj *pageobj;
649
pdf_obj *pageref;
650
pdf_obj *rsrc;
651
652
pageref = pdf_lookup_page_obj(ctx, glo->doc, page-1);
653
pageobj = pdf_resolve_indirect(ctx, pageref);
654
655
if (!pageobj)
656
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot retrieve info from page %d", page);
657
658
gatherdimensions(ctx, glo, page, pageref, pageobj);
659
660
rsrc = pdf_dict_get(ctx, pageobj, PDF_NAME_Resources);
661
gatherresourceinfo(ctx, glo, page, rsrc, show);
662
}
663
664
static void
665
printinfo(fz_context *ctx, globals *glo, char *filename, int show, int page)
666
{
667
int i;
668
int j;
669
fz_output *out = glo->out;
670
671
#define PAGE_FMT "\t%d\t(%d %d R):\t"
672
673
if (show & DIMENSIONS && glo->dims > 0)
674
{
675
fz_printf(ctx, out, "Mediaboxes (%d):\n", glo->dims);
676
for (i = 0; i < glo->dims; i++)
677
{
678
fz_printf(ctx, out, PAGE_FMT "[ %g %g %g %g ]\n",
679
glo->dim[i].page,
680
pdf_to_num(ctx, glo->dim[i].pageref),
681
pdf_to_gen(ctx, glo->dim[i].pageref),
682
glo->dim[i].u.dim.bbox->x0,
683
glo->dim[i].u.dim.bbox->y0,
684
glo->dim[i].u.dim.bbox->x1,
685
glo->dim[i].u.dim.bbox->y1);
686
}
687
fz_printf(ctx, out, "\n");
688
}
689
690
if (show & FONTS && glo->fonts > 0)
691
{
692
fz_printf(ctx, out, "Fonts (%d):\n", glo->fonts);
693
for (i = 0; i < glo->fonts; i++)
694
{
695
fz_printf(ctx, out, PAGE_FMT "%s '%s' (%d %d R)\n",
696
glo->font[i].page,
697
pdf_to_num(ctx, glo->font[i].pageref),
698
pdf_to_gen(ctx, glo->font[i].pageref),
699
pdf_to_name(ctx, glo->font[i].u.font.subtype),
700
pdf_to_name(ctx, glo->font[i].u.font.name),
701
pdf_to_num(ctx, glo->font[i].u.font.obj),
702
pdf_to_gen(ctx, glo->font[i].u.font.obj));
703
}
704
fz_printf(ctx, out, "\n");
705
}
706
707
if (show & IMAGES && glo->images > 0)
708
{
709
fz_printf(ctx, out, "Images (%d):\n", glo->images);
710
for (i = 0; i < glo->images; i++)
711
{
712
char *cs = NULL;
713
char *altcs = NULL;
714
715
fz_printf(ctx, out, PAGE_FMT "[ ",
716
glo->image[i].page,
717
pdf_to_num(ctx, glo->image[i].pageref),
718
pdf_to_gen(ctx, glo->image[i].pageref));
719
720
if (pdf_is_array(ctx, glo->image[i].u.image.filter))
721
{
722
int n = pdf_array_len(ctx, glo->image[i].u.image.filter);
723
for (j = 0; j < n; j++)
724
{
725
pdf_obj *obj = pdf_array_get(ctx, glo->image[i].u.image.filter, j);
726
char *filter = fz_strdup(ctx, pdf_to_name(ctx, obj));
727
728
if (strstr(filter, "Decode"))
729
*(strstr(filter, "Decode")) = '\0';
730
731
fz_printf(ctx, out, "%s%s",
732
filter,
733
j == pdf_array_len(ctx, glo->image[i].u.image.filter) - 1 ? "" : " ");
734
fz_free(ctx, filter);
735
}
736
}
737
else if (glo->image[i].u.image.filter)
738
{
739
pdf_obj *obj = glo->image[i].u.image.filter;
740
char *filter = fz_strdup(ctx, pdf_to_name(ctx, obj));
741
742
if (strstr(filter, "Decode"))
743
*(strstr(filter, "Decode")) = '\0';
744
745
fz_printf(ctx, out, "%s", filter);
746
fz_free(ctx, filter);
747
}
748
else
749
fz_printf(ctx, out, "Raw");
750
751
if (glo->image[i].u.image.cs)
752
{
753
cs = fz_strdup(ctx, pdf_to_name(ctx, glo->image[i].u.image.cs));
754
755
if (!strncmp(cs, "Device", 6))
756
{
757
int len = strlen(cs + 6);
758
memmove(cs + 3, cs + 6, len + 1);
759
cs[3 + len + 1] = '\0';
760
}
761
if (strstr(cs, "ICC"))
762
fz_strlcpy(cs, "ICC", 4);
763
if (strstr(cs, "Indexed"))
764
fz_strlcpy(cs, "Idx", 4);
765
if (strstr(cs, "Pattern"))
766
fz_strlcpy(cs, "Pat", 4);
767
if (strstr(cs, "Separation"))
768
fz_strlcpy(cs, "Sep", 4);
769
}
770
if (glo->image[i].u.image.altcs)
771
{
772
altcs = fz_strdup(ctx, pdf_to_name(ctx, glo->image[i].u.image.altcs));
773
774
if (!strncmp(altcs, "Device", 6))
775
{
776
int len = strlen(altcs + 6);
777
memmove(altcs + 3, altcs + 6, len + 1);
778
altcs[3 + len + 1] = '\0';
779
}
780
if (strstr(altcs, "ICC"))
781
fz_strlcpy(altcs, "ICC", 4);
782
if (strstr(altcs, "Indexed"))
783
fz_strlcpy(altcs, "Idx", 4);
784
if (strstr(altcs, "Pattern"))
785
fz_strlcpy(altcs, "Pat", 4);
786
if (strstr(altcs, "Separation"))
787
fz_strlcpy(altcs, "Sep", 4);
788
}
789
790
fz_printf(ctx, out, " ] %dx%d %dbpc %s%s%s (%d %d R)\n",
791
pdf_to_int(ctx, glo->image[i].u.image.width),
792
pdf_to_int(ctx, glo->image[i].u.image.height),
793
glo->image[i].u.image.bpc ? pdf_to_int(ctx, glo->image[i].u.image.bpc) : 1,
794
glo->image[i].u.image.cs ? cs : "ImageMask",
795
glo->image[i].u.image.altcs ? " " : "",
796
glo->image[i].u.image.altcs ? altcs : "",
797
pdf_to_num(ctx, glo->image[i].u.image.obj),
798
pdf_to_gen(ctx, glo->image[i].u.image.obj));
799
800
fz_free(ctx, cs);
801
fz_free(ctx, altcs);
802
}
803
fz_printf(ctx, out, "\n");
804
}
805
806
if (show & SHADINGS && glo->shadings > 0)
807
{
808
fz_printf(ctx, out, "Shading patterns (%d):\n", glo->shadings);
809
for (i = 0; i < glo->shadings; i++)
810
{
811
char *shadingtype[] =
812
{
813
"",
814
"Function",
815
"Axial",
816
"Radial",
817
"Triangle mesh",
818
"Lattice",
819
"Coons patch",
820
"Tensor patch",
821
};
822
823
fz_printf(ctx, out, PAGE_FMT "%s (%d %d R)\n",
824
glo->shading[i].page,
825
pdf_to_num(ctx, glo->shading[i].pageref),
826
pdf_to_gen(ctx, glo->shading[i].pageref),
827
shadingtype[pdf_to_int(ctx, glo->shading[i].u.shading.type)],
828
pdf_to_num(ctx, glo->shading[i].u.shading.obj),
829
pdf_to_gen(ctx, glo->shading[i].u.shading.obj));
830
}
831
fz_printf(ctx, out, "\n");
832
}
833
834
if (show & PATTERNS && glo->patterns > 0)
835
{
836
fz_printf(ctx, out, "Patterns (%d):\n", glo->patterns);
837
for (i = 0; i < glo->patterns; i++)
838
{
839
if (pdf_to_int(ctx, glo->pattern[i].u.pattern.type) == 1)
840
{
841
char *painttype[] =
842
{
843
"",
844
"Colored",
845
"Uncolored",
846
};
847
char *tilingtype[] =
848
{
849
"",
850
"Constant",
851
"No distortion",
852
"Constant/fast tiling",
853
};
854
855
fz_printf(ctx, out, PAGE_FMT "Tiling %s %s (%d %d R)\n",
856
glo->pattern[i].page,
857
pdf_to_num(ctx, glo->pattern[i].pageref),
858
pdf_to_gen(ctx, glo->pattern[i].pageref),
859
painttype[pdf_to_int(ctx, glo->pattern[i].u.pattern.paint)],
860
tilingtype[pdf_to_int(ctx, glo->pattern[i].u.pattern.tiling)],
861
pdf_to_num(ctx, glo->pattern[i].u.pattern.obj),
862
pdf_to_gen(ctx, glo->pattern[i].u.pattern.obj));
863
}
864
else
865
{
866
fz_printf(ctx, out, PAGE_FMT "Shading %d %d R (%d %d R)\n",
867
glo->pattern[i].page,
868
pdf_to_num(ctx, glo->pattern[i].pageref),
869
pdf_to_gen(ctx, glo->pattern[i].pageref),
870
pdf_to_num(ctx, glo->pattern[i].u.pattern.shading),
871
pdf_to_gen(ctx, glo->pattern[i].u.pattern.shading),
872
pdf_to_num(ctx, glo->pattern[i].u.pattern.obj),
873
pdf_to_gen(ctx, glo->pattern[i].u.pattern.obj));
874
}
875
}
876
fz_printf(ctx, out, "\n");
877
}
878
879
if (show & XOBJS && glo->forms > 0)
880
{
881
fz_printf(ctx, out, "Form xobjects (%d):\n", glo->forms);
882
for (i = 0; i < glo->forms; i++)
883
{
884
fz_printf(ctx, out, PAGE_FMT "Form%s%s%s%s (%d %d R)\n",
885
glo->form[i].page,
886
pdf_to_num(ctx, glo->form[i].pageref),
887
pdf_to_gen(ctx, glo->form[i].pageref),
888
glo->form[i].u.form.groupsubtype ? " " : "",
889
glo->form[i].u.form.groupsubtype ? pdf_to_name(ctx, glo->form[i].u.form.groupsubtype) : "",
890
glo->form[i].u.form.groupsubtype ? " Group" : "",
891
glo->form[i].u.form.reference ? " Reference" : "",
892
pdf_to_num(ctx, glo->form[i].u.form.obj),
893
pdf_to_gen(ctx, glo->form[i].u.form.obj));
894
}
895
fz_printf(ctx, out, "\n");
896
}
897
898
if (show & XOBJS && glo->psobjs > 0)
899
{
900
fz_printf(ctx, out, "Postscript xobjects (%d):\n", glo->psobjs);
901
for (i = 0; i < glo->psobjs; i++)
902
{
903
fz_printf(ctx, out, PAGE_FMT "(%d %d R)\n",
904
glo->psobj[i].page,
905
pdf_to_num(ctx, glo->psobj[i].pageref),
906
pdf_to_gen(ctx, glo->psobj[i].pageref),
907
pdf_to_num(ctx, glo->psobj[i].u.form.obj),
908
pdf_to_gen(ctx, glo->psobj[i].u.form.obj));
909
}
910
fz_printf(ctx, out, "\n");
911
}
912
}
913
914
static void
915
showinfo(fz_context *ctx, globals *glo, char *filename, int show, char *pagelist)
916
{
917
int page, spage, epage;
918
char *spec, *dash;
919
int allpages;
920
int pagecount;
921
fz_output *out = glo->out;
922
923
if (!glo->doc)
924
infousage();
925
926
allpages = !strcmp(pagelist, "1-");
927
928
pagecount = pdf_count_pages(ctx, glo->doc);
929
spec = fz_strsep(&pagelist, ",");
930
while (spec && pagecount)
931
{
932
dash = strchr(spec, '-');
933
934
if (dash == spec)
935
spage = epage = pagecount;
936
else
937
spage = epage = atoi(spec);
938
939
if (dash)
940
{
941
if (strlen(dash) > 1)
942
epage = atoi(dash + 1);
943
else
944
epage = pagecount;
945
}
946
947
if (spage > epage)
948
page = spage, spage = epage, epage = page;
949
950
spage = fz_clampi(spage, 1, pagecount);
951
epage = fz_clampi(epage, 1, pagecount);
952
953
if (allpages)
954
fz_printf(ctx, out, "Retrieving info from pages %d-%d...\n", spage, epage);
955
for (page = spage; page <= epage; page++)
956
{
957
gatherpageinfo(ctx, glo, page, show);
958
if (!allpages)
959
{
960
fz_printf(ctx, out, "Page %d:\n", page);
961
printinfo(ctx, glo, filename, show, page);
962
fz_printf(ctx, out, "\n");
963
clearinfo(ctx, glo);
964
}
965
}
966
967
spec = fz_strsep(&pagelist, ",");
968
}
969
970
if (allpages)
971
printinfo(ctx, glo, filename, show, -1);
972
}
973
974
static int arg_is_page_range(const char *arg)
975
{
976
int c;
977
978
while ((c = *arg++) != 0)
979
{
980
if ((c < '0' || c > '9') && (c != '-') && (c != ','))
981
return 0;
982
}
983
return 1;
984
}
985
986
static void
987
pdfinfo_info(fz_context *ctx, fz_output *out, char *filename, char *password, int show, char *argv[], int argc)
988
{
989
enum { NO_FILE_OPENED, NO_INFO_GATHERED, INFO_SHOWN } state;
990
int argidx = 0;
991
globals glo = { 0 };
992
993
glo.out = out;
994
glo.ctx = ctx;
995
996
state = NO_FILE_OPENED;
997
while (argidx < argc)
998
{
999
if (state == NO_FILE_OPENED || !arg_is_page_range(argv[argidx]))
1000
{
1001
if (state == NO_INFO_GATHERED)
1002
{
1003
showinfo(ctx, &glo, filename, show, "1-");
1004
}
1005
1006
closexref(ctx, &glo);
1007
1008
filename = argv[argidx];
1009
fz_printf(ctx, out, "%s:\n", filename);
1010
glo.doc = pdf_open_document(glo.ctx, filename);
1011
if (pdf_needs_password(ctx, glo.doc))
1012
if (!pdf_authenticate_password(ctx, glo.doc, password))
1013
fz_throw(glo.ctx, FZ_ERROR_GENERIC, "cannot authenticate password: %s", filename);
1014
glo.pagecount = pdf_count_pages(ctx, glo.doc);
1015
1016
showglobalinfo(ctx, &glo);
1017
state = NO_INFO_GATHERED;
1018
}
1019
else
1020
{
1021
showinfo(ctx, &glo, filename, show, argv[argidx]);
1022
state = INFO_SHOWN;
1023
}
1024
1025
argidx++;
1026
}
1027
1028
if (state == NO_INFO_GATHERED)
1029
showinfo(ctx, &glo, filename, show, "1-");
1030
1031
closexref(ctx, &glo);
1032
}
1033
1034
int pdfinfo_main(int argc, char **argv)
1035
{
1036
char *filename = "";
1037
char *password = "";
1038
int show = ALL;
1039
int c;
1040
fz_output *out = NULL;
1041
int ret;
1042
fz_context *ctx;
1043
1044
while ((c = fz_getopt(argc, argv, "FISPXMp:")) != -1)
1045
{
1046
switch (c)
1047
{
1048
case 'F': if (show == ALL) show = FONTS; else show |= FONTS; break;
1049
case 'I': if (show == ALL) show = IMAGES; else show |= IMAGES; break;
1050
case 'S': if (show == ALL) show = SHADINGS; else show |= SHADINGS; break;
1051
case 'P': if (show == ALL) show = PATTERNS; else show |= PATTERNS; break;
1052
case 'X': if (show == ALL) show = XOBJS; else show |= XOBJS; break;
1053
case 'M': if (show == ALL) show = DIMENSIONS; else show |= DIMENSIONS; break;
1054
case 'p': password = fz_optarg; break;
1055
default:
1056
infousage();
1057
break;
1058
}
1059
}
1060
1061
if (fz_optind == argc)
1062
infousage();
1063
1064
ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
1065
if (!ctx)
1066
{
1067
fprintf(stderr, "cannot initialise context\n");
1068
exit(1);
1069
}
1070
1071
fz_var(out);
1072
1073
ret = 0;
1074
fz_try(ctx)
1075
{
1076
out = fz_new_output_with_file(ctx, stdout, 0);
1077
pdfinfo_info(ctx, out, filename, password, show, &argv[fz_optind], argc-fz_optind);
1078
}
1079
fz_catch(ctx)
1080
{
1081
ret = 1;
1082
}
1083
fz_drop_output(ctx, out);
1084
fz_drop_context(ctx);
1085
return ret;
1086
}
1087
1088