1049 lines
25 KiB
C
1049 lines
25 KiB
C
/*
|
|
* Information tool.
|
|
* Print information about the input pdf.
|
|
*/
|
|
|
|
#include "mupdf/fitz.h"
|
|
#include "mupdf/pdf.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
enum
|
|
{
|
|
DIMENSIONS = 0x01,
|
|
FONTS = 0x02,
|
|
IMAGES = 0x04,
|
|
SHADINGS = 0x08,
|
|
PATTERNS = 0x10,
|
|
XOBJS = 0x20,
|
|
ALL = DIMENSIONS | FONTS | IMAGES | SHADINGS | PATTERNS | XOBJS
|
|
};
|
|
|
|
struct info
|
|
{
|
|
int page;
|
|
pdf_obj *pageref;
|
|
union {
|
|
struct {
|
|
pdf_obj *obj;
|
|
} info;
|
|
struct {
|
|
pdf_obj *obj;
|
|
} crypt;
|
|
struct {
|
|
pdf_obj *obj;
|
|
fz_rect *bbox;
|
|
} dim;
|
|
struct {
|
|
pdf_obj *obj;
|
|
pdf_obj *subtype;
|
|
pdf_obj *name;
|
|
pdf_obj *encoding;
|
|
} font;
|
|
struct {
|
|
pdf_obj *obj;
|
|
pdf_obj *width;
|
|
pdf_obj *height;
|
|
pdf_obj *bpc;
|
|
pdf_obj *filter;
|
|
pdf_obj *cs;
|
|
pdf_obj *altcs;
|
|
} image;
|
|
struct {
|
|
pdf_obj *obj;
|
|
pdf_obj *type;
|
|
} shading;
|
|
struct {
|
|
pdf_obj *obj;
|
|
pdf_obj *type;
|
|
pdf_obj *paint;
|
|
pdf_obj *tiling;
|
|
pdf_obj *shading;
|
|
} pattern;
|
|
struct {
|
|
pdf_obj *obj;
|
|
pdf_obj *groupsubtype;
|
|
pdf_obj *reference;
|
|
} form;
|
|
} u;
|
|
};
|
|
|
|
typedef struct globals_s
|
|
{
|
|
pdf_document *doc;
|
|
fz_context *ctx;
|
|
fz_output *out;
|
|
int pagecount;
|
|
struct info *dim;
|
|
int dims;
|
|
struct info *font;
|
|
int fonts;
|
|
struct info *image;
|
|
int images;
|
|
struct info *shading;
|
|
int shadings;
|
|
struct info *pattern;
|
|
int patterns;
|
|
struct info *form;
|
|
int forms;
|
|
struct info *psobj;
|
|
int psobjs;
|
|
} globals;
|
|
|
|
static void clearinfo(fz_context *ctx, globals *glo)
|
|
{
|
|
int i;
|
|
|
|
if (glo->dim)
|
|
{
|
|
for (i = 0; i < glo->dims; i++)
|
|
fz_free(ctx, glo->dim[i].u.dim.bbox);
|
|
fz_free(ctx, glo->dim);
|
|
glo->dim = NULL;
|
|
glo->dims = 0;
|
|
}
|
|
|
|
if (glo->font)
|
|
{
|
|
fz_free(ctx, glo->font);
|
|
glo->font = NULL;
|
|
glo->fonts = 0;
|
|
}
|
|
|
|
if (glo->image)
|
|
{
|
|
fz_free(ctx, glo->image);
|
|
glo->image = NULL;
|
|
glo->images = 0;
|
|
}
|
|
|
|
if (glo->shading)
|
|
{
|
|
fz_free(ctx, glo->shading);
|
|
glo->shading = NULL;
|
|
glo->shadings = 0;
|
|
}
|
|
|
|
if (glo->pattern)
|
|
{
|
|
fz_free(ctx, glo->pattern);
|
|
glo->pattern = NULL;
|
|
glo->patterns = 0;
|
|
}
|
|
|
|
if (glo->form)
|
|
{
|
|
fz_free(ctx, glo->form);
|
|
glo->form = NULL;
|
|
glo->forms = 0;
|
|
}
|
|
|
|
if (glo->psobj)
|
|
{
|
|
fz_free(ctx, glo->psobj);
|
|
glo->psobj = NULL;
|
|
glo->psobjs = 0;
|
|
}
|
|
}
|
|
|
|
static void closexref(fz_context *ctx, globals *glo)
|
|
{
|
|
if (glo->doc)
|
|
{
|
|
pdf_drop_document(ctx, glo->doc);
|
|
glo->doc = NULL;
|
|
}
|
|
|
|
clearinfo(ctx, glo);
|
|
}
|
|
|
|
static void
|
|
infousage(void)
|
|
{
|
|
fprintf(stderr,
|
|
"usage: mutool info [options] file.pdf [pages]\n"
|
|
"\t-p -\tpassword for decryption\n"
|
|
"\t-F\tlist fonts\n"
|
|
"\t-I\tlist images\n"
|
|
"\t-M\tlist dimensions\n"
|
|
"\t-P\tlist patterns\n"
|
|
"\t-S\tlist shadings\n"
|
|
"\t-X\tlist form and postscript xobjects\n"
|
|
"\tpages\tcomma separated list of page numbers and ranges\n"
|
|
);
|
|
exit(1);
|
|
}
|
|
|
|
static void
|
|
showglobalinfo(fz_context *ctx, globals *glo)
|
|
{
|
|
pdf_obj *obj;
|
|
fz_output *out = glo->out;
|
|
pdf_document *doc = glo->doc;
|
|
int version = pdf_version(ctx, doc);
|
|
|
|
fz_write_printf(ctx, out, "\nPDF-%d.%d\n", version / 10, version % 10);
|
|
|
|
obj = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Info));
|
|
if (obj)
|
|
{
|
|
fz_write_printf(ctx, out, "Info object (%d 0 R):\n", pdf_to_num(ctx, obj));
|
|
pdf_print_obj(ctx, out, pdf_resolve_indirect(ctx, obj), 1, 1);
|
|
}
|
|
|
|
obj = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Encrypt));
|
|
if (obj)
|
|
{
|
|
fz_write_printf(ctx, out, "\nEncryption object (%d 0 R):\n", pdf_to_num(ctx, obj));
|
|
pdf_print_obj(ctx, out, pdf_resolve_indirect(ctx, obj), 1, 1);
|
|
}
|
|
|
|
fz_write_printf(ctx, out, "\nPages: %d\n\n", glo->pagecount);
|
|
}
|
|
|
|
static void
|
|
gatherdimensions(fz_context *ctx, globals *glo, int page, pdf_obj *pageref)
|
|
{
|
|
fz_rect bbox;
|
|
pdf_obj *obj;
|
|
int j;
|
|
|
|
obj = pdf_dict_get(ctx, pageref, PDF_NAME(MediaBox));
|
|
if (!pdf_is_array(ctx, obj))
|
|
return;
|
|
|
|
bbox = pdf_to_rect(ctx, obj);
|
|
|
|
obj = pdf_dict_get(ctx, pageref, PDF_NAME(UserUnit));
|
|
if (pdf_is_real(ctx, obj))
|
|
{
|
|
float unit = pdf_to_real(ctx, obj);
|
|
bbox.x0 *= unit;
|
|
bbox.y0 *= unit;
|
|
bbox.x1 *= unit;
|
|
bbox.y1 *= unit;
|
|
}
|
|
|
|
for (j = 0; j < glo->dims; j++)
|
|
if (!memcmp(glo->dim[j].u.dim.bbox, &bbox, sizeof (fz_rect)))
|
|
break;
|
|
|
|
if (j < glo->dims)
|
|
return;
|
|
|
|
glo->dim = fz_realloc_array(ctx, glo->dim, glo->dims+1, struct info);
|
|
glo->dims++;
|
|
|
|
glo->dim[glo->dims - 1].page = page;
|
|
glo->dim[glo->dims - 1].pageref = pageref;
|
|
glo->dim[glo->dims - 1].u.dim.bbox = NULL;
|
|
glo->dim[glo->dims - 1].u.dim.bbox = fz_malloc(ctx, sizeof(fz_rect));
|
|
memcpy(glo->dim[glo->dims - 1].u.dim.bbox, &bbox, sizeof (fz_rect));
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
gatherfonts(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *dict)
|
|
{
|
|
int i, n;
|
|
|
|
n = pdf_dict_len(ctx, dict);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
pdf_obj *fontdict = NULL;
|
|
pdf_obj *subtype = NULL;
|
|
pdf_obj *basefont = NULL;
|
|
pdf_obj *name = NULL;
|
|
pdf_obj *encoding = NULL;
|
|
int k;
|
|
|
|
fontdict = pdf_dict_get_val(ctx, dict, i);
|
|
if (!pdf_is_dict(ctx, fontdict))
|
|
{
|
|
fz_warn(ctx, "not a font dict (%d 0 R)", pdf_to_num(ctx, fontdict));
|
|
continue;
|
|
}
|
|
|
|
subtype = pdf_dict_get(ctx, fontdict, PDF_NAME(Subtype));
|
|
basefont = pdf_dict_get(ctx, fontdict, PDF_NAME(BaseFont));
|
|
if (!basefont || pdf_is_null(ctx, basefont))
|
|
name = pdf_dict_get(ctx, fontdict, PDF_NAME(Name));
|
|
encoding = pdf_dict_get(ctx, fontdict, PDF_NAME(Encoding));
|
|
if (pdf_is_dict(ctx, encoding))
|
|
encoding = pdf_dict_get(ctx, encoding, PDF_NAME(BaseEncoding));
|
|
|
|
for (k = 0; k < glo->fonts; k++)
|
|
if (!pdf_objcmp(ctx, glo->font[k].u.font.obj, fontdict))
|
|
break;
|
|
|
|
if (k < glo->fonts)
|
|
continue;
|
|
|
|
glo->font = fz_realloc_array(ctx, glo->font, glo->fonts+1, struct info);
|
|
glo->fonts++;
|
|
|
|
glo->font[glo->fonts - 1].page = page;
|
|
glo->font[glo->fonts - 1].pageref = pageref;
|
|
glo->font[glo->fonts - 1].u.font.obj = fontdict;
|
|
glo->font[glo->fonts - 1].u.font.subtype = subtype;
|
|
glo->font[glo->fonts - 1].u.font.name = basefont ? basefont : name;
|
|
glo->font[glo->fonts - 1].u.font.encoding = encoding;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gatherimages(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *dict)
|
|
{
|
|
int i, n;
|
|
|
|
n = pdf_dict_len(ctx, dict);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
pdf_obj *imagedict;
|
|
pdf_obj *type;
|
|
pdf_obj *width;
|
|
pdf_obj *height;
|
|
pdf_obj *bpc = NULL;
|
|
pdf_obj *filter = NULL;
|
|
pdf_obj *cs = NULL;
|
|
pdf_obj *altcs;
|
|
int k;
|
|
|
|
imagedict = pdf_dict_get_val(ctx, dict, i);
|
|
if (!pdf_is_dict(ctx, imagedict))
|
|
{
|
|
fz_warn(ctx, "not an image dict (%d 0 R)", pdf_to_num(ctx, imagedict));
|
|
continue;
|
|
}
|
|
|
|
type = pdf_dict_get(ctx, imagedict, PDF_NAME(Subtype));
|
|
if (!pdf_name_eq(ctx, type, PDF_NAME(Image)))
|
|
continue;
|
|
|
|
filter = pdf_dict_get(ctx, imagedict, PDF_NAME(Filter));
|
|
|
|
altcs = NULL;
|
|
cs = pdf_dict_get(ctx, imagedict, PDF_NAME(ColorSpace));
|
|
if (pdf_is_array(ctx, cs))
|
|
{
|
|
pdf_obj *cses = cs;
|
|
|
|
cs = pdf_array_get(ctx, cses, 0);
|
|
if (pdf_name_eq(ctx, cs, PDF_NAME(DeviceN)) || pdf_name_eq(ctx, cs, PDF_NAME(Separation)))
|
|
{
|
|
altcs = pdf_array_get(ctx, cses, 2);
|
|
if (pdf_is_array(ctx, altcs))
|
|
altcs = pdf_array_get(ctx, altcs, 0);
|
|
}
|
|
}
|
|
|
|
width = pdf_dict_get(ctx, imagedict, PDF_NAME(Width));
|
|
height = pdf_dict_get(ctx, imagedict, PDF_NAME(Height));
|
|
bpc = pdf_dict_get(ctx, imagedict, PDF_NAME(BitsPerComponent));
|
|
|
|
for (k = 0; k < glo->images; k++)
|
|
if (!pdf_objcmp(ctx, glo->image[k].u.image.obj, imagedict))
|
|
break;
|
|
|
|
if (k < glo->images)
|
|
continue;
|
|
|
|
glo->image = fz_realloc_array(ctx, glo->image, glo->images+1, struct info);
|
|
glo->images++;
|
|
|
|
glo->image[glo->images - 1].page = page;
|
|
glo->image[glo->images - 1].pageref = pageref;
|
|
glo->image[glo->images - 1].u.image.obj = imagedict;
|
|
glo->image[glo->images - 1].u.image.width = width;
|
|
glo->image[glo->images - 1].u.image.height = height;
|
|
glo->image[glo->images - 1].u.image.bpc = bpc;
|
|
glo->image[glo->images - 1].u.image.filter = filter;
|
|
glo->image[glo->images - 1].u.image.cs = cs;
|
|
glo->image[glo->images - 1].u.image.altcs = altcs;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gatherforms(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *dict)
|
|
{
|
|
int i, n;
|
|
|
|
n = pdf_dict_len(ctx, dict);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
pdf_obj *xobjdict;
|
|
pdf_obj *type;
|
|
pdf_obj *subtype;
|
|
pdf_obj *group;
|
|
pdf_obj *groupsubtype;
|
|
pdf_obj *reference;
|
|
int k;
|
|
|
|
xobjdict = pdf_dict_get_val(ctx, dict, i);
|
|
if (!pdf_is_dict(ctx, xobjdict))
|
|
{
|
|
fz_warn(ctx, "not a xobject dict (%d 0 R)", pdf_to_num(ctx, xobjdict));
|
|
continue;
|
|
}
|
|
|
|
type = pdf_dict_get(ctx, xobjdict, PDF_NAME(Subtype));
|
|
if (!pdf_name_eq(ctx, type, PDF_NAME(Form)))
|
|
continue;
|
|
|
|
subtype = pdf_dict_get(ctx, xobjdict, PDF_NAME(Subtype2));
|
|
if (!pdf_name_eq(ctx, subtype, PDF_NAME(PS)))
|
|
continue;
|
|
|
|
group = pdf_dict_get(ctx, xobjdict, PDF_NAME(Group));
|
|
groupsubtype = pdf_dict_get(ctx, group, PDF_NAME(S));
|
|
reference = pdf_dict_get(ctx, xobjdict, PDF_NAME(Ref));
|
|
|
|
for (k = 0; k < glo->forms; k++)
|
|
if (!pdf_objcmp(ctx, glo->form[k].u.form.obj, xobjdict))
|
|
break;
|
|
|
|
if (k < glo->forms)
|
|
continue;
|
|
|
|
glo->form = fz_realloc_array(ctx, glo->form, glo->forms+1, struct info);
|
|
glo->forms++;
|
|
|
|
glo->form[glo->forms - 1].page = page;
|
|
glo->form[glo->forms - 1].pageref = pageref;
|
|
glo->form[glo->forms - 1].u.form.obj = xobjdict;
|
|
glo->form[glo->forms - 1].u.form.groupsubtype = groupsubtype;
|
|
glo->form[glo->forms - 1].u.form.reference = reference;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gatherpsobjs(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *dict)
|
|
{
|
|
int i, n;
|
|
|
|
n = pdf_dict_len(ctx, dict);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
pdf_obj *xobjdict;
|
|
pdf_obj *type;
|
|
pdf_obj *subtype;
|
|
int k;
|
|
|
|
xobjdict = pdf_dict_get_val(ctx, dict, i);
|
|
if (!pdf_is_dict(ctx, xobjdict))
|
|
{
|
|
fz_warn(ctx, "not a xobject dict (%d 0 R)", pdf_to_num(ctx, xobjdict));
|
|
continue;
|
|
}
|
|
|
|
type = pdf_dict_get(ctx, xobjdict, PDF_NAME(Subtype));
|
|
subtype = pdf_dict_get(ctx, xobjdict, PDF_NAME(Subtype2));
|
|
if (!pdf_name_eq(ctx, type, PDF_NAME(PS)) &&
|
|
(!pdf_name_eq(ctx, type, PDF_NAME(Form)) || !pdf_name_eq(ctx, subtype, PDF_NAME(PS))))
|
|
continue;
|
|
|
|
for (k = 0; k < glo->psobjs; k++)
|
|
if (!pdf_objcmp(ctx, glo->psobj[k].u.form.obj, xobjdict))
|
|
break;
|
|
|
|
if (k < glo->psobjs)
|
|
continue;
|
|
|
|
glo->psobj = fz_realloc_array(ctx, glo->psobj, glo->psobjs+1, struct info);
|
|
glo->psobjs++;
|
|
|
|
glo->psobj[glo->psobjs - 1].page = page;
|
|
glo->psobj[glo->psobjs - 1].pageref = pageref;
|
|
glo->psobj[glo->psobjs - 1].u.form.obj = xobjdict;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gathershadings(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *dict)
|
|
{
|
|
int i, n;
|
|
|
|
n = pdf_dict_len(ctx, dict);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
pdf_obj *shade;
|
|
pdf_obj *type;
|
|
int k;
|
|
|
|
shade = pdf_dict_get_val(ctx, dict, i);
|
|
if (!pdf_is_dict(ctx, shade))
|
|
{
|
|
fz_warn(ctx, "not a shading dict (%d 0 R)", pdf_to_num(ctx, shade));
|
|
continue;
|
|
}
|
|
|
|
type = pdf_dict_get(ctx, shade, PDF_NAME(ShadingType));
|
|
if (!pdf_is_int(ctx, type) || pdf_to_int(ctx, type) < 1 || pdf_to_int(ctx, type) > 7)
|
|
{
|
|
fz_warn(ctx, "not a shading type (%d 0 R)", pdf_to_num(ctx, shade));
|
|
type = NULL;
|
|
}
|
|
|
|
for (k = 0; k < glo->shadings; k++)
|
|
if (!pdf_objcmp(ctx, glo->shading[k].u.shading.obj, shade))
|
|
break;
|
|
|
|
if (k < glo->shadings)
|
|
continue;
|
|
|
|
glo->shading = fz_realloc_array(ctx, glo->shading, glo->shadings+1, struct info);
|
|
glo->shadings++;
|
|
|
|
glo->shading[glo->shadings - 1].page = page;
|
|
glo->shading[glo->shadings - 1].pageref = pageref;
|
|
glo->shading[glo->shadings - 1].u.shading.obj = shade;
|
|
glo->shading[glo->shadings - 1].u.shading.type = type;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gatherpatterns(fz_context *ctx, globals *glo, int page, pdf_obj *pageref, pdf_obj *dict)
|
|
{
|
|
int i, n;
|
|
|
|
n = pdf_dict_len(ctx, dict);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
pdf_obj *patterndict;
|
|
pdf_obj *type;
|
|
pdf_obj *paint = NULL;
|
|
pdf_obj *tiling = NULL;
|
|
pdf_obj *shading = NULL;
|
|
int k;
|
|
|
|
patterndict = pdf_dict_get_val(ctx, dict, i);
|
|
if (!pdf_is_dict(ctx, patterndict))
|
|
{
|
|
fz_warn(ctx, "not a pattern dict (%d 0 R)", pdf_to_num(ctx, patterndict));
|
|
continue;
|
|
}
|
|
|
|
type = pdf_dict_get(ctx, patterndict, PDF_NAME(PatternType));
|
|
if (!pdf_is_int(ctx, type) || pdf_to_int(ctx, type) < 1 || pdf_to_int(ctx, type) > 2)
|
|
{
|
|
fz_warn(ctx, "not a pattern type (%d 0 R)", pdf_to_num(ctx, patterndict));
|
|
type = NULL;
|
|
}
|
|
|
|
if (pdf_to_int(ctx, type) == 1)
|
|
{
|
|
paint = pdf_dict_get(ctx, patterndict, PDF_NAME(PaintType));
|
|
if (!pdf_is_int(ctx, paint) || pdf_to_int(ctx, paint) < 1 || pdf_to_int(ctx, paint) > 2)
|
|
{
|
|
fz_warn(ctx, "not a pattern paint type (%d 0 R)", pdf_to_num(ctx, patterndict));
|
|
paint = NULL;
|
|
}
|
|
|
|
tiling = pdf_dict_get(ctx, patterndict, PDF_NAME(TilingType));
|
|
if (!pdf_is_int(ctx, tiling) || pdf_to_int(ctx, tiling) < 1 || pdf_to_int(ctx, tiling) > 3)
|
|
{
|
|
fz_warn(ctx, "not a pattern tiling type (%d 0 R)", pdf_to_num(ctx, patterndict));
|
|
tiling = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
shading = pdf_dict_get(ctx, patterndict, PDF_NAME(Shading));
|
|
}
|
|
|
|
for (k = 0; k < glo->patterns; k++)
|
|
if (!pdf_objcmp(ctx, glo->pattern[k].u.pattern.obj, patterndict))
|
|
break;
|
|
|
|
if (k < glo->patterns)
|
|
continue;
|
|
|
|
glo->pattern = fz_realloc_array(ctx, glo->pattern, glo->patterns+1, struct info);
|
|
glo->patterns++;
|
|
|
|
glo->pattern[glo->patterns - 1].page = page;
|
|
glo->pattern[glo->patterns - 1].pageref = pageref;
|
|
glo->pattern[glo->patterns - 1].u.pattern.obj = patterndict;
|
|
glo->pattern[glo->patterns - 1].u.pattern.type = type;
|
|
glo->pattern[glo->patterns - 1].u.pattern.paint = paint;
|
|
glo->pattern[glo->patterns - 1].u.pattern.tiling = tiling;
|
|
glo->pattern[glo->patterns - 1].u.pattern.shading = shading;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gatherresourceinfo(fz_context *ctx, globals *glo, int page, pdf_obj *rsrc, int show)
|
|
{
|
|
pdf_obj *pageref;
|
|
pdf_obj *font;
|
|
pdf_obj *xobj;
|
|
pdf_obj *shade;
|
|
pdf_obj *pattern;
|
|
pdf_obj *subrsrc;
|
|
int i;
|
|
|
|
pageref = pdf_lookup_page_obj(ctx, glo->doc, page-1);
|
|
if (!pageref)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot retrieve info from page %d", page);
|
|
|
|
/* stop on cyclic resource dependencies */
|
|
if (pdf_mark_obj(ctx, rsrc))
|
|
return;
|
|
|
|
fz_try(ctx)
|
|
{
|
|
font = pdf_dict_get(ctx, rsrc, PDF_NAME(Font));
|
|
if (show & FONTS && font)
|
|
{
|
|
int n;
|
|
|
|
gatherfonts(ctx, glo, page, pageref, font);
|
|
n = pdf_dict_len(ctx, font);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
pdf_obj *obj = pdf_dict_get_val(ctx, font, i);
|
|
|
|
subrsrc = pdf_dict_get(ctx, obj, PDF_NAME(Resources));
|
|
if (subrsrc && pdf_objcmp(ctx, rsrc, subrsrc))
|
|
gatherresourceinfo(ctx, glo, page, subrsrc, show);
|
|
}
|
|
}
|
|
|
|
xobj = pdf_dict_get(ctx, rsrc, PDF_NAME(XObject));
|
|
if (show & (IMAGES|XOBJS) && xobj)
|
|
{
|
|
int n;
|
|
|
|
if (show & IMAGES)
|
|
gatherimages(ctx, glo, page, pageref, xobj);
|
|
if (show & XOBJS)
|
|
{
|
|
gatherforms(ctx, glo, page, pageref, xobj);
|
|
gatherpsobjs(ctx, glo, page, pageref, xobj);
|
|
}
|
|
n = pdf_dict_len(ctx, xobj);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
pdf_obj *obj = pdf_dict_get_val(ctx, xobj, i);
|
|
subrsrc = pdf_dict_get(ctx, obj, PDF_NAME(Resources));
|
|
if (subrsrc && pdf_objcmp(ctx, rsrc, subrsrc))
|
|
gatherresourceinfo(ctx, glo, page, subrsrc, show);
|
|
}
|
|
}
|
|
|
|
shade = pdf_dict_get(ctx, rsrc, PDF_NAME(Shading));
|
|
if (show & SHADINGS && shade)
|
|
gathershadings(ctx, glo, page, pageref, shade);
|
|
|
|
pattern = pdf_dict_get(ctx, rsrc, PDF_NAME(Pattern));
|
|
if (show & PATTERNS && pattern)
|
|
{
|
|
int n;
|
|
gatherpatterns(ctx, glo, page, pageref, pattern);
|
|
n = pdf_dict_len(ctx, pattern);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
pdf_obj *obj = pdf_dict_get_val(ctx, pattern, i);
|
|
subrsrc = pdf_dict_get(ctx, obj, PDF_NAME(Resources));
|
|
if (subrsrc && pdf_objcmp(ctx, rsrc, subrsrc))
|
|
gatherresourceinfo(ctx, glo, page, subrsrc, show);
|
|
}
|
|
}
|
|
}
|
|
fz_always(ctx)
|
|
pdf_unmark_obj(ctx, rsrc);
|
|
fz_catch(ctx)
|
|
fz_rethrow(ctx);
|
|
}
|
|
|
|
static void
|
|
gatherpageinfo(fz_context *ctx, globals *glo, int page, int show)
|
|
{
|
|
pdf_obj *pageref;
|
|
pdf_obj *rsrc;
|
|
|
|
pageref = pdf_lookup_page_obj(ctx, glo->doc, page-1);
|
|
|
|
if (!pageref)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "cannot retrieve info from page %d", page);
|
|
|
|
gatherdimensions(ctx, glo, page, pageref);
|
|
|
|
rsrc = pdf_dict_get(ctx, pageref, PDF_NAME(Resources));
|
|
gatherresourceinfo(ctx, glo, page, rsrc, show);
|
|
}
|
|
|
|
static void
|
|
printinfo(fz_context *ctx, globals *glo, char *filename, int show, int page)
|
|
{
|
|
int i;
|
|
int j;
|
|
fz_output *out = glo->out;
|
|
|
|
#define PAGE_FMT_zu "\t%d\t(%d 0 R):\t"
|
|
|
|
if (show & DIMENSIONS && glo->dims > 0)
|
|
{
|
|
fz_write_printf(ctx, out, "Mediaboxes (%d):\n", glo->dims);
|
|
for (i = 0; i < glo->dims; i++)
|
|
{
|
|
fz_write_printf(ctx, out, PAGE_FMT_zu "[ %g %g %g %g ]\n",
|
|
glo->dim[i].page,
|
|
pdf_to_num(ctx, glo->dim[i].pageref),
|
|
glo->dim[i].u.dim.bbox->x0,
|
|
glo->dim[i].u.dim.bbox->y0,
|
|
glo->dim[i].u.dim.bbox->x1,
|
|
glo->dim[i].u.dim.bbox->y1);
|
|
}
|
|
fz_write_printf(ctx, out, "\n");
|
|
}
|
|
|
|
if (show & FONTS && glo->fonts > 0)
|
|
{
|
|
fz_write_printf(ctx, out, "Fonts (%d):\n", glo->fonts);
|
|
for (i = 0; i < glo->fonts; i++)
|
|
{
|
|
fz_write_printf(ctx, out, PAGE_FMT_zu "%s '%s' %s%s(%d 0 R)\n",
|
|
glo->font[i].page,
|
|
pdf_to_num(ctx, glo->font[i].pageref),
|
|
pdf_to_name(ctx, glo->font[i].u.font.subtype),
|
|
pdf_to_name(ctx, glo->font[i].u.font.name),
|
|
glo->font[i].u.font.encoding ? pdf_to_name(ctx, glo->font[i].u.font.encoding) : "",
|
|
glo->font[i].u.font.encoding ? " " : "",
|
|
pdf_to_num(ctx, glo->font[i].u.font.obj));
|
|
}
|
|
fz_write_printf(ctx, out, "\n");
|
|
}
|
|
|
|
if (show & IMAGES && glo->images > 0)
|
|
{
|
|
fz_write_printf(ctx, out, "Images (%d):\n", glo->images);
|
|
for (i = 0; i < glo->images; i++)
|
|
{
|
|
char *cs = NULL;
|
|
char *altcs = NULL;
|
|
|
|
fz_write_printf(ctx, out, PAGE_FMT_zu "[ ",
|
|
glo->image[i].page,
|
|
pdf_to_num(ctx, glo->image[i].pageref));
|
|
|
|
if (pdf_is_array(ctx, glo->image[i].u.image.filter))
|
|
{
|
|
int n = pdf_array_len(ctx, glo->image[i].u.image.filter);
|
|
for (j = 0; j < n; j++)
|
|
{
|
|
pdf_obj *obj = pdf_array_get(ctx, glo->image[i].u.image.filter, j);
|
|
char *filter = fz_strdup(ctx, pdf_to_name(ctx, obj));
|
|
|
|
if (strstr(filter, "Decode"))
|
|
*(strstr(filter, "Decode")) = '\0';
|
|
|
|
fz_write_printf(ctx, out, "%s%s",
|
|
filter,
|
|
j == pdf_array_len(ctx, glo->image[i].u.image.filter) - 1 ? "" : " ");
|
|
fz_free(ctx, filter);
|
|
}
|
|
}
|
|
else if (glo->image[i].u.image.filter)
|
|
{
|
|
pdf_obj *obj = glo->image[i].u.image.filter;
|
|
char *filter = fz_strdup(ctx, pdf_to_name(ctx, obj));
|
|
|
|
if (strstr(filter, "Decode"))
|
|
*(strstr(filter, "Decode")) = '\0';
|
|
|
|
fz_write_printf(ctx, out, "%s", filter);
|
|
fz_free(ctx, filter);
|
|
}
|
|
else
|
|
fz_write_printf(ctx, out, "Raw");
|
|
|
|
if (glo->image[i].u.image.cs)
|
|
{
|
|
cs = fz_strdup(ctx, pdf_to_name(ctx, glo->image[i].u.image.cs));
|
|
|
|
if (!strncmp(cs, "Device", 6))
|
|
{
|
|
size_t len = strlen(cs + 6);
|
|
memmove(cs + 3, cs + 6, len + 1);
|
|
cs[3 + len + 1] = '\0';
|
|
}
|
|
if (strstr(cs, "ICC"))
|
|
fz_strlcpy(cs, "ICC", 4);
|
|
if (strstr(cs, "Indexed"))
|
|
fz_strlcpy(cs, "Idx", 4);
|
|
if (strstr(cs, "Pattern"))
|
|
fz_strlcpy(cs, "Pat", 4);
|
|
if (strstr(cs, "Separation"))
|
|
fz_strlcpy(cs, "Sep", 4);
|
|
}
|
|
if (glo->image[i].u.image.altcs)
|
|
{
|
|
altcs = fz_strdup(ctx, pdf_to_name(ctx, glo->image[i].u.image.altcs));
|
|
|
|
if (!strncmp(altcs, "Device", 6))
|
|
{
|
|
size_t len = strlen(altcs + 6);
|
|
memmove(altcs + 3, altcs + 6, len + 1);
|
|
altcs[3 + len + 1] = '\0';
|
|
}
|
|
if (strstr(altcs, "ICC"))
|
|
fz_strlcpy(altcs, "ICC", 4);
|
|
if (strstr(altcs, "Indexed"))
|
|
fz_strlcpy(altcs, "Idx", 4);
|
|
if (strstr(altcs, "Pattern"))
|
|
fz_strlcpy(altcs, "Pat", 4);
|
|
if (strstr(altcs, "Separation"))
|
|
fz_strlcpy(altcs, "Sep", 4);
|
|
}
|
|
|
|
fz_write_printf(ctx, out, " ] %dx%d %dbpc %s%s%s (%d 0 R)\n",
|
|
pdf_to_int(ctx, glo->image[i].u.image.width),
|
|
pdf_to_int(ctx, glo->image[i].u.image.height),
|
|
glo->image[i].u.image.bpc ? pdf_to_int(ctx, glo->image[i].u.image.bpc) : 1,
|
|
glo->image[i].u.image.cs ? cs : "ImageMask",
|
|
glo->image[i].u.image.altcs ? " " : "",
|
|
glo->image[i].u.image.altcs ? altcs : "",
|
|
pdf_to_num(ctx, glo->image[i].u.image.obj));
|
|
|
|
fz_free(ctx, cs);
|
|
fz_free(ctx, altcs);
|
|
}
|
|
fz_write_printf(ctx, out, "\n");
|
|
}
|
|
|
|
if (show & SHADINGS && glo->shadings > 0)
|
|
{
|
|
fz_write_printf(ctx, out, "Shading patterns (%d):\n", glo->shadings);
|
|
for (i = 0; i < glo->shadings; i++)
|
|
{
|
|
char *shadingtype[] =
|
|
{
|
|
"",
|
|
"Function",
|
|
"Axial",
|
|
"Radial",
|
|
"Triangle mesh",
|
|
"Lattice",
|
|
"Coons patch",
|
|
"Tensor patch",
|
|
};
|
|
|
|
fz_write_printf(ctx, out, PAGE_FMT_zu "%s (%d 0 R)\n",
|
|
glo->shading[i].page,
|
|
pdf_to_num(ctx, glo->shading[i].pageref),
|
|
shadingtype[pdf_to_int(ctx, glo->shading[i].u.shading.type)],
|
|
pdf_to_num(ctx, glo->shading[i].u.shading.obj));
|
|
}
|
|
fz_write_printf(ctx, out, "\n");
|
|
}
|
|
|
|
if (show & PATTERNS && glo->patterns > 0)
|
|
{
|
|
fz_write_printf(ctx, out, "Patterns (%d):\n", glo->patterns);
|
|
for (i = 0; i < glo->patterns; i++)
|
|
{
|
|
if (pdf_to_int(ctx, glo->pattern[i].u.pattern.type) == 1)
|
|
{
|
|
char *painttype[] =
|
|
{
|
|
"",
|
|
"Colored",
|
|
"Uncolored",
|
|
};
|
|
char *tilingtype[] =
|
|
{
|
|
"",
|
|
"Constant",
|
|
"No distortion",
|
|
"Constant/fast tiling",
|
|
};
|
|
|
|
fz_write_printf(ctx, out, PAGE_FMT_zu "Tiling %s %s (%d 0 R)\n",
|
|
glo->pattern[i].page,
|
|
pdf_to_num(ctx, glo->pattern[i].pageref),
|
|
painttype[pdf_to_int(ctx, glo->pattern[i].u.pattern.paint)],
|
|
tilingtype[pdf_to_int(ctx, glo->pattern[i].u.pattern.tiling)],
|
|
pdf_to_num(ctx, glo->pattern[i].u.pattern.obj));
|
|
}
|
|
else
|
|
{
|
|
fz_write_printf(ctx, out, PAGE_FMT_zu "Shading %d 0 R (%d 0 R)\n",
|
|
glo->pattern[i].page,
|
|
pdf_to_num(ctx, glo->pattern[i].pageref),
|
|
pdf_to_num(ctx, glo->pattern[i].u.pattern.shading),
|
|
pdf_to_num(ctx, glo->pattern[i].u.pattern.obj));
|
|
}
|
|
}
|
|
fz_write_printf(ctx, out, "\n");
|
|
}
|
|
|
|
if (show & XOBJS && glo->forms > 0)
|
|
{
|
|
fz_write_printf(ctx, out, "Form xobjects (%d):\n", glo->forms);
|
|
for (i = 0; i < glo->forms; i++)
|
|
{
|
|
fz_write_printf(ctx, out, PAGE_FMT_zu "Form%s%s%s%s (%d 0 R)\n",
|
|
glo->form[i].page,
|
|
pdf_to_num(ctx, glo->form[i].pageref),
|
|
glo->form[i].u.form.groupsubtype ? " " : "",
|
|
glo->form[i].u.form.groupsubtype ? pdf_to_name(ctx, glo->form[i].u.form.groupsubtype) : "",
|
|
glo->form[i].u.form.groupsubtype ? " Group" : "",
|
|
glo->form[i].u.form.reference ? " Reference" : "",
|
|
pdf_to_num(ctx, glo->form[i].u.form.obj));
|
|
}
|
|
fz_write_printf(ctx, out, "\n");
|
|
}
|
|
|
|
if (show & XOBJS && glo->psobjs > 0)
|
|
{
|
|
fz_write_printf(ctx, out, "Postscript xobjects (%d):\n", glo->psobjs);
|
|
for (i = 0; i < glo->psobjs; i++)
|
|
{
|
|
fz_write_printf(ctx, out, PAGE_FMT_zu "(%d 0 R)\n",
|
|
glo->psobj[i].page,
|
|
pdf_to_num(ctx, glo->psobj[i].pageref),
|
|
pdf_to_num(ctx, glo->psobj[i].u.form.obj));
|
|
}
|
|
fz_write_printf(ctx, out, "\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
showinfo(fz_context *ctx, globals *glo, char *filename, int show, const char *pagelist)
|
|
{
|
|
int page, spage, epage;
|
|
int allpages;
|
|
int pagecount;
|
|
fz_output *out = glo->out;
|
|
|
|
if (!glo->doc)
|
|
infousage();
|
|
|
|
allpages = !strcmp(pagelist, "1-N");
|
|
|
|
pagecount = pdf_count_pages(ctx, glo->doc);
|
|
|
|
while ((pagelist = fz_parse_page_range(ctx, pagelist, &spage, &epage, pagecount)))
|
|
{
|
|
if (allpages)
|
|
fz_write_printf(ctx, out, "Retrieving info from pages %d-%d...\n", spage, epage);
|
|
for (page = spage; page <= epage; page++)
|
|
{
|
|
gatherpageinfo(ctx, glo, page, show);
|
|
if (!allpages)
|
|
{
|
|
fz_write_printf(ctx, out, "Page %d:\n", page);
|
|
printinfo(ctx, glo, filename, show, page);
|
|
fz_write_printf(ctx, out, "\n");
|
|
clearinfo(ctx, glo);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (allpages)
|
|
printinfo(ctx, glo, filename, show, -1);
|
|
}
|
|
|
|
static void
|
|
pdfinfo_info(fz_context *ctx, fz_output *out, char *filename, char *password, int show, char *argv[], int argc)
|
|
{
|
|
enum { NO_FILE_OPENED, NO_INFO_GATHERED, INFO_SHOWN } state;
|
|
int argidx = 0;
|
|
globals glo = { 0 };
|
|
|
|
glo.out = out;
|
|
glo.ctx = ctx;
|
|
|
|
state = NO_FILE_OPENED;
|
|
|
|
fz_try(ctx)
|
|
{
|
|
while (argidx < argc)
|
|
{
|
|
if (state == NO_FILE_OPENED || !fz_is_page_range(ctx, argv[argidx]))
|
|
{
|
|
if (state == NO_INFO_GATHERED)
|
|
{
|
|
showinfo(ctx, &glo, filename, show, "1-N");
|
|
}
|
|
|
|
closexref(ctx, &glo);
|
|
|
|
filename = argv[argidx];
|
|
fz_write_printf(ctx, out, "%s:\n", filename);
|
|
glo.doc = pdf_open_document(glo.ctx, filename);
|
|
if (pdf_needs_password(ctx, glo.doc))
|
|
if (!pdf_authenticate_password(ctx, glo.doc, password))
|
|
fz_throw(glo.ctx, FZ_ERROR_GENERIC, "cannot authenticate password: %s", filename);
|
|
glo.pagecount = pdf_count_pages(ctx, glo.doc);
|
|
|
|
showglobalinfo(ctx, &glo);
|
|
state = NO_INFO_GATHERED;
|
|
}
|
|
else
|
|
{
|
|
showinfo(ctx, &glo, filename, show, argv[argidx]);
|
|
state = INFO_SHOWN;
|
|
}
|
|
|
|
argidx++;
|
|
}
|
|
|
|
if (state == NO_INFO_GATHERED)
|
|
showinfo(ctx, &glo, filename, show, "1-N");
|
|
}
|
|
fz_always(ctx)
|
|
closexref(ctx, &glo);
|
|
fz_catch(ctx)
|
|
fz_rethrow(ctx);
|
|
}
|
|
|
|
int pdfinfo_main(int argc, char **argv)
|
|
{
|
|
char *filename = "";
|
|
char *password = "";
|
|
int show = ALL;
|
|
int c;
|
|
int ret;
|
|
fz_context *ctx;
|
|
|
|
while ((c = fz_getopt(argc, argv, "FISPXMp:")) != -1)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'F': if (show == ALL) show = FONTS; else show |= FONTS; break;
|
|
case 'I': if (show == ALL) show = IMAGES; else show |= IMAGES; break;
|
|
case 'S': if (show == ALL) show = SHADINGS; else show |= SHADINGS; break;
|
|
case 'P': if (show == ALL) show = PATTERNS; else show |= PATTERNS; break;
|
|
case 'X': if (show == ALL) show = XOBJS; else show |= XOBJS; break;
|
|
case 'M': if (show == ALL) show = DIMENSIONS; else show |= DIMENSIONS; break;
|
|
case 'p': password = fz_optarg; break;
|
|
default:
|
|
infousage();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fz_optind == argc)
|
|
infousage();
|
|
|
|
ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
|
|
if (!ctx)
|
|
{
|
|
fprintf(stderr, "cannot initialise context\n");
|
|
exit(1);
|
|
}
|
|
|
|
ret = 0;
|
|
fz_try(ctx)
|
|
pdfinfo_info(ctx, fz_stdout(ctx), filename, password, show, &argv[fz_optind], argc-fz_optind);
|
|
fz_catch(ctx)
|
|
ret = 1;
|
|
fz_drop_context(ctx);
|
|
return ret;
|
|
}
|