Repository navigation
Expand file tree
/
Copy pathobjects.cc
More file actions
95 lines (73 loc) · 2.7 KB
/
Copy pathobjects.cc
File metadata and controls
95 lines (73 loc) · 2.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// -*- mode: c++ -*-
// Copyright 2026 Thinkoid, LLC
//
// ypdf-objects: enumerate the indirect objects in a PDF.
//
// ypdf-objects sample.pdf
//
// The document is loaded through its cross-reference chain, so the listing
// covers objects packed into compressed object streams too.
//
#include <ypdf/model/doc.hh>
#include <boost/iostreams/device/mapped_file.hpp>
#include <cstdio>
namespace ast = ypdf::parser::ast;
namespace io = boost::iostreams;
namespace model = ypdf::model;
// The variant alternative, spelled out for the listing.
static const char *kind(const ast::obj_t &obj)
{
return boost::apply_visitor(
ypdf::detail::overloaded_{
[](const ast::null_t &) { return "null"; },
[](bool) { return "bool"; },
[](int) { return "int"; },
[](double) { return "real"; },
[](const ast::ref_t &) { return "ref"; },
[](const ast::string_t &) { return "string"; },
[](const ast::name_t &) { return "name"; },
[](const ast::array_t &) { return "array"; },
[](const ast::dict_t &) { return "dict"; },
[](const ast::stream_t &) { return "stream"; } },
obj);
}
int main(int argc, char **argv)
{
if (argc < 2) {
std::fprintf(stderr, "usage: ypdf-objects <file.pdf>\n");
return 2;
}
io::mapped_file_source src(argv[1]);
auto doc = model::load(src.data(), src.data() + src.size());
if (!doc) {
std::fprintf(stderr, "ypdf-objects: %s: no objects\n", argv[1]);
return 1;
}
size_t n = 0;
for (const auto &xref : doc->xrefs) {
const auto &iobj = model::fetch(*doc, xref);
// A free entry, or one the fetch could not resolve, has no body.
if (iobj.body.empty())
continue;
const auto &head = iobj.body.front();
const auto &ref = iobj.xref.ref();
std::printf("%5d %d %-6s", ref.num, ref.gen, kind(head));
// /Type is what a reader recognizes the object by, when it says.
if (ast::is< ast::dict_t >(head)) {
const auto &dict = ast::as< ast::dict_t >(head);
if (ast::has(dict, "/Type")) {
const auto &type = ast::at(dict, "/Type");
std::printf(" %s", ast::as< ast::name_t >(type).c_str());
}
}
// A dictionary followed by its stream data: name the head above,
// measure the tail here.
if (iobj.body.size() > 1 && ast::is< ast::stream_t >(iobj.body.back()))
std::printf(" [%zu bytes]",
ast::as< ast::stream_t >(iobj.body.back()).size());
std::printf("\n");
++n;
}
std::printf("%zu objects\n", n);
return 0;
}