1use crate::error::PdfError;
8use crate::objects::PdfDict;
9
10#[derive(Debug, Clone, PartialEq)]
12pub enum Filter {
13 FlateDecode,
14 LZWDecode,
15 ASCIIHexDecode,
16 ASCII85Decode,
17 RunLengthDecode,
18 DCTDecode,
19 CCITTFaxDecode,
20 JPXDecode,
21 JBIG2Decode,
22}
23
24pub fn parse_filters(
29 dict: &PdfDict,
30 resolver: Option<&crate::resolver::Resolver>,
31) -> Result<(Vec<Filter>, Vec<Option<PdfDict>>), PdfError> {
32 let filter_obj = match dict.get(b"Filter") {
33 Some(obj) => obj,
34 None => return Ok((Vec::new(), Vec::new())),
35 };
36
37 let resolved_filter;
39 let filter_obj = if let crate::objects::PdfObj::Ref(_, _) = filter_obj {
40 if let Some(r) = resolver {
41 resolved_filter = r.deref(filter_obj).unwrap_or_else(|_| filter_obj.clone());
42 &resolved_filter
43 } else {
44 filter_obj
45 }
46 } else {
47 filter_obj
48 };
49
50 let filter_names: Vec<&[u8]> = match filter_obj {
51 crate::objects::PdfObj::Name(n) => vec![n.as_slice()],
52 crate::objects::PdfObj::Array(arr) => {
53 arr.iter()
55 .filter_map(|o| {
56 if let Some(n) = o.as_name() {
57 return Some(n);
58 }
59 None
60 })
61 .collect()
62 }
63 _ => return Ok((Vec::new(), Vec::new())),
64 };
65
66 let mut filters = Vec::new();
67 for name in &filter_names {
68 filters.push(filter_from_name(name)?);
69 }
70
71 let dp_obj = dict.get(b"DecodeParms");
73 let resolved_dp;
74 let dp_obj = match dp_obj {
75 Some(crate::objects::PdfObj::Ref(_, _)) if resolver.is_some() => {
76 resolved_dp = resolver.unwrap().deref(dp_obj.unwrap()).ok();
77 resolved_dp.as_ref()
78 }
79 other => other,
80 };
81
82 let parms = match dp_obj {
83 Some(crate::objects::PdfObj::Dict(d)) => vec![Some(d.clone())],
84 Some(crate::objects::PdfObj::Array(arr)) => arr
85 .iter()
86 .map(|o| match o {
87 crate::objects::PdfObj::Dict(d) => Some(d.clone()),
88 crate::objects::PdfObj::Ref(_, _) if resolver.is_some() => resolver
89 .unwrap()
90 .deref(o)
91 .ok()
92 .and_then(|r| r.as_dict().cloned()),
93 _ => None,
94 })
95 .collect(),
96 _ => vec![None; filters.len()],
97 };
98
99 let mut parms = parms;
101 while parms.len() < filters.len() {
102 parms.push(None);
103 }
104
105 for (i, filter) in filters.iter().enumerate() {
112 if *filter != Filter::CCITTFaxDecode {
113 continue;
114 }
115 let dp = parms[i].get_or_insert_with(PdfDict::new);
116 if dp.get_int(b"Columns").is_none()
117 && let Some(w) = dict.get_int(b"Width")
118 {
119 dp.insert(b"Columns".to_vec(), crate::objects::PdfObj::Int(w));
120 }
121 if dp.get_int(b"Rows").is_none()
122 && let Some(h) = dict.get_int(b"Height")
123 {
124 dp.insert(b"Rows".to_vec(), crate::objects::PdfObj::Int(h));
125 }
126 }
127
128 Ok((filters, parms))
129}
130
131fn filter_from_name(name: &[u8]) -> Result<Filter, PdfError> {
132 match name {
133 b"FlateDecode" | b"Fl" => Ok(Filter::FlateDecode),
134 b"LZWDecode" | b"LZW" => Ok(Filter::LZWDecode),
135 b"ASCIIHexDecode" | b"AHx" => Ok(Filter::ASCIIHexDecode),
136 b"ASCII85Decode" | b"A85" => Ok(Filter::ASCII85Decode),
137 b"RunLengthDecode" | b"RL" => Ok(Filter::RunLengthDecode),
138 b"DCTDecode" | b"DCT" => Ok(Filter::DCTDecode),
139 b"CCITTFaxDecode" | b"CCF" => Ok(Filter::CCITTFaxDecode),
140 b"JPXDecode" | b"JPX" => Ok(Filter::JPXDecode),
141 b"JBIG2Decode" | b"JBIG2" => Ok(Filter::JBIG2Decode),
142 _ if name.starts_with(b"Flate") => Ok(Filter::FlateDecode),
144 _ if name.starts_with(b"LZW") => Ok(Filter::LZWDecode),
145 _ if name.starts_with(b"ASCIIHex") => Ok(Filter::ASCIIHexDecode),
146 _ if name.starts_with(b"ASCII85") => Ok(Filter::ASCII85Decode),
147 _ if name.starts_with(b"RunLength") => Ok(Filter::RunLengthDecode),
148 _ if name.starts_with(b"CCITT") => Ok(Filter::CCITTFaxDecode),
149 _ if name.starts_with(b"JPX") => Ok(Filter::JPXDecode),
150 _ if name.starts_with(b"JBIG2") => Ok(Filter::JBIG2Decode),
151 _ => Err(PdfError::UnsupportedFilter(
152 String::from_utf8_lossy(name).into(),
153 )),
154 }
155}
156
157pub fn decode_stream(
159 raw_data: &[u8],
160 filters: &[Filter],
161 decode_parms: &[Option<PdfDict>],
162 jbig2_globals: Option<&[u8]>,
163) -> Result<Vec<u8>, PdfError> {
164 let mut data = raw_data.to_vec();
165
166 for (i, filter) in filters.iter().enumerate() {
167 let parms = decode_parms.get(i).and_then(|p| p.as_ref());
168 data = match filter {
169 Filter::FlateDecode => decode_flate(&data, parms)?,
170 Filter::LZWDecode => decode_lzw(&data, parms)?,
171 Filter::ASCIIHexDecode => decode_ascii_hex(&data)?,
172 Filter::ASCII85Decode => decode_ascii85(&data)?,
173 Filter::RunLengthDecode => decode_run_length(&data)?,
174 Filter::DCTDecode => decode_dct(&data)?,
175 Filter::CCITTFaxDecode => decode_ccittfax(&data, parms)?,
176 #[cfg(feature = "jpx")]
177 Filter::JPXDecode => decode_jpx(&data)?,
178 #[cfg(not(feature = "jpx"))]
179 Filter::JPXDecode => {
180 return Err(PdfError::UnsupportedFilter("JPXDecode (disabled)".into()));
181 }
182 Filter::JBIG2Decode => decode_jbig2(&data, jbig2_globals)?,
183 };
184 }
185
186 Ok(data)
187}
188
189fn decode_flate(data: &[u8], parms: Option<&PdfDict>) -> Result<Vec<u8>, PdfError> {
191 let (zlib_output, zlib_clean, _) = decode_flate_inner(data, true);
194 let output = if zlib_clean {
195 zlib_output?
196 } else {
197 let zlib_data = zlib_output.unwrap_or_default();
203 if data.len() > 2 {
204 let (raw_output, _, _) = decode_flate_inner(&data[2..], false);
205 let raw_data = raw_output.unwrap_or_default();
206 if raw_data.len() > zlib_data.len()
207 && raw_data[..zlib_data.len()] == zlib_data[..]
208 && looks_like_valid_continuation(&raw_data, zlib_data.len())
209 {
210 raw_data
214 } else if !zlib_data.is_empty() {
215 zlib_data
216 } else if !raw_data.is_empty() {
217 raw_data
218 } else {
219 return Err(PdfError::DecompressionError(
220 "flate: decompression failed".into(),
221 ));
222 }
223 } else if !zlib_data.is_empty() {
224 zlib_data
225 } else {
226 return Err(PdfError::DecompressionError(
227 "flate: decompression failed".into(),
228 ));
229 }
230 };
231
232 if let Some(parms) = parms {
234 let predictor = parms.get_int(b"Predictor").unwrap_or(1);
235 if predictor > 1 {
236 return apply_predictor(&output, parms, predictor);
237 }
238 }
239
240 Ok(output)
241}
242
243fn looks_like_valid_continuation(data: &[u8], start: usize) -> bool {
248 if start >= data.len() {
249 return false;
250 }
251 let sample = &data[start..data.len().min(start + 64)];
253 let printable = sample
254 .iter()
255 .filter(|&&b| b.is_ascii_graphic() || b.is_ascii_whitespace())
256 .count();
257 printable * 5 >= sample.len() * 4
259}
260
261fn decode_flate_inner(data: &[u8], zlib: bool) -> (Result<Vec<u8>, PdfError>, bool, usize) {
265 use flate2::Decompress;
266
267 let mut decompressor = Decompress::new(zlib);
268 let mut output = Vec::with_capacity(data.len() * 3);
269 let mut buf = [0u8; 8192];
270 let mut input_offset = 0;
271
272 loop {
273 let before_in = decompressor.total_in() as usize;
274 let before_out = decompressor.total_out() as usize;
275 let result = decompressor.decompress(
276 &data[input_offset..],
277 &mut buf,
278 flate2::FlushDecompress::None,
279 );
280
281 let consumed = decompressor.total_in() as usize - before_in;
282 let produced = decompressor.total_out() as usize - before_out;
283 input_offset += consumed;
284 output.extend_from_slice(&buf[..produced]);
285
286 match result {
287 Ok(status) => match status {
288 flate2::Status::StreamEnd => return (Ok(output), true, input_offset),
289 flate2::Status::Ok | flate2::Status::BufError => {
290 if consumed == 0 && produced == 0 {
291 return (Ok(output), true, input_offset);
292 }
293 }
294 },
295 Err(_) if !output.is_empty() => {
296 return (Ok(output), false, input_offset);
298 }
299 Err(e) => {
300 return (
301 Err(PdfError::DecompressionError(format!("flate: {e}"))),
302 false,
303 input_offset,
304 );
305 }
306 }
307 }
308}
309
310fn decode_lzw(data: &[u8], parms: Option<&PdfDict>) -> Result<Vec<u8>, PdfError> {
315 let early_change = parms.and_then(|p| p.get_int(b"EarlyChange")).unwrap_or(1) != 0;
316
317 let output = lzw_decode(data, early_change)
318 .ok_or_else(|| PdfError::DecompressionError("lzw: decode failed".into()))?;
319
320 if let Some(parms) = parms {
322 let predictor = parms.get_int(b"Predictor").unwrap_or(1);
323 if predictor > 1 {
324 return apply_predictor(&output, parms, predictor);
325 }
326 }
327
328 Ok(output)
329}
330
331const LZW_CLEAR_TABLE: usize = 256;
334const LZW_EOD: usize = 257;
335const LZW_MAX_ENTRIES: usize = 4096;
336const LZW_INITIAL_SIZE: usize = 258;
337
338fn lzw_decode(data: &[u8], early_change: bool) -> Option<Vec<u8>> {
340 let mut table = LzwTable::new(early_change);
341 let mut bit_size = table.code_length();
342 let mut reader = LzwBitReader::new(data);
343 let mut decoded = Vec::new();
344 let mut prev: Option<usize> = None;
345
346 loop {
347 let next = match reader.read(bit_size) {
348 Some(code) => code as usize,
349 None => {
350 return Some(decoded);
352 }
353 };
354
355 match next {
356 LZW_CLEAR_TABLE => {
357 table.clear();
358 prev = None;
359 bit_size = table.code_length();
360 }
361 LZW_EOD => return Some(decoded),
362 new => {
363 if new > table.size() {
364 if decoded.is_empty() {
366 return None;
367 }
368 return Some(decoded);
369 }
370
371 if new < table.size() {
372 let entry = table.get(new)?;
373 let first_byte = entry[0];
374 decoded.extend_from_slice(entry);
375
376 if let Some(prev_code) = prev {
377 table.register(prev_code, first_byte);
378 }
379 } else if new == table.size() && prev.is_some() {
380 let prev_code = prev.unwrap();
382 let prev_entry = table.get(prev_code)?;
383 let first_byte = prev_entry[0];
384
385 let new_entry = table.register(prev_code, first_byte)?;
386 decoded.extend_from_slice(new_entry);
387 } else {
388 if decoded.is_empty() {
389 return None;
390 }
391 return Some(decoded);
392 }
393
394 bit_size = table.code_length();
395 prev = Some(new);
396 }
397 }
398 }
399}
400
401struct LzwTable {
403 early_change: bool,
404 entries: Vec<Option<Vec<u8>>>,
405}
406
407impl LzwTable {
408 fn new(early_change: bool) -> Self {
409 let mut entries: Vec<_> = (0..=255u8).map(|b| Some(vec![b])).collect();
410 entries.push(None); entries.push(None); Self {
413 early_change,
414 entries,
415 }
416 }
417
418 fn push(&mut self, entry: Vec<u8>) -> Option<&[u8]> {
419 if self.entries.len() >= LZW_MAX_ENTRIES {
420 None
421 } else {
422 self.entries.push(Some(entry));
423 self.entries.last()?.as_deref()
424 }
425 }
426
427 fn register(&mut self, prev: usize, new_byte: u8) -> Option<&[u8]> {
428 let prev_entry = self.get(prev)?;
429 let mut new_entry = Vec::with_capacity(prev_entry.len() + 1);
430 new_entry.extend(prev_entry);
431 new_entry.push(new_byte);
432 self.push(new_entry)
433 }
434
435 fn get(&self, index: usize) -> Option<&[u8]> {
436 self.entries.get(index)?.as_deref()
437 }
438
439 fn clear(&mut self) {
440 self.entries.truncate(LZW_INITIAL_SIZE);
441 }
442
443 fn size(&self) -> usize {
444 self.entries.len()
445 }
446
447 fn code_length(&self) -> u8 {
448 let adjusted = self.entries.len() + if self.early_change { 1 } else { 0 };
449 if adjusted >= 2048 {
450 12
451 } else if adjusted >= 1024 {
452 11
453 } else if adjusted >= 512 {
454 10
455 } else {
456 9
457 }
458 }
459}
460
461struct LzwBitReader<'a> {
463 data: &'a [u8],
464 bit_pos: usize,
465}
466
467impl<'a> LzwBitReader<'a> {
468 fn new(data: &'a [u8]) -> Self {
469 Self { data, bit_pos: 0 }
470 }
471
472 fn read(&mut self, bit_size: u8) -> Option<u32> {
473 let byte_pos = self.bit_pos / 8;
474 if byte_pos >= self.data.len() {
475 return None;
476 }
477 let bit_offset = self.bit_pos % 8;
478 let end_byte = (self.bit_pos + bit_size as usize - 1) / 8;
479
480 let mut buf = [0u8; 8];
482 for (i, b) in buf.iter_mut().enumerate().take(end_byte - byte_pos + 1) {
483 *b = *self.data.get(byte_pos + i)?;
484 }
485 let bits = u64::from_be_bytes(buf);
486 let shift = 64 - bit_offset - bit_size as usize;
487 let mask = (1u64 << bit_size) - 1;
488 let value = ((bits >> shift) & mask) as u32;
489
490 self.bit_pos += bit_size as usize;
491 Some(value)
492 }
493}
494
495fn decode_ascii_hex(data: &[u8]) -> Result<Vec<u8>, PdfError> {
497 let mut result = Vec::with_capacity(data.len() / 2);
498 let mut high: Option<u8> = None;
499
500 for &b in data {
501 if b == b'>' {
502 break;
503 }
504 if b.is_ascii_whitespace() {
505 continue;
506 }
507 let nibble = hex_digit(b)
508 .ok_or_else(|| PdfError::DecompressionError(format!("invalid hex digit: 0x{b:02x}")))?;
509 match high {
510 None => high = Some(nibble),
511 Some(h) => {
512 result.push(h << 4 | nibble);
513 high = None;
514 }
515 }
516 }
517 if let Some(h) = high {
518 result.push(h << 4);
519 }
520
521 Ok(result)
522}
523
524fn decode_ascii85(data: &[u8]) -> Result<Vec<u8>, PdfError> {
526 let mut result = Vec::with_capacity(data.len() * 4 / 5);
527 let mut tuple: u64 = 0;
528 let mut count = 0u8;
529
530 for &b in data {
531 if b == b'~' {
532 break; }
534 if b.is_ascii_whitespace() {
535 continue;
536 }
537 if b == b'z' && count == 0 {
538 result.extend_from_slice(&[0, 0, 0, 0]);
539 continue;
540 }
541 if !(b'!'..=b'u').contains(&b) {
542 continue; }
544 tuple = tuple * 85 + (b - b'!') as u64;
545 count += 1;
546 if count == 5 {
547 result.push((tuple >> 24) as u8);
548 result.push((tuple >> 16) as u8);
549 result.push((tuple >> 8) as u8);
550 result.push(tuple as u8);
551 tuple = 0;
552 count = 0;
553 }
554 }
555
556 if count > 0 {
558 for _ in count..5 {
559 tuple = tuple * 85 + 84; }
561 for i in 0..(count - 1) {
562 result.push((tuple >> (24 - i * 8)) as u8);
563 }
564 }
565
566 Ok(result)
567}
568
569fn decode_run_length(data: &[u8]) -> Result<Vec<u8>, PdfError> {
571 let mut result = Vec::new();
572 let mut i = 0;
573
574 while i < data.len() {
575 let length_byte = data[i];
576 i += 1;
577 if length_byte < 128 {
578 let count = length_byte as usize + 1;
580 if i + count > data.len() {
581 break;
582 }
583 result.extend_from_slice(&data[i..i + count]);
584 i += count;
585 } else if length_byte > 128 {
586 if i >= data.len() {
588 break;
589 }
590 let count = 257 - length_byte as usize;
591 let val = data[i];
592 i += 1;
593 for _ in 0..count {
594 result.push(val);
595 }
596 } else {
597 break;
599 }
600 }
601
602 Ok(result)
603}
604
605fn decode_dct(data: &[u8]) -> Result<Vec<u8>, PdfError> {
612 use jpeg_decoder::Decoder;
613
614 #[cfg(target_arch = "wasm32")]
625 if let Some(pixels) = decode_dct_via_zune(data) {
626 return Ok(pixels);
627 }
628
629 let mut decoder = Decoder::new(data);
630
631 if has_adobe_rgb_marker(data) || is_raw_rgb_jpeg(data) {
637 decoder.set_color_transform(jpeg_decoder::ColorTransform::RGB);
638 } else if needs_ycck_override(data) {
639 decoder.set_color_transform(jpeg_decoder::ColorTransform::YCCK);
640 }
641
642 let pixels = match decoder.decode() {
643 Ok(p) => p,
644 Err(e) => {
645 if let Some(pixels) = decode_dct_zune(data) {
649 return Ok(pixels);
650 }
651 if let Some(patched) = patch_jpeg_dnl_height(data) {
655 return decode_dct(&patched);
656 }
657 if let Some(pixels) = decode_dct_tolerant(data) {
659 return Ok(pixels);
660 }
661 {
664 let mut padded = data.to_vec();
665 if padded.last() == Some(&0xFF) {
667 padded.pop();
668 }
669 padded.extend_from_slice(&[0xFF, 0xD9]);
670 let mut retry_dec = Decoder::new(&padded[..]);
671 if has_adobe_rgb_marker(&padded) || is_raw_rgb_jpeg(&padded) {
672 retry_dec.set_color_transform(jpeg_decoder::ColorTransform::RGB);
673 } else if needs_ycck_override(&padded) {
674 retry_dec.set_color_transform(jpeg_decoder::ColorTransform::YCCK);
675 }
676 if let Ok(pixels) = retry_dec.decode() {
677 if let Some(info) = retry_dec.info()
679 && info.pixel_format == jpeg_decoder::PixelFormat::CMYK32
680 {
681 let mut result = pixels;
682 for b in result.iter_mut() {
683 *b = 255 - *b;
684 }
685 return Ok(result);
686 }
687 return Ok(pixels);
688 }
689 }
690 return Err(PdfError::DecompressionError(format!("DCTDecode: {e}")));
691 }
692 };
693
694 if let Some(info) = decoder.info()
700 && info.pixel_format == jpeg_decoder::PixelFormat::CMYK32
701 {
702 let mut result = pixels;
703 for b in result.iter_mut() {
704 *b = 255 - *b;
705 }
706 return Ok(result);
707 }
708
709 Ok(pixels)
710}
711
712fn catch_silent<F, T>(f: F) -> Option<T>
716where
717 F: FnOnce() -> Option<T> + std::panic::UnwindSafe,
718{
719 let prev = std::panic::take_hook();
720 std::panic::set_hook(Box::new(|_| {}));
721 let result = std::panic::catch_unwind(f).ok().flatten();
722 std::panic::set_hook(prev);
723 result
724}
725
726#[cfg(target_arch = "wasm32")]
733fn decode_dct_via_zune(data: &[u8]) -> Option<Vec<u8>> {
734 use zune_jpeg::JpegDecoder;
735 let n_comps = jpeg_dimensions_and_components(data)
736 .map(|(_, _, n)| n)
737 .unwrap_or(3);
738 let out_cs = match n_comps {
739 1 => zune_core::colorspace::ColorSpace::Luma,
740 4 => zune_core::colorspace::ColorSpace::CMYK,
741 _ => zune_core::colorspace::ColorSpace::RGB,
742 };
743 let options = zune_core::options::DecoderOptions::default().jpeg_set_out_colorspace(out_cs);
744 let mut decoder = JpegDecoder::new_with_options(std::io::Cursor::new(data), options);
745 decoder.decode().ok()
746}
747
748fn decode_dct_zune(data: &[u8]) -> Option<Vec<u8>> {
751 use zune_jpeg::JpegDecoder;
752 let data = data.to_vec();
756 catch_silent(move || {
757 let options = zune_core::options::DecoderOptions::default()
758 .jpeg_set_out_colorspace(zune_core::colorspace::ColorSpace::LumaA);
759 let mut decoder = JpegDecoder::new_with_options(std::io::Cursor::new(&data), options);
760 decoder.decode().ok()
761 })
762}
763
764fn decode_dct_tolerant(data: &[u8]) -> Option<Vec<u8>> {
768 let n_comps = jpeg_dimensions_and_components(data)
771 .map(|(_, _, n)| n)
772 .unwrap_or(3);
773 let data = data.to_vec();
774 catch_silent(move || {
775 use zune_jpeg::JpegDecoder;
776 let out_cs = match n_comps {
777 1 => zune_core::colorspace::ColorSpace::Luma,
778 4 => zune_core::colorspace::ColorSpace::CMYK,
779 _ => zune_core::colorspace::ColorSpace::RGB,
780 };
781 let options = zune_core::options::DecoderOptions::default()
782 .set_strict_mode(false)
783 .jpeg_set_out_colorspace(out_cs);
784 let mut decoder = JpegDecoder::new_with_options(std::io::Cursor::new(&data), options);
785 decoder.decode().ok()
786 })
787}
788
789fn patch_jpeg_dnl_height(data: &[u8]) -> Option<Vec<u8>> {
794 let dnl_height = {
796 let mut pos = 0;
797 let mut found = None;
798 while pos + 4 < data.len() {
799 if data[pos] == 0xFF && data[pos + 1] == 0xDC {
800 if pos + 5 < data.len() {
802 let h = ((data[pos + 4] as u16) << 8) | data[pos + 5] as u16;
803 found = Some((pos, h));
804 }
805 break;
806 }
807 pos += 1;
808 }
809 found
810 };
811 let (dnl_pos, height) = dnl_height?;
812 if height == 0 {
813 return None;
814 }
815
816 let mut patched = data.to_vec();
818 let mut pos = 2; while pos + 8 < patched.len() {
820 if patched[pos] != 0xFF {
821 pos += 1;
822 continue;
823 }
824 let marker = patched[pos + 1];
825 if (0xC0..=0xC3).contains(&marker) {
826 patched[pos + 5] = (height >> 8) as u8;
829 patched[pos + 6] = (height & 0xFF) as u8;
830 break;
831 }
832 if marker == 0xDA {
833 break; }
835 if pos + 3 < patched.len() {
837 let seg_len = ((patched[pos + 2] as usize) << 8) | patched[pos + 3] as usize;
838 pos += 2 + seg_len;
839 } else {
840 break;
841 }
842 }
843
844 if dnl_pos + 6 <= patched.len() {
846 patched.drain(dnl_pos..dnl_pos + 6);
847 }
848
849 Some(patched)
850}
851
852pub fn patch_jpeg_sof_height(data: &mut [u8], new_height: u16) {
859 if data.len() < 2 || data[0] != 0xFF || data[1] != 0xD8 {
860 return;
861 }
862 let mut pos = 2;
863 while pos + 4 < data.len() {
864 if data[pos] != 0xFF {
865 pos += 1;
866 continue;
867 }
868 let marker = data[pos + 1];
869 if (0xC0..=0xCF).contains(&marker) && marker != 0xC4 && marker != 0xC8 && marker != 0xCC {
870 if pos + 6 < data.len() {
871 data[pos + 5] = (new_height >> 8) as u8;
872 data[pos + 6] = (new_height & 0xFF) as u8;
873 }
874 return;
875 }
876 if marker == 0xDA {
877 return; }
879 let seg_len = if pos + 3 < data.len() {
880 ((data[pos + 2] as usize) << 8) | data[pos + 3] as usize
881 } else {
882 return;
883 };
884 pos += 2 + seg_len;
885 }
886}
887
888pub(crate) fn jpeg_dimensions_and_components(data: &[u8]) -> Option<(u32, u32, u8)> {
890 if data.len() < 2 || data[0] != 0xFF || data[1] != 0xD8 {
891 return None;
892 }
893 let mut pos = 2;
894 while pos + 4 < data.len() {
895 if data[pos] != 0xFF {
896 pos += 1;
897 continue;
898 }
899 let marker = data[pos + 1];
900 if (0xC0..=0xCF).contains(&marker) && marker != 0xC4 && marker != 0xC8 && marker != 0xCC {
901 if pos + 9 < data.len() {
902 let h = ((data[pos + 5] as u32) << 8) | data[pos + 6] as u32;
903 let w = ((data[pos + 7] as u32) << 8) | data[pos + 8] as u32;
904 let n = data[pos + 9];
905 return Some((w, h, n));
906 }
907 }
908 if marker == 0xDA {
909 break;
910 }
911 let seg_len = ((data[pos + 2] as usize) << 8) | data[pos + 3] as usize;
912 pos += 2 + seg_len;
913 }
914 None
915}
916
917pub fn jpeg_dimensions(data: &[u8]) -> Option<(u32, u32)> {
921 if data.len() < 2 || data[0] != 0xFF || data[1] != 0xD8 {
922 return None;
923 }
924 let mut pos = 2;
925 while pos + 4 < data.len() {
926 if data[pos] != 0xFF {
927 pos += 1;
928 continue;
929 }
930 let marker = data[pos + 1];
931 if (0xC0..=0xCF).contains(&marker) && marker != 0xC4 && marker != 0xC8 && marker != 0xCC {
933 if pos + 9 < data.len() {
934 let mut h = ((data[pos + 5] as u32) << 8) | data[pos + 6] as u32;
935 let w = ((data[pos + 7] as u32) << 8) | data[pos + 8] as u32;
936 if h == 0 || h == 0xFFFF {
938 if let Some(dnl_h) = find_dnl_height(data) {
939 h = dnl_h as u32;
940 }
941 }
942 return Some((w, h));
943 }
944 }
945 if marker == 0xDA {
946 break; }
948 let seg_len = ((data[pos + 2] as usize) << 8) | data[pos + 3] as usize;
949 pos += 2 + seg_len;
950 }
951 None
952}
953
954fn find_dnl_height(data: &[u8]) -> Option<u16> {
956 let mut pos = 0;
957 while pos + 5 < data.len() {
958 if data[pos] == 0xFF && data[pos + 1] == 0xDC && pos + 5 < data.len() {
959 return Some(((data[pos + 4] as u16) << 8) | data[pos + 5] as u16);
960 }
961 pos += 1;
962 }
963 None
964}
965
966fn has_adobe_rgb_marker(data: &[u8]) -> bool {
971 let mut has_ct0 = false;
972 let mut uniform_sampling = false;
973 let mut i = 2; while i + 4 < data.len() {
975 if data[i] != 0xFF {
976 break;
977 }
978 let marker = data[i + 1];
979 if marker == 0xDA {
980 break; }
982 let len = u16::from_be_bytes([data[i + 2], data[i + 3]]) as usize;
983 if i + 2 + len > data.len() {
984 break;
985 }
986 if marker == 0xEE && len >= 14 {
989 let color_transform = data[i + 2 + 13];
990 has_ct0 = color_transform == 0;
991 }
992 if (marker == 0xC0 || marker == 0xC2) && i + 9 < data.len() {
994 let ncomp = data[i + 9] as usize;
995 if ncomp == 3 && i + 10 + ncomp * 3 <= data.len() {
996 let s0 = data[i + 11]; let s1 = data[i + 14]; let s2 = data[i + 17]; uniform_sampling = s0 == s1 && s1 == s2;
1000 }
1001 }
1002 i += 2 + len;
1003 }
1004 has_ct0 && uniform_sampling
1005}
1006
1007fn is_raw_rgb_jpeg(data: &[u8]) -> bool {
1012 let mut has_jfif = false;
1013 let mut has_adobe = false;
1014 let mut non_standard_ids = false;
1015 let mut uniform_sampling = false;
1016 let mut n_components = 0u8;
1017 let mut i = 2; while i + 4 < data.len() {
1019 if data[i] != 0xFF {
1020 break;
1021 }
1022 let marker = data[i + 1];
1023 if marker == 0xDA {
1024 break;
1025 }
1026 let len = u16::from_be_bytes([data[i + 2], data[i + 3]]) as usize;
1027 if i + 2 + len > data.len() {
1028 break;
1029 }
1030 if marker == 0xE0 && len >= 7 && &data[i + 4..i + 9] == b"JFIF\x00" {
1031 has_jfif = true;
1032 }
1033 if marker == 0xEE && len >= 7 && &data[i + 4..i + 9] == b"Adobe" {
1034 has_adobe = true;
1035 }
1036 if (marker == 0xC0 || marker == 0xC2) && i + 9 < data.len() {
1037 n_components = data[i + 9];
1038 if n_components == 3 && i + 10 + 9 <= data.len() {
1039 let id0 = data[i + 10];
1040 let id1 = data[i + 13];
1041 let id2 = data[i + 16];
1042 non_standard_ids = !(id0 == 1 && id1 == 2 && id2 == 3);
1044 let s0 = data[i + 11];
1045 let s1 = data[i + 14];
1046 let s2 = data[i + 17];
1047 uniform_sampling = s0 == s1 && s1 == s2;
1048 }
1049 }
1050 i += 2 + len;
1051 }
1052 n_components == 3 && non_standard_ids && uniform_sampling && !has_jfif && !has_adobe
1054}
1055
1056fn needs_ycck_override(data: &[u8]) -> bool {
1063 let mut last_ct = None;
1064 let mut decoder_would_miss = false;
1065 let mut n_components = 0u8;
1066 let mut i = 2; while i + 4 < data.len() {
1068 if data[i] != 0xFF {
1069 break;
1070 }
1071 let marker = data[i + 1];
1072 if marker == 0xDA {
1073 break; }
1075 let len = u16::from_be_bytes([data[i + 2], data[i + 3]]) as usize;
1076 if i + 2 + len > data.len() {
1077 break;
1078 }
1079 if marker == 0xEE && len >= 14 && &data[i + 4..i + 9] == b"Adobe" {
1081 let ct = data[i + 2 + 13];
1082 last_ct = Some(ct);
1083 decoder_would_miss = data[i + 9] != 0;
1086 }
1087 if (marker == 0xC0 || marker == 0xC2) && i + 9 < data.len() {
1089 n_components = data[i + 9];
1090 }
1091 i += 2 + len;
1092 }
1093 last_ct == Some(2) && decoder_would_miss && n_components == 4
1096}
1097
1098fn decode_ccittfax(data: &[u8], parms: Option<&PdfDict>) -> Result<Vec<u8>, PdfError> {
1100 use crate::objects::PdfObj;
1101
1102 let k = parms.and_then(|p| p.get_int(b"K")).unwrap_or(0) as i32;
1103 let columns = parms.and_then(|p| p.get_int(b"Columns")).unwrap_or(1728) as u16;
1104 let rows_limit = parms.and_then(|p| p.get_int(b"Rows")).unwrap_or(0) as u32;
1105 let end_of_block = parms
1106 .and_then(|p| match p.get(b"EndOfBlock") {
1107 Some(PdfObj::Bool(b)) => Some(*b),
1108 _ => None,
1109 })
1110 .unwrap_or(true);
1111 let black_is1 = parms
1112 .and_then(|p| match p.get(b"BlackIs1") {
1113 Some(PdfObj::Bool(b)) => Some(*b),
1114 _ => None,
1115 })
1116 .unwrap_or(false);
1117
1118 let encoded_byte_align = parms
1119 .and_then(|p| match p.get(b"EncodedByteAlign") {
1120 Some(PdfObj::Bool(b)) => Some(*b),
1121 _ => None,
1122 })
1123 .unwrap_or(false);
1124
1125 let encoding = if k < 0 {
1126 hayro_ccitt::EncodingMode::Group4
1127 } else if k == 0 {
1128 hayro_ccitt::EncodingMode::Group3_1D
1129 } else {
1130 hayro_ccitt::EncodingMode::Group3_2D { k: k as u32 }
1131 };
1132
1133 let settings = hayro_ccitt::DecodeSettings {
1134 columns: columns as u32,
1135 rows: if rows_limit > 0 { rows_limit } else { u32::MAX },
1136 end_of_block,
1137 end_of_line: false,
1138 rows_are_byte_aligned: encoded_byte_align,
1139 encoding,
1140 invert_black: false,
1141 };
1142
1143 decode_ccitt_hayro(data, &settings, black_is1)
1144}
1145
1146struct CcittByteDecoder {
1149 output: Vec<u8>,
1150 current_byte: u8,
1151 bit_pos: u8,
1152 black_is1: bool,
1153}
1154
1155impl CcittByteDecoder {
1156 fn new(black_is1: bool) -> Self {
1157 Self {
1158 output: Vec::new(),
1159 current_byte: 0,
1160 bit_pos: 0,
1161 black_is1,
1162 }
1163 }
1164
1165 fn flush_byte(&mut self) {
1166 if self.bit_pos > 0 {
1167 let remaining = 8 - self.bit_pos;
1169 self.current_byte <<= remaining;
1170 if !self.black_is1 {
1171 self.current_byte |= (1u8 << remaining) - 1;
1173 }
1174 self.output.push(self.current_byte);
1175 self.current_byte = 0;
1176 self.bit_pos = 0;
1177 }
1178 }
1179}
1180
1181impl hayro_ccitt::Decoder for CcittByteDecoder {
1182 fn push_pixel(&mut self, white: bool) {
1183 let bit = if self.black_is1 { !white } else { white };
1186 self.current_byte = (self.current_byte << 1) | (bit as u8);
1187 self.bit_pos += 1;
1188 if self.bit_pos == 8 {
1189 self.output.push(self.current_byte);
1190 self.current_byte = 0;
1191 self.bit_pos = 0;
1192 }
1193 }
1194
1195 fn push_pixel_chunk(&mut self, white: bool, chunk_count: u32) {
1196 if self.bit_pos != 0 {
1199 for _ in 0..chunk_count * 8 {
1200 self.push_pixel(white);
1201 }
1202 return;
1203 }
1204 let byte = if (self.black_is1 && !white) || (!self.black_is1 && white) {
1205 0xFF
1206 } else {
1207 0x00
1208 };
1209 for _ in 0..chunk_count {
1210 self.output.push(byte);
1211 }
1212 }
1213
1214 fn next_line(&mut self) {
1215 self.flush_byte();
1216 }
1217}
1218
1219fn decode_ccitt_hayro(
1226 data: &[u8],
1227 settings: &hayro_ccitt::DecodeSettings,
1228 black_is1: bool,
1229) -> Result<Vec<u8>, PdfError> {
1230 let mut decoder = CcittByteDecoder::new(black_is1);
1231 let hayro_err = hayro_ccitt::decode(data, &mut decoder, settings).err();
1232
1233 if let Some(e) = hayro_err
1236 && e != hayro_ccitt::DecodeError::UnexpectedEof
1237 {
1238 let fallback = decode_ccitt_fax(data, settings, black_is1);
1239 use std::sync::atomic::{AtomicBool, Ordering};
1240 static WARNED: AtomicBool = AtomicBool::new(false);
1241 if fallback.len() > decoder.output.len() {
1242 if !WARNED.swap(true, Ordering::Relaxed) {
1243 eprintln!(
1244 "[CCITT] hayro-ccitt error: {} — fell back to `fax` crate",
1245 e
1246 );
1247 }
1248 return Ok(fallback);
1249 }
1250 if !WARNED.swap(true, Ordering::Relaxed) {
1251 eprintln!("[CCITT] decode warning: {} (using partial data)", e);
1252 }
1253 }
1254 Ok(decoder.output)
1255}
1256
1257fn decode_ccitt_fax(
1260 data: &[u8],
1261 settings: &hayro_ccitt::DecodeSettings,
1262 black_is1: bool,
1263) -> Vec<u8> {
1264 let width = settings.columns as u16;
1265 let row_bytes = settings.columns.div_ceil(8) as usize;
1266 let mut out: Vec<u8> = Vec::new();
1267 let white_byte: u8 = if black_is1 { 0x00 } else { 0xFF };
1271 let black_byte: u8 = !white_byte;
1272
1273 let rows_limit = if settings.rows == u32::MAX || settings.rows == 0 {
1274 None
1275 } else {
1276 Some(settings.rows.min(u16::MAX as u32) as u16)
1277 };
1278
1279 let mut emit_row = |transitions: &[u16]| {
1280 let mut row = vec![white_byte; row_bytes];
1282 let mut color_white = true;
1284 let mut cursor: u16 = 0;
1285 let iter = transitions.iter().copied().chain(std::iter::once(width));
1287 for next in iter {
1288 let end = next.min(width);
1289 if !color_white && end > cursor {
1290 fill_bits(&mut row, cursor as usize, end as usize, black_byte != 0);
1291 }
1292 color_white = !color_white;
1293 cursor = end;
1294 if cursor >= width {
1295 break;
1296 }
1297 }
1298 out.extend_from_slice(&row);
1299 };
1300
1301 match settings.encoding {
1302 hayro_ccitt::EncodingMode::Group4 => {
1303 let _ = fax::decoder::decode_g4(data.iter().copied(), width, rows_limit, &mut emit_row);
1304 }
1305 hayro_ccitt::EncodingMode::Group3_1D | hayro_ccitt::EncodingMode::Group3_2D { .. } => {
1306 let _ = fax::decoder::decode_g3(data.iter().copied(), &mut emit_row);
1307 }
1308 }
1309
1310 if let Some(target_rows) = rows_limit {
1316 let expected = row_bytes * target_rows as usize;
1317 if out.len() < expected {
1318 out.resize(expected, white_byte);
1319 }
1320 }
1321
1322 out
1323}
1324
1325fn fill_bits(row: &mut [u8], start: usize, end: usize, black_is_one: bool) {
1329 if end <= start {
1330 return;
1331 }
1332 for x in start..end {
1333 let byte = x / 8;
1334 let bit = 0x80u8 >> (x % 8);
1335 if black_is_one {
1336 row[byte] |= bit;
1337 } else {
1338 row[byte] &= !bit;
1339 }
1340 }
1341}
1342
1343fn decode_jbig2(data: &[u8], globals: Option<&[u8]>) -> Result<Vec<u8>, PdfError> {
1345 #[cfg(not(target_arch = "wasm32"))]
1353 let image = {
1354 let data_owned = data.to_vec();
1355 let globals_owned = globals.map(|g| g.to_vec());
1356 let (tx, rx) = std::sync::mpsc::channel();
1357 std::thread::spawn(move || {
1358 let result = hayro_jbig2::decode_embedded(&data_owned, globals_owned.as_deref());
1359 let _ = tx.send(result);
1360 });
1361 let timeout_secs = 5 + (data.len() as u64 / (1024 * 1024)) * 5;
1366 rx.recv_timeout(std::time::Duration::from_secs(timeout_secs))
1367 .map_err(|_| PdfError::DecompressionError("JBIG2: decode timed out".into()))?
1368 .map_err(|e| PdfError::DecompressionError(format!("JBIG2: {e}")))?
1369 };
1370
1371 #[cfg(target_arch = "wasm32")]
1372 let image = hayro_jbig2::decode_embedded(data, globals)
1373 .map_err(|e| PdfError::DecompressionError(format!("JBIG2: {e}")))?;
1374
1375 let row_bytes = (image.width as usize).div_ceil(8);
1380 let mut packed = vec![0xFFu8; row_bytes * image.height as usize];
1381 for y in 0..image.height as usize {
1382 for x in 0..image.width as usize {
1383 if image.data[y * image.width as usize + x] {
1384 packed[y * row_bytes + x / 8] &= !(0x80 >> (x % 8));
1385 }
1386 }
1387 }
1388 Ok(packed)
1389}
1390
1391#[cfg(feature = "jpx")]
1395fn decode_jpx(data: &[u8]) -> Result<Vec<u8>, PdfError> {
1396 if data.is_empty() {
1397 return Ok(Vec::new());
1398 }
1399
1400 let image = hayro_jpeg2000::Image::new(data, &hayro_jpeg2000::DecodeSettings::default())
1401 .map_err(|e| PdfError::DecompressionError(format!("JPXDecode: {e}")))?;
1402
1403 image
1404 .decode()
1405 .map_err(|e| PdfError::DecompressionError(format!("JPXDecode: {e}")))
1406}
1407
1408#[cfg(feature = "jpx")]
1420pub fn decode_jpx_no_palette(data: &[u8]) -> Result<(Vec<u8>, u8), PdfError> {
1421 if data.is_empty() {
1422 return Ok((Vec::new(), 8));
1423 }
1424
1425 let settings = hayro_jpeg2000::DecodeSettings {
1426 resolve_palette_indices: false,
1427 ..Default::default()
1428 };
1429 let image = hayro_jpeg2000::Image::new(data, &settings)
1430 .map_err(|e| PdfError::DecompressionError(format!("JPXDecode: {e}")))?;
1431 let bit_depth = image.original_bit_depth();
1432
1433 let pixels = image
1434 .decode()
1435 .map_err(|e| PdfError::DecompressionError(format!("JPXDecode: {e}")))?;
1436 Ok((pixels, bit_depth))
1437}
1438
1439#[cfg(feature = "jpx")]
1443pub fn jpx_color_info(data: &[u8]) -> Option<(u8, bool)> {
1444 let image =
1445 hayro_jpeg2000::Image::new(data, &hayro_jpeg2000::DecodeSettings::default()).ok()?;
1446 Some((image.color_space().num_channels(), image.has_alpha()))
1447}
1448
1449#[cfg(feature = "jpx")]
1452pub fn jpx_dimensions(data: &[u8]) -> Option<(u32, u32)> {
1453 let image =
1454 hayro_jpeg2000::Image::new(data, &hayro_jpeg2000::DecodeSettings::default()).ok()?;
1455 Some((image.width(), image.height()))
1456}
1457
1458pub fn decode_pre_jpx(raw: &[u8], dict: &crate::objects::PdfDict) -> Vec<u8> {
1461 let (filters, parms) = parse_filters(dict, None).unwrap_or_default();
1462 let pre_count = filters
1464 .iter()
1465 .take_while(|f| !matches!(f, Filter::JPXDecode))
1466 .count();
1467 if pre_count == 0 {
1468 return raw.to_vec();
1469 }
1470 let pre_parms: Vec<_> = parms.into_iter().take(pre_count).collect();
1471 decode_stream(raw, &filters[..pre_count], &pre_parms, None).unwrap_or_else(|_| raw.to_vec())
1472}
1473
1474fn apply_predictor(data: &[u8], parms: &PdfDict, predictor: i64) -> Result<Vec<u8>, PdfError> {
1476 let columns = parms.get_int(b"Columns").unwrap_or(1) as usize;
1477 let colors = parms.get_int(b"Colors").unwrap_or(1) as usize;
1478 let bpc = parms.get_int(b"BitsPerComponent").unwrap_or(8) as usize;
1479
1480 let bytes_per_pixel = (colors * bpc).div_ceil(8);
1481 let row_bytes = (columns * colors * bpc).div_ceil(8);
1482
1483 if predictor == 2 {
1484 if bpc < 8 {
1486 apply_tiff_predictor_subbyte(data, columns, colors, bpc, row_bytes)
1488 } else if bpc == 16 {
1489 apply_tiff_predictor_16bit(data, columns, colors, row_bytes)
1491 } else {
1492 apply_tiff_predictor(data, row_bytes, bytes_per_pixel)
1493 }
1494 } else if predictor >= 10 {
1495 apply_png_predictor(data, row_bytes, bytes_per_pixel)
1497 } else {
1498 Ok(data.to_vec())
1499 }
1500}
1501
1502fn apply_tiff_predictor_subbyte(
1505 data: &[u8],
1506 columns: usize,
1507 colors: usize,
1508 bpc: usize,
1509 row_bytes: usize,
1510) -> Result<Vec<u8>, PdfError> {
1511 let samples_per_row = columns * colors;
1512 let mask = (1u8 << bpc) - 1; let mut result = Vec::with_capacity(data.len());
1514
1515 for row in data.chunks(row_bytes) {
1516 let mut out_row = vec![0u8; row.len()];
1517 out_row[..row.len()].copy_from_slice(row);
1519
1520 let mut prev = vec![0u8; colors];
1522 for col in 0..columns {
1523 for c in 0..colors {
1524 let sample_idx = col * colors + c;
1525 if sample_idx >= samples_per_row {
1526 break;
1527 }
1528 let bit_offset = sample_idx * bpc;
1529 let byte_idx = bit_offset / 8;
1530 let bit_pos = 8 - bpc - (bit_offset % 8); if byte_idx >= row.len() {
1532 break;
1533 }
1534 let encoded = (row[byte_idx] >> bit_pos) & mask;
1535 let decoded = (encoded.wrapping_add(prev[c])) & mask;
1536 prev[c] = decoded;
1537 out_row[byte_idx] = (out_row[byte_idx] & !(mask << bit_pos)) | (decoded << bit_pos);
1539 }
1540 }
1541 result.extend_from_slice(&out_row);
1542 }
1543
1544 Ok(result)
1545}
1546
1547fn apply_tiff_predictor_16bit(
1552 data: &[u8],
1553 columns: usize,
1554 colors: usize,
1555 row_bytes: usize,
1556) -> Result<Vec<u8>, PdfError> {
1557 let mut result = Vec::with_capacity(data.len());
1558
1559 for row in data.chunks(row_bytes) {
1560 let mut out_row = vec![0u8; row.len()];
1561 let mut prev = vec![0u16; colors];
1562
1563 for col in 0..columns {
1564 for c in 0..colors {
1565 let byte_idx = (col * colors + c) * 2;
1566 if byte_idx + 1 >= row.len() {
1567 break;
1568 }
1569 let encoded = u16::from_be_bytes([row[byte_idx], row[byte_idx + 1]]);
1570 let decoded = encoded.wrapping_add(prev[c]);
1571 prev[c] = decoded;
1572 let [hi, lo] = decoded.to_be_bytes();
1573 out_row[byte_idx] = hi;
1574 out_row[byte_idx + 1] = lo;
1575 }
1576 }
1577 result.extend_from_slice(&out_row);
1578 }
1579
1580 Ok(result)
1581}
1582
1583fn apply_tiff_predictor(
1585 data: &[u8],
1586 row_bytes: usize,
1587 bytes_per_pixel: usize,
1588) -> Result<Vec<u8>, PdfError> {
1589 let mut result = Vec::with_capacity(data.len());
1590
1591 for row in data.chunks(row_bytes) {
1592 let mut out_row = vec![0u8; row.len()];
1593 for i in 0..row.len() {
1594 let left = if i >= bytes_per_pixel {
1595 out_row[i - bytes_per_pixel]
1596 } else {
1597 0
1598 };
1599 out_row[i] = row[i].wrapping_add(left);
1600 }
1601 result.extend_from_slice(&out_row);
1602 }
1603
1604 Ok(result)
1605}
1606
1607fn apply_png_predictor(
1609 data: &[u8],
1610 row_bytes: usize,
1611 bytes_per_pixel: usize,
1612) -> Result<Vec<u8>, PdfError> {
1613 let stride = row_bytes + 1;
1615
1616 if row_bytes > 0
1620 && !data.is_empty()
1621 && data.len().is_multiple_of(row_bytes)
1622 && !data.len().is_multiple_of(stride)
1623 {
1624 return Ok(data.to_vec());
1625 }
1626
1627 let num_rows = data.len() / stride;
1628 let mut result = Vec::with_capacity(num_rows * row_bytes);
1629 let mut prev_row = vec![0u8; row_bytes];
1630
1631 for row_idx in 0..num_rows {
1632 let row_start = row_idx * stride;
1633 if row_start >= data.len() {
1634 break;
1635 }
1636 let filter_type = data[row_start];
1637 let row_data = &data[row_start + 1..std::cmp::min(row_start + stride, data.len())];
1638 let mut out_row = vec![0u8; row_data.len()];
1639
1640 match filter_type {
1641 0 => {
1642 out_row.copy_from_slice(row_data);
1644 }
1645 1 => {
1646 for i in 0..row_data.len() {
1648 let left = if i >= bytes_per_pixel {
1649 out_row[i - bytes_per_pixel]
1650 } else {
1651 0
1652 };
1653 out_row[i] = row_data[i].wrapping_add(left);
1654 }
1655 }
1656 2 => {
1657 for i in 0..row_data.len() {
1659 let up = if i < prev_row.len() { prev_row[i] } else { 0 };
1660 out_row[i] = row_data[i].wrapping_add(up);
1661 }
1662 }
1663 3 => {
1664 for i in 0..row_data.len() {
1666 let left = if i >= bytes_per_pixel {
1667 out_row[i - bytes_per_pixel] as u16
1668 } else {
1669 0
1670 };
1671 let up = if i < prev_row.len() {
1672 prev_row[i] as u16
1673 } else {
1674 0
1675 };
1676 out_row[i] = row_data[i].wrapping_add(((left + up) / 2) as u8);
1677 }
1678 }
1679 4 => {
1680 for i in 0..row_data.len() {
1682 let left = if i >= bytes_per_pixel {
1683 out_row[i - bytes_per_pixel]
1684 } else {
1685 0
1686 };
1687 let up = if i < prev_row.len() { prev_row[i] } else { 0 };
1688 let up_left = if i >= bytes_per_pixel && i - bytes_per_pixel < prev_row.len() {
1689 prev_row[i - bytes_per_pixel]
1690 } else {
1691 0
1692 };
1693 out_row[i] = row_data[i].wrapping_add(paeth(left, up, up_left));
1694 }
1695 }
1696 _ => {
1697 out_row.copy_from_slice(row_data);
1699 }
1700 }
1701
1702 prev_row[..out_row.len()].copy_from_slice(&out_row);
1703 result.extend_from_slice(&out_row);
1704 }
1705
1706 Ok(result)
1707}
1708
1709fn paeth(a: u8, b: u8, c: u8) -> u8 {
1711 let a = a as i16;
1712 let b = b as i16;
1713 let c = c as i16;
1714 let p = a + b - c;
1715 let pa = (p - a).abs();
1716 let pb = (p - b).abs();
1717 let pc = (p - c).abs();
1718 if pa <= pb && pa <= pc {
1719 a as u8
1720 } else if pb <= pc {
1721 b as u8
1722 } else {
1723 c as u8
1724 }
1725}
1726
1727fn hex_digit(b: u8) -> Option<u8> {
1728 match b {
1729 b'0'..=b'9' => Some(b - b'0'),
1730 b'a'..=b'f' => Some(b - b'a' + 10),
1731 b'A'..=b'F' => Some(b - b'A' + 10),
1732 _ => None,
1733 }
1734}
1735
1736#[cfg(test)]
1737mod tests {
1738 use super::*;
1739
1740 #[test]
1741 fn flate_round_trip() {
1742 use flate2::Compression;
1743 use flate2::write::ZlibEncoder;
1744 use std::io::Write;
1745
1746 let original = b"Hello, PDF world! This is a test of FlateDecode.";
1747 let mut enc = ZlibEncoder::new(Vec::new(), Compression::default());
1748 enc.write_all(original).unwrap();
1749 let compressed = enc.finish().unwrap();
1750
1751 let decoded = decode_flate(&compressed, None).unwrap();
1752 assert_eq!(&decoded, original);
1753 }
1754
1755 #[test]
1756 fn ascii_hex_decode() {
1757 let decoded = decode_ascii_hex(b"48656C6C6F>").unwrap();
1758 assert_eq!(&decoded, b"Hello");
1759 }
1760
1761 #[test]
1762 fn ascii_hex_odd_digits() {
1763 let decoded = decode_ascii_hex(b"ABC>").unwrap();
1764 assert_eq!(decoded, vec![0xAB, 0xC0]);
1765 }
1766
1767 #[test]
1768 fn ascii85_decode() {
1769 let decoded = decode_ascii85(b"FCfN8~>").unwrap();
1773 assert_eq!(&decoded, b"test");
1774 }
1775
1776 #[test]
1777 fn ascii85_z_shortcut() {
1778 let decoded = decode_ascii85(b"z~>").unwrap();
1779 assert_eq!(decoded, vec![0, 0, 0, 0]);
1780 }
1781
1782 #[test]
1783 fn run_length_decode() {
1784 let data = vec![2, b'A', b'B', b'C', 253, b'X', 128];
1786 let decoded = decode_run_length(&data).unwrap();
1787 assert_eq!(&decoded, b"ABCXXXX");
1788 }
1789
1790 #[test]
1791 fn png_predictor_none() {
1792 let data = vec![0, 10, 20, 30];
1794 let result = apply_png_predictor(&data, 3, 1).unwrap();
1795 assert_eq!(result, vec![10, 20, 30]);
1796 }
1797
1798 #[test]
1799 fn png_predictor_sub() {
1800 let data = vec![1, 5, 3, 4];
1803 let result = apply_png_predictor(&data, 3, 1).unwrap();
1804 assert_eq!(result, vec![5, 8, 12]);
1805 }
1806
1807 #[test]
1808 fn png_predictor_up() {
1809 let data = vec![0, 10, 20, 30, 2, 5, 5, 5];
1813 let result = apply_png_predictor(&data, 3, 1).unwrap();
1814 assert_eq!(result, vec![10, 20, 30, 15, 25, 35]);
1815 }
1816
1817 #[test]
1818 fn filter_chain() {
1819 use flate2::Compression;
1820 use flate2::write::ZlibEncoder;
1821 use std::io::Write;
1822
1823 let original = b"filter chain test data";
1824 let mut enc = ZlibEncoder::new(Vec::new(), Compression::default());
1825 enc.write_all(original).unwrap();
1826 let compressed = enc.finish().unwrap();
1827
1828 let mut hex = String::new();
1830 for b in &compressed {
1831 hex.push_str(&format!("{b:02X}"));
1832 }
1833 hex.push('>');
1834
1835 let filters = vec![Filter::ASCIIHexDecode, Filter::FlateDecode];
1836 let parms = vec![None, None];
1837 let decoded = decode_stream(hex.as_bytes(), &filters, &parms, None).unwrap();
1838 assert_eq!(&decoded, original);
1839 }
1840}