Skip to content

Commit 182aa3b

Browse files
authored
Merge pull request #249
pdf-object: remove cross-reference entry wrapper
2 parents 27ef806 + 6c31ce6 commit 182aa3b

8 files changed

Lines changed: 105 additions & 117 deletions

File tree

crates/pdf-document/src/object_loader.rs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ use crate::error::PdfReaderError;
1919
use crate::object_stream::read_object_stream;
2020
use crate::reader::{EncryptionContext, object_id};
2121
use pdf_object::{
22-
cross_reference_table::{CrossReferenceEntry, CrossReferenceEntryType},
23-
error::ObjectError,
22+
cross_reference_table::CrossReferenceEntryType, error::ObjectError,
2423
object_variant::ObjectVariant,
2524
};
2625
use pdf_object_collection::object_collection::ObjectCollection;
@@ -45,12 +44,12 @@ struct LoadPlan {
4544

4645
impl LoadPlan {
4746
/// Partitions live xref entries into normal objects and compressed-stream batches.
48-
fn from_entries(entries: &BTreeMap<usize, CrossReferenceEntry>) -> Self {
47+
fn from_entries(entries: &BTreeMap<usize, CrossReferenceEntryType>) -> Self {
4948
let mut normal_objects = entries
5049
.values()
51-
.filter_map(|entry| match entry.entry_type {
52-
CrossReferenceEntryType::Normal { byte_offset, .. } if byte_offset != 0 => {
53-
Some(byte_offset)
50+
.filter_map(|entry| match entry {
51+
CrossReferenceEntryType::Normal { byte_offset, .. } if *byte_offset != 0 => {
52+
Some(*byte_offset)
5453
}
5554
_ => None,
5655
})
@@ -67,14 +66,14 @@ impl LoadPlan {
6766
let CrossReferenceEntryType::Compressed {
6867
object_stream_number,
6968
index_within_stream,
70-
} = entry.entry_type
69+
} = entry
7170
else {
7271
continue;
7372
};
7473
compressed_streams
75-
.entry(object_stream_number)
74+
.entry(*object_stream_number)
7675
.or_default()
77-
.entry(index_within_stream)
76+
.entry(*index_within_stream)
7877
.or_default()
7978
.push(object_number);
8079
}
@@ -264,7 +263,7 @@ pub(super) struct ObjectLoader<'input, 'loader> {
264263
impl<'input, 'loader> ObjectLoader<'input, 'loader> {
265264
/// Creates an object loader for one parsed cross-reference table.
266265
pub(super) fn new(
267-
entries: &BTreeMap<usize, CrossReferenceEntry>,
266+
entries: &BTreeMap<usize, CrossReferenceEntryType>,
268267
parser: &'loader mut PdfParser<'input>,
269268
encryption: EncryptionContext,
270269
diagnostics: &'loader mut Vec<PdfReadDiagnostic>,
@@ -476,12 +475,12 @@ mod tests {
476475
#[test]
477476
fn load_plan_partitions_entries_and_groups_compressed_indexes() {
478477
let entries = BTreeMap::from([
479-
(0, CrossReferenceEntry::new_free(0, 65_535)),
480-
(1, CrossReferenceEntry::new_normal(10, 0)),
481-
(2, CrossReferenceEntry::new_compressed(5, 1)),
482-
(3, CrossReferenceEntry::new_compressed(5, 1)),
483-
(4, CrossReferenceEntry::new_normal(0, 0)),
484-
(5, CrossReferenceEntry::new_normal(50, 0)),
478+
(0, CrossReferenceEntryType::new_free(0, 65_535)),
479+
(1, CrossReferenceEntryType::new_normal(10, 0)),
480+
(2, CrossReferenceEntryType::new_compressed(5, 1)),
481+
(3, CrossReferenceEntryType::new_compressed(5, 1)),
482+
(4, CrossReferenceEntryType::new_normal(0, 0)),
483+
(5, CrossReferenceEntryType::new_normal(50, 0)),
485484
]);
486485

487486
let plan = LoadPlan::from_entries(&entries);

crates/pdf-document/src/reader.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use pdf_object::object_id::PdfObjectId;
1414
use pdf_object::object_lookup::ObjectLookupExt;
1515
use pdf_object::object_resolver::{ObjectResolver, PassthroughResolver};
1616
use pdf_object::{
17-
cross_reference_table::{CrossReferenceEntry, CrossReferenceTable},
17+
cross_reference_table::{CrossReferenceEntryType, CrossReferenceTable},
1818
error::ObjectError,
1919
object_variant::ObjectVariant,
2020
trailer::Trailer,
@@ -94,7 +94,7 @@ impl EncryptionContext {
9494
/// Creates decryption state from the trailer's optional encryption entry.
9595
fn from_trailer(
9696
trailer: &mut Trailer,
97-
entries: &BTreeMap<usize, CrossReferenceEntry>,
97+
entries: &BTreeMap<usize, CrossReferenceEntryType>,
9898
parser: &mut PdfParser,
9999
password: &[u8],
100100
diagnostics: &mut Vec<PdfReadDiagnostic>,
@@ -171,7 +171,7 @@ fn extract_page_tree(
171171
/// Resolves and parses the trailer's encryption dictionary without decrypting it.
172172
fn load_encrypt_dictionary(
173173
encrypt_reference: ObjectVariant,
174-
entries: &BTreeMap<usize, CrossReferenceEntry>,
174+
entries: &BTreeMap<usize, CrossReferenceEntryType>,
175175
parser: &mut PdfParser,
176176
) -> Result<EncryptDictionary, PdfReaderError> {
177177
let object = match encrypt_reference {

crates/pdf-object-collection/src/object_collection.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ impl ObjectCollection {
216216
(
217217
k.to_string(),
218218
json!({
219-
"entry_type": format!("{:?}", v.entry_type)
219+
"entry_type": format!("{v:?}")
220220
}),
221221
)
222222
})

crates/pdf-object/src/cross_reference_table.rs

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ use crate::trailer::Trailer;
1111
#[derive(Debug, PartialEq, Clone)]
1212
pub struct CrossReferenceTable {
1313
/// The map of object numbers to cross-reference entries.
14-
pub entries: BTreeMap<usize, CrossReferenceEntry>,
14+
pub entries: BTreeMap<usize, CrossReferenceEntryType>,
1515
/// The trailer associated with this cross-reference table.
1616
pub trailer: Trailer,
1717
}
1818

1919
impl CrossReferenceTable {
20-
pub fn new(entries: BTreeMap<usize, CrossReferenceEntry>, trailer: Trailer) -> Self {
20+
pub fn new(entries: BTreeMap<usize, CrossReferenceEntryType>, trailer: Trailer) -> Self {
2121
CrossReferenceTable { entries, trailer }
2222
}
2323
}
2424

25-
/// Distinguishes the type of a cross-reference entry.
25+
/// Represents a cross-reference entry.
2626
#[derive(Debug, PartialEq, Clone)]
2727
pub enum CrossReferenceEntryType {
2828
/// Type 1: object at a byte offset in the file (traditional).
@@ -42,60 +42,52 @@ pub enum CrossReferenceEntryType {
4242
},
4343
}
4444

45-
#[derive(Debug, PartialEq, Clone)]
46-
pub struct CrossReferenceEntry {
47-
pub entry_type: CrossReferenceEntryType,
48-
}
49-
50-
impl CrossReferenceEntry {
45+
impl CrossReferenceEntryType {
46+
/// Creates a normal entry for an uncompressed object at a byte offset.
5147
pub fn new_normal(byte_offset: usize, generation_number: usize) -> Self {
52-
CrossReferenceEntry {
53-
entry_type: CrossReferenceEntryType::Normal {
54-
byte_offset,
55-
generation_number,
56-
},
48+
CrossReferenceEntryType::Normal {
49+
byte_offset,
50+
generation_number,
5751
}
5852
}
5953

54+
/// Creates an entry for an object stored within a compressed object stream.
6055
pub fn new_compressed(object_stream_number: usize, index_within_stream: usize) -> Self {
61-
CrossReferenceEntry {
62-
entry_type: CrossReferenceEntryType::Compressed {
63-
object_stream_number,
64-
index_within_stream,
65-
},
56+
CrossReferenceEntryType::Compressed {
57+
object_stream_number,
58+
index_within_stream,
6659
}
6760
}
6861

62+
/// Creates an entry for a free object.
6963
pub fn new_free(next_free_object: usize, generation_number: usize) -> Self {
70-
CrossReferenceEntry {
71-
entry_type: CrossReferenceEntryType::Free {
72-
next_free_object,
73-
generation_number,
74-
},
64+
CrossReferenceEntryType::Free {
65+
next_free_object,
66+
generation_number,
7567
}
7668
}
7769

7870
/// Returns the byte offset if this is a Normal entry.
7971
pub fn byte_offset(&self) -> Option<usize> {
80-
match &self.entry_type {
72+
match self {
8173
CrossReferenceEntryType::Normal { byte_offset, .. } => Some(*byte_offset),
8274
_ => None,
8375
}
8476
}
8577

8678
/// Returns true if this is a Normal (in-use, uncompressed) entry.
8779
pub fn is_normal(&self) -> bool {
88-
matches!(self.entry_type, CrossReferenceEntryType::Normal { .. })
80+
matches!(self, CrossReferenceEntryType::Normal { .. })
8981
}
9082

9183
/// Returns true if this is a Free entry.
9284
pub fn is_free(&self) -> bool {
93-
matches!(self.entry_type, CrossReferenceEntryType::Free { .. })
85+
matches!(self, CrossReferenceEntryType::Free { .. })
9486
}
9587

9688
/// Returns true if this is a Compressed entry.
9789
pub fn is_compressed(&self) -> bool {
98-
matches!(self.entry_type, CrossReferenceEntryType::Compressed { .. })
90+
matches!(self, CrossReferenceEntryType::Compressed { .. })
9991
}
10092
}
10193

crates/pdf-parser/src/cross_reference_stream.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::collections::BTreeMap;
22

33
use pdf_object::{
4-
cross_reference_table::{CrossReferenceEntry, CrossReferenceTable},
4+
cross_reference_table::{CrossReferenceEntryType, CrossReferenceTable},
55
object_lookup::ObjectLookupExt,
66
object_resolver::ObjectResolver,
77
stream::StreamObject,
@@ -162,7 +162,7 @@ impl<'data> XrefStreamEntryDecoder<'data> {
162162
}
163163

164164
/// Returns the next complete entry, or `None` when the data is truncated.
165-
fn next_entry(&mut self) -> Option<CrossReferenceEntry> {
165+
fn next_entry(&mut self) -> Option<CrossReferenceEntryType> {
166166
let end = self.position.saturating_add(self.layout.entry_width);
167167
let bytes = self.data.get(self.position..end)?;
168168
let entry = self.decode_entry(bytes)?;
@@ -171,7 +171,7 @@ impl<'data> XrefStreamEntryDecoder<'data> {
171171
}
172172

173173
/// Decodes one entry according to the `/W` field widths.
174-
fn decode_entry(&self, bytes: &[u8]) -> Option<CrossReferenceEntry> {
174+
fn decode_entry(&self, bytes: &[u8]) -> Option<CrossReferenceEntryType> {
175175
let type_end = self.layout.type_width;
176176
let second_end = type_end.checked_add(self.layout.second_field_width)?;
177177
let third_end = second_end.checked_add(self.layout.third_field_width)?;
@@ -189,10 +189,10 @@ impl<'data> XrefStreamEntryDecoder<'data> {
189189
let third_value = read_field(third_field);
190190

191191
Some(match entry_type {
192-
0 => CrossReferenceEntry::new_free(second_value, third_value),
193-
1 => CrossReferenceEntry::new_normal(second_value, third_value),
194-
2 => CrossReferenceEntry::new_compressed(second_value, third_value),
195-
_ => CrossReferenceEntry::new_free(0, 0),
192+
0 => CrossReferenceEntryType::new_free(second_value, third_value),
193+
1 => CrossReferenceEntryType::new_normal(second_value, third_value),
194+
2 => CrossReferenceEntryType::new_compressed(second_value, third_value),
195+
_ => CrossReferenceEntryType::new_free(0, 0),
196196
})
197197
}
198198
}
@@ -284,7 +284,7 @@ mod tests {
284284

285285
let e2 = &table.entries[&2];
286286
assert!(e2.is_compressed());
287-
match &e2.entry_type {
287+
match e2 {
288288
CrossReferenceEntryType::Compressed {
289289
object_stream_number,
290290
index_within_stream,
@@ -349,7 +349,7 @@ mod tests {
349349

350350
let table = parse_xref_stream(&stream, &PassthroughResolver).unwrap();
351351

352-
assert_eq!(table.entries[&0], CrossReferenceEntry::new_free(0, 0));
352+
assert_eq!(table.entries[&0], CrossReferenceEntryType::new_free(0, 0));
353353
}
354354

355355
/// Builds a compressed xref stream with FlateDecode + PNG Up predictor,
@@ -469,7 +469,7 @@ mod tests {
469469

470470
let e3 = &table.entries[&3];
471471
assert!(e3.is_compressed());
472-
match &e3.entry_type {
472+
match e3 {
473473
CrossReferenceEntryType::Compressed {
474474
object_stream_number,
475475
index_within_stream,

crates/pdf-parser/src/cross_reference_table.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
use std::collections::BTreeMap;
22

33
use pdf_object::{
4-
cross_reference_table::{
5-
CrossReferenceEntry, CrossReferenceEntryType, CrossReferenceStatus, CrossReferenceTable,
6-
},
4+
cross_reference_table::{CrossReferenceEntryType, CrossReferenceStatus, CrossReferenceTable},
75
object_resolver::ObjectResolver,
86
};
97
use pdf_tokenizer::PdfToken;
@@ -37,7 +35,7 @@ impl PdfParser<'_> {
3735
/// Parses the contiguous subsections that follow an `xref` keyword.
3836
pub(crate) fn parse_cross_reference_subsections(
3937
&mut self,
40-
) -> Result<BTreeMap<usize, CrossReferenceEntry>, ParserError> {
38+
) -> Result<BTreeMap<usize, CrossReferenceEntryType>, ParserError> {
4139
let mut entries = BTreeMap::new();
4240

4341
loop {
@@ -60,7 +58,7 @@ impl PdfParser<'_> {
6058
/// Parses one xref subsection header and its declared number of entries.
6159
fn parse_cross_reference_subsection(
6260
&mut self,
63-
) -> Result<(usize, Vec<CrossReferenceEntry>), ParserError> {
61+
) -> Result<(usize, Vec<CrossReferenceEntryType>), ParserError> {
6462
let start_object_number = self.read_number::<usize>(true)?;
6563
self.skip_whitespace_and_comments();
6664
let entry_count = self.read_number::<usize>(true)?;
@@ -76,7 +74,7 @@ impl PdfParser<'_> {
7674
/// Parses a traditional xref row into its corresponding entry.
7775
pub(crate) fn parse_cross_reference_entry(
7876
&mut self,
79-
) -> Result<CrossReferenceEntry, ParserError> {
77+
) -> Result<CrossReferenceEntryType, ParserError> {
8078
self.skip_whitespace_and_comments();
8179
let field1 = self.read_number::<usize>(true)?;
8280
self.skip_whitespace_and_comments();
@@ -93,9 +91,9 @@ impl PdfParser<'_> {
9391
})?;
9492

9593
Ok(match status {
96-
CrossReferenceStatus::Normal => CrossReferenceEntry::new_normal(field1, field2),
94+
CrossReferenceStatus::Normal => CrossReferenceEntryType::new_normal(field1, field2),
9795
CrossReferenceStatus::Free | CrossReferenceStatus::Old => {
98-
CrossReferenceEntry::new_free(field1, field2)
96+
CrossReferenceEntryType::new_free(field1, field2)
9997
}
10098
})
10199
}
@@ -105,8 +103,8 @@ impl PdfParser<'_> {
105103
/// leading free-object-zero pattern encountered in some PDFs.
106104
fn normalize_xref_subsection_entries(
107105
start_object_number: usize,
108-
entries: Vec<CrossReferenceEntry>,
109-
) -> Vec<(usize, CrossReferenceEntry)> {
106+
entries: Vec<CrossReferenceEntryType>,
107+
) -> Vec<(usize, CrossReferenceEntryType)> {
110108
let has_leading_free_object_zero = start_object_number > 0
111109
&& entries
112110
.first()
@@ -132,14 +130,12 @@ fn normalize_xref_subsection_entries(
132130

133131
/// Returns whether an entry is the object-zero free entry incorrectly included
134132
/// at the start of a non-zero xref subsection.
135-
fn is_malformed_leading_free_object_zero(entry: &CrossReferenceEntry) -> bool {
133+
fn is_malformed_leading_free_object_zero(entry: &CrossReferenceEntryType) -> bool {
136134
matches!(
137135
entry,
138-
CrossReferenceEntry {
139-
entry_type: CrossReferenceEntryType::Free {
140-
next_free_object: 0,
141-
generation_number: 65_535,
142-
}
136+
CrossReferenceEntryType::Free {
137+
next_free_object: 0,
138+
generation_number: 65_535,
143139
}
144140
)
145141
}
@@ -271,8 +267,8 @@ mod tests {
271267
#[test]
272268
fn test_normalize_xref_subsection_entries_uses_declared_range() {
273269
let entries = vec![
274-
CrossReferenceEntry::new_normal(17, 0),
275-
CrossReferenceEntry::new_normal(81, 0),
270+
CrossReferenceEntryType::new_normal(17, 0),
271+
CrossReferenceEntryType::new_normal(81, 0),
276272
];
277273

278274
let normalized = normalize_xref_subsection_entries(4, entries);
@@ -284,9 +280,9 @@ mod tests {
284280
#[test]
285281
fn test_normalize_xref_subsection_entries_handles_leading_free_object_zero() {
286282
let entries = vec![
287-
CrossReferenceEntry::new_free(0, 65_535),
288-
CrossReferenceEntry::new_normal(17, 0),
289-
CrossReferenceEntry::new_normal(81, 0),
283+
CrossReferenceEntryType::new_free(0, 65_535),
284+
CrossReferenceEntryType::new_normal(17, 0),
285+
CrossReferenceEntryType::new_normal(81, 0),
290286
];
291287

292288
let normalized = normalize_xref_subsection_entries(4, entries);

0 commit comments

Comments
 (0)