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