1use std::io::Read;
15
16use lopdf::{Dictionary, Document, Object};
17
18#[derive(Debug, Clone)]
20pub struct ImageCodec {
21 pub name: String,
22 pub parms: Option<Dictionary>,
23}
24
25#[derive(Debug)]
27pub enum Unsupported {
28 Filter(String),
29 Predictor(i64),
30 Corrupt(&'static str),
31}
32
33fn deref<'a>(doc: &'a Document, obj: &'a Object) -> &'a Object {
34 match obj {
35 Object::Reference(id) => doc.get_object(*id).unwrap_or(obj),
36 o => o,
37 }
38}
39
40pub fn filters(doc: &Document, dict: &Dictionary) -> Vec<(String, Option<Dictionary>)> {
42 let names: Vec<String> = match dict
43 .get(b"Filter")
44 .ok()
45 .or_else(|| {
46 dict.get(b"F")
47 .ok()
48 .filter(|o| !matches!(o, Object::String(..)))
49 })
50 .map(|o| deref(doc, o))
51 {
52 Some(Object::Name(n)) => vec![String::from_utf8_lossy(n).into_owned()],
53 Some(Object::Array(a)) => a
54 .iter()
55 .filter_map(|o| match deref(doc, o) {
56 Object::Name(n) => Some(String::from_utf8_lossy(n).into_owned()),
57 _ => None,
58 })
59 .collect(),
60 _ => Vec::new(),
61 };
62 let parms: Vec<Option<Dictionary>> = match dict
63 .get(b"DecodeParms")
64 .ok()
65 .or_else(|| dict.get(b"DP").ok())
66 .map(|o| deref(doc, o))
67 {
68 Some(Object::Dictionary(d)) => vec![Some(d.clone())],
69 Some(Object::Array(a)) => a
70 .iter()
71 .map(|o| match deref(doc, o) {
72 Object::Dictionary(d) => Some(d.clone()),
73 _ => None,
74 })
75 .collect(),
76 _ => Vec::new(),
77 };
78 names
79 .into_iter()
80 .enumerate()
81 .map(|(i, n)| (n, parms.get(i).cloned().flatten()))
82 .collect()
83}
84
85pub fn apply(
89 doc: &Document,
90 data: &[u8],
91 chain: &[(String, Option<Dictionary>)],
92) -> Result<(Vec<u8>, Option<ImageCodec>), Unsupported> {
93 let mut cur: Vec<u8> = data.to_vec();
94 for (name, parms) in chain {
95 cur = match name.as_str() {
96 "FlateDecode" | "Fl" => predictor(doc, inflate(&cur), parms.as_ref())?,
97 "LZWDecode" | "LZW" => {
98 let early = parms
99 .as_ref()
100 .and_then(|p| p.get(b"EarlyChange").ok())
101 .and_then(|o| deref(doc, o).as_i64().ok())
102 .map(|v| v != 0)
103 .unwrap_or(true);
104 predictor(doc, lzw(&cur, early), parms.as_ref())?
105 }
106 "RunLengthDecode" | "RL" => run_length(&cur),
107 "ASCII85Decode" | "A85" => ascii85(&cur),
108 "ASCIIHexDecode" | "AHx" => ascii_hex(&cur),
109 "DCTDecode" | "DCT" | "JPXDecode" | "JBIG2Decode" | "CCITTFaxDecode" | "CCF" => {
110 return Ok((
111 cur,
112 Some(ImageCodec {
113 name: match name.as_str() {
114 "DCT" => "DCTDecode".into(),
115 "CCF" => "CCITTFaxDecode".into(),
116 n => n.to_string(),
117 },
118 parms: parms.clone(),
119 }),
120 ));
121 }
122 other => return Err(Unsupported::Filter(other.to_string())),
123 };
124 }
125 Ok((cur, None))
126}
127
128fn inflate(data: &[u8]) -> Vec<u8> {
131 let mut out = Vec::new();
132 let mut dec = flate2::read::ZlibDecoder::new(data);
133 let mut buf = [0u8; 1 << 16];
134 loop {
135 match dec.read(&mut buf) {
136 Ok(0) => break,
137 Ok(n) => out.extend_from_slice(&buf[..n]),
138 Err(_) => break,
139 }
140 }
141 out
142}
143
144fn lzw(data: &[u8], early_change: bool) -> Vec<u8> {
146 use weezl::{decode::Decoder, BitOrder};
147 let mut dec = if early_change {
148 Decoder::with_tiff_size_switch(BitOrder::Msb, 8)
149 } else {
150 Decoder::new(BitOrder::Msb, 8)
151 };
152 let mut out = Vec::new();
153 let mut input = data;
154 let mut buf = vec![0u8; 1 << 16];
155 loop {
156 let r = dec.decode_bytes(input, &mut buf);
157 out.extend_from_slice(&buf[..r.consumed_out]);
158 input = &input[r.consumed_in..];
159 match r.status {
160 Ok(weezl::LzwStatus::Done) | Err(_) => break,
161 Ok(_) if r.consumed_in == 0 && r.consumed_out == 0 => break,
162 Ok(_) => {}
163 }
164 }
165 out
166}
167
168fn run_length(data: &[u8]) -> Vec<u8> {
169 let mut out = Vec::new();
170 let mut i = 0;
171 while i < data.len() {
172 let l = data[i] as usize;
173 i += 1;
174 match l {
175 128 => break,
176 0..=127 => {
177 let end = (i + l + 1).min(data.len());
178 out.extend_from_slice(&data[i..end]);
179 i = end;
180 }
181 _ => {
182 if let Some(&b) = data.get(i) {
183 out.extend(std::iter::repeat_n(b, 257 - l));
184 }
185 i += 1;
186 }
187 }
188 }
189 out
190}
191
192fn ascii_hex(data: &[u8]) -> Vec<u8> {
193 let mut out = Vec::new();
194 let mut hi: Option<u8> = None;
195 for &c in data {
196 if c == b'>' {
197 break;
198 }
199 let v = match c {
200 b'0'..=b'9' => c - b'0',
201 b'a'..=b'f' => c - b'a' + 10,
202 b'A'..=b'F' => c - b'A' + 10,
203 _ => continue,
204 };
205 match hi.take() {
206 None => hi = Some(v),
207 Some(h) => out.push(h << 4 | v),
208 }
209 }
210 if let Some(h) = hi {
211 out.push(h << 4);
212 }
213 out
214}
215
216fn ascii85(data: &[u8]) -> Vec<u8> {
217 let mut out = Vec::new();
218 let mut group = [0u8; 5];
219 let mut n = 0;
220 let mut i = 0;
221 if data.starts_with(b"<~") {
222 i = 2;
223 }
224 while i < data.len() {
225 let c = data[i];
226 i += 1;
227 match c {
228 b'~' => break,
229 b'z' if n == 0 => out.extend_from_slice(&[0, 0, 0, 0]),
230 b'!'..=b'u' => {
231 group[n] = c - b'!';
232 n += 1;
233 if n == 5 {
234 let v = group.iter().fold(0u32, |acc, &d| {
235 acc.wrapping_mul(85).wrapping_add(u32::from(d))
236 });
237 out.extend_from_slice(&v.to_be_bytes());
238 n = 0;
239 }
240 }
241 _ => {}
242 }
243 }
244 if n > 0 {
245 for g in group.iter_mut().skip(n) {
246 *g = 84;
247 }
248 let v = group.iter().fold(0u32, |acc, &d| {
249 acc.wrapping_mul(85).wrapping_add(u32::from(d))
250 });
251 out.extend_from_slice(&v.to_be_bytes()[..n - 1]);
252 }
253 out
254}
255
256fn predictor(
258 doc: &Document,
259 data: Vec<u8>,
260 parms: Option<&Dictionary>,
261) -> Result<Vec<u8>, Unsupported> {
262 let Some(p) = parms else { return Ok(data) };
263 let int = |k: &[u8], d: i64| {
264 p.get(k)
265 .ok()
266 .and_then(|o| deref(doc, o).as_i64().ok())
267 .unwrap_or(d)
268 };
269 let pred = int(b"Predictor", 1);
270 if pred <= 1 {
271 return Ok(data);
272 }
273 let colors = int(b"Colors", 1).max(1) as usize;
274 let bpc = int(b"BitsPerComponent", 8).max(1) as usize;
275 let columns = int(b"Columns", 1).max(1) as usize;
276 let bpp = (colors * bpc).div_ceil(8).max(1);
277 let row_len = (colors * bpc * columns).div_ceil(8);
278 match pred {
279 2 => {
280 if bpc != 8 {
281 return Err(Unsupported::Predictor(pred));
282 }
283 let mut data = data;
284 for row in data.chunks_exact_mut(row_len) {
285 for i in bpp..row.len() {
286 row[i] = row[i].wrapping_add(row[i - bpp]);
287 }
288 }
289 Ok(data)
290 }
291 10..=15 => {
292 let mut out = Vec::with_capacity(data.len());
293 let mut prev = vec![0u8; row_len];
294 for chunk in data.chunks(row_len + 1) {
295 let Some((&ft, row)) = chunk.split_first() else {
296 break;
297 };
298 let mut cur = row.to_vec();
299 cur.resize(row_len, 0);
300 for i in 0..row_len {
301 let a = if i >= bpp { cur[i - bpp] } else { 0 };
302 let b = prev[i];
303 let c = if i >= bpp { prev[i - bpp] } else { 0 };
304 let x = cur[i];
305 cur[i] = match ft {
306 0 => x,
307 1 => x.wrapping_add(a),
308 2 => x.wrapping_add(b),
309 3 => x.wrapping_add(((u16::from(a) + u16::from(b)) / 2) as u8),
310 4 => {
311 let (ia, ib, ic) = (i16::from(a), i16::from(b), i16::from(c));
312 let pa = (ib - ic).abs();
313 let pb = (ia - ic).abs();
314 let pc = (ia + ib - 2 * ic).abs();
315 let pr = if pa <= pb && pa <= pc {
316 a
317 } else if pb <= pc {
318 b
319 } else {
320 c
321 };
322 x.wrapping_add(pr)
323 }
324 _ => return Err(Unsupported::Corrupt("PNG predictor row filter")),
325 };
326 }
327 out.extend_from_slice(&cur);
328 prev = cur;
329 }
330 Ok(out)
331 }
332 other => Err(Unsupported::Predictor(other)),
333 }
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339
340 #[test]
341 fn run_length_and_ascii_filters() {
342 assert_eq!(
343 run_length(&[2, b'a', b'b', b'c', 254, b'z', 128]),
344 b"abczzz"
345 );
346 assert_eq!(ascii_hex(b"48 65 6C6c 6F>"), b"Hello");
347 assert_eq!(ascii85(b"<~87cURD]i,\"Ebo80~>"), b"Hello World!");
348 assert_eq!(ascii85(b"z~>"), [0, 0, 0, 0]);
349 }
350
351 #[test]
352 fn png_up_predictor_on_one_bit_rows() {
353 let doc = Document::new();
355 let mut parms = Dictionary::new();
356 parms.set("Predictor", 12);
357 parms.set("Colors", 1);
358 parms.set("BitsPerComponent", 1);
359 parms.set("Columns", 16);
360 let data = vec![0u8, 0b1010_1010, 0b0000_1111, 2, 0b0101_0101, 0b1111_0000];
361 let out = predictor(&doc, data, Some(&parms)).unwrap();
362 assert_eq!(
363 out,
364 vec![0b1010_1010, 0b0000_1111, 0b1111_1111, 0b1111_1111]
365 );
366 }
367
368 #[test]
369 fn flate_round_trip_with_trailing_garbage() {
370 use std::io::Write;
371 let mut enc = flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
372 enc.write_all(b"scan line bytes").unwrap();
373 let mut z = enc.finish().unwrap();
374 z.extend_from_slice(b"junk");
375 assert_eq!(inflate(&z), b"scan line bytes");
376 }
377}