imprimatur 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +8 -0
- data/Cargo.lock +835 -0
- data/Cargo.toml +44 -0
- data/LICENSE.txt +29 -0
- data/README.md +314 -0
- data/ext/imprimatur_native/cli/Cargo.toml +30 -0
- data/ext/imprimatur_native/cli/src/app.rs +183 -0
- data/ext/imprimatur_native/cli/src/cli/completion.rs +26 -0
- data/ext/imprimatur_native/cli/src/cli/grep.rs +359 -0
- data/ext/imprimatur_native/cli/src/cli/images.rs +137 -0
- data/ext/imprimatur_native/cli/src/cli/limits.rs +49 -0
- data/ext/imprimatur_native/cli/src/cli/markdown.rs +128 -0
- data/ext/imprimatur_native/cli/src/cli.rs +71 -0
- data/ext/imprimatur_native/cli/src/main.rs +15 -0
- data/ext/imprimatur_native/core/Cargo.toml +33 -0
- data/ext/imprimatur_native/core/LICENSE +29 -0
- data/ext/imprimatur_native/core/README.md +105 -0
- data/ext/imprimatur_native/core/src/doc/paragraph.rs +112 -0
- data/ext/imprimatur_native/core/src/doc/table.rs +41 -0
- data/ext/imprimatur_native/core/src/doc.rs +210 -0
- data/ext/imprimatur_native/core/src/docx.rs +343 -0
- data/ext/imprimatur_native/core/src/error.rs +113 -0
- data/ext/imprimatur_native/core/src/lib.rs +38 -0
- data/ext/imprimatur_native/core/src/opc.rs +51 -0
- data/ext/imprimatur_native/core/src/pdf/font.rs +96 -0
- data/ext/imprimatur_native/core/src/pdf/layout/wrap.rs +161 -0
- data/ext/imprimatur_native/core/src/pdf/layout.rs +280 -0
- data/ext/imprimatur_native/core/src/pdf/metrics.rs +139 -0
- data/ext/imprimatur_native/core/src/pdf/objects.rs +141 -0
- data/ext/imprimatur_native/core/src/pdf.rs +84 -0
- data/ext/imprimatur_native/core/src/read/budget.rs +140 -0
- data/ext/imprimatur_native/core/src/read/content.rs +266 -0
- data/ext/imprimatur_native/core/src/read/docx/styles.rs +118 -0
- data/ext/imprimatur_native/core/src/read/docx.rs +336 -0
- data/ext/imprimatur_native/core/src/read/image/dimensions.rs +215 -0
- data/ext/imprimatur_native/core/src/read/image.rs +233 -0
- data/ext/imprimatur_native/core/src/read/inflate.rs +44 -0
- data/ext/imprimatur_native/core/src/read/json.rs +172 -0
- data/ext/imprimatur_native/core/src/read/markdown.rs +254 -0
- data/ext/imprimatur_native/core/src/read/package.rs +191 -0
- data/ext/imprimatur_native/core/src/read/pdf/cmap.rs +216 -0
- data/ext/imprimatur_native/core/src/read/pdf/content/geometry.rs +79 -0
- data/ext/imprimatur_native/core/src/read/pdf/content/operands.rs +156 -0
- data/ext/imprimatur_native/core/src/read/pdf/content.rs +439 -0
- data/ext/imprimatur_native/core/src/read/pdf/crypt.rs +256 -0
- data/ext/imprimatur_native/core/src/read/pdf/document.rs +420 -0
- data/ext/imprimatur_native/core/src/read/pdf/filter.rs +258 -0
- data/ext/imprimatur_native/core/src/read/pdf/font.rs +315 -0
- data/ext/imprimatur_native/core/src/read/pdf/glyphs.rs +179 -0
- data/ext/imprimatur_native/core/src/read/pdf/glyphs.txt +6298 -0
- data/ext/imprimatur_native/core/src/read/pdf/images.rs +365 -0
- data/ext/imprimatur_native/core/src/read/pdf/layout.rs +451 -0
- data/ext/imprimatur_native/core/src/read/pdf/lexer.rs +255 -0
- data/ext/imprimatur_native/core/src/read/pdf/object/name.rs +44 -0
- data/ext/imprimatur_native/core/src/read/pdf/object.rs +261 -0
- data/ext/imprimatur_native/core/src/read/pdf/xref.rs +346 -0
- data/ext/imprimatur_native/core/src/read/pdf.rs +53 -0
- data/ext/imprimatur_native/core/src/read/span.rs +22 -0
- data/ext/imprimatur_native/core/src/read/xlsx/numbers.rs +155 -0
- data/ext/imprimatur_native/core/src/read/xlsx.rs +340 -0
- data/ext/imprimatur_native/core/src/read.rs +145 -0
- data/ext/imprimatur_native/core/src/style.rs +82 -0
- data/ext/imprimatur_native/core/src/test_support.rs +163 -0
- data/ext/imprimatur_native/core/src/xlsx/cell.rs +112 -0
- data/ext/imprimatur_native/core/src/xlsx/comment.rs +256 -0
- data/ext/imprimatur_native/core/src/xlsx/date.rs +173 -0
- data/ext/imprimatur_native/core/src/xlsx/format.rs +226 -0
- data/ext/imprimatur_native/core/src/xlsx/package.rs +268 -0
- data/ext/imprimatur_native/core/src/xlsx/range.rs +124 -0
- data/ext/imprimatur_native/core/src/xlsx/styles.rs +240 -0
- data/ext/imprimatur_native/core/src/xlsx/text.rs +122 -0
- data/ext/imprimatur_native/core/src/xlsx/validation.rs +258 -0
- data/ext/imprimatur_native/core/src/xlsx/worksheet.rs +350 -0
- data/ext/imprimatur_native/core/src/xlsx.rs +120 -0
- data/ext/imprimatur_native/core/src/xml/reader.rs +238 -0
- data/ext/imprimatur_native/core/src/xml.rs +95 -0
- data/ext/imprimatur_native/core/src/zip/reader.rs +144 -0
- data/ext/imprimatur_native/core/src/zip.rs +161 -0
- data/ext/imprimatur_native/extconf.rb +45 -0
- data/ext/imprimatur_native/ffi/Cargo.toml +21 -0
- data/ext/imprimatur_native/ffi/src/convert.rs +132 -0
- data/ext/imprimatur_native/ffi/src/document.rs +138 -0
- data/ext/imprimatur_native/ffi/src/lib.rs +301 -0
- data/ext/imprimatur_native/ffi/src/read.rs +182 -0
- data/ext/imprimatur_native/ffi/src/style.rs +85 -0
- data/ext/imprimatur_native/ffi/src/validation.rs +56 -0
- data/lib/imprimatur/document.rb +127 -0
- data/lib/imprimatur/error.rb +22 -0
- data/lib/imprimatur/formula.rb +13 -0
- data/lib/imprimatur/native.rb +17 -0
- data/lib/imprimatur/reader.rb +59 -0
- data/lib/imprimatur/style.rb +31 -0
- data/lib/imprimatur/validation.rb +35 -0
- data/lib/imprimatur/version.rb +5 -0
- data/lib/imprimatur/xlsx.rb +183 -0
- data/lib/imprimatur.rb +111 -0
- metadata +157 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
//! A PDF file's objects on demand: loaded once and shared, decrypted, and
|
|
2
|
+
//! their streams decoded; then the pages, with what they inherit.
|
|
3
|
+
|
|
4
|
+
use std::borrow::Cow;
|
|
5
|
+
use std::cell::RefCell;
|
|
6
|
+
use std::collections::HashMap;
|
|
7
|
+
use std::ops::Deref;
|
|
8
|
+
use std::rc::Rc;
|
|
9
|
+
|
|
10
|
+
use super::crypt::Crypt;
|
|
11
|
+
use super::filter;
|
|
12
|
+
use super::lexer::{Lexer, Token};
|
|
13
|
+
use super::object::{Dict, Name, Object, Parser, Stream, malformed};
|
|
14
|
+
use super::xref::{Entry, Xref, is_limit};
|
|
15
|
+
use crate::read::Budget;
|
|
16
|
+
use crate::{Error, Result};
|
|
17
|
+
|
|
18
|
+
/// How many references in a row resolve before giving up on a cycle.
|
|
19
|
+
const MAX_HOPS: usize = 16;
|
|
20
|
+
|
|
21
|
+
/// An object borrowed from its container, or a loaded indirect one.
|
|
22
|
+
pub enum Resolved<'a> {
|
|
23
|
+
Borrowed(&'a Object),
|
|
24
|
+
Shared(Rc<Object>),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
impl Resolved<'_> {
|
|
28
|
+
/// Shared ownership of the object: the loaded one, or a copy of a
|
|
29
|
+
/// borrowed direct one.
|
|
30
|
+
pub fn into_shared(self) -> Rc<Object> {
|
|
31
|
+
match self {
|
|
32
|
+
Self::Borrowed(object) => Rc::new(object.clone()),
|
|
33
|
+
Self::Shared(object) => object,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
impl Deref for Resolved<'_> {
|
|
39
|
+
type Target = Object;
|
|
40
|
+
|
|
41
|
+
fn deref(&self) -> &Object {
|
|
42
|
+
match self {
|
|
43
|
+
Self::Borrowed(object) => object,
|
|
44
|
+
Self::Shared(object) => object,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// An object stream's decoded data and where each of its objects starts.
|
|
50
|
+
struct ObjectStream {
|
|
51
|
+
data: Vec<u8>,
|
|
52
|
+
offsets: Vec<(u32, usize)>,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pub struct Page {
|
|
56
|
+
pub resources: Option<Rc<Object>>,
|
|
57
|
+
pub contents: Vec<Object>,
|
|
58
|
+
pub media_box: [f64; 4],
|
|
59
|
+
pub rotate: i64,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub struct Document<'a> {
|
|
63
|
+
data: &'a [u8],
|
|
64
|
+
budget: &'a Budget<'a>,
|
|
65
|
+
xref: Xref,
|
|
66
|
+
crypt: Option<Crypt>,
|
|
67
|
+
objects: RefCell<HashMap<u32, Rc<Object>>>,
|
|
68
|
+
streams: RefCell<HashMap<u32, Rc<ObjectStream>>>,
|
|
69
|
+
loading: RefCell<Vec<u32>>,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
impl<'a> Document<'a> {
|
|
73
|
+
/// Reads the cross-reference table and opens the encryption, if any.
|
|
74
|
+
pub fn open(data: &'a [u8], budget: &'a Budget<'a>) -> Result<Self> {
|
|
75
|
+
let xref = Xref::read(data, budget)?;
|
|
76
|
+
let mut document = Self {
|
|
77
|
+
data,
|
|
78
|
+
budget,
|
|
79
|
+
xref,
|
|
80
|
+
crypt: None,
|
|
81
|
+
objects: RefCell::default(),
|
|
82
|
+
streams: RefCell::default(),
|
|
83
|
+
loading: RefCell::default(),
|
|
84
|
+
};
|
|
85
|
+
if let Some(encrypt) = document.xref.trailer.get(b"Encrypt").cloned() {
|
|
86
|
+
let encrypt = document.resolve(&encrypt)?;
|
|
87
|
+
if let Some(encrypt) = encrypt.as_dict() {
|
|
88
|
+
let ids = document.xref.trailer.get(b"ID").and_then(Object::as_array);
|
|
89
|
+
let id =
|
|
90
|
+
ids.and_then(|ids| ids.first()).and_then(Object::as_bytes).unwrap_or_default();
|
|
91
|
+
document.crypt = Some(Crypt::new(encrypt, id)?);
|
|
92
|
+
// Objects read while opening it were not decrypted.
|
|
93
|
+
document.objects.borrow_mut().clear();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
Ok(document)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
pub const fn budget(&self) -> &'a Budget<'a> {
|
|
100
|
+
self.budget
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
pub const fn trailer(&self) -> &Dict {
|
|
104
|
+
&self.xref.trailer
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/// Object `number`, `Null` when it is missing or unreadable.
|
|
108
|
+
pub fn get(&self, number: u32) -> Result<Rc<Object>> {
|
|
109
|
+
if let Some(found) = self.objects.borrow().get(&number) {
|
|
110
|
+
return Ok(Rc::clone(found));
|
|
111
|
+
}
|
|
112
|
+
if self.loading.borrow().contains(&number) || self.loading.borrow().len() > MAX_HOPS * 4 {
|
|
113
|
+
return Ok(Rc::new(Object::Null));
|
|
114
|
+
}
|
|
115
|
+
self.budget.tick()?;
|
|
116
|
+
self.loading.borrow_mut().push(number);
|
|
117
|
+
let loaded = self.load(number);
|
|
118
|
+
self.loading.borrow_mut().pop();
|
|
119
|
+
let object = match loaded {
|
|
120
|
+
Ok(object) => Rc::new(object),
|
|
121
|
+
Err(error) if is_limit(&error) || matches!(error, Error::PasswordRequired) => {
|
|
122
|
+
return Err(error);
|
|
123
|
+
}
|
|
124
|
+
Err(_) => Rc::new(Object::Null),
|
|
125
|
+
};
|
|
126
|
+
self.objects.borrow_mut().insert(number, Rc::clone(&object));
|
|
127
|
+
Ok(object)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// `object`, or what it refers to (through references to references).
|
|
131
|
+
pub fn resolve<'b>(&self, object: &'b Object) -> Result<Resolved<'b>> {
|
|
132
|
+
let Object::Ref(mut number, _) = *object else {
|
|
133
|
+
return Ok(Resolved::Borrowed(object));
|
|
134
|
+
};
|
|
135
|
+
for _ in 0..MAX_HOPS {
|
|
136
|
+
let found = self.get(number)?;
|
|
137
|
+
match *found {
|
|
138
|
+
Object::Ref(next, _) => number = next,
|
|
139
|
+
_ => return Ok(Resolved::Shared(found)),
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
Ok(Resolved::Shared(Rc::new(Object::Null)))
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/// A dictionary entry, resolved.
|
|
146
|
+
pub fn entry<'b>(&self, dict: &'b Dict, key: &[u8]) -> Result<Option<Resolved<'b>>> {
|
|
147
|
+
dict.get(key).map(|value| self.resolve(value)).transpose()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
fn load(&self, number: u32) -> Result<Object> {
|
|
151
|
+
match self.xref.entries.get(&number) {
|
|
152
|
+
Some(&Entry::Offset { offset, generation }) => {
|
|
153
|
+
let mut parser = Parser::new(self.data, offset, self.budget.max_depth());
|
|
154
|
+
let length = |length: &Object| match *length {
|
|
155
|
+
Object::Integer(length) => usize::try_from(length).ok(),
|
|
156
|
+
Object::Ref(number, _) => self
|
|
157
|
+
.get(number)
|
|
158
|
+
.ok()?
|
|
159
|
+
.as_integer()
|
|
160
|
+
.and_then(|length| usize::try_from(length).ok()),
|
|
161
|
+
_ => None,
|
|
162
|
+
};
|
|
163
|
+
let (found, _, mut object) = parser.indirect(&length)?;
|
|
164
|
+
if found != number {
|
|
165
|
+
return Err(malformed("the cross-reference table points at the wrong object"));
|
|
166
|
+
}
|
|
167
|
+
if let Some(crypt) = &self.crypt {
|
|
168
|
+
decrypt_strings(&mut object, crypt, (number, generation));
|
|
169
|
+
}
|
|
170
|
+
Ok(object)
|
|
171
|
+
}
|
|
172
|
+
Some(&Entry::Compressed { stream, index }) => self.compressed(stream, number, index),
|
|
173
|
+
_ => Ok(Object::Null),
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/// An object inside an object stream (never encrypted on its own: the
|
|
178
|
+
/// stream is).
|
|
179
|
+
fn compressed(&self, stream_number: u32, number: u32, index: u32) -> Result<Object> {
|
|
180
|
+
let stream = self.object_stream(stream_number)?;
|
|
181
|
+
let at = usize::try_from(index)
|
|
182
|
+
.ok()
|
|
183
|
+
.and_then(|index| stream.offsets.get(index))
|
|
184
|
+
.filter(|(found, _)| *found == number)
|
|
185
|
+
.or_else(|| stream.offsets.iter().find(|(found, _)| *found == number));
|
|
186
|
+
let Some(&(_, offset)) = at else { return Ok(Object::Null) };
|
|
187
|
+
Parser::new(&stream.data, offset, self.budget.max_depth()).object()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
fn object_stream(&self, number: u32) -> Result<Rc<ObjectStream>> {
|
|
191
|
+
if let Some(found) = self.streams.borrow().get(&number) {
|
|
192
|
+
return Ok(Rc::clone(found));
|
|
193
|
+
}
|
|
194
|
+
let object = self.get(number)?;
|
|
195
|
+
let Object::Stream(stream) = &*object else {
|
|
196
|
+
return Err(malformed("an object stream that is not a stream"));
|
|
197
|
+
};
|
|
198
|
+
let data = self.decode(stream, Some(number))?;
|
|
199
|
+
let size = |key: &[u8]| {
|
|
200
|
+
stream.dict.get(key).and_then(Object::as_integer).and_then(|n| usize::try_from(n).ok())
|
|
201
|
+
};
|
|
202
|
+
let (count, first) = (size(b"N").unwrap_or(0), size(b"First").unwrap_or(0));
|
|
203
|
+
let mut lexer = Lexer::new(&data, 0);
|
|
204
|
+
let mut offsets = Vec::with_capacity(count.min(100_000));
|
|
205
|
+
for _ in 0..count {
|
|
206
|
+
let (Some(Token::Integer(number)), Some(Token::Integer(offset))) =
|
|
207
|
+
(lexer.next(), lexer.next())
|
|
208
|
+
else {
|
|
209
|
+
break;
|
|
210
|
+
};
|
|
211
|
+
if let (Ok(number), Ok(offset)) = (u32::try_from(number), usize::try_from(offset)) {
|
|
212
|
+
offsets.push((number, first.saturating_add(offset)));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
let stream = Rc::new(ObjectStream { data, offsets });
|
|
216
|
+
self.streams.borrow_mut().insert(number, Rc::clone(&stream));
|
|
217
|
+
Ok(stream)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// A stream's data: decrypted (with its object's key) and decoded.
|
|
221
|
+
pub fn decode(&self, stream: &Stream, number: Option<u32>) -> Result<Vec<u8>> {
|
|
222
|
+
let raw = self.raw(stream, number)?;
|
|
223
|
+
let filters = self.filters(&stream.dict)?;
|
|
224
|
+
decode_with(&raw, &filters, self.budget)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/// A stream's data decrypted, not yet decoded.
|
|
228
|
+
pub fn raw(&self, stream: &Stream, number: Option<u32>) -> Result<Cow<'a, [u8]>> {
|
|
229
|
+
let raw = self
|
|
230
|
+
.data
|
|
231
|
+
.get(stream.data.clone())
|
|
232
|
+
.ok_or_else(|| malformed("stream data past the end"))?;
|
|
233
|
+
Ok(match (&self.crypt, number) {
|
|
234
|
+
(Some(crypt), Some(number)) if !stream.dict.has_name(b"Type", b"XRef") => {
|
|
235
|
+
let generation = match self.xref.entries.get(&number) {
|
|
236
|
+
Some(&Entry::Offset { generation, .. }) => generation,
|
|
237
|
+
_ => 0,
|
|
238
|
+
};
|
|
239
|
+
Cow::Owned(crypt.decrypt(true, (number, generation), raw))
|
|
240
|
+
}
|
|
241
|
+
_ => Cow::Borrowed(raw),
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/// A stream's filters in order, each with its decode parameters.
|
|
246
|
+
pub fn filters(&self, dict: &Dict) -> Result<Vec<Filter>> {
|
|
247
|
+
let filter = self.entry(dict, b"Filter")?;
|
|
248
|
+
let params = self.entry(dict, b"DecodeParms")?;
|
|
249
|
+
let mut filters = Vec::new();
|
|
250
|
+
match filter.as_deref() {
|
|
251
|
+
Some(Object::Name(name)) => {
|
|
252
|
+
filters.push((name.clone(), self.params(params.as_deref(), None)?));
|
|
253
|
+
}
|
|
254
|
+
Some(Object::Array(names)) => {
|
|
255
|
+
for (index, name) in names.iter().enumerate() {
|
|
256
|
+
if let Object::Name(name) = name {
|
|
257
|
+
filters.push((name.clone(), self.params(params.as_deref(), Some(index))?));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
_ => {}
|
|
262
|
+
}
|
|
263
|
+
Ok(filters)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/// The decode parameters of one filter: the dictionary, or the
|
|
267
|
+
/// `index`th of an array of them.
|
|
268
|
+
fn params(&self, params: Option<&Object>, index: Option<usize>) -> Result<Option<Rc<Object>>> {
|
|
269
|
+
let chosen = match (params, index) {
|
|
270
|
+
(Some(Object::Array(all)), Some(index)) => all.get(index),
|
|
271
|
+
(Some(Object::Array(_)), None) | (None, _) => None,
|
|
272
|
+
(Some(single), _) => Some(single),
|
|
273
|
+
};
|
|
274
|
+
chosen.map(|object| self.resolve(object).map(Resolved::into_shared)).transpose()
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/// A stream given by reference (or directly), decoded; `None` when it is
|
|
278
|
+
/// not a stream.
|
|
279
|
+
pub fn stream_data(&self, object: &Object) -> Result<Option<Vec<u8>>> {
|
|
280
|
+
let number = object.as_reference().map(|(number, _)| number);
|
|
281
|
+
let resolved = self.resolve(object)?;
|
|
282
|
+
match &*resolved {
|
|
283
|
+
Object::Stream(stream) => self.decode(stream, number).map(Some),
|
|
284
|
+
_ => Ok(None),
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/// The pages in order. When the page tree is broken, every object of
|
|
289
|
+
/// type `/Page`, by object number.
|
|
290
|
+
pub fn pages(&self) -> Result<Vec<Page>> {
|
|
291
|
+
let mut pages = Vec::new();
|
|
292
|
+
if let Some(root) = self.trailer().get(b"Root").cloned() {
|
|
293
|
+
let root = self.resolve(&root)?;
|
|
294
|
+
if let Some(tree) = root.as_dict().and_then(|root| root.get(b"Pages")).cloned() {
|
|
295
|
+
let inherited =
|
|
296
|
+
Inherited { resources: None, media_box: [0.0, 0.0, 612.0, 792.0], rotate: 0 };
|
|
297
|
+
let mut visited = Vec::new();
|
|
298
|
+
self.walk(&tree, &inherited, 0, &mut visited, &mut pages)?;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if pages.is_empty() {
|
|
302
|
+
let mut numbers: Vec<u32> = self.xref.entries.keys().copied().collect();
|
|
303
|
+
numbers.sort_unstable();
|
|
304
|
+
for number in numbers {
|
|
305
|
+
let object = self.get(number)?;
|
|
306
|
+
if let Some(dict) = object.as_dict()
|
|
307
|
+
&& dict.has_name(b"Type", b"Page")
|
|
308
|
+
{
|
|
309
|
+
let inherited = Inherited {
|
|
310
|
+
resources: None,
|
|
311
|
+
media_box: [0.0, 0.0, 612.0, 792.0],
|
|
312
|
+
rotate: 0,
|
|
313
|
+
};
|
|
314
|
+
pages.push(self.page(dict, &inherited)?);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
Ok(pages)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
fn walk(
|
|
322
|
+
&self,
|
|
323
|
+
node: &Object,
|
|
324
|
+
inherited: &Inherited,
|
|
325
|
+
depth: usize,
|
|
326
|
+
visited: &mut Vec<u32>,
|
|
327
|
+
pages: &mut Vec<Page>,
|
|
328
|
+
) -> Result<()> {
|
|
329
|
+
self.budget.nest(depth)?;
|
|
330
|
+
if let Some((number, _)) = node.as_reference() {
|
|
331
|
+
if visited.contains(&number) {
|
|
332
|
+
return Ok(());
|
|
333
|
+
}
|
|
334
|
+
visited.push(number);
|
|
335
|
+
}
|
|
336
|
+
let node = self.resolve(node)?;
|
|
337
|
+
let Some(dict) = node.as_dict() else { return Ok(()) };
|
|
338
|
+
let inherited = self.inherit(dict, inherited)?;
|
|
339
|
+
match self.entry(dict, b"Kids")?.as_deref() {
|
|
340
|
+
Some(Object::Array(kids)) if !dict.has_name(b"Type", b"Page") => {
|
|
341
|
+
for kid in kids {
|
|
342
|
+
self.walk(kid, &inherited, depth + 1, visited, pages)?;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
_ => pages.push(self.page(dict, &inherited)?),
|
|
346
|
+
}
|
|
347
|
+
Ok(())
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
fn inherit(&self, dict: &Dict, inherited: &Inherited) -> Result<Inherited> {
|
|
351
|
+
let resources = match dict.get(b"Resources") {
|
|
352
|
+
Some(object) => Some(self.resolve(object)?.into_shared()),
|
|
353
|
+
None => inherited.resources.clone(),
|
|
354
|
+
};
|
|
355
|
+
let media_box = match self.entry(dict, b"MediaBox")?.as_deref().and_then(Object::as_array) {
|
|
356
|
+
Some(corners) if corners.len() == 4 => {
|
|
357
|
+
let mut media_box = [0.0; 4];
|
|
358
|
+
for (slot, corner) in media_box.iter_mut().zip(corners) {
|
|
359
|
+
*slot = self.resolve(corner)?.as_number().unwrap_or(0.0);
|
|
360
|
+
}
|
|
361
|
+
media_box
|
|
362
|
+
}
|
|
363
|
+
_ => inherited.media_box,
|
|
364
|
+
};
|
|
365
|
+
let rotate = self
|
|
366
|
+
.entry(dict, b"Rotate")?
|
|
367
|
+
.and_then(|rotate| rotate.as_integer())
|
|
368
|
+
.unwrap_or(inherited.rotate);
|
|
369
|
+
Ok(Inherited { resources, media_box, rotate })
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
fn page(&self, dict: &Dict, inherited: &Inherited) -> Result<Page> {
|
|
373
|
+
let inherited = self.inherit(dict, inherited)?;
|
|
374
|
+
let contents = match dict.get(b"Contents") {
|
|
375
|
+
Some(reference @ Object::Ref(..)) => match &*self.resolve(reference)? {
|
|
376
|
+
Object::Array(parts) => parts.clone(),
|
|
377
|
+
_ => vec![reference.clone()],
|
|
378
|
+
},
|
|
379
|
+
Some(Object::Array(parts)) => parts.clone(),
|
|
380
|
+
Some(other) => vec![other.clone()],
|
|
381
|
+
None => Vec::new(),
|
|
382
|
+
};
|
|
383
|
+
Ok(Page {
|
|
384
|
+
resources: inherited.resources,
|
|
385
|
+
contents,
|
|
386
|
+
media_box: inherited.media_box,
|
|
387
|
+
rotate: inherited.rotate,
|
|
388
|
+
})
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/// A filter's name and its decode parameters.
|
|
393
|
+
pub type Filter = (Name, Option<Rc<Object>>);
|
|
394
|
+
|
|
395
|
+
/// `raw` through `filters`, in order.
|
|
396
|
+
pub fn decode_with(raw: &[u8], filters: &[Filter], budget: &Budget<'_>) -> Result<Vec<u8>> {
|
|
397
|
+
let filters: Vec<(&[u8], Option<&Dict>)> = filters
|
|
398
|
+
.iter()
|
|
399
|
+
.map(|(name, params)| (&**name, params.as_deref().and_then(Object::as_dict)))
|
|
400
|
+
.collect();
|
|
401
|
+
filter::decode(raw, &filters, budget)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
struct Inherited {
|
|
405
|
+
resources: Option<Rc<Object>>,
|
|
406
|
+
media_box: [f64; 4],
|
|
407
|
+
rotate: i64,
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/// Decrypts every string in an object freshly read from the file.
|
|
411
|
+
fn decrypt_strings(object: &mut Object, crypt: &Crypt, id: (u32, u16)) {
|
|
412
|
+
match object {
|
|
413
|
+
Object::String(bytes) => *bytes = crypt.decrypt(false, id, bytes),
|
|
414
|
+
Object::Array(items) => items.iter_mut().for_each(|item| decrypt_strings(item, crypt, id)),
|
|
415
|
+
Object::Dict(dict) | Object::Stream(Stream { dict, .. }) => {
|
|
416
|
+
dict.0.iter_mut().for_each(|(_, value)| decrypt_strings(value, crypt, id));
|
|
417
|
+
}
|
|
418
|
+
_ => {}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
//! Stream filters (ISO 32000-1, 7.4): what text and fonts are compressed
|
|
2
|
+
//! with. Image codecs are not decoded; nothing here reads images.
|
|
3
|
+
|
|
4
|
+
#[cfg(test)]
|
|
5
|
+
mod tests;
|
|
6
|
+
|
|
7
|
+
use super::object::{Dict, Object, malformed};
|
|
8
|
+
use crate::read::Budget;
|
|
9
|
+
use crate::{Error, Result};
|
|
10
|
+
|
|
11
|
+
/// Applies `filters` in order, each with its parameters.
|
|
12
|
+
pub fn decode(
|
|
13
|
+
data: &[u8],
|
|
14
|
+
filters: &[(&[u8], Option<&Dict>)],
|
|
15
|
+
budget: &Budget<'_>,
|
|
16
|
+
) -> Result<Vec<u8>> {
|
|
17
|
+
let mut current: Option<Vec<u8>> = None;
|
|
18
|
+
for &(name, params) in filters {
|
|
19
|
+
let input = current.as_deref().unwrap_or(data);
|
|
20
|
+
let output = match name {
|
|
21
|
+
b"FlateDecode" | b"Fl" => predict(inflate(input, budget)?, params)?,
|
|
22
|
+
b"LZWDecode" | b"LZW" => {
|
|
23
|
+
let early = params
|
|
24
|
+
.and_then(|params| params.get(b"EarlyChange"))
|
|
25
|
+
.and_then(Object::as_integer)
|
|
26
|
+
!= Some(0);
|
|
27
|
+
predict(lzw(input, early, budget)?, params)?
|
|
28
|
+
}
|
|
29
|
+
b"ASCII85Decode" | b"A85" => ascii85(input),
|
|
30
|
+
b"ASCIIHexDecode" | b"AHx" => super::lexer::hex_bytes(
|
|
31
|
+
input.split(|&byte| byte == b'>').next().unwrap_or_default(),
|
|
32
|
+
),
|
|
33
|
+
b"RunLengthDecode" | b"RL" => run_length(input),
|
|
34
|
+
b"Crypt" => continue,
|
|
35
|
+
_ => {
|
|
36
|
+
return Err(Error::UnsupportedDocument(format!(
|
|
37
|
+
"the {} stream filter",
|
|
38
|
+
String::from_utf8_lossy(name)
|
|
39
|
+
)));
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
budget.inflate(output.len())?;
|
|
43
|
+
budget.check()?;
|
|
44
|
+
current = Some(output);
|
|
45
|
+
}
|
|
46
|
+
Ok(current.unwrap_or_else(|| data.to_vec()))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// zlib, or raw deflate when the header is missing. A truncated or
|
|
50
|
+
/// corrupt stream yields what inflated before the damage, as viewers show.
|
|
51
|
+
fn inflate(data: &[u8], budget: &Budget<'_>) -> Result<Vec<u8>> {
|
|
52
|
+
let zlib = data.len() > 2
|
|
53
|
+
&& data[0] & 0x0F == 8
|
|
54
|
+
&& (u16::from(data[0]) << 8 | u16::from(data[1])).is_multiple_of(31);
|
|
55
|
+
let expected = data.len().saturating_mul(4);
|
|
56
|
+
let (output, complete) =
|
|
57
|
+
crate::read::inflate::inflate(data, zlib, expected, budget.inflatable())?;
|
|
58
|
+
if complete || !output.is_empty() {
|
|
59
|
+
return Ok(output);
|
|
60
|
+
}
|
|
61
|
+
if zlib {
|
|
62
|
+
// A header that only looked like zlib's: try the bytes as raw deflate.
|
|
63
|
+
let (output, _) =
|
|
64
|
+
crate::read::inflate::inflate(data, false, expected, budget.inflatable())?;
|
|
65
|
+
if !output.is_empty() {
|
|
66
|
+
return Ok(output);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
Err(malformed("a stream does not inflate"))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// Undoes a PNG (10 and up) or TIFF (2) predictor.
|
|
73
|
+
fn predict(data: Vec<u8>, params: Option<&Dict>) -> Result<Vec<u8>> {
|
|
74
|
+
let Some(params) = params else { return Ok(data) };
|
|
75
|
+
let number =
|
|
76
|
+
|key: &[u8], default: i64| params.get(key).and_then(Object::as_integer).unwrap_or(default);
|
|
77
|
+
let predictor = number(b"Predictor", 1);
|
|
78
|
+
if predictor < 2 {
|
|
79
|
+
return Ok(data);
|
|
80
|
+
}
|
|
81
|
+
let (colors, bits, columns) =
|
|
82
|
+
(number(b"Colors", 1), number(b"BitsPerComponent", 8), number(b"Columns", 1));
|
|
83
|
+
let invalid = || malformed("bad predictor parameters");
|
|
84
|
+
let bits_per_pixel =
|
|
85
|
+
usize::try_from(colors.checked_mul(bits).ok_or_else(invalid)?).map_err(|_| invalid())?;
|
|
86
|
+
let columns = usize::try_from(columns).map_err(|_| invalid())?;
|
|
87
|
+
let pixel = bits_per_pixel.div_ceil(8).max(1);
|
|
88
|
+
let row = (bits_per_pixel.checked_mul(columns).ok_or_else(invalid)?).div_ceil(8);
|
|
89
|
+
if row == 0 {
|
|
90
|
+
return Err(invalid());
|
|
91
|
+
}
|
|
92
|
+
if predictor == 2 {
|
|
93
|
+
let mut data = data;
|
|
94
|
+
if bits == 8 {
|
|
95
|
+
for line in data.chunks_mut(row) {
|
|
96
|
+
for at in pixel..line.len() {
|
|
97
|
+
line[at] = line[at].wrapping_add(line[at - pixel]);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return Ok(data);
|
|
102
|
+
}
|
|
103
|
+
let mut out = Vec::with_capacity(data.len());
|
|
104
|
+
let mut previous = vec![0_u8; row];
|
|
105
|
+
for chunk in data.chunks(row + 1) {
|
|
106
|
+
let (&kind, line) = chunk.split_first().ok_or_else(invalid)?;
|
|
107
|
+
let mut current = line.to_vec();
|
|
108
|
+
current.resize(row, 0);
|
|
109
|
+
for at in 0..row {
|
|
110
|
+
let left = if at >= pixel { current[at - pixel] } else { 0 };
|
|
111
|
+
let up = previous[at];
|
|
112
|
+
let up_left = if at >= pixel { previous[at - pixel] } else { 0 };
|
|
113
|
+
current[at] = current[at].wrapping_add(match kind {
|
|
114
|
+
1 => left,
|
|
115
|
+
2 => up,
|
|
116
|
+
3 => left.midpoint(up),
|
|
117
|
+
4 => paeth(left, up, up_left),
|
|
118
|
+
_ => 0,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
out.extend_from_slice(¤t[..line.len().min(row)]);
|
|
122
|
+
previous = current;
|
|
123
|
+
}
|
|
124
|
+
Ok(out)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fn paeth(left: u8, up: u8, up_left: u8) -> u8 {
|
|
128
|
+
let estimate = i16::from(left) + i16::from(up) - i16::from(up_left);
|
|
129
|
+
let (a, b, c) = (
|
|
130
|
+
(estimate - i16::from(left)).abs(),
|
|
131
|
+
(estimate - i16::from(up)).abs(),
|
|
132
|
+
(estimate - i16::from(up_left)).abs(),
|
|
133
|
+
);
|
|
134
|
+
if a <= b && a <= c {
|
|
135
|
+
left
|
|
136
|
+
} else if b <= c {
|
|
137
|
+
up
|
|
138
|
+
} else {
|
|
139
|
+
up_left
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// LZW with 9 to 12 bit codes; `early` is the usual `EarlyChange 1`.
|
|
144
|
+
fn lzw(data: &[u8], early: bool, budget: &Budget<'_>) -> Result<Vec<u8>> {
|
|
145
|
+
const CLEAR: u16 = 256;
|
|
146
|
+
const END: u16 = 257;
|
|
147
|
+
// Each code: the code it extends, its last byte, its length.
|
|
148
|
+
let mut prefix = vec![0_u16; 4096];
|
|
149
|
+
let mut last = vec![0_u8; 4096];
|
|
150
|
+
let mut length = vec![0_u16; 4096];
|
|
151
|
+
for (code, byte) in (0..=255_u8).enumerate() {
|
|
152
|
+
last[code] = byte;
|
|
153
|
+
length[code] = 1;
|
|
154
|
+
}
|
|
155
|
+
let (mut next, mut width, mut previous): (u16, u32, Option<u16>) = (258, 9, None);
|
|
156
|
+
let (mut buffer, mut buffered) = (0_u32, 0_u32);
|
|
157
|
+
let mut out = Vec::with_capacity(data.len() * 3);
|
|
158
|
+
let limit = budget.inflatable();
|
|
159
|
+
for &byte in data {
|
|
160
|
+
buffer = buffer << 8 | u32::from(byte);
|
|
161
|
+
buffered += 8;
|
|
162
|
+
while buffered >= width {
|
|
163
|
+
buffered -= width;
|
|
164
|
+
let code = u16::try_from((buffer >> buffered) & ((1 << width) - 1)).unwrap_or(END);
|
|
165
|
+
if code == CLEAR {
|
|
166
|
+
(next, width, previous) = (258, 9, None);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if code == END {
|
|
170
|
+
return Ok(out);
|
|
171
|
+
}
|
|
172
|
+
let known = code < next;
|
|
173
|
+
let source =
|
|
174
|
+
if known { code } else { previous.ok_or_else(|| malformed("bad LZW code"))? };
|
|
175
|
+
let start = out.len();
|
|
176
|
+
let size = usize::from(length[usize::from(source)]);
|
|
177
|
+
out.resize(start + size, 0);
|
|
178
|
+
let mut walk = source;
|
|
179
|
+
for at in (start..start + size).rev() {
|
|
180
|
+
out[at] = last[usize::from(walk)];
|
|
181
|
+
walk = prefix[usize::from(walk)];
|
|
182
|
+
}
|
|
183
|
+
let first = out[start];
|
|
184
|
+
if !known {
|
|
185
|
+
out.push(first);
|
|
186
|
+
}
|
|
187
|
+
if let Some(previous) = previous
|
|
188
|
+
&& next < 4096
|
|
189
|
+
{
|
|
190
|
+
prefix[usize::from(next)] = previous;
|
|
191
|
+
last[usize::from(next)] = first;
|
|
192
|
+
length[usize::from(next)] = length[usize::from(previous)].saturating_add(1);
|
|
193
|
+
next += 1;
|
|
194
|
+
}
|
|
195
|
+
previous = Some(code);
|
|
196
|
+
let threshold = u32::from(next) + u32::from(early);
|
|
197
|
+
width = match threshold {
|
|
198
|
+
0..=511 => 9,
|
|
199
|
+
512..=1023 => 10,
|
|
200
|
+
1024..=2047 => 11,
|
|
201
|
+
_ => 12,
|
|
202
|
+
};
|
|
203
|
+
if out.len() > limit {
|
|
204
|
+
return Err(Error::LimitExceeded("decompressed size"));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
Ok(out)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
fn ascii85(data: &[u8]) -> Vec<u8> {
|
|
212
|
+
let mut out = Vec::with_capacity(data.len() * 4 / 5 + 4);
|
|
213
|
+
let (mut group, mut count) = (0_u32, 0);
|
|
214
|
+
for &byte in data {
|
|
215
|
+
match byte {
|
|
216
|
+
b'~' => break,
|
|
217
|
+
b'z' if count == 0 => out.extend_from_slice(&[0; 4]),
|
|
218
|
+
b'!'..=b'u' => {
|
|
219
|
+
group = group.wrapping_mul(85).wrapping_add(u32::from(byte - b'!'));
|
|
220
|
+
count += 1;
|
|
221
|
+
if count == 5 {
|
|
222
|
+
out.extend_from_slice(&group.to_be_bytes());
|
|
223
|
+
(group, count) = (0, 0);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
_ => {}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if count > 1 {
|
|
230
|
+
for _ in count..5 {
|
|
231
|
+
group = group.wrapping_mul(85).wrapping_add(84);
|
|
232
|
+
}
|
|
233
|
+
out.extend_from_slice(&group.to_be_bytes()[..count - 1]);
|
|
234
|
+
}
|
|
235
|
+
out
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
fn run_length(data: &[u8]) -> Vec<u8> {
|
|
239
|
+
let mut out = Vec::with_capacity(data.len() * 2);
|
|
240
|
+
let mut at = 0;
|
|
241
|
+
while let Some(&length) = data.get(at) {
|
|
242
|
+
match length {
|
|
243
|
+
128 => break,
|
|
244
|
+
0..=127 => {
|
|
245
|
+
let run = usize::from(length) + 1;
|
|
246
|
+
out.extend_from_slice(&data[at + 1..(at + 1 + run).min(data.len())]);
|
|
247
|
+
at += 1 + run;
|
|
248
|
+
}
|
|
249
|
+
_ => {
|
|
250
|
+
if let Some(&byte) = data.get(at + 1) {
|
|
251
|
+
out.resize(out.len() + 257 - usize::from(length), byte);
|
|
252
|
+
}
|
|
253
|
+
at += 2;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
out
|
|
258
|
+
}
|