use super::{Handle, HandleStore};
use std::cmp::Ordering;
use std::sync::LazyLock;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FilterType {
#[default]
Null = 0,
Range = 1,
Endstream = 2,
Concat = 3,
Arc4 = 4,
Aesd = 5,
Ascii85 = 6,
AsciiHex = 7,
RunLength = 8,
Dct = 9,
Fax = 10,
Flate = 11,
Lzw = 12,
Predict = 13,
Jbig2 = 14,
Brotli = 15,
Sgilog16 = 16,
Sgilog24 = 17,
Sgilog32 = 18,
Thunder = 19,
Libarchive = 20,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct FzRange {
pub offset: i64,
pub length: u64,
}
#[derive(Debug)]
pub struct FilterStream {
pub filter_type: FilterType,
pub data: Vec<u8>,
pub position: usize,
pub params: FilterParams,
pub chain: Vec<Handle>,
pub decoded: Vec<u8>,
pub decoded_complete: bool,
}
#[derive(Debug, Clone, Default)]
pub struct FilterParams {
pub length: u64,
pub offset: i64,
pub ranges: Vec<FzRange>,
pub key: Vec<u8>,
pub color_transform: i32,
pub invert_cmyk: i32,
pub l2factor: i32,
pub k: i32,
pub end_of_line: bool,
pub encoded_byte_align: bool,
pub columns: i32,
pub rows: i32,
pub end_of_block: bool,
pub black_is_1: bool,
pub window_bits: i32,
pub early_change: bool,
pub min_bits: i32,
pub reverse_bits: bool,
pub old_tiff: bool,
pub predictor: i32,
pub colors: i32,
pub bpc: i32,
pub width: i32,
pub globals: Option<Handle>,
pub embedded: bool,
pub max_streams: i32,
pub pad: bool,
}
impl Default for FilterStream {
fn default() -> Self {
Self {
filter_type: FilterType::Null,
data: Vec::new(),
position: 0,
params: FilterParams::default(),
chain: Vec::new(),
decoded: Vec::new(),
decoded_complete: false,
}
}
}
impl FilterStream {
pub fn new(filter_type: FilterType) -> Self {
Self {
filter_type,
..Default::default()
}
}
pub fn decode(&mut self) -> Result<(), &'static str> {
if self.decoded_complete {
return Ok(());
}
match self.filter_type {
FilterType::Null => self.decode_null(),
FilterType::Ascii85 => self.decode_ascii85(),
FilterType::AsciiHex => self.decode_asciihex(),
FilterType::RunLength => self.decode_runlength(),
FilterType::Flate => self.decode_flate(),
FilterType::Lzw => self.decode_lzw(),
FilterType::Arc4 => self.decode_arc4(),
FilterType::Aesd => self.decode_aesd(),
FilterType::Predict => self.decode_predict(),
FilterType::Dct => self.decode_dct(),
FilterType::Fax => self.decode_fax(),
FilterType::Brotli => self.decode_brotli(),
FilterType::Jbig2 => self.decode_jbig2(),
FilterType::Range => self.decode_range(),
FilterType::Endstream => self.decode_endstream(),
FilterType::Concat => self.decode_concat(),
_ => {
self.decoded = std::mem::take(&mut self.data);
Ok(())
}
}?;
self.decoded_complete = true;
Ok(())
}
fn decode_null(&mut self) -> Result<(), &'static str> {
let len = self.params.length as usize;
let offset = self.params.offset.max(0) as usize;
if offset < self.data.len() {
let end = (offset + len).min(self.data.len());
self.decoded = self.data[offset..end].to_vec();
}
Ok(())
}
fn decode_ascii85(&mut self) -> Result<(), &'static str> {
let mut output = Vec::new();
let mut group: u32 = 0;
let mut count = 0;
for &byte in &self.data {
match byte {
b'z' if count == 0 => {
output.extend_from_slice(&[0, 0, 0, 0]);
}
b'~' => {
break;
}
b'!'..=b'u' => {
group = group * 85 + (byte - b'!') as u32;
count += 1;
if count == 5 {
output.push((group >> 24) as u8);
output.push((group >> 16) as u8);
output.push((group >> 8) as u8);
output.push(group as u8);
group = 0;
count = 0;
}
}
b' ' | b'\n' | b'\r' | b'\t' => {
}
_ => {
}
}
}
if count > 0 {
for _ in count..5 {
group = group * 85 + 84; }
for i in 0..(count - 1) {
output.push((group >> (24 - i * 8)) as u8);
}
}
self.decoded = output;
Ok(())
}
fn decode_asciihex(&mut self) -> Result<(), &'static str> {
let mut output = Vec::new();
let mut high_nibble: Option<u8> = None;
for &byte in &self.data {
let nibble = match byte {
b'0'..=b'9' => Some(byte - b'0'),
b'a'..=b'f' => Some(byte - b'a' + 10),
b'A'..=b'F' => Some(byte - b'A' + 10),
b'>' => break, b' ' | b'\n' | b'\r' | b'\t' => continue, _ => None,
};
if let Some(n) = nibble {
if let Some(high) = high_nibble {
output.push((high << 4) | n);
high_nibble = None;
} else {
high_nibble = Some(n);
}
}
}
if let Some(high) = high_nibble {
output.push(high << 4);
}
self.decoded = output;
Ok(())
}
fn decode_runlength(&mut self) -> Result<(), &'static str> {
let mut output = Vec::new();
let mut i = 0;
while i < self.data.len() {
let count = self.data[i];
i += 1;
match count.cmp(&128) {
Ordering::Equal => break,
Ordering::Less => {
let n = (count as usize) + 1;
if i + n <= self.data.len() {
output.extend_from_slice(&self.data[i..i + n]);
i += n;
} else {
break;
}
}
Ordering::Greater => {
if i < self.data.len() {
let byte = self.data[i];
i += 1;
let n = 257 - count as usize;
for _ in 0..n {
output.push(byte);
}
} else {
break;
}
}
}
}
self.decoded = output;
Ok(())
}
fn decode_flate(&mut self) -> Result<(), &'static str> {
use flate2::read::ZlibDecoder;
use std::io::Read;
if self.data.is_empty() {
self.decoded = Vec::new();
return Ok(());
}
if self.params.window_bits < 0 {
use flate2::read::DeflateDecoder;
let mut decoder = DeflateDecoder::new(&self.data[..]);
let mut output = Vec::new();
if decoder.read_to_end(&mut output).is_ok() {
self.decoded = output;
} else {
self.decoded = Vec::new();
}
} else {
let mut decoder = ZlibDecoder::new(&self.data[..]);
let mut output = Vec::new();
if decoder.read_to_end(&mut output).is_ok() {
self.decoded = output;
} else {
use flate2::read::DeflateDecoder;
let mut decoder = DeflateDecoder::new(&self.data[..]);
let mut fallback = Vec::new();
if decoder.read_to_end(&mut fallback).is_ok() {
self.decoded = fallback;
} else {
self.decoded = Vec::new();
}
}
}
Ok(())
}
fn decode_lzw(&mut self) -> Result<(), &'static str> {
let early_change = if self.params.early_change { 1 } else { 0 };
let min_bits = self.params.min_bits.max(9) as usize;
let reverse_bits = self.params.reverse_bits;
let mut output = Vec::new();
let mut dictionary: Vec<Vec<u8>> = (0..256).map(|i| vec![i as u8]).collect();
let clear_code = 256;
let eoi_code = 257;
let mut bits = min_bits;
let mut bit_pos = 0;
let mut prev_code: Option<usize> = None;
loop {
let code = self.read_lzw_code(bit_pos, bits, reverse_bits);
bit_pos += bits;
if code == clear_code {
dictionary.truncate(258);
bits = min_bits;
prev_code = None;
continue;
}
if code == eoi_code {
break;
}
let entry = match code.cmp(&dictionary.len()) {
Ordering::Less => dictionary[code].clone(),
Ordering::Equal => {
if let Some(prev) = prev_code {
let mut e = dictionary[prev].clone();
e.push(e[0]);
e
} else {
break;
}
}
Ordering::Greater => break,
};
output.extend_from_slice(&entry);
if let Some(prev) = prev_code {
let mut new_entry = dictionary[prev].clone();
new_entry.push(entry[0]);
dictionary.push(new_entry);
if dictionary.len() >= (1 << bits) - early_change && bits < 12 {
bits += 1;
}
}
prev_code = Some(code);
if bit_pos / 8 >= self.data.len() {
break;
}
}
self.decoded = output;
Ok(())
}
fn read_lzw_code(&self, bit_pos: usize, bits: usize, reverse: bool) -> usize {
let byte_pos = bit_pos / 8;
let bit_offset = bit_pos % 8;
if byte_pos >= self.data.len() {
return 257; }
let mut code: usize = 0;
if reverse {
for i in 0..bits {
let bp = byte_pos + (bit_offset + i) / 8;
let bo = (bit_offset + i) % 8;
if bp < self.data.len() && (self.data[bp] & (1 << bo)) != 0 {
code |= 1 << i;
}
}
} else {
for i in 0..bits {
let bp = byte_pos + (bit_offset + i) / 8;
let bo = 7 - (bit_offset + i) % 8;
if bp < self.data.len() && (self.data[bp] & (1 << bo)) != 0 {
code |= 1 << (bits - 1 - i);
}
}
}
code
}
fn decode_arc4(&mut self) -> Result<(), &'static str> {
if self.params.key.is_empty() {
self.decoded = std::mem::take(&mut self.data);
return Ok(());
}
let mut s: [u8; 256] = [0; 256];
for i in 0..256 {
s[i] = i as u8;
}
let key = &self.params.key;
let key_len = key.len();
let mut j: usize = 0;
for i in 0..256 {
j = (j + s[i] as usize + key[i % key_len] as usize) % 256;
s.swap(i, j);
}
let mut output = Vec::with_capacity(self.data.len());
let mut i: usize = 0;
j = 0;
for &byte in &self.data {
i = (i + 1) % 256;
j = (j + s[i] as usize) % 256;
s.swap(i, j);
let k = s[(s[i] as usize + s[j] as usize) % 256];
output.push(byte ^ k);
}
self.decoded = output;
Ok(())
}
fn decode_aesd(&mut self) -> Result<(), &'static str> {
use aes::Aes128;
use cbc::cipher::{BlockDecryptMut, KeyIvInit};
if self.params.key.is_empty() {
self.decoded = std::mem::take(&mut self.data);
return Ok(());
}
if self.data.len() < 16 {
self.decoded = std::mem::take(&mut self.data);
return Ok(());
}
let iv = &self.data[..16];
let ciphertext = &self.data[16..];
if ciphertext.is_empty() || ciphertext.len() % 16 != 0 {
self.decoded = std::mem::take(&mut self.data);
return Ok(());
}
let mut key_bytes = [0u8; 16];
let copy_len = self.params.key.len().min(16);
key_bytes[..copy_len].copy_from_slice(&self.params.key[..copy_len]);
let mut iv_bytes = [0u8; 16];
iv_bytes.copy_from_slice(iv);
type Aes128CbcDec = cbc::Decryptor<Aes128>;
let mut buf = ciphertext.to_vec();
match Aes128CbcDec::new(&key_bytes.into(), &iv_bytes.into())
.decrypt_padded_mut::<cbc::cipher::block_padding::Pkcs7>(&mut buf)
{
Ok(plaintext) => {
self.decoded = plaintext.to_vec();
}
Err(_) => {
let mut buf2 = ciphertext.to_vec();
Aes128CbcDec::new(&key_bytes.into(), &iv_bytes.into())
.decrypt_padded_mut::<cbc::cipher::block_padding::NoPadding>(&mut buf2)
.map_err(|_| "AES decryption failed")?;
self.decoded = buf2;
}
}
Ok(())
}
fn decode_predict(&mut self) -> Result<(), &'static str> {
let predictor = self.params.predictor;
if predictor == 1 {
self.decoded = std::mem::take(&mut self.data);
return Ok(());
}
let columns = self.params.columns.max(1) as usize;
let colors = self.params.colors.max(1) as usize;
let bpc = self.params.bpc.max(1) as usize;
let bytes_per_pixel = (colors * bpc + 7) / 8;
let row_bytes = (columns * colors * bpc + 7) / 8;
if predictor == 2 {
self.decode_tiff_predictor(row_bytes, bytes_per_pixel)
} else {
self.decode_png_predictor(row_bytes, bytes_per_pixel)
}
}
fn decode_tiff_predictor(
&mut self,
row_bytes: usize,
bytes_per_pixel: usize,
) -> Result<(), &'static str> {
let mut output = Vec::with_capacity(self.data.len());
for row in self.data.chunks(row_bytes) {
let mut prev = vec![0u8; bytes_per_pixel];
for pixel in row.chunks(bytes_per_pixel) {
for (i, &byte) in pixel.iter().enumerate() {
let decoded = byte.wrapping_add(prev.get(i).copied().unwrap_or(0));
output.push(decoded);
if i < prev.len() {
prev[i] = decoded;
}
}
}
}
self.decoded = output;
Ok(())
}
fn decode_png_predictor(
&mut self,
row_bytes: usize,
bytes_per_pixel: usize,
) -> Result<(), &'static str> {
let stride = row_bytes + 1;
let num_rows = (self.data.len() + stride - 1) / stride;
let mut output = Vec::with_capacity(num_rows * row_bytes);
let mut prev_row = vec![0u8; row_bytes];
let mut current_row = vec![0u8; row_bytes];
for row in self.data.chunks(stride) {
if row.is_empty() {
continue;
}
let filter = row[0];
let row_data = if row.len() > 1 { &row[1..] } else { &[] };
current_row.clear();
current_row.reserve(row_bytes);
for (i, &byte) in row_data.iter().enumerate() {
let a = if i >= bytes_per_pixel {
current_row[i - bytes_per_pixel]
} else {
0
};
let b = if i < prev_row.len() { prev_row[i] } else { 0 };
let c = if i >= bytes_per_pixel && i - bytes_per_pixel < prev_row.len() {
prev_row[i - bytes_per_pixel]
} else {
0
};
let decoded = match filter {
0 => byte, 1 => byte.wrapping_add(a), 2 => byte.wrapping_add(b), 3 => byte.wrapping_add(((a as u16 + b as u16) / 2) as u8), 4 => byte.wrapping_add(paeth_predictor(a, b, c)), _ => byte,
};
current_row.push(decoded);
}
output.extend_from_slice(¤t_row);
std::mem::swap(&mut prev_row, &mut current_row);
}
self.decoded = output;
Ok(())
}
fn decode_dct(&mut self) -> Result<(), &'static str> {
use crate::pdf::filter::{DCTDecodeParams, decode_dct};
let params = DCTDecodeParams {
color_transform: self.params.color_transform,
};
let params_opt = if self.params.color_transform >= 0 {
Some(¶ms)
} else {
None
};
match decode_dct(&self.data, params_opt) {
Ok(decoded) => {
self.decoded = decoded;
Ok(())
}
Err(_) => {
self.decoded = std::mem::take(&mut self.data);
Ok(())
}
}
}
fn decode_fax(&mut self) -> Result<(), &'static str> {
use crate::pdf::filter::{CCITTFaxDecodeParams, decode_ccitt_fax};
let params = CCITTFaxDecodeParams {
k: self.params.k,
end_of_line: self.params.end_of_line,
encoded_byte_align: self.params.encoded_byte_align,
columns: self.params.columns,
rows: self.params.rows,
end_of_block: self.params.end_of_block,
black_is_1: self.params.black_is_1,
..Default::default()
};
match decode_ccitt_fax(&self.data, ¶ms) {
Ok(decoded) => {
self.decoded = decoded;
Ok(())
}
Err(_) => {
self.decoded = std::mem::take(&mut self.data);
Ok(())
}
}
}
fn decode_brotli(&mut self) -> Result<(), &'static str> {
use std::io::Read;
let mut decoder = brotli::Decompressor::new(&self.data[..], 4096);
let mut output = Vec::new();
if decoder.read_to_end(&mut output).is_ok() {
self.decoded = output;
} else {
self.decoded = std::mem::take(&mut self.data);
}
Ok(())
}
fn decode_jbig2(&mut self) -> Result<(), &'static str> {
use crate::pdf::filter::{JBIG2DecodeParams, decode_jbig2};
let globals_data = if let Some(globals_handle) = self.params.globals {
if let Some(buf_arc) = super::BUFFERS.get(globals_handle) {
if let Ok(guard) = buf_arc.lock() {
Some(guard.data().to_vec())
} else {
None
}
} else {
None
}
} else {
None
};
let params = JBIG2DecodeParams {
jbig2_globals: globals_data,
};
match decode_jbig2(&self.data, Some(¶ms)) {
Ok(decoded) => {
self.decoded = decoded;
Ok(())
}
Err(_) => {
self.decoded = std::mem::take(&mut self.data);
Ok(())
}
}
}
fn decode_range(&mut self) -> Result<(), &'static str> {
if self.params.ranges.is_empty() {
let offset = self.params.offset.max(0) as usize;
let length = self.params.length as usize;
if offset < self.data.len() {
let end = if length > 0 {
(offset + length).min(self.data.len())
} else {
self.data.len()
};
self.decoded = self.data[offset..end].to_vec();
}
} else {
let mut output = Vec::new();
for range in &self.params.ranges {
let offset = range.offset.max(0) as usize;
let length = range.length as usize;
if offset < self.data.len() {
let end = (offset + length).min(self.data.len());
output.extend_from_slice(&self.data[offset..end]);
}
}
self.decoded = output;
}
Ok(())
}
fn decode_endstream(&mut self) -> Result<(), &'static str> {
if let Some(pos) = self.data.windows(9).position(|w| w == b"endstream") {
self.decoded = self.data[..pos].to_vec();
} else {
self.decoded = std::mem::take(&mut self.data);
}
Ok(())
}
fn decode_concat(&mut self) -> Result<(), &'static str> {
if self.chain.is_empty() {
self.decoded = std::mem::take(&mut self.data);
return Ok(());
}
let mut output = Vec::new();
for &handle in &self.chain {
if let Some(sub_arc) = FILTER_STREAMS.get(handle) {
if let Ok(mut sub_guard) = sub_arc.lock() {
let _ = sub_guard.decode();
output.extend_from_slice(&sub_guard.decoded);
}
}
}
if !self.data.is_empty() {
output.extend_from_slice(&self.data);
}
self.decoded = output;
Ok(())
}
pub fn read(&mut self, buf: &mut [u8]) -> usize {
if !self.decoded_complete {
let _ = self.decode();
}
let available = self.decoded.len().saturating_sub(self.position);
let to_read = buf.len().min(available);
if to_read > 0 {
buf[..to_read].copy_from_slice(&self.decoded[self.position..self.position + to_read]);
self.position += to_read;
}
to_read
}
}
#[inline(always)]
fn paeth_predictor(a: u8, b: u8, c: u8) -> u8 {
let p = a as i32 + b as i32 - c as i32;
let pa = (p - a as i32).abs();
let pb = (p - b as i32).abs();
let pc = (p - c as i32).abs();
if pa <= pb && pa <= pc {
a
} else if pb <= pc {
b
} else {
c
}
}
#[derive(Debug, Clone, Default)]
pub struct Jbig2Globals {
pub refs: i32,
pub data: Vec<u8>,
}
pub static JBIG2_GLOBALS: LazyLock<HandleStore<Jbig2Globals>> = LazyLock::new(HandleStore::new);
pub static FILTER_STREAMS: LazyLock<HandleStore<FilterStream>> = LazyLock::new(HandleStore::new);
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_null_filter(
_ctx: Handle,
chain: Handle,
len: u64,
offset: i64,
) -> Handle {
let mut filter = FilterStream::new(FilterType::Null);
filter.params.length = len;
filter.params.offset = offset;
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_range_filter(
_ctx: Handle,
chain: Handle,
ranges: *const FzRange,
nranges: i32,
) -> Handle {
let mut filter = FilterStream::new(FilterType::Range);
if !ranges.is_null() && nranges > 0 {
let ranges_slice = unsafe { std::slice::from_raw_parts(ranges, nranges as usize) };
filter.params.ranges = ranges_slice.to_vec();
}
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
for range in &filter.params.ranges {
let start = range.offset.max(0) as usize;
let end = (start + range.length as usize).min(source.decoded.len());
if start < source.decoded.len() {
filter.data.extend_from_slice(&source.decoded[start..end]);
}
}
}
}
filter.decoded = std::mem::take(&mut filter.data);
filter.decoded_complete = true;
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_endstream_filter(
_ctx: Handle,
chain: Handle,
len: u64,
offset: i64,
) -> Handle {
fz_open_null_filter(_ctx, chain, len, offset)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_concat(_ctx: Handle, max: i32, pad: i32) -> Handle {
let mut filter = FilterStream::new(FilterType::Concat);
filter.params.max_streams = max;
filter.params.pad = pad != 0;
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_concat_push_drop(_ctx: Handle, concat: Handle, chain: Handle) {
if let Some(arc) = FILTER_STREAMS.get(concat) {
if let Ok(mut filter) = arc.lock() {
filter.chain.push(chain);
if let Some(chain_arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = chain_arc.lock() {
let _ = source.decode();
filter.data.extend_from_slice(&source.decoded);
}
}
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_arc4(_ctx: Handle, chain: Handle, key: *const u8, keylen: u32) -> Handle {
let mut filter = FilterStream::new(FilterType::Arc4);
if !key.is_null() && keylen > 0 {
filter.params.key = unsafe { std::slice::from_raw_parts(key, keylen as usize) }.to_vec();
}
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_aesd(_ctx: Handle, chain: Handle, key: *const u8, keylen: u32) -> Handle {
let mut filter = FilterStream::new(FilterType::Aesd);
if !key.is_null() && keylen > 0 {
filter.params.key = unsafe { std::slice::from_raw_parts(key, keylen as usize) }.to_vec();
}
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_a85d(_ctx: Handle, chain: Handle) -> Handle {
let mut filter = FilterStream::new(FilterType::Ascii85);
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_ahxd(_ctx: Handle, chain: Handle) -> Handle {
let mut filter = FilterStream::new(FilterType::AsciiHex);
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_rld(_ctx: Handle, chain: Handle) -> Handle {
let mut filter = FilterStream::new(FilterType::RunLength);
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_dctd(
_ctx: Handle,
chain: Handle,
color_transform: i32,
invert_cmyk: i32,
l2factor: i32,
_jpegtables: Handle,
) -> Handle {
let mut filter = FilterStream::new(FilterType::Dct);
filter.params.color_transform = color_transform;
filter.params.invert_cmyk = invert_cmyk;
filter.params.l2factor = l2factor;
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_faxd(
_ctx: Handle,
chain: Handle,
k: i32,
end_of_line: i32,
encoded_byte_align: i32,
columns: i32,
rows: i32,
end_of_block: i32,
black_is_1: i32,
) -> Handle {
let mut filter = FilterStream::new(FilterType::Fax);
filter.params.k = k;
filter.params.end_of_line = end_of_line != 0;
filter.params.encoded_byte_align = encoded_byte_align != 0;
filter.params.columns = columns;
filter.params.rows = rows;
filter.params.end_of_block = end_of_block != 0;
filter.params.black_is_1 = black_is_1 != 0;
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_flated(_ctx: Handle, chain: Handle, window_bits: i32) -> Handle {
let mut filter = FilterStream::new(FilterType::Flate);
filter.params.window_bits = window_bits;
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_libarchived(_ctx: Handle, chain: Handle) -> Handle {
let mut filter = FilterStream::new(FilterType::Libarchive);
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_brotlid(_ctx: Handle, chain: Handle) -> Handle {
let mut filter = FilterStream::new(FilterType::Brotli);
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_lzwd(
_ctx: Handle,
chain: Handle,
early_change: i32,
min_bits: i32,
reverse_bits: i32,
old_tiff: i32,
) -> Handle {
let mut filter = FilterStream::new(FilterType::Lzw);
filter.params.early_change = early_change != 0;
filter.params.min_bits = if min_bits > 0 { min_bits } else { 9 };
filter.params.reverse_bits = reverse_bits != 0;
filter.params.old_tiff = old_tiff != 0;
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_predict(
_ctx: Handle,
chain: Handle,
predictor: i32,
columns: i32,
colors: i32,
bpc: i32,
) -> Handle {
let mut filter = FilterStream::new(FilterType::Predict);
filter.params.predictor = predictor;
filter.params.columns = columns;
filter.params.colors = colors;
filter.params.bpc = bpc;
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_jbig2d(
_ctx: Handle,
chain: Handle,
globals: Handle,
embedded: i32,
) -> Handle {
let mut filter = FilterStream::new(FilterType::Jbig2);
filter.params.globals = if globals != 0 { Some(globals) } else { None };
filter.params.embedded = embedded != 0;
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_load_jbig2_globals(_ctx: Handle, buf: Handle) -> Handle {
let mut globals = Jbig2Globals {
refs: 1,
data: Vec::new(),
};
if let Some(arc) = crate::ffi::BUFFERS.get(buf) {
if let Ok(buffer) = arc.lock() {
globals.data = buffer.data().to_vec();
}
}
JBIG2_GLOBALS.insert(globals)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_keep_jbig2_globals(_ctx: Handle, globals: Handle) -> Handle {
if let Some(arc) = JBIG2_GLOBALS.get(globals) {
if let Ok(mut g) = arc.lock() {
g.refs += 1;
}
}
globals
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_drop_jbig2_globals(_ctx: Handle, globals: Handle) {
if globals == 0 {
return;
}
let should_drop = if let Some(arc) = JBIG2_GLOBALS.get(globals) {
if let Ok(mut g) = arc.lock() {
g.refs -= 1;
g.refs <= 0
} else {
false
}
} else {
false
};
if should_drop {
JBIG2_GLOBALS.remove(globals);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_jbig2_globals_data(_ctx: Handle, globals: Handle) -> Handle {
if let Some(arc) = JBIG2_GLOBALS.get(globals) {
if let Ok(g) = arc.lock() {
let buffer = crate::ffi::buffer::Buffer::from_data(&g.data);
return crate::ffi::BUFFERS.insert(buffer);
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_sgilog16(_ctx: Handle, chain: Handle, w: i32) -> Handle {
let mut filter = FilterStream::new(FilterType::Sgilog16);
filter.params.width = w;
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_sgilog24(_ctx: Handle, chain: Handle, w: i32) -> Handle {
let mut filter = FilterStream::new(FilterType::Sgilog24);
filter.params.width = w;
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_sgilog32(_ctx: Handle, chain: Handle, w: i32) -> Handle {
let mut filter = FilterStream::new(FilterType::Sgilog32);
filter.params.width = w;
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_open_thunder(_ctx: Handle, chain: Handle, w: i32) -> Handle {
let mut filter = FilterStream::new(FilterType::Thunder);
filter.params.width = w;
if let Some(arc) = FILTER_STREAMS.get(chain) {
if let Ok(mut source) = arc.lock() {
let _ = source.decode();
filter.data = source.decoded.clone();
}
}
FILTER_STREAMS.insert(filter)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_drop_filter(_ctx: Handle, filter: Handle) {
FILTER_STREAMS.remove(filter);
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_filter_read(_ctx: Handle, filter: Handle, buf: *mut u8, len: usize) -> usize {
if buf.is_null() || len == 0 {
return 0;
}
if let Some(arc) = FILTER_STREAMS.get(filter) {
if let Ok(mut f) = arc.lock() {
let slice = unsafe { std::slice::from_raw_parts_mut(buf, len) };
return f.read(slice);
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_filter_size(_ctx: Handle, filter: Handle) -> usize {
if let Some(arc) = FILTER_STREAMS.get(filter) {
if let Ok(mut f) = arc.lock() {
let _ = f.decode();
return f.decoded.len();
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_filter_data(_ctx: Handle, filter: Handle) -> *const u8 {
if let Some(arc) = FILTER_STREAMS.get(filter) {
if let Ok(mut f) = arc.lock() {
let _ = f.decode();
return f.decoded.as_ptr();
}
}
std::ptr::null()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ascii85_decode() {
let mut filter = FilterStream::new(FilterType::Ascii85);
filter.data = b"FCfN8~>".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"test");
}
#[test]
fn test_asciihex_decode() {
let mut filter = FilterStream::new(FilterType::AsciiHex);
filter.data = b"48656C6C6F>".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"Hello");
}
#[test]
fn test_runlength_decode() {
let mut filter = FilterStream::new(FilterType::RunLength);
filter.data = vec![3, b'H', b'e', b'l', b'l', 254, b'o', 128];
filter.decode().unwrap();
assert_eq!(filter.decoded, b"Hellooo");
}
#[test]
fn test_arc4_decode() {
let mut filter = FilterStream::new(FilterType::Arc4);
filter.params.key = b"key".to_vec();
filter.data = vec![0x1a, 0xd2, 0x82]; filter.decode().unwrap();
assert!(!filter.decoded.is_empty());
}
#[test]
fn test_null_filter() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = b"Hello World!".to_vec();
source.decoded = source.data.clone();
source.decoded_complete = true;
let source_handle = FILTER_STREAMS.insert(source);
let filter = fz_open_null_filter(ctx, source_handle, 5, 0);
let size = fz_filter_size(ctx, filter);
assert_eq!(size, 5);
fz_drop_filter(ctx, filter);
fz_drop_filter(ctx, source_handle);
}
#[test]
fn test_concat_filter() {
let ctx = 0;
let mut source1 = FilterStream::new(FilterType::Null);
source1.decoded = b"Hello ".to_vec();
source1.decoded_complete = true;
let h1 = FILTER_STREAMS.insert(source1);
let mut source2 = FilterStream::new(FilterType::Null);
source2.decoded = b"World!".to_vec();
source2.decoded_complete = true;
let h2 = FILTER_STREAMS.insert(source2);
let concat = fz_open_concat(ctx, 2, 0);
fz_concat_push_drop(ctx, concat, h1);
fz_concat_push_drop(ctx, concat, h2);
if let Some(arc) = FILTER_STREAMS.get(concat) {
if let Ok(f) = arc.lock() {
assert_eq!(f.data, b"Hello World!");
}
}
fz_drop_filter(ctx, concat);
}
#[test]
fn test_flate_decode() {
let mut filter = FilterStream::new(FilterType::Flate);
filter.data = vec![
0x78, 0x9c, 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00, 0x05, 0x8c, 0x01, 0xf5,
];
filter.params.window_bits = 15;
filter.decode().unwrap();
assert_eq!(filter.decoded, b"Hello");
}
#[test]
fn test_predict_none() {
let mut filter = FilterStream::new(FilterType::Predict);
filter.params.predictor = 1; filter.data = b"Hello".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"Hello");
}
#[test]
fn test_jbig2_globals() {
let ctx = 0;
let buffer = crate::ffi::buffer::Buffer::from_data(b"test globals");
let buf_handle = crate::ffi::BUFFERS.insert(buffer);
let globals = fz_load_jbig2_globals(ctx, buf_handle);
assert!(globals > 0);
let kept = fz_keep_jbig2_globals(ctx, globals);
assert_eq!(kept, globals);
fz_drop_jbig2_globals(ctx, globals);
fz_drop_jbig2_globals(ctx, globals);
}
#[test]
fn test_decode_null_with_offset() {
let mut filter = FilterStream::new(FilterType::Null);
filter.data = b"Hello World".to_vec();
filter.params.offset = 6;
filter.params.length = 5;
filter.decode().unwrap();
assert_eq!(filter.decoded, b"World");
}
#[test]
fn test_decode_null_offset_outside_data() {
let mut filter = FilterStream::new(FilterType::Null);
filter.data = b"Hi".to_vec();
filter.params.offset = 10;
filter.params.length = 5;
filter.decode().unwrap();
assert!(filter.decoded.is_empty());
}
#[test]
fn test_decode_ascii85_z_compression() {
let mut filter = FilterStream::new(FilterType::Ascii85);
filter.data = b"zz~>".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded.len(), 8);
assert_eq!(filter.decoded, vec![0, 0, 0, 0, 0, 0, 0, 0]);
}
#[test]
fn test_decode_ascii85_whitespace() {
let mut filter = FilterStream::new(FilterType::Ascii85);
filter.data = b"F C f N 8~>".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"test");
}
#[test]
fn test_decode_ascii85_remaining_bytes() {
let mut filter = FilterStream::new(FilterType::Ascii85);
filter.data = b"FC~>".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded.len(), 1);
}
#[test]
fn test_decode_asciihex_odd_nibble() {
let mut filter = FilterStream::new(FilterType::AsciiHex);
filter.data = b"486>".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, vec![0x48, 0x60]);
}
#[test]
fn test_decode_asciihex_whitespace() {
let mut filter = FilterStream::new(FilterType::AsciiHex);
filter.data = b"48 65 6C 6C 6F>".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"Hello");
}
#[test]
fn test_decode_runlength_copy_overflow() {
let mut filter = FilterStream::new(FilterType::RunLength);
filter.data = vec![5, b'a', b'b', b'c'];
filter.decode().unwrap();
assert!(filter.decoded.is_empty());
}
#[test]
fn test_decode_runlength_repeat() {
let mut filter = FilterStream::new(FilterType::RunLength);
filter.data = vec![255, b'x', 128];
filter.decode().unwrap();
assert_eq!(filter.decoded.len(), 2);
assert_eq!(filter.decoded, vec![b'x', b'x']);
}
#[test]
fn test_decode_flate_empty() {
let mut filter = FilterStream::new(FilterType::Flate);
filter.data = vec![];
filter.decode().unwrap();
assert!(filter.decoded.is_empty());
}
#[test]
fn test_decode_flate_raw_deflate() {
let mut filter = FilterStream::new(FilterType::Flate);
filter.params.window_bits = -15;
filter.data = vec![0x01, 0x05, 0x00, 0xfa, 0xff, b'H', b'e', b'l', b'l', b'o'];
filter.decode().unwrap();
assert_eq!(filter.decoded, b"Hello");
}
#[test]
fn test_decode_arc4_empty_key() {
let mut filter = FilterStream::new(FilterType::Arc4);
filter.params.key = vec![];
filter.data = b"Hello".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"Hello");
}
#[test]
fn test_decode_aesd_empty_key() {
let mut filter = FilterStream::new(FilterType::Aesd);
filter.params.key = vec![];
filter.data = b"0123456789abcdef".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded.len(), 16);
}
#[test]
fn test_decode_aesd_short_data() {
let mut filter = FilterStream::new(FilterType::Aesd);
filter.params.key = b"key1234567890123".to_vec();
filter.data = b"short".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"short");
}
#[test]
fn test_decode_predict_tiff() {
let mut filter = FilterStream::new(FilterType::Predict);
filter.params.predictor = 2;
filter.params.columns = 2;
filter.params.colors = 1;
filter.params.bpc = 8;
filter.data = vec![1, 2, 3, 4];
filter.decode().unwrap();
assert_eq!(filter.decoded.len(), 4);
}
#[test]
fn test_decode_predict_png() {
let mut filter = FilterStream::new(FilterType::Predict);
filter.params.predictor = 10;
filter.params.columns = 2;
filter.params.colors = 1;
filter.params.bpc = 8;
filter.data = vec![0, 0, 0, 0, 0, 0];
filter.decode().unwrap();
assert_eq!(filter.decoded.len(), 4);
}
#[test]
fn test_decode_range_with_ranges() {
let mut filter = FilterStream::new(FilterType::Range);
filter.data = b"Hello World".to_vec();
filter.params.ranges = vec![
FzRange {
offset: 0,
length: 5,
},
FzRange {
offset: 6,
length: 5,
},
];
filter.decode().unwrap();
assert_eq!(filter.decoded, b"HelloWorld");
}
#[test]
fn test_decode_range_empty_ranges_uses_offset_length() {
let mut filter = FilterStream::new(FilterType::Range);
filter.data = b"Hello World".to_vec();
filter.params.offset = 0;
filter.params.length = 5;
filter.decode().unwrap();
assert_eq!(filter.decoded, b"Hello");
}
#[test]
fn test_decode_range_length_zero() {
let mut filter = FilterStream::new(FilterType::Range);
filter.data = b"Hello".to_vec();
filter.params.offset = 0;
filter.params.length = 0;
filter.decode().unwrap();
assert_eq!(filter.decoded, b"Hello");
}
#[test]
fn test_decode_endstream_with_marker() {
let mut filter = FilterStream::new(FilterType::Endstream);
filter.data = b"stream data endstream trailing".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"stream data ");
}
#[test]
fn test_decode_endstream_without_marker() {
let mut filter = FilterStream::new(FilterType::Endstream);
filter.data = b"raw data no marker".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"raw data no marker");
}
#[test]
fn test_decode_concat_empty_chain() {
let mut filter = FilterStream::new(FilterType::Concat);
filter.data = b"own data".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"own data");
}
#[test]
fn test_decode_concat_with_chain() {
let mut sub1 = FilterStream::new(FilterType::Null);
sub1.decoded = b"Part1".to_vec();
sub1.decoded_complete = true;
let h1 = FILTER_STREAMS.insert(sub1);
let mut sub2 = FilterStream::new(FilterType::Null);
sub2.decoded = b"Part2".to_vec();
sub2.decoded_complete = true;
let h2 = FILTER_STREAMS.insert(sub2);
let mut filter = FilterStream::new(FilterType::Concat);
filter.chain = vec![h1, h2];
filter.data = b"Part3".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"Part1Part2Part3");
fz_drop_filter(0, h1);
fz_drop_filter(0, h2);
}
#[test]
fn test_decode_sgilog16() {
let mut filter = FilterStream::new(FilterType::Sgilog16);
filter.data = b"test data".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"test data");
}
#[test]
fn test_decode_brotli_error_fallback() {
let mut filter = FilterStream::new(FilterType::Brotli);
filter.data = b"invalid brotli".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"invalid brotli");
}
#[test]
fn test_decode_already_complete() {
let mut filter = FilterStream::new(FilterType::Ascii85);
filter.data = b"FCfN8~>".to_vec();
filter.decoded_complete = true;
filter.decoded = b"old".to_vec();
filter.decode().unwrap();
assert_eq!(filter.decoded, b"old");
}
#[test]
fn test_filter_read() {
let mut filter = FilterStream::new(FilterType::AsciiHex);
filter.data = b"48656C6C6F>".to_vec();
let mut buf = [0u8; 3];
let n = filter.read(&mut buf);
assert_eq!(n, 3);
assert_eq!(&buf, b"Hel");
let n2 = filter.read(&mut buf);
assert_eq!(n2, 2);
assert_eq!(&buf[..2], b"lo");
}
#[test]
fn test_fz_filter_read_null_buf() {
let mut source = FilterStream::new(FilterType::Null);
source.decoded = b"data".to_vec();
source.decoded_complete = true;
let h = FILTER_STREAMS.insert(source);
let n = fz_filter_read(0, h, std::ptr::null_mut(), 10);
assert_eq!(n, 0);
fz_drop_filter(0, h);
}
#[test]
fn test_fz_filter_read_zero_len() {
let mut source = FilterStream::new(FilterType::Null);
source.decoded = b"data".to_vec();
source.decoded_complete = true;
let h = FILTER_STREAMS.insert(source);
let mut buf = [0u8; 4];
let n = fz_filter_read(0, h, buf.as_mut_ptr(), 0);
assert_eq!(n, 0);
fz_drop_filter(0, h);
}
#[test]
fn test_fz_filter_read_invalid_handle() {
let mut buf = [0u8; 4];
let n = fz_filter_read(0, 0, buf.as_mut_ptr(), 4);
assert_eq!(n, 0);
}
#[test]
fn test_fz_filter_size_invalid_handle() {
assert_eq!(fz_filter_size(0, 0), 0);
}
#[test]
fn test_fz_filter_data_invalid_handle() {
assert!(fz_filter_data(0, 0).is_null());
}
#[test]
fn test_fz_open_range_filter_null_ranges() {
let h = fz_open_range_filter(0, 0, std::ptr::null(), 0);
assert!(h > 0);
let size = fz_filter_size(0, h);
assert_eq!(size, 0);
fz_drop_filter(0, h);
}
#[test]
fn test_fz_open_arc4_null_key() {
let h = fz_open_arc4(0, 0, std::ptr::null(), 0);
assert!(h > 0);
fz_drop_filter(0, h);
}
#[test]
fn test_fz_open_aesd_null_key() {
let h = fz_open_aesd(0, 0, std::ptr::null(), 0);
assert!(h > 0);
fz_drop_filter(0, h);
}
#[test]
fn test_fz_open_a85d_chain_zero() {
let h = fz_open_a85d(0, 0);
assert!(h > 0);
fz_drop_filter(0, h);
}
#[test]
fn test_fz_drop_jbig2_globals_zero() {
fz_drop_jbig2_globals(0, 0);
}
#[test]
fn test_fz_jbig2_globals_data_invalid() {
assert_eq!(fz_jbig2_globals_data(0, 0), 0);
}
#[test]
fn test_fz_open_endstream_filter() {
let mut source = FilterStream::new(FilterType::Null);
source.data = b"Hello".to_vec();
source.decoded = b"Hello".to_vec();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_endstream_filter(0, sh, 5, 0);
assert!(h > 0);
assert_eq!(fz_filter_size(0, h), 5);
fz_drop_filter(0, h);
fz_drop_filter(0, sh);
}
#[test]
fn test_fz_open_range_filter_with_ranges() {
let mut source = FilterStream::new(FilterType::Null);
source.data = b"Hello World".to_vec();
source.decoded = b"Hello World".to_vec();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let ranges = [
FzRange {
offset: 0,
length: 5,
},
FzRange {
offset: 6,
length: 5,
},
];
let h = fz_open_range_filter(0, sh, ranges.as_ptr(), 2);
assert!(h > 0);
assert_eq!(fz_filter_size(0, h), 10);
fz_drop_filter(0, h);
fz_drop_filter(0, sh);
}
#[test]
fn test_fz_concat_push_drop_invalid_concat() {
fz_concat_push_drop(0, 0, 0);
}
#[test]
fn test_fz_open_lzwd_min_bits_zero() {
let h = fz_open_lzwd(0, 0, 0, 0, 0, 0);
assert!(h > 0);
fz_drop_filter(0, h);
}
#[test]
fn test_fz_filter_read_valid() {
let mut source = FilterStream::new(FilterType::AsciiHex);
source.data = b"48656C6C6F>".to_vec();
source.decoded = b"Hello".to_vec();
source.decoded_complete = true;
let h = FILTER_STREAMS.insert(source);
let mut buf = [0u8; 10];
let n = fz_filter_read(0, h, buf.as_mut_ptr(), 10);
assert_eq!(n, 5);
assert_eq!(&buf[..5], b"Hello");
fz_drop_filter(0, h);
}
#[test]
fn test_fz_filter_data_valid() {
let mut source = FilterStream::new(FilterType::Null);
source.decoded = b"data".to_vec();
source.decoded_complete = true;
let h = FILTER_STREAMS.insert(source);
let ptr = fz_filter_data(0, h);
assert!(!ptr.is_null());
unsafe {
assert_eq!(std::slice::from_raw_parts(ptr, 4), b"data");
}
fz_drop_filter(0, h);
}
#[test]
fn test_decode_lzw() {
let mut filter = FilterStream::new(FilterType::Lzw);
filter.params.min_bits = 9;
filter.params.reverse_bits = false;
filter.data = vec![0x80, 0x0c, 0x00, 0x81, 0x00, 0x01, 0x01];
filter.decode().unwrap();
assert!(!filter.decoded.is_empty());
}
#[test]
fn test_decode_lzw_reverse_bits() {
let mut filter = FilterStream::new(FilterType::Lzw);
filter.params.min_bits = 9;
filter.params.reverse_bits = true;
filter.data = vec![0x80, 0x0c, 0x00, 0x81, 0x00, 0x01, 0x01];
filter.decode().unwrap();
assert!(!filter.decoded.is_empty());
}
#[test]
fn test_decode_aesd_invalid_ciphertext_length() {
let mut filter = FilterStream::new(FilterType::Aesd);
filter.params.key = b"key1234567890123".to_vec();
filter.data = vec![0u8; 20];
filter.decode().unwrap();
assert_eq!(filter.decoded.len(), 20);
}
#[test]
fn test_decode_predict_png_paeth() {
let mut filter = FilterStream::new(FilterType::Predict);
filter.params.predictor = 15;
filter.params.columns = 2;
filter.params.colors = 1;
filter.params.bpc = 8;
filter.data = vec![4, 10, 20, 30, 40, 0, 0, 0, 0];
filter.decode().unwrap();
assert!(!filter.decoded.is_empty());
}
#[test]
fn test_decode_ascii85_invalid_char() {
let mut filter = FilterStream::new(FilterType::Ascii85);
filter.data = b"FCfN8\x00~>".to_vec();
filter.decode().unwrap();
assert!(!filter.decoded.is_empty());
}
#[test]
fn test_decode_flate_zlib_fallback() {
let mut filter = FilterStream::new(FilterType::Flate);
filter.params.window_bits = 15;
filter.data = vec![0x01, 0x05, 0x00, 0xfa, 0xff, b'H', b'e', b'l', b'l', b'o'];
filter.decode().unwrap();
assert_eq!(filter.decoded, b"Hello");
}
#[test]
fn test_fz_open_null_filter_with_chain() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = b"Hello World".to_vec();
source.decoded = b"Hello World".to_vec();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_null_filter(ctx, sh, 5, 0);
assert!(h > 0);
assert_eq!(fz_filter_size(ctx, h), 5);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_arc4_with_key() {
let ctx = 0;
let key = b"secret";
let h = fz_open_arc4(ctx, 0, key.as_ptr(), key.len() as u32);
assert!(h > 0);
fz_drop_filter(ctx, h);
}
#[test]
fn test_fz_open_aesd_with_key() {
let ctx = 0;
let key = b"key1234567890123";
let h = fz_open_aesd(ctx, 0, key.as_ptr(), key.len() as u32);
assert!(h > 0);
fz_drop_filter(ctx, h);
}
#[test]
fn test_fz_open_a85d_with_chain() {
let ctx = 0;
let h = fz_open_a85d(ctx, 0);
assert!(h > 0);
fz_drop_filter(ctx, h);
}
#[test]
fn test_fz_open_ahxd_with_chain() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = b"48656C6C6F>".to_vec();
source.decoded = b"Hello".to_vec();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_ahxd(ctx, sh);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_rld_with_chain() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = vec![3, b'H', b'e', b'l', b'l', 128];
source.decoded = b"Hell".to_vec();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_rld(ctx, sh);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_dctd() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = vec![0xFF, 0xD8, 0xFF];
source.decoded = source.data.clone();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_dctd(ctx, sh, 1, 0, 0, 0);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_faxd() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = vec![0u8; 10];
source.decoded = source.data.clone();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_faxd(ctx, sh, 0, 0, 0, 1728, 0, 0, 0);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_flated() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = vec![
0x78, 0x9c, 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00, 0x05, 0x8c, 0x01, 0xf5,
];
source.decoded = source.data.clone();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_flated(ctx, sh, 15);
assert!(h > 0);
assert_eq!(fz_filter_size(ctx, h), 5);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_libarchived() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = b"archive data".to_vec();
source.decoded = source.data.clone();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_libarchived(ctx, sh);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_brotlid() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = b"invalid".to_vec();
source.decoded = source.data.clone();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_brotlid(ctx, sh);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_lzwd_with_chain() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = vec![0x80, 0x0c, 0x00];
source.decoded = source.data.clone();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_lzwd(ctx, sh, 0, 9, 0, 0);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_predict_with_chain() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = b"Hello".to_vec();
source.decoded = source.data.clone();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_predict(ctx, sh, 1, 5, 1, 8);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_jbig2d() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = vec![0u8; 10];
source.decoded = source.data.clone();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_jbig2d(ctx, sh, 0, 0);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_load_jbig2_globals_invalid_buf() {
let h = fz_load_jbig2_globals(0, 0);
assert!(h > 0);
fz_drop_jbig2_globals(0, h);
}
#[test]
fn test_fz_open_sgilog16() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = b"data".to_vec();
source.decoded = source.data.clone();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_sgilog16(ctx, sh, 100);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_sgilog24() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = b"data".to_vec();
source.decoded = source.data.clone();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_sgilog24(ctx, sh, 100);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_sgilog32() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = b"data".to_vec();
source.decoded = source.data.clone();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_sgilog32(ctx, sh, 100);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_open_thunder() {
let ctx = 0;
let mut source = FilterStream::new(FilterType::Null);
source.data = b"data".to_vec();
source.decoded = source.data.clone();
source.decoded_complete = true;
let sh = FILTER_STREAMS.insert(source);
let h = fz_open_thunder(ctx, sh, 100);
assert!(h > 0);
fz_drop_filter(ctx, h);
fz_drop_filter(ctx, sh);
}
#[test]
fn test_fz_drop_filter() {
let mut filter = FilterStream::new(FilterType::Null);
filter.data = b"x".to_vec();
let h = FILTER_STREAMS.insert(filter);
fz_drop_filter(0, h);
}
#[test]
fn test_decode_png_predictor_filter_unknown() {
let mut filter = FilterStream::new(FilterType::Predict);
filter.params.predictor = 11;
filter.params.columns = 2;
filter.params.colors = 1;
filter.params.bpc = 8;
filter.data = vec![99, 0, 0, 0, 0, 0, 0, 0, 0];
filter.decode().unwrap();
assert!(!filter.decoded.is_empty());
}
#[test]
fn test_decode_tiff_predictor_pixel_mismatch() {
let mut filter = FilterStream::new(FilterType::Predict);
filter.params.predictor = 2;
filter.params.columns = 2;
filter.params.colors = 1;
filter.params.bpc = 8;
filter.data = vec![1, 2];
filter.decode().unwrap();
assert_eq!(filter.decoded.len(), 2);
}
#[test]
fn test_decode_png_predictor_empty_row() {
let mut filter = FilterStream::new(FilterType::Predict);
filter.params.predictor = 10;
filter.params.columns = 2;
filter.params.colors = 1;
filter.params.bpc = 8;
filter.data = vec![0];
filter.decode().unwrap();
assert!(filter.decoded.is_empty());
}
}