Skip to content

Commit 07a8307

Browse files
authored
Fix some PDF test files (#350)
1 parent 3680739 commit 07a8307

21 files changed

Lines changed: 448 additions & 459 deletions

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ set(ODR_SOURCE_FILES
141141
"src/odr/internal/pdf/pdf_document_element.cpp"
142142
"src/odr/internal/pdf/pdf_document_parser.cpp"
143143
"src/odr/internal/pdf/pdf_file.cpp"
144+
"src/odr/internal/pdf/pdf_file_object.cpp"
144145
"src/odr/internal/pdf/pdf_file_parser.cpp"
145146
"src/odr/internal/pdf/pdf_graphics_operator.cpp"
146147
"src/odr/internal/pdf/pdf_graphics_operator_parser.cpp"

src/odr/internal/html/pdf_file.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,17 @@ Html html::translate_pdf_file(const PdfFile &pdf_file,
7171
o << "height:" << page_box[3].as_real() / 72.0 << "in;";
7272
}));
7373

74-
pdf::IndirectObject page_contents_object =
75-
parser.read_object(page->contents_reference);
76-
std::string stream = parser.read_object_stream(page_contents_object);
77-
std::string page_content = crypto::util::zlib_inflate(stream);
74+
std::string stream;
75+
for (const auto &content_reference : page->contents_reference) {
76+
pdf::IndirectObject page_contents_object =
77+
parser.read_object(content_reference);
78+
std::string page_content =
79+
parser.read_object_stream(page_contents_object);
80+
page_content = crypto::util::zlib_inflate(page_content);
81+
stream += page_content;
82+
}
7883

79-
std::istringstream ss(page_content);
84+
std::istringstream ss(stream);
8085
pdf::GraphicsOperatorParser parser2(ss);
8186
pdf::GraphicsState state;
8287
while (!ss.eof()) {

src/odr/internal/pdf/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Apart from that PDF can also modify the spacing between the characters for examp
4141
- Document Structure https://www.oreilly.com/library/view/pdf-explained/9781449321581/ch04.html
4242
- Cross-reference stream https://www.verypdf.com/document/pdf-format-reference/pg_0106.htm
4343
- Graphics Operators
44-
- https://gendignoux.com/blog/images/pdf-graphics/cheat-sheet-by-nc-sa.png
44+
- https://github.com/gendx/pdf-cheat-sheets/blob/master/pdf-graphics.clean.pdf
4545
- https://pdfa.org/wp-content/uploads/2023/08/PDF-Operators-CheatSheet.pdf
4646
- General https://gendignoux.com/blog/2017/01/05/pdf-graphics.html
4747
- Text https://www.syncfusion.com/succinctly-free-ebooks/pdf/text-operators

src/odr/internal/pdf/pdf_cmap_parser.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,14 @@ std::variant<Object, std::string> CMapParser::read_token() const {
3737

3838
std::string token;
3939
while (true) {
40-
int_type c = sb().sgetc();
40+
int_type c = m_parser.geti();
4141
if (c == eof) {
42-
in().setstate(std::ios::eofbit);
4342
return token;
4443
}
4544
if (ObjectParser::is_whitespace(c)) {
4645
return token;
4746
}
48-
sb().sbumpc();
47+
m_parser.bumpc();
4948
token += (char_type)c;
5049
}
5150
}
@@ -76,10 +75,10 @@ void CMapParser::read_bfchar(std::uint32_t n, CMap &cmap) const {
7675
reinterpret_cast<const char16_t *>(unicode.data()), unicode.size() / 2);
7776

7877
if (glyph.length() != 1) {
79-
throw std::runtime_error("unexpected glyph length");
78+
std::cerr << "unexpected glyph length" << std::endl;
8079
}
8180
if (unicode16.length() != 1) {
82-
throw std::runtime_error("unexpected unicode length");
81+
std::cerr << "unexpected unicode length" << std::endl;
8382
}
8483

8584
cmap.map_bfchar(glyph[0], unicode16[0]);

src/odr/internal/pdf/pdf_document_element.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ struct Page : Element {
4949
std::vector<Annotation *> annotations;
5050

5151
// TODO remove
52-
ObjectReference contents_reference;
52+
std::vector<ObjectReference> contents_reference;
5353
};
5454

5555
struct Annotation : Element {};

src/odr/internal/pdf/pdf_document_parser.cpp

Lines changed: 87 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include <odr/internal/pdf/pdf_document_element.hpp>
77
#include <odr/internal/pdf/pdf_file_parser.hpp>
88

9+
#include <functional>
10+
#include <iostream>
911
#include <sstream>
1012

1113
namespace odr::internal::pdf {
@@ -27,7 +29,7 @@ pdf::Font *parse_font(DocumentParser &parser, const ObjectReference &reference,
2729
font->object = dictionary;
2830

2931
if (dictionary.has_key("ToUnicode")) {
30-
auto to_unicode_obj =
32+
IndirectObject to_unicode_obj =
3133
parser.read_object(dictionary["ToUnicode"].as_reference());
3234
std::string stream = parser.read_object_stream(to_unicode_obj);
3335
std::string inflate = crypto::util::zlib_inflate(stream);
@@ -39,26 +41,21 @@ pdf::Font *parse_font(DocumentParser &parser, const ObjectReference &reference,
3941
return font;
4042
}
4143

42-
pdf::Resources *parse_resources(DocumentParser &parser,
43-
const ObjectReference &reference,
44+
pdf::Resources *parse_resources(DocumentParser &parser, const Object &object,
4445
Document &document) {
4546
Resources *resources = document.create_element<Resources>();
4647

47-
IndirectObject object = parser.read_object(reference);
48-
const Dictionary &dictionary = object.object.as_dictionary();
48+
Dictionary dictionary = parser.resolve_object_copy(object).as_dictionary();
4949

5050
resources->type = Type::resources;
51-
resources->object_reference = reference;
5251
resources->object = dictionary;
5352

54-
if (dictionary["Font"].is_reference()) {
55-
Dictionary table = parser.read_object(dictionary["Font"].as_reference())
56-
.object.as_dictionary();
57-
for (const auto &[key, value] : table) {
53+
if (!dictionary["Font"].is_null()) {
54+
Dictionary font_table =
55+
parser.resolve_object_copy(dictionary["Font"]).as_dictionary();
56+
for (const auto &[key, value] : font_table) {
5857
resources->font[key] = parse_font(parser, value.as_reference(), document);
5958
}
60-
} else {
61-
throw std::runtime_error("problem");
6259
}
6360

6461
return resources;
@@ -90,12 +87,21 @@ pdf::Page *parse_page(DocumentParser &parser, const ObjectReference &reference,
9087
page->object_reference = reference;
9188
page->object = dictionary;
9289
page->parent = dynamic_cast<Pages *>(parent);
93-
page->contents_reference = dictionary["Contents"].as_reference();
94-
page->resources =
95-
parse_resources(parser, dictionary["Resources"].as_reference(), document);
90+
page->resources = parse_resources(parser, dictionary["Resources"], document);
91+
92+
if (dictionary["Contents"].is_reference()) {
93+
page->contents_reference = {dictionary["Contents"].as_reference()};
94+
} else {
95+
for (const Object &e : dictionary["Contents"].as_array()) {
96+
page->contents_reference.push_back(e.as_reference());
97+
}
98+
}
9699

97100
if (dictionary.has_key("Annots")) {
98-
for (Object annotation : dictionary["Annots"].as_array()) {
101+
// TODO why rvalue not working?
102+
Array annotations =
103+
parser.resolve_object_copy(dictionary["Annots"]).as_array();
104+
for (const Object &annotation : annotations) {
99105
page->annotations.push_back(
100106
parse_annotation(parser, annotation.as_reference(), document));
101107
}
@@ -116,7 +122,7 @@ pdf::Pages *parse_pages(DocumentParser &parser,
116122
pages->object = dictionary;
117123
pages->count = dictionary["Count"].as_integer();
118124

119-
for (const auto &kid : dictionary["Kids"].as_array()) {
125+
for (const Object &kid : dictionary["Kids"].as_array()) {
120126
pages->kids.push_back(
121127
parse_page_or_pages(parser, kid.as_reference(), document, pages));
122128
}
@@ -169,10 +175,17 @@ const FileParser &DocumentParser::parser() const { return m_parser; }
169175

170176
const Xref &DocumentParser::xref() const { return m_xref; }
171177

172-
IndirectObject DocumentParser::read_object(const ObjectReference &reference) {
173-
std::uint32_t position = m_xref.table[reference.id].position;
178+
const IndirectObject &
179+
DocumentParser::read_object(const ObjectReference &reference) {
180+
if (auto it = m_objects.find(reference); it != std::end(m_objects)) {
181+
return it->second;
182+
}
183+
184+
std::uint32_t position = m_xref.table.at(reference).position;
174185
in().seekg(position);
175-
return parser().read_indirect_object();
186+
IndirectObject object = parser().read_indirect_object();
187+
188+
return m_objects.emplace(reference, std::move(object)).first->second;
176189
}
177190

178191
std::string
@@ -198,15 +211,64 @@ std::string DocumentParser::read_object_stream(const IndirectObject &object) {
198211
std::unique_ptr<Document> DocumentParser::parse_document() {
199212
parser().seek_start_xref();
200213
StartXref start_xref = parser().read_start_xref();
201-
in().seekg(start_xref.start);
202214

203-
m_xref = parser().read_xref();
204-
parser().parser().skip_whitespace();
205-
Trailer trailer = parser().read_trailer();
215+
std::uint32_t xref_position = start_xref.start;
216+
std::optional<Trailer> trailer;
217+
218+
while (true) {
219+
in().seekg(xref_position);
220+
221+
m_xref.append(parser().read_xref());
222+
parser().parser().skip_whitespace();
223+
Trailer new_trailer = parser().read_trailer();
224+
if (!trailer) {
225+
trailer = new_trailer;
226+
}
227+
228+
if (new_trailer.dictionary.has_key("Prev")) {
229+
xref_position = new_trailer.dictionary["Prev"].as_integer();
230+
continue;
231+
}
232+
233+
break;
234+
}
206235

207236
auto document = std::make_unique<Document>();
208-
document->catalog = parse_catalog(*this, trailer.root_reference, *document);
237+
document->catalog =
238+
parse_catalog(*this, trailer->root_reference(), *document);
209239
return document;
210240
}
211241

242+
void DocumentParser::resolve_object(Object &object) {
243+
if (object.is_reference()) {
244+
object = read_object(object.as_reference()).object;
245+
}
246+
}
247+
248+
void DocumentParser::deep_resolve_object(Object &object) {
249+
if (object.is_reference()) {
250+
object = read_object(object.as_reference()).object;
251+
} else if (object.is_array()) {
252+
for (Object &e : object.as_array()) {
253+
deep_resolve_object(e);
254+
}
255+
} else if (object.is_dictionary()) {
256+
for (auto &[k, v] : object.as_dictionary()) {
257+
deep_resolve_object(v);
258+
}
259+
}
260+
}
261+
262+
Object DocumentParser::resolve_object_copy(const Object &object) {
263+
Object result = object;
264+
resolve_object(result);
265+
return result;
266+
}
267+
268+
Object DocumentParser::deep_resolve_object_copy(const Object &object) {
269+
Object result = object;
270+
deep_resolve_object(result);
271+
return result;
272+
}
273+
212274
} // namespace odr::internal::pdf

src/odr/internal/pdf/pdf_document_parser.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <odr/internal/pdf/pdf_file_parser.hpp>
66

77
#include <iosfwd>
8+
#include <map>
89
#include <memory>
910

1011
namespace odr::internal::pdf {
@@ -19,15 +20,22 @@ class DocumentParser {
1920
const FileParser &parser() const;
2021
const Xref &xref() const;
2122

22-
IndirectObject read_object(const ObjectReference &reference);
23+
const IndirectObject &read_object(const ObjectReference &reference);
2324
std::string read_object_stream(const ObjectReference &reference);
2425
std::string read_object_stream(const IndirectObject &object);
2526

27+
void resolve_object(Object &object);
28+
void deep_resolve_object(Object &object);
29+
30+
Object resolve_object_copy(const Object &object);
31+
Object deep_resolve_object_copy(const Object &object);
32+
2633
std::unique_ptr<Document> parse_document();
2734

2835
private:
2936
FileParser m_parser;
3037
Xref m_xref;
38+
std::map<ObjectReference, IndirectObject> m_objects;
3139
};
3240

3341
} // namespace odr::internal::pdf
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
11
#include <odr/internal/pdf/pdf_file_object.hpp>
22

3-
namespace odr::internal::pdf {}
3+
namespace odr::internal::pdf {
4+
5+
const ObjectReference &Trailer::root_reference() const {
6+
return dictionary["Root"].as_reference();
7+
}
8+
9+
void Xref::append(const Xref &xref) {
10+
table.insert(std::begin(xref.table), std::end(xref.table));
11+
}
12+
13+
} // namespace odr::internal::pdf

src/odr/internal/pdf/pdf_file_object.hpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,22 @@ struct IndirectObject {
1919

2020
struct Trailer {
2121
std::uint32_t size;
22-
ObjectReference root_reference;
23-
ObjectReference info_reference;
2422

2523
Dictionary dictionary;
24+
25+
const ObjectReference &root_reference() const;
2626
};
2727

2828
struct Xref {
2929
struct Entry {
3030
std::uint32_t position{};
31-
std::uint32_t generation{};
3231
bool in_use{};
3332
};
34-
using Table = std::map<std::uint32_t, Entry>;
33+
using Table = std::map<ObjectReference, Entry>;
3534

3635
Table table;
36+
37+
void append(const Xref &xref);
3738
};
3839

3940
struct StartXref {

src/odr/internal/pdf/pdf_file_parser.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ IndirectObject FileParser::read_indirect_object() const {
2929
}
3030

3131
result.object = m_parser.read_object();
32-
m_parser.skip_line();
32+
m_parser.skip_whitespace();
3333

3434
auto next = m_parser.read_line();
3535

@@ -49,16 +49,13 @@ IndirectObject FileParser::read_indirect_object() const {
4949
}
5050

5151
Trailer FileParser::read_trailer() const {
52-
if (std::string line = m_parser.read_line(); line != "trailer") {
53-
throw std::runtime_error("expected trailer");
54-
}
52+
m_parser.expect_characters("trailer");
53+
m_parser.skip_whitespace();
5554

5655
Trailer result;
5756

5857
result.dictionary = m_parser.read_dictionary();
5958
result.size = result.dictionary["Size"].as_integer();
60-
result.root_reference = result.dictionary["Root"].as_reference();
61-
result.info_reference = result.dictionary["Info"].as_reference();
6259

6360
m_parser.skip_line();
6461
m_parser.skip_whitespace();
@@ -89,11 +86,11 @@ Xref FileParser::read_xref() const {
8986

9087
entry.position = m_parser.read_unsigned_integer();
9188
m_parser.skip_whitespace();
92-
entry.generation = m_parser.read_unsigned_integer();
89+
std::uint64_t generation = m_parser.read_unsigned_integer();
9390
m_parser.skip_whitespace();
9491
entry.in_use = m_parser.read_line().at(0) == 'n';
9592

96-
result.table.emplace(first_id + i, std::move(entry));
93+
result.table.emplace(ObjectReference(first_id + i, generation), entry);
9794
}
9895
}
9996
}

0 commit comments

Comments
 (0)