/* * Information tool. * Print information about the input pdf. */ #include "mupdf/fitz.h" #include "mupdf/pdf.h" #include #include #include 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; }